RealtimeStyleTransferRuntime/Source/LyraGame/Character/LyraCharacter.cpp

518 lines
17 KiB
C++
Raw Normal View History

2022-05-23 18:41:30 +00:00
// Copyright Epic Games, Inc. All Rights Reserved.
#include "LyraCharacter.h"
#include "LyraCharacterMovementComponent.h"
#include "LyraLogChannels.h"
#include "LyraGameplayTags.h"
#include "Character/LyraPawnExtensionComponent.h"
#include "Components/CapsuleComponent.h"
#include "Components/SkeletalMeshComponent.h"
#include "Components/InputComponent.h"
#include "Camera/LyraCameraComponent.h"
#include "AbilitySystem/LyraAbilitySystemComponent.h"
#include "Character/LyraHealthComponent.h"
#include "Player/LyraPlayerController.h"
#include "Player/LyraPlayerState.h"
#include "System/LyraSignificanceManager.h"
#include "Net/UnrealNetwork.h"
#include "TimerManager.h"
static FName NAME_LyraCharacterCollisionProfile_Capsule(TEXT("LyraPawnCapsule"));
static FName NAME_LyraCharacterCollisionProfile_Mesh(TEXT("LyraPawnMesh"));
ALyraCharacter::ALyraCharacter(const FObjectInitializer& ObjectInitializer)
: Super(ObjectInitializer.SetDefaultSubobjectClass<ULyraCharacterMovementComponent>(ACharacter::CharacterMovementComponentName))
{
// Avoid ticking characters if possible.
PrimaryActorTick.bCanEverTick = false;
PrimaryActorTick.bStartWithTickEnabled = false;
NetCullDistanceSquared = 900000000.0f;
UCapsuleComponent* CapsuleComp = GetCapsuleComponent();
check(CapsuleComp);
CapsuleComp->InitCapsuleSize(40.0f, 90.0f);
CapsuleComp->SetCollisionProfileName(NAME_LyraCharacterCollisionProfile_Capsule);
USkeletalMeshComponent* MeshComp = GetMesh();
check(MeshComp);
MeshComp->SetRelativeRotation(FRotator(0.0f, -90.0f, 0.0f)); // Rotate mesh to be X forward since it is exported as Y forward.
MeshComp->SetCollisionProfileName(NAME_LyraCharacterCollisionProfile_Mesh);
ULyraCharacterMovementComponent* LyraMoveComp = CastChecked<ULyraCharacterMovementComponent>(GetCharacterMovement());
LyraMoveComp->GravityScale = 1.0f;
LyraMoveComp->MaxAcceleration = 2400.0f;
LyraMoveComp->BrakingFrictionFactor = 1.0f;
LyraMoveComp->BrakingFriction = 6.0f;
LyraMoveComp->GroundFriction = 8.0f;
LyraMoveComp->BrakingDecelerationWalking = 1400.0f;
LyraMoveComp->bUseControllerDesiredRotation = false;
LyraMoveComp->bOrientRotationToMovement = false;
LyraMoveComp->RotationRate = FRotator(0.0f, 720.0f, 0.0f);
LyraMoveComp->bAllowPhysicsRotationDuringAnimRootMotion = false;
LyraMoveComp->GetNavAgentPropertiesRef().bCanCrouch = true;
LyraMoveComp->bCanWalkOffLedgesWhenCrouching = true;
LyraMoveComp->SetCrouchedHalfHeight(65.0f);
PawnExtComponent = CreateDefaultSubobject<ULyraPawnExtensionComponent>(TEXT("PawnExtensionComponent"));
PawnExtComponent->OnAbilitySystemInitialized_RegisterAndCall(FSimpleMulticastDelegate::FDelegate::CreateUObject(this, &ThisClass::OnAbilitySystemInitialized));
PawnExtComponent->OnAbilitySystemUninitialized_Register(FSimpleMulticastDelegate::FDelegate::CreateUObject(this, &ThisClass::OnAbilitySystemUninitialized));
HealthComponent = CreateDefaultSubobject<ULyraHealthComponent>(TEXT("HealthComponent"));
HealthComponent->OnDeathStarted.AddDynamic(this, &ThisClass::OnDeathStarted);
HealthComponent->OnDeathFinished.AddDynamic(this, &ThisClass::OnDeathFinished);
CameraComponent = CreateDefaultSubobject<ULyraCameraComponent>(TEXT("CameraComponent"));
CameraComponent->SetRelativeLocation(FVector(-300.0f, 0.0f, 75.0f));
bUseControllerRotationPitch = false;
bUseControllerRotationYaw = true;
bUseControllerRotationRoll = false;
BaseEyeHeight = 80.0f;
CrouchedEyeHeight = 50.0f;
}
void ALyraCharacter::PreInitializeComponents()
{
Super::PreInitializeComponents();
}
void ALyraCharacter::BeginPlay()
{
Super::BeginPlay();
UWorld* World = GetWorld();
const bool bRegisterWithSignificanceManager = !IsNetMode(NM_DedicatedServer);
if (bRegisterWithSignificanceManager)
{
if (ULyraSignificanceManager* SignificanceManager = USignificanceManager::Get<ULyraSignificanceManager>(World))
{
//@TODO: SignificanceManager->RegisterObject(this, (EFortSignificanceType)SignificanceType);
}
}
}
void ALyraCharacter::EndPlay(const EEndPlayReason::Type EndPlayReason)
{
Super::EndPlay(EndPlayReason);
UWorld* World = GetWorld();
const bool bRegisterWithSignificanceManager = !IsNetMode(NM_DedicatedServer);
if (bRegisterWithSignificanceManager)
{
if (ULyraSignificanceManager* SignificanceManager = USignificanceManager::Get<ULyraSignificanceManager>(World))
{
SignificanceManager->UnregisterObject(this);
}
}
}
void ALyraCharacter::Reset()
{
DisableMovementAndCollision();
K2_OnReset();
UninitAndDestroy();
}
void ALyraCharacter::GetLifetimeReplicatedProps(TArray< FLifetimeProperty >& OutLifetimeProps) const
{
Super::GetLifetimeReplicatedProps(OutLifetimeProps);
DOREPLIFETIME_CONDITION(ThisClass, ReplicatedAcceleration, COND_SimulatedOnly);
DOREPLIFETIME(ThisClass, MyTeamID)
}
void ALyraCharacter::PreReplication(IRepChangedPropertyTracker& ChangedPropertyTracker)
{
Super::PreReplication(ChangedPropertyTracker);
if (UCharacterMovementComponent* MovementComponent = GetCharacterMovement())
{
// Compress Acceleration: XY components as direction + magnitude, Z component as direct value
const double MaxAccel = MovementComponent->MaxAcceleration;
const FVector CurrentAccel = MovementComponent->GetCurrentAcceleration();
double AccelXYRadians, AccelXYMagnitude;
FMath::CartesianToPolar(CurrentAccel.X, CurrentAccel.Y, AccelXYMagnitude, AccelXYRadians);
ReplicatedAcceleration.AccelXYRadians = FMath::FloorToInt((AccelXYRadians / TWO_PI) * 255.0); // [0, 2PI] -> [0, 255]
ReplicatedAcceleration.AccelXYMagnitude = FMath::FloorToInt((AccelXYMagnitude / MaxAccel) * 255.0); // [0, MaxAccel] -> [0, 255]
ReplicatedAcceleration.AccelZ = FMath::FloorToInt((CurrentAccel.Z / MaxAccel) * 127.0); // [-MaxAccel, MaxAccel] -> [-127, 127]
}
}
void ALyraCharacter::NotifyControllerChanged()
{
const FGenericTeamId OldTeamId = GetGenericTeamId();
Super::NotifyControllerChanged();
// Update our team ID based on the controller
if (HasAuthority() && (Controller != nullptr))
{
if (ILyraTeamAgentInterface* ControllerWithTeam = Cast<ILyraTeamAgentInterface>(Controller))
{
MyTeamID = ControllerWithTeam->GetGenericTeamId();
ConditionalBroadcastTeamChanged(this, OldTeamId, MyTeamID);
}
}
}
ALyraPlayerController* ALyraCharacter::GetLyraPlayerController() const
{
return CastChecked<ALyraPlayerController>(Controller, ECastCheckedType::NullAllowed);
}
ALyraPlayerState* ALyraCharacter::GetLyraPlayerState() const
{
return CastChecked<ALyraPlayerState>(GetPlayerState(), ECastCheckedType::NullAllowed);
}
ULyraAbilitySystemComponent* ALyraCharacter::GetLyraAbilitySystemComponent() const
{
return Cast<ULyraAbilitySystemComponent>(GetAbilitySystemComponent());
}
UAbilitySystemComponent* ALyraCharacter::GetAbilitySystemComponent() const
{
return PawnExtComponent->GetLyraAbilitySystemComponent();
}
void ALyraCharacter::OnAbilitySystemInitialized()
{
ULyraAbilitySystemComponent* LyraASC = GetLyraAbilitySystemComponent();
check(LyraASC);
HealthComponent->InitializeWithAbilitySystem(LyraASC);
InitializeGameplayTags();
}
void ALyraCharacter::OnAbilitySystemUninitialized()
{
HealthComponent->UninitializeFromAbilitySystem();
}
void ALyraCharacter::PossessedBy(AController* NewController)
{
const FGenericTeamId OldTeamID = MyTeamID;
Super::PossessedBy(NewController);
PawnExtComponent->HandleControllerChanged();
// Grab the current team ID and listen for future changes
if (ILyraTeamAgentInterface* ControllerAsTeamProvider = Cast<ILyraTeamAgentInterface>(NewController))
{
MyTeamID = ControllerAsTeamProvider->GetGenericTeamId();
ControllerAsTeamProvider->GetTeamChangedDelegateChecked().AddDynamic(this, &ThisClass::OnControllerChangedTeam);
}
ConditionalBroadcastTeamChanged(this, OldTeamID, MyTeamID);
}
void ALyraCharacter::UnPossessed()
{
AController* const OldController = Controller;
// Stop listening for changes from the old controller
const FGenericTeamId OldTeamID = MyTeamID;
if (ILyraTeamAgentInterface* ControllerAsTeamProvider = Cast<ILyraTeamAgentInterface>(OldController))
{
ControllerAsTeamProvider->GetTeamChangedDelegateChecked().RemoveAll(this);
}
Super::UnPossessed();
PawnExtComponent->HandleControllerChanged();
// Determine what the new team ID should be afterwards
MyTeamID = DetermineNewTeamAfterPossessionEnds(OldTeamID);
ConditionalBroadcastTeamChanged(this, OldTeamID, MyTeamID);
}
void ALyraCharacter::OnRep_Controller()
{
Super::OnRep_Controller();
PawnExtComponent->HandleControllerChanged();
}
void ALyraCharacter::OnRep_PlayerState()
{
Super::OnRep_PlayerState();
PawnExtComponent->HandlePlayerStateReplicated();
}
void ALyraCharacter::SetupPlayerInputComponent(UInputComponent* PlayerInputComponent)
{
Super::SetupPlayerInputComponent(PlayerInputComponent);
PawnExtComponent->SetupPlayerInputComponent();
}
void ALyraCharacter::InitializeGameplayTags()
{
// Clear tags that may be lingering on the ability system from the previous pawn.
if (ULyraAbilitySystemComponent* LyraASC = GetLyraAbilitySystemComponent())
{
const FLyraGameplayTags& GameplayTags = FLyraGameplayTags::Get();
for (const TPair<uint8, FGameplayTag>& TagMapping : GameplayTags.MovementModeTagMap)
{
if (TagMapping.Value.IsValid())
{
LyraASC->SetLooseGameplayTagCount(TagMapping.Value, 0);
}
}
for (const TPair<uint8, FGameplayTag>& TagMapping : GameplayTags.CustomMovementModeTagMap)
{
if (TagMapping.Value.IsValid())
{
LyraASC->SetLooseGameplayTagCount(TagMapping.Value, 0);
}
}
ULyraCharacterMovementComponent* LyraMoveComp = CastChecked<ULyraCharacterMovementComponent>(GetCharacterMovement());
SetMovementModeTag(LyraMoveComp->MovementMode, LyraMoveComp->CustomMovementMode, true);
}
}
void ALyraCharacter::GetOwnedGameplayTags(FGameplayTagContainer& TagContainer) const
{
if (const ULyraAbilitySystemComponent* LyraASC = GetLyraAbilitySystemComponent())
{
LyraASC->GetOwnedGameplayTags(TagContainer);
}
}
bool ALyraCharacter::HasMatchingGameplayTag(FGameplayTag TagToCheck) const
{
if (const ULyraAbilitySystemComponent* LyraASC = GetLyraAbilitySystemComponent())
{
return LyraASC->HasMatchingGameplayTag(TagToCheck);
}
return false;
}
bool ALyraCharacter::HasAllMatchingGameplayTags(const FGameplayTagContainer& TagContainer) const
{
if (const ULyraAbilitySystemComponent* LyraASC = GetLyraAbilitySystemComponent())
{
return LyraASC->HasAllMatchingGameplayTags(TagContainer);
}
return false;
}
bool ALyraCharacter::HasAnyMatchingGameplayTags(const FGameplayTagContainer& TagContainer) const
{
if (const ULyraAbilitySystemComponent* LyraASC = GetLyraAbilitySystemComponent())
{
return LyraASC->HasAnyMatchingGameplayTags(TagContainer);
}
return false;
}
void ALyraCharacter::FellOutOfWorld(const class UDamageType& dmgType)
{
HealthComponent->DamageSelfDestruct(/*bFellOutOfWorld=*/ true);
}
void ALyraCharacter::OnDeathStarted(AActor*)
{
DisableMovementAndCollision();
}
void ALyraCharacter::OnDeathFinished(AActor*)
{
GetWorld()->GetTimerManager().SetTimerForNextTick(this, &ThisClass::DestroyDueToDeath);
}
void ALyraCharacter::DisableMovementAndCollision()
{
if (Controller)
{
Controller->SetIgnoreMoveInput(true);
}
UCapsuleComponent* CapsuleComp = GetCapsuleComponent();
check(CapsuleComp);
CapsuleComp->SetCollisionEnabled(ECollisionEnabled::NoCollision);
CapsuleComp->SetCollisionResponseToAllChannels(ECR_Ignore);
ULyraCharacterMovementComponent* LyraMoveComp = CastChecked<ULyraCharacterMovementComponent>(GetCharacterMovement());
LyraMoveComp->StopMovementImmediately();
LyraMoveComp->DisableMovement();
}
void ALyraCharacter::DestroyDueToDeath()
{
K2_OnDeathFinished();
UninitAndDestroy();
}
void ALyraCharacter::UninitAndDestroy()
{
if (GetLocalRole() == ROLE_Authority)
{
DetachFromControllerPendingDestroy();
SetLifeSpan(0.1f);
}
// Uninitialize the ASC if we're still the avatar actor (otherwise another pawn already did it when they became the avatar actor)
if (ULyraAbilitySystemComponent* LyraASC = GetLyraAbilitySystemComponent())
{
if (LyraASC->GetAvatarActor() == this)
{
PawnExtComponent->UninitializeAbilitySystem();
}
}
SetActorHiddenInGame(true);
}
void ALyraCharacter::OnMovementModeChanged(EMovementMode PrevMovementMode, uint8 PreviousCustomMode)
{
Super::OnMovementModeChanged(PrevMovementMode, PreviousCustomMode);
ULyraCharacterMovementComponent* LyraMoveComp = CastChecked<ULyraCharacterMovementComponent>(GetCharacterMovement());
SetMovementModeTag(PrevMovementMode, PreviousCustomMode, false);
SetMovementModeTag(LyraMoveComp->MovementMode, LyraMoveComp->CustomMovementMode, true);
}
void ALyraCharacter::SetMovementModeTag(EMovementMode MovementMode, uint8 CustomMovementMode, bool bTagEnabled)
{
if (ULyraAbilitySystemComponent* LyraASC = GetLyraAbilitySystemComponent())
{
const FLyraGameplayTags& GameplayTags = FLyraGameplayTags::Get();
const FGameplayTag* MovementModeTag = nullptr;
if (MovementMode == MOVE_Custom)
{
MovementModeTag = GameplayTags.CustomMovementModeTagMap.Find(CustomMovementMode);
}
else
{
MovementModeTag = GameplayTags.MovementModeTagMap.Find(MovementMode);
}
if (MovementModeTag && MovementModeTag->IsValid())
{
LyraASC->SetLooseGameplayTagCount(*MovementModeTag, (bTagEnabled ? 1 : 0));
}
}
}
void ALyraCharacter::ToggleCrouch()
{
const ULyraCharacterMovementComponent* LyraMoveComp = CastChecked<ULyraCharacterMovementComponent>(GetCharacterMovement());
if (bIsCrouched || LyraMoveComp->bWantsToCrouch)
{
UnCrouch();
}
else if (LyraMoveComp->IsMovingOnGround())
{
Crouch();
}
}
void ALyraCharacter::OnStartCrouch(float HalfHeightAdjust, float ScaledHalfHeightAdjust)
{
if (ULyraAbilitySystemComponent* LyraASC = GetLyraAbilitySystemComponent())
{
LyraASC->SetLooseGameplayTagCount(FLyraGameplayTags::Get().Status_Crouching, 1);
}
Super::OnStartCrouch(HalfHeightAdjust, ScaledHalfHeightAdjust);
}
void ALyraCharacter::OnEndCrouch(float HalfHeightAdjust, float ScaledHalfHeightAdjust)
{
if (ULyraAbilitySystemComponent* LyraASC = GetLyraAbilitySystemComponent())
{
LyraASC->SetLooseGameplayTagCount(FLyraGameplayTags::Get().Status_Crouching, 0);
}
Super::OnEndCrouch(HalfHeightAdjust, ScaledHalfHeightAdjust);
}
bool ALyraCharacter::CanJumpInternal_Implementation() const
{
// same as ACharacter's implementation but without the crouch check
return JumpIsAllowedInternal();
}
void ALyraCharacter::OnRep_ReplicatedAcceleration()
{
if (ULyraCharacterMovementComponent* LyraMovementComponent = Cast<ULyraCharacterMovementComponent>(GetCharacterMovement()))
{
// Decompress Acceleration
const double MaxAccel = LyraMovementComponent->MaxAcceleration;
const double AccelXYMagnitude = double(ReplicatedAcceleration.AccelXYMagnitude) * MaxAccel / 255.0; // [0, 255] -> [0, MaxAccel]
const double AccelXYRadians = double(ReplicatedAcceleration.AccelXYRadians) * TWO_PI / 255.0; // [0, 255] -> [0, 2PI]
FVector UnpackedAcceleration(FVector::ZeroVector);
FMath::PolarToCartesian(AccelXYMagnitude, AccelXYRadians, UnpackedAcceleration.X, UnpackedAcceleration.Y);
UnpackedAcceleration.Z = double(ReplicatedAcceleration.AccelZ) * MaxAccel / 127.0; // [-127, 127] -> [-MaxAccel, MaxAccel]
LyraMovementComponent->SetReplicatedAcceleration(UnpackedAcceleration);
}
}
void ALyraCharacter::SetGenericTeamId(const FGenericTeamId& NewTeamID)
{
if (GetController() == nullptr)
{
if (HasAuthority())
{
const FGenericTeamId OldTeamID = MyTeamID;
MyTeamID = NewTeamID;
ConditionalBroadcastTeamChanged(this, OldTeamID, MyTeamID);
}
else
{
UE_LOG(LogLyraTeams, Error, TEXT("You can't set the team ID on a character (%s) except on the authority"), *GetPathNameSafe(this));
}
}
else
{
UE_LOG(LogLyraTeams, Error, TEXT("You can't set the team ID on a possessed character (%s); it's driven by the associated controller"), *GetPathNameSafe(this));
}
}
FGenericTeamId ALyraCharacter::GetGenericTeamId() const
{
return MyTeamID;
}
FOnLyraTeamIndexChangedDelegate* ALyraCharacter::GetOnTeamIndexChangedDelegate()
{
return &OnTeamChangedDelegate;
}
void ALyraCharacter::OnControllerChangedTeam(UObject* TeamAgent, int32 OldTeam, int32 NewTeam)
{
const FGenericTeamId MyOldTeamID = MyTeamID;
MyTeamID = IntegerToGenericTeamId(NewTeam);
ConditionalBroadcastTeamChanged(this, MyOldTeamID, MyTeamID);
}
void ALyraCharacter::OnRep_MyTeamID(FGenericTeamId OldTeamID)
{
ConditionalBroadcastTeamChanged(this, OldTeamID, MyTeamID);
}