GASATHON/Project/Source/Gasa/Game/GasaPlayerController.cpp
2024-04-26 21:36:09 -04:00

461 lines
13 KiB
C++

#include "GasaPlayerController.h"
#include "GasaPlayerController_Inlines.h"
#include "Networking/GasaNetLibrary_Inlines.h"
#include "AbilitySystemComponent.h"
#include "DrawDebugHelpers.h"
#include "Engine/LocalPlayer.h"
#include "EnhancedInputComponent.h"
#include "EnhancedInputSubsystems.h"
#include "Characters/GasaCharacter.h"
#include "Characters/PlayerCharacter.h"
#include "Components/CapsuleComponent.h"
#include "Interfaces/NetworkPredictionInterface.h"
#include "Kismet/KismetSystemLibrary.h"
#include "Net/UnrealNetwork.h"
#include "GameFramework/PawnMovementComponent.h"
#include "GasaDevOptions.h"
#include "GasaGameInstance.h"
#include "GasaGameState.h"
#include "GasaPlayerState.h"
#include "AbilitySystem/GasaAbilitySystemComponent.h"
#include "Actors/CameraMount.h"
#include "UI/GasaHUD.h"
#include "UI/WidgetController.h"
using namespace Gasa;
AGasaPlayerController::AGasaPlayerController()
{
PrimaryActorTick.bCanEverTick = true;
bAutoManageActiveCameraTarget = false;
// Replication
bReplicates = true;
}
void AGasaPlayerController::OnSeamlessTravelStart()
{
}
#pragma region GameFramework
void AGasaPlayerController::Client_CheckIfOwnerReady()
{
if (IsServer())
return;
UGasaGameInstance* GI = GetGameInstance<UGasaGameInstance>();
if ( ! GI->IsGameFrameworkInitialized() || PlayerState == NULL || ! IsValid(GetPawn()))
return;
NetOwner_OnReady();
}
void AGasaPlayerController::NetOwner_OnReady()
{
NetLog("Net Owner of controller is ready to play.");
if ( ! IsNetOwner() || bNetOwnerReady)
return;
BP_NetOwner_OnReady();
Event_NetOwner_OnReady.Broadcast(this);
bNetOwnerReady = true;
AGasaGameState* GS = Cast<AGasaGameState>(GetWorld()->GetGameState());
if (GS)
GS->NotifyPlayerPawnReady(GetPawn());
if (IsClient())
ServerRPC_R_NotifyOwningClientReady();
Cam = GetWorld()->SpawnActor<ACameraMount>(GetDevOptions()->Template_PlayerCamera.Get(), FActorSpawnParameters() );
SetViewTarget(Cam);
AGasaPlayerState* PS = GetPlayerState();
APlayerCharacter* PlayerChar = GetPawn<APlayerCharacter>();
{
PlayerChar->AbilitySystem = PS->AbilitySystem;
PlayerChar->Attributes = PS->Attributes;
PS->AbilitySystem->InitAbilityActorInfo(PS, PlayerChar);
Cast<UGasaAbilitySystemComp>(PS->AbilitySystem)->OnAbilityActorInfoSet();
}
Cam->AttachToActor(PlayerChar, FAttachmentTransformRules::KeepRelativeTransform);
}
void AGasaPlayerController::OnGameFrameworkInitialized()
{
NetLog("Received game framework initialization.");
if (IsNetOwner())
{
Server_SetNetOwner_GameFrameworkInitialized();
Client_CheckIfOwnerReady();
}
AGasaGameState* GS = GetGameState(this);
NullGuard_DEV(GS, Log, "OnGameFrameworkInitialized: GS is null");
GS->Event_OnSeamlessTravelStart.AddDynamic( this, & ThisClass::OnSeamlessTravelStart );
BP_OnGameFrameworkInitialized();
}
void AGasaPlayerController::OnPawnReady()
{
NetLog("Player is ready.");
// Originally: Super::OnPossess(PawnToPossess);
{
ChangeState(NAME_Playing);
if (bAutoManageActiveCameraTarget)
{
AutoManageActiveCameraTarget(GetPawn());
ResetCameraMode();
}
}
// Override this and add your own conditions...
BP_OnPawnReady();
if (IsServer() && IsNetOwner())
{
// The server host doesn't have to wait for the player state to replicate.
NetOwner_OnReady();
}
}
void AGasaPlayerController::Server_SetupOnPawnReadyBinds(APawn* PawnToBindTo)
{
if (IsClient())
return;
#if 0
if (PawnToBindTo->IsA(AGasaPawn::StaticClass()))
{
Cast<AGasaPawn>(PawnToBindTo)->Event_OnPawnReady.AddUniqueDynamic(this, & ThisClass::OnPawnReady);
}
else
#endif
if (PawnToBindTo->IsA(AGasaCharacter::StaticClass()))
{
Cast<AGasaCharacter>(PawnToBindTo)->Event_OnPawnReady.AddUniqueDynamic(this, & ThisClass::OnPawnReady);
}
}
void AGasaPlayerController::Server_SetNetOwner_GameFrameworkInitialized()
{
if (IsClient())
{
ServerRPC_R_SetNetOwner_GameFrameworkInitialized();
return;
}
bNetOwner_GameFrameworkInitialized = true;
if (Event_NetOwner_OnGameFrameworkInitialized.IsBound())
{
Event_NetOwner_OnGameFrameworkInitialized.Broadcast(this);
Event_NetOwner_OnGameFrameworkInitialized.Clear();
}
}
void AGasaPlayerController::ServerRPC_R_NotifyOwningClientReady_Implementation()
{
NetLog("Net Owner Ready: Notified via RPC.");
BP_NetOwner_OnReady();
bNetOwnerReady = true;
Event_NetOwner_OnReady.Broadcast(this);
Event_NetOwner_OnReady.Clear();
AGasaGameState* GS = GetGameState(this);
if (GS)
GS->NotifyPlayerPawnReady(GetPawn());
}
void AGasaPlayerController::ServerRPC_R_SetNetOwner_GameFrameworkInitialized_Implementation()
{
Server_SetNetOwner_GameFrameworkInitialized();
}
#pragma endregion GameFramework
#pragma region Input
void AGasaPlayerController::Move(FInputActionValue const& ActionValue)
{
APawn* pawn = GetPawn<APawn>();
if (pawn == nullptr )
return;
// Note(Ed): I did the follow optimization for practice, they are completely unnecessary for this context.
#if 0
FVector2D AxisV = ActionValue.Get<FVector2D>();
FRotator ControlRot = GetControlRotation();
FRotator YawRot = FRotator(0.f, ControlRot.Yaw, 0.f);
FVector FwdDir = FRotationMatrix(YawRot).GetUnitAxis(EAxis::X);
FVector RightDir = FRotationMatrix(YawRot).GetUnitAxis(EAxis::Y);
PPawn->AddMovementInput(FwdDir, AxisV.Y);
PPawn->AddMovementInput(RightDir, AxisV.X);
#else
FVector2f AxisV = FVector2f(ActionValue.Get<FVector2D>());
FQuat4f // FQuat isomorphic to FRotor (Hypothetical Def)
ControlRotor = FQuat4f(GetControlRotation().Quaternion());
// ControlRotor.Normalize(); // The Quaternion should always be a versor with UE...
FVector3f HorizontalForward = ControlRotor.RotateVector(FVector3f::ForwardVector);
// HorizontalForward.Normalize();
// TODO(Ed): Profile which is faster just to know... (atan2 vs FindBetweenVectors)
// HorizontalForward.Z = 0;
// FQuat4f
// YawRotor = FQuat4f::FindBetweenVectors(FVector3f::ForwardVector, HorizontalForward);
// YawRotor.Normalize(); // The Quaternion should always be a versor with UE...
// Need only one axis of rotation so this might be a possible optimization
float YawAngle = FMath::Atan2(HorizontalForward.Y, HorizontalForward.X);
FQuat4f YawRotor = FQuat4f(FVector3f::UpVector, YawAngle);
// Rotate the combined input by the yaw rotor to get the movement direction
FVector MoveDir = (FVector) YawRotor.RotateVector( FVector3f(AxisV.Y, AxisV.X, 0.f));
pawn->AddMovementInput( MoveDir );
#endif
}
#pragma endregion Input
#pragma region PlayerController
bool AGasaPlayerController::CanRestartPlayer()
{
bool BaseCheck =
PlayerState &&
!PlayerState->IsOnlyASpectator() &&
HasClientLoadedCurrentWorld() &&
PendingSwapConnection == NULL
;
return BaseCheck && bNetOwner_GameFrameworkInitialized;
}
void AGasaPlayerController::ClientSetHUD_Implementation(TSubclassOf<AHUD> NewHUDClass)
{
Super::ClientSetHUD_Implementation(NewHUDClass);
AGasaPlayerState* PS = GetPlayerState();
AGasaHUD* HUD = GetHUD<AGasaHUD>();
FWidgetControllerData Data = { this, PS, PS->AbilitySystem, PS->Attributes };
HUD->InitHostWidget(& Data);
}
void AGasaPlayerController::ClientUpdateLevelStreamingStatus_Implementation(FName PackageName, bool bNewShouldBeLoaded, bool bNewShouldBeVisible,
bool bNewShouldBlockOnLoad, int32 LODIndex, FNetLevelVisibilityTransactionId TransactionId, bool bNewShouldBlockOnUnload)
{
Super::ClientUpdateLevelStreamingStatus_Implementation(PackageName, bNewShouldBeLoaded, bNewShouldBeVisible, bNewShouldBlockOnLoad, LODIndex,
TransactionId, bNewShouldBlockOnUnload);
NetLog("ClientUpdateLevelStreamingStatus");
NetLog(FString("PackageName : ") + PackageName.ToString());
NetLog(FString("NewShouldBeLoaded : ") + FString(bNewShouldBeLoaded ? "true" : "false"));
NetLog(FString("NewShouldBeVisible : ") + FString(bNewShouldBeVisible ? "true" : "false"));
NetLog(FString("bNewShouldBlockOnLoad : ") + FString(bNewShouldBlockOnLoad ? "true" : "false"));
NetLog(FString("bNewShouldBlockOnUnload: ") + FString(bNewShouldBlockOnUnload ? "true" : "false"));
NetLog(FString("LODIndex : ") + FString::FromInt( LODIndex ));
}
// TODO(Ed): We need to setup Net Slime...
void AGasaPlayerController::OnPossess(APawn* PawnPossesed)
{
// Super::OnPossess(PawnPossesed);
{
if (PawnPossesed && (PlayerState == NULL || !PlayerState->IsOnlyASpectator()) )
{
// ====================================================================Originally: Super::OnPossess(PawnToPossess);
const bool bNewPawn = (GetPawn() != PawnPossesed);
if (GetPawn() && bNewPawn)
UnPossess();
if (PawnPossesed->Controller != NULL)
PawnPossesed->Controller->UnPossess();
PawnPossesed->PossessedBy(this);
// update rotation to match possessed pawn's rotation
SetControlRotation( PawnPossesed->GetActorRotation() );
SetPawn(PawnPossesed);
check(GetPawn() != NULL);
if (GetPawn() && GetPawn()->PrimaryActorTick.bStartWithTickEnabled)
GetPawn()->SetActorTickEnabled(true);
INetworkPredictionInterface* NetworkPredictionInterface = GetPawn()
? Cast<INetworkPredictionInterface>(GetPawn()->GetMovementComponent())
: nullptr;
if (NetworkPredictionInterface)
NetworkPredictionInterface->ResetPredictionData_Server();
AcknowledgedPawn = NULL;
// Local PCs will have the Restart() triggered right away in ClientRestart (via PawnClientRestart()), but the server should call Restart() locally for remote PCs.
// We're really just trying to avoid calling Restart() multiple times.
if (!IsLocalPlayerController())
GetPawn()->Restart();
ClientRestart(GetPawn());
// Moved to: void AGasaPlayerController::OnPawnReady
#if 0
ChangeState( NAME_Playing );
if (bAutoManageActiveCameraTarget)
{
AutoManageActiveCameraTarget(GetPawn());
ResetCameraMode();
}
#endif
//==========================================================End of=================== Originally: Super::OnPossess(PawnToPossess);
NetLog("OnPossess");
Server_SetupOnPawnReadyBinds(PawnPossesed);
Event_OnPawnPossessed.Broadcast();
}
}
}
void AGasaPlayerController::OnRep_Pawn()
{
Super::OnRep_Pawn();
NetLog("OnRep_Pawn");
Client_CheckIfOwnerReady();
}
void AGasaPlayerController::OnUnPossess()
{
Super::OnUnPossess();
}
void AGasaPlayerController::PlayerTick(float DeltaTime)
{
Super::PlayerTick(DeltaTime);
// Cursor Trace
for (int32 do_once = 0; do_once != 1; ++ do_once)
{
FHitResult CursorHit;
GetHitResultUnderCursor(ECC_Pawn, false, CursorHit);
if (! CursorHit.bBlockingHit)
break;
HoverPrev = HoverCurr;
HoverCurr = Cast<AGasaCharacter>(CursorHit.GetActor());
if (HoverPrev == nullptr)
{
// We didn't have a prev to de-highlight so we just need to highlight newly detected character.
if (HoverCurr)
HoverCurr->Highlight();
// No matter what we need to not go to the next case as there is no previous.
break;
}
//else Previous is valid...
// We are no longer hovering the previous with no new character, we just need to de-highlight previous.
if ( HoverCurr == nullptr )
HoverPrev->Dehighlight();
// We had a prev and curr change between frames. They both don't match; we need to switch highlighting.
else if ( HoverPrev != HoverCurr )
{
HoverPrev->Dehighlight();
HoverCurr->Highlight();
}
}
}
void AGasaPlayerController::PostSeamlessTravel()
{
Super::PostSeamlessTravel();
}
void AGasaPlayerController::SetupInputComponent()
{
Super::SetupInputComponent();
UEnhancedInputComponent*
EIC = CastChecked<UEnhancedInputComponent>(InputComponent);
{
EIC->BindAction(IA_Move, ETriggerEvent::Triggered, this, &ThisClass::Move);
}
}
void AGasaPlayerController::SpawnDefaultHUD()
{
Super::SpawnDefaultHUD();
}
#pragma endregion PlayerController
#pragma region Actor
void AGasaPlayerController::BeginPlay()
{
Super::BeginPlay();
NetLog("BeginPlay");
UGasaGameInstance* GI = GetGameInstance<UGasaGameInstance>();
GI->Event_OnGameFrameworkInitialized.AddUniqueDynamic(this, & AGasaPlayerController::OnGameFrameworkInitialized);
GI->NotifyGameFrameworkClassReady(EGameFrameworkClassFlag::PlayerController);
if (IsLocalController())
{
check(IMC);
UEnhancedInputLocalPlayerSubsystem*
EILP_Subsystem = ULocalPlayer::GetSubsystem<UEnhancedInputLocalPlayerSubsystem>(GetLocalPlayer());
check(EILP_Subsystem);
EILP_Subsystem->AddMappingContext(IMC, 0);
{
bShowMouseCursor = true;
DefaultMouseCursor = EMouseCursor::Default;
FInputModeGameAndUI MouseMode;
MouseMode.SetLockMouseToViewportBehavior(EMouseLockMode::DoNotLock);
MouseMode.SetHideCursorDuringCapture(false);
SetInputMode(MouseMode);
}
}
}
void AGasaPlayerController::PostInitializeComponents()
{
Super::PostInitializeComponents();
}
void AGasaPlayerController::Tick(float DeltaSeconds)
{
Super::Tick(DeltaSeconds);
#if 0
switch (HighlightState)
{
case EHighlight::Disabled:
break;
case EHighlight::Enabled:
{
UCapsuleComponent* Capsule = GetCapsuleComponent();
UKismetSystemLibrary::DrawDebugCapsule(this
, Capsule->GetComponentLocation()
, Capsule->GetScaledCapsuleHalfHeight()
, Capsule->GetScaledCapsuleRadius()
, Capsule->GetComponentRotation()
, HighlightColor
, 0.f
, 1.f
);
}
break;
}
#endif
}
void AGasaPlayerController::GetLifetimeReplicatedProps(TArray<FLifetimeProperty>& OutLifetimeProps) const
{
Super::GetLifetimeReplicatedProps(OutLifetimeProps);
DOREPLIFETIME(AGasaPlayerController, bNetOwner_GameFrameworkInitialized);
}
#pragma endregion Actor