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

@@ -0,0 +1,106 @@
#pragma once
#include "CommonActivatableWidget.h"
#include "CoreMinimal.h"
#include "ZMMOAttributeTypes.h"
#include "ZMMOStatusWindowWidget.generated.h"
class UButton;
class UTextBlock;
class UCommonButtonBase;
class UZMMOAttributeComponent;
/**
* Tela "Status" — atributos do jogador + alocacao via botões `+`.
*
* Modo Menu: cursor visível, movimento bloqueado, Esc fecha. Push em
* UI.Layer.GameMenu pelo `UUIInGameFlowSubsystem::ToggleScreen(StatusWindow)`.
* HUD continua visível em UI.Layer.Game por baixo.
*
* Composicao do WBP_StatusWindow (BindWidgetOptional pra permitir variantes):
* - Labels de stats primarios: StrText / AgiText / VitText / IntText / DexText / LukText
* - Botões +: PlusBtn_Str / PlusBtn_Agi / PlusBtn_Vit / PlusBtn_Int / PlusBtn_Dex / PlusBtn_Luk
* - Derivados (server-authoritative): AtkText / MatkText / DefText / MdefText /
* HitText / FleeText / CritText / AspdText
* - StatusPointText (pontos disponíveis pra alocar)
* - CloseBtn (fecha — fallback do Esc/back handler)
*
* Workflow:
* 1. NativeOnActivated: busca AttributeComponent (PlayerState do player local)
* 2. Subscreve OnAttributesChanged + OnStatAllocReply
* 3. Refresh inicial via snapshot atual
* 4. Clique no `+X` chama `Comp->RequestStatAlloc(X, 1)` → server valida
* 5. Server responde com S_ATTRIBUTE_STAT_ALLOC_OK + (se ok) snapshot novo
* 6. UI atualiza via delegate
*/
UCLASS(Abstract, Blueprintable, BlueprintType)
class ZMMO_API UZMMOStatusWindowWidget : public UCommonActivatableWidget
{
GENERATED_BODY()
public:
UZMMOStatusWindowWidget(const FObjectInitializer& ObjectInitializer);
protected:
virtual void NativeOnActivated() override;
virtual void NativeOnDeactivated() override;
/// Modo Menu: cursor visível + bloqueia input de gameplay (movimento).
virtual TOptional<FUIInputConfig> GetDesiredInputConfig() const override;
/// Texto dos stats (label + valor). BindWidgetOptional pra permitir
/// WBPs custom (ex.: sem display de SP em variantes).
UPROPERTY(BlueprintReadOnly, meta = (BindWidgetOptional), Category = "Zeus|Status") UTextBlock* StrText = nullptr;
UPROPERTY(BlueprintReadOnly, meta = (BindWidgetOptional), Category = "Zeus|Status") UTextBlock* AgiText = nullptr;
UPROPERTY(BlueprintReadOnly, meta = (BindWidgetOptional), Category = "Zeus|Status") UTextBlock* VitText = nullptr;
UPROPERTY(BlueprintReadOnly, meta = (BindWidgetOptional), Category = "Zeus|Status") UTextBlock* IntText = nullptr;
UPROPERTY(BlueprintReadOnly, meta = (BindWidgetOptional), Category = "Zeus|Status") UTextBlock* DexText = nullptr;
UPROPERTY(BlueprintReadOnly, meta = (BindWidgetOptional), Category = "Zeus|Status") UTextBlock* LukText = nullptr;
/// Pontos restantes pra alocar + label "Status Points: N".
UPROPERTY(BlueprintReadOnly, meta = (BindWidgetOptional), Category = "Zeus|Status") UTextBlock* StatusPointText = nullptr;
/// Derivados (sempre calculados pelo servidor — cliente NUNCA recalcula).
/// Mostra `base + bonus` quando aplicavel (camadas equip/buff vem do server
/// pre-somadas no snapshot). BindWidgetOptional pra suportar HUDs minimalistas.
UPROPERTY(BlueprintReadOnly, meta = (BindWidgetOptional), Category = "Zeus|Status|Derived") UTextBlock* AtkText = nullptr;
UPROPERTY(BlueprintReadOnly, meta = (BindWidgetOptional), Category = "Zeus|Status|Derived") UTextBlock* MatkText = nullptr;
UPROPERTY(BlueprintReadOnly, meta = (BindWidgetOptional), Category = "Zeus|Status|Derived") UTextBlock* DefText = nullptr;
UPROPERTY(BlueprintReadOnly, meta = (BindWidgetOptional), Category = "Zeus|Status|Derived") UTextBlock* MdefText = nullptr;
UPROPERTY(BlueprintReadOnly, meta = (BindWidgetOptional), Category = "Zeus|Status|Derived") UTextBlock* HitText = nullptr;
UPROPERTY(BlueprintReadOnly, meta = (BindWidgetOptional), Category = "Zeus|Status|Derived") UTextBlock* FleeText = nullptr;
UPROPERTY(BlueprintReadOnly, meta = (BindWidgetOptional), Category = "Zeus|Status|Derived") UTextBlock* CritText = nullptr;
UPROPERTY(BlueprintReadOnly, meta = (BindWidgetOptional), Category = "Zeus|Status|Derived") UTextBlock* AspdText = nullptr;
/// Botoes + por stat.
UPROPERTY(BlueprintReadOnly, meta = (BindWidgetOptional), Category = "Zeus|Status") UButton* PlusBtn_Str = nullptr;
UPROPERTY(BlueprintReadOnly, meta = (BindWidgetOptional), Category = "Zeus|Status") UButton* PlusBtn_Agi = nullptr;
UPROPERTY(BlueprintReadOnly, meta = (BindWidgetOptional), Category = "Zeus|Status") UButton* PlusBtn_Vit = nullptr;
UPROPERTY(BlueprintReadOnly, meta = (BindWidgetOptional), Category = "Zeus|Status") UButton* PlusBtn_Int = nullptr;
UPROPERTY(BlueprintReadOnly, meta = (BindWidgetOptional), Category = "Zeus|Status") UButton* PlusBtn_Dex = nullptr;
UPROPERTY(BlueprintReadOnly, meta = (BindWidgetOptional), Category = "Zeus|Status") UButton* PlusBtn_Luk = nullptr;
/// Botao Close (fallback do Esc/back).
UPROPERTY(BlueprintReadOnly, meta = (BindWidgetOptional), Category = "Zeus|Status") UButton* CloseBtn = nullptr;
private:
void BindToLocalPlayer();
void UnbindFromComponent();
void RefreshFromSnapshot(const FZMMOAttributesSnapshot& Snap);
UFUNCTION() void HandleAttributesChanged(const FZMMOAttributesSnapshot& Snapshot);
UFUNCTION() void HandleStatAllocReply(bool bAccepted, int32 Reason);
UFUNCTION() void OnPlusStr();
UFUNCTION() void OnPlusAgi();
UFUNCTION() void OnPlusVit();
UFUNCTION() void OnPlusInt();
UFUNCTION() void OnPlusDex();
UFUNCTION() void OnPlusLuk();
UFUNCTION() void OnCloseClicked();
void RequestAlloc(int32 StatId);
UPROPERTY(Transient)
TWeakObjectPtr<UZMMOAttributeComponent> BoundComponent;
};