haxis/Source/UnrealProject/Spawners/CreatureSpawn.cpp

354 lines
9.3 KiB
C++

// 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<USphereComponent>(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<ADefaultGameMode>(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<ADamageTakenModifier> subclass = ADamageTakenModifier::StaticClass();
m_invulnerableModifier = GetWorld()->SpawnActor<AModifier>(subclass);
if (m_invulnerableModifier != nullptr)
{
Cast<ADamageTakenModifier>(m_invulnerableModifier)->damageTakenMultiplier = 0.0f;
m_invulnerableModifier->character = m_mobs[0];
manager->AddModifier(m_invulnerableModifier);
Cast<ATouhouBoss>(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<ANetworkCharacter>(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<ANetworkCharacter>(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<ANPCBase>(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;
}