// 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 menuWidgetClass; static TSubclassOf ingameMenuPanelClass; ADefaultPlayerController::ADefaultPlayerController(const FObjectInitializer& init) : Super(init) { menuWidgetClass = ConstructorHelpers::FClassFinder(TEXT("/Game/Assets/GUI/WEEGEE_Menu")).Class; ingameMenuPanelClass = ConstructorHelpers::FClassFinder(TEXT("/Game/Assets/GUI/Menus/WEEGEE_IngameSubMenu")).Class; characterClassProperties = ConstructorHelpers::FObjectFinder(TEXT("/Game/Assets/Abilities/Classes")).Object; respawnWidgetClass = ConstructorHelpers::FClassFinder(TEXT("/Game/Assets/GUI/WEEGEE_Respawn")).Class; HUDWidgetClass = ConstructorHelpers::FClassFinder(TEXT("/Game/Assets/GUI/WEEGEE_HUD")).Class; characterClass = ConstructorHelpers::FClassFinder(TEXT("/Game/Assets/Blueprints/Creatures/BP_NetworkPlayer")).Class; ghostClass = ConstructorHelpers::FClassFinder (TEXT("/Game/Assets/Blueprints/Creatures/BP_NetworkGhost")).Class; skilltreeWidgetClass = ConstructorHelpers::FClassFinder (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(this, HUDWidgetClass); m_hud->AddToViewport(); m_OnPawnChanged(); // Create skill tree widget m_skillTreeWidget = CreateWidget(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(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(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(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(pawn); // Get possessed character, if any ANetworkPlayer* character = Cast(pawn); // Get possessed ghost, if any ANetworkGhost* ghost = Cast(pawn); // Assign player name to character ACharacterBase* charBase = Cast(pawn); UDefaultGameInstance* inst = Cast(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 skills = m_skillTree->GetSkillsForLevel(Cast(PlayerState)); TArray equip; for(FIngameSkillTreeSkill& s : skills) { equip.Add(s.skillObject); } ghost->EquipSkills(equip); ghost->SetCustomizations(setupState.customizations); } Cast(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(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()) { isKoth = true; } if (!isKoth && inPawn->IsA(ANetworkPossessable::StaticClass())) { // Spawn a ghost FTransform f; f.SetScale3D(FVector(1, 1, 1)); ANetworkPossessable* asPossessable = Cast(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(ghostClass, f, params); ADefaultPlayerState* playerState = Cast(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(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(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& updatedSkills, const TArray& 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(GetPawn()); if(!player) return; if(!m_skillTree) return; TArray skills = m_skillTree->GetSkillsForLevel(Cast(PlayerState)); TArray skillsToEquip; // Calculate skill delta TArray 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(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(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(pawn); ANetworkPossessable* possessable = Cast(pawn); const bool isValid = IsValid(pawn) && pawn->IsA(); if (m_hud) { // Calculate skill prototypes if (m_skillTree) { TArray 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(GetWorld(), respawnWidgetClass); m_respawnWidget->AddToViewport(30); } } } } void ADefaultPlayerController::m_UpdateUI() { ANetworkPlayer* character = Cast(GetPawn()); if(m_hud && character) { ADefaultPlayerState* state = Cast(PlayerState); TArray 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::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& ADefaultPlayerController::GetAbilities(APawn* targetPawn) { if (!IsValid(targetPawn)) targetPawn = GetPawn(); ANetworkPlayer* netPlayer = Cast(targetPawn); if (netPlayer) { return netPlayer->abilities; } static TArray 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(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(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(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(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(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(GetPawn()); if (result.Actor.Get() && result.Actor.Get()->IsA(ANetworkSwitch::StaticClass()) && pawn) { pawn->ToggleSwitch(Cast(result.Actor.Get())); } } } void ADefaultPlayerController::ToggleSwitch() { ANetworkPlayer* pawn = Cast(GetPawn()); if (pawn) { TArray 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(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(PlayerState); check(playerState); ADefaultGameMode* gameMode = world->GetAuthGameMode(); check(gameMode); if(gameMode->GetMatchState() != MatchState::InProgress) return; int32 team = playerState->GetTeam(); ADefaultGameMode* mode = Cast(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(PlayerState); check(playerState); ADefaultGameMode* gameMode = world->GetAuthGameMode(); check(gameMode); if (gameMode->GetMatchState() != MatchState::InProgress) return; int32 team = playerState->GetTeam(); ADefaultGameMode* mode = Cast(world->GetAuthGameMode()); check(mode); FTransform f; f.SetTranslation(location + FVector(0, 0, 120)); f.SetRotation(rotation.Quaternion()); ANetworkPlayer* character = world->SpawnActor(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& 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& OutLifetimeProps) const { Super::GetLifetimeReplicatedProps(OutLifetimeProps); DOREPLIFETIME(ADefaultPlayerController, m_skills); DOREPLIFETIME(ADefaultPlayerController, m_defaultPlayer); } void ADefaultPlayerController::UpdateLevel() { if(IsLocalController()) { ADefaultPlayerState* state = Cast(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(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(PlayerState); if(state) { state->SetLevel(level); } #endif } void ADefaultPlayerController::TestWinGame(int32 team) { if(Role == ROLE_Authority) { ADefaultGameMode* gm = Cast(GetWorld()->GetAuthGameMode()); AKingOfTheHillGameMode* kgm = Cast(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(GetPawn()); if(netPlayer) { UAbilityInfo* info = netPlayer->basicAttack; // Tick normal basic attacks if(info && info->actionType == EAbilityActionType::Normal) { netPlayer->CastAbility(info); } } } void ADefaultPlayerController::m_StopBasicAttack() { }