475 lines
14 KiB
C++
475 lines
14 KiB
C++
// Project Lab - NHTV Igad
|
|
#include "UnrealProject.h"
|
|
#include "NetworkPlayer.h"
|
|
#include "DefaultPlayerController.h"
|
|
#include "ItemBase.h"
|
|
#include "BaseSkillObject.h"
|
|
#include "AbilityInfo.h"
|
|
#include "NetworkSwitch.h"
|
|
#include "DefaultGameMode.h"
|
|
#include "DefaultPlayerState.h"
|
|
#include "IngameSkillTree.h"
|
|
|
|
#include "Modifier.h"
|
|
#include "SpawnerBase.h"
|
|
#include "TeamData.h"
|
|
|
|
static UTeamData* teamData;
|
|
|
|
ANetworkPlayer::ANetworkPlayer()
|
|
{
|
|
bReplicates = true;
|
|
|
|
m_SetupCamera();
|
|
|
|
m_initialised = false;
|
|
m_keyState = 0;
|
|
|
|
healthCurve = ConstructorHelpers::FObjectFinder<UCurveFloat>(TEXT("/Game/ScalingCurves/CF_PlayerHealth")).Object;
|
|
healthRegenCurve = ConstructorHelpers::FObjectFinder<UCurveFloat>(TEXT("/Game/ScalingCurves/CF_PlayerHealthRegen")).Object;
|
|
manaCurve = ConstructorHelpers::FObjectFinder<UCurveFloat>(TEXT("/Game/ScalingCurves/CF_PlayerMana")).Object;
|
|
manaRegenCurve = ConstructorHelpers::FObjectFinder<UCurveFloat>(TEXT("/Game/ScalingCurves/CF_PlayerManaRegen")).Object;
|
|
armorCurve = ConstructorHelpers::FObjectFinder<UCurveFloat>(TEXT("/Game/ScalingCurves/CF_PlayerArmor")).Object;
|
|
attackSpeedCurve = ConstructorHelpers::FObjectFinder<UCurveFloat>(TEXT("/Game/ScalingCurves/CF_PlayerAttackSpeed")).Object;
|
|
|
|
creatureDamageTakenCurve = ConstructorHelpers::FObjectFinder<UCurveFloat>(TEXT("/Game/ScalingCurves/CF_CreatureDamageTaken")).Object;
|
|
creatureDamageDealtCurve = ConstructorHelpers::FObjectFinder<UCurveFloat>(TEXT("/Game/ScalingCurves/CF_CreatureDamageDealt")).Object;
|
|
teamData = ConstructorHelpers::FObjectFinder<UTeamData>(TEXT("/Game/Assets/TeamData")).Object;
|
|
|
|
playerCircle = CreateDefaultSubobject<UStaticMeshComponent>("Player Circle");
|
|
playerCircle->AttachTo(RootComponent);
|
|
|
|
visionRadius = 2500;
|
|
}
|
|
void ANetworkPlayer::BeginPlay()
|
|
{
|
|
m_levelStatsInitialized = false;
|
|
Super::BeginPlay();
|
|
m_initialised = false;
|
|
}
|
|
void ANetworkPlayer::EndPlay(const EEndPlayReason::Type EndPlayReason)
|
|
{
|
|
Super::EndPlay(EndPlayReason);
|
|
|
|
if (Role != ROLE_Authority)
|
|
return;
|
|
|
|
// Return the keys
|
|
ANetworkPlayer* const killedBy = Cast<ANetworkPlayer>(GetLastPlayerDamage());
|
|
if (m_health <= 0 && IsValid(killedBy) && killedBy->GetTeam() != GetTeam())
|
|
{
|
|
FText Source = FText::FromString(killedBy->playerName);
|
|
FText Target = FText::FromString(playerName);
|
|
onPlayerKilled.Broadcast(Source, Target, killedBy->GetTeam(), GetTeam());
|
|
// Assign keys to the player that killed us
|
|
for (auto iter = m_keyOrigins.CreateIterator(); iter; ++iter)
|
|
{
|
|
ASpawnerBase** find = killedBy->m_keyOrigins.Find(iter->Key);
|
|
if (!find)
|
|
killedBy->m_keyOrigins.Add(iter->Key, iter->Value);
|
|
else
|
|
iter->Value->hasDroppedKey = false;
|
|
}
|
|
killedBy->m_keyState |= m_keyState;
|
|
}
|
|
else
|
|
{
|
|
// Return the keys we have
|
|
for (auto iter = m_keyOrigins.CreateIterator(); iter; ++iter)
|
|
{
|
|
if (IsValid(iter->Value))
|
|
iter->Value->hasDroppedKey = false;
|
|
}
|
|
}
|
|
m_keyState = 0;
|
|
m_keyOrigins.Reset();
|
|
|
|
// Clean up screen effects and delegates.
|
|
m_filterTimelines.Empty();
|
|
for (int32 i = 0; i < m_delegates.Num(); i++)
|
|
{
|
|
GetWorldTimerManager().ClearTimer(m_delegates[i]);
|
|
}
|
|
m_delegates.Empty();
|
|
}
|
|
void ANetworkPlayer::Tick(float DeltaSeconds)
|
|
{
|
|
Super::Tick(DeltaSeconds);
|
|
|
|
for (auto& timeline : m_filterTimelines)
|
|
{
|
|
timeline.Value.TickTimeline(DeltaSeconds);
|
|
}
|
|
|
|
// Server only
|
|
if(Role == ROLE_Authority)
|
|
{
|
|
ADefaultPlayerState* state = Cast<ADefaultPlayerState>(PlayerState);
|
|
if(!m_levelStatsInitialized)
|
|
{
|
|
m_modifierManager->RecalculateCharacter();
|
|
CheckStatsOverflow();
|
|
if(!m_initialised)
|
|
{
|
|
m_health = m_maxHealth;
|
|
m_mana = m_maxMana;
|
|
m_initialised = true;
|
|
}
|
|
m_levelStatsInitialized = true;
|
|
}
|
|
}
|
|
|
|
}
|
|
|
|
void ANetworkPlayer::PossessedBy(AController* NewController)
|
|
{
|
|
Super::PossessedBy(NewController);
|
|
OnRep_PlayerState();
|
|
}
|
|
void ANetworkPlayer::OnRep_PlayerState()
|
|
{
|
|
Super::OnRep_PlayerState();
|
|
|
|
// Initialize player circle
|
|
UMaterialInstanceDynamic* mi = playerCircle->CreateDynamicMaterialInstance(0);
|
|
FLinearColor teamColor = teamData->GetTeamColor(Cast<ADefaultPlayerState>(PlayerState)->GetTeam());
|
|
mi->SetVectorParameterValue("Color", teamColor);
|
|
}
|
|
|
|
void ANetworkPlayer::ResetModifiers()
|
|
{
|
|
// Learn abilities
|
|
m_SetLevelStats();
|
|
Super::ResetModifiers();
|
|
}
|
|
|
|
void ANetworkPlayer::OnLevelUp_Server(int32 newLevel)
|
|
{
|
|
check(Role == ROLE_Authority);
|
|
|
|
// Learn abilities
|
|
//m_SetLevelStats();
|
|
|
|
// Recalculate modifiers for new level
|
|
if(m_modifierManager)
|
|
m_modifierManager->RecalculateCharacter();
|
|
CheckStatsOverflow();
|
|
|
|
// Call blueprint level up event (server-side)
|
|
OnLevelUp(newLevel);
|
|
}
|
|
|
|
class ANetworkPlayer* ANetworkPlayer::GetTeamMate() const
|
|
{
|
|
ADefaultPlayerState* state = Cast<ADefaultPlayerState>(PlayerState);
|
|
if (state && state->teamMate)
|
|
return state->teamMate->character;
|
|
return nullptr;
|
|
}
|
|
|
|
void ANetworkPlayer::m_SetLevelStats()
|
|
{
|
|
Super::m_SetLevelStats();
|
|
// Update stats
|
|
ADefaultPlayerState* state = Cast<ADefaultPlayerState>(PlayerState);
|
|
|
|
float level = 1;
|
|
if(IsValid(state))
|
|
level = (float)state->GetLevel();
|
|
|
|
float newHealth = SampleHealthCurve(level);
|
|
float newMana = SampleManaCurve(level);
|
|
float newArmor = SampleArmorCurve(level);
|
|
float newAttackSpeed = SampleAttackSpeedCurve(level);
|
|
|
|
//GWPRINT(L"Setting level bases stats for level " + level + L"(" + levelScale + L")");
|
|
//GWPRINT(L"Health = " + m_maxHealth + L"->" + newHealth);
|
|
//GWPRINT(L"Mana = " + m_maxMana + L"->" + newMana);
|
|
//GWPRINT(L"Armor = " + m_armor + L"->" + newArmor);
|
|
//GWPRINT(L"Attack Speed = " + m_attackSpeed + L"->" + newAs);
|
|
|
|
if(!m_levelStatsInitialized)
|
|
{
|
|
m_health = newHealth;
|
|
m_mana = newMana;
|
|
}
|
|
|
|
m_maxHealth = newHealth;
|
|
m_maxMana = newMana;
|
|
baseArmor = newArmor;
|
|
m_attackSpeed = newAttackSpeed;
|
|
|
|
if(Role == ROLE_Authority)
|
|
{
|
|
// Send level-up/learn skill command
|
|
ADefaultPlayerController* controller = Cast<ADefaultPlayerController>(GetController());
|
|
if(controller)
|
|
{
|
|
controller->LearnSkillsForLevel();
|
|
}
|
|
}
|
|
}
|
|
|
|
void ANetworkPlayer::OnLevelUp_Implementation(int32 newLevel)
|
|
{
|
|
m_levelStatsInitialized = false;
|
|
}
|
|
|
|
bool ANetworkPlayer::ToggleSwitch_Validate(ANetworkSwitch* networkSwitch)
|
|
{
|
|
return networkSwitch && FVector::Dist(GetActorLocation(), networkSwitch->GetActorLocation()) < 600;
|
|
}
|
|
void ANetworkPlayer::ToggleSwitch_Implementation(ANetworkSwitch* networkSwitch)
|
|
{
|
|
if(FVector::Dist(GetActorLocation(), networkSwitch->GetActorLocation()) < 300)
|
|
networkSwitch->Toggle();
|
|
}
|
|
|
|
float ANetworkPlayer::SampleHealthCurve(float in)
|
|
{
|
|
if(GetCharacterClassProperties().healthCurve)
|
|
{
|
|
return GetCharacterClassProperties().healthCurve->GetFloatValue(in);
|
|
}
|
|
return healthCurve->GetFloatValue(in);
|
|
}
|
|
float ANetworkPlayer::SampleManaCurve(float in)
|
|
{
|
|
if(GetCharacterClassProperties().manaCurve)
|
|
{
|
|
return GetCharacterClassProperties().manaCurve->GetFloatValue(in);
|
|
}
|
|
return manaCurve->GetFloatValue(in);
|
|
}
|
|
float ANetworkPlayer::SampleArmorCurve(float in)
|
|
{
|
|
if(GetCharacterClassProperties().armorCurve)
|
|
{
|
|
return GetCharacterClassProperties().armorCurve->GetFloatValue(in);
|
|
}
|
|
return armorCurve->GetFloatValue(in);
|
|
}
|
|
float ANetworkPlayer::SampleAttackSpeedCurve(float in)
|
|
{
|
|
if(GetCharacterClassProperties().attackSpeedCurve)
|
|
{
|
|
return GetCharacterClassProperties().attackSpeedCurve->GetFloatValue(in);
|
|
}
|
|
return attackSpeedCurve->GetFloatValue(in);
|
|
}
|
|
|
|
bool ANetworkPlayer::HasKey(PlayerKeyType keyType) const
|
|
{
|
|
if (keyType == PlayerKeyType::None)
|
|
return m_keyState == 0;
|
|
return ((1 << (int32)keyType) & m_keyState) != 0;
|
|
}
|
|
bool ANetworkPlayer::HasAnyKey() const
|
|
{
|
|
return m_keyState != 0;
|
|
}
|
|
bool ANetworkPlayer::AssignKey(PlayerKeyType keyType, ASpawnerBase* origin)
|
|
{
|
|
check(Role == ROLE_Authority);
|
|
|
|
if (HasKey(keyType))
|
|
return false;
|
|
|
|
origin->hasDroppedKey = true;
|
|
|
|
// Store the origin for later
|
|
m_keyOrigins.Add((uint8)keyType, origin);
|
|
m_keyState += (1 << (int32)keyType);
|
|
return true;
|
|
}
|
|
bool ANetworkPlayer::ClearKey(PlayerKeyType keyType)
|
|
{
|
|
check(Role == ROLE_Authority);
|
|
|
|
if (!HasKey(keyType))
|
|
return false;
|
|
|
|
// Allow the boss to drop keys again
|
|
ASpawnerBase** find = m_keyOrigins.Find((uint8)keyType);
|
|
if (find)
|
|
{
|
|
if(IsValid(*find)) (*find)->hasDroppedKey = false;
|
|
m_keyOrigins.Remove((uint8)keyType);
|
|
}
|
|
m_keyState -= (1 << (int32)keyType);
|
|
return true;
|
|
}
|
|
|
|
void ANetworkPlayer::GetLifetimeReplicatedProps(TArray<FLifetimeProperty>& OutLifetimeProps) const
|
|
{
|
|
Super::GetLifetimeReplicatedProps(OutLifetimeProps);
|
|
|
|
DOREPLIFETIME(ANetworkPlayer, m_keyState);
|
|
}
|
|
|
|
FCharacterClassProperty ANetworkPlayer::GetCharacterClassProperties() const
|
|
{
|
|
static FCharacterClassProperty dummy;
|
|
if(!Controller)
|
|
return dummy;
|
|
return Cast<ADefaultPlayerController>(Controller)->GetCharacterClassProperties();
|
|
}
|
|
|
|
//Screen Filters
|
|
void ANetworkPlayer::SetFilterDuration(float duration, UPostProcessComponent* filter)
|
|
{
|
|
FTimerHandle uniqueHandle;
|
|
FTimerDelegate stopFilterDelegate = FTimerDelegate::CreateUObject(this, &ANetworkPlayer::StopFilter, filter, uniqueHandle);
|
|
GetWorldTimerManager().SetTimer(uniqueHandle, stopFilterDelegate, duration, false);
|
|
m_delegates.Add(uniqueHandle);
|
|
}
|
|
|
|
void ANetworkPlayer::SetTimelineDuration(float duration, UPostProcessComponent* filter)
|
|
{
|
|
FTimerHandle uniqueHandle;
|
|
FTimerDelegate stopFilterDelegate = FTimerDelegate::CreateUObject(this, &ANetworkPlayer::StopTimeline, filter, uniqueHandle);
|
|
GetWorldTimerManager().SetTimer(uniqueHandle, stopFilterDelegate, duration, false);
|
|
m_delegates.Add(uniqueHandle);
|
|
}
|
|
|
|
void ANetworkPlayer::StartFilter(float duration, UPostProcessComponent* filter, UCurveFloat* timelineCurve)
|
|
{
|
|
if ( m_filterTimelines.Contains(filter) ) return;
|
|
if (timelineCurve)
|
|
{
|
|
FTimeline timeLine;
|
|
FOnTimelineFloatStatic timelineDelegate;
|
|
timelineDelegate.BindUObject(this, &ANetworkPlayer::FilterUpdate, filter);
|
|
timeLine.AddInterpFloat(timelineCurve, timelineDelegate);
|
|
timeLine.SetPlayRate(1.0f / duration);
|
|
timeLine.PlayFromStart();
|
|
m_filterTimelines.Add(filter, timeLine);
|
|
SetFilterDuration(duration, filter);
|
|
}
|
|
filter->bEnabled = true;
|
|
}
|
|
|
|
void ANetworkPlayer::StartFilterWithDelegate(float duration, UPostProcessComponent* filter, UCurveFloat* timelineCurve, FPostAction action)
|
|
{
|
|
if ( m_filterTimelines.Contains(filter) ) return;
|
|
if (timelineCurve)
|
|
{
|
|
FTimeline timeLine;
|
|
FOnTimelineFloatStatic timelineDelegate;
|
|
timelineDelegate.BindUObject(this, &ANetworkPlayer::FilterUpdate, filter, action);
|
|
timeLine.AddInterpFloat(timelineCurve, timelineDelegate);
|
|
timeLine.SetPlayRate(1.0f / duration);
|
|
timeLine.PlayFromStart();
|
|
m_filterTimelines.Add(filter, timeLine);
|
|
SetFilterDuration(duration, filter);
|
|
}
|
|
filter->bEnabled = true;
|
|
}
|
|
|
|
void ANetworkPlayer::EnableFilter(float duration, UPostProcessComponent* filter, UCurveFloat* timelineCurve)
|
|
{
|
|
if (m_filterTimelines.Contains(filter)) return;
|
|
if (!timelineCurve)
|
|
{
|
|
YERROR("No timelineCurve given.");
|
|
return;
|
|
}
|
|
|
|
FTimeline timeLine;
|
|
FOnTimelineFloatStatic timelineDelegate;
|
|
timelineDelegate.BindUObject(this, &ANetworkPlayer::FilterUpdate, filter);
|
|
timeLine.AddInterpFloat(timelineCurve, timelineDelegate);
|
|
timeLine.SetPlayRate(1.0f / duration);
|
|
timeLine.PlayFromStart();
|
|
m_filterTimelines.Add(filter, timeLine);
|
|
SetTimelineDuration(duration, filter);
|
|
filter->bEnabled = true;
|
|
}
|
|
|
|
void ANetworkPlayer::DisableFilter(float duration, UPostProcessComponent* filter, UCurveFloat* timeline)
|
|
{
|
|
StartFilter(duration, filter, timeline);
|
|
}
|
|
|
|
void ANetworkPlayer::EnableFilterWithDelegate(float duration, UPostProcessComponent* filter, UCurveFloat* timelineCurve, FPostAction action)
|
|
{
|
|
if (m_filterTimelines.Contains(filter)) return;
|
|
if (!timelineCurve)
|
|
{
|
|
YERROR("No timelineCurve given.");
|
|
return;
|
|
}
|
|
|
|
FTimeline timeLine;
|
|
FOnTimelineFloatStatic timelineDelegate;
|
|
timelineDelegate.BindUObject(this, &ANetworkPlayer::FilterUpdate, filter, action);
|
|
timeLine.AddInterpFloat(timelineCurve, timelineDelegate);
|
|
timeLine.SetPlayRate(1.0f / duration);
|
|
timeLine.PlayFromStart();
|
|
m_filterTimelines.Add(filter, timeLine);
|
|
SetTimelineDuration(duration, filter);
|
|
filter->bEnabled = true;
|
|
}
|
|
|
|
void ANetworkPlayer::DisableFilterWithDelegate(float duration, UPostProcessComponent* filter, UCurveFloat* timeline, FPostAction action)
|
|
{
|
|
StartFilterWithDelegate(duration, filter, timeline, action);
|
|
}
|
|
|
|
void ANetworkPlayer::StopFilter(UPostProcessComponent* filter, FTimerHandle handle)
|
|
{
|
|
filter->bEnabled = false;
|
|
m_delegates.Remove(handle);
|
|
if (!m_filterTimelines.Contains(filter))
|
|
{
|
|
YERROR("Can't find filter in timeline.");
|
|
return;
|
|
}
|
|
m_filterTimelines.Remove(filter);
|
|
}
|
|
|
|
void ANetworkPlayer::StopTimeline( UPostProcessComponent* filter, FTimerHandle handle )
|
|
{
|
|
m_delegates.Remove(handle);
|
|
if (!m_filterTimelines.Contains(filter))
|
|
{
|
|
YERROR("Can't find filter in timeline.");
|
|
return;
|
|
}
|
|
m_filterTimelines.Remove(filter);
|
|
}
|
|
|
|
void ANetworkPlayer::FilterUpdate(float value, UPostProcessComponent* filter)
|
|
{
|
|
filter->BlendWeight = value;
|
|
}
|
|
|
|
void ANetworkPlayer::FilterUpdate(float value, UPostProcessComponent* filter, FPostAction action)
|
|
{
|
|
action.ExecuteIfBound(value, filter);
|
|
}
|
|
|
|
void ANetworkPlayer::AddThreatLevel_Client_Implementation(const float a_NewThreat)
|
|
{
|
|
threats.Add(a_NewThreat);
|
|
|
|
threatLevel = a_NewThreat > threatLevel ? a_NewThreat : threatLevel;
|
|
}
|
|
|
|
void ANetworkPlayer::RemoveThreatLevel_Client_Implementation(const float a_NewThreat)
|
|
{
|
|
threats.RemoveSingle(a_NewThreat);
|
|
|
|
if (a_NewThreat == threatLevel)
|
|
{
|
|
threatLevel = 0;
|
|
for (int32 i = 0; i < threats.Num(); i++)
|
|
{
|
|
if (threats[i] > threatLevel)
|
|
{
|
|
threatLevel = threats[i];
|
|
}
|
|
}
|
|
}
|
|
}
|