VocationLife/Source/VocationLife/Private/VocationPlayerController.cpp
pixachux 932157eda5 Add Steam/LAN multiplayer menu with map and IP:Port connect, plus headless web admin.
Players can host or join via Steam or LAN, pick maps, and connect by address; dedicated servers expose an HTTP panel for port and game rules.

Co-authored-by: Cursor <cursoragent@cursor.com>
2026-07-22 19:06:28 +02:00

312 lines
11 KiB
C++

// Copyright VocationLife Project. All Rights Reserved.
#include "VocationPlayerController.h"
#include "VocationCharacter.h"
#include "VocationHUD.h"
#include "VocationSessionSubsystem.h"
#include "VocationGameInstance.h"
#include "VocationMainMenuWidget.h"
#include "EnhancedInputComponent.h"
#include "EnhancedInputSubsystems.h"
#include "InputAction.h"
#include "InputMappingContext.h"
#include "InputCoreTypes.h"
#include "Blueprint/UserWidget.h"
#include "Kismet/GameplayStatics.h"
AVocationPlayerController::AVocationPlayerController()
{
}
void AVocationPlayerController::BeginPlay()
{
Super::BeginPlay();
CreateRuntimeInputAssets();
if (ULocalPlayer* LocalPlayer = GetLocalPlayer())
{
if (UEnhancedInputLocalPlayerSubsystem* Subsystem =
LocalPlayer->GetSubsystem<UEnhancedInputLocalPlayerSubsystem>())
{
Subsystem->AddMappingContext(SharedMappingContext, SharedMappingPriority);
Subsystem->AddMappingContext(TopDownMappingContext, ModeMappingPriority);
}
}
if (AVocationHUD* HUD = Cast<AVocationHUD>(GetHUD()))
{
VocationHUD = HUD;
}
if (UVocationGameInstance* GI = GetGameInstance<UVocationGameInstance>())
{
GI->SetGraphicsPreset(GI->GetGraphicsPreset(), GI->IsRayTracingEnabled());
}
// Show multiplayer menu on first load (local player only).
if (IsLocalController() && !IsRunningDedicatedServer())
{
ToggleMultiplayerMenu();
}
}
void AVocationPlayerController::SetupInputComponent()
{
Super::SetupInputComponent();
if (UEnhancedInputComponent* EnhancedInput = Cast<UEnhancedInputComponent>(InputComponent))
{
if (AVocationCharacter* Character = Cast<AVocationCharacter>(GetPawn()))
{
EnhancedInput->BindAction(MoveAction, ETriggerEvent::Triggered, Character, &AVocationCharacter::HandleMove);
EnhancedInput->BindAction(LookAction, ETriggerEvent::Triggered, Character, &AVocationCharacter::HandleLook);
EnhancedInput->BindAction(JumpAction, ETriggerEvent::Started, Character, &AVocationCharacter::HandleJumpStarted);
EnhancedInput->BindAction(InteractAction, ETriggerEvent::Started, Character, &AVocationCharacter::HandleInteract);
EnhancedInput->BindAction(AttackAction, ETriggerEvent::Started, Character, &AVocationCharacter::HandleAttack);
EnhancedInput->BindAction(ToggleCameraAction, ETriggerEvent::Started, Character, &AVocationCharacter::HandleToggleCamera);
EnhancedInput->BindAction(InventoryAction, ETriggerEvent::Started, Character, &AVocationCharacter::HandleInventory);
EnhancedInput->BindAction(PauseAction, ETriggerEvent::Started, this, &AVocationPlayerController::TogglePauseMenu);
}
}
InputComponent->BindKey(EKeys::F1, IE_Pressed, this, &AVocationPlayerController::ApplyLowGraphics).bConsumeInput = false;
InputComponent->BindKey(EKeys::F2, IE_Pressed, this, &AVocationPlayerController::ApplyMediumGraphics).bConsumeInput = false;
InputComponent->BindKey(EKeys::F3, IE_Pressed, this, &AVocationPlayerController::ApplyHighGraphics).bConsumeInput = false;
InputComponent->BindKey(EKeys::F4, IE_Pressed, this, &AVocationPlayerController::ApplyUltraGraphics).bConsumeInput = false;
InputComponent->BindKey(EKeys::F5, IE_Pressed, this, &AVocationPlayerController::ApplyRayTracingGraphics).bConsumeInput = false;
InputComponent->BindKey(EKeys::M, IE_Pressed, this, &AVocationPlayerController::ToggleMultiplayerMenu).bConsumeInput = false;
InputComponent->BindKey(EKeys::H, IE_Pressed, this, &AVocationPlayerController::HostCoopGame).bConsumeInput = false;
InputComponent->BindKey(EKeys::J, IE_Pressed, this, &AVocationPlayerController::JoinFirstFoundSession).bConsumeInput = false;
InputComponent->BindKey(EKeys::S, IE_Pressed, this, &AVocationPlayerController::QuickSave).bConsumeInput = false;
}
void AVocationPlayerController::OnPossess(APawn* InPawn)
{
Super::OnPossess(InPawn);
SetupInputComponent();
if (AVocationCharacter* Character = Cast<AVocationCharacter>(InPawn))
{
ApplyInputMappingForCameraMode(Character->GetCameraMode());
}
}
void AVocationPlayerController::TogglePauseMenu()
{
bPauseMenuOpen = !bPauseMenuOpen;
SetPause(bPauseMenuOpen);
if (VocationHUD)
{
VocationHUD->SetPauseVisible(bPauseMenuOpen);
}
}
void AVocationPlayerController::ShowInteractionPrompt(const FText& Prompt)
{
if (VocationHUD)
{
VocationHUD->SetInteractionPrompt(Prompt);
}
}
void AVocationPlayerController::ClearInteractionPrompt()
{
if (VocationHUD)
{
VocationHUD->ClearInteractionPrompt();
}
}
void AVocationPlayerController::ToggleMultiplayerMenu()
{
if (MultiplayerMenu && MultiplayerMenu->IsInViewport())
{
MultiplayerMenu->RemoveFromParent();
bShowMouseCursor = false;
SetInputMode(FInputModeGameOnly());
return;
}
MultiplayerMenu = CreateWidget<UVocationMainMenuWidget>(this, UVocationMainMenuWidget::StaticClass());
if (MultiplayerMenu)
{
MultiplayerMenu->AddToViewport(100);
bShowMouseCursor = true;
FInputModeGameAndUI Mode;
Mode.SetLockMouseToViewportBehavior(EMouseLockMode::DoNotLock);
Mode.SetHideCursorDuringCapture(false);
SetInputMode(Mode);
}
}
void AVocationPlayerController::HostCoopGame()
{
if (UVocationSessionSubsystem* Sessions = GetGameInstance()->GetSubsystem<UVocationSessionSubsystem>())
{
Sessions->HostSession(4, TEXT("VocationLife"), TEXT("/Engine/Maps/Entry"), 7777, EVocationNetBackend::Auto, false);
}
}
void AVocationPlayerController::FindCoopGames()
{
if (UVocationSessionSubsystem* Sessions = GetGameInstance()->GetSubsystem<UVocationSessionSubsystem>())
{
Sessions->FindSessions();
}
}
void AVocationPlayerController::JoinCoopGame(int32 SessionIndex)
{
if (UVocationSessionSubsystem* Sessions = GetGameInstance()->GetSubsystem<UVocationSessionSubsystem>())
{
Sessions->JoinSessionByIndex(SessionIndex);
}
}
void AVocationPlayerController::ApplyInputMappingForCameraMode(EVocationCameraMode CameraMode)
{
if (ULocalPlayer* LocalPlayer = GetLocalPlayer())
{
if (UEnhancedInputLocalPlayerSubsystem* Subsystem =
LocalPlayer->GetSubsystem<UEnhancedInputLocalPlayerSubsystem>())
{
Subsystem->RemoveMappingContext(FirstPersonMappingContext);
Subsystem->RemoveMappingContext(TopDownMappingContext);
if (CameraMode == EVocationCameraMode::FirstPerson)
{
Subsystem->AddMappingContext(FirstPersonMappingContext, ModeMappingPriority);
}
else
{
Subsystem->AddMappingContext(TopDownMappingContext, ModeMappingPriority);
}
}
}
}
void AVocationPlayerController::CreateRuntimeInputAssets()
{
MoveAction = NewObject<UInputAction>(this, TEXT("IA_Move"));
MoveAction->ValueType = EInputActionValueType::Axis2D;
LookAction = NewObject<UInputAction>(this, TEXT("IA_Look"));
LookAction->ValueType = EInputActionValueType::Axis2D;
JumpAction = NewObject<UInputAction>(this, TEXT("IA_Jump"));
InteractAction = NewObject<UInputAction>(this, TEXT("IA_Interact"));
AttackAction = NewObject<UInputAction>(this, TEXT("IA_Attack"));
ToggleCameraAction = NewObject<UInputAction>(this, TEXT("IA_ToggleCamera"));
InventoryAction = NewObject<UInputAction>(this, TEXT("IA_Inventory"));
PauseAction = NewObject<UInputAction>(this, TEXT("IA_Pause"));
SharedMappingContext = NewObject<UInputMappingContext>(this, TEXT("IMC_Vocation_Shared"));
SharedMappingContext->MapKey(JumpAction, EKeys::SpaceBar);
SharedMappingContext->MapKey(InteractAction, EKeys::E);
SharedMappingContext->MapKey(AttackAction, EKeys::LeftMouseButton);
SharedMappingContext->MapKey(ToggleCameraAction, EKeys::C);
SharedMappingContext->MapKey(InventoryAction, EKeys::I);
SharedMappingContext->MapKey(PauseAction, EKeys::Escape);
SharedMappingContext->MapKey(JumpAction, EKeys::Gamepad_FaceButton_Bottom);
SharedMappingContext->MapKey(InteractAction, EKeys::Gamepad_FaceButton_Left);
SharedMappingContext->MapKey(AttackAction, EKeys::Gamepad_RightTrigger);
SharedMappingContext->MapKey(ToggleCameraAction, EKeys::Gamepad_Special_Right);
SharedMappingContext->MapKey(InventoryAction, EKeys::Gamepad_FaceButton_Top);
SharedMappingContext->MapKey(PauseAction, EKeys::Gamepad_Special_Left);
FirstPersonMappingContext = NewObject<UInputMappingContext>(this, TEXT("IMC_Vocation_FP"));
FirstPersonMappingContext->MapKey(MoveAction, EKeys::W);
FirstPersonMappingContext->MapKey(MoveAction, EKeys::S);
FirstPersonMappingContext->MapKey(MoveAction, EKeys::A);
FirstPersonMappingContext->MapKey(MoveAction, EKeys::D);
FirstPersonMappingContext->MapKey(LookAction, EKeys::Mouse2D);
FirstPersonMappingContext->MapKey(MoveAction, EKeys::Gamepad_LeftX);
FirstPersonMappingContext->MapKey(MoveAction, EKeys::Gamepad_LeftY);
FirstPersonMappingContext->MapKey(LookAction, EKeys::Gamepad_RightX);
FirstPersonMappingContext->MapKey(LookAction, EKeys::Gamepad_RightY);
TopDownMappingContext = NewObject<UInputMappingContext>(this, TEXT("IMC_Vocation_TP"));
TopDownMappingContext->MapKey(MoveAction, EKeys::W);
TopDownMappingContext->MapKey(MoveAction, EKeys::S);
TopDownMappingContext->MapKey(MoveAction, EKeys::A);
TopDownMappingContext->MapKey(MoveAction, EKeys::D);
TopDownMappingContext->MapKey(LookAction, EKeys::Mouse2D);
TopDownMappingContext->MapKey(MoveAction, EKeys::Gamepad_LeftX);
TopDownMappingContext->MapKey(MoveAction, EKeys::Gamepad_LeftY);
TopDownMappingContext->MapKey(LookAction, EKeys::Gamepad_RightX);
}
void AVocationPlayerController::ApplyLowGraphics()
{
if (UVocationGameInstance* GI = GetGameInstance<UVocationGameInstance>())
{
GI->SetGraphicsPreset(EVocationGraphicsPreset::Low, false);
}
}
void AVocationPlayerController::ApplyMediumGraphics()
{
if (UVocationGameInstance* GI = GetGameInstance<UVocationGameInstance>())
{
GI->SetGraphicsPreset(EVocationGraphicsPreset::Medium, false);
}
}
void AVocationPlayerController::ApplyHighGraphics()
{
if (UVocationGameInstance* GI = GetGameInstance<UVocationGameInstance>())
{
GI->SetGraphicsPreset(EVocationGraphicsPreset::High, false);
}
}
void AVocationPlayerController::ApplyUltraGraphics()
{
if (UVocationGameInstance* GI = GetGameInstance<UVocationGameInstance>())
{
GI->SetGraphicsPreset(EVocationGraphicsPreset::Ultra, false);
}
}
void AVocationPlayerController::ApplyRayTracingGraphics()
{
if (UVocationGameInstance* GI = GetGameInstance<UVocationGameInstance>())
{
GI->SetGraphicsPreset(EVocationGraphicsPreset::RayTracing, true);
}
}
void AVocationPlayerController::JoinFirstFoundSession()
{
if (UVocationSessionSubsystem* Sessions = GetGameInstance()->GetSubsystem<UVocationSessionSubsystem>())
{
Sessions->OnSessionSearchComplete.AddDynamic(this, &AVocationPlayerController::HandleSessionSearchForJoin);
Sessions->FindSessions();
}
}
void AVocationPlayerController::HandleSessionSearchForJoin(const TArray<FString>& SessionNames)
{
if (UVocationSessionSubsystem* Sessions = GetGameInstance()->GetSubsystem<UVocationSessionSubsystem>())
{
Sessions->OnSessionSearchComplete.RemoveDynamic(this, &AVocationPlayerController::HandleSessionSearchForJoin);
if (SessionNames.Num() > 0)
{
Sessions->JoinSessionByIndex(0);
}
}
}
void AVocationPlayerController::QuickSave()
{
if (UVocationGameInstance* GI = GetGameInstance<UVocationGameInstance>())
{
GI->SaveGame();
if (VocationHUD)
{
VocationHUD->ShowDebugStatus(TEXT("Game saved."));
}
}
}