feat(attributes/ui): Status Window com alocação STR/AGI/VIT/INT/DEX/LUK + derivados

- ZMMOStatusWindowWidget (UCommonActivatableWidget, modo Menu): grid 2 colunas
  estilo RO. Esquerda: stats primários com botões + por stat (RequestStatAlloc).
  Direita: derivados ATK/MATK/DEF/MDEF/HIT/FLEE/CRIT/ASPD vindos do snapshot
  do server — cliente NUNCA recalcula (anti-cheat foundation).
- Botão + envia C_STAT_ALLOC → server valida cost RO (floor((stat-1)/10)+2),
  aplica + recalcula derivados + SaveCharFull async, manda
  S_ATTRIBUTE_SNAPSHOT_FULL + reply. UI atualiza em tempo real via
  OnAttributesChanged.
- ZMMOAttributeComponent ganha RequestStatAlloc + NotifyStatAllocReply +
  delegate OnStatAllocReply para feedback de rejeição.
- ZMMOAttributeNetworkHandler: HandleStatAllocReply roteia pro componente do
  player local (reply não traz EntityId — sempre quem fez o request).
- PlayerController: hotkey Alt+A (FInputChord legacy + BindKey, sem precisar
  de IA asset). ToggleStatusWindow via UUIInGameFlowSubsystem.
- WBP_StatusWindow: layout 2 colunas via MCP set_widget_tree (43 widgets) +
  BgBorder escuro semi-transparente + centralizado no overlay.
- DA_InGameScreenSet: StatusWindow → WBP_StatusWindow.
This commit is contained in:
2026-05-23 11:22:58 -03:00
parent 396223e2a8
commit f585b0d2b9
10 changed files with 372 additions and 0 deletions

View File

@@ -1,5 +1,8 @@
#include "ZMMOAttributeComponent.h"
#include "Engine/GameInstance.h"
#include "ZeusNetworkSubsystem.h"
UZMMOAttributeComponent::UZMMOAttributeComponent()
{
PrimaryComponentTick.bCanEverTick = false;
@@ -34,3 +37,19 @@ void UZMMOAttributeComponent::NotifyLevelUp(int32 NewBaseLevel, int32 StatusPoin
// aqui apenas dispara o delegate para efeitos visuais imediatos.
OnLevelUp.Broadcast(NewBaseLevel, StatusPointDelta);
}
void UZMMOAttributeComponent::NotifyStatAllocReply(bool bAccepted, int32 Reason)
{
OnStatAllocReply.Broadcast(bAccepted, Reason);
}
void UZMMOAttributeComponent::RequestStatAlloc(int32 StatId, int32 Amount)
{
UWorld* World = GetWorld();
if (!World) { return; }
UGameInstance* GI = World->GetGameInstance();
if (!GI) { return; }
UZeusNetworkSubsystem* Net = GI->GetSubsystem<UZeusNetworkSubsystem>();
if (!Net) { return; }
Net->SendStatAlloc(StatId, Amount);
}