summaryrefslogtreecommitdiff
path: root/src/engine/objects
diff options
context:
space:
mode:
Diffstat (limited to 'src/engine/objects')
-rw-r--r--src/engine/objects/Bat.cpp7
-rw-r--r--src/engine/objects/Bat.h13
-rw-r--r--src/engine/objects/BombKart.cpp222
-rw-r--r--src/engine/objects/BombKart.h37
-rw-r--r--src/engine/objects/Boos.cpp80
-rw-r--r--src/engine/objects/Boos.h23
-rw-r--r--src/engine/objects/ChainChomp.cpp3
-rw-r--r--src/engine/objects/CheepCheep.cpp61
-rw-r--r--src/engine/objects/CheepCheep.h26
-rw-r--r--src/engine/objects/Crab.cpp47
-rw-r--r--src/engine/objects/Crab.h15
-rw-r--r--src/engine/objects/Flagpole.cpp24
-rw-r--r--src/engine/objects/Flagpole.h18
-rw-r--r--src/engine/objects/GrandPrixBalloons.cpp6
-rw-r--r--src/engine/objects/GrandPrixBalloons.h11
-rw-r--r--src/engine/objects/Hedgehog.cpp99
-rw-r--r--src/engine/objects/Hedgehog.h19
-rw-r--r--src/engine/objects/HotAirBalloon.cpp15
-rw-r--r--src/engine/objects/HotAirBalloon.h15
-rw-r--r--src/engine/objects/Object.cpp79
-rw-r--r--src/engine/objects/Object.h21
-rw-r--r--src/engine/objects/Penguin.cpp128
-rw-r--r--src/engine/objects/Penguin.h28
-rw-r--r--src/engine/objects/Podium.cpp5
-rw-r--r--src/engine/objects/Podium.h11
-rw-r--r--src/engine/objects/Seagull.cpp15
-rw-r--r--src/engine/objects/Seagull.h12
-rw-r--r--src/engine/objects/Snowman.cpp54
-rw-r--r--src/engine/objects/Snowman.h14
-rw-r--r--src/engine/objects/Thwomp.cpp185
-rw-r--r--src/engine/objects/Thwomp.h33
-rw-r--r--src/engine/objects/TrashBin.cpp13
-rw-r--r--src/engine/objects/TrashBin.h17
-rw-r--r--src/engine/objects/Trophy.cpp111
-rw-r--r--src/engine/objects/Trophy.h22
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;