diff options
Diffstat (limited to 'src/engine/objects')
35 files changed, 1203 insertions, 286 deletions
diff --git a/src/engine/objects/Bat.cpp b/src/engine/objects/Bat.cpp index e4f3a2086..25c28c751 100644 --- a/src/engine/objects/Bat.cpp +++ b/src/engine/objects/Bat.cpp @@ -19,8 +19,13 @@ const char* sBoardwalkTexList[] = { gTextureBat1, gTextureBat2, gTextureBat3, gT size_t OBat::_count = 0; -OBat::OBat(const FVector& pos, const IRotator& rot) { +OBat::OBat(const SpawnParams& params) : OObject(params) { Name = "Bat"; + ResourceName = "mk:bat"; + + //! @warning this likely needs to be rot.Set() + IRotator rot = params.Rotation.value_or(IRotator(0, 0, 0)); + find_unused_obj_index(&_objectIndex); init_texture_object(_objectIndex, (uint8_t*) d_course_banshee_boardwalk_bat_tlut, sBoardwalkTexList, 0x20U, diff --git a/src/engine/objects/Bat.h b/src/engine/objects/Bat.h index d6b7e8774..75318e5d8 100644 --- a/src/engine/objects/Bat.h +++ b/src/engine/objects/Bat.h @@ -28,7 +28,18 @@ extern "C" { */ class OBat : public OObject { public: - explicit OBat(const FVector& pos, const IRotator& rot); + + // This is simply a helper function to keep Spawning code clean + static inline OBat* Spawn(const FVector& pos, const IRotator& rot) { + SpawnParams params = { + .Name = "mk:bat", + .Location = pos, + .Rotation = rot, + }; + return static_cast<OBat*>(gWorldInstance.AddObject(new OBat(params))); + } + + explicit OBat(const SpawnParams& params); ~OBat() { _count--; diff --git a/src/engine/objects/BombKart.cpp b/src/engine/objects/BombKart.cpp index 78d4762a9..c465aea43 100644 --- a/src/engine/objects/BombKart.cpp +++ b/src/engine/objects/BombKart.cpp @@ -29,52 +29,61 @@ extern s8 gPlayerCount; size_t OBombKart::_count = 0; -OBombKart::OBombKart(FVector pos, TrackPathPoint* waypoint, uint16_t waypointIndex, uint16_t state, f32 unk_3C) { +OBombKart::OBombKart(const SpawnParams& params) : OObject(params) { Name = "Bomb Kart"; + ResourceName = "mk:bomb_kart"; + _idx = _count; - Vec3f _pos = {0, 0, 0}; + uint32_t pathIndex = params.PathIndex.value_or(0); + uint32_t pathPoint = params.PathPoint.value_or(0); + FVector constPos; - 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 + // Spawn kart on a surface with the provided position + if (params.Location.has_value()) { + constPos = params.Location.value(); // 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.y > 2000.0f) ? pos.y : 2000.0f; - _pos[0] = pos.x; - _pos[1] = spawn_actor_on_surface(pos.x, height, pos.z); - _pos[2] = pos.z; + f32 height = (constPos.y > 2000.0f) ? constPos.y : 2000.0f; + constPos.y = spawn_actor_on_surface(constPos.x, height, constPos.z); + } else { // Spawn kart on waypoint + constPos.x = gTrackPaths[pathIndex][pathPoint].x; + constPos.y = gTrackPaths[pathIndex][pathPoint].y; + constPos.z = gTrackPaths[pathIndex][pathPoint].z; } - 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]); + Behaviour = static_cast<OBombKart::States>(params.Behaviour.value_or(OBombKart::States::COUNTERCLOCKWISE)); + SpeedB = params.SpeedB.value_or(2.7f); // Chase speed + + WaypointIndex = params.PathPoint.value_or(0); + Unk_3C = params.Speed.value_or(0); + + Pos[0] = constPos.x; + Pos[1] = constPos.y; + Pos[2] = constPos.z; + CenterY = constPos.y; + WheelPos[0][0] = constPos.x; + WheelPos[0][1] = constPos.y; + WheelPos[0][2] = constPos.z; + WheelPos[1][0] = constPos.x; + WheelPos[1][1] = constPos.y; + WheelPos[1][2] = constPos.z; + WheelPos[2][0] = constPos.x; + WheelPos[2][1] = constPos.y; + WheelPos[2][2] = constPos.z; + WheelPos[3][0] = constPos.x; + WheelPos[3][1] = constPos.y; + WheelPos[3][2] = constPos.z; + check_bounding_collision(&_Collision, 2.0f, constPos.x, constPos.y, constPos.z); find_unused_obj_index(&_objectIndex); + Object* object = &gObjectList[_objectIndex]; + + object->origin_pos[0] = Pos[0]; + object->origin_pos[1] = Pos[1]; + object->origin_pos[2] = Pos[2]; + _count++; } @@ -103,7 +112,7 @@ void OBombKart::Tick() { f32 sp94; f32 sp88; Vec3f newPos; - States state; + OBombKart::States state; u16 bounceTimer; UNUSED u16 sp4C; u16 temp_t6; @@ -112,7 +121,7 @@ void OBombKart::Tick() { TrackPathPoint* temp_v0_4; Player* player; - state = State; + state = Behaviour; if (state == States::DISABLED) { return; @@ -137,6 +146,7 @@ void OBombKart::Tick() { if ((((temp_f0 * temp_f0) + (temp_f2 * temp_f2)) + (temp_f12 * temp_f12)) < 25.0f) { circleTimer = 0; state = States::EXPLODE; + Behaviour = States::EXPLODE; player->soundEffects |= 0x400000; player->type &= ~0x2000; } @@ -151,6 +161,7 @@ void OBombKart::Tick() { 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; + Behaviour = States::EXPLODE; circleTimer = 0; if (IsFrappeSnowland()) { player->soundEffects |= 0x01000000; @@ -163,44 +174,44 @@ void OBombKart::Tick() { } } switch(state) { - case States::CCW: + case States::COUNTERCLOCKWISE: 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 = &gTrackPaths[0][waypoint]; - newPos[0] = temp_v0_2->posX + sp118; + newPos[0] = temp_v0_2->x + sp118; newPos[1] = CenterY + 3.5f; - newPos[2] = temp_v0_2->posZ + temp_f0_3; + newPos[2] = temp_v0_2->z + 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; + D_80162FC0[0] = temp_v0_2->x + sp118; + D_80162FC0[1] = temp_v0_2->y; + D_80162FC0[2] = temp_v0_2->z + temp_f0_3; someRot = (get_angle_between_two_vectors(D_80162FB0, D_80162FC0) * 0xFFFF) / 65520; break; - case States::CW: + case States::CLOCKWISE: 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 = &gTrackPaths[0][waypoint]; - newPos[0] = temp_v0_2->posX + sp118; + newPos[0] = temp_v0_2->x + sp118; newPos[1] = CenterY + 3.5f; - newPos[2] = temp_v0_2->posZ + temp_f0_3; + newPos[2] = temp_v0_2->z + 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; + D_80162FC0[0] = temp_v0_2->x + sp118; + D_80162FC0[1] = temp_v0_2->y; + D_80162FC0[2] = temp_v0_2->z + temp_f0_3; someRot = (get_angle_between_two_vectors(D_80162FB0, D_80162FC0) * 0xFFFF) / 65520; break; case States::STATIONARY: @@ -218,13 +229,13 @@ void OBombKart::Tick() { } if (((s32) waypoint) < 0x1A) { temp_v0_2 = &gTrackPaths[3][(waypoint + 1) % gPathCountByPathIndex[3]]; - D_80162FB0[0] = temp_v0_2->posX; - D_80162FB0[1] = temp_v0_2->posY; - D_80162FB0[2] = temp_v0_2->posZ; + D_80162FB0[0] = temp_v0_2->x; + D_80162FB0[1] = temp_v0_2->y; + D_80162FB0[2] = temp_v0_2->z; temp_v0_4 = &gTrackPaths[3][(waypoint + 2) % gPathCountByPathIndex[3]]; - D_80162FC0[0] = temp_v0_4->posX; - D_80162FC0[1] = temp_v0_4->posY; - D_80162FC0[2] = temp_v0_4->posZ; + D_80162FC0[0] = temp_v0_4->x; + D_80162FC0[1] = temp_v0_4->y; + D_80162FC0[2] = temp_v0_4->z; someRot = (get_angle_between_two_vectors(D_80162FB0, D_80162FC0) * 0xFFFF) / 65520; } else { D_80162FB0[0] = newPos[0]; @@ -266,13 +277,13 @@ void OBombKart::Tick() { break; case States::EXPLODE: temp_v0_2 = &gTrackPaths[0][waypoint]; - D_80162FB0[0] = temp_v0_2->posX; - D_80162FB0[1] = temp_v0_2->posY; - D_80162FB0[2] = temp_v0_2->posZ; + D_80162FB0[0] = temp_v0_2->x; + D_80162FB0[1] = temp_v0_2->y; + D_80162FB0[2] = temp_v0_2->z; temp_v0_4 = &gTrackPaths[0][(waypoint + 1) % gPathCountByPathIndex[0]]; - D_80162FC0[0] = temp_v0_4->posX; - D_80162FC0[1] = temp_v0_4->posY; - D_80162FC0[2] = temp_v0_4->posZ; + D_80162FC0[0] = temp_v0_4->x; + D_80162FC0[1] = temp_v0_4->y; + D_80162FC0[2] = temp_v0_4->z; newPos[1] += 3.0f - (circleTimer * 0.3f); someRot = (get_angle_between_two_vectors(D_80162FB0, D_80162FC0) * 0xFFFF) / 65520; break; @@ -290,8 +301,9 @@ void OBombKart::Tick() { spA0 = temp_f2_4; sp94 = temp_f2_4; sp88 = temp_f2_4; - if (circleTimer >= 31) { + if (circleTimer > 30) { state = States::DISABLED; + Behaviour = States::DISABLED; } } else { sp118 = coss(0xFFFF - someRot) * 1.5f; @@ -326,7 +338,7 @@ void OBombKart::Tick() { WaypointIndex = waypoint; Unk_3C = unk_3C; SomeRot = someRot; - State = state; + // State = state; BounceTimer = bounceTimer; CircleTimer = circleTimer; } @@ -373,8 +385,8 @@ void OBombKart::Draw(s32 cameraId) { } // huh??? - s32 state = State; - if (State != States::DISABLED) { + OBombKart::States state = Behaviour; + if (state != States::DISABLED) { gObjectList[_objectIndex].pos[0] = Pos[0]; gObjectList[_objectIndex].pos[1] = Pos[1]; gObjectList[_objectIndex].pos[2] = Pos[2]; @@ -386,7 +398,7 @@ void OBombKart::Draw(s32 cameraId) { D_80183E80[2] = 0x8000; func_800563DC(_objectIndex, cameraId, 0x000000FF); OBombKart::SomeRender(camera->pos); - if (((u32) temp_s4 < 0x4E21U) && (state != BOMB_STATE_EXPLODED)) { + if (((u32) temp_s4 < 0x4E21U) && (state != OBombKart::States::EXPLODE)) { OBombKart::LoadMtx(); } } @@ -436,7 +448,9 @@ void OBombKart::Waypoint(s32 screenId) { playerWaypoint = gNearestPathPointByPlayerId[screenId]; playerHUD[screenId].unk_74 = 0; - if ((State == States::EXPLODE) || (State == States::DISABLED)) { return; }; + + OBombKart::States state = Behaviour; + if ((state == States::EXPLODE) || (state == States::DISABLED)) { return; }; bombWaypoint = WaypointIndex; waypointDiff = bombWaypoint - playerWaypoint; if ((waypointDiff < -5) || (waypointDiff > 0x1E)) { return; }; @@ -455,7 +469,7 @@ Player* OBombKart::FindTarget() { } void OBombKart::Chase(Player* player, Vec3f pos) { - const f32 speed = 2.7f; // Speed the kart uses in a chase + const f32 speed = SpeedB; // Speed the kart uses in a chase if (!player) return; // Ensure player is valid @@ -471,18 +485,18 @@ void OBombKart::Chase(Player* player, Vec3f pos) { // Reset distance if (xz_dist > 700.0f) { _target = NULL; - pos[0] = _spawnPos[0]; - pos[1] = _spawnPos[1]; - pos[2] = _spawnPos[2]; + pos[0] = gObjectList[_objectIndex].origin_pos[0]; + pos[1] = gObjectList[_objectIndex].origin_pos[1]; + pos[2] = gObjectList[_objectIndex].origin_pos[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]; + pos[0] = gObjectList[_objectIndex].origin_pos[0]; + pos[1] = gObjectList[_objectIndex].origin_pos[1]; + pos[2] = gObjectList[_objectIndex].origin_pos[2]; return; } @@ -509,4 +523,62 @@ void OBombKart::Chase(Player* player, Vec3f pos) { pos[2] = newPosition[2]; check_bounding_collision(&_Collision, 10.0f, pos[0], pos[1], pos[2]); -}
\ No newline at end of file +} + +void OBombKart::Translate(FVector pos) { + Pos[0] = pos.x; + Pos[1] = pos.y; + Pos[2] = pos.z; + if (_objectIndex != -1) { + Object* object = &gObjectList[_objectIndex]; + object->pos[0] = pos.x; + object->pos[1] = pos.y; + object->pos[2] = pos.z; + object->origin_pos[0] = pos.x; + object->origin_pos[1] = pos.y; + object->origin_pos[2] = pos.z; + } else { + printf("Editor tried to translate null OObject\n"); + } +} + +void OBombKart::DrawEditorProperties() { + Object* obj = &gObjectList[_objectIndex]; + + ImGui::Text("Behaviour"); + ImGui::SameLine(); + + int32_t behaviour = static_cast<int32_t>(Behaviour); + const char* items[] = { "Disabled", "Counterclockwise", "Clockwise", "Stationary", "Chase", "Explode", "Podium" }; + + if (ImGui::Combo("##Behaviour", &behaviour, items, IM_ARRAYSIZE(items))) { + Behaviour = static_cast<OBombKart::States>(behaviour); + } + + ImGui::Text("Location"); + ImGui::SameLine(); + FVector location = FVector(obj->pos[0], obj->pos[1], obj->pos[2]); + if (ImGui::DragFloat3("##Location", (float*)&location)) { + Translate(location); + gEditor.eObjectPicker.eGizmo.Pos = location; + } + + ImGui::SameLine(); + if (ImGui::Button(ICON_FA_UNDO "##ResetPos")) { + FVector location = FVector(0, 0, 0); + Translate(location); + gEditor.eObjectPicker.eGizmo.Pos = location; + } + + ImGui::Text("Chase Speed"); + ImGui::SameLine(); + + float speed = SpeedB; + if (ImGui::DragFloat("##Speed", &speed, 0.1f)) { + SpeedB = speed; + } + ImGui::SameLine(); + if (ImGui::Button(ICON_FA_UNDO "##ResetSpeed")) { + SpeedB = 2.7f; + } +} diff --git a/src/engine/objects/BombKart.h b/src/engine/objects/BombKart.h index 86c2c0ef3..20c8392a0 100644 --- a/src/engine/objects/BombKart.h +++ b/src/engine/objects/BombKart.h @@ -26,8 +26,8 @@ class OBombKart : public OObject { public: enum States : uint16_t { // 0,1,3,5 DISABLED, - CCW, - CW, + COUNTERCLOCKWISE, + CLOCKWISE, STATIONARY, CHASE, EXPLODE, @@ -49,8 +49,34 @@ class OBombKart : public OObject { f32 CenterY; // Center of the circle Collision _Collision; + + // This is simply a helper function to keep Spawning code clean + // Spawn object at a position + static inline OBombKart* Spawn(FVector pos, uint16_t behaviour, f32 unk_3C) { + SpawnParams params = { + .Name = "mk:bomb_kart", + .Behaviour = behaviour, + .Location = pos, + .Speed = unk_3C, // Only used for podium ceremony. Arbitrarily chose Speed for this + .SpeedB = 2.7f, // Chase speed + }; + return static_cast<OBombKart*>(gWorldInstance.AddObject(new OBombKart(params))); + } + + // Spawn object at a point along the tracks path + static inline OBombKart* Spawn(uint32_t pathIndex, uint32_t pathPoint, uint16_t behaviour, f32 unk_3C) { + SpawnParams params = { + .Name = "mk:bomb_kart", + .Behaviour = behaviour, + .PathIndex = pathIndex, + .PathPoint = pathPoint, + .Speed = unk_3C, // Only used for podium ceremony. Arbitrarily chose Speed for this + }; + return static_cast<OBombKart*>(gWorldInstance.AddObject(new OBombKart(params))); + } + // Set waypoint to NULL if using a spawn position and not a waypoint. - explicit OBombKart(FVector pos, TrackPathPoint* waypoint, uint16_t waypointIndex, uint16_t state, f32 unk_3C); + explicit OBombKart(const SpawnParams& params); ~OBombKart() { _count--; @@ -62,17 +88,20 @@ class OBombKart : public OObject { virtual void Tick() override; virtual void Draw(s32 cameraId) override; + virtual void Translate(FVector pos) override; + virtual void DrawEditorProperties() override; void DrawBattle(s32 cameraId); void SomeRender(Vec3f arg1); void LoadMtx(); void Waypoint(s32 screenId); + OBombKart::States Behaviour = OBombKart::States::COUNTERCLOCKWISE; + float SpeedB = 2.7f; 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/Boos.cpp b/src/engine/objects/Boos.cpp index dbd380901..a22b0edd0 100644 --- a/src/engine/objects/Boos.cpp +++ b/src/engine/objects/Boos.cpp @@ -23,12 +23,20 @@ extern "C" { size_t OBoos::_count = 0; -OBoos::OBoos(size_t numBoos, const IPathSpan& leftBoundary, const IPathSpan& active, const IPathSpan& rightBoundary) { +OBoos::OBoos(const SpawnParams& params) : OObject(params) { Name = "Boos"; + ResourceName = "mk:boos"; + + size_t numBoos = params.Count.value_or(5); + + ActiveZone = params.TriggerSpan.value_or(IPathSpan(30, 50)); + LeftTrigger = params.LeftExitSpan.value_or(IPathSpan(0, 10)); + RightTrigger = params.RightExitSpan.value_or(IPathSpan(80, 100)); + // Max five boos allowed due to limited splines // D_800E5D9C if (numBoos > 10) { - printf("Boos.cpp: Only 10 boos allowed.\n"); + printf("[Boos.cpp] Only 10 boos allowed.\n"); numBoos = 10; } @@ -40,9 +48,14 @@ OBoos::OBoos(size_t numBoos, const IPathSpan& leftBoundary, const IPathSpan& act } _numBoos = numBoos; - _leftBoundary = leftBoundary; - _active = active; - _rightBoundary = rightBoundary; +} + +void OBoos::SetSpawnParams(SpawnParams& params) { + OObject::SetSpawnParams(params); + params.Count = _numBoos; + params.LeftExitSpan = LeftTrigger; + params.TriggerSpan = ActiveZone; + params.RightExitSpan = RightTrigger; } void OBoos::Tick() { @@ -123,7 +136,8 @@ void OBoos::func_8007CA70(void) { if (_isActive == false) { _playerId = OBoos::func_8007C9F8(); point = &gNearestPathPointByPlayerId[_playerId]; - if ((*point > _active.Start) && (*point < _active.End)) { + + if ((*point > ActiveZone.Start) && (*point < ActiveZone.End)) { // First group entrance OBoos::BooStart(0, _playerId); } @@ -131,11 +145,14 @@ void OBoos::func_8007CA70(void) { if (_isActive == true) { point = &gNearestPathPointByPlayerId[_playerId]; - if ((*point > _leftBoundary.Start) && (*point < _leftBoundary.End)) { + // Left boundary + if ((*point > LeftTrigger.Start) && (*point < LeftTrigger.End)) { // First group exit reverse direction OBoos::BooExit(0); } - if ((*point > _rightBoundary.Start) && (*point < _rightBoundary.End)) { + + // Right boundary + if ((*point > RightTrigger.Start) && (*point < RightTrigger.End)) { // First group exit OBoos::BooExit(0); } @@ -274,3 +291,50 @@ void OBoos::BooExit(s32 group) { _isActive = false; } + +void OBoos::DrawEditorProperties() { + ImGui::Text("Num Boos"); + ImGui::SameLine(); + + int count = static_cast<int>(_count); + if (ImGui::InputInt("##Count", &count)) { + // Clamp to uint32_t range (only lower bound needed if assuming positive values) + if (count < 0) count = 0; + if (count > 10) count = 10; + _count = static_cast<uint32_t>(count); + } + ImGui::SameLine(); + if (ImGui::Button(ICON_FA_UNDO "##ResetCount")) { + _count = 5; + } + + ImGui::Text("Left Exit Span"); + ImGui::SameLine(); + + if (ImGui::DragInt2("##LeftExitSpan", (int*)&LeftTrigger)) { + } + ImGui::SameLine(); + if (ImGui::Button(ICON_FA_UNDO "##ResetLeftExitSpan")) { + LeftTrigger = IPathSpan(0, 0); + } + + ImGui::Text("Trigger Span"); + ImGui::SameLine(); + + if (ImGui::DragInt2("##TriggerSpan", (int*)&ActiveZone)) { + } + ImGui::SameLine(); + if (ImGui::Button(ICON_FA_UNDO "##ResetTriggerSpan")) { + ActiveZone = IPathSpan(0, 0); + } + + ImGui::Text("Right Exit Span"); + ImGui::SameLine(); + + if (ImGui::DragInt2("##RightExitSpan", (int*)&RightTrigger)) { + } + ImGui::SameLine(); + if (ImGui::Button(ICON_FA_UNDO "##ResetRightExitSpan")) { + RightTrigger = IPathSpan(0, 0); + } +} diff --git a/src/engine/objects/Boos.h b/src/engine/objects/Boos.h index cc49df474..693405dcb 100644 --- a/src/engine/objects/Boos.h +++ b/src/engine/objects/Boos.h @@ -36,7 +36,19 @@ extern "C" { */ class OBoos : public OObject { public: - explicit OBoos(size_t numBoos, const IPathSpan& leftBoundary, const IPathSpan& active, const IPathSpan& rightBoundary); + // This is simply a helper function to keep Spawning code clean + static inline OBoos* Spawn(size_t numBoos, const IPathSpan& leftBoundary, const IPathSpan& triggerBoundary, const IPathSpan& rightBoundary) { + SpawnParams params = { + .Name = "mk:boos", + .Count = numBoos, + .LeftExitSpan = leftBoundary, + .TriggerSpan = triggerBoundary, + .RightExitSpan = rightBoundary, + }; + return static_cast<OBoos*>(gWorldInstance.AddObject(new OBoos(params))); + } + + explicit OBoos(const SpawnParams& params); ~OBoos() { _count--; @@ -46,8 +58,10 @@ public: return _count; } + virtual void SetSpawnParams(SpawnParams& params) override; virtual void Tick() override; virtual void Draw(s32 cameraId) override; + virtual void DrawEditorProperties() override; void func_800523B8(s32 objectIndex, s32 arg1, u32 arg2); void func_8007CA70(void); @@ -59,6 +73,9 @@ public: void BooExit(s32 someIndex); void func_8007C550(s32 objectIndex); + IPathSpan LeftTrigger; + IPathSpan ActiveZone; + IPathSpan RightTrigger; private: FVector _pos; static size_t _count; @@ -68,8 +85,4 @@ private: bool _isActive = false; s32 _playerId = 0; - - IPathSpan _leftBoundary; - IPathSpan _active; - IPathSpan _rightBoundary; }; diff --git a/src/engine/objects/ChainChomp.cpp b/src/engine/objects/ChainChomp.cpp index 0d7ec6bc9..fd3faa622 100644 --- a/src/engine/objects/ChainChomp.cpp +++ b/src/engine/objects/ChainChomp.cpp @@ -18,6 +18,7 @@ size_t OChainChomp::_count = 0; OChainChomp::OChainChomp() { Name = "Chain Chomp"; + ResourceName = "mk:chain_chomp"; _idx = _count; init_object(indexObjectList2[_count], 0); _objectIndex = indexObjectList2[_count]; @@ -92,7 +93,7 @@ void OChainChomp::func_80085878(s32 objectIndex, s32 arg1) { object->unk_084[8] = (arg1 * 0x12C) + 0x1F4; set_obj_origin_pos(objectIndex, 0.0f, -15.0f, 0.0f); temp_v0 = &gCurrentTrackPath[(u16) object->unk_084[8]]; - set_obj_origin_offset(objectIndex, temp_v0->posX, temp_v0->posY, temp_v0->posZ); + set_obj_origin_offset(objectIndex, temp_v0->x, temp_v0->y, temp_v0->z); set_obj_direction_angle(objectIndex, 0U, 0U, 0U); object->unk_034 = 4.0f; object->type = get_animation_length(d_rainbow_road_unk3, 0); diff --git a/src/engine/objects/CheepCheep.cpp b/src/engine/objects/CheepCheep.cpp index ed489dc88..a2432f57e 100644 --- a/src/engine/objects/CheepCheep.cpp +++ b/src/engine/objects/CheepCheep.cpp @@ -1,8 +1,10 @@ #include "CheepCheep.h" +#include "port/Game.h" #include "assets/banshee_boardwalk_data.h" #include "assets/common_data.h" + extern "C" { #include "math_util.h" #include "math_util_2.h" @@ -17,17 +19,22 @@ extern Vec3s D_800E634C[]; extern Lights1 D_800E45C0[]; } -OCheepCheep::OCheepCheep(const FVector& pos, CheepType type, IPathSpan span) { +OCheepCheep::OCheepCheep(const SpawnParams& params) : OObject(params) { Name = "Cheep Cheep"; - _type = type; - _spawnPos = pos; - _span = span; + ResourceName = "mk:cheep_cheep"; + _behaviour = static_cast<Behaviour>(params.Behaviour.value_or(0)); +} + +void OCheepCheep::SetSpawnParams(SpawnParams& params) { + OObject::SetSpawnParams(params); + params.Behaviour = static_cast<int16_t>(_behaviour); + params.PathSpan = ActivationPoints; } void OCheepCheep::Tick() { // update_cheep_cheep s32 objectIndex; - switch (_type) { - case CheepType::RACE: + switch (_behaviour) { + case Behaviour::RACE: UNUSED s32 pad; OCheepCheep::func_8007BD04(0); @@ -35,7 +42,7 @@ void OCheepCheep::Tick() { // update_cheep_cheep OCheepCheep::func_8007BBBC(objectIndex); object_calculate_new_pos_offset(objectIndex); break; - case CheepType::PODIUM_CEREMONY: + case Behaviour::PODIUM_CEREMONY: objectIndex = indexObjectList2[0]; if (D_801658BC == 1) { D_801658BC = 0; @@ -115,9 +122,11 @@ void OCheepCheep::func_8007BD04(s32 playerId) { objectIndex = indexObjectList2[0]; if (gObjectList[objectIndex].state == 0) { - if (((s32) gNearestPathPointByPlayerId[playerId] >= _span.Start) && - ((s32) gNearestPathPointByPlayerId[playerId] <= _span.End)) { - set_obj_origin_pos(objectIndex, xOrientation * _spawnPos.x, _spawnPos.y, _spawnPos.z); + IPathSpan span = ActivationPoints; + if (((s32) gNearestPathPointByPlayerId[playerId] >= span.Start) && + ((s32) gNearestPathPointByPlayerId[playerId] <= span.End)) { + FVector pos = SpawnPos; + set_obj_origin_pos(objectIndex, xOrientation * pos.x, pos.y, pos.z); init_object(objectIndex, 1); } } @@ -242,3 +251,35 @@ void OCheepCheep::func_8007BFB0(s32 objectIndex) { object_add_velocity_offset_y(objectIndex); object_calculate_new_pos_offset(objectIndex); } + +void OCheepCheep::DrawEditorProperties() { + Object* obj = &gObjectList[_objectIndex]; + + ImGui::Text("Location"); + ImGui::SameLine(); + FVector location = FVector(obj->pos[0], obj->pos[1], obj->pos[2]); + if (ImGui::DragFloat3("##Location", (float*)&location)) { + Translate(location); + gEditor.eObjectPicker.eGizmo.Pos = location; + } + + ImGui::SameLine(); + if (ImGui::Button(ICON_FA_UNDO "##ResetPos")) { + FVector location = FVector(0, 0, 0); + Translate(location); + gEditor.eObjectPicker.eGizmo.Pos = location; + } + + IPathSpan span = ActivationPoints; + + ImGui::Text("Path Span"); + ImGui::SameLine(); + + if (ImGui::DragInt2("##PathSpan", (int*)&span)) { + ActivationPoints = span; + } + ImGui::SameLine(); + if (ImGui::Button(ICON_FA_UNDO "##ResetPathSpan")) { + ActivationPoints = IPathSpan(0.0f, 0.0f); + } +} diff --git a/src/engine/objects/CheepCheep.h b/src/engine/objects/CheepCheep.h index 32e9a5aeb..4cbe8bd60 100644 --- a/src/engine/objects/CheepCheep.h +++ b/src/engine/objects/CheepCheep.h @@ -3,6 +3,7 @@ #include <libultraship.h> #include <vector> #include "Object.h" +#include "engine/CoreMath.h" #include "World.h" @@ -18,20 +19,30 @@ extern "C" { class OCheepCheep : public OObject { public: - enum CheepType { + enum class Behaviour : int16_t { RACE, PODIUM_CEREMONY }; - enum Behaviour : uint16_t { - }; + // This is simply a helper function to keep Spawning code clean + static inline OCheepCheep* Spawn(const FVector& pos, Behaviour behaviour, IPathSpan span) { + SpawnParams params = { + .Name = "mk:cheep_cheep", + .Behaviour = static_cast<int16_t>(behaviour), + .Location = pos, + .PathSpan = span, + }; + return static_cast<OCheepCheep*>(gWorldInstance.AddObject(new OCheepCheep(params))); + } -public: + explicit OCheepCheep(const SpawnParams& params); - explicit OCheepCheep(const FVector& pos, CheepType type, IPathSpan span); + IPathSpan ActivationPoints; // Path points activation points + virtual void SetSpawnParams(SpawnParams& params) override; virtual void Tick() override; virtual void Draw(s32 cameraId) override; + virtual void DrawEditorProperties() override; void func_8007BBBC(s32 objectIndex); void func_8007BD04(s32 playerId); void init_var_cheep_cheep(s32 objectIndex); @@ -41,8 +52,5 @@ public: private: s32 _idx; - CheepType _type; - FVector _spawnPos; - IPathSpan _span; - + Behaviour _behaviour; }; diff --git a/src/engine/objects/Crab.cpp b/src/engine/objects/Crab.cpp index 98c781b5a..9e4e4687b 100644 --- a/src/engine/objects/Crab.cpp +++ b/src/engine/objects/Crab.cpp @@ -26,24 +26,31 @@ extern "C" { size_t OCrab::_count = 0; -OCrab::OCrab(const FVector2D& start, const FVector2D& end) { +OCrab::OCrab(const SpawnParams& params) : OObject(params) { Name = "Crab"; + ResourceName = "mk:crab"; _idx = _count; - _start = start; - _end = end; + _start = params.PatrolStart.value_or(FVector2D(0, 0)); + _end = params.PatrolEnd.value_or(FVector2D(0, 0)); find_unused_obj_index(&_objectIndex); init_object(_objectIndex, 0); - gObjectList[_objectIndex].pos[0] = gObjectList[_objectIndex].origin_pos[0] = start.x * xOrientation; - gObjectList[_objectIndex].pos[2] = gObjectList[_objectIndex].origin_pos[2] = start.z; + gObjectList[_objectIndex].pos[0] = gObjectList[_objectIndex].origin_pos[0] = _start.x * xOrientation; + gObjectList[_objectIndex].pos[2] = gObjectList[_objectIndex].origin_pos[2] = _start.z; - gObjectList[_objectIndex].unk_01C[0] = end.x * xOrientation; - gObjectList[_objectIndex].unk_01C[2] = end.z; + gObjectList[_objectIndex].unk_01C[0] = _end.x * xOrientation; + gObjectList[_objectIndex].unk_01C[2] = _end.z; _count++; } +void OCrab::SetSpawnParams(SpawnParams& params) { + params.Name = std::string(ResourceName); + params.PatrolStart = _start; + params.PatrolEnd = _end; +} + void OCrab::Tick(void) { s32 objectIndex = _objectIndex; if (gObjectList[objectIndex].state != 0) { @@ -189,3 +196,29 @@ void OCrab::func_80082E18(s32 objectIndex) { func_80089F24(objectIndex); } } + +void OCrab::DrawEditorProperties() { + ImGui::Text("Start Location"); + ImGui::SameLine(); + + if (ImGui::DragFloat2("##PathSpan", (float*)&_start)) { + } + ImGui::SameLine(); + if (ImGui::Button(ICON_FA_UNDO "##ResetPathSpan")) { + _start = FVector2D(0.0f, 0.0f); + gObjectList[_objectIndex].pos[0] = gObjectList[_objectIndex].origin_pos[0] = _start.x * xOrientation; + gObjectList[_objectIndex].pos[2] = gObjectList[_objectIndex].origin_pos[2] = _start.z; + } + + ImGui::Text("Patrol Location"); + ImGui::SameLine(); + + if (ImGui::DragFloat2("##PatrolLoc", (float*)&_end)) { + } + ImGui::SameLine(); + if (ImGui::Button(ICON_FA_UNDO "##ResetPatrolLoc")) { + _end = FVector2D(0.0f, 0.0f); + gObjectList[_objectIndex].unk_01C[0] = _end.x * xOrientation; + gObjectList[_objectIndex].unk_01C[2] = _end.z; + } +} diff --git a/src/engine/objects/Crab.h b/src/engine/objects/Crab.h index 4cadd3b87..d2c694ff1 100644 --- a/src/engine/objects/Crab.h +++ b/src/engine/objects/Crab.h @@ -30,10 +30,23 @@ extern "C" { */ class OCrab : public OObject { public: - explicit OCrab(const FVector2D& start, const FVector2D& end); + // This is simply a helper function to keep Spawning code clean + static inline OCrab* Spawn(const FVector2D& start, const FVector2D& end) { + SpawnParams params = { + .Name = "mk:crab", + .PatrolStart = start, + .PatrolEnd = end, + }; + return static_cast<OCrab*>(gWorldInstance.AddObject(new OCrab(params))); + } + + explicit OCrab(const SpawnParams& params); virtual void Tick() override; virtual void Draw(s32 cameraId) override; + virtual void SetSpawnParams(SpawnParams& params) override; + virtual void DrawEditorProperties() override; + void DrawModel(s32 cameraId); void init_ktb_crab(s32 objectIndex); diff --git a/src/engine/objects/Flagpole.cpp b/src/engine/objects/Flagpole.cpp index acd74ba2b..58581e3c9 100644 --- a/src/engine/objects/Flagpole.cpp +++ b/src/engine/objects/Flagpole.cpp @@ -15,16 +15,24 @@ extern "C" { size_t OFlagpole::_count = 0; -OFlagpole::OFlagpole(const FVector& pos, s16 direction) { +OFlagpole::OFlagpole(const SpawnParams& params) : OObject(params) { Name = "Flagpole"; + ResourceName = "mk:flagpole"; _idx = _count; - _pos = pos; - _direction = direction; find_unused_obj_index(&_objectIndex); - init_object(_objectIndex, 0); + SpawnPos = params.Location.value_or(FVector(0, 0, 0)); + gObjectList[_objectIndex].pos[0] = SpawnPos.x; + gObjectList[_objectIndex].pos[1] = SpawnPos.y; + gObjectList[_objectIndex].pos[2] = SpawnPos.z; + + SpawnRot = params.Rotation.value_or(IRotator(0, 0, 0)); + gObjectList[_objectIndex].orientation[0] = SpawnRot.pitch; + gObjectList[_objectIndex].orientation[1] = SpawnRot.yaw; + gObjectList[_objectIndex].orientation[2] = SpawnRot.roll; + _count++; } @@ -49,7 +57,7 @@ void OFlagpole::Draw(s32 cameraId) { // func_80055228 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, + rsp_set_matrix_transformation(gObjectList[objectIndex].pos, gObjectList[objectIndex].orientation, gObjectList[objectIndex].sizeScaling); if (gIsGamePaused == 0) { gObjectList[objectIndex].unk_0A2 = render_animated_model((Armature*) gObjectList[objectIndex].model, @@ -67,9 +75,11 @@ void OFlagpole::func_80082F1C(s32 objectIndex) { 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); + FVector pos = SpawnPos; + 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); + IRotator rot = SpawnRot; + set_obj_orientation(objectIndex, rot.pitch, rot.yaw, rot.roll); // changed from directional_angle to orientation for editor support } void OFlagpole::func_80083018(s32 objectIndex) { diff --git a/src/engine/objects/Flagpole.h b/src/engine/objects/Flagpole.h index 5824fcf18..db5621a2d 100644 --- a/src/engine/objects/Flagpole.h +++ b/src/engine/objects/Flagpole.h @@ -17,9 +17,23 @@ extern "C" { #include "some_data.h" } +// This used to use directional_angle for rot. It now uses orientation for editor compatibility. +// There doesn't seem to be any reason this actor's behaviour would differ from this class OFlagpole : public OObject { public: - explicit OFlagpole(const FVector& pos, s16 direction); + explicit OFlagpole(const SpawnParams& params); + + // This is simply a helper function to keep Spawning code clean + static inline OFlagpole* Spawn(FVector pos, s16 direction) { + IRotator rot; + rot.Set(0, direction, 0); + SpawnParams params = { + .Name = "mk:flagpole", + .Location = pos, + .Rotation = rot, + }; + return static_cast<OFlagpole*>(gWorldInstance.AddObject(new OFlagpole(params))); + } ~OFlagpole() { _count--; @@ -38,8 +52,6 @@ public: void func_80083060(s32 objectIndex); private: - FVector _pos; - s16 _direction; static size_t _count; size_t _idx; }; diff --git a/src/engine/objects/GrandPrixBalloons.cpp b/src/engine/objects/GrandPrixBalloons.cpp index 1315589f2..05ba880c6 100644 --- a/src/engine/objects/GrandPrixBalloons.cpp +++ b/src/engine/objects/GrandPrixBalloons.cpp @@ -17,8 +17,10 @@ extern "C" { size_t OGrandPrixBalloons::_count = 0; -OGrandPrixBalloons::OGrandPrixBalloons(const FVector& pos) { - Pos = pos; +OGrandPrixBalloons::OGrandPrixBalloons(const SpawnParams& params) : OObject(params) { + Name = "Grand Prix Balloons"; + ResourceName = "mk:grand_prix_balloons"; + Pos = params.Location.value_or(FVector(0, 0, 0)); _active = 1; if (gPlayerCount == 1) { diff --git a/src/engine/objects/GrandPrixBalloons.h b/src/engine/objects/GrandPrixBalloons.h index 93ec04479..04bac0915 100644 --- a/src/engine/objects/GrandPrixBalloons.h +++ b/src/engine/objects/GrandPrixBalloons.h @@ -25,7 +25,16 @@ extern "C" { class OGrandPrixBalloons : public OObject { public: - explicit OGrandPrixBalloons(const FVector& pos); + explicit OGrandPrixBalloons(const SpawnParams& params); + + // This is simply a helper function to keep Spawning code clean + static inline OGrandPrixBalloons* Spawn(const FVector& pos) { + SpawnParams params = { + .Name = "mk:grand_prix_balloons", + .Location = pos, + }; + return static_cast<OGrandPrixBalloons*>(gWorldInstance.AddObject(new OGrandPrixBalloons(params))); + } ~OGrandPrixBalloons() { _count--; diff --git a/src/engine/objects/Hedgehog.cpp b/src/engine/objects/Hedgehog.cpp index 1c2aada9d..a36853a91 100644 --- a/src/engine/objects/Hedgehog.cpp +++ b/src/engine/objects/Hedgehog.cpp @@ -1,5 +1,7 @@ #include "Hedgehog.h" -#include "World.h" +#include "engine/World.h" +#include "port/Game.h" +#include "port/interpolation/FrameInterpolation.h" extern "C" { #include "render_objects.h" @@ -11,60 +13,64 @@ extern "C" { #include "code_80086E70.h" #include "code_80057C60.h" } -#include "port/interpolation/FrameInterpolation.h" size_t OHedgehog::_count = 0; -OHedgehog::OHedgehog(const FVector& pos, const FVector2D& patrolPoint, s16 unk) { +OHedgehog::OHedgehog(const SpawnParams& params) : OObject(params) { Name = "Hedgehog"; + ResourceName = "mk:hedgehog"; _idx = _count; - _pos = pos; - - s32 objectId = indexObjectList2[_idx]; - _objectIndex = objectId; - 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; + SpawnPos = params.Location.value_or(FVector(0, 0, 0)); + PatrolEnd = params.PatrolEnd.value_or(FVector2D(0, 0)); + + find_unused_obj_index(&_objectIndex); + + init_object(_objectIndex, 0); + gObjectList[_objectIndex].pos[0] = gObjectList[_objectIndex].origin_pos[0] = SpawnPos.x * xOrientation; + gObjectList[_objectIndex].pos[1] = gObjectList[_objectIndex].surfaceHeight = SpawnPos.y + 6.0; + gObjectList[_objectIndex].pos[2] = gObjectList[_objectIndex].origin_pos[2] = SpawnPos.z; + gObjectList[_objectIndex].unk_0D5 = (u8) params.Behaviour.value_or(9); + gObjectList[_objectIndex].unk_09C = PatrolEnd.x * xOrientation; + gObjectList[_objectIndex].unk_09E = PatrolEnd.z; _count++; } -void OHedgehog::Tick() { - s32 objectIndex = indexObjectList2[_idx]; +void OHedgehog::SetSpawnParams(SpawnParams& params) { + params.Name = std::string(ResourceName); + params.Location = SpawnPos; + params.PatrolEnd = PatrolEnd; +} - OHedgehog::func_800833D0(objectIndex, _idx); - OHedgehog::func_80083248(objectIndex); - OHedgehog::func_80083474(objectIndex); +void OHedgehog::Tick() { + 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 + clear_object_flag(_objectIndex, 0x00600000); // The fix } void OHedgehog::Draw(s32 cameraId) { - s32 objectIndex = indexObjectList2[_idx]; - u32 something = func_8008A364(objectIndex, cameraId, 0x4000U, 0x000003E8); + 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 (is_obj_flag_status_active(_objectIndex, VISIBLE) != 0) { + set_object_flag(_objectIndex, 0x00200000); if (something < 0x2711U) { - set_object_flag(objectIndex, 0x00000020); + set_object_flag(_objectIndex, 0x00000020); } else { - clear_object_flag(objectIndex, 0x00000020); + clear_object_flag(_objectIndex, 0x00000020); } if (something < 0x57E41U) { - set_object_flag(objectIndex, 0x00400000); + set_object_flag(_objectIndex, 0x00400000); } if (something < 0x52211U) { - OHedgehog::func_800555BC(objectIndex, cameraId); + OHedgehog::func_800555BC(_objectIndex, cameraId); } } } @@ -113,7 +119,7 @@ void OHedgehog::func_8004A870(s32 objectIndex, f32 arg1) { const char* sHedgehogTexList[] = { d_course_yoshi_valley_hedgehog }; -void OHedgehog::func_8008311C(s32 objectIndex, s32 arg1) { +void OHedgehog::func_8008311C(s32 objectIndex, s32 id) { Object* object; Vtx* vtx = (Vtx*) LOAD_ASSET_RAW(common_vtx_hedgehog); @@ -128,7 +134,7 @@ void OHedgehog::func_8008311C(s32 objectIndex, s32 arg1) { 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; + object->unk_034 = ((id % 6) * 0.1) + 0.5; func_80086E70(objectIndex); set_object_flag(objectIndex, 0x04000600); object->boundingBoxSize = 2; @@ -168,12 +174,12 @@ void OHedgehog::func_80083248(s32 objectIndex) { } } -void OHedgehog::func_800833D0(s32 objectIndex, s32 arg1) { +void OHedgehog::func_800833D0(s32 objectIndex, s32 id) { switch (gObjectList[objectIndex].state) { case 0: break; case 1: - OHedgehog::func_8008311C(objectIndex, arg1); + OHedgehog::func_8008311C(objectIndex, id); break; case 2: func_80072D3C(objectIndex, 0, 1, 4, -1); @@ -193,3 +199,32 @@ void OHedgehog::func_80083474(s32 objectIndex) { func_80089F24(objectIndex); } } + +void OHedgehog::DrawEditorProperties() { + Object* obj = &gObjectList[_objectIndex]; + + ImGui::Text("Location"); + ImGui::SameLine(); + FVector location = FVector(obj->pos[0], obj->pos[1], obj->pos[2]); + if (ImGui::DragFloat3("##Location", (float*)&location)) { + Translate(location); + gEditor.eObjectPicker.eGizmo.Pos = location; + } + + ImGui::SameLine(); + if (ImGui::Button(ICON_FA_UNDO "##ResetPos")) { + FVector location = FVector(0, 0, 0); + Translate(location); + gEditor.eObjectPicker.eGizmo.Pos = location; + } + + ImGui::Text("Patrol Location"); + ImGui::SameLine(); + + if (ImGui::DragFloat2("##PatrolLoc", (float*)&PatrolEnd)) { + } + ImGui::SameLine(); + if (ImGui::Button(ICON_FA_UNDO "##ResetPatrolLoc")) { + PatrolEnd = FVector2D(0.0f, 0.0f); + } +}
\ No newline at end of file diff --git a/src/engine/objects/Hedgehog.h b/src/engine/objects/Hedgehog.h index 2057d894d..7a90f9a91 100644 --- a/src/engine/objects/Hedgehog.h +++ b/src/engine/objects/Hedgehog.h @@ -20,11 +20,22 @@ extern "C" { /** * @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. + * @arg behaviour unknown, seems unused. */ class OHedgehog : public OObject { public: - explicit OHedgehog(const FVector& pos, const FVector2D& patrolPoint, s16 unk); + explicit OHedgehog(const SpawnParams& params); + + // This is simply a helper function to keep Spawning code clean + static inline OHedgehog* Spawn(const FVector& pos, const FVector2D& patrolPoint, s16 behaviour) { + SpawnParams params = { + .Name = "mk:hedgehog", + .Behaviour = behaviour, // Appears to be unused + .Location = pos, + .PatrolEnd = patrolPoint, + }; + return static_cast<OHedgehog*>(gWorldInstance.AddObject(new OHedgehog(params))); + } ~OHedgehog() { _count--; @@ -36,6 +47,8 @@ public: virtual void Tick() override; virtual void Draw(s32 cameraId) override; + virtual void SetSpawnParams(SpawnParams& params) override; + virtual void DrawEditorProperties() override; void func_800555BC(s32 objectIndex, s32 cameraId); void func_8004A870(s32 objectIndex, f32 arg1); @@ -47,7 +60,7 @@ public: private: - FVector _pos; + FVector2D PatrolEnd; static size_t _count; size_t _idx; }; diff --git a/src/engine/objects/HotAirBalloon.cpp b/src/engine/objects/HotAirBalloon.cpp index f1e367d8d..b30c8e3ed 100644 --- a/src/engine/objects/HotAirBalloon.cpp +++ b/src/engine/objects/HotAirBalloon.cpp @@ -14,9 +14,11 @@ extern "C" { #include "actors.h" } -OHotAirBalloon::OHotAirBalloon(const FVector& pos) { +OHotAirBalloon::OHotAirBalloon(const SpawnParams& params) : OObject(params) { Name = "Hot Air Balloon"; - _pos = pos; + ResourceName = "mk:hot_air_balloon"; + + SpawnPos = params.Location.value_or(FVector{0.0f, 0.0f, 0.0f}); D_80165898 = 0; @@ -30,9 +32,14 @@ OHotAirBalloon::OHotAirBalloon(const FVector& pos) { find_unused_obj_index(&_objectIndex); + init_object(_objectIndex, 0); } +void OHotAirBalloon::SetSpawnParams(SpawnParams& params) { + OObject::SetSpawnParams(params); +} + void OHotAirBalloon::Tick() { s32 objectIndex = _objectIndex; @@ -100,10 +107,10 @@ 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_pos(objectIndex, xOrientation * SpawnPos.x, SpawnPos.y, SpawnPos.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_pos(objectIndex, xOrientation * SpawnPos.x, SpawnPos.y, SpawnPos.z); set_obj_origin_offset(objectIndex, 0.0f, 300.0f, 0.0f); } func_8008B844(objectIndex); diff --git a/src/engine/objects/HotAirBalloon.h b/src/engine/objects/HotAirBalloon.h index 301e111c4..7199a7cf4 100644 --- a/src/engine/objects/HotAirBalloon.h +++ b/src/engine/objects/HotAirBalloon.h @@ -19,16 +19,27 @@ extern "C" { class OHotAirBalloon : public OObject { public: - explicit OHotAirBalloon(const FVector& pos); + explicit OHotAirBalloon(const SpawnParams& params); + + // This is simply a helper function to keep Spawning code clean + static inline OHotAirBalloon* Spawn(FVector pos) { + SpawnParams params = { + .Name = "mk:hot_air_balloon", + .Location = pos, + }; + return static_cast<OHotAirBalloon*>(gWorldInstance.AddObject(new OHotAirBalloon(params))); + } virtual void Tick() override; virtual void Draw(s32 cameraId) override; + virtual void SetSpawnParams(SpawnParams& params) 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; + FVector Pos; bool *_visible; }; diff --git a/src/engine/objects/Object.cpp b/src/engine/objects/Object.cpp index 4e4fd6766..d7f9ac616 100644 --- a/src/engine/objects/Object.cpp +++ b/src/engine/objects/Object.cpp @@ -10,9 +10,25 @@ extern "C" { //GameActor() -OObject::OObject() {} +OObject::OObject() { +} +OObject::OObject(SpawnParams params) { + ResourceName = "mk:object"; // This needs to be overridden in derived classes + SpawnPos = params.Location.value_or(FVector{0.0f, 0.0f, 0.0f}); + SpawnRot = params.Rotation.value_or(IRotator{0, 0, 0}); + SpawnScale = params.Scale.value_or(FVector(0, 0, 0)); + Speed = params.Speed.value_or(0.0f); +} + +void OObject::SetSpawnParams(SpawnParams& params) { + params.Name = ResourceName; + params.Location = SpawnPos; + params.Rotation = SpawnRot; + params.Scale = SpawnScale; + params.Speed = Speed; +} - // Virtual functions to be overridden by derived classes +// Virtual functions to be overridden by derived classes void OObject::Tick() { } void OObject::Tick60fps() {} void OObject::Draw(s32 cameraId) { } @@ -21,3 +37,62 @@ void OObject::Destroy() { bPendingDestroy = true; } void OObject::Reset() { } + +FVector OObject::GetLocation() const { + if (_objectIndex != -1) { + Object* object = &gObjectList[_objectIndex]; + return FVector(object->pos[0], object->pos[1], object->pos[2]); + } + printf("Editor tried to get null OObject\n"); + return FVector(0, 0, 0); +}; + +IRotator OObject::GetRotation() const { + if (_objectIndex != -1) { + Object* object = &gObjectList[_objectIndex]; + return IRotator(object->orientation[0], object->orientation[1], object->orientation[2]); + } + printf("Editor tried to get null OObject\n"); + return IRotator(0, 0, 0); +} + +FVector OObject::GetScale() const { + if (_objectIndex != -1) { + Object* object = &gObjectList[_objectIndex]; + return FVector(object->sizeScaling, object->sizeScaling, object->sizeScaling); + } + printf("Editor tried to get null OObject\n"); + return FVector(0, 0, 0); +} + +void OObject::Translate(FVector pos) { + if (_objectIndex != -1) { + SpawnPos = pos; + + Object* object = &gObjectList[_objectIndex]; + + object->pos[0] = pos.x; + object->pos[1] = pos.y; + object->pos[2] = pos.z; + object->origin_pos[0] = pos.x; + object->origin_pos[1] = pos.y; + object->origin_pos[2] = pos.z; + } else { + printf("Editor tried to translate null OObject\n"); + } +} +void OObject::Rotate(IRotator rot) { + if (_objectIndex != -1) { + SpawnRot = rot; + Object* object = &gObjectList[_objectIndex]; + object->orientation[0] = rot.pitch; + object->orientation[1] = rot.yaw; + object->orientation[2] = rot.roll; + } else { + printf("Editor tried to rotate null OObject\n"); + } +} + +void OObject::SetScale(FVector scale) { + SpawnScale = scale; +} diff --git a/src/engine/objects/Object.h b/src/engine/objects/Object.h index 9dd44397b..2a2a51e21 100644 --- a/src/engine/objects/Object.h +++ b/src/engine/objects/Object.h @@ -1,6 +1,10 @@ #pragma once #include <libultraship.h> +#include "engine/SpawnParams.h" + +// Editor +#include "engine/editor/EditorMath.h" extern "C" { #include "camera.h" @@ -12,17 +16,34 @@ public: uint8_t uuid[16]; Object o; const char* Name = ""; + const char* ResourceName = ""; bool bPendingDestroy = false; s32 _objectIndex = -1; + const char* Model = ""; + + FVector SpawnPos = {0.0f, 0.0f, 0.0f}; + IRotator SpawnRot = {0, 0, 0}; + FVector SpawnScale = {1.0f, 1.0f, 1.0f}; + float Speed = 0.0f; + std::vector<Triangle> Triangles; virtual ~OObject() = default; explicit OObject(); + explicit OObject(SpawnParams params); + virtual void SetSpawnParams(SpawnParams& params); virtual void Tick(); virtual void Tick60fps(); virtual void Draw(s32 cameraId); virtual void Expire(); virtual void Destroy(); // Mark object for deletion at the start of the next frame virtual void Reset(); + FVector GetLocation() const; + IRotator GetRotation() const; + FVector GetScale() const; + virtual void Translate(FVector pos); + void Rotate(IRotator rot); + void SetScale(FVector scale); + virtual void DrawEditorProperties() { DrawDefaultEditorProperties(); }; }; diff --git a/src/engine/objects/Penguin.cpp b/src/engine/objects/Penguin.cpp index d283101b3..34c7248dd 100644 --- a/src/engine/objects/Penguin.cpp +++ b/src/engine/objects/Penguin.cpp @@ -33,12 +33,11 @@ extern "C" { extern s8 gPlayerCount; } - -OPenguin::OPenguin(FVector pos, u16 direction, PenguinType type, Behaviour behaviour) { +OPenguin::OPenguin(const SpawnParams& params) : OObject(params) { Name = "Penguin"; - _type = type; - _bhv = behaviour; - + ResourceName = "mk:penguin"; + FVector pos = params.Location.value_or(FVector(0, 0, 0)); + Speed = params.Speed.value_or(0); find_unused_obj_index(&_objectIndex); init_object(_objectIndex, 0); @@ -47,9 +46,9 @@ OPenguin::OPenguin(FVector pos, u16 direction, PenguinType type, Behaviour behav object->origin_pos[0] = pos.x * xOrientation; object->origin_pos[1] = pos.y; object->origin_pos[2] = pos.z; - object->unk_0C6 = direction; + object->unk_0C6 = params.Rotation.value_or(IRotator(0, 0, 0)).yaw; - switch(type) { + switch(static_cast<PenguinType>(Type)) { case PenguinType::CHICK: object->surfaceHeight = 5.0f; object->sizeScaling = 0.04f; @@ -76,7 +75,7 @@ void OPenguin::Tick(void) { s32 objectIndex = _objectIndex; if (gObjectList[objectIndex].state != 0) { - if (_type == PenguinType::EMPEROR) { + if (Type == PenguinType::EMPEROR) { OPenguin::EmperorPenguin(objectIndex); } else { OPenguin::OtherPenguin(objectIndex); @@ -144,7 +143,7 @@ void OPenguin::Behaviours(s32 objectIndex) { // func_800850B0 Object* object; object = &gObjectList[objectIndex]; - switch (_bhv) { + switch (SpawnBhv) { case 1: // emperor OPenguin::func_80085080(objectIndex); break; @@ -379,9 +378,9 @@ void OPenguin::InitOtherPenguin(s32 objectIndex) { // 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) { + switch(SpawnBhv) { case Behaviour::CIRCLE: - object->unk_01C[1] = Diameter; + object->unk_01C[1] = Speed; if (_toggle) { object->unk_0C4 = 0x8000; @@ -400,7 +399,7 @@ void OPenguin::InitOtherPenguin(s32 objectIndex) { case Behaviour::STRUT: if (gIsMirrorMode) { - object->unk_0C6 = MirrorModeAngleOffset; + object->unk_0C6 = SpawnRot.roll; // Roll is used to save mirror mode angle offset; } set_obj_direction_angle(objectIndex, 0U, object->unk_0C6 + 0x8000, 0U); @@ -417,3 +416,108 @@ void OPenguin::InitOtherPenguin(s32 objectIndex) { void OPenguin::Reset() { _toggle = false; } + +void OPenguin::DrawEditorProperties() { + ImGui::Text("Location"); + ImGui::SameLine(); + FVector location = GetLocation(); + if (ImGui::DragFloat3("##Location", (float*)&location)) { + Translate(location); + gEditor.eObjectPicker.eGizmo.Pos = location; + } + + ImGui::SameLine(); + if (ImGui::Button(ICON_FA_UNDO "##ResetPos")) { + FVector location = FVector(0, 0, 0); + Translate(location); + gEditor.eObjectPicker.eGizmo.Pos = location; + } + + ImGui::Text("Rotation"); + ImGui::SameLine(); + + IRotator objRot = GetRotation(); + + // Convert to temporary int values (to prevent writing 32bit values to 16bit variables) + int rot[3] = { + objRot.pitch, + objRot.yaw, + objRot.roll + }; + + if (ImGui::DragInt3("##Rotation", rot, 5.0f)) { + for (size_t i = 0; i < 3; i++) { + // Wrap around 0–65535 + rot[i] = (rot[i] % 65536 + 65536) % 65536; + } + IRotator newRot; + newRot.Set( + static_cast<uint16_t>(rot[0]), + static_cast<uint16_t>(rot[1]), + static_cast<uint16_t>(rot[2]) + ); + Rotate(newRot); + } + + ImGui::SameLine(); + if (ImGui::Button(ICON_FA_UNDO "##ResetRot")) { + IRotator rot = IRotator(0, 0, 0); + Rotate(rot); + } + + ImGui::Text("Spawn Mode"); + ImGui::SameLine(); + + int32_t type = static_cast<int32_t>(Type); + const char* items[] = { "CHICK", "ADULT", "CREDITS", "EMPEROR" }; + + if (ImGui::Combo("##Type", &type, items, IM_ARRAYSIZE(items))) { + Type = static_cast<PenguinType>(type); + + // Update type values + Object* object = &gObjectList[this->_objectIndex]; + switch(static_cast<PenguinType>(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; + } + } + + ImGui::Text("Spawn Mode"); + ImGui::SameLine(); + + int32_t behaviour = static_cast<int32_t>(SpawnBhv); + const char* items2[] = { "DISABLED", "STRUT", "CIRCLE", "SLIDE3", "SLIDE4", "UNK", "SLIDE6" }; + + if (ImGui::Combo("##Behaviour", &behaviour, items2, IM_ARRAYSIZE(items2))) { + SpawnBhv = static_cast<Behaviour>(behaviour); + } + + ImGui::Text("Diameter"); + ImGui::SameLine(); + + float speed = Speed; + if (ImGui::DragFloat("##Speed", &speed, 0.1f)) { + Speed = speed; + } + ImGui::SameLine(); + if (ImGui::Button(ICON_FA_UNDO "##ResetSpeed")) { + Speed = 0.0f; + } +} diff --git a/src/engine/objects/Penguin.h b/src/engine/objects/Penguin.h index 91d5687b9..4c9c04dd6 100644 --- a/src/engine/objects/Penguin.h +++ b/src/engine/objects/Penguin.h @@ -17,14 +17,14 @@ extern "C" { class OPenguin : public OObject { public: - enum PenguinType : uint32_t { + enum PenguinType : int16_t { CHICK, ADULT, CREDITS, EMPEROR }; - enum Behaviour : uint16_t { + enum Behaviour : int16_t { DISABLED, STRUT, // Emperor penguin CIRCLE, // Waddle in a circle @@ -35,14 +35,30 @@ public: }; public: - f32 Diameter = 0.0f; // Waddle in a circle around the spawn point at this diameter. - uint16_t MirrorModeAngleOffset; + explicit OPenguin(const SpawnParams& params); - explicit OPenguin(FVector pos, u16 direction, PenguinType type, Behaviour behaviour); + // This is simply a helper function to keep Spawning code clean + static inline OPenguin* Spawn(FVector pos, u16 direction, u16 mirrorModeAngleOffset, f32 diameter, PenguinType type, Behaviour behaviour) { + IRotator rot; + rot.Set(0, direction, mirrorModeAngleOffset); + SpawnParams params = { + .Name = "mk:penguin", + .Type = type, + .Behaviour = behaviour, + .Location = pos, + .Rotation = rot, + .Speed = diameter, // Diameter of the walking circle + }; + return static_cast<OPenguin*>(gWorldInstance.AddObject(new OPenguin(params))); + } + + PenguinType Type = PenguinType::CHICK; + Behaviour SpawnBhv = Behaviour::STRUT; virtual void Tick() override; virtual void Draw(s32 cameraId) override; virtual void Reset() override; + virtual void DrawEditorProperties() override; private: void Behaviours(s32 objectIndex); void EmperorPenguin(s32 objectIndex); @@ -55,6 +71,4 @@ private: void InitOtherPenguin(s32 objectIndex); static bool _toggle; - PenguinType _type; - Behaviour _bhv; }; diff --git a/src/engine/objects/Podium.cpp b/src/engine/objects/Podium.cpp index ec396ca53..d5e09d799 100644 --- a/src/engine/objects/Podium.cpp +++ b/src/engine/objects/Podium.cpp @@ -25,9 +25,10 @@ extern Vec3s D_800E634C[]; // { 0xf380, 0x0013, 0xfe14 }, // }; -OPodium::OPodium(const FVector& pos) { +OPodium::OPodium(const SpawnParams& params) : OObject(params) { Name = "Podium"; - _pos = pos; + ResourceName = "mk:podium"; + _pos = params.Location.value_or(FVector(0, 0, 0)); find_unused_obj_index(&_podium1Index); find_unused_obj_index(&_podium2Index); diff --git a/src/engine/objects/Podium.h b/src/engine/objects/Podium.h index fafdfe63d..f4f02fc8c 100644 --- a/src/engine/objects/Podium.h +++ b/src/engine/objects/Podium.h @@ -24,7 +24,16 @@ public: public: - explicit OPodium(const FVector& pos); + explicit OPodium(const SpawnParams& params); + + // This is simply a helper function to keep Spawning code clean + static inline OPodium* Spawn(const FVector& pos) { + SpawnParams params = { + .Name = "mk:podium", + .Location = pos, + }; + return static_cast<OPodium*>(gWorldInstance.AddObject(new OPodium(params))); + } virtual void Tick() override; virtual void Draw(s32 cameraId) override; diff --git a/src/engine/objects/Seagull.cpp b/src/engine/objects/Seagull.cpp index 17c35c07d..61a7f39ca 100644 --- a/src/engine/objects/Seagull.cpp +++ b/src/engine/objects/Seagull.cpp @@ -31,12 +31,11 @@ SplineData* D_800E633C[] = { &D_800E6034, &D_800E60F0, &D_800E61B4, &D_800E6280 size_t OSeagull::_count = 0; -OSeagull::OSeagull(FVector pos) { +OSeagull::OSeagull(const SpawnParams& params) : OObject(params) { Name = "Seagull"; + ResourceName = "mk:seagull"; _idx = _count; - _pos.x = pos.x; - _pos.y = pos.y; - _pos.z = pos.z; + FVector pos = params.Location.value_or(FVector(0, 0, 0)); s16 randZ; s16 randX; @@ -49,6 +48,11 @@ OSeagull::OSeagull(FVector pos) { init_object(_objectIndex, 0); + Object* object = &gObjectList[_objectIndex]; + + object->pos[0] = pos.x; + object->pos[1] = pos.y; + object->pos[2] = pos.z; set_obj_origin_pos(_objectIndex, pos.x, pos.y, pos.z); if (_idx < (NUM_SEAGULLS / 2)) { @@ -171,7 +175,8 @@ void OSeagull::func_8008241C(s32 objectIndex, s32 arg1) { randY = random_int(0x0014); randZ = random_int(0x00C8) + -100.0; - set_obj_origin_pos(objectIndex, (randX + _pos.x) * xOrientation, randY + _pos.y, randZ + _pos.z); + FVector pos = SpawnPos; + set_obj_origin_pos(objectIndex, (randX + pos.x) * xOrientation, randY + pos.y, randZ + pos.z); set_obj_direction_angle(objectIndex, 0U, 0U, 0U); gObjectList[objectIndex].unk_034 = 1.0f; func_80086EF0(objectIndex); diff --git a/src/engine/objects/Seagull.h b/src/engine/objects/Seagull.h index 42474159e..73d06a900 100644 --- a/src/engine/objects/Seagull.h +++ b/src/engine/objects/Seagull.h @@ -19,7 +19,7 @@ extern "C" { //! @todo unk_0D5 needs to be a struct variable probably. What does it do? Behaviour? class OSeagull : public OObject { public: - explicit OSeagull(FVector pos); + explicit OSeagull(const SpawnParams& params); ~OSeagull() { _count--; @@ -29,6 +29,15 @@ public: return _count; } + // This is simply a helper function to keep Spawning code clean + static inline OSeagull* Spawn(const FVector& pos) { + SpawnParams params = { + .Name = "mk:seagull", + .Location = pos, + }; + return static_cast<OSeagull*>(gWorldInstance.AddObject(new OSeagull(params))); + } + virtual void Tick() override; virtual void Draw(s32 cameraId) override; @@ -38,7 +47,6 @@ public: void func_8008241C(s32 objectIndex, s32 arg1); void func_80082714(s32 objectIndex, s32 arg1); private: - FVector _pos; static size_t _count; s32 _idx; bool _toggle; diff --git a/src/engine/objects/Snowman.cpp b/src/engine/objects/Snowman.cpp index cbb708fbf..dd98e2df0 100644 --- a/src/engine/objects/Snowman.cpp +++ b/src/engine/objects/Snowman.cpp @@ -17,31 +17,32 @@ static const char* sSnowmanHeadList[] = { d_course_frappe_snowland_snowman_head size_t OSnowman::_count = 0; -OSnowman::OSnowman(const FVector& pos) { +OSnowman::OSnowman(const SpawnParams& params) : OObject(params) { Name = "Snowman"; + ResourceName = "mk:snowman"; _idx = _count; - _pos = pos; + Pos = params.Location.value_or(FVector(0, 0, 0)); find_unused_obj_index(&_headIndex); init_object(_headIndex, 0); _objectIndex = _headIndex; - gObjectList[_headIndex].origin_pos[0] = pos.x * xOrientation; - gObjectList[_headIndex].origin_pos[1] = pos.y + 5.0 + 3.0; - gObjectList[_headIndex].origin_pos[2] = pos.z; - gObjectList[_headIndex].pos[0] = pos.x * xOrientation; - gObjectList[_headIndex].pos[1] = pos.y + 5.0 + 3.0; - gObjectList[_headIndex].pos[2] = pos.z; + gObjectList[_headIndex].origin_pos[0] = Pos.x * xOrientation; + gObjectList[_headIndex].origin_pos[1] = Pos.y + 5.0 + 3.0; + gObjectList[_headIndex].origin_pos[2] = Pos.z; + gObjectList[_headIndex].pos[0] = Pos.x * xOrientation; + gObjectList[_headIndex].pos[1] = Pos.y + 5.0 + 3.0; + gObjectList[_headIndex].pos[2] = Pos.z; find_unused_obj_index(&_bodyIndex); init_object(_bodyIndex, 0); - gObjectList[_bodyIndex].origin_pos[0] = pos.x * xOrientation; - gObjectList[_bodyIndex].origin_pos[1] = pos.y + 3.0; - gObjectList[_bodyIndex].origin_pos[2] = pos.z; + gObjectList[_bodyIndex].origin_pos[0] = Pos.x * xOrientation; + gObjectList[_bodyIndex].origin_pos[1] = Pos.y + 3.0; + gObjectList[_bodyIndex].origin_pos[2] = Pos.z; gObjectList[_bodyIndex].unk_0D5 = 0; // Section Id no longer used. - gObjectList[_bodyIndex].pos[0] = pos.x * xOrientation; - gObjectList[_bodyIndex].pos[1] = pos.y + 3.0; - gObjectList[_bodyIndex].pos[2] = pos.z; + gObjectList[_bodyIndex].pos[0] = Pos.x * xOrientation; + gObjectList[_bodyIndex].pos[1] = Pos.y + 3.0; + gObjectList[_bodyIndex].pos[2] = Pos.z; _count++; } @@ -348,6 +349,31 @@ void OSnowman::func_80083B0C(s32 objectIndex) { set_object_flag(objectIndex, 0x04000210); } +void OSnowman::Translate(FVector pos) { + if ((_objectIndex != -1) && (_bodyIndex != -1)) { + SpawnPos = pos; + + Object* object = &gObjectList[_objectIndex]; + + object->pos[0] = pos.x; + object->pos[1] = pos.y; + object->pos[2] = pos.z; + object->origin_pos[0] = pos.x; + object->origin_pos[1] = pos.y; + object->origin_pos[2] = pos.z; + + object = &gObjectList[_bodyIndex]; + object->pos[0] = pos.x; + object->pos[1] = pos.y - 5.0; + object->pos[2] = pos.z; + object->origin_pos[0] = pos.x; + object->origin_pos[1] = pos.y - 5.0; + object->origin_pos[2] = pos.z; + } else { + printf("Editor tried to translate null OObject\n"); + } +} + void OSnowman::func_80083538(s32 objectIndex, Vec3f arg1, s32 arg2, s32 arg3) { Object* object; diff --git a/src/engine/objects/Snowman.h b/src/engine/objects/Snowman.h index f33ee3b53..42217f040 100644 --- a/src/engine/objects/Snowman.h +++ b/src/engine/objects/Snowman.h @@ -19,7 +19,16 @@ extern "C" { class OSnowman : public OObject { public: - explicit OSnowman(const FVector& pos); + // This is simply a helper function to keep Spawning code clean + static inline OSnowman* Spawn(FVector pos) { + SpawnParams params = { + .Name = "mk:snowman", + .Location = FVector(pos.x, pos.y, pos.z), + }; + return static_cast<OSnowman*>(gWorldInstance.AddObject(new OSnowman(params))); + } + + explicit OSnowman(const SpawnParams& params); ~OSnowman() { _count--; @@ -31,6 +40,7 @@ public: virtual void Tick() override; virtual void Draw(s32 cameraId) override; + virtual void Translate(FVector pos) override; void DrawHead(s32); void DrawBody(s32); @@ -47,7 +57,7 @@ public: void func_8008379C(s32 objectIndex); private: - FVector _pos; + FVector Pos; static size_t _count; size_t _idx; s32 _headIndex; diff --git a/src/engine/objects/Thwomp.cpp b/src/engine/objects/Thwomp.cpp index 2f8281cb6..b597f26ba 100644 --- a/src/engine/objects/Thwomp.cpp +++ b/src/engine/objects/Thwomp.cpp @@ -2,6 +2,8 @@ #include <libultra/gbi.h> #include "Thwomp.h" #include <vector> +#include "engine/courses/Course.h" +#include "engine/World.h" #include "port/Game.h" #include "port/interpolation/FrameInterpolation.h" @@ -48,32 +50,56 @@ s16 D_800E597C[] = { 0x0000, 0x0000, 0x4000, 0x8000, 0x8000, 0xc000 }; size_t OThwomp::_count = 0; size_t OThwomp::_rand = 0; -OThwomp::OThwomp(s16 x, s16 z, s16 direction, f32 scale, s16 behaviour, s16 primAlpha, u16 boundingBoxSize) { +OThwomp::OThwomp(const SpawnParams& params) : OObject(params) { // s16 x, s16 z, s16 direction, f32 scale, s16 behaviour, s16 primAlpha, u16 boundingBoxSize) { + FVector loc = params.Location.value_or(FVector{0, 0, 0}); + IRotator rot = params.Rotation.value_or(IRotator{0, 0, 0}); + BoundingBoxSize = params.BoundingBoxSize.value_or(0); + Behaviour = static_cast<OThwomp::States>(params.Behaviour.value_or(1)); + PrimAlpha = params.PrimAlpha.value_or(0); + FVector scale = params.Scale.value_or(FVector(0, 0, 0)); + Name = "Thwomp"; + ResourceName = "mk:thwomp"; + Model = "d_course_bowsers_castle_dl_thwomp"; _idx = _count; - _faceDirection = direction; - _boundingBoxSize = boundingBoxSize; - State = (States)behaviour; + _faceDirection = rot.yaw; find_unused_obj_index(&_objectIndex); s32 objectId = _objectIndex; 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].origin_pos[0] = loc.x * xOrientation; + gObjectList[objectId].origin_pos[2] = loc.z; + gObjectList[objectId].unk_0D5 = Behaviour; + gObjectList[objectId].primAlpha = PrimAlpha; + gObjectList[objectId].boundingBoxSize = BoundingBoxSize + 5; + + if (scale.y == 0.0f) { + scale.y = 1.0f; } - gObjectList[objectId].sizeScaling = scale; + gObjectList[objectId].sizeScaling = scale.y; _count++; } +void OThwomp::SetSpawnParams(SpawnParams& params) { + Object* object = &gObjectList[_objectIndex]; + params.Name = std::string(ResourceName); + params.Location = FVector( + object->origin_pos[0], + object->origin_pos[1], + object->origin_pos[2] + ); + IRotator rot; rot.Set(0, object->orientation[1], 0); + params.Rotation = rot; + params.Scale = FVector(0, object->sizeScaling, 0); + params.Behaviour = Behaviour; + params.PrimAlpha = PrimAlpha; + params.BoundingBoxSize = BoundingBoxSize; + +} + void OThwomp::Tick60fps() { // func_80081210 Player* player; s32 objectIndex; @@ -94,23 +120,23 @@ void OThwomp::Tick60fps() { // func_80081210 } if (gObjectList[_objectIndex].state != 0) { - switch (State) { - case STATIONARY: + switch(Behaviour) { + case States::STATIONARY: OThwomp::StationaryBehaviour(_objectIndex); break; - case MOVE_AND_ROTATE: + case States::MOVE_AND_ROTATE: OThwomp::MoveAndRotateBehaviour(_objectIndex); break; - case MOVE_FAR: + case States::MOVE_FAR: OThwomp::MoveFarBehaviour(_objectIndex); break; - case STATIONARY_FAST: + case States::STATIONARY_FAST: OThwomp::StationaryFastBehaviour(_objectIndex); break; - case JAILED: + case States::JAILED: OThwomp::JailedBehaviour(_objectIndex); break; - case SLIDE: + case States::SLIDE: OThwomp::SlidingBehaviour(_objectIndex); break; } @@ -138,8 +164,6 @@ void OThwomp::Tick60fps() { // func_80081210 OThwomp::AddParticles(_objectIndex); } - - if (_idx == 0) { for (var_s4 = 0; var_s4 < gObjectParticle2_SIZE; var_s4++) { // @port: Tag the transform. @@ -641,7 +665,7 @@ void OThwomp::func_80080B28(s32 objectIndex, s32 playerId) { } } else if ((temp_f0 <= 17.5) && (func_80072320(objectIndex, 1) != 0) && (is_within_horizontal_distance_of_player(objectIndex, player, - (player->speed * 0.5) + _boundingBoxSize) != 0)) { + (player->speed * 0.5) + BoundingBoxSize) != 0)) { if ((player->type & 0x8000) && !(player->type & 0x100)) { if (is_obj_flag_status_active(objectIndex, 0x04000000) != 0) { func_80072180(); @@ -677,8 +701,8 @@ void OThwomp::Draw(s32 cameraId) { Camera* camera; Object* object; - camera = &camera1[cameraId]; - if (cameraId == PLAYER_ONE) { + camera = &cameras[cameraId]; + if (cameraId == PLAYER_ONE || cameraId == 4) { // 4 == freecam clear_object_flag(objectIndex, 0x00070000); func_800722CC(objectIndex, 0x00000110); } @@ -704,7 +728,7 @@ void OThwomp::Draw(s32 cameraId) { objectIndex = gObjectParticle3[i]; if (objectIndex != NULL_OBJECT_ID) { object = &gObjectList[objectIndex]; - if ((object->state > 0) && (State == MOVE_FAR) && (gMatrixHudCount <= MTX_HUD_POOL_SIZE_MAX)) { + if ((object->state > 0) && (Behaviour == States::MOVE_FAR)) { rsp_set_matrix_transformation(object->pos, object->orientation, object->sizeScaling); gSPVertex(gDisplayListHead++, (uintptr_t) D_0D005C00, 3, 0); gSPDisplayList(gDisplayListHead++, (Gfx*) D_0D006930); @@ -723,7 +747,7 @@ void OThwomp::Draw(s32 cameraId) { 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)) { + if ((object->state >= 2) && (Behaviour == States::MOVE_AND_ROTATE)) { 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); @@ -734,6 +758,7 @@ void OThwomp::Draw(s32 cameraId) { void OThwomp::DrawModel(s32 objectIndex) { if ((gObjectList[objectIndex].state >= 2) && (func_80072354(objectIndex, 0x00000040) != 0)) { + FrameInterpolation_RecordOpenChild("Thwomp_Main", (uintptr_t) TAG_THWOMP(this)); func_8004A7AC(objectIndex, 1.75f); rsp_set_matrix_transformation(gObjectList[objectIndex].pos, gObjectList[objectIndex].orientation, gObjectList[objectIndex].sizeScaling); @@ -743,6 +768,7 @@ void OThwomp::DrawModel(s32 objectIndex) { 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); + FrameInterpolation_RecordCloseChild(); } } @@ -1448,3 +1474,108 @@ void OThwomp::func_8007E63C(s32 objectIndex) { break; } } + +void OThwomp::DrawEditorProperties() { + ImGui::Text("Behaviour"); + ImGui::SameLine(); + + int32_t behaviour = static_cast<int32_t>(Behaviour); + const char* items[] = { "Disabled", "Stationary", "Move and Rotate", "Move Far", "Stationary Fast", "Slide", "Jailed" }; + + if (ImGui::Combo("##Behaviour", &behaviour, items, IM_ARRAYSIZE(items))) { + Behaviour = static_cast<OThwomp::States>(behaviour); + gObjectList[_objectIndex].unk_0D5 = static_cast<uint8_t>(behaviour); + gObjectList[_objectIndex].state = behaviour; + } + + ImGui::Text("Location"); + ImGui::SameLine(); + FVector location = GetLocation(); + if (ImGui::DragFloat3("##Location", (float*)&location)) { + Translate(location); + gEditor.eObjectPicker.eGizmo.Pos = location; + } + + ImGui::SameLine(); + if (ImGui::Button(ICON_FA_UNDO "##ResetPos")) { + FVector location = FVector(0, 0, 0); + Translate(location); + gEditor.eObjectPicker.eGizmo.Pos = location; + } + + ImGui::Text("Rotation"); + ImGui::SameLine(); + + IRotator objRot = GetRotation(); + + // Convert to temporary int values (to prevent writing 32bit values to 16bit variables) + int rot[3] = { + objRot.pitch, + objRot.yaw, + objRot.roll + }; + + if (ImGui::DragInt3("##Rotation", rot, 5.0f)) { + for (size_t i = 0; i < 3; i++) { + // Wrap around 0–65535 + rot[i] = (rot[i] % 65536 + 65536) % 65536; + } + IRotator newRot; + newRot.Set( + static_cast<uint16_t>(rot[0]), + static_cast<uint16_t>(rot[1]), + static_cast<uint16_t>(rot[2]) + ); + Rotate(newRot); + } + + ImGui::SameLine(); + if (ImGui::Button(ICON_FA_UNDO "##ResetRot")) { + IRotator rot = IRotator(0, 0, 0); + Rotate(rot); + } + + FVector scale = GetScale(); + ImGui::Text("Scale "); + ImGui::SameLine(); + + if (ImGui::DragFloat3("##Scale", (float*)&scale, 0.1f)) { + SetScale(scale); + gObjectList[_objectIndex].sizeScaling = scale.y; + } + ImGui::SameLine(); + if (ImGui::Button(ICON_FA_UNDO "##ResetScale")) { + FVector scale = FVector(1.0f, 1.0f, 1.0f); + SetScale(scale); + gObjectList[_objectIndex].sizeScaling = 1.0f; + } + + int32_t primAlpha = PrimAlpha; + ImGui::Text("Prim Alpha"); + ImGui::SameLine(); + + if (ImGui::InputInt("##PrimAlpha", (int*)&primAlpha)) { + PrimAlpha = static_cast<int16_t>(primAlpha); + gObjectList[_objectIndex].primAlpha = static_cast<int16_t>(primAlpha); + } + ImGui::SameLine(); + if (ImGui::Button(ICON_FA_UNDO "##ResetPrimAlpha")) { + PrimAlpha = 0; + gObjectList[_objectIndex].primAlpha = 0; + } + + int32_t boundingBoxSize = static_cast<int32_t>(BoundingBoxSize); + ImGui::Text("Bounding Box Size"); + ImGui::SameLine(); + + if (ImGui::InputInt("##BoundingBoxSize", (int*)&boundingBoxSize)) { + if (boundingBoxSize < 0) boundingBoxSize = 0; + BoundingBoxSize = static_cast<OThwomp::States>(boundingBoxSize); + gObjectList[_objectIndex].boundingBoxSize = static_cast<uint16_t>(boundingBoxSize); + } + ImGui::SameLine(); + if (ImGui::Button(ICON_FA_UNDO "##ResetBoundingBoxSize")) { + BoundingBoxSize = 0; + gObjectList[_objectIndex].boundingBoxSize = 0; + } +} diff --git a/src/engine/objects/Thwomp.h b/src/engine/objects/Thwomp.h index 6822b4e31..a76872093 100644 --- a/src/engine/objects/Thwomp.h +++ b/src/engine/objects/Thwomp.h @@ -2,10 +2,15 @@ #include <libultraship.h> #include <vector> - #include "engine/World.h" +#include "engine/SpawnParams.h" +#include "engine/CoreMath.h" + #include "engine/objects/Object.h" +class World; +extern World gWorldInstance; + extern "C" { #include "macros.h" #include "main.h" @@ -42,9 +47,24 @@ public: JAILED // Has no collision }; - States State = States::DISABLED; + // This is simply a helper function to keep Spawning code clean + static inline OThwomp* Spawn(s16 x, s16 z, s16 direction, f32 scale, s16 behaviour, s16 primAlpha, u16 boundingBoxSize = 7) { + IRotator rot; + rot.Set(0, direction, 0); + + SpawnParams params = { + .Name = "mk:thwomp", + .Behaviour = behaviour, + .Location = FVector(x, 0, z), + .Rotation = rot, + .Scale = FVector(0, scale, 0), + .PrimAlpha = primAlpha, + .BoundingBoxSize = boundingBoxSize + }; + return static_cast<OThwomp*>(gWorldInstance.AddObject(new OThwomp(params))); + } - explicit OThwomp(s16 x, s16 z, s16 direction, f32 scale, s16 behaviour, s16 primAlpha, u16 boundingBoxSize = 7); + explicit OThwomp(const SpawnParams& params); ~OThwomp() { _count--; @@ -54,8 +74,10 @@ public: return _count; } + virtual void SetSpawnParams(SpawnParams& params) override; virtual void Tick60fps() override; virtual void Draw(s32 cameraId) override; + virtual void DrawEditorProperties() override; void SetVisibility(s32 objectIndex); void func_80080B28(s32 objectIndex, s32 playerId); void DrawModel(s32); @@ -109,6 +131,10 @@ public: void func_8008078C(s32 objectIndex); void func_8007E63C(s32 objectIndex); + + u16 BoundingBoxSize; + OThwomp::States Behaviour; + int16_t PrimAlpha; private: static size_t _count; static size_t _rand; @@ -116,5 +142,4 @@ private: 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 index 57c702275..871151b8f 100644 --- a/src/engine/objects/TrashBin.cpp +++ b/src/engine/objects/TrashBin.cpp @@ -21,12 +21,13 @@ extern "C" { #define DEGREES_FLOAT_TO_SHORT(Degrees) ((s16)((Degrees) * (0x8000 / 180.0f))) -OTrashBin::OTrashBin(const FVector& pos, const IRotator& rotation, f32 scale, OTrashBin::Behaviour bhv) { - Name = "Trashbin"; - _pos = pos; - _rot = rotation; - _scale = scale; - _bhv = bhv; +OTrashBin::OTrashBin(const SpawnParams& params) : OObject(params) { + Name = "Trash Bin"; + ResourceName = "mk:trash_bin"; + _pos = params.Location.value_or(FVector(0, 0, 0)); + _rot = params.Rotation.value_or(IRotator(0, 0, 0)); + _scale = params.Scale.value_or(FVector(0, 0, 0)).y; // Only y + _bhv = static_cast<Behaviour>(params.Behaviour.value_or(0)); find_unused_obj_index(&_objectIndex); diff --git a/src/engine/objects/TrashBin.h b/src/engine/objects/TrashBin.h index 3e878e1c0..4f2ea4132 100644 --- a/src/engine/objects/TrashBin.h +++ b/src/engine/objects/TrashBin.h @@ -20,11 +20,24 @@ extern "C" { class OTrashBin : public OObject { public: - enum Behaviour { + enum Behaviour : int16_t { STATIC, // The lid stays shut MUNCHING // The lid opens/closes in a scary munching manner }; - explicit OTrashBin(const FVector& pos, const IRotator& rotation, f32 scale, OTrashBin::Behaviour bhv); + + // This is simply a helper function to keep Spawning code clean + static inline OTrashBin* Spawn(const FVector& pos, const IRotator& rot, f32 scale, OTrashBin::Behaviour bhv) { + SpawnParams params = { + .Name = "mk:trash_bin", + .Behaviour = bhv, + .Location = pos, + .Rotation = rot, + .Scale = FVector(0, scale, 0), + }; + return static_cast<OTrashBin*>(gWorldInstance.AddObject(new OTrashBin(params))); + } + + explicit OTrashBin(const SpawnParams& params); virtual void Tick() override; virtual void Draw(s32 cameraId) override; diff --git a/src/engine/objects/Trophy.cpp b/src/engine/objects/Trophy.cpp index e3713158d..ba5435953 100644 --- a/src/engine/objects/Trophy.cpp +++ b/src/engine/objects/Trophy.cpp @@ -20,12 +20,14 @@ extern "C" { #include "menu_items.h" } -OTrophy::OTrophy(const FVector& pos, TrophyType trophy, Behaviour bhv) { +OTrophy::OTrophy(const SpawnParams& params) : OObject(params) { Name = "Trophy"; - _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; + ResourceName = "mk:trophy"; + _type = static_cast<TrophyType>(params.Type.value_or(0)); + FVector spawnPos = params.Location.value_or(FVector(0, 0, 0)); + spawnPos.y += 16.0f; // Adjust the height so the trophy sits on the surface when positioned to 0,0,0 + SpawnPos = spawnPos; // Don't save the + 16.0f adjustment + _bhv = static_cast<Behaviour>(params.Behaviour.value_or(0)); find_unused_obj_index(&_objectIndex); @@ -34,10 +36,10 @@ OTrophy::OTrophy(const FVector& pos, TrophyType trophy, Behaviour bhv) { // Thus this will need to be changed if that's not desired. gTrophyIndex = _objectIndex; - if (bhv == OTrophy::Behaviour::PODIUM_CEREMONY) { + if (_bhv == OTrophy::Behaviour::PODIUM_CEREMONY) { _toggleVisibility = &D_801658CE; } else { - _toggle = 1; + _toggle = true; _toggleVisibility = &_toggle; _isMod = true; } @@ -47,7 +49,7 @@ OTrophy::OTrophy(const FVector& pos, TrophyType trophy, Behaviour bhv) { init_object(_objectIndex, 0); } - switch (trophy) { + switch (_type) { case TrophyType::GOLD: gObjectList[_objectIndex].model = (Gfx*)gold_trophy_dl10; break; @@ -83,16 +85,23 @@ OTrophy::OTrophy(const FVector& pos, TrophyType trophy, Behaviour bhv) { } 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; + 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; _emitter = reinterpret_cast<StarEmitter*>(gWorldInstance.AddEmitter(new StarEmitter())); } +void OTrophy::SetSpawnParams(SpawnParams& params) { + OObject::SetSpawnParams(params); + Object *object = &gObjectList[_objectIndex]; + params.Type = _type; + params.Behaviour = _bhv; +} + void OTrophy::Tick() { // func_80086D80 s32 objectIndex = _objectIndex; s32 var_s0; @@ -123,8 +132,9 @@ void OTrophy::Tick() { // func_80086D80 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_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; @@ -221,9 +231,14 @@ void OTrophy::Draw(s32 cameraId) { if (*_toggleVisibility == true) { object = &gObjectList[listIndex]; if (object->state >= 2) { - gSPMatrix(gDisplayListHead++, GetPerspMatrix(0), + // Prevents a perspective glitch + if (CVarGetInteger("gFreecam", 0) == true) { + cameraId = CAMERA_FREECAM; + } + + gSPMatrix(gDisplayListHead++, GetPerspMatrix(cameraId), G_MTX_NOPUSH | G_MTX_LOAD | G_MTX_PROJECTION); - gSPMatrix(gDisplayListHead++, GetLookAtMatrix(0), + gSPMatrix(gDisplayListHead++, GetLookAtMatrix(cameraId), 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); @@ -234,7 +249,7 @@ void OTrophy::Draw(s32 cameraId) { gSPDisplayList(gDisplayListHead++, (Gfx*)D_0D0077A0); gSPDisplayList(gDisplayListHead++, object->model); - gSPMatrix(gDisplayListHead++, GetLookAtMatrix(0), + gSPMatrix(gDisplayListHead++, GetLookAtMatrix(cameraId), G_MTX_NOPUSH | G_MTX_MUL | G_MTX_PROJECTION); mtxf_identity(someMatrix2); render_set_position(someMatrix2, 0); @@ -355,3 +370,61 @@ void OTrophy::func_80086C6C(s32 objectIndex) { _emitter->Emit(sp24, D_801658F4); } } + +void OTrophy::DrawEditorProperties() { + ImGui::Text("Location"); + ImGui::SameLine(); + FVector location = GetLocation(); + if (ImGui::DragFloat3("##Location", (float*)&location)) { + Translate(location); + gEditor.eObjectPicker.eGizmo.Pos = location; + } + + ImGui::SameLine(); + if (ImGui::Button(ICON_FA_UNDO "##ResetPos")) { + FVector location = FVector(0, 0, 0); + Translate(location); + gEditor.eObjectPicker.eGizmo.Pos = location; + } + + ImGui::Text("Cup"); + ImGui::SameLine(); + + int32_t type = static_cast<int32_t>(_type); + const char* items[] = { "Bronze", "Silver", "Gold", "Bronze 150", "Silver 150", "Gold 150" }; + + if (ImGui::Combo("##Type", &type, items, IM_ARRAYSIZE(items))) { + _type = static_cast<TrophyType>(type); + + switch (_type) { + 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; + } + } + + ImGui::Text("Behaviour"); + ImGui::SameLine(); + + int32_t behaviour = static_cast<int32_t>(_bhv); + const char* items2[] = { "Podium Ceremony", "Stationary", "Opposing Dual-axis Rotation", "Single-axis Rotation", "Go Fish" }; + + if (ImGui::Combo("##Behaviour", &behaviour, items2, IM_ARRAYSIZE(items2))) { + _bhv = static_cast<Behaviour>(behaviour); + } +} diff --git a/src/engine/objects/Trophy.h b/src/engine/objects/Trophy.h index 016115dcd..628248edc 100644 --- a/src/engine/objects/Trophy.h +++ b/src/engine/objects/Trophy.h @@ -19,7 +19,7 @@ extern "C" { class OTrophy : public OObject { public: - enum TrophyType { + enum TrophyType : int16_t { BRONZE, SILVER, GOLD, @@ -28,7 +28,7 @@ public: GOLD_150, }; - enum Behaviour { + enum Behaviour : int16_t { PODIUM_CEREMONY, STATIONARY, ROTATE, // A dual-axis opposing rotation @@ -36,10 +36,23 @@ public: GO_FISH, }; - explicit OTrophy(const FVector& pos, TrophyType trophy, Behaviour bhv); + // This is simply a helper function to keep Spawning code clean + static inline OTrophy* Spawn(const FVector& pos, TrophyType trophy, Behaviour bhv) { + SpawnParams params = { + .Name = "mk:trophy", + .Type = trophy, + .Behaviour = bhv, + .Location = pos, + }; + return static_cast<OTrophy*>(gWorldInstance.AddObject(new OTrophy(params))); + } + explicit OTrophy(const SpawnParams& params); + + virtual void SetSpawnParams(SpawnParams& params) override; virtual void Tick() override; virtual void Draw(s32 cameraId) override; + virtual void DrawEditorProperties() override; void func_80086700(s32 objectIndex); void func_80086940(s32 objectIndex); void func_80086C14(s32 objectIndex); @@ -48,8 +61,7 @@ public: private: StarEmitter* _emitter; - TrophyType _trophy; - FVector _spawnPos; + TrophyType _type; Behaviour _bhv; int8_t _toggle; int8_t *_toggleVisibility; |
