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:
@@ -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);
|
||||
}
|
||||
|
||||
@@ -3,6 +3,7 @@
|
||||
#include "Engine/GameInstance.h"
|
||||
#include "Engine/World.h"
|
||||
#include "GameFramework/GameStateBase.h"
|
||||
#include "GameFramework/PlayerController.h"
|
||||
#include "GameFramework/PlayerState.h"
|
||||
#include "ZMMOAttributeComponent.h"
|
||||
#include "ZMMOAttributeTypes.h"
|
||||
@@ -91,6 +92,8 @@ void UZMMOAttributeNetworkHandler::Initialize(FSubsystemCollectionBase& Collecti
|
||||
this, &UZMMOAttributeNetworkHandler::HandleHpSpUpdate);
|
||||
LevelUpHandle = Net->OnLevelUp.AddUObject(
|
||||
this, &UZMMOAttributeNetworkHandler::HandleLevelUp);
|
||||
StatAllocReplyHandle = Net->OnStatAllocReply.AddUObject(
|
||||
this, &UZMMOAttributeNetworkHandler::HandleStatAllocReply);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -113,6 +116,11 @@ void UZMMOAttributeNetworkHandler::Deinitialize()
|
||||
Net->OnLevelUp.Remove(LevelUpHandle);
|
||||
LevelUpHandle.Reset();
|
||||
}
|
||||
if (StatAllocReplyHandle.IsValid())
|
||||
{
|
||||
Net->OnStatAllocReply.Remove(StatAllocReplyHandle);
|
||||
StatAllocReplyHandle.Reset();
|
||||
}
|
||||
}
|
||||
Super::Deinitialize();
|
||||
}
|
||||
@@ -179,3 +187,22 @@ void UZMMOAttributeNetworkHandler::HandleLevelUp(int32 EntityId, int32 NewBaseLe
|
||||
Comp->NotifyLevelUp(NewBaseLevel, StatusPointDelta);
|
||||
}
|
||||
}
|
||||
|
||||
void UZMMOAttributeNetworkHandler::HandleStatAllocReply(bool bAccepted, int32 Reason)
|
||||
{
|
||||
// S_ATTRIBUTE_STAT_ALLOC_OK nao traz EntityId no payload — sempre do
|
||||
// player local que fez o request. Busca o AttributeComponent via PC.
|
||||
if (UZMMOAttributeComponent* Comp = FindLocalPlayerAttributeComponent())
|
||||
{
|
||||
Comp->NotifyStatAllocReply(bAccepted, Reason);
|
||||
}
|
||||
}
|
||||
|
||||
UZMMOAttributeComponent* UZMMOAttributeNetworkHandler::FindLocalPlayerAttributeComponent() const
|
||||
{
|
||||
UWorld* World = GetWorld();
|
||||
if (!World) { return nullptr; }
|
||||
APlayerController* PC = World->GetFirstPlayerController();
|
||||
if (!PC || !PC->PlayerState) { return nullptr; }
|
||||
return PC->PlayerState->FindComponentByClass<UZMMOAttributeComponent>();
|
||||
}
|
||||
|
||||
@@ -9,6 +9,10 @@ DECLARE_DYNAMIC_MULTICAST_DELEGATE_OneParam(FZMMOOnAttributesChanged, const FZMM
|
||||
DECLARE_DYNAMIC_MULTICAST_DELEGATE_TwoParams(FZMMOOnHpSpChanged, int32, Hp, int32, Sp);
|
||||
DECLARE_DYNAMIC_MULTICAST_DELEGATE_TwoParams(FZMMOOnLevelUp, int32, NewBaseLevel, int32, StatusPointDelta);
|
||||
|
||||
/// Resposta server ao C_STAT_ALLOC. Reason e' EAllocRejectReason (None=0,
|
||||
/// InvalidStat=1, InvalidAmount=2, NotEnoughPoints=3, StatCapped=4, NoSession=5).
|
||||
DECLARE_DYNAMIC_MULTICAST_DELEGATE_TwoParams(FZMMOOnStatAllocReply, bool, bAccepted, int32, Reason);
|
||||
|
||||
/**
|
||||
* Componente de atributos do MMO ligado ao ator que o representa em-jogo
|
||||
* (player local, proxies remotos, NPCs futuros).
|
||||
@@ -44,6 +48,17 @@ public:
|
||||
UFUNCTION(BlueprintCallable, Category = "Zeus|Attributes")
|
||||
void NotifyLevelUp(int32 NewBaseLevel, int32 StatusPointDelta);
|
||||
|
||||
/// Notifica resposta server ao C_STAT_ALLOC. UI pode reagir (toast/SFX
|
||||
/// quando aceito, erro vermelho quando rejeitado).
|
||||
UFUNCTION(BlueprintCallable, Category = "Zeus|Attributes")
|
||||
void NotifyStatAllocReply(bool bAccepted, int32 Reason);
|
||||
|
||||
/// Envia C_STAT_ALLOC pro server. Wrapper conveniente — busca o
|
||||
/// UZeusNetworkSubsystem via GameInstance + chama SendStatAlloc.
|
||||
/// `StatId`: 0=STR, 1=AGI, 2=VIT, 3=INT, 4=DEX, 5=LUK. `Amount`: 1..10.
|
||||
UFUNCTION(BlueprintCallable, Category = "Zeus|Attributes")
|
||||
void RequestStatAlloc(int32 StatId, int32 Amount = 1);
|
||||
|
||||
/// Seed do EntityId vindo de S_SPAWN_PLAYER local. Chamado pelo
|
||||
/// `AZMMOPlayerCharacter` antes do primeiro S_ATTRIBUTE_SNAPSHOT_FULL
|
||||
/// chegar, garantindo que o NetworkHandler consiga rotear via lookup
|
||||
@@ -80,6 +95,10 @@ public:
|
||||
UPROPERTY(BlueprintAssignable, Category = "Zeus|Attributes")
|
||||
FZMMOOnLevelUp OnLevelUp;
|
||||
|
||||
/** Resposta ao C_STAT_ALLOC (aceito ou rejeitado). UI reage. */
|
||||
UPROPERTY(BlueprintAssignable, Category = "Zeus|Attributes")
|
||||
FZMMOOnStatAllocReply OnStatAllocReply;
|
||||
|
||||
protected:
|
||||
UPROPERTY(BlueprintReadOnly, Category = "Zeus|Attributes")
|
||||
FZMMOAttributesSnapshot Current;
|
||||
|
||||
@@ -36,10 +36,16 @@ private:
|
||||
void HandleAttributeSnapshotFull(const FZeusAttributesPayload& Payload);
|
||||
void HandleHpSpUpdate(int32 EntityId, int32 Hp, int32 Sp);
|
||||
void HandleLevelUp(int32 EntityId, int32 NewBaseLevel, int32 StatusPointDelta);
|
||||
void HandleStatAllocReply(bool bAccepted, int32 Reason);
|
||||
|
||||
UZeusNetworkSubsystem* GetZeusNetSubsystem() const;
|
||||
|
||||
/// Acha o AttributeComponent do player local (Pawn->PlayerState).
|
||||
/// Usado pro StatAllocReply (sem EntityId no payload).
|
||||
class UZMMOAttributeComponent* FindLocalPlayerAttributeComponent() const;
|
||||
|
||||
FDelegateHandle SnapshotFullHandle;
|
||||
FDelegateHandle HpSpUpdateHandle;
|
||||
FDelegateHandle LevelUpHandle;
|
||||
FDelegateHandle StatAllocReplyHandle;
|
||||
};
|
||||
|
||||
Reference in New Issue
Block a user