feat(ui/hud): painel de vitais (Nome/Job/Level + HP/SP/EXP) substituindo HpSpBar
UZMMOHudPlayerVitalsWidget (novo): sub-widget do HUD com 4 UUIProgressBar_Base (HP/SP/BASE EXP/JOB EXP) + nome/classe/level. ApplySnapshot anima HP/SP via SetTargetPrimaryLevel e formata os readouts; ApplyHpSp pro tick rapido. UZMMOHudWidget propaga o snapshot pro PlayerVitals e resolve identidade: nome do AZMMOPlayerState.CharName + job via UZMMOJobsLibrary.GetJobDisplayName(ClassId). HpSpBar legado removido do WBP_HUD. WBP_PlayerVitalsPanel: layout compacto (520w) com colunas alinhadas (label 28 / bar FILL / readout 116 right-align) independente do tamanho do numero. Sem portrait. MIs por barra: UI_M_ProgressBar_SP/BaseEXP/JobEXP/Loading (cores do hud_layout_modal_New.html). EXP/JOB readout mostra exp absoluto por ora — barras de EXP aguardam o servidor enviar exp-to-next no snapshot pra calcular a porcentagem. Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
This commit is contained in:
Binary file not shown.
BIN
Content/ZMMO/UI/HUD/WBP_PlayerVitalsPanel.uasset
Normal file
BIN
Content/ZMMO/UI/HUD/WBP_PlayerVitalsPanel.uasset
Normal file
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
96
Source/ZMMO/Game/UI/InGame/ZMMOHudPlayerVitalsWidget.cpp
Normal file
96
Source/ZMMO/Game/UI/InGame/ZMMOHudPlayerVitalsWidget.cpp
Normal file
@@ -0,0 +1,96 @@
|
||||
#include "ZMMOHudPlayerVitalsWidget.h"
|
||||
|
||||
#include "Components/TextBlock.h"
|
||||
#include "UI/Widgets/UIProgressBar_Base.h"
|
||||
#include "Internationalization/Text.h"
|
||||
|
||||
namespace
|
||||
{
|
||||
/** Formata int com separador de milhar do locale (ex.: 9840 -> "9,840"). */
|
||||
FText FormatNumber(int64 Value)
|
||||
{
|
||||
return FText::AsNumber(Value);
|
||||
}
|
||||
|
||||
float SafeRatio(int32 Current, int32 Max)
|
||||
{
|
||||
return Max > 0 ? FMath::Clamp(static_cast<float>(Current) / static_cast<float>(Max), 0.f, 1.f) : 0.f;
|
||||
}
|
||||
}
|
||||
|
||||
void UZMMOHudPlayerVitalsWidget::ApplySnapshot(const FZMMOAttributesSnapshot& Snapshot)
|
||||
{
|
||||
LastSnapshot = Snapshot;
|
||||
|
||||
// HP / SP (pool efetivo — % real)
|
||||
ApplyHpSp(Snapshot.Hp, Snapshot.MaxHp, Snapshot.Sp, Snapshot.MaxSp);
|
||||
|
||||
// Level
|
||||
if (LvlText)
|
||||
{
|
||||
LvlText->SetText(FText::FromString(FString::Printf(TEXT("LVL %d"), Snapshot.BaseLevel)));
|
||||
}
|
||||
|
||||
// BASE / JOB EXP — sem "exp-to-next" no snapshot. Readout mostra valor
|
||||
// absoluto acumulado; as barras ficam pra quando a curva de exp existir.
|
||||
if (ExpReadout)
|
||||
{
|
||||
ExpReadout->SetText(FormatNumber(Snapshot.BaseExp));
|
||||
}
|
||||
if (JobReadout)
|
||||
{
|
||||
JobReadout->SetText(FormatNumber(Snapshot.JobExp));
|
||||
}
|
||||
|
||||
OnSnapshotApplied(Snapshot);
|
||||
}
|
||||
|
||||
void UZMMOHudPlayerVitalsWidget::ApplyHpSp(int32 Hp, int32 MaxHp, int32 Sp, int32 MaxSp)
|
||||
{
|
||||
LastSnapshot.Hp = Hp;
|
||||
LastSnapshot.MaxHp = MaxHp;
|
||||
LastSnapshot.Sp = Sp;
|
||||
LastSnapshot.MaxSp = MaxSp;
|
||||
|
||||
if (HpBar)
|
||||
{
|
||||
HpBar->SetTargetPrimaryLevel(SafeRatio(Hp, MaxHp));
|
||||
}
|
||||
if (SpBar)
|
||||
{
|
||||
SpBar->SetTargetPrimaryLevel(SafeRatio(Sp, MaxSp));
|
||||
}
|
||||
if (HpReadout)
|
||||
{
|
||||
HpReadout->SetText(FText::Format(
|
||||
NSLOCTEXT("ZMMOHud", "HpSpReadout", "{0}/{1}"),
|
||||
FormatNumber(Hp), FormatNumber(MaxHp)));
|
||||
}
|
||||
if (SpReadout)
|
||||
{
|
||||
SpReadout->SetText(FText::Format(
|
||||
NSLOCTEXT("ZMMOHud", "HpSpReadout", "{0}/{1}"),
|
||||
FormatNumber(Sp), FormatNumber(MaxSp)));
|
||||
}
|
||||
}
|
||||
|
||||
void UZMMOHudPlayerVitalsWidget::SetCharName(const FString& InName)
|
||||
{
|
||||
if (NameText)
|
||||
{
|
||||
NameText->SetText(FText::FromString(InName));
|
||||
}
|
||||
}
|
||||
|
||||
void UZMMOHudPlayerVitalsWidget::SetClassName(const FString& InClassName)
|
||||
{
|
||||
if (ClassText)
|
||||
{
|
||||
ClassText->SetText(FText::FromString(InClassName));
|
||||
}
|
||||
}
|
||||
|
||||
void UZMMOHudPlayerVitalsWidget::OnSnapshotApplied_Implementation(const FZMMOAttributesSnapshot& /*Snapshot*/)
|
||||
{
|
||||
// Default vazio — skin do WBP estende se quiser (low-HP flash, etc.).
|
||||
}
|
||||
92
Source/ZMMO/Game/UI/InGame/ZMMOHudPlayerVitalsWidget.h
Normal file
92
Source/ZMMO/Game/UI/InGame/ZMMOHudPlayerVitalsWidget.h
Normal file
@@ -0,0 +1,92 @@
|
||||
#pragma once
|
||||
|
||||
#include "CommonUserWidget.h"
|
||||
#include "CoreMinimal.h"
|
||||
#include "ZMMOAttributeTypes.h"
|
||||
#include "ZMMOHudPlayerVitalsWidget.generated.h"
|
||||
|
||||
class UUIProgressBar_Base;
|
||||
class UTextBlock;
|
||||
|
||||
/**
|
||||
* Sub-widget do HUD: painel de vitais do player (portrait + nome/job/level +
|
||||
* barras HP/SP/BASE EXP/JOB EXP). Sem auto-bind no AttributeComponent — o
|
||||
* parent (UZMMOHudWidget) propaga snapshots via ApplySnapshot/ApplyHpSp.
|
||||
*
|
||||
* Barras são UUIProgressBar_Base (material custom Primary/Secondary). HP/SP
|
||||
* têm pool efetivo no snapshot → % real. BASE/JOB EXP só têm exp acumulado
|
||||
* (sem "exp-to-next") — barras de EXP ficam pra quando a curva existir;
|
||||
* por ora o readout mostra o valor absoluto.
|
||||
*
|
||||
* WBP filho (WBP_PlayerVitalsPanel) deve ter widgets com EXATAMENTE estes
|
||||
* nomes: HpBar, SpBar (obrigatórios); ExpBar, JobBar, NameText, ClassText,
|
||||
* LvlText, HpReadout, SpReadout, ExpReadout, JobReadout (opcionais).
|
||||
*/
|
||||
UCLASS(Abstract, Blueprintable, BlueprintType)
|
||||
class ZMMO_API UZMMOHudPlayerVitalsWidget : public UCommonUserWidget
|
||||
{
|
||||
GENERATED_BODY()
|
||||
|
||||
public:
|
||||
/** Atualiza todos os campos a partir do snapshot. Chamado pelo UZMMOHudWidget. */
|
||||
UFUNCTION(BlueprintCallable, Category = "Zeus|HUD")
|
||||
void ApplySnapshot(const FZMMOAttributesSnapshot& Snapshot);
|
||||
|
||||
/** Atualiza apenas HP/SP (otimização do tick — sem mexer em level/exp). */
|
||||
UFUNCTION(BlueprintCallable, Category = "Zeus|HUD")
|
||||
void ApplyHpSp(int32 Hp, int32 MaxHp, int32 Sp, int32 MaxSp);
|
||||
|
||||
/** Nome do char — vem do PlayerState (não está no snapshot). */
|
||||
UFUNCTION(BlueprintCallable, Category = "Zeus|HUD")
|
||||
void SetCharName(const FString& InName);
|
||||
|
||||
/** Nome do job/classe — vem do JobsDatabase via ClassId (não é string no snapshot). */
|
||||
UFUNCTION(BlueprintCallable, Category = "Zeus|HUD")
|
||||
void SetClassName(const FString& InClassName);
|
||||
|
||||
protected:
|
||||
/** Hook BP pra skin reagir (cores low-HP, animações, etc.). */
|
||||
UFUNCTION(BlueprintNativeEvent, Category = "Zeus|HUD")
|
||||
void OnSnapshotApplied(const FZMMOAttributesSnapshot& Snapshot);
|
||||
virtual void OnSnapshotApplied_Implementation(const FZMMOAttributesSnapshot& Snapshot);
|
||||
|
||||
// === BindWidget — barras (HP/SP obrigatórias) ===
|
||||
UPROPERTY(BlueprintReadOnly, meta = (BindWidget), Category = "Zeus|HUD")
|
||||
TObjectPtr<UUIProgressBar_Base> HpBar;
|
||||
|
||||
UPROPERTY(BlueprintReadOnly, meta = (BindWidget), Category = "Zeus|HUD")
|
||||
TObjectPtr<UUIProgressBar_Base> SpBar;
|
||||
|
||||
UPROPERTY(BlueprintReadOnly, meta = (BindWidgetOptional), Category = "Zeus|HUD")
|
||||
TObjectPtr<UUIProgressBar_Base> ExpBar;
|
||||
|
||||
UPROPERTY(BlueprintReadOnly, meta = (BindWidgetOptional), Category = "Zeus|HUD")
|
||||
TObjectPtr<UUIProgressBar_Base> JobBar;
|
||||
|
||||
// === BindWidget — textos (todos opcionais) ===
|
||||
UPROPERTY(BlueprintReadOnly, meta = (BindWidgetOptional), Category = "Zeus|HUD")
|
||||
TObjectPtr<UTextBlock> NameText;
|
||||
|
||||
UPROPERTY(BlueprintReadOnly, meta = (BindWidgetOptional), Category = "Zeus|HUD")
|
||||
TObjectPtr<UTextBlock> ClassText;
|
||||
|
||||
UPROPERTY(BlueprintReadOnly, meta = (BindWidgetOptional), Category = "Zeus|HUD")
|
||||
TObjectPtr<UTextBlock> LvlText;
|
||||
|
||||
UPROPERTY(BlueprintReadOnly, meta = (BindWidgetOptional), Category = "Zeus|HUD")
|
||||
TObjectPtr<UTextBlock> HpReadout;
|
||||
|
||||
UPROPERTY(BlueprintReadOnly, meta = (BindWidgetOptional), Category = "Zeus|HUD")
|
||||
TObjectPtr<UTextBlock> SpReadout;
|
||||
|
||||
UPROPERTY(BlueprintReadOnly, meta = (BindWidgetOptional), Category = "Zeus|HUD")
|
||||
TObjectPtr<UTextBlock> ExpReadout;
|
||||
|
||||
UPROPERTY(BlueprintReadOnly, meta = (BindWidgetOptional), Category = "Zeus|HUD")
|
||||
TObjectPtr<UTextBlock> JobReadout;
|
||||
|
||||
private:
|
||||
/** Último snapshot recebido (cache pra ApplyHpSp manter MaxHp/MaxSp). */
|
||||
UPROPERTY(Transient)
|
||||
FZMMOAttributesSnapshot LastSnapshot;
|
||||
};
|
||||
@@ -10,6 +10,9 @@
|
||||
#include "ZMMO.h"
|
||||
#include "ZMMOAttributeComponent.h"
|
||||
#include "ZMMOHudHpSpWidget.h"
|
||||
#include "ZMMOHudPlayerVitalsWidget.h"
|
||||
#include "ZMMOJobsLibrary.h"
|
||||
#include "ZMMOPlayerState.h"
|
||||
|
||||
UZMMOHudWidget::UZMMOHudWidget(const FObjectInitializer& ObjectInitializer)
|
||||
: Super(ObjectInitializer)
|
||||
@@ -98,12 +101,28 @@ void UZMMOHudWidget::UnbindFromComponent()
|
||||
|
||||
void UZMMOHudWidget::HandleAttributesChanged(const FZMMOAttributesSnapshot& Snapshot)
|
||||
{
|
||||
// Propaga snapshot aos sub-widgets que ja existem. Em V1 so' HpSpBar.
|
||||
// Propaga snapshot aos sub-widgets que ja existem.
|
||||
if (HpSpBar)
|
||||
{
|
||||
HpSpBar->ApplySnapshot(Snapshot);
|
||||
}
|
||||
// Futuros: PlayerInfo->ApplySnapshot, Buffs->RefreshIcons, etc.
|
||||
if (PlayerVitals)
|
||||
{
|
||||
PlayerVitals->ApplySnapshot(Snapshot);
|
||||
|
||||
// Identidade: nome vem do PlayerState (não está no snapshot); job vem
|
||||
// do JobsSubsystem resolvendo ClassId -> display name.
|
||||
if (UZMMOAttributeComponent* Comp = BoundComponent.Get())
|
||||
{
|
||||
if (const AZMMOPlayerState* PS = Cast<AZMMOPlayerState>(Comp->GetOwner()))
|
||||
{
|
||||
PlayerVitals->SetCharName(PS->CharName);
|
||||
}
|
||||
}
|
||||
PlayerVitals->SetClassName(
|
||||
UZMMOJobsLibrary::GetJobDisplayName(this, Snapshot.ClassId).ToString());
|
||||
}
|
||||
// Futuros: Buffs->RefreshIcons, etc.
|
||||
}
|
||||
|
||||
void UZMMOHudWidget::HandleHpSpChanged(int32 Hp, int32 Sp)
|
||||
@@ -115,6 +134,10 @@ void UZMMOHudWidget::HandleHpSpChanged(int32 Hp, int32 Sp)
|
||||
{
|
||||
HpSpBar->ApplyHpSp(Hp, Snap.MaxHp, Sp, Snap.MaxSp);
|
||||
}
|
||||
if (PlayerVitals)
|
||||
{
|
||||
PlayerVitals->ApplyHpSp(Hp, Snap.MaxHp, Sp, Snap.MaxSp);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -10,6 +10,7 @@ class UTextBlock;
|
||||
class UWidget;
|
||||
class UZMMOAttributeComponent;
|
||||
class UZMMOHudHpSpWidget;
|
||||
class UZMMOHudPlayerVitalsWidget;
|
||||
|
||||
/**
|
||||
* Root da HUD de gameplay. UCommonActivatableWidget pushed pelo
|
||||
@@ -74,6 +75,12 @@ protected:
|
||||
UPROPERTY(BlueprintReadOnly, meta = (BindWidgetOptional), Category = "Zeus|HUD")
|
||||
UZMMOHudHpSpWidget* HpSpBar = nullptr;
|
||||
|
||||
/// Painel de vitais novo (portrait + nome/job/level + HP/SP/EXP/JOB bars).
|
||||
/// Substitui o HpSpBar legado. BindWidgetOptional — coexiste durante a
|
||||
/// migração; o WBP_HUD pode ter um ou outro.
|
||||
UPROPERTY(BlueprintReadOnly, meta = (BindWidgetOptional), Category = "Zeus|HUD")
|
||||
UZMMOHudPlayerVitalsWidget* PlayerVitals = nullptr;
|
||||
|
||||
/// Container do toast "LEVEL UP!" (Fase 3). Hidden por default; em
|
||||
/// `HandleLevelUp` vira visivel por `LevelUpToastDurationSec` segundos
|
||||
/// (ver `Show/HideLevelUpToast`). WBP_HUD coloca um Border + Image + TextBlock
|
||||
|
||||
Reference in New Issue
Block a user