349 lines
9.7 KiB
C++
349 lines
9.7 KiB
C++
// Project Lab - NHTV Igad
|
|
|
|
#include "UnrealProject.h"
|
|
#include "NPCBase.h"
|
|
#include "MinionAnimInstance.h"
|
|
#include "SpawnerBase.h"
|
|
#include "modifier.h"
|
|
#include "NetworkPlayer.h"
|
|
#include "TeamData.h"
|
|
#include "BossBase.h"
|
|
#include "DrawDebugHelpers.h"
|
|
#include "AI/Navigation/NavigationPath.h"
|
|
|
|
static UTeamData* teamData;
|
|
|
|
ANPCBase::ANPCBase()
|
|
{
|
|
teamData = ConstructorHelpers::FObjectFinder<UTeamData>(L"/Game/Assets/TeamData").Object;
|
|
|
|
PrimaryActorTick.bCanEverTick = true;
|
|
m_spawn = nullptr;
|
|
m_dead = false;
|
|
baseAttackSpeed = 2.0f;
|
|
baseMaxHealth = 200.0f;
|
|
baseArmor = 30.0f;
|
|
baseMaxMana = 1000.0f;
|
|
baseAttackSpeed = 1.0f;
|
|
maxAvoidanceForce = 200.0f;
|
|
m_reachedStart = false;
|
|
//visionTrigger = CreateDefaultSubobject<UCubeComponent>(TEXT("Vision Trigger"));
|
|
|
|
}
|
|
void ANPCBase::BeginPlay()
|
|
{
|
|
Super::BeginPlay();
|
|
if (Role != ROLE_Authority)
|
|
return;
|
|
|
|
m_health = m_maxHealth = baseMaxHealth;
|
|
m_mana = m_maxMana = baseMaxMana;
|
|
if (m_spawn)
|
|
m_targetPoint = m_spawn->GetActorLocation();
|
|
else m_targetPoint = GetActorLocation();
|
|
collisionRadius = GetCapsuleComponent()->GetScaledCapsuleRadius();
|
|
m_walkSpeed = GetCharacterMovement()->MaxWalkSpeed;
|
|
m_OnRep_Team();
|
|
GetCapsuleComponent()->BodyInstance.bLockXRotation = true;
|
|
GetCapsuleComponent()->BodyInstance.bLockYRotation = true;
|
|
animInstance = Cast<UMinionAnimInstance>(GetMesh()->GetAnimInstance());
|
|
patrolTime = 5.0f;
|
|
}
|
|
void ANPCBase::EndPlay(const EEndPlayReason::Type EndPlayReason)
|
|
{
|
|
Super::EndPlay(EndPlayReason);
|
|
if (Role != ROLE_Authority)
|
|
return;
|
|
if(m_spawn && EndPlayReason == EEndPlayReason::Destroyed)
|
|
m_spawn->OnMobDie(this);
|
|
|
|
}
|
|
void ANPCBase::Tick(float deltaTime)
|
|
{
|
|
Super::Tick(deltaTime);
|
|
if (Role != ROLE_Authority)
|
|
return;
|
|
UNavigationSystem* navSys = UNavigationSystem::GetCurrent(GetWorld());
|
|
if (!navSys || !GetController() || !m_spawn)
|
|
{
|
|
//RERROR("Boss base does not have a navigation system");
|
|
return;
|
|
}
|
|
|
|
if (!IsValid(target))
|
|
target = nullptr;
|
|
|
|
if (m_dead)
|
|
{
|
|
return;
|
|
}
|
|
if (!IsValid(target))
|
|
{
|
|
m_spawn->GetNewTarget(this);
|
|
}
|
|
|
|
if (!IsValid(target) && !m_hasGeneral)
|
|
{
|
|
MoveToPoint(m_targetPoint);
|
|
}
|
|
else
|
|
{
|
|
if (FVector::DistSquared(m_spawn->GetActorLocation(), GetActorLocation()) > m_spawn->deaggroRadius * m_spawn->deaggroRadius)
|
|
{
|
|
// Reset aggro radius
|
|
target = nullptr;
|
|
m_isPulled = false;
|
|
}
|
|
}
|
|
|
|
|
|
|
|
}
|
|
int32 ANPCBase::NativeDealDamage(class ANetworkCharacter* dealer, int32 damage, float armorPercentageIgnore, class UAbilityInfo* ability)
|
|
{
|
|
target = dealer;
|
|
m_OnRep_Team();
|
|
return Super::NativeDealDamage(dealer, damage, armorPercentageIgnore, ability);
|
|
}
|
|
void ANPCBase::ResetModifiers()
|
|
{
|
|
Super::ResetModifiers();
|
|
m_maxHealth = baseMaxHealth;
|
|
m_maxMana = baseMaxMana;
|
|
m_attackSpeed = baseAttackSpeed;
|
|
m_armor = baseArmor;
|
|
}
|
|
|
|
void ANPCBase::OnDeath(UAbilityInfo* ability)
|
|
{
|
|
//should be removed later, this is in here untill the animations are actually in the game
|
|
Super::OnDeath(ability);
|
|
return;
|
|
target = nullptr;
|
|
m_dead = true;
|
|
m_modifierManager->ClearModifiers();
|
|
}
|
|
|
|
void ANPCBase::CalcAvoidance(const FVector& target)
|
|
{
|
|
UNavigationSystem* NavSys = UNavigationSystem::GetCurrent(GetWorld());
|
|
// NavSys->SimpleMoveToLocation(this->Controller, target);
|
|
if (Cast<ABossBase>(this))
|
|
{
|
|
NavSys->SimpleMoveToLocation(this->Controller, target);
|
|
return;
|
|
}
|
|
FVector newLoc = FVector::ZeroVector;
|
|
newLoc = GetClosestPathLocation(target) - GetActorLocation();
|
|
newLoc.Normalize();
|
|
newLoc *= GetCharacterMovement()->GetMaxSpeed();
|
|
newLoc = newLoc + CalcAvoidanceVector(); //+ calc avoicance vector;
|
|
FVector vel = TruncateVector(GetCharacterMovement()->Velocity + newLoc, GetCharacterMovement()->GetMaxSpeed());
|
|
vel += GetActorLocation();
|
|
NavSys->SimpleMoveToLocation(this->Controller, vel);
|
|
SetActorRotation(FVector(FVector(vel.X, vel.Y, GetActorLocation().Z) - GetActorLocation()).Rotation());
|
|
|
|
}
|
|
|
|
void ANPCBase::PatrolBetweenLocations(FVector startlocation, FVector endLocation, float deltaTime)
|
|
{
|
|
FVector actorlocation = GetActorLocation();
|
|
if (m_reachedStart)
|
|
{
|
|
MoveToPoint(endLocation);
|
|
enableVisonCone = false;
|
|
}
|
|
else
|
|
{
|
|
MoveToPoint(startlocation);
|
|
enableVisonCone = false;
|
|
}
|
|
//wait a few second to walk back
|
|
float dist = FVector::DistSquaredXY(startlocation, actorlocation);
|
|
if (dist < 50.0f*50.0f)
|
|
{
|
|
m_patrolTimer -= deltaTime;
|
|
enableVisonCone = true;
|
|
if (m_patrolTimer <= 0)
|
|
{
|
|
m_reachedStart = true;
|
|
m_patrolTimer = patrolTime;
|
|
|
|
}
|
|
|
|
|
|
}
|
|
dist = FVector::DistSquaredXY(endLocation, actorlocation);
|
|
if (dist< 50.0f*50.0f)
|
|
{
|
|
m_patrolTimer -= deltaTime;
|
|
enableVisonCone = true;
|
|
if (m_patrolTimer <= 0)
|
|
{
|
|
m_reachedStart = false;
|
|
m_patrolTimer = patrolTime;
|
|
}
|
|
}
|
|
|
|
}
|
|
void ANPCBase::MoveToPoint(const FVector& target)
|
|
{
|
|
UNavigationSystem* NavSys = UNavigationSystem::GetCurrent(GetWorld());
|
|
GetCharacterMovement()->MaxWalkSpeed = m_walkSpeed;
|
|
float distance = FVector::DistSquared(target, GetActorLocation());
|
|
if (IsPendingKill() || IsPendingKillPending())
|
|
return;
|
|
|
|
if (!IsStunned())
|
|
{
|
|
CalcAvoidance( target);
|
|
SetActorRotation(FVector(FVector(target.X, target.Y, GetActorLocation().Z) - GetActorLocation()).Rotation());
|
|
|
|
}
|
|
else
|
|
{
|
|
|
|
NavSys->SimpleMoveToLocation(this->Controller, GetActorLocation());
|
|
}
|
|
|
|
|
|
|
|
}
|
|
void ANPCBase::MoveToPointSlow(const FVector& target, float dist)
|
|
{
|
|
UNavigationSystem* NavSys = UNavigationSystem::GetCurrent(GetWorld());
|
|
if (IsPendingKill() || IsPendingKillPending())
|
|
return;
|
|
GetCharacterMovement()->MaxWalkSpeed = m_walkSpeed/2;
|
|
if (m_lastLoc != target)
|
|
{
|
|
if (m_lastLoc == FVector::ZeroVector || FVector::DistSquaredXY(m_lastLoc, GetActorLocation()) < dist*dist)
|
|
{
|
|
//RPRINT("test");
|
|
m_lastLoc =UNavigationSystem::ProjectPointToNavigation(GetWorld(), target);
|
|
//m_lastLoc = target;
|
|
}
|
|
}
|
|
MoveToPoint(m_lastLoc);
|
|
|
|
}
|
|
void ANPCBase::OnOverlapBegin(class AActor* OtherActor, class UPrimitiveComponent* OtherComp, int32 OtherBodyIndex, bool bFromSweep, const FHitResult& SweepResult)
|
|
{
|
|
if (!IsValid(OtherActor))
|
|
return;
|
|
if (!OtherActor->IsA(ANPCBase::StaticClass()))
|
|
return;
|
|
collisionList.Add(Cast<ANPCBase>(OtherActor));
|
|
|
|
}
|
|
void ANPCBase::OnOverlapEnd(class AActor* OtherActor, class UPrimitiveComponent* OtherComp, int32 OtherBodyIndex)
|
|
{
|
|
if (!IsValid(OtherActor))
|
|
return;
|
|
if (!OtherActor->IsA(ANPCBase::StaticClass()))
|
|
return;
|
|
collisionList.Remove(Cast<ANPCBase>(OtherActor));
|
|
}
|
|
void ANPCBase::SetSpawn(class ASpawnerBase* spawn)
|
|
{
|
|
check(Role == ROLE_Authority);
|
|
check(!m_spawn);
|
|
m_spawn = spawn;
|
|
}
|
|
void ANPCBase::SetControlPoint(FVector controlPoint)
|
|
{
|
|
check(Role == ROLE_Authority);
|
|
m_targetPoint = controlPoint;
|
|
}
|
|
|
|
void ANPCBase::m_OnRep_Team()
|
|
{
|
|
Super::m_OnRep_Team();
|
|
if(!m_materialInstance)
|
|
{
|
|
m_materialInstance = GetMesh()->CreateDynamicMaterialInstance(0);
|
|
}
|
|
if(m_materialInstance)
|
|
{
|
|
float scalar = (float)m_health / (float)m_maxHealth;
|
|
m_materialInstance->SetVectorParameterValue("TeamColor", teamData->GetTeamColor(GetTeam()));
|
|
m_materialInstance->SetScalarParameterValue("FlameEdgeSoftness", 100.0f - (100.0f*scalar));
|
|
m_materialInstance->SetScalarParameterValue("FlameEffectIntensity", (scalar));
|
|
m_materialInstance->SetScalarParameterValue("RandomOffset", FMath::FRand());
|
|
}
|
|
}
|
|
|
|
void ANPCBase::UnsetSpawn()
|
|
{
|
|
check(Role == ROLE_Authority);
|
|
m_spawn = nullptr;
|
|
}
|
|
void ANPCBase::ChangeNPCAnimation_Implementation(uint8 state )
|
|
{
|
|
if (IsValid(animInstance))
|
|
animInstance->ChangeAnimationStateTo((EMinionAnimState)(state));
|
|
}
|
|
FVector ANPCBase::CalcAvoidanceVector()
|
|
{
|
|
FVector retVector = FVector::ZeroVector;
|
|
float dynLenght = GetCharacterMovement()->Velocity.Size() / GetCharacterMovement()->GetMaxSpeed();
|
|
//change getactorforward by velocity
|
|
FVector vel = GetCharacterMovement()->Velocity;
|
|
vel.Normalize();
|
|
aheadvec1 =GetActorLocation()+ vel*dynLenght;
|
|
aheadvec2 = GetActorLocation() + vel*dynLenght*0.5;
|
|
// DrawDebugSphere(GetWorld(), aheadvec1, 24, 32, FColor(255, 255, 255));
|
|
// DrawDebugSphere(GetWorld(), aheadvec2, 24, 32, FColor(255, 0, 0));
|
|
ANPCBase* obstacle = GetClosestObstacle();
|
|
if (IsValid(obstacle))
|
|
{
|
|
retVector = aheadvec1 - obstacle->GetActorLocation();
|
|
retVector.Z = 0.0f;
|
|
retVector.Normalize();
|
|
retVector *= maxAvoidanceForce;
|
|
|
|
}
|
|
// DrawDebugLine(GetWorld(), GetActorLocation(), GetActorLocation() + retVector, FColor(255, 255, 0));
|
|
return retVector;
|
|
}///
|
|
ANPCBase* ANPCBase::GetClosestObstacle()
|
|
{
|
|
ANPCBase* obstacle = nullptr;
|
|
float distance = 1e34;
|
|
for (int i = 0; i < m_spawn->m_mobs.Num(); i++)
|
|
{
|
|
ANPCBase* current = m_spawn->m_mobs[i];
|
|
if(!IsValid(current)||current==this)
|
|
continue;
|
|
float tempdistance = FVector::DistSquared(current->GetActorLocation(), this->GetActorLocation());
|
|
if (tempdistance < distance)
|
|
{
|
|
distance = tempdistance;
|
|
obstacle = current;
|
|
}
|
|
}
|
|
if (!IsValid(obstacle))
|
|
return nullptr;
|
|
if (FVector::DistSquared(aheadvec1, obstacle->GetActorLocation())>100 * 100 && FVector::DistSquared(aheadvec2, obstacle->GetActorLocation())>100 * 100)
|
|
obstacle = nullptr;
|
|
return obstacle;
|
|
}
|
|
FVector ANPCBase::TruncateVector(FVector inVector, float maxForce)
|
|
{
|
|
if (inVector.Size() > maxForce)
|
|
{
|
|
inVector.Normalize();
|
|
inVector *= maxForce;
|
|
}
|
|
return inVector;
|
|
}
|
|
FVector ANPCBase::GetClosestPathLocation(FVector endlocation)
|
|
{
|
|
FVector returnVec = FVector();
|
|
UNavigationPath *tpath;
|
|
UNavigationSystem* navsys = UNavigationSystem::GetCurrent(GetWorld());
|
|
tpath = navsys->FindPathToLocationSynchronously(GetWorld(), GetActorLocation(), endlocation);
|
|
if (tpath == nullptr||tpath->PathPoints.Num()<=1)
|
|
return endlocation;
|
|
return tpath->PathPoints[tpath->PathPoints.Num()-1];
|
|
} |