// 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("Spawner Root"); RootComponent = spawnerRoot; displayMesh = CreateDefaultSubobject(TEXT("Mesh")); displayMesh->bHiddenInGame = true; displayMesh->bGenerateOverlapEvents = false; displayMesh->SetCollisionEnabled(ECollisionEnabled::NoCollision); displayMesh->AttachTo(RootComponent); // Kill particle effect killParticle = CreateDefaultSubobject(TEXT("Kill Particle1")); killParticle->AttachTo(RootComponent); displayArrow = CreateDefaultSubobject(TEXT("Arrow")); displayArrow->AttachTo(displayMesh); displayDoors = CreateDefaultSubobject(TEXT("Visualizer")); displayDoors->AttachTo(displayMesh); visualizerComponent = CreateDefaultSubobject(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 test; GetAttachedActors(test); for (int i = 0; i < test.Num(); i++) { if (Cast(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(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 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(m_mobs[i])->hasGeneral = hasGeneral; } if (m_mobs[i]->IsA(AGeneralEnemy::StaticClass())) { AGeneralEnemy* asGeneral = Cast(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(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(&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(&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(creature); asGeneral->formationPoints = formationPoints; } else { if (creature->IsA(AEnemyBase::StaticClass())) formationEnemies.Add(Cast(creature)); } m_threatMap[index].clear(); /* for (ANetworkPlayer* player : m_nearbyPlayers) { auto ret = m_threatMap[index].insert(std::pair(player, 0)); if (!ret.second) { FPRINT("adding failed"); } }*/ if (creature->IsA(AMiniBossCreature::StaticClass()) && dropKeyFragmentIndex >= keyFragmentMin && dropKeyFragmentIndex <= keyFragmentMax) Cast(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(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(character); if (player) { // ANetworkPlayer* player = Cast(*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::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 ASpawnerBase::GetNearbyPlayers() { TArray players; for(ANetworkCharacter* character : m_nearbyPlayers) { ANetworkPlayer* player = Cast(character); if(player) { players.Add(player); } } return players; } void ASpawnerBase::GetLifetimeReplicatedProps(TArray& OutLifetimeProps) const { Super::GetLifetimeReplicatedProps(OutLifetimeProps); DOREPLIFETIME(ASpawnerBase, team); }