// Project Lab - NHTV Igad #include "UnrealProject.h" #include "Modifier.h" #include "NetworkCharacter.h" #include "NativeModifiers.h" ModifierManager::ModifierManager(class ANetworkCharacter* character) : m_character(character) { check(m_character); } ModifierManager::~ModifierManager() { ClearModifiers(false); } void ModifierManager::ClearModifiers(bool shouldCallEnd) { for (AModifier* modifier : m_modifiers) { if (shouldCallEnd) modifier->m_started = false; modifier->Destroy(); } m_modifiers.SetNum(0); RecalculateCharacter(); } void ModifierManager::Tick(float DeltaSeconds) { for (int32 i = 0; i < m_modifiers.Num();) { AModifier* modifier = m_modifiers[i]; if (modifier != nullptr && IsValid(modifier)) { if (modifier->ShouldBeRemoved() || modifier->m_forceDestroy) { modifier->Destroy(); m_modifiers.RemoveAt(i); RecalculateCharacter(); continue; } else { modifier->Tick(DeltaSeconds); } } else { m_modifiers.RemoveAt(i); if (modifier != 0) { GERROR("Modifier \"" + modifier->GetName() + "\" was destroyed, but not in the usual way"); } RecalculateCharacter(); continue; } i++; } } AModifier* ModifierManager::AddModifier(TSubclassOf buffClass, float duration) { if (!buffClass) { FWERROR(L"Invalid modifier class"); return nullptr; } UWorld* world = m_character->GetWorld(); check(world); AModifier* modifier = world->SpawnActor(buffClass); modifier->m_Init(m_character, duration); return AddModifier(modifier); } AModifier* ModifierManager::AddModifier(AModifier* modifierInstance) { if (!modifierInstance) { FWERROR(L"Invalid buff added"); return nullptr; } m_modifiers.Add(modifierInstance); m_modifiers.Sort([](const AModifier& i, const AModifier& j)->bool { return i > j; }); modifierInstance->m_Start(); RecalculateCharacter(); return modifierInstance; } TArray ModifierManager::GetModifiersOfClass(TSubclassOf modifierClass) { TArray ret; for (AModifier* mod : m_modifiers) { if (mod->IsA(modifierClass)) { ret.Add(mod); } } return ret; } void ModifierManager::BroadcastManaDrainFailed() { for(AModifier* modifier : m_modifiers) { if (modifier != nullptr && IsValid(modifier)) { modifier->onManaDrainFailed.Broadcast(); } } } //reset all modifiable stats so that modifiers can recalculate them void ModifierManager::m_Reset() { m_character->ResetModifiers(); } void ModifierManager::RecalculateCharacter() { m_Reset(); for (AModifier* it : m_modifiers) { it->ModifyCharacter(); } m_character->CheckStatsOverflow(); } AModifier::AModifier() { PrimaryActorTick.bCanEverTick = true; bReplicates = true; lifeTime = 0; ticksPerSecond = 0; m_dontDestroy = false; RootComponent = CreateDefaultSubobject("Root"); } bool AModifier::operator < (const AModifier& other) const { return priority < other.priority; } bool AModifier::operator > (const AModifier& other) const { return priority > other.priority; } void AModifier::EndPlay(const EEndPlayReason::Type EndPlayReason) { if (m_started) ModifierEnd(); } void AModifier::ModifyCharacter() { } bool AModifier::ShouldBeRemoved() const { return (lifeTime <= 0.0f) && !m_dontDestroy; } void AModifier::ForceDestroy() { m_forceDestroy = true; } void AModifier::ModifierTick() { } void AModifier::ModifierStart() { } void AModifier::ModifierEnd() { } bool AModifier::OnDamage(int32& damage, ANetworkCharacter* damageDealer) { return true; } bool AModifier::OnDealDamage(int32& damage, ANetworkCharacter* damageTaker) { return true; } void AModifier::OnStandardAttack(ANetworkCharacter* character) { return; } void AModifier::AfterDamage(int32) { return; } float AModifier::m_MultiplyEffect(float value, EffectType effectType, bool positive) { switch (effectType) { case EffectType::additive: { if (!positive) return value * target->m_negativeEffectMultiplier; else return value * target->m_positiveEffectMultiplier; } case EffectType::multiplicative: { if (!positive) return value * (1.0f/target->m_negativeEffectMultiplier); else return value * target->m_positiveEffectMultiplier; } default: return 0.0f; } } //character modification functions void AModifier::m_AddMaxMana(int32 maxMana) { if (IsValid(target)) { maxMana = m_MultiplyEffect(maxMana, additive, maxMana>0); target->m_maxMana += maxMana; target->AddMana(maxMana); } } void AModifier::m_MultiplyManaRegenMultiplier(float manaRegenMultiplier) { if (IsValid(target)) { manaRegenMultiplier = m_MultiplyEffect(manaRegenMultiplier, multiplicative, manaRegenMultiplier>1); target->m_manaRegenMultiplier *= manaRegenMultiplier; } } void AModifier::m_MultiplyManaUsageMultiplier(float manaUsageMultiplier) { if (IsValid(target)) { manaUsageMultiplier = m_MultiplyEffect(manaUsageMultiplier, multiplicative, manaUsageMultiplier>1); target->m_manaUsageMultiplier *= manaUsageMultiplier; } } void AModifier::m_AddBlockedMana(int32 blockedMana) { if (IsValid(target)) { if (target->m_blockedMana + blockedMana > target->m_maxMana) { ModifierManager* mod = character->GetModifierManager(); if (mod != nullptr) mod->BroadcastManaDrainFailed(); } else target->m_blockedMana += blockedMana; } } void AModifier::m_AddDamageMultiplier(float multiplier) { if (IsValid(target)) { multiplier = m_MultiplyEffect(multiplier, additive, multiplier > 0); target->m_damageMultiplier += multiplier; } } void AModifier::m_AddMaxHealth(int32 health) { if (IsValid(target)) { health = m_MultiplyEffect(health, additive, health >0); target->m_maxHealth += health; } } void AModifier::m_AddMovementSpeedMultiplier(float multiplier) { if (IsValid(target)) { multiplier = m_MultiplyEffect(multiplier, multiplicative, multiplier > 0); target->GetCharacterMovement()->MaxWalkSpeed *= multiplier; } } void AModifier::m_AddIgnoreArmor(float normalizedPercentage) { if (IsValid(target)) { normalizedPercentage = m_MultiplyEffect(normalizedPercentage, additive, normalizedPercentage > 0); target->m_ignoreArmor += normalizedPercentage; } } void AModifier::m_AddArmor(float armor) { if (IsValid(target)) { armor = m_MultiplyEffect(armor, additive, armor > 0); target->m_armor += armor; } } void AModifier::m_MultiplyArmor(float armor) { if (IsValid(target)) { armor = m_MultiplyEffect(armor, multiplicative, armor > 1); target->m_armor *= armor; } } void AModifier::m_MultiplyAttackDamage(float damageMultiplier) { if (IsValid(target)) { damageMultiplier = m_MultiplyEffect(damageMultiplier, multiplicative, damageMultiplier>1); FPRINT(damageMultiplier); target->m_attackDamageMultiplier *= damageMultiplier; } } void AModifier::m_AddMagicDamage(float damageMultiplier) { if (IsValid(target)) { damageMultiplier = m_MultiplyEffect(damageMultiplier, multiplicative, damageMultiplier > 1); target->m_magicDamageMultiplier *= damageMultiplier; } } void AModifier::m_AddAttackSpeed(float amount) { if (IsValid(target)) { amount = m_MultiplyEffect(amount, additive, amount> 0); target->m_attackSpeed += amount; } } void AModifier::m_AddAttackSpeedMultiplier(float multiplier) { if (IsValid(target)) { multiplier = m_MultiplyEffect(multiplier, multiplicative, multiplier > 1); target->m_attackSpeed *= multiplier; } } void AModifier::m_AddCooldownReduction(float amount) { if (IsValid(target)) { amount = m_MultiplyEffect(amount, additive, amount > 0); target->m_cooldownReduction += amount; } } void AModifier::m_AddCooldownReductionMultiplier(float multiplier) { if (IsValid(target)) { multiplier = m_MultiplyEffect(multiplier, multiplicative, multiplier > 1); target->m_cooldownReduction *= multiplier; } } void AModifier::m_MultiplyDamageTakenMultiplier(float multiplier) { if (IsValid(target)) { multiplier = m_MultiplyEffect(multiplier, multiplicative, multiplier < 0); target->m_damageTakenMultiplier *= multiplier; } } void AModifier::m_MultiplyPositiveEffectMultiplier(float multiplier) { if (IsValid(target)) target->m_positiveEffectMultiplier *= multiplier; } void AModifier::m_MultiplyNegativeEffectMultiplier(float multiplier) { if (IsValid(target)) target->m_negativeEffectMultiplier *= multiplier; } void AModifier::m_addChannelMovementSpeedMultiplier(float channelMovementSpeedMultiplier) { if (IsValid(target)) { channelMovementSpeedMultiplier = m_MultiplyEffect(channelMovementSpeedMultiplier, additive, channelMovementSpeedMultiplier > 0); target->m_castingMovementspeedMultiplier += channelMovementSpeedMultiplier; } } void AModifier::m_MultiplyMagicDamageMultiplier(float multiplier) { if (IsValid(target)) { multiplier = m_MultiplyEffect(multiplier, additive, multiplier> 0); target->m_magicDamageMultiplier *= multiplier; } } void AModifier::m_AddStunnedState() { if (IsValid(target)) if (target->canBeStunned) { target->m_stunned = true; target->m_InteruptSpellcasting(); } } void AModifier::m_AddSilencedState() { if (IsValid(target)) if (target->canBeSilenced) { target->m_silenceCount++; target->m_silenced = true; target->m_InteruptSpellcasting(); } } void AModifier::m_RemoveSilencedState() { if (IsValid(target)) { target->m_silenceCount--; if (target->m_silenceCount == 0) { target->m_silenced = false; } else if (target->m_silenceCount < 0) { FERROR("m_silenceCount <0 in removeSilenceState"); } } } void AModifier::m_Start() { if (!m_started) { if (IsValid(target)) { if (ticksPerSecond <= 0) m_tickRate = 0.0f; else m_tickRate = 1.0f / ticksPerSecond; m_started = true; if (lifeTime == 0) { m_dontDestroy = true; } ModifierStart(); } else { GWERROR(L"Can't start modifier " + GetName() + " target not set"); } } else { GWERROR(L"modifier started twice"); } } void AModifier::Tick(float DeltaTime) { Super::Tick(DeltaTime); if (!IsValid(target) || !m_started) return; if (ShouldBeRemoved() || m_forceDestroy) return; if (m_tickRate > 0.0f) { m_tickTimer += DeltaTime; while (m_tickTimer >= m_tickRate) { ModifierTick(); m_tickTimer -= m_tickRate; } } lifeTime -= DeltaTime; } void AModifier::m_Init(class ANetworkCharacter* a_character, float duration) { target = a_character; lifeTime = duration; }