1335 lines
40 KiB
C++
1335 lines
40 KiB
C++
// Project Lab - NHTV Igad
|
|
|
|
#include "UnrealProject.h"
|
|
#include "DefaultGameInstance.h"
|
|
#include "DefaultGameState.h"
|
|
#include "KingOfTheHillGameMode.h"
|
|
#include "DefaultPlayer.h"
|
|
#include "DefaultPlayerState.h"
|
|
#include "DefaultPlayerController.h"
|
|
#include "CombatTextWidget.h"
|
|
#include "CharacterSettings.h"
|
|
#include "NetworkPlayer.h"
|
|
#include "NetworkGhost.h"
|
|
#include "NetworkSwitch.h"
|
|
#include "BaseSkillObject.h"
|
|
#include "PlayerSpawn.h"
|
|
#include "MiniMapWidget.h"
|
|
#include "IngameHUD.h"
|
|
#include "CombatTextWidget.h"
|
|
#include "AbilityInfo.h"
|
|
#include "HealthBar.h"
|
|
#include "LobbyMenu.h"
|
|
#include "ItemBase.h"
|
|
#include "SkillTreeWidget.h"
|
|
#include "IngameSkillTree.h"
|
|
#include "ButtonBarSwitcher.h"
|
|
#include "MenuScreenBase.h"
|
|
|
|
#include "SessionManager.h"
|
|
#include "PreCastAbilityEventGroup.h"
|
|
#include "MenuScreen.h"
|
|
#include "WidgetBlueprintLibrary.h"
|
|
|
|
// Use Inputmanager when not on playstation
|
|
#if PLATFORM_SPECIFIC_WIN == 0
|
|
#include "InputManager.hpp"
|
|
using namespace Input;
|
|
#endif
|
|
|
|
static UClass* HUDWidgetClass;
|
|
static UClass* characterClass;
|
|
static UClass* ghostClass;
|
|
static UClass* respawnWidgetClass;
|
|
static UClass* skilltreeWidgetClass;
|
|
static TSubclassOf<class UMenuScreen> menuWidgetClass;
|
|
static TSubclassOf<class UMenuPanel> ingameMenuPanelClass;
|
|
|
|
ADefaultPlayerController::ADefaultPlayerController(const FObjectInitializer& init)
|
|
: Super(init)
|
|
{
|
|
menuWidgetClass = ConstructorHelpers::FClassFinder<UMenuScreen>(TEXT("/Game/Assets/GUI/WEEGEE_Menu")).Class;
|
|
ingameMenuPanelClass = ConstructorHelpers::FClassFinder<UMenuPanel>(TEXT("/Game/Assets/GUI/Menus/WEEGEE_IngameSubMenu")).Class;
|
|
|
|
characterClassProperties = ConstructorHelpers::FObjectFinder<UCharacterClassPropertySet>(TEXT("/Game/Assets/Abilities/Classes")).Object;
|
|
respawnWidgetClass = ConstructorHelpers::FClassFinder<UUserWidget>(TEXT("/Game/Assets/GUI/WEEGEE_Respawn")).Class;
|
|
HUDWidgetClass = ConstructorHelpers::FClassFinder<UUserWidget>(TEXT("/Game/Assets/GUI/WEEGEE_HUD")).Class;
|
|
characterClass = ConstructorHelpers::FClassFinder<ANetworkPlayer>(TEXT("/Game/Assets/Blueprints/Creatures/BP_NetworkPlayer")).Class;
|
|
ghostClass = ConstructorHelpers::FClassFinder<ANetworkGhost> (TEXT("/Game/Assets/Blueprints/Creatures/BP_NetworkGhost")).Class;
|
|
skilltreeWidgetClass = ConstructorHelpers::FClassFinder<USkillTreeWidget> (TEXT("/Game/Assets/GUI/WEEGEE_SkillTree")).Class;
|
|
PrimaryActorTick.bCanEverTick = true;
|
|
|
|
m_framesSinceMenuFocus = 0;
|
|
m_rightMouseDown = false;
|
|
m_holdPosition = false;
|
|
m_skillTreeInitialized = false;
|
|
m_holdDirection = FVector();
|
|
m_coopModifier = false;
|
|
}
|
|
|
|
void ADefaultPlayerController::BeginPlay()
|
|
{
|
|
Super::BeginPlay();
|
|
|
|
if(IsLocalController())
|
|
{
|
|
m_hud = CreateWidget<UIngameHUD>(this, HUDWidgetClass);
|
|
m_hud->AddToViewport();
|
|
m_OnPawnChanged();
|
|
|
|
// Create skill tree widget
|
|
m_skillTreeWidget = CreateWidget<USkillTreeWidget>(this, skilltreeWidgetClass);
|
|
check(m_skillTreeWidget);
|
|
m_skillTreeWidget->AddToViewport();
|
|
|
|
// Build initial tree
|
|
m_skillTreeWidget->BuildFromState(setupState.skills);
|
|
|
|
// Load the skill tree
|
|
m_skillTreeWidget->SetIsInteractive(false);
|
|
m_skillTreeWidget->SetIsEnabled(true);
|
|
}
|
|
|
|
if(GetWorld()->IsPlayInEditor() && Role == ROLE_Authority)
|
|
{
|
|
// Load setup state from settings (slot 0)
|
|
UCharacterSettings* settings = Cast<UDefaultGameInstance>(GetGameInstance())->GetCharacterSettings();
|
|
if(settings->characterSaves.Num() > 0)
|
|
{
|
|
const FCharacterSave& save = settings->characterSaves[0];
|
|
setupState.customizations = save.characterCustomization;
|
|
setupState.skills = save.skillTreeState;
|
|
setupState.characterClass = save.characterClass;
|
|
}
|
|
|
|
// Handle setupState when not comming from seamless travel
|
|
OnRep_Setup();
|
|
}
|
|
|
|
this->bShowMouseCursor = true;
|
|
}
|
|
void ADefaultPlayerController::EndPlay(const EEndPlayReason::Type EndPlayReason)
|
|
{
|
|
Super::EndPlay(EndPlayReason);
|
|
|
|
if (m_hud)
|
|
m_hud->RemoveFromViewport();
|
|
|
|
if(IsValid(m_defaultPlayer))
|
|
{
|
|
m_defaultPlayer->Destroy();
|
|
}
|
|
if(m_respawnWidget)
|
|
{
|
|
m_respawnWidget->RemoveFromParent();
|
|
}
|
|
}
|
|
|
|
void ADefaultPlayerController::Destroyed()
|
|
{
|
|
Super::Destroyed();
|
|
|
|
#if PLATFORM_SPECIFIC_WIN == 0
|
|
GPRINT("Unbinding Input manager bindings on " + GetName());
|
|
InputManager* inputManager = InputManager::GetInstance();
|
|
|
|
inputManager->DeregisterCallback(IJP_TRIGGERRIGHT, IME_PRESSED, this, &ADefaultPlayerController::CastAbility0);
|
|
inputManager->DeregisterCallback(IJP_FACEDOWN, IME_PRESSED, this, &ADefaultPlayerController::CastAbility1);
|
|
inputManager->DeregisterCallback(IJP_FACELEFT, IME_PRESSED, this, &ADefaultPlayerController::CastAbility2);
|
|
inputManager->DeregisterCallback(IJP_FACEUP, IME_PRESSED, this, &ADefaultPlayerController::CastAbility3);
|
|
inputManager->DeregisterCallback(IJP_FACERIGHT, IME_PRESSED, this, &ADefaultPlayerController::CastAbility4);
|
|
inputManager->DeregisterCallback(IJP_TRIGGERRIGHT, IME_RELEASED, this, &ADefaultPlayerController::UnCastAbility0);
|
|
inputManager->DeregisterCallback(IJP_FACEDOWN, IME_RELEASED, this, &ADefaultPlayerController::UnCastAbility1);
|
|
inputManager->DeregisterCallback(IJP_FACELEFT, IME_RELEASED, this, &ADefaultPlayerController::UnCastAbility2);
|
|
inputManager->DeregisterCallback(IJP_FACEUP, IME_RELEASED, this, &ADefaultPlayerController::UnCastAbility3);
|
|
inputManager->DeregisterCallback(IJP_FACERIGHT, IME_RELEASED, this, &ADefaultPlayerController::UnCastAbility4);
|
|
inputManager->DeregisterCallback(IJP_START, IME_PRESSED, this, &ADefaultPlayerController::ToggleMenu);
|
|
inputManager->DeregisterCallback(IJP_SHOULDERLEFT, IME_PRESSED, this, &ADefaultPlayerController::ToggleSkillTree);
|
|
inputManager->DeregisterCallback(IJP_SHOULDERLEFT, IME_RELEASED, this, &ADefaultPlayerController::ToggleSkillTree);
|
|
inputManager->DeregisterCallback(IJP_TRIGGERLEFT, IME_PRESSED, this, &ADefaultPlayerController::CoopModifierOn);
|
|
inputManager->DeregisterCallback(IJP_TRIGGERLEFT, IME_RELEASED, this, &ADefaultPlayerController::CoopModifierOff);
|
|
#endif
|
|
}
|
|
|
|
void ADefaultPlayerController::SetupInputComponent()
|
|
{
|
|
if (GetWorld()->WorldType == EWorldType::Preview)
|
|
return;
|
|
|
|
Super::SetupInputComponent();
|
|
|
|
#if PLATFORM_SPECIFIC_WIN == 0
|
|
GPRINT("Creating Input manager bindings on " + GetName());
|
|
InputManager* inputManager = InputManager::GetInstance();
|
|
|
|
inputManager->RegisterCallback(IJP_TRIGGERRIGHT, IME_PRESSED, this, &ADefaultPlayerController::CastAbility0);
|
|
inputManager->RegisterCallback(IJP_FACEDOWN, IME_PRESSED, this, &ADefaultPlayerController::CastAbility1);
|
|
inputManager->RegisterCallback(IJP_FACELEFT, IME_PRESSED, this, &ADefaultPlayerController::CastAbility2);
|
|
inputManager->RegisterCallback(IJP_FACEUP, IME_PRESSED, this, &ADefaultPlayerController::CastAbility3);
|
|
inputManager->RegisterCallback(IJP_FACERIGHT, IME_PRESSED, this, &ADefaultPlayerController::CastAbility4);
|
|
inputManager->RegisterCallback(IJP_TRIGGERRIGHT, IME_RELEASED, this, &ADefaultPlayerController::UnCastAbility0);
|
|
inputManager->RegisterCallback(IJP_FACEDOWN, IME_RELEASED, this, &ADefaultPlayerController::UnCastAbility1);
|
|
inputManager->RegisterCallback(IJP_FACELEFT, IME_RELEASED, this, &ADefaultPlayerController::UnCastAbility2);
|
|
inputManager->RegisterCallback(IJP_FACEUP, IME_RELEASED, this, &ADefaultPlayerController::UnCastAbility3);
|
|
inputManager->RegisterCallback(IJP_FACERIGHT, IME_RELEASED, this, &ADefaultPlayerController::UnCastAbility4);
|
|
|
|
inputManager->RegisterCallback(IJP_START, IME_PRESSED, this, &ADefaultPlayerController::ToggleMenu);
|
|
inputManager->RegisterCallback(IJP_SHOULDERLEFT, IME_PRESSED, this, &ADefaultPlayerController::ToggleSkillTree);
|
|
inputManager->RegisterCallback(IJP_SHOULDERLEFT, IME_RELEASED, this, &ADefaultPlayerController::ToggleSkillTree);
|
|
inputManager->RegisterCallback(IJP_TRIGGERLEFT, IME_PRESSED, this, &ADefaultPlayerController::CoopModifierOn);
|
|
inputManager->RegisterCallback(IJP_TRIGGERLEFT, IME_RELEASED, this, &ADefaultPlayerController::CoopModifierOff);
|
|
#endif
|
|
|
|
InputComponent->BindAction("ToggleMenu", IE_Pressed, this, &ADefaultPlayerController::ToggleMenu);
|
|
InputComponent->BindAction("ToggleSkillTree", IE_Pressed, this, &ADefaultPlayerController::ToggleSkillTree);
|
|
InputComponent->BindAction("ToggleSkillTree", IE_Released, this, &ADefaultPlayerController::ToggleSkillTree);
|
|
InputComponent->BindAction("CoopModifier", IE_Pressed, this, &ADefaultPlayerController::CoopModifierOn);
|
|
InputComponent->BindAction("CoopModifier", IE_Released, this, &ADefaultPlayerController::CoopModifierOff);
|
|
|
|
|
|
#if PLATFORM_SPECIFIC_WIN == 0
|
|
#define ABILITY_KEYBINDING_OP(__name, __id) \
|
|
InputComponent->BindAction(__name, IE_Pressed, this, &ADefaultPlayerController::CastAbility##__id);\
|
|
InputComponent->BindAction(__name, IE_Released, this, &ADefaultPlayerController::UnCastAbility##__id);
|
|
|
|
#include "AbilityBindings.h"
|
|
#else // On playstation, have repeating attack button
|
|
InputComponent->BindAction("Attack", IE_Pressed, this, &ADefaultPlayerController::CastAbility0);
|
|
InputComponent->BindAction("CastAbility1", IE_Pressed, this, &ADefaultPlayerController::CastAbility1);
|
|
InputComponent->BindAction("CastAbility2", IE_Pressed, this, &ADefaultPlayerController::CastAbility2);
|
|
InputComponent->BindAction("CastAbility3", IE_Pressed, this, &ADefaultPlayerController::CastAbility3);
|
|
InputComponent->BindAction("CastAbility4", IE_Pressed, this, &ADefaultPlayerController::CastAbility4);
|
|
InputComponent->BindAction("Attack", IE_Released, this, &ADefaultPlayerController::UnCastAbility0);
|
|
InputComponent->BindAction("CastAbility1", IE_Released, this, &ADefaultPlayerController::UnCastAbility1);
|
|
InputComponent->BindAction("CastAbility2", IE_Released, this, &ADefaultPlayerController::UnCastAbility2);
|
|
InputComponent->BindAction("CastAbility3", IE_Released, this, &ADefaultPlayerController::UnCastAbility3);
|
|
InputComponent->BindAction("CastAbility4", IE_Released, this, &ADefaultPlayerController::UnCastAbility4);
|
|
#endif
|
|
|
|
// We have 2 versions of the rotation bindings to handle different kinds of devices differently
|
|
// "turn" handles devices that provide an absolute delta, such as a mouse.
|
|
// "turnrate" is for devices that we choose to treat as a rate of change, such as an analog joystick
|
|
|
|
InputComponent->BindAxis("MoveForward", this, &ADefaultPlayerController::MoveForward);
|
|
InputComponent->BindAxis("MoveRight", this, &ADefaultPlayerController::MoveRight);
|
|
|
|
InputComponent->BindAxis("TurnRate", this, &ADefaultPlayerController::TurnAtRate);
|
|
InputComponent->BindAxis("LookUp", this, &ADefaultPlayerController::LookUp);
|
|
InputComponent->BindAxis("LookUpRate", this, &ADefaultPlayerController::LookUpAtRate);
|
|
|
|
|
|
InputComponent->BindAction("RightMouse", IE_Pressed, this, &ADefaultPlayerController::RightMouseDown);
|
|
InputComponent->BindAction("RightMouse", IE_Released, this, &ADefaultPlayerController::RightMouseUp);
|
|
InputComponent->BindAction("LeftMouse", IE_Pressed, this, &ADefaultPlayerController::LeftMouseDown);
|
|
InputComponent->BindAction("LeftMouse", IE_Released, this, &ADefaultPlayerController::LeftMouseUp);
|
|
|
|
InputComponent->BindAction("HoldPosition", IE_Pressed, this, &ADefaultPlayerController::HoldDown);
|
|
InputComponent->BindAction("HoldPosition", IE_Released, this, &ADefaultPlayerController::HoldUp);
|
|
|
|
}
|
|
|
|
void ADefaultPlayerController::Tick(float DeltaSeconds)
|
|
{
|
|
APawn* pawn = GetPawnOrSpectator();
|
|
|
|
if (IsLocalController() && IsValid(m_defaultPlayer) && IsValid(pawn) && pawn != m_defaultPlayer)
|
|
m_defaultPlayer->SetActorLocation(pawn->GetActorLocation());
|
|
|
|
// Tick basic attacks while the user keeps the button held
|
|
if(m_usingBasicAttack)
|
|
{
|
|
m_TickBasicAttack();
|
|
}
|
|
|
|
// Set the menu focus state
|
|
if(!m_ingameMenuPanel)
|
|
m_framesSinceMenuFocus++;
|
|
else
|
|
m_framesSinceMenuFocus = 0;
|
|
|
|
|
|
ANetworkGhost* const asGhost = Cast<ANetworkGhost>(pawn);
|
|
if (IsValid(asGhost) && asGhost->CanRespawn())
|
|
{
|
|
// Respawn the player character when we are done spooking
|
|
SpawnCharacterForClient(asGhost->GetActorLocation(), asGhost->GetActorRotation());
|
|
asGhost->Destroy();
|
|
}
|
|
|
|
// Get possessed character, if any
|
|
ANetworkPossessable* const character = Cast<ANetworkPossessable>(pawn);
|
|
if(IsValid(character) && IsLocalController() && PlayerState && HasGameFocus())
|
|
{
|
|
// Do UI / Input updates on this controller
|
|
m_UpdateUI();
|
|
|
|
#if PLATFORM_SPECIFIC_WIN == 0
|
|
InputManager* inputManager = InputManager::GetInstance();
|
|
|
|
Joystick* joystick = inputManager->joystick;
|
|
if (joystick)
|
|
{
|
|
TeaLib::Math::Vector2f lStick = joystick->GetDelta(IJA_LSTICK);
|
|
if (lStick.Length() > 0.2f)
|
|
{
|
|
FVector forward = (character->CameraBoom->GetForwardVector() * -lStick.y) + (character->CameraBoom->GetRightVector() * lStick.x);
|
|
FRotator current = character->GetActorRotation();
|
|
FRotator difference = forward.Rotation();
|
|
character->SetActorRotation(FRotator(current.Pitch, difference.Yaw, current.Roll));
|
|
{
|
|
MoveForward(-lStick.y);
|
|
MoveRight(lStick.x);
|
|
}
|
|
}
|
|
}
|
|
#endif
|
|
|
|
FVector2D mousePos;
|
|
if(GetMousePosition(mousePos.X, mousePos.Y))
|
|
{
|
|
FVector location;
|
|
FVector direction;
|
|
if(DeprojectMousePositionToWorld(location, direction))
|
|
{
|
|
const FVector origin = character->TopDownCamera->GetComponentLocation();
|
|
const FVector position = pawn->GetActorLocation();
|
|
const FVector normal = FVector(0, 0, 1);
|
|
float entry = FVector::DotProduct(position - origin, normal) / FVector::DotProduct(direction, normal);
|
|
|
|
const FVector lookatPos = origin + direction * entry;
|
|
|
|
FVector dif = lookatPos - pawn->GetActorLocation();
|
|
dif.Normalize();
|
|
if(!m_rightMouseDown)
|
|
{
|
|
// Only update lookat if the mouse had moved
|
|
if(m_mouseLast != mousePos)
|
|
{
|
|
FRotator current = pawn->GetActorRotation();
|
|
FRotator difference = dif.Rotation();
|
|
character->SetCharacterRotation(FRotator(current.Pitch, difference.Yaw, current.Roll));
|
|
m_mouseLast = mousePos;
|
|
}
|
|
}
|
|
else
|
|
character->AddMovementInput(dif, 1);
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
void ADefaultPlayerController::Possess(APawn* pawn)
|
|
{
|
|
Super::Possess(pawn);
|
|
|
|
// Store the initialy posesesed player, since it is the default player.
|
|
if (!IsValid(m_defaultPlayer))
|
|
m_defaultPlayer = Cast<ADefaultPlayer>(pawn);
|
|
|
|
// Get possessed character, if any
|
|
ANetworkPlayer* character = Cast<ANetworkPlayer>(pawn);
|
|
|
|
// Get possessed ghost, if any
|
|
ANetworkGhost* ghost = Cast<ANetworkGhost>(pawn);
|
|
|
|
// Assign player name to character
|
|
ACharacterBase* charBase = Cast<ACharacterBase>(pawn);
|
|
UDefaultGameInstance* inst = Cast<UDefaultGameInstance>(GetGameInstance());
|
|
if(charBase)
|
|
charBase->playerName = inst->sessionManager->GetPlayerName(*PlayerState->UniqueId);
|
|
|
|
// Set the character on the player state
|
|
if(Role == ROLE_Authority)
|
|
{
|
|
if(PlayerState)
|
|
{
|
|
// Customize ghost character
|
|
if(ghost)
|
|
{
|
|
TArray<FIngameSkillTreeSkill> skills = m_skillTree->GetSkillsForLevel(Cast<ADefaultPlayerState>(PlayerState));
|
|
TArray<UBaseSkillObject*> equip;
|
|
for(FIngameSkillTreeSkill& s : skills)
|
|
{
|
|
equip.Add(s.skillObject);
|
|
}
|
|
ghost->EquipSkills(equip);
|
|
ghost->SetCustomizations(setupState.customizations);
|
|
}
|
|
|
|
Cast<ADefaultPlayerState>(PlayerState)->character = character;
|
|
}
|
|
|
|
// Host should handle possess events directly instead of throug OnRep_Pawn
|
|
m_OnPawnChanged();
|
|
}
|
|
}
|
|
|
|
void ADefaultPlayerController::SeamlessTravelFrom(class APlayerController* OldPC)
|
|
{
|
|
Super::SeamlessTravelFrom(OldPC);
|
|
m_seamlessTravelDone = true;
|
|
// Handle receiving the setup state
|
|
OnRep_Setup();
|
|
}
|
|
|
|
void ADefaultPlayerController::PawnPendingDestroy(APawn* inPawn)
|
|
{
|
|
Super::PawnPendingDestroy(inPawn);
|
|
|
|
ANetworkPlayer* asPlayer = Cast<ANetworkPlayer>(inPawn);
|
|
if(IsValid(asPlayer)) // The player is destroyed?
|
|
{
|
|
// Clear learned skills array
|
|
m_alreadyLearnedSkills.Reset();
|
|
}
|
|
|
|
bool isKoth = false;
|
|
UWorld* world = GetWorld();
|
|
if(!IsValid(world))
|
|
return;
|
|
if(world->bIsTearingDown)
|
|
return;
|
|
AGameMode* gm = world->GetAuthGameMode();
|
|
if(gm && gm->IsA<AKingOfTheHillGameMode>())
|
|
{
|
|
isKoth = true;
|
|
}
|
|
|
|
|
|
if (!isKoth && inPawn->IsA(ANetworkPossessable::StaticClass()))
|
|
{
|
|
// Spawn a ghost
|
|
FTransform f;
|
|
f.SetScale3D(FVector(1, 1, 1));
|
|
ANetworkPossessable* asPossessable = Cast<ANetworkPossessable>(inPawn);
|
|
if (asPossessable->GetCharacterMovement()->IsMovingOnGround())
|
|
{
|
|
// We can spawn the ghost at the location of the dead player
|
|
f.SetTranslation(asPossessable->GetActorLocation());
|
|
f.SetRotation(asPossessable->GetActorRotation().Quaternion());
|
|
}
|
|
else if (playerLocationHistory.Num() > 0)
|
|
{
|
|
// The player was falling, we must refer to the location history for a valid spawn spot
|
|
f.SetTranslation(playerLocationHistory[0].location);
|
|
f.SetRotation(playerLocationHistory[0].rotation.Quaternion());
|
|
}
|
|
|
|
FActorSpawnParameters params;
|
|
params.bNoFail = true;
|
|
params.SpawnCollisionHandlingOverride = ESpawnActorCollisionHandlingMethod::AlwaysSpawn;
|
|
ANetworkGhost* ghost = GetWorld()->SpawnActor<ANetworkGhost>(ghostClass, f, params);
|
|
ADefaultPlayerState* playerState = Cast<ADefaultPlayerState>(PlayerState);
|
|
check(playerState);
|
|
ghost->SetTeam(playerState->GetTeam());
|
|
check(ghost);
|
|
Possess(ghost);
|
|
}
|
|
|
|
|
|
// Handle reposessing the default player
|
|
if(IsValid(m_defaultPlayer))
|
|
{
|
|
// Posess the default player if nothing is possessed
|
|
if (isKoth)
|
|
{
|
|
Possess(m_defaultPlayer);
|
|
m_defaultPlayer->OnSpawn();
|
|
}
|
|
}
|
|
else
|
|
{
|
|
GERROR("Can't reposses default player, it hasen't previously been set");
|
|
}
|
|
|
|
// Update possessed character on player state
|
|
if(PlayerState)
|
|
{
|
|
Cast<ADefaultPlayerState>(PlayerState)->character = nullptr;
|
|
}
|
|
|
|
// Host should handle possess events directly instead of throug OnRep_Pawn
|
|
m_OnPawnChanged();
|
|
}
|
|
|
|
void ADefaultPlayerController::UpdatePlayerPosition(const FVector& location, const FRotator& rotator)
|
|
{
|
|
if (playerLocationHistory.Num() == 0)
|
|
{
|
|
playerLocationHistory.Add(ADefaultPlayerController::Location(location, rotator));
|
|
}
|
|
else
|
|
{
|
|
// Add only if distance to last point is larger than 100
|
|
ADefaultPlayerController::Location& mostRecent = playerLocationHistory.Last();
|
|
const FVector2D thisPos2D = FVector2D(location.X, location.Y);
|
|
if (FVector2D::DistSquared(FVector2D(mostRecent.location.X, mostRecent.location.Y), thisPos2D) > 10000)
|
|
playerLocationHistory.Add(ADefaultPlayerController::Location(location, rotator));
|
|
|
|
// Ensure that we dont exceed more than 3
|
|
if (playerLocationHistory.Num() > 3)
|
|
playerLocationHistory.RemoveAt(0, playerLocationHistory.Num() - 3);
|
|
}
|
|
}
|
|
|
|
void ADefaultPlayerController::OnRep_Pawn()
|
|
{
|
|
Super::OnRep_Pawn();
|
|
m_OnPawnChanged();
|
|
}
|
|
void ADefaultPlayerController::OnRep_Setup()
|
|
{
|
|
Super::OnRep_Setup();
|
|
if(IsLocalController())
|
|
{
|
|
if(!m_skillTreeInitialized)
|
|
{
|
|
// Request the server to initialize the skilltree
|
|
GWPRINT(L"Loading skill tree for " + GetName());
|
|
UCharacterSettings* settings = Cast<UDefaultGameInstance>(GetGameInstance())->GetCharacterSettings();
|
|
|
|
// Update state on the widget
|
|
if(m_skillTreeWidget)
|
|
{
|
|
m_skillTreeWidget->BuildFromState(setupState.skills);
|
|
}
|
|
|
|
m_skillTreeInitialized = true;
|
|
}
|
|
}
|
|
|
|
// Create a the ingame skilltree on clients and server for ability unlock calculation
|
|
if(!m_skillTree)
|
|
{
|
|
m_CreateIngameSkilltree();
|
|
m_skillTree->BuildFromState(setupState.skills);
|
|
}
|
|
|
|
// Assign basic attack
|
|
if(characterClassProperties)
|
|
{
|
|
if(characterClassProperties->classes.Num() > 0)
|
|
{
|
|
setupState.characterClass = FMath::Clamp(setupState.characterClass, 0, characterClassProperties->classes.Num()-1);
|
|
m_classProperties = characterClassProperties->classes[setupState.characterClass];
|
|
}
|
|
}
|
|
else
|
|
{
|
|
GERROR("characterClassProperties is not assigned");
|
|
}
|
|
}
|
|
|
|
FCharacterClassProperty ADefaultPlayerController::GetCharacterClassProperties() const
|
|
{
|
|
return m_classProperties;
|
|
}
|
|
|
|
class UIngameHUD* ADefaultPlayerController::GetHUD()
|
|
{
|
|
return m_hud;
|
|
}
|
|
|
|
void ADefaultPlayerController::OnLevelUpClient_Implementation(const TArray<FIngameSkillTreeSkill>& updatedSkills, const TArray<FIngameSkillTreeSkill>& oldSkills)
|
|
{
|
|
m_alreadyLearnedSkills = oldSkills;
|
|
|
|
// Show level up hud
|
|
if (m_hud)
|
|
m_hud->OnLevelUp(updatedSkills);
|
|
UpdateLevel();
|
|
|
|
// Add abilities to the list of learned abilities
|
|
for (int32 i = 0; i < updatedSkills.Num(); i++)
|
|
{
|
|
UAbilityInfo* info = updatedSkills[i].selectedEffect;
|
|
if (info)
|
|
{
|
|
m_learnedAbilities.Add(info);
|
|
}
|
|
}
|
|
}
|
|
void ADefaultPlayerController::LearnSkillsForLevel()
|
|
{
|
|
ANetworkPlayer* player = Cast<ANetworkPlayer>(GetPawn());
|
|
if(!player)
|
|
return;
|
|
if(!m_skillTree)
|
|
return;
|
|
|
|
TArray<FIngameSkillTreeSkill> skills = m_skillTree->GetSkillsForLevel(Cast<ADefaultPlayerState>(PlayerState));
|
|
|
|
TArray<UBaseSkillObject*> skillsToEquip;
|
|
|
|
// Calculate skill delta
|
|
TArray<FIngameSkillTreeSkill> updatedSkills;
|
|
for (int32 i = 0; i < skills.Num(); i++)
|
|
{
|
|
FIngameSkillTreeSkill& skillNew = skills[i];
|
|
bool found = false;
|
|
for (int32 j = 0; j < m_alreadyLearnedSkills.Num(); j++)
|
|
{
|
|
FIngameSkillTreeSkill& skillOld = m_alreadyLearnedSkills[j];
|
|
if (skillOld.skillObject == skillNew.skillObject)
|
|
{
|
|
if(skillOld.level < skillNew.level)
|
|
{
|
|
updatedSkills.Add(skills[i]);
|
|
}
|
|
found = true;
|
|
break;
|
|
}
|
|
}
|
|
if (!found)
|
|
updatedSkills.Add(skills[i]);
|
|
|
|
skillsToEquip.Add(skillNew.skillObject);
|
|
}
|
|
|
|
// Override player basic attack by class
|
|
if(player->abilities.Num() < 1)
|
|
player->abilities.SetNumZeroed(1);
|
|
player->abilities[0] = player->basicAttack = m_classProperties.basicAttack;
|
|
|
|
// Equip skill items on all replicas of this player
|
|
player->EquipSkills(skillsToEquip);
|
|
|
|
// Update customizations
|
|
player->SetCustomizations(setupState.customizations);
|
|
|
|
// Learn abilities
|
|
player->LearnSkills_Server(updatedSkills);
|
|
|
|
// Equip class specific items, overrides equipment set by the skill tree
|
|
if(m_classProperties.classItems.Num() > 0)
|
|
player->EquipItems(m_classProperties.classItems);
|
|
|
|
// Equip more skill items
|
|
if(PlayerState)
|
|
{
|
|
FString steamID = Cast<UDefaultGameInstance>(GetGameInstance())->sessionManager->GetSteamID(*PlayerState->UniqueId);
|
|
if(steamID == "76561198008045751")
|
|
{
|
|
GPRINT("Hoi frank");
|
|
}
|
|
}
|
|
|
|
OnLevelUpClient(updatedSkills, m_alreadyLearnedSkills);
|
|
|
|
// Update already learned skills
|
|
for(int32 i = 0; i < m_alreadyLearnedSkills.Num(); i++)
|
|
{
|
|
bool found = false;
|
|
for(int32 j = 0; j < skills.Num(); j++)
|
|
{
|
|
if(skills[j].skillObject == m_alreadyLearnedSkills[i].skillObject)
|
|
{
|
|
found = true;
|
|
break;
|
|
}
|
|
}
|
|
if(!found)
|
|
{
|
|
m_alreadyLearnedSkills[i].power = 0.0f;
|
|
m_alreadyLearnedSkills[i].level = 0;
|
|
skills.Add(m_alreadyLearnedSkills[i]);
|
|
}
|
|
}
|
|
|
|
m_alreadyLearnedSkills = skills;
|
|
}
|
|
int32 ADefaultPlayerController::GetCurrentAbilityLevel(class UAbilityInfo* ability)
|
|
{
|
|
for(int32 i = 0; i < m_alreadyLearnedSkills.Num(); i++)
|
|
{
|
|
if(m_alreadyLearnedSkills[i].selectedEffect == ability)
|
|
{
|
|
return m_alreadyLearnedSkills[i].level;
|
|
}
|
|
}
|
|
return 0;
|
|
}
|
|
|
|
bool ADefaultPlayerController::GetAbilityButtonLocation(UAbilityInfo* ability, int32& slot, bool& isAlt)
|
|
{
|
|
isAlt = false;
|
|
for(slot = 0; slot < m_mainSkillMapping.Num(); slot++)
|
|
{
|
|
if(m_mainSkillMapping[slot] == ability)
|
|
return true;
|
|
}
|
|
isAlt = true;
|
|
for(slot = 0; slot < m_altSkillMapping.Num(); slot++)
|
|
{
|
|
if(m_altSkillMapping[slot] == ability)
|
|
return true;
|
|
}
|
|
return false;
|
|
}
|
|
|
|
void ADefaultPlayerController::ToggleSkillTree()
|
|
{
|
|
if(IsLocalController() && HasGameFocus())
|
|
{
|
|
if(!m_skillTreeWidget->IsShow())
|
|
{
|
|
m_skillTreeWidget->Show();
|
|
}
|
|
else
|
|
{
|
|
m_skillTreeWidget->Hide();
|
|
}
|
|
}
|
|
}
|
|
void ADefaultPlayerController::ToggleMenu()
|
|
{
|
|
if(!m_ingameMenu)
|
|
{
|
|
m_ingameMenu = CreateWidget<UMenuScreen>(GetWorld(), menuWidgetClass);
|
|
m_ingameMenu->AddToViewport(50);
|
|
}
|
|
if(!m_ingameMenuPanel)
|
|
{
|
|
m_ingameMenu->OnShow();
|
|
m_ingameMenuPanel = m_ingameMenu->OpenScreenMenu(ingameMenuPanelClass);
|
|
}
|
|
else
|
|
{
|
|
OnHideUI();
|
|
}
|
|
UWidgetBlueprintLibrary::SetFocusToGameViewport();
|
|
}
|
|
void ADefaultPlayerController::OnHideUI()
|
|
{
|
|
if(!m_ingameMenuPanel)
|
|
return;
|
|
m_ingameMenu->OnHide();
|
|
m_ingameMenu->CloseSubMenu(m_ingameMenuPanel);
|
|
m_ingameMenuPanel = nullptr;
|
|
UWidgetBlueprintLibrary::SetFocusToGameViewport();
|
|
}
|
|
|
|
void ADefaultPlayerController::m_OnPawnChanged()
|
|
{
|
|
APawn* pawn = GetPawnOrSpectator();
|
|
ANetworkPlayer* player = Cast<ANetworkPlayer>(pawn);
|
|
ANetworkPossessable* possessable = Cast<ANetworkPossessable>(pawn);
|
|
const bool isValid = IsValid(pawn) && pawn->IsA<ANetworkPossessable>();
|
|
|
|
if (m_hud)
|
|
{
|
|
// Calculate skill prototypes
|
|
if (m_skillTree)
|
|
{
|
|
TArray<FIngameSkillTreeSkill> skillsetPrototype = m_skillTree->GetSkillsForLevel(1.0f);
|
|
m_hud->NativeOnAssignCharacter(possessable, skillsetPrototype);
|
|
|
|
// Set Skill bindings
|
|
m_mainSkillMapping.SetNum(0);
|
|
m_altSkillMapping.SetNum(0);
|
|
for (int32 i = 0; i < skillsetPrototype.Num(); i++)
|
|
{
|
|
switch (skillsetPrototype[i].abilityType)
|
|
{
|
|
case 0:
|
|
m_mainSkillMapping.Add(skillsetPrototype[i].selectedEffect);
|
|
break;
|
|
case 1:
|
|
m_altSkillMapping.Add(skillsetPrototype[i].selectedEffect);
|
|
break;
|
|
}
|
|
}
|
|
}
|
|
m_hud->SetVisibility(isValid ? ESlateVisibility::Visible : ESlateVisibility::Hidden);
|
|
}
|
|
|
|
// Create respawn widget on local player
|
|
if(IsLocalController())
|
|
{
|
|
if (isValid)
|
|
{
|
|
if (m_respawnWidget)
|
|
{
|
|
m_respawnWidget->RemoveFromViewport();
|
|
m_respawnWidget = nullptr;
|
|
}
|
|
}
|
|
else // Assume default player
|
|
{
|
|
// Create respawn widget
|
|
if(!m_respawnWidget)
|
|
{
|
|
m_respawnWidget = CreateWidget<UUserWidget>(GetWorld(), respawnWidgetClass);
|
|
m_respawnWidget->AddToViewport(30);
|
|
}
|
|
}
|
|
}
|
|
}
|
|
void ADefaultPlayerController::m_UpdateUI()
|
|
{
|
|
ANetworkPlayer* character = Cast<ANetworkPlayer>(GetPawn());
|
|
if(m_hud && character)
|
|
{
|
|
ADefaultPlayerState* state = Cast<ADefaultPlayerState>(PlayerState);
|
|
|
|
TArray<AAbilityState*> abilityStates;
|
|
for(int32 i = 0; i < character->abilities.Num(); ++i)
|
|
{
|
|
UAbilityInfo* ability = character->abilities[i];
|
|
AAbilityState* state = character->GetAbilityState(ability);
|
|
state->cooldownRate = 1.0f - (state->onCooldownTimer / ability->cooldown);
|
|
if (ability->cooldown < 0.1f)
|
|
state->cooldownRate = 1.0f;
|
|
abilityStates.Add(state);
|
|
}
|
|
m_hud->UpdateCooldowns(abilityStates, character);
|
|
}
|
|
}
|
|
|
|
void ADefaultPlayerController::m_CreateIngameSkilltree()
|
|
{
|
|
if (!m_skillTree)
|
|
{
|
|
// Create logic-only skilltree
|
|
m_skillTree = GetWorld()->SpawnActor<AIngameSkillTree>(AIngameSkillTree::StaticClass());
|
|
check(m_skillTree);
|
|
}
|
|
}
|
|
|
|
void ADefaultPlayerController::OnCharacterCreated(ANetworkCharacter* character)
|
|
{
|
|
if (m_hud)
|
|
{
|
|
m_hud->OnCharacterCreated(character);
|
|
}
|
|
else
|
|
{
|
|
GWWARNING(L"OnCharacterCreated called on a controller without a HUD");
|
|
}
|
|
}
|
|
void ADefaultPlayerController::OnCharacterDestroyed(ANetworkCharacter* character)
|
|
{
|
|
if (m_hud)
|
|
{
|
|
m_hud->OnCharacterDestroyed(character);
|
|
}
|
|
else
|
|
{
|
|
GWWARNING(L"OnCharacterDestroyed called on a controller without a HUD");
|
|
}
|
|
}
|
|
|
|
const TArray<class UAbilityInfo*>& ADefaultPlayerController::GetAbilities(APawn* targetPawn)
|
|
{
|
|
if (!IsValid(targetPawn))
|
|
targetPawn = GetPawn();
|
|
ANetworkPlayer* netPlayer = Cast<ANetworkPlayer>(targetPawn);
|
|
if (netPlayer)
|
|
{
|
|
return netPlayer->abilities;
|
|
}
|
|
static TArray<class UAbilityInfo*> dummy;
|
|
return dummy;
|
|
}
|
|
|
|
bool ADefaultPlayerController::HasGameFocus()
|
|
{
|
|
return m_framesSinceMenuFocus > 10;
|
|
}
|
|
|
|
void ADefaultPlayerController::CastAbility(int32 index)
|
|
{
|
|
if(!HasGameFocus())
|
|
return;
|
|
|
|
// Basic Attack?
|
|
if(index == 0)
|
|
{
|
|
m_StartBasicAttack();
|
|
m_usingBasicAttack = true;
|
|
}
|
|
|
|
ANetworkPlayer* netPlayer = Cast<ANetworkPlayer>(GetPawn());
|
|
UAbilityInfo* info = GetMappedAbility(index);
|
|
if(netPlayer)
|
|
{
|
|
// Obtain assigned basic attack ability
|
|
if(index == 0)
|
|
info = netPlayer->basicAttack;
|
|
if(info)
|
|
{
|
|
netPlayer->CastAbility(info);
|
|
}
|
|
}
|
|
}
|
|
void ADefaultPlayerController::UnCastAbility(int32 index)
|
|
{
|
|
if(!HasGameFocus())
|
|
return;
|
|
|
|
// Basic Attack?
|
|
if(index == 0)
|
|
{
|
|
m_StopBasicAttack();
|
|
m_usingBasicAttack = false;
|
|
}
|
|
|
|
// Only main-bar abilities and basic attack
|
|
if(index >= 5)
|
|
return;
|
|
|
|
|
|
// This casts abilities again which are hold abilities to turn them off
|
|
ANetworkPlayer* netPlayer = Cast<ANetworkPlayer>(GetPawn());
|
|
UAbilityInfo* info = GetMappedAbility(index);
|
|
|
|
if (info && info->precastEvent && netPlayer && IsValid(netPlayer->m_currentPreCast))
|
|
{
|
|
netPlayer->m_currentPreCast->StartAbility();
|
|
return;
|
|
}
|
|
if(netPlayer && info)
|
|
{
|
|
if(info->actionType == EAbilityActionType::Hold)
|
|
{
|
|
AAbilityState* state = netPlayer->GetAbilityState(info);
|
|
if(state->toggleState == 1)
|
|
netPlayer->CastAbility(info);
|
|
}
|
|
}
|
|
}
|
|
UAbilityInfo* ADefaultPlayerController::GetMappedAbility(int32 index)
|
|
{
|
|
ANetworkPlayer* netPlayer = Cast<ANetworkPlayer>(GetPawn());
|
|
if(netPlayer)
|
|
{
|
|
if(index == 0)
|
|
{
|
|
if(netPlayer->abilities.Num() > 0)
|
|
return netPlayer->abilities[0];
|
|
}
|
|
else
|
|
{
|
|
bool useAlt = index > 4;
|
|
index = index - (useAlt ? 5 : 1);
|
|
|
|
UAbilityInfo* ability = m_hud->buttonBarSwitcher->GetAbilityMapping(index, useAlt);
|
|
if(ability)
|
|
{
|
|
if(m_learnedAbilities.Contains(ability))
|
|
{
|
|
return ability;
|
|
}
|
|
}
|
|
}
|
|
}
|
|
return nullptr;
|
|
}
|
|
|
|
void ADefaultPlayerController::TurnAtRate(float Rate)
|
|
{
|
|
if(!HasGameFocus())
|
|
return;
|
|
|
|
APawn* pawn = GetPawn();
|
|
if (!pawn)
|
|
return;
|
|
// calculate delta for this frame from the rate information
|
|
pawn->AddControllerYawInput(Rate * 45.f * GetWorld()->GetDeltaSeconds());
|
|
}
|
|
void ADefaultPlayerController::LookUp(float Rate)
|
|
{
|
|
if(!HasGameFocus())
|
|
return;
|
|
|
|
APawn* pawn = GetPawn();
|
|
if (!pawn)
|
|
return;
|
|
pawn->AddControllerPitchInput(Rate);
|
|
}
|
|
void ADefaultPlayerController::LookUpAtRate(float Rate)
|
|
{
|
|
if(!HasGameFocus())
|
|
return;
|
|
|
|
APawn* pawn = GetPawn();
|
|
if (!pawn)
|
|
return;
|
|
// calculate delta for this frame from the rate information
|
|
pawn->AddControllerPitchInput(Rate * 45.f * GetWorld()->GetDeltaSeconds());
|
|
}
|
|
void ADefaultPlayerController::MoveForward(float Value)
|
|
{
|
|
if(!HasGameFocus())
|
|
return;
|
|
|
|
ANetworkPossessable* pawn = Cast<ANetworkPossessable>(GetPawn());
|
|
if (!pawn)
|
|
return;
|
|
if (Value != 0.0f)
|
|
{
|
|
FVector forward = pawn->CameraBoom->GetForwardVector();
|
|
forward.Z = 0;
|
|
forward.Normalize();
|
|
pawn->AddMovementInput(forward, Value);
|
|
}
|
|
}
|
|
void ADefaultPlayerController::MoveRight(float Value)
|
|
{
|
|
if(!HasGameFocus())
|
|
return;
|
|
|
|
ANetworkPossessable* pawn = Cast<ANetworkPossessable>(GetPawn());
|
|
if (!pawn)
|
|
return;
|
|
if (Value != 0.0f)
|
|
{
|
|
FVector right = pawn->CameraBoom->GetRightVector();
|
|
right.Z = 0;
|
|
right.Normalize();
|
|
pawn->AddMovementInput(right, Value);
|
|
}
|
|
}
|
|
|
|
#define ABILITY_ATTACK(__id, __name) \
|
|
void ADefaultPlayerController::CastAbility##__id()\
|
|
{\
|
|
CastAbility(__id);\
|
|
}\
|
|
void ADefaultPlayerController::UnCastAbility##__id()\
|
|
{\
|
|
UnCastAbility(__id);\
|
|
}
|
|
#define ABILITY_OP(__id, __name) \
|
|
void ADefaultPlayerController::CastAbility##__id()\
|
|
{\
|
|
if(m_coopModifier)\
|
|
CastAbility(__id + 4);\
|
|
else\
|
|
CastAbility(__id);\
|
|
}\
|
|
void ADefaultPlayerController::UnCastAbility##__id()\
|
|
{\
|
|
UnCastAbility(__id);\
|
|
}
|
|
#include "AbilityBindings.h"
|
|
|
|
void ADefaultPlayerController::RightMouseDown()
|
|
{
|
|
m_rightMouseDown = true;
|
|
}
|
|
void ADefaultPlayerController::RightMouseUp()
|
|
{
|
|
m_rightMouseDown = false;
|
|
}
|
|
void ADefaultPlayerController::LeftMouseDown()
|
|
{
|
|
if(!HasGameFocus())
|
|
return;
|
|
|
|
// Check for switches
|
|
FHitResult result;
|
|
if (GetHitResultUnderCursor(ECollisionChannel::ECC_WorldDynamic, false, result))
|
|
{
|
|
ANetworkPlayer* pawn = Cast<ANetworkPlayer>(GetPawn());
|
|
if (result.Actor.Get() && result.Actor.Get()->IsA(ANetworkSwitch::StaticClass()) && pawn)
|
|
{
|
|
pawn->ToggleSwitch(Cast<ANetworkSwitch>(result.Actor.Get()));
|
|
}
|
|
}
|
|
}
|
|
|
|
void ADefaultPlayerController::ToggleSwitch()
|
|
{
|
|
ANetworkPlayer* pawn = Cast<ANetworkPlayer>(GetPawn());
|
|
if (pawn)
|
|
{
|
|
TArray<FOverlapResult> hits;
|
|
|
|
FCollisionQueryParams TraceParams(FName(TEXT("OverlapMulti Trace")), false);
|
|
TraceParams.bTraceComplex = false;
|
|
TraceParams.bReturnPhysicalMaterial = false;
|
|
FVector position = pawn->GetActorLocation();
|
|
FQuat rotation = FQuat();
|
|
FCollisionShape colShape = FCollisionShape::MakeSphere(300);
|
|
|
|
GetWorld()->OverlapMultiByObjectType(hits, position, rotation, FCollisionObjectQueryParams::AllDynamicObjects, colShape, TraceParams);
|
|
|
|
for (size_t i = 0; i < hits.Num(); i++)
|
|
{
|
|
if (hits[i].GetActor()->IsA(ANetworkSwitch::StaticClass()))
|
|
{
|
|
pawn->ToggleSwitch(Cast<ANetworkSwitch>(hits[i].GetActor()));
|
|
break;
|
|
}
|
|
}
|
|
|
|
}
|
|
}
|
|
|
|
void ADefaultPlayerController::LeftMouseUp()
|
|
{
|
|
|
|
}
|
|
void ADefaultPlayerController::HoldDown()
|
|
{
|
|
m_holdPosition = true;
|
|
}
|
|
void ADefaultPlayerController::HoldUp()
|
|
{
|
|
m_holdPosition = false;
|
|
}
|
|
|
|
void ADefaultPlayerController::SpawnCharacterForClient()
|
|
{
|
|
check(Role == ROLE_Authority);
|
|
UWorld* const world = GetWorld();
|
|
check(world);
|
|
|
|
ADefaultPlayerState* playerState = Cast<ADefaultPlayerState>(PlayerState);
|
|
check(playerState);
|
|
ADefaultGameMode* gameMode = world->GetAuthGameMode<ADefaultGameMode>();
|
|
check(gameMode);
|
|
if(gameMode->GetMatchState() != MatchState::InProgress)
|
|
return;
|
|
|
|
int32 team = playerState->GetTeam();
|
|
|
|
ADefaultGameMode* mode = Cast<ADefaultGameMode>(world->GetAuthGameMode());
|
|
check(mode);
|
|
|
|
// Get the spawns and spawn randomly
|
|
APlayerSpawn* const spawn = mode->GetOptimalSpawn(team);
|
|
if (IsValid(spawn))
|
|
{
|
|
SpawnCharacterForClient(spawn->GetActorLocation() + FVector(0, 0, 120), spawn->GetActorRotation());
|
|
}
|
|
else
|
|
JERROR("No spawn points available");
|
|
}
|
|
void ADefaultPlayerController::SpawnCharacterForClient(const FVector& location, const FRotator& rotation)
|
|
{
|
|
check(Role == ROLE_Authority);
|
|
UWorld* const world = GetWorld();
|
|
check(world);
|
|
|
|
ADefaultPlayerState* playerState = Cast<ADefaultPlayerState>(PlayerState);
|
|
check(playerState);
|
|
ADefaultGameMode* gameMode = world->GetAuthGameMode<ADefaultGameMode>();
|
|
check(gameMode);
|
|
if (gameMode->GetMatchState() != MatchState::InProgress)
|
|
return;
|
|
|
|
int32 team = playerState->GetTeam();
|
|
|
|
ADefaultGameMode* mode = Cast<ADefaultGameMode>(world->GetAuthGameMode());
|
|
check(mode);
|
|
|
|
FTransform f;
|
|
f.SetTranslation(location + FVector(0, 0, 120));
|
|
f.SetRotation(rotation.Quaternion());
|
|
ANetworkPlayer* character = world->SpawnActor<ANetworkPlayer>(characterClass, f);
|
|
if (!character)
|
|
{
|
|
GWERROR(L"Failed to spawn character for client, update client??");
|
|
return;
|
|
}
|
|
character->SetTeam(team);
|
|
|
|
mode->RegisterPlayer(*character);
|
|
Possess(character);
|
|
|
|
#if UE_INCLUDE_METRICS
|
|
if (Role == ROLE_Authority)
|
|
{
|
|
// Register a new player, with name
|
|
std::wstring playerName = std::wstring(L"Player ") + GetUniqueID();
|
|
if (IsValid(playerState))
|
|
{
|
|
playerName = std::wstring() + playerState->PlayerName;
|
|
if (playerName.length() > 512)
|
|
playerName.resize(512);
|
|
}
|
|
|
|
// Assign metrics handle
|
|
if (!metricsHandle)
|
|
metricsHandle = &Metrics::RegisterPlayer(playerName, uint8(team));
|
|
character->metricsHandle = metricsHandle;
|
|
metricsHandle->OnPlayerSpawn(location.X, location.Y);
|
|
}
|
|
#endif
|
|
}
|
|
|
|
bool ADefaultPlayerController::LearnSkillOnServer_Validate(UBaseSkillObject* object)
|
|
{
|
|
return object != nullptr && (m_skills.Find(object) == INDEX_NONE);
|
|
}
|
|
void ADefaultPlayerController::LearnSkillOnServer_Implementation(UBaseSkillObject* object)
|
|
{
|
|
m_skills.Push(object);
|
|
}
|
|
bool ADefaultPlayerController::UnlearnSkillOnServer_Validate( UBaseSkillObject* object )
|
|
{
|
|
return object != nullptr && (m_skills.Find( object ) > INDEX_NONE);
|
|
}
|
|
void ADefaultPlayerController::UnlearnSkillOnServer_Implementation( UBaseSkillObject* object )
|
|
{
|
|
m_skills.Remove( object );
|
|
}
|
|
|
|
void ADefaultPlayerController::OnLearnSkill(UBaseSkillObject* object)
|
|
{
|
|
//JPRINT("Learned skill " + object );
|
|
//LearnSkillOnServer( object );
|
|
}
|
|
void ADefaultPlayerController::OnUnlearnSkill(UBaseSkillObject* object)
|
|
{
|
|
//JPRINT("Unlearned skill " + object );
|
|
//UnlearnSkillOnServer( object );
|
|
}
|
|
|
|
TArray<UBaseSkillObject*>& ADefaultPlayerController::GetSkills()
|
|
{
|
|
return m_skills;
|
|
}
|
|
|
|
void ADefaultPlayerController::SpawnCombatText_Implementation(const FVector& position, const FString& string, const FLinearColor& color)
|
|
{
|
|
if (IsValid(m_hud) && IsValid(m_hud->combatText))
|
|
m_hud->combatText->CreateCombatText(position, string, color);
|
|
}
|
|
void ADefaultPlayerController::SpawnArcingCombatText_Implementation(const FVector& position, const FString& string, const FLinearColor& color)
|
|
{
|
|
if (IsValid(m_hud) && IsValid(m_hud->combatText))
|
|
m_hud->combatText->CreateArcingCombatText(position, string, color);
|
|
}
|
|
|
|
void ADefaultPlayerController::GetLifetimeReplicatedProps(TArray<FLifetimeProperty>& OutLifetimeProps) const
|
|
{
|
|
Super::GetLifetimeReplicatedProps(OutLifetimeProps);
|
|
|
|
DOREPLIFETIME(ADefaultPlayerController, m_skills);
|
|
DOREPLIFETIME(ADefaultPlayerController, m_defaultPlayer);
|
|
}
|
|
|
|
void ADefaultPlayerController::UpdateLevel()
|
|
{
|
|
if(IsLocalController())
|
|
{
|
|
ADefaultPlayerState* state = Cast<ADefaultPlayerState>(PlayerState);
|
|
if(m_skillTreeWidget && state)
|
|
{
|
|
m_skillTreeWidget->UpdateLevel(state->GetLevel() / (float)state->GetMaxLevel());
|
|
}
|
|
}
|
|
}
|
|
|
|
void ADefaultPlayerController::SetLevel(int32 level)
|
|
{
|
|
SetLevel_Server(level);
|
|
}
|
|
|
|
void ADefaultPlayerController::Suicide()
|
|
{
|
|
Suicide_Server();
|
|
}
|
|
bool ADefaultPlayerController::Suicide_Server_Validate()
|
|
{
|
|
return true;
|
|
}
|
|
void ADefaultPlayerController::Suicide_Server_Implementation()
|
|
{
|
|
#if WITH_EDITORONLY_DATA == 1
|
|
ANetworkPlayer* player = Cast<ANetworkPlayer>(GetPawn());
|
|
if(player && player->abilities[0])
|
|
{
|
|
player->NativeDealDamage(player, 9999999, 1.0f, player->abilities[0]);
|
|
}
|
|
#endif
|
|
}
|
|
|
|
bool ADefaultPlayerController::SetLevel_Server_Validate(int32 level)
|
|
{
|
|
return true;
|
|
}
|
|
void ADefaultPlayerController::SetLevel_Server_Implementation(int32 level)
|
|
{
|
|
#if WITH_EDITORONLY_DATA == 1
|
|
ADefaultPlayerState* state = Cast<ADefaultPlayerState>(PlayerState);
|
|
if(state)
|
|
{
|
|
state->SetLevel(level);
|
|
}
|
|
#endif
|
|
}
|
|
|
|
void ADefaultPlayerController::TestWinGame(int32 team)
|
|
{
|
|
if(Role == ROLE_Authority)
|
|
{
|
|
ADefaultGameMode* gm = Cast<ADefaultGameMode>(GetWorld()->GetAuthGameMode());
|
|
AKingOfTheHillGameMode* kgm = Cast<AKingOfTheHillGameMode>(GetWorld()->GetAuthGameMode());
|
|
if(kgm)
|
|
{
|
|
kgm->WinGame(team);
|
|
}
|
|
else if(gm)
|
|
{
|
|
gm->EndMatch();
|
|
|
|
auto players = gm->GetPlayers();
|
|
// Destroy all player pawns so that they respawn as spectators
|
|
for(int32 i = 0; i < players.Num(); i++)
|
|
{
|
|
if(IsValid(players[i]))
|
|
{
|
|
players[i]->Destroy(true);
|
|
}
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
void ADefaultPlayerController::CoopModifierOn()
|
|
{
|
|
if(!HasGameFocus())
|
|
return;
|
|
|
|
m_coopModifier = true;
|
|
if(m_hud && m_hud->buttonBarSwitcher)
|
|
{
|
|
m_hud->buttonBarSwitcher->ToggleSkillSet(m_coopModifier);
|
|
}
|
|
}
|
|
void ADefaultPlayerController::CoopModifierOff()
|
|
{
|
|
if(!HasGameFocus())
|
|
return;
|
|
|
|
m_coopModifier = false;
|
|
if(m_hud && m_hud->buttonBarSwitcher)
|
|
{
|
|
m_hud->buttonBarSwitcher->ToggleSkillSet(m_coopModifier);
|
|
}
|
|
}
|
|
|
|
void ADefaultPlayerController::m_StartBasicAttack()
|
|
{
|
|
}
|
|
void ADefaultPlayerController::m_TickBasicAttack()
|
|
{
|
|
ANetworkPlayer* netPlayer = Cast<ANetworkPlayer>(GetPawn());
|
|
if(netPlayer)
|
|
{
|
|
UAbilityInfo* info = netPlayer->basicAttack;
|
|
|
|
// Tick normal basic attacks
|
|
if(info && info->actionType == EAbilityActionType::Normal)
|
|
{
|
|
netPlayer->CastAbility(info);
|
|
}
|
|
}
|
|
}
|
|
void ADefaultPlayerController::m_StopBasicAttack()
|
|
{
|
|
} |