Files
ZMMO/Source/ZMMO/Game/Controller/ZMMOPlayerController.cpp
Mateus Rodrigues 6391372f00 feat(jobs): Jobs.1 client UI infra (handler + library + tecla J) + UI tweaks
Infraestrutura cliente da promocao de classe (Jobs.1). WBP_JobChangePanel
NAO esta neste commit — foi exploracao descartada (criar UI dinamica de
botoes via MCP BP graph esbarrou em limites do CreateWidget node). A
fundacao C++ fica pronta pra ser usada quando voltarmos pra UI.

INFRA C++ NOVA (Source/ZMMOJobs/)
* UZMMOJobChangeNetworkHandler (WorldSubsystem novo) — bind no
  OnJobChangeResult do plugin e re-broadcast via FZMMOOnJobChangeResultBP
  (dynamic multicast assignavel em BP). WBP futura escuta direto.
* UZMMOJobsSubsystem helpers: GetJobsByTier(EZMMOJobTier) +
  GetEligibleNextJobs(currentClassId) — filtra por ParentClassId. Pure.
* UZMMOJobsLibrary BPFL wrappers + SendJobChangeRequest convenience
  (delega pro UZeusNetworkSubsystem). Dep ZeusNetwork adicionada no
  Build.cs.

ENUM + INPUT
* EZMMOInGameUIState ganha JobChangePanel (entre StatusWindow e
  Inventory). DA_InGameScreenSet pendente de receber entry quando
  WBP for criada (tecla J vai falhar silenciosamente ate la).
* ZMMOPlayerController: tecla J -> ToggleJobChangePanel ->
  Flow->ToggleScreen(JobChangePanel). Espelha pattern do Alt+A do
  StatusWindow (InputComponent legacy BindKey + FInputChord).

UI TWEAKS
* WBP_StatusWindow + DT_UI_Styles: ajustes visuais no Editor durante a
  sessao (sem mudanca funcional documentavel — provavelmente fontes/
  spacing/cores).

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
2026-05-24 01:37:04 -03:00

144 lines
4.3 KiB
C++

#include "ZMMOPlayerController.h"
#include "Blueprint/UserWidget.h"
#include "Components/InputComponent.h"
#include "EnhancedInputSubsystems.h"
#include "Engine/GameInstance.h"
#include "Engine/LocalPlayer.h"
#include "Framework/Commands/InputChord.h"
#include "InputMappingContext.h"
#include "UObject/ConstructorHelpers.h"
#include "Widgets/Input/SVirtualJoystick.h"
#include "ZMMO.h"
#include "UI/InGame/UIInGameFlowSubsystem.h"
#include "UI/InGameTypes.h"
AZMMOPlayerController::AZMMOPlayerController()
{
// Defaults dos Input Mapping Contexts (alinhados ao ZClientMMO). Permitem
// instanciar AZMMOPlayerController directamente como
// `PlayerControllerClass` do AZMMOGameMode sem exigir um BP filho. Se o
// projeto adicionar mais IMCs, podemos extender via BP filho ou via
// arquivos de Config (UPROPERTY EditAnywhere abaixo).
static ConstructorHelpers::FObjectFinder<UInputMappingContext> DefaultImc(
TEXT("/Game/Input/IMC_Default.IMC_Default"));
static ConstructorHelpers::FObjectFinder<UInputMappingContext> MouseLookImc(
TEXT("/Game/Input/IMC_MouseLook.IMC_MouseLook"));
if (DefaultImc.Succeeded())
{
DefaultMappingContexts.Add(DefaultImc.Object);
}
if (MouseLookImc.Succeeded())
{
MobileExcludedMappingContexts.Add(MouseLookImc.Object);
}
}
void AZMMOPlayerController::BeginPlay()
{
Super::BeginPlay();
if (IsLocalPlayerController())
{
// Limpa estado de input herdado da UI do FrontEnd (UIManagerSubsystem
// e LocalPlayerSubsystem — sobrevive ao travel, e o controller anterior
// (AZMMOFrontEndPlayerController) tinha setado FInputModeUIOnly +
// bShowMouseCursor=true). Sem este reset, WASD/Look podem nao chegar
// ao pawn porque o Slate ainda esta com foco no widget antigo.
FInputModeGameOnly InputMode;
InputMode.SetConsumeCaptureMouseDown(true);
SetInputMode(InputMode);
bShowMouseCursor = false;
FlushPressedKeys();
}
if (ShouldUseTouchControls() && IsLocalPlayerController())
{
MobileControlsWidget = CreateWidget<UUserWidget>(this, MobileControlsWidgetClass);
if (MobileControlsWidget)
{
MobileControlsWidget->AddToPlayerScreen(0);
}
else
{
UE_LOG(LogZMMO, Error, TEXT("Could not spawn mobile controls widget."));
}
}
}
void AZMMOPlayerController::SetupInputComponent()
{
Super::SetupInputComponent();
if (!IsLocalPlayerController())
{
return;
}
if (UEnhancedInputLocalPlayerSubsystem* Subsystem = ULocalPlayer::GetSubsystem<UEnhancedInputLocalPlayerSubsystem>(GetLocalPlayer()))
{
for (UInputMappingContext* CurrentContext : DefaultMappingContexts)
{
Subsystem->AddMappingContext(CurrentContext, 0);
}
if (!ShouldUseTouchControls())
{
for (UInputMappingContext* CurrentContext : MobileExcludedMappingContexts)
{
Subsystem->AddMappingContext(CurrentContext, 0);
}
}
}
// Hotkey global Alt+A -> toggle StatusWindow. Uso InputComponent legacy
// (BindKey com FInputChord) pra nao depender de assets InputAction.
// Migrar pra Enhanced Input (IA_OpenStatus, IA_OpenInventory, etc.) quando
// vier outro menu (Inventory I, SkillTree K).
if (InputComponent)
{
FInputKeyBinding& Binding = InputComponent->BindKey(
FInputChord(EKeys::A, /*bShift*/ false, /*bCtrl*/ false, /*bAlt*/ true, /*bCmd*/ false),
IE_Pressed,
this, &AZMMOPlayerController::ToggleStatusWindow);
Binding.bConsumeInput = true;
Binding.bExecuteWhenPaused = true; // dispara mesmo com StatusWindow aberto (CommonUI menu mode)
// Hotkey J -> toggle JobChangePanel (Jobs.1).
FInputKeyBinding& JobBinding = InputComponent->BindKey(
FInputChord(EKeys::J, /*bShift*/ false, /*bCtrl*/ false, /*bAlt*/ false, /*bCmd*/ false),
IE_Pressed,
this, &AZMMOPlayerController::ToggleJobChangePanel);
JobBinding.bConsumeInput = true;
JobBinding.bExecuteWhenPaused = true;
}
}
void AZMMOPlayerController::ToggleStatusWindow()
{
if (UGameInstance* GI = GetGameInstance())
{
if (UUIInGameFlowSubsystem* Flow = GI->GetSubsystem<UUIInGameFlowSubsystem>())
{
Flow->ToggleScreen(EZMMOInGameUIState::StatusWindow);
}
}
}
void AZMMOPlayerController::ToggleJobChangePanel()
{
if (UGameInstance* GI = GetGameInstance())
{
if (UUIInGameFlowSubsystem* Flow = GI->GetSubsystem<UUIInGameFlowSubsystem>())
{
Flow->ToggleScreen(EZMMOInGameUIState::JobChangePanel);
}
}
}
bool AZMMOPlayerController::ShouldUseTouchControls() const
{
return SVirtualJoystick::ShouldDisplayTouchInterface() || bForceTouchControls;
}