DarkflameServer/dGame/Entity.h
Aaron Kimbrell 192c8cf974
feat: refactor vanity (#1477)
* feat: refactor vanity
cleanup code to be generalized for objects
remove unused party feature
add fallback to data to text
Allow for better organizing data in multiple files
remove special case flag values in favor of config data
general cleanup and fixes

* newline at eof's
2024-02-25 16:59:10 -06:00

538 lines
15 KiB
C++

#pragma once
#include <map>
#include <functional>
#include <typeinfo>
#include <type_traits>
#include <unordered_map>
#include <vector>
#include "NiPoint3.h"
#include "NiQuaternion.h"
#include "LDFFormat.h"
#include "eKillType.h"
namespace Loot {
class Info;
};
namespace tinyxml2 {
class XMLDocument;
};
class Player;
class EntityInfo;
class User;
class Spawner;
class ScriptComponent;
class dpEntity;
class EntityTimer;
class Component;
class Item;
class Character;
class EntityCallbackTimer;
class PositionUpdate;
enum class eTriggerEventType;
enum class eGameMasterLevel : uint8_t;
enum class eReplicaComponentType : uint32_t;
enum class eReplicaPacketType : uint8_t;
enum class eCinematicEvent : uint32_t;
namespace CppScripts {
class Script;
};
/**
* An entity in the world. Has multiple components.
*/
class Entity {
public:
explicit Entity(const LWOOBJID& objectID, EntityInfo info, User* parentUser = nullptr, Entity* parentEntity = nullptr);
~Entity();
void Initialize();
bool operator==(const Entity& other) const;
bool operator!=(const Entity& other) const;
/**
* Getters
*/
const LWOOBJID& GetObjectID() const { return m_ObjectID; }
const LOT GetLOT() const { return m_TemplateID; }
Character* GetCharacter() const { return m_Character; }
eGameMasterLevel GetGMLevel() const { return m_GMLevel; }
uint8_t GetCollectibleID() const;
Entity* GetParentEntity() const { return m_ParentEntity; }
std::vector<std::string>& GetGroups() { return m_Groups; };
Spawner* GetSpawner() const { return m_Spawner; }
LWOOBJID GetSpawnerID() const { return m_SpawnerID; }
const std::vector<LDFBaseData*>& GetSettings() const { return m_Settings; }
const std::vector<LDFBaseData*>& GetNetworkSettings() const { return m_NetworkSettings; }
bool GetIsDead() const;
bool GetPlayerReadyForUpdates() const { return m_PlayerIsReadyForUpdates; }
bool GetIsGhostingCandidate() const;
void SetIsGhostingCandidate(bool value) { m_IsGhostingCandidate = value; };
int8_t GetObservers() const;
uint16_t GetNetworkId() const;
Entity* GetOwner() const;
const NiPoint3& GetDefaultPosition() const;
const NiQuaternion& GetDefaultRotation() const;
float GetDefaultScale() const;
const NiPoint3& GetPosition() const;
const NiQuaternion& GetRotation() const;
const SystemAddress& GetSystemAddress() const;
/**
* Setters
*/
void SetCharacter(Character* value) { m_Character = value; }
void SetGMLevel(eGameMasterLevel value);
void SetOwnerOverride(LWOOBJID value);
void SetPlayerReadyForUpdates() { m_PlayerIsReadyForUpdates = true; }
void SetObservers(int8_t value);
void SetNetworkId(uint16_t id);
void SetPosition(const NiPoint3& position);
void SetRotation(const NiQuaternion& rotation);
void SetRespawnPos(const NiPoint3& position);
void SetRespawnRot(const NiQuaternion& rotation);
/**
* Component management
*/
Component* GetComponent(eReplicaComponentType componentID) const;
template<typename T>
T* GetComponent() const;
template<typename T>
bool TryGetComponent(eReplicaComponentType componentId, T*& component) const;
bool HasComponent(eReplicaComponentType componentId) const;
void AddComponent(eReplicaComponentType componentId, Component* component);
std::vector<ScriptComponent*> GetScriptComponents();
void Subscribe(LWOOBJID scriptObjId, CppScripts::Script* scriptToAdd, const std::string& notificationName);
void Unsubscribe(LWOOBJID scriptObjId, const std::string& notificationName);
void SetProximityRadius(float proxRadius, std::string name);
void SetProximityRadius(dpEntity* entity, std::string name);
void AddChild(Entity* child);
void RemoveChild(Entity* child);
void RemoveParent();
// Adds a timer to start next frame with the given name and time.
void AddTimer(std::string name, float time);
void AddCallbackTimer(float time, std::function<void()> callback);
bool HasTimer(const std::string& name);
void CancelCallbackTimers();
void CancelAllTimers();
void CancelTimer(const std::string& name);
void AddToGroup(const std::string& group);
bool IsPlayer() const;
std::unordered_map<eReplicaComponentType, Component*>& GetComponents() { return m_Components; } // TODO: Remove
void WriteBaseReplicaData(RakNet::BitStream* outBitStream, eReplicaPacketType packetType);
void WriteComponents(RakNet::BitStream* outBitStream, eReplicaPacketType packetType);
void UpdateXMLDoc(tinyxml2::XMLDocument* doc);
void Update(float deltaTime);
// Events
void OnCollisionProximity(LWOOBJID otherEntity, const std::string& proxName, const std::string& status);
void OnCollisionPhantom(LWOOBJID otherEntity);
void OnCollisionLeavePhantom(LWOOBJID otherEntity);
void OnFireEventServerSide(Entity* sender, std::string args, int32_t param1 = -1, int32_t param2 = -1, int32_t param3 = -1);
void OnActivityStateChangeRequest(const LWOOBJID senderID, const int32_t value1, const int32_t value2,
const std::u16string& stringValue);
void OnCinematicUpdate(Entity* self, Entity* sender, eCinematicEvent event, const std::u16string& pathName,
float_t pathTime, float_t totalTime, int32_t waypoint);
void NotifyObject(Entity* sender, const std::string& name, int32_t param1 = 0, int32_t param2 = 0);
void OnEmoteReceived(int32_t emote, Entity* target);
void OnUse(Entity* originator);
void OnHitOrHealResult(Entity* attacker, int32_t damage);
void OnHit(Entity* attacker);
void OnZonePropertyEditBegin();
void OnZonePropertyEditEnd();
void OnZonePropertyModelEquipped();
void OnZonePropertyModelPlaced(Entity* player);
void OnZonePropertyModelPickedUp(Entity* player);
void OnZonePropertyModelRemoved(Entity* player);
void OnZonePropertyModelRemovedWhileEquipped(Entity* player);
void OnZonePropertyModelRotated(Entity* player);
void OnMessageBoxResponse(Entity* sender, int32_t button, const std::u16string& identifier, const std::u16string& userData);
void OnChoiceBoxResponse(Entity* sender, int32_t button, const std::u16string& buttonIdentifier, const std::u16string& identifier);
void RequestActivityExit(Entity* sender, LWOOBJID player, bool canceled);
void Smash(const LWOOBJID source = LWOOBJID_EMPTY, const eKillType killType = eKillType::VIOLENT, const std::u16string& deathType = u"");
void Kill(Entity* murderer = nullptr, const eKillType killType = eKillType::SILENT);
void AddQuickBuildCompleteCallback(const std::function<void(Entity* user)>& callback) const;
void AddCollisionPhantomCallback(const std::function<void(Entity* target)>& callback);
void AddDieCallback(const std::function<void()>& callback);
void Resurrect();
void AddLootItem(const Loot::Info& info);
void PickupItem(const LWOOBJID& objectID);
bool CanPickupCoins(uint64_t count);
void RegisterCoinDrop(uint64_t count);
void ScheduleKillAfterUpdate(Entity* murderer = nullptr);
void TriggerEvent(eTriggerEventType event, Entity* optionalTarget = nullptr);
void ScheduleDestructionAfterUpdate() { m_ShouldDestroyAfterUpdate = true; }
const NiPoint3& GetRespawnPosition() const;
const NiQuaternion& GetRespawnRotation() const;
void Sleep();
void Wake();
bool IsSleeping() const;
/*
* Utility
*/
/**
* Retroactively corrects the model vault size due to incorrect initialization in a previous patch.
*
*/
void RetroactiveVaultSize();
bool GetBoolean(const std::u16string& name) const;
int32_t GetI32(const std::u16string& name) const;
int64_t GetI64(const std::u16string& name) const;
void SetBoolean(const std::u16string& name, bool value);
void SetI32(const std::u16string& name, int32_t value);
void SetI64(const std::u16string& name, int64_t value);
bool HasVar(const std::u16string& name) const;
template<typename T>
const T& GetVar(const std::u16string& name) const;
template<typename T>
void SetVar(const std::u16string& name, T value);
void SendNetworkVar(const std::string& data, const SystemAddress& sysAddr);
template<typename T>
void SetNetworkVar(const std::u16string& name, T value, const SystemAddress& sysAddr = UNASSIGNED_SYSTEM_ADDRESS);
template<typename T>
void SetNetworkVar(const std::u16string& name, std::vector<T> value, const SystemAddress& sysAddr = UNASSIGNED_SYSTEM_ADDRESS);
template<typename T>
T GetNetworkVar(const std::u16string& name);
/**
* Get the LDF value and cast it as T.
*/
template<typename T>
T GetVarAs(const std::u16string& name) const;
template<typename ComponentType, typename... VaArgs>
ComponentType* AddComponent(VaArgs... args);
/**
* Get the LDF data.
*/
LDFBaseData* GetVarData(const std::u16string& name) const;
/**
* Get the LDF value and convert it to a string.
*/
std::string GetVarAsString(const std::u16string& name) const;
/*
* Collision
*/
std::vector<LWOOBJID> GetTargetsInPhantom();
Entity* GetScheduledKiller() { return m_ScheduleKiller; }
void ProcessPositionUpdate(PositionUpdate& update);
void SetScale(const float scale);
protected:
LWOOBJID m_ObjectID;
LOT m_TemplateID;
std::vector<LDFBaseData*> m_Settings;
std::vector<LDFBaseData*> m_NetworkSettings;
NiPoint3 m_DefaultPosition;
NiQuaternion m_DefaultRotation;
float m_Scale;
Spawner* m_Spawner;
LWOOBJID m_SpawnerID;
bool m_HasSpawnerNodeID;
uint32_t m_SpawnerNodeID;
Character* m_Character;
Entity* m_ParentEntity; //For spawners and the like
std::vector<Entity*> m_ChildEntities;
eGameMasterLevel m_GMLevel;
uint16_t m_CollectibleID;
std::vector<std::string> m_Groups;
uint16_t m_NetworkID;
std::vector<std::function<void()>> m_DieCallbacks;
std::vector<std::function<void(Entity* target)>> m_PhantomCollisionCallbacks;
std::unordered_map<eReplicaComponentType, Component*> m_Components;
std::vector<EntityTimer> m_Timers;
std::vector<EntityTimer> m_PendingTimers;
std::vector<EntityCallbackTimer> m_CallbackTimers;
std::vector<EntityCallbackTimer> m_PendingCallbackTimers;
bool m_ShouldDestroyAfterUpdate = false;
LWOOBJID m_OwnerOverride;
Entity* m_ScheduleKiller;
bool m_PlayerIsReadyForUpdates = false;
bool m_IsGhostingCandidate = false;
int8_t m_Observers = 0;
bool m_IsParentChildDirty = true;
/*
* Collision
*/
std::vector<LWOOBJID> m_TargetsInPhantom;
};
/**
* Template definitions.
*/
template<typename T>
bool Entity::TryGetComponent(const eReplicaComponentType componentId, T*& component) const {
const auto& index = m_Components.find(componentId);
if (index == m_Components.end()) {
component = nullptr;
return false;
}
component = dynamic_cast<T*>(index->second);
return true;
}
template <typename T>
T* Entity::GetComponent() const {
return dynamic_cast<T*>(GetComponent(T::ComponentType));
}
template<typename T>
const T& Entity::GetVar(const std::u16string& name) const {
auto* data = GetVarData(name);
if (data == nullptr) {
return LDFData<T>::Default;
}
auto* typed = dynamic_cast<LDFData<T>*>(data);
if (typed == nullptr) {
return LDFData<T>::Default;
}
return typed->GetValue();
}
template<typename T>
T Entity::GetVarAs(const std::u16string& name) const {
const auto data = GetVarAsString(name);
return GeneralUtils::TryParse<T>(data).value_or(LDFData<T>::Default);
}
template<typename T>
void Entity::SetVar(const std::u16string& name, T value) {
auto* data = GetVarData(name);
if (data == nullptr) {
auto* data = new LDFData<T>(name, value);
m_Settings.push_back(data);
return;
}
auto* typed = dynamic_cast<LDFData<T>*>(data);
if (typed == nullptr) {
return;
}
typed->SetValue(value);
}
template<typename T>
void Entity::SetNetworkVar(const std::u16string& name, T value, const SystemAddress& sysAddr) {
LDFData<T>* newData = nullptr;
for (auto* data : m_NetworkSettings) {
if (data->GetKey() != name)
continue;
newData = dynamic_cast<LDFData<T>*>(data);
if (newData != nullptr) {
newData->SetValue(value);
} else { // If we're changing types
m_NetworkSettings.erase(
std::remove(m_NetworkSettings.begin(), m_NetworkSettings.end(), data), m_NetworkSettings.end()
);
delete data;
}
break;
}
if (newData == nullptr) {
newData = new LDFData<T>(name, value);
}
m_NetworkSettings.push_back(newData);
SendNetworkVar(newData->GetString(true), sysAddr);
}
template<typename T>
void Entity::SetNetworkVar(const std::u16string& name, std::vector<T> values, const SystemAddress& sysAddr) {
std::stringstream updates;
auto index = 1;
for (const auto& value : values) {
LDFData<T>* newData = nullptr;
const auto& indexedName = name + u"." + GeneralUtils::to_u16string(index);
for (auto* data : m_NetworkSettings) {
if (data->GetKey() != indexedName)
continue;
newData = dynamic_cast<LDFData<T>*>(data);
newData->SetValue(value);
break;
}
if (newData == nullptr) {
newData = new LDFData<T>(indexedName, value);
}
m_NetworkSettings.push_back(newData);
if (index == values.size()) {
updates << newData->GetString(true);
} else {
updates << newData->GetString(true) << "\n";
}
index++;
}
SendNetworkVar(updates.str(), sysAddr);
}
template<typename T>
T Entity::GetNetworkVar(const std::u16string& name) {
for (auto* data : m_NetworkSettings) {
if (data == nullptr || data->GetKey() != name)
continue;
auto* typed = dynamic_cast<LDFData<T>*>(data);
if (typed == nullptr)
continue;
return typed->GetValue();
}
return LDFData<T>::Default;
}
/**
* @brief Adds a component of type ComponentType to this entity and forwards the arguments to the constructor.
*
* @tparam ComponentType The component class type to add. Must derive from Component.
* @tparam VaArgs The argument types to forward to the constructor.
* @param args The arguments to forward to the constructor. The first argument passed to the ComponentType constructor will be this entity.
* @return ComponentType* The added component. Will never return null.
*/
template<typename ComponentType, typename... VaArgs>
inline ComponentType* Entity::AddComponent(VaArgs... args) {
static_assert(std::is_base_of_v<Component, ComponentType>, "ComponentType must be a Component");
// Get the component if it already exists, or default construct a nullptr
auto*& componentToReturn = m_Components[ComponentType::ComponentType];
// If it doesn't exist, create it and forward the arguments to the constructor
if (!componentToReturn) {
componentToReturn = new ComponentType(this, std::forward<VaArgs>(args)...);
} else {
// In this case the block is already allocated and ready for use
// so we use a placement new to construct the component again as was requested by the caller.
// Placement new means we already have memory allocated for the object, so this just calls its constructor again.
// This is useful for when we want to create a new object in the same memory location as an old one.
componentToReturn->~Component();
new(componentToReturn) ComponentType(this, std::forward<VaArgs>(args)...);
}
// Finally return the created or already existing component.
// Because of the assert above, this should always be a ComponentType* but I need a way to guarantee the map cannot be modifed outside this function
// To allow a static cast here instead of a dynamic one.
return dynamic_cast<ComponentType*>(componentToReturn);
}