354 lines
9.3 KiB
C++
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;
|
|
} |