haxis/Source/UnrealProject/GameState/DefaultPlayerController.cpp

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()
{
}