haxis/Source/UnrealProject/Creatures/NPCBase.cpp

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];
}