diff options
| author | MegaMech <MegaMech@users.noreply.github.com> | 2024-12-31 09:50:21 -0700 |
|---|---|---|
| committer | GitHub <noreply@github.com> | 2024-12-31 09:50:21 -0700 |
| commit | 1101dcafb08a1f17b06306ff44a9d869ffeb7862 (patch) | |
| tree | e63c623057b555fedd3d017b5d6c34d12de31098 /src/engine/objects | |
| parent | 7e6aba50a2a2fa80db153ad154f5ae77cc344650 (diff) | |
| parent | 7ee714e4e93609a5dbf1dc648df0ce504e3d7284 (diff) | |
Merge pull request #133 from MegaMech/next
Fix loadtile, Impl more OObjects
Diffstat (limited to 'src/engine/objects')
32 files changed, 6763 insertions, 0 deletions
diff --git a/src/engine/objects/BombKart.cpp b/src/engine/objects/BombKart.cpp new file mode 100644 index 000000000..c33b4b107 --- /dev/null +++ b/src/engine/objects/BombKart.cpp @@ -0,0 +1,528 @@ +#include <libultraship.h> +#include "BombKart.h" +#include <vector> + +#include "port/Game.h" +#include "engine/Matrix.h" + +extern "C" { +#include "macros.h" +#include "main.h" +#include "actors.h" +#include "math_util.h" +#include "sounds.h" +#include "update_objects.h" +#include "render_player.h" +#include "external.h" +#include "bomb_kart.h" +#include "collision.h" +#include "code_80086E70.h" +#include "render_objects.h" +#include "code_80057C60.h" +#include "defines.h" +#include "code_80005FD0.h" +#include "math_util_2.h" +#include "collision.h" +extern s8 gPlayerCount; +} + +size_t OBombKart::_count = 0; + +OBombKart::OBombKart(Vec3f pos, TrackWaypoint* waypoint, uint16_t waypointIndex, uint16_t state, f32 unk_3C) { + _idx = _count; + Vec3f _pos = {0, 0, 0}; + + if (waypoint) { // Spawn kart on waypoint + _pos[0] = waypoint->posX; + _pos[1] = waypoint->posY; + _pos[2] = waypoint->posZ; + } else { // Spawn kart on a surface with the provided position + + // Set height to the default value of 2000.0f unless Pos[1] is higher. + // This allows placing these on very high surfaces. + f32 height = (pos[1] > 2000.0f) ? pos[1] : 2000.0f; + _pos[0] = pos[0]; + _pos[1] = spawn_actor_on_surface(pos[0], height, pos[2]); + _pos[2] = pos[2]; + } + + WaypointIndex = waypointIndex; + Unk_3C = unk_3C; + State = static_cast<States>(state); + + Pos[0] = _pos[0]; + Pos[1] = _pos[1]; + Pos[2] = _pos[2]; + _spawnPos[0] = _pos[0]; + _spawnPos[1] = _pos[1]; + _spawnPos[2] = _pos[2]; + CenterY = _pos[1]; + WheelPos[0][0] = _pos[0]; + WheelPos[0][1] = _pos[1]; + WheelPos[0][2] = _pos[2]; + WheelPos[1][0] = _pos[0]; + WheelPos[1][1] = _pos[1]; + WheelPos[1][2] = _pos[2]; + WheelPos[2][0] = _pos[0]; + WheelPos[2][1] = _pos[1]; + WheelPos[2][2] = _pos[2]; + WheelPos[3][0] = _pos[0]; + WheelPos[3][1] = _pos[1]; + WheelPos[3][2] = _pos[2]; + check_bounding_collision(&_Collision, 2.0f, _pos[0], _pos[1], _pos[2]); + + _count++; +} + +void OBombKart::Spawn() { + find_unused_obj_index(&ObjectIndex); +} + +void OBombKart::BeginPlay() { + +} + +void OBombKart::Tick() { + f32 sp118; + f32 var_f18; + TrackWaypoint* temp_v0_2; + f32 temp_f0_3; + f32 sp108; + f32 temp_f14; + f32 temp_f16; + f32 temp_f0; + f32 temp_f0_4; + u16 waypoint; + f32 unk_3C; + u16 someRot; + f32 temp_f12; + f32 temp_f12_3; + f32 temp_f12_4; + f32 temp_f14_2; + f32 spAC; + f32 temp_f16_2; + f32 spA0; + f32 temp_f2; + f32 temp_f2_4; + f32 sp94; + f32 sp88; + Vec3f newPos; + States state; + u16 bounceTimer; + UNUSED u16 sp4C; + u16 temp_t6; + u16 temp_t7; + u16 circleTimer; + TrackWaypoint* temp_v0_4; + Player* player; + + state = State; + + if (state == States::DISABLED) { + return; + } + + if (((Unk_4A != 1) || (GetCourse() == GetPodiumCeremony()))) { + newPos[0] = Pos[0]; + newPos[1] = Pos[1]; + newPos[2] = Pos[2]; + waypoint = WaypointIndex; + unk_3C = Unk_3C; + someRot = SomeRot; + bounceTimer = BounceTimer; + circleTimer = CircleTimer; + if ((state != States::DISABLED) && (state != States::EXPLODE)) { + if (GetCourse() == GetPodiumCeremony()) { + if (D_8016347E == 1) { + player = gPlayerFour; + temp_f0 = newPos[0] - player->pos[0]; + temp_f2 = newPos[2] - player->pos[1]; + temp_f12 = newPos[2] - player->pos[2]; + if ((((temp_f0 * temp_f0) + (temp_f2 * temp_f2)) + (temp_f12 * temp_f12)) < 25.0f) { + circleTimer = 0; + state = States::EXPLODE; + player->soundEffects |= 0x400000; + player->type &= ~0x2000; + } + } + } else { + + for (size_t i = 0; i < gPlayerCount; i++) { + player = &gPlayers[i]; + if (!(player->effects & 0x80000000)) { + temp_f0 = newPos[0] - player->pos[0]; + temp_f2 = newPos[1] - player->pos[1]; + temp_f12 = newPos[2] - player->pos[2]; + if ((((temp_f0 * temp_f0) + (temp_f2 * temp_f2)) + (temp_f12 * temp_f12)) < 25.0f) { + state = States::EXPLODE; + circleTimer = 0; + if (GetCourse() == GetFrappeSnowland()) { + player->soundEffects |= 0x01000000; + } else { + player->soundEffects |= 0x400000; + } + } + } + } + } + } + switch(state) { + case States::CCW: + circleTimer = (circleTimer + 356) % 360; + temp_t6 = (circleTimer * 0xFFFF) / 360; + sp118 = coss(temp_t6) * 25.0; + temp_f0_3 = sins(temp_t6) * 25.0; + temp_v0_2 = &D_80164550[0][waypoint]; + newPos[0] = temp_v0_2->posX + sp118; + newPos[1] = CenterY + 3.5f; + newPos[2] = temp_v0_2->posZ + temp_f0_3; + D_80162FB0[0] = newPos[0]; + D_80162FB0[1] = newPos[1]; + D_80162FB0[2] = newPos[2]; + temp_t7 = (((circleTimer + 1) % 360) * 0xFFFF) / 360; + sp118 = coss(temp_t7) * 25.0; + temp_f0_3 = sins(temp_t7) * 25.0; + D_80162FC0[0] = temp_v0_2->posX + sp118; + D_80162FC0[1] = temp_v0_2->posY; + D_80162FC0[2] = temp_v0_2->posZ + temp_f0_3; + someRot = (get_angle_between_two_vectors(D_80162FB0, D_80162FC0) * 0xFFFF) / 65520; + break; + case States::CW: + circleTimer = (circleTimer + 4) % 360; + temp_t6 = (circleTimer * 0xFFFF) / 360; + sp118 = coss(temp_t6) * 25.0; + temp_f0_3 = sins(temp_t6) * 25.0; + temp_v0_2 = &D_80164550[0][waypoint]; + newPos[0] = temp_v0_2->posX + sp118; + newPos[1] = CenterY + 3.5f; + newPos[2] = temp_v0_2->posZ + temp_f0_3; + D_80162FB0[0] = newPos[0]; + D_80162FB0[1] = newPos[1]; + D_80162FB0[2] = newPos[2]; + temp_t7 = (((circleTimer + 1) % 360) * 0xFFFF) / 360; + sp118 = coss(temp_t7) * 25.0; + temp_f0_3 = sins(temp_t7) * 25.0; + D_80162FC0[0] = temp_v0_2->posX + sp118; + D_80162FC0[1] = temp_v0_2->posY; + D_80162FC0[2] = temp_v0_2->posZ + temp_f0_3; + someRot = (get_angle_between_two_vectors(D_80162FB0, D_80162FC0) * 0xFFFF) / 65520; + break; + case States::STATIONARY: + newPos[1] = CenterY + 3.5f; + someRot = 0; + break; + + case States::PODIUM_CEREMONY: + if ((D_8016347C == 0) || (gNearestWaypointByPlayerId[3] < 5)) { + break; + } else { + waypoint = func_8000D2B4(newPos[0], newPos[1], newPos[2], waypoint, 3); + if ((waypoint < 0) || (gWaypointCountByPathIndex[3] < waypoint)) { + waypoint = 0; + } + if (((s32) waypoint) < 0x1A) { + temp_v0_2 = &D_80164550[3][(waypoint + 1) % gWaypointCountByPathIndex[3]]; + D_80162FB0[0] = temp_v0_2->posX; + D_80162FB0[1] = temp_v0_2->posY; + D_80162FB0[2] = temp_v0_2->posZ; + temp_v0_4 = &D_80164550[3][(waypoint + 2) % gWaypointCountByPathIndex[3]]; + D_80162FC0[0] = temp_v0_4->posX; + D_80162FC0[1] = temp_v0_4->posY; + D_80162FC0[2] = temp_v0_4->posZ; + someRot = (get_angle_between_two_vectors(D_80162FB0, D_80162FC0) * 0xFFFF) / 65520; + } else { + D_80162FB0[0] = newPos[0]; + D_80162FB0[1] = newPos[1]; + D_80162FB0[2] = newPos[2]; + D_80162FC0[0] = -2409.197f; + D_80162FC0[1] = 0.0f; + D_80162FC0[2] = -355.254f; + someRot = (get_angle_between_two_vectors(D_80162FB0, D_80162FC0) * 0xFFFF) / 65520; + } + temp_f14 = ((D_80162FB0[0] + D_80162FC0[0]) * 0.5f) - newPos[0]; + temp_f16 = ((D_80162FB0[2] + D_80162FC0[2]) * 0.5f) - newPos[2]; + temp_f0_4 = sqrtf((temp_f14 * temp_f14) + (temp_f16 * temp_f16)); + if (temp_f0_4 > 0.01f) { + newPos[0] += (Unk_3C * temp_f14) / temp_f0_4; + newPos[2] += (Unk_3C * temp_f16) / temp_f0_4; + } else { + newPos[0] += temp_f14 / 5.0f; + newPos[2] += temp_f16 / 5.0f; + } + newPos[1] = calculate_surface_height(newPos[0], 2000.0f, newPos[2], _Collision.meshIndexZX) + 3.5f; + if (newPos[1] < (-1000.0)) { + newPos[1] = Pos[1]; + } + check_bounding_collision(&_Collision, 10.0f, newPos[0], newPos[1], newPos[2]); + } + break; + case States::CHASE: + if (!_target) { + _target = OBombKart::FindTarget(); + } else { + OBombKart::Chase(_target, newPos); + } + break; + case States::EXPLODE: + temp_v0_2 = &D_80164550[0][waypoint]; + D_80162FB0[0] = temp_v0_2->posX; + D_80162FB0[1] = temp_v0_2->posY; + D_80162FB0[2] = temp_v0_2->posZ; + temp_v0_4 = &D_80164550[0][(waypoint + 1) % gWaypointCountByPathIndex[0]]; + D_80162FC0[0] = temp_v0_4->posX; + D_80162FC0[1] = temp_v0_4->posY; + D_80162FC0[2] = temp_v0_4->posZ; + newPos[1] += 3.0f - (circleTimer * 0.3f); + someRot = (get_angle_between_two_vectors(D_80162FB0, D_80162FC0) * 0xFFFF) / 65520; + break; + default: + break; + } + + if (state == States::EXPLODE) { + sp108 = 2.0f * circleTimer; + sp118 = coss(0xFFFF - someRot) * circleTimer; + var_f18 = sins(0xFFFF - someRot) * circleTimer; + circleTimer++; + temp_f2_4 = (newPos[1] - 2.3f) + (sp108 / 3.0f); + spAC = temp_f2_4; + spA0 = temp_f2_4; + sp94 = temp_f2_4; + sp88 = temp_f2_4; + if (circleTimer >= 31) { + state = States::DISABLED; + } + } else { + sp118 = coss(0xFFFF - someRot) * 1.5f; + var_f18 = sins(0xFFFF - someRot) * 1.5f; + temp_f16_2 = newPos[1] - 2.3f; + temp_f12_3 = (bounceTimer % 3) * 0.15f; + temp_f14_2 = temp_f16_2 - temp_f12_3; + temp_f12_4 = temp_f16_2 + temp_f12_3; + spAC = temp_f14_2; + sp94 = temp_f14_2; + spA0 = temp_f12_4; + sp88 = temp_f12_4; + newPos[1] += sins((bounceTimer * 0x13FFEC) / 360); + bounceTimer = (bounceTimer + 1) % 18; + } + + WheelPos[0][0] = (sp118 - var_f18) + newPos[0]; + WheelPos[0][1] = spAC; + WheelPos[0][2] = (var_f18 + sp118) + newPos[2]; + WheelPos[1][0] = (var_f18 + sp118) + newPos[0]; + WheelPos[1][1] = spA0; + WheelPos[1][2] = (var_f18 - sp118) + newPos[2]; + WheelPos[2][0] = ((-sp118) - var_f18) + newPos[0]; + WheelPos[2][1] = sp94; + WheelPos[2][2] = ((-var_f18) + sp118) + newPos[2]; + WheelPos[3][0] = ((-sp118) + var_f18) + newPos[0]; + WheelPos[3][1] = sp88; + WheelPos[3][2] = ((-var_f18) - sp118) + newPos[2]; + Pos[0] = newPos[0]; + Pos[1] = newPos[1]; + Pos[2] = newPos[2]; + WaypointIndex = waypoint; + Unk_3C = unk_3C; + SomeRot = someRot; + State = state; + BounceTimer = bounceTimer; + CircleTimer = circleTimer; + } +} + +void OBombKart::Draw(s32 cameraId) { + + if (gModeSelection == BATTLE) { + return; + } + + if (GetCourse() == GetPodiumCeremony()) { + if ((_idx == 0) && (WaypointIndex < 16)) { + return; + } else { + cameraId = PLAYER_FOUR; + } + } + + Camera* camera; + s32 temp_s4; + s32 i; + s32 state; + + if (gGamestate == ENDING) { + cameraId = 0; + } + camera = &camera1[cameraId]; + if (cameraId == PLAYER_ONE) { + if (is_obj_flag_status_active(ObjectIndex, 0x00200000) != 0) { + Unk_4A = 0; + } else if (gGamestate != ENDING) { + Unk_4A = 1; + } + clear_object_flag(ObjectIndex, 0x00200000); + } + + // huh??? + state = State; + if (State != States::DISABLED) { + gObjectList[ObjectIndex].pos[0] = Pos[0]; + gObjectList[ObjectIndex].pos[1] = Pos[1]; + gObjectList[ObjectIndex].pos[2] = Pos[2]; + temp_s4 = func_8008A364(ObjectIndex, cameraId, 0x31C4U, 0x000001F4); + if (is_obj_flag_status_active(ObjectIndex, VISIBLE) != 0) { + set_object_flag(ObjectIndex, 0x00200000); + D_80183E80[0] = 0; + D_80183E80[1] = func_800418AC(Pos[0], Pos[2], camera->pos); + D_80183E80[2] = 0x8000; + func_800563DC(ObjectIndex, cameraId, 0x000000FF); + OBombKart::SomeRender(camera->pos); + if (((u32) temp_s4 < 0x4E21U) && (state != BOMB_STATE_EXPLODED)) { + OBombKart::LoadMtx(); + } + } + } +} + +void OBombKart::DrawBattle(s32 cameraId) { + if (gModeSelection != BATTLE) { + return; + } + Player* temp_v0; + s32 temp_s1; + s32 playerId; + Object* object; + + for (playerId = 0; playerId < gPlayerCount; playerId++) { + object = &gObjectList[ObjectIndex]; + if (object->state != 0) { + temp_s1 = object->primAlpha; + temp_v0 = &gPlayerOne[playerId]; + object->pos[0] = temp_v0->pos[0]; + object->pos[1] = temp_v0->pos[1] - 2.0; + object->pos[2] = temp_v0->pos[2]; + object->surfaceHeight = temp_v0->unk_074; + func_800563DC(ObjectIndex, cameraId, temp_s1); + func_8005669C(ObjectIndex, cameraId, temp_s1); + func_800568A0(ObjectIndex, cameraId); + } + } +} + +void OBombKart::SomeRender(Vec3f arg1) { + D_80183E80[0] = 0; + D_80183E80[2] = 0x8000; + gSPDisplayList(gDisplayListHead++, (Gfx*)D_0D0079C8); + load_texture_block_rgba16_mirror((u8*) D_0D02AA58, 0x00000010, 0x00000010); + D_80183E80[1] = func_800418AC(WheelPos[0][0], WheelPos[0][2], arg1); + func_800431B0(WheelPos[0], D_80183E80, 0.15f, (Vtx*)common_vtx_rectangle); + D_80183E80[1] = func_800418AC(WheelPos[1][0], WheelPos[1][2], arg1); + func_800431B0(WheelPos[1], D_80183E80, 0.15f, (Vtx*)common_vtx_rectangle); + D_80183E80[1] = func_800418AC(WheelPos[2][0], WheelPos[2][2], arg1); + func_800431B0(WheelPos[2], D_80183E80, 0.15f, (Vtx*)common_vtx_rectangle); + D_80183E80[1] = func_800418AC(WheelPos[3][0], WheelPos[3][2], arg1); + func_800431B0(WheelPos[3], D_80183E80, 0.15f, (Vtx*)common_vtx_rectangle); + gSPTexture(gDisplayListHead++, 1, 1, 0, G_TX_RENDERTILE, G_OFF); +} + +void OBombKart::LoadMtx() { + Mat4 mat; + + D_80183E50[0] = Pos[0]; + D_80183E50[1] = CenterY + 1.0; + D_80183E50[2] = Pos[2]; + set_transform_matrix(mat, _Collision.orientationVector, D_80183E50, 0U, 0.5f); + //convert_to_fixed_point_matrix(&gGfxPool->mtxHud[gMatrixHudCount], mat); + + AddHudMatrix(mat, G_MTX_LOAD | G_MTX_NOPUSH | G_MTX_MODELVIEW); + + // gSPMatrix(gDisplayListHead++, VIRTUAL_TO_PHYSICAL(&gGfxPool->mtxHud[gMatrixHudCount++]), + // G_MTX_LOAD | G_MTX_NOPUSH | G_MTX_MODELVIEW); + gSPDisplayList(gDisplayListHead++, (Gfx*)D_0D007B98); +} + +void OBombKart::Waypoint(s32 screenId) { + s32 playerWaypoint; + s32 bombWaypoint; + s32 waypointDiff; + + playerWaypoint = gNearestWaypointByPlayerId[screenId]; + playerHUD[screenId].unk_74 = 0; + if ((State == States::EXPLODE) || (State == States::DISABLED)) { return; }; + bombWaypoint = WaypointIndex; + waypointDiff = bombWaypoint - playerWaypoint; + if ((waypointDiff < -5) || (waypointDiff > 0x1E)) { return; }; + playerHUD[screenId].unk_74 = 1; +} + +void OBombKart::Collision(s32 playerId, Player* player) { + +} + +Player* OBombKart::FindTarget() { + for (size_t i = 0; i < NUM_PLAYERS; i++) { + if (gPlayers[i].type & PLAYER_HUMAN) { + if (is_within_distance_2d(Pos[0], Pos[2], gPlayers[i].pos[0], gPlayers[i].pos[2], 500)) { + return &gPlayers[i]; + } + } + } + return NULL; +} + +void OBombKart::Chase(Player* player, Vec3f pos) { + const f32 speed = 2.7f; // Speed the kart uses in a chase + + if (!player) return; // Ensure player is valid + + // Calculate the directional vector toward the player + Vec3f direction; + direction[0] = player->pos[0] - pos[0]; + direction[1] = player->pos[1] - pos[1]; + direction[2] = player->pos[2] - pos[2]; + + // Calculate the distance in the XZ plane + f32 xz_dist = sqrtf((direction[0] * direction[0]) + (direction[2] * direction[2])); + + // Reset distance + if (xz_dist > 700.0f) { + _target = NULL; + pos[0] = _spawnPos[0]; + pos[1] = _spawnPos[1]; + pos[2] = _spawnPos[2]; + return; + } + + // Break off the chase if player has boo item + if (player->effects & BOO_EFFECT) { + _target = NULL; + pos[0] = _spawnPos[0]; + pos[1] = _spawnPos[1]; + pos[2] = _spawnPos[2]; + return; + } + + if (xz_dist > 0.0f) { + + // Normalize the direction vector in the XZ plane + direction[0] /= xz_dist; + direction[2] /= xz_dist; + + // Scale the movement by a fixed step size + pos[0] += direction[0] * speed; + pos[2] += direction[2] * speed; + } + + Vec3f newPosition; + newPosition[0] = pos[0]; + newPosition[1] = pos[1]; + newPosition[2] = pos[2]; + + newPosition[1] = calculate_surface_height(pos[0], 2000.0f, pos[2], _Collision.meshIndexZX) + 3.5f; + + pos[0] = newPosition[0]; + pos[1] = newPosition[1]; + pos[2] = newPosition[2]; + + check_bounding_collision(&_Collision, 10.0f, pos[0], pos[1], pos[2]); +}
\ No newline at end of file diff --git a/src/engine/objects/BombKart.h b/src/engine/objects/BombKart.h new file mode 100644 index 000000000..7ceba7ecb --- /dev/null +++ b/src/engine/objects/BombKart.h @@ -0,0 +1,85 @@ +#pragma once + +#include <libultraship.h> +#include "src/engine/vehicles/Vehicle.h" +#include <vector> +#include "engine/Matrix.h" + +#include "World.h" + +extern "C" { +#include "macros.h" +#include "main.h" +#include "vehicles.h" +#include "waypoints.h" +#include "common_structs.h" +#include "objects.h" +} + +/** + * Used in VS mode + * + * This differs from the other vehicle classes in that it does not get added to the standard actor list + * So this is sort of its own thing. Draw call in different place too. + */ +class OBombKart { +private: + enum States : uint16_t { // 0,1,3,5 + DISABLED, + CCW, + CW, + STATIONARY, + CHASE, + EXPLODE, + PODIUM_CEREMONY, + }; + +public: + + const char* Type; + + Vec3f Pos; + Vec3f WheelPos[4]; //! @todo Turn WheelPos into a struct + f32 Unk_3C; + u16 SomeRot; // Some angle + u16 WaypointIndex; // The waypoint the kart circles + States State = States::DISABLED; + u16 BounceTimer = 0; + u16 CircleTimer = 0; + u16 Unk_4A = 0; + s16 Unk_4C = 1; + f32 CenterY; // Center of the circle + s32 ObjectIndex = 0; // Index into gObjectList + Collision _Collision; + + // Set waypoint to NULL if using a spawn position and not a waypoint. + explicit OBombKart(Vec3f pos, TrackWaypoint* waypoint, uint16_t waypointIndex, uint16_t state, f32 unk_3C); + + ~OBombKart() { + _count--; + } + + static size_t GetCount() { + return _count; + } + + void Spawn(); + void BeginPlay(); + void Tick(); + void Draw(s32 cameraId); + void DrawBattle(s32 cameraId); + void Collision(s32 playerId, Player* player); + void SomeRender(Vec3f arg1); + void LoadMtx(); + void Waypoint(s32 screenId); +private: + static size_t _count; + s32 _idx; + Player* FindTarget(); + void Chase(Player*, Vec3f pos); + + Vec3f _spawnPos; + Player* _target = NULL; + + +}; diff --git a/src/engine/objects/CheepCheep.cpp b/src/engine/objects/CheepCheep.cpp new file mode 100644 index 000000000..10cdf6602 --- /dev/null +++ b/src/engine/objects/CheepCheep.cpp @@ -0,0 +1,243 @@ +#include "CheepCheep.h" + +#include "assets/banshee_boardwalk_data.h" +#include "assets/common_data.h" + +extern "C" { +#include "math_util.h" +#include "math_util_2.h" +#include "render_objects.h" +#include "update_objects.h" +#include "code_800029B0.h" +#include "code_80086E70.h" +#include "waypoints.h" +#include "code_80057C60.h" +#include "some_data.h" +extern Vec3s D_800E634C[]; +extern Lights1 D_800E45C0[]; +} + +OCheepCheep::OCheepCheep(const FVector& pos, CheepType type, IPathSpan span) { + _type = type; + _spawnPos = pos; + _span = span; +} + +void OCheepCheep::Tick() { // update_cheep_cheep + s32 objectIndex; + switch (_type) { + case CheepType::RACE: + UNUSED s32 pad; + + OCheepCheep::func_8007BD04(0); + objectIndex = indexObjectList2[0]; + OCheepCheep::func_8007BBBC(objectIndex); + object_calculate_new_pos_offset(objectIndex); + break; + case CheepType::PODIUM_CEREMONY: + objectIndex = indexObjectList2[0]; + if (D_801658BC == 1) { + D_801658BC = 0; + init_object(objectIndex, 0); + } + if (gObjectList[objectIndex].state != 0) { + OCheepCheep::func_8007BEC8(objectIndex); + OCheepCheep::func_8007BFB0(objectIndex); + } + break; + } +} + +void OCheepCheep::Draw(s32 cameraId) { // func_8005217C + Lights1* D_800E45C0l = (Lights1*) (D_800E45C0); + Object* object; + s32 temp_a3; + temp_a3 = indexObjectList2[0]; + object = &gObjectList[temp_a3]; + if (object->state >= 2) { + if (is_obj_flag_status_active(temp_a3, 0x10) != 0) { + rsp_set_matrix_transformation(object->pos, object->direction_angle, object->sizeScaling); + func_800520C0(temp_a3); + + gSPDisplayList(gDisplayListHead++, (Gfx*)D_0D007828); + gSPLight(gDisplayListHead++, &D_800E45C0l[0].l[0], LIGHT_1); + gSPLight(gDisplayListHead++, &D_800E45C0l[0].a, LIGHT_2); + gSPDisplayList(gDisplayListHead++, (Gfx*)d_course_banshee_boardwalk_dl_7B38); + gSPLight(gDisplayListHead++, &D_800E45C0l[1].l[0], LIGHT_1); + gSPLight(gDisplayListHead++, &D_800E45C0l[1].a, LIGHT_2); + gSPDisplayList(gDisplayListHead++, (Gfx*)d_course_banshee_boardwalk_dl_7978); + gSPLight(gDisplayListHead++, &D_800E45C0l[2].l[0], LIGHT_1); + gSPLight(gDisplayListHead++, &D_800E45C0l[2].a, LIGHT_2); + gSPDisplayList(gDisplayListHead++, (Gfx*)d_course_banshee_boardwalk_dl_78C0); + gSPLight(gDisplayListHead++, &D_800E45C0l[3].l[0], LIGHT_1); + gSPLight(gDisplayListHead++, &D_800E45C0l[3].a, LIGHT_2); + gSPDisplayList(gDisplayListHead++, (Gfx*)d_course_banshee_boardwalk_dl_7650); + } + } +} + +void OCheepCheep::func_8007BBBC(s32 objectIndex) { + f32 var_f14; + Object* object; + + object = &gObjectList[objectIndex]; + switch (object->state) { + case 1: + func_800735BC(objectIndex, (Gfx*)d_course_banshee_boardwalk_dl_cheep_cheep, 2.0f); + set_object_flag(objectIndex, 0x00000010); + object->unk_0D5 = 0; + break; + case 2: + if (gIsMirrorMode != 0) { + func_80087E08(objectIndex, 18.0f, 0.7f, 25.0f, (s16) -0x00005800, 0x0000012C); + } else { + func_80087E08(objectIndex, 18.0f, 0.7f, 25.0f, (s16) 0x00005800, 0x0000012C); + } + if (object->velocity[2] < 0.0f) { + var_f14 = -object->velocity[2]; + } else { + var_f14 = object->velocity[2]; + } + object->direction_angle[0] = func_80041658(object->velocity[1], var_f14); + set_and_run_timer_object(objectIndex, 0x00000046); + break; + case 3: + func_80072428(objectIndex); + break; + case 0: + break; + } +} + +void OCheepCheep::func_8007BD04(s32 playerId) { + s32 objectIndex; + + objectIndex = indexObjectList2[0]; + if (gObjectList[objectIndex].state == 0) { + if (((s32) gNearestWaypointByPlayerId[playerId] >= _span.Start) && + ((s32) gNearestWaypointByPlayerId[playerId] <= _span.End)) { + set_obj_origin_pos(objectIndex, xOrientation * _spawnPos.x, _spawnPos.y, _spawnPos.z); + init_object(objectIndex, 1); + } + } +} + +void OCheepCheep::init_var_cheep_cheep(s32 objectIndex) { + Object* object; + + object = &gObjectList[objectIndex]; + object->unk_0D5 = 1; + object->status = 0; + object->model = (Gfx*)d_course_banshee_boardwalk_dl_cheep_cheep; + object->sizeScaling = 0.2f; + object_next_state(objectIndex); + set_obj_origin_pos(objectIndex, D_800E634C[0][0], D_800E634C[0][1] + 55.0, D_800E634C[0][2]); + set_obj_origin_offset(objectIndex, 0.0f, 30.0f, 0.0f); + set_obj_direction_angle(objectIndex, 0U, 0x3800U, 0U); +} + +void OCheepCheep::func_8007BEC8(s32 objectIndex) { + Object* object; + + object = &gObjectList[objectIndex]; + switch (object->state) { + case 1: + OCheepCheep::init_var_cheep_cheep(objectIndex); + break; + case 2: + if (set_and_run_timer_object(objectIndex, 0x0000003C) != 0) { + set_object_flag(objectIndex, 0x00000010); + func_80086E70(objectIndex); + } + break; + case 3: + if (object->unk_0AE == 0) { + object_next_state(objectIndex); + } + break; + case 4: + if (set_and_run_timer_object(objectIndex, 0x0000000A) != 0) { + func_8008701C(objectIndex, 2); + } + break; + case 5: + if (object->unk_0AE == 0) { + func_80072428(objectIndex); + } + break; + case 0: + default: + break; + } +} + +void OCheepCheep::func_8007BFB0(s32 objectIndex) { + Object* object; + + object = &gObjectList[objectIndex]; + switch (object->unk_0AE) { + case 0: + break; + case 1: + object->velocity[1] = -0.2f; + if ((f64) object->offset[1] <= 0.0) { + object->offset[1] = 0.0f; + object->velocity[1] = 0.0f; + func_80086F60(objectIndex); + } + break; + case 2: + if (func_800871AC(objectIndex, 0x00000014) != 0) { + object->unk_084[7] = 0x0040; + } + break; + case 3: + object->sizeScaling = (f32) ((f64) object->sizeScaling - 0.0015); + if ((s32) object->direction_angle[0] >= 0xA01) { + object->unk_084[7] -= 4; + } + if (u16_step_up_towards(object->direction_angle, 0x0C00U, (u16) object->unk_084[7]) != 0) { + func_80086FD4(objectIndex); + } + break; + case 4: + object->sizeScaling = (f32) ((f64) object->sizeScaling - 0.0015); + object->unk_034 = 0.001f; + func_80086FD4(objectIndex); + object->unk_084[7] = 0; + break; + case 5: + if (object->unk_034 <= 0.004) { + object->unk_034 += 0.0002; + } + object->sizeScaling += object->unk_034; + s16_step_up_towards(&object->unk_084[7], 0x0100, 0x0010); + object->direction_angle[0] -= object->unk_084[7]; + if (func_80087060(objectIndex, 0x00000035) != 0) { + func_80086FD4(objectIndex); + } + break; + case 6: + if (func_80087060(objectIndex, 0x0000000F) != 0) { + func_80086FD4(objectIndex); + D_801658CE = 1; + } + break; + case 7: + object->sizeScaling = (f32) ((f64) object->sizeScaling - 0.05); + if ((f64) object->sizeScaling <= 0.01) { + clear_object_flag(objectIndex, 0x00000010); + object->sizeScaling = 0.000001f; + func_80086FD4(objectIndex); + } + break; + case 8: + func_80086F60(objectIndex); + break; + } + if (object->unk_0AE < 0xA) { + func_80074344(objectIndex, &object->sizeScaling, 0.2f, 0.21f, 0.001f, 0, -1); + } + object_add_velocity_offset_y(objectIndex); + object_calculate_new_pos_offset(objectIndex); +} diff --git a/src/engine/objects/CheepCheep.h b/src/engine/objects/CheepCheep.h new file mode 100644 index 000000000..32e9a5aeb --- /dev/null +++ b/src/engine/objects/CheepCheep.h @@ -0,0 +1,48 @@ +#pragma once + +#include <libultraship.h> +#include <vector> +#include "Object.h" + +#include "World.h" + +extern "C" { +#include "macros.h" +#include "main.h" +#include "common_structs.h" +#include "objects.h" +#include "course_offsets.h" +#include "some_data.h" +} + + +class OCheepCheep : public OObject { +public: + enum CheepType { + RACE, + PODIUM_CEREMONY + }; + + enum Behaviour : uint16_t { + }; + +public: + + explicit OCheepCheep(const FVector& pos, CheepType type, IPathSpan span); + + virtual void Tick() override; + virtual void Draw(s32 cameraId) override; + void func_8007BBBC(s32 objectIndex); + void func_8007BD04(s32 playerId); + void init_var_cheep_cheep(s32 objectIndex); + void func_8007BEC8(s32 objectIndex); + void func_8007BFB0(s32 objectIndex); + +private: + + s32 _idx; + CheepType _type; + FVector _spawnPos; + IPathSpan _span; + +}; diff --git a/src/engine/objects/Crab.cpp b/src/engine/objects/Crab.cpp new file mode 100644 index 000000000..b05bc85e3 --- /dev/null +++ b/src/engine/objects/Crab.cpp @@ -0,0 +1,175 @@ +#include <libultraship.h> +#include <libultra/gbi.h> +#include "Crab.h" +#include <vector> + +#include "port/Game.h" + +extern "C" { +#include "macros.h" +#include "main.h" +#include "defines.h" +#include "camera.h" +#include "update_objects.h" +#include "render_objects.h" +#include "actors.h" +#include "code_80057C60.h" +#include "code_80086E70.h" +#include "math_util.h" +#include "math_util_2.h" +#include "code_80005FD0.h" +#include "some_data.h" +#include "ceremony_and_credits.h" +#include "assets/koopa_troopa_beach_data.h" +} + +size_t OCrab::_count = 0; + +OCrab::OCrab(const FVector2D& start, const FVector2D& end) { + s32 objectId; + _idx = _count; + _start = start; + _end = end; + + objectId = indexObjectList1[_idx]; + init_object(objectId, 0); + gObjectList[objectId].pos[0] = gObjectList[objectId].origin_pos[0] = start.x * xOrientation; + gObjectList[objectId].pos[2] = gObjectList[objectId].origin_pos[2] = start.z; + + gObjectList[objectId].unk_01C[0] = end.x * xOrientation; + gObjectList[objectId].unk_01C[2] = end.z; + + _count++; +} + +void OCrab::Tick(void) { + s32 objectIndex; + + objectIndex = indexObjectList1[_idx]; + if (gObjectList[objectIndex].state != 0) { + OCrab::func_80082B34(objectIndex); + func_8008A6DC(objectIndex, 500.0f); + OCrab::func_80082C30(objectIndex); + OCrab::func_80082E18(objectIndex); + } +} + +void OCrab::Draw(s32 cameraId) { + Camera* camera; + s32 objectIndex = indexObjectList1[_idx]; + if (gObjectList[objectIndex].state >= 2) { + Vtx* vtx = (Vtx*) LOAD_ASSET_RAW(common_vtx_hedgehog); + camera = &camera1[cameraId]; + func_8004A6EC(objectIndex, 0.5f); + gObjectList[objectIndex].orientation[1] = + func_800418AC(gObjectList[objectIndex].pos[0], gObjectList[objectIndex].pos[2], camera->pos); + draw_2d_texture_at(gObjectList[objectIndex].pos, gObjectList[objectIndex].orientation, + gObjectList[objectIndex].sizeScaling, (u8*) gObjectList[objectIndex].activeTLUT, + (uint8_t*)gObjectList[objectIndex].activeTexture, vtx, 64, 64, + 64, 32); + } +} + +void OCrab::DrawModel(s32 cameraId) { + s32 someIndex; + s32 test; + + test = indexObjectList1[_idx]; + func_8008A364(test, cameraId, 0x2AABU, 800); + if (is_obj_flag_status_active(test, VISIBLE) != 0) { + Camera *camera; + s32 objectIndex; + + if (gObjectList[objectIndex].state >= 2) { + camera = &camera1[cameraId]; + func_8004A6EC(objectIndex, 0.5f); + gObjectList[objectIndex].orientation[1] = func_800418AC(gObjectList[objectIndex].pos[0], gObjectList[objectIndex].pos[2], camera->pos); + draw_2d_texture_at(gObjectList[objectIndex].pos, gObjectList[objectIndex].orientation, gObjectList[objectIndex].sizeScaling, (u8*) gObjectList[objectIndex].activeTLUT, (u8*)gObjectList[objectIndex].activeTexture, (Vtx*)common_vtx_hedgehog, 0x00000040, 0x00000040, 0x00000040, 0x00000020); + } + } +} + +void OCrab::init_ktb_crab(s32 objectIndex) { + Object* object; + + init_texture_object(objectIndex, (uint8_t*) d_course_koopa_troopa_beach_crab_tlut, + (const char**) d_course_koopa_troopa_beach_crab_frames, 64, (u16) 64); + object = &gObjectList[objectIndex]; + object->sizeScaling = 0.15f; + object->textureListIndex = 0; + object_next_state(objectIndex); + object->boundingBoxSize = 1; + set_object_flag(objectIndex, 0x04000420); + func_80086EAC(objectIndex, 0, 1); + set_obj_origin_offset(objectIndex, 0.0f, 0.0f, 0.0f); + set_obj_orientation(objectIndex, 0U, 0U, 0x8000U); + object->unk_034 = 1.5f; + set_object_flag(objectIndex, 0x00000200); +} + +void OCrab::func_80082B34(s32 objectIndex) { + switch (gObjectList[objectIndex].state) { /* irregular */ + case 0: + break; + case 1: + OCrab::init_ktb_crab(objectIndex); + break; + case 2: + func_80072E54(objectIndex, 0, 3, 1, 2, -1); + break; + case 3: + func_80072E54(objectIndex, 4, 6, 1, 2, -1); + break; + } + if (gObjectList[objectIndex].state >= 2) { + func_80073514(objectIndex); + } +} + +void OCrab::func_80082C30(s32 objectIndex) { + switch (gObjectList[objectIndex].unk_0AE) { + case 1: + if (func_80087A0C(objectIndex, gObjectList[objectIndex].origin_pos[0], gObjectList[objectIndex].unk_01C[0], + gObjectList[objectIndex].origin_pos[2], gObjectList[objectIndex].unk_01C[2]) != 0) { + func_800726CC(objectIndex, 3); + func_80086FD4(objectIndex); + } + break; + case 2: + if (func_80087104(objectIndex, 0x003CU) != 0) { + gObjectList[objectIndex].unk_034 = 0.8f; + func_800726CC(objectIndex, 2); + func_80086FD4(objectIndex); + } + break; + case 3: + if (func_80087954(objectIndex, 0x0000003C) != 0) { + func_80086FD4(objectIndex); + func_800726CC(objectIndex, 3); + } + break; + case 4: + if (func_80087104(objectIndex, 0x003CU) != 0) { + func_800726CC(objectIndex, 2); + func_80086FD4(objectIndex); + } + break; + case 5: + if (func_8008789C(objectIndex, 0x0000003C) != 0) { + func_800726CC(objectIndex, 3); + func_8008701C(objectIndex, 2); + } + break; + } + object_calculate_new_pos_offset(objectIndex); + if (is_obj_flag_status_active(objectIndex, VISIBLE) != 0) { + func_80088538(objectIndex); + gObjectList[objectIndex].pos[1] = (f32) (gObjectList[objectIndex].surfaceHeight + 2.5); + } +} + +void OCrab::func_80082E18(s32 objectIndex) { + if (gObjectList[objectIndex].state >= 2) { + func_80089F24(objectIndex); + } +} diff --git a/src/engine/objects/Crab.h b/src/engine/objects/Crab.h new file mode 100644 index 000000000..281b080ca --- /dev/null +++ b/src/engine/objects/Crab.h @@ -0,0 +1,48 @@ +#pragma once + +#include <libultraship.h> +#include <vector> +#include "engine/objects/Object.h" +#include "World.h" + +extern "C" { +#include "macros.h" +#include "main.h" +#include "vehicles.h" +#include "waypoints.h" +#include "common_structs.h" +#include "objects.h" +#include "course_offsets.h" +#include "some_data.h" +} + +/** + * @arg start x and z spawn location + * @arg end x and z patrol location + * + * Crab patrols between start and end. + * The game automatically places the actor on the course surface. + * Therefore, providing a Y height is unnecessary. + * + * Crab appears to have a maximum patrolling distance and will patrol between + * end --> max distance rather than start --> end or start --> max distance. + */ +class OCrab : public OObject { +public: + explicit OCrab(const FVector2D& start, const FVector2D& end); + + virtual void Tick() override; + virtual void Draw(s32 cameraId) override; + void DrawModel(s32 cameraId); + + void init_ktb_crab(s32 objectIndex); + void func_80082B34(s32 objectIndex); + void func_80082C30(s32 objectIndex); + void func_80082E18(s32 objectIndex); + +private: + FVector2D _start; + FVector2D _end; + static size_t _count; + s32 _idx; +}; diff --git a/src/engine/objects/Flagpole.cpp b/src/engine/objects/Flagpole.cpp new file mode 100644 index 000000000..682f74915 --- /dev/null +++ b/src/engine/objects/Flagpole.cpp @@ -0,0 +1,85 @@ +#include "Flagpole.h" +#include "World.h" + +extern "C" { +#include "code_800029B0.h" +#include "render_objects.h" +#include "update_objects.h" +#include "assets/yoshi_valley_data.h" +#include "assets/common_data.h" +#include "math_util.h" +#include "math_util_2.h" +#include "code_80086E70.h" +#include "code_80057C60.h" +} + +size_t OFlagpole::_count = 0; + +OFlagpole::OFlagpole(const FVector& pos, s16 direction) { + _idx = _count; + _pos = pos; + _direction = direction; + + init_object(indexObjectList1[_idx], 0); + + _count++; +} + +void OFlagpole::Tick() { // func_80083080 + s32 objectIndex = indexObjectList1[_idx]; + + if (gObjectList[objectIndex].state != 0) { + OFlagpole::func_80083018(objectIndex); + OFlagpole::func_80083060(objectIndex); + } +} + +void OFlagpole::Draw(s32 cameraId) { // func_80055228 + s32 objectIndex = indexObjectList1[_idx]; + + func_8008A364(objectIndex, cameraId, 0x4000U, 0x000005DC); + if (is_obj_flag_status_active(objectIndex, VISIBLE) != 0) { + OFlagpole::func_80055164(objectIndex); + } +} + +void OFlagpole::func_80055164(s32 objectIndex) { // func_80055164 + if (gObjectList[objectIndex].state >= 2) { + gSPDisplayList(gDisplayListHead++, (Gfx*)D_0D0077A0); + rsp_set_matrix_transformation(gObjectList[objectIndex].pos, gObjectList[objectIndex].direction_angle, + gObjectList[objectIndex].sizeScaling); + if (gIsGamePaused == 0) { + gObjectList[objectIndex].unk_0A2 = render_animated_model((Armature*) gObjectList[objectIndex].model, + (Animation**) gObjectList[objectIndex].vertex, 0, + gObjectList[objectIndex].unk_0A2); + } else { + render_animated_model((Armature*) gObjectList[objectIndex].model, + (Animation**) gObjectList[objectIndex].vertex, 0, gObjectList[objectIndex].unk_0A2); + } + } +} + +void OFlagpole::func_80082F1C(s32 objectIndex) { + gObjectList[objectIndex].model = (Gfx*) d_course_yoshi_valley_unk5; + gObjectList[objectIndex].vertex = (Vtx*) d_course_yoshi_valley_unk4; + gObjectList[objectIndex].sizeScaling = 0.027f; + object_next_state(objectIndex); + set_obj_origin_pos(objectIndex, _pos.x * xOrientation, _pos.y, _pos.z); + set_obj_origin_offset(objectIndex, 0.0f, 0.0f, 0.0f); + set_obj_direction_angle(objectIndex, 0U, _direction, 0U); +} + +void OFlagpole::func_80083018(s32 objectIndex) { + switch (gObjectList[objectIndex].state) { + case 1: + OFlagpole::func_80082F1C(objectIndex); + break; + case 0: + default: + break; + } +} + +void OFlagpole::func_80083060(s32 objectIndex) { + object_calculate_new_pos_offset(objectIndex); +} diff --git a/src/engine/objects/Flagpole.h b/src/engine/objects/Flagpole.h new file mode 100644 index 000000000..5824fcf18 --- /dev/null +++ b/src/engine/objects/Flagpole.h @@ -0,0 +1,45 @@ +#pragma once + +#include <libultraship.h> +#include <vector> +#include "Object.h" + +#include "World.h" + +extern "C" { +#include "macros.h" +#include "main.h" +#include "vehicles.h" +#include "waypoints.h" +#include "common_structs.h" +#include "objects.h" +#include "camera.h" +#include "some_data.h" +} + +class OFlagpole : public OObject { +public: + explicit OFlagpole(const FVector& pos, s16 direction); + + ~OFlagpole() { + _count--; + } + + static size_t GetCount() { + return _count; + } + + virtual void Tick() override; + virtual void Draw(s32 cameraId) override; + + void func_80055164(s32 objectIndex); + void func_80082F1C(s32 objectIndex); + void func_80083018(s32 objectIndex); + void func_80083060(s32 objectIndex); + +private: + FVector _pos; + s16 _direction; + static size_t _count; + size_t _idx; +}; diff --git a/src/engine/objects/Hedgehog.cpp b/src/engine/objects/Hedgehog.cpp new file mode 100644 index 000000000..133a1f38d --- /dev/null +++ b/src/engine/objects/Hedgehog.cpp @@ -0,0 +1,183 @@ +#include "Hedgehog.h" +#include "World.h" + +extern "C" { +#include "render_objects.h" +#include "update_objects.h" +#include "assets/yoshi_valley_data.h" +#include "assets/common_data.h" +#include "math_util.h" +#include "math_util_2.h" +#include "code_80086E70.h" +#include "code_80057C60.h" +} + +size_t OHedgehog::_count = 0; + +OHedgehog::OHedgehog(const FVector& pos, const FVector2D& patrolPoint, s16 unk) { + _idx = _count; + _pos = pos; + + s32 objectId = indexObjectList2[_idx]; + init_object(objectId, 0); + gObjectList[objectId].pos[0] = gObjectList[objectId].origin_pos[0] = pos.x * xOrientation; + gObjectList[objectId].pos[1] = gObjectList[objectId].surfaceHeight = pos.y + 6.0; + gObjectList[objectId].pos[2] = gObjectList[objectId].origin_pos[2] = pos.z; + gObjectList[objectId].unk_0D5 = (u8)unk; + gObjectList[objectId].unk_09C = patrolPoint.x * xOrientation; + gObjectList[objectId].unk_09E = patrolPoint.z; + + _count++; +} + +void OHedgehog::Tick() { + s32 objectIndex = indexObjectList2[_idx]; + + OHedgehog::func_800833D0(objectIndex, _idx); + OHedgehog::func_80083248(objectIndex); + OHedgehog::func_80083474(objectIndex); + + // This func clears a bit from all hedgehogs. This results in setting the height of all hedgehogs to zero. + // The solution is to only clear the bit from the current instance; `self` or `this` + //func_80072120(indexObjectList2, NUM_HEDGEHOGS); + clear_object_flag(objectIndex, 0x00600000); // The fix +} + +void OHedgehog::Draw(s32 cameraId) { + s32 objectIndex = indexObjectList2[_idx]; + u32 something = func_8008A364(objectIndex, cameraId, 0x4000U, 0x000003E8); + + if (CVarGetInteger("gNoCulling", 0) == 1) { + something = MIN(something, 0x52211U - 1); + } + if (is_obj_flag_status_active(objectIndex, VISIBLE) != 0) { + set_object_flag(objectIndex, 0x00200000); + if (something < 0x2711U) { + set_object_flag(objectIndex, 0x00000020); + } else { + clear_object_flag(objectIndex, 0x00000020); + } + if (something < 0x57E41U) { + set_object_flag(objectIndex, 0x00400000); + } + if (something < 0x52211U) { + OHedgehog::func_800555BC(objectIndex, cameraId); + } + } +} + +void OHedgehog::func_800555BC(s32 objectIndex, s32 cameraId) { + Camera* camera; + + if (gObjectList[objectIndex].state >= 2) { + camera = &camera1[cameraId]; + OHedgehog::func_8004A870(objectIndex, 0.7f); + gObjectList[objectIndex].orientation[1] = + func_800418AC(gObjectList[objectIndex].pos[0], gObjectList[objectIndex].pos[2], camera->pos); + draw_2d_texture_at(gObjectList[objectIndex].pos, gObjectList[objectIndex].orientation, + gObjectList[objectIndex].sizeScaling, (u8*) gObjectList[objectIndex].activeTLUT, + (u8*)gObjectList[objectIndex].activeTexture, gObjectList[objectIndex].vertex, 64, 64, 64, 32); + } +} + +void OHedgehog::func_8004A870(s32 objectIndex, f32 arg1) { + Mat4 mtx; + Object* object; + + if ((is_obj_flag_status_active(objectIndex, 0x00000020) != 0) && + (is_obj_flag_status_active(objectIndex, 0x00800000) != 0)) { + object = &gObjectList[objectIndex]; + D_80183E50[0] = object->pos[0]; + D_80183E50[1] = object->surfaceHeight + 0.8; + D_80183E50[2] = object->pos[2]; + set_transform_matrix(mtx, object->unk_01C, D_80183E50, 0U, arg1); + // convert_to_fixed_point_matrix(&gGfxPool->mtxHud[gMatrixHudCount], mtx); + // gSPMatrix(gDisplayListHead++, VIRTUAL_TO_PHYSICAL(&gGfxPool->mtxHud[gMatrixHudCount++]), + // G_MTX_NOPUSH | G_MTX_LOAD | G_MTX_MODELVIEW); + + AddHudMatrix(mtx, G_MTX_NOPUSH | G_MTX_LOAD | G_MTX_MODELVIEW); + gSPDisplayList(gDisplayListHead++, (Gfx*)D_0D007B98); + } +} + +const char* sHedgehogTexList[] = { d_course_yoshi_valley_hedgehog }; + +void OHedgehog::func_8008311C(s32 objectIndex, s32 arg1) { + Object* object; + Vtx* vtx = (Vtx*) LOAD_ASSET_RAW(common_vtx_hedgehog); + + init_texture_object(objectIndex, (u8*)d_course_yoshi_valley_hedgehog_tlut, sHedgehogTexList, 0x40U, (u16) 0x00000040); + object = &gObjectList[objectIndex]; + object->activeTLUT = d_course_yoshi_valley_hedgehog_tlut; + object->activeTexture = d_course_yoshi_valley_hedgehog; + object->vertex = vtx; + object->sizeScaling = 0.2f; + object->textureListIndex = 0; + object_next_state(objectIndex); + set_obj_origin_offset(objectIndex, 0.0f, 0.0f, 0.0f); + set_obj_orientation(objectIndex, 0U, 0U, 0x8000U); + object->unk_034 = ((arg1 % 6) * 0.1) + 0.5; + func_80086E70(objectIndex); + set_object_flag(objectIndex, 0x04000600); + object->boundingBoxSize = 2; +} + +void OHedgehog::func_80083248(s32 objectIndex) { + switch (gObjectList[objectIndex].unk_0AE) { + case 0: + break; + case 1: + if (func_80087A0C(objectIndex, gObjectList[objectIndex].origin_pos[0], gObjectList[objectIndex].unk_09C, + gObjectList[objectIndex].origin_pos[2], gObjectList[objectIndex].unk_09E) != 0) { + func_80086FD4(objectIndex); + } + break; + case 2: + func_800871AC(objectIndex, 0x0000003C); + break; + case 3: + if (func_80087A0C(objectIndex, gObjectList[objectIndex].unk_09C, gObjectList[objectIndex].origin_pos[0], + gObjectList[objectIndex].unk_09E, gObjectList[objectIndex].origin_pos[2]) != 0) { + func_80086FD4(objectIndex); + } + break; + case 4: + if (func_80087060(objectIndex, 0x0000003C) != 0) { + func_8008701C(objectIndex, 1); + } + break; + } + object_calculate_new_pos_offset(objectIndex); + if (is_obj_flag_status_active(objectIndex, 0x00200000) != 0) { + if (is_obj_flag_status_active(objectIndex, 0x00400000) != 0) { + func_8008861C(objectIndex); + } + gObjectList[objectIndex].pos[1] = gObjectList[objectIndex].surfaceHeight + 6.0; + } +} + +void OHedgehog::func_800833D0(s32 objectIndex, s32 arg1) { + switch (gObjectList[objectIndex].state) { + case 0: + break; + case 1: + OHedgehog::func_8008311C(objectIndex, arg1); + break; + case 2: + func_80072D3C(objectIndex, 0, 1, 4, -1); + break; + } + if (gObjectList[objectIndex].textureListIndex == 0) { + Vtx* vtx = (Vtx*) LOAD_ASSET_RAW(common_vtx_hedgehog); + gObjectList[objectIndex].vertex = vtx; + } else { + Vtx* vtx = (Vtx*) LOAD_ASSET_RAW(D_0D006130); + gObjectList[objectIndex].vertex = vtx; + } +} + +void OHedgehog::func_80083474(s32 objectIndex) { + if (gObjectList[objectIndex].state >= 2) { + func_80089F24(objectIndex); + } +} diff --git a/src/engine/objects/Hedgehog.h b/src/engine/objects/Hedgehog.h new file mode 100644 index 000000000..2057d894d --- /dev/null +++ b/src/engine/objects/Hedgehog.h @@ -0,0 +1,53 @@ +#pragma once + +#include <libultraship.h> +#include <vector> +#include "Object.h" + +#include "engine/World.h" + +extern "C" { +#include "macros.h" +#include "main.h" +#include "vehicles.h" +#include "waypoints.h" +#include "common_structs.h" +#include "objects.h" +#include "camera.h" +#include "some_data.h" +} + +/** + * @arg pos FVector xyz spawn position + * @arg patrolPoint FVector2D xz patrol to location. Actor automatically calculates the Y value + * @arg unk unknown. Likely actor type. + */ +class OHedgehog : public OObject { +public: + explicit OHedgehog(const FVector& pos, const FVector2D& patrolPoint, s16 unk); + + ~OHedgehog() { + _count--; + } + + static size_t GetCount() { + return _count; + } + + virtual void Tick() override; + virtual void Draw(s32 cameraId) override; + + void func_800555BC(s32 objectIndex, s32 cameraId); + void func_8004A870(s32 objectIndex, f32 arg1); + + void func_8008311C(s32 objectIndex, s32 arg1); + void func_80083248(s32 objectIndex); + void func_800833D0(s32 objectIndex, s32 arg1); + void func_80083474(s32 objectIndex); + + +private: + FVector _pos; + static size_t _count; + size_t _idx; +}; diff --git a/src/engine/objects/HotAirBalloon.cpp b/src/engine/objects/HotAirBalloon.cpp new file mode 100644 index 000000000..234eef6d9 --- /dev/null +++ b/src/engine/objects/HotAirBalloon.cpp @@ -0,0 +1,174 @@ +#include "HotAirBalloon.h" +#include "World.h" +#include "port/Game.h" + +extern "C" { +#include "render_objects.h" +#include "update_objects.h" +#include "assets/luigi_raceway_data.h" +#include "assets/common_data.h" +#include "math_util.h" +#include "math_util_2.h" +#include "code_80086E70.h" +#include "code_80057C60.h" +#include "actors.h" +} + +OHotAirBalloon::OHotAirBalloon(const FVector& pos) { + _pos = pos; + + D_80165898 = 0; + + // Spawn balloon on second lap. + if (GetCourse() == GetLuigiRaceway()) { + _visible = (bool*)&D_80165898; + } else { // Spawn balloon on race start + bool mod = true; + _visible = &mod; + } + + init_object(indexObjectList1[0], 0); +} + +void OHotAirBalloon::Tick() { + s32 objectIndex = indexObjectList1[0]; + + if (*_visible) { + if (gObjectList[objectIndex].state != 0) { + OHotAirBalloon::func_80085768(objectIndex); + OHotAirBalloon::func_80085534(objectIndex); + object_calculate_new_pos_offset(objectIndex); + if (gObjectList[objectIndex].state >= 2) { + gActorHotAirBalloonItemBox->pos[0] = gObjectList[objectIndex].pos[0]; + gActorHotAirBalloonItemBox->pos[1] = gObjectList[objectIndex].pos[1] - 10.0; + gActorHotAirBalloonItemBox->pos[2] = gObjectList[objectIndex].pos[2]; + } + } + } +} + +void OHotAirBalloon::Draw(s32 cameraId) { + s32 objectIndex; + objectIndex = indexObjectList1[0]; + if (*_visible) { + if (gGamestate != CREDITS_SEQUENCE) { + func_8008A1D0(objectIndex, cameraId, 0x000005DC, 0x00000BB8); + if (is_obj_flag_status_active(objectIndex, VISIBLE) != 0) { + OHotAirBalloon::func_80055CCC(objectIndex, cameraId); + } + } else { + clear_object_flag(objectIndex, 0x00100000); + OHotAirBalloon::func_80055CCC(objectIndex, cameraId); + } + } +} + +void OHotAirBalloon::func_80055CCC(s32 objectIndex, s32 cameraId) { + UNUSED s32 pad; + f32 test; + Camera* camera; + + camera = &camera1[cameraId]; + if (gObjectList[objectIndex].state >= 2) { + func_8008A454(objectIndex, cameraId, 0x0000012C); + test = gObjectList[objectIndex].pos[1] - gObjectList[objectIndex].surfaceHeight; + func_8004A6EC(objectIndex, (20.0 / test) + 0.5); + if (is_obj_index_flag_status_inactive(objectIndex, 0x00100000) != 0) { + func_80043328(gObjectList[objectIndex].pos, (u16*) gObjectList[objectIndex].direction_angle, + gObjectList[objectIndex].sizeScaling, (Gfx*)d_course_luigi_raceway_dl_F960); + gSPDisplayList(gDisplayListHead++, (Gfx*)d_course_luigi_raceway_dl_F650); + } else { + D_80183E80[0] = (s16) gObjectList[objectIndex].direction_angle[0]; + D_80183E80[1] = + (s16) (func_800418AC(gObjectList[objectIndex].pos[0], gObjectList[objectIndex].pos[2], camera->pos) + + 0x8000); + D_80183E80[2] = (u16) gObjectList[objectIndex].direction_angle[2]; + func_80043328(gObjectList[objectIndex].pos, D_80183E80, gObjectList[objectIndex].sizeScaling, + (Gfx*)d_course_luigi_raceway_dl_FBE0); + gSPDisplayList(gDisplayListHead++, (Gfx*)d_course_luigi_raceway_dl_FA20); + if (gPlayerCountSelection1 == 1) { + gObjectList[objectIndex].direction_angle[1] = 0; + } + } + } +} + +void OHotAirBalloon::init_hot_air_balloon(s32 objectIndex) { + gObjectList[objectIndex].sizeScaling = 1.0f; + gObjectList[objectIndex].model = (Gfx*)d_course_luigi_raceway_dl_F960; + if (gGamestate != CREDITS_SEQUENCE) { + set_obj_origin_pos(objectIndex, xOrientation * _pos.x, _pos.y, _pos.z); + set_obj_origin_offset(objectIndex, 0.0f, 300.0f, 0.0f); + } else { + set_obj_origin_pos(objectIndex, xOrientation * _pos.x, _pos.y, _pos.z); + set_obj_origin_offset(objectIndex, 0.0f, 300.0f, 0.0f); + } + func_8008B844(objectIndex); + func_800886F4(objectIndex); + func_80086EF0(objectIndex); + gObjectList[objectIndex].velocity[1] = -2.0f; + init_actor_hot_air_balloon_item_box(0.0f, 0.0f, 0.0f); + object_next_state(objectIndex); +} + +void OHotAirBalloon::func_80085534(s32 objectIndex) { + switch (gObjectList[objectIndex].unk_0AE) { + case 1: + if (gObjectList[objectIndex].offset[1] <= 18.0) { + func_80086FD4(objectIndex); + } + break; + case 2: + f32_step_towards(&gObjectList[objectIndex].velocity[1], 0.0f, 0.05f); + if (gObjectList[objectIndex].velocity[1] == 0.0) { + func_80086FD4(objectIndex); + } + break; + case 3: + func_800871AC(objectIndex, 1); + break; + case 4: + f32_step_towards(&gObjectList[objectIndex].velocity[1], 1.0f, 0.05f); + if (gObjectList[objectIndex].velocity[1] == 1.0) { + func_80086FD4(objectIndex); + } + break; + case 5: + func_800871AC(objectIndex, 90); + break; + case 6: + f32_step_towards(&gObjectList[objectIndex].velocity[1], 0.0f, 0.05f); + if (gObjectList[objectIndex].velocity[1] == 0.0) { + func_80086FD4(objectIndex); + } + break; + case 7: + f32_step_towards(&gObjectList[objectIndex].velocity[1], -1.0f, 0.05f); + if (gObjectList[objectIndex].velocity[1] == -1.0) { + func_80086FD4(objectIndex); + } + break; + case 8: + func_800871AC(objectIndex, 90); + break; + case 9: + f32_step_towards(&gObjectList[objectIndex].velocity[1], 0.0f, 0.05f); + if (func_80087060(objectIndex, 90) != 0) { + func_8008701C(objectIndex, 3); + } + break; + } + object_add_velocity_offset_y(objectIndex); + gObjectList[objectIndex].direction_angle[1] += 0x100; +} + +void OHotAirBalloon::func_80085768(s32 objectIndex) { + switch (gObjectList[objectIndex].state) { /* irregular */ + case 1: + OHotAirBalloon::init_hot_air_balloon(objectIndex); + break; + case 0: + case 2: + break; + } +} diff --git a/src/engine/objects/HotAirBalloon.h b/src/engine/objects/HotAirBalloon.h new file mode 100644 index 000000000..301e111c4 --- /dev/null +++ b/src/engine/objects/HotAirBalloon.h @@ -0,0 +1,34 @@ +#pragma once + +#include <libultraship.h> +#include <vector> +#include "Object.h" + +#include "World.h" + +extern "C" { +#include "macros.h" +#include "main.h" +#include "vehicles.h" +#include "waypoints.h" +#include "common_structs.h" +#include "objects.h" +#include "camera.h" +#include "some_data.h" +} + +class OHotAirBalloon : public OObject { +public: + explicit OHotAirBalloon(const FVector& pos); + + virtual void Tick() override; + virtual void Draw(s32 cameraId) override; + void func_80055CCC(s32 objectIndex, s32 cameraId); + void init_hot_air_balloon(s32 objectIndex); + void func_80085534(s32 objectIndex); + void func_80085768(s32 objectIndex); + +private: + FVector _pos; + bool *_visible; +}; diff --git a/src/engine/objects/Lakitu.cpp b/src/engine/objects/Lakitu.cpp new file mode 100644 index 000000000..1c8547bc8 --- /dev/null +++ b/src/engine/objects/Lakitu.cpp @@ -0,0 +1,856 @@ +#include <libultraship.h> +#include <libultra/gbi.h> +#include "Lakitu.h" +#include <vector> + +#include "port/Game.h" + +extern "C" { +#include "macros.h" +#include "main.h" +#include "actors.h" +#include "math_util.h" +#include "sounds.h" +#include "update_objects.h" +#include "render_player.h" +#include "external.h" +#include "bomb_kart.h" +#include "collision.h" +#include "code_80086E70.h" +#include "render_objects.h" +#include "code_80057C60.h" +#include "defines.h" +#include "code_80005FD0.h" +#include "math_util_2.h" +#include "collision.h" +#include "assets/bowsers_castle_data.h" +#include "ceremony_and_credits.h" +#include "objects.h" +#include "update_objects.h" +#include "render_objects.h" +#include "course_offsets.h" +#include "data/some_data.h" +#include "race_logic.h" +#include "effects.h" +#include "memory.h" +extern s8 gPlayerCount; +} + +OLakitu::OLakitu(s32 playerId, LakituType type) { + _playerId = playerId; + + init_object(gIndexLakituList[playerId], (s32)type); +} + +void OLakitu::Activate(LakituType type) { + init_object(gIndexLakituList[_playerId], (s32)type); +} + +void OLakitu::Tick() { + OLakitu::func_8007AA44(_playerId); +} + +void OLakitu::Tick60fps() { // update_object_lakitu + s32 playerId = _playerId; + s32 objectIndex = gIndexLakituList[playerId]; + + switch (gObjectList[objectIndex].unk_0D8) { + case 0: + break; + case 1: + OLakitu::update_object_lakitu_starter(objectIndex, playerId); + func_8008BFFC(objectIndex); + break; + case 2: + OLakitu::update_object_lakitu_checkered_flag(objectIndex, playerId); + func_8008BFFC(objectIndex); + break; + case 3: + OLakitu::update_object_lakitu_fishing(objectIndex, playerId); + break; + case 4: + OLakitu::update_object_lakitu_second_lap(objectIndex, playerId); + func_8008BFFC(objectIndex); + break; + case 5: + OLakitu::update_object_lakitu_final_lap(objectIndex, playerId); + func_8008BFFC(objectIndex); + break; + case 6: + OLakitu::update_object_lakitu_reverse(objectIndex, playerId); + func_8008BFFC(objectIndex); + break; + case 7: + OLakitu::update_object_lakitu_fishing2(objectIndex, playerId); + break; + } +} + +void OLakitu::Draw(s32 cameraId) { + UNUSED s32 stackPadding; + Camera* camera; + f32 var_f0; + f32 var_f2; + s32 objectIndex; + Object* object; + + objectIndex = gIndexLakituList[cameraId]; + camera = &camera1[cameraId]; + if (is_obj_flag_status_active(objectIndex, 0x00000010) != 0) { + object = &gObjectList[objectIndex]; + object->orientation[0] = 0; + object->orientation[1] = func_800418AC(object->pos[0], object->pos[2], camera->pos); + object->orientation[2] = 0x8000; + if (func_80072354(objectIndex, 2) != 0) { + draw_2d_texture_at(object->pos, object->orientation, object->sizeScaling, (u8*) object->activeTLUT, + (u8*)object->activeTexture, object->vertex, (s32) object->textureWidth, + (s32) object->textureHeight, (s32) object->textureWidth, + (s32) object->textureHeight / 2); + } else { + func_800485C4(object->pos, object->orientation, object->sizeScaling, (s32) object->primAlpha, + (u8*) object->activeTLUT, (u8*)object->activeTexture, object->vertex, (s32) object->textureWidth, + (s32) object->textureHeight, (s32) object->textureWidth, (s32) object->textureHeight / 2); + } + if (gScreenModeSelection == SCREEN_MODE_1P) { + var_f0 = object->pos[0] - camera->pos[0]; + var_f2 = object->pos[2] - camera->pos[2]; + if (var_f0 < 0.0f) { + var_f0 = -var_f0; + } + if (var_f2 < 0.0f) { + var_f2 = -var_f2; + } + if ((var_f0 + var_f2) <= 200.0) { + func_8004A630(&D_8018C0B0[cameraId], object->pos, 0.35f); + } + } + } +} + +void OLakitu::func_80079114(s32 objectIndex, s32 playerId, s32 arg2) { + s32 a; + if (gObjectList[objectIndex].state >= 2) { + if ((u8) gObjectList[objectIndex].unk_0D8 == 1) { + if (playerId == 0) { + func_80074894(objectIndex, gLakituTexturePtr); + return; + } + a = gIndexLakituList[0]; + gObjectList[objectIndex].activeTLUT = gObjectList[a].activeTLUT; + gObjectList[objectIndex].activeTexture = gObjectList[a].activeTexture; + if (0) {} + return; + } + switch (arg2) { + case 0: + func_800748F4(objectIndex, gLakituTexturePtr); + break; + case 1: + func_800748C4(objectIndex, gLakituTexturePtr); + break; + case 2: + func_80074894(objectIndex, gLakituTexturePtr); + break; + } + } +} + +void OLakitu::func_800791F0(s32 objectIndex, s32 playerId) { + Player* player = &gPlayerOne[playerId]; + + if ((gObjectList[objectIndex].unk_0D8 != 3) && (gObjectList[objectIndex].unk_0D8 != 7)) { + func_800722CC(objectIndex, 1); + if (CourseManager_GetProps()->LakituTowType == LakituTowType::ICE) { + player->unk_0CA &= 0xFFEF; + } + } + + if (CourseManager_GetProps()->LakituTowType == LakituTowType::ICE) { + func_800722CC(objectIndex, 0x00000010); + player->unk_0CA &= 0xFFDF; + } + func_800C9018(playerId, SOUND_ARG_LOAD(0x01, 0x00, 0xFA, 0x28)); +} + +static const char* sLakituTextures[] = { + gTextureLakituNoLights1, gTextureLakituNoLights2, gTextureLakituNoLights3, gTextureLakituNoLights4, + gTextureLakituNoLights5, gTextureLakituNoLights6, gTextureLakituNoLights7, gTextureLakituNoLights8, + gTextureLakituRedLights01, gTextureLakituRedLights02, gTextureLakituRedLights03, gTextureLakituRedLights04, + gTextureLakituRedLights05, gTextureLakituRedLights06, gTextureLakituRedLights07, gTextureLakituRedLights08, + gTextureLakituRedLights09, gTextureLakituRedLights10, gTextureLakituRedLights11, gTextureLakituRedLights12, + gTextureLakituRedLights13, gTextureLakituRedLights14, gTextureLakituRedLights15, gTextureLakituRedLights16, + gTextureLakituBlueLight1, gTextureLakituBlueLight2, gTextureLakituBlueLight3, gTextureLakituBlueLight4, + gTextureLakituBlueLight5, gTextureLakituBlueLight6, gTextureLakituBlueLight7, gTextureLakituBlueLight8, +}; + +void OLakitu::init_obj_lakitu_starter_and_checkered_flag(s32 objectIndex, s32 playerId) { + if (playerId == 0) { + D_801656F0 = 0; + D_8018D168 = 0; + } + + // u8 *tlut = (u8 *) LOAD_ASSET_RAW(common_tlut_lakitu_countdown); + // u8 *lights = (u8 *) LOAD_ASSET_RAW(gTextureLakituNoLights1); + + init_texture_object( + objectIndex, + (u8*) load_lakitu_tlut_x64(common_tlut_lakitu_countdown, ARRAY_COUNT(common_tlut_lakitu_countdown)), + sLakituTextures, 56, (u16) 72); + Vtx* vtx = (Vtx*) LOAD_ASSET_RAW(common_vtx_lakitu); + gObjectList[objectIndex].vertex = vtx; + gObjectList[objectIndex].sizeScaling = 0.15f; + clear_object_flag(objectIndex, 0x00000010); + object_next_state(objectIndex); + gObjectList[objectIndex].unk_048 = D_8018D180; +} + +void OLakitu::update_object_lakitu_starter(s32 objectIndex, s32 arg1) { + UNUSED s32 pad; + switch (gObjectList[objectIndex].state) { + case 0: + break; + case 1: + OLakitu::init_obj_lakitu_starter_and_checkered_flag(objectIndex, arg1); + break; + case 2: + set_and_run_timer_object(objectIndex, gObjectList[objectIndex].unk_048); + if ((gObjectList[objectIndex].timer == 0x00000055) && (gPlayerCount == 3) && (arg1 == 0)) { + D_8018D168 = 1; + } + break; + case 3: + set_object_flag(objectIndex, 0x00000010); + func_80086F10(objectIndex, 1, &D_800E67B8); // set a spline + object_next_state(objectIndex); + break; + case 4: + if ((set_and_run_timer_object(objectIndex, 0x0000001E) != false) && (gPlayerCount != 3) && (arg1 == 0)) { + D_8018D168 = 1; + } + break; + case 5: + set_and_run_timer_object(objectIndex, 0x0000001E); + break; + case 6: + func_80072E54(objectIndex, 1, 7, 1, 2, 0); + break; + case 7: + if (set_and_run_timer_object(objectIndex, 0x00000014) != 0) { + gObjectList[objectIndex].tlutList += 0x200; + if (arg1 == 0) { + play_sound2(SOUND_ACTION_COUNTDOWN_LIGHT); + } + } + break; + case 8: + func_80072E54(objectIndex, 8, 0x0000000F, 1, 6, 0); + break; + case 9: + if ((set_and_run_timer_object(objectIndex, 8) != 0) && (arg1 == 0)) { + play_sound2(SOUND_ACTION_COUNTDOWN_LIGHT); + } + break; + case 10: + if ((func_80072E54(objectIndex, 0x00000010, 0x00000017, 1, 6, 0) != 0) && (arg1 == 0)) { + D_801656F0 = 1; + } + break; + case 11: + if (set_and_run_timer_object(objectIndex, 8) != 0) { + gObjectList[objectIndex].tlutList += 0x200; + if (arg1 == 0) { + play_sound2(SOUND_ACTION_GREEN_LIGHT); + } + } + break; + case 12: + func_80072E54(objectIndex, 0x00000018, 0x0000001B, 1, 6, 0); + break; + case 13: + if (arg1 == 0) { + OLakitu::func_800729EC(objectIndex); + D_8018D160 = 1; + break; + } + object_next_state(objectIndex); + break; + case 14: + set_and_run_timer_object(objectIndex, 0x00000078); + break; + case 15: + func_80072428(objectIndex); + break; + } +} + +void OLakitu::func_800729EC(s32 objectIndex) { + u32 temp_v1 = 1; + s32 i; + + start_race(); + object_next_state(objectIndex); + D_8018D2BC = 1; + D_8018D2A4 = 1; + + if (GetCourse() != GetYoshiValley()) { + for (i = 0; i < gPlayerCount; i++) { + playerHUD[i].unk_81 = temp_v1; + } + } + func_8005AB20(); +} + +static const char* sLakituCheckeredList[] = { + gTextureLakituCheckeredFlag01, gTextureLakituCheckeredFlag02, gTextureLakituCheckeredFlag03, + gTextureLakituCheckeredFlag04, gTextureLakituCheckeredFlag05, gTextureLakituCheckeredFlag06, + gTextureLakituCheckeredFlag07, gTextureLakituCheckeredFlag08, gTextureLakituCheckeredFlag09, + gTextureLakituCheckeredFlag10, gTextureLakituCheckeredFlag11, gTextureLakituCheckeredFlag12, + gTextureLakituCheckeredFlag13, gTextureLakituCheckeredFlag14, gTextureLakituCheckeredFlag15, + gTextureLakituCheckeredFlag16, gTextureLakituCheckeredFlag17, gTextureLakituCheckeredFlag18, + gTextureLakituCheckeredFlag19, gTextureLakituCheckeredFlag20, gTextureLakituCheckeredFlag21, + gTextureLakituCheckeredFlag22, gTextureLakituCheckeredFlag23, gTextureLakituCheckeredFlag24, + gTextureLakituCheckeredFlag25, gTextureLakituCheckeredFlag26, gTextureLakituCheckeredFlag27, + gTextureLakituCheckeredFlag28, gTextureLakituCheckeredFlag29, gTextureLakituCheckeredFlag30, + gTextureLakituCheckeredFlag31, gTextureLakituCheckeredFlag32 +}; + +void OLakitu::init_obj_lakitu_checkered_flag(s32 objectIndex, s32 playerIndex) { + Object* object; + + OLakitu::func_800791F0(objectIndex, playerIndex); + + u8* tex = (u8*) LOAD_ASSET_RAW(common_tlut_lakitu_checkered_flag); + Vtx* vtx = (Vtx*) LOAD_ASSET_RAW(common_vtx_also_lakitu); + + init_texture_object(objectIndex, (u8*) tex, sLakituCheckeredList, 0x48U, (u16) 0x00000038); + object = &gObjectList[objectIndex]; + object->activeTexture = *gObjectList[objectIndex].textureList; + object->vertex = vtx; + object->pos[2] = 5000.0f; + object->pos[1] = 5000.0f; + object->pos[0] = 5000.0f; + object->sizeScaling = 0.15f; + func_80086F10(objectIndex, 2, &D_800E6834); + clear_object_flag(objectIndex, 0x00000010); + object_next_state(objectIndex); +} + +void OLakitu::update_object_lakitu_checkered_flag(s32 objectIndex, s32 playerIndex) { + switch (gObjectList[objectIndex].state) { + case 0: + break; + case 1: + OLakitu::init_obj_lakitu_checkered_flag(objectIndex, playerIndex); + break; + case 2: + set_object_flag(objectIndex, 0x00000010); + object_next_state(objectIndex); + break; + case 3: + func_80072E54(objectIndex, 0, 0x0000001F, 1, 2, -1); + break; + case 4: + func_80072428(objectIndex); + break; + } +} + +void OLakitu::func_800797AC(s32 playerId) { + s32 objectIndex; + Player* player; + + objectIndex = gIndexLakituList[playerId]; + player = &gPlayerOne[playerId]; + //if ((GetCourse() == GetSherbetLand()) && (player->unk_0CA & 1)) { + if ((CourseManager_GetProps()->LakituTowType == LakituTowType::ICE) && (player->unk_0CA & 1)) { + init_object(objectIndex, 7); + player->unk_0CA |= 0x10; + } else { + init_object(objectIndex, 3); + } + func_800722A4(objectIndex, 1); +} + +void OLakitu::func_80079860(s32 playerId) { + s32 objectIndex; + Player* player; + + objectIndex = gIndexLakituList[playerId]; + player = &gPlayerOne[playerId]; + if ((func_80072354(objectIndex, 1) != 0) && + (((func_802ABDF4(player->collision.meshIndexZX) != 0) && (player->collision.surfaceDistance[2] <= 3.0f)) || + (player->unk_0CA & 1) || ((player->surfaceType == OUT_OF_BOUNDS) && !(player->effects & 8)))) { + func_80090778(player); + OLakitu::func_800797AC(playerId); + } +} + +void OLakitu::func_8007993C(s32 objectIndex, Player* player) { + if (player->unk_0CA & 4) { + func_800722A4(objectIndex, 2); + gObjectList[objectIndex].primAlpha = player->unk_0C6; + return; + } + func_800722CC(objectIndex, 2); +} + +static const char* sLakituFishingTextures[] = { gTextureLakituFishing1, gTextureLakituFishing2, gTextureLakituFishing3, + gTextureLakituFishing4 }; + +void OLakitu::init_obj_lakitu_red_flag_fishing(s32 objectIndex, s32 arg1) { + + u8* tlut = (u8*) LOAD_ASSET_RAW(common_tlut_lakitu_fishing); + Vtx* vtx = (Vtx*) LOAD_ASSET_RAW(D_0D005F30); + + OLakitu::func_800791F0(objectIndex, arg1); + init_texture_object(objectIndex, tlut, sLakituFishingTextures, 0x38U, (u16) 0x00000048); + gObjectList[objectIndex].vertex = vtx; + gObjectList[objectIndex].sizeScaling = 0.15f; + func_80086E70(objectIndex); + clear_object_flag(objectIndex, 0x00000010); + func_80073720(objectIndex); + object_next_state(objectIndex); + func_800C8F80((u8) arg1, 0x0100FA28); +} + +void OLakitu::func_80079A5C(s32 objectIndex, UNUSED Player* player) { + switch (gObjectList[objectIndex].unk_0AE) { + case 0: + break; + case 1: + gObjectList[objectIndex].origin_pos[2] = 0.0f; + gObjectList[objectIndex].origin_pos[1] = 0.0f; + gObjectList[objectIndex].origin_pos[0] = 0.0f; + gObjectList[objectIndex].offset[2] = 0.0f; + gObjectList[objectIndex].offset[0] = 0.0f; + gObjectList[objectIndex].offset[1] = 80.0f; + func_80086FD4(objectIndex); + break; + case 2: + if (f32_step_down_towards(&gObjectList[objectIndex].offset[1], 5.0f, 1.0f) != 0) { + func_80086F60(objectIndex); + } + break; + case 3: + if (f32_step_up_towards(&gObjectList[objectIndex].offset[1], 100.0f, 1.0f) != 0) { + func_80086F60(objectIndex); + } + break; + } +} + +void OLakitu::update_object_lakitu_fishing(s32 objectIndex, s32 playerId) { + Player* player = &gPlayerOne[playerId]; + + switch (gObjectList[objectIndex].state) { /* switch 1; irregular */ + case 0: /* switch 1 */ + break; + case 1: /* switch 1 */ + OLakitu::init_obj_lakitu_red_flag_fishing(objectIndex, playerId); + break; + case 2: /* switch 1 */ + set_object_flag(objectIndex, 0x00000010); + func_800736E0(objectIndex); + object_next_state(objectIndex); + break; + case 3: /* switch 1 */ + func_800730BC(objectIndex, 0, 3, 1, 2, -1); + break; + } + switch (gObjectList[objectIndex].unk_0D6) { + case 0: + break; + case 1: + if (func_80086FA4(objectIndex) != 0) { + func_80073654(objectIndex); + } + break; + case 2: + func_80090868(player); + func_80073654(objectIndex); + break; + case 3: + if (!(player->unk_0CA & 2)) { + func_80086EAC(objectIndex, 0, 3); + func_80073654(objectIndex); + } + break; + case 4: + if (func_80086FA4(objectIndex) != 0) { + func_80073654(objectIndex); + } + break; + case 5: + func_800722CC(objectIndex, 1); + func_800C9018((u8) playerId, SOUND_ARG_LOAD(0x01, 0x00, 0xFA, 0x28)); + func_80072428(objectIndex); + func_80073720(objectIndex); + break; + } + if (gObjectList[objectIndex].state >= 2) { + OLakitu::func_8007993C(objectIndex, player); + } + OLakitu::func_80079A5C(objectIndex, player); +} + +void OLakitu::update_object_lakitu_fishing2(s32 objectIndex, s32 playerId) { + Player* player = &gPlayerOne[playerId]; + + switch (gObjectList[objectIndex].state) { /* switch 1; irregular */ + case 0: /* switch 1 */ + break; + case 1: /* switch 1 */ + OLakitu::init_obj_lakitu_red_flag_fishing(objectIndex, playerId); + break; + case 2: /* switch 1 */ + set_object_flag(objectIndex, 0x00000010); + func_800736E0(objectIndex); + player->unk_0CA |= 0x80; + object_next_state(objectIndex); + break; + case 3: /* switch 1 */ + func_800730BC(objectIndex, 0, 3, 1, 2, -1); + break; + } + switch (gObjectList[objectIndex].unk_0D6) { + case 1: + if (func_80086FA4(objectIndex) != 0) { + func_800C9060((u8) playerId, 0x1900A055U); + func_80073654(objectIndex); + } + break; + case 2: + func_80090868(player); + func_800722A4(objectIndex, 4); + func_80073654(objectIndex); + break; + case 3: + if ((player->surfaceType == ICE) && !(player->unk_0CA & 1) && + ((f64) player->collision.surfaceDistance[2] <= 30.0)) { + func_800722A4(objectIndex, 8); + } + if (!(player->unk_0CA & 2)) { + func_80086EAC(objectIndex, 0, 3); + func_80073654(objectIndex); + } + break; + case 4: + func_8007375C(objectIndex, 0x0000001E); + break; + case 5: + player->unk_0CA &= 0xFF7F; + func_800722A4(objectIndex, 0x00000010); + func_800722A4(objectIndex, 0x00000020); + func_800722CC(objectIndex, 4); + func_800722CC(objectIndex, 8); + func_80073654(objectIndex); + func_800C9060((u8) playerId, 0x1900A056U); + break; + case 6: + if (func_8007375C(objectIndex, 0x000000A0) != 0) { + func_800722CC(objectIndex, 0x00000010); + player->unk_0CA &= 0xFFEF; + player->unk_0CA |= 0x20; + } + break; + case 7: + func_8007375C(objectIndex, 0x0000003C); + break; + case 8: + func_80073720(objectIndex); + func_80072428(objectIndex); + player->unk_0CA &= 0xFFDF; + func_800722CC(objectIndex, 1); + func_800C9018((u8) playerId, SOUND_ARG_LOAD(0x01, 0x00, 0xFA, 0x28)); + break; + } + + if (gObjectList[objectIndex].state >= 2) { + OLakitu::func_8007993C(objectIndex, player); + } + OLakitu::func_80079A5C(objectIndex, player); +} + +static const char* sLakituSecondLapTextures[] = { + gTextureLakituSecondLap01, gTextureLakituSecondLap02, gTextureLakituSecondLap03, gTextureLakituSecondLap04, + gTextureLakituSecondLap05, gTextureLakituSecondLap06, gTextureLakituSecondLap07, gTextureLakituSecondLap08, + gTextureLakituSecondLap09, gTextureLakituSecondLap10, gTextureLakituSecondLap11, gTextureLakituSecondLap12, + gTextureLakituSecondLap13, gTextureLakituSecondLap14, gTextureLakituSecondLap15, gTextureLakituSecondLap16 +}; + +void OLakitu::func_8007A060(s32 objectIndex, s32 playerIndex) { + Object* object; + + OLakitu::func_800791F0(objectIndex, playerIndex); + + u8* tlut = (u8*) LOAD_ASSET_RAW(common_tlut_lakitu_second_lap); + Vtx* vtx = (Vtx*) LOAD_ASSET_RAW(common_vtx_also_lakitu); + + init_texture_object(objectIndex, tlut, sLakituSecondLapTextures, 0x48U, (u16) 0x00000038); + object = &gObjectList[objectIndex]; + object->activeTexture = *gObjectList[objectIndex].textureList; + object->vertex = vtx; + object->pos[2] = 5000.0f; + object->pos[1] = 5000.0f; + object->pos[0] = 5000.0f; + object->sizeScaling = 0.15f; + clear_object_flag(objectIndex, 0x00000010); + func_80086F10(objectIndex, 5, &D_800E694C); + object_next_state(objectIndex); +} + +void OLakitu::update_object_lakitu_second_lap(s32 objectIndex, s32 playerIndex) { + switch (gObjectList[objectIndex].state) { + case 0: + break; + case 1: + OLakitu::func_8007A060(objectIndex, playerIndex); + break; + case 2: + set_object_flag(objectIndex, 0x00000010); + object_next_state(objectIndex); + break; + case 3: + set_and_run_timer_object(objectIndex, 0x00000014); + break; + case 4: + func_80072E54(objectIndex, 0, 0x0000000F, 1, 2, 1); + break; + case 5: + set_and_run_timer_object(objectIndex, 0x0000003C); + break; + case 6: + func_80072F88(objectIndex, 0x0000000F, 0, 1, 2, 1); + break; + case 7: + if (gObjectList[objectIndex].unk_0AE == 0) { + func_80072428(objectIndex); + } + break; + } +} + +static const char* sLakituFinalLapTextures[] = { + gTextureLakituFinalLap01, gTextureLakituFinalLap02, gTextureLakituFinalLap03, gTextureLakituFinalLap04, + gTextureLakituFinalLap05, gTextureLakituFinalLap06, gTextureLakituFinalLap07, gTextureLakituFinalLap08, + gTextureLakituFinalLap09, gTextureLakituFinalLap10, gTextureLakituFinalLap11, gTextureLakituFinalLap12, + gTextureLakituFinalLap13, gTextureLakituFinalLap14, gTextureLakituFinalLap15, gTextureLakituFinalLap16, +}; + +void OLakitu::func_8007A228(s32 objectIndex, s32 playerIndex) { + Object* object; + + OLakitu::func_800791F0(objectIndex, playerIndex); + + u8* tlut = (u8*) LOAD_ASSET_RAW(common_tlut_lakitu_final_lap); + Vtx* vtx = (Vtx*) LOAD_ASSET_RAW(common_vtx_also_lakitu); + + init_texture_object(objectIndex, tlut, sLakituFinalLapTextures, 0x48U, (u16) 0x00000038); + object = &gObjectList[objectIndex]; + object->activeTexture = *gObjectList[objectIndex].textureList; + object->vertex = vtx; + object->pos[2] = 5000.0f; + object->pos[1] = 5000.0f; + object->pos[0] = 5000.0f; + object->sizeScaling = 0.15f; + clear_object_flag(objectIndex, 0x00000010); + func_80086F10(objectIndex, 5, &D_800E694C); + object_next_state(objectIndex); +} + +void OLakitu::update_object_lakitu_final_lap(s32 objectIndex, s32 playerIndex) { + switch (gObjectList[objectIndex].state) { + case 0: + break; + case 1: + OLakitu::func_8007A228(objectIndex, playerIndex); + break; + case 2: + set_object_flag(objectIndex, 0x00000010); + object_next_state(objectIndex); + break; + case 3: + set_and_run_timer_object(objectIndex, 0x00000014); + break; + case 4: + func_80072E54(objectIndex, 0, 0x0000000F, 1, 2, 1); + break; + case 5: + set_and_run_timer_object(objectIndex, 0x0000003C); + break; + case 6: + func_80072F88(objectIndex, 0x0000000F, 0, 1, 2, 1); + break; + case 7: + if (gObjectList[objectIndex].unk_0AE == 0) { + func_80072428(objectIndex); + } + break; + } +} + +static const char* sLakituReverseTextures[] = { + gTextureLakituReverse01, gTextureLakituReverse02, gTextureLakituReverse03, gTextureLakituReverse04, + gTextureLakituReverse05, gTextureLakituReverse06, gTextureLakituReverse07, gTextureLakituReverse08, + gTextureLakituReverse09, gTextureLakituReverse10, gTextureLakituReverse11, gTextureLakituReverse12, + gTextureLakituReverse13, gTextureLakituReverse14, gTextureLakituReverse15, gTextureLakituReverse16 +}; + +void OLakitu::func_8007A3F0(s32 objectIndex, s32 arg1) { + f32 var = 5000.0f; + OLakitu::func_800791F0(objectIndex, arg1); + + u8* tlut = (u8*) LOAD_ASSET_RAW(common_tlut_lakitu_reverse); + Vtx* vtx = (Vtx*) LOAD_ASSET_RAW(common_vtx_also_lakitu); + + init_texture_object(objectIndex, tlut, sLakituReverseTextures, 72, (u16) 56); + gObjectList[objectIndex].activeTexture = *gObjectList[objectIndex].textureList; + gObjectList[objectIndex].vertex = vtx; + gObjectList[objectIndex].pos[2] = var; + gObjectList[objectIndex].pos[1] = var; + gObjectList[objectIndex].pos[0] = var; + gObjectList[objectIndex].sizeScaling = 0.15f; + clear_object_flag(objectIndex, 0x00000010); + func_80086F10(objectIndex, 6, &D_800E69B0); + gObjectList[objectIndex].unk_0D6 = 0; + object_next_state(objectIndex); + func_800C8F80((u8) arg1, 0x0100FA28); +} + +void OLakitu::update_object_lakitu_reverse(s32 objectIndex, s32 playerId) { + Player* sp2C = &gPlayerOne[playerId]; + + switch (gObjectList[objectIndex].state) { + case 0: + break; + case 1: + OLakitu::func_8007A3F0(objectIndex, playerId); + break; + case 2: + set_object_flag(objectIndex, 0x00000010); + gObjectList[objectIndex].unk_0D6 = 1; + object_next_state(objectIndex); + break; + case 3: + func_800730BC(objectIndex, 0, 0x0000000F, 1, 2, -1); + break; + case 4: + func_80072428(objectIndex); + break; + } + switch (gObjectList[objectIndex].unk_0D6) { + case 1: + if ((gObjectList[objectIndex].state >= 3) && (!(sp2C->effects & 0x400000))) { + func_80086F10(objectIndex, 6, &D_800E69F4); + gObjectList[objectIndex].unk_0D6 = 2; + gObjectList[objectIndex].unk_04C = 0x00000050; + func_800C9018((u8) playerId, SOUND_ARG_LOAD(0x01, 0x00, 0xFA, 0x28)); + return; + } + return; + case 2: + gObjectList[objectIndex].unk_04C--; + if (gObjectList[objectIndex].unk_04C == 0) { + object_next_state(objectIndex); + gObjectList[objectIndex].unk_0D6 = 0; + } + break; + } +} + +void OLakitu::func_8007A66C(s32 objectIndex) { + Player* player = &gPlayers[_playerId]; + Camera* camera = &cameras[_playerId]; + u16 rot = 0x8000 - camera->rot[1]; + + gObjectList[objectIndex].pos[0] = + (player->pos[0] + + (coss(rot) * (gObjectList[objectIndex].origin_pos[0] + gObjectList[objectIndex].offset[0]))) - + (sins(rot) * (gObjectList[objectIndex].origin_pos[2] + gObjectList[objectIndex].offset[2])); + gObjectList[objectIndex].pos[1] = + player->unk_074 + gObjectList[objectIndex].origin_pos[1] + gObjectList[objectIndex].offset[1]; + gObjectList[objectIndex].pos[2] = + (player->pos[2] + + (sins(rot) * (gObjectList[objectIndex].origin_pos[0] + gObjectList[objectIndex].offset[0]))) + + (coss(rot) * (gObjectList[objectIndex].origin_pos[2] + gObjectList[objectIndex].offset[2])); +} + +void OLakitu::func_8007A778(s32 objectIndex) { + Player* player = &gPlayers[_playerId]; + Camera* camera = &cameras[_playerId]; + u16 rot = 0x8000 - camera->rot[1]; + + gObjectList[objectIndex].pos[0] = + (player->pos[0] + + (coss(rot) * (gObjectList[objectIndex].origin_pos[0] + gObjectList[objectIndex].offset[0]))) - + (sins(rot) * (gObjectList[objectIndex].origin_pos[2] + gObjectList[objectIndex].offset[2])); + gObjectList[objectIndex].pos[1] = + player->pos[1] + gObjectList[objectIndex].origin_pos[1] + gObjectList[objectIndex].offset[1]; + gObjectList[objectIndex].pos[2] = + (player->pos[2] + + (sins(rot) * (gObjectList[objectIndex].origin_pos[0] + gObjectList[objectIndex].offset[0]))) + + (coss(rot) * (gObjectList[objectIndex].origin_pos[2] + gObjectList[objectIndex].offset[2])); +} + +void OLakitu::func_8007A88C(s32 playerId) { + s32 objectIndex; + Player* player; + + objectIndex = gIndexLakituList[playerId]; + player = &gPlayerOne[playerId]; + + if ((gObjectList[objectIndex].state == 0) && (player->effects & 0x400000)) { + //func_800790E4(playerId); + init_object(gIndexLakituList[playerId], 6); + } +} + +void OLakitu::func_8007A910(s32 arg0) { + if (D_801657B4 == 0) { + OLakitu::func_8007A88C(arg0); + } + func_80079860(arg0); +} + +// animate lakitu? +void OLakitu::func_8007AA44(s32 playerId) { + s32 objectIndex; + + OLakitu::func_8007A910(playerId); + objectIndex = gIndexLakituList[playerId]; + gLakituTexturePtr = (const char**)&gLakituTextureBuffer[playerId]; + switch (gObjectList[objectIndex].unk_0D8) { + case 1: + OLakitu::func_80079114(objectIndex, playerId, 2); + OLakitu::func_8007A66C(objectIndex); + break; + case 2: + OLakitu::func_80079114(objectIndex, playerId, 0); + OLakitu::func_8007A66C(objectIndex); + break; + case 3: + OLakitu::func_80079114(objectIndex, playerId, 0); + OLakitu::func_8007A778(objectIndex); + break; + case 4: + OLakitu::func_80079114(objectIndex, playerId, 0); + OLakitu::func_8007A66C(objectIndex); + break; + case 5: + OLakitu::func_80079114(objectIndex, playerId, 0); + OLakitu::func_8007A66C(objectIndex); + break; + case 6: + OLakitu::func_80079114(objectIndex, playerId, 0); + OLakitu::func_8007A66C(objectIndex); + break; + case 7: + OLakitu::func_80079114(objectIndex, playerId, 0); + OLakitu::func_8007A778(objectIndex); + break; + case 0: + default: + break; + } +} + diff --git a/src/engine/objects/Lakitu.h b/src/engine/objects/Lakitu.h new file mode 100644 index 000000000..1944733ee --- /dev/null +++ b/src/engine/objects/Lakitu.h @@ -0,0 +1,82 @@ +#pragma once + +#include <libultraship.h> +#include <vector> +#include "Object.h" +#include "World.h" + +extern "C" { +#include "macros.h" +#include "main.h" +#include "vehicles.h" +#include "waypoints.h" +#include "common_structs.h" +#include "objects.h" +#include "course_offsets.h" +} + +/** + * Note that you only want 1 lakitu spawned in per human player + * Otherwise Lakitu will animate faster than normal. + */ +class OLakitu : public OObject { +public: + enum LakituType : uint32_t { + STARTER = 1, + FINISH, // Checkered flag + TOW, // Picks up an out of bounds player + SECOND_LAP, + FINAL_LAP, + REVERSE, + TOW_ICE_CUBE, // Picks up an out of bounds player in sherbet land as an ice-cube + }; + + enum LakituTowType : uint16_t { + NORMAL, + ICE, // Used in sherbet land to put an ice-cube on the player + }; + +public: + explicit OLakitu(s32 playerId, LakituType type); + + void Activate(LakituType type); // Triggers Lakitu into a behaviour + + virtual void Tick() override; + virtual void Tick60fps() override; + virtual void Draw(s32 playerId) override; + + void func_80078F64(); + void func_80079054(s32 playerId); + void func_80079084(s32 playerId); + void func_800790B4(s32 playerId); + void func_800790E4(s32 playerId); + void func_80079114(s32 objectIndex, s32 playerId, s32 arg2); + void func_800791F0(s32 objectIndex, s32 playerId); + void init_obj_lakitu_starter_and_checkered_flag(s32 objectIndex, s32 playerId); + void update_object_lakitu_starter(s32 objectIndex, s32 arg1); + void func_800729EC(s32 objectIndex); + void init_obj_lakitu_checkered_flag(s32 objectIndex, s32 playerIndex); + void update_object_lakitu_checkered_flag(s32 objectIndex, s32 playerIndex); + void func_800797AC(s32 playerId); + void func_80079860(s32 playerId); + void func_8007993C(s32 objectIndex, Player* player); + void init_obj_lakitu_red_flag_fishing(s32 objectIndex, s32 arg1); + void func_80079A5C(s32 objectIndex, UNUSED Player* player); + void update_object_lakitu_fishing(s32 objectIndex, s32 playerId); + void update_object_lakitu_fishing2(s32 objectIndex, s32 playerId); + void func_8007A060(s32 objectIndex, s32 playerIndex); + void update_object_lakitu_second_lap(s32 objectIndex, s32 playerIndex); + void func_8007A228(s32 objectIndex, s32 playerIndex); + void update_object_lakitu_final_lap(s32 objectIndex, s32 playerIndex); + void func_8007A3F0(s32 objectIndex, s32 arg1); + void update_object_lakitu_reverse(s32 objectIndex, s32 playerId); + void func_8007A66C(s32 objectIndex); + void func_8007A778(s32 objectIndex); + void func_8007A88C(s32 playerId); + void func_8007A910(s32 arg0); + void func_8007AA44(s32 playerId); // animate lakitu + +private: + LakituType _type; + s32 _playerId; +}; diff --git a/src/engine/objects/Mole.cpp b/src/engine/objects/Mole.cpp new file mode 100644 index 000000000..7cc21e118 --- /dev/null +++ b/src/engine/objects/Mole.cpp @@ -0,0 +1,422 @@ +#include <libultraship.h> +#include <libultra/gbi.h> +#include "Mole.h" + +extern "C" { +#include "macros.h" +#include "main.h" +#include "defines.h" +#include "camera.h" +#include "update_objects.h" +#include "render_objects.h" +#include "actors.h" +#include "code_80057C60.h" +#include "code_80086E70.h" +#include "math_util.h" +#include "math_util_2.h" +#include "code_80005FD0.h" +#include "some_data.h" +#include "ceremony_and_credits.h" +#include "assets/moo_moo_farm_data.h" +#include "sounds.h" +#include "external.h" +} + +OMole::OMole(Vec3f pos) { + + find_unused_obj_index(&indexObjectList1[0]); +} + +void OMole::Tick() { + s32 var_s1; + s32 objectIndex; + + for (var_s1 = 0; var_s1 < D_8018D1C8; var_s1++) { + objectIndex = indexObjectList1[var_s1]; + if (gObjectList[objectIndex].state == 0) { + if (are_players_in_course_section(8, 9) != 0) { + func_80081FF4(objectIndex, 1); + } + } else { + OMole::func_800821AC(objectIndex, 1); + } + } + + for (var_s1 = 0; var_s1 < D_8018D1D0; var_s1++) { + objectIndex = indexObjectList2[var_s1]; + if (gObjectList[objectIndex].state == 0) { + if (are_players_in_course_section(0x0010, 0x0013) != 0) { + OMole::func_80081FF4(objectIndex, 2); + } + } else { + OMole::func_800821AC(objectIndex, 2); + } + } + + for (var_s1 = 0; var_s1 < D_8018D1D8; var_s1++) { + objectIndex = indexObjectList3[var_s1]; + if (gObjectList[objectIndex].state == 0) { + if (are_players_in_course_section(0x0011, 0x0014) != 0) { + func_80081FF4(objectIndex, 3); + } + } else { + OMole::func_800821AC(objectIndex, 3); + } + } + + for (var_s1 = 0; var_s1 < gObjectParticle2_SIZE; var_s1++) { + objectIndex = gObjectParticle2[var_s1]; + if (gObjectList[objectIndex].state != 0) { + OMole::func_80081790(objectIndex); + } + } +} + +void OMole::Draw(s32 cameraId) { + +} + +void OMole::func_80081790(s32 objectIndex) { + switch (gObjectList[objectIndex].state) { + case 0: + break; /* irregular */ + case 1: + if (func_80087E08(objectIndex, gObjectList[objectIndex].velocity[1], 0.3f, gObjectList[objectIndex].unk_034, + gObjectList[objectIndex].orientation[1], 0x00000032) != 0) { + object_next_state(objectIndex); + } + object_calculate_new_pos_offset(objectIndex); + break; + case 2: + func_80072428(objectIndex); + func_80086F60(objectIndex); + break; + } +} + + + + + +void OMole::func_80081AFC(s32 objectIndex, s32 arg1) { + s8* sp2C; + Object* object; + + object = &gObjectList[objectIndex]; + switch (object->state) { + case 0x1: + OMole::func_80081848(objectIndex); + break; + case 0x2: + if (object->unk_04C == 0) { + func_80086EAC(objectIndex, 2, 1); + object_next_state(objectIndex); + set_object_flag(objectIndex, 0x00000200); + } else { + object->unk_04C--; + } + break; + case 0x3: + if (object->unk_0AE == 0) { + func_80086EAC(objectIndex, 2, 4); + func_8008153C(objectIndex); + object_next_state(objectIndex); + func_800C98B8(object->pos, object->velocity, SOUND_ARG_LOAD(0x19, 0x01, 0x80, 0x07)); + } + break; + case 0x4: + if (func_80072E54(objectIndex, 1, 6, 1, 2, 0) != 0) { + func_800726CC(objectIndex, 0x00000064); + } + break; + case 0xA: + func_80072E54(objectIndex, 1, 6, 1, 0, -1); + if (object->unk_0AE == 0) { + func_800726CC(objectIndex, 0x00000064); + } + break; + case 0x64: + if (object->unk_0AE == 0) { + clear_object_flag(objectIndex, 0x00000200); + func_80072428(objectIndex); + switch (arg1) { /* switch 1; irregular */ + case 1: /* switch 1 */ + sp2C = D_8018D198; + break; + case 2: /* switch 1 */ + sp2C = D_8018D1A8; + break; + case 3: /* switch 1 */ + sp2C = D_8018D1B8; + break; + } + sp2C[object->type] = 0; + } + break; + case 0: + default: + break; + } + if (object->state >= 2) { + func_80073514(objectIndex); + } +} + +void OMole::func_80081D34(s32 objectIndex) { + Player* player; + Camera* var_s4; + s32 var_s2; + s32 var_s5; + Object* object; + + var_s5 = 0; + player = gPlayerOne; + var_s4 = camera1; + for (var_s2 = 0; var_s2 < D_8018D158; var_s2++, player++, var_s4++) { + if ((is_obj_flag_status_active(objectIndex, 0x00000200) != 0) && !(player->effects & 0x80000000) && + (has_collided_with_player(objectIndex, player) != 0)) { + if ((player->type & 0x8000) && !(player->type & 0x100)) { + var_s5 = 1; + object = &gObjectList[objectIndex]; + if (is_obj_flag_status_active(objectIndex, 0x04000000) != 0) { + func_80072180(); + } + if (player->effects & 0x200) { + func_800C9060(var_s2, 0x1900A046U); + } else { + player->soundEffects |= 2; + } + object->direction_angle[1] = var_s4->rot[1]; + object->velocity[1] = (player->unk_094 / 2) + 3.0; + object->unk_034 = player->unk_094 + 1.0; + if (object->velocity[1] >= 5.0) { + object->velocity[1] = 5.0f; + } + if (object->unk_034 >= 4.0) { + object->velocity[1] = 4.0f; + } + } + } + } + if (var_s5 != 0) { + object = &gObjectList[objectIndex]; + clear_object_flag(objectIndex, 0x00000200); + func_80086F60(objectIndex); + set_obj_origin_pos(objectIndex, object->pos[0], object->pos[1], object->pos[2]); + set_obj_origin_offset(objectIndex, 0.0f, 0.0f, 0.0f); + func_80086EAC(objectIndex, 2, 0x000A); + func_800726CC(objectIndex, 0x0000000A); + } +} + + +void OMole::func_80081FF4(s32 objectIndex, s32 arg1) { + UNUSED s32 stackPadding0; + UNUSED s32 stackPadding1; + s32 moleCount; + s16 var_v1; + s16 offset; + s32 var_a0; + s8* var_a2; + + init_object(objectIndex, 0); + gObjectList[objectIndex].unk_04C = random_int(0x001EU) + 5; + switch (arg1) { /* irregular */ + case 1: + var_a2 = D_8018D198; + moleCount = NUM_GROUP1_MOLES; + offset = 0; + break; + case 2: + var_a2 = D_8018D1A8; + moleCount = NUM_GROUP2_MOLES; + offset = 24; + // offset = NUM_GROUP1_MOLES; + break; + case 3: + var_a2 = D_8018D1B8; + moleCount = NUM_GROUP3_MOLES; + offset = 57; + // offset = NUM_GROUP1_MOLES + NUM_GROUP2_MOLES; + break; + } + var_v1 = random_int(moleCount); + for (var_a0 = 0; var_a0 < moleCount; var_a0++) { + if (var_a2[var_v1] != 0) { + var_v1++; + if (var_v1 == moleCount) { + var_v1 = 0; + } + } else { + var_a2[var_v1] = 1; + gObjectList[objectIndex].type = var_v1; + break; + } + } + /* + Ideally `gMoleSpawns` wouldn't be a union at all and its just be a list of Vec3s + Even more ideally each mole group would have its own array for its spawns + gObjectList[objectIndex].origin_pos[0] = gMoleSpawns.asVec3sList[offset + var_v1][0] * xOrientation; + gObjectList[objectIndex].origin_pos[1] = gMoleSpawns.asVec3sList[offset + var_v1][1] - 9.0; + gObjectList[objectIndex].origin_pos[2] = gMoleSpawns.asVec3sList[offset + var_v1][2]; + */ + gObjectList[objectIndex].origin_pos[0] = gMoleSpawns.asFlatList[offset + (var_v1 * 3) + 0] * xOrientation; + gObjectList[objectIndex].origin_pos[1] = gMoleSpawns.asFlatList[offset + (var_v1 * 3) + 1] - 9.0; + gObjectList[objectIndex].origin_pos[2] = gMoleSpawns.asFlatList[offset + (var_v1 * 3) + 2]; +} + +void OMole::func_80081848(s32 objectIndex) { + u8* mole = (u8*) LOAD_ASSET_RAW(d_course_moo_moo_farm_mole_frames); + u8* tlut = (u8*) LOAD_ASSET_RAW(d_course_moo_moo_farm_mole_tlut); + init_texture_object(objectIndex, (u8*)d_course_moo_moo_farm_mole_tlut, (const char**) mole, 0x20U, (u16) 0x00000040); + gObjectList[objectIndex].sizeScaling = 0.15f; + gObjectList[objectIndex].textureListIndex = 0; + set_obj_origin_offset(objectIndex, 0.0f, 0.0f, 0.0f); + set_obj_direction_angle(objectIndex, 0U, 0U, 0U); + set_obj_orientation(objectIndex, 0U, 0U, 0x8000U); + gObjectList[objectIndex].boundingBoxSize = 6; + gObjectList[objectIndex].velocity[1] = 4.0f; + set_object_flag(objectIndex, 0x04000000); + object_next_state(objectIndex); +} + + +void OMole::func_80081924(s32 objectIndex) { + switch (gObjectList[objectIndex].unk_0AE) { + case 1: + if (f32_step_up_towards(&gObjectList[objectIndex].offset[1], 9.0f, 0.7f) != 0) { + func_80086FD4(objectIndex); + } + break; + case 2: + func_800871AC(objectIndex, 0x0000000A); + break; + case 3: + if (f32_step_down_towards(&gObjectList[objectIndex].offset[1], 3.0f, 1.0f) != 0) { + func_80086F60(objectIndex); + } + break; + case 4: + func_80087D24(objectIndex, 3.6f, 0.25f, 0.0f); + break; + case 5: + func_80086F60(objectIndex); + break; + case 10: + gObjectList[objectIndex].orientation[2] += 0x1000; + gObjectList[objectIndex].velocity[1] -= 0.184; + func_8008751C(objectIndex); + object_add_velocity_offset_xyz(objectIndex); + if (gObjectList[objectIndex].pos[1] <= -10.0) { + func_80086F60(objectIndex); + } + break; + case 0: + default: + break; + } +} + + +void OMole::func_80081A88(s32 objectIndex) { + switch (gObjectList[objectIndex].unk_0DD) { /* irregular */ + case 0: + break; + case 1: + func_8008B724(objectIndex); + break; + case 2: + OMole::func_80081924(objectIndex); + break; + } + object_calculate_new_pos_offset(objectIndex); +} + +void OMole::func_800821AC(s32 objectIndex, s32 arg1) { + if (gObjectList[objectIndex].state != 0) { + OMole::func_80081AFC(objectIndex, arg1); + OMole::func_80081A88(objectIndex); + OMole::func_80081D34(objectIndex); + } +} + +void OMole::func_80054E10(s32 objectIndex) { + if (gObjectList[objectIndex].state > 0) { + if (is_obj_flag_status_active(objectIndex, 0x00800000) != 0) { + D_80183E50[0] = gObjectList[objectIndex].pos[0]; + D_80183E50[1] = gObjectList[objectIndex].surfaceHeight + 0.8; + D_80183E50[2] = gObjectList[objectIndex].pos[2]; + D_80183E70[0] = gObjectList[objectIndex].velocity[0]; + D_80183E70[1] = gObjectList[objectIndex].velocity[1]; + D_80183E70[2] = gObjectList[objectIndex].velocity[2]; + func_8004A9B8(gObjectList[objectIndex].sizeScaling); + } + } +} + +// Almost certainly responsible for spawning/handling the moles on Moo Moo farm +void OMole::func_80054EB8() { + s32 someIndex; + + for (someIndex = 0; someIndex < NUM_TOTAL_MOLES; someIndex++) { + func_80054E10(gObjectParticle1[someIndex]); + } +} + +void OMole::func_80054D00(s32 objectIndex, s32 cameraId) { + Camera* camera; + + camera = &camera1[cameraId]; + if (gObjectList[objectIndex].state >= 3) { + func_8008A364(objectIndex, cameraId, 0x2AABU, 0x0000012C); + if (is_obj_flag_status_active(objectIndex, VISIBLE) != 0) { + D_80183E80[0] = (s16) gObjectList[objectIndex].orientation[0]; + D_80183E80[1] = + func_800418AC(gObjectList[objectIndex].pos[0], gObjectList[objectIndex].pos[2], camera->pos); + D_80183E80[2] = (u16) gObjectList[objectIndex].orientation[2]; + func_80048130(gObjectList[objectIndex].pos, (u16*) D_80183E80, gObjectList[objectIndex].sizeScaling, + (u8*) gObjectList[objectIndex].activeTLUT, (u8*)gObjectList[objectIndex].activeTexture, + (Vtx*)LOAD_ASSET_RAW(D_0D0062B0), 0x00000020, 0x00000040, 0x00000020, 0x00000040, 5); + } + } +} + +void OMole::func_80054F04(s32 cameraId) { + s32 var_s2; + s32 objectIndex; + Camera* sp44; + Object* object; + + sp44 = &camera1[cameraId]; + gSPDisplayList(gDisplayListHead++, (Gfx*)D_0D0079C8); + load_texture_block_rgba16_mirror((u8*) LOAD_ASSET_RAW(d_course_moo_moo_farm_mole_dirt), 0x00000010, 0x00000010); + for (var_s2 = 0; var_s2 < gObjectParticle2_SIZE; var_s2++) { + objectIndex = gObjectParticle2[var_s2]; + object = &gObjectList[objectIndex]; + if (object->state > 0) { + func_8008A364(objectIndex, cameraId, 0x2AABU, 0x000000C8); + if ((is_obj_flag_status_active(objectIndex, VISIBLE) != 0) && (gMatrixHudCount <= MTX_HUD_POOL_SIZE_MAX)) { + object->orientation[1] = func_800418AC(object->pos[0], object->pos[2], sp44->pos); + rsp_set_matrix_gObjectList(objectIndex); + gSPDisplayList(gDisplayListHead++, (Gfx*)D_0D006980); + } + } + } + gSPTexture(gDisplayListHead++, 1, 1, 0, G_TX_RENDERTILE, G_OFF); +} + +void OMole::render_object_moles(s32 cameraId) { + s32 i; + + for (i = 0; i < NUM_GROUP1_MOLES; i++) { + OMole::func_80054D00(indexObjectList1[i], cameraId); + } + for (i = 0; i < NUM_GROUP2_MOLES; i++) { + OMole::func_80054D00(indexObjectList2[i], cameraId); + } + for (i = 0; i < NUM_GROUP3_MOLES; i++) { + OMole::func_80054D00(indexObjectList3[i], cameraId); + } + OMole::func_80054EB8(); + OMole::func_80054F04(cameraId); +}
\ No newline at end of file diff --git a/src/engine/objects/Mole.h b/src/engine/objects/Mole.h new file mode 100644 index 000000000..7ff95c58c --- /dev/null +++ b/src/engine/objects/Mole.h @@ -0,0 +1,52 @@ +#pragma once + +#include <libultraship.h> +#include <vector> +#include "Object.h" + +extern "C" { +#include "macros.h" +#include "main.h" +#include "vehicles.h" +#include "waypoints.h" +#include "common_structs.h" +#include "objects.h" +#include "course_offsets.h" +#include "some_data.h" +} + + +class OMole : public OObject { +public: + enum Behaviour : uint16_t { + }; + +public: + explicit OMole(Vec3f pos); + + virtual void Tick() override; + virtual void Draw(s32 cameraId) override; + + void func_80054E10(s32 objectIndex); + void func_80054EB8(); + void func_80054F04(s32 cameraId); + void render_object_moles(s32 cameraId); + void func_80054D00(s32 objectIndex, s32 cameraId); + void func_800821AC(s32 objectIndex, s32 arg1); + void func_80081FF4(s32 objectIndex, s32 arg1); + void func_80081D34(s32 objectIndex); + void func_80081AFC(s32 objectIndex, s32 arg1); + void func_80081A88(s32 objectIndex); + void func_80081924(s32 objectIndex); + void func_80081848(s32 objectIndex); + void func_80081790(s32 objectIndex); + +private: + s32 _idx; + s32 _state; + s32 _timer; + s32 _status; + bool _toggle; + + SplineData *spline; +}; diff --git a/src/engine/objects/Object.cpp b/src/engine/objects/Object.cpp new file mode 100644 index 000000000..eab390aa1 --- /dev/null +++ b/src/engine/objects/Object.cpp @@ -0,0 +1,23 @@ +#include <libultraship.h> +#include "Object.h" + +#include "World.h" + +extern "C" { + #include "camera.h" +} + + + //GameActor() + +OObject::OObject() {} + + // Virtual functions to be overridden by derived classes +void OObject::Tick() { } +void OObject::Tick60fps() {} +void OObject::Draw(s32 cameraId) { } +void OObject::Collision() {} +void OObject::Expire() { } +void OObject::Destroy() { + PendingDestroy = true; +} diff --git a/src/engine/objects/Object.h b/src/engine/objects/Object.h new file mode 100644 index 000000000..af2df81b5 --- /dev/null +++ b/src/engine/objects/Object.h @@ -0,0 +1,26 @@ +#pragma once + +#include <libultraship.h> + +extern "C" { + #include "camera.h" + #include "objects.h" +} + +class OObject { +public: + uint8_t uuid[16]; + Object o; + bool PendingDestroy = false; + + virtual ~OObject() = default; + + explicit OObject(); + + virtual void Tick(); + virtual void Tick60fps(); + virtual void Draw(s32 cameraId); + virtual void Collision(); + virtual void Expire(); + virtual void Destroy(); // Mark object for deletion at the start of the next frame +}; diff --git a/src/engine/objects/Penguin.cpp b/src/engine/objects/Penguin.cpp new file mode 100644 index 000000000..c690e1ef2 --- /dev/null +++ b/src/engine/objects/Penguin.cpp @@ -0,0 +1,413 @@ +#include <libultraship.h> +#include <libultra/gbi.h> +#include "Penguin.h" +#include <vector> + +#include "port/Game.h" + +extern "C" { +#include "macros.h" +#include "main.h" +#include "actors.h" +#include "math_util.h" +#include "sounds.h" +#include "update_objects.h" +#include "render_player.h" +#include "external.h" +#include "bomb_kart.h" +#include "collision.h" +#include "code_80086E70.h" +#include "render_objects.h" +#include "code_80057C60.h" +#include "defines.h" +#include "code_80005FD0.h" +#include "math_util_2.h" +#include "collision.h" +#include "assets/bowsers_castle_data.h" +#include "ceremony_and_credits.h" +#include "objects.h" +#include "update_objects.h" +#include "render_objects.h" +#include "course_offsets.h" +#include "data/some_data.h" +extern s8 gPlayerCount; +} + +size_t OPenguin::_count = 0; + +OPenguin::OPenguin(Vec3f pos, u16 direction, PenguinType type, Behaviour behaviour) { + _idx = _count; + _type = type; + _bhv = behaviour; + + if (_idx >= 32) { + printf("MAX penguin REACHED (32), skipping\n"); + return; + } + + s32 objectIndex = indexObjectList1[_idx]; + init_object(objectIndex, 0); + + Object *object = &gObjectList[objectIndex]; + object->origin_pos[0] = pos[0] * xOrientation; + object->origin_pos[1] = pos[1]; + object->origin_pos[2] = pos[2]; + object->unk_0C6 = direction; + + switch(type) { + case PenguinType::CHICK: + object->surfaceHeight = 5.0f; + object->sizeScaling = 0.04f; + object->boundingBoxSize = 4; + break; + case PenguinType::ADULT: + object->surfaceHeight = -80.0f; + object->sizeScaling = 0.08f; + object->boundingBoxSize = 4; + break; + case PenguinType::CREDITS: + object->surfaceHeight = -80.0f; + object->sizeScaling = 0.08f; + object->sizeScaling = 0.15f; + break; + case PenguinType::EMPEROR: + object->sizeScaling = 0.2f; + object->boundingBoxSize = 0x000C; + break; + } + + _count++; +} + +void OPenguin::Tick(void) { + s32 objectIndex; + + objectIndex = indexObjectList1[_idx]; + if (gObjectList[objectIndex].state != 0) { + if (_type == PenguinType::EMPEROR) { + OPenguin::EmperorPenguin(objectIndex); + } else { + OPenguin::OtherPenguin(objectIndex); + } + + OPenguin::Behaviours(objectIndex, _idx); + } + if (func_80072320(objectIndex, 1) != 0) { + func_80089820(objectIndex, 1.75f, 1.5f, 0x1900A046U); + } else if (func_80072320(objectIndex, 8) != 0) { + func_80089820(objectIndex, 1.3f, 1.0f, 0x1900A046U); + } else { + func_80089820(objectIndex, 1.5f, 1.25f, 0x1900A046U); + } + if ((is_obj_flag_status_active(objectIndex, 0x02000000) != 0) && + (func_80072354(objectIndex, 0x00000020) != 0)) { + func_800722A4(objectIndex, 0x00000060); + clear_object_flag(objectIndex, 0x02000000); + } +} + +void OPenguin::Draw(s32 cameraId) { + s32 objectIndex; + s32 temp_s1; + s32 drawDistance; + u16 var_s1; + u32 var_s3; + + if (gPlayerCountSelection1 == 1) { + var_s3 = 0x0003D090; + } else if (gPlayerCountSelection1 == 2) { + var_s3 = 0x00027100; + } else { + var_s3 = 0x00015F90; + } + + objectIndex = indexObjectList1[_idx]; + if (gObjectList[objectIndex].state >= 2) { + if (gPlayerCountSelection1 == 1) { + var_s1 = 0x4000; + if (_idx == 0) { + drawDistance = 0x000005DC; + } else if (func_80072320(objectIndex, 8) != 0) { + drawDistance = 0x00000320; + } else { + drawDistance = 0x000003E8; + } + } else { + if (func_80072320(objectIndex, 8) != 0) { + drawDistance = 0x000001F4; + var_s1 = 0x4000; + } else { + drawDistance = 0x00000258; + var_s1 = 0x5555; + } + } + temp_s1 = func_8008A364(objectIndex, cameraId, var_s1, drawDistance); + if (is_obj_flag_status_active(objectIndex, VISIBLE) != 0) { + func_800557B4(objectIndex, (u32) temp_s1, var_s3); + } + } +} + +void OPenguin::Behaviours(s32 objectIndex, s32 arg1) { // func_800850B0 + Object* object; + + object = &gObjectList[objectIndex]; + switch (_bhv) { + case 1: // emperor + OPenguin::func_80085080(objectIndex); + break; + case 2: + OPenguin::func_8008502C(objectIndex, arg1); + break; + case 3: + OPenguin::func_80084D2C(objectIndex, 0); + break; + case 4: + OPenguin::func_80084D2C(objectIndex, 1); + break; + case 5: + OPenguin::func_80084D2C(objectIndex, 2); + break; + case 6: + OPenguin::func_80084D2C(objectIndex, 3); + break; + } + if (func_80072320(objectIndex, 0x00000020) != 0) { + if (func_80072320(objectIndex, 0x00000040) != 0) { + func_800722CC(objectIndex, 0x00000040); + object->unk_084[6] = 0; + object->unk_084[7] = 0x0096; + } + if (object->unk_084[7] == 0) { + func_800722CC(objectIndex, 0x00000020); + } else { + object->unk_084[7]--; + object->orientation[0] = object->direction_angle[0]; + object->orientation[1] += 0x2000; + object->orientation[2] = object->direction_angle[2]; + } + } else { + object->orientation[0] = object->direction_angle[0]; + object->orientation[1] = object->direction_angle[1]; + object->orientation[2] = object->direction_angle[2]; + } +} + +void OPenguin::func_80084D2C(s32 objectIndex, s32 arg1) { + f32 sp24; + + switch (gObjectList[objectIndex].unk_0AE) { + case 0: + break; + case 1: + gObjectList[objectIndex].direction_angle[1] = + func_800417B4(gObjectList[objectIndex].direction_angle[1], gObjectList[objectIndex].unk_0C6); + if (gObjectList[objectIndex].direction_angle[1] == gObjectList[objectIndex].unk_0C6) { + gObjectList[objectIndex].unk_09C = 4; + gObjectList[objectIndex].unk_034 = 0.4f; + func_80086FD4(objectIndex); + } + break; + case 2: + f32_step_towards(&gObjectList[objectIndex].unk_034, 0.8f, 0.02f); + if (func_80087060(objectIndex, 0x0000000F) != 0) { + func_800722A4(objectIndex, 1); + func_800722A4(objectIndex, 2); + gObjectList[objectIndex].unk_09C = 1; + gObjectList[objectIndex].unk_0D8 = 1; + gObjectList[objectIndex].textureListIndex = 0; + gObjectList[objectIndex].type = + get_animation_length(d_course_sherbet_land_unk_data11, gObjectList[objectIndex].unk_0D8); + func_800726CC(objectIndex, 3); + func_80086FD4(objectIndex); + if (func_80072354(objectIndex, 0x00000020) != 0) { + func_800722A4(objectIndex, 0x00000080); + } + } + break; + case 3: + switch (arg1) { /* switch 1; irregular */ + case 0: /* switch 1 */ + sp24 = 1.0f; + break; + case 1: /* switch 1 */ + sp24 = 1.5f; + break; + case 2: /* switch 1 */ + sp24 = 2.0f; + break; + case 3: /* switch 1 */ + sp24 = 2.5f; + break; + } + f32_step_towards(&gObjectList[objectIndex].unk_034, sp24, 0.15f); + if ((func_80072354(objectIndex, 2) != 0) && (sp24 == gObjectList[objectIndex].unk_034)) { + func_80086FD4(objectIndex); + } + break; + case 4: + if (func_80087060(objectIndex, 0x0000001E) != 0) { + func_800722CC(objectIndex, 1); + func_80086FD4(objectIndex); + } + break; + case 5: + f32_step_towards(&gObjectList[objectIndex].unk_034, 0.4f, 0.2f); + if (func_80087060(objectIndex, 0x0000000A) != 0) { + func_800722A4(objectIndex, 2); + gObjectList[objectIndex].unk_0D8 = 2; + gObjectList[objectIndex].textureListIndex = 0; + gObjectList[objectIndex].type = + get_animation_length(d_course_sherbet_land_unk_data11, gObjectList[objectIndex].unk_0D8); + func_800726CC(objectIndex, 3); + func_80086FD4(objectIndex); + } + break; + case 6: + if (func_80072354(objectIndex, 2) != 0) { + gObjectList[objectIndex].unk_0D8 = 0; + gObjectList[objectIndex].textureListIndex = 0; + gObjectList[objectIndex].type = + get_animation_length(d_course_sherbet_land_unk_data11, gObjectList[objectIndex].unk_0D8); + gObjectList[objectIndex].unk_0C6 += 0x8000; + func_800726CC(objectIndex, 2); + func_8008701C(objectIndex, 1); + } + break; + } + func_8008781C(objectIndex); + object_calculate_new_pos_offset(objectIndex); +} + +void OPenguin::func_80085080(s32 objectIndex) { + func_8008B78C(objectIndex); + object_calculate_new_pos_offset(objectIndex); + func_800873F4(objectIndex); +} + +void OPenguin::func_8008502C(s32 objectIndex, s32 arg1) { + func_80088038(objectIndex, gObjectList[objectIndex].unk_01C[1], gObjectList[objectIndex].unk_0C6); + object_calculate_new_pos_offset(objectIndex); + func_800873F4(objectIndex); +} + + +void OPenguin::EmperorPenguin(s32 objectIndex) { + switch (gObjectList[objectIndex].state) { + case 0: + break; + case 1: + OPenguin::InitEmperorPenguin(objectIndex); + break; + case 2: + func_80072E54(objectIndex, 0, gObjectList[objectIndex].type, 1, 0, -1); + break; + } +} + +void OPenguin::InitEmperorPenguin(s32 objectIndex) { + Object* object; + + object = &gObjectList[objectIndex]; + object->unk_0D8 = 0; + object->model = (Gfx*) d_course_sherbet_land_unk_data1; + object->vertex = (Vtx*) d_course_sherbet_land_unk_data11; + //object->sizeScaling = 0.2f; + //object->boundingBoxSize = 0x000C; + object->unk_09C = 1; + set_obj_direction_angle(objectIndex, 0U, 0U, 0U); + object->unk_0DD = 1; + func_80086EF0(objectIndex); + object->spline = D_800E672C[0]; + set_object_flag(objectIndex, 0x04000800); + object->type = get_animation_length(d_course_sherbet_land_unk_data11, 0); + object_next_state(objectIndex); +} + +void OPenguin::OtherPenguin(s32 objectIndex) { + Object* object; + + object = &gObjectList[objectIndex]; + switch (object->state) { + case 0: + break; + case 1: + OPenguin::InitOtherPenguin(objectIndex); + break; + case 2: + func_80072E54(objectIndex, 0, (s32) object->type, (s32) object->unk_09C, 0, -1); + if (func_80072354(objectIndex, 0x00000020) != 0) { + if (object->unk_084[6] == 0) { + object->unk_084[6] = random_int(0x005AU) + 0x5A; + func_800722A4(objectIndex, 0x00000080); + } else { + object->unk_084[6]--; + } + } + break; + case 3: + func_80072E54(objectIndex, 0, object->type, 1, 0, 0); + break; + case 4: + func_800722CC(objectIndex, 2); + object_next_state(objectIndex); + break; + } + if (func_80072320(objectIndex, 0x00000020) != 0) { + if (object->unk_084[6] == 0) { + func_800722A4(objectIndex, 0x00000080); + object->unk_084[6] = 0x0010; + } else { + object->unk_084[6]--; + } + } + if (func_80072320(objectIndex, 0x00000080) != 0) { + func_800722CC(objectIndex, 0x00000080); + if (func_80072320(objectIndex, 0x00000010) != 0) { + func_800C98B8(object->pos, object->velocity, SOUND_ARG_LOAD(0x19, 0x00, 0x70, 0x49)); + } else { + func_800C98B8(object->pos, object->velocity, SOUND_ARG_LOAD(0x19, 0x00, 0x70, 0x17)); + } + } +} + +void OPenguin::InitOtherPenguin(s32 objectIndex) { + Object* object; + + object = &gObjectList[objectIndex]; + object->unk_0D8 = 0; + object->model = (Gfx*) d_course_sherbet_land_unk_data1; + object->vertex = (Vtx*) d_course_sherbet_land_unk_data11; + //object->boundingBoxSize = 4; + object->unk_09C = 2; + object->unk_04C = random_int(0x012CU); + set_object_flag(objectIndex, 0x04000220); + + // This code has been significantly refactored from the original func_800845C8 + // Into a switch statement instead of checking for the index of the penguin + switch(_bhv) { + case Behaviour::CIRCLE: + object->unk_01C[1] = Diameter; + object->unk_0C4 = (_idx << 0xF) & 0xFFFF; + //object->unk_0DD = 2; + func_800722A4(objectIndex, 8); + break; + case Behaviour::SLIDE3: + case Behaviour::SLIDE4: + case Behaviour::SLIDE6: + case Behaviour::STRUT: + + if (gIsMirrorMode) { + object->unk_0C6 = MirrorModeAngleOffset; + } + + set_obj_direction_angle(objectIndex, 0U, object->unk_0C6 + 0x8000, 0U); + func_800722A4(objectIndex, 20); + break; + } + + func_80086EF0(objectIndex); + object->unk_034 = 0.0f; + object->type = get_animation_length(d_course_sherbet_land_unk_data11, 0); + object_next_state(objectIndex); +} diff --git a/src/engine/objects/Penguin.h b/src/engine/objects/Penguin.h new file mode 100644 index 000000000..63c2207c1 --- /dev/null +++ b/src/engine/objects/Penguin.h @@ -0,0 +1,71 @@ +#pragma once + +#include <libultraship.h> +#include <vector> +#include "engine/World.h" +#include "engine/objects/Object.h" + +extern "C" { +#include "macros.h" +#include "main.h" +#include "vehicles.h" +#include "waypoints.h" +#include "common_structs.h" +#include "objects.h" +#include "course_offsets.h" +} + +class OPenguin : public OObject { +public: + enum PenguinType : uint32_t { + CHICK, + ADULT, + CREDITS, + EMPEROR + }; + + enum Behaviour : uint16_t { + DISABLED, + STRUT, // Emperor penguin + CIRCLE, // Waddle in a circle + SLIDE3, + SLIDE4, + UNK, // unused + SLIDE6, + }; + +public: + f32 Diameter = 0.0f; // Waddle in a circle around the spawn point at this diameter. + uint16_t MirrorModeAngleOffset; + + explicit OPenguin(Vec3f pos, u16 direction, PenguinType type, Behaviour behaviour); + + ~OPenguin() { + _count--; + } + + static size_t GetCount() { + return _count; + } + + + virtual void Tick() override; + virtual void Draw(s32 cameraId) override; +private: + void Behaviours(s32 objectIndex, s32 arg1); + void EmperorPenguin(s32 objectIndex); + void func_80085080(s32 objectIndex); + void func_8008502C(s32 objectIndex, s32 arg1); + void func_80084D2C(s32 objectIndex, s32 arg1); + + void InitEmperorPenguin(s32 objectIndex); + void OtherPenguin(s32 objectIndex); + void InitOtherPenguin(s32 objectIndex); + + static size_t _count; + s32 _idx; + PenguinType _type; + Behaviour _bhv; + + +}; diff --git a/src/engine/objects/Podium.cpp b/src/engine/objects/Podium.cpp new file mode 100644 index 000000000..1922c98a5 --- /dev/null +++ b/src/engine/objects/Podium.cpp @@ -0,0 +1,142 @@ +#include "Podium.h" +#include "assets/ceremony_data.h" + +extern "C" { +#include "main.h" +#include "defines.h" +#include "update_objects.h" +#include "render_objects.h" +#include "code_80057C60.h" +#include "podium_ceremony_actors.h" +#include "math_util.h" +#include "math_util_2.h" +#include "assets/common_data.h" +#include "some_data.h" +#include "code_80091440.h" +#include "code_80086E70.h" +#include "code_80086E70.h" +extern Vec3s D_800E634C[]; +} + +// Might be Cheep Cheep related? +// Vec3s D_800E634C[] = { +// { 0xf37e, 0x0013, 0xfe22 }, +// { 0xf37b, 0x0013, 0xfe31 }, +// { 0xf380, 0x0013, 0xfe14 }, +// }; + +OPodium::OPodium(const FVector& pos) { + + _pos = pos; + + for (size_t i = 0; i < NUM_PODIUMS; i++) { + s32 objectIndex = indexObjectList1[i]; + //init_object(objectIndex, 0); + //set_obj_origin_pos(objectIndex, pos.x - 1.5, pos.y, pos.z); + } +} + +void OPodium::Tick() { // func_80086604 + s32 objectIndex; + if ((D_8016347C != 0) && (D_802874D8.unk1D < 3)) { + if (D_801658C6 == 0) { + for (size_t i = 0; i < 3; i++) { + objectIndex = indexObjectList1[i]; + init_object(objectIndex, 0); + } + D_801658C6 = 1; + } + } + for (size_t i = 0; i != NUM_PODIUMS; i++) { + objectIndex = indexObjectList1[i]; + if (gObjectList[objectIndex].state != 0) { + OPodium::func_80086528(objectIndex, i); + OPodium::func_80086424(objectIndex); + } + } +} + +void OPodium::Draw(s32 cameraId) { // func_80055F48 + for (size_t i = 0; i < NUM_PODIUMS; i++) { + Object* object; + + object = &gObjectList[indexObjectList1[i]]; + if (object->state >= 2) { + //func_80043220(object->pos, object->direction_angle, object->sizeScaling, object->model); + rsp_set_matrix_transformation(object->pos, object->direction_angle, object->sizeScaling); + gSPDisplayList(gDisplayListHead++, (Gfx*)D_0D0077A0); + gSPDisplayList(gDisplayListHead++, object->model); + } + } +} + +void OPodium::func_8008629C(s32 objectIndex, s32 arg1) { + switch (arg1) { /* irregular */ + case 0: + gObjectList[objectIndex].model = (Gfx*)podium_dl3; + gObjectList[objectIndex].unk_04C = 0x00000038; + break; + case 1: + gObjectList[objectIndex].model = (Gfx*)podium2_dl3; + gObjectList[objectIndex].unk_04C = 0x0000002B; + break; + case 2: + gObjectList[objectIndex].model = (Gfx*)podium3_dl3; + gObjectList[objectIndex].unk_04C = 0x0000001E; + break; + default: + break; + } + gObjectList[objectIndex].sizeScaling = 1.0f; + set_obj_origin_pos(objectIndex, _pos.x - 1.5, _pos.y, _pos.z); + set_obj_origin_offset(objectIndex, 0.0f, -10.0f, 0.0f); + set_obj_direction_angle(objectIndex, 0U, 0xF8E4U, 0U); + gObjectList[objectIndex].unk_048 = 0; + object_next_state(objectIndex); +} + +void OPodium::func_80086424(s32 objectIndex) { + switch (gObjectList[objectIndex].unk_0AE) { + case 0: + break; + case 1: + gObjectList[objectIndex].velocity[1] = 0.75f; + func_80086FD4(objectIndex); + break; + case 2: + if (gObjectList[objectIndex].offset[1] >= -2.0) { + gObjectList[objectIndex].velocity[1] -= 0.1; + } + object_add_velocity_offset_y(objectIndex); + if (gObjectList[objectIndex].offset[1] >= 0.0) { + gObjectList[objectIndex].offset[1] = 0.0f; + gObjectList[objectIndex].velocity[1] = 0.0f; + func_80086F60(objectIndex); + } + break; + } + object_calculate_new_pos_offset(objectIndex); +} + +void OPodium::func_80086528(s32 objectIndex, s32 arg1) { + switch (gObjectList[objectIndex].state) { /* irregular */ + case 1: + func_8008629C(objectIndex, arg1); + break; + case 2: + if (set_and_run_timer_object(objectIndex, gObjectList[objectIndex].unk_04C) != 0) { + func_80091440(arg1); + func_80086E70(objectIndex); + object_next_state(objectIndex); + } + break; + case 0: + break; + case 3: + if (gObjectList[objectIndex].unk_0AE == 0) { + gObjectList[objectIndex].unk_048 = 1; + object_next_state(objectIndex); + } + break; + } +} diff --git a/src/engine/objects/Podium.h b/src/engine/objects/Podium.h new file mode 100644 index 000000000..ea79d7232 --- /dev/null +++ b/src/engine/objects/Podium.h @@ -0,0 +1,39 @@ +#pragma once + +#include <libultraship.h> +#include <vector> + +#include "World.h" + +extern "C" { +#include "macros.h" +#include "main.h" +#include "vehicles.h" +#include "waypoints.h" +#include "common_structs.h" +#include "objects.h" +#include "course_offsets.h" +#include "some_data.h" +} + + +class OPodium : public OObject { +public: + enum Behaviour : uint16_t { + }; + +public: + + explicit OPodium(const FVector& pos); + + virtual void Tick() override; + virtual void Draw(s32 cameraId) override; + void func_8008629C(s32 objectIndex, s32 arg1); + void func_80086424(s32 objectIndex); + void func_80086528(s32 objectIndex, s32 arg1); + +private: + + s32 _idx; + FVector _pos; +}; diff --git a/src/engine/objects/Seagull.cpp b/src/engine/objects/Seagull.cpp new file mode 100644 index 000000000..cced70348 --- /dev/null +++ b/src/engine/objects/Seagull.cpp @@ -0,0 +1,194 @@ +#include <libultraship.h> +#include <libultra/gbi.h> +#include "Seagull.h" +#include <vector> +#include "World.h" + +#include "port/Game.h" + +extern "C" { +#include "macros.h" +#include "main.h" +#include "defines.h" +#include "camera.h" +#include "update_objects.h" +#include "render_objects.h" +#include "actors.h" +#include "code_80057C60.h" +#include "code_80086E70.h" +#include "math_util.h" +#include "math_util_2.h" +#include "code_80005FD0.h" +#include "some_data.h" +#include "ceremony_and_credits.h" +extern SplineData D_800E6034; +extern SplineData D_800E60F0; +extern SplineData D_800E61B4; +extern SplineData D_800E6280; +} + +SplineData* D_800E633C[] = { &D_800E6034, &D_800E60F0, &D_800E61B4, &D_800E6280 }; + +size_t OSeagull::_count = 0; + +OSeagull::OSeagull(Vec3f pos) { + size_t objectIndex; + _idx = _count; + _pos[0] = pos[0]; + _pos[1] = pos[1]; + _pos[2] = pos[2]; + + s16 randZ; + s16 randX; + s16 randY; + randX = random_int(200) + -100.0; + randY = random_int(20); + randZ = random_int(200) + -100.0; + + //for (i = 0; i < NUM_SEAGULLS; i++) { + + + objectIndex = indexObjectList2[_idx]; + init_object(objectIndex, 0); + + + set_obj_origin_pos(objectIndex, pos[0], pos[1], pos[2]); + if (_idx < (NUM_SEAGULLS / 2)) { + gObjectList[objectIndex].unk_0D5 = 0; + } else { + gObjectList[objectIndex].unk_0D5 = 1; + } + + _count++; +} + +void OSeagull::Tick() { + Object* object; + s32 objectIndex = indexObjectList2[_idx]; + + object = &gObjectList[objectIndex]; + if (object->state == 0) { + return; + } + + OSeagull::func_80082714(objectIndex, _idx); + OSeagull::func_8008275C(objectIndex); + if (func_80072320(objectIndex, 2) != 0) { + func_800722CC(objectIndex, 2); + if (D_80165A90 != 0) { + D_80165A90 = 0; + D_80183E40[0] = 0.0f; + D_80183E40[1] = 0.0f; + D_80183E40[2] = 0.0f; + if (gGamestate != CREDITS_SEQUENCE) { + func_800C98B8(object->pos, D_80183E40, SOUND_ARG_LOAD(0x19, 0x01, 0x70, 0x43)); + } else { + objectIndex = indexObjectList2[1]; + if (gCutsceneShotTimer <= 150) { + object = &gObjectList[objectIndex]; + func_800C98B8(object->pos, D_80183E40, SOUND_ARG_LOAD(0x19, 0x01, 0x70, 0x43)); + } + } + } + } + //} + if (D_80165900 != 0) { + D_80165900 -= 1; + } else { + if (gGamestate != CREDITS_SEQUENCE) { + D_80165900 = 60; + } else { + D_80165900 = 15; + } + if ((D_80165908 != 0) && (D_80165A90 == 0)) { + D_80165A90 = 1; + } + } + D_80165908 = 0; +} + +void OSeagull::Draw(s32 cameraId) { // render_object_seagulls + s32 objectIndex = indexObjectList2[_idx]; + + if (func_8008A364(objectIndex, cameraId, 0x5555U, 0x000005DC) < 0x9C401 && CVarGetInteger("gNoCulling", 0) == 0) { + D_80165908 = 1; + _toggle = true; + } + if (is_obj_flag_status_active(objectIndex, VISIBLE) != 0) { + OSeagull::func_800552BC(objectIndex); + } +} + +void OSeagull::func_800552BC(s32 objectIndex) { + if (gObjectList[objectIndex].state >= 2) { + rsp_set_matrix_transformation(gObjectList[objectIndex].pos, gObjectList[objectIndex].direction_angle, + gObjectList[objectIndex].sizeScaling); + gSPDisplayList(gDisplayListHead++, (Gfx*)D_0D0077D0); + if (gIsGamePaused == 0) { + gObjectList[objectIndex].unk_0A2 = render_animated_model((Armature*) gObjectList[objectIndex].model, + (Animation**) gObjectList[objectIndex].vertex, 0, + gObjectList[objectIndex].unk_0A2); + } else { + render_animated_model((Armature*) gObjectList[objectIndex].model, + (Animation**) gObjectList[objectIndex].vertex, 0, gObjectList[objectIndex].unk_0A2); + } + } +} + +void OSeagull::func_8008275C(s32 objectIndex) { + UNUSED s32 stackPadding; + switch (gObjectList[objectIndex].unk_0DD) { + case 1: + func_8008B78C(objectIndex); + object_calculate_new_pos_offset(objectIndex); + break; + case 2: + func_8008B78C(objectIndex); + vec3f_copy(gObjectList[objectIndex].unk_01C, gObjectList[objectIndex].pos); + func_8000D940(gObjectList[objectIndex].origin_pos, (s16*) &gObjectList[objectIndex].unk_0C6, + gObjectList[objectIndex].unk_034, 0.0f, 0); + gObjectList[objectIndex].offset[0] *= 2.0; + gObjectList[objectIndex].offset[1] *= 2.5; + gObjectList[objectIndex].offset[2] *= 2.0; + object_calculate_new_pos_offset(objectIndex); + gObjectList[objectIndex].direction_angle[1] = + get_angle_between_two_vectors(gObjectList[objectIndex].unk_01C, gObjectList[objectIndex].pos); + break; + } + func_800873F4(objectIndex); +} + +void OSeagull::func_8008241C(s32 objectIndex, s32 arg1) { + s16 randZ; + s16 randX; + s16 randY; + + gObjectList[objectIndex].unk_0D8 = 1; + gObjectList[objectIndex].model = (Gfx*) d_course_koopa_troopa_beach_unk4; + gObjectList[objectIndex].vertex = (Vtx*) d_course_koopa_troopa_beach_unk_data5; + gObjectList[objectIndex].sizeScaling = 0.2f; + gObjectList[objectIndex].unk_0DD = 1; + randX = random_int(0x00C8) + -100.0; + randY = random_int(0x0014); + randZ = random_int(0x00C8) + -100.0; + + set_obj_origin_pos(objectIndex, (randX + _pos[0]) * xOrientation, randY + _pos[1], randZ + _pos[2]); + set_obj_direction_angle(objectIndex, 0U, 0U, 0U); + gObjectList[objectIndex].unk_034 = 1.0f; + func_80086EF0(objectIndex); + gObjectList[objectIndex].spline = D_800E633C[arg1 % 4]; + set_object_flag(objectIndex, 0x00000800); + object_next_state(objectIndex); +} + + +void OSeagull::func_80082714(s32 objectIndex, s32 arg1) { + switch (gObjectList[objectIndex].state) { + case 1: + OSeagull::func_8008241C(objectIndex, arg1); + break; + case 0: + default: + break; + } +}
\ No newline at end of file diff --git a/src/engine/objects/Seagull.h b/src/engine/objects/Seagull.h new file mode 100644 index 000000000..c6898dcc2 --- /dev/null +++ b/src/engine/objects/Seagull.h @@ -0,0 +1,47 @@ +#pragma once + +#include <libultraship.h> +#include <vector> +#include "Object.h" + +#include "engine/World.h" + +extern "C" { +#include "macros.h" +#include "main.h" +#include "vehicles.h" +#include "waypoints.h" +#include "common_structs.h" +#include "objects.h" +#include "camera.h" +} + +//! @todo unk_0D5 needs to be a struct variable probably. What does it do? Behaviour? +class OSeagull : public OObject { +public: + explicit OSeagull(Vec3f pos); + + ~OSeagull() { + _count--; + } + + static size_t GetCount() { + return _count; + } + + virtual void Tick() override; + virtual void Draw(s32 cameraId) override; + + void func_800552BC(s32 objectIndex); + + void func_8008275C(s32 objectIndex); + void func_8008241C(s32 objectIndex, s32 arg1); + void func_80082714(s32 objectIndex, s32 arg1); +private: + Vec3f _pos; + static size_t _count; + s32 _idx; + bool _toggle; + + SplineData *spline; +}; diff --git a/src/engine/objects/Snowman.cpp b/src/engine/objects/Snowman.cpp new file mode 100644 index 000000000..c05f464ef --- /dev/null +++ b/src/engine/objects/Snowman.cpp @@ -0,0 +1,343 @@ +#include "Snowman.h" +#include "World.h" + +extern "C" { +#include "render_objects.h" +#include "update_objects.h" +#include "assets/frappe_snowland_data.h" +#include "assets/common_data.h" +#include "math_util.h" +#include "math_util_2.h" +#include "code_80086E70.h" +#include "code_80057C60.h" +} + +static const char* sSnowmanHeadList[] = { d_course_frappe_snowland_snowman_head }; + +size_t OSnowman::_count = 0; + +OSnowman::OSnowman(const FVector& pos) { + _idx = _count; + _pos = pos; + + s32 objectId = indexObjectList2[_idx]; + init_object(objectId, 0); + gObjectList[objectId].origin_pos[0] = pos.x * xOrientation; + gObjectList[objectId].origin_pos[1] = pos.y + 5.0 + 3.0; + gObjectList[objectId].origin_pos[2] = pos.z; + gObjectList[objectId].pos[0] = pos.x * xOrientation; + gObjectList[objectId].pos[1] = pos.y + 5.0 + 3.0; + gObjectList[objectId].pos[2] = pos.z; + + objectId = indexObjectList1[_idx]; + init_object(objectId, 0); + gObjectList[objectId].origin_pos[0] = pos.x * xOrientation; + gObjectList[objectId].origin_pos[1] = pos.y + 3.0; + gObjectList[objectId].origin_pos[2] = pos.z; + gObjectList[objectId].unk_0D5 = 0; // Section Id no longer used. + + gObjectList[objectId].pos[0] = pos.x * xOrientation; + gObjectList[objectId].pos[1] = pos.y + 3.0; + gObjectList[objectId].pos[2] = pos.z; + + _count++; +} + +void OSnowman::Tick() { + s32 var_s0; + s32 var_s3; + s32 var_s4; + s32 objectIndex; + Object* object; + + //! @todo quick hack to add the snow particles on hit. Need to separate into its own class + if (_idx == 0) { + for (var_s0 = 0; var_s0 < gObjectParticle2_SIZE; var_s0++) { + objectIndex = gObjectParticle2[var_s0]; + + if (objectIndex == DELETED_OBJECT_ID) { + continue; + } + + if (gObjectList[objectIndex].state == 0) { + continue; + } + func_8008379C(objectIndex); + if (gObjectList[objectIndex].state != 0) { + continue; + } + delete_object_wrapper(&gObjectParticle2[var_s0]); + if (var_s0) {} // ?? + } + } + + //for (var_s0 = 0; var_s0 < NUM_SNOWMEN; var_s0++) { + var_s4 = indexObjectList1[_idx]; + var_s3 = indexObjectList2[_idx]; + OSnowman::func_80083A94(var_s3); // snowman head + OSnowman::func_80083C04(var_s4); // snowman body + if (is_obj_index_flag_status_inactive(var_s4, 0x00001000) != 0) { + object = &gObjectList[var_s4]; + if ((are_players_in_course_section(object->unk_0D5 - 1, object->unk_0D5 + 1) != 0) && (func_80089B50(var_s4) != 0)) { + set_object_flag(var_s4, 0x00001000); + clear_object_flag(var_s4, 0x00000010); + func_800726CC(var_s4, 0x0000000A); + func_8008701C(var_s3, 0x0000000A); + OSnowman::func_800836F0(object->pos); + } + } else if (func_80072320(var_s4, 2) != 0) { + func_800722CC(var_s4, 2); + func_8008701C(var_s3, 0x00000014); + } + //} +} + +void OSnowman::Draw(s32 cameraId) { + OSnowman::DrawHead(cameraId); + OSnowman::DrawBody(cameraId); +} + +void OSnowman::func_800836F0(Vec3f pos) { + s32 objectIndex; + s32 i; + + for (i = 0; i < D_8018D3BC; i++) { + objectIndex = add_unused_obj_index(&gObjectParticle2[0], &gNextFreeObjectParticle2, gObjectParticle2_SIZE); + if (objectIndex == NULL_OBJECT_ID) { + break; + } + OSnowman::func_80083538(objectIndex, pos, i, D_8018D3BC); + } +} + +void OSnowman::DrawHead(s32 cameraId) { + s32 objectIndex; + Camera* camera = &camera1[cameraId]; + objectIndex = indexObjectList1[_idx]; + if (gObjectList[objectIndex].state >= 2) { + func_8008A364(objectIndex, cameraId, 0x2AABU, 0x00000258); + if (is_obj_flag_status_active(objectIndex, VISIBLE) != 0) { + D_80183E80[0] = (s16) gObjectList[objectIndex].orientation[0]; + D_80183E80[1] = + func_800418AC(gObjectList[objectIndex].pos[0], gObjectList[objectIndex].pos[2], camera->pos); + D_80183E80[2] = (u16) gObjectList[objectIndex].orientation[2]; + if (is_obj_flag_status_active(objectIndex, 0x00000010) != 0) { + draw_2d_texture_at(gObjectList[objectIndex].pos, (u16*) D_80183E80, + gObjectList[objectIndex].sizeScaling, (u8*) gObjectList[objectIndex].activeTLUT, + (u8*)gObjectList[objectIndex].activeTexture, gObjectList[objectIndex].vertex, + 0x00000040, 0x00000040, 0x00000040, 0x00000020); + } + objectIndex = indexObjectList2[_idx]; + D_80183E80[0] = (s16) gObjectList[objectIndex].orientation[0]; + D_80183E80[2] = (u16) gObjectList[objectIndex].orientation[2]; + draw_2d_texture_at(gObjectList[objectIndex].pos, (u16*) D_80183E80, + gObjectList[objectIndex].sizeScaling, (u8*) gObjectList[objectIndex].activeTLUT, + (u8*)gObjectList[objectIndex].activeTexture, gObjectList[objectIndex].vertex, 0x00000040, + 0x00000040, 0x00000040, 0x00000020); + } + } +} + +void OSnowman::DrawBody(s32 cameraId) { + UNUSED s32 stackPadding[2]; + Camera* sp44; + s32 someIndex; + s32 objectIndex; + Object* object; + + sp44 = &camera1[cameraId]; + load_texture_and_tlut((u8*)d_course_frappe_snowland_snow_tlut, (u8*)d_course_frappe_snowland_snow, 0x00000020, 0x00000020); + + //! @todo quick hack to add the snow particles on hit. Need to separate into its own class + if (_idx == 0) { + for (someIndex = 0; someIndex < gObjectParticle2_SIZE; someIndex++) { + objectIndex = gObjectParticle2[someIndex]; + if (objectIndex != NULL_OBJECT_ID) { + object = &gObjectList[objectIndex]; + if (object->state > 0) { + func_8008A364(objectIndex, cameraId, 0x2AABU, 0x000001F4); + if (is_obj_flag_status_active(objectIndex, VISIBLE) != 0) { + object->orientation[1] = func_800418AC(object->pos[0], object->pos[2], sp44->pos); + rsp_set_matrix_gObjectList(objectIndex); + gSPDisplayList(gDisplayListHead++, (Gfx*)D_0D0069E0); + } + } + } + } + } + gSPTexture(gDisplayListHead++, 1, 1, 0, G_TX_RENDERTILE, G_OFF); +} + +void OSnowman::func_80083C04(s32 objectIndex) { + Object* object; + + object = &gObjectList[objectIndex]; + switch (object->state) { + case 0: + break; + case 1: + OSnowman::func_80083B0C(objectIndex); + break; + case 2: + set_and_run_timer_object(objectIndex, 0x00000096); + break; + case 10: + if (set_and_run_timer_object(objectIndex, 0x0000012C) != 0) { + func_800722A4(objectIndex, 2); + } + break; + case 11: + if (set_and_run_timer_object(objectIndex, 0x0000000A) != 0) { + set_object_flag(objectIndex, 0x00000010); + object->sizeScaling = 0.001f; + } + break; + case 12: + if (func_80074118(objectIndex, &object->sizeScaling, 0.001f, 0.1f, 0.0025f, 0, 0) != 0) { + object_next_state(objectIndex); + } + break; + case 13: + func_800726CC(objectIndex, 2); + clear_object_flag(objectIndex, 0x00001000); + break; + } + if (object->state >= 2) { + func_80073514(objectIndex); + } + OSnowman::func_80083BE4(objectIndex); +} + +void OSnowman::func_80083BE4(s32 objectIndex) { + object_calculate_new_pos_offset(objectIndex); +} + +void OSnowman::func_80083868(s32 objectIndex) { + Object* object; + Vtx* vtx = (Vtx*) LOAD_ASSET_RAW(D_0D0061B0); + init_texture_object(objectIndex, (u8*)d_course_frappe_snowland_snowman_tlut, (const char**)sSnowmanHeadList, 0x40U, (u16) 0x00000040); + object = &gObjectList[objectIndex]; + object->vertex = vtx; + object->sizeScaling = 0.1f; + object->textureListIndex = 0; + object_next_state(objectIndex); + set_obj_origin_offset(objectIndex, 0.0f, 0.0f, 0.0f); + object->orientation[0] = 0; + object->orientation[1] = 0; + object->orientation[2] = 0x8000; + object->primAlpha = random_int(0x2000U) - 0x1000; + func_80086E70(objectIndex); + object->unk_034 = 1.5f; + set_object_flag(objectIndex, 0x00000200); +} + +void OSnowman::func_80083948(s32 objectIndex) { + switch (gObjectList[objectIndex].unk_0AE) { + case 1: + func_80086FD4(objectIndex); + break; + case 2: + func_800871AC(objectIndex, 0x00000014); + break; + case 3: + func_8008701C(objectIndex, 1); + break; + case 10: + func_80087C48(objectIndex, 10.0f, 0.5f, 0x0000000A); + break; + case 11: + func_80087D24(objectIndex, 0.0f, 0.2f, -7.0f); + break; + case 20: + if (f32_step_up_towards(&gObjectList[objectIndex].offset[1], 0.0f, 0.2f) != 0) { + func_80073800(objectIndex, 0); + func_8008701C(objectIndex, 1); + } + break; + case 0: + default: + break; + } + object_calculate_new_pos_offset(objectIndex); + OSnowman::func_80073D0C(objectIndex, &gObjectList[objectIndex].primAlpha, -0x00001000, 0x00001000, 0x00000400, 1, -1); + gObjectList[objectIndex].orientation[2] = gObjectList[objectIndex].primAlpha + 0x8000; +} + +bool OSnowman::func_80073D0C(s32 objectIndex, s16* arg1, s32 arg2, s32 arg3, s32 arg4, s32 arg5, s32 arg6) { + return func_80073B78(0, objectIndex, arg1, arg2, arg3, arg4, arg5, arg6); +} + +void OSnowman::func_80083A94(s32 objectIndex) { + switch (gObjectList[objectIndex].state) { + case 0: + break; + case 1: + OSnowman::func_80083868(objectIndex); + break; + } + if (gObjectList[objectIndex].state >= 2) { + func_80073514(objectIndex); + } + OSnowman::func_80083948(objectIndex); +} + +static const char* sSnowmanBodyList[] = { d_course_frappe_snowland_snowman_body }; + +void OSnowman::func_80083B0C(s32 objectIndex) { + Vtx* vtx = (Vtx*) LOAD_ASSET_RAW(common_vtx_hedgehog); + init_texture_object(objectIndex, (u8*)d_course_frappe_snowland_snowman_tlut, (const char**)sSnowmanBodyList, 0x40U, (u16) 0x00000040); + gObjectList[objectIndex].vertex = vtx; + gObjectList[objectIndex].sizeScaling = 0.1f; + gObjectList[objectIndex].textureListIndex = 0; + object_next_state(objectIndex); + set_obj_origin_offset(objectIndex, 0.0f, 0.0f, 0.0f); + gObjectList[objectIndex].orientation[0] = 0; + gObjectList[objectIndex].orientation[1] = 0; + gObjectList[objectIndex].orientation[2] = 0x8000; + gObjectList[objectIndex].boundingBoxSize = 2; + gObjectList[objectIndex].unk_034 = 1.5f; + set_object_flag(objectIndex, 0x04000210); +} + + +void OSnowman::func_80083538(s32 objectIndex, Vec3f arg1, s32 arg2, s32 arg3) { + Object* object; + + init_object(objectIndex, 0); + object = &gObjectList[objectIndex]; + object->activeTexture = (const char*)d_course_frappe_snowland_snow; + object->textureList = (const char**)d_course_frappe_snowland_snow; + object->activeTLUT = d_course_frappe_snowland_snow_tlut; + object->tlutList = (u8*)d_course_frappe_snowland_snow_tlut; + object->sizeScaling = random_int(0x0064U); + object->sizeScaling = (object->sizeScaling * 0.001) + 0.05; + object->velocity[1] = random_int(0x0014U); + object->velocity[1] = (object->velocity[1] * 0.5) + 2.6; + object->unk_034 = random_int(0x000AU); + object->unk_034 = (object->unk_034 * 0.1) + 4.5; + object->direction_angle[1] = (arg2 << 0x10) / arg3; + object->origin_pos[0] = arg1[0]; + object->origin_pos[1] = arg1[1]; + object->origin_pos[2] = arg1[2]; + object->primAlpha = random_int(0x4000U) + 0x1000; +} + +void OSnowman::func_8008379C(s32 objectIndex) { + switch (gObjectList[objectIndex].state) { + case 0: + break; + case 1: + if (func_80087E08(objectIndex, gObjectList[objectIndex].velocity[1], 0.74f, + gObjectList[objectIndex].unk_034, gObjectList[objectIndex].direction_angle[1], + 0x00000064) != 0) { + object_next_state(objectIndex); + } + break; + case 2: + func_80086F60(objectIndex); + func_80072428(objectIndex); + break; + } + object_calculate_new_pos_offset(objectIndex); + gObjectList[objectIndex].orientation[2] += gObjectList[objectIndex].primAlpha; +} diff --git a/src/engine/objects/Snowman.h b/src/engine/objects/Snowman.h new file mode 100644 index 000000000..e3e2a0138 --- /dev/null +++ b/src/engine/objects/Snowman.h @@ -0,0 +1,54 @@ +#pragma once + +#include <libultraship.h> +#include <vector> +#include "Object.h" + +#include "World.h" + +extern "C" { +#include "macros.h" +#include "main.h" +#include "vehicles.h" +#include "waypoints.h" +#include "common_structs.h" +#include "objects.h" +#include "camera.h" +#include "some_data.h" +} + +class OSnowman : public OObject { +public: + explicit OSnowman(const FVector& pos); + + ~OSnowman() { + _count--; + } + + static size_t GetCount() { + return _count; + } + + virtual void Tick() override; + virtual void Draw(s32 cameraId) override; + + void DrawHead(s32); + void DrawBody(s32); + + void func_80083868(s32); + void func_80083948(s32); + void func_80083A94(s32); + void func_80083B0C(s32); + void func_80083C04(s32); + void func_80083BE4(s32); + void func_800836F0(Vec3f); + bool func_80073D0C(s32 objectIndex, s16* arg1, s32 arg2, s32 arg3, s32 arg4, s32 arg5, s32 arg6); + void func_80083538(s32 objectIndex, Vec3f arg1, s32 arg2, s32 arg3); + void func_8008379C(s32 objectIndex); + + +private: + FVector _pos; + static size_t _count; + size_t _idx; +}; diff --git a/src/engine/objects/Thwomp.cpp b/src/engine/objects/Thwomp.cpp new file mode 100644 index 000000000..afa12b03b --- /dev/null +++ b/src/engine/objects/Thwomp.cpp @@ -0,0 +1,1450 @@ +#include <libultraship.h> +#include <libultra/gbi.h> +#include "Thwomp.h" +#include <vector> + +#include "port/Game.h" + +extern "C" { +#include "macros.h" +#include "main.h" +#include "actors.h" +#include "math_util.h" +#include "sounds.h" +#include "update_objects.h" +#include "render_player.h" +#include "external.h" +#include "bomb_kart.h" +#include "collision.h" +#include "code_80086E70.h" +#include "render_objects.h" +#include "code_80057C60.h" +#include "defines.h" +#include "code_80005FD0.h" +#include "math_util_2.h" +#include "collision.h" +#include "assets/bowsers_castle_data.h" +#include "ceremony_and_credits.h" +#include "objects.h" +#include "update_objects.h" +#include "render_objects.h" +#include "some_data.h" +extern s8 gPlayerCount; +} + +f32 D_800E594C[][2] = { + { -8.0, 8.0 }, + { 8.0, 8.0 }, + { 0.0, 0.0 }, + { 8.0, -8.0 }, + { -8.0, -8.0 }, + // This feels super fake, but it matches + { -0.0, 0.0 }, +}; + +s16 D_800E597C[] = { 0x0000, 0x0000, 0x4000, 0x8000, 0x8000, 0xc000 }; + +size_t OThwomp::_count = 0; + +OThwomp::OThwomp(s16 x, s16 z, s16 direction, f32 scale, s16 behaviour, s16 primAlpha, u16 boundingBoxSize) { + _idx = _count; + _faceDirection = direction; + _boundingBoxSize = boundingBoxSize; + State = (States)behaviour; + + if (_idx >= 32) { + printf("MAX THWOMPS REACHED (32), skipping\n"); + return; + } + s32 objectId = indexObjectList1[_idx]; + init_object(objectId, 0); + gObjectList[objectId].origin_pos[0] = x * xOrientation; + gObjectList[objectId].origin_pos[2] = z; + gObjectList[objectId].unk_0D5 = behaviour; + gObjectList[objectId].primAlpha = primAlpha; + gObjectList[objectId].boundingBoxSize = boundingBoxSize + 5; + + if (scale == 0.0f) { + scale = 1.0f; + } + + gObjectList[objectId].sizeScaling = scale; + + _count++; +} + +void OThwomp::Tick60fps() { // func_80081210 + Player* player; + s32 objectIndex; + s32 var_s2_3; + s32 var_s4; + + objectIndex = indexObjectList1[_idx]; + func_800722CC(objectIndex, 0x00000010); + OThwomp::SetVisibility(objectIndex); + + OThwomp::func_8007F8D8(); + + // Tick lights only one time. This gets applied to every thwomp. + // Running this more than once per frame will result in the lights moving at a high rate of speed. + if (_idx == 0) { + D_80165834[0] += 0x100; + D_80165834[1] += 0x200; + } + + objectIndex = indexObjectList1[_idx]; + if (gObjectList[objectIndex].state != 0) { + switch (State) { + case STATIONARY: + OThwomp::StationaryBehaviour(objectIndex); + break; + case MOVE_AND_ROTATE: + OThwomp::MoveAndRotateBehaviour(objectIndex); + break; + case MOVE_FAR: + OThwomp::MoveFarBehaviour(objectIndex); + break; + case STATIONARY_FAST: + OThwomp::StationaryFastBehaviour(objectIndex); + break; + case JAILED: + OThwomp::JailedBehaviour(objectIndex); + break; + case SLIDE: + OThwomp::SlidingBehaviour(objectIndex); + break; + } + } + + player = gPlayerOne; + for (var_s4 = 0; var_s4 < NUM_PLAYERS; var_s4++, player++) { + player->tyres[FRONT_LEFT].unk_14 &= ~3; + player->unk_046 &= ~0x0006; + + objectIndex = indexObjectList1[_idx]; + if (!(player->effects & BOO_EFFECT)) { + OThwomp::func_80080B28(objectIndex, var_s4); + } + if (is_obj_flag_status_active(objectIndex, 0x00020000) != 0) { + OThwomp::SetPlayerCrushedEffect(objectIndex, player); + } + if (is_obj_flag_status_active(objectIndex, 0x00010000) != 0) { + OThwomp::func_80080A4C(objectIndex, var_s4); + } + + } + OThwomp::func_8007542C(3); + + objectIndex = indexObjectList1[_idx]; + if (func_80072320(objectIndex, 0x00000020) == 0) { + return; + } + + func_800722CC(objectIndex, 0x00000020); + OThwomp::AddParticles(objectIndex); + + for (var_s4 = 0; var_s4 < gObjectParticle2_SIZE; var_s4++) { + objectIndex = gObjectParticle2[var_s4]; + if (objectIndex == DELETED_OBJECT_ID) { + return; + } + if (gObjectList[objectIndex].state == 0) { + return; + } + OThwomp::func_800810F4(objectIndex); + if (gObjectList[objectIndex].state != 0) { + return; + } + delete_object_wrapper(&gObjectParticle2[var_s4]); + } +} + +void OThwomp::func_800810F4(s32 objectIndex) { + switch (gObjectList[objectIndex].state) { /* irregular */ + case 0: + break; + case 1: + OThwomp::func_80081080(objectIndex); + break; + case 2: + object_add_velocity_offset_xz(objectIndex); + f32_step_up_towards(&gObjectList[objectIndex].offset[1], 14.0f, 0.5f); + func_8007415C(objectIndex, &gObjectList[objectIndex].sizeScaling, 0.25f, 0.75f, 0.025f, 1, 0); + if (func_80073B00(objectIndex, &gObjectList[objectIndex].primAlpha, 0x000000FF, 0, 4, 0, 0) != 0) { + object_next_state(objectIndex); + } + break; + case 3: + func_80072428(objectIndex); + break; + } + object_calculate_new_pos_offset(objectIndex); +} + +void OThwomp::func_80081080(s32 objectIndex) { + Object* object; + + object = &gObjectList[objectIndex]; + object->activeTexture = (const char*)D_8018D490; + object->textureList = (const char**)D_8018D490; + object->primAlpha = 0x00FF; + object->direction_angle[1] = 0; + object->orientation[0] = 0; + object->orientation[2] = 0; + object->offset[0] = 0.0f; + object->offset[1] = 0.0f; + object->offset[2] = 0.0f; + object->sizeScaling = 0.25f; + object_next_state(objectIndex); +} + +void OThwomp::AddParticles(s32 arg0) { + s32 objectIndex; + s32 i; + + for (i = 0; i < 6; i++) { + objectIndex = add_unused_obj_index(gObjectParticle2, &gNextFreeObjectParticle2, gObjectParticle2_SIZE); + if (objectIndex == NULL_OBJECT_ID) { + break; + } + OThwomp::func_80080E8C(objectIndex, arg0, i); + } +} + +#ifdef NON_MATCHING +// https://decomp.me/scratch/YMJDJ +// No idea what the problem is +void OThwomp::func_80080E8C(s32 objectIndex1, s32 objectIndex2, s32 arg2) { + u16 anAngle; + f32 thing0; + f32 thing1; + f32* temp_v1; + + init_object(objectIndex1, arg2); + temp_v1 = D_800E594C[arg2]; + gObjectList[objectIndex1].unk_0D5 = 2; + anAngle = gObjectList[objectIndex2].direction_angle[1]; + thing1 = func_800416D8(temp_v1[1], temp_v1[0], anAngle); + thing0 = func_80041724(temp_v1[1], temp_v1[0], anAngle); + gObjectList[objectIndex1].origin_pos[0] = gObjectList[objectIndex2].pos[0] + thing0; + gObjectList[objectIndex1].origin_pos[1] = gObjectList[objectIndex2].surfaceHeight - 9.0; + gObjectList[objectIndex1].origin_pos[2] = gObjectList[objectIndex2].pos[2] + thing1; + anAngle = D_800E597C[arg2] + gObjectList[objectIndex2].direction_angle[1]; + gObjectList[objectIndex1].velocity[0] = sins(anAngle) * 0.6; + gObjectList[objectIndex1].velocity[2] = coss(anAngle) * 0.6; +} +#else +GLOBAL_ASM("asm/non_matchings/update_objects/func_80080E8C.s") +#endif + +void OThwomp::SetPlayerCrushedEffect(s32 objectIndex, Player* player) { + if (is_within_horizontal_distance_of_player(objectIndex, player, 12.0f) != 0) { + player->tyres[FRONT_LEFT].unk_14 |= 3; + } +} + +void OThwomp::func_80080A4C(s32 objectIndex, s32 cameraPlayerId) { + Camera* camera = &camera1[cameraPlayerId]; + Player* player = &gPlayerOne[cameraPlayerId]; + + if (gScreenModeSelection != SCREEN_MODE_3P_4P_SPLITSCREEN) { + if ((func_80072320(objectIndex, 0x00000010) != 0) && + (is_within_horizontal_distance_of_player(objectIndex, player, 500.0f) != false)) { + func_8001CA10(camera); + func_800C98B8(gObjectList[objectIndex].pos, gObjectList[objectIndex].velocity, + SOUND_ARG_LOAD(0x19, 0x00, 0x80, 0x0F)); + } + } +} + +void OThwomp::func_8007542C(s32 arg0) { + s32 objectIndex; + s32 var_s2; + s32* var_s3; + Object* object; + + D_8016582C[0] += 0x2000; + D_8016582C[1] += 0x1000; + D_8016582C[2] += 0x1800; + for (var_s2 = 0; var_s2 < 0x80; var_s2++) { + switch (arg0) { /* irregular */ + case 1: + var_s3 = gObjectParticle1; + break; + case 2: + var_s3 = gObjectParticle2; + break; + case 3: + var_s3 = gObjectParticle3; + break; + } + objectIndex = var_s3[var_s2]; + if (objectIndex != DELETED_OBJECT_ID) { + object = &gObjectList[objectIndex]; + if (object->state != 0) { + OThwomp::func_80074FD8(objectIndex); + if (object->state == 0) { + delete_object_wrapper(&var_s3[var_s2]); + } + } + } + } +} + +void OThwomp::func_80074FD8(s32 objectIndex) { + switch (gObjectList[objectIndex].state) { /* irregular */ + case 0: + break; + case 1: + if (func_80087E08(objectIndex, gObjectList[objectIndex].velocity[1], 0.12f, + gObjectList[objectIndex].unk_034, gObjectList[objectIndex].direction_angle[1], + 0x00000064) != 0) { + object_next_state(objectIndex); + } + object_calculate_new_pos_offset(objectIndex); + gObjectList[objectIndex].orientation[0] += D_8016582C[0]; + gObjectList[objectIndex].orientation[1] += D_8016582C[1]; + gObjectList[objectIndex].orientation[2] += D_8016582C[2]; + break; + case 2: + func_80086F60(objectIndex); + func_80072428(objectIndex); + break; + } +} + +void OThwomp::SetVisibility(s32 objectIndex) { // func_8008A4CC + s32 loopIndex; + Camera* camera; + + clear_object_flag(objectIndex, 0x00070000); + for (loopIndex = 0, camera = camera1; loopIndex < gPlayerCountSelection1; loopIndex++, camera++) { + if (gObjectList[objectIndex].state != 0) { + if ((D_8018CF68[loopIndex] >= (gObjectList[objectIndex].unk_0DF - 1)) && + ((gObjectList[objectIndex].unk_0DF + 1) >= D_8018CF68[loopIndex])) { + set_object_flag(objectIndex, 0x00010000); + if (D_8018CF68[loopIndex] == gObjectList[objectIndex].unk_0DF) { + set_object_flag(objectIndex, 0x00020000); + } + if (is_object_visible_on_camera(objectIndex, camera, 0x2AABU) != 0) { + set_object_flag(objectIndex, VISIBLE); + } + } + } + } +} + +void OThwomp::func_8007F8D8() { + Player* player; + s32 objectIndex; + s32 var_s0; + s32 someIndex; + s32 var_s4; + Object* object; + + player = gPlayerOne; + var_s4 = 1; + + objectIndex = indexObjectList1[_idx]; + object = &gObjectList[objectIndex]; + if (object->unk_0D5 == 3) { + var_s0 = 0; + if ((object->state >= 2) && (func_80072354(objectIndex, 8) != 0)) { + var_s0 = 1; + } + var_s4 *= var_s0; + } + + if (var_s4 != 0) { + for (var_s0 = 0; var_s0 < 4; var_s0++, player++) { + if ((player->type & PLAYER_EXISTS) && !(player->type & PLAYER_KART_AI)) { + if (OThwomp::func_8007F75C(var_s0) != 0) { + break; + } + } + } + } +} + +s32 OThwomp::func_8007F75C(s32 playerId) { + s32 someIndex; + s32 objectIndex; + s32 temp_s7; + s32 var_s6; + s32 waypoint; + + waypoint = gNearestWaypointByPlayerId[playerId]; + var_s6 = 0; + if ((waypoint >= 0xAA) && (waypoint < 0xB5)) { + temp_s7 = random_int(0x0032U) + 0x32; + objectIndex = indexObjectList1[_idx]; + if (gObjectList[objectIndex].unk_0D5 == 3) { + var_s6 = 1; + OThwomp::func_8007F660(objectIndex, playerId, temp_s7); + } + + } else if ((waypoint >= 0xD7) && (waypoint < 0xE2)) { + objectIndex = indexObjectList1[_idx]; + if (gObjectList[objectIndex].unk_0D5 == 3) { + var_s6 = 1; + OThwomp::func_8007F6C4(objectIndex, playerId); + } + } + return var_s6; +} + +s32 OThwomp::func_8007E50C(s32 objectIndex, Player* player, Camera* camera) { + s32 sp24; + + sp24 = 0; + if ((func_80072354(objectIndex, 4) != 0) && + (is_within_horizontal_distance_of_player(objectIndex, player, 300.0f) != 0) && + (func_8008A0B4(objectIndex, player, camera, 0x4000U) != 0) && + (func_8008A060(objectIndex, camera, 0x1555U) != 0)) { + func_800722A4(objectIndex, 4); + sp24 = 1; + } + return sp24; +} + +s32 OThwomp::func_8007E59C(s32 objectIndex) { + Camera* camera; + Player* player; + s32 temp_v0; + s32 someIndex; + + temp_v0 = 0; + player = gPlayerOne; + camera = camera1; + for (someIndex = 0; someIndex < gPlayerCountSelection1; someIndex++) { + temp_v0 = OThwomp::func_8007E50C(objectIndex, player++, camera++); + if (temp_v0 != 0) { + break; + } + } + return temp_v0; +} + +void OThwomp::func_8007F544(s32 objectIndex) { + switch (gObjectList[objectIndex].unk_0DD) { /* irregular */ + case 1: + OThwomp::func_8007EFBC(objectIndex); + break; + case 2: + OThwomp::func_8007F280(objectIndex); + break; + } +} + +void OThwomp::func_8007EFBC(s32 objectIndex) { + switch (gObjectList[objectIndex].unk_0AE) { + case 1: + if (func_80073E18(objectIndex, (u16*) &gObjectList[objectIndex].orientation[1], 0x0800U, 0x00008000) != 0) { + gObjectList[objectIndex].unk_01C[0] = (f32) ((f64) xOrientation * 200.0); + func_80086FD4(objectIndex); + } + break; + case 2: + if (f32_step_towards(gObjectList[objectIndex].offset, gObjectList[objectIndex].unk_01C[0], 4.0f) != 0) { + func_80086FD4(objectIndex); + } + break; + case 3: + if (func_80073E18(objectIndex, (u16*) &gObjectList[objectIndex].orientation[1], 0x0400U, 0x00008000) != 0) { + func_800726CC(objectIndex, 3); + func_80086FD4(objectIndex); + } + break; + case 5: + if (func_80073E18(objectIndex, (u16*) &gObjectList[objectIndex].orientation[1], 0x0400U, 0x0000C000) != 0) { + func_80086FD4(objectIndex); + } + break; + case 6: + if (f32_step_down_towards(&gObjectList[objectIndex].offset[2], -100.0f, 2.0f) != 0) { + func_80086FD4(objectIndex); + } + break; + case 7: + if (func_80073E18(objectIndex, (u16*) &gObjectList[objectIndex].orientation[1], 0x0400U, 0x00004000) != 0) { + func_80086FD4(objectIndex); + func_800726CC(objectIndex, 3); + } + break; + case 9: + if (func_80073E18(objectIndex, (u16*) &gObjectList[objectIndex].orientation[1], 0x0400U, 0x00010000) != 0) { + func_80086FD4(objectIndex); + } + break; + case 10: + if (f32_step_towards(gObjectList[objectIndex].offset, 0.0f, 4.0f) != 0) { + func_80086FD4(objectIndex); + } + break; + case 11: + if (func_80073E18(objectIndex, (u16*) &gObjectList[objectIndex].orientation[1], 0x0400U, 0x00010000) != 0) { + func_80086FD4(objectIndex); + func_800726CC(objectIndex, 3); + } + break; + case 13: + if (func_80073E18(objectIndex, (u16*) &gObjectList[objectIndex].orientation[1], 0x0400U, 0x00014000) != 0) { + func_80086FD4(objectIndex); + } + break; + case 14: + if (f32_step_up_towards(&gObjectList[objectIndex].offset[2], 0.0f, 2.0f) != 0) { + func_80086FD4(objectIndex); + } + break; + case 15: + if (func_80073E18(objectIndex, (u16*) &gObjectList[objectIndex].orientation[1], 0x0400U, 0x0000C000) != 0) { + func_80086FD4(objectIndex); + func_800726CC(objectIndex, 3); + } + break; + case 17: + func_8008701C(objectIndex, 1); + break; + case 0: + default: + break; + } +} + +void OThwomp::func_8007F280(s32 objectIndex) { + switch (gObjectList[objectIndex].unk_0AE) { + case 1: + if (func_80073E18(objectIndex, (u16*) &gObjectList[objectIndex].orientation[1], 0x0400U, 0x00010000) != 0) { + gObjectList[objectIndex].unk_01C[0] = (f32) ((f64) xOrientation * -200.0); + func_80086FD4(objectIndex); + } + break; + case 2: + if (f32_step_towards(gObjectList[objectIndex].offset, gObjectList[objectIndex].unk_01C[0], 4.0f) != 0) { + func_80086FD4(objectIndex); + } + break; + case 3: + if (func_80073E18(objectIndex, (u16*) &gObjectList[objectIndex].orientation[1], 0x0400U, 0x00010000) != 0) { + func_800726CC(objectIndex, 3); + func_80086FD4(objectIndex); + } + break; + case 5: + if (func_80073E18(objectIndex, (u16*) &gObjectList[objectIndex].orientation[1], 0x0400U, 0x00004000) != 0) { + func_80086FD4(objectIndex); + } + break; + case 6: + if (f32_step_up_towards(&gObjectList[objectIndex].offset[2], 100.0f, 2.0f) != 0) { + func_80086FD4(objectIndex); + } + break; + case 7: + if (func_80073E18(objectIndex, (u16*) &gObjectList[objectIndex].orientation[1], 0x0400U, 0x0000C000) != 0) { + func_80086FD4(objectIndex); + func_800726CC(objectIndex, 3); + } + break; + case 9: + if (func_80073E18(objectIndex, (u16*) &gObjectList[objectIndex].orientation[1], 0x0400U, 0x00008000) != 0) { + func_80086FD4(objectIndex); + } + break; + case 10: + if (f32_step_towards(gObjectList[objectIndex].offset, 0.0f, 4.0f) != 0) { + func_80086FD4(objectIndex); + } + break; + case 11: + if (func_80073E18(objectIndex, (u16*) &gObjectList[objectIndex].orientation[1], 0x0400U, 0x00008000) != 0) { + func_80086FD4(objectIndex); + func_800726CC(objectIndex, 3); + } + break; + case 13: + if (func_80073E18(objectIndex, (u16*) &gObjectList[objectIndex].orientation[1], 0x0400U, 0x0000C000) != 0) { + func_80086FD4(objectIndex); + } + break; + case 14: + if (f32_step_down_towards(&gObjectList[objectIndex].offset[2], 0.0f, 2.0f) != 0) { + func_80086FD4(objectIndex); + } + break; + case 15: + if (func_80073E18(objectIndex, (u16*) &gObjectList[objectIndex].orientation[1], 0x0400U, 0x00014000) != 0) { + func_80086FD4(objectIndex); + func_800726CC(objectIndex, 3); + } + break; + case 17: + func_8008701C(objectIndex, 1); + break; + case 0: + default: + break; + } +} + +void OThwomp::func_8007F660(s32 objectIndex, s32 arg1, s32 arg2) { + Object* object; + + func_800722A4(objectIndex, 8); + func_80086E70(objectIndex); + object = &gObjectList[objectIndex]; + object->unk_0DD = 1; + object->unk_0D1 = arg1; + object->unk_048 = arg2; +} + +void OThwomp::func_8007F6C4(s32 objectIndex, s32 playerId) { + Player* player; + + player = &gPlayerOne[playerId]; + func_800722A4(objectIndex, 8); + func_80086E70(objectIndex); + gObjectList[objectIndex].unk_0DD = 2; + gObjectList[objectIndex].unk_01C[0] = player->pos[0] - gObjectList[objectIndex].origin_pos[0]; + gObjectList[objectIndex].unk_0D1 = playerId; +} + +void OThwomp::func_80080B28(s32 objectIndex, s32 playerId) { + f32 temp_f0; + Player* temp_s0; + + temp_s0 = &gPlayerOne[playerId]; + if (is_obj_flag_status_active(objectIndex, 0x00000200) != 0) { + if (!(temp_s0->soundEffects & 0x100)) { + temp_f0 = func_80088F54(objectIndex, temp_s0); + if ((temp_f0 <= 9.0) && !(temp_s0->effects & 0x04000000) && + (has_collided_horizontally_with_player(objectIndex, temp_s0) != 0)) { + if ((temp_s0->type & 0x8000) && !(temp_s0->type & 0x100)) { + if (!(temp_s0->effects & 0x200)) { + func_80089474(objectIndex, playerId, 1.4f, 1.1f, SOUND_ARG_LOAD(0x19, 0x00, 0xA0, 0x4C)); + } else if (func_80072354(objectIndex, 0x00000040) != 0) { + if (temp_s0->type & 0x1000) { + func_800C98B8(temp_s0->pos, temp_s0->velocity, SOUND_ARG_LOAD(0x19, 0x01, 0xA2, 0x4A)); + } else { + func_800C9060((u8) playerId, SOUND_ARG_LOAD(0x19, 0x01, 0xA2, 0x4A)); + } + OThwomp::func_80080DE4(objectIndex); + func_80075304(gObjectList[objectIndex].pos, 3, 3, D_8018D3C4); + clear_object_flag(objectIndex, 0x00000200); + func_800722A4(objectIndex, 0x00000040); + func_80086F60(objectIndex); + func_800726CC(objectIndex, 0x000000C8); + } + } + } else if ((temp_f0 <= 17.5) && (func_80072320(objectIndex, 1) != 0) && + (is_within_horizontal_distance_of_player(objectIndex, temp_s0, (temp_s0->unk_094 * 0.5) + _boundingBoxSize) != + 0)) { + if ((temp_s0->type & 0x8000) && !(temp_s0->type & 0x100)) { + if (is_obj_flag_status_active(objectIndex, 0x04000000) != 0) { + func_80072180(); + } + func_800722A4(objectIndex, 2); + temp_s0->unk_040 = (s16) objectIndex; + temp_s0->unk_046 |= 2; + temp_s0->soundEffects |= 0x100; + func_80088FF0(temp_s0); + } + } + } else { + func_80088FF0(temp_s0); + } + } +} + +Lights1 D_800E4638 = gdSPDefLights1(85, 85, 85, 255, 255, 255, 0, -120, 0); + +Lights1 D_800E4650 = gdSPDefLights1(85, 85, 0, 255, 255, 0, 0, 120, 0); + +Lights1 D_800E4668 = gdSPDefLights1(85, 85, 85, 255, 255, 255, -66, 82, -55); + +Lights1 D_800E4680 = gdSPDefLights1(85, 85, 85, 255, 255, 255, 0, 0, 120); + +Lights1 D_800E4698 = gdSPDefLights1(85, 85, 85, 255, 255, 255, 0, 0, 120); + +void OThwomp::Draw(s32 cameraId) { + s32 objectIndex = 0; + s32 i; + UNUSED s32 stackPadding0; + s16 minusone, plusone; + Camera* camera; + Object* object; + + camera = &camera1[cameraId]; + if (cameraId == PLAYER_ONE) { + objectIndex = indexObjectList1[_idx]; + clear_object_flag(objectIndex, 0x00070000); + func_800722CC(objectIndex, 0x00000110); + } + + OThwomp::TranslateThwompLights(); + + objectIndex = indexObjectList1[_idx]; + minusone = gObjectList[objectIndex].unk_0DF - 1; + plusone = gObjectList[objectIndex].unk_0DF + 1; + + if (gGamestate != CREDITS_SEQUENCE) { + OThwomp::DrawModel(objectIndex); + } + + gSPDisplayList(gDisplayListHead++, (Gfx*)D_0D0079C8); + gDPSetCombineMode(gDisplayListHead++, G_CC_MODULATEIA, G_CC_MODULATEIA); + gSPNumLights(gDisplayListHead++, 1); + gSPLight(gDisplayListHead++, &D_800E4668.l[0], LIGHT_1); + gSPLight(gDisplayListHead++, &D_800E4668.a, LIGHT_2); + gSPClearGeometryMode(gDisplayListHead++, G_CULL_BOTH); + gSPSetGeometryMode(gDisplayListHead++, G_SHADE | G_LIGHTING | G_SHADING_SMOOTH); + load_texture_block_rgba16_mirror((u8*)d_course_bowsers_castle_thwomp_side, 0x00000020, 0x00000020); + for (i = 0; i < gObjectParticle3_SIZE; i++) { + objectIndex = gObjectParticle3[i]; + if (objectIndex != NULL_OBJECT_ID) { + object = &gObjectList[objectIndex]; + if ((object->state > 0) && (State == MOVE_FAR) && (gMatrixHudCount <= MTX_HUD_POOL_SIZE_MAX)) { + rsp_set_matrix_transformation(object->pos, object->orientation, object->sizeScaling); + gSPVertex(gDisplayListHead++, (uintptr_t)D_0D005C00, 3, 0); + gSPDisplayList(gDisplayListHead++, (Gfx*)D_0D006930); + } + } + } + gSPSetGeometryMode(gDisplayListHead++, G_CULL_BACK); + gSPClearGeometryMode(gDisplayListHead++, G_LIGHTING); + gSPTexture(gDisplayListHead++, 0x0001, 0x0001, 0, G_TX_RENDERTILE, G_OFF); + gSPDisplayList(gDisplayListHead++, (Gfx*)D_0D007AE0); + load_texture_block_ia8_nomirror(D_8018D490, 0x00000020, 0x00000020); + func_8004B3C8(0); + D_80183E80[0] = 0; + D_80183E80[2] = 0x8000; + for (i = 0; i < gObjectParticle2_SIZE; i++) { + objectIndex = gObjectParticle2[i]; + if (objectIndex != NULL_OBJECT_ID) { + object = &gObjectList[objectIndex]; + if ((object->state >= 2) && (State == MOVE_AND_ROTATE) && (gMatrixHudCount <= MTX_HUD_POOL_SIZE_MAX)) { + func_8004B138(0x000000FF, 0x000000FF, 0x000000FF, (s32) object->primAlpha); + D_80183E80[1] = func_800418AC(object->pos[0], object->pos[2], camera->pos); + func_800431B0(object->pos, D_80183E80, object->sizeScaling, (Vtx*)D_0D005AE0); + } + } + } +} + +void OThwomp::DrawModel(s32 objectIndex) { + if ((gObjectList[objectIndex].state >= 2) && (func_80072354(objectIndex, 0x00000040) != 0)) { + func_8004A7AC(objectIndex, 1.75f); + rsp_set_matrix_transformation(gObjectList[objectIndex].pos, gObjectList[objectIndex].orientation, + gObjectList[objectIndex].sizeScaling); + OThwomp::ThwompLights(objectIndex); + gSPDisplayList(gDisplayListHead++, (Gfx*)D_0D007828); + gDPSetTextureLUT(gDisplayListHead++, G_TT_RGBA16); + gDPLoadTLUT_pal256(gDisplayListHead++, d_course_bowsers_castle_thwomp_tlut); + rsp_load_texture_mask((u8*)gObjectList[objectIndex].activeTexture, 0x00000010, 0x00000040, 4); + gSPDisplayList(gDisplayListHead++, gObjectList[objectIndex].model); + } +} + +void OThwomp::TranslateThwompLights() { + func_800419F8(); + D_800E4638.l[0].l.dir[0] = D_80165840[0]; + D_800E4638.l[0].l.dir[1] = D_80165840[1]; + D_800E4638.l[0].l.dir[2] = D_80165840[2]; +} + +void OThwomp::ThwompLights(s32 objectIndex) { + // Why these don't just use `gSPSetLights1` calls... + switch (gObjectList[objectIndex].type) { // hmm very strange 80165C18 + case 0: + gSPLight(gDisplayListHead++, &D_800E4638.l[0], LIGHT_1); + gSPLight(gDisplayListHead++, &D_800E4638.a, LIGHT_2); + break; + case 1: + gSPLight(gDisplayListHead++, &D_800E4650.l[0], LIGHT_1); + gSPLight(gDisplayListHead++, &D_800E4650.a, LIGHT_2); + break; + case 2: + gSPLight(gDisplayListHead++, &D_800E4668.l[0], LIGHT_1); + gSPLight(gDisplayListHead++, &D_800E4668.a, LIGHT_2); + break; + case 3: + gSPLight(gDisplayListHead++, &D_800E4680.l[0], LIGHT_1); + gSPLight(gDisplayListHead++, &D_800E4680.a, LIGHT_2); + break; + case 4: + gSPLight(gDisplayListHead++, &D_800E4698.l[0], LIGHT_1); + gSPLight(gDisplayListHead++, &D_800E4698.a, LIGHT_2); + break; + default: + break; + } +} + +void OThwomp::func_80080DE4(s32 arg0) { + Player* player; + s32 var_v1; + + player = gPlayerOne; + for (var_v1 = 0; var_v1 < NUM_PLAYERS; var_v1++, player++) { + if (arg0 == player->unk_040) { + player->soundEffects &= ~0x100; + player->unk_040 = -1; + } + } +} + +/** Behaviours **/ + +void OThwomp::StationaryBehaviour(s32 objectIndex) { // func_8007ED6C + UNUSED s32 stackPadding[4]; + switch (gObjectList[objectIndex].state) { + case 0: + break; + case 1: + OThwomp::func_8007EC30(objectIndex); + break; + case 2: + set_and_run_timer_object(objectIndex, 0x0000003C); + break; + case 3: + func_80072568(objectIndex, 0x00000032); + break; + case 4: + if (func_8007E59C(objectIndex) != 0) { + func_800725E8(objectIndex, 0x0000012C, 2); + } else { + func_800726CC(objectIndex, 2); + } + break; + } + OThwomp::func_8007E63C(objectIndex); + object_calculate_new_pos_offset(objectIndex); + gObjectList[objectIndex].direction_angle[1] = gObjectList[objectIndex].orientation[1]; + func_80073514(objectIndex); +} + +// Stationary +void OThwomp::func_8007EC30(s32 objectIndex) { + Object* object; + + object = &gObjectList[objectIndex]; + object->surfaceHeight = 0.0f; + object->origin_pos[1] = 0.0f; + set_obj_origin_offset(objectIndex, 0.0f, 0.0f, 0.0f); + if (gIsMirrorMode != 0) { + set_obj_direction_angle(objectIndex, 0U, -_faceDirection, 0U); + set_obj_orientation(objectIndex, 0U, -_faceDirection, 0U); + } else { + set_obj_direction_angle(objectIndex, 0U, _faceDirection, 0U); + set_obj_orientation(objectIndex, 0U, _faceDirection, 0U); + } + init_texture_object(objectIndex, (uint8_t*) d_course_bowsers_castle_thwomp_tlut, (const char**) d_course_bowsers_castle_thwomp_faces, + 0x10U, (u16) 0x00000040); + object->model = (Gfx*)d_course_bowsers_castle_dl_thwomp; + object->unk_01C[1] = 30.0f; + set_object_flag(objectIndex, 0x05000220); + object->type = 0; + object->unk_0DF = 6; + func_800724DC(objectIndex); + object_next_state(objectIndex); +} + +void OThwomp::MoveAndRotateBehaviour(s32 objectIndex) { // func_8007F5A8 + + switch (gObjectList[objectIndex].state) { /* irregular */ + case 0: + break; + case 1: + OThwomp::func_8007EE5C(objectIndex); + break; + case 3: + func_80072568(objectIndex, 0x00000032); + break; + case 4: + func_80086FD4(objectIndex); + object_next_state(objectIndex); + break; + } + OThwomp::func_8007E63C(objectIndex); + func_8007F544(objectIndex); + object_calculate_new_pos_offset(objectIndex); + func_80073514(objectIndex); +} + +// MoveAndRotateBehaviour +void OThwomp::func_8007EE5C(s32 objectIndex) { + Object* object; + + init_texture_object(objectIndex, (u8*)d_course_bowsers_castle_thwomp_tlut, (const char**) d_course_bowsers_castle_thwomp_faces, + 0x10U, (u16) 0x00000040); + object = &gObjectList[objectIndex]; + object->model = (Gfx*)d_course_bowsers_castle_dl_thwomp; + set_object_flag(objectIndex, 0x04000220); + object->type = 0; + object->unk_0DF = 6; + func_80086E70(objectIndex); + object->surfaceHeight = 0.0f; + object->origin_pos[1] = 0.0f; + set_obj_origin_offset(objectIndex, 0.0f, 20.0f, 0.0f); + object->unk_01C[1] = 20.0f; + if (gIsMirrorMode != 0) { + set_obj_direction_angle(objectIndex, 0U, -_faceDirection, 0U); + set_obj_orientation(objectIndex, 0U, -_faceDirection, 0U); + } else { + set_obj_direction_angle(objectIndex, 0U, _faceDirection, 0U); + set_obj_orientation(objectIndex, 0U, _faceDirection, 0U); + } + object->unk_0AE = 1; + if (object->primAlpha == 0) { + object->unk_0DD = 1; + } else { + object->unk_0DD = 2; + } + object_next_state(objectIndex); +} + +void OThwomp::MoveFarBehaviour(s32 objectIndex) { // func_8007FFC0 + switch (gObjectList[objectIndex].state) { + case 0: + break; + case 1: + OThwomp::func_8007FA08(objectIndex); + break; + case 3: + func_80072568(objectIndex, 0x00000032); + break; + case 4: + object_next_state(objectIndex); + func_80086FD4(objectIndex); + break; + } + OThwomp::func_8007E63C(objectIndex); + OThwomp::func_8007FF5C(objectIndex); + object_calculate_new_pos_offset(objectIndex); + func_80073514(objectIndex); +} + +// MoveFarBehaviour +void OThwomp::func_8007FA08(s32 objectIndex) { + Object* object; + + init_texture_object(objectIndex, (u8*)d_course_bowsers_castle_thwomp_tlut, (const char**) d_course_bowsers_castle_thwomp_faces, + 0x10U, (u16) 0x00000040); + object = &gObjectList[objectIndex]; + object->model = (Gfx*)d_course_bowsers_castle_dl_thwomp; + set_object_flag(objectIndex, 0x04000220); + object->type = 0; + object->surfaceHeight = 0.0f; + object->origin_pos[1] = 0.0f; + set_obj_origin_offset(objectIndex, 0.0f, 0.0f, 0.0f); + set_obj_direction_angle(objectIndex, 0U, 0U, 0U); + if (gIsMirrorMode != 0) { + set_obj_orientation(objectIndex, 0U, 0xC000U, 0U); + } else { + set_obj_orientation(objectIndex, 0U, 0x4000U, 0U); + } + object->velocity[0] = 0.0f; + object->direction_angle[1] = object->orientation[1]; + object->unk_0DD = 1; + object->unk_0DF = 8; + object->offset[1] = 15.0f; + object->unk_01C[1] = 15.0f; + object_next_state(objectIndex); +} + +void OThwomp::func_8007FF5C(s32 objectIndex) { + switch (gObjectList[objectIndex].unk_0DD) { + case 1: + OThwomp::func_8007FB48(objectIndex); + break; + case 2: + OThwomp::func_8007FEA4(objectIndex); + break; + } +} + +void OThwomp::func_8007FB48(s32 objectIndex) { + s32 var_v0; + UNUSED s32 stackPadding; + Player* player; + + player = &gPlayerOne[gObjectList[objectIndex].unk_0D1]; + switch (gObjectList[objectIndex].unk_0AE) { + case 1: + gObjectList[objectIndex].unk_0B0 = 0x00A0; + gObjectList[objectIndex].offset[0] = 0.0f; + gObjectList[objectIndex].offset[2] = 0.0f; + gObjectList[objectIndex].velocity[2] = 0.0f; + func_80086FD4(objectIndex); + break; + case 2: + gObjectList[objectIndex].velocity[0] = player->unk_094 * xOrientation * 1.25; + if (gObjectList[objectIndex].unk_048 >= gObjectList[objectIndex].unk_0B0) { + if (gObjectList[objectIndex].unk_0B0 == gObjectList[objectIndex].unk_048) { + if (D_8018D400 & 1) { + gObjectList[objectIndex].velocity[2] = 1.5f; + } else { + gObjectList[objectIndex].velocity[2] = -1.5f; + } + } + if (gObjectList[objectIndex].velocity[2] >= 0.0) { + if (gObjectList[objectIndex].offset[2] >= 40.0) { + gObjectList[objectIndex].velocity[2] = -1.5f; + } + } else if ((f64) gObjectList[objectIndex].offset[2] <= -40.0) { + gObjectList[objectIndex].velocity[2] = 1.5f; + } + } + object_add_velocity_offset_xz(objectIndex); + if (gObjectList[objectIndex].unk_0B0 < 0x65) { + gObjectList[objectIndex].orientation[1] = func_800417B4( + gObjectList[objectIndex].orientation[1], (gObjectList[objectIndex].direction_angle[1] + 0x8000)); + if (gObjectList[objectIndex].unk_0B0 == 0x0064) { + gObjectList[objectIndex].textureListIndex = 1; + } + } + var_v0 = 0; + if (gIsMirrorMode != 0) { + if (gObjectList[objectIndex].offset[0] <= -1000.0) { + var_v0 = 1; + } + } else if (gObjectList[objectIndex].offset[0] >= 1000.0) { + var_v0 = 1; + } + gObjectList[objectIndex].unk_0B0--; + if ((gObjectList[objectIndex].unk_0B0 == 0) || (var_v0 != 0)) { + gObjectList[objectIndex].unk_034 = 0.0f; + func_800726CC(objectIndex, 3); + func_80086FD4(objectIndex); + } + break; + case 4: + f32_step_towards(&gObjectList[objectIndex].offset[2], 0.0f, 2.0f); + f32_step_towards(gObjectList[objectIndex].offset, 0.0f, 5.0f); + if ((gObjectList[objectIndex].offset[0] + gObjectList[objectIndex].offset[2]) == 0.0) { + func_80086FD4(objectIndex); + } + break; + case 5: + gObjectList[objectIndex].orientation[1] = + func_800417B4(gObjectList[objectIndex].orientation[1], gObjectList[objectIndex].direction_angle[1]); + if (gObjectList[objectIndex].orientation[1] == gObjectList[objectIndex].direction_angle[1]) { + func_800722CC(objectIndex, 8); + func_80086FD4(objectIndex); + gObjectList[objectIndex].textureListIndex = 0; + } + break; + case 0: + case 3: + default: + break; + } +} + +void OThwomp::func_8007FEA4(s32 objectIndex) { + Object* object; + + object = &gObjectList[objectIndex]; + switch (object->unk_0AE) { + case 1: + if (f32_step_towards(&object->offset[0], object->unk_01C[0], 5.0f) != 0) { + func_800726CC(objectIndex, 3); + func_80086FD4(objectIndex); + break; + } + case 0: + case 2: + break; + case 3: + if (f32_step_towards(&object->offset[0], 0.0f, 5.0f) != 0) { + func_80086FD4(objectIndex); + func_800722CC(objectIndex, 8); + } + break; + } +} + +void OThwomp::StationaryFastBehaviour(s32 objectIndex) { // func_800801FC + Object* object; + + object = &gObjectList[objectIndex]; + switch (object->state) { + case 0: + break; + case 1: + OThwomp::func_80080078(objectIndex); + break; + case 2: + set_and_run_timer_object(objectIndex, object->timer); + break; + case 3: + func_80072568(objectIndex, 0x00000032); + break; + case 4: + object->timer = 0x0000003C; + func_800726CC(objectIndex, 2); + break; + } + OThwomp::func_8007E63C(objectIndex); + object_calculate_new_pos_offset(objectIndex); + func_80073514(objectIndex); +} + +// StationaryFastBehaviour +void OThwomp::func_80080078(s32 objectIndex) { // func_80080078 + Object* object; + + init_texture_object(objectIndex, (u8*)d_course_bowsers_castle_thwomp_tlut, (const char**) d_course_bowsers_castle_thwomp_faces, + 0x10U, (u16) 0x00000040); + object = &gObjectList[objectIndex]; + object->model = (Gfx*)d_course_bowsers_castle_dl_thwomp; + set_object_flag(objectIndex, 0x04000220); + object->type = 2; + object->unk_0DF = 8; + set_obj_direction_angle(objectIndex, 0U, 0U, 0U); + object->surfaceHeight = 0.0f; + object->origin_pos[1] = 0.0f; + set_obj_origin_offset(objectIndex, 0.0f, 0.0f, 0.0f); + object->unk_01C[1] = 30.0f; + if (gIsMirrorMode != 0) { + set_obj_orientation(objectIndex, 0U, -_faceDirection, 0U); + } else { + set_obj_orientation(objectIndex, 0U, _faceDirection, 0U); + } + switch (object->primAlpha) { /* irregular */ + case 0: + object->timer = 2; + break; + case 1: + object->timer = 0x0000003C; + break; + case 2: + object->timer = 0x00000078; + break; + case 3: + object->timer = 0x000000B4; + break; + } + func_800724DC(objectIndex); + object_next_state(objectIndex); +} + +void OThwomp::JailedBehaviour(s32 objectIndex) { // func_80080408 + switch (gObjectList[objectIndex].state) { + case 0: + break; + case 1: + OThwomp::func_800802C0(objectIndex); + break; + case 2: + func_8008A6DC(objectIndex, 100.0f); + if (is_obj_flag_status_active(objectIndex, VISIBLE) != 0) { + func_800C98B8(gObjectList[objectIndex].pos, gObjectList[objectIndex].velocity, + SOUND_ARG_LOAD(0x19, 0x01, 0x80, 0x45)); + object_next_state(objectIndex); + } + break; + case 3: + if (func_800730BC(objectIndex, 3, 5, 1, 6, 6) != 0) { + gObjectList[objectIndex].textureListIndex = 0; + } + break; + case 4: + if (set_and_run_timer_object(objectIndex, 0x0000012C) != 0) { + func_800726CC(objectIndex, 2); + } + break; + } + object_calculate_new_pos_offset(objectIndex); + func_80073514(objectIndex); +} + +// JailedBehaviour +void OThwomp::func_800802C0(s32 objectIndex) { + Object* object; + + object = &gObjectList[objectIndex]; + object->unk_0D8 = 0; + init_texture_object(objectIndex, (u8*)d_course_bowsers_castle_thwomp_tlut, (const char**) d_course_bowsers_castle_thwomp_faces, + 0x10U, (u16) 0x00000040); + object->model = (Gfx*)d_course_bowsers_castle_dl_thwomp; + object->textureListIndex = 0; + //object->sizeScaling = 1.5f; + set_object_flag(objectIndex, 0x05000220); + object->type = 1; + object->unk_0DF = 6; + set_obj_origin_offset(objectIndex, 0.0f, 0.0f, 0.0f); + set_obj_direction_angle(objectIndex, 0U, 0U, 0U); + object->surfaceHeight = 0.0f; + object->origin_pos[1] = 0.0f; + object->offset[1] = 10.0f; + object->unk_01C[1] = 10.0f; + if (gIsMirrorMode != 0) { + set_obj_orientation(objectIndex, 0U, 0x4000U, 0U); + } else { + set_obj_orientation(objectIndex, 0U, 0xC000U, 0U); + } + object->offset[0] = 0.0f; + object->offset[2] = 0.0f; + func_800724DC(objectIndex); + object_next_state(objectIndex); +} + +void OThwomp::SlidingBehaviour(s32 objectIndex) { // func_800808CC + switch (gObjectList[objectIndex].state) { /* irregular */ + case 0: + break; + case 1: + OThwomp::func_80080524(objectIndex); + break; + case 2: + func_800730BC(objectIndex, 3, 5, 1, 6, -1); + break; + } + if (gObjectList[objectIndex].state >= 2) { + OThwomp::func_8007E63C(objectIndex); + OThwomp::func_8008085C(objectIndex); + func_80073514(objectIndex); + if (gGamestate != 9) { + if ((D_8018D40C == 0) && (gObjectList[objectIndex].state == 2)) { + func_800C98B8(gObjectList[objectIndex].pos, gObjectList[objectIndex].velocity, + SOUND_ARG_LOAD(0x19, 0x03, 0x60, 0x45)); + } + } else if ((gCutsceneShotTimer < 0xBF) && (((s16) gCutsceneShotTimer % 88) == 0x0000001E)) { + func_800C98B8(gObjectList[objectIndex].pos, gObjectList[objectIndex].velocity, + SOUND_ARG_LOAD(0x19, 0x03, 0x60, 0x45)); + } + } +} + +void OThwomp::func_8008085C(s32 objectIndex) { + switch (gObjectList[objectIndex].unk_0DD) { + case 1: + OThwomp::func_800806BC(objectIndex); + break; + case 2: + OThwomp::func_8008078C(objectIndex); + break; + } + object_calculate_new_pos_offset(objectIndex); +} + +void OThwomp::func_800806BC(s32 objectIndex) { + switch (gObjectList[objectIndex].unk_0AE) { + case 0: + break; + case 1: + if (f32_step_towards(&gObjectList[objectIndex].offset[2], 250.0f, gObjectList[objectIndex].velocity[2]) != + 0) { + gObjectList[objectIndex].velocity[2] = -gObjectList[objectIndex].velocity[2]; + func_80086FD4(objectIndex); + } + break; + case 2: + if (f32_step_towards(&gObjectList[objectIndex].offset[2], 0.0f, gObjectList[objectIndex].velocity[2]) != + 0) { + gObjectList[objectIndex].velocity[2] = -gObjectList[objectIndex].velocity[2]; + func_8008701C(objectIndex, 1); + } + break; + } +} + +void OThwomp::func_8008078C(s32 objectIndex) { + switch (gObjectList[objectIndex].unk_0AE) { + case 0: + break; + case 1: + if (f32_step_towards(&gObjectList[objectIndex].offset[2], -250.0f, gObjectList[objectIndex].velocity[2]) != + 0) { + gObjectList[objectIndex].velocity[2] = -gObjectList[objectIndex].velocity[2]; + func_80086FD4(objectIndex); + } + break; + case 2: + if (f32_step_towards(&gObjectList[objectIndex].offset[2], 0.0f, gObjectList[objectIndex].velocity[2]) != + 0) { + gObjectList[objectIndex].velocity[2] = -gObjectList[objectIndex].velocity[2]; + func_8008701C(objectIndex, 1); + } + break; + } +} + +// SlidingBehaviour +void OThwomp::func_80080524(s32 objectIndex) { + Object* object; + + init_texture_object(objectIndex, (u8*)d_course_bowsers_castle_thwomp_tlut, (const char**) d_course_bowsers_castle_thwomp_faces, + 0x10U, (u16) 0x00000040); + object = &gObjectList[objectIndex]; + object->model = (Gfx*)d_course_bowsers_castle_dl_thwomp; + object->textureListIndex = 0; + set_object_flag(objectIndex, 0x04000220); + object->type = 0; + object->unk_0DF = 0x0A; + func_80086E70(objectIndex); + set_obj_origin_offset(objectIndex, 0.0f, 0.0f, 0.0f); + object->surfaceHeight = 70.0f; + object->origin_pos[1] = 70.0f; + object->unk_01C[1] = 0.0f; + set_obj_direction_angle(objectIndex, 0U, 0U, 0U); + if ((gIsMirrorMode != 0) || (gGamestate == CREDITS_SEQUENCE)) { + set_obj_orientation(objectIndex, 0U, -_faceDirection, 0U); + } else { + set_obj_orientation(objectIndex, 0U, _faceDirection, 0U); + } + switch (object->primAlpha) { /* irregular */ + case 0: + object->unk_0DD = 2; + object->velocity[2] = -1.0f; + break; + case 1: + object->unk_0DD = 2; + object->velocity[2] = -1.5f; + break; + } + func_800722A4(objectIndex, 0x00000080); + object_next_state(objectIndex); +} + +void OThwomp::func_8007E63C(s32 objectIndex) { + switch (gObjectList[objectIndex].state) { /* irregular */ + case 0x32: + if (f32_step_up_towards(&gObjectList[objectIndex].offset[1], gObjectList[objectIndex].unk_01C[1] + 15.0, + 1.5f) != 0) { + set_object_flag(objectIndex, 0x00000200); + func_800722A4(objectIndex, 1); + func_800722CC(objectIndex, 2); + object_next_state(objectIndex); + } + break; + case 0x33: + if (f32_step_down_towards(&gObjectList[objectIndex].offset[1], 0.0f, 2.0f) != 0) { + if (gObjectList[objectIndex].offset[1] >= 16.0f) { + gObjectList[objectIndex].textureListIndex = 0; + } else if (gObjectList[objectIndex].offset[1] >= 8.0f) { + gObjectList[objectIndex].textureListIndex = 1; + } else { + gObjectList[objectIndex].textureListIndex = 2; + } + func_800722CC(objectIndex, 1); + if (is_obj_flag_status_active(objectIndex, 0x00010000) != 0) { + func_800722A4(objectIndex, 0x00000010); + if (is_obj_flag_status_active(objectIndex, VISIBLE) != 0) { + func_800722A4(objectIndex, 0x00000020); + } + } + if (func_80072320(objectIndex, 2) != 0) { + func_800726CC(objectIndex, 0x00000064); + } else { + object_next_state(objectIndex); + } + } + break; + case 0x34: + func_80072AAC(objectIndex, 3, 6); + break; + case 0x35: + func_80072AAC(objectIndex, 2, 0x00000032); + break; + case 0x36: + if (gObjectList[objectIndex].offset[1] >= 20.0f) { + gObjectList[objectIndex].textureListIndex = 0; + } else if (gObjectList[objectIndex].offset[1] >= 18.0f) { + gObjectList[objectIndex].textureListIndex = 1; + } + if (f32_step_up_towards(&gObjectList[objectIndex].offset[1], gObjectList[objectIndex].unk_01C[1], 0.5f) != + 0) { + clear_object_flag(objectIndex, 0x00000200); + func_8007266C(objectIndex); + } + break; + case 0x64: + func_80072E54(objectIndex, 3, 5, 1, 8, 0); + break; + case 0x65: + set_and_run_timer_object(objectIndex, 0x0000001E); + break; + case 0x66: + if (f32_step_up_towards(&gObjectList[objectIndex].offset[1], 20.0f, 1.5f) != 0) { + object_next_state(objectIndex); + } + break; + case 0x67: + if (f32_step_down_towards(&gObjectList[objectIndex].offset[1], 0.0f, 1.5f) != 0) { + if (is_obj_flag_status_active(objectIndex, 0x00020000) != 0) { + func_800722A4(objectIndex, 0x00000010); + if (is_obj_flag_status_active(objectIndex, VISIBLE) != 0) { + func_800722A4(objectIndex, 0x00000020); + } + } + object_next_state(objectIndex); + } + break; + case 0x68: + if (f32_step_up_towards(&gObjectList[objectIndex].offset[1], 12.0f, 1.5f) != 0) { + object_next_state(objectIndex); + } + break; + case 0x69: + if (f32_step_down_towards(&gObjectList[objectIndex].offset[1], 0.0f, 1.5f) != 0) { + if (is_obj_flag_status_active(objectIndex, 0x00020000) != 0) { + func_800722A4(objectIndex, 0x00000010); + if (is_obj_flag_status_active(objectIndex, VISIBLE) != 0) { + func_800722A4(objectIndex, 0x00000020); + } + } + func_800C98B8(gObjectList[objectIndex].pos, gObjectList[objectIndex].velocity, + SOUND_ARG_LOAD(0x19, 0x01, 0x80, 0x45)); + object_next_state(objectIndex); + } + break; + case 0x6A: + if (func_8007326C(objectIndex, 5, 3, 1, 6, 3) != 0) { + OThwomp::func_80080DE4(objectIndex); + } + break; + case 0x6B: + if (gObjectList[objectIndex].offset[1] >= 22.0f) { + gObjectList[objectIndex].textureListIndex = 0; + } else if (gObjectList[objectIndex].offset[1] >= 20.0f) { + gObjectList[objectIndex].textureListIndex = 1; + } else if (gObjectList[objectIndex].offset[1] >= 18.0f) { + gObjectList[objectIndex].textureListIndex = 2; + } else if (gObjectList[objectIndex].offset[1] >= 16.0f) { + gObjectList[objectIndex].textureListIndex = 3; + } else if (gObjectList[objectIndex].offset[1] >= 14.0f) { + gObjectList[objectIndex].textureListIndex = 4; + } else { + func_800730BC(objectIndex, 3, 5, 1, 6, -1); + } + if (f32_step_up_towards(&gObjectList[objectIndex].offset[1], gObjectList[objectIndex].unk_01C[1], 0.5f) != + 0) { + set_object_timer_state(objectIndex, 0); + object_next_state(objectIndex); + } + break; + case 0x6C: + if (set_and_run_timer_object(objectIndex, 0x00000064) != 0) { + func_800722CC(objectIndex, 2); + clear_object_flag(objectIndex, 0x00000200); + func_8007266C(objectIndex); + } + break; + case 0xC8: + if (set_and_run_timer_object(objectIndex, 0x0000012C) != 0) { + func_80072320(objectIndex, 0x00000080); + func_80072428(objectIndex); + func_800726CC(objectIndex, 1); + } + break; + case 0x12C: + if (func_80073E18(objectIndex, &gObjectList[objectIndex].orientation[1], 0x0400U, 0x00008000) != 0) { + func_800722CC(objectIndex, 4); + func_8007266C(objectIndex); + } + break; + } +} diff --git a/src/engine/objects/Thwomp.h b/src/engine/objects/Thwomp.h new file mode 100644 index 000000000..22c674bb5 --- /dev/null +++ b/src/engine/objects/Thwomp.h @@ -0,0 +1,120 @@ +#pragma once + +#include <libultraship.h> +#include <vector> + +#include "engine/World.h" +#include "engine/objects/Object.h" + +extern "C" { +#include "macros.h" +#include "main.h" +#include "vehicles.h" +#include "waypoints.h" +#include "common_structs.h" +#include "objects.h" +#include "camera.h" +#include "some_data.h" +} + +//! @todo Make shadow size bigger if thwomp is scaled up +//! @todo make adjustable properties for squishSize and boundingBoxSize + +/** + * Thwomp OObject + * + * The game automatically places the actor on the ground so you do not need to provide a Y coordinate value. + * + * @arg direction the rotational direction the thwomp is facing. + * @arg behaviour the behaviour of the thwomp, uses values 1-6. + * @arg primAlpha unknown + * @arg boundingBoxSize optional. The size of the bounding box for the thwomp. Default value is 12 + */ +class OThwomp : public OObject { +private: + enum States : uint16_t { + DISABLED, + STATIONARY, + MOVE_AND_ROTATE, + MOVE_FAR, // Requires enough space to work + STATIONARY_FAST, + SLIDE, // Moves sideways left / right + JAILED // Has no collision + }; + +public: + States State = States::DISABLED; + + explicit OThwomp(s16 x, s16 z, s16 direction, f32 scale, s16 behaviour, s16 primAlpha, u16 boundingBoxSize = 7); + + ~OThwomp() { + _count--; + } + + static size_t GetCount() { + return _count; + } + + virtual void Tick60fps() override; + virtual void Draw(s32 cameraId) override; + void SetVisibility(s32 objectIndex); + void func_80080B28(s32 objectIndex, s32 playerId); + void DrawModel(s32); + void TranslateThwompLights(); + void ThwompLights(s32 objectIndex); + void func_80080DE4(s32 arg0); + s32 func_8007F75C(s32 playerId); + void func_8007F8D8(); + void SetPlayerCrushedEffect(s32 objectIndex, Player* player); + void func_80080A4C(s32 objectIndex, s32 cameraPlayerId); + void func_8007542C(s32 arg0); + void func_80074FD8(s32 objectIndex); + void AddParticles(s32 arg0); + + s32 func_8007E50C(s32 objectIndex, Player* player, Camera* camera); + s32 func_8007E59C(s32 objectIndex); + + void func_8007F544(s32 objectIndex); + void func_8007EFBC(s32 objectIndex); + void func_8007F280(s32 objectIndex); + + void func_8007F660(s32 objectIndex, s32 arg1, s32 arg2); + void func_80080E8C(s32 objectIndex1, s32 objectIndex2, s32 arg2); + void func_8007F6C4(s32 objectIndex, s32 playerId); + + void func_800810F4(s32 objectIndex); + void func_80081080(s32 objectIndex); + + void StationaryBehaviour(s32 objectIndex); + void func_8007EC30(s32 objectIndex); + + void MoveAndRotateBehaviour(s32 objectIndex); + void func_8007EE5C(s32 objectIndex); + + void MoveFarBehaviour(s32 objectIndex); + void func_8007FA08(s32 objectIndex); + void func_8007FF5C(s32 objectIndex); + void func_8007FB48(s32 objectIndex); + void func_8007FEA4(s32 objectIndex); + + void StationaryFastBehaviour(s32 objectIndex); + void func_80080078(s32 objectIndex); + + void JailedBehaviour(s32 objectIndex); + void func_800802C0(s32 objectIndex); + + void SlidingBehaviour(s32 objectIndex); + void func_80080524(s32 objectIndex); + void func_8008085C(s32 objectIndex); + void func_800806BC(s32 objectIndex); + void func_8008078C(s32 objectIndex); + + void func_8007E63C(s32 objectIndex); +private: + static size_t _count; + s32 _idx; + s16 _faceDirection; + //! @todo Write this better. This effects the squish size and the bounding box size. + // We should probably return to the programmer the pointer to the actor so they can do thwomp->squishSize = value. + u16 _boundingBoxSize; +}; diff --git a/src/engine/objects/TrashBin.cpp b/src/engine/objects/TrashBin.cpp new file mode 100644 index 000000000..43d43b1fc --- /dev/null +++ b/src/engine/objects/TrashBin.cpp @@ -0,0 +1,231 @@ +#include <libultraship.h> +#include <libultra/gbi.h> +#include "TrashBin.h" +#include "World.h" +#include "port/Game.h" + +extern "C" { +#include "main.h" +#include "code_800029B0.h" +#include "render_objects.h" +#include "update_objects.h" +#include "assets/banshee_boardwalk_data.h" +#include "assets/common_data.h" +#include "math_util.h" +#include "math_util_2.h" +#include "code_80086E70.h" +#include "code_80057C60.h" +#include "audio/external.h" +} + +#define DEGREES_FLOAT_TO_SHORT(Degrees) ((s16)((Degrees) * (0x8000 / 180.0f))) + +OTrashBin::OTrashBin(const FVector& pos, const FRotation& rotation, f32 scale, OTrashBin::Behaviour bhv) { + _pos = pos; + _rot = rotation; + _scale = scale; + _bhv = bhv; + + init_object(indexObjectList1[1], 0); + + if (GetCourse() != GetBansheeBoardwalk()) { + _drawBin = true; + } +} + +void OTrashBin::Tick() { + s32 objectIndex = indexObjectList1[1]; + OTrashBin::func_8007E00C(objectIndex); + + switch(_bhv) { + case STATIC: + break; + case MUNCHING: + func_8007DDC0(objectIndex); + break; + } +} + +void OTrashBin::Draw(s32 cameraId) { + s32 objectIndex; + Object* object; + + objectIndex = indexObjectList1[1]; + func_8008A364(objectIndex, cameraId, 0x5555U, 0x00000320); + if (is_obj_flag_status_active(objectIndex, VISIBLE) != 0) { + object = &gObjectList[objectIndex]; + if (object->state >= 2) { + func_80043220(object->pos, object->orientation, object->sizeScaling, object->model); + } + + if (_drawBin) { + Mat4 mtx; + Vec3f Pos = { _pos.x + 63, _pos.y + 12, _pos.z + 25 }; + Vec3s Rot = { 0, 0x4000, 0 }; + mtxf_pos_rotation_xyz(mtx, Pos, Rot); + //mtxf_scale(mtx, 1.0f); + if (render_set_position(mtx, 0) != 0) { + gSPDisplayList(gDisplayListHead++, BinMod); + } + } + } +} + +void OTrashBin::init_bb_trash_bin(s32 objectIndex) { + gObjectList[objectIndex].sizeScaling = 1.0f; + gObjectList[objectIndex].model = (Gfx*)d_course_banshee_boardwalk_dl_trash_bin; + gObjectList[objectIndex].unk_04C = 0; + gObjectList[objectIndex].unk_084[7] = 0; + set_obj_orientation(objectIndex, 0U, 0U, 0U); + gObjectList[objectIndex].orientation[0] = DEGREES_FLOAT_TO_SHORT(_rot.pitch); + gObjectList[objectIndex].orientation[1] = DEGREES_FLOAT_TO_SHORT(_rot.yaw); + gObjectList[objectIndex].orientation[2] = DEGREES_FLOAT_TO_SHORT(_rot.roll); + gObjectList[objectIndex].pos[0] = _pos.x; + if (_drawBin) { + // Position the lid on-top of the box. + // This might not be the best fix, but this allows the box to be placed on a surface + // when spawned at 0,0,0 + gObjectList[objectIndex].pos[0] = _pos.y + 32.0f; + } else { + // Stock implementation for banshee boardwalk + gObjectList[objectIndex].pos[1] = _pos.y; + } + gObjectList[objectIndex].pos[2] = _pos.z; + set_obj_velocity(objectIndex, 0.0f, 0.0f, 0.0f); + gObjectList[objectIndex].type = 0; + object_next_state(objectIndex); +} + +#undef DEGREES_FLOAT_TO_SHORT + +void OTrashBin::func_8007E00C(s32 objectIndex) { + switch (gObjectList[objectIndex].state) { + case 1: + init_bb_trash_bin(objectIndex); + break; + case 3: + D_8018CFB0 = 1; + object_next_state(objectIndex); + break; + case 4: + set_and_run_timer_object(objectIndex, 0x000000D2); + if (D_80165594 == 0) { + if (gCCSelection < CC_150) { + func_8007D714(1); + func_8007D714(1); + } else { + func_8007D714(1); + func_8007D714(1); + func_8007D714(1); + func_8007D714(1); + } + } + func_80073CB0(objectIndex, &gObjectList[objectIndex].primAlpha, -0x00002000, 0, 0x00000400, 0, -1); + gObjectList[objectIndex].orientation[2] = gObjectList[objectIndex].primAlpha; + if (gObjectList[objectIndex].unk_084[7] == 0) { + func_800C98B8(gObjectList[objectIndex].pos, gObjectList[objectIndex].velocity, + SOUND_ARG_LOAD(0x19, 0x01, 0x90, 0x4E)); + gObjectList[objectIndex].unk_084[7] = 0x0014; + } else { + gObjectList[objectIndex].unk_084[7]--; + } + break; + case 5: + gObjectList[objectIndex].orientation[2] = func_800417B4(gObjectList[objectIndex].orientation[2], 0U); + if (gObjectList[objectIndex].orientation[2] == 0) { + object_next_state(objectIndex); + } + break; + case 6: + gObjectList[objectIndex].orientation[2] = 0; + gObjectList[objectIndex].unk_084[7] = 0; + object_next_state(objectIndex); + D_8018CFB0 = 0; + break; + case 0: + case 2: + default: + break; + } +} + +Gfx OTrashBin::BinMod[] = +{ + gsSPTexture(0xFFFF, 0xFFFF, 0, G_TX_RENDERTILE, G_ON), + gsDPTileSync(), + gsDPSetTile(G_IM_FMT_RGBA, G_IM_SIZ_16b, 8, 0x0000, G_TX_RENDERTILE, 0, G_TX_NOMIRROR | G_TX_WRAP, 5, G_TX_NOLOD, G_TX_NOMIRROR | G_TX_WRAP, 5, G_TX_NOLOD), + gsDPSetTileSize(G_TX_RENDERTILE, 0, 0, 0x007C, 0x007C), + gsDPSetTextureImage(G_IM_FMT_RGBA, G_IM_SIZ_16b, 1, gTexture651B20), + gsDPTileSync(), + gsDPSetTile(G_IM_FMT_RGBA, G_IM_SIZ_16b, 0, 0x0000, G_TX_LOADTILE, 0, G_TX_NOMIRROR | G_TX_WRAP, G_TX_NOMASK, G_TX_NOLOD, G_TX_NOMIRROR | G_TX_WRAP, G_TX_NOMASK, G_TX_NOLOD), + gsDPLoadSync(), + gsDPLoadBlock(G_TX_LOADTILE, 0, 0, 1023, 256), + gsSPVertex(BinVtx, 20, 0), + gsSP2Triangles(0, 1, 2, 0, 0, 2, 3, 0), + gsSP2Triangles(4, 5, 6, 0, 4, 6, 7, 0), + gsSP2Triangles(8, 9, 10, 0, 8, 10, 11, 0), + gsSP2Triangles(12, 13, 14, 0, 12, 14, 15, 0), + gsSP2Triangles(16, 17, 18, 0, 16, 18, 19, 0), + gsSPVertex(BinVtx2, 20, 0), + gsSP2Triangles(0, 1, 2, 0, 0, 2, 3, 0), + gsSP2Triangles(4, 5, 6, 0, 4, 6, 7, 0), + gsSP2Triangles(8, 9, 10, 0, 8, 10, 11, 0), + gsSP2Triangles(12, 13, 14, 0, 12, 14, 15, 0), + gsSP2Triangles(16, 17, 18, 0, 16, 18, 19, 0), + gsDPPipeSync(), + gsDPSetCombineLERP(0, 0, 0, SHADE, 0, 0, 0, ENVIRONMENT, 0, 0, 0, SHADE, 0, 0, 0, ENVIRONMENT), + gsSPTexture(65535, 65535, 0, 0, 0), + gsSPEndDisplayList(), +}; + + + + + + + +Vtx OTrashBin::BinVtx[20] = { + {{ {25, -13, 62}, 0, {0, -2303}, {255, 255, 255, 0} }}, + {{ {-25, -13, 62}, 0, {1280, -2303}, {255, 255, 255, 0} }}, + {{ {-25, -13, -63}, 0, {1280, 896}, {255, 255, 255, 0} }}, + {{ {25, -13, -63}, 0, {0, 895}, {255, 255, 255, 0} }}, + {{ {25, -13, -63}, 0, {0, 1113}, {255, 255, 255, 0} }}, + {{ {25, 20, -63}, 0, {0, 256}, {255, 255, 255, 0} }}, + {{ {25, 20, 62}, 0, {3199, 256}, {255, 255, 255, 0} }}, + {{ {25, -13, 62}, 0, {3199, 1113}, {255, 255, 255, 0} }}, + {{ {-25, -13, -63}, 0, {0, 1113}, {255, 255, 255, 0} }}, + {{ {-25, 20, -63}, 0, {0, 255}, {255, 255, 255, 0} }}, + {{ {25, 20, -63}, 0, {1280, 255}, {255, 255, 255, 0} }}, + {{ {25, -13, -63}, 0, {1280, 1113}, {255, 255, 255, 0} }}, + {{ {-25, -13, 62}, 0, {0, 1113}, {255, 255, 255, 0} }}, + {{ {-25, 20, 62}, 0, {0, 255}, {255, 255, 255, 0} }}, + {{ {-25, 20, -63}, 0, {3200, 255}, {255, 255, 255, 0} }}, + {{ {-25, -13, -63}, 0, {3200, 1113}, {255, 255, 255, 0} }}, + {{ {25, -13, 62}, 0, {0, 1113}, {255, 255, 255, 0} }}, + {{ {25, 20, 62}, 0, {0, 256}, {255, 255, 255, 0} }}, + {{ {-25, 20, 62}, 0, {1279, 256}, {255, 255, 255, 0} }}, + {{ {-25, -13, 62}, 0, {1279, 1113}, {255, 255, 255, 0} }}, +}; + +Vtx OTrashBin::BinVtx2[20] = { + {{ {19, -8, 52}, 0, {3199, 1113}, {255, 255, 255, 0} }}, + {{ {25, 20, 62}, 0, {3199, 256}, {255, 255, 255, 0} }}, + {{ {25, 20, -63}, 0, {0, 256}, {255, 255, 255, 0} }}, + {{ {19, -8, -52}, 0, {0, 1113}, {255, 255, 255, 0} }}, + {{ {19, -8, -52}, 0, {1280, 1113}, {255, 255, 255, 0} }}, + {{ {25, 20, -63}, 0, {1280, 255}, {255, 255, 255, 0} }}, + {{ {-25, 20, -63}, 0, {0, 255}, {255, 255, 255, 0} }}, + {{ {-19, -8, -52}, 0, {0, 1113}, {255, 255, 255, 0} }}, + {{ {-19, -8, 52}, 0, {1279, 1113}, {255, 255, 255, 0} }}, + {{ {-25, 20, 62}, 0, {1279, 256}, {255, 255, 255, 0} }}, + {{ {25, 20, 62}, 0, {0, 256}, {255, 255, 255, 0} }}, + {{ {19, -8, 52}, 0, {0, 1113}, {255, 255, 255, 0} }}, + {{ {19, -8, -52}, 0, {0, 895}, {255, 255, 255, 0} }}, + {{ {-19, -8, -52}, 0, {1280, 896}, {255, 255, 255, 0} }}, + {{ {-19, -8, 52}, 0, {1280, -2303}, {255, 255, 255, 0} }}, + {{ {19, -8, 52}, 0, {0, -2303}, {255, 255, 255, 0} }}, + {{ {-19, -8, -52}, 0, {3200, 1113}, {255, 255, 255, 0} }}, + {{ {-25, 20, -63}, 0, {3200, 255}, {255, 255, 255, 0} }}, + {{ {-25, 20, 62}, 0, {0, 255}, {255, 255, 255, 0} }}, + {{ {-19, -8, 52}, 0, {0, 1113}, {255, 255, 255, 0} }}, +}; diff --git a/src/engine/objects/TrashBin.h b/src/engine/objects/TrashBin.h new file mode 100644 index 000000000..2ef500f92 --- /dev/null +++ b/src/engine/objects/TrashBin.h @@ -0,0 +1,45 @@ +#pragma once + +#include <libultraship.h> +#include <vector> +#include "Object.h" + +#include "World.h" + +extern "C" { +#include "macros.h" +#include "main.h" +#include "vehicles.h" +#include "waypoints.h" +#include "common_structs.h" +#include "objects.h" +#include "camera.h" +#include "some_data.h" +} + +class OTrashBin : public OObject { +public: + + enum Behaviour { + STATIC, // The lid stays shut + MUNCHING // The lid opens/closes in a scary munching manner + }; + explicit OTrashBin(const FVector& pos, const FRotation& rotation, f32 scale, OTrashBin::Behaviour bhv); + + virtual void Tick() override; + virtual void Draw(s32 cameraId) override; + void func_8007E00C(s32 objectIndex); + void init_bb_trash_bin(s32); + +private: + static Gfx BinMod[]; + static Vtx BinVtx[]; + static Vtx BinVtx2[]; + + Behaviour _bhv; + FVector _pos; + FRotation _rot; + float _scale; + size_t _idx; + bool _drawBin = false; +}; diff --git a/src/engine/objects/Trophy.cpp b/src/engine/objects/Trophy.cpp new file mode 100644 index 000000000..c14cc23ca --- /dev/null +++ b/src/engine/objects/Trophy.cpp @@ -0,0 +1,394 @@ +#include "Trophy.h" +#include "assets/common_data.h" +#include "assets/data_segment2.h" +extern "C" { +#include "main.h" +#include "defines.h" +#include "update_objects.h" +#include "code_80057C60.h" +#include "assets/ceremony_data.h" +#include "podium_ceremony_actors.h" +#include "math_util.h" +#include "math_util_2.h" +#include "data/some_data.h" +#include "engine/Matrix.h" +#include "render_objects.h" +#include "code_80086E70.h" +#include "code_80091750.h" +} + +OTrophy::OTrophy(const FVector& pos, TrophyType trophy, Behaviour bhv) { + s32 objectIndex = indexObjectList1[3]; + _trophy = trophy; + _spawnPos = pos; + _spawnPos.y += 16.0f; // Adjust the height so the trophy sits on the surface when positioned to 0,0,0 + _bhv = bhv; + + if (bhv == OTrophy::Behaviour::PODIUM_CEREMONY) { + _toggleVisibility = &D_801658CE; + } else { + int8_t toggle = 1; + _toggleVisibility = &toggle; + _isMod = true; + } + + // This allows spawning for mods + if (*_toggleVisibility == true) { + init_object(objectIndex, 0); + } + + switch (trophy) { + case TrophyType::GOLD: + gObjectList[objectIndex].model = (Gfx*)gold_trophy_dl10; + break; + case TrophyType::SILVER: + gObjectList[objectIndex].model = (Gfx*)gold_trophy_dl12; + break; + case TrophyType::BRONZE: + gObjectList[objectIndex].model = (Gfx*)gold_trophy_dl14; + break; + case TrophyType::GOLD_150: + gObjectList[objectIndex].model = (Gfx*)gold_trophy_dl11; + break; + case TrophyType::SILVER_150: + gObjectList[objectIndex].model = (Gfx*)gold_trophy_dl13; + break; + case TrophyType::BRONZE_150: + gObjectList[objectIndex].model = (Gfx*)gold_trophy_dl15; + break; + } + + // Set defaults for modded behaviours + if (_bhv != OTrophy::Behaviour::PODIUM_CEREMONY) { + gObjectList[objectIndex].sizeScaling = 0.025f; + gObjectList[objectIndex].unk_084[1] = 0x0200; + object_next_state(objectIndex); + func_80086E70(objectIndex); + } + + switch(_bhv) { + case OTrophy::Behaviour::GO_FISH: + gObjectList[objectIndex].sizeScaling = 0.010f; + break; + } + + Object *object = &gObjectList[objectIndex]; + object->origin_pos[0] = _spawnPos.x; + object->origin_pos[1] = _spawnPos.y; + object->origin_pos[2] = _spawnPos.z; + object->pos[0] = _spawnPos.x; + object->pos[1] = _spawnPos.y; + object->pos[2] = _spawnPos.z; +} + +void OTrophy::Tick() { // func_80086D80 + s32 objectIndex = indexObjectList1[3]; + s32 var_s0; + + // Fallback for podium ceremony where the trophy is not spawned until it is needed + if ((*_toggleVisibility == true) && (D_801658DC == 0) && (_isMod == false)) { + init_object(objectIndex, 0); + D_801658DC = 1; + } + + switch(_bhv) { + case OTrophy::Behaviour::PODIUM_CEREMONY: + if (gObjectList[objectIndex].state != 0 && (*_toggleVisibility == true)) { + OTrophy::func_80086C14(objectIndex); + OTrophy::func_80086940(objectIndex); + if (D_801658F4 != 0) { + if (D_8016559C == 0) { + OTrophy::func_80086C6C(objectIndex); + } + } else { + for (var_s0 = 0; var_s0 < 2; var_s0++) { + OTrophy::func_80086C6C(objectIndex); + } + } + } + break; + + case OTrophy::Behaviour::STATIONARY: + if (gObjectList[objectIndex].state != 0) { + gObjectList[objectIndex].sizeScaling = 0.025f; + set_obj_origin_pos(objectIndex, _spawnPos.x, + _spawnPos.y + 16.0, _spawnPos.z); + set_obj_origin_offset(objectIndex, 0.0f, 0.0f, 0.0f); + set_obj_direction_angle(objectIndex, 0U, 0U, 0U); + gObjectList[objectIndex].unk_084[1] = 0x0200; + object_next_state(objectIndex); + func_80086E70(objectIndex); + OTrophy::func_80086940(objectIndex); + } + break; + case OTrophy::Behaviour::ROTATE: + if (gObjectList[objectIndex].state != 0) { + + gObjectList[objectIndex].direction_angle[0] += 0x400; + gObjectList[objectIndex].direction_angle[1] -= 0x200; + } + break; + case OTrophy::Behaviour::ROTATE2: + if (gObjectList[objectIndex].state != 0) { + + gObjectList[objectIndex].direction_angle[0] += 0x400; + gObjectList[objectIndex].direction_angle[1] = 0xE800; + gObjectList[objectIndex].direction_angle[2] = 0xDA00; + } + break; + case OTrophy::Behaviour::GO_FISH: + if (gObjectList[objectIndex].state != 0) { + + // Get the player's yaw + Player *player = &gPlayers[0]; + float yaw = (player->rotation[1] + 0x4000) * (M_PI / 32768.0f); // Convert degrees to radians + + // Calculate forward direction based on yaw (same as before) + float lookAtX = player->pos[0] + cos(yaw); + float lookAtZ = player->pos[2] + sin(yaw); + + float forwardX = lookAtX - player->pos[0]; + float forwardY = 0; // Optional: Ignore height changes + float forwardZ = lookAtZ - player->pos[2]; + + // Normalize the forward vector + float length = sqrtf(forwardX * forwardX + forwardZ * forwardZ); + if (length != 0) { + forwardX /= length; + forwardZ /= length; + } + + float distance = 30.0f; + + // Calculate the object's position in front of the player (same as before) + gObjectList[objectIndex].pos[0] = player->pos[0] + forwardX * distance; + gObjectList[objectIndex].pos[1] = player->pos[1] + 8.0f; // Optional height offset + gObjectList[objectIndex].pos[2] = player->pos[2] + forwardZ * distance; + + // Apply more sensitive random movement based on player velocity to simulate floating behavior + float velocityFactor = 0.1f; // Increased factor for more sensitivity + gObjectList[objectIndex].pos[0] += player->velocity[0] * velocityFactor; + gObjectList[objectIndex].pos[1] += player->velocity[1] * velocityFactor; // Optional: Add vertical movement + gObjectList[objectIndex].pos[2] += player->velocity[2] * velocityFactor; + + // Increase oscillation for more dynamic movement (sine wave effect) + float oscillationSpeed = 4.0f; // Increased speed for quicker oscillations + float oscillationAmplitude = 0.4f; // Increased amplitude for more noticeable movement + gObjectList[objectIndex].pos[1] += oscillationAmplitude * sinf(oscillationSpeed * gGlobalTimer); // Vertical oscillation based on time + + // Now use smooth interpolation (lerp) to gradually move the trophy towards its target position + float lerpFactor = 0.25f; // Increased to make the trophy follow the player more quickly + + gObjectList[objectIndex].pos[0] = _oldPos[0] + lerpFactor * (gObjectList[objectIndex].pos[0] - _oldPos[0]); + gObjectList[objectIndex].pos[1] = _oldPos[1] + lerpFactor * (gObjectList[objectIndex].pos[1] - _oldPos[1]); + gObjectList[objectIndex].pos[2] = _oldPos[2] + lerpFactor * (gObjectList[objectIndex].pos[2] - _oldPos[2]); + + // Save the current position for the next frame + _oldPos[0] = gObjectList[objectIndex].pos[0]; + _oldPos[1] = gObjectList[objectIndex].pos[1]; + _oldPos[2] = gObjectList[objectIndex].pos[2]; + + + gObjectList[objectIndex].direction_angle[0] += 0x400; + gObjectList[objectIndex].direction_angle[1] -= 0x200; + } + break; + } +} + +void OTrophy::Draw(s32 cameraId) { + s32 listIndex = indexObjectList1[3]; + Mat4 someMatrix1; + Mat4 someMatrix2; + Object* object; + if (*_toggleVisibility == true) { + object = &gObjectList[listIndex]; + if (object->state >= 2) { + gSPMatrix(gDisplayListHead++, VIRTUAL_TO_PHYSICAL(&gGfxPool->mtxPersp[0]), + G_MTX_NOPUSH | G_MTX_LOAD | G_MTX_PROJECTION); + gSPMatrix(gDisplayListHead++, VIRTUAL_TO_PHYSICAL(&gGfxPool->mtxLookAt[0]), + G_MTX_NOPUSH | G_MTX_LOAD | G_MTX_MODELVIEW); + mtxf_set_matrix_transformation(someMatrix1, object->pos, object->direction_angle, object->sizeScaling); + //convert_to_fixed_point_matrix(&gGfxPool->mtxHud[gMatrixHudCount], someMatrix1); + //gSPMatrix(gDisplayListHead++, VIRTUAL_TO_PHYSICAL(&gGfxPool->mtxHud[gMatrixHudCount++]), + // G_MTX_NOPUSH | G_MTX_MUL | G_MTX_MODELVIEW); + + AddHudMatrix(someMatrix1, G_MTX_NOPUSH | G_MTX_MUL | G_MTX_MODELVIEW); + + gSPDisplayList(gDisplayListHead++, (Gfx*)D_0D0077A0); + gSPDisplayList(gDisplayListHead++, object->model); + gSPMatrix(gDisplayListHead++, VIRTUAL_TO_PHYSICAL(&gGfxPool->mtxLookAt[0]), + G_MTX_NOPUSH | G_MTX_MUL | G_MTX_PROJECTION); + mtxf_identity(someMatrix2); + render_set_position(someMatrix2, 0); + } + + switch (_bhv) { + case GO_FISH: + size_t numTrophies = 0; + func_80057A50(40, 0, (char*) "Trophies Collected: ", (s16) numTrophies); + break; + } + } +} + +void OTrophy::func_80086700(s32 objectIndex) { + gObjectList[objectIndex].sizeScaling = 0.005f; + set_obj_origin_pos(objectIndex, gObjectList[indexObjectList2[0]].pos[0], + gObjectList[indexObjectList2[0]].pos[1] + 16.0, gObjectList[indexObjectList2[0]].pos[2]); + set_obj_origin_offset(objectIndex, 0.0f, 0.0f, 0.0f); + set_obj_direction_angle(objectIndex, 0U, 0U, 0U); + gObjectList[objectIndex].unk_084[1] = 0x0200; + object_next_state(objectIndex); + func_80086E70(objectIndex); +} + +void OTrophy::func_80086940(s32 objectIndex) { + Object* object; + + object = &gObjectList[objectIndex]; + switch (object->unk_0AE) { + case 0: + break; + case 1: + func_80086FD4(objectIndex); + break; + case 2: + f32_step_towards(&object->sizeScaling, 0.025f, 0.001f); + func_80087C48(objectIndex, 6.0f, 0.1f, 0x000000C8); + if ((f64) object->velocity[1] <= 0.0) { + func_8008701C(objectIndex, 3); + } + break; + case 3: + func_800871AC(objectIndex, 0x00000064); + break; + case 4: + D_801658D6 = 1; + object->velocity[1] = -0.4f; + func_80086FD4(objectIndex); + // object->origin_pos[1] = 90.0f; + object->offset[1] = 60.0f; + // switch (D_802874D8.unk1D) { + // case 1: + // object->origin_pos[0] -= 3.0; + // object->origin_pos[2] += 15.0; + // break; + // case 2: + // object->origin_pos[0] -= 2.0; + // object->origin_pos[2] -= 15.0; + // break; + // } + break; + case 5: + if ((f64) object->offset[1] <= 8.0) { + f32_step_towards(&object->velocity[1], -0.1f, -0.01f); + } + object_add_velocity_offset_y(objectIndex); + if ((f64) object->offset[1] <= 0.0) { + func_80086FD4(objectIndex); + } + break; + case 6: + if (func_800871AC(objectIndex, 0x00000041) != 0) { + D_801658F4 = 1; + } + break; + case 7: + if (func_800871AC(objectIndex, 0x00000064) != 0) { + func_8009265C(); + func_80086F60(objectIndex); + } + break; + } + if (D_801658D6 != 0) { + object->direction_angle[0] += 0x400; + object->direction_angle[1] = 0xE800; + object->direction_angle[2] = 0xDA00; + } else { + object->direction_angle[0] += 0x400; + object->direction_angle[1] -= 0x200; + } + object_calculate_new_pos_offset(objectIndex); +} + +void OTrophy::func_80086C14(s32 objectIndex) { + switch (gObjectList[objectIndex].state) { /* irregular */ + case 1: + OTrophy::func_80086700(objectIndex); + break; + case 0: + case 2: + break; + } +} + +void OTrophy::func_80086C6C(s32 objectIndex) { + Vec3f sp24; + + sp24[0] = (gObjectList[objectIndex].pos[0] - 5.0f) + random_int(0x000AU); + sp24[2] = (gObjectList[objectIndex].pos[2] - 5.0f) + random_int(0x000AU); + if (D_801658F4 != 0) { + sp24[1] = gObjectList[objectIndex].pos[1] + 14.0; + } else { + sp24[1] = gObjectList[objectIndex].pos[1] - 2.0; + } + func_800773D8(sp24, (s32) D_801658F4); +} + +void OTrophy::func_800773D8(f32* arg0, s32 arg1) { + s32 objectIndex = add_unused_obj_index(gObjectParticle3, &gNextFreeObjectParticle3, gObjectParticle3_SIZE); + if (objectIndex != NULL_OBJECT_ID) { + func_80077138(objectIndex, arg0, arg1); + } +} + +void OTrophy::func_80077138(s32 objectIndex, Vec3f arg1, s32 arg2) { + s8 temp_v0_3; + Vec3s sp30; + + init_object(objectIndex, arg2); + gObjectList[objectIndex].unk_0D5 = 0x0C; + gObjectList[objectIndex].sizeScaling = 0.05f; + set_obj_origin_pos(objectIndex, arg1[0], arg1[1], arg1[2]); + set_obj_orientation(objectIndex, 0U, 0U, 0U); + set_obj_origin_offset(objectIndex, 0.0f, 0.0f, 0.0f); + switch (arg2) { + case 0: + gObjectList[objectIndex].velocity[1] = -1.0f; + gObjectList[objectIndex].unk_034 = (f32) ((random_int(0x004BU) * 0.01) + 0.25); + gObjectList[objectIndex].direction_angle[1] = random_int(0x0040U) << 0xA; + func_8008751C(objectIndex); + gObjectList[objectIndex].unk_084[5] = 0x001E; + break; + case 1: + gObjectList[objectIndex].velocity[1] = 1.5f; + gObjectList[objectIndex].unk_034 = (f32) ((random_int(0x0064U) * 0.01) + 0.5); + gObjectList[objectIndex].direction_angle[1] = random_int(0x0040U) << 0xA; + func_8008751C(objectIndex); + gObjectList[objectIndex].unk_084[5] = 0x0032; + break; + } + temp_v0_3 = random_int(0x000CU); + if (temp_v0_3 < 9) { + func_8005C674(temp_v0_3, &sp30[2], &sp30[1], sp30); + gObjectList[objectIndex].unk_048 = 0; + gObjectList[objectIndex].unk_084[0] = sp30[2]; + gObjectList[objectIndex].unk_084[1] = sp30[1]; + gObjectList[objectIndex].unk_084[2] = sp30[0]; + } else { + temp_v0_3 = random_int(3U); + func_8005C6B4(temp_v0_3, &sp30[2], &sp30[1], sp30); + gObjectList[objectIndex].unk_084[0] = sp30[2]; + gObjectList[objectIndex].unk_084[1] = sp30[1]; + gObjectList[objectIndex].unk_084[2] = sp30[0]; + gObjectList[objectIndex].unk_084[4] = temp_v0_3; + gObjectList[objectIndex].unk_048 = 1; + } + gObjectList[objectIndex].primAlpha = 0x00FF; + gObjectList[objectIndex].unk_084[3] = random_int(0x0800U) + 0x400; + if ((gObjectList[objectIndex].direction_angle[1] < 0x3000) || + (gObjectList[objectIndex].direction_angle[1] >= 0xB001)) { + gObjectList[objectIndex].unk_084[3] = -gObjectList[objectIndex].unk_084[3]; + } +}
\ No newline at end of file diff --git a/src/engine/objects/Trophy.h b/src/engine/objects/Trophy.h new file mode 100644 index 000000000..a227ef5b9 --- /dev/null +++ b/src/engine/objects/Trophy.h @@ -0,0 +1,58 @@ +#pragma once + +#include <libultraship.h> +#include <vector> +#include "Object.h" +#include "World.h" + +extern "C" { +#include "macros.h" +#include "main.h" +#include "vehicles.h" +#include "waypoints.h" +#include "common_structs.h" +#include "objects.h" +#include "course_offsets.h" +#include "some_data.h" +} + +class OTrophy : public OObject { +public: + enum TrophyType { + BRONZE, + SILVER, + GOLD, + BRONZE_150, + SILVER_150, + GOLD_150, + }; + + enum Behaviour { + PODIUM_CEREMONY, + STATIONARY, + ROTATE, // A dual-axis opposing rotation + ROTATE2, // A single-axis rotation + GO_FISH, + }; + + explicit OTrophy(const FVector& pos, TrophyType trophy, Behaviour bhv); + + virtual void Tick() override; + virtual void Draw(s32 cameraId) override; + void func_80086700(s32 objectIndex); + void func_80086940(s32 objectIndex); + void func_80086C14(s32 objectIndex); + void func_80086C6C(s32 objectIndex); + void func_800773D8(f32* arg0, s32 arg1); + void func_80077138(s32 objectIndex, Vec3f arg1, s32 arg2); + +private: + + s32 _idx; + TrophyType _trophy; + FVector _spawnPos; + Behaviour _bhv; + int8_t *_toggleVisibility; + Vec3f _oldPos; + bool _isMod = false; +}; |
