Refactor damage calculations and add additional modifiers

This commit is contained in:
wincent
2024-07-06 00:02:30 +02:00
parent 756dc4e44f
commit 5e3312850c
23 changed files with 1061 additions and 394 deletions

View File

@@ -556,7 +556,7 @@ void DestroyableComponent::Repair(const uint32_t armor) {
}
void DestroyableComponent::Damage(uint32_t damage, const LWOOBJID source, uint32_t skillID, bool echo) {
void DestroyableComponent::Damage(uint32_t damage, const LWOOBJID source, uint32_t skillID, bool echo, bool isDamageOverTime) {
if (GetHealth() <= 0) {
return;
}
@@ -572,7 +572,9 @@ void DestroyableComponent::Damage(uint32_t damage, const LWOOBJID source, uint32
return;
}
OnDamageCalculation(m_Parent, source, skillID, damage);
if (!isDamageOverTime) {
OnDamageCalculation(m_Parent, source, skillID, damage);
}
// If this entity has damage reduction, reduce the damage to a minimum of 1
if (m_DamageReduction > 0 && damage > 0) {
@@ -641,6 +643,42 @@ void DestroyableComponent::Damage(uint32_t damage, const LWOOBJID source, uint32
cb(attacker);
}
std::stringstream damageUIMessage;
auto damagedPosition = m_Parent->GetPosition();
// Add a slight random offset to the damage position
damagedPosition.x += (rand() % 10 - 5) / 5.0f;
damagedPosition.y += (rand() % 10 - 5) / 5.0f;
damagedPosition.z += (rand() % 10 - 5) / 5.0f;
int colorR = 255;
int colorG = 255;
int colorB = 255;
int colorA = 0;
if (m_Parent->IsPlayer()) {
// Make the damage red
colorR = 0;
colorG = 255;
colorB = 0;
colorA = 0;
}
const auto damageText = damage != 0 ? std::to_string(damage) : "Miss";
damageUIMessage << 0.0825 << ";" << 0.12 << ";" << damagedPosition.x << ";" << damagedPosition.y + 4.5f << ";" << damagedPosition.z << ";" << 0.1 << ";";
damageUIMessage << 200 << ";" << 200 << ";" << 0.5 << ";" << 1.0 << ";" << damageText << ";" << 4 << ";" << 4 << ";" << colorR << ";" << colorG << ";" << colorB << ";";
damageUIMessage << colorA;
const auto damageUIStr = damageUIMessage.str();
if (m_Parent->IsPlayer()) {
m_Parent->SetNetworkVar<std::string>(u"renderText", damageUIStr, UNASSIGNED_SYSTEM_ADDRESS);
} else if (attacker->IsPlayer()) {
attacker->SetNetworkVar<std::string>(u"renderText", damageUIStr, UNASSIGNED_SYSTEM_ADDRESS);
}
if (health != 0) {
auto* combatComponent = m_Parent->GetComponent<BaseCombatAIComponent>();

View File

@@ -384,8 +384,9 @@ public:
* @param source the attacker that caused this damage
* @param skillID the skill that damaged this entity
* @param echo whether or not to serialize the damage
* @param isDamageOverTime whether or not this damage is over time
*/
void Damage(uint32_t damage, LWOOBJID source, uint32_t skillID = 0, bool echo = true);
void Damage(uint32_t damage, LWOOBJID source, uint32_t skillID = 0, bool echo = true, bool isDamageOverTime = false);
/**
* Smashes this entity, notifying all clients

View File

@@ -1,6 +1,7 @@
#include "InventoryComponent.h"
#include <sstream>
#include <algorithm>
#include "Entity.h"
#include "Item.h"
@@ -43,6 +44,7 @@ Observable<InventoryComponent*, Item*> InventoryComponent::OnItemDestroyed;
Observable<InventoryComponent*, Item*> InventoryComponent::OnItemEquipped;
Observable<InventoryComponent*, Item*> InventoryComponent::OnItemUnequipped;
Observable<InventoryComponent*, Item*> InventoryComponent::OnItemLoaded;
Observable<InventoryComponent*, Item*> InventoryComponent::OnCountChanged;
InventoryComponent::InventoryComponent(Entity* parent) : Component(parent) {
this->m_Dirty = true;
@@ -1131,27 +1133,13 @@ void InventoryComponent::AddItemSkills(const LOT lot) {
return;
}
const auto skill = FindSkill(lot);
if (m_PrimarySkill != 0) {
GameMessages::SendRemoveSkill(m_Parent, m_PrimarySkill);
}
const auto index = m_Skills.find(slot);
m_PrimarySkill = FindSkill(lot);
if (index != m_Skills.end()) {
const auto old = index->second;
if (!old.empty()) {
const auto firstElem = *old.begin();
GameMessages::SendRemoveSkill(m_Parent, firstElem);
}
}
m_Skills.erase(slot);
if (skill != 0) {
m_Skills.insert_or_assign(slot, std::set<uint32_t>{ skill });
GameMessages::SendAddSkill(m_Parent, skill, slot);
}
GameMessages::SendAddSkill(m_Parent, m_PrimarySkill, slot);
}
void InventoryComponent::RemoveItemSkills(const LOT lot) {
@@ -1163,27 +1151,11 @@ void InventoryComponent::RemoveItemSkills(const LOT lot) {
return;
}
const auto index = m_Skills.find(slot);
GameMessages::SendRemoveSkill(m_Parent, m_PrimarySkill);
if (index == m_Skills.end()) {
return;
}
m_PrimarySkill = 1;
const auto old = index->second;
if (!old.empty()) {
const auto firstElem = *old.begin();
GameMessages::SendRemoveSkill(m_Parent, firstElem);
}
m_Skills.erase(slot);
if (slot == BehaviorSlot::Primary) {
m_Skills.insert_or_assign(BehaviorSlot::Primary, std::set<uint32_t>{ 1 });
GameMessages::SendAddSkill(m_Parent, 1, BehaviorSlot::Primary);
}
GameMessages::SendAddSkill(m_Parent, m_PrimarySkill, BehaviorSlot::Primary);
}
void InventoryComponent::TriggerPassiveAbility(PassiveAbilityTrigger trigger, Entity* target) {
@@ -1360,6 +1332,71 @@ void InventoryComponent::SetNPCItems(const std::vector<LOT>& items) {
Game::entityManager->SerializeEntity(m_Parent);
}
bool InventoryComponent::AddSkill(uint32_t skillId) {
if (std::find(m_Skills.begin(), m_Skills.end(), skillId) != m_Skills.end()) {
return false;
}
m_Skills.push_back(skillId);
UpdateSkills();
return true;
}
bool InventoryComponent::RemoveSkill(uint32_t skillId) {
const auto index = std::find(m_Skills.begin(), m_Skills.end(), skillId);
if (index == m_Skills.end()) {
return false;
}
m_Skills.erase(index);
UpdateSkills();
return true;
}
void InventoryComponent::UpdateSkills() {
// There are two skills active at the same time. If the rotation index is greater than the amount of skills / 2, set it to the max number is can be.
// This is to prevent the rotation index from going out of bounds.
if (m_SkillRotationIndex * 2 >= m_Skills.size()) {
m_SkillRotationIndex = 0;
}
const auto startIndex = m_SkillRotationIndex * 2;
const auto activeSkillA = m_Skills.size() > startIndex ? m_Skills[startIndex] : 0;
const auto activeSkillB = m_Skills.size() > startIndex + 1 ? m_Skills[startIndex + 1] : 0;
std::cout << "Skill rotation index: " << m_SkillRotationIndex << " Active skills: " << activeSkillA << " " << activeSkillB << "\n";
GameMessages::SendRemoveSkill(m_Parent, m_ActiveSkills.first);
GameMessages::SendRemoveSkill(m_Parent, m_ActiveSkills.second);
m_ActiveSkills = { activeSkillA, activeSkillB };
if (activeSkillA != 0) {
GameMessages::SendAddSkill(m_Parent, activeSkillA, BehaviorSlot::Head);
}
if (activeSkillB != 0) {
GameMessages::SendAddSkill(m_Parent, activeSkillB, BehaviorSlot::Offhand);
}
}
void InventoryComponent::RotateSkills() {
m_SkillRotationIndex++;
if (m_SkillRotationIndex * 2 >= m_Skills.size()) {
m_SkillRotationIndex = 0;
}
UpdateSkills();
}
InventoryComponent::~InventoryComponent() {
for (const auto& inventory : m_Inventories) {
delete inventory.second;
@@ -1614,60 +1651,4 @@ void InventoryComponent::UpdatePetXml(tinyxml2::XMLDocument& document) {
}
bool InventoryComponent::SetSkill(int32_t slot, uint32_t skillId) {
BehaviorSlot behaviorSlot = BehaviorSlot::Invalid;
if (slot == 1) behaviorSlot = BehaviorSlot::Primary;
else if (slot == 2) behaviorSlot = BehaviorSlot::Offhand;
else if (slot == 3) behaviorSlot = BehaviorSlot::Neck;
else if (slot == 4) behaviorSlot = BehaviorSlot::Head;
else if (slot == 5) behaviorSlot = BehaviorSlot::Consumable;
else return false;
return SetSkill(behaviorSlot, skillId);
}
bool InventoryComponent::SetSkill(BehaviorSlot slot, uint32_t skillId) {
if (skillId == 0) return false;
const auto index = m_Skills.find(slot);
if (index == m_Skills.end()) {
m_Skills.insert_or_assign(slot, std::set<uint32_t>{ skillId });
} else {
auto& existing = index->second;
existing.insert(skillId);
}
return true;
}
void InventoryComponent::UnsetSkill(uint32_t skillId) {
for (auto& pair : m_Skills) {
auto& skills = pair.second;
skills.erase(skillId);
}
}
void InventoryComponent::SetSkill(uint32_t skillId) {
UnsetSkill(skillId);
const auto& slotA = m_Skills.find(BehaviorSlot::Head);
const auto& slotB = m_Skills.find(BehaviorSlot::Neck);
const auto& slotC = m_Skills.find(BehaviorSlot::Offhand);
// Pick the first one which has less than 3 skills
std::set<uint32_t>* slot = nullptr;
if (slotA == m_Skills.end() || slotA->second.size() < 3) {
slot = &m_Skills[BehaviorSlot::Head];
} else if (slotB == m_Skills.end() || slotB->second.size() < 3) {
slot = &m_Skills[BehaviorSlot::Neck];
} else if (slotC == m_Skills.end() || slotC->second.size() < 3) {
slot = &m_Skills[BehaviorSlot::Offhand];
}
if (slot == nullptr) {
return;
}
slot->insert(skillId);
}

View File

@@ -368,12 +368,14 @@ public:
*/
void UnequipScripts(Item* unequippedItem);
const std::map<BehaviorSlot, std::set<uint32_t>>& GetSkills(){ return m_Skills; };
uint32_t GetPrimarySkill() const { return m_PrimarySkill; }
bool SetSkill(int32_t slot, uint32_t skillId);
bool SetSkill(BehaviorSlot slot, uint32_t skillId);
void UnsetSkill(uint32_t skillId);
void SetSkill(uint32_t skillId);
const std::vector<uint32_t>& GetSkills(){ return m_Skills; };
bool AddSkill(uint32_t skillId);
bool RemoveSkill(uint32_t skillId);
void RotateSkills();
void UpdateSkills();
~InventoryComponent() override;
@@ -382,6 +384,7 @@ public:
static Observable<InventoryComponent*, Item*> OnItemEquipped;
static Observable<InventoryComponent*, Item*> OnItemUnequipped;
static Observable<InventoryComponent*, Item*> OnItemLoaded;
static Observable<InventoryComponent*, Item*> OnCountChanged;
private:
/**
@@ -391,10 +394,16 @@ private:
std::map<BehaviorSlot, uint32_t> m_ActivatorSkills;
uint32_t m_PrimarySkill = 0;
/**
* The skills that this entity currently has active
*/
std::map<BehaviorSlot, std::set<uint32_t>> m_Skills;
std::vector<uint32_t> m_Skills;
std::pair<uint32_t, uint32_t> m_ActiveSkills;
uint32_t m_SkillRotationIndex = 0;
/**
* The pets this entity has, mapped by object ID and pet info