diff options
| author | MegaMech <7255464+MegaMech@users.noreply.github.com> | 2024-12-05 21:41:58 -0700 |
|---|---|---|
| committer | MegaMech <7255464+MegaMech@users.noreply.github.com> | 2024-12-05 21:41:58 -0700 |
| commit | 19135c9ee83643ea863d5725c22319e502938cac (patch) | |
| tree | ec4792402d73607e1c80e8b6a4cf766c5ac0e171 /src/engine/objects | |
| parent | 7628e98e37d72aa508aa1af276ef8e3485df339c (diff) | |
load course dl textures from otr and move objects
Diffstat (limited to 'src/engine/objects')
| -rw-r--r-- | src/engine/objects/OBombKart.cpp | 526 | ||||
| -rw-r--r-- | src/engine/objects/OBombKart.h | 73 | ||||
| -rw-r--r-- | src/engine/objects/OPenguin.cpp | 383 | ||||
| -rw-r--r-- | src/engine/objects/OPenguin.h | 68 | ||||
| -rw-r--r-- | src/engine/objects/OThwomp.cpp | 669 | ||||
| -rw-r--r-- | src/engine/objects/OThwomp.h | 77 |
6 files changed, 1796 insertions, 0 deletions
diff --git a/src/engine/objects/OBombKart.cpp b/src/engine/objects/OBombKart.cpp new file mode 100644 index 000000000..e51340b8f --- /dev/null +++ b/src/engine/objects/OBombKart.cpp @@ -0,0 +1,526 @@ +#include <libultraship.h> +#include "OBombKart.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; +} + +OBombKart::OBombKart(Vec3f pos, TrackWaypoint* waypoint, uint16_t waypointIndex, uint16_t state, f32 unk_3C) { + + 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]); +} + +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()) { + // This isn't functionally equivallent. + // Technicaly it should be if (kart[0].WaypointIndex < 16) + if (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; + } + set_object_flag_status_false(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_status_true(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/OBombKart.h b/src/engine/objects/OBombKart.h new file mode 100644 index 000000000..4500e41c2 --- /dev/null +++ b/src/engine/objects/OBombKart.h @@ -0,0 +1,73 @@ +#pragma once + +#include <libultraship.h> +#include "src/engine/vehicles/Vehicle.h" +#include <vector> +#include "engine/Matrix.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); + + void Spawn(); + void BeginPlay(); + void Tick(); + void Draw(s32 playerId); + void DrawBattle(s32 cameraId); + void Collision(s32 playerId, Player* player); + void SomeRender(Vec3f arg1); + void LoadMtx(); + void Waypoint(s32 screenId); +private: + Player* FindTarget(); + void Chase(Player*, Vec3f pos); + + Vec3f _spawnPos; + Player* _target = NULL; + + +}; diff --git a/src/engine/objects/OPenguin.cpp b/src/engine/objects/OPenguin.cpp new file mode 100644 index 000000000..f43a76ffc --- /dev/null +++ b/src/engine/objects/OPenguin.cpp @@ -0,0 +1,383 @@ +#include <libultraship.h> +#include <libultra/gbi.h> +#include "OPenguin.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; +} + +OPenguin::OPenguin(s32 i, Vec3f pos, u16 direction, PenguinType type, Behaviour behaviour) { + if (i >= 32) { + printf("MAX penguin REACHED (32), skipping\n"); + return; + } + _idx = i; + _type = type; + _bhv = behaviour; + + s32 objectIndex = indexObjectList1[i]; + 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; + } + +} + +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); + set_object_flag_status_false(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) { + Object* object; + + object = &gObjectList[objectIndex]; + switch (_bhv) { + case 1: // emperor + func_80085080(objectIndex); + break; + case 2: + func_8008502C(objectIndex, arg1); + break; + case 3: + func_80084D2C(objectIndex, 0); + break; + case 4: + func_80084D2C(objectIndex, 1); + break; + case 5: + func_80084D2C(objectIndex, 2); + break; + case 6: + 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::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_status_true(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_status_true(objectIndex, 0x04000220); + + 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; + } + + if ((_idx > 0) && (_idx < 9)) { + if ((_idx == 1) || (_idx == 2)) { + //object->unk_0C6 = 0x0150; + //object->unk_01C[1] = 100.0f; + } else if ((_idx == 3) || (_idx == 4)) { + //object->unk_0C6 = 0x0100; + //object->unk_01C[1] = 80.0f; + } else if ((_idx == 5) || (_idx == 6)) { + //object->unk_0C6 = 0xFF00; + //object->unk_01C[1] = 80.0f; + } else if ((_idx == 7) || (_idx == 8)) { + //object->unk_0C6 = 0x0150; + //object->unk_01C[1] = 80.0f; + } + //object->unk_0C4 = (_idx << 0xF) & 0xFFFF; + //object->surfaceHeight = -80.0f; + //object->sizeScaling = 0.08f; + //object->unk_0DD = 2; + //func_800722A4(objectIndex, 8); + } else if ((_idx > 8) && (_idx < 15)) { + switch (_idx) { + case 9: + if (gGamestate != CREDITS_SEQUENCE) { + } else { + //object->sizeScaling = 0.15f; + } + //object->unk_0C6 = 0x9000; + if (gIsMirrorMode != 0) { + //object->unk_0C6 -= 0x4000; + } + //object->unk_0DD = 3; + break; + case 10: + //object->unk_0C6 = 0x5000; + if (gIsMirrorMode != 0) { + //object->unk_0C6 += 0x8000; + } + //object->unk_0DD = 4; + break; + case 11: + //object->unk_0C6 = 0xC000; + //object->unk_0DD = 6; + if (gIsMirrorMode != 0) { + //object->unk_0C6 += 0x8000; + } + break; + case 12: + //object->unk_0C6 = 0x4000; + //object->unk_0DD = 6; + if (gIsMirrorMode != 0) { + //object->unk_0C6 += 0x8000; + } + break; + case 13: + // object->unk_0C6 = 0x8000; + //object->unk_0DD = 6; + if (gIsMirrorMode != 0) { + //object->unk_0C6 -= 0x4000; + } + break; + case 14: + //object->unk_0C6 = 0x9000; + //object->unk_0DD = 6; + if (gIsMirrorMode != 0) { + //object->unk_0C6 -= 0x4000; + } + break; + default: + break; + } + //set_obj_direction_angle(objectIndex, 0U, object->unk_0C6 + 0x8000, 0U); + //object->surfaceHeight = 5.0f; + //object->sizeScaling = 0.04f; + //func_800722A4(objectIndex, 0x00000014); + } + 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/OPenguin.h b/src/engine/objects/OPenguin.h new file mode 100644 index 000000000..111f629ba --- /dev/null +++ b/src/engine/objects/OPenguin.h @@ -0,0 +1,68 @@ +#pragma once + +#include <libultraship.h> +#include <vector> + +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" +} + +//! @todo Make shadow size bigger if thwomp is scaled up +//! @todo make adjustable properties for squishSize and boundingBoxSize + + /** + * Thwomp GameObject + * + * 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 OPenguin { +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(s32 i, Vec3f pos, u16 direction, PenguinType type, Behaviour behaviour); + + void Tick(); + void Draw(s32 playerId); +private: + void Behaviours(s32 objectIndex, s32 arg1); + void EmperorPenguin(s32 objectIndex); + void InitEmperorPenguin(s32 objectIndex); + void OtherPenguin(s32 objectIndex); + void InitOtherPenguin(s32 objectIndex); + + s32 _idx; + PenguinType _type; + Behaviour _bhv; + + +}; diff --git a/src/engine/objects/OThwomp.cpp b/src/engine/objects/OThwomp.cpp new file mode 100644 index 000000000..e2700f015 --- /dev/null +++ b/src/engine/objects/OThwomp.cpp @@ -0,0 +1,669 @@ +#include <libultraship.h> +#include <libultra/gbi.h> +#include "OThwomp.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" +extern s8 gPlayerCount; +} + +OThwomp::OThwomp(s32 i, s16 x, s16 z, s16 direction, f32 scale, s16 behaviour, s16 primAlpha, u16 boundingBoxSize) { + if (i >= 32) { + printf("MAX THWOMPS REACHED (32), skipping\n"); + return; + } + _idx = i; + _faceDirection = direction; + _boundingBoxSize = boundingBoxSize; + State = (States)behaviour; + s32 objectId = indexObjectList1[i]; + 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; +} + +void OThwomp::Tick() { // func_80081210 + Player* player; + s32 objectIndex; + s32 var_s2_3; + s32 var_s4; + + objectIndex = indexObjectList1[_idx]; + func_800722CC(objectIndex, 0x00000010); + func_8008A4CC(objectIndex); + + OThwomp::func_8007F8D8(); + + 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) { + func_80080A14(objectIndex, player); + } + if (is_obj_flag_status_active(objectIndex, 0x00010000) != 0) { + func_80080A4C(objectIndex, var_s4); + } + + } + func_8007542C(3); + + objectIndex = indexObjectList1[_idx]; + if (func_80072320(objectIndex, 0x00000020) == 0) { + return; + } + + func_800722CC(objectIndex, 0x00000020); + func_80080FEC(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; + } + func_800810F4(objectIndex); + if (gObjectList[objectIndex].state != 0) { + return; + } + delete_object_wrapper(&gObjectParticle2[var_s4]); + } +} + +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; + func_8007F660(objectIndex, playerId, temp_s7); + } + + } else if ((waypoint >= 0xD7) && (waypoint < 0xE2)) { + objectIndex = indexObjectList1[_idx]; + if (gObjectList[objectIndex].unk_0D5 == 3) { + var_s6 = 1; + func_8007F6C4(objectIndex, playerId); + } + } + return var_s6; +} + +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)); + } + func_80080DE4(objectIndex); + func_80075304(gObjectList[objectIndex].pos, 3, 3, D_8018D3C4); + set_object_flag_status_false(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); + } + } +} + +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]; + set_object_flag_status_false(objectIndex, 0x00070000); + func_800722CC(objectIndex, 0x00000110); + } + + translate_thwomp_lights(objectIndex); + + 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); + thwomp_lights(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); + } +} + +/** 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; + } + 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_status_true(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; + } + 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_status_true(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; + } + func_8007E63C(objectIndex); + 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_status_true(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::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; + } + 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_status_true(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_status_true(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) { + func_8007E63C(objectIndex); + 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)); + } + } +} + +// 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_status_true(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); +} diff --git a/src/engine/objects/OThwomp.h b/src/engine/objects/OThwomp.h new file mode 100644 index 000000000..2d8a2a075 --- /dev/null +++ b/src/engine/objects/OThwomp.h @@ -0,0 +1,77 @@ +#pragma once + +#include <libultraship.h> +#include <vector> + +extern "C" { +#include "macros.h" +#include "main.h" +#include "vehicles.h" +#include "waypoints.h" +#include "common_structs.h" +#include "objects.h" +} + +//! @todo Make shadow size bigger if thwomp is scaled up +//! @todo make adjustable properties for squishSize and boundingBoxSize + +/** + * Thwomp GameObject + * + * 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 { +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(s32 i, s16 x, s16 z, s16 direction, f32 scale, s16 behaviour, s16 primAlpha, u16 boundingBoxSize); + + void Tick(); + void func_80080B28(s32 objectIndex, s32 playerId); + void Draw(s32 playerId); + void DrawModel(s32); + s32 func_8007F75C(s32 playerId); + void func_8007F8D8(); + + 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 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); +private: + 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; + + +}; |
