feat: Property behaviors partially functional (#1759)

* most of gameplay tab works

* smash unsmash and wait working

* Add pausing of models and behaviors

* working basic behaviors

* play sound functioning

* add resetting

* Fix asynchronous actions executing other strips actions

* Add comments, remove dead code etc.

* Skip Smashes if they coincide with a UnSmash

Remove debug logs

Comment on return
This commit is contained in:
David Markowitz
2025-05-05 00:17:39 -07:00
committed by GitHub
parent 841b754b01
commit 3ebc6709db
13 changed files with 465 additions and 64 deletions

View File

@@ -10,12 +10,50 @@
#include "SimplePhysicsComponent.h"
#include "Database.h"
#include "DluAssert.h"
ModelComponent::ModelComponent(Entity* parent) : Component(parent) {
m_OriginalPosition = m_Parent->GetDefaultPosition();
m_OriginalRotation = m_Parent->GetDefaultRotation();
m_IsPaused = false;
m_NumListeningInteract = 0;
m_userModelID = m_Parent->GetVarAs<LWOOBJID>(u"userModelID");
RegisterMsg(MessageType::Game::REQUEST_USE, this, &ModelComponent::OnRequestUse);
RegisterMsg(MessageType::Game::RESET_MODEL_TO_DEFAULTS, this, &ModelComponent::OnResetModelToDefaults);
}
bool ModelComponent::OnResetModelToDefaults(GameMessages::GameMsg& msg) {
auto& reset = static_cast<GameMessages::ResetModelToDefaults&>(msg);
for (auto& behavior : m_Behaviors) behavior.HandleMsg(reset);
GameMessages::UnSmash unsmash;
unsmash.target = GetParent()->GetObjectID();
unsmash.duration = 0.0f;
unsmash.Send(UNASSIGNED_SYSTEM_ADDRESS);
m_NumListeningInteract = 0;
m_NumActiveUnSmash = 0;
m_Dirty = true;
Game::entityManager->SerializeEntity(GetParent());
return true;
}
bool ModelComponent::OnRequestUse(GameMessages::GameMsg& msg) {
bool toReturn = false;
if (!m_IsPaused) {
auto& requestUse = static_cast<GameMessages::RequestUse&>(msg);
for (auto& behavior : m_Behaviors) behavior.HandleMsg(requestUse);
toReturn = true;
}
return toReturn;
}
void ModelComponent::Update(float deltaTime) {
if (m_IsPaused) return;
for (auto& behavior : m_Behaviors) {
behavior.Update(deltaTime, *this);
}
}
void ModelComponent::LoadBehaviors() {
@@ -29,9 +67,9 @@ void ModelComponent::LoadBehaviors() {
LOG_DEBUG("Loading behavior %d", behaviorId.value());
auto& inserted = m_Behaviors.emplace_back();
inserted.SetBehaviorId(*behaviorId);
const auto behaviorStr = Database::Get()->GetBehavior(behaviorId.value());
tinyxml2::XMLDocument behaviorXml;
auto res = behaviorXml.Parse(behaviorStr.c_str(), behaviorStr.size());
LOG_DEBUG("Behavior %i %d: %s", res, behaviorId.value(), behaviorStr.c_str());
@@ -45,6 +83,11 @@ void ModelComponent::LoadBehaviors() {
}
}
void ModelComponent::Resume() {
m_Dirty = true;
m_IsPaused = false;
}
void ModelComponent::Serialize(RakNet::BitStream& outBitStream, bool bIsInitialUpdate) {
// ItemComponent Serialization. Pets do not get this serialization.
if (!m_Parent->HasComponent(eReplicaComponentType::PET)) {
@@ -56,14 +99,14 @@ void ModelComponent::Serialize(RakNet::BitStream& outBitStream, bool bIsInitialU
//actual model component:
outBitStream.Write1(); // Yes we are writing model info
outBitStream.Write0(); // Is pickable
outBitStream.Write(m_NumListeningInteract > 0); // Is pickable
outBitStream.Write<uint32_t>(2); // Physics type
outBitStream.Write(m_OriginalPosition); // Original position
outBitStream.Write(m_OriginalRotation); // Original rotation
outBitStream.Write1(); // We are writing behavior info
outBitStream.Write<uint32_t>(0); // Number of behaviors
outBitStream.Write1(); // Is this model paused
outBitStream.Write<uint32_t>(m_Behaviors.size()); // Number of behaviors
outBitStream.Write(m_IsPaused); // Is this model paused
if (bIsInitialUpdate) outBitStream.Write0(); // We are not writing model editing info
}
@@ -135,3 +178,28 @@ std::array<std::pair<int32_t, std::string>, 5> ModelComponent::GetBehaviorsForSa
}
return toReturn;
}
void ModelComponent::AddInteract() {
LOG_DEBUG("Adding interact %i", m_NumListeningInteract);
m_Dirty = true;
m_NumListeningInteract++;
}
void ModelComponent::RemoveInteract() {
DluAssert(m_NumListeningInteract > 0);
LOG_DEBUG("Removing interact %i", m_NumListeningInteract);
m_Dirty = true;
m_NumListeningInteract--;
}
void ModelComponent::AddUnSmash() {
LOG_DEBUG("Adding UnSmash %i", m_NumActiveUnSmash);
m_NumActiveUnSmash++;
}
void ModelComponent::RemoveUnSmash() {
// Players can assign an UnSmash without a Smash so an assert would be bad here
if (m_NumActiveUnSmash == 0) return;
LOG_DEBUG("Removing UnSmash %i", m_NumActiveUnSmash);
m_NumActiveUnSmash--;
}

View File

@@ -30,6 +30,10 @@ public:
ModelComponent(Entity* parent);
void LoadBehaviors();
void Update(float deltaTime) override;
bool OnRequestUse(GameMessages::GameMsg& msg);
bool OnResetModelToDefaults(GameMessages::GameMsg& msg);
void Serialize(RakNet::BitStream& outBitStream, bool bIsInitialUpdate) override;
@@ -59,7 +63,7 @@ public:
/**
* Main gateway for all behavior messages to be passed to their respective behaviors.
*
*
* @tparam Msg The message type to pass
* @param args the arguments of the message to be deserialized
*/
@@ -68,7 +72,7 @@ public:
static_assert(std::is_base_of_v<BehaviorMessageBase, Msg>, "Msg must be a BehaviorMessageBase");
Msg msg{ args };
for (auto&& behavior : m_Behaviors) {
if (behavior.GetBehaviorId() == msg.GetBehaviorId()) {
if (behavior.GetBehaviorId() == msg.GetBehaviorId()) {
behavior.HandleMsg(msg);
return;
}
@@ -109,12 +113,35 @@ public:
void SendBehaviorListToClient(AMFArrayValue& args) const;
void SendBehaviorBlocksToClient(int32_t behaviorToSend, AMFArrayValue& args) const;
void VerifyBehaviors();
std::array<std::pair<int32_t, std::string>, 5> GetBehaviorsForSave() const;
const std::vector<PropertyBehavior>& GetBehaviors() const { return m_Behaviors; };
void AddInteract();
void RemoveInteract();
void Pause() { m_Dirty = true; m_IsPaused = true; }
void AddUnSmash();
void RemoveUnSmash();
bool IsUnSmashing() const { return m_NumActiveUnSmash != 0; }
void Resume();
private:
// Number of Actions that are awaiting an UnSmash to finish.
uint32_t m_NumActiveUnSmash{};
// Whether or not this component needs to have its extra data serialized.
bool m_Dirty{};
// The number of strips listening for a RequestUse GM to come in.
uint32_t m_NumListeningInteract{};
// Whether or not the model is paused and should reject all interactions regarding behaviors.
bool m_IsPaused{};
/**
* The behaviors of the model
* Note: This is a vector because the order of the behaviors matters when serializing to the client.

View File

@@ -255,6 +255,18 @@ void PropertyManagementComponent::OnStartBuilding() {
// Push equipped items
if (inventoryComponent) inventoryComponent->PushEquippedItems();
for (auto modelID : models | std::views::keys) {
auto* model = Game::entityManager->GetEntity(modelID);
if (model) {
auto* modelComponent = model->GetComponent<ModelComponent>();
if (modelComponent) modelComponent->Pause();
Game::entityManager->SerializeEntity(model);
GameMessages::ResetModelToDefaults reset;
reset.target = modelID;
model->HandleMsg(reset);
}
}
}
void PropertyManagementComponent::OnFinishBuilding() {
@@ -267,6 +279,18 @@ void PropertyManagementComponent::OnFinishBuilding() {
UpdateApprovedStatus(false);
Save();
for (auto modelID : models | std::views::keys) {
auto* model = Game::entityManager->GetEntity(modelID);
if (model) {
auto* modelComponent = model->GetComponent<ModelComponent>();
if (modelComponent) modelComponent->Resume();
Game::entityManager->SerializeEntity(model);
GameMessages::ResetModelToDefaults reset;
reset.target = modelID;
model->HandleMsg(reset);
}
}
}
void PropertyManagementComponent::UpdateModelPosition(const LWOOBJID id, const NiPoint3 position, NiQuaternion rotation) {
@@ -318,6 +342,8 @@ void PropertyManagementComponent::UpdateModelPosition(const LWOOBJID id, const N
Entity* newEntity = Game::entityManager->CreateEntity(info);
if (newEntity != nullptr) {
Game::entityManager->ConstructEntity(newEntity);
auto* modelComponent = newEntity->GetComponent<ModelComponent>();
if (modelComponent) modelComponent->Pause();
// Make sure the propMgmt doesn't delete our model after the server dies
// Trying to do this after the entity is constructed. Shouldn't really change anything but
@@ -363,6 +389,8 @@ void PropertyManagementComponent::UpdateModelPosition(const LWOOBJID id, const N
info.nodes[0]->config.push_back(new LDFData<int>(u"componentWhitelist", 1));
auto* model = spawner->Spawn();
auto* modelComponent = model->GetComponent<ModelComponent>();
if (modelComponent) modelComponent->Pause();
models.insert_or_assign(model->GetObjectID(), spawnerId);