feat(ui/hud): barras EXP/JOB em % via exp-to-next do snapshot

FZMMOAttributesSnapshot ganha BaseExpToNext/JobExpToNext espelhando o wire do
server. ApplySnapshot liga ExpBar/JobBar pelo ratio (exp / exp-to-next) e os
readouts mostram a porcentagem (ou "MAX" no cap).

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
This commit is contained in:
2026-05-28 03:58:33 -03:00
parent 484c8d5d4f
commit ede49c1777
3 changed files with 39 additions and 13 deletions

View File

@@ -7,16 +7,27 @@
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;
}
/** Ratio 0..1 pras barras de EXP/JOB (int64). ToNext<=0 = cap -> barra cheia. */
float ExpRatio(int64 Cur, int64 ToNext)
{
return ToNext > 0
? FMath::Clamp(static_cast<float>(static_cast<double>(Cur) / static_cast<double>(ToNext)), 0.f, 1.f)
: 1.f;
}
/** Readout de progressao em "%": numero (Default) + "%" (unit/cinza).
* Cap (ToNext<=0) -> "MAX". Markup resolve no DT_UI_VitalsReadout. */
FText ExpReadoutText(int64 Cur, int64 ToNext)
{
if (ToNext <= 0) { return FText::FromString(TEXT("MAX")); }
const double Pct = static_cast<double>(Cur) / static_cast<double>(ToNext) * 100.0;
return FText::FromString(FString::Printf(TEXT("%.2f<unit>%%</>"), Pct));
}
}
void UZMMOHudPlayerVitalsWidget::ApplySnapshot(const FZMMOAttributesSnapshot& Snapshot)
@@ -33,16 +44,24 @@ void UZMMOHudPlayerVitalsWidget::ApplySnapshot(const FZMMOAttributesSnapshot& Sn
FString::Printf(TEXT("<pct>LVL </><num>%d</>"), Snapshot.BaseLevel)));
}
// BASE / JOB EXP — sem "exp-to-next" no snapshot. Readout mostra valor
// absoluto acumulado (estilo Default, sem markup de %); as barras + o "%"
// ficam pra quando a curva de exp existir no snapshot do servidor.
// BASE / JOB EXP — barras em % (exp atual / exp-to-next, resolvido pelo
// server via JobsDatabase). Em cap (ToNext=0) a barra enche e o readout
// vira "MAX". Readout exibe a porcentagem (numero + "%" cinza).
if (ExpBar)
{
ExpBar->SetTargetPrimaryLevel(ExpRatio(Snapshot.BaseExp, Snapshot.BaseExpToNext));
}
if (JobBar)
{
JobBar->SetTargetPrimaryLevel(ExpRatio(Snapshot.JobExp, Snapshot.JobExpToNext));
}
if (ExpReadout)
{
ExpReadout->SetText(FormatNumber(Snapshot.BaseExp));
ExpReadout->SetText(ExpReadoutText(Snapshot.BaseExp, Snapshot.BaseExpToNext));
}
if (JobReadout)
{
JobReadout->SetText(FormatNumber(Snapshot.JobExp));
JobReadout->SetText(ExpReadoutText(Snapshot.JobExp, Snapshot.JobExpToNext));
}
OnSnapshotApplied(Snapshot);

View File

@@ -19,9 +19,11 @@ namespace
S.EntityId = P.EntityId;
S.ClassId = P.ClassId;
S.BaseLevel = P.BaseLevel;
S.BaseExp = P.BaseExp;
S.BaseExp = P.BaseExp;
S.BaseExpToNext = P.BaseExpToNext;
S.JobLevel = P.JobLevel;
S.JobExp = P.JobExp;
S.JobExp = P.JobExp;
S.JobExpToNext = P.JobExpToNext;
S.Str = P.Str;
S.Agi = P.Agi;
S.Vit = P.Vit;

View File

@@ -26,8 +26,13 @@ struct ZMMOATTRIBUTES_API FZMMOAttributesSnapshot
// === Progressao ===
UPROPERTY(BlueprintReadOnly, Category = "Zeus|Attributes") int32 BaseLevel = 1;
UPROPERTY(BlueprintReadOnly, Category = "Zeus|Attributes") int64 BaseExp = 0;
/** EXP pra subir o proximo base level (server resolve via JobsDatabase).
* 0 = ja' em cap -> barra cheia. % = BaseExp / BaseExpToNext. */
UPROPERTY(BlueprintReadOnly, Category = "Zeus|Attributes") int64 BaseExpToNext = 0;
UPROPERTY(BlueprintReadOnly, Category = "Zeus|Attributes") int32 JobLevel = 1;
UPROPERTY(BlueprintReadOnly, Category = "Zeus|Attributes") int64 JobExp = 0;
/** EXP pra subir o proximo job level. 0 = ja' em cap -> barra cheia. */
UPROPERTY(BlueprintReadOnly, Category = "Zeus|Attributes") int64 JobExpToNext = 0;
// === Stats primarios (Camada 1 base; efetivos vao em derivados) ===
UPROPERTY(BlueprintReadOnly, Category = "Zeus|Attributes|Stats") int32 Str = 1;