HAxis sos
This commit is contained in:
165
Source/UnrealProject/Abilities/AbilityEventGroup.cpp
Normal file
165
Source/UnrealProject/Abilities/AbilityEventGroup.cpp
Normal file
@@ -0,0 +1,165 @@
|
||||
// Project Lab - NHTV Igad
|
||||
|
||||
#include "UnrealProject.h"
|
||||
#include "AbilityEventGroup.h"
|
||||
#include "NetworkCharacter.h"
|
||||
#include "NPCBase.h"
|
||||
|
||||
AAbilityEventGroup::AAbilityEventGroup()
|
||||
{
|
||||
// Set this actor to call Tick() every frame. You can turn this off to improve performance if you don't need it.
|
||||
PrimaryActorTick.bCanEverTick = true;
|
||||
channelEvent = false;
|
||||
stunWhileChannel = true;
|
||||
progressionType = EAbilityEventGroupProgressionType::Continue;
|
||||
duration = 1.0f;
|
||||
m_endEventSend = false;
|
||||
m_hasBeenDestroyed = false;
|
||||
allowRotateWhileChannel = false;
|
||||
}
|
||||
|
||||
void AAbilityEventGroup::BeginPlay()
|
||||
{
|
||||
check(Role == ROLE_Authority);
|
||||
Super::BeginPlay();
|
||||
m_life = 0.0f;
|
||||
|
||||
if(!character || character->IsActorBeingDestroyed())
|
||||
{
|
||||
m_MoveToNextGroup();
|
||||
}
|
||||
else
|
||||
{
|
||||
// Handle the destruction of the caster
|
||||
character->OnDestroyed.AddDynamic(this, &AAbilityEventGroup::m_OnCharacterDestroyed);
|
||||
}
|
||||
}
|
||||
void AAbilityEventGroup::EndPlay(const EEndPlayReason::Type EndPlayReason)
|
||||
{
|
||||
check(Role == ROLE_Authority);
|
||||
if(EndPlayReason == EEndPlayReason::Destroyed)
|
||||
{
|
||||
Super::EndPlay(EndPlayReason);
|
||||
AAbilityEventGroup* nextGroup = nullptr;
|
||||
if(m_nextGroups.Num() > 0)
|
||||
{
|
||||
//GWPRINT(L"Move next");
|
||||
nextGroup = SpawnSequence(character, abilityInfo, abilityState, m_nextGroups);
|
||||
}
|
||||
m_SendEndEvent(nextGroup);
|
||||
}
|
||||
}
|
||||
void AAbilityEventGroup::Tick(float DeltaTime)
|
||||
{
|
||||
Super::Tick(DeltaTime);
|
||||
|
||||
if(IsPendingKill())
|
||||
return;
|
||||
|
||||
m_life += DeltaTime;
|
||||
if(progressionType == EAbilityEventGroupProgressionType::Continue)
|
||||
{
|
||||
if(m_life >= duration || m_continue)
|
||||
m_MoveToNextGroup();
|
||||
}
|
||||
else
|
||||
{
|
||||
if(m_continue)
|
||||
m_MoveToNextGroup();
|
||||
}
|
||||
}
|
||||
|
||||
void AAbilityEventGroup::m_OnCharacterDestroyed()
|
||||
{
|
||||
m_MoveToNextGroup();
|
||||
}
|
||||
void AAbilityEventGroup::m_MoveToNextGroup()
|
||||
{
|
||||
if(m_hasBeenDestroyed)
|
||||
return;
|
||||
m_hasBeenDestroyed = true;
|
||||
if(IsValid(this) && !IsPendingKillPending())
|
||||
Destroy();
|
||||
}
|
||||
|
||||
void AAbilityEventGroup::m_SendEndEvent(AAbilityEventGroup* next)
|
||||
{
|
||||
if(!m_endEventSend)
|
||||
{
|
||||
onAbilityEventGroupEnded.Broadcast(abilityInfo, this, next);
|
||||
m_endEventSend = true;
|
||||
}
|
||||
}
|
||||
|
||||
void AAbilityEventGroup::Interrupt()
|
||||
{
|
||||
//GPRINT("Interrupting ability");
|
||||
if(IsPendingKill())
|
||||
return;
|
||||
m_nextGroups.Empty(); // Don't execute any follow up events
|
||||
m_hasBeenDestroyed = true;
|
||||
}
|
||||
|
||||
void AAbilityEventGroup::TransitionTo(class AAbilityEventGroup* newGroup)
|
||||
{
|
||||
check(newGroup);
|
||||
check(abilityInfo);
|
||||
check(abilityState);
|
||||
newGroup->character = character;
|
||||
newGroup->abilityInfo = abilityInfo;
|
||||
newGroup->abilityState = abilityState;
|
||||
newGroup->m_nextGroups = m_nextGroups;
|
||||
m_nextGroups.Empty();
|
||||
|
||||
m_SendEndEvent(newGroup);
|
||||
|
||||
//GWPRINT(L"TransitionTo next");
|
||||
m_MoveToNextGroup();
|
||||
}
|
||||
|
||||
void AAbilityEventGroup::NextGroup()
|
||||
{
|
||||
m_continue = true;
|
||||
}
|
||||
|
||||
ANetworkCharacter* AAbilityEventGroup::GetTarget()
|
||||
{
|
||||
ANPCBase* creature = Cast<ANPCBase>(character);
|
||||
if(creature)
|
||||
{
|
||||
return creature->target;
|
||||
}
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
float AAbilityEventGroup::GetLifetimeRate() const
|
||||
{
|
||||
float rate = m_life / duration;
|
||||
return FMath::Fmod(rate, 1.0f + FLT_EPSILON);
|
||||
}
|
||||
float AAbilityEventGroup::GetLifetime() const
|
||||
{
|
||||
return m_life;
|
||||
}
|
||||
|
||||
AAbilityEventGroup* AAbilityEventGroup::SpawnSequence(ANetworkCharacter* character, class UAbilityInfo* abilityInfo, class AAbilityState* abilityState, TArray<TSubclassOf<class AAbilityEventGroup>> groups)
|
||||
{
|
||||
UWorld* world = character->GetWorld();
|
||||
check(world);
|
||||
check(groups.Num() > 0);
|
||||
|
||||
AAbilityEventGroup* group = world->SpawnActorDeferred<AAbilityEventGroup>(groups[0], FTransform::Identity);
|
||||
check(group);
|
||||
check(abilityInfo);
|
||||
check(abilityState);
|
||||
|
||||
group->character = character;
|
||||
group->abilityState = abilityState;
|
||||
group->abilityInfo = abilityInfo;
|
||||
groups.RemoveAt(0);
|
||||
group->m_nextGroups = groups;
|
||||
|
||||
UGameplayStatics::FinishSpawningActor(group, FTransform::Identity);
|
||||
|
||||
return group;
|
||||
}
|
||||
85
Source/UnrealProject/Abilities/AbilityEventGroup.h
Normal file
85
Source/UnrealProject/Abilities/AbilityEventGroup.h
Normal file
@@ -0,0 +1,85 @@
|
||||
// Project Lab - NHTV Igad
|
||||
|
||||
#pragma once
|
||||
|
||||
#include "DealDamageProxy.h"
|
||||
#include "AbilityEventGroup.generated.h"
|
||||
|
||||
UENUM(BlueprintType)
|
||||
enum class EAbilityEventGroupProgressionType : uint8
|
||||
{
|
||||
// Continues when the duration of the group has expired
|
||||
Continue,
|
||||
// Loops until the ability itself decides to continue, or is activated again
|
||||
Loop
|
||||
};
|
||||
|
||||
|
||||
UCLASS()
|
||||
class UNREALPROJECT_API AAbilityEventGroup : public ADealDamageProxy
|
||||
{
|
||||
GENERATED_BODY()
|
||||
|
||||
public:
|
||||
AAbilityEventGroup();
|
||||
|
||||
virtual void BeginPlay() override final;
|
||||
virtual void EndPlay(const EEndPlayReason::Type EndPlayReason);
|
||||
virtual void Tick(float DeltaSeconds) override final;
|
||||
|
||||
// Used to construct an ability group sequence from an array of ability groups
|
||||
static AAbilityEventGroup* SpawnSequence(class ANetworkCharacter* character, class UAbilityInfo* abilityInfo, class AAbilityState* abilityState, TArray<TSubclassOf<class AAbilityEventGroup>> groups);
|
||||
|
||||
// Try to interrupt this group chain if it's a channelEvent
|
||||
void Interrupt();
|
||||
|
||||
// Changes the current group to execute this group now
|
||||
void TransitionTo(class AAbilityEventGroup* newGroup);
|
||||
|
||||
// Try to continue this group if it's progression type is not duration bound
|
||||
UFUNCTION(BlueprintCallable, Category = "Ability")
|
||||
void NextGroup();
|
||||
|
||||
// Value from 0 to 1 representing the life cycle of this event, loops back to 0 if the event is looping after the life exceeded the duration set in the group
|
||||
UFUNCTION(BlueprintCallable, Category = "Ability Group")
|
||||
float GetLifetimeRate() const;
|
||||
UFUNCTION(BlueprintCallable, Category = "Ability Group")
|
||||
float GetLifetime() const;
|
||||
|
||||
UFUNCTION(BlueprintCallable, Category = "Ability")
|
||||
class ANetworkCharacter* GetTarget();
|
||||
|
||||
UPROPERTY(BlueprintReadOnly, Category = "Ability Group")
|
||||
class AAbilityState* abilityState;
|
||||
UPROPERTY(BlueprintReadOnly, EditDefaultsOnly, Category = "Ability Group")
|
||||
float duration;
|
||||
UPROPERTY(EditDefaultsOnly, Category = "Ability Group")
|
||||
bool channelEvent;
|
||||
UPROPERTY(EditDefaultsOnly, Category = "Ability Group")
|
||||
bool stunWhileChannel;
|
||||
UPROPERTY(EditDefaultsOnly, Category = "Ability Group")
|
||||
bool allowRotateWhileChannel;
|
||||
|
||||
// Setting this to true makes the ability
|
||||
UPROPERTY(EditDefaultsOnly, Category = "Ability Group")
|
||||
EAbilityEventGroupProgressionType progressionType;
|
||||
|
||||
// Called when the group's duration has expired or NextGroup is called
|
||||
DECLARE_DYNAMIC_MULTICAST_DELEGATE_ThreeParams(FOnAbilityEventGroupEnded, class UAbilityInfo*, ability, class AAbilityEventGroup*, current, class AAbilityEventGroup*, next);
|
||||
FOnAbilityEventGroupEnded onAbilityEventGroupEnded;
|
||||
|
||||
private:
|
||||
UFUNCTION()
|
||||
void m_OnCharacterDestroyed();
|
||||
void m_MoveToNextGroup();
|
||||
void m_SendEndEvent(AAbilityEventGroup* next = nullptr);
|
||||
|
||||
float m_life;
|
||||
|
||||
UPROPERTY()
|
||||
TArray<TSubclassOf<AAbilityEventGroup>> m_nextGroups;
|
||||
|
||||
bool m_hasBeenDestroyed;
|
||||
bool m_endEventSend;
|
||||
bool m_continue;
|
||||
};
|
||||
17
Source/UnrealProject/Abilities/AbilityFilter.h
Normal file
17
Source/UnrealProject/Abilities/AbilityFilter.h
Normal file
@@ -0,0 +1,17 @@
|
||||
// Project Lab - NHTV Igad
|
||||
|
||||
#pragma once
|
||||
#include "AbilityFilter.generated.h"
|
||||
|
||||
#define ABILITY_FILTER_ENEMY 0x1
|
||||
#define ABILITY_FILTER_ALLY 0x2
|
||||
#define ABILITY_FILTER_SELF 0x10
|
||||
|
||||
UENUM(BlueprintType)
|
||||
enum class EAbilityFilter : uint8
|
||||
{
|
||||
EnemyAll = 0x1,
|
||||
AllyAll = 0x2,
|
||||
All = 0x3,
|
||||
Self = 0x10,
|
||||
};
|
||||
56
Source/UnrealProject/Abilities/AbilityInfo.cpp
Normal file
56
Source/UnrealProject/Abilities/AbilityInfo.cpp
Normal file
@@ -0,0 +1,56 @@
|
||||
// Project Lab - NHTV Igad
|
||||
|
||||
#include "UnrealProject.h"
|
||||
#include "AbilityInfo.h"
|
||||
#include "AbilityState.h"
|
||||
|
||||
namespace
|
||||
{
|
||||
inline uint32 Hash32(const void* data, uint32 length)
|
||||
{
|
||||
const uint32 offset = 2166136261U;
|
||||
const uint32 prime = 16777619U;
|
||||
const unsigned char* data_bytes = (const unsigned char*)data;
|
||||
|
||||
uint32 value = offset;
|
||||
for (uint32 next = 0; next < length; ++next)
|
||||
{
|
||||
value ^= (uint32)data_bytes[next];
|
||||
value *= prime;
|
||||
}
|
||||
return value;
|
||||
}
|
||||
}
|
||||
|
||||
UAbilityInfo::UAbilityInfo()
|
||||
{
|
||||
name = L"<Designers!: Add Name>";
|
||||
description = L"<Designers!: Add Description>";
|
||||
cooldown = 1.0f;
|
||||
AICastRange = 0.0f;
|
||||
rotateTowardsPlayer = false;
|
||||
abilityState = AAbilityState::StaticClass();
|
||||
|
||||
FString strName = GetPathName();
|
||||
m_hash = Hash32(strName.GetCharArray().GetData(), strName.Len() * sizeof(wchar_t));
|
||||
}
|
||||
|
||||
uint32 UAbilityInfo::GetStaticHash() const
|
||||
{
|
||||
return m_hash;
|
||||
}
|
||||
|
||||
void AAbilityState::NativeSetCooldown()
|
||||
{
|
||||
SetCooldown();
|
||||
cooldownRate = (currentCooldownDuration > 0.0f) ? (onCooldownTimer / currentCooldownDuration) : 0.0f;
|
||||
}
|
||||
void AAbilityState::SetCooldown_Implementation()
|
||||
{
|
||||
currentCooldownDuration = info->cooldown;
|
||||
onCooldownTimer = currentCooldownDuration;
|
||||
}
|
||||
bool AAbilityState::IsMaxed() const
|
||||
{
|
||||
return power >= 1.0f;
|
||||
}
|
||||
96
Source/UnrealProject/Abilities/AbilityInfo.h
Normal file
96
Source/UnrealProject/Abilities/AbilityInfo.h
Normal file
@@ -0,0 +1,96 @@
|
||||
// Project Lab - NHTV Igad
|
||||
|
||||
#pragma once
|
||||
|
||||
#include "Engine/DataAsset.h"
|
||||
#include "Items/ItemBase.h"
|
||||
#include "AbilityInfo.generated.h"
|
||||
|
||||
UENUM(BlueprintType)
|
||||
enum class EAbilityCategory : uint8
|
||||
{
|
||||
Unassigned = 0,
|
||||
Ranged,
|
||||
Melee
|
||||
};
|
||||
UENUM(BlueprintType)
|
||||
enum class EAbilityType : uint8
|
||||
{
|
||||
Basic,
|
||||
Ability
|
||||
};
|
||||
UENUM(BlueprintType)
|
||||
enum class EAbilityActionType : uint8
|
||||
{
|
||||
Normal,
|
||||
Toggle,
|
||||
Hold
|
||||
};
|
||||
|
||||
|
||||
USTRUCT()
|
||||
struct FAbilityItem
|
||||
{
|
||||
GENERATED_BODY()
|
||||
|
||||
UPROPERTY(EditAnywhere)
|
||||
USkeletalMesh* mesh;
|
||||
UPROPERTY(EditAnywhere)
|
||||
EItemTypeEnum type;
|
||||
};
|
||||
|
||||
UCLASS(BlueprintType)
|
||||
class UNREALPROJECT_API UAbilityInfo : public UDataAsset
|
||||
{
|
||||
GENERATED_BODY()
|
||||
public:
|
||||
UAbilityInfo();
|
||||
|
||||
UPROPERTY(EditDefaultsOnly, BlueprintReadOnly, Category = "Ability")
|
||||
FString name;
|
||||
UPROPERTY(EditDefaultsOnly, BlueprintReadOnly, Category = "Ability")
|
||||
FString description;
|
||||
UPROPERTY(EditDefaultsOnly, BlueprintReadOnly, Category = "Ability")
|
||||
float cooldown;
|
||||
UPROPERTY(EditDefaultsOnly)
|
||||
TArray<TSubclassOf<class AAbilityEventGroup>> events;
|
||||
UPROPERTY(EditDefaultsOnly)
|
||||
TSubclassOf<class APreCastAbilityEventGroup> precastEvent;
|
||||
UPROPERTY(EditDefaultsOnly)
|
||||
TSubclassOf<class AAbilityState> abilityState;
|
||||
UPROPERTY(EditDefaultsOnly, BlueprintReadOnly, Category = "Ability")
|
||||
UTexture2D* icon;
|
||||
UPROPERTY(EditDefaultsOnly, BlueprintReadOnly, Category = "Ability")
|
||||
float AICastRange;
|
||||
UPROPERTY(EditDefaultsOnly, BlueprintReadOnly, Category = "Ability")
|
||||
bool passive;
|
||||
UPROPERTY(EditDefaultsOnly, BlueprintReadOnly, Category = "Ability")
|
||||
bool rotateTowardsPlayer;
|
||||
UPROPERTY(EditDefaultsOnly, BlueprintReadOnly, Category = "Ability")
|
||||
int32 mana;
|
||||
|
||||
// The action type of the ability, Toggle, Hold or Normal
|
||||
UPROPERTY(EditDefaultsOnly, BlueprintReadOnly, Category = "Ability")
|
||||
EAbilityActionType actionType;
|
||||
// The type of the ability Ability(On AbilityBar) or Basic(Left Mouse Click).
|
||||
UPROPERTY(EditDefaultsOnly, BlueprintReadOnly, Category = "Ability")
|
||||
EAbilityType abilityType;
|
||||
// If the ability is Ranged or Melee
|
||||
UPROPERTY(EditDefaultsOnly, BlueprintReadOnly, Category = "Ability")
|
||||
EAbilityCategory abilityCategory;
|
||||
|
||||
UPROPERTY(EditAnywhere)
|
||||
bool isVisible;
|
||||
UPROPERTY(EditAnywhere, meta = (EditCondition = "isVisible"))
|
||||
TArray<FAbilityItem> itemsToEquip;
|
||||
|
||||
bool IsHoldOrToggle() const
|
||||
{
|
||||
return actionType == EAbilityActionType::Hold || actionType == EAbilityActionType::Toggle;
|
||||
}
|
||||
|
||||
uint32 GetStaticHash() const;
|
||||
|
||||
private:
|
||||
uint32 m_hash;
|
||||
};
|
||||
37
Source/UnrealProject/Abilities/AbilityState.h
Normal file
37
Source/UnrealProject/Abilities/AbilityState.h
Normal file
@@ -0,0 +1,37 @@
|
||||
// Project Lab - NHTV Igad
|
||||
|
||||
#pragma once
|
||||
#include "AbilityState.generated.h"
|
||||
|
||||
UCLASS()
|
||||
class AAbilityState : public AActor
|
||||
{
|
||||
GENERATED_BODY()
|
||||
public:
|
||||
// Sets the ability to be on cooldown
|
||||
virtual void NativeSetCooldown();
|
||||
UFUNCTION(BlueprintNativeEvent, Category = "Ability State")
|
||||
void SetCooldown();
|
||||
|
||||
UFUNCTION(BlueprintCallable, Category = "Ability State")
|
||||
bool IsMaxed() const;
|
||||
|
||||
// Total duration of cooldown
|
||||
UPROPERTY(BlueprintReadWrite)
|
||||
float currentCooldownDuration;
|
||||
|
||||
// Current state of cooldown going from max->0
|
||||
UPROPERTY(BlueprintReadWrite)
|
||||
float onCooldownTimer;
|
||||
|
||||
// Current state of cooldown going from 1->0
|
||||
UPROPERTY(BlueprintReadOnly)
|
||||
float cooldownRate;
|
||||
|
||||
UPROPERTY(BlueprintReadOnly)
|
||||
float power;
|
||||
UPROPERTY(BlueprintReadOnly)
|
||||
bool toggleState;
|
||||
UPROPERTY(BlueprintReadOnly)
|
||||
class UAbilityInfo* info;
|
||||
};
|
||||
221
Source/UnrealProject/Abilities/AbilityTriggerBase.cpp
Normal file
221
Source/UnrealProject/Abilities/AbilityTriggerBase.cpp
Normal file
@@ -0,0 +1,221 @@
|
||||
// Project Lab - NHTV Igad
|
||||
|
||||
#include "UnrealProject.h"
|
||||
#include "NetworkCharacter.h"
|
||||
#include "AbilityTriggerBase.h"
|
||||
#include "BlueprintAbilityLibrary.h"
|
||||
|
||||
|
||||
AAbilityTriggerBase::AAbilityTriggerBase()
|
||||
{
|
||||
PrimaryActorTick.bCanEverTick = true;
|
||||
|
||||
filter = EAbilityFilter::EnemyAll;
|
||||
autoDestroy = true;
|
||||
bReplicateMovement = true;
|
||||
bReplicates = true;
|
||||
duration = 1.0f;
|
||||
ticksPerSecond = 0.0f;
|
||||
m_lifeTime = 0.0f;
|
||||
}
|
||||
|
||||
void AAbilityTriggerBase::BeginPlay()
|
||||
{
|
||||
if (Role == ROLE_Authority)
|
||||
{
|
||||
if (!character)
|
||||
{
|
||||
GWERROR(L"No owner assigned to trigger " + GetName());
|
||||
this->SetActorEnableCollision(false);
|
||||
Destroy();
|
||||
}
|
||||
if (ticksPerSecond > 0)
|
||||
{
|
||||
m_tickTime = 1.0f / ticksPerSecond;
|
||||
}
|
||||
else
|
||||
{
|
||||
m_tickTime = 0.0f;
|
||||
}
|
||||
}
|
||||
|
||||
//make sure the collision delegates are working
|
||||
UShapeComponent* root = Cast<UShapeComponent>(RootComponent);
|
||||
if (root != nullptr && !delegatesSet)
|
||||
{
|
||||
root->OnComponentBeginOverlap.AddDynamic(this, &AAbilityTriggerBase::OnOverlapBegin);
|
||||
root->OnComponentEndOverlap.AddDynamic(this, &AAbilityTriggerBase::OnOverlapEnd);
|
||||
delegatesSet = true;
|
||||
}
|
||||
else
|
||||
FWARNING("rootComponent is not a UShapeComponent");
|
||||
Super::BeginPlay();
|
||||
if (Role == ROLE_Authority)
|
||||
UpdateOverlaps();
|
||||
}
|
||||
|
||||
|
||||
void AAbilityTriggerBase::EndPlay(const EEndPlayReason::Type EndPlayReason)
|
||||
{
|
||||
if (Role == ROLE_Authority)
|
||||
{
|
||||
while (m_actorsInside.Num()>0)
|
||||
{
|
||||
if (IsValid(m_actorsInside.GetData()[0]))
|
||||
{
|
||||
LeaveEvent(Cast<ANetworkCharacter>(m_actorsInside.GetData()[0]));
|
||||
}
|
||||
}
|
||||
}
|
||||
Super::EndPlay(EndPlayReason);
|
||||
}
|
||||
|
||||
void AAbilityTriggerBase::GetLifetimeReplicatedProps(TArray<FLifetimeProperty>& OutLifetimeProps) const
|
||||
{
|
||||
Super::GetLifetimeReplicatedProps(OutLifetimeProps);
|
||||
DOREPLIFETIME_CONDITION(AAbilityTriggerBase, duration, COND_InitialOnly);
|
||||
}
|
||||
|
||||
void AAbilityTriggerBase::Tick(float DeltaTime)
|
||||
{
|
||||
m_lifeTime += DeltaTime;
|
||||
|
||||
Super::Tick(DeltaTime);
|
||||
|
||||
if(Role != ROLE_Authority || !IsValid(this))
|
||||
return;
|
||||
|
||||
UpdateOverlaps();
|
||||
if(autoDestroy && m_lifeTime >= duration)
|
||||
{
|
||||
Destroy();
|
||||
return;
|
||||
}
|
||||
|
||||
if(m_tickTime > 0)
|
||||
{
|
||||
m_tickTimer += DeltaTime;
|
||||
while(m_tickTimer > m_tickTime)
|
||||
{
|
||||
m_tickTimer -= m_tickTime;
|
||||
for(AActor* actorInside : m_actorsInside)
|
||||
{
|
||||
if (CollisionCheck(Cast<ANetworkCharacter>(actorInside)))
|
||||
{
|
||||
OnTriggerTick(Cast<ANetworkCharacter>(actorInside));
|
||||
NativeOnLocalTriggerTick(Cast<ANetworkCharacter>(actorInside));
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void AAbilityTriggerBase::OnOverlapBegin(class AActor* OtherActor, class UPrimitiveComponent* OtherComp, int32 OtherBodyIndex, bool bFromSweep, const FHitResult& SweepResult)
|
||||
{
|
||||
if(Role != ROLE_Authority)
|
||||
return;
|
||||
|
||||
if(OtherActor == NULL)
|
||||
{
|
||||
FWPRINT(L"Error - wrong otherunit on OnOverlapBegin ");
|
||||
return;
|
||||
}
|
||||
if(character == NULL)
|
||||
{
|
||||
FWPRINT(L"Error - m_character NULL on OnOverlapBegin ");
|
||||
return;
|
||||
}
|
||||
|
||||
ANetworkCharacter* otherUnit = Cast<ANetworkCharacter>(OtherActor);
|
||||
if(!otherUnit->GetHitable())
|
||||
return;
|
||||
if(!ULib::CheckAbilityFilter(filter, character, otherUnit))
|
||||
return;
|
||||
if(hitOnce && m_CheckActorsHit(otherUnit))
|
||||
return;
|
||||
|
||||
m_actorsInside.Add(OtherActor);
|
||||
if (CollisionCheck(Cast<ANetworkCharacter>(OtherActor)))
|
||||
{
|
||||
HitEvent(otherUnit);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
void AAbilityTriggerBase::OnOverlapEnd(class AActor * OtherActor, class UPrimitiveComponent* OtherComp, int32 OtherBodyIndex)
|
||||
{
|
||||
if(Role == ROLE_Authority)
|
||||
LeaveEvent(Cast<ANetworkCharacter>(OtherActor));
|
||||
}
|
||||
|
||||
void AAbilityTriggerBase::HitEvent(class ANetworkCharacter* otherUnit)
|
||||
{
|
||||
if (hitOnce)
|
||||
m_actorsHit.Add(otherUnit);
|
||||
OnTriggerHit(otherUnit);
|
||||
}
|
||||
void AAbilityTriggerBase::LeaveEvent(class ANetworkCharacter* otherActor)
|
||||
{
|
||||
if (Role == ROLE_Authority)
|
||||
{
|
||||
for (auto it = (m_actorsInside.CreateIterator()); it; it++)
|
||||
{
|
||||
if (*it == otherActor)
|
||||
{
|
||||
m_actorsInside.Remove(otherActor);
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
if (!ULib::CheckAbilityFilter(filter, character, otherActor))
|
||||
return;
|
||||
OnTriggerLeave(otherActor);
|
||||
}
|
||||
}
|
||||
|
||||
float AAbilityTriggerBase::GetLifeTime() const
|
||||
{
|
||||
return m_lifeTime;
|
||||
}
|
||||
|
||||
float AAbilityTriggerBase::GetLifeTimeRate() const
|
||||
{
|
||||
return FMath::Clamp(m_lifeTime / duration, 0.0f, 1.0f);
|
||||
}
|
||||
|
||||
bool AAbilityTriggerBase::m_CheckActorsHit(class ANetworkCharacter* otherUnit)
|
||||
{
|
||||
for (ANetworkCharacter* character : m_actorsHit)
|
||||
{
|
||||
if (character == otherUnit)
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
bool AAbilityTriggerBase::CollisionCheck(ANetworkCharacter* otheractor)
|
||||
{
|
||||
return IsValid(otheractor);
|
||||
}
|
||||
void AAbilityTriggerBase::OnTriggerHit_Implementation(ANetworkCharacter* actor)
|
||||
{
|
||||
|
||||
}
|
||||
void AAbilityTriggerBase::OnTriggerTick_Implementation(ANetworkCharacter* actor)
|
||||
{
|
||||
|
||||
}
|
||||
void AAbilityTriggerBase::OnTriggerLeave_Implementation(ANetworkCharacter* actor)
|
||||
{
|
||||
|
||||
}
|
||||
|
||||
void AAbilityTriggerBase::OnLocalTriggerTick_Implementation(ANetworkCharacter* actor)
|
||||
{
|
||||
|
||||
}
|
||||
|
||||
void AAbilityTriggerBase::NativeOnLocalTriggerTick_Implementation(ANetworkCharacter* actor)
|
||||
{
|
||||
OnLocalTriggerTick(actor);
|
||||
}
|
||||
71
Source/UnrealProject/Abilities/AbilityTriggerBase.h
Normal file
71
Source/UnrealProject/Abilities/AbilityTriggerBase.h
Normal file
@@ -0,0 +1,71 @@
|
||||
// Project Lab - NHTV Igad
|
||||
|
||||
#pragma once
|
||||
|
||||
#include "DealDamageProxy.h"
|
||||
#include "AbilityFilter.h"
|
||||
#include "AbilityTriggerBase.generated.h"
|
||||
|
||||
|
||||
UCLASS()
|
||||
class UNREALPROJECT_API AAbilityTriggerBase : public ADealDamageProxy
|
||||
{
|
||||
GENERATED_BODY()
|
||||
|
||||
public:
|
||||
AAbilityTriggerBase();
|
||||
|
||||
virtual void BeginPlay() override;
|
||||
virtual void EndPlay(const EEndPlayReason::Type EndPlayReason) override;
|
||||
virtual void Tick( float DeltaSeconds ) override;
|
||||
virtual bool CollisionCheck(ANetworkCharacter* otheractor);
|
||||
|
||||
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 void HitEvent(class ANetworkCharacter* otherActor);
|
||||
virtual void LeaveEvent(class ANetworkCharacter* otherActor);
|
||||
|
||||
UFUNCTION(BlueprintCallable, Category="Ability")
|
||||
float GetLifeTime() const;
|
||||
UFUNCTION(BlueprintCallable, Category="Ability")
|
||||
float GetLifeTimeRate() const;
|
||||
|
||||
UPROPERTY(BlueprintReadWrite, Replicated, Category = "Ability", meta = (ExposeOnSpawn))
|
||||
float duration;
|
||||
UPROPERTY(BlueprintReadWrite, Category = "Ability", meta = (ExposeOnSpawn))
|
||||
bool hitOnce;
|
||||
|
||||
UPROPERTY(BlueprintReadWrite, meta = (ExposeOnSpawn))
|
||||
EAbilityFilter filter;
|
||||
|
||||
UFUNCTION(BlueprintNativeEvent)
|
||||
void OnTriggerHit(ANetworkCharacter* actor);
|
||||
UFUNCTION(BlueprintNativeEvent)
|
||||
void OnTriggerTick(ANetworkCharacter* actor);
|
||||
UFUNCTION(BlueprintCallable, NetMulticast, Reliable, Category = "trigger")
|
||||
void NativeOnLocalTriggerTick(ANetworkCharacter* actor);
|
||||
|
||||
UFUNCTION(BlueprintNativeEvent)
|
||||
void OnLocalTriggerTick(ANetworkCharacter* actor);
|
||||
UFUNCTION(BlueprintNativeEvent)
|
||||
void OnTriggerLeave(ANetworkCharacter* actor);
|
||||
|
||||
UPROPERTY(BlueprintReadWrite, VisibleAnywhere, Category = "Ability", meta = (ExposeOnSpawn))
|
||||
bool autoDestroy;
|
||||
UPROPERTY(BlueprintReadOnly, VisibleAnywhere, Category = "Ability", meta = (ExposeOnSpawn))
|
||||
float ticksPerSecond;
|
||||
bool m_CheckActorsHit(class ANetworkCharacter* otherUnit);
|
||||
|
||||
|
||||
|
||||
protected:
|
||||
TArray<AActor*> m_actorsInside;
|
||||
TArray<ANetworkCharacter*> m_actorsHit;
|
||||
float m_tickTime;
|
||||
float m_lifeTime;
|
||||
float m_tickTimer;
|
||||
bool delegatesSet = false;
|
||||
};
|
||||
31
Source/UnrealProject/Abilities/AuraTrigger.cpp
Normal file
31
Source/UnrealProject/Abilities/AuraTrigger.cpp
Normal file
@@ -0,0 +1,31 @@
|
||||
// Project Lab - NHTV Igad
|
||||
|
||||
#include "UnrealProject.h"
|
||||
#include "Modifier.h"
|
||||
#include "NetworkCharacter.h"
|
||||
#include "AuraTrigger.h"
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
AModifier* AAuraTrigger::GetModifier(class ANetworkCharacter* targetCharacter)
|
||||
{
|
||||
auto it = data.Find(targetCharacter);
|
||||
if (it==nullptr)
|
||||
return nullptr;
|
||||
return *it;
|
||||
}
|
||||
void AAuraTrigger::SetModifier(class ANetworkCharacter* targetCharacter, AModifier* modifier)
|
||||
{
|
||||
data.Add(targetCharacter, modifier);
|
||||
}
|
||||
|
||||
void AAuraTrigger::Tick(float DeltaSeconds)
|
||||
{
|
||||
Super::Tick(DeltaSeconds);
|
||||
if (Role == ROLE_Authority && IsValid(followObject))
|
||||
{
|
||||
SetActorLocation(followObject->GetActorLocation());
|
||||
}
|
||||
}
|
||||
26
Source/UnrealProject/Abilities/AuraTrigger.h
Normal file
26
Source/UnrealProject/Abilities/AuraTrigger.h
Normal file
@@ -0,0 +1,26 @@
|
||||
// Project Lab - NHTV Igad
|
||||
|
||||
#pragma once
|
||||
|
||||
#include "Abilities/ConeTrigger.h"
|
||||
#include "AuraTrigger.generated.h"
|
||||
|
||||
/**
|
||||
*
|
||||
*/
|
||||
UCLASS()
|
||||
class UNREALPROJECT_API AAuraTrigger : public AConeTrigger
|
||||
{
|
||||
GENERATED_BODY()
|
||||
|
||||
public:
|
||||
virtual void Tick(float DeltaSeconds) override;
|
||||
UFUNCTION(BlueprintCallable, category = "Trigger")
|
||||
class AModifier* GetModifier(class ANetworkCharacter* targetCharacter);
|
||||
UFUNCTION(BlueprintCallable, category = "Trigger")
|
||||
void SetModifier(class ANetworkCharacter* targetCharacter, AModifier* modifier);
|
||||
UPROPERTY()
|
||||
TMap<class ANetworkCharacter*, class AModifier*> data;
|
||||
UPROPERTY(BlueprintReadWrite, Category = "Ability", meta = (ExposeOnSpawn))
|
||||
AActor* followObject;
|
||||
};
|
||||
326
Source/UnrealProject/Abilities/BlueprintAbilityLibrary.cpp
Normal file
326
Source/UnrealProject/Abilities/BlueprintAbilityLibrary.cpp
Normal file
@@ -0,0 +1,326 @@
|
||||
#include "UnrealProject.h"
|
||||
#include "BlueprintAbilityLibrary.h"
|
||||
|
||||
#include "DealDamageProxy.h"
|
||||
#include "AbilityEventGroup.h"
|
||||
#include "AbilityTriggerBase.h"
|
||||
#include "Modifier.h"
|
||||
#include "NetworkCharacter.h"
|
||||
#include "ProjectileBase.h"
|
||||
#include "Class.h"
|
||||
|
||||
ULib::ULib(const FObjectInitializer& init)
|
||||
: Super(init)
|
||||
{
|
||||
}
|
||||
AActor* ULib::BeginSpawning2(UObject* WorldContextObject, TSubclassOf<AActor> ActorClass)
|
||||
{
|
||||
return UGameplayStatics::BeginDeferredActorSpawnFromClass(WorldContextObject, ActorClass, FTransform());
|
||||
}
|
||||
AActor* ULib::FinishSpawning2(class AActor* Actor)
|
||||
{
|
||||
if(!Actor)
|
||||
return nullptr;
|
||||
return UGameplayStatics::FinishSpawningActor(Actor, FTransform());
|
||||
}
|
||||
|
||||
AActor* ULib::BeginSpawningGroup(UObject* WorldContextObject, TSubclassOf<AActor> ActorClass)
|
||||
{
|
||||
if(!WorldContextObject->GetWorld()->GetAuthGameMode())
|
||||
return nullptr;
|
||||
AActor* actor = UGameplayStatics::BeginDeferredActorSpawnFromClass(WorldContextObject, ActorClass, FTransform());
|
||||
if(!actor)
|
||||
return nullptr;
|
||||
AAbilityEventGroup* newGroup = Cast<AAbilityEventGroup>(actor);
|
||||
check(newGroup);
|
||||
|
||||
AAbilityEventGroup* parent = Cast<AAbilityEventGroup>(WorldContextObject);
|
||||
//GWPRINT(L"Parent = " + parent);
|
||||
if(!parent)
|
||||
{
|
||||
GWERROR(L"Cannot call FinishSpawningGroup from any other source than AAbilityEventGroup");
|
||||
newGroup->Destroy();
|
||||
return nullptr;
|
||||
}
|
||||
parent->TransitionTo(newGroup);
|
||||
return newGroup;
|
||||
}
|
||||
AActor* ULib::FinishSpawningGroup(class AActor* Actor)
|
||||
{
|
||||
if(!Actor)
|
||||
return nullptr;
|
||||
return FinishSpawning2(Actor);
|
||||
}
|
||||
|
||||
AActor* ULib::BeginSpawningModifier(UObject* WorldContextObject, TSubclassOf<AActor> ActorClass)
|
||||
{
|
||||
if(!WorldContextObject->GetWorld()->GetAuthGameMode())
|
||||
return nullptr;
|
||||
AActor* actor = UGameplayStatics::BeginDeferredActorSpawnFromClass(WorldContextObject, ActorClass, FTransform());
|
||||
if(!actor)
|
||||
return nullptr;
|
||||
AModifier* newModifier = Cast<AModifier>(actor);
|
||||
check(newModifier);
|
||||
|
||||
ADealDamageProxy* damageProxy = Cast<ADealDamageProxy>(WorldContextObject);
|
||||
if(damageProxy)
|
||||
{
|
||||
newModifier->character = damageProxy->character;
|
||||
newModifier->abilityInfo = damageProxy->abilityInfo;
|
||||
}
|
||||
else
|
||||
{
|
||||
GWERROR(L"Cannot spawn modifier from a source that is not a ADealDamageProxy");
|
||||
newModifier->Destroy();
|
||||
return nullptr;
|
||||
}
|
||||
return newModifier;
|
||||
}
|
||||
AActor* ULib::FinishSpawningModifier(class AActor* Actor, class ANetworkCharacter* target)
|
||||
{
|
||||
if(!IsValid(Actor))
|
||||
return nullptr;
|
||||
AModifier* mod = Cast<AModifier>(Actor);
|
||||
if(!IsValid(target))
|
||||
{
|
||||
FString name = "<none>";
|
||||
if(mod->abilityInfo)
|
||||
name = mod->abilityInfo->GetName();
|
||||
GWERROR(L"Modifier needs a target (spawned by " + name + L")");
|
||||
Actor->Destroy();
|
||||
return nullptr;
|
||||
}
|
||||
//GWPRINT(L"Modifier Target = " + target);
|
||||
ModifierManager* mm = target->GetModifierManager();
|
||||
if(!mm)
|
||||
{
|
||||
GWWARNING(L"Can't spawn modifier on a target that doesn't have a modifier manager. Might be in the process of destroying");
|
||||
Actor->Destroy();
|
||||
return nullptr;
|
||||
}
|
||||
mod->target = target;
|
||||
check(mod);
|
||||
mm->AddModifier(mod);
|
||||
|
||||
return FinishSpawning2(Actor);
|
||||
}
|
||||
|
||||
AActor* ULib::BeginSpawningTrigger(UObject* WorldContextObject, TSubclassOf<AActor> ActorClass, const FTransform& SpawnTransform)
|
||||
{
|
||||
if(!WorldContextObject->GetWorld()->GetAuthGameMode())
|
||||
return nullptr;
|
||||
AActor* actor = UGameplayStatics::BeginDeferredActorSpawnFromClass(WorldContextObject, ActorClass, SpawnTransform);
|
||||
if(!actor)
|
||||
return nullptr;
|
||||
AAbilityTriggerBase* newTrigger = Cast<AAbilityTriggerBase>(actor);
|
||||
check(newTrigger);
|
||||
ADealDamageProxy* damageProxy = Cast<ADealDamageProxy>(WorldContextObject);
|
||||
if(damageProxy)
|
||||
{
|
||||
newTrigger->character = damageProxy->character;
|
||||
newTrigger->abilityInfo = damageProxy->abilityInfo;
|
||||
}
|
||||
else
|
||||
{
|
||||
GWERROR(L"Cannot spawn trigger from a source that is not a ADealDamageProxy");
|
||||
actor->Destroy();
|
||||
return nullptr;
|
||||
}
|
||||
return newTrigger;
|
||||
}
|
||||
AActor* ULib::FinishSpawningTrigger(class AActor* Actor, const FTransform & SpawnTransform)
|
||||
{
|
||||
if(!Actor)
|
||||
return nullptr;
|
||||
return UGameplayStatics::FinishSpawningActor(Actor, SpawnTransform);
|
||||
}
|
||||
|
||||
AActor* ULib::BeginSpawningProjectile(UObject* WorldContextObject, TSubclassOf<AActor> ActorClass, const FTransform& SpawnTransform)
|
||||
{
|
||||
if(!WorldContextObject->GetWorld()->GetAuthGameMode())
|
||||
return nullptr;
|
||||
AActor* actor = UGameplayStatics::BeginDeferredActorSpawnFromClass(WorldContextObject, ActorClass, SpawnTransform);
|
||||
if(!actor)
|
||||
return nullptr;
|
||||
AProjectileBase* newProjectile = Cast<AProjectileBase>(actor);
|
||||
check(newProjectile);
|
||||
ADealDamageProxy* damageProxy = Cast<ADealDamageProxy>(WorldContextObject);
|
||||
if(damageProxy)
|
||||
{
|
||||
newProjectile->character = damageProxy->character;
|
||||
newProjectile->abilityInfo = damageProxy->abilityInfo;
|
||||
}
|
||||
else
|
||||
{
|
||||
GWERROR(L"Cannot spawn projectile from a source that is not a ADealDamageProxy");
|
||||
actor->Destroy();
|
||||
return nullptr;
|
||||
}
|
||||
return newProjectile;
|
||||
}
|
||||
AActor* ULib::FinishSpawningProjectile(class AActor* Actor, const FTransform& SpawnTransform)
|
||||
{
|
||||
return UGameplayStatics::FinishSpawningActor(Actor, SpawnTransform);
|
||||
}
|
||||
|
||||
bool ULib::IsContainedInCone(FVector origin, FVector forward, float coneAngle, AActor* other)
|
||||
{
|
||||
if(other->IsPendingKillPending())
|
||||
return false;
|
||||
|
||||
//always hits at 360 degrees
|
||||
if(coneAngle >= 360.0f)
|
||||
return true;
|
||||
|
||||
FVector dist = other->GetActorLocation() - origin;
|
||||
dist.Normalize();
|
||||
forward.Normalize();
|
||||
float dot = FVector::DotProduct(dist, forward);
|
||||
float angle = acosf(dot);
|
||||
|
||||
float tempMaxAngle = coneAngle / 360;
|
||||
tempMaxAngle *= PI;
|
||||
|
||||
bool ret = tempMaxAngle > angle;
|
||||
return ret;
|
||||
}
|
||||
int32 ULib::CharacterConeOverlap(UObject* WorldContextObject, FVector origin, float coneRadius,
|
||||
FCharacterConeOverlapCallback callback, FVector forward, float coneAngle, EAbilityFilter filter)
|
||||
{
|
||||
UWorld* world = GEngine->GetWorldFromContextObject(WorldContextObject);
|
||||
check(world);
|
||||
|
||||
if(world->GetAuthGameMode() == nullptr)
|
||||
return 0;
|
||||
m_overlapResultArray.Empty();
|
||||
// Get the character
|
||||
ANetworkCharacter* character = Cast<ANetworkCharacter>(WorldContextObject);
|
||||
if(!character)
|
||||
{
|
||||
ADealDamageProxy* proxy = Cast<ADealDamageProxy>(WorldContextObject);
|
||||
if(!proxy)
|
||||
{
|
||||
GWERROR(L"CharacterConeOverlap failed, no caller character or proxy");
|
||||
return 0;
|
||||
}
|
||||
character = proxy->character;
|
||||
}
|
||||
|
||||
FCollisionShape shape;
|
||||
shape.SetCapsule(coneRadius, 400.0f);
|
||||
|
||||
TArray<FOverlapResult> overlaps;
|
||||
if(!world->OverlapMultiByObjectType(
|
||||
overlaps, origin, FQuat::Identity,
|
||||
FCollisionObjectQueryParams::AllDynamicObjects, shape,
|
||||
FCollisionQueryParams::DefaultQueryParam))
|
||||
{
|
||||
return 0;
|
||||
}
|
||||
TArray<class ANetworkCharacter*> test;
|
||||
if(callback.IsBound())
|
||||
{
|
||||
for(int32 i = 0; i < overlaps.Num(); i++)
|
||||
{
|
||||
FOverlapResult& overlap = overlaps[i];
|
||||
ANetworkCharacter* target = Cast<ANetworkCharacter>(overlap.GetActor());
|
||||
if (target && (overlap.Component->GetCollisionProfileName() == FName("Units") || overlap.Component->GetCollisionProfileName() == FName("Players")) &&
|
||||
IsContainedInCone(origin, forward, coneAngle, target) &&
|
||||
ULib::CheckAbilityFilter(filter, character, target))
|
||||
{
|
||||
|
||||
callback.Execute(target);
|
||||
m_overlapResultArray.Add(target);
|
||||
}
|
||||
}
|
||||
}
|
||||
// = test;
|
||||
return overlaps.Num();
|
||||
}
|
||||
|
||||
int32 ULib::ClosestCharacterConeOverlap(UObject* WorldContextObject, FVector origin, float coneRadius,
|
||||
FClosestCharacterConeOverlapCallback callback, FVector forward, float coneAngle, EAbilityFilter filter)
|
||||
{
|
||||
UWorld* world = GEngine->GetWorldFromContextObject(WorldContextObject);
|
||||
check(world);
|
||||
|
||||
if (world->GetAuthGameMode() == nullptr)
|
||||
return 0;
|
||||
|
||||
// Get the character
|
||||
ANetworkCharacter* character = Cast<ANetworkCharacter>(WorldContextObject);
|
||||
if (!character)
|
||||
{
|
||||
ADealDamageProxy* proxy = Cast<ADealDamageProxy>(WorldContextObject);
|
||||
if (!proxy)
|
||||
{
|
||||
GWERROR(L"CharacterConeOverlap failed, no caller character or proxy");
|
||||
return 0;
|
||||
}
|
||||
character = proxy->character;
|
||||
}
|
||||
|
||||
FCollisionShape shape;
|
||||
shape.SetCapsule(coneRadius, 400.0f);
|
||||
|
||||
TArray<FOverlapResult> overlaps;
|
||||
if (!world->OverlapMultiByObjectType(
|
||||
overlaps, origin, FQuat::Identity,
|
||||
FCollisionObjectQueryParams::AllDynamicObjects, shape,
|
||||
FCollisionQueryParams::DefaultQueryParam))
|
||||
{
|
||||
return 0;
|
||||
}
|
||||
ANetworkCharacter* closest = nullptr;
|
||||
float dist = 9999999999999.0f;
|
||||
if (callback.IsBound())
|
||||
{
|
||||
for (int32 i = 0; i < overlaps.Num(); i++)
|
||||
{
|
||||
FOverlapResult& overlap = overlaps[i];
|
||||
ANetworkCharacter* target = Cast<ANetworkCharacter>(overlap.GetActor());
|
||||
if (target && IsContainedInCone(origin, forward, coneAngle, target) && ULib::CheckAbilityFilter(filter, character, target))
|
||||
{
|
||||
if ((target->GetActorLocation() - origin).Size() < dist)
|
||||
{
|
||||
closest = target;
|
||||
dist = (target->GetActorLocation() - origin).Size();
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
if(closest)
|
||||
callback.Execute(closest);
|
||||
return overlaps.Num();
|
||||
|
||||
}
|
||||
TArray<ANetworkCharacter*> ULib::m_overlapResultArray;
|
||||
bool ULib::CheckAbilityFilter(EAbilityFilter filter, class ANetworkCharacter* self, class ANetworkCharacter* other)
|
||||
{
|
||||
if(!self)
|
||||
return false;
|
||||
if(!other)
|
||||
return false;
|
||||
if(((uint8)filter & ABILITY_FILTER_ENEMY) != 0)
|
||||
{
|
||||
if(self->GetTeam() != other->GetTeam())
|
||||
return true;
|
||||
}
|
||||
if(((uint8)filter & ABILITY_FILTER_ALLY) != 0)
|
||||
{
|
||||
if(self->GetTeam() == other->GetTeam())
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
TArray<ANetworkCharacter*>ULib::GetCharacterOverlaps()
|
||||
{
|
||||
TArray<ANetworkCharacter*> returnArray;
|
||||
for (ANetworkCharacter* character : m_overlapResultArray)
|
||||
{
|
||||
if(IsValid(character))
|
||||
returnArray.Add(character);
|
||||
}
|
||||
return returnArray;
|
||||
}
|
||||
59
Source/UnrealProject/Abilities/BlueprintAbilityLibrary.h
Normal file
59
Source/UnrealProject/Abilities/BlueprintAbilityLibrary.h
Normal file
@@ -0,0 +1,59 @@
|
||||
#pragma once
|
||||
#include "Kismet/BlueprintFunctionLibrary.h"
|
||||
#include "AbilityFilter.h"
|
||||
#include "BlueprintAbilityLibrary.Generated.h"
|
||||
|
||||
UCLASS()
|
||||
class ULib : public UBlueprintFunctionLibrary
|
||||
{
|
||||
GENERATED_UCLASS_BODY()
|
||||
|
||||
public:
|
||||
UFUNCTION(BlueprintCallable, Category = "Spawning", meta = (WorldContext = "WorldContextObject", UnsafeDuringActorConstruction = "true", BlueprintInternalUseOnly = "true"))
|
||||
static AActor* BeginSpawning2(UObject* WorldContextObject, TSubclassOf<AActor> ActorClass);
|
||||
UFUNCTION(BlueprintCallable, Category = "Spawning", meta = (UnsafeDuringActorConstruction = "true", BlueprintInternalUseOnly = "true"))
|
||||
static AActor* FinishSpawning2(class AActor* Actor);
|
||||
|
||||
UFUNCTION(BlueprintCallable, Category = "Spawning", meta = (WorldContext = "WorldContextObject", UnsafeDuringActorConstruction = "true", BlueprintInternalUseOnly = "true"))
|
||||
static AActor* BeginSpawningGroup(UObject* WorldContextObject, TSubclassOf<AActor> ActorClass);
|
||||
UFUNCTION(BlueprintCallable, Category = "Spawning", meta = (UnsafeDuringActorConstruction = "true", BlueprintInternalUseOnly = "true"))
|
||||
static AActor* FinishSpawningGroup(class AActor* Actor);
|
||||
|
||||
UFUNCTION(BlueprintCallable, Category = "Spawning", meta = (WorldContext = "WorldContextObject", UnsafeDuringActorConstruction = "true", BlueprintInternalUseOnly = "true"))
|
||||
static AActor* BeginSpawningModifier(UObject* WorldContextObject, TSubclassOf<AActor> ActorClass);
|
||||
UFUNCTION(BlueprintCallable, Category = "Spawning", meta = (UnsafeDuringActorConstruction = "true", BlueprintInternalUseOnly = "true"))
|
||||
static AActor* FinishSpawningModifier(class AActor* Actor, class ANetworkCharacter* Target);
|
||||
|
||||
UFUNCTION(BlueprintCallable, Category = "Spawning", meta = (WorldContext = "WorldContextObject", UnsafeDuringActorConstruction = "true", BlueprintInternalUseOnly = "true"))
|
||||
static AActor* BeginSpawningTrigger(UObject* WorldContextObject, TSubclassOf<AActor> ActorClass, const FTransform& SpawnTransform);
|
||||
UFUNCTION(BlueprintCallable, Category = "Spawning", meta = (UnsafeDuringActorConstruction = "true", BlueprintInternalUseOnly = "true"))
|
||||
static AActor* FinishSpawningTrigger(class AActor* Actor, const FTransform& SpawnTransform);
|
||||
|
||||
UFUNCTION(BlueprintCallable, Category = "Spawning", meta = (WorldContext = "WorldContextObject", UnsafeDuringActorConstruction = "true", BlueprintInternalUseOnly = "true"))
|
||||
static AActor* BeginSpawningProjectile(UObject* WorldContextObject, TSubclassOf<AActor> ActorClass, const FTransform& SpawnTransform);
|
||||
UFUNCTION(BlueprintCallable, Category = "Spawning", meta = (UnsafeDuringActorConstruction = "true", BlueprintInternalUseOnly = "true"))
|
||||
static AActor* FinishSpawningProjectile(class AActor* Actor, const FTransform& SpawnTransform);
|
||||
|
||||
UFUNCTION(BlueprintCallable, Category = "Trigger")
|
||||
static bool IsContainedInCone(FVector origin, FVector forward, float coneAngle, AActor* other);
|
||||
|
||||
DECLARE_DYNAMIC_DELEGATE_OneParam(FCharacterConeOverlapCallback, class ANetworkCharacter*, character);
|
||||
UFUNCTION(BlueprintCallable, Category = "Trigger", meta = (WorldContext = "WorldContextObject"))
|
||||
static int32 CharacterConeOverlap(UObject* WorldContextObject, FVector origin, float coneRadius,
|
||||
FCharacterConeOverlapCallback callback, FVector forward = FVector(1, 0, 0), float coneAngle = 360.0f, EAbilityFilter filter = EAbilityFilter::EnemyAll);
|
||||
UFUNCTION(BlueprintCallable, Category = "Trigger", meta = (WorldContext = "WorldContextObject"))
|
||||
static TArray<class ANetworkCharacter*> GetCharacterOverlaps();
|
||||
|
||||
DECLARE_DYNAMIC_DELEGATE_OneParam(FClosestCharacterConeOverlapCallback, class ANetworkCharacter*, character);
|
||||
UFUNCTION(BlueprintCallable, Category = "Trigger", meta = (WorldContext = "WorldContextObject"))
|
||||
static int32 ClosestCharacterConeOverlap(UObject* WorldContextObject, FVector origin, float coneRadius,
|
||||
FClosestCharacterConeOverlapCallback callback, FVector forward = FVector(1, 0, 0), float coneAngle = 360.0f, EAbilityFilter filter = EAbilityFilter::EnemyAll);
|
||||
|
||||
UFUNCTION(BlueprintCallable, Category = "Damage Proxy")
|
||||
static bool CheckAbilityFilter(EAbilityFilter filter, class ANetworkCharacter* self, class ANetworkCharacter* other);
|
||||
|
||||
static TArray<class ANetworkCharacter*> m_overlapResultArray;
|
||||
|
||||
private:
|
||||
|
||||
};
|
||||
58
Source/UnrealProject/Abilities/BombProjectile.cpp
Normal file
58
Source/UnrealProject/Abilities/BombProjectile.cpp
Normal file
@@ -0,0 +1,58 @@
|
||||
// Project Lab - NHTV Igad
|
||||
|
||||
#include "UnrealProject.h"
|
||||
#include "BombProjectile.h"
|
||||
#include "NetworkCharacter.h"
|
||||
|
||||
#include "Effect.h"
|
||||
#include "EffectFunctionLibrary.h"
|
||||
#include "BlueprintAbilityLibrary.h"
|
||||
|
||||
|
||||
ABombProjectile::ABombProjectile()
|
||||
{
|
||||
PrimaryActorTick.bCanEverTick = true;
|
||||
|
||||
m_progress = 0.0f;
|
||||
maxDistance = 100000.0f;
|
||||
}
|
||||
|
||||
void ABombProjectile::BeginPlay()
|
||||
{
|
||||
Super::BeginPlay();
|
||||
travelTime = travelTime < 0.01f ? 0.01f : travelTime;
|
||||
|
||||
float distance = FVector::Dist(source, target);
|
||||
}
|
||||
|
||||
void ABombProjectile::GetLifetimeReplicatedProps(TArray<FLifetimeProperty>& OutLifetimeProps) const
|
||||
{
|
||||
Super::GetLifetimeReplicatedProps(OutLifetimeProps);
|
||||
|
||||
DOREPLIFETIME_CONDITION(ABombProjectile, source, COND_InitialOnly);
|
||||
DOREPLIFETIME_CONDITION(ABombProjectile, target, COND_InitialOnly);
|
||||
DOREPLIFETIME_CONDITION(ABombProjectile, travelTime, COND_InitialOnly);
|
||||
}
|
||||
|
||||
void ABombProjectile::NativeFixedProjectileTick(float DeltaTime)
|
||||
{
|
||||
if(travelTime <= 0 || m_finished)
|
||||
return;
|
||||
|
||||
m_progress += DeltaTime;
|
||||
if(m_progress >= travelTime)
|
||||
m_progress = travelTime;
|
||||
|
||||
const float lerp = m_progress / travelTime;
|
||||
const float sin = FMath::Sin(lerp * PI);
|
||||
FVector newLocation = FMath::Lerp(source, target, lerp) + FVector(0, 0, sin * travelHeight);
|
||||
SetActorLocation(newLocation, true);
|
||||
|
||||
float distToEnd = (target - newLocation).Size();
|
||||
|
||||
if(lerp >= 1.0f)
|
||||
{
|
||||
// Server only
|
||||
Finish();
|
||||
}
|
||||
}
|
||||
30
Source/UnrealProject/Abilities/BombProjectile.h
Normal file
30
Source/UnrealProject/Abilities/BombProjectile.h
Normal file
@@ -0,0 +1,30 @@
|
||||
// Project Lab - NHTV Igad
|
||||
|
||||
#pragma once
|
||||
|
||||
#include "ProjectileBase.h"
|
||||
#include "BombProjectile.generated.h"
|
||||
|
||||
UCLASS()
|
||||
class UNREALPROJECT_API ABombProjectile : public AProjectileBase
|
||||
{
|
||||
GENERATED_BODY()
|
||||
|
||||
public:
|
||||
ABombProjectile();
|
||||
|
||||
virtual void BeginPlay() override;
|
||||
|
||||
UPROPERTY(BlueprintReadWrite, Replicated, Category = "BombProjectile", meta = (ExposeOnSpawn))
|
||||
FVector source;
|
||||
UPROPERTY(BlueprintReadWrite, Replicated, Category = "BombProjectile", meta = (ExposeOnSpawn))
|
||||
FVector target;
|
||||
UPROPERTY(BlueprintReadWrite, Replicated, Category = "BombProjectile", meta = (ExposeOnSpawn))
|
||||
float travelTime;
|
||||
UPROPERTY(BlueprintReadWrite, Replicated, Category = "BombProjectile", meta = (ExposeOnSpawn))
|
||||
float travelHeight;
|
||||
virtual void NativeFixedProjectileTick(float DeltaTime) override;
|
||||
|
||||
private:
|
||||
float m_progress;
|
||||
};
|
||||
97
Source/UnrealProject/Abilities/BossBarageBunny.cpp
Normal file
97
Source/UnrealProject/Abilities/BossBarageBunny.cpp
Normal file
@@ -0,0 +1,97 @@
|
||||
// Project Lab - NHTV Igad
|
||||
|
||||
#include "UnrealProject.h"
|
||||
|
||||
#include "NPCBase.h"
|
||||
#include "NetworkPlayer.h"
|
||||
#include "BossBarageBunny.h"
|
||||
|
||||
|
||||
ABossBarageBunny::ABossBarageBunny()
|
||||
{
|
||||
PrimaryActorTick.bCanEverTick = true;
|
||||
|
||||
bReplicates = true;
|
||||
bReplicateMovement = true;
|
||||
|
||||
boss = nullptr;
|
||||
|
||||
BeamEmitter = CreateDefaultSubobject<UParticleSystemComponent>(TEXT("Visual"));
|
||||
BeamEmitter->AttachTo(RootComponent);
|
||||
|
||||
m_lifeTime = 1;
|
||||
}
|
||||
|
||||
void ABossBarageBunny::BeginPlay()
|
||||
{
|
||||
Super::BeginPlay();
|
||||
|
||||
SpawnDefaultController();
|
||||
}
|
||||
|
||||
void ABossBarageBunny::Tick( float DeltaTime )
|
||||
{
|
||||
Super::Tick( DeltaTime );
|
||||
|
||||
// Update the line material
|
||||
if (IsValid(boss))
|
||||
{
|
||||
BeamEmitter->SetBeamSourcePoint(0, GetActorLocation(), 0);
|
||||
BeamEmitter->SetBeamTargetPoint(0, boss->GetActorLocation(), 0);
|
||||
}
|
||||
else
|
||||
{
|
||||
BeamEmitter->SetActive(false);
|
||||
GetCharacterMovement()->MaxWalkSpeed = 0;
|
||||
}
|
||||
|
||||
// Server only
|
||||
if (Role != ROLE_Authority)
|
||||
return;
|
||||
|
||||
m_lifeTime -= DeltaTime;
|
||||
if (m_lifeTime <= 0)
|
||||
{
|
||||
Destroy();
|
||||
return;
|
||||
}
|
||||
|
||||
// Boss destroyed?
|
||||
if (!IsValid(boss))
|
||||
return;
|
||||
|
||||
UNavigationSystem* const nav = UNavigationSystem::GetCurrent(GetWorld());
|
||||
if (!IsValid(nav)) return;
|
||||
|
||||
AController* const controller = GetController();
|
||||
if (!IsValid(controller))
|
||||
{
|
||||
JERROR("BossBarageBase has no controller");
|
||||
return;
|
||||
}
|
||||
|
||||
ANPCBase* const asNPC = Cast<ANPCBase>(boss);
|
||||
if (!IsValid(asNPC) || !IsValid(asNPC->target))
|
||||
return;
|
||||
|
||||
nav->SimpleMoveToLocation(controller, asNPC->target->GetActorLocation());
|
||||
}
|
||||
|
||||
|
||||
void ABossBarageBunny::Setup(AActor* boss, float lifeTime)
|
||||
{
|
||||
this->boss = boss;
|
||||
m_lifeTime = lifeTime;
|
||||
}
|
||||
|
||||
void ABossBarageBunny::ClearBoss()
|
||||
{
|
||||
this->boss = nullptr;
|
||||
}
|
||||
|
||||
void ABossBarageBunny::GetLifetimeReplicatedProps(TArray<FLifetimeProperty>& OutLifetimeProps) const
|
||||
{
|
||||
Super::GetLifetimeReplicatedProps(OutLifetimeProps);
|
||||
|
||||
DOREPLIFETIME(ABossBarageBunny, boss);
|
||||
}
|
||||
36
Source/UnrealProject/Abilities/BossBarageBunny.h
Normal file
36
Source/UnrealProject/Abilities/BossBarageBunny.h
Normal file
@@ -0,0 +1,36 @@
|
||||
// Project Lab - NHTV Igad
|
||||
|
||||
#pragma once
|
||||
|
||||
#include "GameFramework/Actor.h"
|
||||
#include "BossBarageBunny.generated.h"
|
||||
|
||||
UCLASS()
|
||||
class UNREALPROJECT_API ABossBarageBunny : public ACharacter
|
||||
{
|
||||
GENERATED_BODY()
|
||||
|
||||
public:
|
||||
ABossBarageBunny();
|
||||
|
||||
virtual void BeginPlay() override;
|
||||
|
||||
virtual void Tick( float DeltaSeconds ) override;
|
||||
|
||||
|
||||
UPROPERTY(EditAnywhere, Category = "Bunny")
|
||||
class UParticleSystemComponent* BeamEmitter;
|
||||
|
||||
UFUNCTION(BlueprintCallable, Category = "Bunny")
|
||||
void Setup(class AActor* boss, float lifeTime);
|
||||
|
||||
UFUNCTION(BlueprintCallable, Category = "Bunny")
|
||||
void ClearBoss();
|
||||
|
||||
|
||||
UPROPERTY(Replicated)
|
||||
class AActor* boss;
|
||||
|
||||
private:
|
||||
float m_lifeTime;
|
||||
};
|
||||
43
Source/UnrealProject/Abilities/BossTargetBunny.cpp
Normal file
43
Source/UnrealProject/Abilities/BossTargetBunny.cpp
Normal file
@@ -0,0 +1,43 @@
|
||||
// Project Lab - NHTV Igad
|
||||
|
||||
#include "UnrealProject.h"
|
||||
#include "BossTargetBunny.h"
|
||||
|
||||
|
||||
ABossTargetBunny::ABossTargetBunny()
|
||||
{
|
||||
PrimaryActorTick.bCanEverTick = true;
|
||||
|
||||
bReplicates = true;
|
||||
bReplicateMovement = true;
|
||||
|
||||
m_lifeTime = 0;
|
||||
}
|
||||
|
||||
void ABossTargetBunny::BeginPlay()
|
||||
{
|
||||
Super::BeginPlay();
|
||||
|
||||
}
|
||||
|
||||
void ABossTargetBunny::Tick( float DeltaTime )
|
||||
{
|
||||
Super::Tick( DeltaTime );
|
||||
|
||||
// Server only
|
||||
if (Role != ROLE_Authority)
|
||||
return;
|
||||
|
||||
m_lifeTime -= DeltaTime;
|
||||
if (m_lifeTime <= 0)
|
||||
{
|
||||
Destroy();
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
void ABossTargetBunny::Setup(float lifeTime)
|
||||
{
|
||||
m_lifeTime = lifeTime;
|
||||
}
|
||||
26
Source/UnrealProject/Abilities/BossTargetBunny.h
Normal file
26
Source/UnrealProject/Abilities/BossTargetBunny.h
Normal file
@@ -0,0 +1,26 @@
|
||||
// Project Lab - NHTV Igad
|
||||
|
||||
#pragma once
|
||||
|
||||
#include "GameFramework/Actor.h"
|
||||
#include "BossTargetBunny.generated.h"
|
||||
|
||||
UCLASS()
|
||||
class UNREALPROJECT_API ABossTargetBunny : public AActor
|
||||
{
|
||||
GENERATED_BODY()
|
||||
|
||||
public:
|
||||
ABossTargetBunny();
|
||||
|
||||
virtual void BeginPlay() override;
|
||||
|
||||
virtual void Tick( float DeltaSeconds ) override;
|
||||
|
||||
|
||||
UFUNCTION(BlueprintCallable, Category = "Bunny")
|
||||
void Setup(float lifeTime);
|
||||
|
||||
private:
|
||||
float m_lifeTime;
|
||||
};
|
||||
110
Source/UnrealProject/Abilities/ConeComponent.cpp
Normal file
110
Source/UnrealProject/Abilities/ConeComponent.cpp
Normal file
@@ -0,0 +1,110 @@
|
||||
// Project Lab - NHTV Igad
|
||||
|
||||
#include "UnrealProject.h"
|
||||
#include "ConeComponent.h"
|
||||
|
||||
#define CONEARCVERTEXCOUNT 50
|
||||
|
||||
// Sets default values for this component's properties
|
||||
UConeComponent::UConeComponent(const FObjectInitializer& ObjectInitializer) : Super(ObjectInitializer)
|
||||
{
|
||||
ShapeColor = FColor(223, 149, 157, 255);
|
||||
bUseEditorCompositing = true;
|
||||
}
|
||||
|
||||
void UConeComponent::BeginPlay()
|
||||
{
|
||||
UpdateCapsule();
|
||||
Super::BeginPlay();
|
||||
}
|
||||
|
||||
void UConeComponent::UpdateCapsule()
|
||||
{
|
||||
SetCapsuleSize(coneRadius, 400);
|
||||
}
|
||||
|
||||
// Create sceneproxy to show the cone in the editor
|
||||
FPrimitiveSceneProxy* UConeComponent::CreateSceneProxy()
|
||||
{
|
||||
class FDrawConeSceneProxy : public FPrimitiveSceneProxy
|
||||
{
|
||||
public:
|
||||
const UConeComponent* component;
|
||||
FDrawConeSceneProxy(const UConeComponent* InComponent)
|
||||
: FPrimitiveSceneProxy(InComponent)
|
||||
, bDrawOnlyIfSelected(InComponent->bDrawOnlyIfSelected)
|
||||
, component(InComponent)
|
||||
{
|
||||
bWillEverBeLit = false;
|
||||
}
|
||||
|
||||
|
||||
virtual void GetDynamicMeshElements(const TArray<const FSceneView*>& Views, const FSceneViewFamily& ViewFamily, uint32 VisibilityMap, FMeshElementCollector& Collector) const
|
||||
{
|
||||
QUICK_SCOPE_CYCLE_COUNTER(STAT_GetDynamicMeshElements_DrawDynamicElements);
|
||||
|
||||
for (int32 ViewIndex = 0; ViewIndex < Views.Num(); ViewIndex++)
|
||||
{
|
||||
|
||||
if (VisibilityMap & (1 << ViewIndex))
|
||||
{
|
||||
const FSceneView* View = Views[ViewIndex];
|
||||
const FLinearColor DrawCapsuleColor = GetViewSelectionColor(ShapeColor, *View, IsSelected(), IsHovered(), false, IsIndividuallySelected());
|
||||
|
||||
FPrimitiveDrawInterface* PDI = Collector.GetPDI(ViewIndex);
|
||||
|
||||
FTransform transform = component->GetComponentTransform();
|
||||
FVector base = transform.GetLocation();
|
||||
|
||||
const float scale = 1.0f / 180.0f * PI;
|
||||
float baseRot = (360.0f - transform.GetRotation().Euler().Z) * scale;
|
||||
|
||||
FVector scaleVec = transform.GetScale3D();
|
||||
FVector forward = FVector(scaleVec.X, 0, 0);
|
||||
FVector right = FVector(0, scaleVec.Y, 0);
|
||||
float angle = (component->coneAngle) / 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) * component->coneRadius;
|
||||
rot += anglestep;
|
||||
}
|
||||
|
||||
for (int i = 0; i < CONEARCVERTEXCOUNT-1; i++)
|
||||
{
|
||||
|
||||
PDI->DrawLine(linePoints[i], linePoints[i + 1], ShapeColor, SDPG_World);
|
||||
}
|
||||
PDI->DrawLine(base, linePoints[0], ShapeColor, SDPG_World);
|
||||
PDI->DrawLine(base, linePoints[CONEARCVERTEXCOUNT - 1], ShapeColor, SDPG_World);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
virtual FPrimitiveViewRelevance GetViewRelevance(const FSceneView* View) const override
|
||||
{
|
||||
const bool bVisible = !bDrawOnlyIfSelected || IsSelected();
|
||||
FPrimitiveViewRelevance Result;
|
||||
Result.bDrawRelevance = IsShown(View) && bVisible;
|
||||
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 FColor ShapeColor = FColor(255,0,0,255);
|
||||
const FTransform transform;
|
||||
};
|
||||
return new FDrawConeSceneProxy(this);
|
||||
}
|
||||
26
Source/UnrealProject/Abilities/ConeComponent.h
Normal file
26
Source/UnrealProject/Abilities/ConeComponent.h
Normal file
@@ -0,0 +1,26 @@
|
||||
// Project Lab - NHTV Igad
|
||||
|
||||
#pragma once
|
||||
|
||||
#include "Components/CapsuleComponent.h"
|
||||
#include "ConeComponent.generated.h"
|
||||
|
||||
|
||||
UCLASS(ClassGroup = "Collision", editinlinenew, hidecategories = (Object, LOD, Lighting, TextureStreaming), meta = (DisplayName = "Cone Collision", BlueprintSpawnableComponent))
|
||||
class UNREALPROJECT_API UConeComponent : public UCapsuleComponent
|
||||
{
|
||||
GENERATED_UCLASS_BODY()
|
||||
|
||||
public:
|
||||
void BeginPlay() override;
|
||||
|
||||
void UpdateCapsule();
|
||||
|
||||
UPROPERTY(EditAnywhere, export, Category = Shape, meta = (ClampMin = "0", UIMin = "0"))
|
||||
float coneRadius = 500.0f;
|
||||
|
||||
UPROPERTY(EditAnywhere, export, Category = Shape, meta = (ClampMin = "0", UIMin = "0"))
|
||||
float coneAngle = 50;
|
||||
|
||||
virtual FPrimitiveSceneProxy* CreateSceneProxy() override;
|
||||
};
|
||||
50
Source/UnrealProject/Abilities/ConeTrigger.cpp
Normal file
50
Source/UnrealProject/Abilities/ConeTrigger.cpp
Normal file
@@ -0,0 +1,50 @@
|
||||
// Project Lab - NHTV Igad
|
||||
|
||||
#include "UnrealProject.h"
|
||||
#include "ConeTrigger.h"
|
||||
#include "ConeComponent.h"
|
||||
#include "NetworkCharacter.h"
|
||||
#include "BlueprintAbilityLibrary.h" // For Cone overlap code
|
||||
|
||||
AConeTrigger::AConeTrigger()
|
||||
{
|
||||
PrimaryActorTick.bCanEverTick = true;
|
||||
|
||||
m_coneTrigger = CreateDefaultSubobject <UConeComponent>(TEXT("trigger"));
|
||||
RootComponent = m_coneTrigger;
|
||||
m_coneTrigger->SetCollisionProfileName(TEXT("Triggers"));
|
||||
}
|
||||
|
||||
|
||||
void AConeTrigger::BeginPlay()
|
||||
{
|
||||
m_coneTrigger->SetWorldTransform(GetTransform());
|
||||
if (!delegatesSet)
|
||||
{
|
||||
m_coneTrigger->OnComponentBeginOverlap.AddDynamic(this, &AConeTrigger::OnOverlapBegin);
|
||||
m_coneTrigger->OnComponentEndOverlap.AddDynamic(this, &AConeTrigger::OnOverlapEnd);
|
||||
delegatesSet = true;
|
||||
}
|
||||
SetCone(radius, angle);
|
||||
Super::BeginPlay();
|
||||
}
|
||||
|
||||
|
||||
void AConeTrigger::Tick( float DeltaTime )
|
||||
{
|
||||
Super::Tick( DeltaTime );
|
||||
}
|
||||
|
||||
void AConeTrigger::SetCone(float inputRadius, float inputAngle)
|
||||
{
|
||||
m_coneTrigger->coneAngle = (inputAngle);
|
||||
m_coneTrigger->coneRadius = (inputRadius);
|
||||
m_coneTrigger->UpdateCapsule();
|
||||
}
|
||||
|
||||
|
||||
|
||||
bool AConeTrigger::CollisionCheck(ANetworkCharacter* otheractor)
|
||||
{
|
||||
return ULib::IsContainedInCone(GetActorLocation(), GetActorForwardVector(), m_coneTrigger->coneAngle, otheractor);
|
||||
}
|
||||
27
Source/UnrealProject/Abilities/ConeTrigger.h
Normal file
27
Source/UnrealProject/Abilities/ConeTrigger.h
Normal file
@@ -0,0 +1,27 @@
|
||||
// Project Lab - NHTV Igad
|
||||
|
||||
#pragma once
|
||||
#include "AbilityTriggerBase.h"
|
||||
#include "GameFramework/Actor.h"
|
||||
#include "ConeTrigger.generated.h"
|
||||
|
||||
UCLASS()
|
||||
class UNREALPROJECT_API AConeTrigger : public AAbilityTriggerBase
|
||||
{
|
||||
GENERATED_BODY()
|
||||
|
||||
public:
|
||||
AConeTrigger();
|
||||
|
||||
virtual void BeginPlay() override;
|
||||
virtual void Tick( float DeltaSeconds ) override;
|
||||
virtual bool CollisionCheck(ANetworkCharacter* otheractor) override;
|
||||
void SetCone(float radius, float angle);
|
||||
UPROPERTY(VisibleAnywhere, BlueprintReadWrite, meta = (ExposeOnSpawn), Category = "Trigger")
|
||||
float radius;
|
||||
UPROPERTY(VisibleAnywhere, BlueprintReadWrite, meta = (ExposeOnSpawn), Category = "Trigger")
|
||||
float angle;
|
||||
private:
|
||||
UPROPERTY(VisibleAnywhere, Category = "Trigger")
|
||||
class UConeComponent* m_coneTrigger;
|
||||
};
|
||||
67
Source/UnrealProject/Abilities/DealDamageProxy.cpp
Normal file
67
Source/UnrealProject/Abilities/DealDamageProxy.cpp
Normal file
@@ -0,0 +1,67 @@
|
||||
#include "UnrealProject.h"
|
||||
#include "NetworkCharacter.h"
|
||||
#include "DealDamageProxy.h"
|
||||
#include "DefaultPlayerState.h"
|
||||
#include "ScalingGraph.h"
|
||||
#include "AbilityInfo.h"
|
||||
|
||||
float ADealDamageProxy::GetAbilityPowerScale() const
|
||||
{
|
||||
if(!character)
|
||||
return 0.0f;
|
||||
AAbilityState* state = character->GetAbilityState(abilityInfo);
|
||||
if(!state)
|
||||
return 0.0f;
|
||||
return state->power;
|
||||
}
|
||||
|
||||
float ADealDamageProxy::ScaleGraphCurve(const UCurveFloat* val)
|
||||
{
|
||||
if (!IsValid(val))
|
||||
{
|
||||
GEngine->AddOnScreenDebugMessage((int32)abilityInfo->GetUniqueID(), 2.0f, FColor(255, 20, 20, 255), FString("Invalid curve argument in ") + GetName() + " [" + abilityInfo->GetName() + "]");
|
||||
JERROR("Invalid curve argument in " + GetName() + " [" + abilityInfo->GetName() + "]");
|
||||
return 0;
|
||||
}
|
||||
|
||||
if(abilityInfo->abilityType == EAbilityType::Basic)
|
||||
{
|
||||
return ScaleGraphCurveByLevel(val);
|
||||
}
|
||||
float f = GetAbilityPowerScale();
|
||||
f = FMath::Clamp(f, 0.0f, 1.0f);
|
||||
return val->GetFloatValue(f);
|
||||
}
|
||||
|
||||
float ADealDamageProxy::ScaleGraphCurveByLevel(const UCurveFloat* val)
|
||||
{
|
||||
if(!IsValid(val))
|
||||
{
|
||||
GEngine->AddOnScreenDebugMessage((int32)abilityInfo->GetUniqueID(), 2.0f, FColor(255, 20, 20, 255), FString("Invalid curve argument in ") + GetName() + " [" + abilityInfo->GetName() + "]");
|
||||
JERROR("Invalid curve argument in " + GetName() + " [" + abilityInfo->GetName() + "]");
|
||||
return 0;
|
||||
}
|
||||
|
||||
float f = 0.0f;
|
||||
if(character)
|
||||
{
|
||||
ADefaultPlayerState* ps = Cast<ADefaultPlayerState>(character->PlayerState);
|
||||
if(ps)
|
||||
{
|
||||
f = FMath::Clamp((float)ps->GetLevel() / (float)ps->GetMaxLevel(), 0.0f, 1.0f);
|
||||
}
|
||||
}
|
||||
return val->GetFloatValue(f);
|
||||
}
|
||||
|
||||
float ULevelScaleLibrary::ScaleGraphCurveFloat(float in, const UCurveFloat* val)
|
||||
{
|
||||
if (!IsValid(val))
|
||||
{
|
||||
JERROR("Invalid curve argument");
|
||||
return 0;
|
||||
}
|
||||
|
||||
in = FMath::Clamp(in, 0.0f, 1.0f);
|
||||
return val->GetFloatValue(in);
|
||||
}
|
||||
28
Source/UnrealProject/Abilities/DealDamageProxy.h
Normal file
28
Source/UnrealProject/Abilities/DealDamageProxy.h
Normal file
@@ -0,0 +1,28 @@
|
||||
#pragma once
|
||||
#include "AbilityFilter.h"
|
||||
#include "ScalingGraph.h"
|
||||
#include "DealDamageProxy.Generated.h"
|
||||
|
||||
UCLASS()
|
||||
class ADealDamageProxy : public AActor
|
||||
{
|
||||
GENERATED_BODY()
|
||||
|
||||
public:
|
||||
UFUNCTION(BlueprintCallable, Category = "Damage Scaling")
|
||||
float GetAbilityPowerScale() const;
|
||||
|
||||
// Scales samples the input curve anywhere from 0-1 based on the currently cast ability's power(or level for basic attacks)
|
||||
UFUNCTION(BlueprintCallable, Category = "Scaling Graph")
|
||||
float ScaleGraphCurve(const UCurveFloat* val);
|
||||
// Same as ScaleGraphCurve but this one scales with the player level
|
||||
UFUNCTION(BlueprintCallable, Category = "Scaling Graph")
|
||||
float ScaleGraphCurveByLevel(const UCurveFloat* val);
|
||||
|
||||
// The character that started this sequence of proxies and the dealer of the damage
|
||||
UPROPERTY(BlueprintReadOnly, Category="Damage Proxy")
|
||||
class ANetworkCharacter* character;
|
||||
// The ability that was cast to spawn this object
|
||||
UPROPERTY(BlueprintReadOnly, Category="Damage Proxy")
|
||||
class UAbilityInfo* abilityInfo;
|
||||
};
|
||||
47
Source/UnrealProject/Abilities/HomingProjectile.cpp
Normal file
47
Source/UnrealProject/Abilities/HomingProjectile.cpp
Normal file
@@ -0,0 +1,47 @@
|
||||
// Project Lab - NHTV Igad
|
||||
|
||||
#include "UnrealProject.h"
|
||||
#include "HomingProjectile.h"
|
||||
#include "DefaultGameMode.h"
|
||||
#include "NetworkPlayer.h"
|
||||
|
||||
void AHomingProjectile::BeginPlay()
|
||||
{
|
||||
Super::BeginPlay();
|
||||
|
||||
float maxdist = BIG_NUMBER;
|
||||
if (Role == ROLE_Authority)
|
||||
{
|
||||
if (targetCreature == nullptr)
|
||||
{
|
||||
UWorld* const world = GetWorld();
|
||||
if (!world) return;
|
||||
ADefaultGameMode* mode = Cast<ADefaultGameMode>(world->GetAuthGameMode());
|
||||
if (!mode) return;
|
||||
|
||||
TArray<class ANetworkPlayer*> players = mode->GetPlayers();
|
||||
|
||||
for (ANetworkPlayer* player : players)
|
||||
{
|
||||
float dist = (player->GetActorLocation() - GetActorLocation()).SizeSquared();
|
||||
if (dist < maxdist)
|
||||
{
|
||||
maxdist = dist;
|
||||
targetCreature = player;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void AHomingProjectile::NativeMove(float DeltaTime)
|
||||
{
|
||||
if (targetCreature == nullptr)
|
||||
return;
|
||||
FVector dir = targetCreature->GetActorLocation() - GetActorLocation();
|
||||
FRotator newrot = FRotationMatrix::MakeFromX(dir).Rotator();
|
||||
|
||||
SetActorRotation(newrot);
|
||||
|
||||
Super::NativeMove(DeltaTime);
|
||||
}
|
||||
22
Source/UnrealProject/Abilities/HomingProjectile.h
Normal file
22
Source/UnrealProject/Abilities/HomingProjectile.h
Normal file
@@ -0,0 +1,22 @@
|
||||
// Project Lab - NHTV Igad
|
||||
|
||||
#pragma once
|
||||
|
||||
#include "ProjectileBase.h"
|
||||
#include "HomingProjectile.generated.h"
|
||||
|
||||
/**
|
||||
*
|
||||
*/
|
||||
UCLASS()
|
||||
class UNREALPROJECT_API AHomingProjectile : public AProjectileBase
|
||||
{
|
||||
GENERATED_BODY()
|
||||
|
||||
public:
|
||||
virtual void BeginPlay() override;
|
||||
virtual void NativeMove(float DeltaTime) override;
|
||||
|
||||
UPROPERTY(BlueprintReadWrite, category = "projectile", meta = (ExposeOnSpawn))
|
||||
class ANetworkCharacter* targetCreature;
|
||||
};
|
||||
22
Source/UnrealProject/Abilities/HoverProjectile.cpp
Normal file
22
Source/UnrealProject/Abilities/HoverProjectile.cpp
Normal file
@@ -0,0 +1,22 @@
|
||||
// ProjeLab - NHTV Igad
|
||||
|
||||
#include "UnrealProject.h"
|
||||
#include "HoverProjectile.h"
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
void AHoverProjectile::NativeMove(float DeltaTime)
|
||||
{
|
||||
//moves with sweep to get collision
|
||||
float distance = speed * DeltaTime;
|
||||
FVector newpos = GetActorForwardVector() * distance + GetActorLocation();
|
||||
FHitResult outHit;
|
||||
FVector offset;
|
||||
if (GetWorld()->LineTraceSingleByChannel(outHit, newpos, newpos - FVector(0, 0, hoverHeight + 10), ECollisionChannel::ECC_GameTraceChannel7))
|
||||
offset = outHit.ImpactPoint + FVector(0, 0, hoverHeight) - GetActorLocation();
|
||||
else
|
||||
offset = newpos - FVector(0, 0, 10) - GetActorLocation();
|
||||
AddActorWorldOffset(offset,true);
|
||||
}
|
||||
23
Source/UnrealProject/Abilities/HoverProjectile.h
Normal file
23
Source/UnrealProject/Abilities/HoverProjectile.h
Normal file
@@ -0,0 +1,23 @@
|
||||
// Project Lab - NHTV Igad
|
||||
|
||||
#pragma once
|
||||
|
||||
#include "Abilities/ProjectileBase.h"
|
||||
#include "HoverProjectile.generated.h"
|
||||
|
||||
/**
|
||||
*
|
||||
*/
|
||||
UCLASS()
|
||||
class UNREALPROJECT_API AHoverProjectile : public AProjectileBase
|
||||
{
|
||||
GENERATED_BODY()
|
||||
|
||||
|
||||
public:
|
||||
UPROPERTY(VisibleAnywhere, BlueprintReadWrite, Category = "Projectile", meta = (ExposeOnSpawn))
|
||||
float hoverHeight;
|
||||
|
||||
virtual void NativeMove(float DeltaTime)override;
|
||||
|
||||
};
|
||||
103
Source/UnrealProject/Abilities/IngameSkillTree.cpp
Normal file
103
Source/UnrealProject/Abilities/IngameSkillTree.cpp
Normal file
@@ -0,0 +1,103 @@
|
||||
#include "UnrealProject.h"
|
||||
#include "IngameSkillTree.h"
|
||||
#include "HexMap.h"
|
||||
#include "BaseSkillObject.h"
|
||||
#include "DefaultPlayerState.h"
|
||||
|
||||
void AIngameSkillTree::BuildFromState(const FSkillTreeState& state)
|
||||
{
|
||||
for(int32 i = 0; i < state.objects.Num(); i++)
|
||||
{
|
||||
const FSkillTreeStateObject& obj = state.objects[i];
|
||||
if(!obj.skillObject)
|
||||
{
|
||||
GWARNING("Invalid skill object found in skill tree state, please reset your build.");
|
||||
continue;
|
||||
}
|
||||
|
||||
FIngameSkillTreeSkill igs;
|
||||
igs.placedPoints = obj.placedPoints;
|
||||
igs.skillObject = obj.skillObject->GetDefaultObject<UBaseSkillObject>();
|
||||
igs.selectedEffectIndex = obj.selectedEffect;
|
||||
|
||||
// Check valid effect index
|
||||
if(igs.selectedEffectIndex < 0 || igs.selectedEffectIndex >= igs.skillObject->abilityEffects.Num())
|
||||
{
|
||||
GWARNING("Ability effect out of range for ability " + obj.skillObject->GetName());
|
||||
continue;
|
||||
}
|
||||
|
||||
igs.selectedEffect = igs.skillObject->abilityEffects[igs.selectedEffectIndex];
|
||||
|
||||
igs.abilityType = -1;
|
||||
switch (igs.skillObject->skillShapeType)
|
||||
{
|
||||
case ESkillShapeType::Active:
|
||||
igs.abilityType = 0;
|
||||
break;
|
||||
case ESkillShapeType::Coop:
|
||||
case ESkillShapeType::Sustain:
|
||||
igs.abilityType = 1;
|
||||
break;
|
||||
case ESkillShapeType::Passive:
|
||||
igs.abilityType = 2;
|
||||
break;
|
||||
}
|
||||
|
||||
if(igs.abilityType == -1)
|
||||
{
|
||||
GWARNING("Ability type invalid for ability " + obj.skillObject->GetName());
|
||||
continue;
|
||||
}
|
||||
|
||||
m_skills.Add(igs);
|
||||
}
|
||||
}
|
||||
|
||||
AIngameSkillTree::AIngameSkillTree(const FObjectInitializer& init)
|
||||
{
|
||||
}
|
||||
|
||||
AIngameSkillTree::~AIngameSkillTree()
|
||||
{
|
||||
}
|
||||
|
||||
void AIngameSkillTree::BeginPlay()
|
||||
{
|
||||
}
|
||||
|
||||
TArray<FIngameSkillTreeSkill> AIngameSkillTree::GetSkillsForLevel(ADefaultPlayerState* player)
|
||||
{
|
||||
float powerLevel = player->GetLevel() / (float)player->GetMaxLevel();
|
||||
return GetSkillsForLevel(powerLevel);
|
||||
}
|
||||
|
||||
TArray<FIngameSkillTreeSkill> AIngameSkillTree::GetSkillsForLevel(float powerLevel)
|
||||
{
|
||||
TArray<FIngameSkillTreeSkill> res;
|
||||
for (int32 i = 0; i < m_skills.Num(); i++)
|
||||
{
|
||||
UpdatePowerForSkill(m_skills[i], powerLevel);
|
||||
if (m_skills[i].power > 0.0f)
|
||||
res.Add(m_skills[i]);
|
||||
}
|
||||
return res;
|
||||
}
|
||||
|
||||
void AIngameSkillTree::UpdatePowerForSkill(FIngameSkillTreeSkill& skill, float level)
|
||||
{
|
||||
float offset = (1.0f - level) * 16.0f;
|
||||
|
||||
int32 hexcount = 0;
|
||||
|
||||
for(int32 i = 0; i < skill.placedPoints.Num(); i++)
|
||||
{
|
||||
float YPos = (skill.placedPoints[i].X & 1) ?
|
||||
(float(skill.placedPoints[i].Y) + 0.5f) :
|
||||
(float(skill.placedPoints[i].Y));
|
||||
if(YPos >= offset) hexcount++;
|
||||
}
|
||||
|
||||
skill.level = hexcount;
|
||||
skill.power = float(hexcount) / float(skill.placedPoints.Num());
|
||||
}
|
||||
45
Source/UnrealProject/Abilities/IngameSkillTree.h
Normal file
45
Source/UnrealProject/Abilities/IngameSkillTree.h
Normal file
@@ -0,0 +1,45 @@
|
||||
#pragma once
|
||||
#include "SkillTreeState.h"
|
||||
#include "IngameSkillTree.Generated.h"
|
||||
|
||||
USTRUCT()
|
||||
struct FIngameSkillTreeSkill
|
||||
{
|
||||
GENERATED_BODY()
|
||||
public:
|
||||
UPROPERTY()
|
||||
TArray<FIntPoint> placedPoints;
|
||||
UPROPERTY(BlueprintReadOnly)
|
||||
class UBaseSkillObject* skillObject;
|
||||
UPROPERTY(BlueprintReadOnly)
|
||||
class UAbilityInfo* selectedEffect;
|
||||
UPROPERTY(BlueprintReadOnly)
|
||||
int32 selectedEffectIndex;
|
||||
UPROPERTY(BlueprintReadOnly)
|
||||
float power;
|
||||
UPROPERTY(BlueprintReadOnly)
|
||||
int32 level;
|
||||
|
||||
// Kind of ability on the button bar
|
||||
UPROPERTY(BlueprintReadOnly)
|
||||
int32 abilityType;
|
||||
};
|
||||
|
||||
UCLASS()
|
||||
class AIngameSkillTree : public AActor
|
||||
{
|
||||
GENERATED_BODY()
|
||||
public:
|
||||
AIngameSkillTree(const FObjectInitializer& init);
|
||||
~AIngameSkillTree();
|
||||
virtual void BeginPlay() override;
|
||||
|
||||
void BuildFromState(const FSkillTreeState& state);
|
||||
TArray<FIngameSkillTreeSkill> GetSkillsForLevel(class ADefaultPlayerState* player);
|
||||
TArray<FIngameSkillTreeSkill> GetSkillsForLevel(float level);
|
||||
void UpdatePowerForSkill(FIngameSkillTreeSkill& skill, float level);
|
||||
|
||||
private:
|
||||
UPROPERTY()
|
||||
TArray<FIngameSkillTreeSkill> m_skills;
|
||||
};
|
||||
60
Source/UnrealProject/Abilities/IntimidatingAuraTrigger.cpp
Normal file
60
Source/UnrealProject/Abilities/IntimidatingAuraTrigger.cpp
Normal file
@@ -0,0 +1,60 @@
|
||||
// Project Lab - NHTV Igad
|
||||
|
||||
#include "UnrealProject.h"
|
||||
#include "IntimidatingAuraTrigger.h"
|
||||
#include "NetworkCharacter.h"
|
||||
#include "NativeModifiers.h"
|
||||
#include "Modifier.h"
|
||||
|
||||
|
||||
|
||||
AIntimidatingAuraTrigger::AIntimidatingAuraTrigger()
|
||||
{
|
||||
|
||||
}
|
||||
void AIntimidatingAuraTrigger::BeginPlay()
|
||||
{
|
||||
Super::BeginPlay();
|
||||
}
|
||||
void AIntimidatingAuraTrigger::Tick(float deltaTime)
|
||||
{
|
||||
Super::Tick(deltaTime);
|
||||
}
|
||||
void AIntimidatingAuraTrigger::HitEvent(ANetworkCharacter* otherActor)
|
||||
{
|
||||
|
||||
ModifierManager* manager = otherActor->GetModifierManager();
|
||||
if (playerMap.Find(otherActor)== nullptr)
|
||||
{
|
||||
RERROR("2 modifiers in the intimidatin aura");
|
||||
}
|
||||
if (manager)
|
||||
{
|
||||
AAttackSpeedModifierConstant* ASModifier = GetWorld()->SpawnActor<AAttackSpeedModifierConstant>();
|
||||
ASModifier->lifeTime = 0;
|
||||
ASModifier->attackSpeedMultiplier = attackSpeedMultiplier;
|
||||
ASModifier->target = otherActor;
|
||||
manager->AddModifier(ASModifier);
|
||||
|
||||
ASpeedModifier* SModifier = GetWorld()->SpawnActor<ASpeedModifier>();
|
||||
SModifier->lifeTime = 0;
|
||||
SModifier->speedMultiplier = MovementSpeedMultiplier;
|
||||
SModifier->target = otherActor;
|
||||
manager->AddModifier(ASModifier);
|
||||
// std::pair <AModifier*, AModifier*> pair = std::pair <AModifier*, AModifier*>((AModifier*)ASModifier, (AModifier*)SModifier);
|
||||
playerMap.Add(otherActor, FIntimidatingAuraPair(ASModifier,SModifier));
|
||||
|
||||
}
|
||||
return Super::HitEvent(otherActor);
|
||||
}
|
||||
void AIntimidatingAuraTrigger::LeaveEvent(ANetworkCharacter* otherActor)
|
||||
{
|
||||
|
||||
auto it =playerMap.Find(otherActor);
|
||||
if (it == nullptr)
|
||||
return Super::LeaveEvent(otherActor);
|
||||
it->modifier0->ForceDestroy();
|
||||
it->modifier1->ForceDestroy();
|
||||
playerMap.Remove(otherActor);
|
||||
return Super::LeaveEvent(otherActor);
|
||||
}
|
||||
42
Source/UnrealProject/Abilities/IntimidatingAuraTrigger.h
Normal file
42
Source/UnrealProject/Abilities/IntimidatingAuraTrigger.h
Normal file
@@ -0,0 +1,42 @@
|
||||
// Project Lab - NHTV Igad
|
||||
|
||||
#pragma once
|
||||
#include "Abilities/ConeTrigger.h"
|
||||
#include "IntimidatingAuraTrigger.generated.h"
|
||||
|
||||
/**
|
||||
*
|
||||
*/
|
||||
USTRUCT()
|
||||
struct FIntimidatingAuraPair
|
||||
{
|
||||
GENERATED_BODY()
|
||||
public:
|
||||
FIntimidatingAuraPair(){}
|
||||
FIntimidatingAuraPair(class AModifier* m0, class AModifier*m1) { modifier0 = m0; modifier1 = m1; }
|
||||
UPROPERTY()
|
||||
class AModifier* modifier0;
|
||||
UPROPERTY()
|
||||
class AModifier* modifier1;
|
||||
};
|
||||
|
||||
UCLASS()
|
||||
class UNREALPROJECT_API AIntimidatingAuraTrigger : public AConeTrigger
|
||||
{
|
||||
GENERATED_BODY()
|
||||
public:
|
||||
AIntimidatingAuraTrigger();
|
||||
|
||||
virtual void BeginPlay() override;
|
||||
virtual void Tick(float DeltaSeconds) override;
|
||||
virtual void HitEvent(class ANetworkCharacter* otherActor)override;
|
||||
virtual void LeaveEvent(class ANetworkCharacter* otherActor)override;
|
||||
UPROPERTY()
|
||||
TMap<class ANetworkCharacter*, FIntimidatingAuraPair > playerMap;
|
||||
UPROPERTY(meta = (ExposeOnSpawn), BlueprintReadWrite)
|
||||
float attackSpeedMultiplier;
|
||||
UPROPERTY(meta = (ExposeOnSpawn), BlueprintReadWrite)
|
||||
float MovementSpeedMultiplier;
|
||||
|
||||
|
||||
};
|
||||
477
Source/UnrealProject/Abilities/Modifier.cpp
Normal file
477
Source/UnrealProject/Abilities/Modifier.cpp
Normal file
@@ -0,0 +1,477 @@
|
||||
// Project Lab - NHTV Igad
|
||||
|
||||
#include "UnrealProject.h"
|
||||
#include "Modifier.h"
|
||||
|
||||
#include "NetworkCharacter.h"
|
||||
#include "NativeModifiers.h"
|
||||
|
||||
ModifierManager::ModifierManager(class ANetworkCharacter* character)
|
||||
: m_character(character)
|
||||
{
|
||||
check(m_character);
|
||||
}
|
||||
ModifierManager::~ModifierManager()
|
||||
{
|
||||
ClearModifiers(false);
|
||||
}
|
||||
|
||||
void ModifierManager::ClearModifiers(bool shouldCallEnd)
|
||||
{
|
||||
for (AModifier* modifier : m_modifiers)
|
||||
{
|
||||
if (shouldCallEnd)
|
||||
modifier->m_started = false;
|
||||
modifier->Destroy();
|
||||
}
|
||||
m_modifiers.SetNum(0);
|
||||
RecalculateCharacter();
|
||||
}
|
||||
void ModifierManager::Tick(float DeltaSeconds)
|
||||
{
|
||||
for (int32 i = 0; i < m_modifiers.Num();)
|
||||
{
|
||||
AModifier* modifier = m_modifiers[i];
|
||||
if (modifier != nullptr && IsValid(modifier))
|
||||
{
|
||||
if (modifier->ShouldBeRemoved() || modifier->m_forceDestroy)
|
||||
{
|
||||
modifier->Destroy();
|
||||
m_modifiers.RemoveAt(i);
|
||||
RecalculateCharacter();
|
||||
continue;
|
||||
}
|
||||
else
|
||||
{
|
||||
modifier->Tick(DeltaSeconds);
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
m_modifiers.RemoveAt(i);
|
||||
if (modifier != 0)
|
||||
{
|
||||
GERROR("Modifier \"" + modifier->GetName() + "\" was destroyed, but not in the usual way");
|
||||
}
|
||||
RecalculateCharacter();
|
||||
continue;
|
||||
}
|
||||
i++;
|
||||
}
|
||||
}
|
||||
|
||||
AModifier* ModifierManager::AddModifier(TSubclassOf<class AModifier> buffClass, float duration)
|
||||
{
|
||||
if (!buffClass)
|
||||
{
|
||||
FWERROR(L"Invalid modifier class");
|
||||
return nullptr;
|
||||
}
|
||||
UWorld* world = m_character->GetWorld();
|
||||
check(world);
|
||||
AModifier* modifier = world->SpawnActor<AModifier>(buffClass);
|
||||
modifier->m_Init(m_character, duration);
|
||||
return AddModifier(modifier);
|
||||
}
|
||||
AModifier* ModifierManager::AddModifier(AModifier* modifierInstance)
|
||||
{
|
||||
if (!modifierInstance)
|
||||
{
|
||||
FWERROR(L"Invalid buff added");
|
||||
return nullptr;
|
||||
}
|
||||
m_modifiers.Add(modifierInstance);
|
||||
m_modifiers.Sort([](const AModifier& i, const AModifier& j)->bool { return i > j; });
|
||||
modifierInstance->m_Start();
|
||||
RecalculateCharacter();
|
||||
return modifierInstance;
|
||||
}
|
||||
|
||||
TArray<AModifier*> ModifierManager::GetModifiersOfClass(TSubclassOf<class AModifier> modifierClass)
|
||||
{
|
||||
TArray<AModifier*> ret;
|
||||
for (AModifier* mod : m_modifiers)
|
||||
{
|
||||
if (mod->IsA(modifierClass))
|
||||
{
|
||||
ret.Add(mod);
|
||||
}
|
||||
}
|
||||
return ret;
|
||||
}
|
||||
void ModifierManager::BroadcastManaDrainFailed()
|
||||
{
|
||||
for(AModifier* modifier : m_modifiers)
|
||||
{
|
||||
if (modifier != nullptr && IsValid(modifier))
|
||||
{
|
||||
modifier->onManaDrainFailed.Broadcast();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
//reset all modifiable stats so that modifiers can recalculate them
|
||||
void ModifierManager::m_Reset()
|
||||
{
|
||||
m_character->ResetModifiers();
|
||||
}
|
||||
void ModifierManager::RecalculateCharacter()
|
||||
{
|
||||
m_Reset();
|
||||
for (AModifier* it : m_modifiers)
|
||||
{
|
||||
it->ModifyCharacter();
|
||||
}
|
||||
m_character->CheckStatsOverflow();
|
||||
}
|
||||
|
||||
|
||||
AModifier::AModifier()
|
||||
{
|
||||
PrimaryActorTick.bCanEverTick = true;
|
||||
bReplicates = true;
|
||||
lifeTime = 0;
|
||||
ticksPerSecond = 0;
|
||||
m_dontDestroy = false;
|
||||
RootComponent = CreateDefaultSubobject<USceneComponent>("Root");
|
||||
}
|
||||
|
||||
bool AModifier::operator < (const AModifier& other) const
|
||||
{
|
||||
return priority < other.priority;
|
||||
}
|
||||
bool AModifier::operator > (const AModifier& other) const
|
||||
{
|
||||
return priority > other.priority;
|
||||
}
|
||||
|
||||
|
||||
void AModifier::EndPlay(const EEndPlayReason::Type EndPlayReason)
|
||||
{
|
||||
if (m_started)
|
||||
ModifierEnd();
|
||||
}
|
||||
|
||||
void AModifier::ModifyCharacter()
|
||||
{
|
||||
}
|
||||
bool AModifier::ShouldBeRemoved() const
|
||||
{
|
||||
return (lifeTime <= 0.0f) && !m_dontDestroy;
|
||||
}
|
||||
void AModifier::ForceDestroy()
|
||||
{
|
||||
m_forceDestroy = true;
|
||||
}
|
||||
void AModifier::ModifierTick()
|
||||
{
|
||||
}
|
||||
|
||||
void AModifier::ModifierStart()
|
||||
{
|
||||
|
||||
}
|
||||
|
||||
void AModifier::ModifierEnd()
|
||||
{
|
||||
|
||||
}
|
||||
bool AModifier::OnDamage(int32& damage, ANetworkCharacter* damageDealer)
|
||||
{
|
||||
return true;
|
||||
}
|
||||
bool AModifier::OnDealDamage(int32& damage, ANetworkCharacter* damageTaker)
|
||||
{
|
||||
return true;
|
||||
}
|
||||
void AModifier::OnStandardAttack(ANetworkCharacter* character)
|
||||
{
|
||||
return;
|
||||
}
|
||||
void AModifier::AfterDamage(int32)
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
float AModifier::m_MultiplyEffect(float value, EffectType effectType, bool positive)
|
||||
{
|
||||
switch (effectType)
|
||||
{
|
||||
case EffectType::additive:
|
||||
{
|
||||
if (!positive)
|
||||
return value * target->m_negativeEffectMultiplier;
|
||||
else
|
||||
return value * target->m_positiveEffectMultiplier;
|
||||
}
|
||||
case EffectType::multiplicative:
|
||||
{
|
||||
if (!positive)
|
||||
return value * (1.0f/target->m_negativeEffectMultiplier);
|
||||
else
|
||||
return value * target->m_positiveEffectMultiplier;
|
||||
}
|
||||
default:
|
||||
return 0.0f;
|
||||
}
|
||||
}
|
||||
|
||||
//character modification functions
|
||||
void AModifier::m_AddMaxMana(int32 maxMana)
|
||||
{
|
||||
if (IsValid(target))
|
||||
{
|
||||
maxMana = m_MultiplyEffect(maxMana, additive, maxMana>0);
|
||||
target->m_maxMana += maxMana;
|
||||
target->AddMana(maxMana);
|
||||
}
|
||||
}
|
||||
void AModifier::m_MultiplyManaRegenMultiplier(float manaRegenMultiplier)
|
||||
{
|
||||
if (IsValid(target))
|
||||
{
|
||||
manaRegenMultiplier = m_MultiplyEffect(manaRegenMultiplier, multiplicative, manaRegenMultiplier>1);
|
||||
target->m_manaRegenMultiplier *= manaRegenMultiplier;
|
||||
}
|
||||
}
|
||||
void AModifier::m_MultiplyManaUsageMultiplier(float manaUsageMultiplier)
|
||||
{
|
||||
if (IsValid(target))
|
||||
{
|
||||
manaUsageMultiplier = m_MultiplyEffect(manaUsageMultiplier, multiplicative, manaUsageMultiplier>1);
|
||||
target->m_manaUsageMultiplier *= manaUsageMultiplier;
|
||||
}
|
||||
}
|
||||
void AModifier::m_AddBlockedMana(int32 blockedMana)
|
||||
{
|
||||
if (IsValid(target))
|
||||
{
|
||||
if (target->m_blockedMana + blockedMana > target->m_maxMana)
|
||||
{
|
||||
ModifierManager* mod = character->GetModifierManager();
|
||||
if (mod != nullptr)
|
||||
mod->BroadcastManaDrainFailed();
|
||||
}
|
||||
else
|
||||
target->m_blockedMana += blockedMana;
|
||||
}
|
||||
}
|
||||
void AModifier::m_AddDamageMultiplier(float multiplier)
|
||||
{
|
||||
if (IsValid(target))
|
||||
{
|
||||
multiplier = m_MultiplyEffect(multiplier, additive, multiplier > 0);
|
||||
target->m_damageMultiplier += multiplier;
|
||||
}
|
||||
}
|
||||
void AModifier::m_AddMaxHealth(int32 health)
|
||||
{
|
||||
if (IsValid(target))
|
||||
{
|
||||
health = m_MultiplyEffect(health, additive, health >0);
|
||||
target->m_maxHealth += health;
|
||||
}
|
||||
}
|
||||
void AModifier::m_AddMovementSpeedMultiplier(float multiplier)
|
||||
{
|
||||
if (IsValid(target))
|
||||
{
|
||||
multiplier = m_MultiplyEffect(multiplier, multiplicative, multiplier > 0);
|
||||
target->GetCharacterMovement()->MaxWalkSpeed *= multiplier;
|
||||
}
|
||||
}
|
||||
void AModifier::m_AddIgnoreArmor(float normalizedPercentage)
|
||||
{
|
||||
if (IsValid(target))
|
||||
{
|
||||
normalizedPercentage = m_MultiplyEffect(normalizedPercentage, additive, normalizedPercentage > 0);
|
||||
target->m_ignoreArmor += normalizedPercentage;
|
||||
}
|
||||
}
|
||||
void AModifier::m_AddArmor(float armor)
|
||||
{
|
||||
if (IsValid(target))
|
||||
{
|
||||
armor = m_MultiplyEffect(armor, additive, armor > 0);
|
||||
target->m_armor += armor;
|
||||
}
|
||||
}
|
||||
|
||||
void AModifier::m_MultiplyArmor(float armor)
|
||||
{
|
||||
if (IsValid(target))
|
||||
{
|
||||
armor = m_MultiplyEffect(armor, multiplicative, armor > 1);
|
||||
target->m_armor *= armor;
|
||||
}
|
||||
}
|
||||
void AModifier::m_MultiplyAttackDamage(float damageMultiplier)
|
||||
{
|
||||
if (IsValid(target))
|
||||
{
|
||||
damageMultiplier = m_MultiplyEffect(damageMultiplier, multiplicative, damageMultiplier>1);
|
||||
FPRINT(damageMultiplier);
|
||||
target->m_attackDamageMultiplier *= damageMultiplier;
|
||||
}
|
||||
}
|
||||
void AModifier::m_AddMagicDamage(float damageMultiplier)
|
||||
{
|
||||
if (IsValid(target))
|
||||
{
|
||||
damageMultiplier = m_MultiplyEffect(damageMultiplier, multiplicative, damageMultiplier > 1);
|
||||
target->m_magicDamageMultiplier *= damageMultiplier;
|
||||
}
|
||||
}
|
||||
void AModifier::m_AddAttackSpeed(float amount)
|
||||
{
|
||||
if (IsValid(target))
|
||||
{
|
||||
amount = m_MultiplyEffect(amount, additive, amount> 0);
|
||||
target->m_attackSpeed += amount;
|
||||
}
|
||||
}
|
||||
void AModifier::m_AddAttackSpeedMultiplier(float multiplier)
|
||||
{
|
||||
if (IsValid(target))
|
||||
{
|
||||
multiplier = m_MultiplyEffect(multiplier, multiplicative, multiplier > 1);
|
||||
target->m_attackSpeed *= multiplier;
|
||||
}
|
||||
}
|
||||
void AModifier::m_AddCooldownReduction(float amount)
|
||||
{
|
||||
if (IsValid(target))
|
||||
{
|
||||
amount = m_MultiplyEffect(amount, additive, amount > 0);
|
||||
target->m_cooldownReduction += amount;
|
||||
}
|
||||
}
|
||||
void AModifier::m_AddCooldownReductionMultiplier(float multiplier)
|
||||
{
|
||||
if (IsValid(target))
|
||||
{
|
||||
multiplier = m_MultiplyEffect(multiplier, multiplicative, multiplier > 1);
|
||||
target->m_cooldownReduction *= multiplier;
|
||||
}
|
||||
}
|
||||
void AModifier::m_MultiplyDamageTakenMultiplier(float multiplier)
|
||||
{
|
||||
if (IsValid(target))
|
||||
{
|
||||
multiplier = m_MultiplyEffect(multiplier, multiplicative, multiplier < 0);
|
||||
target->m_damageTakenMultiplier *= multiplier;
|
||||
}
|
||||
}
|
||||
void AModifier::m_MultiplyPositiveEffectMultiplier(float multiplier)
|
||||
{
|
||||
if (IsValid(target))
|
||||
target->m_positiveEffectMultiplier *= multiplier;
|
||||
}
|
||||
void AModifier::m_MultiplyNegativeEffectMultiplier(float multiplier)
|
||||
{
|
||||
if (IsValid(target))
|
||||
target->m_negativeEffectMultiplier *= multiplier;
|
||||
}
|
||||
void AModifier::m_addChannelMovementSpeedMultiplier(float channelMovementSpeedMultiplier)
|
||||
{
|
||||
if (IsValid(target))
|
||||
{
|
||||
channelMovementSpeedMultiplier = m_MultiplyEffect(channelMovementSpeedMultiplier, additive, channelMovementSpeedMultiplier > 0);
|
||||
target->m_castingMovementspeedMultiplier += channelMovementSpeedMultiplier;
|
||||
}
|
||||
}
|
||||
void AModifier::m_MultiplyMagicDamageMultiplier(float multiplier)
|
||||
{
|
||||
if (IsValid(target))
|
||||
{
|
||||
multiplier = m_MultiplyEffect(multiplier, additive, multiplier> 0);
|
||||
target->m_magicDamageMultiplier *= multiplier;
|
||||
}
|
||||
}
|
||||
|
||||
void AModifier::m_AddStunnedState()
|
||||
{
|
||||
if (IsValid(target))
|
||||
if (target->canBeStunned)
|
||||
{
|
||||
target->m_stunned = true;
|
||||
target->m_InteruptSpellcasting();
|
||||
}
|
||||
}
|
||||
|
||||
void AModifier::m_AddSilencedState()
|
||||
{
|
||||
if (IsValid(target))
|
||||
if (target->canBeSilenced)
|
||||
{
|
||||
target->m_silenceCount++;
|
||||
target->m_silenced = true;
|
||||
target->m_InteruptSpellcasting();
|
||||
}
|
||||
}
|
||||
|
||||
void AModifier::m_RemoveSilencedState()
|
||||
{
|
||||
if (IsValid(target))
|
||||
{
|
||||
target->m_silenceCount--;
|
||||
if (target->m_silenceCount == 0)
|
||||
{
|
||||
target->m_silenced = false;
|
||||
}
|
||||
else if (target->m_silenceCount < 0)
|
||||
{
|
||||
FERROR("m_silenceCount <0 in removeSilenceState");
|
||||
}
|
||||
}
|
||||
}
|
||||
void AModifier::m_Start()
|
||||
{
|
||||
if (!m_started)
|
||||
{
|
||||
if (IsValid(target))
|
||||
{
|
||||
if (ticksPerSecond <= 0)
|
||||
m_tickRate = 0.0f;
|
||||
else
|
||||
m_tickRate = 1.0f / ticksPerSecond;
|
||||
m_started = true;
|
||||
if (lifeTime == 0)
|
||||
{
|
||||
m_dontDestroy = true;
|
||||
}
|
||||
ModifierStart();
|
||||
}
|
||||
else
|
||||
{
|
||||
GWERROR(L"Can't start modifier " + GetName() + " target not set");
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
GWERROR(L"modifier started twice");
|
||||
}
|
||||
}
|
||||
void AModifier::Tick(float DeltaTime)
|
||||
{
|
||||
Super::Tick(DeltaTime);
|
||||
if (!IsValid(target) || !m_started)
|
||||
return;
|
||||
if (ShouldBeRemoved() || m_forceDestroy)
|
||||
return;
|
||||
if (m_tickRate > 0.0f)
|
||||
{
|
||||
m_tickTimer += DeltaTime;
|
||||
while (m_tickTimer >= m_tickRate)
|
||||
{
|
||||
ModifierTick();
|
||||
m_tickTimer -= m_tickRate;
|
||||
}
|
||||
}
|
||||
lifeTime -= DeltaTime;
|
||||
}
|
||||
void AModifier::m_Init(class ANetworkCharacter* a_character, float duration)
|
||||
{
|
||||
target = a_character;
|
||||
lifeTime = duration;
|
||||
}
|
||||
122
Source/UnrealProject/Abilities/Modifier.h
Normal file
122
Source/UnrealProject/Abilities/Modifier.h
Normal file
@@ -0,0 +1,122 @@
|
||||
// Project Lab - NHTV Igad
|
||||
|
||||
#pragma once
|
||||
|
||||
#include "DealDamageProxy.h"
|
||||
#include <list>
|
||||
#include "AbilityInfo.h"
|
||||
#include "Modifier.generated.h"
|
||||
|
||||
class ModifierManager
|
||||
{
|
||||
public:
|
||||
ModifierManager(class ANetworkCharacter* character);
|
||||
~ModifierManager();
|
||||
|
||||
void ClearModifiers(bool shouldCallEnd = true);
|
||||
void Tick(float DeltaSeconds);
|
||||
|
||||
class AModifier* AddModifier(TSubclassOf<class AModifier> modifierClass, float duration);
|
||||
class AModifier* AddModifier(AModifier* modifierInstance);
|
||||
TArray<class AModifier*> GetModifiersOfClass(TSubclassOf<class AModifier> modifierClass);
|
||||
void RecalculateCharacter();
|
||||
void BroadcastManaDrainFailed();
|
||||
|
||||
private:
|
||||
void m_Reset();
|
||||
|
||||
friend class ANetworkCharacter;
|
||||
TArray<class AModifier*> m_modifiers;
|
||||
class ANetworkCharacter* m_character;
|
||||
};
|
||||
|
||||
UCLASS()
|
||||
class UNREALPROJECT_API AModifier : public ADealDamageProxy
|
||||
{
|
||||
GENERATED_BODY()
|
||||
|
||||
friend class ADefaultPlayerController;
|
||||
friend class ModifierManager;
|
||||
friend class ANetworkCharacter;
|
||||
friend class AOnDamageModifier;
|
||||
|
||||
public:
|
||||
AModifier();
|
||||
virtual void Tick(float DeltaSeconds) override final;
|
||||
virtual void EndPlay(const EEndPlayReason::Type EndPlayReason) override;
|
||||
|
||||
virtual void ModifierTick();
|
||||
virtual void ModifierStart();
|
||||
virtual void ModifierEnd();
|
||||
virtual bool OnDamage(int32& damage, ANetworkCharacter* damageDealer);
|
||||
virtual bool OnDealDamage(int32& damage, ANetworkCharacter* damageTaker);
|
||||
virtual void OnStandardAttack(ANetworkCharacter* character);
|
||||
virtual void AfterDamage(int32 damage);
|
||||
|
||||
UFUNCTION(BlueprintCallable, Category = "modifiers")
|
||||
virtual bool ShouldBeRemoved() const;
|
||||
virtual void ModifyCharacter();
|
||||
UFUNCTION(BlueprintCallable, Category = "modifiers")
|
||||
void ForceDestroy();
|
||||
|
||||
UPROPERTY(VisibleAnywhere, BlueprintReadWrite, Category = "modifiers", meta=(ExposeOnSpawn))
|
||||
float lifeTime;
|
||||
UPROPERTY()
|
||||
ANetworkCharacter* target;
|
||||
UPROPERTY(EditDefaultsOnly, Category = "modifiers")
|
||||
int32 ticksPerSecond;
|
||||
UPROPERTY(VisibleAnywhere, BlueprintReadWrite, Category = "modifiers", meta = (ExposeOnSpawn))
|
||||
int32 priority;
|
||||
|
||||
DECLARE_DYNAMIC_MULTICAST_DELEGATE(FOnManaDrainFailed);
|
||||
|
||||
UPROPERTY(BlueprintAssignable, Category = "Mana Drain")
|
||||
FOnManaDrainFailed onManaDrainFailed;
|
||||
|
||||
bool operator < (const AModifier&) const;
|
||||
bool operator > (const AModifier&) const;
|
||||
protected:
|
||||
enum EffectType
|
||||
{
|
||||
additive = 0,
|
||||
multiplicative
|
||||
};
|
||||
float m_MultiplyEffect(float value, EffectType effectType, bool positive);
|
||||
|
||||
// Modification Functions
|
||||
void m_AddMaxMana(int32 maxMana);
|
||||
void m_MultiplyManaRegenMultiplier(float manaRegenMultiplier);
|
||||
void m_AddBlockedMana(int32 blockedMana);
|
||||
void m_AddDamageMultiplier(float multiplier);
|
||||
void m_AddIgnoreArmor(float normalizedPercentage);
|
||||
void m_AddArmor(float armor);
|
||||
void m_MultiplyArmor(float armor);
|
||||
void m_MultiplyDamageTakenMultiplier(float multiplier);
|
||||
void m_AddMaxHealth(int32 health);
|
||||
void m_AddMovementSpeedMultiplier(float multiplier);
|
||||
void m_AddAttackSpeed(float amount);
|
||||
void m_AddAttackSpeedMultiplier(float multiplier);
|
||||
void m_AddCooldownReduction(float amount);
|
||||
void m_AddCooldownReductionMultiplier(float multiplier);
|
||||
void m_AddStunnedState();
|
||||
void m_AddSilencedState();
|
||||
void m_RemoveSilencedState();
|
||||
void m_MultiplyAttackDamage(float damageMultiplier);
|
||||
void m_AddMagicDamage(float damageMultiplier);
|
||||
void m_MultiplyManaUsageMultiplier(float manaUsageMultiplier);
|
||||
void m_MultiplyNegativeEffectMultiplier(float effectMultiplier);
|
||||
void m_MultiplyPositiveEffectMultiplier(float effectMultiplier);
|
||||
void m_MultiplyMagicDamageMultiplier(float effectMultiplier);
|
||||
void m_addChannelMovementSpeedMultiplier(float channelMovementSpeedMultiplier);
|
||||
float m_tickRate;
|
||||
|
||||
private:
|
||||
void m_Start();
|
||||
void m_Init(class ANetworkCharacter* character, float duration);
|
||||
|
||||
bool m_forceDestroy;
|
||||
int m_debugWarning;
|
||||
bool m_started;
|
||||
float m_tickTimer;
|
||||
bool m_dontDestroy;
|
||||
};
|
||||
704
Source/UnrealProject/Abilities/NativeModifiers.cpp
Normal file
704
Source/UnrealProject/Abilities/NativeModifiers.cpp
Normal file
@@ -0,0 +1,704 @@
|
||||
// Project Lab - NHTV Igad
|
||||
|
||||
#include "UnrealProject.h"
|
||||
|
||||
#include "NetworkCharacter.h"
|
||||
#include "NetworkPlayer.h"
|
||||
#include "NativeModifiers.h"
|
||||
#include "EffectFunctionLibrary.h"
|
||||
#include "Effect.h"
|
||||
|
||||
#define standardTicksPerSecond 20
|
||||
//SpeedModifier
|
||||
ASpeedModifier::ASpeedModifier()
|
||||
{
|
||||
|
||||
}
|
||||
|
||||
void ASpeedModifier::ModifyCharacter()
|
||||
{
|
||||
UCharacterMovementComponent* characterMovement = target->GetCharacterMovement();
|
||||
characterMovement->MaxWalkSpeed *= speedMultiplier;
|
||||
}
|
||||
|
||||
//RegenModifier
|
||||
ARegenModifier::ARegenModifier()
|
||||
{
|
||||
ticksPerSecond = 5;
|
||||
}
|
||||
|
||||
bool ARegenModifier::ShouldBeRemoved() const
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
void ARegenModifier::ModifierTick()
|
||||
{
|
||||
if ( m_noDamageTime < target->GetTimeSinceDamage())
|
||||
target->AddHealth(regenPerTick);
|
||||
}
|
||||
|
||||
//RegenModier
|
||||
AManaRegenModifier::AManaRegenModifier()
|
||||
{
|
||||
ticksPerSecond = standardTicksPerSecond;
|
||||
}
|
||||
|
||||
bool AManaRegenModifier::ShouldBeRemoved() const
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
void AManaRegenModifier::ModifierTick()
|
||||
{
|
||||
target->AddMana(regenPerTick);
|
||||
}
|
||||
|
||||
//DOTModifier
|
||||
ADOTModifier::ADOTModifier()
|
||||
{
|
||||
ticksPerSecond = 3;
|
||||
}
|
||||
|
||||
void ADOTModifier::ModifierTick()
|
||||
{
|
||||
if (IsValid(target))
|
||||
target->DealDamage(this, damagePerTick, 0.0f);
|
||||
}
|
||||
|
||||
//ADModifier
|
||||
AADModifier::AADModifier()
|
||||
{
|
||||
|
||||
}
|
||||
|
||||
void AADModifier::ModifyCharacter()
|
||||
{
|
||||
float add = ScaleGraphCurve(scalingGraph);
|
||||
m_MultiplyAttackDamage(add);
|
||||
}
|
||||
//ADModifier
|
||||
AAPModifier::AAPModifier()
|
||||
{
|
||||
|
||||
}
|
||||
|
||||
void AAPModifier::ModifyCharacter()
|
||||
{
|
||||
float add = ScaleGraphCurve(scalingGraph);
|
||||
m_AddMagicDamage(add);
|
||||
}
|
||||
|
||||
//MaxHealthModifier
|
||||
AMaxHealthModifier::AMaxHealthModifier()
|
||||
{
|
||||
}
|
||||
|
||||
void AMaxHealthModifier::ModifyCharacter()
|
||||
{
|
||||
//m_AddMaxHealth(bonusMaxHealth);
|
||||
float add = ScaleGraphCurve(scalingGraph);
|
||||
m_AddMaxHealth(add);
|
||||
}
|
||||
|
||||
//AConstMaxHealthModifier
|
||||
AConstMaxHealthModifier::AConstMaxHealthModifier()
|
||||
{
|
||||
}
|
||||
|
||||
void AConstMaxHealthModifier::ModifyCharacter()
|
||||
{
|
||||
m_AddMaxHealth(add);
|
||||
}
|
||||
|
||||
//BlockManaModifier
|
||||
ABlockManaModifier::ABlockManaModifier()
|
||||
{
|
||||
|
||||
}
|
||||
|
||||
void ABlockManaModifier::ModifyCharacter()
|
||||
{
|
||||
m_AddBlockedMana(Mana);
|
||||
}
|
||||
|
||||
//ManaPerSecModifier
|
||||
AManaDrainModifier::AManaDrainModifier()
|
||||
{
|
||||
ticksPerSecond = standardTicksPerSecond;
|
||||
}
|
||||
|
||||
void AManaDrainModifier::ModifierTick()
|
||||
{
|
||||
if (ManaPerTick > target->GetMana())
|
||||
{
|
||||
ModifierManager* mod = character->GetModifierManager();
|
||||
if (mod != nullptr)
|
||||
mod->BroadcastManaDrainFailed();
|
||||
}
|
||||
else
|
||||
{
|
||||
target->RemoveMana(ManaPerTick);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
//ManaPerSecModifier
|
||||
AManaDrainCurveModifier::AManaDrainCurveModifier()
|
||||
{
|
||||
ticksPerSecond = standardTicksPerSecond;
|
||||
}
|
||||
|
||||
void AManaDrainCurveModifier::ModifierTick()
|
||||
{
|
||||
float mana = ScaleGraphCurve(ManaPerTick);
|
||||
if (mana > target->GetMana())
|
||||
{
|
||||
ModifierManager* mod = character->GetModifierManager();
|
||||
if (mod != nullptr)
|
||||
mod->BroadcastManaDrainFailed();
|
||||
}
|
||||
else
|
||||
{
|
||||
target->RemoveMana(mana);
|
||||
}
|
||||
}
|
||||
|
||||
//HealthModifier
|
||||
AHealModifier::AHealModifier()
|
||||
{
|
||||
ticksPerSecond = standardTicksPerSecond;
|
||||
}
|
||||
|
||||
void AHealModifier::ModifierTick()
|
||||
{
|
||||
target->AddHealth(health);
|
||||
m_shouldBeRemoved = true;
|
||||
}
|
||||
|
||||
bool AHealModifier::ShouldBeRemoved() const
|
||||
{
|
||||
return m_shouldBeRemoved;
|
||||
}
|
||||
|
||||
//stunModifier
|
||||
static TSubclassOf<AEffect> stunEffectType;
|
||||
AStunModifier::AStunModifier()
|
||||
{
|
||||
stunEffectType = ConstructorHelpers::FClassFinder<AEffect>(TEXT("/Game/Assets/Art/Effects/FX_Stunned")).Class;
|
||||
}
|
||||
void AStunModifier::ModifierStart()
|
||||
{
|
||||
if (target->CanBeStunned() && lifeTime > 0.0f)
|
||||
UEffectFunctionLibrary::CreateEffect(target, stunEffectType, target,0.0f, lifeTime);
|
||||
}
|
||||
void AStunModifier::ModifierEnd()
|
||||
{
|
||||
}
|
||||
void AStunModifier::ModifierTick()
|
||||
{
|
||||
}
|
||||
void AStunModifier::ModifyCharacter()
|
||||
{
|
||||
m_AddStunnedState();
|
||||
}
|
||||
//stunModifier
|
||||
|
||||
ASilenceModifier::ASilenceModifier()
|
||||
{
|
||||
}
|
||||
void ASilenceModifier::ModifierStart()
|
||||
{
|
||||
m_AddSilencedState();
|
||||
}
|
||||
|
||||
|
||||
void ASilenceModifier::ModifierEnd()
|
||||
{
|
||||
m_RemoveSilencedState();
|
||||
}
|
||||
//ADamageTakenModifier
|
||||
ADamageTakenModifier::ADamageTakenModifier()
|
||||
{
|
||||
|
||||
}
|
||||
|
||||
void ADamageTakenModifier::ModifyCharacter()
|
||||
{
|
||||
m_MultiplyDamageTakenMultiplier(damageTakenMultiplier);
|
||||
}
|
||||
|
||||
|
||||
bool ADamageTakenModifier::ShouldBeRemoved() const
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
//AVisibilityModifier
|
||||
AVisibilityModifier::AVisibilityModifier()
|
||||
{
|
||||
|
||||
}
|
||||
|
||||
bool AVisibilityModifier::ShouldBeRemoved() const
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
void AVisibilityModifier::ModifierTick()
|
||||
{
|
||||
m_timer -= m_tickRate;
|
||||
if (m_timer < 0 && visibleTime > 0 && visibleBreak> 0)
|
||||
{
|
||||
if (m_visible)
|
||||
{
|
||||
m_timer = visibleBreak;
|
||||
target->m_visible = false;
|
||||
}
|
||||
else
|
||||
{
|
||||
m_timer = visibleTime;
|
||||
target->m_visible = true;
|
||||
}
|
||||
|
||||
m_visible = !m_visible;
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
//ADodgeDeathModifier
|
||||
ADodgeDeathModifier::ADodgeDeathModifier()
|
||||
{
|
||||
|
||||
}
|
||||
|
||||
bool ADodgeDeathModifier::ShouldBeRemoved() const
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
|
||||
void ADodgeDeathModifier::ModifierTick()
|
||||
{
|
||||
m_timer += m_tickRate;
|
||||
}
|
||||
bool ADodgeDeathModifier::OnDamage(int32& damage, ANetworkCharacter* damageDealer)
|
||||
{
|
||||
if (damage >= target->GetHealth() && m_timer > ScaleGraphCurve(cooldown))
|
||||
{
|
||||
float chance = (float)(rand() % 100) / 100.0f;
|
||||
if (ScaleGraphCurve(dodgeChance) > chance)
|
||||
{
|
||||
target->AddHealth(ScaleGraphCurve(heal));
|
||||
m_timer = 0;
|
||||
return false;
|
||||
}
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
//ACooldownReductionModifier
|
||||
ACooldownReductionModifier::ACooldownReductionModifier()
|
||||
{
|
||||
}
|
||||
|
||||
void ACooldownReductionModifier::ModifyCharacter()
|
||||
{
|
||||
float add = ScaleGraphCurve(scalingGraph);
|
||||
m_AddCooldownReduction(add);
|
||||
}
|
||||
|
||||
//AAttackSpeedModifier
|
||||
AAttackSpeedModifier::AAttackSpeedModifier()
|
||||
{
|
||||
|
||||
}
|
||||
|
||||
void AAttackSpeedModifier::ModifyCharacter()
|
||||
{
|
||||
float add = ScaleGraphCurve(scalingGraph);
|
||||
m_AddAttackSpeedMultiplier(add);
|
||||
}
|
||||
|
||||
//AAttackSpeedModifierConstant
|
||||
AAttackSpeedModifierConstant::AAttackSpeedModifierConstant()
|
||||
{
|
||||
|
||||
}
|
||||
|
||||
void AAttackSpeedModifierConstant::ModifyCharacter()
|
||||
{
|
||||
m_AddAttackSpeedMultiplier(attackSpeedMultiplier);
|
||||
}
|
||||
|
||||
bool AAttackSpeedModifierConstant::ShouldBeRemoved() const
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
//AArmorIgnoreModifier
|
||||
AArmorIgnoreModifier::AArmorIgnoreModifier()
|
||||
{
|
||||
|
||||
}
|
||||
|
||||
void AArmorIgnoreModifier::ModifyCharacter()
|
||||
{
|
||||
float add = ScaleGraphCurve(scalingGraph);
|
||||
m_AddIgnoreArmor(add);
|
||||
}
|
||||
|
||||
|
||||
//ADodgeDeathModifier
|
||||
ABeserkModifier::ABeserkModifier()
|
||||
{
|
||||
ticksPerSecond = standardTicksPerSecond;
|
||||
m_active = false;
|
||||
}
|
||||
|
||||
bool ABeserkModifier::ShouldBeRemoved() const
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
void ABeserkModifier::AfterDamage(int32 damage)
|
||||
{
|
||||
float deltahealth = (float)target->GetHealth() / (float)target->GetMaxHealth();
|
||||
|
||||
if (deltahealth < ScaleGraphCurve(healthThreshold))
|
||||
{
|
||||
if (!m_active)
|
||||
{
|
||||
m_hasToActivate = true;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
|
||||
void ABeserkModifier::ModifierTick()
|
||||
{
|
||||
float deltahealth = (float)target->GetHealth() / (float)target->GetMaxHealth();
|
||||
if (m_hasToActivate)
|
||||
{
|
||||
m_hasToActivate = false;
|
||||
m_active = true;
|
||||
m_ASModifier = GetWorld()->SpawnActor<AAttackSpeedModifierConstant>();
|
||||
m_ASModifier->lifeTime = 0.0f;
|
||||
m_ASModifier->target = target;
|
||||
m_ASModifier->attackSpeedMultiplier = ScaleGraphCurve(attackSpeedMultiplier);
|
||||
target->GetModifierManager()->AddModifier(m_ASModifier);
|
||||
}
|
||||
if (deltahealth > ScaleGraphCurve(healthThreshold))
|
||||
{
|
||||
if (m_active)
|
||||
{
|
||||
m_active = false;
|
||||
m_ASModifier->ForceDestroy();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
//AArmorReductionModifier
|
||||
AArmorReductionModifier::AArmorReductionModifier()
|
||||
{
|
||||
|
||||
}
|
||||
|
||||
|
||||
void AArmorReductionModifier::ModifyCharacter()
|
||||
{
|
||||
m_AddArmor(armorReduction);
|
||||
}
|
||||
|
||||
|
||||
//AHealthRegenModifier
|
||||
AHealthRegenModifier::AHealthRegenModifier()
|
||||
{
|
||||
ticksPerSecond = standardTicksPerSecond;
|
||||
}
|
||||
|
||||
|
||||
void AHealthRegenModifier::ModifierTick()
|
||||
{
|
||||
float add = ScaleGraphCurve(scalingGraph);
|
||||
target->AddHealth(add);
|
||||
}
|
||||
|
||||
//AOnStandardAttackDOTModifier
|
||||
AOnStandardAttackDOTModifier::AOnStandardAttackDOTModifier()
|
||||
{
|
||||
ticksPerSecond = standardTicksPerSecond;
|
||||
}
|
||||
|
||||
|
||||
void AOnStandardAttackDOTModifier::ModifierTick()
|
||||
{
|
||||
m_cooldownTimer += m_tickRate;
|
||||
}
|
||||
|
||||
void AOnStandardAttackDOTModifier::OnStandardAttack(ANetworkCharacter* targetcharacter)
|
||||
{
|
||||
if (!IsValid(targetcharacter))
|
||||
return;
|
||||
ADOTModifier* DOTModifier = GetWorld()->SpawnActor<ADOTModifier>();
|
||||
DOTModifier->lifeTime = ScaleGraphCurve(damageDuration);
|
||||
DOTModifier->damagePerTick = ScaleGraphCurve(damagePerTick);
|
||||
// DOTModifier->target = targetcharacter;
|
||||
DOTModifier->character = character;
|
||||
DOTModifier->abilityInfo = abilityInfo;
|
||||
DOTModifier->target = targetcharacter;
|
||||
targetcharacter->GetModifierManager()->AddModifier(DOTModifier);
|
||||
m_cooldownTimer = ScaleGraphCurve(cooldown);
|
||||
}
|
||||
|
||||
//AReturnDamageModifier
|
||||
AReturnDamageModifier::AReturnDamageModifier()
|
||||
{
|
||||
ticksPerSecond = standardTicksPerSecond;
|
||||
|
||||
}
|
||||
bool AReturnDamageModifier::OnDamage(int32& damage, ANetworkCharacter* damageDealer)
|
||||
{
|
||||
if (damage >= 10 && IsValid(damageDealer))
|
||||
{
|
||||
|
||||
float returnDamage = ScaleGraphCurve(scalingGraph);
|
||||
damageDealer->DealDamage(this, damage * returnDamage, 0.0f);
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
|
||||
//AReturnDamageModifier
|
||||
ARedirectDamageModifier::ARedirectDamageModifier()
|
||||
{
|
||||
ticksPerSecond = standardTicksPerSecond;
|
||||
}
|
||||
|
||||
bool ARedirectDamageModifier::OnDamage(int32& damage, ANetworkCharacter* damageDealer)
|
||||
{
|
||||
if (damage >= 10 && IsValid(character))
|
||||
{
|
||||
float redirect = ScaleGraphCurve(redirectScalingGraph);
|
||||
if (redirect > 1)
|
||||
{
|
||||
FWARNING("redirect > 1, will act as if it is 0.9f");
|
||||
redirect = 0.9f;
|
||||
}
|
||||
if (redirect < 0)
|
||||
{
|
||||
FWARNING("redirect < 0, will act as if it is 0.0f");
|
||||
redirect = 0.0f;
|
||||
}
|
||||
float absorb = ScaleGraphCurve(absorbScalingGraph);
|
||||
|
||||
if (absorb > 1)
|
||||
{
|
||||
FWARNING("absorb > 1, will act as if it is 1.0f");
|
||||
absorb = 1.0f;
|
||||
}
|
||||
if (absorb < 0.0f )
|
||||
{
|
||||
FWARNING("absorb < 0, will act as if it is 0.0f");
|
||||
absorb = 0.0f;
|
||||
}
|
||||
character->DealDamage(this, damage * (redirect * absorb), 0.0f);
|
||||
damage *= (1 - redirect);
|
||||
DamageEvent();
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
|
||||
//ATrustModifier
|
||||
|
||||
//set delegate in gamestate to know when the teammate respawns.
|
||||
ATrustModifier::ATrustModifier()
|
||||
{
|
||||
ticksPerSecond = 2;
|
||||
}
|
||||
|
||||
void ATrustModifier::ModifierTick()
|
||||
{
|
||||
if (IsValid(character))
|
||||
{
|
||||
target->GetModifierManager()->RecalculateCharacter();
|
||||
}
|
||||
else
|
||||
ForceDestroy();
|
||||
}
|
||||
|
||||
void ATrustModifier::ModifyCharacter()
|
||||
{
|
||||
if (IsValid(character))
|
||||
{
|
||||
float add = ScaleGraphCurve(damageScalingGraph);
|
||||
float deltahealth =1 + ( 1.0f - ((float)character->GetHealth() / (float)character->GetMaxHealth()));
|
||||
m_MultiplyAttackDamage(add * deltahealth);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
//AArmorIncreaseModifier
|
||||
AArmorIncreaseModifier::AArmorIncreaseModifier()
|
||||
{
|
||||
ticksPerSecond = standardTicksPerSecond;
|
||||
}
|
||||
|
||||
void AArmorIncreaseModifier::ModifyCharacter()
|
||||
{
|
||||
float add = ScaleGraphCurve(armorScalingGraph);
|
||||
m_AddArmor(add);
|
||||
}
|
||||
|
||||
//AMoveToModifier
|
||||
AMoveToModifier::AMoveToModifier()
|
||||
{
|
||||
ticksPerSecond = standardTicksPerSecond;
|
||||
}
|
||||
void AMoveToModifier::ModifierStart()
|
||||
{
|
||||
m_movePerTick = (targetPos - target->GetActorLocation()) / (moveTime * ticksPerSecond);
|
||||
|
||||
}
|
||||
|
||||
void AMoveToModifier::ModifierTick()
|
||||
{
|
||||
target->AddActorWorldOffset(m_movePerTick, true);
|
||||
}
|
||||
|
||||
//AHealthRegenPercentageModifier
|
||||
AHealthRegenPercentageModifier::AHealthRegenPercentageModifier()
|
||||
{
|
||||
ticksPerSecond = standardTicksPerSecond;
|
||||
}
|
||||
|
||||
|
||||
void AHealthRegenPercentageModifier::ModifierTick()
|
||||
{
|
||||
float add = ScaleGraphCurve(scalingGraph);
|
||||
add /= ticksPerSecond;
|
||||
int32 health = character->GetMaxHealth();
|
||||
health *= add;
|
||||
target->AddHealth(health);
|
||||
}
|
||||
|
||||
//AManaRegenMultiplierModifier
|
||||
AManaRegenMultiplierModifier::AManaRegenMultiplierModifier()
|
||||
{
|
||||
ticksPerSecond = standardTicksPerSecond;
|
||||
}
|
||||
|
||||
void AManaRegenMultiplierModifier::ModifyCharacter()
|
||||
{
|
||||
float add = ScaleGraphCurve(manaRegenMultiplierScalingGraph);
|
||||
m_MultiplyManaRegenMultiplier(add);
|
||||
}
|
||||
|
||||
//AManaCostMultiplierModifier
|
||||
AManaCostMultiplierModifier::AManaCostMultiplierModifier()
|
||||
{
|
||||
ticksPerSecond = standardTicksPerSecond;
|
||||
}
|
||||
|
||||
void AManaCostMultiplierModifier::ModifyCharacter()
|
||||
{
|
||||
float add = ScaleGraphCurve(manaCostMultiplierScalingGraph);
|
||||
m_MultiplyManaUsageMultiplier(add);
|
||||
}
|
||||
|
||||
//AManaCostMultiplierModifier
|
||||
AEffectMultiplierModifier::AEffectMultiplierModifier()
|
||||
{
|
||||
ticksPerSecond = standardTicksPerSecond;
|
||||
}
|
||||
|
||||
void AEffectMultiplierModifier::ModifyCharacter()
|
||||
{
|
||||
float add = ScaleGraphCurve(effectMultiplierScalingGraph);
|
||||
if (positive)
|
||||
m_MultiplyPositiveEffectMultiplier(add);
|
||||
else
|
||||
m_MultiplyNegativeEffectMultiplier(add);
|
||||
}
|
||||
|
||||
//ACastingMovementSpeedMultiplierModifier
|
||||
ACastingMovementSpeedMultiplierModifier::ACastingMovementSpeedMultiplierModifier()
|
||||
{
|
||||
ticksPerSecond = standardTicksPerSecond;
|
||||
}
|
||||
|
||||
void ACastingMovementSpeedMultiplierModifier::ModifyCharacter()
|
||||
{
|
||||
float add = ScaleGraphCurve(movementSpeedMultiplierScalingGraph);
|
||||
m_addChannelMovementSpeedMultiplier(add);
|
||||
}
|
||||
|
||||
//AReaperModifier
|
||||
AReaperModifier::AReaperModifier()
|
||||
{
|
||||
ticksPerSecond = standardTicksPerSecond;
|
||||
}
|
||||
|
||||
bool AReaperModifier::OnDealDamage(int32& damage, ANetworkCharacter* damageTaker)
|
||||
{
|
||||
float multiplier = ScaleGraphCurve(damagemultiplier);
|
||||
float norm = ScaleGraphCurve(threshhold);
|
||||
|
||||
float deltahealth = (float)damageTaker->GetHealth() / (float)damageTaker->GetMaxHealth();
|
||||
if ((deltahealth < norm && smaller) || (deltahealth > norm && !smaller))
|
||||
{
|
||||
damage *= multiplier;
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
//AMagicDamageMultiplierModifier
|
||||
AMagicDamageMultiplierModifier::AMagicDamageMultiplierModifier()
|
||||
{
|
||||
ticksPerSecond = standardTicksPerSecond;
|
||||
}
|
||||
|
||||
void AMagicDamageMultiplierModifier::ModifyCharacter()
|
||||
{
|
||||
float add = ScaleGraphCurve(magicDamageMultiplierScalingGraph);
|
||||
m_MultiplyMagicDamageMultiplier(add);
|
||||
}
|
||||
|
||||
//AStandardMeleeModifier
|
||||
AStandardMeleeModifier::AStandardMeleeModifier()
|
||||
{
|
||||
count = 0;
|
||||
ticksPerSecond = standardTicksPerSecond;
|
||||
}
|
||||
|
||||
void AStandardMeleeModifier::ModifierTick()
|
||||
{
|
||||
m_timer += m_tickRate;
|
||||
if (m_timer >= maxTime)
|
||||
{
|
||||
ForceDestroy();
|
||||
}
|
||||
}
|
||||
|
||||
bool AStandardMeleeModifier::AddCount()
|
||||
{
|
||||
count++;
|
||||
count%=maxCount;
|
||||
m_timer = 0;
|
||||
return count == 0;
|
||||
}
|
||||
|
||||
//ADamageToManaModifier
|
||||
ADamageToManaModifier::ADamageToManaModifier()
|
||||
{
|
||||
|
||||
}
|
||||
|
||||
void ADamageToManaModifier::AfterDamage(int32 damage)
|
||||
{
|
||||
float addMultiplier = ScaleGraphCurve(multiplier);
|
||||
target->AddMana(damage * addMultiplier);
|
||||
}
|
||||
683
Source/UnrealProject/Abilities/NativeModifiers.h
Normal file
683
Source/UnrealProject/Abilities/NativeModifiers.h
Normal file
@@ -0,0 +1,683 @@
|
||||
#pragma once
|
||||
#include "Abilities/Modifier.h"
|
||||
#include "ScalingGraph.h"
|
||||
#include "NativeModifiers.generated.h"
|
||||
|
||||
// Increase/Decrease movement speed
|
||||
UCLASS()
|
||||
class UNREALPROJECT_API ASpeedModifier : public AModifier
|
||||
{
|
||||
GENERATED_BODY()
|
||||
|
||||
public:
|
||||
ASpeedModifier();
|
||||
|
||||
virtual void ModifyCharacter();
|
||||
|
||||
//adjusts the movementspeed of the character
|
||||
UPROPERTY(VisibleAnywhere, BlueprintReadWrite, meta=(ExposeOnSpawn), Category = "Modifier")
|
||||
float speedMultiplier;
|
||||
};
|
||||
|
||||
|
||||
// Health Regen over time
|
||||
UCLASS()
|
||||
class UNREALPROJECT_API ARegenModifier : public AModifier
|
||||
{
|
||||
GENERATED_BODY()
|
||||
|
||||
public:
|
||||
ARegenModifier();
|
||||
|
||||
virtual void ModifierTick() override;
|
||||
virtual bool ShouldBeRemoved() const override;
|
||||
|
||||
//the ammount of regen per tick (5 ticks per second)
|
||||
UPROPERTY(VisibleAnywhere, BlueprintReadWrite, meta=(ExposeOnSpawn), Category = "Modifier")
|
||||
float regenPerTick = 5.0f;
|
||||
//the total ammount of time the modifier should tick
|
||||
UPROPERTY(VisibleAnywhere, BlueprintReadWrite, meta=(ExposeOnSpawn), Category = "Modifier")
|
||||
float regenTime;
|
||||
|
||||
private:
|
||||
FVector m_pos;
|
||||
float m_noDamageTime = 5.0f;
|
||||
};
|
||||
|
||||
// Mana regen over time
|
||||
UCLASS()
|
||||
class UNREALPROJECT_API AManaRegenModifier : public AModifier
|
||||
{
|
||||
GENERATED_BODY()
|
||||
|
||||
public:
|
||||
AManaRegenModifier();
|
||||
|
||||
virtual void ModifierTick() override;
|
||||
virtual bool ShouldBeRemoved() const override;
|
||||
|
||||
//the ammount of mana per tick (20 ticks per second)
|
||||
UPROPERTY(VisibleAnywhere, BlueprintReadWrite, meta=(ExposeOnSpawn), Category = "Modifier")
|
||||
float regenPerTick;
|
||||
};
|
||||
|
||||
// Damage over time
|
||||
UCLASS()
|
||||
class UNREALPROJECT_API ADOTModifier : public AModifier
|
||||
{
|
||||
GENERATED_BODY()
|
||||
|
||||
public:
|
||||
ADOTModifier();
|
||||
|
||||
virtual void ModifierTick() override;
|
||||
|
||||
//damage per tick (3 ticks per second)
|
||||
UPROPERTY(VisibleAnywhere, BlueprintReadWrite, meta=(ExposeOnSpawn), Category = "Modifier")
|
||||
float damagePerTick = 5.0f;
|
||||
};
|
||||
|
||||
// Attack speed multiplier that scales with ability power
|
||||
UCLASS()
|
||||
class UNREALPROJECT_API AAttackSpeedModifier : public AModifier
|
||||
{
|
||||
GENERATED_BODY()
|
||||
|
||||
public:
|
||||
AAttackSpeedModifier();
|
||||
virtual void ModifyCharacter();
|
||||
|
||||
//adjusts the attack speed of the character
|
||||
UPROPERTY(VisibleAnywhere, BlueprintReadWrite, meta=(ExposeOnSpawn), Category = "Modifier")
|
||||
UCurveFloat* scalingGraph;
|
||||
};
|
||||
|
||||
// Attack speed multiplier
|
||||
UCLASS()
|
||||
class UNREALPROJECT_API AAttackSpeedModifierConstant : public AModifier
|
||||
{
|
||||
GENERATED_BODY()
|
||||
|
||||
public:
|
||||
AAttackSpeedModifierConstant();
|
||||
virtual void ModifyCharacter();
|
||||
bool ShouldBeRemoved() const override;
|
||||
|
||||
//adjusts the attack speed of the character
|
||||
UPROPERTY(VisibleAnywhere, BlueprintReadWrite, meta = (ExposeOnSpawn), Category = "Modifier")
|
||||
float attackSpeedMultiplier;
|
||||
};
|
||||
|
||||
// Cooldown reduction
|
||||
UCLASS()
|
||||
class UNREALPROJECT_API ACooldownReductionModifier : public AModifier
|
||||
{
|
||||
GENERATED_BODY()
|
||||
|
||||
public:
|
||||
ACooldownReductionModifier();
|
||||
virtual void ModifyCharacter();
|
||||
|
||||
//adjusts the cooldown of every ability of the character, except for that standard attack
|
||||
UPROPERTY(VisibleAnywhere, BlueprintReadWrite, meta=(ExposeOnSpawn), Category = "Modifier")
|
||||
UCurveFloat* scalingGraph;
|
||||
};
|
||||
|
||||
// Increase/Decrease Damage (multiplier)
|
||||
UCLASS()
|
||||
class UNREALPROJECT_API AADModifier : public AModifier
|
||||
{
|
||||
GENERATED_BODY()
|
||||
|
||||
public:
|
||||
AADModifier();
|
||||
|
||||
virtual void ModifyCharacter() override;
|
||||
|
||||
//adjusts the damage of the standard attack
|
||||
UPROPERTY(VisibleAnywhere, BlueprintReadWrite, meta = (ExposeOnSpawn), Category = "Modifier")
|
||||
UCurveFloat* scalingGraph;
|
||||
};
|
||||
|
||||
|
||||
// Increase/Decrease magic Damage (multiplier)
|
||||
UCLASS()
|
||||
class UNREALPROJECT_API AAPModifier : public AModifier
|
||||
{
|
||||
GENERATED_BODY()
|
||||
|
||||
public:
|
||||
AAPModifier();
|
||||
|
||||
virtual void ModifyCharacter() override;
|
||||
|
||||
//adjusts the damage of all abilities of the character except for the standard attack
|
||||
UPROPERTY(VisibleAnywhere, BlueprintReadWrite, meta = (ExposeOnSpawn), Category = "Modifier")
|
||||
UCurveFloat* scalingGraph;
|
||||
};
|
||||
|
||||
// Increase/Decrease max health
|
||||
UCLASS()
|
||||
class UNREALPROJECT_API AMaxHealthModifier : public AModifier
|
||||
{
|
||||
GENERATED_BODY()
|
||||
|
||||
public:
|
||||
AMaxHealthModifier();
|
||||
virtual void ModifyCharacter();
|
||||
|
||||
//adjusts the max health of the character
|
||||
UPROPERTY(VisibleAnywhere, BlueprintReadWrite, meta = (ExposeOnSpawn), Category = "Modifier")
|
||||
UCurveFloat* scalingGraph;
|
||||
};
|
||||
|
||||
// Increase/Decrease max health with a int32
|
||||
UCLASS()
|
||||
class UNREALPROJECT_API AConstMaxHealthModifier : public AModifier
|
||||
{
|
||||
GENERATED_BODY()
|
||||
|
||||
public:
|
||||
AConstMaxHealthModifier();
|
||||
virtual void ModifyCharacter();
|
||||
|
||||
//adjusts the max health of the character
|
||||
UPROPERTY(VisibleAnywhere, BlueprintReadWrite, meta = (ExposeOnSpawn), Category = "Modifier")
|
||||
int32 add;
|
||||
};
|
||||
|
||||
// Increase/Decrease blocked mana
|
||||
UCLASS()
|
||||
class UNREALPROJECT_API ABlockManaModifier : public AModifier
|
||||
{
|
||||
GENERATED_BODY()
|
||||
|
||||
public:
|
||||
ABlockManaModifier();
|
||||
|
||||
virtual void ModifyCharacter() override;
|
||||
// mana blocked
|
||||
UPROPERTY(VisibleAnywhere, BlueprintReadWrite, meta=(ExposeOnSpawn), Category = "Modifier")
|
||||
int32 Mana;
|
||||
};
|
||||
|
||||
// Mana over time cost with a curve
|
||||
UCLASS()
|
||||
class UNREALPROJECT_API AManaDrainCurveModifier : public AModifier
|
||||
{
|
||||
GENERATED_BODY()
|
||||
|
||||
public:
|
||||
AManaDrainCurveModifier();
|
||||
|
||||
virtual void ModifierTick() override;
|
||||
|
||||
//mana drained per tick (20 ticks per second)
|
||||
UPROPERTY(VisibleAnywhere, BlueprintReadWrite, meta=(ExposeOnSpawn), Category = "Modifier")
|
||||
UCurveFloat* ManaPerTick;
|
||||
};
|
||||
|
||||
|
||||
// Mana over time cost
|
||||
UCLASS()
|
||||
class UNREALPROJECT_API AManaDrainModifier : public AModifier
|
||||
{
|
||||
GENERATED_BODY()
|
||||
|
||||
public:
|
||||
AManaDrainModifier();
|
||||
|
||||
virtual void ModifierTick() override;
|
||||
|
||||
//mana drained per tick (20 ticks per second)
|
||||
UPROPERTY(VisibleAnywhere, BlueprintReadWrite, meta = (ExposeOnSpawn), Category = "Modifier")
|
||||
float ManaPerTick;
|
||||
};
|
||||
|
||||
// Heal health 1 time
|
||||
UCLASS()
|
||||
class UNREALPROJECT_API AHealModifier : public AModifier
|
||||
{
|
||||
GENERATED_BODY()
|
||||
|
||||
public:
|
||||
AHealModifier();
|
||||
|
||||
virtual void ModifierTick() override;
|
||||
virtual bool ShouldBeRemoved() const override;
|
||||
//the ammount of health that is healed
|
||||
UPROPERTY(VisibleAnywhere, BlueprintReadWrite, meta=(ExposeOnSpawn), Category = "Modifier")
|
||||
float health;
|
||||
|
||||
private:
|
||||
bool m_shouldBeRemoved = false;
|
||||
};
|
||||
|
||||
// Stun
|
||||
UCLASS()
|
||||
class UNREALPROJECT_API AStunModifier : public AModifier
|
||||
{
|
||||
GENERATED_BODY()
|
||||
|
||||
public:
|
||||
AStunModifier();
|
||||
virtual void ModifierStart() override;
|
||||
virtual void ModifierEnd() override;
|
||||
virtual void ModifierTick() override;
|
||||
virtual void ModifyCharacter() override;
|
||||
};
|
||||
|
||||
// Stun
|
||||
UCLASS()
|
||||
class UNREALPROJECT_API ASilenceModifier : public AModifier
|
||||
{
|
||||
GENERATED_BODY()
|
||||
|
||||
public:
|
||||
ASilenceModifier();
|
||||
virtual void ModifierStart() override;
|
||||
virtual void ModifierEnd() override;
|
||||
};
|
||||
|
||||
// Damage taken multiplier (only meant for bosses, will not work with a timer)
|
||||
UCLASS()
|
||||
class UNREALPROJECT_API ADamageTakenModifier : public AModifier
|
||||
{
|
||||
GENERATED_BODY()
|
||||
|
||||
public:
|
||||
ADamageTakenModifier();
|
||||
|
||||
virtual void ModifyCharacter() override;
|
||||
virtual bool ShouldBeRemoved() const override;
|
||||
|
||||
//adjusts the damage taken.
|
||||
UPROPERTY(VisibleAnywhere, BlueprintReadWrite, meta=(ExposeOnSpawn), Category = "Modifier")
|
||||
float damageTakenMultiplier;
|
||||
};
|
||||
|
||||
|
||||
|
||||
//gives invisibility on the minimap with set breaks DEPRECATED
|
||||
UCLASS()
|
||||
class UNREALPROJECT_API AVisibilityModifier : public AModifier
|
||||
{
|
||||
GENERATED_BODY()
|
||||
public:
|
||||
AVisibilityModifier();
|
||||
|
||||
virtual void ModifierTick() override;
|
||||
virtual bool ShouldBeRemoved() const override;
|
||||
//DEPRECATED
|
||||
UPROPERTY(VisibleAnywhere, BlueprintReadWrite, meta = (ExposeOnSpawn), Category = "Modifier")
|
||||
float visibleBreak = 5.0f;
|
||||
//DEPRECATED
|
||||
UPROPERTY(VisibleAnywhere, BlueprintReadWrite, meta = (ExposeOnSpawn), Category = "Modifier")
|
||||
float visibleTime = 1.0f;
|
||||
|
||||
private:
|
||||
float m_timer;
|
||||
bool m_visible;
|
||||
};
|
||||
|
||||
|
||||
//gives a chance at healing instead of damage if the blow is the killing blow
|
||||
UCLASS()
|
||||
class UNREALPROJECT_API ADodgeDeathModifier : public AModifier
|
||||
{
|
||||
GENERATED_BODY()
|
||||
|
||||
float m_timer;
|
||||
public:
|
||||
ADodgeDeathModifier();
|
||||
|
||||
virtual bool ShouldBeRemoved() const override;
|
||||
virtual void ModifierTick() override;
|
||||
//the chance between 0 and 1 and the effect will be proced
|
||||
UPROPERTY(VisibleAnywhere, BlueprintReadWrite, meta = (ExposeOnSpawn), Category = "Modifier")
|
||||
UCurveFloat* dodgeChance;
|
||||
//the ammount of health that is healed on the effect
|
||||
UPROPERTY(VisibleAnywhere, BlueprintReadWrite, meta = (ExposeOnSpawn), Category = "Modifier")
|
||||
UCurveFloat* heal;
|
||||
|
||||
//the cooldown for the dodgedeath in seconds
|
||||
UPROPERTY(VisibleAnywhere, BlueprintReadWrite, meta = (ExposeOnSpawn), Category = "Modifier")
|
||||
UCurveFloat* cooldown;
|
||||
|
||||
|
||||
|
||||
virtual bool OnDamage(int32& damage, ANetworkCharacter* damageDealer) override;
|
||||
};
|
||||
|
||||
//ignores a part of the enemy armor on all abilities
|
||||
UCLASS()
|
||||
class UNREALPROJECT_API AArmorIgnoreModifier : public AModifier
|
||||
{
|
||||
GENERATED_BODY()
|
||||
|
||||
public:
|
||||
AArmorIgnoreModifier();
|
||||
virtual void ModifyCharacter();
|
||||
//the ammount of armor that is ignored (between 0 and 1)
|
||||
UPROPERTY(VisibleAnywhere, BlueprintReadWrite, meta = (ExposeOnSpawn), Category = "Modifier")
|
||||
UCurveFloat* scalingGraph;
|
||||
};
|
||||
|
||||
//gives a chance at healing instead of damage if the blow is the killing blow
|
||||
UCLASS()
|
||||
class UNREALPROJECT_API ABeserkModifier : public AModifier
|
||||
{
|
||||
GENERATED_BODY()
|
||||
public:
|
||||
ABeserkModifier();
|
||||
|
||||
|
||||
virtual bool ShouldBeRemoved() const override;
|
||||
|
||||
//the threshhold (between 0 and 1) where the effect will be proced
|
||||
UPROPERTY(VisibleAnywhere, BlueprintReadWrite, meta = (ExposeOnSpawn), Category = "Modifier")
|
||||
UCurveFloat* healthThreshold;
|
||||
//adjusts the attackspeed
|
||||
UPROPERTY(VisibleAnywhere, BlueprintReadWrite, meta = (ExposeOnSpawn), Category = "Modifier")
|
||||
UCurveFloat* attackSpeedMultiplier;
|
||||
|
||||
|
||||
void m_Start();
|
||||
virtual void ModifierTick() override;
|
||||
private:
|
||||
bool m_active;
|
||||
bool m_hasToActivate;
|
||||
class AAttackSpeedModifierConstant* m_ASModifier;
|
||||
|
||||
virtual void AfterDamage(int32 damage) override;
|
||||
};
|
||||
|
||||
|
||||
// Increase/Decrease Damage (multiplier)
|
||||
UCLASS()
|
||||
class UNREALPROJECT_API AArmorReductionModifier : public AModifier
|
||||
{
|
||||
GENERATED_BODY()
|
||||
|
||||
public:
|
||||
AArmorReductionModifier();
|
||||
|
||||
virtual void ModifyCharacter() override;
|
||||
//adjusts the armor
|
||||
UPROPERTY(VisibleAnywhere, BlueprintReadWrite, meta = (ExposeOnSpawn), Category = "Modifier")
|
||||
float armorReduction;
|
||||
};
|
||||
|
||||
|
||||
//regens health Per Tick
|
||||
UCLASS()
|
||||
class UNREALPROJECT_API AHealthRegenModifier : public AModifier
|
||||
{
|
||||
GENERATED_BODY()
|
||||
|
||||
public:
|
||||
AHealthRegenModifier();
|
||||
//the ammount of health that is regened per tick (20 ticks per second)
|
||||
UPROPERTY(VisibleAnywhere, BlueprintReadWrite, meta = (ExposeOnSpawn), Category = "Modifier")
|
||||
UCurveFloat* scalingGraph;
|
||||
|
||||
virtual void ModifierTick() override;
|
||||
};
|
||||
|
||||
//deals DOT on a standard attack
|
||||
UCLASS()
|
||||
class UNREALPROJECT_API AOnStandardAttackDOTModifier : public AModifier
|
||||
{
|
||||
GENERATED_BODY()
|
||||
|
||||
private:
|
||||
float m_cooldownTimer;
|
||||
public:
|
||||
AOnStandardAttackDOTModifier();
|
||||
//damage per tick on standard attack hit(3 ticks per second)
|
||||
UPROPERTY(VisibleAnywhere, BlueprintReadWrite, meta = (ExposeOnSpawn), Category = "Modifier")
|
||||
UCurveFloat* damagePerTick;
|
||||
//the cooldown of the effect
|
||||
UPROPERTY(VisibleAnywhere, BlueprintReadWrite, meta = (ExposeOnSpawn), Category = "Modifier")
|
||||
UCurveFloat* cooldown;
|
||||
//the duration of the effect( of the DOT)
|
||||
UPROPERTY(VisibleAnywhere, BlueprintReadWrite, meta = (ExposeOnSpawn), Category = "Modifier")
|
||||
UCurveFloat* damageDuration;
|
||||
|
||||
virtual void ModifierTick() override;
|
||||
virtual void OnStandardAttack(ANetworkCharacter* targetcharacter) override;
|
||||
};
|
||||
|
||||
//returns a part of the damage
|
||||
UCLASS()
|
||||
class UNREALPROJECT_API AReturnDamageModifier : public AModifier
|
||||
{
|
||||
GENERATED_BODY()
|
||||
public:
|
||||
AReturnDamageModifier();
|
||||
|
||||
//chance between 0 and 1 of procing the effect
|
||||
UPROPERTY(VisibleAnywhere, BlueprintReadWrite, meta = (ExposeOnSpawn), Category = "Modifier")
|
||||
UCurveFloat* scalingGraph;
|
||||
|
||||
virtual bool OnDamage(int32& damage, ANetworkCharacter* damageDealer) override;
|
||||
};
|
||||
|
||||
//redirects a part of the damage to the ally
|
||||
UCLASS()
|
||||
class UNREALPROJECT_API ARedirectDamageModifier : public AModifier
|
||||
{
|
||||
GENERATED_BODY()
|
||||
public:
|
||||
ARedirectDamageModifier();
|
||||
|
||||
//chance between 0 and 1, the ammount that is being redirected (how much goes from the ally to the player
|
||||
UPROPERTY(VisibleAnywhere, BlueprintReadWrite, meta = (ExposeOnSpawn), Category = "Modifier")
|
||||
UCurveFloat* redirectScalingGraph;
|
||||
//chance between 0 and 1, the ammount of what is redirected that is being absorbed
|
||||
UPROPERTY(VisibleAnywhere, BlueprintReadWrite, meta = (ExposeOnSpawn), Category = "Modifier")
|
||||
UCurveFloat* absorbScalingGraph;
|
||||
|
||||
UFUNCTION(BlueprintImplementableEvent, Category = "Modifier")
|
||||
void DamageEvent();
|
||||
|
||||
virtual bool OnDamage(int32& damage, ANetworkCharacter* damageDealer);
|
||||
};
|
||||
|
||||
//increases attack damage of ally based on missing health
|
||||
UCLASS()
|
||||
class UNREALPROJECT_API ATrustModifier : public AModifier
|
||||
{
|
||||
GENERATED_BODY()
|
||||
|
||||
public:
|
||||
ATrustModifier();
|
||||
virtual void ModifierTick() override;
|
||||
virtual void ModifyCharacter() override;
|
||||
//the ammount of damage the ally gets multiplied by the normalised value of the missing health
|
||||
UPROPERTY(VisibleAnywhere, BlueprintReadWrite, meta = (ExposeOnSpawn), Category = "Modifier")
|
||||
UCurveFloat* damageScalingGraph;
|
||||
};
|
||||
|
||||
|
||||
//increases armor
|
||||
UCLASS()
|
||||
class UNREALPROJECT_API AArmorIncreaseModifier : public AModifier
|
||||
{
|
||||
GENERATED_BODY()
|
||||
|
||||
public:
|
||||
AArmorIncreaseModifier();
|
||||
virtual void ModifyCharacter();
|
||||
|
||||
UPROPERTY(VisibleAnywhere, BlueprintReadWrite, meta = (ExposeOnSpawn), Category = "Modifier")
|
||||
UCurveFloat* armorScalingGraph;
|
||||
};
|
||||
|
||||
//increases attack damage of ally based on missing health
|
||||
UCLASS()
|
||||
class UNREALPROJECT_API AMoveToModifier : public AModifier
|
||||
{
|
||||
GENERATED_BODY()
|
||||
|
||||
public:
|
||||
AMoveToModifier();
|
||||
virtual void ModifierStart();
|
||||
virtual void ModifierTick() override;
|
||||
|
||||
UPROPERTY(VisibleAnywhere, BlueprintReadWrite, meta = (ExposeOnSpawn), Category = "Modifier")
|
||||
FVector targetPos;
|
||||
UPROPERTY(VisibleAnywhere, BlueprintReadWrite, meta = (ExposeOnSpawn), Category = "Modifier")
|
||||
float moveTime;
|
||||
private:
|
||||
FVector m_movePerTick;
|
||||
};
|
||||
|
||||
//increases attack damage of ally based on missing health
|
||||
UCLASS()
|
||||
class UNREALPROJECT_API AHealthRegenPercentageModifier : public AModifier
|
||||
{
|
||||
GENERATED_BODY()
|
||||
|
||||
public:
|
||||
AHealthRegenPercentageModifier();
|
||||
|
||||
//normalised percentage (0-1)
|
||||
UPROPERTY(VisibleAnywhere, BlueprintReadWrite, meta = (ExposeOnSpawn), Category = "Modifier")
|
||||
UCurveFloat* scalingGraph;
|
||||
|
||||
virtual void ModifierTick() override;
|
||||
};
|
||||
|
||||
//increases mana regen multiplier
|
||||
UCLASS()
|
||||
class UNREALPROJECT_API AManaRegenMultiplierModifier : public AModifier
|
||||
{
|
||||
GENERATED_BODY()
|
||||
|
||||
public:
|
||||
AManaRegenMultiplierModifier();
|
||||
virtual void ModifyCharacter();
|
||||
|
||||
UPROPERTY(VisibleAnywhere, BlueprintReadWrite, meta = (ExposeOnSpawn), Category = "Modifier")
|
||||
UCurveFloat* manaRegenMultiplierScalingGraph;
|
||||
};
|
||||
|
||||
|
||||
//increases mana regen multiplier
|
||||
UCLASS()
|
||||
class UNREALPROJECT_API AManaCostMultiplierModifier : public AModifier
|
||||
{
|
||||
GENERATED_BODY()
|
||||
|
||||
public:
|
||||
AManaCostMultiplierModifier();
|
||||
virtual void ModifyCharacter();
|
||||
|
||||
UPROPERTY(VisibleAnywhere, BlueprintReadWrite, meta = (ExposeOnSpawn), Category = "Modifier")
|
||||
UCurveFloat* manaCostMultiplierScalingGraph;
|
||||
};
|
||||
|
||||
//debuffs positive effects
|
||||
UCLASS()
|
||||
class UNREALPROJECT_API AEffectMultiplierModifier : public AModifier
|
||||
{
|
||||
GENERATED_BODY()
|
||||
|
||||
public:
|
||||
AEffectMultiplierModifier();
|
||||
virtual void ModifyCharacter();
|
||||
|
||||
UPROPERTY(VisibleAnywhere, BlueprintReadWrite, meta = (ExposeOnSpawn), Category = "Modifier")
|
||||
bool positive;
|
||||
|
||||
UPROPERTY(VisibleAnywhere, BlueprintReadWrite, meta = (ExposeOnSpawn), Category = "Modifier")
|
||||
UCurveFloat* effectMultiplierScalingGraph;
|
||||
};
|
||||
|
||||
//debuffs positive effects
|
||||
UCLASS()
|
||||
class UNREALPROJECT_API ACastingMovementSpeedMultiplierModifier : public AModifier
|
||||
{
|
||||
GENERATED_BODY()
|
||||
|
||||
public:
|
||||
ACastingMovementSpeedMultiplierModifier();
|
||||
virtual void ModifyCharacter();
|
||||
|
||||
|
||||
UPROPERTY(VisibleAnywhere, BlueprintReadWrite, meta = (ExposeOnSpawn), Category = "Modifier")
|
||||
UCurveFloat* movementSpeedMultiplierScalingGraph;
|
||||
};
|
||||
|
||||
// deals more damage depending on the enemies hp (black/white threshhold)
|
||||
UCLASS()
|
||||
class UNREALPROJECT_API AReaperModifier : public AModifier
|
||||
{
|
||||
GENERATED_BODY()
|
||||
|
||||
public:
|
||||
AReaperModifier();
|
||||
virtual bool OnDealDamage(int32& damage, ANetworkCharacter* damageTaker) override;
|
||||
|
||||
//true == more damage when current health is less than threshhold false == more damage when current health is more than threshhold
|
||||
UPROPERTY(VisibleAnywhere, BlueprintReadWrite, meta = (ExposeOnSpawn), Category = "Modifier")
|
||||
bool smaller;
|
||||
UPROPERTY(VisibleAnywhere, BlueprintReadWrite, meta = (ExposeOnSpawn), Category = "Modifier")
|
||||
UCurveFloat* damagemultiplier;
|
||||
UPROPERTY(VisibleAnywhere, BlueprintReadWrite, meta = (ExposeOnSpawn), Category = "Modifier")
|
||||
UCurveFloat* threshhold;
|
||||
};
|
||||
|
||||
//increases/decreases magic damage
|
||||
UCLASS()
|
||||
class UNREALPROJECT_API AMagicDamageMultiplierModifier : public AModifier
|
||||
{
|
||||
GENERATED_BODY()
|
||||
|
||||
public:
|
||||
AMagicDamageMultiplierModifier();
|
||||
virtual void ModifyCharacter();
|
||||
|
||||
|
||||
UPROPERTY(VisibleAnywhere, BlueprintReadWrite, meta = (ExposeOnSpawn), Category = "Modifier")
|
||||
UCurveFloat* magicDamageMultiplierScalingGraph;
|
||||
};
|
||||
|
||||
//increases/decreases magic damage
|
||||
UCLASS()
|
||||
class UNREALPROJECT_API AStandardMeleeModifier : public AModifier
|
||||
{
|
||||
GENERATED_BODY()
|
||||
|
||||
float m_timer;
|
||||
public:
|
||||
virtual void ModifierTick() override;
|
||||
AStandardMeleeModifier();
|
||||
UPROPERTY(VisibleAnywhere, BlueprintReadWrite, meta = (ExposeOnSpawn), Category = "Modifier")
|
||||
float maxTime;
|
||||
UPROPERTY(VisibleAnywhere, BlueprintReadWrite, meta = (ExposeOnSpawn), Category = "Modifier")
|
||||
int32 maxCount;
|
||||
UPROPERTY(VisibleAnywhere, BlueprintReadWrite, Category = "Modifier")
|
||||
int32 count;
|
||||
UPROPERTY(VisibleAnywhere, BlueprintReadWrite, meta = (ExposeOnSpawn), Category = "Modifier")
|
||||
int32 team;
|
||||
UFUNCTION(BlueprintCallable, Category = "Modifier")
|
||||
bool AddCount();
|
||||
};
|
||||
|
||||
//converts damage recieved to mana
|
||||
UCLASS()
|
||||
class UNREALPROJECT_API ADamageToManaModifier : public AModifier
|
||||
{
|
||||
GENERATED_BODY()
|
||||
public:
|
||||
ADamageToManaModifier();
|
||||
|
||||
//the threshhold (between 0 and 1) where the effect will be proced
|
||||
UPROPERTY(VisibleAnywhere, BlueprintReadWrite, meta = (ExposeOnSpawn), Category = "Modifier")
|
||||
UCurveFloat* multiplier;
|
||||
private:
|
||||
|
||||
virtual void AfterDamage(int32 damage) override;
|
||||
};
|
||||
69
Source/UnrealProject/Abilities/PreCastAbilityEventGroup.cpp
Normal file
69
Source/UnrealProject/Abilities/PreCastAbilityEventGroup.cpp
Normal file
@@ -0,0 +1,69 @@
|
||||
// Project Lab - NHTV Igad
|
||||
|
||||
#include "UnrealProject.h"
|
||||
#include "AbilityInfo.h"
|
||||
#include "NetworkCharacter.h"
|
||||
#include "PreCastAbilityEventGroup.h"
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
void APreCastAbilityEventGroup::BeginPlay()
|
||||
{
|
||||
check(Role == ROLE_Authority);
|
||||
Super::BeginPlay();
|
||||
|
||||
if (!character || character->IsActorBeingDestroyed())
|
||||
{
|
||||
Destroy();
|
||||
}
|
||||
else
|
||||
{
|
||||
// Handle the destruction of the caster
|
||||
character->OnDestroyed.AddDynamic(this, &APreCastAbilityEventGroup::m_OnCharacterDestroyed);
|
||||
}
|
||||
}
|
||||
void APreCastAbilityEventGroup::EndPlay(const EEndPlayReason::Type EndPlayReason)
|
||||
{
|
||||
check(Role == ROLE_Authority);
|
||||
if (EndPlayReason == EEndPlayReason::Destroyed)
|
||||
{
|
||||
Super::EndPlay(EndPlayReason);
|
||||
StartAbility();
|
||||
}
|
||||
}
|
||||
void APreCastAbilityEventGroup::Tick(float DeltaTime)
|
||||
{
|
||||
Super::Tick(DeltaTime);
|
||||
|
||||
if (IsPendingKill())
|
||||
return;
|
||||
}
|
||||
|
||||
void APreCastAbilityEventGroup::m_OnCharacterDestroyed()
|
||||
{
|
||||
Destroy();
|
||||
}
|
||||
|
||||
void APreCastAbilityEventGroup::StartAbility()
|
||||
{
|
||||
character->m_CastAbility_Server(abilityInfo);
|
||||
Destroy();
|
||||
}
|
||||
|
||||
|
||||
APreCastAbilityEventGroup* APreCastAbilityEventGroup::InitPreCast(UAbilityInfo* info, ANetworkCharacter* character)
|
||||
{
|
||||
UWorld* world = character->GetWorld();
|
||||
check(world);
|
||||
|
||||
APreCastAbilityEventGroup* group = world->SpawnActorDeferred<APreCastAbilityEventGroup>(info->precastEvent, FTransform::Identity);
|
||||
|
||||
group->character = character;
|
||||
//group->abilityState = character->GetAbilityState(info);
|
||||
group->abilityInfo = info;
|
||||
|
||||
UGameplayStatics::FinishSpawningActor(group, FTransform::Identity);
|
||||
return group;
|
||||
}
|
||||
26
Source/UnrealProject/Abilities/PreCastAbilityEventGroup.h
Normal file
26
Source/UnrealProject/Abilities/PreCastAbilityEventGroup.h
Normal file
@@ -0,0 +1,26 @@
|
||||
// Project Lab - NHTV Igad
|
||||
|
||||
#pragma once
|
||||
|
||||
#include "Abilities/DealDamageProxy.h"
|
||||
#include "PreCastAbilityEventGroup.generated.h"
|
||||
|
||||
/**
|
||||
*
|
||||
*/
|
||||
UCLASS()
|
||||
class UNREALPROJECT_API APreCastAbilityEventGroup : public ADealDamageProxy
|
||||
{
|
||||
GENERATED_BODY()
|
||||
public:
|
||||
|
||||
virtual void BeginPlay() override final;
|
||||
virtual void EndPlay(const EEndPlayReason::Type EndPlayReason);
|
||||
virtual void Tick(float DeltaSeconds) override final;
|
||||
|
||||
void StartAbility();
|
||||
static APreCastAbilityEventGroup* InitPreCast(UAbilityInfo* info, ANetworkCharacter* character);
|
||||
private:
|
||||
UFUNCTION()
|
||||
void m_OnCharacterDestroyed();
|
||||
};
|
||||
174
Source/UnrealProject/Abilities/ProjectileBase.cpp
Normal file
174
Source/UnrealProject/Abilities/ProjectileBase.cpp
Normal file
@@ -0,0 +1,174 @@
|
||||
// Project Lab - NHTV Igad
|
||||
|
||||
#include "UnrealProject.h"
|
||||
#include "NetworkCharacter.h"
|
||||
|
||||
#include "ProjectileBase.h"
|
||||
#include "BlueprintAbilityLibrary.h"
|
||||
|
||||
#include "AbilityTriggerBase.h"
|
||||
|
||||
AProjectileBase::AProjectileBase()
|
||||
{
|
||||
m_finishTimer = 0.0f;
|
||||
m_fixedTimer = 0.0f;
|
||||
maxDistance = 1000.0f;
|
||||
distanceTraveled = 0.0f;
|
||||
keepAliveAfterFinish = 1.0f;
|
||||
filter = EAbilityFilter::EnemyAll;
|
||||
m_finished = false;
|
||||
|
||||
//server client replication
|
||||
bReplicateMovement = true;
|
||||
bReplicates = true;
|
||||
bAlwaysRelevant = true;
|
||||
PrimaryActorTick.bCanEverTick = true;
|
||||
}
|
||||
|
||||
void AProjectileBase::BeginPlay()
|
||||
{
|
||||
// Get collider component
|
||||
collider = Cast<UPrimitiveComponent>(GetRootComponent());
|
||||
if(!collider)
|
||||
{
|
||||
GWERROR(L"Projectile does not have a collider root!");
|
||||
Destroy();
|
||||
return;
|
||||
}
|
||||
|
||||
|
||||
if(Role != ROLE_Authority)
|
||||
{
|
||||
collider->SetCollisionEnabled(ECollisionEnabled::NoCollision);
|
||||
}
|
||||
else
|
||||
{
|
||||
collider->OnComponentHit.AddDynamic(this, &AProjectileBase::m_OnHit);
|
||||
collider->OnComponentBeginOverlap.AddDynamic(this, &AProjectileBase::m_OnOverlapBegin);
|
||||
// collider->SetCollisionProfileName("Projectiles");
|
||||
}
|
||||
Super::BeginPlay();
|
||||
}
|
||||
|
||||
void AProjectileBase::NativeFixedProjectileTick(float DeltaTime)
|
||||
{
|
||||
FixedProjectileTick(DeltaTime);
|
||||
}
|
||||
|
||||
//call this in inherited classes, will call move
|
||||
void AProjectileBase::Tick(float DeltaTime)
|
||||
{
|
||||
Super::Tick(DeltaTime);
|
||||
|
||||
// Fixed timestep for projectiles
|
||||
m_fixedTimer += DeltaTime;
|
||||
FVector startPos = GetActorLocation();
|
||||
const float delta = 1.0f / 60.0f;
|
||||
while(m_fixedTimer >= delta)
|
||||
{
|
||||
NativeFixedProjectileTick(delta);
|
||||
if (autoMove)
|
||||
{
|
||||
NativeMove(DeltaTime);
|
||||
|
||||
FVector newLocation = GetActorLocation();
|
||||
FVector movedVector = newLocation - startPos;
|
||||
startPos = newLocation;
|
||||
|
||||
distanceTraveled += movedVector.Size();
|
||||
}
|
||||
if (distanceTraveled > maxDistance)
|
||||
{
|
||||
Finish();
|
||||
break;
|
||||
}
|
||||
m_fixedTimer -= delta;
|
||||
}
|
||||
|
||||
if(m_finished && autoDestroy)
|
||||
{
|
||||
if((m_finishTimer -= DeltaTime) <= 0.0f)
|
||||
{
|
||||
Destroy();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
void AProjectileBase::m_OnOverlapBegin(class AActor* OtherActor, class UPrimitiveComponent* OtherComp, int32 OtherBodyIndex, bool bFromSweep, const FHitResult& SweepResult)
|
||||
{
|
||||
ANetworkCharacter* netChar = Cast<ANetworkCharacter>(OtherActor);
|
||||
if(netChar && ULib::CheckAbilityFilter(filter, character, netChar))
|
||||
{
|
||||
OnProjectileHit(netChar);
|
||||
}
|
||||
}
|
||||
void AProjectileBase::m_OnHit(class AActor* OtherActor, class UPrimitiveComponent* OtherComp, FVector NormalImpulse, const FHitResult& Hit)
|
||||
{
|
||||
Finish();
|
||||
}
|
||||
|
||||
void AProjectileBase::Finish_Implementation()
|
||||
{
|
||||
if(!m_finished)
|
||||
{
|
||||
if(Role == ROLE_Authority)
|
||||
{
|
||||
NativeServerOnFinish();
|
||||
}
|
||||
NativeOnProjectileFinished();
|
||||
if(collider)
|
||||
collider->SetCollisionEnabled(ECollisionEnabled::NoCollision);
|
||||
m_finishTimer = keepAliveAfterFinish;
|
||||
m_finished = true;
|
||||
}
|
||||
}
|
||||
|
||||
void AProjectileBase::FixedProjectileTick_Implementation(float DeltaTime)
|
||||
{
|
||||
}
|
||||
void AProjectileBase::OnProjectileHit_Implementation(ANetworkCharacter* character)
|
||||
{
|
||||
NativeOnProjectileHit(character);
|
||||
}
|
||||
|
||||
void AProjectileBase::GetLifetimeReplicatedProps(TArray<FLifetimeProperty>& OutLifetimeProps) const
|
||||
{
|
||||
Super::GetLifetimeReplicatedProps(OutLifetimeProps);
|
||||
|
||||
DOREPLIFETIME_CONDITION(AProjectileBase, speed, COND_InitialOnly);
|
||||
DOREPLIFETIME_CONDITION(AProjectileBase, filter, COND_InitialOnly);
|
||||
DOREPLIFETIME_CONDITION(AProjectileBase, maxDistance, COND_InitialOnly);
|
||||
DOREPLIFETIME_CONDITION(AProjectileBase, autoMove, COND_InitialOnly);
|
||||
DOREPLIFETIME_CONDITION(AProjectileBase, autoDestroy, COND_InitialOnly);
|
||||
}
|
||||
|
||||
void AProjectileBase::Move(float DeltaTime)
|
||||
{
|
||||
NativeMove(DeltaTime);
|
||||
}
|
||||
|
||||
void AProjectileBase::NativeMove(float DeltaTime)
|
||||
{
|
||||
//moves with sweep to get collision
|
||||
float distance = speed * DeltaTime;
|
||||
AddActorWorldOffset(GetActorForwardVector() * distance,true);
|
||||
}
|
||||
|
||||
void AProjectileBase::OnProjectileFinished_Implementation()
|
||||
{
|
||||
|
||||
}
|
||||
void AProjectileBase::NativeOnProjectileFinished()
|
||||
{
|
||||
OnProjectileFinished();
|
||||
}
|
||||
|
||||
void AProjectileBase::NativeOnProjectileHit(ANetworkCharacter* hitCharacter)
|
||||
{
|
||||
}
|
||||
|
||||
void AProjectileBase::NativeServerOnFinish()
|
||||
{
|
||||
ServerOnFinish();
|
||||
}
|
||||
78
Source/UnrealProject/Abilities/ProjectileBase.h
Normal file
78
Source/UnrealProject/Abilities/ProjectileBase.h
Normal file
@@ -0,0 +1,78 @@
|
||||
// Project Lab - NHTV Igad
|
||||
|
||||
#pragma once
|
||||
|
||||
#include "DealDamageProxy.h"
|
||||
#include "ProjectileBase.generated.h"
|
||||
|
||||
/**
|
||||
*
|
||||
*/
|
||||
UCLASS()
|
||||
class UNREALPROJECT_API AProjectileBase : public ADealDamageProxy
|
||||
{
|
||||
GENERATED_BODY()
|
||||
|
||||
public:
|
||||
AProjectileBase();
|
||||
virtual void BeginPlay() override;
|
||||
virtual void Tick(float DeltaSeconds) override;
|
||||
|
||||
UFUNCTION(BlueprintNativeEvent, Category = "Projectile")
|
||||
void FixedProjectileTick(float DeltaTime);
|
||||
virtual void NativeFixedProjectileTick(float DeltaTime);
|
||||
|
||||
UFUNCTION(BlueprintCallable, Category = "Projectile")
|
||||
void Move(float DeltaTime);
|
||||
virtual void NativeMove(float DeltaTime);
|
||||
|
||||
UFUNCTION(BlueprintCallable, Category = "Projectile")
|
||||
bool IsFinished() const { return m_finished; }
|
||||
|
||||
UFUNCTION(BlueprintNativeEvent, Category = "Projectile")
|
||||
void OnProjectileFinished();
|
||||
virtual void NativeOnProjectileFinished();
|
||||
|
||||
UFUNCTION(BlueprintNativeEvent, Category = "Projectile")
|
||||
void OnProjectileHit(ANetworkCharacter* hitCharacter);
|
||||
virtual void NativeOnProjectileHit(ANetworkCharacter* hitCharacter);
|
||||
|
||||
virtual void NativeServerOnFinish();
|
||||
UFUNCTION(BlueprintImplementableEvent, Category = "Project")
|
||||
void ServerOnFinish();
|
||||
|
||||
// Transfers projectile to the finished state, disabling collision, but still updating ticks
|
||||
UFUNCTION(BlueprintCallable, NetMulticast, Reliable, Category = "Projectile")
|
||||
void Finish();
|
||||
|
||||
UPROPERTY(VisibleAnywhere, BlueprintReadWrite, Replicated, Category = "Projectile", meta = (ExposeOnSpawn))
|
||||
float speed;
|
||||
UPROPERTY(VisibleAnywhere, BlueprintReadWrite, Replicated, Category = "Projectile", meta = (ExposeOnSpawn))
|
||||
EAbilityFilter filter;
|
||||
UPROPERTY(VisibleAnywhere, BlueprintReadWrite, Replicated, Category = "Projectile", meta = (ExposeOnSpawn))
|
||||
float maxDistance;
|
||||
UPROPERTY(VisibleAnywhere, BlueprintReadWrite, Replicated, Category = "Projectile", meta = (ExposeOnSpawn))
|
||||
bool autoMove = true;
|
||||
UPROPERTY(VisibleAnywhere, BlueprintReadWrite, Replicated, Category = "Projectile", meta = (ExposeOnSpawn))
|
||||
bool autoDestroy = true;
|
||||
|
||||
UPROPERTY(VisibleAnywhere, BlueprintReadWrite, Category = "Projectile")
|
||||
float distanceTraveled;
|
||||
|
||||
UPROPERTY(BlueprintReadOnly, EditDefaultsOnly, Category = "Projectile")
|
||||
float keepAliveAfterFinish;
|
||||
|
||||
UPROPERTY(BlueprintReadOnly, Category = "Projectile")
|
||||
UPrimitiveComponent* collider;
|
||||
|
||||
protected:
|
||||
// Internal event handlers
|
||||
UFUNCTION()
|
||||
void m_OnOverlapBegin(class AActor* OtherActor, class UPrimitiveComponent* OtherComp, int32 OtherBodyIndex, bool bFromSweep, const FHitResult& SweepResult);
|
||||
UFUNCTION()
|
||||
void m_OnHit(class AActor* OtherActor, class UPrimitiveComponent* OtherComp, FVector NormalImpulse, const FHitResult& Hit);
|
||||
|
||||
bool m_finished;
|
||||
float m_finishTimer;
|
||||
float m_fixedTimer;
|
||||
};
|
||||
12
Source/UnrealProject/Abilities/ScalingGraph.h
Normal file
12
Source/UnrealProject/Abilities/ScalingGraph.h
Normal file
@@ -0,0 +1,12 @@
|
||||
#pragma once
|
||||
#include "ScalingGraph.Generated.h"
|
||||
|
||||
|
||||
UCLASS()
|
||||
class ULevelScaleLibrary : public UBlueprintFunctionLibrary
|
||||
{
|
||||
GENERATED_BODY()
|
||||
public:
|
||||
UFUNCTION(BlueprintCallable, Category = "Scaling Graph")
|
||||
static float ScaleGraphCurveFloat(float in, const UCurveFloat* val);
|
||||
};
|
||||
34
Source/UnrealProject/Abilities/SpiralProjectile.cpp
Normal file
34
Source/UnrealProject/Abilities/SpiralProjectile.cpp
Normal file
@@ -0,0 +1,34 @@
|
||||
// Project Lab - NHTV Igad
|
||||
|
||||
#include "UnrealProject.h"
|
||||
#include "NetworkCharacter.h"
|
||||
#include "SpiralProjectile.h"
|
||||
|
||||
#define SPIRALTIMESTEP (0.02f)
|
||||
|
||||
void ASpiralProjectile::BeginPlay()
|
||||
{
|
||||
Super::BeginPlay();
|
||||
if (Role != ROLE_Authority)
|
||||
return;
|
||||
// Set the rotation of the projectile so that it is facing away from the caster.
|
||||
SetActorRotation(FRotator((this->GetActorLocation() - character->GetActorLocation()).Rotation()));
|
||||
m_elapsedTime = 0;
|
||||
}
|
||||
|
||||
void ASpiralProjectile::NativeMove(float DeltaTime)
|
||||
{
|
||||
m_elapsedTime += DeltaTime;
|
||||
// Fixed update
|
||||
while (m_elapsedTime >= SPIRALTIMESTEP)
|
||||
{
|
||||
m_elapsedTime -= SPIRALTIMESTEP;
|
||||
float distance = speed * SPIRALTIMESTEP;
|
||||
// Rotate the projectile according to how far it has traveled.
|
||||
FRotator rotation = FRotator(0, distance * 0.25f, 0);
|
||||
AddActorWorldRotation(rotation);
|
||||
// Move the projectile forwards.
|
||||
AddActorWorldOffset(GetActorForwardVector() * (distance * 2), true);
|
||||
}
|
||||
}
|
||||
|
||||
22
Source/UnrealProject/Abilities/SpiralProjectile.h
Normal file
22
Source/UnrealProject/Abilities/SpiralProjectile.h
Normal file
@@ -0,0 +1,22 @@
|
||||
// Project Lab - NHTV Igad
|
||||
|
||||
#pragma once
|
||||
|
||||
#include "ProjectileBase.h"
|
||||
#include "SpiralProjectile.generated.h"
|
||||
|
||||
/**
|
||||
*
|
||||
*/
|
||||
UCLASS()
|
||||
class UNREALPROJECT_API ASpiralProjectile : public AProjectileBase
|
||||
{
|
||||
GENERATED_BODY()
|
||||
|
||||
public:
|
||||
virtual void BeginPlay() override;
|
||||
virtual void NativeMove(float DeltaTime) override;
|
||||
|
||||
private:
|
||||
float m_elapsedTime;
|
||||
};
|
||||
28
Source/UnrealProject/Abilities/TrailActor.cpp
Normal file
28
Source/UnrealProject/Abilities/TrailActor.cpp
Normal file
@@ -0,0 +1,28 @@
|
||||
// Project Lab - NHTV Igad
|
||||
|
||||
#include "UnrealProject.h"
|
||||
#include "TrailActor.h"
|
||||
|
||||
|
||||
ATrailActor::ATrailActor()
|
||||
{
|
||||
|
||||
}
|
||||
|
||||
void ATrailActor::BeginPlay()
|
||||
{
|
||||
Super::BeginPlay();
|
||||
|
||||
}
|
||||
|
||||
|
||||
|
||||
void ATrailActor::DynamicDestroy()
|
||||
{
|
||||
Destroy();
|
||||
}
|
||||
|
||||
void ATrailActor::DynamicAdd()
|
||||
{
|
||||
GetOwner()->OnDestroyed.AddDynamic(this, &ATrailActor::DynamicDestroy);
|
||||
}
|
||||
21
Source/UnrealProject/Abilities/TrailActor.h
Normal file
21
Source/UnrealProject/Abilities/TrailActor.h
Normal file
@@ -0,0 +1,21 @@
|
||||
// Project Lab - NHTV Igad
|
||||
|
||||
#pragma once
|
||||
|
||||
#include "GameFramework/Actor.h"
|
||||
#include "TrailActor.generated.h"
|
||||
|
||||
UCLASS()
|
||||
class UNREALPROJECT_API ATrailActor : public AActor
|
||||
{
|
||||
GENERATED_BODY()
|
||||
|
||||
public:
|
||||
ATrailActor();
|
||||
|
||||
virtual void BeginPlay() override;
|
||||
|
||||
UFUNCTION()
|
||||
void DynamicDestroy();
|
||||
void DynamicAdd();
|
||||
};
|
||||
93
Source/UnrealProject/Abilities/TrailTrigger.cpp
Normal file
93
Source/UnrealProject/Abilities/TrailTrigger.cpp
Normal file
@@ -0,0 +1,93 @@
|
||||
// Project Lab - NHTV Igad
|
||||
|
||||
#include "UnrealProject.h"
|
||||
#include "TrailTrigger.h"
|
||||
#include "Networkcharacter.h"
|
||||
#include "TrailActor.h"
|
||||
#include "Effect.h"
|
||||
#include "EffectFunctionLibrary.h"
|
||||
|
||||
ATrailTrigger::ATrailTrigger()
|
||||
{
|
||||
}
|
||||
|
||||
void ATrailTrigger::BeginPlay()
|
||||
{
|
||||
Super::BeginPlay();
|
||||
if (Role == ROLE_Authority)
|
||||
{
|
||||
if (character == NULL)
|
||||
{
|
||||
FERROR("m_character is NULL");
|
||||
Destroy();
|
||||
return;
|
||||
}
|
||||
m_lastPos = character->GetActorLocation();
|
||||
SpawnTrail(FVector(0, 0, 0), true);
|
||||
}
|
||||
}
|
||||
|
||||
void ATrailTrigger::SpawnTrail(FVector offset, bool first)
|
||||
{
|
||||
|
||||
m_lastPos += offset;
|
||||
UCapsuleComponent* newobject = NewObject<UCapsuleComponent>(this);
|
||||
newobject->SetCollisionProfileName(TEXT("Triggers"));
|
||||
newobject->SetCapsuleRadius(colliderRadius, false);
|
||||
newobject->SetCapsuleHalfHeight(500, false);
|
||||
newobject->SetWorldLocation(m_lastPos);
|
||||
newobject->OnComponentBeginOverlap.AddDynamic(this, &AAbilityTriggerBase::OnOverlapBegin);
|
||||
newobject->OnComponentEndOverlap.AddDynamic(this, &AAbilityTriggerBase::OnOverlapEnd);
|
||||
newobject->RegisterComponent();
|
||||
if(first)
|
||||
RootComponent = newobject;
|
||||
|
||||
FTransform effectTransform;
|
||||
effectTransform.SetTranslation(m_lastPos);
|
||||
effectTransform.SetRotation(character->GetActorRotation().Quaternion());
|
||||
UEffectFunctionLibrary::CreateEffectAt(this, trailEffectClass, effectTransform,0.0f,lifeTime);
|
||||
|
||||
//ATrailActor* newTrailActor = GetWorld()->SpawnActor<ATrailActor>(trailActors);
|
||||
//if (newTrailActor == nullptr)
|
||||
// return;6
|
||||
//newTrailActor->SetActorLocation(m_lastPos);
|
||||
//newTrailActor->SetOwner(this);
|
||||
//newTrailActor->DynamicAdd();
|
||||
}
|
||||
|
||||
void ATrailTrigger::Tick(float DeltaTime)
|
||||
{
|
||||
Super::Tick(DeltaTime);
|
||||
if (Role != ROLE_Authority || !IsValid(this))
|
||||
return;
|
||||
if (colliderSpawnDistance == 0)
|
||||
{
|
||||
FERROR("colliderSpawnDistance == 0");
|
||||
return;
|
||||
}
|
||||
//m_timer += DeltaTime;
|
||||
if ( !character ) return;
|
||||
FVector distvec = character->GetActorLocation() - m_lastPos;
|
||||
distvec.Z = 0;
|
||||
float dist = distvec.Size();
|
||||
m_totalTimer += DeltaTime;
|
||||
lifeTime -= DeltaTime;
|
||||
if (lifeTime < 0)
|
||||
{
|
||||
Destroy();
|
||||
return;
|
||||
}
|
||||
if (m_totalTimer > totalSpawnTime)
|
||||
{
|
||||
return;
|
||||
}
|
||||
while (dist > colliderSpawnDistance)
|
||||
{
|
||||
UCapsuleComponent* newobject = NewObject<UCapsuleComponent>(this);
|
||||
distvec.Normalize();
|
||||
dist -= colliderSpawnDistance;
|
||||
|
||||
SpawnTrail(distvec *colliderSpawnDistance);
|
||||
}
|
||||
|
||||
}
|
||||
40
Source/UnrealProject/Abilities/TrailTrigger.h
Normal file
40
Source/UnrealProject/Abilities/TrailTrigger.h
Normal file
@@ -0,0 +1,40 @@
|
||||
// Project Lab - NHTV Igad
|
||||
|
||||
#pragma once
|
||||
|
||||
#include "Abilities/AbilityTriggerBase.h"
|
||||
#include "TrailTrigger.generated.h"
|
||||
|
||||
/**
|
||||
*
|
||||
*/
|
||||
UCLASS()
|
||||
class UNREALPROJECT_API ATrailTrigger : public AAbilityTriggerBase
|
||||
{
|
||||
GENERATED_BODY()
|
||||
public:
|
||||
virtual void BeginPlay() override;
|
||||
|
||||
ATrailTrigger();
|
||||
UPROPERTY(EditAnywhere, BlueprintReadWrite, Category = "Trigger", meta = (ClampMin = "0", UIMin = "0"))
|
||||
float totalSpawnTime = 1.5f;
|
||||
|
||||
UPROPERTY(EditAnywhere, BlueprintReadWrite, Category = "Trigger", meta = (ClampMin = "0", UIMin = "0"))
|
||||
float lifeTime = 5.0f;
|
||||
|
||||
UPROPERTY(EditAnywhere, BlueprintReadWrite, Category = "Trigger", meta = (ClampMin = "0", UIMin = "0"))
|
||||
float colliderSpawnDistance = 50.0f;
|
||||
UPROPERTY(EditAnywhere, BlueprintReadWrite, Category = "Trigger", meta = (ClampMin = "0", UIMin = "0"))
|
||||
float colliderRadius = 50.0f;
|
||||
UPROPERTY(EditAnywhere, BlueprintReadWrite, Category = "Trigger", meta = (ClampMin = "0", UIMin = "0"))
|
||||
TSubclassOf<class AEffect> trailEffectClass;
|
||||
|
||||
|
||||
virtual void Tick(float DeltaSeconds) override;
|
||||
|
||||
private:
|
||||
void SpawnTrail(FVector offset, bool first = false);
|
||||
float m_tickTimer = 0.0f;
|
||||
FVector m_lastPos;
|
||||
float m_totalTimer = 0.0f;
|
||||
};
|
||||
52
Source/UnrealProject/Abilities/poisonAuraTrigger.cpp
Normal file
52
Source/UnrealProject/Abilities/poisonAuraTrigger.cpp
Normal file
@@ -0,0 +1,52 @@
|
||||
// Project Lab - NHTV Igad
|
||||
|
||||
#include "UnrealProject.h"
|
||||
#include "poisonAuraTrigger.h"
|
||||
#include "NetworkCharacter.h"
|
||||
#include "NativeModifiers.h"
|
||||
#include "Modifier.h"
|
||||
|
||||
|
||||
|
||||
ApoisonAuraTrigger::ApoisonAuraTrigger()
|
||||
{
|
||||
|
||||
}
|
||||
void ApoisonAuraTrigger::BeginPlay()
|
||||
{
|
||||
Super::BeginPlay();
|
||||
}
|
||||
void ApoisonAuraTrigger::Tick(float deltaTime)
|
||||
{
|
||||
Super::Tick(deltaTime);
|
||||
}
|
||||
void ApoisonAuraTrigger::HitEvent(ANetworkCharacter* otherActor)
|
||||
{
|
||||
|
||||
ModifierManager* manager = otherActor->GetModifierManager();
|
||||
if (playerMap.Find(otherActor) == nullptr)
|
||||
{
|
||||
RERROR("2 modifiers in the intimidatin aura");
|
||||
}
|
||||
if (manager)
|
||||
{
|
||||
ADOTModifier* ASModifier = GetWorld()->SpawnActor<ADOTModifier>();
|
||||
ASModifier->lifeTime = 0;
|
||||
ASModifier->damagePerTick = damage;
|
||||
ASModifier->target = otherActor;
|
||||
manager->AddModifier(ASModifier);
|
||||
playerMap.Add(otherActor, ASModifier);
|
||||
|
||||
}
|
||||
return Super::HitEvent(otherActor);
|
||||
}
|
||||
void ApoisonAuraTrigger::LeaveEvent(ANetworkCharacter* otherActor)
|
||||
{
|
||||
|
||||
auto it = playerMap.Find(otherActor);
|
||||
if (it == nullptr)
|
||||
return Super::LeaveEvent(otherActor);
|
||||
(*it)->ForceDestroy();
|
||||
playerMap.Remove(otherActor);
|
||||
return Super::LeaveEvent(otherActor);
|
||||
}
|
||||
28
Source/UnrealProject/Abilities/poisonAuraTrigger.h
Normal file
28
Source/UnrealProject/Abilities/poisonAuraTrigger.h
Normal file
@@ -0,0 +1,28 @@
|
||||
// Project Lab - NHTV Igad
|
||||
|
||||
#pragma once
|
||||
#include <map>
|
||||
#include "Abilities/ConeTrigger.h"
|
||||
#include "poisonAuraTrigger.generated.h"
|
||||
|
||||
/**
|
||||
*
|
||||
*/
|
||||
|
||||
|
||||
UCLASS()
|
||||
class UNREALPROJECT_API ApoisonAuraTrigger : public AConeTrigger
|
||||
{
|
||||
GENERATED_BODY()
|
||||
public:
|
||||
ApoisonAuraTrigger();
|
||||
|
||||
virtual void BeginPlay() override;
|
||||
virtual void Tick(float DeltaSeconds) override;
|
||||
virtual void HitEvent(class ANetworkCharacter* otherActor)override;
|
||||
virtual void LeaveEvent(class ANetworkCharacter* otherActor)override;
|
||||
UPROPERTY()
|
||||
TMap<class ANetworkCharacter*, class AModifier* > playerMap;
|
||||
UPROPERTY(meta = (ExposeOnSpawn), BlueprintReadWrite)
|
||||
float damage;
|
||||
};
|
||||
37
Source/UnrealProject/Creatures/AnimationProxy.cpp
Normal file
37
Source/UnrealProject/Creatures/AnimationProxy.cpp
Normal file
@@ -0,0 +1,37 @@
|
||||
#include "UnrealProject.h"
|
||||
#include "AnimationProxy.h"
|
||||
|
||||
bool FMainAnimProxy::Evaluate(FPoseContext& pose)
|
||||
{
|
||||
// Evalueate the animation pose according to the blueprint
|
||||
if(GetRootNode() != NULL)
|
||||
{
|
||||
GetRootNode()->Evaluate(pose);
|
||||
}
|
||||
|
||||
// Now set bone scales for character customizations
|
||||
SetBoneScale(boneNames[0], FVector(charCustomization.headScale), pose);
|
||||
|
||||
SetBoneScale(boneNames[1], FVector(1, charCustomization.legThicknessYScale, charCustomization.legThicknessZScale), pose);
|
||||
SetBoneScale(boneNames[2], FVector(1, charCustomization.legThicknessYScale, charCustomization.legThicknessZScale), pose);
|
||||
|
||||
SetBoneScale(boneNames[3], FVector(charCustomization.leftArmScale), pose);
|
||||
SetBoneScale(boneNames[4], FVector(charCustomization.rightArmScale), pose);
|
||||
|
||||
SetBoneScale(boneNames[5], FVector(charCustomization.torsoScale), pose);
|
||||
|
||||
SetBoneScale(boneNames[6], FVector(charCustomization.overallScale), pose);
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
void FMainAnimProxy::SetBoneScale(const FName boneName, const FVector scale, FPoseContext& pose)
|
||||
{
|
||||
int32 boneIndex = this->GetSkelMeshComponent()->GetBoneIndex(boneName);
|
||||
// Check if bone is in the skeleton.
|
||||
if(boneIndex != INDEX_NONE)
|
||||
{
|
||||
FTransform& boneTransform = pose.Pose[FCompactPoseBoneIndex(boneIndex)];
|
||||
boneTransform.SetScale3D(scale);
|
||||
}
|
||||
}
|
||||
22
Source/UnrealProject/Creatures/AnimationProxy.h
Normal file
22
Source/UnrealProject/Creatures/AnimationProxy.h
Normal file
@@ -0,0 +1,22 @@
|
||||
#pragma once
|
||||
#include "Animation/AnimInstanceProxy.h"
|
||||
#include "PlayerSetupState.h"
|
||||
|
||||
// Animation instance proxy that sets bone scales for character customizations
|
||||
// This animation proxy just calculates the animation blueprint's pose and then applies bone scaled to them based on character customizations
|
||||
struct FMainAnimProxy : FAnimInstanceProxy
|
||||
{
|
||||
// The parent animation
|
||||
FCharacterCustomization charCustomization;
|
||||
|
||||
// Head
|
||||
// Thigh L
|
||||
// Thigh R
|
||||
// UpperArm L
|
||||
// UpperArm R
|
||||
// Root
|
||||
FName boneNames[7];
|
||||
|
||||
virtual bool Evaluate(FPoseContext& pose) override;
|
||||
void SetBoneScale(const FName boneName, const FVector scale, FPoseContext& pose);
|
||||
};
|
||||
@@ -0,0 +1,64 @@
|
||||
// Project Lab - NHTV Igad
|
||||
|
||||
#include "UnrealProject.h"
|
||||
#include "BTTaskNodeClimb.h"
|
||||
#include "EnemyAIController.h"
|
||||
#include "BehaviorTree/BehaviorTree.h"
|
||||
#include "BehaviorTree/BlackboardComponent.h"
|
||||
#include "BehaviorTree/Blackboard/BlackboardKeyType_Object.h"
|
||||
#include "BehaviorTree/Blackboard/BlackboardKeyType_Vector.h"
|
||||
|
||||
|
||||
UBTTaskNodeClimb::UBTTaskNodeClimb()
|
||||
{
|
||||
NodeName = "Climb";
|
||||
bNotifyTick = true;
|
||||
// accept only actors and vectors
|
||||
BlackboardKey.AddObjectFilter(this, GET_MEMBER_NAME_CHECKED(UBTTaskNodeClimb, BlackboardKey), AActor::StaticClass());
|
||||
BlackboardKey.AddVectorFilter(this, GET_MEMBER_NAME_CHECKED(UBTTaskNodeClimb, BlackboardKey));
|
||||
directionKey.AddVectorFilter(this, GET_MEMBER_NAME_CHECKED(UBTTaskNodeClimb, directionKey));
|
||||
directionKey.AddObjectFilter(this, GET_MEMBER_NAME_CHECKED(UBTTaskNodeClimb, directionKey), AActor::StaticClass());
|
||||
}
|
||||
void UBTTaskNodeClimb::InitializeFromAsset(UBehaviorTree& Asset)
|
||||
{
|
||||
Super::InitializeFromAsset(Asset);
|
||||
|
||||
UBlackboardData &BBAsset = *GetBlackboardAsset();
|
||||
BlackboardKey.ResolveSelectedKey(BBAsset);
|
||||
directionKey.ResolveSelectedKey(BBAsset);
|
||||
}
|
||||
|
||||
EBTNodeResult::Type UBTTaskNodeClimb::ExecuteTask(UBehaviorTreeComponent& OwnerComp, uint8* NodeMemory)
|
||||
{
|
||||
EBTNodeResult::Type NodeResult = EBTNodeResult::InProgress;
|
||||
NodeResult = PeformClimbTask(OwnerComp, NodeMemory);
|
||||
return NodeResult;
|
||||
}
|
||||
EBTNodeResult::Type UBTTaskNodeClimb::PeformClimbTask(UBehaviorTreeComponent& OwnerComp, uint8* NodeMemory)
|
||||
{
|
||||
const UBlackboardComponent* MyBlackboard = OwnerComp.GetBlackboardComponent();
|
||||
AAIController* MyController = OwnerComp.GetAIOwner();
|
||||
AEnemyAIController* cont = Cast<AEnemyAIController>(OwnerComp.GetAIOwner());
|
||||
EBTNodeResult::Type NodeResult = EBTNodeResult::Failed;
|
||||
if (cont && MyBlackboard)
|
||||
{
|
||||
const FVector TargetLocation = MyBlackboard->GetValue<UBlackboardKeyType_Vector>(BlackboardKey.GetSelectedKeyID());
|
||||
FVector TargetDirection = FVector::ZeroVector;
|
||||
MyBlackboard->GetLocationFromEntry(directionKey.GetSelectedKeyID(), TargetDirection);
|
||||
|
||||
if (cont->VerticleMoveTo(TargetLocation, TargetDirection,AcceptableRadius ))
|
||||
{
|
||||
NodeResult = EBTNodeResult::Succeeded;
|
||||
}
|
||||
else NodeResult = EBTNodeResult::InProgress;
|
||||
}
|
||||
return NodeResult;
|
||||
}
|
||||
void UBTTaskNodeClimb::TickTask(UBehaviorTreeComponent& OwnerComp, uint8* NodeMemory, float DeltaSeconds)
|
||||
{
|
||||
const EBTNodeResult::Type NodeResult = PeformClimbTask(OwnerComp, NodeMemory);
|
||||
if (NodeResult != EBTNodeResult::InProgress)
|
||||
{
|
||||
FinishLatentTask(OwnerComp, NodeResult);
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,36 @@
|
||||
// Project Lab - NHTV Igad
|
||||
|
||||
#pragma once
|
||||
|
||||
#include "BehaviorTree/Tasks/BTTask_BlackboardBase.h"
|
||||
#include "BTTaskNodeClimb.generated.h"
|
||||
|
||||
/**
|
||||
*
|
||||
*/
|
||||
UCLASS(config = Game)
|
||||
class UNREALPROJECT_API UBTTaskNodeClimb : public UBTTaskNode
|
||||
{
|
||||
GENERATED_BODY()
|
||||
public :
|
||||
UPROPERTY(config, Category = Node, EditAnywhere, meta = (ClampMin = "0.0"))
|
||||
float AcceptableRadius;
|
||||
FName GetSelectedBlackboardKey() const;
|
||||
UBTTaskNodeClimb();
|
||||
virtual EBTNodeResult::Type ExecuteTask(UBehaviorTreeComponent& OwnerComp, uint8* NodeMemory) override;
|
||||
virtual void TickTask(UBehaviorTreeComponent& OwnerComp, uint8* NodeMemory, float DeltaSeconds) override;
|
||||
virtual void InitializeFromAsset(UBehaviorTree& Asset) override;
|
||||
UPROPERTY(EditAnywhere, Category = Blackboard)
|
||||
FBlackboardKeySelector directionKey;
|
||||
UPROPERTY(EditAnywhere, Category = Blackboard)
|
||||
FBlackboardKeySelector BlackboardKey;
|
||||
protected:
|
||||
EBTNodeResult::Type PeformClimbTask(UBehaviorTreeComponent& OwnerComp, uint8* NodeMemory);
|
||||
|
||||
|
||||
};
|
||||
|
||||
FORCEINLINE FName UBTTaskNodeClimb::GetSelectedBlackboardKey() const
|
||||
{
|
||||
return directionKey.SelectedKeyName;
|
||||
}
|
||||
@@ -0,0 +1,56 @@
|
||||
// Project Lab - NHTV Igad
|
||||
|
||||
#include "UnrealProject.h"
|
||||
#include "BTTaskNodeJumpFence.h"
|
||||
#include "EnemyAIController.h"
|
||||
#include "BehaviorTree/BehaviorTree.h"
|
||||
#include "BehaviorTree/BlackboardComponent.h"
|
||||
#include "BehaviorTree/Blackboard/BlackboardKeyType_Object.h"
|
||||
#include "BehaviorTree/Blackboard/BlackboardKeyType_Vector.h"
|
||||
|
||||
UBTTaskNodeJumpFence::UBTTaskNodeJumpFence()
|
||||
{
|
||||
NodeName = "Jump Fence";
|
||||
bNotifyTick = true;
|
||||
BlackboardKey.AddObjectFilter(this, GET_MEMBER_NAME_CHECKED(UBTTaskNodeJumpFence, BlackboardKey), AActor::StaticClass());
|
||||
BlackboardKey.AddVectorFilter(this, GET_MEMBER_NAME_CHECKED(UBTTaskNodeJumpFence, BlackboardKey));
|
||||
}
|
||||
void UBTTaskNodeJumpFence::InitializeFromAsset(UBehaviorTree& asset)
|
||||
{
|
||||
Super::InitializeFromAsset(asset);
|
||||
UBlackboardData& BBAsset = *GetBlackboardAsset();
|
||||
BlackboardKey.ResolveSelectedKey(BBAsset);
|
||||
}
|
||||
EBTNodeResult::Type UBTTaskNodeJumpFence::ExecuteTask(UBehaviorTreeComponent& OwnerComp, uint8* NodeMemory)
|
||||
{
|
||||
EBTNodeResult::Type NodeResult = EBTNodeResult::InProgress;
|
||||
NodeResult = PefromJumpFenceTask(OwnerComp, NodeMemory);
|
||||
return NodeResult;
|
||||
}
|
||||
EBTNodeResult::Type UBTTaskNodeJumpFence::PefromJumpFenceTask(UBehaviorTreeComponent& OwnerComp, uint8* NodeMemory)
|
||||
{
|
||||
const UBlackboardComponent* MyBlackboard = OwnerComp.GetBlackboardComponent();
|
||||
AAIController* MyController = OwnerComp.GetAIOwner();
|
||||
AEnemyAIController* cont = Cast<AEnemyAIController>(OwnerComp.GetAIOwner());
|
||||
EBTNodeResult::Type NodeResult = EBTNodeResult::Failed;
|
||||
if (cont && MyBlackboard)
|
||||
{
|
||||
const FVector TargetLocation = MyBlackboard->GetValue<UBlackboardKeyType_Vector>(BlackboardKey.GetSelectedKeyID());
|
||||
FVector TargetDirection = FVector::ZeroVector;
|
||||
if (cont->JumpFence(TargetLocation))
|
||||
{
|
||||
NodeResult = EBTNodeResult::Succeeded;
|
||||
}
|
||||
else NodeResult = EBTNodeResult::InProgress;
|
||||
}
|
||||
return NodeResult;
|
||||
|
||||
}
|
||||
void UBTTaskNodeJumpFence::TickTask(UBehaviorTreeComponent& OwnerComp, uint8* NodeMemory, float DeltaSeconds)
|
||||
{
|
||||
const EBTNodeResult::Type NodeResult = PefromJumpFenceTask(OwnerComp, NodeMemory);
|
||||
if (NodeResult != EBTNodeResult::InProgress)
|
||||
{
|
||||
FinishLatentTask(OwnerComp, NodeResult);
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,25 @@
|
||||
// Project Lab - NHTV Igad
|
||||
|
||||
#pragma once
|
||||
|
||||
#include "BehaviorTree/BTTaskNode.h"
|
||||
#include "BTTaskNodeJumpFence.generated.h"
|
||||
|
||||
/**
|
||||
*
|
||||
*/
|
||||
UCLASS()
|
||||
class UNREALPROJECT_API UBTTaskNodeJumpFence : public UBTTaskNode
|
||||
{
|
||||
GENERATED_BODY()
|
||||
public:
|
||||
UBTTaskNodeJumpFence();
|
||||
virtual EBTNodeResult::Type ExecuteTask(UBehaviorTreeComponent& OwnerComp, uint8* NodeMemory) override;
|
||||
virtual void TickTask(UBehaviorTreeComponent& OwnerComp, uint8* NodeMemory, float DeltaSeconds) override;
|
||||
virtual void InitializeFromAsset(UBehaviorTree& Asset) override;
|
||||
UPROPERTY(EditAnywhere, Category = Blackboard)
|
||||
FBlackboardKeySelector BlackboardKey;
|
||||
protected:
|
||||
EBTNodeResult::Type PefromJumpFenceTask(UBehaviorTreeComponent& OwnerComp, uint8* NodeMemory);
|
||||
|
||||
};
|
||||
@@ -0,0 +1,178 @@
|
||||
// Project Lab - NHTV Igad
|
||||
|
||||
#include "UnrealProject.h"
|
||||
#include "EnemyAIController.h"
|
||||
#include "EnemyBase.h"
|
||||
#include "NetworkCharacter.h"
|
||||
#include "Navigation/CrowdFollowingComponent.h"
|
||||
|
||||
#include "BehaviorTree/BehaviorTree.h"
|
||||
#include "BehaviorTree/BlackboardComponent.h"
|
||||
|
||||
#include "BehaviorTree/BehaviorTreeComponent.h"
|
||||
#include "BehaviorTree/BlackBoard/BlackboardKeyAllTypes.h"
|
||||
|
||||
#include "DrawDebugHelpers.h"
|
||||
|
||||
AEnemyAIController::AEnemyAIController(const FObjectInitializer& PCIP)
|
||||
//: Super(PCIP.SetDefaultSubobjectClass<UCrowdFollowingComponent>(TEXT("PathFollowingComponent")))
|
||||
{
|
||||
blackBoardComp = CreateDefaultSubobject<UBlackboardComponent>(FName("BlackBoardComp"));
|
||||
behaviorTreeComp = CreateDefaultSubobject<UBehaviorTreeComponent>(FName("BehaviorTreeComp"));
|
||||
climbSpeed = 1.0f;
|
||||
}
|
||||
void AEnemyAIController::Tick(float deltaTime)
|
||||
{
|
||||
Super::Tick(deltaTime);
|
||||
|
||||
}
|
||||
void AEnemyAIController::Possess(APawn* pawn)
|
||||
{
|
||||
Super::Possess(pawn);
|
||||
AEnemyBase* enemy = Cast<AEnemyBase>(pawn);
|
||||
if (enemy &&enemy->treeBehavior)
|
||||
{
|
||||
if (!enemy->treeBehavior->BlackboardAsset)
|
||||
{
|
||||
RERROR("There is no blackboard asset in the behavior tree");
|
||||
return;
|
||||
}
|
||||
blackBoardComp->InitializeBlackboard(*(enemy->treeBehavior->BlackboardAsset));
|
||||
behaviorTreeComp->StartTree(*enemy->treeBehavior);
|
||||
}
|
||||
}
|
||||
bool AEnemyAIController::VerticleMoveTo(FVector destination, FVector direction,float acceptRadius)
|
||||
{
|
||||
FVector location =GetPawn()->GetActorLocation();
|
||||
FVector dest = destination;
|
||||
dest -= location;
|
||||
|
||||
dest *= direction;
|
||||
FVector newDest= dest;
|
||||
float height = newDest.Size();
|
||||
|
||||
|
||||
|
||||
if (height < acceptRadius&&destination.Z <location.Z)
|
||||
{
|
||||
// GetCharacter()->GetCharacterMovement()->GravityScale = 1;
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
GetPawn()->SetActorLocation(FVector(location.X+(direction.X*10), location.Y+(direction.Y*10) , (location.Z)+(direction.Z*10)));
|
||||
GetCharacter()->GetCharacterMovement()->GravityScale = 0;
|
||||
|
||||
return false;
|
||||
|
||||
}
|
||||
bool AEnemyAIController::JumpFence(FVector destination)
|
||||
{
|
||||
TArray<FHitResult> result;
|
||||
UNavigationSystem* navsys = UNavigationSystem::GetCurrent(GetWorld());
|
||||
if (!navsys)
|
||||
{
|
||||
RERROR("There is no navigation system in the current level, please check if there is one or contact a developer!");
|
||||
return false;
|
||||
}
|
||||
ANPCBase* character = Cast<ANPCBase>(GetPawn());
|
||||
FVector direction;
|
||||
FNavLocation navLocation;
|
||||
character->SetActorEnableCollision(false);
|
||||
DrawDebugLine(GetWorld(), GetPawn()->GetActorLocation(), GetPawn()->GetActorLocation() + GetActorUpVector()*-10000.0f, FColor(255, 0, 0),false, -1, 0, 12.333);
|
||||
FCollisionQueryParams collisionParams;
|
||||
collisionParams.AddIgnoredActor(this->GetPawn());
|
||||
GetWorld()->LineTraceMultiByChannel(result, GetPawn()->GetActorLocation(), GetPawn()->GetActorLocation() + GetActorUpVector()*-10000.0f, ECollisionChannel::ECC_WorldStatic, collisionParams);
|
||||
for (int i = 0; i < result.Num(); i++)
|
||||
{
|
||||
|
||||
if(result[i].ImpactPoint!=GetPawn()->GetActorLocation())
|
||||
{
|
||||
if (!navsys->ProjectPointToNavigation(GetPawn()->GetActorLocation(), navLocation))
|
||||
{
|
||||
continue;
|
||||
}
|
||||
GetCharacter()->GetCharacterMovement()->GravityScale = 1;
|
||||
SetBool(false,FName("ClimbKey"));
|
||||
navsys->SimpleMoveToLocation(this,destination);
|
||||
character->PlayNormalAnimation();
|
||||
character->SetActorEnableCollision(true);
|
||||
return true;
|
||||
}
|
||||
}
|
||||
character->PlayFencAnimation();
|
||||
direction = destination - GetPawn()->GetActorLocation();
|
||||
direction.Normalize();
|
||||
GetPawn()->SetActorLocation(GetPawn()->GetActorLocation()+(direction * 10));
|
||||
return false;
|
||||
}
|
||||
void AEnemyAIController::BeginInactiveState()
|
||||
{
|
||||
Super::BeginInactiveState();
|
||||
}
|
||||
void AEnemyAIController::SetVector(FVector vector, FName name)
|
||||
{
|
||||
if (blackBoardComp)
|
||||
{
|
||||
blackBoardComp->SetValueAsVector(name, (vector));
|
||||
}
|
||||
|
||||
}
|
||||
void AEnemyAIController::SetBool(bool boolean, FName name)
|
||||
{
|
||||
if (blackBoardComp)
|
||||
{
|
||||
blackBoardComp->SetValueAsBool(name, (boolean));
|
||||
}
|
||||
|
||||
}
|
||||
void AEnemyAIController::SetTarget(ANetworkCharacter* player)
|
||||
{
|
||||
if (blackBoardComp)
|
||||
{
|
||||
blackBoardComp->SetValueAsObject((FName("Target")), (player));
|
||||
}
|
||||
}
|
||||
ANetworkCharacter* AEnemyAIController::GetTarget()
|
||||
{
|
||||
if (blackBoardComp)
|
||||
{
|
||||
return Cast<ANetworkCharacter>(blackBoardComp->GetValue<UBlackboardKeyType_Object>(FName("Target")));
|
||||
}
|
||||
return nullptr;
|
||||
|
||||
}
|
||||
|
||||
FVector AEnemyAIController::GetLocationTarget()
|
||||
{
|
||||
if (blackBoardComp)
|
||||
{
|
||||
return FVector(blackBoardComp->GetValue<UBlackboardKeyType_Vector>(FName("TargetLocation")));
|
||||
}
|
||||
return FVector();
|
||||
}
|
||||
|
||||
FVector AEnemyAIController::GetHomeLocation()
|
||||
{
|
||||
if (blackBoardComp)
|
||||
{
|
||||
return FVector(blackBoardComp->GetValue<UBlackboardKeyType_Vector>(FName("HomeLocation")));
|
||||
}
|
||||
return FVector();
|
||||
}
|
||||
FVector AEnemyAIController::GetSelfLocation()
|
||||
{
|
||||
if (blackBoardComp)
|
||||
{
|
||||
return FVector(blackBoardComp->GetValue<UBlackboardKeyType_Vector>(FName("SelfLocation")));
|
||||
}
|
||||
return FVector();
|
||||
}
|
||||
bool AEnemyAIController::GetClimbKey()
|
||||
{
|
||||
if (blackBoardComp)
|
||||
{
|
||||
return blackBoardComp->GetValue<UBlackboardKeyType_Bool>((FName("ClimbKey")));
|
||||
}
|
||||
else return false;
|
||||
}
|
||||
@@ -0,0 +1,56 @@
|
||||
// Project Lab - NHTV Igad
|
||||
|
||||
#pragma once
|
||||
|
||||
#include "AIController.h"
|
||||
#include "EnemyAIController.generated.h"
|
||||
|
||||
/**
|
||||
*
|
||||
*/
|
||||
UCLASS()
|
||||
class UNREALPROJECT_API AEnemyAIController : public AAIController
|
||||
{
|
||||
GENERATED_BODY()
|
||||
|
||||
public:
|
||||
AEnemyAIController(const FObjectInitializer& PCIP);
|
||||
UPROPERTY(transient)
|
||||
class UBlackboardComponent* blackBoardComp;
|
||||
|
||||
UPROPERTY(transient)
|
||||
class UBehaviorTreeComponent* behaviorTreeComp;
|
||||
UPROPERTY(transient)
|
||||
float climbSpeed;
|
||||
virtual void Tick(float deltaTime)override;
|
||||
virtual void Possess(APawn* pawn) override;
|
||||
virtual void BeginInactiveState() override;
|
||||
bool VerticleMoveTo(FVector destination,FVector direction, float acceptRadius);
|
||||
bool JumpFence(FVector destination);
|
||||
|
||||
|
||||
|
||||
UFUNCTION(BlueprintCallable, Category = Behavior)
|
||||
void SetVector(FVector vector, FName name);
|
||||
UFUNCTION(BlueprintCallable, Category = Behavior)
|
||||
void SetBool(bool boolean, FName name);
|
||||
UFUNCTION(BlueprintCallable, Category = Behavior)
|
||||
void SetTarget(class ANetworkCharacter* player);
|
||||
UFUNCTION(BlueprintCallable, Category = Behavior)
|
||||
class ANetworkCharacter* GetTarget();
|
||||
UFUNCTION(BlueprintCallable, Category = Behavior)
|
||||
FVector GetLocationTarget();
|
||||
UFUNCTION(BlueprintCallable, Category = Behavior)
|
||||
FVector GetHomeLocation();
|
||||
UFUNCTION(BlueprintCallable, Category = Behavior)
|
||||
FVector GetSelfLocation();
|
||||
// UFUNCTION(BlueprintCallable, Category = Behavior)
|
||||
// bool GetEnableTree();
|
||||
UFUNCTION(BlueprintCallable, Category = Behavior)
|
||||
bool GetClimbKey();
|
||||
protected:
|
||||
uint8 targetkey;
|
||||
|
||||
|
||||
};
|
||||
|
||||
96
Source/UnrealProject/Creatures/BossBase.cpp
Normal file
96
Source/UnrealProject/Creatures/BossBase.cpp
Normal file
@@ -0,0 +1,96 @@
|
||||
// Project Lab - NHTV Igad
|
||||
|
||||
#include "UnrealProject.h"
|
||||
#include "BossBase.h"
|
||||
#include "MusicPlayer.h"
|
||||
#include "SpawnerBase.h"
|
||||
#include "NetworkPlayer.h"
|
||||
#include "KingOfTheHillGameMode.h"
|
||||
#include "KOTHBossSpawner.h"
|
||||
|
||||
|
||||
ABossBase::ABossBase()
|
||||
{
|
||||
PrimaryActorTick.bCanEverTick = true;
|
||||
|
||||
}
|
||||
|
||||
void ABossBase::BeginPlay()
|
||||
{
|
||||
Super::BeginPlay();
|
||||
|
||||
m_usesMana = false;
|
||||
m_musicPlayer = nullptr;
|
||||
|
||||
// Search for a music player in the world.
|
||||
for (TActorIterator<AMusicPlayer> actorItr(GetWorld()); actorItr; ++actorItr)
|
||||
{
|
||||
m_musicPlayer = *actorItr;
|
||||
break;
|
||||
}
|
||||
|
||||
if (Role != ROLE_Authority)
|
||||
return;
|
||||
}
|
||||
void ABossBase::EndPlay(const EEndPlayReason::Type EndPlayReason)
|
||||
{
|
||||
Super::EndPlay(EndPlayReason);
|
||||
|
||||
if (Role != ROLE_Authority)
|
||||
return;
|
||||
|
||||
// Stop playing boss music.
|
||||
if (m_musicPlayer != nullptr)
|
||||
m_musicPlayer->SetInCombat(false);
|
||||
|
||||
}
|
||||
void ABossBase::Tick(float deltaTime)
|
||||
{
|
||||
Super::Tick(deltaTime);
|
||||
if (Role != ROLE_Authority)
|
||||
return;
|
||||
|
||||
if((!IsPendingKill() || !IsPendingKill()) && GetTeam() < 5)
|
||||
{
|
||||
AKOTHBossSpawner* bSpawner = Cast<AKOTHBossSpawner>(m_spawn);
|
||||
AKingOfTheHillGameMode* kothMode = Cast<AKingOfTheHillGameMode>(GetWorld()->GetAuthGameMode());
|
||||
if(kothMode&&bSpawner)
|
||||
kothMode->AddScore(GetTeam(), deltaTime);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
void ABossBase::m_Engaged()
|
||||
{
|
||||
// Set music to combat music.
|
||||
if (m_musicPlayer != nullptr)
|
||||
m_musicPlayer->SetInCombat(true);
|
||||
}
|
||||
void ABossBase::m_Disengaged()
|
||||
{
|
||||
// Set music to regular music.
|
||||
if (m_musicPlayer != nullptr)
|
||||
m_musicPlayer->SetInCombat(false);
|
||||
}
|
||||
void ABossBase::NativeOnKilled(class ANetworkCharacter* killer, class UAbilityInfo* ability)
|
||||
{
|
||||
Super::NativeOnKilled(killer, ability);
|
||||
if (Role != ROLE_Authority)
|
||||
return;
|
||||
if (m_spawn &&m_spawn->possesable)
|
||||
{
|
||||
// m_spawn->CaptureCamp(killer->GetTeam());
|
||||
//AKOTHBossSpawner* bSpawner = Cast<AKOTHBossSpawner>(m_spawn);
|
||||
//AKingOfTheHillGameMode* kothMode = Cast<AKingOfTheHillGameMode>(GetWorld()->GetAuthGameMode());
|
||||
//if (kothMode&&bSpawner)
|
||||
//{
|
||||
/* for (TActorIterator<ASpawnerBase>actorIt(GetWorld()); actorIt; ++actorIt)
|
||||
{
|
||||
|
||||
ASpawnerBase *spawn = *actorIt;
|
||||
if(!spawn->possesable)
|
||||
spawn->SetTeam(killer->GetTeam());
|
||||
}
|
||||
}*/
|
||||
}
|
||||
}
|
||||
28
Source/UnrealProject/Creatures/BossBase.h
Normal file
28
Source/UnrealProject/Creatures/BossBase.h
Normal file
@@ -0,0 +1,28 @@
|
||||
// Project Lab - NHTV Igad
|
||||
|
||||
#pragma once
|
||||
|
||||
#include "Creatures/NPCBase.h"
|
||||
#include "BossBase.generated.h"
|
||||
|
||||
/**
|
||||
*
|
||||
*/
|
||||
UCLASS()
|
||||
class UNREALPROJECT_API ABossBase : public ANPCBase
|
||||
{
|
||||
GENERATED_BODY()
|
||||
public:
|
||||
ABossBase();
|
||||
virtual void BeginPlay() override;
|
||||
virtual void EndPlay(const EEndPlayReason::Type EndPlayReason) override;
|
||||
virtual void Tick(float deltaTime) override;
|
||||
virtual void NativeOnKilled(class ANetworkCharacter* killer, class UAbilityInfo* ability) override;
|
||||
protected:
|
||||
virtual void m_Engaged();
|
||||
virtual void m_Disengaged();
|
||||
float timer;
|
||||
private:
|
||||
UPROPERTY()
|
||||
class AMusicPlayer* m_musicPlayer;
|
||||
};
|
||||
300
Source/UnrealProject/Creatures/CharacterBase.cpp
Normal file
300
Source/UnrealProject/Creatures/CharacterBase.cpp
Normal file
@@ -0,0 +1,300 @@
|
||||
#include "UnrealProject.h"
|
||||
#include "SpawnerBase.h"
|
||||
#include "CharacterBase.h"
|
||||
#include "ItemBase.h"
|
||||
#include "BaseSkillObject.h"
|
||||
#include "WiebertAnimation.h"
|
||||
#include "DefaultGameState.h"
|
||||
#include "MainCharacterAnimation.h"
|
||||
#include "SkillTreeState.h"
|
||||
|
||||
|
||||
ACharacterBase::ACharacterBase()
|
||||
{
|
||||
m_team = 0;
|
||||
bReplicates = true;
|
||||
|
||||
visionRadius = 1000;
|
||||
}
|
||||
|
||||
void ACharacterBase::BeginPlay()
|
||||
{
|
||||
Super::BeginPlay();
|
||||
|
||||
// If this actor is attached to a spawner base, it must be ignored
|
||||
AActor* const parent = GetAttachParentActor();
|
||||
if (parent != nullptr && parent->IsA<ASpawnerBase>())
|
||||
return;
|
||||
|
||||
// Register to the minimap
|
||||
UWorld* const world = GetWorld();
|
||||
if (IsValid(world))
|
||||
{
|
||||
ADefaultGameState* const gameState = Cast<ADefaultGameState>(world->GetGameState());
|
||||
if (IsValid(gameState))
|
||||
{
|
||||
const FVector location = GetActorLocation();
|
||||
minimapHandle.position = FVector2D(location.X, location.Y);
|
||||
minimapHandle.character = this;
|
||||
gameState->minimapQuadtree.AddObject(minimapHandle);
|
||||
}
|
||||
}
|
||||
}
|
||||
void ACharacterBase::EndPlay(const EEndPlayReason::Type EndPlayReason)
|
||||
{
|
||||
Super::EndPlay(EndPlayReason);
|
||||
|
||||
// Unregister to the minimap
|
||||
ADefaultGameState* const gameState = Cast<ADefaultGameState>(GetWorld()->GetGameState());
|
||||
if (IsValid(gameState) && minimapHandle.root)
|
||||
gameState->minimapQuadtree.RemoveObject(minimapHandle);
|
||||
|
||||
minimapHandle.root = nullptr;
|
||||
minimapHandle.node = nullptr;
|
||||
minimapHandle.character = nullptr;
|
||||
}
|
||||
void ACharacterBase::Tick(float deltaTime)
|
||||
{
|
||||
Super::Tick(deltaTime);
|
||||
|
||||
const FVector location = GetActorLocation();
|
||||
minimapHandle.position = FVector2D(location.X, location.Y);
|
||||
}
|
||||
|
||||
void ACharacterBase::m_EquipSkills_Implementation(const TArray<UBaseSkillObject*>& skills)
|
||||
{
|
||||
for(UBaseSkillObject* skill : skills)
|
||||
{
|
||||
// Check if not yet equiped
|
||||
if(!m_equipedSkills.Contains(skill))
|
||||
{
|
||||
AItemBase::CreateItemsFromSkill(GetWorld(), skill, this);
|
||||
m_equipedSkills.Add(skill);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void ACharacterBase::EquipSkills(const TArray<UBaseSkillObject*>& skills)
|
||||
{
|
||||
m_EquipSkills(skills);
|
||||
}
|
||||
void ACharacterBase::EquipSkillsFromSkillTreeState(const struct FSkillTreeState& skState)
|
||||
{
|
||||
TArray<UBaseSkillObject*> skillObjects;
|
||||
for(const FSkillTreeStateObject& obj : skState.objects)
|
||||
{
|
||||
UBaseSkillObject* skillObj = obj.skillObject->GetDefaultObject<UBaseSkillObject>();
|
||||
if(!skillObj)
|
||||
continue;
|
||||
skillObjects.Add(skillObj);
|
||||
}
|
||||
m_EquipSkills(skillObjects);
|
||||
}
|
||||
void ACharacterBase::EquipItems_Implementation(const TArray<TSubclassOf<AItemBase>>& itemClasses)
|
||||
{
|
||||
for(auto& itemClass : itemClasses)
|
||||
{
|
||||
EquipItem_Implementation(itemClass);
|
||||
}
|
||||
}
|
||||
void ACharacterBase::EquipItem_Implementation(TSubclassOf<class AItemBase> itemClass)
|
||||
{
|
||||
if(itemClass == nullptr)
|
||||
return;
|
||||
|
||||
AItemBase* item = GetWorld()->SpawnActor<AItemBase>(itemClass);
|
||||
AItemBase* existing = AItemBase::CheckSlotOccupied(item->type, this);
|
||||
if(existing)
|
||||
existing->Destroy();
|
||||
if(item)
|
||||
{
|
||||
if(!item->AttachItemToCharacter(this))
|
||||
{
|
||||
item->Destroy();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void ACharacterBase::SendInitialAppearance(APlayerController* pc)
|
||||
{
|
||||
if(Role == ROLE_Authority)
|
||||
{
|
||||
m_ReceiveInitialAppearance(pc, GetEquipedSkills(), GetCharacterCustomization());
|
||||
}
|
||||
}
|
||||
|
||||
void ACharacterBase::m_ReceiveInitialAppearance_Implementation(APlayerController* pc, const TArray<class UBaseSkillObject*>& itemClasses, const FCharacterCustomization& cc)
|
||||
{
|
||||
APlayerController* localPC = GetGameInstance()->GetFirstLocalPlayerController();
|
||||
if(pc == localPC)
|
||||
{
|
||||
EquipSkills(itemClasses);
|
||||
SetCustomizations_Implementation(cc);
|
||||
}
|
||||
}
|
||||
|
||||
void ACharacterBase::Destroyed()
|
||||
{
|
||||
auto destroyItems = m_items;
|
||||
for(auto item : destroyItems)
|
||||
{
|
||||
if(IsValid(item))
|
||||
{
|
||||
item->Destroy();
|
||||
}
|
||||
}
|
||||
Super::Destroyed();
|
||||
}
|
||||
|
||||
TArray<TSubclassOf<AItemBase>> ACharacterBase::GetEquipedItemClasses() const
|
||||
{
|
||||
TArray<TSubclassOf<AItemBase>> ret;
|
||||
for(auto item : m_items)
|
||||
{
|
||||
ret.Add(item->GetClass());
|
||||
}
|
||||
return ret;
|
||||
}
|
||||
const TArray<class AItemBase*>& ACharacterBase::GetEquipedItems() const
|
||||
{
|
||||
return m_items;
|
||||
}
|
||||
|
||||
const FCharacterCustomization& ACharacterBase::GetCharacterCustomization() const
|
||||
{
|
||||
static FCharacterCustomization dummy;
|
||||
USkeletalMeshComponent* skMesh = GetMesh();
|
||||
if(skMesh)
|
||||
{
|
||||
UWiebertAnimation* animBP = Cast<UWiebertAnimation>(skMesh->GetAnimInstance());
|
||||
if(animBP)
|
||||
{
|
||||
return animBP->charCustomization;
|
||||
}
|
||||
else
|
||||
{
|
||||
UMainCharacterAnimation* charBP = Cast<UMainCharacterAnimation>(skMesh->GetAnimInstance());
|
||||
if (charBP)
|
||||
{
|
||||
return charBP->charCustomization;
|
||||
}
|
||||
}
|
||||
}
|
||||
return dummy;
|
||||
}
|
||||
|
||||
const TArray<class UBaseSkillObject*>& ACharacterBase::GetEquipedSkills() const
|
||||
{
|
||||
return m_equipedSkills;
|
||||
}
|
||||
|
||||
void ACharacterBase::UnequipSkills_Implementation(const TArray<UBaseSkillObject*>& skills)
|
||||
{
|
||||
for(auto skill : skills)
|
||||
{
|
||||
if(m_equipedSkills.Contains(skill))
|
||||
{
|
||||
TArray<AItemBase*> items;
|
||||
m_mappedItems.MultiFind(skill, items);
|
||||
for(auto item : items)
|
||||
item->Destroy();
|
||||
m_equipedSkills.Remove(skill);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void ACharacterBase::ClearEquipment_Implementation()
|
||||
{
|
||||
auto clearItems = m_items;
|
||||
for(auto item : clearItems)
|
||||
{
|
||||
if(IsValid(item))
|
||||
{
|
||||
item->Destroy();
|
||||
}
|
||||
}
|
||||
m_mappedItems.Reset();
|
||||
m_equipedSkills.SetNum(0);
|
||||
m_items.SetNum(0);
|
||||
}
|
||||
void ACharacterBase::SetCustomizations_Implementation(const FCharacterCustomization& cc)
|
||||
{
|
||||
USkeletalMeshComponent* skMesh = GetMesh();
|
||||
if(skMesh)
|
||||
{
|
||||
UWiebertAnimation* animBP = Cast<UWiebertAnimation>(skMesh->GetAnimInstance());
|
||||
if(animBP)
|
||||
{
|
||||
animBP->SetCharacterCustomization(cc);
|
||||
}
|
||||
else
|
||||
{
|
||||
UMainCharacterAnimation* charBP = Cast<UMainCharacterAnimation>(skMesh->GetAnimInstance());
|
||||
if (charBP)
|
||||
{
|
||||
charBP->SetCharacterCustomization(cc);
|
||||
}
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
GERROR("Can't set customization, no skeletal mesh on character");
|
||||
}
|
||||
}
|
||||
|
||||
void ACharacterBase::m_OnItemAdded(AItemBase* item)
|
||||
{
|
||||
// Add item to list of items assigned to given skill object
|
||||
if(item->skillObject)
|
||||
m_mappedItems.Add(item->skillObject, item);
|
||||
m_items.Add(item);
|
||||
}
|
||||
void ACharacterBase::m_OnItemRemoved(AItemBase* item)
|
||||
{
|
||||
// Remove mapped item
|
||||
m_mappedItems.RemoveSingle(item->skillObject, item);
|
||||
m_items.Remove(item);
|
||||
}
|
||||
|
||||
void ACharacterBase::m_OnRep_Team()
|
||||
{
|
||||
|
||||
}
|
||||
|
||||
int32 ACharacterBase::GetTeam() const
|
||||
{
|
||||
return m_team;
|
||||
}
|
||||
void ACharacterBase::SetTeam(int32 team)
|
||||
{
|
||||
check(Role == ROLE_Authority); // Server only call
|
||||
m_team = team;
|
||||
m_OnRep_Team();
|
||||
}
|
||||
|
||||
void ACharacterBase::GetLifetimeReplicatedProps(TArray<FLifetimeProperty>& OutLifetimeProps) const
|
||||
{
|
||||
Super::GetLifetimeReplicatedProps(OutLifetimeProps);
|
||||
|
||||
DOREPLIFETIME(ACharacterBase, m_team);
|
||||
DOREPLIFETIME(ACharacterBase, playerName);
|
||||
}
|
||||
|
||||
void ACharacterBase::RegisterEffect(class AEffect* effect)
|
||||
{
|
||||
if(effect != nullptr)
|
||||
m_effects.Add(effect);
|
||||
}
|
||||
void ACharacterBase::UnRegisterEffect(class AEffect* effect)
|
||||
{
|
||||
if(effect == nullptr)
|
||||
{
|
||||
FWARNING("invalid effect is unregistering");
|
||||
return;
|
||||
}
|
||||
m_effects.Remove(effect);
|
||||
}
|
||||
TArray<class AEffect*> ACharacterBase::GetEffects()
|
||||
{
|
||||
return m_effects;
|
||||
}
|
||||
94
Source/UnrealProject/Creatures/CharacterBase.h
Normal file
94
Source/UnrealProject/Creatures/CharacterBase.h
Normal file
@@ -0,0 +1,94 @@
|
||||
#pragma once
|
||||
#include "GameFramework/Character.h"
|
||||
#include "PlayerSetupState.h"
|
||||
#include "MiniMap.h"
|
||||
#include "CharacterBase.generated.h"
|
||||
|
||||
UCLASS()
|
||||
class ACharacterBase : public ACharacter
|
||||
{
|
||||
GENERATED_BODY()
|
||||
friend class AItemBase;
|
||||
public:
|
||||
ACharacterBase();
|
||||
|
||||
virtual void BeginPlay() override;
|
||||
virtual void EndPlay(const EEndPlayReason::Type EndPlayReason) override;
|
||||
virtual void Tick(float deltaTime) override;
|
||||
|
||||
UFUNCTION(BlueprintCallable, Category = "Appearance")
|
||||
void EquipSkills(const TArray<UBaseSkillObject*>& skills);
|
||||
UFUNCTION(BlueprintCallable, Category = "Appearance")
|
||||
void EquipSkillsFromSkillTreeState(const struct FSkillTreeState& skState);
|
||||
UFUNCTION(NetMulticast, Reliable)
|
||||
void EquipItems(const TArray<TSubclassOf<class AItemBase>>& itemClasses);
|
||||
UFUNCTION(NetMulticast, Reliable)
|
||||
void EquipItem(TSubclassOf<class AItemBase> itemClasses);
|
||||
|
||||
void SendInitialAppearance(APlayerController* pc);
|
||||
|
||||
void Destroyed() override;
|
||||
|
||||
UFUNCTION(BlueprintCallable, NetMulticast, Reliable, Category = "Appearance")
|
||||
void UnequipSkills(const TArray<UBaseSkillObject*>& skills);
|
||||
UFUNCTION(BlueprintCallable, NetMulticast, Reliable, Category = "Appearance")
|
||||
void ClearEquipment();
|
||||
UFUNCTION(BlueprintCallable, NetMulticast, Reliable, Category = "Appearance")
|
||||
void SetCustomizations(const FCharacterCustomization& cc);
|
||||
|
||||
UFUNCTION(BlueprintCallable, Category = "Appearance")
|
||||
const TArray<class UBaseSkillObject*>& GetEquipedSkills() const;
|
||||
UFUNCTION(BlueprintCallable, Category = "Appearance")
|
||||
const TArray<class AItemBase*>& GetEquipedItems() const;
|
||||
UFUNCTION(BlueprintCallable, Category = "Appearance")
|
||||
TArray<TSubclassOf<class AItemBase>> GetEquipedItemClasses() const;
|
||||
UFUNCTION(BlueprintCallable, Category = "Appearance")
|
||||
const FCharacterCustomization& GetCharacterCustomization() const;
|
||||
|
||||
UFUNCTION(BlueprintCallable, Category = "Team")
|
||||
int32 GetTeam() const;
|
||||
UFUNCTION(BlueprintCallable, Category = "Team")
|
||||
virtual void SetTeam(int32 team);
|
||||
|
||||
// Tracking functions for effects on characters
|
||||
UFUNCTION(BlueprintCallable, Category = "Effect")
|
||||
void RegisterEffect(class AEffect* effect);
|
||||
UFUNCTION(BlueprintCallable, Category = "Effect")
|
||||
void UnRegisterEffect(class AEffect* effect);
|
||||
UFUNCTION(BlueprintCallable, Category = "Effect")
|
||||
TArray<class AEffect*> GetEffects();
|
||||
|
||||
// The name of the player that controls this unit (Only for ghosts, players and illusions)
|
||||
UPROPERTY(BlueprintReadOnly, Replicated)
|
||||
FString playerName;
|
||||
|
||||
UPROPERTY(EditDefaultsOnly, BlueprintReadOnly, Category = "Vision")
|
||||
float visionRadius;
|
||||
|
||||
MiniMap::MinimapHandle minimapHandle;
|
||||
|
||||
private:
|
||||
// Trigerred from RequestInitialAppearance send back to the client
|
||||
UFUNCTION(NetMulticast, Reliable)
|
||||
void m_ReceiveInitialAppearance(APlayerController* pc, const TArray<class UBaseSkillObject*>& itemClasses, const FCharacterCustomization& cc);
|
||||
// Internal usage EquipSkills to allow multiple ways to call this function from blueprints
|
||||
UFUNCTION(NetMulticast, Reliable)
|
||||
void m_EquipSkills(const TArray<UBaseSkillObject*>& skills);
|
||||
void m_OnItemAdded(AItemBase* item);
|
||||
void m_OnItemRemoved(AItemBase* item);
|
||||
|
||||
protected:
|
||||
UFUNCTION()
|
||||
virtual void m_OnRep_Team();
|
||||
|
||||
UPROPERTY(ReplicatedUsing=m_OnRep_Team)
|
||||
int32 m_team;
|
||||
|
||||
// Maps skills to the items it spawned
|
||||
TMultiMap<class UBaseSkillObject*, class AItemBase*> m_mappedItems;
|
||||
TArray<class UBaseSkillObject*> m_equipedSkills;
|
||||
UPROPERTY()
|
||||
TArray<class AItemBase*> m_items;
|
||||
|
||||
TArray<class AEffect*> m_effects;
|
||||
};
|
||||
190
Source/UnrealProject/Creatures/EnemyBase.cpp
Normal file
190
Source/UnrealProject/Creatures/EnemyBase.cpp
Normal file
@@ -0,0 +1,190 @@
|
||||
// Project Lab - NHTV Igad
|
||||
|
||||
#include "UnrealProject.h"
|
||||
#include "SpawnerBase.h"
|
||||
#include "EnemyBase.h"
|
||||
#include "AbilityInfo.h"
|
||||
#include "NetworkPlayer.h"
|
||||
#include "GeneralEnemy.h"
|
||||
#include "BehaviorTree/EnemyAIController.h"
|
||||
#include "BehaviorTree/BehaviorTree.h"
|
||||
#include "MinionAnimInstance.h"
|
||||
|
||||
AEnemyBase::AEnemyBase()
|
||||
{
|
||||
PrimaryActorTick.bCanEverTick = true;
|
||||
finnishedAttack = false;
|
||||
informGeneralTime = 2.0f;
|
||||
m_tempTarget = nullptr;
|
||||
// AIControllerClass = AEnemyAIController::StaticClass();
|
||||
}
|
||||
void AEnemyBase::BeginPlay()
|
||||
{
|
||||
Super::BeginPlay();
|
||||
|
||||
specialAbilityChance = m_ReCalcChance(specialAbilityChance);
|
||||
enemycontroller = Cast<AEnemyAIController>(GetController());
|
||||
if (enemycontroller && m_spawn)
|
||||
{
|
||||
enemycontroller->SetBool(true, FName("ClimbKey"));
|
||||
enemycontroller->SetVector(FVector(0, 0, 1), FName("ClimbDirection"));
|
||||
enemycontroller->SetVector(m_spawn->GetActorLocation() + FVector(0, 0, 100), FName("ClimbLocation"));
|
||||
|
||||
}
|
||||
else
|
||||
{
|
||||
|
||||
FActorSpawnParameters SpawnInfo;
|
||||
SpawnInfo.Instigator = Instigator;
|
||||
SpawnInfo.SpawnCollisionHandlingOverride = ESpawnActorCollisionHandlingMethod::AlwaysSpawn;
|
||||
SpawnInfo.OverrideLevel = GetLevel();
|
||||
SpawnInfo.ObjectFlags |= RF_Transient; // We never want to save AI controllers into a map
|
||||
AController* NewController = GetWorld()->SpawnActor<AController>(AEnemyAIController::StaticClass(), GetActorLocation(), GetActorRotation(), SpawnInfo);
|
||||
if (NewController != NULL)
|
||||
{
|
||||
// if successful will result in setting this->Controller
|
||||
// as part of possession mechanics
|
||||
NewController->Possess(this);
|
||||
}
|
||||
enemycontroller = Cast<AEnemyAIController>(GetController());
|
||||
if (enemycontroller && m_spawn)
|
||||
{
|
||||
if (m_spawn->SpawnLocation.Num() > 0)
|
||||
{
|
||||
PlayClimbingAnimation();
|
||||
enemycontroller->SetBool(true, FName("ClimbKey"));
|
||||
enemycontroller->SetVector(FVector(0, 0, 1), FName("ClimbDirection"));
|
||||
enemycontroller->SetVector(m_spawn->GetActorLocation() + FVector(0, 0, 100), FName("ClimbLocation"));
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
// RERROR("There is no valid controller on this enemy");
|
||||
}
|
||||
}
|
||||
hasSpawned = false;
|
||||
|
||||
}
|
||||
void AEnemyBase::EndPlay(const EEndPlayReason::Type EndPlayReason)
|
||||
{
|
||||
Super::EndPlay(EndPlayReason);
|
||||
general = nullptr;
|
||||
if (m_spawn)
|
||||
{
|
||||
|
||||
if (m_spawn->hasGeneral)
|
||||
{
|
||||
m_spawn->formationEnemies.Remove(this);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void AEnemyBase::Tick(float deltaTime)
|
||||
{
|
||||
Super::Tick(deltaTime);
|
||||
if (Role != ROLE_Authority)
|
||||
{
|
||||
return;
|
||||
}
|
||||
m_confusionTimer -= deltaTime;
|
||||
if (!IsA(AGeneralEnemy::StaticClass()))
|
||||
m_hasGeneral = hasGeneral;
|
||||
if (!hasGeneral && enemycontroller && enemycontroller->IsA(AEnemyAIController::StaticClass()))
|
||||
{
|
||||
|
||||
enemycontroller->SetBool(false, FName("EnableTree"));
|
||||
}
|
||||
|
||||
if (IsValid(target))
|
||||
{
|
||||
if (hasGeneral)
|
||||
{
|
||||
m_informGeneralTimer -= deltaTime;
|
||||
if (m_informGeneralTimer < 0.0f&&IsValid(general))
|
||||
InformGeneral();
|
||||
}
|
||||
else
|
||||
{
|
||||
SwitchAgro();
|
||||
}
|
||||
}
|
||||
else if(!hasGeneral)
|
||||
{
|
||||
FVector randpos = FMath::VRand();
|
||||
randpos.Z = 0.0f;
|
||||
float randomRange = FMath::FRandRange(m_spawn->aggroRadius / 4, m_spawn->aggroRadius);
|
||||
|
||||
randpos = randpos*randomRange;
|
||||
|
||||
MoveToPointSlow(randpos + m_spawn->GetActorLocation(), 50.0f);
|
||||
}
|
||||
}
|
||||
void AEnemyBase::InformGeneral()
|
||||
{
|
||||
if (!IsValid(general) || !IsValid(target))
|
||||
return;
|
||||
if (FVector::DistSquared(m_spawn->GetActorLocation(), target->GetActorLocation()) > m_spawn->deaggroRadius*m_spawn->deaggroRadius)
|
||||
return;
|
||||
if( !IsValid(general->target))
|
||||
{
|
||||
general->target = m_tempTarget;
|
||||
ChangeNPCAnimation((uint8)EMinionAnimState::MAS_Pointing);
|
||||
//CHANGEANIMATION(EMinionAnimState::MAS_Pointing);
|
||||
|
||||
m_informGeneralTimer = informGeneralTime;
|
||||
m_tempTarget = nullptr;
|
||||
}
|
||||
}
|
||||
void AEnemyBase::SwitchAgro()
|
||||
{
|
||||
if (m_spawn->GetNearbyPlayers().Num() <= 1)
|
||||
return;
|
||||
float mostDamage =0.0f;
|
||||
ANetworkCharacter* character = nullptr;
|
||||
for (int i = 0; i < m_spawn->GetNearbyPlayers().Num(); i++)
|
||||
{
|
||||
if (m_spawn->GetNearbyPlayers()[i]->GetTotalDamage()>mostDamage)
|
||||
{
|
||||
mostDamage = m_spawn->GetNearbyPlayers()[i]->GetTotalDamage();
|
||||
character = m_spawn->GetNearbyPlayers()[i];
|
||||
}
|
||||
}
|
||||
if(IsValid(character))
|
||||
this->target = character;
|
||||
}
|
||||
void AEnemyBase::ConfusionBehavior()
|
||||
{
|
||||
FVector randpos = FMath::VRand();
|
||||
randpos.Z = 0.0f;
|
||||
float randomRange = FMath::FRandRange(m_spawn->aggroRadius / 4, m_spawn->aggroRadius);
|
||||
|
||||
randpos = randpos*randomRange;
|
||||
|
||||
MoveToPointSlow(randpos + m_spawn->GetActorLocation(), 50.0f);
|
||||
}
|
||||
void AEnemyBase::ChargeBehavior()
|
||||
{
|
||||
|
||||
}
|
||||
void AEnemyBase::OffensiveBehavior()
|
||||
{
|
||||
|
||||
|
||||
}
|
||||
void AEnemyBase::RangedBehavior()
|
||||
{
|
||||
}
|
||||
void AEnemyBase::ResetConfusionTimer(float confusionTime)
|
||||
{
|
||||
m_confusionTimer = confusionTime;
|
||||
}
|
||||
float AEnemyBase::m_ReCalcChance(float chance)
|
||||
{
|
||||
return (1000 - (chance * 10));
|
||||
}
|
||||
int32 AEnemyBase::NativeDealDamage(class ANetworkCharacter* dealer, int32 damage, float armorPercentageIgnore, class UAbilityInfo* ability)
|
||||
{
|
||||
m_tempTarget = dealer;
|
||||
m_informGeneralTimer = informGeneralTime;
|
||||
return Super::NativeDealDamage(dealer, damage, armorPercentageIgnore, ability);
|
||||
}
|
||||
60
Source/UnrealProject/Creatures/EnemyBase.h
Normal file
60
Source/UnrealProject/Creatures/EnemyBase.h
Normal file
@@ -0,0 +1,60 @@
|
||||
// Project Lab - NHTV Igad
|
||||
|
||||
#pragma once
|
||||
|
||||
#include "Creatures/NPCBase.h"
|
||||
#include "EnemyBase.generated.h"
|
||||
|
||||
/**
|
||||
*
|
||||
*/
|
||||
|
||||
UCLASS()
|
||||
class UNREALPROJECT_API AEnemyBase : public ANPCBase
|
||||
{
|
||||
GENERATED_BODY()
|
||||
public:
|
||||
AEnemyBase();
|
||||
virtual void BeginPlay() override;
|
||||
virtual void EndPlay(const EEndPlayReason::Type EndPlayReason) override;
|
||||
virtual void Tick(float deltaTime) override;
|
||||
virtual int32 NativeDealDamage(class ANetworkCharacter* dealer, int32 damage, float armorPercentageIgnore, class UAbilityInfo* ability) override;
|
||||
|
||||
void OffensiveBehavior();
|
||||
void RangedBehavior();
|
||||
void ChargeBehavior();
|
||||
void InformGeneral();
|
||||
void SwitchAgro();
|
||||
void ConfusionBehavior();
|
||||
void ResetConfusionTimer(float confusionTime);
|
||||
UPROPERTY(EditAnywhere, Category = Behavior)
|
||||
class UBehaviorTree* treeBehavior;
|
||||
UPROPERTY(EditAnywhere, Category = "Attack Settings")
|
||||
float informGeneralTime;
|
||||
UPROPERTY(EditAnywhere, Category = "Ability Settings")
|
||||
class UAbilityInfo* specialAbility;
|
||||
UPROPERTY(EditAnywhere, Category = "Ability Settings")
|
||||
class UAbilityInfo* meleeAbility;
|
||||
UPROPERTY(EditAnywhere, Category = "Ability Settings")
|
||||
class UAbilityInfo* rangedAbility;
|
||||
UPROPERTY(EditAnywhere, Category = "Ability Settings")
|
||||
float specialAbilityChance;
|
||||
FVector scatterLocation;
|
||||
bool hasGeneral;
|
||||
bool finnishedAttack;
|
||||
bool hasSpawned;
|
||||
UPROPERTY()
|
||||
class AEnemyAIController* enemycontroller;
|
||||
UPROPERTY()
|
||||
class AGeneralEnemy* general;
|
||||
UPROPERTY()
|
||||
ANetworkCharacter* m_tempTarget;
|
||||
protected:
|
||||
float m_ReCalcChance(float chance);
|
||||
bool m_rotateToPlayer;
|
||||
float m_lastPercentage;
|
||||
class UAbilityInfo* m_tempAbility;
|
||||
float m_informGeneralTimer;
|
||||
float m_confusionTimer;
|
||||
|
||||
};
|
||||
237
Source/UnrealProject/Creatures/GeneralEnemy.cpp
Normal file
237
Source/UnrealProject/Creatures/GeneralEnemy.cpp
Normal file
@@ -0,0 +1,237 @@
|
||||
// Project Lab - NHTV Igad
|
||||
|
||||
#include "UnrealProject.h"
|
||||
#include "GeneralEnemy.h"
|
||||
#include "SpawnerBase.h"
|
||||
#include "BehaviorTree/EnemyAIController.h"
|
||||
#include "NetworkPlayer.h"
|
||||
#include "OffensiveEnemy.h"
|
||||
#include "MinionAnimInstance.h"
|
||||
|
||||
AGeneralEnemy::AGeneralEnemy()
|
||||
{
|
||||
|
||||
PrimaryActorTick.bCanEverTick = true;
|
||||
}
|
||||
void AGeneralEnemy::BeginPlay()
|
||||
{
|
||||
Super::BeginPlay();
|
||||
if (Role != ROLE_Authority)
|
||||
return;
|
||||
if (m_spawn)
|
||||
{
|
||||
m_spawn->hasGeneral = true;
|
||||
FVector addVector;
|
||||
for (int i = 0; i < 4; i++)
|
||||
{
|
||||
addVector = m_spawn->GetActorForwardVector().RotateAngleAxis(i * 90, FVector::UpVector);
|
||||
patrolLocations.Add(addVector);
|
||||
}
|
||||
}
|
||||
if (distance == 0)
|
||||
{
|
||||
distance = 100.0f;
|
||||
}
|
||||
/* for (int i = 0; i < formationEnemies.Num(); i++)
|
||||
{
|
||||
formationEnemies[i]->general = this;
|
||||
}*/
|
||||
|
||||
|
||||
}
|
||||
void AGeneralEnemy::EndPlay(const EEndPlayReason::Type EndPlayReason)
|
||||
{
|
||||
Super::EndPlay(EndPlayReason);
|
||||
if (Role != ROLE_Authority)
|
||||
return;
|
||||
for (int i = 0; i < formationEnemies.Num(); i++)
|
||||
{
|
||||
formationEnemies[i]->ResetConfusionTimer(3.0f);
|
||||
formationEnemies[i]->general = nullptr;
|
||||
}
|
||||
if (m_spawn != nullptr)
|
||||
m_spawn->hasGeneral = false;
|
||||
//formationEnemies.Empty();
|
||||
}
|
||||
void AGeneralEnemy::Tick(float deltaTime)
|
||||
{
|
||||
Super::Tick(deltaTime);
|
||||
if (Role != ROLE_Authority)
|
||||
return;
|
||||
if (!IsValid(m_spawn))
|
||||
{
|
||||
RERROR("There is no spawn for the general enemy");
|
||||
return;
|
||||
}
|
||||
for (int i = 0; i < formationEnemies.Num(); i++)
|
||||
{
|
||||
if (IsValid(formationEnemies[i]->general))
|
||||
continue;
|
||||
formationEnemies[i]->general = this;
|
||||
|
||||
}
|
||||
|
||||
if (IsValid(target))
|
||||
{
|
||||
|
||||
|
||||
FVector2D locationVec = FVector2D(target->GetActorLocation()) - FVector2D(GetActorLocation());
|
||||
locationVec.Normalize();
|
||||
if (FVector::DistSquaredXY(GetActorLocation(), target->GetActorLocation()) > m_spawn->formationDistance* m_spawn->formationDistance)
|
||||
{
|
||||
|
||||
MoveToPoint((target->GetActorLocation()));// +(FVector(locationVec, 0))* m_spawn->formationDistance));
|
||||
DefaultFormation();
|
||||
|
||||
}
|
||||
else
|
||||
{
|
||||
GetCharacterMovement()->Velocity = FVector::ZeroVector;
|
||||
MoveToPoint(GetActorLocation());
|
||||
GetCharacterMovement()->Velocity = FVector::ZeroVector;
|
||||
SetActorRotation(FVector(target->GetActorLocation() - GetActorLocation()).Rotation());
|
||||
ChargeFormation();
|
||||
}
|
||||
|
||||
}
|
||||
else
|
||||
{
|
||||
IdleFormation(deltaTime);
|
||||
}
|
||||
}
|
||||
void AGeneralEnemy::SingleTargetFormation(int enemyNum)
|
||||
{
|
||||
if ((formationEnemies[enemyNum]->enemycontroller)->IsA(AEnemyAIController::StaticClass()))
|
||||
{
|
||||
formationEnemies[enemyNum]->target = target;
|
||||
formationEnemies[enemyNum]->enemycontroller->SetBool(true, FName("EnableTree"));
|
||||
formationEnemies[enemyNum]->SetActorRotation(FVector(target->GetActorLocation() - formationEnemies[enemyNum]->GetActorLocation()).Rotation());
|
||||
if (count < 3)
|
||||
{
|
||||
formationEnemies[enemyNum]->enemycontroller->SetBool(false, FName("GuardKey"));
|
||||
formationEnemies[enemyNum]->enemycontroller->SetVector(target->GetActorLocation() + target->GetActorForwardVector() * 200, FName("FrontLoc"));
|
||||
formationEnemies[enemyNum]->enemycontroller->SetVector(target->GetActorLocation() + target->GetActorForwardVector() * -200, FName("BacktLoc"));
|
||||
formationEnemies[enemyNum]->enemycontroller->SetVector(target->GetActorLocation() + target->GetActorRightVector() *-200, FName("LeftLoc"));
|
||||
formationEnemies[enemyNum]->enemycontroller->SetVector(target->GetActorLocation() + target->GetActorRightVector() * 200, FName("RightLoc"));
|
||||
}
|
||||
else
|
||||
{
|
||||
formationEnemies[enemyNum]->enemycontroller->SetBool(true, FName("GuardKey"));
|
||||
formationEnemies[enemyNum]->enemycontroller->SetVector(GetActorLocation() + GetActorForwardVector() * 100, FName("HomeLocation"));
|
||||
}
|
||||
count++;
|
||||
}
|
||||
|
||||
}
|
||||
void AGeneralEnemy::ChargeFormation()
|
||||
{
|
||||
if (!IsValid(target))
|
||||
return;
|
||||
count = 0;
|
||||
TArray<ANetworkPlayer*> targetArray = m_spawn->GetNearbyPlayers();
|
||||
if (targetArray.Num() == 0)
|
||||
return;
|
||||
for (int i = 0; i < formationEnemies.Num(); i++)
|
||||
{
|
||||
if (targetArray.Num() == 1)
|
||||
{
|
||||
SingleTargetFormation(i);
|
||||
continue;
|
||||
}
|
||||
if (Cast<AOffensiveEnemy>(formationEnemies[i]))
|
||||
{
|
||||
formationEnemies[i]->target = m_spawn->GetClosestPlayer();
|
||||
formationEnemies[i]->MoveToPoint(target->GetActorLocation());
|
||||
}
|
||||
else
|
||||
{
|
||||
ANetworkPlayer* newTarget = nullptr;
|
||||
for (int j = 0; j < targetArray.Num(); j++)
|
||||
{
|
||||
if(targetArray[j]==m_spawn->GetClosestPlayer())
|
||||
continue;
|
||||
newTarget = targetArray[j];
|
||||
continue;
|
||||
}
|
||||
formationEnemies[i]->target = newTarget;
|
||||
}
|
||||
}
|
||||
}
|
||||
void AGeneralEnemy::DefaultFormation()
|
||||
{
|
||||
FVector rotatedFormation;
|
||||
for (int i = 0; i < formationEnemies.Num(); i++)
|
||||
{
|
||||
if (!IsValid(formationEnemies[i]) || !IsValid(m_spawn) || (m_spawn->formationPoints.Num() + 1) < formationEnemies.Num())
|
||||
{
|
||||
continue;
|
||||
}
|
||||
|
||||
|
||||
formationEnemies[i]->target = target;
|
||||
rotatedFormation = GetActorRotation().RotateVector(FVector(m_spawn->formationPoints[i + 1].X - m_spawn->formationPoints[0].X, m_spawn->formationPoints[i + 1].Y - m_spawn->formationPoints[0].Y, 0));
|
||||
|
||||
formationEnemies[i]->MoveToPoint(GetActorLocation() + rotatedFormation);
|
||||
formationEnemies[i]->SetActorRotation(FRotator(0, m_spawn->formationRotation[i + 1], 0) + (GetActorRotation()));
|
||||
|
||||
formationEnemies[i]->enemycontroller->SetBool(false, FName("EnableTree"));
|
||||
formationEnemies[i]->enemycontroller->SetVector(formationEnemies[i]->GetActorLocation(), FName("HomeLocation"));
|
||||
|
||||
|
||||
}
|
||||
}
|
||||
int AGeneralEnemy::NativeDealDamage(class ANetworkCharacter* dealer, int32 damage, float armorPercentageIgnore, class UAbilityInfo* ability)
|
||||
{
|
||||
//CHANGEANIMATION(EMinionAnimState::MAS_Pointing);
|
||||
ChangeNPCAnimation((uint8)EMinionAnimState::MAS_Pointing);
|
||||
return Super::NativeDealDamage(dealer, damage, armorPercentageIgnore, ability);
|
||||
}
|
||||
void AGeneralEnemy::NativeOnKilled(class ANetworkCharacter* killer, class UAbilityInfo* ability)
|
||||
{
|
||||
Super::NativeOnKilled(killer, ability);
|
||||
if (Role != ROLE_Authority)
|
||||
return;
|
||||
if (m_spawn &&m_spawn->possesable)
|
||||
{
|
||||
int32 team = killer->GetTeam();
|
||||
m_spawn->CaptureCamp(team);
|
||||
|
||||
}
|
||||
}
|
||||
void AGeneralEnemy::IdleFormation(float deltaTime)
|
||||
{
|
||||
// Move to a random point near the spawn point;
|
||||
|
||||
//controll enemies in the formation
|
||||
int counter = 0;
|
||||
for (int i = 0; i < formationEnemies.Num(); i++)
|
||||
{
|
||||
if (!IsValid(formationEnemies[i]) || !IsValid(m_spawn) || (m_spawn->formationPoints.Num() + 1) < formationEnemies.Num())
|
||||
{
|
||||
continue;
|
||||
}
|
||||
|
||||
if (!formationEnemies[i]->m_tempTarget)
|
||||
{
|
||||
if (Cast<AOffensiveEnemy>(formationEnemies[i])&&counter<4)
|
||||
{
|
||||
formationEnemies[i]->PatrolBetweenLocations (patrolLocations[counter].RotateAngleAxis( 90, FVector::UpVector)*500+ patrolLocations[counter]* 500 + m_spawn->GetActorLocation(), patrolLocations[counter].RotateAngleAxis( 270, FVector::UpVector) * 500 + patrolLocations[counter] * 500 + m_spawn->GetActorLocation(), deltaTime);
|
||||
counter++;
|
||||
continue;
|
||||
}
|
||||
|
||||
formationEnemies[i]->MoveToPoint(FVector(m_spawn->formationPoints[i + 1],0)+m_spawn->GetActorLocation());
|
||||
//formationEnemies[i]->MoveToPointSlow(randpos + m_spawn->GetActorLocation(), 50.0f);
|
||||
|
||||
|
||||
}
|
||||
else
|
||||
{
|
||||
if(formationEnemies[i]->IsA(AOffensiveEnemy::StaticClass()))
|
||||
Cast<AOffensiveEnemy>(formationEnemies[i])->BasicBehavior();
|
||||
if(formationEnemies[i]->IsA(ARangedEnemy::StaticClass()))
|
||||
Cast<ARangedEnemy>(formationEnemies[i])->BasicBehavior();
|
||||
}
|
||||
formationEnemies[i]->target = nullptr;
|
||||
}
|
||||
}
|
||||
40
Source/UnrealProject/Creatures/GeneralEnemy.h
Normal file
40
Source/UnrealProject/Creatures/GeneralEnemy.h
Normal file
@@ -0,0 +1,40 @@
|
||||
// Project Lab - NHTV Igad
|
||||
|
||||
#pragma once
|
||||
|
||||
#include "Creatures/RangedEnemy.h"
|
||||
#include "GeneralEnemy.generated.h"
|
||||
|
||||
/**
|
||||
*
|
||||
*/
|
||||
UCLASS()
|
||||
class UNREALPROJECT_API AGeneralEnemy : public ARangedEnemy
|
||||
{
|
||||
GENERATED_BODY()
|
||||
|
||||
|
||||
public:
|
||||
AGeneralEnemy();
|
||||
virtual void BeginPlay() override;
|
||||
virtual void EndPlay(const EEndPlayReason::Type EndPlayReason) override;
|
||||
virtual void Tick(float deltaTime) override;
|
||||
virtual void NativeOnKilled(class ANetworkCharacter* killer, class UAbilityInfo* ability) override;
|
||||
virtual int NativeDealDamage(class ANetworkCharacter* dealer, int32 damage, float armorPercentageIgnore, class UAbilityInfo* ability)override;
|
||||
void IdleFormation(float deltaTime);
|
||||
void ChargeFormation();
|
||||
void DefaultFormation();
|
||||
void SingleTargetFormation(int enemyNum );
|
||||
TArray<FVector2D> formationPoints;
|
||||
TArray<FVector> patrolLocations;
|
||||
UPROPERTY()
|
||||
TArray<class AEnemyBase*> formationEnemies;
|
||||
UPROPERTY(EditAnywhere, Category = "General settings")
|
||||
float distance;
|
||||
UPROPERTY(EditAnywhere, Category = "General settings")
|
||||
float oFormationRadius;
|
||||
private:
|
||||
int count;
|
||||
|
||||
|
||||
};
|
||||
189
Source/UnrealProject/Creatures/IllusionCharacter.cpp
Normal file
189
Source/UnrealProject/Creatures/IllusionCharacter.cpp
Normal file
@@ -0,0 +1,189 @@
|
||||
// Project Lab - NHTV Igad
|
||||
|
||||
#include "UnrealProject.h"
|
||||
#include "Creatures/NetworkCharacter.h"
|
||||
#include "Creatures/NetworkPlayer.h"
|
||||
#include "IllusionCharacter.h"
|
||||
#include "BlueprintAbilityLibrary.h"
|
||||
#include "AIController.h"
|
||||
#include "DealDamageProxy.h"
|
||||
#include "AbilityInfo.h"
|
||||
#include "NativeModifiers.h"
|
||||
|
||||
AIllusionCharacter::AIllusionCharacter()
|
||||
{
|
||||
PrimaryActorTick.bCanEverTick = true;
|
||||
aggroRange = 400;
|
||||
acceptenceDistance = 200;
|
||||
lifeTime = 30;
|
||||
}
|
||||
|
||||
void AIllusionCharacter::BeginPlay()
|
||||
{
|
||||
Super::BeginPlay();
|
||||
|
||||
if (Role != ROLE_Authority)
|
||||
{
|
||||
return;
|
||||
}
|
||||
FActorSpawnParameters SpawnInfo;
|
||||
SpawnInfo.Instigator = Instigator;
|
||||
SpawnInfo.SpawnCollisionHandlingOverride = ESpawnActorCollisionHandlingMethod::AlwaysSpawn;
|
||||
SpawnInfo.OverrideLevel = GetLevel();
|
||||
SpawnInfo.ObjectFlags |= RF_Transient; // We never want to save AI controllers into a map
|
||||
AController* NewController = GetWorld()->SpawnActor<AController>(AAIController::StaticClass(), GetActorLocation(), GetActorRotation(), SpawnInfo);
|
||||
|
||||
if (NewController != NULL)
|
||||
{
|
||||
// if successful will result in setting this->Controller
|
||||
// as part of possession mechanics
|
||||
NewController->Possess(this);
|
||||
}
|
||||
|
||||
if (IsValid(illOwner))
|
||||
{
|
||||
m_maxHealth = illOwner->GetMaxHealth();
|
||||
m_health = illOwner->GetHealth();
|
||||
}
|
||||
else
|
||||
{
|
||||
FERROR("NO ILLOWNER IN ILLUSIONCHARACTER");
|
||||
}
|
||||
if (illOwner)
|
||||
{
|
||||
abilities.Add(illOwner->abilities[0]);
|
||||
m_attackSpeed =illOwner->m_attackSpeed;
|
||||
}
|
||||
m_isdying = false;
|
||||
}
|
||||
void AIllusionCharacter::EndPlay(const EEndPlayReason::Type EndPlayReason)
|
||||
{
|
||||
Super::EndPlay(EndPlayReason);
|
||||
}
|
||||
void AIllusionCharacter::Tick(float deltaTime)
|
||||
{
|
||||
Super::Tick(deltaTime);
|
||||
if (Role != ROLE_Authority)
|
||||
{
|
||||
return;
|
||||
}
|
||||
lifeTime -= deltaTime;
|
||||
if (lifeTime <= 0)
|
||||
{
|
||||
if(!m_isdying)
|
||||
Kill();
|
||||
return;
|
||||
}
|
||||
UNavigationSystem* NavSys = UNavigationSystem::GetCurrent(GetWorld());
|
||||
if (!NavSys)
|
||||
{
|
||||
RERROR("there is no nav sys");
|
||||
}
|
||||
if (!GetController())
|
||||
{
|
||||
RERROR("there is no controller");
|
||||
}
|
||||
if (!IsValid(illOwner))
|
||||
{
|
||||
Kill();
|
||||
return;
|
||||
}
|
||||
if (!IsValid(target))
|
||||
{
|
||||
attacking = false;
|
||||
//targetList = ULib::GetCharacterOverlaps();
|
||||
target = GetClosestTarget();
|
||||
if (FVector::DistSquared(illOwner->GetActorLocation(), GetActorLocation()) > acceptenceDistance*acceptenceDistance)
|
||||
{
|
||||
MoveTo(illOwner->GetActorLocation());
|
||||
FPRINT("moving to the illOwner");
|
||||
}
|
||||
else MoveTo(GetActorLocation());
|
||||
return;
|
||||
}
|
||||
if (!attacking)
|
||||
{
|
||||
attacking = IsAttacking(1.0f);
|
||||
return;
|
||||
}
|
||||
Attack();
|
||||
|
||||
}
|
||||
void AIllusionCharacter::Attack()
|
||||
{
|
||||
int randdistance =0;
|
||||
randdistance += ((int)abilities[0]->AICastRange / 2);
|
||||
if (FVector::Dist(GetActorLocation(), target->GetActorLocation()) < randdistance)
|
||||
{
|
||||
MoveTo(GetActorLocation());
|
||||
SetActorRotation(FVector(target->GetActorLocation() - GetActorLocation()).Rotation());
|
||||
}
|
||||
else MoveTo(target->GetActorLocation());
|
||||
for (int i = 0; i < abilities.Num(); i++)
|
||||
{
|
||||
if ((abilities[i])&&FVector::DistSquared(GetActorLocation(),target->GetActorLocation())<abilities[i]->AICastRange*abilities[i]->AICastRange)
|
||||
{
|
||||
CastAbility(abilities[i]);
|
||||
}
|
||||
}
|
||||
}
|
||||
ANetworkCharacter* AIllusionCharacter::GetClosestTarget()
|
||||
{
|
||||
ANetworkCharacter* returnTarget = nullptr;
|
||||
float distance = 1e34;
|
||||
for (ANetworkCharacter* character : targetList)
|
||||
{
|
||||
if(!IsValid(character)||character==this || character->GetTeam()==GetTeam())
|
||||
continue;
|
||||
float tempdistance = FVector::DistSquared(character->GetActorLocation(), GetActorLocation());
|
||||
if (distance > tempdistance)
|
||||
{
|
||||
distance = tempdistance;
|
||||
returnTarget = character;
|
||||
}
|
||||
}
|
||||
return returnTarget;
|
||||
}
|
||||
void AIllusionCharacter::MoveTo(FVector Location)
|
||||
{
|
||||
UNavigationSystem* NavSys = UNavigationSystem::GetCurrent(GetWorld());
|
||||
if (IsPendingKill() || IsPendingKillPending())
|
||||
return;
|
||||
if (!IsStunned())
|
||||
{
|
||||
NavSys->SimpleMoveToLocation(this->Controller, Location);
|
||||
}
|
||||
else
|
||||
{
|
||||
NavSys->SimpleMoveToLocation(this->Controller, GetActorLocation());
|
||||
}
|
||||
}
|
||||
bool AIllusionCharacter::IsAttacking(float changePercentage)
|
||||
{
|
||||
int num = rand() % 100;
|
||||
if (num > (int)changePercentage)
|
||||
{
|
||||
return false;
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
void AIllusionCharacter::Kill()
|
||||
{
|
||||
if (IsPendingKill() || IsPendingKillPending())
|
||||
{
|
||||
RERROR("the character is pending kill");
|
||||
return;
|
||||
|
||||
}
|
||||
ADOTModifier* deathMod = GetWorld()->SpawnActor<ADOTModifier>();
|
||||
deathMod->damagePerTick = 500;
|
||||
deathMod->lifeTime = 0;
|
||||
deathMod->target = this;
|
||||
deathMod->character = this;
|
||||
deathMod->abilityInfo = abilityInfo;
|
||||
m_modifierManager->AddModifier(deathMod);
|
||||
m_isdying = true;
|
||||
//m_health = 0;
|
||||
// m_shouldBeDestroyed = true;
|
||||
}
|
||||
46
Source/UnrealProject/Creatures/IllusionCharacter.h
Normal file
46
Source/UnrealProject/Creatures/IllusionCharacter.h
Normal file
@@ -0,0 +1,46 @@
|
||||
// Project Lab - NHTV Igad
|
||||
|
||||
#pragma once
|
||||
|
||||
#include "Creatures/NetworkCharacter.h"
|
||||
#include "IllusionCharacter.generated.h"
|
||||
|
||||
/**
|
||||
*
|
||||
*/
|
||||
UCLASS()
|
||||
class UNREALPROJECT_API AIllusionCharacter : public ANetworkCharacter
|
||||
{
|
||||
GENERATED_BODY()
|
||||
public:
|
||||
AIllusionCharacter();
|
||||
virtual void BeginPlay() override;
|
||||
virtual void Tick(float deltaTime)override;
|
||||
virtual void EndPlay(const EEndPlayReason::Type EndPlayReason) override;
|
||||
void MoveTo(FVector Location);
|
||||
void Attack();
|
||||
UFUNCTION(BluePrintCallable, Category = "Illusion Properties")
|
||||
void Kill();
|
||||
class ANetworkCharacter* target;
|
||||
class ANetworkCharacter* GetClosestTarget();
|
||||
// float percentage is a range between 0 and 100 where 100 is always attacking and 0 never attacking
|
||||
bool IsAttacking(float changePercentage);
|
||||
UPROPERTY(EditAnywhere, BlueprintReadWrite,Category = "Illusion Properties", meta = (ExposeOnSpawn))
|
||||
class ANetworkPlayer* illOwner;
|
||||
UPROPERTY(editAnywhere, Category = "Illusion Properties")
|
||||
float aggroRange;
|
||||
|
||||
UPROPERTY(editAnywhere, BlueprintReadWrite,Category = "Illusion Properties", meta = (ExposeOnSpawn))
|
||||
float lifeTime;
|
||||
UPROPERTY(editAnywhere, BlueprintReadWrite, Category = "Illusion Properties", meta = (ExposeOnSpawn))
|
||||
class UAbilityInfo* abilityInfo;
|
||||
UPROPERTY(editAnywhere, Category = "Illusion Properties")
|
||||
float acceptenceDistance;
|
||||
UPROPERTY(editAnywhere, BlueprintReadWrite, Category = "Illusion Properties", meta = (ExposeOnSpawn))
|
||||
TArray<class ANetworkCharacter*> targetList;
|
||||
bool attacking;
|
||||
|
||||
private:
|
||||
bool m_isdying;
|
||||
|
||||
};
|
||||
69
Source/UnrealProject/Creatures/MainCharacterAnimation.cpp
Normal file
69
Source/UnrealProject/Creatures/MainCharacterAnimation.cpp
Normal file
@@ -0,0 +1,69 @@
|
||||
// Project Lab - NHTV Igad
|
||||
|
||||
#include "UnrealProject.h"
|
||||
#include "MainCharacterAnimation.h"
|
||||
#include "NetworkCharacter.h"
|
||||
#include "Animation/AnimNodeBase.h"
|
||||
#include "AnimationProxy.h"
|
||||
|
||||
UMainCharacterAnimation::UMainCharacterAnimation(const FObjectInitializer& init)
|
||||
: Super(init)
|
||||
{
|
||||
m_mainAnimProxy = nullptr;
|
||||
character = nullptr;
|
||||
attacking = false;
|
||||
charCustomization = FCharacterCustomization();
|
||||
m_swingAnimationSequence = 0;
|
||||
}
|
||||
void UMainCharacterAnimation::NativeInitializeAnimation()
|
||||
{
|
||||
attacking = false;
|
||||
Super::NativeInitializeAnimation();
|
||||
character = Cast<ANetworkCharacter>(this->GetOwningActor());
|
||||
if (character)
|
||||
{
|
||||
m_swingAnimationSequence = character->swingAnimationSequence;
|
||||
}
|
||||
}
|
||||
void UMainCharacterAnimation::NativeUpdateAnimation(float DeltaSeconds)
|
||||
{
|
||||
if(character && character->swingAnimationSequence > m_swingAnimationSequence)
|
||||
{
|
||||
attacking = true;
|
||||
m_swingAnimationSequence = character->swingAnimationSequence;
|
||||
}
|
||||
}
|
||||
void UMainCharacterAnimation::SetCharacterCustomization(const FCharacterCustomization& characterCustomization)
|
||||
{
|
||||
charCustomization = characterCustomization;
|
||||
if(m_mainAnimProxy)
|
||||
{
|
||||
m_mainAnimProxy->charCustomization = charCustomization;
|
||||
}
|
||||
}
|
||||
FAnimInstanceProxy* UMainCharacterAnimation::CreateAnimInstanceProxy()
|
||||
{
|
||||
check(!m_mainAnimProxy);
|
||||
m_mainAnimProxy = new FMainAnimProxy();
|
||||
|
||||
m_mainAnimProxy->boneNames[0] = "Slave_Head_Ctrl_Jnt";
|
||||
m_mainAnimProxy->boneNames[1] = "Slave_L_Hip_Ctrl_Jnt";
|
||||
m_mainAnimProxy->boneNames[2] = "Slave_R_Hip_Ctrl_Jnt";
|
||||
m_mainAnimProxy->boneNames[3] = "Slave_L_Result_Shoulder_Ctrl_Jnt";
|
||||
m_mainAnimProxy->boneNames[4] = "Slave_R_Result_Shoulder_Ctrl_Jnt";
|
||||
m_mainAnimProxy->boneNames[5] = "Slave_Result_Spine06";
|
||||
m_mainAnimProxy->boneNames[6] = "skeleton";
|
||||
|
||||
|
||||
return m_mainAnimProxy;
|
||||
}
|
||||
void UMainCharacterAnimation::DestroyAnimInstanceProxy(FAnimInstanceProxy* InProxy)
|
||||
{
|
||||
check(InProxy == m_mainAnimProxy);
|
||||
delete InProxy;
|
||||
m_mainAnimProxy = nullptr;
|
||||
}
|
||||
void UMainCharacterAnimation::OnSwingAnimation_Implementation()
|
||||
{
|
||||
}
|
||||
|
||||
43
Source/UnrealProject/Creatures/MainCharacterAnimation.h
Normal file
43
Source/UnrealProject/Creatures/MainCharacterAnimation.h
Normal file
@@ -0,0 +1,43 @@
|
||||
// Project Lab - NHTV Igad
|
||||
|
||||
#pragma once
|
||||
|
||||
#include "Animation/AnimInstance.h"
|
||||
#include "PlayerSetupState.h"
|
||||
#include "MainCharacterAnimation.generated.h"
|
||||
|
||||
/**
|
||||
*
|
||||
*/
|
||||
UCLASS()
|
||||
class UNREALPROJECT_API UMainCharacterAnimation : public UAnimInstance
|
||||
{
|
||||
GENERATED_BODY()
|
||||
|
||||
public:
|
||||
UMainCharacterAnimation(const FObjectInitializer& init);
|
||||
|
||||
virtual void NativeInitializeAnimation() override;
|
||||
virtual void NativeUpdateAnimation(float DeltaSeconds) override;
|
||||
|
||||
virtual FAnimInstanceProxy* CreateAnimInstanceProxy();
|
||||
virtual void DestroyAnimInstanceProxy(FAnimInstanceProxy* InProxy);
|
||||
|
||||
UFUNCTION(BlueprintNativeEvent, Category = "Animation")
|
||||
void OnSwingAnimation();
|
||||
|
||||
UFUNCTION(BlueprintCallable, Category = "Animation")
|
||||
void SetCharacterCustomization(const FCharacterCustomization& characterCustomization);
|
||||
|
||||
UPROPERTY(BlueprintReadOnly, Category = "Animation")
|
||||
class ANetworkCharacter* character;
|
||||
UPROPERTY(BlueprintReadWrite, Category = "Animation")
|
||||
bool attacking;
|
||||
FCharacterCustomization charCustomization;
|
||||
|
||||
private:
|
||||
// Keeps track of how many times an animation was triggered
|
||||
int32 m_swingAnimationSequence;
|
||||
struct FMainAnimProxy* m_mainAnimProxy;
|
||||
|
||||
};
|
||||
113
Source/UnrealProject/Creatures/MiniBossCreature.cpp
Normal file
113
Source/UnrealProject/Creatures/MiniBossCreature.cpp
Normal file
@@ -0,0 +1,113 @@
|
||||
// Project Lab - NHTV Igad
|
||||
|
||||
#include "UnrealProject.h"
|
||||
#include "MiniBossCreature.h"
|
||||
#include "NetworkPlayer.h"
|
||||
#include "CreatureSpawn.h"
|
||||
#include "AbilityInfo.h"
|
||||
#include "Effect.h"
|
||||
|
||||
|
||||
AMiniBossCreature::AMiniBossCreature()
|
||||
{
|
||||
PrimaryActorTick.bCanEverTick = true;
|
||||
m_usesMana = false;
|
||||
|
||||
target = nullptr;
|
||||
m_spawn = nullptr;
|
||||
|
||||
m_castTimer = 0;
|
||||
|
||||
keyDropped = PlayerKeyType::None;
|
||||
}
|
||||
|
||||
void AMiniBossCreature::BeginPlay()
|
||||
{
|
||||
Super::BeginPlay();
|
||||
|
||||
if (Role != ROLE_Authority)
|
||||
return;
|
||||
|
||||
// Select two random abilities to cast
|
||||
if (IsValid(inheritAbilities) && IsValid(inheritAbilities.GetDefaultObject()))
|
||||
{
|
||||
TArray<class UAbilityInfo*>& bossAbilities = inheritAbilities.GetDefaultObject()->abilities;
|
||||
if (bossAbilities.Num() > 1)
|
||||
{
|
||||
TArray<int32> abilityIndices;
|
||||
for (int32 i = 0; i < bossAbilities.Num(); i++)
|
||||
abilityIndices.Add(i);
|
||||
|
||||
while (m_bossAbilityPattern.Num() < 2)
|
||||
{
|
||||
const int32 index = FMath::Rand() % abilityIndices.Num();
|
||||
const int32 ability = abilityIndices[index];
|
||||
abilityIndices.RemoveAt(index);
|
||||
m_bossAbilityPattern.Add(ability);
|
||||
}
|
||||
}
|
||||
else if (bossAbilities.Num() == 1)
|
||||
m_bossAbilityPattern.Add(0);
|
||||
}
|
||||
}
|
||||
void AMiniBossCreature::EndPlay(const EEndPlayReason::Type EndPlayReason)
|
||||
{
|
||||
Super::EndPlay(EndPlayReason);
|
||||
|
||||
if (Role != ROLE_Authority)
|
||||
return;
|
||||
|
||||
// Drop a key
|
||||
if (m_health <= 0)
|
||||
{
|
||||
ANetworkPlayer* const killedBy = Cast<ANetworkPlayer>(GetLastPlayerDamage());
|
||||
if (IsValid(killedBy) && keyDropped != PlayerKeyType::None && IsValid(m_spawn) && !m_spawn->hasDroppedKey)
|
||||
{
|
||||
killedBy->AssignKey(keyDropped, m_spawn);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void AMiniBossCreature::Tick(float deltaTime)
|
||||
{
|
||||
Super::Tick(deltaTime);
|
||||
|
||||
if (Role != ROLE_Authority)
|
||||
return;
|
||||
|
||||
// If the distance of the target to the spawn is greater than the aggro range of the spawner,
|
||||
// then lose the target.
|
||||
if (IsValid(target))
|
||||
{
|
||||
float targetDistSqr = FVector::DistSquared(target->GetActorLocation(), m_spawn->SpawnResetPosition());
|
||||
float aggroSqr = m_spawn->deaggroRadius * m_spawn->deaggroRadius;
|
||||
if (aggroSqr < targetDistSqr)
|
||||
{
|
||||
// Reset
|
||||
target = nullptr;
|
||||
m_castTimer = 0;
|
||||
}
|
||||
}
|
||||
|
||||
if (!IsValid(target))
|
||||
return;
|
||||
|
||||
// Boss casting logic
|
||||
MoveToPoint(target->GetActorLocation());
|
||||
m_castTimer += deltaTime;
|
||||
|
||||
if (m_castTimer > 2.5f && IsValid(inheritAbilities) && IsValid(inheritAbilities.GetDefaultObject()))
|
||||
{
|
||||
TArray<class UAbilityInfo*>& bossAbilities = inheritAbilities.GetDefaultObject()->abilities;
|
||||
|
||||
if (bossAbilities.Num() > 0 && m_bossAbilityPattern.Num() > 0)
|
||||
{
|
||||
// Cast a random ability
|
||||
UAbilityInfo* ability = bossAbilities[m_bossAbilityPattern[FMath::Rand() % m_bossAbilityPattern.Num()]];
|
||||
if (IsValid(ability)&& ability->AICastRange*ability->AICastRange>FVector::DistSquared(GetActorLocation(),target->GetActorLocation()))
|
||||
CastAbility(ability);
|
||||
}
|
||||
|
||||
m_castTimer = 0;
|
||||
}
|
||||
}
|
||||
34
Source/UnrealProject/Creatures/MiniBossCreature.h
Normal file
34
Source/UnrealProject/Creatures/MiniBossCreature.h
Normal file
@@ -0,0 +1,34 @@
|
||||
// Project Lab - NHTV Igad
|
||||
|
||||
#pragma once
|
||||
|
||||
#include "Creatures/BossBase.h"
|
||||
#include "PlayerKeyType.h"
|
||||
#include "MiniBossCreature.generated.h"
|
||||
|
||||
/**
|
||||
*
|
||||
*/
|
||||
UCLASS()
|
||||
class UNREALPROJECT_API AMiniBossCreature : public ABossBase
|
||||
{
|
||||
GENERATED_BODY()
|
||||
|
||||
public:
|
||||
AMiniBossCreature();
|
||||
virtual void BeginPlay() override;
|
||||
virtual void EndPlay(const EEndPlayReason::Type EndPlayReason) override;
|
||||
virtual void Tick(float deltaTime) override;
|
||||
|
||||
UPROPERTY(EditAnywhere, BlueprintReadWrite, Category = "Miniboss")
|
||||
TSubclassOf<class ABossBase> inheritAbilities;
|
||||
|
||||
PlayerKeyType keyDropped;
|
||||
|
||||
UPROPERTY(EditAnywhere, BlueprintReadWrite, Category = "Miniboss")
|
||||
TSubclassOf<class AEffect> keyEffect;
|
||||
private:
|
||||
TArray<int32> m_bossAbilityPattern;
|
||||
float m_castTimer;
|
||||
|
||||
};
|
||||
61
Source/UnrealProject/Creatures/MinionAnimInstance.cpp
Normal file
61
Source/UnrealProject/Creatures/MinionAnimInstance.cpp
Normal file
@@ -0,0 +1,61 @@
|
||||
// Project Lab - NHTV Igad
|
||||
|
||||
#include "UnrealProject.h"
|
||||
#include "MinionAnimInstance.h"
|
||||
|
||||
UMinionAnimInstance::UMinionAnimInstance(const FObjectInitializer& init)
|
||||
: Super(init)
|
||||
{
|
||||
m_animationState = EMinionAnimState::MAS_Idle;
|
||||
}
|
||||
|
||||
void UMinionAnimInstance::NativeInitializeAnimation()
|
||||
{
|
||||
Super::NativeInitializeAnimation();
|
||||
m_animationState = EMinionAnimState::MAS_Idle;
|
||||
}
|
||||
|
||||
void UMinionAnimInstance::NativeUpdateAnimation(float deltaSeconds)
|
||||
{
|
||||
Super::NativeUpdateAnimation(deltaSeconds);
|
||||
|
||||
APawn* ownerPawn = TryGetPawnOwner();
|
||||
|
||||
if (IsValid(ownerPawn))
|
||||
{
|
||||
const FVector velocity = ownerPawn->GetVelocity();
|
||||
const FVector forward = ownerPawn->GetActorForwardVector().GetSafeNormal2D();
|
||||
|
||||
m_movementSpeed = velocity.Size();
|
||||
|
||||
float angleBetween = FMath::FindDeltaAngle(forward.HeadingAngle(), velocity.GetSafeNormal2D().HeadingAngle());
|
||||
m_movementDirectionRelative = FVector(cos(angleBetween), sin(angleBetween), 0);
|
||||
}
|
||||
}
|
||||
|
||||
EMinionAnimState UMinionAnimInstance::GetAnimationState()
|
||||
{
|
||||
return m_animationState;
|
||||
}
|
||||
|
||||
bool UMinionAnimInstance::IsInAnimationState(const EMinionAnimState minionStateCheck)
|
||||
{
|
||||
return (m_animationState == minionStateCheck);
|
||||
}
|
||||
|
||||
void UMinionAnimInstance::ChangeAnimationStateTo(EMinionAnimState newState)
|
||||
{
|
||||
m_animationState = newState;
|
||||
}
|
||||
|
||||
void UMinionAnimInstance::BPChangeAnimationStateTo(EMinionAnimState newState)
|
||||
{
|
||||
m_animationState = newState;
|
||||
OnAnimationStateChanged.Broadcast(newState);
|
||||
}
|
||||
|
||||
void UMinionAnimInstance::PlaySpecificAnimationByInt(int32 animationToPlay)
|
||||
{
|
||||
m_specificAnimationIndex = animationToPlay;
|
||||
ChangeAnimationStateTo(EMinionAnimState::MAS_Specific);
|
||||
}
|
||||
66
Source/UnrealProject/Creatures/MinionAnimInstance.h
Normal file
66
Source/UnrealProject/Creatures/MinionAnimInstance.h
Normal file
@@ -0,0 +1,66 @@
|
||||
// Project Lab - NHTV Igad
|
||||
|
||||
#pragma once
|
||||
|
||||
#include "Animation/AnimInstance.h"
|
||||
#include "MinionAnimInstance.generated.h"
|
||||
|
||||
// Enum that shows in what state the Animation Instance is in.
|
||||
UENUM(BlueprintType)
|
||||
enum class EMinionAnimState : uint8
|
||||
{
|
||||
MAS_Idle UMETA(DisplayName = "Idle"),
|
||||
MAS_Pointing UMETA(DisplayName = "Pointing"),
|
||||
MAS_Attacking UMETA(DisplayName = "Attacking"),
|
||||
MAS_Casting UMETA(DisplayName = "Casting"),
|
||||
MAS_Guarding UMETA(DisplayName = "Guarding"),
|
||||
MAS_Staggered UMETA(DisplayName = "Staggered"),
|
||||
MAS_Stunned UMETA(DisplayName = "Stunned"),
|
||||
MAS_Killed UMETA(DisplayName = "Killed"),
|
||||
MAS_Specific UMETA(DisplayName = "Specific")
|
||||
};
|
||||
|
||||
// Delegate declaration for when the Animation Instance changes its state from within.
|
||||
DECLARE_DYNAMIC_MULTICAST_DELEGATE_OneParam(FOnAnimationStateChanged, EMinionAnimState, newState);
|
||||
|
||||
/**
|
||||
* Animation instance that handles all state changing, variable gathering and notifying the AI class.
|
||||
*/
|
||||
UCLASS()
|
||||
class UNREALPROJECT_API UMinionAnimInstance : public UAnimInstance
|
||||
{
|
||||
GENERATED_BODY()
|
||||
public:
|
||||
UMinionAnimInstance(const FObjectInitializer& init);
|
||||
|
||||
virtual void NativeInitializeAnimation() override;
|
||||
virtual void NativeUpdateAnimation(float deltaSeconds) override;
|
||||
|
||||
// Function to get the animation state of the animation instance.
|
||||
EMinionAnimState GetAnimationState();
|
||||
// Helper function for checking if the minion is in a certain state.
|
||||
bool IsInAnimationState(const EMinionAnimState minionStateCheck);
|
||||
// Function that changes the state of the minion. Does not trigger OnAnimationStateChanged.
|
||||
UFUNCTION(BlueprintCallable, Category = "Animation State")
|
||||
void ChangeAnimationStateTo(EMinionAnimState newState);
|
||||
// Function that changes the state of the minion. Triggers OnAnimationStateChanged.
|
||||
// Made specifically for use in the Animation Blueprint.
|
||||
UFUNCTION(BlueprintCallable, Category = "Animation State")
|
||||
void BPChangeAnimationStateTo(EMinionAnimState newState);
|
||||
// Delegate for when the state of the minion changes.
|
||||
UPROPERTY(BlueprintAssignable, Category = "Animation State")
|
||||
FOnAnimationStateChanged OnAnimationStateChanged;
|
||||
|
||||
UFUNCTION()
|
||||
void PlaySpecificAnimationByInt(int32 animationToPlay);
|
||||
|
||||
protected: // Member variables are protected.
|
||||
UPROPERTY(EditAnywhere, BlueprintReadWrite, Category = "Animation State")
|
||||
EMinionAnimState m_animationState;
|
||||
UPROPERTY(EditAnywhere, BlueprintReadWrite, Category = "Animation State")
|
||||
float m_movementSpeed;
|
||||
UPROPERTY(EditAnywhere, BlueprintReadWrite, Category = "Animation State")
|
||||
FVector m_movementDirectionRelative;
|
||||
UPROPERTY(EditAnywhere, BlueprintReadWrite, Category = "Animation State")
|
||||
int32 m_specificAnimationIndex;
|
||||
};
|
||||
349
Source/UnrealProject/Creatures/NPCBase.cpp
Normal file
349
Source/UnrealProject/Creatures/NPCBase.cpp
Normal file
@@ -0,0 +1,349 @@
|
||||
// Project Lab - NHTV Igad
|
||||
|
||||
#include "UnrealProject.h"
|
||||
#include "NPCBase.h"
|
||||
#include "MinionAnimInstance.h"
|
||||
#include "SpawnerBase.h"
|
||||
#include "modifier.h"
|
||||
#include "NetworkPlayer.h"
|
||||
#include "TeamData.h"
|
||||
#include "BossBase.h"
|
||||
#include "DrawDebugHelpers.h"
|
||||
#include "AI/Navigation/NavigationPath.h"
|
||||
|
||||
static UTeamData* teamData;
|
||||
|
||||
ANPCBase::ANPCBase()
|
||||
{
|
||||
teamData = ConstructorHelpers::FObjectFinder<UTeamData>(L"/Game/Assets/TeamData").Object;
|
||||
|
||||
PrimaryActorTick.bCanEverTick = true;
|
||||
m_spawn = nullptr;
|
||||
m_dead = false;
|
||||
baseAttackSpeed = 2.0f;
|
||||
baseMaxHealth = 200.0f;
|
||||
baseArmor = 30.0f;
|
||||
baseMaxMana = 1000.0f;
|
||||
baseAttackSpeed = 1.0f;
|
||||
maxAvoidanceForce = 200.0f;
|
||||
m_reachedStart = false;
|
||||
//visionTrigger = CreateDefaultSubobject<UCubeComponent>(TEXT("Vision Trigger"));
|
||||
|
||||
}
|
||||
void ANPCBase::BeginPlay()
|
||||
{
|
||||
Super::BeginPlay();
|
||||
if (Role != ROLE_Authority)
|
||||
return;
|
||||
|
||||
m_health = m_maxHealth = baseMaxHealth;
|
||||
m_mana = m_maxMana = baseMaxMana;
|
||||
if (m_spawn)
|
||||
m_targetPoint = m_spawn->GetActorLocation();
|
||||
else m_targetPoint = GetActorLocation();
|
||||
collisionRadius = GetCapsuleComponent()->GetScaledCapsuleRadius();
|
||||
m_walkSpeed = GetCharacterMovement()->MaxWalkSpeed;
|
||||
m_OnRep_Team();
|
||||
GetCapsuleComponent()->BodyInstance.bLockXRotation = true;
|
||||
GetCapsuleComponent()->BodyInstance.bLockYRotation = true;
|
||||
animInstance = Cast<UMinionAnimInstance>(GetMesh()->GetAnimInstance());
|
||||
patrolTime = 5.0f;
|
||||
}
|
||||
void ANPCBase::EndPlay(const EEndPlayReason::Type EndPlayReason)
|
||||
{
|
||||
Super::EndPlay(EndPlayReason);
|
||||
if (Role != ROLE_Authority)
|
||||
return;
|
||||
if(m_spawn && EndPlayReason == EEndPlayReason::Destroyed)
|
||||
m_spawn->OnMobDie(this);
|
||||
|
||||
}
|
||||
void ANPCBase::Tick(float deltaTime)
|
||||
{
|
||||
Super::Tick(deltaTime);
|
||||
if (Role != ROLE_Authority)
|
||||
return;
|
||||
UNavigationSystem* navSys = UNavigationSystem::GetCurrent(GetWorld());
|
||||
if (!navSys || !GetController() || !m_spawn)
|
||||
{
|
||||
//RERROR("Boss base does not have a navigation system");
|
||||
return;
|
||||
}
|
||||
|
||||
if (!IsValid(target))
|
||||
target = nullptr;
|
||||
|
||||
if (m_dead)
|
||||
{
|
||||
return;
|
||||
}
|
||||
if (!IsValid(target))
|
||||
{
|
||||
m_spawn->GetNewTarget(this);
|
||||
}
|
||||
|
||||
if (!IsValid(target) && !m_hasGeneral)
|
||||
{
|
||||
MoveToPoint(m_targetPoint);
|
||||
}
|
||||
else
|
||||
{
|
||||
if (FVector::DistSquared(m_spawn->GetActorLocation(), GetActorLocation()) > m_spawn->deaggroRadius * m_spawn->deaggroRadius)
|
||||
{
|
||||
// Reset aggro radius
|
||||
target = nullptr;
|
||||
m_isPulled = false;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
|
||||
}
|
||||
int32 ANPCBase::NativeDealDamage(class ANetworkCharacter* dealer, int32 damage, float armorPercentageIgnore, class UAbilityInfo* ability)
|
||||
{
|
||||
target = dealer;
|
||||
m_OnRep_Team();
|
||||
return Super::NativeDealDamage(dealer, damage, armorPercentageIgnore, ability);
|
||||
}
|
||||
void ANPCBase::ResetModifiers()
|
||||
{
|
||||
Super::ResetModifiers();
|
||||
m_maxHealth = baseMaxHealth;
|
||||
m_maxMana = baseMaxMana;
|
||||
m_attackSpeed = baseAttackSpeed;
|
||||
m_armor = baseArmor;
|
||||
}
|
||||
|
||||
void ANPCBase::OnDeath(UAbilityInfo* ability)
|
||||
{
|
||||
//should be removed later, this is in here untill the animations are actually in the game
|
||||
Super::OnDeath(ability);
|
||||
return;
|
||||
target = nullptr;
|
||||
m_dead = true;
|
||||
m_modifierManager->ClearModifiers();
|
||||
}
|
||||
|
||||
void ANPCBase::CalcAvoidance(const FVector& target)
|
||||
{
|
||||
UNavigationSystem* NavSys = UNavigationSystem::GetCurrent(GetWorld());
|
||||
// NavSys->SimpleMoveToLocation(this->Controller, target);
|
||||
if (Cast<ABossBase>(this))
|
||||
{
|
||||
NavSys->SimpleMoveToLocation(this->Controller, target);
|
||||
return;
|
||||
}
|
||||
FVector newLoc = FVector::ZeroVector;
|
||||
newLoc = GetClosestPathLocation(target) - GetActorLocation();
|
||||
newLoc.Normalize();
|
||||
newLoc *= GetCharacterMovement()->GetMaxSpeed();
|
||||
newLoc = newLoc + CalcAvoidanceVector(); //+ calc avoicance vector;
|
||||
FVector vel = TruncateVector(GetCharacterMovement()->Velocity + newLoc, GetCharacterMovement()->GetMaxSpeed());
|
||||
vel += GetActorLocation();
|
||||
NavSys->SimpleMoveToLocation(this->Controller, vel);
|
||||
SetActorRotation(FVector(FVector(vel.X, vel.Y, GetActorLocation().Z) - GetActorLocation()).Rotation());
|
||||
|
||||
}
|
||||
|
||||
void ANPCBase::PatrolBetweenLocations(FVector startlocation, FVector endLocation, float deltaTime)
|
||||
{
|
||||
FVector actorlocation = GetActorLocation();
|
||||
if (m_reachedStart)
|
||||
{
|
||||
MoveToPoint(endLocation);
|
||||
enableVisonCone = false;
|
||||
}
|
||||
else
|
||||
{
|
||||
MoveToPoint(startlocation);
|
||||
enableVisonCone = false;
|
||||
}
|
||||
//wait a few second to walk back
|
||||
float dist = FVector::DistSquaredXY(startlocation, actorlocation);
|
||||
if (dist < 50.0f*50.0f)
|
||||
{
|
||||
m_patrolTimer -= deltaTime;
|
||||
enableVisonCone = true;
|
||||
if (m_patrolTimer <= 0)
|
||||
{
|
||||
m_reachedStart = true;
|
||||
m_patrolTimer = patrolTime;
|
||||
|
||||
}
|
||||
|
||||
|
||||
}
|
||||
dist = FVector::DistSquaredXY(endLocation, actorlocation);
|
||||
if (dist< 50.0f*50.0f)
|
||||
{
|
||||
m_patrolTimer -= deltaTime;
|
||||
enableVisonCone = true;
|
||||
if (m_patrolTimer <= 0)
|
||||
{
|
||||
m_reachedStart = false;
|
||||
m_patrolTimer = patrolTime;
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
void ANPCBase::MoveToPoint(const FVector& target)
|
||||
{
|
||||
UNavigationSystem* NavSys = UNavigationSystem::GetCurrent(GetWorld());
|
||||
GetCharacterMovement()->MaxWalkSpeed = m_walkSpeed;
|
||||
float distance = FVector::DistSquared(target, GetActorLocation());
|
||||
if (IsPendingKill() || IsPendingKillPending())
|
||||
return;
|
||||
|
||||
if (!IsStunned())
|
||||
{
|
||||
CalcAvoidance( target);
|
||||
SetActorRotation(FVector(FVector(target.X, target.Y, GetActorLocation().Z) - GetActorLocation()).Rotation());
|
||||
|
||||
}
|
||||
else
|
||||
{
|
||||
|
||||
NavSys->SimpleMoveToLocation(this->Controller, GetActorLocation());
|
||||
}
|
||||
|
||||
|
||||
|
||||
}
|
||||
void ANPCBase::MoveToPointSlow(const FVector& target, float dist)
|
||||
{
|
||||
UNavigationSystem* NavSys = UNavigationSystem::GetCurrent(GetWorld());
|
||||
if (IsPendingKill() || IsPendingKillPending())
|
||||
return;
|
||||
GetCharacterMovement()->MaxWalkSpeed = m_walkSpeed/2;
|
||||
if (m_lastLoc != target)
|
||||
{
|
||||
if (m_lastLoc == FVector::ZeroVector || FVector::DistSquaredXY(m_lastLoc, GetActorLocation()) < dist*dist)
|
||||
{
|
||||
//RPRINT("test");
|
||||
m_lastLoc =UNavigationSystem::ProjectPointToNavigation(GetWorld(), target);
|
||||
//m_lastLoc = target;
|
||||
}
|
||||
}
|
||||
MoveToPoint(m_lastLoc);
|
||||
|
||||
}
|
||||
void ANPCBase::OnOverlapBegin(class AActor* OtherActor, class UPrimitiveComponent* OtherComp, int32 OtherBodyIndex, bool bFromSweep, const FHitResult& SweepResult)
|
||||
{
|
||||
if (!IsValid(OtherActor))
|
||||
return;
|
||||
if (!OtherActor->IsA(ANPCBase::StaticClass()))
|
||||
return;
|
||||
collisionList.Add(Cast<ANPCBase>(OtherActor));
|
||||
|
||||
}
|
||||
void ANPCBase::OnOverlapEnd(class AActor* OtherActor, class UPrimitiveComponent* OtherComp, int32 OtherBodyIndex)
|
||||
{
|
||||
if (!IsValid(OtherActor))
|
||||
return;
|
||||
if (!OtherActor->IsA(ANPCBase::StaticClass()))
|
||||
return;
|
||||
collisionList.Remove(Cast<ANPCBase>(OtherActor));
|
||||
}
|
||||
void ANPCBase::SetSpawn(class ASpawnerBase* spawn)
|
||||
{
|
||||
check(Role == ROLE_Authority);
|
||||
check(!m_spawn);
|
||||
m_spawn = spawn;
|
||||
}
|
||||
void ANPCBase::SetControlPoint(FVector controlPoint)
|
||||
{
|
||||
check(Role == ROLE_Authority);
|
||||
m_targetPoint = controlPoint;
|
||||
}
|
||||
|
||||
void ANPCBase::m_OnRep_Team()
|
||||
{
|
||||
Super::m_OnRep_Team();
|
||||
if(!m_materialInstance)
|
||||
{
|
||||
m_materialInstance = GetMesh()->CreateDynamicMaterialInstance(0);
|
||||
}
|
||||
if(m_materialInstance)
|
||||
{
|
||||
float scalar = (float)m_health / (float)m_maxHealth;
|
||||
m_materialInstance->SetVectorParameterValue("TeamColor", teamData->GetTeamColor(GetTeam()));
|
||||
m_materialInstance->SetScalarParameterValue("FlameEdgeSoftness", 100.0f - (100.0f*scalar));
|
||||
m_materialInstance->SetScalarParameterValue("FlameEffectIntensity", (scalar));
|
||||
m_materialInstance->SetScalarParameterValue("RandomOffset", FMath::FRand());
|
||||
}
|
||||
}
|
||||
|
||||
void ANPCBase::UnsetSpawn()
|
||||
{
|
||||
check(Role == ROLE_Authority);
|
||||
m_spawn = nullptr;
|
||||
}
|
||||
void ANPCBase::ChangeNPCAnimation_Implementation(uint8 state )
|
||||
{
|
||||
if (IsValid(animInstance))
|
||||
animInstance->ChangeAnimationStateTo((EMinionAnimState)(state));
|
||||
}
|
||||
FVector ANPCBase::CalcAvoidanceVector()
|
||||
{
|
||||
FVector retVector = FVector::ZeroVector;
|
||||
float dynLenght = GetCharacterMovement()->Velocity.Size() / GetCharacterMovement()->GetMaxSpeed();
|
||||
//change getactorforward by velocity
|
||||
FVector vel = GetCharacterMovement()->Velocity;
|
||||
vel.Normalize();
|
||||
aheadvec1 =GetActorLocation()+ vel*dynLenght;
|
||||
aheadvec2 = GetActorLocation() + vel*dynLenght*0.5;
|
||||
// DrawDebugSphere(GetWorld(), aheadvec1, 24, 32, FColor(255, 255, 255));
|
||||
// DrawDebugSphere(GetWorld(), aheadvec2, 24, 32, FColor(255, 0, 0));
|
||||
ANPCBase* obstacle = GetClosestObstacle();
|
||||
if (IsValid(obstacle))
|
||||
{
|
||||
retVector = aheadvec1 - obstacle->GetActorLocation();
|
||||
retVector.Z = 0.0f;
|
||||
retVector.Normalize();
|
||||
retVector *= maxAvoidanceForce;
|
||||
|
||||
}
|
||||
// DrawDebugLine(GetWorld(), GetActorLocation(), GetActorLocation() + retVector, FColor(255, 255, 0));
|
||||
return retVector;
|
||||
}///
|
||||
ANPCBase* ANPCBase::GetClosestObstacle()
|
||||
{
|
||||
ANPCBase* obstacle = nullptr;
|
||||
float distance = 1e34;
|
||||
for (int i = 0; i < m_spawn->m_mobs.Num(); i++)
|
||||
{
|
||||
ANPCBase* current = m_spawn->m_mobs[i];
|
||||
if(!IsValid(current)||current==this)
|
||||
continue;
|
||||
float tempdistance = FVector::DistSquared(current->GetActorLocation(), this->GetActorLocation());
|
||||
if (tempdistance < distance)
|
||||
{
|
||||
distance = tempdistance;
|
||||
obstacle = current;
|
||||
}
|
||||
}
|
||||
if (!IsValid(obstacle))
|
||||
return nullptr;
|
||||
if (FVector::DistSquared(aheadvec1, obstacle->GetActorLocation())>100 * 100 && FVector::DistSquared(aheadvec2, obstacle->GetActorLocation())>100 * 100)
|
||||
obstacle = nullptr;
|
||||
return obstacle;
|
||||
}
|
||||
FVector ANPCBase::TruncateVector(FVector inVector, float maxForce)
|
||||
{
|
||||
if (inVector.Size() > maxForce)
|
||||
{
|
||||
inVector.Normalize();
|
||||
inVector *= maxForce;
|
||||
}
|
||||
return inVector;
|
||||
}
|
||||
FVector ANPCBase::GetClosestPathLocation(FVector endlocation)
|
||||
{
|
||||
FVector returnVec = FVector();
|
||||
UNavigationPath *tpath;
|
||||
UNavigationSystem* navsys = UNavigationSystem::GetCurrent(GetWorld());
|
||||
tpath = navsys->FindPathToLocationSynchronously(GetWorld(), GetActorLocation(), endlocation);
|
||||
if (tpath == nullptr||tpath->PathPoints.Num()<=1)
|
||||
return endlocation;
|
||||
return tpath->PathPoints[tpath->PathPoints.Num()-1];
|
||||
}
|
||||
109
Source/UnrealProject/Creatures/NPCBase.h
Normal file
109
Source/UnrealProject/Creatures/NPCBase.h
Normal file
@@ -0,0 +1,109 @@
|
||||
// Project Lab - NHTV Igad
|
||||
|
||||
#pragma once
|
||||
|
||||
#include "Creatures/NetworkCharacter.h"
|
||||
#include "NPCBase.generated.h"
|
||||
|
||||
/**
|
||||
*
|
||||
*/
|
||||
|
||||
UCLASS()
|
||||
class UNREALPROJECT_API ANPCBase : public ANetworkCharacter
|
||||
{
|
||||
GENERATED_BODY()
|
||||
public:
|
||||
|
||||
ANPCBase();
|
||||
virtual void BeginPlay() override;
|
||||
virtual void EndPlay(const EEndPlayReason::Type EndPlayReason) override;
|
||||
virtual void Tick(float deltaTime) override;
|
||||
virtual int32 NativeDealDamage(class ANetworkCharacter* dealer, int32 damage, float armorPercentageIgnore, class UAbilityInfo* ability) override;
|
||||
// Clears all the modifier stat modifications from this character
|
||||
virtual void ResetModifiers();
|
||||
|
||||
void SetSpawn(class ASpawnerBase* spawn);
|
||||
void UnsetSpawn();
|
||||
void SetControlPoint(FVector controlPoint);
|
||||
|
||||
UPROPERTY(BlueprintAssignable, Category = "Collision")
|
||||
FComponentBeginOverlapSignature OnComponentBeginOverlap;
|
||||
UPROPERTY(BlueprintAssignable, Category = "Collision")
|
||||
FComponentBeginOverlapSignature OnComponentExitOverlap;
|
||||
|
||||
|
||||
UPROPERTY(VisibleAnywhere, Category = "Custom Collision")
|
||||
float maxAvoidanceForce;
|
||||
UPROPERTY(BlueprintReadWrite)
|
||||
TArray< class ANPCBase*>collisionList;
|
||||
UPROPERTY(BlueprintReadWrite)
|
||||
class ANetworkCharacter* target;
|
||||
UFUNCTION(BlueprintImplementableEvent, Category = "Animation")
|
||||
void PlayFencAnimation();
|
||||
UFUNCTION(BlueprintImplementableEvent, Category = "Animation")
|
||||
void PlayNormalAnimation();
|
||||
UFUNCTION(BlueprintImplementableEvent, Category = "Animation")
|
||||
void PlayClimbingAnimation();
|
||||
UFUNCTION(BlueprintImplementableEvent, Category = "Animation")
|
||||
void PlayPointAnimation();
|
||||
UFUNCTION(NetMulticast, Reliable, Category = "Animation")
|
||||
void ChangeNPCAnimation(uint8 state);
|
||||
//Trigger components for the vision of the npcs
|
||||
float patrolTime;
|
||||
float collisionRadius;
|
||||
void CalcAvoidance(const FVector& target);
|
||||
virtual void MoveToPoint(const FVector& target);
|
||||
virtual void MoveToPointSlow(const FVector& target, float dist);
|
||||
void PatrolBetweenLocations(FVector startlocation, FVector endLocation,float deltaTime);
|
||||
ANPCBase* GetClosestObstacle();
|
||||
FVector CalcAvoidanceVector();
|
||||
FVector TruncateVector(FVector inVector, float maxForce);
|
||||
FVector GetClosestPathLocation(FVector endlocation);
|
||||
FVector aheadvec1;
|
||||
FVector aheadvec2;
|
||||
protected:
|
||||
virtual void m_OnRep_Team() override;
|
||||
|
||||
UFUNCTION(BlueprintImplementableEvent, category = "death")
|
||||
void PlayDeathAnimation(UAbilityInfo* ability);
|
||||
|
||||
virtual void OnDeath(UAbilityInfo* ability) override;
|
||||
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);
|
||||
|
||||
UPROPERTY()
|
||||
UMaterialInstanceDynamic* m_materialInstance;
|
||||
|
||||
// Base Stats
|
||||
UPROPERTY(EditDefaultsOnly, Category = "Stats")
|
||||
int32 baseMaxHealth;
|
||||
UPROPERTY(EditDefaultsOnly, Category = "Stats")
|
||||
int32 baseMaxMana;
|
||||
// AKA Attacks per second
|
||||
UPROPERTY(EditDefaultsOnly, Category = "Stats")
|
||||
float baseAttackSpeed;
|
||||
UPROPERTY(BlueprintReadWrite)
|
||||
bool enableVisonCone;
|
||||
|
||||
bool m_dead;
|
||||
|
||||
UPROPERTY()
|
||||
class ASpawnerBase* m_spawn;
|
||||
|
||||
FVector m_targetPoint;
|
||||
bool m_isPulled;
|
||||
bool m_hasGeneral;
|
||||
class UMinionAnimInstance* animInstance;
|
||||
private:
|
||||
FVector m_lastLoc;
|
||||
float m_walkSpeed;
|
||||
bool lastcollision;
|
||||
bool m_reachedStart;
|
||||
FVector previousDirection;
|
||||
float m_patrolTimer;
|
||||
|
||||
|
||||
};
|
||||
1232
Source/UnrealProject/Creatures/NetworkCharacter.cpp
Normal file
1232
Source/UnrealProject/Creatures/NetworkCharacter.cpp
Normal file
File diff suppressed because it is too large
Load Diff
324
Source/UnrealProject/Creatures/NetworkCharacter.h
Normal file
324
Source/UnrealProject/Creatures/NetworkCharacter.h
Normal file
@@ -0,0 +1,324 @@
|
||||
// Project Lab - NHTV Igad
|
||||
|
||||
#pragma once
|
||||
#include <unordered_map>
|
||||
using std::unordered_map;
|
||||
#include "AbilityState.h"
|
||||
#include "ScalingGraph.h"
|
||||
#include "NetworkPossessable.h"
|
||||
#include "IngameSkillTree.h"
|
||||
#include "NetworkCharacter.generated.h"
|
||||
|
||||
class ACreatureSpawn;
|
||||
|
||||
DECLARE_DYNAMIC_MULTICAST_DELEGATE_TwoParams(FCharacterKilled, ANetworkCharacter*, killer, UAbilityInfo*, ability);
|
||||
DECLARE_DYNAMIC_MULTICAST_DELEGATE_ThreeParams(FOnDamageTaken, ANetworkCharacter*, dealer, int32, damage, UAbilityInfo*, ability);
|
||||
|
||||
UCLASS(config = Game)
|
||||
class ANetworkCharacter : public ANetworkPossessable
|
||||
{
|
||||
GENERATED_BODY()
|
||||
public:
|
||||
ANetworkCharacter();
|
||||
|
||||
virtual void BeginPlay() override;
|
||||
virtual void EndPlay(const EEndPlayReason::Type EndPlayReason) override;
|
||||
virtual void Destroyed() override;
|
||||
virtual void OnDeath(UAbilityInfo* ability);
|
||||
void RegisterHealthBar();
|
||||
void UnregisterHealthBar();
|
||||
|
||||
virtual void Tick(float DeltaSeconds) override;
|
||||
|
||||
// This processes the learned abilities on the server and calls LearnSkills on the owning client
|
||||
void LearnSkills_Server(const TArray<FIngameSkillTreeSkill>& skills);
|
||||
// This creates a list of all learned skills on the client's version of the character so that he gets an updated casting bar, etc.
|
||||
UFUNCTION(Client, Reliable)
|
||||
void LearnSkills(const TArray<FIngameSkillTreeSkill>& skills);
|
||||
|
||||
// The Buff/Modifier manager, for server side characters
|
||||
class ModifierManager* GetModifierManager();
|
||||
UFUNCTION(BlueprintCallable, Category = "modifiers")
|
||||
TArray<class AModifier*> GetModifiersOfClass(TSubclassOf<class AModifier> modifierClass);
|
||||
|
||||
virtual void ResetModifiers();
|
||||
void CheckStatsOverflow();
|
||||
|
||||
// Damage dealing
|
||||
UFUNCTION(BlueprintCallable, Category = "Damage", meta = (WorldContext="WorldContextObject"))
|
||||
int32 DealDamage(class UObject* WorldContextObject, int32 damage, float armorPercentageIgnore = 0.0f);
|
||||
virtual int32 NativeDealDamage(class ANetworkCharacter* dealer, int32 damage, float armorPercentageIgnore = 0.0f, UAbilityInfo* ability = nullptr);
|
||||
|
||||
virtual void NativeOnKilled(class ANetworkCharacter* killer, class UAbilityInfo* ability);
|
||||
|
||||
UFUNCTION(BlueprintCallable, Category = "Damage")
|
||||
void OnStandardAttack(ANetworkCharacter* targetCharacter);
|
||||
|
||||
|
||||
|
||||
// Healing
|
||||
UFUNCTION(BlueprintCallable, Category = "Healing")
|
||||
void AddHealth(int32 health);
|
||||
virtual void NativeAddHealth(int32 health);
|
||||
|
||||
void RemoveHealth(int32 health);
|
||||
void AddMana(float mana);
|
||||
void RemoveMana(float mana);
|
||||
UFUNCTION(BlueprintCallable, Category = "Damage")
|
||||
float GetTimeSinceDamage() const;
|
||||
|
||||
UFUNCTION(BlueprintCallable, Category = "Animation")
|
||||
void TriggerSwingAnimation_Server();
|
||||
|
||||
UFUNCTION(BlueprintCallable, Category = "State")
|
||||
int32 GetHealth() const;
|
||||
UFUNCTION(BlueprintCallable, Category = "State")
|
||||
int32 GetMana() const;
|
||||
UFUNCTION(BlueprintCallable, Category = "State")
|
||||
int32 GetBlockedMana() const;
|
||||
UFUNCTION(BlueprintCallable, Category = "State")
|
||||
int32 GetMaxHealth() const;
|
||||
UFUNCTION(BlueprintCallable, Category = "State")
|
||||
int32 GetMaxMana() const;
|
||||
UFUNCTION(BlueprintCallable, Category = "State")
|
||||
int32 GetArmor() const;
|
||||
UFUNCTION(BlueprintCallable, Category = "State")
|
||||
bool GetHitable() const;
|
||||
UFUNCTION(BlueprintCallable, Category = "State")
|
||||
void SetHitable(bool hitable);
|
||||
UFUNCTION(BlueprintCallable, Category = "State")
|
||||
float GetDamageMultiplier() const;
|
||||
UFUNCTION(BlueprintCallable, Category = "State")
|
||||
float GetCooldownReduction() const;
|
||||
UFUNCTION(BlueprintCallable, Category = "State")
|
||||
float GetAttackSpeed() const;
|
||||
UFUNCTION(BlueprintCallable, Category = "State")
|
||||
float GetAttackDamage() const;
|
||||
UFUNCTION(BlueprintCallable, Category = "State")
|
||||
float GetMagicDamage() const;
|
||||
UFUNCTION(BlueprintCallable, Category = "State")
|
||||
float GetTotalDamage() const;
|
||||
UFUNCTION(BlueprintCallable, Category = "State")
|
||||
bool IsStunned() const;
|
||||
UFUNCTION(BlueprintCallable, Category = "State")
|
||||
bool CanRotate() const;
|
||||
UFUNCTION(BlueprintCallable, Category = "State")
|
||||
bool IsSilenced() const;
|
||||
UFUNCTION(BlueprintCallable, Category = "State")
|
||||
bool IsChanneling() const;
|
||||
UFUNCTION(BlueprintCallable, Category = "State")
|
||||
bool IsRanged() const;
|
||||
bool CanBeStunned() const;
|
||||
|
||||
ANetworkCharacter* GetLastPlayerDamage() const;
|
||||
|
||||
void CastAbility(int32 abilityIndex);
|
||||
void CastAbility(class UAbilityInfo* abilityInfo);
|
||||
|
||||
// Returns the ability that is currently being casted(channeling)
|
||||
UFUNCTION(BlueprintCallable, Category = "Ability")
|
||||
class UAbilityInfo* GetCastingAbility();
|
||||
|
||||
// Gets the persistent state of an ability between casts
|
||||
class AAbilityState* GetAbilityState(class UAbilityInfo* abilityInfo);
|
||||
|
||||
// Gets the toggle state for abilities that have isToggleAbility set
|
||||
bool GetAbilityToggleState(class UAbilityInfo* abilityInfo);
|
||||
bool m_initialised;
|
||||
|
||||
// Minimap/FoW visibility
|
||||
bool IsVisible() const;
|
||||
|
||||
// The collection of abilities this character has, for players.
|
||||
// The first ability in this array is always treated the character's basic attack,
|
||||
// and thus is affected by the attack speed variable of the character
|
||||
UPROPERTY(EditDefaultsOnly, Category = "Ability")
|
||||
TArray<class UAbilityInfo*> abilities;
|
||||
|
||||
UPROPERTY(BlueprintReadOnly, Replicated, Category = "Ability")
|
||||
class UAbilityInfo* basicAttack;
|
||||
|
||||
UPROPERTY(EditDefaultsOnly, Category = "Ability")
|
||||
TArray<class UAbilityInfo*> passives;
|
||||
|
||||
UPROPERTY(Replicated, BlueprintReadOnly, Category = "Animation")
|
||||
int32 swingAnimationSequence;
|
||||
|
||||
UPROPERTY(EditAnywhere, Category = "Experience Settings")
|
||||
int32 experienceGainOnKill;
|
||||
|
||||
// Intervals at which damage and healing combat text are spawned
|
||||
UPROPERTY(EditAnywhere, Category = "Combat Text")
|
||||
float healthAccumTimer;
|
||||
UPROPERTY(EditAnywhere, Category = "Combat Text")
|
||||
float damageAccumTimer;
|
||||
|
||||
// Duration before a team's damage is no longer valid
|
||||
// Gets refreshed when a team deals damage to the creature
|
||||
UPROPERTY(EditAnywhere, Category = "Team Damage Table")
|
||||
float teamDamageTimer;
|
||||
|
||||
UPROPERTY(BlueprintAssignable, Category = "Character")
|
||||
FCharacterKilled onCharacterKilled;
|
||||
// SERVER ONLY
|
||||
UPROPERTY(BlueprintAssignable, Category = "Character")
|
||||
FOnDamageTaken onDamageTaken;
|
||||
|
||||
METRICS_EXPR(Metrics::PlayerHandle* metricsHandle);
|
||||
|
||||
protected:
|
||||
// Called when the character is spawned or reset to set initial modifiers like fixed health/mana-regen
|
||||
// overridable
|
||||
virtual void m_SpawnModifiers();
|
||||
|
||||
// This checks stunn state, etc. to allow NetworkPossessable to move
|
||||
virtual bool m_AllowedToMove() override;
|
||||
virtual bool m_AllowedToRotate() override;
|
||||
virtual void m_SetLevelStats();
|
||||
|
||||
UPROPERTY(EditDefaultsOnly, Category = "Stats")
|
||||
int32 baseMovementSpeed;
|
||||
UPROPERTY(EditDefaultsOnly, Category = "Stats")
|
||||
int32 baseArmor;
|
||||
UPROPERTY(EditDefaultsOnly, Category = "Stats")
|
||||
int32 passiveManaRegen;
|
||||
UPROPERTY(EditDefaultsOnly, Category = "Stats")
|
||||
int32 passiveHealthRegen;
|
||||
|
||||
UPROPERTY(Replicated)
|
||||
int32 m_blockedMana;
|
||||
UPROPERTY(Replicated)
|
||||
float m_damageMultiplier;
|
||||
UPROPERTY(Replicated)
|
||||
float m_ignoreArmor;
|
||||
UPROPERTY(Replicated)
|
||||
float m_manaRegenMultiplier;
|
||||
UPROPERTY(Replicated)
|
||||
float m_manaUsageMultiplier;
|
||||
UPROPERTY(Replicated)
|
||||
float m_positiveEffectMultiplier;
|
||||
UPROPERTY(Replicated)
|
||||
float m_negativeEffectMultiplier;
|
||||
UPROPERTY(Replicated)
|
||||
float m_damageTakenMultiplier;
|
||||
UPROPERTY(Replicated)
|
||||
int32 m_armor;
|
||||
UPROPERTY(Replicated)
|
||||
bool m_stunned;
|
||||
UPROPERTY(Replicated)
|
||||
bool m_silenced;
|
||||
//sileneCount is the ammount of silences by modifiers, silence by casting is not taking into account.
|
||||
UPROPERTY(Replicated)
|
||||
uint32 m_silenceCount;
|
||||
UPROPERTY(Replicated)
|
||||
bool m_visible;
|
||||
UPROPERTY(Replicated)
|
||||
bool m_hitable;
|
||||
UPROPERTY(Replicated)
|
||||
bool m_isRanged;
|
||||
|
||||
UPROPERTY(EditDefaultsOnly, Category = "Stats")
|
||||
bool canBeStunned;
|
||||
UPROPERTY(EditDefaultsOnly, Category = "Stats")
|
||||
bool canBeSilenced;
|
||||
|
||||
UPROPERTY(Replicated)
|
||||
int32 m_health;
|
||||
UPROPERTY(Replicated)
|
||||
int32 m_maxHealth;
|
||||
UPROPERTY(Replicated)
|
||||
float m_mana;
|
||||
UPROPERTY(Replicated)
|
||||
int32 m_maxMana;
|
||||
UPROPERTY(Replicated)
|
||||
float m_cooldownReduction;
|
||||
UPROPERTY(Replicated)
|
||||
float m_attackSpeed;
|
||||
UPROPERTY(Replicated)
|
||||
float m_attackDamageMultiplier;
|
||||
UPROPERTY(Replicated)
|
||||
float m_magicDamageMultiplier;
|
||||
|
||||
UPROPERTY(Replicated)
|
||||
bool m_usesMana;
|
||||
|
||||
UPROPERTY(Replicated)
|
||||
float m_castingMovementspeedMultiplier;
|
||||
|
||||
friend class ModifierManager;
|
||||
friend class APreCastAbilityEventGroup;
|
||||
friend class AIllusionCharacter;
|
||||
friend class ADefaultPlayerController;
|
||||
class ModifierManager* m_modifierManager;
|
||||
|
||||
bool m_shouldBeDestroyed = false;
|
||||
private:
|
||||
// Request to the server to cast an ability
|
||||
UFUNCTION(Reliable, Server, WithValidation)
|
||||
void m_CastAbility_Server(class UAbilityInfo* ability);
|
||||
void m_InitAbilitySequence(class UAbilityInfo* ability, class AAbilityState* state);
|
||||
|
||||
// Updates all the cooldown timers and ticks modifiers
|
||||
void m_TickAbilities(float DeltaSeconds);
|
||||
|
||||
void m_SetAbilityCooldown(class UAbilityInfo* ability, class AAbilityState* state);
|
||||
|
||||
// Server only casting interupt
|
||||
void m_InteruptSpellcasting();
|
||||
|
||||
// Called if we know an ability is successfully casted or not
|
||||
UFUNCTION(Reliable, Client)
|
||||
void m_OnAbilityCastConfirm(UAbilityInfo* ability, bool success, bool toggleState, float cooldown, float cooldownTime);
|
||||
|
||||
void m_PreCast(UAbilityInfo* ability);
|
||||
// Called if we know an ability is successfully casted or not (server-side)
|
||||
virtual void m_OnAbilityCastConfirm_Server(UAbilityInfo* ability, bool success);
|
||||
|
||||
// Callback for when an ability progresses to it's next state
|
||||
UFUNCTION()
|
||||
void m_OnAbilityEventGroupEnded(class UAbilityInfo* ability, class AAbilityEventGroup* current, class AAbilityEventGroup* next);
|
||||
|
||||
friend class ADefaultPlayerState;
|
||||
|
||||
// Active Stats
|
||||
struct DamagePeriod
|
||||
{
|
||||
DamagePeriod(int32 damage, float timer) : damage(damage), timer(timer) {}
|
||||
int32 damage;
|
||||
float timer;
|
||||
};
|
||||
unordered_map<class ADefaultPlayerController*, DamagePeriod> m_damageAccum;
|
||||
int32 m_damageAccumValue;
|
||||
float m_damageAccumTimer;
|
||||
int32 m_healAccumValue;
|
||||
float m_healAccumTimer;
|
||||
float m_totalDamage;
|
||||
unordered_map<int32, DamagePeriod> m_teamDamageDealt;
|
||||
|
||||
UPROPERTY()
|
||||
ANetworkCharacter* m_lastPlayerDamage;
|
||||
|
||||
// Keeps ability persistent state between casts
|
||||
UPROPERTY()
|
||||
TMap<class UAbilityInfo*, class AAbilityState*> m_abilityStates;
|
||||
// Keeps ability execution states / active group (server-side)
|
||||
UPROPERTY()
|
||||
TMap<class UAbilityInfo*, class AAbilityEventGroup*> m_activeAbilities;
|
||||
|
||||
friend class AModifier;
|
||||
friend class AVisibilityModifier;
|
||||
float m_timeSinceDamage;
|
||||
UPROPERTY()
|
||||
class AAbilityEventGroup* m_channelGroup;
|
||||
UPROPERTY(Replicated)
|
||||
class UAbilityInfo* m_castingAbility;
|
||||
UPROPERTY(Replicated)
|
||||
bool m_channelStun;
|
||||
UPROPERTY(Replicated)
|
||||
bool m_allowChannelRotation;
|
||||
|
||||
|
||||
class APreCastAbilityEventGroup* m_currentPreCast;
|
||||
|
||||
};
|
||||
|
||||
91
Source/UnrealProject/Creatures/NetworkGhost.cpp
Normal file
91
Source/UnrealProject/Creatures/NetworkGhost.cpp
Normal file
@@ -0,0 +1,91 @@
|
||||
// Project Lab - NHTV Igad
|
||||
|
||||
#include "UnrealProject.h"
|
||||
#include "NetworkGhost.h"
|
||||
#include "NetworkPlayer.h"
|
||||
#include "DefaultPlayerState.h"
|
||||
|
||||
|
||||
// Sets default values
|
||||
ANetworkGhost::ANetworkGhost()
|
||||
{
|
||||
bReplicates = true;
|
||||
|
||||
postProcess = CreateDefaultSubobject<UPostProcessComponent>(TEXT("PostProcess"));
|
||||
postProcess->bEnabled = false;
|
||||
postProcess->AttachTo(RootComponent);
|
||||
|
||||
m_SetupCamera();
|
||||
|
||||
// Configure character movement
|
||||
GetCharacterMovement()->bOrientRotationToMovement = true; // Rotate character to moving direction
|
||||
GetCharacterMovement()->RotationRate = FRotator(0.f, 640.f, 0.f);
|
||||
GetCharacterMovement()->bConstrainToPlane = true;
|
||||
GetCharacterMovement()->bSnapToPlaneAtStart = true;
|
||||
|
||||
shrinesInRange = 0;
|
||||
respawnDuration = 15;
|
||||
allyRespawnRange = 300;
|
||||
}
|
||||
|
||||
void ANetworkGhost::BeginPlay()
|
||||
{
|
||||
Super::BeginPlay();
|
||||
|
||||
m_respawnTime = respawnDuration;
|
||||
}
|
||||
|
||||
void ANetworkGhost::Tick(float DeltaTime)
|
||||
{
|
||||
Super::Tick(DeltaTime);
|
||||
|
||||
// Update the respawn timer (server only)
|
||||
if (Role == ROLE_Authority)
|
||||
{
|
||||
if (m_respawnTime > 0)
|
||||
m_respawnTime -= DeltaTime;
|
||||
}
|
||||
|
||||
// Enable the post process on clients
|
||||
if (IsValid(GetController()))
|
||||
postProcess->bEnabled = GetController()->IsLocalController();
|
||||
}
|
||||
|
||||
|
||||
bool ANetworkGhost::CanRespawn() const
|
||||
{
|
||||
// Cannot respawn yet
|
||||
if (m_respawnTime > 0)
|
||||
return false;
|
||||
|
||||
// If we're falling, were unable to respawn
|
||||
if (!GetCharacterMovement()->IsMovingOnGround())
|
||||
return false;
|
||||
|
||||
// We're at a shrine
|
||||
if (shrinesInRange > 0)
|
||||
return true;
|
||||
|
||||
// Check if the ally is within the radius, so we can respawn next to him
|
||||
ADefaultPlayerState* state = Cast<ADefaultPlayerState>(PlayerState);
|
||||
bool inAllyRange = false;
|
||||
if (IsValid(state) && IsValid(state->teamMate) && IsValid(state->teamMate->character))
|
||||
{
|
||||
if (FVector::DistSquared(GetActorLocation(), state->teamMate->character->GetActorLocation()) <= (allyRespawnRange * allyRespawnRange))
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
float ANetworkGhost::RespawnTime() const
|
||||
{
|
||||
return m_respawnTime;
|
||||
}
|
||||
|
||||
|
||||
void ANetworkGhost::GetLifetimeReplicatedProps(TArray<FLifetimeProperty>& OutLifetimeProps) const
|
||||
{
|
||||
Super::GetLifetimeReplicatedProps(OutLifetimeProps);
|
||||
|
||||
DOREPLIFETIME(ANetworkGhost, m_respawnTime);
|
||||
}
|
||||
44
Source/UnrealProject/Creatures/NetworkGhost.h
Normal file
44
Source/UnrealProject/Creatures/NetworkGhost.h
Normal file
@@ -0,0 +1,44 @@
|
||||
// Project Lab - NHTV Igad
|
||||
|
||||
#pragma once
|
||||
|
||||
#include "Creatures/NetworkPossessable.h"
|
||||
#include "NetworkGhost.generated.h"
|
||||
|
||||
UCLASS()
|
||||
class UNREALPROJECT_API ANetworkGhost : public ANetworkPossessable
|
||||
{
|
||||
GENERATED_BODY()
|
||||
|
||||
public:
|
||||
ANetworkGhost();
|
||||
|
||||
virtual void BeginPlay() override;
|
||||
|
||||
virtual void Tick(float DeltaSeconds) override;
|
||||
|
||||
|
||||
bool CanRespawn() const;
|
||||
|
||||
UPROPERTY(BlueprintReadonly, Category = "Respawn")
|
||||
int32 shrinesInRange;
|
||||
|
||||
// Range to ally for respawn
|
||||
UPROPERTY(EditAnywhere, Category = "Respawn")
|
||||
float allyRespawnRange;
|
||||
|
||||
// Time it takes before the player is allowed to respawn
|
||||
UPROPERTY(EditAnywhere, Category = "Respawn")
|
||||
float respawnDuration;
|
||||
|
||||
// Get time it takes before respawning
|
||||
UFUNCTION(BlueprintCallable, Category = "Respawn")
|
||||
float RespawnTime() const;
|
||||
|
||||
UPROPERTY(EditAnywhere, Category = "PostProcess")
|
||||
UPostProcessComponent* postProcess;
|
||||
|
||||
private:
|
||||
UPROPERTY(Replicated)
|
||||
float m_respawnTime;
|
||||
};
|
||||
474
Source/UnrealProject/Creatures/NetworkPlayer.cpp
Normal file
474
Source/UnrealProject/Creatures/NetworkPlayer.cpp
Normal file
@@ -0,0 +1,474 @@
|
||||
// Project Lab - NHTV Igad
|
||||
#include "UnrealProject.h"
|
||||
#include "NetworkPlayer.h"
|
||||
#include "DefaultPlayerController.h"
|
||||
#include "ItemBase.h"
|
||||
#include "BaseSkillObject.h"
|
||||
#include "AbilityInfo.h"
|
||||
#include "NetworkSwitch.h"
|
||||
#include "DefaultGameMode.h"
|
||||
#include "DefaultPlayerState.h"
|
||||
#include "IngameSkillTree.h"
|
||||
|
||||
#include "Modifier.h"
|
||||
#include "SpawnerBase.h"
|
||||
#include "TeamData.h"
|
||||
|
||||
static UTeamData* teamData;
|
||||
|
||||
ANetworkPlayer::ANetworkPlayer()
|
||||
{
|
||||
bReplicates = true;
|
||||
|
||||
m_SetupCamera();
|
||||
|
||||
m_initialised = false;
|
||||
m_keyState = 0;
|
||||
|
||||
healthCurve = ConstructorHelpers::FObjectFinder<UCurveFloat>(TEXT("/Game/ScalingCurves/CF_PlayerHealth")).Object;
|
||||
healthRegenCurve = ConstructorHelpers::FObjectFinder<UCurveFloat>(TEXT("/Game/ScalingCurves/CF_PlayerHealthRegen")).Object;
|
||||
manaCurve = ConstructorHelpers::FObjectFinder<UCurveFloat>(TEXT("/Game/ScalingCurves/CF_PlayerMana")).Object;
|
||||
manaRegenCurve = ConstructorHelpers::FObjectFinder<UCurveFloat>(TEXT("/Game/ScalingCurves/CF_PlayerManaRegen")).Object;
|
||||
armorCurve = ConstructorHelpers::FObjectFinder<UCurveFloat>(TEXT("/Game/ScalingCurves/CF_PlayerArmor")).Object;
|
||||
attackSpeedCurve = ConstructorHelpers::FObjectFinder<UCurveFloat>(TEXT("/Game/ScalingCurves/CF_PlayerAttackSpeed")).Object;
|
||||
|
||||
creatureDamageTakenCurve = ConstructorHelpers::FObjectFinder<UCurveFloat>(TEXT("/Game/ScalingCurves/CF_CreatureDamageTaken")).Object;
|
||||
creatureDamageDealtCurve = ConstructorHelpers::FObjectFinder<UCurveFloat>(TEXT("/Game/ScalingCurves/CF_CreatureDamageDealt")).Object;
|
||||
teamData = ConstructorHelpers::FObjectFinder<UTeamData>(TEXT("/Game/Assets/TeamData")).Object;
|
||||
|
||||
playerCircle = CreateDefaultSubobject<UStaticMeshComponent>("Player Circle");
|
||||
playerCircle->AttachTo(RootComponent);
|
||||
|
||||
visionRadius = 2500;
|
||||
}
|
||||
void ANetworkPlayer::BeginPlay()
|
||||
{
|
||||
m_levelStatsInitialized = false;
|
||||
Super::BeginPlay();
|
||||
m_initialised = false;
|
||||
}
|
||||
void ANetworkPlayer::EndPlay(const EEndPlayReason::Type EndPlayReason)
|
||||
{
|
||||
Super::EndPlay(EndPlayReason);
|
||||
|
||||
if (Role != ROLE_Authority)
|
||||
return;
|
||||
|
||||
// Return the keys
|
||||
ANetworkPlayer* const killedBy = Cast<ANetworkPlayer>(GetLastPlayerDamage());
|
||||
if (m_health <= 0 && IsValid(killedBy) && killedBy->GetTeam() != GetTeam())
|
||||
{
|
||||
FText Source = FText::FromString(killedBy->playerName);
|
||||
FText Target = FText::FromString(playerName);
|
||||
onPlayerKilled.Broadcast(Source, Target, killedBy->GetTeam(), GetTeam());
|
||||
// Assign keys to the player that killed us
|
||||
for (auto iter = m_keyOrigins.CreateIterator(); iter; ++iter)
|
||||
{
|
||||
ASpawnerBase** find = killedBy->m_keyOrigins.Find(iter->Key);
|
||||
if (!find)
|
||||
killedBy->m_keyOrigins.Add(iter->Key, iter->Value);
|
||||
else
|
||||
iter->Value->hasDroppedKey = false;
|
||||
}
|
||||
killedBy->m_keyState |= m_keyState;
|
||||
}
|
||||
else
|
||||
{
|
||||
// Return the keys we have
|
||||
for (auto iter = m_keyOrigins.CreateIterator(); iter; ++iter)
|
||||
{
|
||||
if (IsValid(iter->Value))
|
||||
iter->Value->hasDroppedKey = false;
|
||||
}
|
||||
}
|
||||
m_keyState = 0;
|
||||
m_keyOrigins.Reset();
|
||||
|
||||
// Clean up screen effects and delegates.
|
||||
m_filterTimelines.Empty();
|
||||
for (int32 i = 0; i < m_delegates.Num(); i++)
|
||||
{
|
||||
GetWorldTimerManager().ClearTimer(m_delegates[i]);
|
||||
}
|
||||
m_delegates.Empty();
|
||||
}
|
||||
void ANetworkPlayer::Tick(float DeltaSeconds)
|
||||
{
|
||||
Super::Tick(DeltaSeconds);
|
||||
|
||||
for (auto& timeline : m_filterTimelines)
|
||||
{
|
||||
timeline.Value.TickTimeline(DeltaSeconds);
|
||||
}
|
||||
|
||||
// Server only
|
||||
if(Role == ROLE_Authority)
|
||||
{
|
||||
ADefaultPlayerState* state = Cast<ADefaultPlayerState>(PlayerState);
|
||||
if(!m_levelStatsInitialized)
|
||||
{
|
||||
m_modifierManager->RecalculateCharacter();
|
||||
CheckStatsOverflow();
|
||||
if(!m_initialised)
|
||||
{
|
||||
m_health = m_maxHealth;
|
||||
m_mana = m_maxMana;
|
||||
m_initialised = true;
|
||||
}
|
||||
m_levelStatsInitialized = true;
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
void ANetworkPlayer::PossessedBy(AController* NewController)
|
||||
{
|
||||
Super::PossessedBy(NewController);
|
||||
OnRep_PlayerState();
|
||||
}
|
||||
void ANetworkPlayer::OnRep_PlayerState()
|
||||
{
|
||||
Super::OnRep_PlayerState();
|
||||
|
||||
// Initialize player circle
|
||||
UMaterialInstanceDynamic* mi = playerCircle->CreateDynamicMaterialInstance(0);
|
||||
FLinearColor teamColor = teamData->GetTeamColor(Cast<ADefaultPlayerState>(PlayerState)->GetTeam());
|
||||
mi->SetVectorParameterValue("Color", teamColor);
|
||||
}
|
||||
|
||||
void ANetworkPlayer::ResetModifiers()
|
||||
{
|
||||
// Learn abilities
|
||||
m_SetLevelStats();
|
||||
Super::ResetModifiers();
|
||||
}
|
||||
|
||||
void ANetworkPlayer::OnLevelUp_Server(int32 newLevel)
|
||||
{
|
||||
check(Role == ROLE_Authority);
|
||||
|
||||
// Learn abilities
|
||||
//m_SetLevelStats();
|
||||
|
||||
// Recalculate modifiers for new level
|
||||
if(m_modifierManager)
|
||||
m_modifierManager->RecalculateCharacter();
|
||||
CheckStatsOverflow();
|
||||
|
||||
// Call blueprint level up event (server-side)
|
||||
OnLevelUp(newLevel);
|
||||
}
|
||||
|
||||
class ANetworkPlayer* ANetworkPlayer::GetTeamMate() const
|
||||
{
|
||||
ADefaultPlayerState* state = Cast<ADefaultPlayerState>(PlayerState);
|
||||
if (state && state->teamMate)
|
||||
return state->teamMate->character;
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
void ANetworkPlayer::m_SetLevelStats()
|
||||
{
|
||||
Super::m_SetLevelStats();
|
||||
// Update stats
|
||||
ADefaultPlayerState* state = Cast<ADefaultPlayerState>(PlayerState);
|
||||
|
||||
float level = 1;
|
||||
if(IsValid(state))
|
||||
level = (float)state->GetLevel();
|
||||
|
||||
float newHealth = SampleHealthCurve(level);
|
||||
float newMana = SampleManaCurve(level);
|
||||
float newArmor = SampleArmorCurve(level);
|
||||
float newAttackSpeed = SampleAttackSpeedCurve(level);
|
||||
|
||||
//GWPRINT(L"Setting level bases stats for level " + level + L"(" + levelScale + L")");
|
||||
//GWPRINT(L"Health = " + m_maxHealth + L"->" + newHealth);
|
||||
//GWPRINT(L"Mana = " + m_maxMana + L"->" + newMana);
|
||||
//GWPRINT(L"Armor = " + m_armor + L"->" + newArmor);
|
||||
//GWPRINT(L"Attack Speed = " + m_attackSpeed + L"->" + newAs);
|
||||
|
||||
if(!m_levelStatsInitialized)
|
||||
{
|
||||
m_health = newHealth;
|
||||
m_mana = newMana;
|
||||
}
|
||||
|
||||
m_maxHealth = newHealth;
|
||||
m_maxMana = newMana;
|
||||
baseArmor = newArmor;
|
||||
m_attackSpeed = newAttackSpeed;
|
||||
|
||||
if(Role == ROLE_Authority)
|
||||
{
|
||||
// Send level-up/learn skill command
|
||||
ADefaultPlayerController* controller = Cast<ADefaultPlayerController>(GetController());
|
||||
if(controller)
|
||||
{
|
||||
controller->LearnSkillsForLevel();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void ANetworkPlayer::OnLevelUp_Implementation(int32 newLevel)
|
||||
{
|
||||
m_levelStatsInitialized = false;
|
||||
}
|
||||
|
||||
bool ANetworkPlayer::ToggleSwitch_Validate(ANetworkSwitch* networkSwitch)
|
||||
{
|
||||
return networkSwitch && FVector::Dist(GetActorLocation(), networkSwitch->GetActorLocation()) < 600;
|
||||
}
|
||||
void ANetworkPlayer::ToggleSwitch_Implementation(ANetworkSwitch* networkSwitch)
|
||||
{
|
||||
if(FVector::Dist(GetActorLocation(), networkSwitch->GetActorLocation()) < 300)
|
||||
networkSwitch->Toggle();
|
||||
}
|
||||
|
||||
float ANetworkPlayer::SampleHealthCurve(float in)
|
||||
{
|
||||
if(GetCharacterClassProperties().healthCurve)
|
||||
{
|
||||
return GetCharacterClassProperties().healthCurve->GetFloatValue(in);
|
||||
}
|
||||
return healthCurve->GetFloatValue(in);
|
||||
}
|
||||
float ANetworkPlayer::SampleManaCurve(float in)
|
||||
{
|
||||
if(GetCharacterClassProperties().manaCurve)
|
||||
{
|
||||
return GetCharacterClassProperties().manaCurve->GetFloatValue(in);
|
||||
}
|
||||
return manaCurve->GetFloatValue(in);
|
||||
}
|
||||
float ANetworkPlayer::SampleArmorCurve(float in)
|
||||
{
|
||||
if(GetCharacterClassProperties().armorCurve)
|
||||
{
|
||||
return GetCharacterClassProperties().armorCurve->GetFloatValue(in);
|
||||
}
|
||||
return armorCurve->GetFloatValue(in);
|
||||
}
|
||||
float ANetworkPlayer::SampleAttackSpeedCurve(float in)
|
||||
{
|
||||
if(GetCharacterClassProperties().attackSpeedCurve)
|
||||
{
|
||||
return GetCharacterClassProperties().attackSpeedCurve->GetFloatValue(in);
|
||||
}
|
||||
return attackSpeedCurve->GetFloatValue(in);
|
||||
}
|
||||
|
||||
bool ANetworkPlayer::HasKey(PlayerKeyType keyType) const
|
||||
{
|
||||
if (keyType == PlayerKeyType::None)
|
||||
return m_keyState == 0;
|
||||
return ((1 << (int32)keyType) & m_keyState) != 0;
|
||||
}
|
||||
bool ANetworkPlayer::HasAnyKey() const
|
||||
{
|
||||
return m_keyState != 0;
|
||||
}
|
||||
bool ANetworkPlayer::AssignKey(PlayerKeyType keyType, ASpawnerBase* origin)
|
||||
{
|
||||
check(Role == ROLE_Authority);
|
||||
|
||||
if (HasKey(keyType))
|
||||
return false;
|
||||
|
||||
origin->hasDroppedKey = true;
|
||||
|
||||
// Store the origin for later
|
||||
m_keyOrigins.Add((uint8)keyType, origin);
|
||||
m_keyState += (1 << (int32)keyType);
|
||||
return true;
|
||||
}
|
||||
bool ANetworkPlayer::ClearKey(PlayerKeyType keyType)
|
||||
{
|
||||
check(Role == ROLE_Authority);
|
||||
|
||||
if (!HasKey(keyType))
|
||||
return false;
|
||||
|
||||
// Allow the boss to drop keys again
|
||||
ASpawnerBase** find = m_keyOrigins.Find((uint8)keyType);
|
||||
if (find)
|
||||
{
|
||||
if(IsValid(*find)) (*find)->hasDroppedKey = false;
|
||||
m_keyOrigins.Remove((uint8)keyType);
|
||||
}
|
||||
m_keyState -= (1 << (int32)keyType);
|
||||
return true;
|
||||
}
|
||||
|
||||
void ANetworkPlayer::GetLifetimeReplicatedProps(TArray<FLifetimeProperty>& OutLifetimeProps) const
|
||||
{
|
||||
Super::GetLifetimeReplicatedProps(OutLifetimeProps);
|
||||
|
||||
DOREPLIFETIME(ANetworkPlayer, m_keyState);
|
||||
}
|
||||
|
||||
FCharacterClassProperty ANetworkPlayer::GetCharacterClassProperties() const
|
||||
{
|
||||
static FCharacterClassProperty dummy;
|
||||
if(!Controller)
|
||||
return dummy;
|
||||
return Cast<ADefaultPlayerController>(Controller)->GetCharacterClassProperties();
|
||||
}
|
||||
|
||||
//Screen Filters
|
||||
void ANetworkPlayer::SetFilterDuration(float duration, UPostProcessComponent* filter)
|
||||
{
|
||||
FTimerHandle uniqueHandle;
|
||||
FTimerDelegate stopFilterDelegate = FTimerDelegate::CreateUObject(this, &ANetworkPlayer::StopFilter, filter, uniqueHandle);
|
||||
GetWorldTimerManager().SetTimer(uniqueHandle, stopFilterDelegate, duration, false);
|
||||
m_delegates.Add(uniqueHandle);
|
||||
}
|
||||
|
||||
void ANetworkPlayer::SetTimelineDuration(float duration, UPostProcessComponent* filter)
|
||||
{
|
||||
FTimerHandle uniqueHandle;
|
||||
FTimerDelegate stopFilterDelegate = FTimerDelegate::CreateUObject(this, &ANetworkPlayer::StopTimeline, filter, uniqueHandle);
|
||||
GetWorldTimerManager().SetTimer(uniqueHandle, stopFilterDelegate, duration, false);
|
||||
m_delegates.Add(uniqueHandle);
|
||||
}
|
||||
|
||||
void ANetworkPlayer::StartFilter(float duration, UPostProcessComponent* filter, UCurveFloat* timelineCurve)
|
||||
{
|
||||
if ( m_filterTimelines.Contains(filter) ) return;
|
||||
if (timelineCurve)
|
||||
{
|
||||
FTimeline timeLine;
|
||||
FOnTimelineFloatStatic timelineDelegate;
|
||||
timelineDelegate.BindUObject(this, &ANetworkPlayer::FilterUpdate, filter);
|
||||
timeLine.AddInterpFloat(timelineCurve, timelineDelegate);
|
||||
timeLine.SetPlayRate(1.0f / duration);
|
||||
timeLine.PlayFromStart();
|
||||
m_filterTimelines.Add(filter, timeLine);
|
||||
SetFilterDuration(duration, filter);
|
||||
}
|
||||
filter->bEnabled = true;
|
||||
}
|
||||
|
||||
void ANetworkPlayer::StartFilterWithDelegate(float duration, UPostProcessComponent* filter, UCurveFloat* timelineCurve, FPostAction action)
|
||||
{
|
||||
if ( m_filterTimelines.Contains(filter) ) return;
|
||||
if (timelineCurve)
|
||||
{
|
||||
FTimeline timeLine;
|
||||
FOnTimelineFloatStatic timelineDelegate;
|
||||
timelineDelegate.BindUObject(this, &ANetworkPlayer::FilterUpdate, filter, action);
|
||||
timeLine.AddInterpFloat(timelineCurve, timelineDelegate);
|
||||
timeLine.SetPlayRate(1.0f / duration);
|
||||
timeLine.PlayFromStart();
|
||||
m_filterTimelines.Add(filter, timeLine);
|
||||
SetFilterDuration(duration, filter);
|
||||
}
|
||||
filter->bEnabled = true;
|
||||
}
|
||||
|
||||
void ANetworkPlayer::EnableFilter(float duration, UPostProcessComponent* filter, UCurveFloat* timelineCurve)
|
||||
{
|
||||
if (m_filterTimelines.Contains(filter)) return;
|
||||
if (!timelineCurve)
|
||||
{
|
||||
YERROR("No timelineCurve given.");
|
||||
return;
|
||||
}
|
||||
|
||||
FTimeline timeLine;
|
||||
FOnTimelineFloatStatic timelineDelegate;
|
||||
timelineDelegate.BindUObject(this, &ANetworkPlayer::FilterUpdate, filter);
|
||||
timeLine.AddInterpFloat(timelineCurve, timelineDelegate);
|
||||
timeLine.SetPlayRate(1.0f / duration);
|
||||
timeLine.PlayFromStart();
|
||||
m_filterTimelines.Add(filter, timeLine);
|
||||
SetTimelineDuration(duration, filter);
|
||||
filter->bEnabled = true;
|
||||
}
|
||||
|
||||
void ANetworkPlayer::DisableFilter(float duration, UPostProcessComponent* filter, UCurveFloat* timeline)
|
||||
{
|
||||
StartFilter(duration, filter, timeline);
|
||||
}
|
||||
|
||||
void ANetworkPlayer::EnableFilterWithDelegate(float duration, UPostProcessComponent* filter, UCurveFloat* timelineCurve, FPostAction action)
|
||||
{
|
||||
if (m_filterTimelines.Contains(filter)) return;
|
||||
if (!timelineCurve)
|
||||
{
|
||||
YERROR("No timelineCurve given.");
|
||||
return;
|
||||
}
|
||||
|
||||
FTimeline timeLine;
|
||||
FOnTimelineFloatStatic timelineDelegate;
|
||||
timelineDelegate.BindUObject(this, &ANetworkPlayer::FilterUpdate, filter, action);
|
||||
timeLine.AddInterpFloat(timelineCurve, timelineDelegate);
|
||||
timeLine.SetPlayRate(1.0f / duration);
|
||||
timeLine.PlayFromStart();
|
||||
m_filterTimelines.Add(filter, timeLine);
|
||||
SetTimelineDuration(duration, filter);
|
||||
filter->bEnabled = true;
|
||||
}
|
||||
|
||||
void ANetworkPlayer::DisableFilterWithDelegate(float duration, UPostProcessComponent* filter, UCurveFloat* timeline, FPostAction action)
|
||||
{
|
||||
StartFilterWithDelegate(duration, filter, timeline, action);
|
||||
}
|
||||
|
||||
void ANetworkPlayer::StopFilter(UPostProcessComponent* filter, FTimerHandle handle)
|
||||
{
|
||||
filter->bEnabled = false;
|
||||
m_delegates.Remove(handle);
|
||||
if (!m_filterTimelines.Contains(filter))
|
||||
{
|
||||
YERROR("Can't find filter in timeline.");
|
||||
return;
|
||||
}
|
||||
m_filterTimelines.Remove(filter);
|
||||
}
|
||||
|
||||
void ANetworkPlayer::StopTimeline( UPostProcessComponent* filter, FTimerHandle handle )
|
||||
{
|
||||
m_delegates.Remove(handle);
|
||||
if (!m_filterTimelines.Contains(filter))
|
||||
{
|
||||
YERROR("Can't find filter in timeline.");
|
||||
return;
|
||||
}
|
||||
m_filterTimelines.Remove(filter);
|
||||
}
|
||||
|
||||
void ANetworkPlayer::FilterUpdate(float value, UPostProcessComponent* filter)
|
||||
{
|
||||
filter->BlendWeight = value;
|
||||
}
|
||||
|
||||
void ANetworkPlayer::FilterUpdate(float value, UPostProcessComponent* filter, FPostAction action)
|
||||
{
|
||||
action.ExecuteIfBound(value, filter);
|
||||
}
|
||||
|
||||
void ANetworkPlayer::AddThreatLevel_Client_Implementation(const float a_NewThreat)
|
||||
{
|
||||
threats.Add(a_NewThreat);
|
||||
|
||||
threatLevel = a_NewThreat > threatLevel ? a_NewThreat : threatLevel;
|
||||
}
|
||||
|
||||
void ANetworkPlayer::RemoveThreatLevel_Client_Implementation(const float a_NewThreat)
|
||||
{
|
||||
threats.RemoveSingle(a_NewThreat);
|
||||
|
||||
if (a_NewThreat == threatLevel)
|
||||
{
|
||||
threatLevel = 0;
|
||||
for (int32 i = 0; i < threats.Num(); i++)
|
||||
{
|
||||
if (threats[i] > threatLevel)
|
||||
{
|
||||
threatLevel = threats[i];
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
155
Source/UnrealProject/Creatures/NetworkPlayer.h
Normal file
155
Source/UnrealProject/Creatures/NetworkPlayer.h
Normal file
@@ -0,0 +1,155 @@
|
||||
// Project Lab - NHTV Igad
|
||||
|
||||
#pragma once
|
||||
|
||||
#include "Creatures/NetworkCharacter.h"
|
||||
#include "IngameSkillTree.h"
|
||||
#include "PlayerKeyType.h"
|
||||
#include "NetworkPlayer.generated.h"
|
||||
|
||||
/**
|
||||
*
|
||||
*/
|
||||
UCLASS()
|
||||
class UNREALPROJECT_API ANetworkPlayer : public ANetworkCharacter
|
||||
{
|
||||
GENERATED_BODY()
|
||||
|
||||
public:
|
||||
ANetworkPlayer();
|
||||
virtual void BeginPlay() override;
|
||||
virtual void EndPlay(const EEndPlayReason::Type EndPlayReason) override;
|
||||
virtual void Tick(float DeltaSeconds) override;
|
||||
|
||||
// For detecting when the player is possessed
|
||||
virtual void PossessedBy(AController* NewController) override;
|
||||
virtual void OnRep_PlayerState() override;
|
||||
|
||||
// Clears all the modifier stat modifications from this character
|
||||
virtual void ResetModifiers();
|
||||
|
||||
UFUNCTION(Server, Reliable, WithValidation)
|
||||
void ToggleSwitch(class ANetworkSwitch* networkSwitch);
|
||||
|
||||
// Server-Side level up event
|
||||
UFUNCTION(BlueprintNativeEvent, Category = "LevelUp")
|
||||
void OnLevelUp(int32 newLevel);
|
||||
// Called server-side when a character levels up
|
||||
void OnLevelUp_Server(int32 newLevel);
|
||||
|
||||
// Get the class properties of this player
|
||||
UFUNCTION(BlueprintCallable, Category = "State")
|
||||
FCharacterClassProperty GetCharacterClassProperties() const;
|
||||
|
||||
UFUNCTION(BlueprintCallable, Category = "Team")
|
||||
class ANetworkPlayer* GetTeamMate() const;
|
||||
|
||||
DECLARE_DYNAMIC_MULTICAST_DELEGATE_FourParams(FOnPlayerKilled, FText, source, FText, target, int32, sourceNum, int32, targetNum);
|
||||
UPROPERTY(BlueprintAssignable, Category = "Game")
|
||||
FOnPlayerKilled onPlayerKilled;
|
||||
|
||||
UPROPERTY(EditAnywhere, BlueprintReadWrite, Category = "Music")
|
||||
float threatLevel;
|
||||
TArray<float> threats;
|
||||
UFUNCTION(Client, Reliable)
|
||||
void AddThreatLevel_Client(const float a_NewThreat);
|
||||
UFUNCTION(Client, Reliable)
|
||||
void RemoveThreatLevel_Client(const float a_NewThreat);
|
||||
|
||||
// Base Stats for all characters
|
||||
UPROPERTY(VisibleAnywhere, Category = "Stats")
|
||||
UCurveFloat* healthCurve;
|
||||
UPROPERTY(VisibleAnywhere, Category = "Stats")
|
||||
UCurveFloat* healthRegenCurve;
|
||||
UPROPERTY(VisibleAnywhere, Category = "Stats")
|
||||
UCurveFloat* manaCurve;
|
||||
UPROPERTY(VisibleAnywhere, Category = "Stats")
|
||||
UCurveFloat* manaRegenCurve;
|
||||
UPROPERTY(VisibleAnywhere, Category = "Stats")
|
||||
UCurveFloat* armorCurve;
|
||||
UPROPERTY(VisibleAnywhere, Category = "Stats")
|
||||
UCurveFloat* attackSpeedCurve;
|
||||
|
||||
// Use these functions to sample the above curves, these functions also check class specific overrides
|
||||
float SampleHealthCurve(float in);
|
||||
float SampleManaCurve(float in);
|
||||
float SampleArmorCurve(float in);
|
||||
float SampleAttackSpeedCurve(float in);
|
||||
|
||||
// Creature level scaling logic
|
||||
// Damage done to creatures
|
||||
UPROPERTY(VisibleAnywhere, Category = "Stats")
|
||||
UCurveFloat* creatureDamageTakenCurve;
|
||||
// Damage dealt by creatures
|
||||
UPROPERTY(VisibleAnywhere, Category = "Stats")
|
||||
UCurveFloat* creatureDamageDealtCurve;
|
||||
|
||||
UFUNCTION(BlueprintCallable, Category = "Key")
|
||||
bool HasKey(PlayerKeyType keyType) const;
|
||||
UFUNCTION(BlueprintCallable, Category = "Key")
|
||||
bool HasAnyKey() const;
|
||||
bool AssignKey(PlayerKeyType keyType, class ASpawnerBase* origin);
|
||||
bool ClearKey(PlayerKeyType keyType);
|
||||
|
||||
// Starts a filter for a duration with a timeline to control the blending.
|
||||
UFUNCTION(BlueprintCallable, Category = "ScreenFilter")
|
||||
void StartFilter(float duration, UPostProcessComponent* filter, UCurveFloat* timeline);
|
||||
|
||||
DECLARE_DYNAMIC_DELEGATE_TwoParams(FPostAction, float, value, UPostProcessComponent*, filter);
|
||||
|
||||
// Starts a filter for a duration with a timeline to control the blending.
|
||||
UFUNCTION(BlueprintCallable, Category = "ScreenFilter")
|
||||
void StartFilterWithDelegate(float duration, UPostProcessComponent* filter, UCurveFloat* timeline, FPostAction action);
|
||||
|
||||
// Starts a filter with a timeline to control the blending.
|
||||
UFUNCTION(BlueprintCallable, Category = "ScreenFilter")
|
||||
void EnableFilter(float duration, UPostProcessComponent* filter, UCurveFloat* timeline);
|
||||
|
||||
// Stops a filter after a duration with a timeline to control the blending.
|
||||
UFUNCTION(BlueprintCallable, Category = "ScreenFilter")
|
||||
void DisableFilter(float duration, UPostProcessComponent* filter, UCurveFloat* timeline);
|
||||
|
||||
// Starts a filter with a timeline to control the blending.
|
||||
UFUNCTION(BlueprintCallable, Category = "ScreenFilter")
|
||||
void EnableFilterWithDelegate(float duration, UPostProcessComponent* filter, UCurveFloat* timeline, FPostAction action);
|
||||
|
||||
// Stops a filter after a duration with a timeline to control the blending.
|
||||
UFUNCTION(BlueprintCallable, Category = "ScreenFilter")
|
||||
void DisableFilterWithDelegate(float duration, UPostProcessComponent* filter, UCurveFloat* timeline, FPostAction action);
|
||||
|
||||
// The ring below the player
|
||||
UPROPERTY(BlueprintReadOnly, VisibleAnywhere)
|
||||
UStaticMeshComponent* playerCircle;
|
||||
|
||||
protected:
|
||||
void m_SetLevelStats() override;
|
||||
private:
|
||||
// Sets the duration of the filter.
|
||||
// Creates a delagate for StopFilter.
|
||||
void SetFilterDuration(float duration, UPostProcessComponent* filter);
|
||||
|
||||
// Sets the duration of the timeline.
|
||||
// Creates a delagate for StopTimeline.
|
||||
void SetTimelineDuration(float duration, UPostProcessComponent* filter);
|
||||
|
||||
// Disables the filter.
|
||||
void StopFilter(UPostProcessComponent* filter, FTimerHandle handle);
|
||||
|
||||
// Clears the timeline.
|
||||
void StopTimeline(UPostProcessComponent* filter, FTimerHandle handle);
|
||||
|
||||
// Updates the blending weight of the filter.
|
||||
void FilterUpdate(float value, UPostProcessComponent* filter);
|
||||
void FilterUpdate(float value, UPostProcessComponent* filter, FPostAction action);
|
||||
|
||||
// TODO: Add filter queue.
|
||||
|
||||
bool m_levelStatsInitialized;
|
||||
|
||||
UPROPERTY(Replicated)
|
||||
uint8 m_keyState;
|
||||
TMap<uint8, class ASpawnerBase*> m_keyOrigins;
|
||||
|
||||
TMap<UPostProcessComponent*, FTimeline> m_filterTimelines;
|
||||
TArray<FTimerHandle> m_delegates;
|
||||
};
|
||||
106
Source/UnrealProject/Creatures/NetworkPossessable.cpp
Normal file
106
Source/UnrealProject/Creatures/NetworkPossessable.cpp
Normal file
@@ -0,0 +1,106 @@
|
||||
// Project Lab - NHTV Igad
|
||||
|
||||
#include "UnrealProject.h"
|
||||
#include "NetworkPossessable.h"
|
||||
#include "DefaultPlayerController.h"
|
||||
|
||||
|
||||
// Sets default values
|
||||
ANetworkPossessable::ANetworkPossessable()
|
||||
{
|
||||
|
||||
}
|
||||
|
||||
// Called every frame
|
||||
void ANetworkPossessable::Tick( float DeltaTime )
|
||||
{
|
||||
Super::Tick( DeltaTime );
|
||||
|
||||
m_UpdateCamera();
|
||||
}
|
||||
|
||||
|
||||
void ANetworkPossessable::AddMovementInput(FVector WorldDirection, float ScaleValue /*= 1.0f*/, bool bForce /*= false*/)
|
||||
{
|
||||
check(!WorldDirection.ContainsNaN());
|
||||
check(!FMath::IsNaN(ScaleValue));
|
||||
if(m_AllowedToMove())
|
||||
Super::AddMovementInput(WorldDirection, ScaleValue, bForce);
|
||||
}
|
||||
void ANetworkPossessable::SetCharacterRotation(FRotator rotator)
|
||||
{
|
||||
if (m_AllowedToRotate())
|
||||
SetActorRotation(rotator);
|
||||
if(Role != ROLE_Authority)
|
||||
{
|
||||
m_SetCharacterRotation_Server(rotator.Yaw);
|
||||
}
|
||||
}
|
||||
void ANetworkPossessable::AddControllerYawInput(float Val)
|
||||
{
|
||||
if(m_AllowedToRotate())
|
||||
Super::AddControllerYawInput(Val);
|
||||
}
|
||||
|
||||
bool ANetworkPossessable::m_SetCharacterRotation_Server_Validate(float rotation)
|
||||
{
|
||||
return true;
|
||||
}
|
||||
void ANetworkPossessable::m_SetCharacterRotation_Server_Implementation(float rotation)
|
||||
{
|
||||
SetActorRotation(FRotator(0.0f, rotation, 0.0f));
|
||||
}
|
||||
|
||||
|
||||
bool ANetworkPossessable::m_AllowedToMove()
|
||||
{
|
||||
return true;
|
||||
}
|
||||
bool ANetworkPossessable::m_AllowedToRotate()
|
||||
{
|
||||
return true;
|
||||
}
|
||||
|
||||
|
||||
void ANetworkPossessable::m_SetupCamera()
|
||||
{
|
||||
// Create a camera boom
|
||||
CameraBoom = CreateDefaultSubobject<USpringArmComponent>(TEXT("CameraBoom"));
|
||||
CameraBoom->AttachTo(RootComponent);
|
||||
CameraBoom->bAbsoluteRotation = true;
|
||||
CameraBoom->TargetArmLength = 1500.f;
|
||||
CameraBoom->RelativeRotation = FRotator(-60.f, 45.f, 0.f);
|
||||
CameraBoom->bDoCollisionTest = false;
|
||||
CameraBoom->bInheritPitch = false;
|
||||
CameraBoom->bInheritYaw = false;
|
||||
CameraBoom->bInheritRoll = false;
|
||||
|
||||
// Create a camera
|
||||
TopDownCamera = CreateDefaultSubobject<UCameraComponent>(TEXT("TopDownCamera"));
|
||||
TopDownCamera->AttachTo(CameraBoom, USpringArmComponent::SocketName);
|
||||
TopDownCamera->bUsePawnControlRotation = false;
|
||||
|
||||
// Don't rotate character to camera direction
|
||||
bUseControllerRotationPitch = false;
|
||||
bUseControllerRotationYaw = false;
|
||||
bUseControllerRotationRoll = false;
|
||||
}
|
||||
void ANetworkPossessable::m_UpdateCamera()
|
||||
{
|
||||
if (!IsValid(CameraBoom)) return;
|
||||
|
||||
ADefaultPlayerController* const controller = Cast<ADefaultPlayerController>(GetController());
|
||||
if (IsValid(controller))
|
||||
{
|
||||
// Ensure the camera does not fall under the ground
|
||||
const FVector playerCoord = GetActorLocation();
|
||||
if (playerCoord.Z < 0)
|
||||
CameraBoom->SetRelativeLocation(FVector(0, 0, -playerCoord.Z));
|
||||
else if (CameraBoom->GetComponentLocation().Z < 0)
|
||||
CameraBoom->SetRelativeLocation(FVector());
|
||||
|
||||
// Update last position
|
||||
if (GetCharacterMovement()->IsMovingOnGround())
|
||||
controller->UpdatePlayerPosition(GetActorLocation(), GetActorRotation());
|
||||
}
|
||||
}
|
||||
35
Source/UnrealProject/Creatures/NetworkPossessable.h
Normal file
35
Source/UnrealProject/Creatures/NetworkPossessable.h
Normal file
@@ -0,0 +1,35 @@
|
||||
// Project Lab - NHTV Igad
|
||||
|
||||
#pragma once
|
||||
|
||||
#include "CharacterBase.h"
|
||||
#include "NetworkPossessable.generated.h"
|
||||
|
||||
UCLASS(Abstract)
|
||||
class UNREALPROJECT_API ANetworkPossessable : public ACharacterBase
|
||||
{
|
||||
GENERATED_BODY()
|
||||
public:
|
||||
ANetworkPossessable();
|
||||
|
||||
virtual void Tick( float DeltaSeconds ) override;
|
||||
|
||||
UPROPERTY(VisibleAnywhere, BlueprintReadOnly, Category = Camera, meta = (AllowPrivateAccess = "true"))
|
||||
class USpringArmComponent* CameraBoom;
|
||||
UPROPERTY(VisibleAnywhere, BlueprintReadOnly, Category = Camera, meta = (AllowPrivateAccess = "true"))
|
||||
class UCameraComponent* TopDownCamera;
|
||||
|
||||
virtual void AddMovementInput(FVector WorldDirection, float ScaleValue = 1.0f, bool bForce = false) override;
|
||||
virtual void SetCharacterRotation(FRotator rotator);
|
||||
virtual void AddControllerYawInput(float Val) override;
|
||||
|
||||
protected:
|
||||
UFUNCTION(Server, Reliable, WithValidation)
|
||||
void m_SetCharacterRotation_Server(float rotation);
|
||||
|
||||
virtual bool m_AllowedToMove();
|
||||
virtual bool m_AllowedToRotate();
|
||||
|
||||
void m_SetupCamera();
|
||||
void m_UpdateCamera();
|
||||
};
|
||||
140
Source/UnrealProject/Creatures/OffensiveEnemy.cpp
Normal file
140
Source/UnrealProject/Creatures/OffensiveEnemy.cpp
Normal file
@@ -0,0 +1,140 @@
|
||||
// Project Lab - NHTV Igad
|
||||
|
||||
#include "UnrealProject.h"
|
||||
#include "OffensiveEnemy.h"
|
||||
#include "AbilityInfo.h"
|
||||
#include "SpawnerBase.h"
|
||||
#include "NetworkPlayer.h"
|
||||
#include "MinionAnimInstance.h"
|
||||
#include "BehaviorTree/EnemyAIController.h"
|
||||
|
||||
|
||||
AOffensiveEnemy::AOffensiveEnemy()
|
||||
{
|
||||
PrimaryActorTick.bCanEverTick = true;
|
||||
}
|
||||
void AOffensiveEnemy::BeginPlay()
|
||||
{
|
||||
Super::BeginPlay();
|
||||
if (Role != ROLE_Authority)
|
||||
return;
|
||||
|
||||
}
|
||||
void AOffensiveEnemy::EndPlay(const EEndPlayReason::Type EndPlayReason)
|
||||
{
|
||||
Super::EndPlay(EndPlayReason);
|
||||
if (Role != ROLE_Authority)
|
||||
return;
|
||||
}
|
||||
void AOffensiveEnemy::Tick(float deltaTime)
|
||||
{
|
||||
Super::Tick(deltaTime);
|
||||
if (Role != ROLE_Authority)
|
||||
return;
|
||||
|
||||
if (IsValid(target))
|
||||
{
|
||||
if (hasGeneral)
|
||||
{
|
||||
GeneralBehavior();
|
||||
}
|
||||
else
|
||||
{
|
||||
if (m_confusionTimer > 0.0f)
|
||||
{
|
||||
ConfusionBehavior();
|
||||
return;
|
||||
}
|
||||
BasicBehavior();
|
||||
}
|
||||
// Move to target
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
void AOffensiveEnemy::GeneralBehavior()
|
||||
{
|
||||
if (!meleeAbility || !specialAbility)
|
||||
{
|
||||
//RERROR("there is no abilities");
|
||||
return;
|
||||
}
|
||||
|
||||
const FVector2D actorLocation = FVector2D(GetActorLocation().X, GetActorLocation().Y);
|
||||
const FVector2D targetLocation = FVector2D(target->GetActorLocation().X, target->GetActorLocation().Y);
|
||||
const float targetDistSqr = FVector2D::DistSquared(targetLocation, actorLocation);
|
||||
m_tempAbility = meleeAbility;
|
||||
if (((m_tempAbility->AICastRange + 150)*(m_tempAbility->AICastRange + 150)) < targetDistSqr)
|
||||
return;
|
||||
if (GetAbilityState(m_tempAbility)->onCooldownTimer <= 0)
|
||||
{
|
||||
CastAbility(m_tempAbility);
|
||||
|
||||
UMinionAnimInstance* animInstance = Cast<UMinionAnimInstance>(GetMesh()->GetAnimInstance());
|
||||
if (IsValid(animInstance))
|
||||
{
|
||||
animInstance->ChangeAnimationStateTo(EMinionAnimState::MAS_Attacking);
|
||||
}
|
||||
|
||||
//m_rotateToPlayer = m_tempAbility->rotateTowardsPlayer;
|
||||
}
|
||||
}
|
||||
void AOffensiveEnemy::BasicBehavior()
|
||||
{
|
||||
if (!IsValid(target))
|
||||
return;
|
||||
if (FVector::DistSquared(target->GetActorLocation(), GetActorLocation()) > 200 * 200)
|
||||
{
|
||||
MoveToPoint(target->GetActorLocation());
|
||||
}
|
||||
else
|
||||
{
|
||||
FVector addvec = GetActorLocation() - target->GetActorLocation();
|
||||
addvec.Normalize();
|
||||
MoveToPoint(target->GetActorLocation()+addvec*200.0f);
|
||||
GetCharacterMovement()->Velocity = FVector::ZeroVector;
|
||||
}
|
||||
SetActorRotation(FVector(target->GetActorLocation() - GetActorLocation()).Rotation());
|
||||
if (!meleeAbility || !specialAbility)
|
||||
{
|
||||
//RERROR("there is no abilities");
|
||||
return;
|
||||
}
|
||||
const FVector2D actorLocation = FVector2D(GetActorLocation().X, GetActorLocation().Y);
|
||||
const FVector2D targetLocation = FVector2D(target->GetActorLocation().X, target->GetActorLocation().Y);
|
||||
const float targetDistSqr = FVector2D::DistSquared(targetLocation, actorLocation);
|
||||
m_tempAbility = meleeAbility;
|
||||
if ((rand() % 1000) > specialAbilityChance&&!m_isPulled)
|
||||
{
|
||||
m_tempAbility = specialAbility;
|
||||
}
|
||||
m_isPulled = true;
|
||||
if (((m_tempAbility->AICastRange + 150)*(m_tempAbility->AICastRange + 150)) < targetDistSqr)
|
||||
return;
|
||||
if (GetAbilityState(m_tempAbility)->onCooldownTimer <= 0)
|
||||
{
|
||||
CastAbility(m_tempAbility);
|
||||
|
||||
UMinionAnimInstance* animInstance = Cast<UMinionAnimInstance>(GetMesh()->GetAnimInstance());
|
||||
if (IsValid(animInstance))
|
||||
{
|
||||
animInstance->ChangeAnimationStateTo(EMinionAnimState::MAS_Attacking);
|
||||
}
|
||||
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
int32 AOffensiveEnemy::NativeDealDamage(class ANetworkCharacter* dealer, int32 damage, float armorPercentageIgnore, class UAbilityInfo* ability)
|
||||
{
|
||||
if (hasGeneral)
|
||||
{
|
||||
float damageMultiplier = FVector::DotProduct(dealer->GetActorForwardVector(), GetActorForwardVector());
|
||||
damageMultiplier += 1.0f;
|
||||
damageMultiplier *= 0.5f;
|
||||
damageMultiplier += 0.5f;
|
||||
return Super::NativeDealDamage(dealer, damage*damageMultiplier, armorPercentageIgnore, ability);
|
||||
}
|
||||
else return Super::NativeDealDamage(dealer, damage, armorPercentageIgnore, ability);
|
||||
}
|
||||
24
Source/UnrealProject/Creatures/OffensiveEnemy.h
Normal file
24
Source/UnrealProject/Creatures/OffensiveEnemy.h
Normal file
@@ -0,0 +1,24 @@
|
||||
// Project Lab - NHTV Igad
|
||||
|
||||
#pragma once
|
||||
|
||||
#include "Creatures/EnemyBase.h"
|
||||
#include "OffensiveEnemy.generated.h"
|
||||
|
||||
/**
|
||||
*
|
||||
*/
|
||||
UCLASS()
|
||||
class UNREALPROJECT_API AOffensiveEnemy : public AEnemyBase
|
||||
{
|
||||
GENERATED_BODY()
|
||||
|
||||
public:
|
||||
AOffensiveEnemy();
|
||||
virtual void BeginPlay() override;
|
||||
virtual void EndPlay(const EEndPlayReason::Type EndPlayReason) override;
|
||||
virtual void Tick(float deltaTime) override;
|
||||
virtual int32 NativeDealDamage(class ANetworkCharacter* dealer, int32 damage, float armorPercentageIgnore, class UAbilityInfo* ability) override;
|
||||
void BasicBehavior();
|
||||
void GeneralBehavior();
|
||||
};
|
||||
16
Source/UnrealProject/Creatures/PlayerKeyType.h
Normal file
16
Source/UnrealProject/Creatures/PlayerKeyType.h
Normal file
@@ -0,0 +1,16 @@
|
||||
// Project Lab - NHTV Igad
|
||||
|
||||
#pragma once
|
||||
|
||||
UENUM(BlueprintType)
|
||||
enum class PlayerKeyType : uint8
|
||||
{
|
||||
BossRoomKeyPart0 = 0,
|
||||
BossRoomKeyPart1 = 1,
|
||||
BossRoomKeyPart2 = 2,
|
||||
TreasureRoomKey = 3,
|
||||
None = 4,
|
||||
};
|
||||
|
||||
constexpr int32 keyFragmentMin = (int32)PlayerKeyType::BossRoomKeyPart0;
|
||||
constexpr int32 keyFragmentMax = (int32)PlayerKeyType::BossRoomKeyPart2;
|
||||
136
Source/UnrealProject/Creatures/RangedEnemy.cpp
Normal file
136
Source/UnrealProject/Creatures/RangedEnemy.cpp
Normal file
@@ -0,0 +1,136 @@
|
||||
// Project Lab - NHTV Igad
|
||||
|
||||
#include "UnrealProject.h"
|
||||
#include "RangedEnemy.h"
|
||||
#include "AbilityInfo.h"
|
||||
#include "SpawnerBase.h"
|
||||
#include "NetworkPlayer.h"
|
||||
#include "MinionAnimInstance.h"
|
||||
|
||||
ARangedEnemy::ARangedEnemy()
|
||||
{
|
||||
PrimaryActorTick.bCanEverTick = true;
|
||||
}
|
||||
void ARangedEnemy::BeginPlay()
|
||||
{
|
||||
Super::BeginPlay();
|
||||
if (Role != ROLE_Authority)
|
||||
return;
|
||||
}
|
||||
void ARangedEnemy::EndPlay(const EEndPlayReason::Type EndPlayReason)
|
||||
{
|
||||
Super::EndPlay(EndPlayReason);
|
||||
if (Role != ROLE_Authority)
|
||||
return;
|
||||
}
|
||||
void ARangedEnemy::Tick(float deltaTime)
|
||||
{
|
||||
Super::Tick(deltaTime);
|
||||
if (Role != ROLE_Authority)
|
||||
return;
|
||||
if (IsValid(target))
|
||||
{
|
||||
|
||||
if (!hasGeneral)
|
||||
{
|
||||
if (m_confusionTimer > 0.0f)
|
||||
{
|
||||
ConfusionBehavior();
|
||||
return;
|
||||
}
|
||||
BasicBehavior();
|
||||
}
|
||||
else
|
||||
{
|
||||
GeneralBehavior();
|
||||
}
|
||||
// Move to target
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
void ARangedEnemy::BasicBehavior()
|
||||
{
|
||||
if (!IsValid(target))
|
||||
return;
|
||||
const FVector2D actorLocation = FVector2D(GetActorLocation().X, GetActorLocation().Y);
|
||||
const FVector2D targetLocation = FVector2D(target->GetActorLocation().X, target->GetActorLocation().Y);
|
||||
const FVector2D spawnLocation = FVector2D(m_spawn->GetActorLocation());
|
||||
const float targetDistSqr = FVector2D::DistSquared(targetLocation, actorLocation);
|
||||
const float spawnTargetDistSqr = FVector2D::DistSquared(targetLocation, spawnLocation);
|
||||
|
||||
if (!meleeAbility || !specialAbility)
|
||||
{
|
||||
RERROR("there is no abilities");
|
||||
return;
|
||||
}
|
||||
if (!rangedAbility)
|
||||
{
|
||||
RERROR("Ranged enemy does not have a ranged ability");
|
||||
}
|
||||
float castRange = rangedAbility->AICastRange;
|
||||
// walking code
|
||||
float testPercentage = (spawnTargetDistSqr) / ((m_spawn->aggroRadius + castRange)*(m_spawn->aggroRadius + castRange));
|
||||
|
||||
FVector newLocation = (target->GetActorLocation() - m_spawn->GetActorLocation());
|
||||
newLocation.Normalize();
|
||||
newLocation *= m_spawn->aggroRadius*testPercentage;
|
||||
FVector walkLocation = newLocation + m_spawn->GetActorLocation();
|
||||
if (m_lastPercentage < testPercentage)
|
||||
MoveToPoint(walkLocation);
|
||||
m_lastPercentage = testPercentage;
|
||||
|
||||
m_tempAbility = rangedAbility;
|
||||
if (targetDistSqr < (200 * 200))
|
||||
{
|
||||
m_tempAbility = meleeAbility;
|
||||
if ((rand() % 1000) > specialAbilityChance)
|
||||
{
|
||||
m_tempAbility = specialAbility;
|
||||
}
|
||||
}
|
||||
if (((m_tempAbility->AICastRange + 100)*(m_tempAbility->AICastRange + 100)) < targetDistSqr)
|
||||
return;
|
||||
if (GetAbilityState(m_tempAbility)->onCooldownTimer <= 0)
|
||||
{
|
||||
ChangeNPCAnimation((uint8)EMinionAnimState::MAS_Casting);
|
||||
CastAbility(m_tempAbility);
|
||||
}
|
||||
SetActorRotation(FVector(target->GetActorLocation() - GetActorLocation()).Rotation());
|
||||
}
|
||||
void ARangedEnemy::GeneralBehavior()
|
||||
{
|
||||
const FVector2D actorLocation = FVector2D(GetActorLocation().X, GetActorLocation().Y);
|
||||
const FVector2D targetLocation = FVector2D(target->GetActorLocation().X, target->GetActorLocation().Y);
|
||||
const FVector2D spawnLocation = FVector2D(m_spawn->GetActorLocation());
|
||||
const float targetDistSqr = FVector2D::DistSquared(targetLocation, actorLocation);
|
||||
const float spawnTargetDistSqr = FVector2D::DistSquared(targetLocation, spawnLocation);
|
||||
|
||||
if (!meleeAbility || !specialAbility)
|
||||
{
|
||||
RERROR("there is no abilities");
|
||||
return;
|
||||
}
|
||||
if (!rangedAbility)
|
||||
{
|
||||
RERROR("Ranged enemy does not have a ranged ability");
|
||||
}
|
||||
float castRange = rangedAbility->AICastRange;
|
||||
// walking code
|
||||
|
||||
|
||||
m_tempAbility = rangedAbility;
|
||||
if (targetDistSqr < (200 * 200))
|
||||
{
|
||||
m_tempAbility = meleeAbility;
|
||||
}
|
||||
if (((m_tempAbility->AICastRange + 100)*(m_tempAbility->AICastRange + 100)) < targetDistSqr)
|
||||
return;
|
||||
if (GetAbilityState(m_tempAbility)->onCooldownTimer <= 0)
|
||||
{
|
||||
ChangeNPCAnimation((uint8)EMinionAnimState::MAS_Casting);
|
||||
CastAbility(m_tempAbility);
|
||||
m_rotateToPlayer = m_tempAbility->rotateTowardsPlayer;
|
||||
}
|
||||
SetActorRotation(FVector(target->GetActorLocation() - GetActorLocation()).Rotation());
|
||||
}
|
||||
26
Source/UnrealProject/Creatures/RangedEnemy.h
Normal file
26
Source/UnrealProject/Creatures/RangedEnemy.h
Normal file
@@ -0,0 +1,26 @@
|
||||
// Project Lab - NHTV Igad
|
||||
|
||||
#pragma once
|
||||
|
||||
#include "Creatures/EnemyBase.h"
|
||||
#include "RangedEnemy.generated.h"
|
||||
|
||||
/**
|
||||
*
|
||||
*/
|
||||
UCLASS()
|
||||
class UNREALPROJECT_API ARangedEnemy : public AEnemyBase
|
||||
{
|
||||
GENERATED_BODY()
|
||||
|
||||
public:
|
||||
ARangedEnemy();
|
||||
virtual void BeginPlay() override;
|
||||
virtual void EndPlay(const EEndPlayReason::Type EndPlayReason) override;
|
||||
virtual void Tick(float deltaTime) override;
|
||||
void BasicBehavior();
|
||||
void GeneralBehavior();
|
||||
private:
|
||||
float m_lastPercentage;
|
||||
class UAbilityInfo* m_tempAbility;
|
||||
};
|
||||
175
Source/UnrealProject/Creatures/TouhouBoss.cpp
Normal file
175
Source/UnrealProject/Creatures/TouhouBoss.cpp
Normal file
@@ -0,0 +1,175 @@
|
||||
// Project Lab - NHTV Igad
|
||||
|
||||
#include "UnrealProject.h"
|
||||
#include "TouhouBoss.h"
|
||||
#include "CreatureSpawn.h"
|
||||
#include "AbilityEventGroup.h"
|
||||
#include "AbilityInfo.h"
|
||||
#include "Modifier.h"
|
||||
#include "NativeModifiers.h"
|
||||
#include "DefaultGameMode.h"
|
||||
#include "NetworkPlayer.h"
|
||||
|
||||
ATouhouBoss::ATouhouBoss()
|
||||
{
|
||||
PrimaryActorTick.bCanEverTick = true;
|
||||
|
||||
/* AudioComp = CreateDefaultSubobject<UAudioComponent>(TEXT("Boss Audio"));
|
||||
if (AudioComp)
|
||||
{
|
||||
|
||||
AudioComp->bAutoActivate = false; // with this true the sounds play at spawn (game starts)
|
||||
}
|
||||
*/
|
||||
outOfCombatRegen = 25.0f;
|
||||
secondPhasePercentage = 0.5f;
|
||||
rotationSpeed = 1;
|
||||
cooldown = 2.5f;
|
||||
}
|
||||
|
||||
void ATouhouBoss::BeginPlay()
|
||||
{
|
||||
Super::BeginPlay();
|
||||
if (Role != ROLE_Authority)
|
||||
return;
|
||||
|
||||
m_engaged = false;
|
||||
}
|
||||
void ATouhouBoss::EndPlay(const EEndPlayReason::Type EndPlayReason)
|
||||
{
|
||||
Super::EndPlay(EndPlayReason);
|
||||
|
||||
if (Role != ROLE_Authority)
|
||||
return;
|
||||
m_walkTimer = 0.0f;
|
||||
|
||||
// Drop a key
|
||||
if (m_health <= 0)
|
||||
{
|
||||
ANetworkPlayer* const killedBy = Cast<ANetworkPlayer>(GetLastPlayerDamage());
|
||||
if (IsValid(killedBy) && IsValid(m_spawn))
|
||||
{
|
||||
killedBy->AssignKey(PlayerKeyType::TreasureRoomKey, m_spawn);
|
||||
onBossKilled.Broadcast(killedBy->GetTeam());
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void ATouhouBoss::m_Engaged()
|
||||
{
|
||||
Super::m_Engaged();
|
||||
m_engaged = true;
|
||||
ModifierManager* mod = GetModifierManager();
|
||||
if (mod != nullptr && m_regenModifier != nullptr)
|
||||
{
|
||||
|
||||
// Stop regenerating health.
|
||||
m_regenModifier->ForceDestroy();
|
||||
m_regenModifier = nullptr;
|
||||
SetShield(false);
|
||||
// Plays a sound indicating that the boss was engaged and start playing the boss music.
|
||||
//PlayEngageSound();
|
||||
}
|
||||
}
|
||||
|
||||
void ATouhouBoss::m_Disengaged()
|
||||
{
|
||||
Super::m_Disengaged();
|
||||
|
||||
ModifierManager* mod = GetModifierManager();
|
||||
if (mod != nullptr && m_regenModifier == nullptr)
|
||||
{
|
||||
m_engaged = false;
|
||||
// Start regenerating health.
|
||||
m_regenModifier = mod->AddModifier(ARegenModifier::StaticClass(), 0);
|
||||
Cast<ARegenModifier>(m_regenModifier)->regenPerTick = outOfCombatRegen;
|
||||
// Remove invincibility if it's active.
|
||||
if (m_invincibilityModifier)
|
||||
{
|
||||
m_invincibilityModifier->ForceDestroy();
|
||||
m_invincibilityModifier = nullptr;
|
||||
SetShield(false);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void ATouhouBoss::Tick(float deltaTime)
|
||||
{
|
||||
Super::Tick(deltaTime);
|
||||
|
||||
// Only execute boss code on the server.
|
||||
if (Role != ROLE_Authority)
|
||||
return;
|
||||
|
||||
// Calculate health as percentage for deciding which phase the boss should be in.
|
||||
float healthPerc = (float)m_health / (float)m_maxHealth;
|
||||
|
||||
// If the distance of the target to the spawn is greater than the aggro range of the spawner,
|
||||
// then lose the target.
|
||||
if (IsValid(target))
|
||||
{
|
||||
float targetDistSqr = FVector::DistSquared(target->GetActorLocation(), m_spawn->SpawnResetPosition());
|
||||
float aggroSqr = m_spawn->deaggroRadius * m_spawn->deaggroRadius;
|
||||
if (aggroSqr < targetDistSqr)
|
||||
{
|
||||
target = nullptr;
|
||||
}
|
||||
}
|
||||
|
||||
//disengage
|
||||
if (!IsValid(target) && m_regenModifier == nullptr)
|
||||
{
|
||||
m_Disengaged();
|
||||
}
|
||||
//engage
|
||||
else if (target && !m_engaged)
|
||||
{
|
||||
m_Engaged();
|
||||
}
|
||||
|
||||
if (m_engaged)
|
||||
{
|
||||
if (m_invincibilityModifier)
|
||||
{
|
||||
m_invincibilityModifier->ForceDestroy();
|
||||
m_invincibilityModifier = nullptr;
|
||||
SetShield(false);
|
||||
}
|
||||
if (!IsValid(target))
|
||||
return;
|
||||
WalkPhase(deltaTime);
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
void ATouhouBoss::WalkPhase(float deltaTime)
|
||||
{
|
||||
MoveToPoint(target->GetActorLocation());
|
||||
m_walkTimer += deltaTime;
|
||||
|
||||
// After 2.5 seconds, cast a random ability.
|
||||
if (m_walkTimer > cooldown && abilities.Num() > 0 && target)
|
||||
{
|
||||
// Cast a random ability
|
||||
UAbilityInfo* ability = abilities[FMath::Rand() % abilities.Num()];
|
||||
if(IsValid(ability))
|
||||
CastAbility(ability);
|
||||
|
||||
m_walkTimer = 0;
|
||||
}
|
||||
}
|
||||
|
||||
void ATouhouBoss::m_SpawnModifiers()
|
||||
{
|
||||
/*ModifierManager* mod = GetModifierManager();
|
||||
if (mod != nullptr)
|
||||
{
|
||||
//m_regenModifier = mod->AddModifier(ARegenModifier::StaticClass(), 0);
|
||||
}*/
|
||||
}
|
||||
|
||||
void ATouhouBoss::SetShield_Implementation(bool on)
|
||||
{
|
||||
FWARNING("shield implemtentation not set in ATouHouBoss");
|
||||
return;
|
||||
}
|
||||
60
Source/UnrealProject/Creatures/TouhouBoss.h
Normal file
60
Source/UnrealProject/Creatures/TouhouBoss.h
Normal file
@@ -0,0 +1,60 @@
|
||||
// Project Lab - NHTV Igad
|
||||
|
||||
#pragma once
|
||||
|
||||
#include "Creatures/BossBase.h"
|
||||
#include "TouhouBoss.generated.h"
|
||||
|
||||
/**
|
||||
*
|
||||
*/
|
||||
UCLASS()
|
||||
class UNREALPROJECT_API ATouhouBoss : public ABossBase
|
||||
{
|
||||
GENERATED_BODY()
|
||||
|
||||
public:
|
||||
ATouhouBoss();
|
||||
virtual void BeginPlay() override;
|
||||
virtual void EndPlay(const EEndPlayReason::Type EndPlayReason) override;
|
||||
virtual void Tick(float deltaTime) override;
|
||||
void WalkPhase(float deltaTime);
|
||||
virtual void m_SpawnModifiers() override;
|
||||
|
||||
//UFUNCTION(NetMulticast, Reliable)
|
||||
//void PlayEngageSound();
|
||||
|
||||
//UPROPERTY(VisibleAnywhere, BlueprintReadOnly, Category = "Audio Component")
|
||||
//UAudioComponent* AudioComp;
|
||||
|
||||
DECLARE_DYNAMIC_MULTICAST_DELEGATE_OneParam(FOnBossKilled, int32, team);
|
||||
UPROPERTY(BlueprintAssignable, Category = "Game")
|
||||
FOnBossKilled onBossKilled;
|
||||
|
||||
UPROPERTY(EditDefaultsOnly, Category = "Shoot Phase")
|
||||
class UAbilityInfo* ability;
|
||||
UPROPERTY(EditDefaultsOnly, Category = "Shoot Phase")
|
||||
class UAbilityInfo* abilityChannel;
|
||||
UPROPERTY(EditDefaultsOnly, Category = "Shoot Phase")
|
||||
float rotationSpeed;
|
||||
UPROPERTY(EditDefaultsOnly, Category = "Shoot Phase")
|
||||
float cooldown;
|
||||
UPROPERTY(EditAnywhere, Category = "Modifiers")
|
||||
float outOfCombatRegen;
|
||||
UPROPERTY(EditAnywhere, Category = "State Transition", meta = (ClampMin = "0.0", ClampMax = "1.0", UIMin = "0.0", UIMax = "1.0"))
|
||||
float secondPhasePercentage;
|
||||
|
||||
UFUNCTION(BlueprintNativeEvent)
|
||||
void SetShield(bool on);
|
||||
private:
|
||||
virtual void m_Engaged();
|
||||
virtual void m_Disengaged();
|
||||
float m_walkTimer;
|
||||
bool m_engaged;
|
||||
|
||||
UPROPERTY()
|
||||
class AModifier* m_regenModifier;
|
||||
|
||||
UPROPERTY()
|
||||
class AModifier* m_invincibilityModifier;
|
||||
};
|
||||
62
Source/UnrealProject/Creatures/WiebertAnimation.cpp
Normal file
62
Source/UnrealProject/Creatures/WiebertAnimation.cpp
Normal file
@@ -0,0 +1,62 @@
|
||||
// Project Lab - NHTV Igad
|
||||
|
||||
#include "UnrealProject.h"
|
||||
#include "WiebertAnimation.h"
|
||||
#include "NetworkCharacter.h"
|
||||
#include "Animation/AnimNodeBase.h"
|
||||
#include "AnimationProxy.h"
|
||||
|
||||
UWiebertAnimation::UWiebertAnimation(const FObjectInitializer& init)
|
||||
: Super(init)
|
||||
{
|
||||
character = nullptr;
|
||||
attacking = false;
|
||||
charCustomization = FCharacterCustomization();
|
||||
m_swingAnimationSequence = 0;
|
||||
}
|
||||
void UWiebertAnimation::NativeInitializeAnimation()
|
||||
{
|
||||
attacking = false;
|
||||
Super::NativeInitializeAnimation();
|
||||
character = Cast<ANetworkCharacter>(this->GetOwningActor());
|
||||
if (character)
|
||||
{
|
||||
m_swingAnimationSequence = character->swingAnimationSequence;
|
||||
}
|
||||
}
|
||||
void UWiebertAnimation::NativeUpdateAnimation(float DeltaSeconds)
|
||||
{
|
||||
if(character && character->swingAnimationSequence > m_swingAnimationSequence)
|
||||
{
|
||||
attacking = true;
|
||||
m_swingAnimationSequence = character->swingAnimationSequence;
|
||||
}
|
||||
}
|
||||
void UWiebertAnimation::SetCharacterCustomization(const FCharacterCustomization& characterCustomization)
|
||||
{
|
||||
charCustomization = characterCustomization;
|
||||
}
|
||||
FAnimInstanceProxy* UWiebertAnimation::CreateAnimInstanceProxy()
|
||||
{
|
||||
check(!m_mainAnimProxy);
|
||||
m_mainAnimProxy = new FMainAnimProxy();
|
||||
|
||||
m_mainAnimProxy->boneNames[0] = "head";
|
||||
m_mainAnimProxy->boneNames[1] = "thigh_l";
|
||||
m_mainAnimProxy->boneNames[2] = "thigh_r";
|
||||
m_mainAnimProxy->boneNames[3] = "upperarm_l";
|
||||
m_mainAnimProxy->boneNames[4] = "upperarm_r";
|
||||
m_mainAnimProxy->boneNames[5] = "spine_03";
|
||||
m_mainAnimProxy->boneNames[6] = "root";
|
||||
|
||||
return m_mainAnimProxy;
|
||||
}
|
||||
void UWiebertAnimation::DestroyAnimInstanceProxy(FAnimInstanceProxy* InProxy)
|
||||
{
|
||||
check(InProxy == m_mainAnimProxy);
|
||||
delete InProxy;
|
||||
m_mainAnimProxy = nullptr;
|
||||
}
|
||||
void UWiebertAnimation::OnSwingAnimation_Implementation()
|
||||
{
|
||||
}
|
||||
42
Source/UnrealProject/Creatures/WiebertAnimation.h
Normal file
42
Source/UnrealProject/Creatures/WiebertAnimation.h
Normal file
@@ -0,0 +1,42 @@
|
||||
// Project Lab - NHTV Igad
|
||||
|
||||
#pragma once
|
||||
|
||||
#include "Animation/AnimInstance.h"
|
||||
#include "PlayerSetupState.h"
|
||||
#include "WiebertAnimation.generated.h"
|
||||
|
||||
/**
|
||||
*
|
||||
*/
|
||||
UCLASS()
|
||||
class UNREALPROJECT_API UWiebertAnimation : public UAnimInstance
|
||||
{
|
||||
GENERATED_BODY()
|
||||
|
||||
public:
|
||||
UWiebertAnimation(const FObjectInitializer& init);
|
||||
|
||||
virtual void NativeInitializeAnimation() override;
|
||||
virtual void NativeUpdateAnimation(float DeltaSeconds) override;
|
||||
|
||||
virtual FAnimInstanceProxy* CreateAnimInstanceProxy();
|
||||
virtual void DestroyAnimInstanceProxy(FAnimInstanceProxy* InProxy);
|
||||
|
||||
UFUNCTION(BlueprintNativeEvent, Category="Animation")
|
||||
void OnSwingAnimation();
|
||||
|
||||
UFUNCTION(BlueprintCallable, Category = "Animation")
|
||||
void SetCharacterCustomization(const FCharacterCustomization& characterCustomization);
|
||||
|
||||
UPROPERTY(BlueprintReadOnly, Category="Animation")
|
||||
class ANetworkCharacter* character;
|
||||
UPROPERTY(BlueprintReadWrite, Category = "Animation")
|
||||
bool attacking;
|
||||
|
||||
FCharacterCustomization charCustomization;
|
||||
private:
|
||||
// Keeps track of how many times an animation was triggered
|
||||
int32 m_swingAnimationSequence;
|
||||
struct FMainAnimProxy* m_mainAnimProxy;
|
||||
};
|
||||
15
Source/UnrealProject/Doodads/BaseSkillObject.cpp
Normal file
15
Source/UnrealProject/Doodads/BaseSkillObject.cpp
Normal file
@@ -0,0 +1,15 @@
|
||||
// Project Lab - NHTV Igad
|
||||
|
||||
#include "UnrealProject.h"
|
||||
#include "BaseSkillObject.h"
|
||||
|
||||
/*
|
||||
void UBaseSkillObject::Serialize( FArchive& a_ar )
|
||||
{
|
||||
Super::Serialize( a_ar );
|
||||
|
||||
UE_LOG( LogTemp, Warning, TEXT( "UBaseSkillObject: Serializing..." ) );
|
||||
|
||||
a_ar << *this;
|
||||
}
|
||||
*/
|
||||
70
Source/UnrealProject/Doodads/BaseSkillObject.h
Normal file
70
Source/UnrealProject/Doodads/BaseSkillObject.h
Normal file
@@ -0,0 +1,70 @@
|
||||
// Project Lab - NHTV Igad
|
||||
|
||||
#pragma once
|
||||
|
||||
#include "SkillObject.h"
|
||||
#include "Items/ItemBase.h"
|
||||
#include "BaseSkillObject.generated.h"
|
||||
|
||||
class AModifier;
|
||||
|
||||
USTRUCT()
|
||||
struct FSkillItem
|
||||
{
|
||||
GENERATED_BODY()
|
||||
|
||||
UPROPERTY(EditAnywhere)
|
||||
USkeletalMesh* mesh;
|
||||
UPROPERTY(EditAnywhere)
|
||||
EItemTypeEnum type;
|
||||
};
|
||||
|
||||
USTRUCT()
|
||||
struct FSkillItemHolder
|
||||
{
|
||||
GENERATED_BODY()
|
||||
|
||||
UPROPERTY(EditAnywhere)
|
||||
bool isVisible;
|
||||
UPROPERTY(EditAnywhere, meta = (EditCondition = "isVisible"))
|
||||
TArray<FSkillItem> itemsToEquip;
|
||||
UPROPERTY(EditDefaultsOnly, meta = (EditCondition = "isVisible"))
|
||||
UParticleSystem* particleEffect;
|
||||
};
|
||||
|
||||
UENUM(BlueprintType)
|
||||
enum class ESkillShapeType : uint8
|
||||
{
|
||||
Active = 0,
|
||||
Passive,
|
||||
Sustain,
|
||||
Coop,
|
||||
};
|
||||
|
||||
UCLASS(Blueprintable)
|
||||
class UNREALPROJECT_API UBaseSkillObject : public USkillObject
|
||||
{
|
||||
GENERATED_BODY()
|
||||
|
||||
public:
|
||||
UPROPERTY(EditAnywhere, BlueprintReadOnly, Category = "Skill")
|
||||
TArray<TSubclassOf<AModifier>> modifiers;
|
||||
UPROPERTY(EditAnywhere, BlueprintReadOnly, Category = "Skill")
|
||||
TArray<class UAbilityInfo*> abilityEffects;
|
||||
UPROPERTY(EditAnywhere, BlueprintReadOnly, Category = "Skill")
|
||||
ESkillShapeType skillShapeType;
|
||||
UPROPERTY(EditAnywhere, BlueprintReadOnly, Category = "Skill")
|
||||
FSkillItemHolder visibleItems;
|
||||
UPROPERTY(EditAnywhere, BlueprintReadOnly, Category = "Skill")
|
||||
FIntPoint pivot;
|
||||
|
||||
/*
|
||||
virtual void Serialize( FArchive& a_ar ) override;
|
||||
|
||||
friend FArchive& operator<<( FArchive& a_ar, UBaseSkillObject& a_v )
|
||||
{
|
||||
UE_LOG( LogTemp, Warning, TEXT( "UBaseSkillObject: << Operator..." ) );
|
||||
return a_ar;
|
||||
}
|
||||
*/
|
||||
};
|
||||
256
Source/UnrealProject/Doodads/CharacterCarousel.cpp
Normal file
256
Source/UnrealProject/Doodads/CharacterCarousel.cpp
Normal file
@@ -0,0 +1,256 @@
|
||||
// Project Lab - NHTV Igad
|
||||
|
||||
#include "UnrealProject.h"
|
||||
#include "CharacterCarousel.h"
|
||||
#include "CharacterBase.h"
|
||||
#include "DefaultGameInstance.h"
|
||||
|
||||
|
||||
|
||||
ACharacterCarousel::ACharacterCarousel()
|
||||
{
|
||||
PrimaryActorTick.bCanEverTick = true;
|
||||
|
||||
selectedCharacterIndex = 0;
|
||||
m_visualSelected = 0;
|
||||
selected = false;
|
||||
|
||||
m_zoom = 0;
|
||||
|
||||
selectedCharacterOffset = FVector(30, -120, 0);
|
||||
}
|
||||
|
||||
|
||||
void ACharacterCarousel::BeginPlay()
|
||||
{
|
||||
check(IsValid(characterBlueprint));
|
||||
|
||||
Super::BeginPlay();
|
||||
|
||||
// Fix the rotation
|
||||
FRotator rotation = GetActorRotation();
|
||||
SetActorRotation(FRotator(0, rotation.Yaw, 0));
|
||||
|
||||
const FVector position = GetActorLocation();
|
||||
const FVector forward = GetActorForwardVector();
|
||||
const FVector right = GetActorRightVector();
|
||||
|
||||
UDefaultGameInstance* instance = Cast<UDefaultGameInstance>(GetGameInstance());
|
||||
check(instance);
|
||||
UCharacterSettings* settings = instance->GetCharacterSettings();
|
||||
check(settings);
|
||||
|
||||
for (int32 i = 0; i < settings->characterSaves.Num(); i++)
|
||||
{
|
||||
FActorSpawnParameters spawnParams;
|
||||
spawnParams.SpawnCollisionHandlingOverride = ESpawnActorCollisionHandlingMethod::AlwaysSpawn;
|
||||
ACharacterBase* actor = GetWorld()->SpawnActor<ACharacterBase>(characterBlueprint, GetActorLocation(), GetActorRotation(), spawnParams);
|
||||
m_characters.Add(actor);
|
||||
}
|
||||
}
|
||||
void ACharacterCarousel::EndPlay(const EEndPlayReason::Type EndPlayReason)
|
||||
{
|
||||
Super::EndPlay(EndPlayReason);
|
||||
|
||||
for (int32 i = 0; i < m_characters.Num(); i++)
|
||||
{
|
||||
if (IsValid(m_characters[i]))
|
||||
m_characters[i]->Destroy();
|
||||
}
|
||||
m_characters.SetNum(0);
|
||||
}
|
||||
|
||||
|
||||
inline float MoveTowardsTarget(float current, float target, float delta, float min = 0.0005f)
|
||||
{
|
||||
const int32 moveDir = target > current ? 1 : -1;
|
||||
float d = FMath::Abs((target - current) * delta);
|
||||
if (d < 0.0005f) d = 0.0005f;
|
||||
|
||||
current += d * moveDir;
|
||||
|
||||
// Clamp
|
||||
current = (moveDir == 1) ?
|
||||
(current > target ? target : current) :
|
||||
(current < target ? target : current);
|
||||
|
||||
return current;
|
||||
}
|
||||
void ACharacterCarousel::Tick(float DeltaTime)
|
||||
{
|
||||
Super::Tick(DeltaTime);
|
||||
|
||||
if (m_characters.Num() == 0)
|
||||
return;
|
||||
|
||||
// Zoom towards the target
|
||||
const float zoomTarget = selected ? 1 : 0;
|
||||
m_zoom = MoveTowardsTarget(m_zoom, zoomTarget, DeltaTime * 10);
|
||||
|
||||
const FVector position = GetActorLocation();
|
||||
const FVector forward = GetActorForwardVector();
|
||||
const FVector right = GetActorRightVector();
|
||||
|
||||
const float carouselRadius = 1200;
|
||||
const float carouselAngle = 10;
|
||||
const FVector pivot = position - forward * carouselRadius;
|
||||
|
||||
// Clamp the selected index (may be altered from a blueprint)
|
||||
selectedCharacterIndex = selectedCharacterIndex < 0 ? 0 : selectedCharacterIndex >= m_characters.Num() ? m_characters.Num() - 1 : selectedCharacterIndex;
|
||||
|
||||
// Animate the carousel
|
||||
const float target = float(selectedCharacterIndex);
|
||||
m_visualSelected = MoveTowardsTarget(m_visualSelected, target, DeltaTime * (selected ? 10 : 5));
|
||||
|
||||
// Spawn and set the characters
|
||||
const FRotator rotation = GetActorRotation();
|
||||
const FVector arm = forward * carouselRadius;
|
||||
const FVector selectedOffset = rotation.RotateVector(selectedCharacterOffset) * m_zoom;
|
||||
|
||||
for (int32 i = 0; i < m_characters.Num(); i++)
|
||||
{
|
||||
const int32 distance = i - selectedCharacterIndex;
|
||||
if (distance < -2 || distance > 2)
|
||||
{
|
||||
// Dont render characters off screen
|
||||
m_characters[i]->GetRootComponent()->SetVisibility(false, true);
|
||||
}
|
||||
else
|
||||
{
|
||||
m_characters[i]->GetRootComponent()->SetVisibility(true, true);
|
||||
|
||||
// Set the position relative from the pivot
|
||||
FRotator rotator(0, -carouselAngle * (float(i) - m_visualSelected), 0);
|
||||
if (m_zoom > 0) rotator.Yaw *= (m_zoom + 1);
|
||||
FVector offset = rotator.RotateVector(arm);
|
||||
if (i == selectedCharacterIndex)
|
||||
offset += selectedOffset;
|
||||
m_characters[i]->SetActorLocation(pivot + offset);
|
||||
m_characters[i]->SetActorRotation(rotator + FRotator(0, rotation.Yaw, 0));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
void ACharacterCarousel::Reset()
|
||||
{
|
||||
selectedCharacterIndex = 0;
|
||||
m_visualSelected = 0;
|
||||
selected = false;
|
||||
|
||||
m_zoom = 0;
|
||||
}
|
||||
|
||||
void ACharacterCarousel::Next()
|
||||
{
|
||||
if(!selected && m_characters.Num() > 0 && selectedCharacterIndex < m_characters.Num() - 1)
|
||||
selectedCharacterIndex++;
|
||||
}
|
||||
void ACharacterCarousel::Previous()
|
||||
{
|
||||
if (!selected && selectedCharacterIndex > 0)
|
||||
selectedCharacterIndex--;
|
||||
}
|
||||
|
||||
void ACharacterCarousel::ToggleSelect(bool enabled)
|
||||
{
|
||||
selected = enabled;
|
||||
}
|
||||
|
||||
void ACharacterCarousel::CreateNewCharacter(const FString& name)
|
||||
{
|
||||
UDefaultGameInstance* instance = Cast<UDefaultGameInstance>(GetGameInstance());
|
||||
check(instance);
|
||||
UCharacterSettings* settings = instance->GetCharacterSettings();
|
||||
check(settings);
|
||||
|
||||
// Create a scene actor
|
||||
ACharacterBase* actor = GetWorld()->SpawnActor<ACharacterBase>(characterBlueprint, GetActorLocation(), GetActorRotation());
|
||||
m_characters.Add(actor);
|
||||
|
||||
// Select this character
|
||||
selected = false;
|
||||
selectedCharacterIndex = m_characters.Num() - 1;
|
||||
m_zoom = 0;
|
||||
|
||||
// Add to save
|
||||
FCharacterSave save;
|
||||
save.name = name;
|
||||
settings->characterSaves.Add(save);
|
||||
instance->SaveSettings();
|
||||
}
|
||||
void ACharacterCarousel::DeleteCharacter(int32 index)
|
||||
{
|
||||
UDefaultGameInstance* instance = Cast<UDefaultGameInstance>(GetGameInstance());
|
||||
check(instance);
|
||||
UCharacterSettings* settings = instance->GetCharacterSettings();
|
||||
check(settings);
|
||||
|
||||
if (index < 0 || index >= settings->characterSaves.Num())
|
||||
{
|
||||
JERROR("Invalid character index");
|
||||
return;
|
||||
}
|
||||
|
||||
// Destroy scene actor
|
||||
m_characters[index]->Destroy();
|
||||
m_characters.RemoveAt(index);
|
||||
if (index == selectedCharacterIndex)
|
||||
selectedCharacterIndex--;
|
||||
|
||||
// Unselect
|
||||
selected = false;
|
||||
m_zoom = 0;
|
||||
|
||||
// Remove from save
|
||||
settings->characterSaves.RemoveAt(index);
|
||||
instance->SaveSettings();
|
||||
}
|
||||
|
||||
ACharacterBase* ACharacterCarousel::GetCharacterModel(int32 index)
|
||||
{
|
||||
if (index < 0 || index >= m_characters.Num())
|
||||
{
|
||||
JERROR("Invalid character index");
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
return m_characters[index];
|
||||
}
|
||||
int32 ACharacterCarousel::GetCharacterNum()
|
||||
{
|
||||
return m_characters.Num();
|
||||
}
|
||||
|
||||
|
||||
void ACharacterCarousel::SaveCharacterName(const FString& name, int32 index)
|
||||
{
|
||||
UDefaultGameInstance* instance = Cast<UDefaultGameInstance>(GetGameInstance());
|
||||
check(instance);
|
||||
UCharacterSettings* settings = instance->GetCharacterSettings();
|
||||
check(settings);
|
||||
|
||||
if (index < 0 || index >= settings->characterSaves.Num())
|
||||
{
|
||||
JERROR("Invalid character index");
|
||||
return;
|
||||
}
|
||||
|
||||
settings->characterSaves[index].name = name;
|
||||
instance->SaveSettings();
|
||||
}
|
||||
FString ACharacterCarousel::GetCharacterName(int32 index)
|
||||
{
|
||||
UDefaultGameInstance* instance = Cast<UDefaultGameInstance>(GetGameInstance());
|
||||
check(instance);
|
||||
UCharacterSettings* settings = instance->GetCharacterSettings();
|
||||
check(settings);
|
||||
|
||||
if (index < 0 || index >= settings->characterSaves.Num())
|
||||
{
|
||||
JERROR("Invalid character index");
|
||||
return FString();
|
||||
}
|
||||
|
||||
return settings->characterSaves[index].name;
|
||||
}
|
||||
70
Source/UnrealProject/Doodads/CharacterCarousel.h
Normal file
70
Source/UnrealProject/Doodads/CharacterCarousel.h
Normal file
@@ -0,0 +1,70 @@
|
||||
// Project Lab - NHTV Igad
|
||||
|
||||
#pragma once
|
||||
|
||||
#include "GameFramework/Actor.h"
|
||||
#include "CharacterSettings.h"
|
||||
#include "CharacterCarousel.generated.h"
|
||||
|
||||
UCLASS()
|
||||
class UNREALPROJECT_API ACharacterCarousel : public AActor
|
||||
{
|
||||
GENERATED_BODY()
|
||||
|
||||
public:
|
||||
// Sets default values for this actor's properties
|
||||
ACharacterCarousel();
|
||||
|
||||
// Called when the game starts or when spawned
|
||||
virtual void BeginPlay() override;
|
||||
virtual void EndPlay(const EEndPlayReason::Type EndPlayReason) override;
|
||||
|
||||
// Called every frame
|
||||
virtual void Tick(float DeltaSeconds) override;
|
||||
|
||||
|
||||
UFUNCTION(BlueprintCallable, Category = "Carousel")
|
||||
void Reset();
|
||||
|
||||
UFUNCTION(BlueprintCallable, Category = "Carousel")
|
||||
void Next();
|
||||
UFUNCTION(BlueprintCallable, Category = "Carousel")
|
||||
void Previous();
|
||||
|
||||
UFUNCTION(BlueprintCallable, Category = "Carousel")
|
||||
void ToggleSelect(bool enabled);
|
||||
|
||||
UFUNCTION(BlueprintCallable, Category = "Carousel")
|
||||
void CreateNewCharacter(const FString& name);
|
||||
UFUNCTION(BlueprintCallable, Category = "Carousel")
|
||||
void DeleteCharacter(int32 index);
|
||||
|
||||
// Index of the current selected character
|
||||
UPROPERTY(BlueprintReadWrite, Category = "Carousel")
|
||||
int32 selectedCharacterIndex;
|
||||
|
||||
// Wether the current character is also selected
|
||||
UPROPERTY(BlueprintReadWrite, Category = "Carousel")
|
||||
bool selected;
|
||||
|
||||
UPROPERTY(EditAnywhere, Category = "Carousel")
|
||||
TSubclassOf<class ACharacterBase> characterBlueprint;
|
||||
|
||||
UFUNCTION(BlueprintCallable, Category = "Carousel")
|
||||
class ACharacterBase* GetCharacterModel(int32 index);
|
||||
UFUNCTION(BlueprintCallable, Category = "Carousel")
|
||||
int32 GetCharacterNum();
|
||||
|
||||
UPROPERTY(EditAnywhere, Category = "Carousel")
|
||||
FVector selectedCharacterOffset;
|
||||
|
||||
UFUNCTION(BlueprintCallable, Category = "Carousel")
|
||||
void SaveCharacterName(const FString& name, int32 index);
|
||||
UFUNCTION(BlueprintCallable, Category = "Carousel")
|
||||
FString GetCharacterName(int32 index);
|
||||
|
||||
private:
|
||||
TArray<class ACharacterBase*> m_characters;
|
||||
float m_visualSelected;
|
||||
float m_zoom;
|
||||
};
|
||||
66
Source/UnrealProject/Doodads/CharacterSettings.cpp
Normal file
66
Source/UnrealProject/Doodads/CharacterSettings.cpp
Normal file
@@ -0,0 +1,66 @@
|
||||
// Project Lab - NHTV Igad
|
||||
|
||||
#include "UnrealProject.h"
|
||||
#include "CharacterSettings.h"
|
||||
#include "SkillTreeWidget.h"
|
||||
#include "UserWidget.h"
|
||||
|
||||
#define SAVE_SLOT_NAME TEXT("WIEBERSAVE tm")
|
||||
|
||||
static UClass* skillTreeWidgetClass;
|
||||
|
||||
UCharacterSettings::UCharacterSettings(const FObjectInitializer& ObjectInitializer)
|
||||
: Super(ObjectInitializer)
|
||||
{
|
||||
skillTreeWidgetClass = ConstructorHelpers::FClassFinder<USkillTreeWidget>(TEXT("/Game/Assets/GUI/WEEGEE_SkillTree")).Class;
|
||||
}
|
||||
|
||||
UCharacterSettings* UCharacterSettings::Load()
|
||||
{
|
||||
UCharacterSettings* settings = Cast<UCharacterSettings>(UGameplayStatics::LoadGameFromSlot(SAVE_SLOT_NAME, 0));
|
||||
if (!settings)
|
||||
{
|
||||
settings = Cast<UCharacterSettings>(UGameplayStatics::CreateSaveGameObject(UCharacterSettings::StaticClass()));
|
||||
}
|
||||
check(settings);
|
||||
|
||||
for (auto& save : settings->characterSaves)
|
||||
{
|
||||
for (TFieldIterator<UProperty> PropIt(FCharacterCustomization::StaticStruct()); PropIt; ++PropIt)
|
||||
{
|
||||
UFloatProperty* fProperty = Cast<UFloatProperty>(*PropIt);
|
||||
|
||||
if (IsValid(fProperty))
|
||||
{
|
||||
float* valPtr = fProperty->GetPropertyValuePtr_InContainer(&save.characterCustomization);
|
||||
*valPtr = FMath::Clamp(*valPtr, 0.5f, 1.5f);
|
||||
}
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
return settings;
|
||||
}
|
||||
|
||||
void UCharacterSettings::Save(UCharacterSettings* settings)
|
||||
{
|
||||
UGameplayStatics::SaveGameToSlot(settings, SAVE_SLOT_NAME, 0);
|
||||
}
|
||||
|
||||
TArray<FCharacterSave> UCharacterSettings::GetValidCharacters() const
|
||||
{
|
||||
UCharacterSettings* settings = Cast<UCharacterSettings>(UGameplayStatics::LoadGameFromSlot(SAVE_SLOT_NAME, 0));
|
||||
if (!settings)
|
||||
{
|
||||
settings = Cast<UCharacterSettings>(UGameplayStatics::CreateSaveGameObject(UCharacterSettings::StaticClass()));
|
||||
}
|
||||
check(settings);
|
||||
|
||||
TArray<FCharacterSave> result;
|
||||
for (FCharacterSave& save : settings->characterSaves)
|
||||
{
|
||||
if (save.skillTreeState.ValidateSkillTree())
|
||||
result.Add(save);
|
||||
}
|
||||
return result;
|
||||
}
|
||||
47
Source/UnrealProject/Doodads/CharacterSettings.h
Normal file
47
Source/UnrealProject/Doodads/CharacterSettings.h
Normal file
@@ -0,0 +1,47 @@
|
||||
// Project Lab - NHTV Igad
|
||||
|
||||
#pragma once
|
||||
|
||||
#include "GameFramework/SaveGame.h"
|
||||
#include "SkillTreeState.h"
|
||||
#include "PlayerSetupState.h"
|
||||
#include "CharacterSettings.generated.h"
|
||||
|
||||
USTRUCT(BlueprintType)
|
||||
struct FCharacterSave
|
||||
{
|
||||
GENERATED_BODY()
|
||||
|
||||
public:
|
||||
UPROPERTY(BlueprintReadWrite, Category = "Save")
|
||||
FString name;
|
||||
|
||||
UPROPERTY(BlueprintReadWrite, Category = "Save")
|
||||
FSkillTreeState skillTreeState;
|
||||
|
||||
UPROPERTY(BlueprintReadWrite, Category = "Save")
|
||||
FCharacterCustomization characterCustomization;
|
||||
|
||||
UPROPERTY(BlueprintReadWrite, Category = "Save")
|
||||
int32 characterClass;
|
||||
};
|
||||
|
||||
UCLASS()
|
||||
class UNREALPROJECT_API UCharacterSettings : public USaveGame
|
||||
{
|
||||
GENERATED_BODY()
|
||||
private:
|
||||
|
||||
public:
|
||||
UCharacterSettings(const FObjectInitializer& ObjectInitializer);
|
||||
|
||||
UPROPERTY(BlueprintReadWrite, Category = "Basic")
|
||||
TArray<FCharacterSave> characterSaves;
|
||||
|
||||
UFUNCTION(BlueprintCallable, Category = "Basic")
|
||||
TArray<FCharacterSave> GetValidCharacters() const;
|
||||
public:
|
||||
static UCharacterSettings* Load();
|
||||
static void Save(UCharacterSettings* settings);
|
||||
|
||||
};
|
||||
74
Source/UnrealProject/Doodads/FixupUtility.cpp
Normal file
74
Source/UnrealProject/Doodads/FixupUtility.cpp
Normal file
@@ -0,0 +1,74 @@
|
||||
// Project Lab - NHTV Igad
|
||||
|
||||
#include "UnrealProject.h"
|
||||
#include "FixupUtility.h"
|
||||
#include "AbilityInfo.h"
|
||||
#include "AssetRegistryInterface.h"
|
||||
#include "AssetRegistryModule.h"
|
||||
#include "SpawnerBase.h"
|
||||
|
||||
void UFixupUtility::FixupAbilityInfos()
|
||||
{
|
||||
FAssetRegistryModule& AssetRegistryModule = FModuleManager::LoadModuleChecked<FAssetRegistryModule>("AssetRegistry");
|
||||
TArray<FAssetData> AssetData;
|
||||
FARFilter Filter;
|
||||
Filter.ClassNames.Add(UAbilityInfo::StaticClass()->GetFName());
|
||||
//Filter.PackagePaths.Add("/Content/Assets/Abilities");
|
||||
AssetRegistryModule.Get().GetAssets(Filter, AssetData);
|
||||
|
||||
for(auto& d : AssetData)
|
||||
{
|
||||
UAbilityInfo* info = Cast<UAbilityInfo>(d.GetAsset());
|
||||
if(info)
|
||||
{
|
||||
GPRINT("Fixing up " + info->GetName());
|
||||
//info->actionType = info->isToggleAbility ? EAbilityActionType::Toggle : EAbilityActionType::Normal;
|
||||
//info->abilityCategory = EAbilityCategory::Unassigned;
|
||||
//info->Modify();
|
||||
}
|
||||
}
|
||||
}
|
||||
void UFixupUtility::FixAICastRanges(const FString& filter, float setRange, bool evenIfNotZero)
|
||||
{
|
||||
FAssetRegistryModule& AssetRegistryModule = FModuleManager::LoadModuleChecked<FAssetRegistryModule>("AssetRegistry");
|
||||
TArray<FAssetData> AssetData;
|
||||
FARFilter Filter;
|
||||
Filter.ClassNames.Add(UAbilityInfo::StaticClass()->GetFName());
|
||||
//Filter.PackagePaths.Add("/Content/Assets/Abilities/Boss");
|
||||
if(filter.Len() > 0)
|
||||
{
|
||||
Filter.PackagePaths.Add(*filter);
|
||||
}
|
||||
AssetRegistryModule.Get().GetAssets(Filter, AssetData);
|
||||
|
||||
for(auto& d : AssetData)
|
||||
{
|
||||
UAbilityInfo* info = Cast<UAbilityInfo>(d.GetAsset());
|
||||
if(info)
|
||||
{
|
||||
GPRINT("Fixing up AIRange on " + info->GetName());
|
||||
if(info->AICastRange == 0 || evenIfNotZero)
|
||||
{
|
||||
info->AICastRange = setRange;
|
||||
info->Modify();
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
void UFixupUtility::FixupSpawners()
|
||||
{
|
||||
UWorld* world = GEngine->GetWorld();
|
||||
if(world)
|
||||
{
|
||||
for(TActorIterator<ASpawnerBase> it(world); it; ++it)
|
||||
{
|
||||
ASpawnerBase* sp = *it;
|
||||
sp->displayMesh->SetRelativeLocation(FVector(0.0f, 0.0f, 0.0f));
|
||||
}
|
||||
}
|
||||
}
|
||||
void UFixupUtility::CrashTheEditor()
|
||||
{
|
||||
uint32* funPointer = (uint32*)0x1;
|
||||
GPRINT(*funPointer);
|
||||
}
|
||||
25
Source/UnrealProject/Doodads/FixupUtility.h
Normal file
25
Source/UnrealProject/Doodads/FixupUtility.h
Normal file
@@ -0,0 +1,25 @@
|
||||
// Project Lab - NHTV Igad
|
||||
|
||||
#pragma once
|
||||
|
||||
#include "GlobalEditorUtilityBase.h"
|
||||
#include "FixupUtility.generated.h"
|
||||
|
||||
/**
|
||||
*
|
||||
*/
|
||||
UCLASS()
|
||||
class UNREALPROJECT_API UFixupUtility : public UGlobalEditorUtilityBase
|
||||
{
|
||||
GENERATED_BODY()
|
||||
|
||||
public:
|
||||
UFUNCTION(BlueprintCallable, Category="HACKS")
|
||||
void FixupAbilityInfos();
|
||||
UFUNCTION(BlueprintCallable, Category = "HACKS")
|
||||
void FixAICastRanges(const FString& filter, float setRange, bool evenIfNotZero = false);
|
||||
UFUNCTION(BlueprintCallable, Category = "HACKS")
|
||||
void FixupSpawners();
|
||||
UFUNCTION(BlueprintCallable, Category = "HACKS")
|
||||
void CrashTheEditor();
|
||||
};
|
||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user