feat: add ObjectRef for scene loading to not rely on runtime id's

This commit is contained in:
2026-10-18 17:15:51 +02:00
parent 94fa2bb4b2
commit d21c2ccfd7
10 changed files with 262 additions and 35 deletions
@@ -6,7 +6,7 @@
#include <cstddef>
#include <vector>
#include <destrum/ObjectModel/ObjectId.h>
#include <destrum/ObjectModel/ObjectRef.h>
class InteractableButton final : public Interactable {
public:
@@ -19,6 +19,7 @@ public:
nlohmann::json Serialize() const override;
void Deserialize(const nlohmann::json& data) override;
void ResolveReferences(const ObjectMap& objects) override;
void ImGuiInspector() override;
[[nodiscard]] std::size_t GetColorIndex() const { return m_ColorIndex; }
@@ -29,7 +30,7 @@ public:
// Every Activatable on a target object is activated when the button is pressed
void AddTarget(const GameObject& target);
void RemoveTarget(ObjectId target);
[[nodiscard]] const std::vector<ObjectId>& GetTargets() const { return m_Targets; }
[[nodiscard]] const std::vector<ObjectRef>& GetTargets() const { return m_Targets; }
private:
void ApplyColor();
@@ -39,7 +40,7 @@ private:
std::size_t m_ColorIndex{0};
int m_PressCount{0};
bool m_CycleColors{true};
std::vector<ObjectId> m_Targets;
std::vector<ObjectRef> m_Targets;
};
#endif // LIGHTKEEPER_INTERACTABLEBUTTON_H
@@ -52,15 +52,16 @@ void InteractableButton::Interact(GameObject& interactor)
void InteractableButton::AddTarget(const GameObject& target)
{
if (target.GetId() == InvalidObjectId || std::ranges::find(m_Targets, target.GetId()) != m_Targets.end()) {
const ObjectRef ref(target);
if (!ref.IsSet() || std::ranges::find(m_Targets, ref) != m_Targets.end()) {
return;
}
m_Targets.push_back(target.GetId());
m_Targets.push_back(ref);
}
void InteractableButton::RemoveTarget(ObjectId target)
{
std::erase(m_Targets, target);
std::erase(m_Targets, ObjectRef(target));
}
void InteractableButton::ActivateTargets(GameObject& interactor)
@@ -70,10 +71,10 @@ void InteractableButton::ActivateTargets(GameObject& interactor)
return;
}
for (const ObjectId targetId : m_Targets) {
GameObject* target = scene->FindGameObject(targetId);
for (const ObjectRef& targetRef : m_Targets) {
GameObject* target = targetRef.Get(*scene);
if (target == nullptr) {
spdlog::warn("{} targets object {} which no longer exists", GetGameObject()->GetName(), targetId);
spdlog::warn("{} targets object {} which no longer exists", GetGameObject()->GetName(), targetRef.GetId());
continue;
}
@@ -122,7 +123,19 @@ void InteractableButton::Deserialize(const nlohmann::json& data)
m_ColorIndex = data.value("colorIndex", m_ColorIndex) % ButtonColors.size();
m_CycleColors = data.value("cycleColors", m_CycleColors);
m_PressCount = data.value("pressCount", m_PressCount);
m_Targets = data.value("targets", std::vector<ObjectId>{});
m_Targets = data.value("targets", std::vector<ObjectRef>{});
}
void InteractableButton::ResolveReferences(const ObjectMap& objects)
{
std::erase_if(m_Targets, [&](ObjectRef& target) {
const ObjectId fileId = target.GetId();
if (!target.Resolve(objects)) {
spdlog::warn("{} targets object {} which is not in the scene file, dropping it", GetGameObject()->GetName(), fileId);
return true;
}
return !target.IsSet();
});
}
void InteractableButton::ImGuiInspector()
@@ -149,8 +162,9 @@ void InteractableButton::DrawTargetsInspector()
}
std::optional<ObjectId> targetToRemove;
for (const ObjectId targetId : m_Targets) {
const GameObject* target = scene->FindGameObject(targetId);
for (const ObjectRef& targetRef : m_Targets) {
const ObjectId targetId = targetRef.GetId();
const GameObject* target = targetRef.Get(*scene);
ImGui::PushID(static_cast<int>(targetId));
if (ImGui::SmallButton("x")) {
targetToRemove = targetId;
@@ -170,7 +184,7 @@ void InteractableButton::DrawTargetsInspector()
if (ImGui::BeginCombo("##AddTarget", "Add target...")) {
for (const auto& object : scene->GetObjects()) {
const bool alreadyTargeted = object != nullptr &&
std::ranges::find(m_Targets, object->GetId()) != m_Targets.end();
std::ranges::find(m_Targets, ObjectRef(*object)) != m_Targets.end();
if (object == nullptr || object.get() == GetGameObject() || alreadyTargeted ||
object->GetComponent<Activatable>() == nullptr) {
continue;