478 lines
11 KiB
C++
478 lines
11 KiB
C++
// 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<class AModifier> buffClass, float duration)
|
|
{
|
|
if (!buffClass)
|
|
{
|
|
FWERROR(L"Invalid modifier class");
|
|
return nullptr;
|
|
}
|
|
UWorld* world = m_character->GetWorld();
|
|
check(world);
|
|
AModifier* modifier = world->SpawnActor<AModifier>(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<AModifier*> ModifierManager::GetModifiersOfClass(TSubclassOf<class AModifier> modifierClass)
|
|
{
|
|
TArray<AModifier*> 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<USceneComponent>("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;
|
|
}
|