// Project Lab - NHTV Igad #include "UnrealProject.h" #include "NPCBase.h" #include "NetworkDoor.h" #include "DefaultGameMode.h" #include "CreatureSpawn.h" #include "Modifier.h" #include "NativeModifiers.h" #include "NetworkPlayer.h" #include "TouhouBoss.h" ACreatureSpawn::ACreatureSpawn() { respawnTime = 5; m_respawnTimer = 0; spawnContinuous = true; spawnTrigger = CreateDefaultSubobject(TEXT("Sphere")); spawnTrigger->SetCollisionProfileName(TEXT("PlayerOverlap")); spawnTrigger->AttachTo(RootComponent); spawnTrigger->OnComponentBeginOverlap.AddDynamic(this, &ACreatureSpawn::OnOverlapBegin); spawnTrigger->OnComponentEndOverlap.AddDynamic(this, &ACreatureSpawn::OnOverlapEnd); spawnTrigger->SetSphereRadius(3000); spawnTrigger->SetVisibility(false); } void ACreatureSpawn::BeginPlay() { Super::BeginPlay(); if (Role != ROLE_Authority) return; if (aggroRadius < 0) aggroRadius = 0; if (respawnTime < 0) respawnTime = 0; if (spawns.Num() == 0) { JWARNING("Empty spawner in scene"); return; } else if (spawns.Num() != 1 && isBoss) { FWARNING("Unexpected amount of spawners for boss"); } // Calculate spawn sub positions if (spawns.Num() > 1) { float angle = (360.0f) / 180.0f * PI; float anglestep = (angle) / (spawns.Num() + 1); float rot = angle * 0.5f; for (int32 i = 0; i < spawns.Num(); i++) { float f = cosf(rot); float r = sinf(rot); rot += anglestep; if (hasGeneral) { m_resetPoints.Add(GetActorLocation() + FVector(f * 200, r * 200, 0)); } else m_resetPoints.Add(FVector(formationPoints[i].X,formationPoints[i].Y,0) + GetActorLocation()); } } else if (spawns.Num() == 1) m_resetPoints.Add(GetActorLocation()); m_respawnTimer = -1.0f; } void ACreatureSpawn::EndPlay(const EEndPlayReason::Type EndPlayReason) { Super::EndPlay(EndPlayReason); } void ACreatureSpawn::Tick(float DeltaTime) { Super::Tick(DeltaTime); if (Role != ROLE_Authority) return; UWorld* const world = GetWorld(); if (!world) return; m_respawnTimer = m_respawnTimer > 0 ? (m_respawnTimer - DeltaTime) : 0; /*if (m_nearbyPlayers.empty()) { // Despawn if there are no players nearby and the timer is depleted if (m_respawnTimer <= 0) { // m_DespawnMobs(); } } else {*/ // Respawn time mobs if the timer has ran out and we are missing mobs if (m_respawnTimer <= 0 && m_mobCount != m_mobs.Num() && spawnContinuous) m_RespawnMobs(); // } // No mobs to update if (m_mobs.Num() == 0) return; // Decrement respawntime bool anyIdle = false; for (int32 i = 0; i < spawns.Num(); i++) { if (!spawns[i] || !m_mobs[i]) continue; // Check if any mob is idle const FVector2D mobPos = FVector2D(m_mobs[i]->GetActorLocation().X, m_mobs[i]->GetActorLocation().Y); const FVector2D targetPos = FVector2D(SpawnResetPosition().X, SpawnResetPosition().Y); const float distSqr = FVector2D::DistSquared(mobPos, targetPos); if (!m_mobs[i]->target && distSqr < deaggroRadius*deaggroRadius) anyIdle = true; } //// We can start attacking if any mob is idle and there is a player nearby //if (anyIdle) //{ // for (int32 i = 0; i < spawns.Num(); i++) // { // if (m_mobs[i] == nullptr) // continue; // //find the biggest threat of the enemies // ANetworkCharacter* threat = m_GetBiggestThreat(i); // if (m_mobs[i]->target != nullptr) // continue; // const FVector2D mobPos = FVector2D(m_mobs[i]->GetActorLocation().X, m_mobs[i]->GetActorLocation().Y); // const FVector2D targetPos = FVector2D(m_resetPoints[i].X, m_resetPoints[i].Y); // const float distSqr = FVector2D::DistSquared(mobPos, targetPos); // if (distSqr < aggroRadius * aggroRadius)// (m_mobs[i]->collisionRadius*m_mobs[i]->collisionRadius)*collisionScaler) // { // FPRINT("idledistsettarget"); // m_mobs[i]->target = threat; // } // // } //} if (isBoss && m_mobs.Num() == 1) { UWorld* const world = GetWorld(); if (!world) return; ADefaultGameMode* mode = Cast(world->GetAuthGameMode()); if (!mode) return; //check if it is more than 1 team int teamcount = 0; int32 team = 0; for (auto iter = m_nearbyPlayers.begin(); iter != m_nearbyPlayers.end(); iter++) { ANetworkCharacter* player = *iter; if ((player->GetActorLocation() - GetActorLocation()).Size() < aggroRadius) { if (teamcount == 0) { team = player->GetTeam(); teamcount++; } else if (team != player->GetTeam()) { teamcount++; break; } } } if (teamcount > 1 && m_invulnerableModifier == nullptr) { if (m_mobs[0] != nullptr) { ModifierManager* manager = m_mobs[0]->GetModifierManager(); TSubclassOf subclass = ADamageTakenModifier::StaticClass(); m_invulnerableModifier = GetWorld()->SpawnActor(subclass); if (m_invulnerableModifier != nullptr) { Cast(m_invulnerableModifier)->damageTakenMultiplier = 0.0f; m_invulnerableModifier->character = m_mobs[0]; manager->AddModifier(m_invulnerableModifier); Cast(m_mobs[0])->SetShield(true); } else { FPRINT("failed to spawn m_invulnerableModifier in creaturespawn"); } } } else if (teamcount <2 && m_invulnerableModifier != nullptr) { m_invulnerableModifier->ForceDestroy(); m_invulnerableModifier = nullptr; } } } int32 ACreatureSpawn::OnMobDie(class ANPCBase* mob) { const int32 idx = Super::OnMobDie(mob); m_respawnTimer = respawnTime; if (m_mobCount == 0) { for (int32 i = 0; i < doorsToOpen.Num(); i++) { if (doorsToOpen[i]) doorsToOpen[i]->SetDoorState(false); } } return idx; } void ACreatureSpawn::SpawnMobs() { m_RespawnMobs(); } void ACreatureSpawn::OnOverlapBegin(AActor* OtherActor, class UPrimitiveComponent* OtherComp, int32 OtherBodyIndex, bool bFromSweep, const FHitResult& SweepResult) { if (!OtherActor) return; if (!OtherActor->IsA(ANetworkCharacter::StaticClass())) return; ANetworkCharacter* player = Cast(OtherActor); if(player) m_OnPlayerEnterOverlap(*player); } void ACreatureSpawn::OnOverlapEnd(AActor* OtherActor, class UPrimitiveComponent* OtherComp, int32 OtherBodyIndex) { if (!OtherActor) return; if (!OtherActor->IsA(ANetworkCharacter::StaticClass())) return; ANetworkCharacter* player = Cast(OtherActor); if(player) m_OnPlayerExitOverlap(*player); } void ACreatureSpawn::m_DespawnMobs() { for (int32 i = 0; i < m_mobs.Num(); i++) { if (m_mobs[i]) { m_mobs[i]->UnsetSpawn(); m_mobs[i]->Destroy(); m_mobs[i] = nullptr; } } for (int32 i = 0; i < doorsToOpen.Num(); i++) { if (doorsToOpen[i]) doorsToOpen[i]->SetDoorState(false); } formationEnemies.Empty(); m_mobCount = 0; } void ACreatureSpawn::m_RespawnMobs() { UWorld* const world = GetWorld(); if (!world) return; for (int32 i = 0; i < m_mobs.Num(); i++) { if (!spawns[i]) continue; if (m_mobs[i] == nullptr) { // Respawn! FTransform spawnTransform = GetTransform(); FVector spawnVector = FVector(); spawnTransform.SetLocation(m_resetPoints[i] + FVector(0, 0, 120)); if (SpawnLocation.Num()>0) { int randnum = rand() % SpawnLocation.Num(); spawnTransform = SpawnLocation[randnum]->GetTransform(); FVector rotation = FVector(GetActorLocation() - spawnTransform.GetLocation()); spawnTransform.SetRotation(FQuat(rotation.Rotation())); //spawnVector = SpawnLocation->GetActorLocation() - GetActorLocation(); //spawnTransform.SetLocation(spawnVector); } ANPCBase* character = world->SpawnActorDeferred(spawns[i], spawnTransform, nullptr, nullptr, ESpawnActorCollisionHandlingMethod::AlwaysSpawn); character->SetSpawn(this); character->SetTeam((int32)team); m_mobs[i] = character; m_mobCount++; m_OnMobSpawn(i); UGameplayStatics::FinishSpawningActor(character, spawnTransform); character->SpawnDefaultController(); } } m_respawnTimer = respawnTime; } void ACreatureSpawn::GetNewTarget(class ANPCBase* mob) { if (mob == nullptr) return; int32 index = -1; for (ANPCBase* currentMob : m_mobs) { index++; if (mob != currentMob) continue; ANetworkCharacter* threat = nullptr; bool end = false; //keeps looping to make sure if the biggest threat is not in aggrorange it still works; int32 biggestThreat = -1; float dist = 1e34; for (ANetworkCharacter *character:m_nearbyPlayers) { if (character->GetTeam() == (int)team) continue; //check if it is bigger than the last biggestThreat const FVector2D spawnpost = FVector2D(GetActorLocation().X, GetActorLocation().Y); const FVector2D targetPos = FVector2D(character->GetActorLocation().X, character->GetActorLocation().Y); const float distSqr = FVector2D::DistSquared(spawnpost, targetPos); //making sure the new biggest thread is withing the aggroRadius if (distSqr < aggroRadius * aggroRadius&&dist>distSqr) { dist = distSqr; threat = character; } } m_resetTimer = resetTimer; currentMob->target = threat; return; } return; }