HAxis sos
This commit is contained in:
354
Source/UnrealProject/Spawners/CreatureSpawn.cpp
Normal file
354
Source/UnrealProject/Spawners/CreatureSpawn.cpp
Normal file
@@ -0,0 +1,354 @@
|
||||
// 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;
|
||||
}
|
||||
54
Source/UnrealProject/Spawners/CreatureSpawn.h
Normal file
54
Source/UnrealProject/Spawners/CreatureSpawn.h
Normal file
@@ -0,0 +1,54 @@
|
||||
// Project Lab - NHTV Igad
|
||||
|
||||
#pragma once
|
||||
|
||||
#include "SpawnerBase.h"
|
||||
#include "CreatureSpawn.generated.h"
|
||||
|
||||
UCLASS()
|
||||
class ACreatureSpawn : public ASpawnerBase
|
||||
{
|
||||
GENERATED_BODY()
|
||||
|
||||
public:
|
||||
ACreatureSpawn();
|
||||
|
||||
virtual void BeginPlay() override;
|
||||
virtual void EndPlay(const EEndPlayReason::Type EndPlayReason) override;
|
||||
|
||||
virtual void Tick(float DeltaTime) override;
|
||||
|
||||
UPROPERTY(EditAnywhere, Category = "Switch Components")
|
||||
TArray<class ANetworkDoor*> doorsToOpen;
|
||||
|
||||
UPROPERTY(EditAnywhere)
|
||||
float respawnTime;
|
||||
|
||||
UPROPERTY(EditAnywhere, Category = "Switch Components")
|
||||
class USphereComponent* spawnTrigger;
|
||||
|
||||
UPROPERTY(EditAnywhere, Category = "Gamestate Components")
|
||||
bool isBoss;
|
||||
UPROPERTY(EditAnywhere, Category = "Gamestate Components")
|
||||
bool spawnContinuous;
|
||||
|
||||
UFUNCTION()
|
||||
void OnOverlapBegin(class AActor* OtherActor, class UPrimitiveComponent* OtherComp, int32 OtherBodyIndex, bool bFromSweep, const FHitResult& SweepResult);
|
||||
UFUNCTION()
|
||||
void OnOverlapEnd(class AActor* OtherActor, class UPrimitiveComponent* OtherComp, int32 OtherBodyIndex);
|
||||
|
||||
virtual int32 OnMobDie(class ANPCBase* mob) override;
|
||||
virtual void GetNewTarget(class ANPCBase* mob) override;
|
||||
void SpawnMobs();
|
||||
|
||||
protected:
|
||||
virtual void m_RespawnMobs();
|
||||
private:
|
||||
TArray<FVector> m_resetPoints;
|
||||
float m_respawnTimer;
|
||||
|
||||
class AModifier* m_invulnerableModifier;
|
||||
|
||||
void m_DespawnMobs();
|
||||
|
||||
};
|
||||
244
Source/UnrealProject/Spawners/CreatureSpawnComponent.cpp
Normal file
244
Source/UnrealProject/Spawners/CreatureSpawnComponent.cpp
Normal file
@@ -0,0 +1,244 @@
|
||||
// Project Lab - NHTV Igad
|
||||
|
||||
#include "UnrealProject.h"
|
||||
#include "CreatureSpawnComponent.h"
|
||||
#include "SpawnerBase.h"
|
||||
#include "GeneralEnemy.h"
|
||||
#include "OffensiveEnemy.h"
|
||||
#include "RangedEnemy.h"
|
||||
#include "EnemyBase.h"
|
||||
|
||||
#define CONEARCVERTEXCOUNT 50
|
||||
|
||||
UCreatureSpawnComponent::UCreatureSpawnComponent(const FObjectInitializer& ObjectInitializer) : Super(ObjectInitializer)
|
||||
{
|
||||
ShapeColor = FColor(223, 149, 157, 255);
|
||||
spawnerBase = Cast<ASpawnerBase>(GetAttachmentRootActor());
|
||||
bUseEditorCompositing = true;
|
||||
}
|
||||
|
||||
FPrimitiveSceneProxy* UCreatureSpawnComponent::CreateSceneProxy()
|
||||
{
|
||||
class FDrawConeSceneProxy : public FPrimitiveSceneProxy
|
||||
{
|
||||
public:
|
||||
const UCreatureSpawnComponent* component;
|
||||
FDrawConeSceneProxy(const UCreatureSpawnComponent* InComponent)
|
||||
: FPrimitiveSceneProxy(InComponent)
|
||||
, spawnerBase(InComponent->spawnerBase)
|
||||
, bDrawOnlyIfSelected(InComponent->bDrawOnlyIfSelected)
|
||||
, component(InComponent)
|
||||
{
|
||||
bWillEverBeLit = false;
|
||||
}
|
||||
|
||||
|
||||
virtual void GetDynamicMeshElements(const TArray<const FSceneView*>& Views, const FSceneViewFamily& ViewFamily, uint32 VisibilityMap, FMeshElementCollector& Collector) const override
|
||||
{
|
||||
QUICK_SCOPE_CYCLE_COUNTER(STAT_GetDynamicMeshElements_DrawDynamicElements);
|
||||
|
||||
const FMatrix& LocalToWorld = GetLocalToWorld();
|
||||
|
||||
for (int32 ViewIndex = 0; ViewIndex < Views.Num(); ViewIndex++)
|
||||
{
|
||||
const FSceneView* View = Views[ViewIndex];
|
||||
|
||||
FPrimitiveDrawInterface* PDI = Collector.GetPDI(ViewIndex);
|
||||
if (spawnerBase)
|
||||
{
|
||||
|
||||
FTransform transform = component->GetAttachParent()->GetComponentTransform();
|
||||
FVector base = transform.GetLocation();
|
||||
|
||||
|
||||
const float scale = 1.0f / 180.0f * PI;
|
||||
float baseRot = (360.0f - transform.GetRotation().Euler().Z) * scale;
|
||||
|
||||
//FVector forward = FVector(1, 0, 0).RotateAngleAxis(0.0f, FVector(0, 0, 1));
|
||||
//FVector right = FVector(0, 1, 0).RotateAngleAxis(0.0f, FVector(0, 0, 1));
|
||||
FVector forward = FVector(1, 0, 0);
|
||||
FVector right = FVector(0, 1, 0);
|
||||
float angle = (360.0f) / 180.0f * PI;
|
||||
|
||||
FVector linePoints[CONEARCVERTEXCOUNT];
|
||||
float anglestep = (angle) / (CONEARCVERTEXCOUNT - 1);
|
||||
|
||||
float rot = baseRot - angle * 0.5f;
|
||||
for (int i = 0; i < CONEARCVERTEXCOUNT; i++)
|
||||
{
|
||||
float f = cosf(rot);
|
||||
float r = sinf(rot);
|
||||
linePoints[i] = base + (forward * f - right * r) * spawnerBase->aggroRadius;
|
||||
rot += anglestep;
|
||||
}
|
||||
|
||||
for (int i = 0; i < CONEARCVERTEXCOUNT - 1; i++)
|
||||
{
|
||||
|
||||
PDI->DrawLine(linePoints[i], linePoints[i + 1], ShapeColor, SDPG_World);
|
||||
}
|
||||
for (int i = 0; i < CONEARCVERTEXCOUNT; i++)
|
||||
{
|
||||
float f = cosf(rot);
|
||||
float r = sinf(rot);
|
||||
linePoints[i] = base + (forward * f - right * r) * spawnerBase->deaggroRadius;
|
||||
rot += anglestep;
|
||||
}
|
||||
|
||||
for (int i = 0; i < CONEARCVERTEXCOUNT - 1; i++)
|
||||
{
|
||||
|
||||
PDI->DrawLine(linePoints[i], linePoints[i + 1], FColor(0,0,255), SDPG_World);
|
||||
}
|
||||
FVector last = spawnerBase->GetActorLocation();
|
||||
FVector first;
|
||||
for (int32 i = 0; i < spawnerBase->controlPoints.Num(); i++)
|
||||
{
|
||||
|
||||
FTransform transform = component->GetAttachParent()->GetComponentTransform();
|
||||
FVector base = transform.GetLocation() + FVector(spawnerBase->controlPoints[i].X, spawnerBase->controlPoints[i].Y, 0);
|
||||
if (i == 0)
|
||||
first = base;
|
||||
|
||||
const float scale = 1.0f / 180.0f * PI;
|
||||
float baseRot = (360.0f - transform.GetRotation().Euler().Z) * scale;
|
||||
|
||||
//FVector forward = FVector(1, 0, 0).RotateAngleAxis(0.0f, FVector(0, 0, 1));
|
||||
//FVector right = FVector(0, 1, 0).RotateAngleAxis(0.0f, FVector(0, 0, 1));
|
||||
FVector forward = FVector(1, 0, 0);
|
||||
FVector right = FVector(0, 1, 0);
|
||||
float angle = (360.0f) / 180.0f * PI;
|
||||
|
||||
FVector linePoints[CONEARCVERTEXCOUNT];
|
||||
float anglestep = (angle) / (CONEARCVERTEXCOUNT - 1);
|
||||
|
||||
float rot = baseRot - angle * 0.5f;
|
||||
for (int i = 0; i < CONEARCVERTEXCOUNT; i++)
|
||||
{
|
||||
float f = cosf(rot);
|
||||
float r = sinf(rot);
|
||||
linePoints[i] = base + (forward * f - right * r) * spawnerBase->drawingRadius;
|
||||
rot += anglestep;
|
||||
}
|
||||
|
||||
for (int i = 0; i < CONEARCVERTEXCOUNT - 1; i++)
|
||||
{
|
||||
|
||||
PDI->DrawLine(linePoints[i], linePoints[i + 1], ShapeColor, SDPG_World);
|
||||
}
|
||||
PDI->DrawLine(base, last, ShapeColor, SDPG_World);
|
||||
last = base;
|
||||
//PDI->DrawLine(base, linePoints[CONEARCVERTEXCOUNT - 1], ShapeColor, SDPG_World);
|
||||
}
|
||||
if (spawnerBase->isConnected)
|
||||
PDI->DrawLine(first, last, ShapeColor, SDPG_World);
|
||||
for (int32 i = 0; i < spawnerBase->formationPoints.Num(); i++)
|
||||
{
|
||||
FColor formationColor = FColor(100, 100, 100);
|
||||
//AEnemyBase *creature = (dynamic_cast<AGeneralEnemy*>(spawnerBase->spawns[i]));
|
||||
FTransform transform = component->GetAttachParent()->GetComponentTransform();
|
||||
FVector base = transform.GetLocation() + spawnerBase->GetActorRotation().RotateVector( FVector(spawnerBase->formationPoints[i].X, spawnerBase->formationPoints[i].Y, 0))*spawnerBase->formationScale;
|
||||
if (i == 0)
|
||||
first = base;
|
||||
|
||||
const float scale = 1.0f / 180.0f * PI;
|
||||
float baseRot = (360.0f - transform.GetRotation().Euler().Z) * scale;
|
||||
|
||||
//FVector forward = FVector(1, 0, 0).RotateAngleAxis(0.0f, FVector(0, 0, 1));
|
||||
//FVector right = FVector(0, 1, 0).RotateAngleAxis(0.0f, FVector(0, 0, 1));
|
||||
FVector forward = FVector(1, 0, 0);
|
||||
FVector right = FVector(0, 1, 0);
|
||||
float angle = (360.0f) / 180.0f * PI;
|
||||
|
||||
FVector linePoints[CONEARCVERTEXCOUNT];
|
||||
float anglestep = (angle) / (CONEARCVERTEXCOUNT - 1);
|
||||
|
||||
float rot = baseRot - angle * 0.5f;
|
||||
for (int j = 0; j < CONEARCVERTEXCOUNT; j++)
|
||||
{
|
||||
float f = cosf(rot);
|
||||
float r = sinf(rot);
|
||||
linePoints[j] = base + (forward * f - right * r) * spawnerBase->drawingRadius;
|
||||
rot += anglestep;
|
||||
}
|
||||
if (i >= spawnerBase->spawns.Num())
|
||||
{
|
||||
formationColor.B = 10;
|
||||
formationColor.G = 10;
|
||||
formationColor.R = 10;
|
||||
|
||||
}
|
||||
else
|
||||
{
|
||||
|
||||
if ((spawnerBase->spawns[i])->IsChildOf(AGeneralEnemy::StaticClass()))
|
||||
{
|
||||
formationColor.B = 255;
|
||||
formationColor.G = 255;
|
||||
formationColor.R = 255;
|
||||
}
|
||||
else
|
||||
{
|
||||
formationColor.B = 42;
|
||||
formationColor.G = 42;
|
||||
formationColor.R = 42;
|
||||
}
|
||||
if ((spawnerBase->spawns[i])->IsChildOf(ARangedEnemy::StaticClass())&& !spawnerBase->spawns[i]->IsChildOf(AGeneralEnemy::StaticClass()))
|
||||
{
|
||||
formationColor.B = 0;
|
||||
formationColor.G = 255;
|
||||
formationColor.R = 0;
|
||||
}
|
||||
|
||||
if ((spawnerBase->spawns[i])->IsChildOf(AOffensiveEnemy::StaticClass()))
|
||||
{
|
||||
formationColor.B = 0;
|
||||
formationColor.G = 0;
|
||||
formationColor.R = 255;
|
||||
}
|
||||
|
||||
}
|
||||
for (int j = 0; j < CONEARCVERTEXCOUNT - 1; j++)
|
||||
{
|
||||
|
||||
PDI->DrawLine(linePoints[j], linePoints[j + 1], formationColor, SDPG_World);
|
||||
}
|
||||
if (i < spawnerBase->formationRotation.Num())
|
||||
{
|
||||
|
||||
PDI->DrawLine(base, base+(FRotator(0, spawnerBase->formationRotation[i],0).RotateVector( spawnerBase->GetActorForwardVector())* spawnerBase->drawingRadius), formationColor, SDPG_World);
|
||||
}
|
||||
else PDI->DrawLine(base, base + (spawnerBase->GetActorForwardVector())* spawnerBase->drawingRadius, formationColor, SDPG_World);
|
||||
// PDI->DrawLine(base, last, formationColor, SDPG_World);
|
||||
last = base;
|
||||
//PDI->DrawLine(base, linePoints[CONEARCVERTEXCOUNT - 1], ShapeColor, SDPG_World);
|
||||
}
|
||||
//if (spawnerBase->isConnected)
|
||||
// PDI->DrawLine(first, last, ShapeColor, SDPG_World);
|
||||
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
virtual FPrimitiveViewRelevance GetViewRelevance(const FSceneView* View) const override
|
||||
{
|
||||
FPrimitiveViewRelevance Result;
|
||||
Result.bDrawRelevance = IsSelected();
|
||||
Result.bDynamicRelevance = true;
|
||||
Result.bShadowRelevance = IsShadowCast(View);
|
||||
Result.bEditorPrimitiveRelevance = UseEditorCompositing(View);
|
||||
return Result;
|
||||
}
|
||||
|
||||
virtual uint32 GetMemoryFootprint(void) const override { return(sizeof(*this) + GetAllocatedSize()); }
|
||||
uint32 GetAllocatedSize(void) const { return(FPrimitiveSceneProxy::GetAllocatedSize()); }
|
||||
|
||||
private:
|
||||
const uint32 bDrawOnlyIfSelected : 1;
|
||||
const ASpawnerBase* spawnerBase;
|
||||
const FColor ShapeColor = FColor(255, 0, 0, 255);
|
||||
const FTransform transform;
|
||||
};
|
||||
|
||||
return new FDrawConeSceneProxy(this);
|
||||
}
|
||||
22
Source/UnrealProject/Spawners/CreatureSpawnComponent.h
Normal file
22
Source/UnrealProject/Spawners/CreatureSpawnComponent.h
Normal file
@@ -0,0 +1,22 @@
|
||||
// Project Lab - NHTV Igad
|
||||
|
||||
#pragma once
|
||||
|
||||
#include "Components/CapsuleComponent.h"
|
||||
#include "CreatureSpawnComponent.generated.h"
|
||||
|
||||
/**
|
||||
*
|
||||
*/
|
||||
class ASpawnerBase;
|
||||
UCLASS()
|
||||
class UNREALPROJECT_API UCreatureSpawnComponent : public UCapsuleComponent
|
||||
{
|
||||
GENERATED_UCLASS_BODY()
|
||||
|
||||
public:
|
||||
ASpawnerBase* spawnerBase;
|
||||
|
||||
virtual FPrimitiveSceneProxy* CreateSceneProxy() override;
|
||||
|
||||
};
|
||||
41
Source/UnrealProject/Spawners/KOTHBossSpawner.cpp
Normal file
41
Source/UnrealProject/Spawners/KOTHBossSpawner.cpp
Normal file
@@ -0,0 +1,41 @@
|
||||
// Project Lab - NHTV Igad
|
||||
|
||||
#include "UnrealProject.h"
|
||||
#include "KOTHBossSpawner.h"
|
||||
#include "NPCBase.h"
|
||||
|
||||
void AKOTHBossSpawner::BeginPlay()
|
||||
{
|
||||
Super::BeginPlay();
|
||||
for (TActorIterator<ASpawnerBase>actorIt(GetWorld()); actorIt; ++actorIt)
|
||||
{
|
||||
ASpawnerBase *spawn = *actorIt;
|
||||
if (!spawn->possesable)
|
||||
spawn->SetTeam((int)team);
|
||||
}
|
||||
}
|
||||
void AKOTHBossSpawner::EndPlay(const EEndPlayReason::Type EndPlayReason)
|
||||
{
|
||||
Super::EndPlay(EndPlayReason);
|
||||
}
|
||||
void AKOTHBossSpawner::Tick(float deltaTime)
|
||||
{
|
||||
Super::Tick(deltaTime);
|
||||
}
|
||||
bool AKOTHBossSpawner::IsBossAlive() const
|
||||
{
|
||||
if(m_mobs.Num() > 0)
|
||||
{
|
||||
if(IsValid(m_mobs[0]))
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
void AKOTHBossSpawner::m_RespawnMobs()
|
||||
{
|
||||
if (IsContested(m_currentTeam) && m_currentTeam<5)
|
||||
{
|
||||
return;
|
||||
}
|
||||
Super::m_RespawnMobs();
|
||||
}
|
||||
26
Source/UnrealProject/Spawners/KOTHBossSpawner.h
Normal file
26
Source/UnrealProject/Spawners/KOTHBossSpawner.h
Normal file
@@ -0,0 +1,26 @@
|
||||
// Project Lab - NHTV Igad
|
||||
|
||||
#pragma once
|
||||
|
||||
#include "Spawners/KOTHSpawnerBase.h"
|
||||
#include "KOTHBossSpawner.generated.h"
|
||||
|
||||
/**
|
||||
*
|
||||
*/
|
||||
UCLASS()
|
||||
class UNREALPROJECT_API AKOTHBossSpawner : public AKOTHSpawnerBase
|
||||
{
|
||||
GENERATED_BODY()
|
||||
public:
|
||||
virtual void BeginPlay() override;
|
||||
virtual void EndPlay(const EEndPlayReason::Type EndPlayReason) override;
|
||||
virtual void Tick(float deltaTime)override;
|
||||
// Check if the KOTH boss is currently alive
|
||||
bool IsBossAlive() const;
|
||||
|
||||
protected:
|
||||
virtual void m_RespawnMobs()override;
|
||||
private:
|
||||
|
||||
};
|
||||
78
Source/UnrealProject/Spawners/KOTHMinionSpawner.cpp
Normal file
78
Source/UnrealProject/Spawners/KOTHMinionSpawner.cpp
Normal file
@@ -0,0 +1,78 @@
|
||||
// Project Lab - NHTV Igad
|
||||
|
||||
#include "UnrealProject.h"
|
||||
#include "NPCBase.h"
|
||||
#include "NetworkDoor.h"
|
||||
#include "DefaultGameMode.h"
|
||||
#include "KOTHMinionSpawner.h"
|
||||
#include "KOTHBossSpawner.h"
|
||||
#include "NetworkCharacter.h"
|
||||
|
||||
|
||||
|
||||
AKOTHMinionSpawner::AKOTHMinionSpawner(const FObjectInitializer& init)
|
||||
: Super(init)
|
||||
{
|
||||
respawnTime = 5;
|
||||
// m_respawnTimer = 0;
|
||||
|
||||
spawnContinuous = true;
|
||||
}
|
||||
void AKOTHMinionSpawner::BeginPlay()
|
||||
{
|
||||
Super::BeginPlay();
|
||||
if (Role == ROLE_Authority)
|
||||
{
|
||||
kothagroRadius = aggroRadius;
|
||||
kothdeagroRadius = deaggroRadius;
|
||||
|
||||
for (TActorIterator<AKOTHBossSpawner>bSpawn(GetWorld()); bSpawn; ++bSpawn)
|
||||
{
|
||||
if (bSpawn)
|
||||
bossSpawner = *bSpawn;
|
||||
}
|
||||
Super::m_RespawnMobs();
|
||||
}
|
||||
}
|
||||
void AKOTHMinionSpawner::Tick(float DeltaTime)
|
||||
{
|
||||
Super::Tick(DeltaTime);
|
||||
|
||||
if (!HasAuthority())
|
||||
return;
|
||||
|
||||
// Reset the camp to the NPC team after an amount of time(captureTime)
|
||||
m_captureTimer -= DeltaTime;
|
||||
if (m_captureTimer < 0 && (int)team < 5)
|
||||
{
|
||||
SetTeam((int)(NPCTeam::Team1));
|
||||
m_reset = true;
|
||||
}
|
||||
}
|
||||
void AKOTHMinionSpawner::GetNewTarget(ANPCBase* mob)
|
||||
{
|
||||
|
||||
if (team == bossSpawner->team || (int)team>5)
|
||||
{
|
||||
aggroRadius = kothagroRadius;
|
||||
deaggroRadius = kothdeagroRadius;
|
||||
Super::GetNewTarget(mob);
|
||||
return;
|
||||
}
|
||||
for (ANPCBase* bMob : bossSpawner->m_mobs)
|
||||
{
|
||||
aggroRadius = 1e34;
|
||||
deaggroRadius = 1e34;
|
||||
ANetworkCharacter* threat = Cast<ANetworkCharacter>(bMob);
|
||||
if (threat)
|
||||
mob->target = threat;
|
||||
|
||||
}
|
||||
}
|
||||
void AKOTHMinionSpawner::m_RespawnMobs()
|
||||
{
|
||||
if (((int)team < 5 || m_reset))
|
||||
{
|
||||
Super::m_RespawnMobs();
|
||||
}
|
||||
}
|
||||
30
Source/UnrealProject/Spawners/KOTHMinionSpawner.h
Normal file
30
Source/UnrealProject/Spawners/KOTHMinionSpawner.h
Normal file
@@ -0,0 +1,30 @@
|
||||
// Project Lab - NHTV Igad
|
||||
|
||||
#pragma once
|
||||
|
||||
#include "Spawners/KOTHSpawnerBase.h"
|
||||
#include "KOTHMinionSpawner.generated.h"
|
||||
|
||||
/**
|
||||
*
|
||||
*/
|
||||
UCLASS()
|
||||
class UNREALPROJECT_API AKOTHMinionSpawner : public AKOTHSpawnerBase
|
||||
{
|
||||
GENERATED_BODY()
|
||||
public:
|
||||
AKOTHMinionSpawner(const FObjectInitializer& init);
|
||||
virtual void GetNewTarget(class ANPCBase* mob)override;
|
||||
virtual void BeginPlay()override;
|
||||
virtual void Tick(float deltaTime) override;
|
||||
|
||||
|
||||
|
||||
UPROPERTY(EditAnywhere, BlueprintReadWrite, Category = "KOTH Spawner Properties")
|
||||
FText CampName;
|
||||
|
||||
private:
|
||||
virtual void m_RespawnMobs()override;
|
||||
float kothagroRadius;
|
||||
float kothdeagroRadius;
|
||||
};
|
||||
132
Source/UnrealProject/Spawners/KOTHSpawnerBase.cpp
Normal file
132
Source/UnrealProject/Spawners/KOTHSpawnerBase.cpp
Normal file
@@ -0,0 +1,132 @@
|
||||
// Project Lab - NHTV Igad
|
||||
|
||||
#include "UnrealProject.h"
|
||||
#include "KOTHSpawnerBase.h"
|
||||
#include "NetworkPlayer.h"
|
||||
#include "KOTHBossSpawner.h"
|
||||
AKOTHSpawnerBase::AKOTHSpawnerBase(const FObjectInitializer& init)
|
||||
{
|
||||
captureRadius = 500;
|
||||
captureTime = 30;
|
||||
possesionTime = 1;
|
||||
|
||||
captureParticle = CreateDefaultSubobject<UParticleSystemComponent>("PossessParticle");
|
||||
captureParticle->AttachTo(RootComponent);
|
||||
}
|
||||
|
||||
void AKOTHSpawnerBase::BeginPlay()
|
||||
{
|
||||
Super::BeginPlay();
|
||||
|
||||
// Assign team
|
||||
m_currentTeam = (int)team;
|
||||
|
||||
beingCaptured = false;
|
||||
}
|
||||
void AKOTHSpawnerBase::Tick(float DeltaTime)
|
||||
{
|
||||
Super::Tick(DeltaTime);
|
||||
|
||||
// Run capture polling code on host
|
||||
if(Role == ROLE_Authority)
|
||||
{
|
||||
if(beingCaptured)
|
||||
{
|
||||
bool capture = false;
|
||||
auto nearbyPlayers = GetNearbyPlayers();
|
||||
// Check contested condition
|
||||
|
||||
if (m_mobCount == 0 && (nearbyPlayers.Num() <= 2))
|
||||
{
|
||||
|
||||
if (nearbyPlayers.Num() > 0 && !IsContested(nearbyPlayers[0]->GetTeam()) && (int)team != nearbyPlayers[0]->GetTeam())
|
||||
{
|
||||
capture = true;
|
||||
|
||||
if (bossSpawner)
|
||||
{
|
||||
if ((int)bossSpawner->team == nearbyPlayers[0]->GetTeam() || (int)team < 5)
|
||||
{
|
||||
capture = false;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if(capture)
|
||||
{
|
||||
m_possesionTimer += DeltaTime;
|
||||
if(m_possesionTimer > possesionTime)
|
||||
{
|
||||
// Nearby team captures camp
|
||||
m_ExeCaptureCamp(nearbyPlayers[0]->GetTeam());
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
m_possesionTimer = 0;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void AKOTHSpawnerBase::m_ExeCaptureCamp_Implementation(int32 targetTeam)
|
||||
{
|
||||
if(Role == ROLE_Authority)
|
||||
{
|
||||
SetTeam(targetTeam);
|
||||
onCampCaptured.Broadcast(targetTeam);
|
||||
m_captureTimer = captureTime;
|
||||
m_currentTeam = (int)NPCTeam::Team1;
|
||||
beingCaptured = false;
|
||||
m_reset = false;
|
||||
}
|
||||
|
||||
// Callback
|
||||
OnEndCaptureCamp(targetTeam);
|
||||
onEndCapture.Broadcast(targetTeam);
|
||||
|
||||
// Deactivate capture particle
|
||||
captureParticle->DeactivateSystem();
|
||||
}
|
||||
|
||||
void AKOTHSpawnerBase::m_OnCampCleared()
|
||||
{
|
||||
Super::m_OnCampCleared();
|
||||
|
||||
if(Role == ROLE_Authority)
|
||||
{
|
||||
beingCaptured = true;
|
||||
}
|
||||
|
||||
// Callback
|
||||
OnBeginCaptureCamp();
|
||||
onBeginCapture.Broadcast();
|
||||
|
||||
// Enable capture particle
|
||||
if(captureParticle->Template)
|
||||
{
|
||||
captureParticle->bAutoDestroy = false;
|
||||
captureParticle->ResetParticles();
|
||||
captureParticle->ActivateSystem();
|
||||
}
|
||||
}
|
||||
|
||||
void AKOTHSpawnerBase::CaptureCamp(int targetTeam)
|
||||
{
|
||||
Super::CaptureCamp(targetTeam);
|
||||
|
||||
m_currentTeam = targetTeam;
|
||||
m_possesionTimer = 0;
|
||||
}
|
||||
|
||||
bool AKOTHSpawnerBase::IsContested(int32 targetTeam) const
|
||||
{
|
||||
for(ANetworkCharacter* character : m_nearbyPlayers)
|
||||
{
|
||||
ANetworkPlayer* player = Cast<ANetworkPlayer>(character);
|
||||
if(player && player->GetTeam() != targetTeam && FVector::DistSquared(GetActorLocation(), player->GetActorLocation()) < captureRadius * captureRadius)
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
70
Source/UnrealProject/Spawners/KOTHSpawnerBase.h
Normal file
70
Source/UnrealProject/Spawners/KOTHSpawnerBase.h
Normal file
@@ -0,0 +1,70 @@
|
||||
// Project Lab - NHTV Igad
|
||||
|
||||
#pragma once
|
||||
|
||||
#include "Spawners/CreatureSpawn.h"
|
||||
#include "KOTHSpawnerBase.generated.h"
|
||||
|
||||
/**
|
||||
*
|
||||
*/
|
||||
UCLASS()
|
||||
class UNREALPROJECT_API AKOTHSpawnerBase : public ACreatureSpawn
|
||||
{
|
||||
GENERATED_BODY()
|
||||
public:
|
||||
AKOTHSpawnerBase(const FObjectInitializer& init);
|
||||
void BeginPlay() override;
|
||||
void Tick(float DeltaTime) override;
|
||||
|
||||
// Overrided capture camp behaviour
|
||||
virtual void CaptureCamp(int team) override;
|
||||
|
||||
DECLARE_DYNAMIC_MULTICAST_DELEGATE(FOnBeginCapture);
|
||||
DECLARE_DYNAMIC_MULTICAST_DELEGATE_OneParam(FOnEndCapture, int32, targetTeam);
|
||||
|
||||
UFUNCTION(BlueprintImplementableEvent, Category = "KOTH Spawner")
|
||||
void OnBeginCaptureCamp();
|
||||
UFUNCTION(BlueprintImplementableEvent, Category = "KOTH Spawner")
|
||||
void OnEndCaptureCamp(int32 targetTeam);
|
||||
UPROPERTY(BlueprintAssignable)
|
||||
FOnBeginCapture onBeginCapture;
|
||||
UPROPERTY(BlueprintAssignable)
|
||||
FOnEndCapture onEndCapture;
|
||||
|
||||
UFUNCTION(BlueprintCallable, Category = "KOTH Spawner")
|
||||
bool IsContested(int32 targetTeam) const;
|
||||
|
||||
DECLARE_DYNAMIC_MULTICAST_DELEGATE_OneParam(FOnCampCaptured, int32, targetTeam);
|
||||
UPROPERTY(BlueprintAssignable, Category = "Game")
|
||||
FOnCampCaptured onCampCaptured;
|
||||
|
||||
UPROPERTY(VisibleAnywhere, BlueprintReadOnly, Category = "Capture Particle")
|
||||
UParticleSystemComponent* captureParticle;
|
||||
|
||||
UPROPERTY(BlueprintReadOnly, Category = "KOTH Spawner Properties")
|
||||
bool beingCaptured;
|
||||
|
||||
UPROPERTY(EditAnywhere, BlueprintReadWrite, Category = "KOTH Spawner Properties")
|
||||
float captureRadius;
|
||||
// The duration the camp is captured for
|
||||
UPROPERTY(EditAnywhere, BlueprintReadWrite, Category = "KOTH Spawner Properties")
|
||||
float captureTime;
|
||||
// The time it takes to capture a camp
|
||||
UPROPERTY(EditAnywhere, BlueprintReadWrite, Category = "KOTH Spawner Properties")
|
||||
float possesionTime;
|
||||
class AKOTHBossSpawner* bossSpawner;
|
||||
|
||||
protected:
|
||||
// Handle to start the capture process
|
||||
void m_OnCampCleared() override;
|
||||
|
||||
// Called when a camp is no longer contested and should be captured by the target team
|
||||
UFUNCTION(NetMulticast, Reliable)
|
||||
void m_ExeCaptureCamp(int32 targetTeam);
|
||||
|
||||
float m_captureTimer;
|
||||
float m_possesionTimer;
|
||||
int m_currentTeam;
|
||||
bool m_reset;
|
||||
};
|
||||
20
Source/UnrealProject/Spawners/LobbySpawn.cpp
Normal file
20
Source/UnrealProject/Spawners/LobbySpawn.cpp
Normal file
@@ -0,0 +1,20 @@
|
||||
// Project Lab - NHTV Igad
|
||||
|
||||
#include "UnrealProject.h"
|
||||
#include "LobbySpawn.h"
|
||||
|
||||
|
||||
ALobbySpawn::ALobbySpawn()
|
||||
{
|
||||
displayMesh = CreateDefaultSubobject<UStaticMeshComponent>(TEXT("Mesh"));
|
||||
displayMesh->bHiddenInGame = true;
|
||||
displayMesh->bGenerateOverlapEvents = false;
|
||||
displayMesh->SetCollisionEnabled(ECollisionEnabled::NoCollision);
|
||||
displayArrow = CreateDefaultSubobject<UArrowComponent>(TEXT("Arrow"));
|
||||
assignedTeam = 0;
|
||||
}
|
||||
|
||||
void ALobbySpawn::BeginPlay()
|
||||
{
|
||||
Super::BeginPlay();
|
||||
}
|
||||
25
Source/UnrealProject/Spawners/LobbySpawn.h
Normal file
25
Source/UnrealProject/Spawners/LobbySpawn.h
Normal file
@@ -0,0 +1,25 @@
|
||||
// Project Lab - NHTV Igad
|
||||
|
||||
#pragma once
|
||||
|
||||
#include "GameFramework/Actor.h"
|
||||
#include "LobbySpawn.generated.h"
|
||||
|
||||
UCLASS()
|
||||
class UNREALPROJECT_API ALobbySpawn : public AActor
|
||||
{
|
||||
GENERATED_BODY()
|
||||
|
||||
public:
|
||||
ALobbySpawn();
|
||||
|
||||
virtual void BeginPlay() override;
|
||||
|
||||
UPROPERTY(EditAnywhere, Category = "Gamestate Components")
|
||||
int32 assignedTeam;
|
||||
|
||||
UPROPERTY(VisibleAnywhere, BlueprintReadOnly, Category = Display)
|
||||
class UStaticMeshComponent* displayMesh;
|
||||
UPROPERTY(VisibleAnywhere, BlueprintReadOnly, Category = Display)
|
||||
class UArrowComponent* displayArrow;
|
||||
};
|
||||
259
Source/UnrealProject/Spawners/PatrolSpawn.cpp
Normal file
259
Source/UnrealProject/Spawners/PatrolSpawn.cpp
Normal file
@@ -0,0 +1,259 @@
|
||||
// Project Lab - NHTV Igad
|
||||
|
||||
#include "UnrealProject.h"
|
||||
#include "PatrolSpawn.h"
|
||||
#include "NPCBase.h"
|
||||
#include "DefaultGameMode.h"
|
||||
#include "NetworkPlayer.h"
|
||||
|
||||
APatrolSpawn::APatrolSpawn()
|
||||
{
|
||||
respawnTime = 5;
|
||||
m_respawnTimer = 0;
|
||||
|
||||
spawnContinuous = true;
|
||||
}
|
||||
|
||||
void APatrolSpawn::BeginPlay()
|
||||
{
|
||||
Super::BeginPlay();
|
||||
if (Role != ROLE_Authority)
|
||||
return;
|
||||
FName name;
|
||||
|
||||
if (aggroRadius < 0) aggroRadius = 0;
|
||||
if (respawnTime < 0) respawnTime = 0;
|
||||
|
||||
if (spawns.Num() == 0)
|
||||
{
|
||||
JWARNING("Empty spawner in scene");
|
||||
return;
|
||||
}
|
||||
|
||||
for (int i = 0; i < controlPoints.Num(); i++)
|
||||
m_controlPoints.Add(FVector(controlPoints[i].X + this->GetActorLocation().X, controlPoints[i].Y + this->GetActorLocation().Y, this->GetActorLocation().Z));
|
||||
|
||||
m_controlPointSign = 1;
|
||||
m_isPulled = false;
|
||||
|
||||
m_currentControlPoint = 0;
|
||||
m_lastPosition = GetActorLocation();
|
||||
|
||||
//SpawnMobs();
|
||||
}
|
||||
|
||||
void APatrolSpawn::EndPlay(const EEndPlayReason::Type EndPlayReason)
|
||||
{
|
||||
Super::EndPlay(EndPlayReason);
|
||||
|
||||
}
|
||||
|
||||
void APatrolSpawn::Tick(float DeltaTime)
|
||||
{
|
||||
Super::Tick(DeltaTime);
|
||||
|
||||
|
||||
|
||||
if (Role != ROLE_Authority)
|
||||
return;
|
||||
|
||||
UWorld* const world = GetWorld();
|
||||
if (!world) return;
|
||||
|
||||
check(m_mobs.Num() == spawns.Num());
|
||||
|
||||
m_respawnTimer = m_respawnTimer > 0 ? (m_respawnTimer - DeltaTime) : 0;
|
||||
if (m_respawnTimer <= 0 && m_mobCount != m_mobs.Num() && spawnContinuous)
|
||||
m_RespawnMobs();
|
||||
if (m_mobs.Num() == 0)
|
||||
return;
|
||||
// Respawn the mobs
|
||||
/* if (spawnContinuous)
|
||||
m_RespawnMobs();*/
|
||||
if (m_controlPoints.Num() <= 0)
|
||||
{
|
||||
RWARNING("There are no control points");
|
||||
return;
|
||||
}
|
||||
|
||||
bool anyIdle = false;
|
||||
for (int32 i = 0; i < spawns.Num(); i++)
|
||||
{
|
||||
if (!spawns[i])
|
||||
continue;
|
||||
|
||||
if (i == 0 && m_mobs[i] != nullptr)
|
||||
{
|
||||
if (m_isPulled == false)
|
||||
{
|
||||
RPRINT("testing");
|
||||
m_lastPosition = m_mobs[i]->GetActorLocation();
|
||||
}
|
||||
else if (!m_mobs[i]->target)
|
||||
{
|
||||
const FVector2D mobPos = FVector2D(m_mobs[i]->GetActorLocation().X, m_mobs[i]->GetActorLocation().Y);
|
||||
const float distSqr = FVector2D::DistSquared(mobPos, FVector2D(m_lastPosition.X, m_lastPosition.Y));
|
||||
if (distSqr < (m_mobs[i]->collisionRadius*m_mobs[i]->collisionRadius)*collisionScaler)
|
||||
m_isPulled = false;
|
||||
}
|
||||
|
||||
|
||||
// Any of the mobs not doing anything?
|
||||
const FVector2D mobPos = FVector2D(m_mobs[i]->GetActorLocation().X, m_mobs[i]->GetActorLocation().Y);
|
||||
const float distSqr = FVector2D::DistSquared(mobPos, FVector2D(m_lastPosition.X, m_lastPosition.Y));
|
||||
|
||||
////// Regeneration (hardcoded, remove in future)
|
||||
if (!m_mobs[i]->target && distSqr < aggroRadius*aggroRadius)
|
||||
{
|
||||
anyIdle = true;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (m_mobs[0] != nullptr && m_isPulled)
|
||||
{
|
||||
m_mobs[0]->SetControlPoint(m_lastPosition);
|
||||
for (int i = 1; i < m_mobs.Num(); i++)
|
||||
{
|
||||
m_mobs[i]->SetControlPoint(m_lastPosition);
|
||||
}
|
||||
}
|
||||
ADefaultGameMode* const mode = Cast<ADefaultGameMode>(world->GetAuthGameMode());
|
||||
if (mode)
|
||||
{
|
||||
FVector controlLocation = GetActorLocation();
|
||||
// check if there are some controll points
|
||||
if (m_mobs[0] != nullptr && !m_isPulled)
|
||||
{
|
||||
if (FVector2D::DistSquared(FVector2D(m_mobs[0]->GetActorLocation().X, m_mobs[0]->GetActorLocation().Y), FVector2D(m_controlPoints[m_currentControlPoint].X, m_controlPoints[m_currentControlPoint].Y)) < 10000)
|
||||
{
|
||||
m_currentControlPoint += m_controlPointSign;
|
||||
if (m_currentControlPoint >= m_controlPoints.Num() || m_currentControlPoint < 0)
|
||||
{
|
||||
m_controlPointSign *= -1;
|
||||
m_currentControlPoint += m_controlPointSign;
|
||||
if (isConnected)
|
||||
{
|
||||
m_currentControlPoint = 0;
|
||||
m_controlPointSign *= -1;
|
||||
}
|
||||
}
|
||||
}
|
||||
// set new control location
|
||||
controlLocation = m_controlPoints[m_currentControlPoint];
|
||||
|
||||
m_mobs[0]->SetControlPoint(controlLocation);
|
||||
if (m_mobs.Num() > 1)
|
||||
{
|
||||
|
||||
for (int i = 1; i < m_mobs.Num(); i++)
|
||||
{
|
||||
m_mobs[i]->SetControlPoint(m_lastPosition);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (anyIdle && !m_isPulled)
|
||||
{
|
||||
/*// Find the closest player (in aggroRadius)
|
||||
ANetworkCharacter* closest = nullptr;
|
||||
float closestDist = BIG_NUMBER;
|
||||
TArray<ANetworkCharacter*> players = mode->GetPlayers();
|
||||
const float aggroSqr = aggroRadius * aggroRadius;
|
||||
for (int32 i = 0; i < players.Num(); i++)
|
||||
{
|
||||
const float distSqr = FVector::DistSquared(m_mobs[0]->GetActorLocation(), players[i]->GetActorLocation());
|
||||
if (distSqr < closestDist && distSqr <= aggroSqr)
|
||||
{
|
||||
closest = players[i];
|
||||
closestDist = distSqr;
|
||||
}
|
||||
}*/
|
||||
ANetworkPlayer* closest = m_GetClosestPlayer();
|
||||
if (closest)
|
||||
{
|
||||
// Attack the closest player
|
||||
for (int32 i = 0; i < spawns.Num(); i++)
|
||||
{
|
||||
if (m_mobs[i] == nullptr)
|
||||
continue;
|
||||
|
||||
m_mobs[i]->target = (closest);
|
||||
m_isPulled = true;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
FVector APatrolSpawn::SpawnResetPosition()
|
||||
{
|
||||
return m_lastPosition;
|
||||
}
|
||||
|
||||
void APatrolSpawn::SpawnMobs()
|
||||
{
|
||||
m_RespawnMobs();
|
||||
}
|
||||
void APatrolSpawn::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();
|
||||
if (hasGeneral)
|
||||
{
|
||||
spawnTransform.SetLocation(FVector(formationPoints[i].X, formationPoints[i].Y,0) + GetActorLocation());
|
||||
}
|
||||
else
|
||||
{
|
||||
spawnTransform.SetLocation(GetActorLocation() + FVector(0, 0, 120));
|
||||
}
|
||||
spawnTransform.SetRotation(FRotator(GetActorRotation() + FRotator(0, formationRotation[i], 0)).Quaternion());
|
||||
ANPCBase* character = world->SpawnActorDeferred<ANPCBase>(spawns[i], spawnTransform, nullptr, nullptr, ESpawnActorCollisionHandlingMethod());
|
||||
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;
|
||||
}
|
||||
class ANetworkPlayer* APatrolSpawn::m_GetClosestPlayer()
|
||||
{
|
||||
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;
|
||||
TArray<ANetworkPlayer*> players = mode->GetPlayers();
|
||||
const float aggroSqr = aggroRadius * aggroRadius;
|
||||
for (int32 i = 0; i < players.Num(); i++)
|
||||
{
|
||||
const float distSqr = FVector2D::DistSquared(FVector2D(m_mobs[0]->GetActorLocation()), FVector2D(players[i]->GetActorLocation()));
|
||||
if (distSqr < closestDist && distSqr <= aggroSqr)
|
||||
{
|
||||
closest = players[i];
|
||||
closestDist = distSqr;
|
||||
}
|
||||
}
|
||||
|
||||
return closest;
|
||||
}
|
||||
39
Source/UnrealProject/Spawners/PatrolSpawn.h
Normal file
39
Source/UnrealProject/Spawners/PatrolSpawn.h
Normal file
@@ -0,0 +1,39 @@
|
||||
// Project Lab - NHTV Igad
|
||||
|
||||
#pragma once
|
||||
|
||||
#include "SpawnerBase.h"
|
||||
#include "PatrolSpawn.generated.h"
|
||||
|
||||
/**
|
||||
*
|
||||
*/
|
||||
UCLASS()
|
||||
class UNREALPROJECT_API APatrolSpawn : public ASpawnerBase
|
||||
{
|
||||
GENERATED_BODY()
|
||||
|
||||
public:
|
||||
APatrolSpawn();
|
||||
virtual void BeginPlay() override;
|
||||
virtual void EndPlay(const EEndPlayReason::Type EndPlayReason) override;
|
||||
virtual void Tick(float DeltaTime) override;
|
||||
|
||||
virtual FVector SpawnResetPosition() override;
|
||||
UPROPERTY(EditAnywhere)
|
||||
float respawnTime;
|
||||
UPROPERTY(EditAnywhere, Category = "Gamestate Components")
|
||||
bool spawnContinuous;
|
||||
void SpawnMobs();
|
||||
|
||||
private:
|
||||
float m_respawnTimer;
|
||||
TArray<FVector> m_controlPoints;
|
||||
int32 m_controlPointSign;
|
||||
int32 m_currentControlPoint;
|
||||
TArray<float> m_respawnTimers;
|
||||
FVector m_lastPosition;
|
||||
bool m_isPulled;
|
||||
void m_RespawnMobs();
|
||||
class ANetworkPlayer* m_GetClosestPlayer();
|
||||
};
|
||||
26
Source/UnrealProject/Spawners/PlayerSpawn.cpp
Normal file
26
Source/UnrealProject/Spawners/PlayerSpawn.cpp
Normal file
@@ -0,0 +1,26 @@
|
||||
// Project Lab - NHTV Igad
|
||||
|
||||
#include "UnrealProject.h"
|
||||
#include "DefaultGameMode.h"
|
||||
#include "PlayerSpawn.h"
|
||||
|
||||
|
||||
APlayerSpawn::APlayerSpawn()
|
||||
{
|
||||
displayMesh = CreateDefaultSubobject<UStaticMeshComponent>(TEXT("Mesh"));
|
||||
displayMesh->bHiddenInGame = true;
|
||||
displayMesh->bGenerateOverlapEvents = false;
|
||||
displayMesh->SetCollisionEnabled(ECollisionEnabled::NoCollision);
|
||||
displayArrow = CreateDefaultSubobject<UArrowComponent>(TEXT("Arrow"));
|
||||
assignedTeam = 0;
|
||||
}
|
||||
|
||||
void APlayerSpawn::BeginPlay()
|
||||
{
|
||||
if (Role != ROLE_Authority)
|
||||
return;
|
||||
|
||||
ADefaultGameMode* mode = Cast<ADefaultGameMode>(GetWorld()->GetAuthGameMode());
|
||||
if (mode)
|
||||
mode->RegisterPlayerSpawn(*this);
|
||||
}
|
||||
25
Source/UnrealProject/Spawners/PlayerSpawn.h
Normal file
25
Source/UnrealProject/Spawners/PlayerSpawn.h
Normal file
@@ -0,0 +1,25 @@
|
||||
// Project Lab - NHTV Igad
|
||||
|
||||
#pragma once
|
||||
|
||||
#include "GameFramework/Actor.h"
|
||||
#include "PlayerSpawn.generated.h"
|
||||
|
||||
UCLASS()
|
||||
class UNREALPROJECT_API APlayerSpawn : public AActor
|
||||
{
|
||||
GENERATED_BODY()
|
||||
|
||||
public:
|
||||
APlayerSpawn();
|
||||
virtual void BeginPlay() override;
|
||||
|
||||
UPROPERTY(EditAnywhere, Category = "Gamestate Components")
|
||||
int32 assignedTeam;
|
||||
|
||||
UPROPERTY(VisibleAnywhere, BlueprintReadOnly, Category = Display)
|
||||
class UStaticMeshComponent* displayMesh;
|
||||
UPROPERTY(VisibleAnywhere, BlueprintReadOnly, Category = Display)
|
||||
class UArrowComponent* displayArrow;
|
||||
|
||||
};
|
||||
476
Source/UnrealProject/Spawners/SpawnerBase.cpp
Normal file
476
Source/UnrealProject/Spawners/SpawnerBase.cpp
Normal file
@@ -0,0 +1,476 @@
|
||||
// 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);
|
||||
}
|
||||
126
Source/UnrealProject/Spawners/SpawnerBase.h
Normal file
126
Source/UnrealProject/Spawners/SpawnerBase.h
Normal file
@@ -0,0 +1,126 @@
|
||||
// Project Lab - NHTV Igad
|
||||
|
||||
#pragma once
|
||||
|
||||
#include "GameFramework/Actor.h"
|
||||
#include <unordered_set>
|
||||
#include <map>
|
||||
using std::unordered_set;
|
||||
using std::map;
|
||||
#include "SpawnerBase.generated.h"
|
||||
|
||||
UENUM(BlueprintType)
|
||||
enum class NPCTeam : uint8
|
||||
{
|
||||
Team1 = 50,
|
||||
};
|
||||
|
||||
UCLASS()
|
||||
class ASpawnerBase : public AActor
|
||||
{
|
||||
GENERATED_BODY()
|
||||
|
||||
public:
|
||||
ASpawnerBase();
|
||||
|
||||
virtual void BeginPlay() override;
|
||||
virtual void EndPlay(const EEndPlayReason::Type EndPlayReason) override;
|
||||
virtual void Tick(float DeltaTime) override;
|
||||
virtual void GetNewTarget(class ANPCBase* mob);
|
||||
UFUNCTION(BlueprintCallable, Category="Capture")
|
||||
virtual void CaptureCamp(int team);
|
||||
void ForceSetTarget(class ANPCBase* mob,class ANetworkPlayer* target);
|
||||
void ForceResetTarget(class ANPCBase* mob);
|
||||
void SetTeam(int team);
|
||||
class ANetworkPlayer* GetClosestPlayer();
|
||||
// Call when the camp is killed entirely
|
||||
UFUNCTION(NetMulticast, Reliable)
|
||||
void OnCampCleared();
|
||||
|
||||
void AddThreat(class ANPCBase* creature, class ANetworkPlayer* character, int32 threat);
|
||||
|
||||
virtual int32 OnMobDie(class ANPCBase* mob);
|
||||
|
||||
virtual FVector SpawnResetPosition();
|
||||
|
||||
int32 GetAliveMobCount();
|
||||
|
||||
// m_nearbyPlayers but as ANetworkPlayer array instead of ANetworkCharacter's
|
||||
TArray<class ANetworkPlayer*> GetNearbyPlayers();
|
||||
|
||||
UPROPERTY(EditAnywhere)
|
||||
float aggroRadius;
|
||||
UPROPERTY(EditAnywhere)
|
||||
float deaggroRadius;
|
||||
UPROPERTY(EditAnywhere, BlueprintReadWrite, Category = "FMod")
|
||||
float threatLevel;
|
||||
|
||||
UPROPERTY(EditAnywhere, Replicated)
|
||||
NPCTeam team;
|
||||
|
||||
UPROPERTY(EditAnywhere)
|
||||
FColor debugColorCode;
|
||||
|
||||
UPROPERTY(VisibleAnywhere, BlueprintReadOnly, Category = Display)
|
||||
class UStaticMeshComponent* displayMesh;
|
||||
UPROPERTY(VisibleAnywhere, BlueprintReadOnly, Category = Display)
|
||||
class UArrowComponent* displayArrow;
|
||||
UPROPERTY(VisibleAnywhere, BlueprintReadOnly, Category = Display)
|
||||
class UNetworkSwitchComponent* displayDoors;
|
||||
|
||||
UPROPERTY(VisibleAnywhere, BlueprintReadOnly, Category="Kill Particle")
|
||||
UParticleSystemComponent* killParticle;
|
||||
|
||||
UPROPERTY(EditAnywhere, BlueprintReadWrite)
|
||||
TArray<TSubclassOf<class ANPCBase>> spawns;
|
||||
UPROPERTY(EditAnywhere, Category = "Spawn Settings")
|
||||
float drawingRadius;
|
||||
UPROPERTY(EditAnywhere, Category = "Spawn Settings")
|
||||
float collisionScaler;
|
||||
UPROPERTY(EditAnywhere, Category = "Spawn Settings")
|
||||
float resetTimer;
|
||||
UPROPERTY(EditAnywhere, Category = "Spawn Settings")
|
||||
bool possesable;
|
||||
UPROPERTY(EditAnywhere, Category = "Spawn Settings")
|
||||
TArray<AActor*> SpawnLocation;
|
||||
|
||||
bool isConnected;
|
||||
|
||||
UPROPERTY(EditAnywhere, Category = "Switch Components")
|
||||
class UCreatureSpawnComponent* visualizerComponent;
|
||||
UPROPERTY(EditAnywhere, Category = "ControlPoints Settings")
|
||||
TArray<FVector2D> controlPoints;
|
||||
UPROPERTY(EditAnywhere, Category = "Formation Settings")
|
||||
float formationDistance;
|
||||
UPROPERTY(EditAnywhere, Category = "Formation Settings")
|
||||
float formationRadius;
|
||||
TArray<FVector2D> formationPoints;
|
||||
|
||||
TArray<float> formationRotation;
|
||||
UPROPERTY(EditAnywhere, Category = "Formation Settings")
|
||||
float formationScale;
|
||||
TArray<class AEnemyBase*> formationEnemies;
|
||||
bool hasGeneral;
|
||||
|
||||
UPROPERTY(EditAnywhere, Category = "Miniboss")
|
||||
int32 dropKeyFragmentIndex;
|
||||
bool hasDroppedKey;
|
||||
TArray<class ANPCBase*> m_mobs;
|
||||
protected:
|
||||
virtual void m_OnCampCleared() {};
|
||||
virtual void m_OnPlayerEnterOverlap(class ANetworkCharacter& player);
|
||||
virtual void m_OnPlayerExitOverlap(class ANetworkCharacter& player);
|
||||
class ANetworkPlayer* m_GetBiggestThreat(int32 index);
|
||||
|
||||
|
||||
|
||||
void m_OnMobSpawn(int32 index);
|
||||
|
||||
int32 m_mobCount;
|
||||
|
||||
unordered_set<class ANetworkCharacter*> m_nearbyPlayers;
|
||||
TArray<map<class ANetworkPlayer*, int32>> m_threatMap;
|
||||
float m_resetTimer;
|
||||
//float formationDistance = 0;
|
||||
//float formationRadius = 0;
|
||||
};
|
||||
Reference in New Issue
Block a user