Add ObjectExtension system

This commit is contained in:
rozlette 2025-04-19 00:29:52 -05:00
parent c5e0e32391
commit 2b284f4b07
6 changed files with 190 additions and 1 deletions

View file

@ -15,6 +15,7 @@
#include <libultraship/libultraship.h>
#include "soh/OTRGlobals.h"
#include "soh/cvar_prefixes.h"
#include "soh/ObjectExtension/ActorListIndex.h"
extern "C" {
#include <z64.h>
@ -924,6 +925,7 @@ void ActorViewerWindow::DrawElement() {
ImGui::Text("Category: %s", acMapping[display->category]);
ImGui::Text("ID: %d", display->id);
ImGui::Text("Parameters: %d", display->params);
ImGui::Text("Actor List Index: %d", GetActorListIndex(display));
},
"Selected Actor");
ImGui::SameLine();

View file

@ -0,0 +1,16 @@
#include "ActorListIndex.h"
#include "soh/ObjectExtension/ObjectExtension.h"
struct ActorListIndex {
s16 index = -1;
};
static ObjectExtension::Register<ActorListIndex> ActorListIndexRegister;
int16_t GetActorListIndex(const Actor* actor) {
const ActorListIndex* index = ObjectExtension::GetInstance().Get<ActorListIndex>(actor);
return index != nullptr ? index->index : ActorListIndex{}.index;
}
void SetActorListIndex(const Actor* actor, int16_t index) {
ObjectExtension::GetInstance().Set<ActorListIndex>(actor, ActorListIndex{ index });
}

View file

@ -0,0 +1,16 @@
#ifndef ACTOR_LIST_INDEX_H
#define ACTOR_LIST_INDEX_H
#ifdef __cplusplus
extern "C" {
#include "z64actor.h"
#endif
int16_t GetActorListIndex(const Actor* actor);
void SetActorListIndex(const Actor* actor, int16_t index);
#ifdef __cplusplus
}
#endif
#endif // ACTOR_LIST_INDEX_H

View file

@ -0,0 +1,25 @@
#include "ObjectExtension.h"
ObjectExtension& ObjectExtension::GetInstance() {
static ObjectExtension instance;
return instance;
}
ObjectExtension::Id ObjectExtension::RegisterId() {
return NextId++;
}
void ObjectExtension::Free(const void* object) {
if (object == nullptr) {
return;
}
std::erase_if(Data, [&object](const auto& iter) {
auto const& [key, value] = iter;
return key.first == object;
});
}
extern "C" void ObjectExtension_Free(const void* object) {
ObjectExtension::GetInstance().Free(object);
}

View file

