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