DarkflameServer/dGame/AdditionalEntityData.cpp
2024-06-02 15:43:35 +02:00

356 lines
9.4 KiB
C++

#include "AdditionalEntityData.h"
#include "NejlikaData.h"
#include <DestroyableComponent.h>
#include <LevelProgressionComponent.h>
#include <InventoryComponent.h>
#include <BaseCombatAIComponent.h>
#include <TeamManager.h>
#include <Item.h>
using namespace nejlika;
float nejlika::AdditionalEntityData::CalculateModifier(ModifierType type, ModifierOperator op, bool resistance) const
{
float total = 0;
for (const auto& modifier : activeModifiers) {
if (modifier.GetType() != type || modifier.GetOperator() != op || modifier.IsResistance() != resistance) {
continue;
}
total += modifier.GetValue();
}
return total;
}
float nejlika::AdditionalEntityData::CalculateModifier(ModifierType type, std::vector<ModifierInstance>& additionalModifiers, ModifierOperator op, bool resistance) const {
float total = 0;
for (const auto& modifier : additionalModifiers) {
if (modifier.GetType() != type || modifier.GetOperator() != op || modifier.IsResistance() != resistance) {
continue;
}
total += modifier.GetValue();
}
return total + CalculateModifier(type, op, resistance);
}
float nejlika::AdditionalEntityData::CalculateModifier(ModifierType type, int32_t level) const
{
const auto templateDataOpt = NejlikaData::GetEntityTemplate(lot);
if (!templateDataOpt.has_value()) {
return 0;
}
const auto& templateData = *templateDataOpt.value();
const auto scaler = templateData.GetScaler(type, false, level);
float additive = CalculateModifier(type, ModifierOperator::Additive, false);
if (scaler != 0 && additive >= scaler) {
additive -= scaler;
}
float multiplicative = CalculateModifier(type, ModifierOperator::Multiplicative, false);
std::cout << "Scaler: " << scaler << " Additive: " << additive << " Multiplicative: " << multiplicative << std::endl;
return (scaler + additive) * (1 + multiplicative / 100);
}
float nejlika::AdditionalEntityData::CalculateModifier(ModifierType type) const {
return CalculateModifier(type, level);
}
float nejlika::AdditionalEntityData::CalculateModifier(ModifierType type, std::vector<ModifierInstance>& additionalModifiers, int32_t level) const
{
const auto templateDataOpt = NejlikaData::GetEntityTemplate(lot);
if (!templateDataOpt.has_value()) {
return 0;
}
const auto& templateData = *templateDataOpt.value();
const auto scaler = templateData.GetScaler(type, false, level);
float additive = CalculateModifier(type, additionalModifiers, ModifierOperator::Additive, false);
if (scaler != 0 && additive >= scaler) {
additive -= scaler;
}
float multiplicative = CalculateModifier(type, additionalModifiers, ModifierOperator::Multiplicative, false);
static const std::unordered_set<ModifierType> damageTypes = {
ModifierType::Slashing,
ModifierType::Piercing,
ModifierType::Bludgeoning,
ModifierType::Fire,
ModifierType::Cold,
ModifierType::Lightning,
ModifierType::Corruption,
ModifierType::Psychic
};
if (damageTypes.contains(type)) {
additive += CalculateModifier(ModifierType::Damage, additionalModifiers, ModifierOperator::Additive, false);
multiplicative += CalculateModifier(ModifierType::Damage, additionalModifiers, ModifierOperator::Multiplicative, false);
}
float total = (scaler + additive) * (1 + multiplicative / 100);
std::cout << "Scaler: " << scaler << " Additive: " << additive << " Multiplicative: " << multiplicative << " Total: " << total << std::endl;
return total;
}
float nejlika::AdditionalEntityData::CalculateResistance(ModifierType type) const
{
return CalculateModifier(type, ModifierOperator::Multiplicative, true);
}
std::vector<ModifierInstance> nejlika::AdditionalEntityData::TriggerUpgradeItems(UpgradeTriggerType triggerType) {
auto* entity = Game::entityManager->GetEntity(id);
if (entity == nullptr) {
return {};
}
auto* inventoryComponent = entity->GetComponent<InventoryComponent>();
if (inventoryComponent == nullptr) {
return {};
}
std::vector<ModifierInstance> result;
for (const auto& itemID : upgradeItems) {
auto* item = inventoryComponent->FindItemById(itemID);
if (item == nullptr) {
continue;
}
const auto upgradeDataOpt = NejlikaData::GetUpgradeTemplate(item->GetLot());
if (!upgradeDataOpt.has_value()) {
continue;
}
const auto& upgradeData = *upgradeDataOpt.value();
const auto modifiers = upgradeData.Trigger(item->GetCount(), triggerType, id);
result.insert(result.end(), modifiers.begin(), modifiers.end());
}
return result;
}
void nejlika::AdditionalEntityData::RollStandardModifiers(int32_t level) {
standardModifiers.clear();
const auto templateDataOpt = NejlikaData::GetEntityTemplate(lot);
if (templateDataOpt.has_value()) {
const auto& templateData = *templateDataOpt.value();
const auto modifiers = templateData.GenerateModifiers(level);
standardModifiers.insert(standardModifiers.end(), modifiers.begin(), modifiers.end());
}
const auto objectDataVec = NejlikaData::GetModifierNameTemplates(ModifierNameType::Object);
for (const auto& objectData : objectDataVec) {
if (objectData.GetLOT() != lot) {
continue;
}
const auto modifiers = objectData.GenerateModifiers(level);
standardModifiers.insert(standardModifiers.end(), modifiers.begin(), modifiers.end());
}
}
void nejlika::AdditionalEntityData::ApplyToEntity() {
const auto templateDataOpt = NejlikaData::GetEntityTemplate(lot);
if (!templateDataOpt.has_value()) {
return;
}
const auto& templateData = *templateDataOpt.value();
auto* entity = Game::entityManager->GetEntity(id);
if (entity == nullptr) {
return;
}
auto* destroyable = entity->GetComponent<DestroyableComponent>();
if (destroyable == nullptr) {
return;
}
auto* levelProgression = entity->GetComponent<LevelProgressionComponent>();
if (levelProgression != nullptr) {
this->level = levelProgression->GetLevel();
}
else {
this->level = templateData.GetMinLevel();
}
if (!initialized) {
RollStandardModifiers(level);
}
activeModifiers = standardModifiers;
auto* inventoryComponent = entity->GetComponent<InventoryComponent>();
if (inventoryComponent) {
for (const auto& [location, item] : inventoryComponent->GetEquippedItems()) {
const auto itemDataOpt = NejlikaData::GetAdditionalItemData(item.id);
if (!itemDataOpt.has_value()) {
continue;
}
const auto& itemData = *itemDataOpt.value();
const auto& itemModifiers = itemData.GetModifierInstances();
activeModifiers.insert(activeModifiers.end(), itemModifiers.begin(), itemModifiers.end());
}
}
destroyable->SetMaxHealth(static_cast<int32_t>(CalculateModifier(ModifierType::Health, level)));
destroyable->SetMaxArmor(static_cast<int32_t>(CalculateModifier(ModifierType::Armor, level)));
if (!entity->IsPlayer()) {
destroyable->SetMaxImagination(static_cast<int32_t>(CalculateModifier(ModifierType::Imagination, level)));
}
if (initialized) {
return;
}
destroyable->SetHealth(destroyable->GetMaxHealth());
destroyable->SetArmor(destroyable->GetMaxArmor());
if (!entity->IsPlayer()) {
destroyable->SetImagination(destroyable->GetMaxImagination());
}
initialized = true;
}
void nejlika::AdditionalEntityData::CheckForRescale(AdditionalEntityData* other) {
auto* entity = Game::entityManager->GetEntity(id);
if (entity == nullptr) {
return;
}
if (entity->IsPlayer()) {
return;
}
auto* baseCombat = entity->GetComponent<BaseCombatAIComponent>();
if (baseCombat == nullptr) {
return;
}
const auto& threats = baseCombat->GetThreats();
int32_t totalThreats = 0;
int32_t totalLevel = 0;
for (const auto& [threat, _] : threats) {
const auto threatEntityOpt = NejlikaData::GetAdditionalEntityData(threat);
if (!threatEntityOpt.has_value()) {
continue;
}
const auto& threatEntity = *threatEntityOpt.value();
if (other->id == threatEntity.id) {
continue;
}
totalLevel += threatEntity.level;
totalThreats++;
}
if (other != nullptr) {
totalLevel += other->level;
totalThreats++;
auto* team = TeamManager::Instance()->GetTeam(other->id);
if (team != nullptr) {
for (const auto& member : team->members) {
const auto memberEntityOpt = NejlikaData::GetAdditionalEntityData(member);
if (!memberEntityOpt.has_value()) {
continue;
}
const auto& memberEntity = *memberEntityOpt.value();
if (other->id == memberEntity.id) {
continue;
}
totalLevel += memberEntity.level;
totalThreats++;
}
}
}
if (totalThreats == 0) {
return;
}
const auto averageLevel = totalLevel / totalThreats;
// Can't rescale to a lower level
if (averageLevel <= level) {
return;
}
level = averageLevel;
auto* destroyable = entity->GetComponent<DestroyableComponent>();
if (destroyable == nullptr) {
return;
}
float healthPercentage = destroyable->GetMaxHealth() == 0 ? 1 : static_cast<float>(destroyable->GetHealth()) / destroyable->GetMaxHealth();
float armorPercentage = destroyable->GetMaxArmor() == 0 ? 1 : static_cast<float>(destroyable->GetArmor()) / destroyable->GetMaxArmor();
float imaginationPercentage = destroyable->GetMaxImagination() == 0 ? 1 : static_cast<float>(destroyable->GetImagination()) / destroyable->GetMaxImagination();
RollStandardModifiers(level);
ApplyToEntity();
destroyable->SetHealth(static_cast<int32_t>(destroyable->GetMaxHealth() * healthPercentage));
destroyable->SetArmor(static_cast<int32_t>(destroyable->GetMaxArmor() * armorPercentage));
destroyable->SetImagination(static_cast<int32_t>(destroyable->GetMaxImagination() * imaginationPercentage));
LOG("Rescaled entity %i to level %d", entity->GetLOT(), level);
}