@ -0,0 +1,116 @@
#pragma once
#ifdef __cplusplus
#include <libultraship/libultraship.h>
#include <any>
#include <limits>
#include <stdint.h>
#include <unordered_map>
#include <vector>
/*
* This class can attach additional data to pointers. It can only attach a single instance of each type of data.
* Use the ObjectExtension::Register class to register a type to be used as an object extension.
* An example usage is:
*
* struct MyData {
* s32 data = -1;
* };
* static ObjectExtension::Register<MyData> MyDataRegister;
*
* Then you can get with
* ObjectExtension::GetInstance().Get<MyData>(ptr);
* and set with
* ObjectExtension::GetInstance().Set<MyData>(ptr, MyData{});
* (or with the returned pointer from Get()).
*/
class ObjectExtension {
public:
using Id = uint32_t;
static constexpr Id InvalidId = std::numeric_limits<Id>::max();
// Registers type T to be used as an object extension
template <typename T> class Register {
public:
Register() {
Id = ObjectExtension::GetInstance().RegisterId();
}
static ObjectExtension::Id Id;
};
// Gets the singleton ObjectExtension instance
static ObjectExtension& GetInstance();
// Gets the data of type T associated with an object, or nullptr if no such data has been attached
template <typename T> T* Get(const void* object) {
assert(ObjectExtension::Register<T>::Id != InvalidId);
if (object == nullptr) {
return nullptr;
}
auto it = Data.find(std::make_pair(object, ObjectExtension::Register<T>::Id));
if (it == Data.end()) {
return nullptr;
}
return std::any_cast<T>(&(it->second));
}
// Sets the data of type T for an object. Data will be copied.
template <typename T> void Set(const void* object, const T&& data) {
assert(ObjectExtension::Register<T>::Id != InvalidId);
if (object != nullptr) {
Data[std::make_pair(object, ObjectExtension::Register<T>::Id)] = data;
}
}
// Returns true if an object has data of type T associated with it
template <typename T> bool Has(const void* object) {
assert(ObjectExtension::Register<T>::Id != InvalidId);
if (object == nullptr) {
return false;
}
return Data.contains(std::make_pair(object, ObjectExtension::Register<T>::Id));
}
// Removes data of type T from an object
template <typename T> void Remove(const void* object) {
assert(ObjectExtension::Register<T>::Id != InvalidId);
Data.erase(std::make_pair(object, ObjectExtension::Register<T>::Id));
}
// Removes all data from an object
void Free(const void* object);
private:
ObjectExtension() = default;
// Returns the next free object extension Id
Id RegisterId();
ObjectExtension::Id NextId = 0;
struct KeyHash {
std::size_t operator()(const std::pair<const void*, ObjectExtension::Id>& key) const {
return std::hash<const void*>{}(key.first) ^ (std::hash<ObjectExtension::Id>{}(key.second) << 1);
}
};
// Collection of all object extension data.
std::unordered_map<std::pair<const void*, ObjectExtension::Id>, std::any, KeyHash> Data;
};
// Static template globals
template <typename T> ObjectExtension::Id ObjectExtension::Register<T>::Id = ObjectExtension::InvalidId;
#else // __cplusplus
void ObjectExtension_Free(const void* object);
#endif // __cplusplus

View file

@ -7,6 +7,8 @@
#include "objects/gameplay_keep/gameplay_keep.h"
#include "objects/gameplay_dangeon_keep/gameplay_dangeon_keep.h"
#include "objects/object_bdoor/object_bdoor.h"
#include "soh/ObjectExtension/ObjectExtension.h"
#include "soh/ObjectExtension/ActorListIndex.h"
#include "soh/frame_interpolation.h"
#include "soh/Enhancements/cosmetics/cosmeticsTypes.h"
#include "soh/Enhancements/enemyrandomizer.h"
@ -2574,7 +2576,11 @@ void Actor_UpdateAll(PlayState* play, ActorContext* actorCtx) {
if (play->numSetupActors != 0) {
actorEntry = &play->setupActorList[0];
for (i = 0; i < play->numSetupActors; i++) {
Actor_SpawnEntry(&play->actorCtx, actorEntry++, play);
Actor* spawnedActor = Actor_SpawnEntry(&play->actorCtx, actorEntry++, play);
// #region SOH [ObjectExtension] ActorListIndex tracking
SetActorListIndex(spawnedActor, (s16)i);
// #endregion
}
play->numSetupActors = 0;
GameInteractor_ExecuteOnSceneSpawnActors();
@ -3352,6 +3358,10 @@ Actor* Actor_Spawn(ActorContext* actorCtx, PlayState* play, s16 actorId, f32 pos
return NULL;
}
// #region SOH [ObjectExtension]
SetActorListIndex(actor, -1);
// #endregion
assert(dbEntry->numLoaded < 255);
dbEntry->numLoaded++;
@ -3494,6 +3504,10 @@ Actor* Actor_Delete(ActorContext* actorCtx, Actor* actor, PlayState* play) {
newHead = Actor_RemoveFromCategory(play, actorCtx, actor);
// #region SOH [ObjectExtension]
ObjectExtension_Free(actor);
// #endregion
ZELDA_ARENA_FREE_DEBUG(actor);
dbEntry->numLoaded--;