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:
BIN
Content/ZMMO/UI/HUD/WBP_StatusWindow.uasset
Normal file
BIN
Content/ZMMO/UI/HUD/WBP_StatusWindow.uasset
Normal file
Binary file not shown.
Binary file not shown.
@@ -1,12 +1,17 @@
|
|||||||
#include "ZMMOPlayerController.h"
|
#include "ZMMOPlayerController.h"
|
||||||
|
|
||||||
#include "Blueprint/UserWidget.h"
|
#include "Blueprint/UserWidget.h"
|
||||||
|
#include "Components/InputComponent.h"
|
||||||
#include "EnhancedInputSubsystems.h"
|
#include "EnhancedInputSubsystems.h"
|
||||||
|
#include "Engine/GameInstance.h"
|
||||||
#include "Engine/LocalPlayer.h"
|
#include "Engine/LocalPlayer.h"
|
||||||
|
#include "Framework/Commands/InputChord.h"
|
||||||
#include "InputMappingContext.h"
|
#include "InputMappingContext.h"
|
||||||
#include "UObject/ConstructorHelpers.h"
|
#include "UObject/ConstructorHelpers.h"
|
||||||
#include "Widgets/Input/SVirtualJoystick.h"
|
#include "Widgets/Input/SVirtualJoystick.h"
|
||||||
#include "ZMMO.h"
|
#include "ZMMO.h"
|
||||||
|
#include "UI/InGame/UIInGameFlowSubsystem.h"
|
||||||
|
#include "UI/InGameTypes.h"
|
||||||
|
|
||||||
AZMMOPlayerController::AZMMOPlayerController()
|
AZMMOPlayerController::AZMMOPlayerController()
|
||||||
{
|
{
|
||||||
@@ -86,6 +91,31 @@ void AZMMOPlayerController::SetupInputComponent()
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// 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)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void AZMMOPlayerController::ToggleStatusWindow()
|
||||||
|
{
|
||||||
|
if (UGameInstance* GI = GetGameInstance())
|
||||||
|
{
|
||||||
|
if (UUIInGameFlowSubsystem* Flow = GI->GetSubsystem<UUIInGameFlowSubsystem>())
|
||||||
|
{
|
||||||
|
Flow->ToggleScreen(EZMMOInGameUIState::StatusWindow);
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
bool AZMMOPlayerController::ShouldUseTouchControls() const
|
bool AZMMOPlayerController::ShouldUseTouchControls() const
|
||||||
|
|||||||
@@ -47,4 +47,7 @@ protected:
|
|||||||
virtual void SetupInputComponent() override;
|
virtual void SetupInputComponent() override;
|
||||||
|
|
||||||
bool ShouldUseTouchControls() const;
|
bool ShouldUseTouchControls() const;
|
||||||
|
|
||||||
|
/** Hotkey Alt+A → toggle StatusWindow via UUIInGameFlowSubsystem. */
|
||||||
|
void ToggleStatusWindow();
|
||||||
};
|
};
|
||||||
|
|||||||
162
Source/ZMMO/Game/UI/InGame/ZMMOStatusWindowWidget.cpp
Normal file
162
Source/ZMMO/Game/UI/InGame/ZMMOStatusWindowWidget.cpp
Normal file
@@ -0,0 +1,162 @@
|
|||||||
|
#include "ZMMOStatusWindowWidget.h"
|
||||||
|
|
||||||
|
#include "CommonInputTypeEnum.h"
|
||||||
|
#include "Components/Button.h"
|
||||||
|
#include "Components/TextBlock.h"
|
||||||
|
#include "Engine/World.h"
|
||||||
|
#include "GameFramework/PlayerController.h"
|
||||||
|
#include "GameFramework/PlayerState.h"
|
||||||
|
#include "Internationalization/Text.h"
|
||||||
|
#include "ZMMO.h"
|
||||||
|
#include "ZMMOAttributeComponent.h"
|
||||||
|
#include "UI/InGame/UIInGameFlowSubsystem.h"
|
||||||
|
#include "UI/InGameTypes.h"
|
||||||
|
|
||||||
|
UZMMOStatusWindowWidget::UZMMOStatusWindowWidget(const FObjectInitializer& ObjectInitializer)
|
||||||
|
: Super(ObjectInitializer)
|
||||||
|
{
|
||||||
|
bIsBackHandler = true; // Esc/B fecha
|
||||||
|
bAutoActivate = true;
|
||||||
|
}
|
||||||
|
|
||||||
|
TOptional<FUIInputConfig> UZMMOStatusWindowWidget::GetDesiredInputConfig() const
|
||||||
|
{
|
||||||
|
// Modo Menu: cursor visivel, input flui pra UI (clicks nos botões),
|
||||||
|
// movimento bloqueado. Padrao pra menus modais.
|
||||||
|
return FUIInputConfig(ECommonInputMode::Menu, EMouseCaptureMode::NoCapture);
|
||||||
|
}
|
||||||
|
|
||||||
|
void UZMMOStatusWindowWidget::NativeOnActivated()
|
||||||
|
{
|
||||||
|
Super::NativeOnActivated();
|
||||||
|
BindToLocalPlayer();
|
||||||
|
|
||||||
|
if (PlusBtn_Str) PlusBtn_Str->OnClicked.AddDynamic(this, &UZMMOStatusWindowWidget::OnPlusStr);
|
||||||
|
if (PlusBtn_Agi) PlusBtn_Agi->OnClicked.AddDynamic(this, &UZMMOStatusWindowWidget::OnPlusAgi);
|
||||||
|
if (PlusBtn_Vit) PlusBtn_Vit->OnClicked.AddDynamic(this, &UZMMOStatusWindowWidget::OnPlusVit);
|
||||||
|
if (PlusBtn_Int) PlusBtn_Int->OnClicked.AddDynamic(this, &UZMMOStatusWindowWidget::OnPlusInt);
|
||||||
|
if (PlusBtn_Dex) PlusBtn_Dex->OnClicked.AddDynamic(this, &UZMMOStatusWindowWidget::OnPlusDex);
|
||||||
|
if (PlusBtn_Luk) PlusBtn_Luk->OnClicked.AddDynamic(this, &UZMMOStatusWindowWidget::OnPlusLuk);
|
||||||
|
if (CloseBtn) CloseBtn->OnClicked.AddDynamic(this, &UZMMOStatusWindowWidget::OnCloseClicked);
|
||||||
|
|
||||||
|
UE_LOG(LogZMMO, Log, TEXT("ZMMOStatusWindow activated"));
|
||||||
|
}
|
||||||
|
|
||||||
|
void UZMMOStatusWindowWidget::NativeOnDeactivated()
|
||||||
|
{
|
||||||
|
UnbindFromComponent();
|
||||||
|
|
||||||
|
if (PlusBtn_Str) PlusBtn_Str->OnClicked.RemoveDynamic(this, &UZMMOStatusWindowWidget::OnPlusStr);
|
||||||
|
if (PlusBtn_Agi) PlusBtn_Agi->OnClicked.RemoveDynamic(this, &UZMMOStatusWindowWidget::OnPlusAgi);
|
||||||
|
if (PlusBtn_Vit) PlusBtn_Vit->OnClicked.RemoveDynamic(this, &UZMMOStatusWindowWidget::OnPlusVit);
|
||||||
|
if (PlusBtn_Int) PlusBtn_Int->OnClicked.RemoveDynamic(this, &UZMMOStatusWindowWidget::OnPlusInt);
|
||||||
|
if (PlusBtn_Dex) PlusBtn_Dex->OnClicked.RemoveDynamic(this, &UZMMOStatusWindowWidget::OnPlusDex);
|
||||||
|
if (PlusBtn_Luk) PlusBtn_Luk->OnClicked.RemoveDynamic(this, &UZMMOStatusWindowWidget::OnPlusLuk);
|
||||||
|
if (CloseBtn) CloseBtn->OnClicked.RemoveDynamic(this, &UZMMOStatusWindowWidget::OnCloseClicked);
|
||||||
|
|
||||||
|
Super::NativeOnDeactivated();
|
||||||
|
}
|
||||||
|
|
||||||
|
void UZMMOStatusWindowWidget::BindToLocalPlayer()
|
||||||
|
{
|
||||||
|
UWorld* World = GetWorld();
|
||||||
|
if (!World) { return; }
|
||||||
|
APlayerController* PC = World->GetFirstPlayerController();
|
||||||
|
if (!PC || !PC->PlayerState) { return; }
|
||||||
|
UZMMOAttributeComponent* Comp = PC->PlayerState->FindComponentByClass<UZMMOAttributeComponent>();
|
||||||
|
if (!Comp || Comp == BoundComponent.Get()) { return; }
|
||||||
|
|
||||||
|
UnbindFromComponent();
|
||||||
|
BoundComponent = Comp;
|
||||||
|
Comp->OnAttributesChanged.AddDynamic(this, &UZMMOStatusWindowWidget::HandleAttributesChanged);
|
||||||
|
Comp->OnStatAllocReply.AddDynamic(this, &UZMMOStatusWindowWidget::HandleStatAllocReply);
|
||||||
|
RefreshFromSnapshot(Comp->GetSnapshot());
|
||||||
|
}
|
||||||
|
|
||||||
|
void UZMMOStatusWindowWidget::UnbindFromComponent()
|
||||||
|
{
|
||||||
|
if (UZMMOAttributeComponent* Old = BoundComponent.Get())
|
||||||
|
{
|
||||||
|
Old->OnAttributesChanged.RemoveDynamic(this, &UZMMOStatusWindowWidget::HandleAttributesChanged);
|
||||||
|
Old->OnStatAllocReply.RemoveDynamic(this, &UZMMOStatusWindowWidget::HandleStatAllocReply);
|
||||||
|
}
|
||||||
|
BoundComponent.Reset();
|
||||||
|
}
|
||||||
|
|
||||||
|
void UZMMOStatusWindowWidget::HandleAttributesChanged(const FZMMOAttributesSnapshot& Snapshot)
|
||||||
|
{
|
||||||
|
RefreshFromSnapshot(Snapshot);
|
||||||
|
}
|
||||||
|
|
||||||
|
void UZMMOStatusWindowWidget::HandleStatAllocReply(bool bAccepted, int32 Reason)
|
||||||
|
{
|
||||||
|
UE_LOG(LogZMMO, Log, TEXT("StatusWindow: alloc reply accepted=%d reason=%d"),
|
||||||
|
bAccepted ? 1 : 0, Reason);
|
||||||
|
// Snapshot subsequente vai disparar HandleAttributesChanged e atualizar UI.
|
||||||
|
// Aqui poderia popup de erro visual quando bAccepted=false (TODO polish).
|
||||||
|
}
|
||||||
|
|
||||||
|
void UZMMOStatusWindowWidget::RefreshFromSnapshot(const FZMMOAttributesSnapshot& S)
|
||||||
|
{
|
||||||
|
if (StrText) StrText->SetText(FText::FromString(FString::Printf(TEXT("STR %d"), S.Str)));
|
||||||
|
if (AgiText) AgiText->SetText(FText::FromString(FString::Printf(TEXT("AGI %d"), S.Agi)));
|
||||||
|
if (VitText) VitText->SetText(FText::FromString(FString::Printf(TEXT("VIT %d"), S.Vit)));
|
||||||
|
if (IntText) IntText->SetText(FText::FromString(FString::Printf(TEXT("INT %d"), S.Int)));
|
||||||
|
if (DexText) DexText->SetText(FText::FromString(FString::Printf(TEXT("DEX %d"), S.Dex)));
|
||||||
|
if (LukText) LukText->SetText(FText::FromString(FString::Printf(TEXT("LUK %d"), S.Luk)));
|
||||||
|
if (StatusPointText)
|
||||||
|
{
|
||||||
|
StatusPointText->SetText(FText::FromString(
|
||||||
|
FString::Printf(TEXT("Status Points: %d"), S.StatusPoint)));
|
||||||
|
}
|
||||||
|
|
||||||
|
// Derivados — sempre vem prontos do servidor (camadas equip/buff ja somadas).
|
||||||
|
// CritX10 vem internamente x10 (1.0 = 10); divide pra exibicao com 1 casa.
|
||||||
|
if (AtkText) AtkText->SetText(FText::FromString(FString::Printf(TEXT("ATK %d"), S.Atk)));
|
||||||
|
if (MatkText) MatkText->SetText(FText::FromString(FString::Printf(TEXT("MATK %d"), S.Matk)));
|
||||||
|
if (DefText) DefText->SetText(FText::FromString(FString::Printf(TEXT("DEF %d"), S.Def)));
|
||||||
|
if (MdefText) MdefText->SetText(FText::FromString(FString::Printf(TEXT("MDEF %d"), S.Mdef)));
|
||||||
|
if (HitText) HitText->SetText(FText::FromString(FString::Printf(TEXT("HIT %d"), S.Hit)));
|
||||||
|
if (FleeText) FleeText->SetText(FText::FromString(FString::Printf(TEXT("FLEE %d"), S.Flee)));
|
||||||
|
if (CritText)
|
||||||
|
{
|
||||||
|
const int32 CritInt = S.CritX10 / 10;
|
||||||
|
const int32 CritFrac = S.CritX10 % 10;
|
||||||
|
CritText->SetText(FText::FromString(FString::Printf(TEXT("CRIT %d.%d"), CritInt, CritFrac)));
|
||||||
|
}
|
||||||
|
if (AspdText) AspdText->SetText(FText::FromString(FString::Printf(TEXT("ASPD %d"), S.Aspd)));
|
||||||
|
// Habilita botões `+` somente se ha status_point disponível.
|
||||||
|
const bool bCanAlloc = S.StatusPoint > 0;
|
||||||
|
if (PlusBtn_Str) PlusBtn_Str->SetIsEnabled(bCanAlloc);
|
||||||
|
if (PlusBtn_Agi) PlusBtn_Agi->SetIsEnabled(bCanAlloc);
|
||||||
|
if (PlusBtn_Vit) PlusBtn_Vit->SetIsEnabled(bCanAlloc);
|
||||||
|
if (PlusBtn_Int) PlusBtn_Int->SetIsEnabled(bCanAlloc);
|
||||||
|
if (PlusBtn_Dex) PlusBtn_Dex->SetIsEnabled(bCanAlloc);
|
||||||
|
if (PlusBtn_Luk) PlusBtn_Luk->SetIsEnabled(bCanAlloc);
|
||||||
|
}
|
||||||
|
|
||||||
|
void UZMMOStatusWindowWidget::RequestAlloc(int32 StatId)
|
||||||
|
{
|
||||||
|
if (UZMMOAttributeComponent* Comp = BoundComponent.Get())
|
||||||
|
{
|
||||||
|
Comp->RequestStatAlloc(StatId, /*Amount*/ 1);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void UZMMOStatusWindowWidget::OnPlusStr() { RequestAlloc(0); }
|
||||||
|
void UZMMOStatusWindowWidget::OnPlusAgi() { RequestAlloc(1); }
|
||||||
|
void UZMMOStatusWindowWidget::OnPlusVit() { RequestAlloc(2); }
|
||||||
|
void UZMMOStatusWindowWidget::OnPlusInt() { RequestAlloc(3); }
|
||||||
|
void UZMMOStatusWindowWidget::OnPlusDex() { RequestAlloc(4); }
|
||||||
|
void UZMMOStatusWindowWidget::OnPlusLuk() { RequestAlloc(5); }
|
||||||
|
|
||||||
|
void UZMMOStatusWindowWidget::OnCloseClicked()
|
||||||
|
{
|
||||||
|
if (UGameInstance* GI = GetGameInstance())
|
||||||
|
{
|
||||||
|
if (UUIInGameFlowSubsystem* Flow = GI->GetSubsystem<UUIInGameFlowSubsystem>())
|
||||||
|
{
|
||||||
|
Flow->SetState(EZMMOInGameUIState::Playing);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
106
Source/ZMMO/Game/UI/InGame/ZMMOStatusWindowWidget.h
Normal file
106
Source/ZMMO/Game/UI/InGame/ZMMOStatusWindowWidget.h
Normal 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;
|
||||||
|
};
|
||||||
@@ -1,5 +1,8 @@
|
|||||||
#include "ZMMOAttributeComponent.h"
|
#include "ZMMOAttributeComponent.h"
|
||||||
|
|
||||||
|
#include "Engine/GameInstance.h"
|
||||||
|
#include "ZeusNetworkSubsystem.h"
|
||||||
|
|
||||||
UZMMOAttributeComponent::UZMMOAttributeComponent()
|
UZMMOAttributeComponent::UZMMOAttributeComponent()
|
||||||
{
|
{
|
||||||
PrimaryComponentTick.bCanEverTick = false;
|
PrimaryComponentTick.bCanEverTick = false;
|
||||||
@@ -34,3 +37,19 @@ void UZMMOAttributeComponent::NotifyLevelUp(int32 NewBaseLevel, int32 StatusPoin
|
|||||||
// aqui apenas dispara o delegate para efeitos visuais imediatos.
|
// aqui apenas dispara o delegate para efeitos visuais imediatos.
|
||||||
OnLevelUp.Broadcast(NewBaseLevel, StatusPointDelta);
|
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/GameInstance.h"
|
||||||
#include "Engine/World.h"
|
#include "Engine/World.h"
|
||||||
#include "GameFramework/GameStateBase.h"
|
#include "GameFramework/GameStateBase.h"
|
||||||
|
#include "GameFramework/PlayerController.h"
|
||||||
#include "GameFramework/PlayerState.h"
|
#include "GameFramework/PlayerState.h"
|
||||||
#include "ZMMOAttributeComponent.h"
|
#include "ZMMOAttributeComponent.h"
|
||||||
#include "ZMMOAttributeTypes.h"
|
#include "ZMMOAttributeTypes.h"
|
||||||
@@ -91,6 +92,8 @@ void UZMMOAttributeNetworkHandler::Initialize(FSubsystemCollectionBase& Collecti
|
|||||||
this, &UZMMOAttributeNetworkHandler::HandleHpSpUpdate);
|
this, &UZMMOAttributeNetworkHandler::HandleHpSpUpdate);
|
||||||
LevelUpHandle = Net->OnLevelUp.AddUObject(
|
LevelUpHandle = Net->OnLevelUp.AddUObject(
|
||||||
this, &UZMMOAttributeNetworkHandler::HandleLevelUp);
|
this, &UZMMOAttributeNetworkHandler::HandleLevelUp);
|
||||||
|
StatAllocReplyHandle = Net->OnStatAllocReply.AddUObject(
|
||||||
|
this, &UZMMOAttributeNetworkHandler::HandleStatAllocReply);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -113,6 +116,11 @@ void UZMMOAttributeNetworkHandler::Deinitialize()
|
|||||||
Net->OnLevelUp.Remove(LevelUpHandle);
|
Net->OnLevelUp.Remove(LevelUpHandle);
|
||||||
LevelUpHandle.Reset();
|
LevelUpHandle.Reset();
|
||||||
}
|
}
|
||||||
|
if (StatAllocReplyHandle.IsValid())
|
||||||
|
{
|
||||||
|
Net->OnStatAllocReply.Remove(StatAllocReplyHandle);
|
||||||
|
StatAllocReplyHandle.Reset();
|
||||||
|
}
|
||||||
}
|
}
|
||||||
Super::Deinitialize();
|
Super::Deinitialize();
|
||||||
}
|
}
|
||||||
@@ -179,3 +187,22 @@ void UZMMOAttributeNetworkHandler::HandleLevelUp(int32 EntityId, int32 NewBaseLe
|
|||||||
Comp->NotifyLevelUp(NewBaseLevel, StatusPointDelta);
|
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(FZMMOOnHpSpChanged, int32, Hp, int32, Sp);
|
||||||
DECLARE_DYNAMIC_MULTICAST_DELEGATE_TwoParams(FZMMOOnLevelUp, int32, NewBaseLevel, int32, StatusPointDelta);
|
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
|
* Componente de atributos do MMO ligado ao ator que o representa em-jogo
|
||||||
* (player local, proxies remotos, NPCs futuros).
|
* (player local, proxies remotos, NPCs futuros).
|
||||||
@@ -44,6 +48,17 @@ public:
|
|||||||
UFUNCTION(BlueprintCallable, Category = "Zeus|Attributes")
|
UFUNCTION(BlueprintCallable, Category = "Zeus|Attributes")
|
||||||
void NotifyLevelUp(int32 NewBaseLevel, int32 StatusPointDelta);
|
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
|
/// Seed do EntityId vindo de S_SPAWN_PLAYER local. Chamado pelo
|
||||||
/// `AZMMOPlayerCharacter` antes do primeiro S_ATTRIBUTE_SNAPSHOT_FULL
|
/// `AZMMOPlayerCharacter` antes do primeiro S_ATTRIBUTE_SNAPSHOT_FULL
|
||||||
/// chegar, garantindo que o NetworkHandler consiga rotear via lookup
|
/// chegar, garantindo que o NetworkHandler consiga rotear via lookup
|
||||||
@@ -80,6 +95,10 @@ public:
|
|||||||
UPROPERTY(BlueprintAssignable, Category = "Zeus|Attributes")
|
UPROPERTY(BlueprintAssignable, Category = "Zeus|Attributes")
|
||||||
FZMMOOnLevelUp OnLevelUp;
|
FZMMOOnLevelUp OnLevelUp;
|
||||||
|
|
||||||
|
/** Resposta ao C_STAT_ALLOC (aceito ou rejeitado). UI reage. */
|
||||||
|
UPROPERTY(BlueprintAssignable, Category = "Zeus|Attributes")
|
||||||
|
FZMMOOnStatAllocReply OnStatAllocReply;
|
||||||
|
|
||||||
protected:
|
protected:
|
||||||
UPROPERTY(BlueprintReadOnly, Category = "Zeus|Attributes")
|
UPROPERTY(BlueprintReadOnly, Category = "Zeus|Attributes")
|
||||||
FZMMOAttributesSnapshot Current;
|
FZMMOAttributesSnapshot Current;
|
||||||
|
|||||||
@@ -36,10 +36,16 @@ private:
|
|||||||
void HandleAttributeSnapshotFull(const FZeusAttributesPayload& Payload);
|
void HandleAttributeSnapshotFull(const FZeusAttributesPayload& Payload);
|
||||||
void HandleHpSpUpdate(int32 EntityId, int32 Hp, int32 Sp);
|
void HandleHpSpUpdate(int32 EntityId, int32 Hp, int32 Sp);
|
||||||
void HandleLevelUp(int32 EntityId, int32 NewBaseLevel, int32 StatusPointDelta);
|
void HandleLevelUp(int32 EntityId, int32 NewBaseLevel, int32 StatusPointDelta);
|
||||||
|
void HandleStatAllocReply(bool bAccepted, int32 Reason);
|
||||||
|
|
||||||
UZeusNetworkSubsystem* GetZeusNetSubsystem() const;
|
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 SnapshotFullHandle;
|
||||||
FDelegateHandle HpSpUpdateHandle;
|
FDelegateHandle HpSpUpdateHandle;
|
||||||
FDelegateHandle LevelUpHandle;
|
FDelegateHandle LevelUpHandle;
|
||||||
|
FDelegateHandle StatAllocReplyHandle;
|
||||||
};
|
};
|
||||||
|
|||||||
Reference in New Issue
Block a user