haxis/Source/UnrealProject/Spawners/SpawnerBase.cpp

476 lines
11 KiB
C++

// Project Lab - NHTV Igad
#include "UnrealProject.h"
#include "NetworkSwitchComponent.h"
#include "CreatureSpawnComponent.h"
#include "DefaultGameMode.h"
#include "SpawnerBase.h"
#include "NPCBase.h"
#include "NetworkCharacter.h"
#include "NetworkPlayer.h"
#include "EnemyBase.h"
#include "GeneralEnemy.h"
#include "MiniBossCreature.h"
ASpawnerBase::ASpawnerBase()
{
PrimaryActorTick.bCanEverTick = true;
USceneComponent* spawnerRoot = CreateDefaultSubobject<USceneComponent>("Spawner Root");
RootComponent = spawnerRoot;
displayMesh = CreateDefaultSubobject<UStaticMeshComponent>(TEXT("Mesh"));
displayMesh->bHiddenInGame = true;
displayMesh->bGenerateOverlapEvents = false;
displayMesh->SetCollisionEnabled(ECollisionEnabled::NoCollision);
displayMesh->AttachTo(RootComponent);
// Kill particle effect
killParticle = CreateDefaultSubobject<UParticleSystemComponent>(TEXT("Kill Particle1"));
killParticle->AttachTo(RootComponent);
displayArrow = CreateDefaultSubobject<UArrowComponent>(TEXT("Arrow"));
displayArrow->AttachTo(displayMesh);
displayDoors = CreateDefaultSubobject<UNetworkSwitchComponent>(TEXT("Visualizer"));
displayDoors->AttachTo(displayMesh);
visualizerComponent = CreateDefaultSubobject<UCreatureSpawnComponent>(TEXT("Control points visualizer"));
visualizerComponent->AttachTo(displayMesh);
team = NPCTeam::Team1;
aggroRadius = 800;
deaggroRadius = aggroRadius;
m_mobCount = 0;
formationScale = 1.0f;
formationDistance = 100;
formationRadius = 100;
resetTimer = 30;
dropKeyFragmentIndex = 0;
hasDroppedKey = false;
bReplicates = true;
bAlwaysRelevant = true;
}
void ASpawnerBase::BeginPlay()
{
Super::BeginPlay();
TArray<AActor*> test;
GetAttachedActors(test);
for (int i = 0; i < test.Num(); i++)
{
if (Cast<ANPCBase>(test[i]))
{
spawns.Add(test[i]->GetClass());
formationPoints.Add(FVector2D(test[i]->GetActorLocation() - GetActorLocation()));
formationRotation.Add(test[i]->GetActorRotation().Yaw);
//RPRINT("mobs" + spawns[i]->GetName());
if (Role == ROLE_Authority)
{
Cast<ANPCBase>(test[i])->UnsetSpawn();
}
(test[i])->Destroy();
(test[i]) = nullptr;
}
else
{
SpawnLocation.Add(test[i]);
}
}
for (int32 i = 0; i < spawns.Num(); i++)
m_mobs.Add(nullptr);
m_threatMap.SetNum(m_mobs.Num());
for (int i = 0; i < spawns.Num(); i++)
{
if (i != 0 && spawns[i]->IsChildOf(AGeneralEnemy::StaticClass()))
{
Swap(spawns[i], spawns[0]);
Swap(formationPoints[i], formationPoints[0]);
Swap(formationRotation[i], formationRotation[0]);
}
}
TArray<FHitResult> hitResult;
GetWorld()->LineTraceMultiByChannel(hitResult, GetActorLocation(), GetActorLocation() + GetActorUpVector()*-10000.0f, ECollisionChannel::ECC_WorldStatic, FCollisionQueryParams(FName(L"spawnerPlacementTrace"),true));
bool hit = false;
float distance = BIG_NUMBER;
FVector impactLocation;
for (int i = 0; i < hitResult.Num(); i++)
{
if (FVector::Dist(FVector(hitResult[i].ImpactPoint), GetActorLocation()) < distance&&FVector(hitResult[i].ImpactPoint)!= GetActorLocation())
{
distance = FVector::Dist(FVector(hitResult[i].ImpactPoint), GetActorLocation());
impactLocation = FVector(hitResult[i].ImpactPoint);
hit = true;
}
}
// Correct spawner position when it can move to the ground below it
if (hit)
{
this->SetActorLocation(FVector(GetActorLocation().X, GetActorLocation().Y, impactLocation.Z));
}
for (int i = 0; i < formationPoints.Num(); i++)
{
formationPoints[i] = FVector2D(GetActorRotation().RotateVector(FVector(formationPoints[i].X, formationPoints[i].Y, 0)));
formationPoints[i] *= formationScale;
}
if (spawns.Num() > formationPoints.Num())
{
int difference = spawns.Num() - formationPoints.Num();
for (int i = 0; i < difference; i++)
{
formationPoints.Add(FVector2D());
}
}
if (spawns.Num() > formationRotation.Num())
{
int difference = spawns.Num() - formationRotation.Num();
for (int i = 0; i < difference; i++)
{
formationRotation.Add(0);
}
}
}
void ASpawnerBase::EndPlay(const EEndPlayReason::Type EndPlayReason)
{
Super::EndPlay(EndPlayReason);
if (Role != ROLE_Authority)
return;
for (int32 i = 0; i < m_mobs.Num(); i++)
{
if (m_mobs[i])
{
m_mobs[i]->UnsetSpawn();
m_mobs[i] = nullptr;
}
}
formationEnemies.Empty();
m_nearbyPlayers.clear();
}
void ASpawnerBase::Tick(float DeltaTime)
{
Super::Tick(DeltaTime);
// if (formationEnemies.Num() > spawns.Num())
// formationEnemies.Empty();
bool reset = false;
m_resetTimer -= DeltaTime;
if (m_resetTimer <= 0)
{
reset = true;
}
for (int i = 0; i < m_mobs.Num(); i++)
{
if (m_mobs[i] == nullptr)
continue;
if (reset)
{
ForceResetTarget(m_mobs[i]);
}
if (m_mobs[i]->IsA(AEnemyBase::StaticClass()))
{
Cast<AEnemyBase>(m_mobs[i])->hasGeneral = hasGeneral;
}
if (m_mobs[i]->IsA(AGeneralEnemy::StaticClass()))
{
AGeneralEnemy* asGeneral = Cast<AGeneralEnemy>(m_mobs[i]);
if (formationEnemies != asGeneral->formationEnemies)
{
asGeneral->formationEnemies = formationEnemies;
}
}
}
}
int32 ASpawnerBase::OnMobDie(class ANPCBase* mob)
{
check(m_mobCount > 0);
check(Role == ROLE_Authority);
int32 idx = -1;
for (int32 i = 0; i < spawns.Num(); i++)
{
if (m_mobs[i] == mob)
{
if (m_mobs[i]->IsA(AGeneralEnemy::StaticClass()))
{
formationEnemies.Empty();
}
if (m_mobs[i]->IsA(AEnemyBase::StaticClass()))
{
AEnemyBase* asEnemyBase = Cast<AEnemyBase>(m_mobs[i]);
if (asEnemyBase->hasGeneral)
formationEnemies.Remove(asEnemyBase);
}
m_mobCount--;
if(m_mobCount == 0)
{
OnCampCleared();
}
m_mobs[i] = nullptr;
idx = i;
m_threatMap[i].clear();
break;
}
}
check(idx >= 0);
return idx;
}
void ASpawnerBase::OnCampCleared_Implementation()
{
if(killParticle->Template)
{
killParticle->ResetParticles();
killParticle->ActivateSystem();
}
m_OnCampCleared();
}
int32 ASpawnerBase::GetAliveMobCount()
{
return m_mobCount;
}
FVector ASpawnerBase::SpawnResetPosition()
{
return GetActorLocation();
}
void ASpawnerBase::m_OnPlayerEnterOverlap(class ANetworkCharacter& player)
{
auto find = m_nearbyPlayers.find(&player);
if (find != m_nearbyPlayers.end())
return;
ANetworkPlayer* playerCast = Cast<ANetworkPlayer>(&player);
if (IsValid(playerCast))
{
playerCast->AddThreatLevel_Client(threatLevel);
}
// Player was succesfully added
m_nearbyPlayers.emplace(&player);
//adding the threat to all mobs
/* for (int32 i = 0; i < m_mobs.Num(); i++)
{
if (m_mobs[i] == nullptr)
continue;
// auto ret = m_threatMap[i].emplace(&player, 0);
if (!ret.second)
{
FPRINT("failed to add player to creaturemap ");
}
}*/
}
void ASpawnerBase::m_OnPlayerExitOverlap(class ANetworkCharacter& player)
{
auto find = m_nearbyPlayers.find(&player);
if (find == m_nearbyPlayers.end())
{
FERROR("leaving player was not found in the nearbyPlayers");
return;
}
ANetworkPlayer* playerCast = Cast<ANetworkPlayer>(&player);
if (IsValid(playerCast))
{
playerCast->RemoveThreatLevel_Client(threatLevel);
}
// Player was succesfully removed
m_nearbyPlayers.erase(find);
}
class ANetworkPlayer* ASpawnerBase::m_GetBiggestThreat(int32 index)
{
ANetworkPlayer* ret = nullptr;
int32 biggestThreat = -1;
for (auto cpair : m_threatMap[index])
{
if (cpair.second > biggestThreat)
{
biggestThreat = cpair.second;
ret = cpair.first;
}
}
return ret;
}
void ASpawnerBase::m_OnMobSpawn(int32 index)
{
ANPCBase* creature = m_mobs[index];
if (creature == nullptr)
{
FWARNING("creature is null at spawn");
return;
}
if (creature->IsA(AGeneralEnemy::StaticClass()))
{
AGeneralEnemy* asGeneral = Cast<AGeneralEnemy>(creature);
asGeneral->formationPoints = formationPoints;
}
else
{
if (creature->IsA(AEnemyBase::StaticClass()))
formationEnemies.Add(Cast<AEnemyBase>(creature));
}
m_threatMap[index].clear();
/* for (ANetworkPlayer* player : m_nearbyPlayers)
{
auto ret = m_threatMap[index].insert(std::pair<ANetworkPlayer*, int32>(player, 0));
if (!ret.second)
{
FPRINT("adding failed");
}
}*/
if (creature->IsA(AMiniBossCreature::StaticClass()) && dropKeyFragmentIndex >= keyFragmentMin && dropKeyFragmentIndex <= keyFragmentMax)
Cast<AMiniBossCreature>(creature)->keyDropped = PlayerKeyType(dropKeyFragmentIndex);
}
class ANetworkPlayer* ASpawnerBase::GetClosestPlayer()
{
if (m_nearbyPlayers.empty())
return nullptr;
UWorld* const world = GetWorld();
if (!world)
return nullptr;
ADefaultGameMode* const mode = Cast<ADefaultGameMode>(world->GetAuthGameMode());
if (!mode)
return nullptr;
// Find the closest player (in aggroRadius)
ANetworkPlayer* closest = nullptr;
float closestDist = BIG_NUMBER;
const float aggroSqr = aggroRadius * aggroRadius;
for (ANetworkCharacter* character :m_nearbyPlayers)
{
ANetworkPlayer *player = Cast<ANetworkPlayer>(character);
if (player)
{
// ANetworkPlayer* player = Cast<ANetworkPlayer>(*iter);
const float distSqr = FVector2D::DistSquared(FVector2D(this->GetActorLocation()), FVector2D(player->GetActorLocation()));
if (distSqr < closestDist && distSqr <= aggroSqr)
{
closest = player;
closestDist = distSqr;
}
}
}
return closest;
}
void ASpawnerBase::AddThreat(class ANPCBase* creature, class ANetworkPlayer* character, int32 threat)
{
if (creature == nullptr || creature->IsPendingKill() || character == nullptr || character->IsPendingKill())
return;
//find the creaturemap
int32 index = 0;
bool found = false;
for (ANPCBase* mob : m_mobs)
{
if (creature == mob)
{
found = true;
break;
}
index++;
}
if (!found)
{
FERROR("creature not part of threatMap");
return;
}
//find the player
map<class ANetworkPlayer*, int32>::iterator it = m_threatMap[index].find(character);
if (it == m_threatMap[index].end())
{
FERROR("character not part of creatureMap");
return;
}
it->second += threat;
}
void ASpawnerBase::GetNewTarget(class ANPCBase* mob)
{
return;
}
void ASpawnerBase::ForceResetTarget(class ANPCBase* mob)
{
mob->target = nullptr;
}
void ASpawnerBase::ForceSetTarget(class ANPCBase* mob,class ANetworkPlayer* target)
{
mob->target = target;
}
void ASpawnerBase::SetTeam(int newTeam)
{
team = (NPCTeam)newTeam;
for (int i = 0; i < m_mobs.Num(); i++)
{
if (!IsValid(m_mobs[i]))
continue;
m_mobs[i]->SetTeam(newTeam);
if (!m_mobs[i]->target)
continue;
if(m_mobs[i]->target->GetTeam() == newTeam)
m_mobs[i]->target = nullptr;
}
}
void ASpawnerBase::CaptureCamp(int team)
{
SetTeam(team);
}
TArray<ANetworkPlayer*> ASpawnerBase::GetNearbyPlayers()
{
TArray<ANetworkPlayer*> players;
for(ANetworkCharacter* character : m_nearbyPlayers)
{
ANetworkPlayer* player = Cast<ANetworkPlayer>(character);
if(player)
{
players.Add(player);
}
}
return players;
}
void ASpawnerBase::GetLifetimeReplicatedProps(TArray<FLifetimeProperty>& OutLifetimeProps) const
{
Super::GetLifetimeReplicatedProps(OutLifetimeProps);
DOREPLIFETIME(ASpawnerBase, team);
}