#include "BuffComponent.h" #include #include "CDClientDatabase.h" #include #include "DestroyableComponent.h" #include "Game.h" #include "dLogger.h" #include "GameMessages.h" #include "SkillComponent.h" #include "ControllablePhysicsComponent.h" #include "EntityManager.h" std::unordered_map> BuffComponent::m_Cache {}; BuffComponent::BuffComponent(Entity* parent) : Component(parent) { } BuffComponent::~BuffComponent() { } void BuffComponent::Serialize(RakNet::BitStream* outBitStream, bool bIsInitialUpdate, unsigned int& flags) { if (!bIsInitialUpdate) return; if (m_Buffs.empty()) { outBitStream->Write0(); } else { outBitStream->Write1(); outBitStream->Write(m_Buffs.size()); for (const auto& buff : m_Buffs) { outBitStream->Write(buff.first); outBitStream->Write0(); outBitStream->Write0(); outBitStream->Write0(); outBitStream->Write0(); outBitStream->Write0(); outBitStream->Write0(); outBitStream->Write0(); outBitStream->Write0(); outBitStream->Write0(); outBitStream->Write0(); outBitStream->Write0(); outBitStream->Write(0); } } outBitStream->Write0(); } void BuffComponent::Update(float deltaTime) { /** * Loop through all buffs and apply deltaTime to ther time. * If they have expired, remove the buff and break. */ for (auto& buff : m_Buffs) { // For damage buffs if (buff.second.tick != 0.0f && buff.second.stacks > 0) { buff.second.tickTime -= deltaTime; if (buff.second.tickTime <= 0.0f) { buff.second.tickTime = buff.second.tick; buff.second.stacks--; SkillComponent::HandleUnmanaged(buff.second.behaviorID, m_Parent->GetObjectID()); } } // These are indefinate buffs, don't update them. if (buff.second.time == 0.0f) { continue; } buff.second.time -= deltaTime; if (buff.second.time <= 0.0f) { RemoveBuff(buff.first); break; } } } void BuffComponent::ApplyBuff(const int32_t id, const float duration, const LWOOBJID source, bool addImmunity, bool cancelOnDamaged, bool cancelOnDeath, bool cancelOnLogout, bool cancelOnRemoveBuff, bool cancelOnUi, bool cancelOnUnequip, bool cancelOnZone) { // Prevent buffs from stacking. if (HasBuff(id)) { return; } GameMessages::SendAddBuff(const_cast(m_Parent->GetObjectID()), source, (uint32_t) id, (uint32_t) duration * 1000, addImmunity, cancelOnDamaged, cancelOnDeath, cancelOnLogout, cancelOnRemoveBuff, cancelOnUi, cancelOnUnequip, cancelOnZone); float tick = 0; float stacks = 0; int32_t behaviorID = 0; const auto& parameters = GetBuffParameters(id); for (const auto& parameter : parameters) { if (parameter.name == "overtime") { auto* behaviorTemplateTable = CDClientManager::Instance()->GetTable("SkillBehavior"); behaviorID = behaviorTemplateTable->GetSkillByID(parameter.values[0]).behaviorID; stacks = static_cast(parameter.values[1]); tick = parameter.values[2]; const auto unknown2 = parameter.values[3]; // Always 0 } } ApplyBuffEffect(id); Buff buff; buff.id = id; buff.time = duration; buff.tick = tick; buff.tickTime = tick; buff.stacks = stacks; buff.source = source; buff.behaviorID = behaviorID; m_Buffs.emplace(id, buff); } void BuffComponent::RemoveBuff(int32_t id) { const auto& iter = m_Buffs.find(id); if (iter == m_Buffs.end()) { return; } m_Buffs.erase(iter); RemoveBuffEffect(id); } bool BuffComponent::HasBuff(int32_t id) { return m_Buffs.find(id) != m_Buffs.end(); } void BuffComponent::ApplyBuffEffect(int32_t id) { const auto& parameters = GetBuffParameters(id); for (const auto& parameter : parameters) { if (parameter.name == "max_health") { const auto maxHealth = parameter.value; auto* destroyable = this->GetParent()->GetComponent(); if (destroyable == nullptr) return; destroyable->SetMaxHealth(destroyable->GetMaxHealth() + maxHealth); } else if (parameter.name == "max_armor") { const auto maxArmor = parameter.value; auto* destroyable = this->GetParent()->GetComponent(); if (destroyable == nullptr) return; destroyable->SetMaxArmor(destroyable->GetMaxArmor() + maxArmor); } else if (parameter.name == "max_imagination") { const auto maxImagination = parameter.value; auto* destroyable = this->GetParent()->GetComponent(); if (destroyable == nullptr) return; destroyable->SetMaxImagination(destroyable->GetMaxImagination() + maxImagination); } else if (parameter.name == "speed") { const auto speed = parameter.value; auto* controllablePhysicsComponent = this->GetParent()->GetComponent(); if (controllablePhysicsComponent == nullptr) return; const auto current = controllablePhysicsComponent->GetSpeedMultiplier(); controllablePhysicsComponent->SetSpeedMultiplier(current + ((speed - 500.0f) / 500.0f)); EntityManager::Instance()->SerializeEntity(this->GetParent()); } } } void BuffComponent::RemoveBuffEffect(int32_t id) { const auto& parameters = GetBuffParameters(id); for (const auto& parameter : parameters) { if (parameter.name == "max_health") { const auto maxHealth = parameter.value; auto* destroyable = this->GetParent()->GetComponent(); if (destroyable == nullptr) return; destroyable->SetMaxHealth(destroyable->GetMaxHealth() - maxHealth); } else if (parameter.name == "max_armor") { const auto maxArmor = parameter.value; auto* destroyable = this->GetParent()->GetComponent(); if (destroyable == nullptr) return; destroyable->SetMaxArmor(destroyable->GetMaxArmor() - maxArmor); } else if (parameter.name == "max_imagination") { const auto maxImagination = parameter.value; auto* destroyable = this->GetParent()->GetComponent(); if (destroyable == nullptr) return; destroyable->SetMaxImagination(destroyable->GetMaxImagination() - maxImagination); } else if (parameter.name == "speed") { const auto speed = parameter.value; auto* controllablePhysicsComponent = this->GetParent()->GetComponent(); if (controllablePhysicsComponent == nullptr) return; const auto current = controllablePhysicsComponent->GetSpeedMultiplier(); controllablePhysicsComponent->SetSpeedMultiplier(current - ((speed - 500.0f) / 500.0f)); EntityManager::Instance()->SerializeEntity(this->GetParent()); } } } void BuffComponent::RemoveAllBuffs() { for (const auto& buff : m_Buffs) { RemoveBuffEffect(buff.first); } m_Buffs.clear(); } void BuffComponent::Reset() { RemoveAllBuffs(); } void BuffComponent::ReApplyBuffs() { for (const auto& buff : m_Buffs) { ApplyBuffEffect(buff.first); } } Entity* BuffComponent::GetParent() const { return m_Parent; } void BuffComponent::LoadFromXML(tinyxml2::XMLDocument* doc) { // Load buffs auto* dest = doc->FirstChildElement("obj")->FirstChildElement("dest"); // Make sure we have a clean buff element. auto* buffElement = dest->FirstChildElement("buff"); // Old character, no buffs to load if (buffElement == nullptr) { return; } auto* buffEntry = buffElement->FirstChildElement("b"); while (buffEntry != nullptr) { int32_t id = buffEntry->IntAttribute("id"); float t = buffEntry->FloatAttribute("t"); float tk = buffEntry->FloatAttribute("tk"); int32_t s = buffEntry->FloatAttribute("s"); LWOOBJID sr = buffEntry->Int64Attribute("sr"); int32_t b = buffEntry->IntAttribute("b"); Buff buff; buff.id = id; buff.time = t; buff.tick = tk; buff.stacks = s; buff.source = sr; buff.behaviorID = b; m_Buffs.emplace(id, buff); buffEntry = buffEntry->NextSiblingElement("b"); } } void BuffComponent::UpdateXml(tinyxml2::XMLDocument* doc) { // Save buffs auto* dest = doc->FirstChildElement("obj")->FirstChildElement("dest"); // Make sure we have a clean buff element. auto* buffElement = dest->FirstChildElement("buff"); if (buffElement == nullptr) { buffElement = doc->NewElement("buff"); dest->LinkEndChild(buffElement); } else { buffElement->DeleteChildren(); } for (const auto& buff : m_Buffs) { auto* buffEntry = doc->NewElement("b"); buffEntry->SetAttribute("id", buff.first); buffEntry->SetAttribute("t", buff.second.time); buffEntry->SetAttribute("tk", buff.second.tick); buffEntry->SetAttribute("s", buff.second.stacks); buffEntry->SetAttribute("sr", buff.second.source); buffEntry->SetAttribute("b", buff.second.behaviorID); buffElement->LinkEndChild(buffEntry); } } const std::vector& BuffComponent::GetBuffParameters(int32_t buffId) { const auto& pair = m_Cache.find(buffId); if (pair != m_Cache.end()) { return pair->second; } std::stringstream query; query << "SELECT * FROM BuffParameters WHERE BuffID = " << std::to_string(buffId) << ";"; auto result = CDClientDatabase::ExecuteQuery(query.str()); std::vector parameters {}; while (!result.eof()) { BuffParameter param; param.buffId = buffId; param.name = result.getStringField(1); param.value = result.getFloatField(2); if (!result.fieldIsNull(3)) { std::istringstream stream(result.getStringField(3)); std::string token; while (std::getline(stream, token, ',')) { try { const auto value = std::stof(token); param.values.push_back(value); } catch (std::invalid_argument& exception) { Game::logger->Log("BuffComponent", "Failed to parse value (%s): (%s)!\n", token.c_str(), exception.what()); } } } parameters.push_back(param); result.nextRow(); } m_Cache.insert_or_assign(buffId, parameters); return m_Cache.find(buffId)->second; }