mirror of
https://github.com/DarkflameUniverse/DarkflameServer.git
synced 2024-12-23 14:03:34 +00:00
cdc9dda3c4
* Visiblity and effect records * Recorder will catch effects from behaviors * Documentation for setting up a scene to play automatically. * Documentation for server-side preconditions.
247 lines
6.8 KiB
C++
247 lines
6.8 KiB
C++
#include "RenderComponent.h"
|
|
|
|
#include <sstream>
|
|
#include <string>
|
|
#include <iomanip>
|
|
|
|
#include "Entity.h"
|
|
#include "PacketUtils.h"
|
|
|
|
#include "CDClientManager.h"
|
|
#include "GameMessages.h"
|
|
#include "Game.h"
|
|
#include "Logger.h"
|
|
#include "CDAnimationsTable.h"
|
|
|
|
std::unordered_map<int32_t, float> RenderComponent::m_DurationCache{};
|
|
|
|
std::unordered_map<int32_t, std::vector<int32_t>> RenderComponent::m_AnimationGroupCache{};
|
|
|
|
RenderComponent::RenderComponent(Entity* parent, int32_t componentId): Component(parent) {
|
|
m_Effects = std::vector<Effect*>();
|
|
m_LastAnimationName = "";
|
|
if (componentId == -1) return;
|
|
|
|
const auto& it = m_AnimationGroupCache.find(componentId);
|
|
|
|
if (it != m_AnimationGroupCache.end()) {
|
|
m_animationGroupIds = it->second;
|
|
|
|
return;
|
|
}
|
|
|
|
auto query = CDClientDatabase::CreatePreppedStmt("SELECT * FROM RenderComponent WHERE id = ?;");
|
|
query.bind(1, componentId);
|
|
auto result = query.execQuery();
|
|
|
|
if (!result.eof()) {
|
|
auto animationGroupIDs = std::string(result.getStringField("animationGroupIDs", ""));
|
|
if (!animationGroupIDs.empty()) {
|
|
auto* animationsTable = CDClientManager::Instance().GetTable<CDAnimationsTable>();
|
|
auto groupIdsSplit = GeneralUtils::SplitString(animationGroupIDs, ',');
|
|
for (auto& groupId : groupIdsSplit) {
|
|
int32_t groupIdInt;
|
|
if (!GeneralUtils::TryParse(groupId, groupIdInt)) {
|
|
LOG("bad animation group Id %s", groupId.c_str());
|
|
continue;
|
|
}
|
|
m_animationGroupIds.push_back(groupIdInt);
|
|
animationsTable->CacheAnimationGroup(groupIdInt);
|
|
}
|
|
}
|
|
}
|
|
result.finalize();
|
|
|
|
m_AnimationGroupCache[componentId] = m_animationGroupIds;
|
|
}
|
|
|
|
RenderComponent::~RenderComponent() {
|
|
for (Effect* eff : m_Effects) {
|
|
if (eff) {
|
|
delete eff;
|
|
eff = nullptr;
|
|
}
|
|
}
|
|
|
|
m_Effects.clear();
|
|
}
|
|
|
|
void RenderComponent::Serialize(RakNet::BitStream* outBitStream, bool bIsInitialUpdate) {
|
|
if (!bIsInitialUpdate) return;
|
|
|
|
outBitStream->Write<uint32_t>(m_Effects.size());
|
|
|
|
for (Effect* eff : m_Effects) {
|
|
// we still need to write 0 as the size for name if it is a nullptr
|
|
if (!eff) {
|
|
outBitStream->Write<uint8_t>(0);
|
|
continue;
|
|
}
|
|
|
|
outBitStream->Write<uint8_t>(eff->name.size());
|
|
// if there is no name, then we don't write anything else
|
|
if (eff->name.empty()) continue;
|
|
|
|
for (const auto& value : eff->name) outBitStream->Write<uint8_t>(value);
|
|
|
|
outBitStream->Write(eff->effectID);
|
|
|
|
outBitStream->Write<uint8_t>(eff->type.size());
|
|
for (const auto& value : eff->type) outBitStream->Write<uint16_t>(value);
|
|
|
|
outBitStream->Write<float_t>(eff->priority);
|
|
outBitStream->Write<int64_t>(eff->secondary);
|
|
}
|
|
}
|
|
|
|
Effect* RenderComponent::AddEffect(const int32_t effectId, const std::string& name, const std::u16string& type, const float priority) {
|
|
auto* eff = new Effect();
|
|
|
|
eff->effectID = effectId;
|
|
eff->name = name;
|
|
eff->type = type;
|
|
eff->priority = priority;
|
|
m_Effects.push_back(eff);
|
|
|
|
return eff;
|
|
}
|
|
|
|
void RenderComponent::RemoveEffect(const std::string& name) {
|
|
uint32_t index = -1;
|
|
|
|
for (auto i = 0u; i < m_Effects.size(); ++i) {
|
|
auto* eff = m_Effects[i];
|
|
|
|
if (eff->name == name) {
|
|
index = i;
|
|
|
|
delete eff;
|
|
|
|
break;
|
|
}
|
|
}
|
|
|
|
if (index == -1) {
|
|
return;
|
|
}
|
|
|
|
m_Effects.erase(m_Effects.begin() + index);
|
|
}
|
|
|
|
void RenderComponent::Update(const float deltaTime) {
|
|
std::vector<Effect*> dead;
|
|
|
|
for (auto* effect : m_Effects) {
|
|
if (effect->time == 0) {
|
|
continue; // Skip persistent effects
|
|
}
|
|
|
|
const auto result = effect->time - deltaTime;
|
|
|
|
if (result <= 0) {
|
|
dead.push_back(effect);
|
|
|
|
continue;
|
|
}
|
|
|
|
effect->time = result;
|
|
}
|
|
|
|
for (auto* effect : dead) {
|
|
// StopEffect(effect->name);
|
|
}
|
|
}
|
|
|
|
void RenderComponent::PlayEffect(const int32_t effectId, const std::u16string& effectType, const std::string& name, const LWOOBJID secondary, const float priority, const float scale, const bool serialize) {
|
|
RemoveEffect(name);
|
|
|
|
GameMessages::SendPlayFXEffect(m_Parent, effectId, effectType, name, secondary, priority, scale, serialize);
|
|
|
|
auto* effect = AddEffect(effectId, name, effectType, priority);
|
|
|
|
const auto& pair = m_DurationCache.find(effectId);
|
|
|
|
if (pair != m_DurationCache.end()) {
|
|
effect->time = pair->second;
|
|
|
|
return;
|
|
}
|
|
|
|
const std::string effectType_str = GeneralUtils::UTF16ToWTF8(effectType);
|
|
|
|
auto query = CDClientDatabase::CreatePreppedStmt(
|
|
"SELECT animation_length FROM Animations WHERE animation_type IN (SELECT animationName FROM BehaviorEffect WHERE effectID = ? AND effectType = ?);");
|
|
query.bind(1, effectId);
|
|
query.bind(2, effectType_str.c_str());
|
|
|
|
auto result = query.execQuery();
|
|
|
|
if (result.eof() || result.fieldIsNull(0)) {
|
|
result.finalize();
|
|
|
|
m_DurationCache[effectId] = 0;
|
|
|
|
effect->time = 0; // Persistent effect
|
|
|
|
return;
|
|
}
|
|
|
|
effect->time = static_cast<float>(result.getFloatField(0));
|
|
|
|
result.finalize();
|
|
|
|
m_DurationCache[effectId] = effect->time;
|
|
}
|
|
|
|
void RenderComponent::StopEffect(const std::string& name, const bool killImmediate) {
|
|
GameMessages::SendStopFXEffect(m_Parent, killImmediate, name);
|
|
|
|
RemoveEffect(name);
|
|
}
|
|
|
|
std::vector<Effect*>& RenderComponent::GetEffects() {
|
|
return m_Effects;
|
|
}
|
|
|
|
|
|
float RenderComponent::PlayAnimation(Entity* self, const std::u16string& animation, float priority, float scale) {
|
|
if (!self) return 0.0f;
|
|
return RenderComponent::PlayAnimation(self, GeneralUtils::UTF16ToWTF8(animation), priority, scale);
|
|
}
|
|
|
|
float RenderComponent::PlayAnimation(Entity* self, const std::string& animation, float priority, float scale) {
|
|
if (!self) return 0.0f;
|
|
return RenderComponent::DoAnimation(self, animation, true, priority, scale);
|
|
}
|
|
|
|
float RenderComponent::GetAnimationTime(Entity* self, const std::u16string& animation) {
|
|
if (!self) return 0.0f;
|
|
return RenderComponent::GetAnimationTime(self, GeneralUtils::UTF16ToWTF8(animation));
|
|
}
|
|
|
|
float RenderComponent::GetAnimationTime(Entity* self, const std::string& animation) {
|
|
if (!self) return 0.0f;
|
|
return RenderComponent::DoAnimation(self, animation, false);
|
|
}
|
|
|
|
|
|
float RenderComponent::DoAnimation(Entity* self, const std::string& animation, bool sendAnimation, float priority, float scale) {
|
|
float returnlength = 0.0f;
|
|
if (!self) return returnlength;
|
|
auto* renderComponent = self->GetComponent<RenderComponent>();
|
|
if (!renderComponent) return returnlength;
|
|
|
|
auto* animationsTable = CDClientManager::Instance().GetTable<CDAnimationsTable>();
|
|
for (auto& groupId : renderComponent->m_animationGroupIds) {
|
|
auto animationGroup = animationsTable->GetAnimation(animation, renderComponent->GetLastAnimationName(), groupId);
|
|
if (animationGroup.FoundData()) {
|
|
auto data = animationGroup.Data();
|
|
renderComponent->SetLastAnimationName(data.animation_name);
|
|
returnlength = data.animation_length;
|
|
}
|
|
}
|
|
if (sendAnimation) GameMessages::SendPlayAnimation(self, GeneralUtils::ASCIIToUTF16(animation), priority, scale);
|
|
if (returnlength == 0.0f) LOG("WARNING: Unable to find animation %s for lot %i in any group.", animation.c_str(), self->GetLOT());
|
|
return returnlength;
|
|
}
|