summaryrefslogtreecommitdiff
path: root/src/engine/objects
diff options
context:
space:
mode:
authorMegaMech <MegaMech@users.noreply.github.com>2024-12-31 09:50:21 -0700
committerGitHub <noreply@github.com>2024-12-31 09:50:21 -0700
commit1101dcafb08a1f17b06306ff44a9d869ffeb7862 (patch)
treee63c623057b555fedd3d017b5d6c34d12de31098 /src/engine/objects
parent7e6aba50a2a2fa80db153ad154f5ae77cc344650 (diff)
parent7ee714e4e93609a5dbf1dc648df0ce504e3d7284 (diff)
Merge pull request #133 from MegaMech/next
Fix loadtile, Impl more OObjects
Diffstat (limited to 'src/engine/objects')
-rw-r--r--src/engine/objects/BombKart.cpp528
-rw-r--r--src/engine/objects/BombKart.h85
-rw-r--r--src/engine/objects/CheepCheep.cpp243
-rw-r--r--src/engine/objects/CheepCheep.h48
-rw-r--r--src/engine/objects/Crab.cpp175
-rw-r--r--src/engine/objects/Crab.h48
-rw-r--r--src/engine/objects/Flagpole.cpp85
-rw-r--r--src/engine/objects/Flagpole.h45
-rw-r--r--src/engine/objects/Hedgehog.cpp183
-rw-r--r--src/engine/objects/Hedgehog.h53
-rw-r--r--src/engine/objects/HotAirBalloon.cpp174
-rw-r--r--src/engine/objects/HotAirBalloon.h34
-rw-r--r--src/engine/objects/Lakitu.cpp856
-rw-r--r--src/engine/objects/Lakitu.h82
-rw-r--r--src/engine/objects/Mole.cpp422
-rw-r--r--src/engine/objects/Mole.h52
-rw-r--r--src/engine/objects/Object.cpp23
-rw-r--r--src/engine/objects/Object.h26
-rw-r--r--src/engine/objects/Penguin.cpp413
-rw-r--r--src/engine/objects/Penguin.h71
-rw-r--r--src/engine/objects/Podium.cpp142
-rw-r--r--src/engine/objects/Podium.h39
-rw-r--r--src/engine/objects/Seagull.cpp194
-rw-r--r--src/engine/objects/Seagull.h47
-rw-r--r--src/engine/objects/Snowman.cpp343
-rw-r--r--src/engine/objects/Snowman.h54
-rw-r--r--src/engine/objects/Thwomp.cpp1450
-rw-r--r--src/engine/objects/Thwomp.h120
-rw-r--r--src/engine/objects/TrashBin.cpp231
-rw-r--r--src/engine/objects/TrashBin.h45
-rw-r--r--src/engine/objects/Trophy.cpp394
-rw-r--r--src/engine/objects/Trophy.h58
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;
+};