refactor: prefixo ZMMO -> Zeus em classes/módulos C++ do cliente
- Renomeia ~50 classes UCLASS/USTRUCT/UENUM/UINTERFACE pra Zeus* (AZeusGameMode, AZeusCharacter, UZeusGameInstance, IZeusEntityInterface, UZeusWorldSubsystem, FZeusEntitySnapshot, etc.) - Encurta AZMMOPlayerCharacter -> AZeusCharacter - Renomeia módulos satélite ZMMOAttributes -> ZeusAttributes e ZMMOJobs -> ZeusJobs (pasta + .Build.cs + .uproject) - Mantém módulo principal ZMMO (renomeia depois quando jogo ganhar nome) - DefaultEngine.ini: ActiveClassRedirects ZMMOX -> ZeusX (preserva BPs/assets) - DefaultGame.ini: sections atualizadas pra ZeusThemeSubsystem/ZeusPlayerState - Category="ZMMO|..." -> "Zeus|..." em UPROPERTYs - Preserva LogZMMO, ZMMO_API, /Script/ZMMO., /Game/ZMMO/, classes Target Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
This commit is contained in:
55
Source/ZeusAttributes/Private/ZeusAttributeComponent.cpp
Normal file
55
Source/ZeusAttributes/Private/ZeusAttributeComponent.cpp
Normal file
@@ -0,0 +1,55 @@
|
||||
#include "ZeusAttributeComponent.h"
|
||||
|
||||
#include "Engine/GameInstance.h"
|
||||
#include "ZeusNetworkSubsystem.h"
|
||||
|
||||
UZeusAttributeComponent::UZeusAttributeComponent()
|
||||
{
|
||||
PrimaryComponentTick.bCanEverTick = false;
|
||||
}
|
||||
|
||||
void UZeusAttributeComponent::ApplySnapshot(const FZeusAttributesSnapshot& InSnapshot)
|
||||
{
|
||||
const int32 OldHp = Current.Hp;
|
||||
const int32 OldSp = Current.Sp;
|
||||
Current = InSnapshot;
|
||||
OnAttributesChanged.Broadcast(Current);
|
||||
if (OldHp != Current.Hp || OldSp != Current.Sp)
|
||||
{
|
||||
OnHpSpChanged.Broadcast(Current.Hp, Current.Sp);
|
||||
}
|
||||
}
|
||||
|
||||
void UZeusAttributeComponent::ApplyHpSpUpdate(int32 NewHp, int32 NewSp)
|
||||
{
|
||||
if (Current.Hp == NewHp && Current.Sp == NewSp)
|
||||
{
|
||||
return;
|
||||
}
|
||||
Current.Hp = NewHp;
|
||||
Current.Sp = NewSp;
|
||||
OnHpSpChanged.Broadcast(Current.Hp, Current.Sp);
|
||||
}
|
||||
|
||||
void UZeusAttributeComponent::NotifyLevelUp(int32 NewBaseLevel, int32 StatusPointDelta)
|
||||
{
|
||||
// O snapshot subsequente vai trazer todos os outros campos atualizados —
|
||||
// aqui apenas dispara o delegate para efeitos visuais imediatos.
|
||||
OnLevelUp.Broadcast(NewBaseLevel, StatusPointDelta);
|
||||
}
|
||||
|
||||
void UZeusAttributeComponent::NotifyStatAllocReply(bool bAccepted, int32 Reason)
|
||||
{
|
||||
OnStatAllocReply.Broadcast(bAccepted, Reason);
|
||||
}
|
||||
|
||||
void UZeusAttributeComponent::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);
|
||||
}
|
||||
218
Source/ZeusAttributes/Private/ZeusAttributeNetworkHandler.cpp
Normal file
218
Source/ZeusAttributes/Private/ZeusAttributeNetworkHandler.cpp
Normal file
@@ -0,0 +1,218 @@
|
||||
#include "ZeusAttributeNetworkHandler.h"
|
||||
|
||||
#include "Engine/GameInstance.h"
|
||||
#include "Engine/World.h"
|
||||
#include "GameFramework/GameStateBase.h"
|
||||
#include "GameFramework/PlayerController.h"
|
||||
#include "GameFramework/PlayerState.h"
|
||||
#include "ZeusAttributeComponent.h"
|
||||
#include "ZeusAttributeTypes.h"
|
||||
#include "ZeusNetworkSubsystem.h"
|
||||
|
||||
namespace
|
||||
{
|
||||
// Helper privado — converte o payload binario do plugin pra USTRUCT
|
||||
// Blueprint que o componente exibe.
|
||||
FZeusAttributesSnapshot ToSnapshot(const FZeusAttributesPayload& P)
|
||||
{
|
||||
FZeusAttributesSnapshot S;
|
||||
S.EntityId = P.EntityId;
|
||||
S.ClassId = P.ClassId;
|
||||
S.BaseLevel = P.BaseLevel;
|
||||
S.BaseExp = P.BaseExp;
|
||||
S.BaseExpToNext = P.BaseExpToNext;
|
||||
S.JobLevel = P.JobLevel;
|
||||
S.JobExp = P.JobExp;
|
||||
S.JobExpToNext = P.JobExpToNext;
|
||||
S.Str = P.Str;
|
||||
S.Agi = P.Agi;
|
||||
S.Vit = P.Vit;
|
||||
S.Int = P.Int;
|
||||
S.Dex = P.Dex;
|
||||
S.Luk = P.Luk;
|
||||
S.StatusPoint = P.StatusPoint;
|
||||
S.SkillPoint = P.SkillPoint;
|
||||
S.Hp = P.Hp;
|
||||
S.MaxHp = P.MaxHp;
|
||||
S.Sp = P.Sp;
|
||||
S.MaxSp = P.MaxSp;
|
||||
S.Money = P.Money;
|
||||
S.AtkBase = P.AtkBase;
|
||||
S.AtkEquipBonus = P.AtkEquipBonus;
|
||||
S.MatkBaseMin = P.MatkBaseMin;
|
||||
S.MatkBaseMax = P.MatkBaseMax;
|
||||
S.MatkEquipBonus = P.MatkEquipBonus;
|
||||
S.DefBase = P.DefBase;
|
||||
S.DefEquipBonus = P.DefEquipBonus;
|
||||
S.MdefBase = P.MdefBase;
|
||||
S.MdefEquipBonus = P.MdefEquipBonus;
|
||||
S.HitBase = P.HitBase;
|
||||
S.HitEquipBonus = P.HitEquipBonus;
|
||||
S.FleeBase = P.FleeBase;
|
||||
S.FleeEquipBonus = P.FleeEquipBonus;
|
||||
S.CritBaseX10 = P.CritBaseX10;
|
||||
S.CritEquipBonusX10 = P.CritEquipBonusX10;
|
||||
S.Aspd = P.Aspd;
|
||||
return S;
|
||||
}
|
||||
|
||||
// Resolve `EntityId -> UZeusAttributeComponent` via PlayerArray do GameState.
|
||||
// AttributeComponent agora vive no PlayerState (vide AZeusPlayerState +
|
||||
// Component Registry em DefaultGame.ini). Itera apenas players (1 por
|
||||
// conexao), nao 1000+ atores — O(N_players).
|
||||
//
|
||||
// Cada UZeusAttributeComponent carrega seu proprio EntityId (seed em
|
||||
// AZeusCharacter::HandleLocalSpawnReady ou no primeiro snapshot).
|
||||
UZeusAttributeComponent* FindComponentForEntity(UWorld* World, int64 EntityId) // PR-HANDOFF-007 int64
|
||||
{
|
||||
if (!World || EntityId == 0) { return nullptr; }
|
||||
const AGameStateBase* GS = World->GetGameState();
|
||||
if (!GS) { return nullptr; }
|
||||
for (APlayerState* PS : GS->PlayerArray)
|
||||
{
|
||||
if (!PS) { continue; }
|
||||
UZeusAttributeComponent* Comp = PS->FindComponentByClass<UZeusAttributeComponent>();
|
||||
if (!Comp) { continue; }
|
||||
if (Comp->GetSnapshot().EntityId == EntityId)
|
||||
{
|
||||
return Comp;
|
||||
}
|
||||
}
|
||||
return nullptr;
|
||||
}
|
||||
}
|
||||
|
||||
bool UZeusAttributeNetworkHandler::ShouldCreateSubsystem(UObject* Outer) const
|
||||
{
|
||||
// Cria apenas para mundos de gameplay (PIE/Game), nao para editor preview.
|
||||
UWorld* World = Cast<UWorld>(Outer);
|
||||
if (!World) { return false; }
|
||||
return World->WorldType == EWorldType::Game || World->WorldType == EWorldType::PIE;
|
||||
}
|
||||
|
||||
void UZeusAttributeNetworkHandler::Initialize(FSubsystemCollectionBase& Collection)
|
||||
{
|
||||
Super::Initialize(Collection);
|
||||
|
||||
if (UZeusNetworkSubsystem* Net = GetZeusNetSubsystem())
|
||||
{
|
||||
SnapshotFullHandle = Net->OnAttributeSnapshotFull.AddUObject(
|
||||
this, &UZeusAttributeNetworkHandler::HandleAttributeSnapshotFull);
|
||||
HpSpUpdateHandle = Net->OnHpSpUpdate.AddUObject(
|
||||
this, &UZeusAttributeNetworkHandler::HandleHpSpUpdate);
|
||||
LevelUpHandle = Net->OnLevelUp.AddUObject(
|
||||
this, &UZeusAttributeNetworkHandler::HandleLevelUp);
|
||||
StatAllocReplyHandle = Net->OnStatAllocReply.AddUObject(
|
||||
this, &UZeusAttributeNetworkHandler::HandleStatAllocReply);
|
||||
}
|
||||
}
|
||||
|
||||
void UZeusAttributeNetworkHandler::Deinitialize()
|
||||
{
|
||||
if (UZeusNetworkSubsystem* Net = GetZeusNetSubsystem())
|
||||
{
|
||||
if (SnapshotFullHandle.IsValid())
|
||||
{
|
||||
Net->OnAttributeSnapshotFull.Remove(SnapshotFullHandle);
|
||||
SnapshotFullHandle.Reset();
|
||||
}
|
||||
if (HpSpUpdateHandle.IsValid())
|
||||
{
|
||||
Net->OnHpSpUpdate.Remove(HpSpUpdateHandle);
|
||||
HpSpUpdateHandle.Reset();
|
||||
}
|
||||
if (LevelUpHandle.IsValid())
|
||||
{
|
||||
Net->OnLevelUp.Remove(LevelUpHandle);
|
||||
LevelUpHandle.Reset();
|
||||
}
|
||||
if (StatAllocReplyHandle.IsValid())
|
||||
{
|
||||
Net->OnStatAllocReply.Remove(StatAllocReplyHandle);
|
||||
StatAllocReplyHandle.Reset();
|
||||
}
|
||||
}
|
||||
Super::Deinitialize();
|
||||
}
|
||||
|
||||
UZeusNetworkSubsystem* UZeusAttributeNetworkHandler::GetZeusNetSubsystem() const
|
||||
{
|
||||
const UWorld* World = GetWorld();
|
||||
if (!World) { return nullptr; }
|
||||
UGameInstance* GI = World->GetGameInstance();
|
||||
if (!GI) { return nullptr; }
|
||||
return GI->GetSubsystem<UZeusNetworkSubsystem>();
|
||||
}
|
||||
|
||||
void UZeusAttributeNetworkHandler::HandleAttributeSnapshotFull(const FZeusAttributesPayload& Payload)
|
||||
{
|
||||
UWorld* World = GetWorld();
|
||||
if (!World) { return; }
|
||||
UZeusAttributeComponent* Comp = FindComponentForEntity(World, Payload.EntityId);
|
||||
if (!Comp)
|
||||
{
|
||||
// Fallback chicken-and-egg: primeiro snapshot chega antes do EntityId
|
||||
// estar seeded no componente. Aplica ao primeiro componente do
|
||||
// PlayerArray que ainda tem EntityId=0. Apos o primeiro apply, o
|
||||
// caminho normal por lookup funciona.
|
||||
const AGameStateBase* GS = World->GetGameState();
|
||||
if (GS)
|
||||
{
|
||||
for (APlayerState* PS : GS->PlayerArray)
|
||||
{
|
||||
if (!PS) { continue; }
|
||||
UZeusAttributeComponent* Candidate = PS->FindComponentByClass<UZeusAttributeComponent>();
|
||||
if (Candidate && Candidate->GetSnapshot().EntityId == 0)
|
||||
{
|
||||
Comp = Candidate;
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
if (Comp)
|
||||
{
|
||||
Comp->ApplySnapshot(ToSnapshot(Payload));
|
||||
}
|
||||
}
|
||||
|
||||
void UZeusAttributeNetworkHandler::HandleHpSpUpdate(int64 EntityId, int32 Hp, int32 Sp)
|
||||
{
|
||||
UWorld* World = GetWorld();
|
||||
if (!World) { return; }
|
||||
UZeusAttributeComponent* Comp = FindComponentForEntity(World, EntityId);
|
||||
if (Comp)
|
||||
{
|
||||
Comp->ApplyHpSpUpdate(Hp, Sp);
|
||||
}
|
||||
}
|
||||
|
||||
void UZeusAttributeNetworkHandler::HandleLevelUp(int64 EntityId, int32 NewBaseLevel, int32 StatusPointDelta)
|
||||
{
|
||||
UWorld* World = GetWorld();
|
||||
if (!World) { return; }
|
||||
UZeusAttributeComponent* Comp = FindComponentForEntity(World, EntityId);
|
||||
if (Comp)
|
||||
{
|
||||
Comp->NotifyLevelUp(NewBaseLevel, StatusPointDelta);
|
||||
}
|
||||
}
|
||||
|
||||
void UZeusAttributeNetworkHandler::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 (UZeusAttributeComponent* Comp = FindLocalPlayerAttributeComponent())
|
||||
{
|
||||
Comp->NotifyStatAllocReply(bAccepted, Reason);
|
||||
}
|
||||
}
|
||||
|
||||
UZeusAttributeComponent* UZeusAttributeNetworkHandler::FindLocalPlayerAttributeComponent() const
|
||||
{
|
||||
UWorld* World = GetWorld();
|
||||
if (!World) { return nullptr; }
|
||||
APlayerController* PC = World->GetFirstPlayerController();
|
||||
if (!PC || !PC->PlayerState) { return nullptr; }
|
||||
return PC->PlayerState->FindComponentByClass<UZeusAttributeComponent>();
|
||||
}
|
||||
13
Source/ZeusAttributes/Private/ZeusAttributesModule.cpp
Normal file
13
Source/ZeusAttributes/Private/ZeusAttributesModule.cpp
Normal file
@@ -0,0 +1,13 @@
|
||||
#include "ZeusAttributesModule.h"
|
||||
|
||||
#include "Modules/ModuleManager.h"
|
||||
|
||||
void FZeusAttributesModule::StartupModule()
|
||||
{
|
||||
}
|
||||
|
||||
void FZeusAttributesModule::ShutdownModule()
|
||||
{
|
||||
}
|
||||
|
||||
IMPLEMENT_MODULE(FZeusAttributesModule, ZeusAttributes)
|
||||
45
Source/ZeusAttributes/Private/ZeusHudHpSpWidget.cpp
Normal file
45
Source/ZeusAttributes/Private/ZeusHudHpSpWidget.cpp
Normal file
@@ -0,0 +1,45 @@
|
||||
#include "ZeusHudHpSpWidget.h"
|
||||
|
||||
#include "Components/ProgressBar.h"
|
||||
#include "Components/TextBlock.h"
|
||||
#include "Internationalization/Text.h"
|
||||
|
||||
void UZeusHudHpSpWidget::ApplySnapshot(const FZeusAttributesSnapshot& Snapshot)
|
||||
{
|
||||
LastSnapshot = Snapshot;
|
||||
OnSnapshotApplied(Snapshot);
|
||||
}
|
||||
|
||||
void UZeusHudHpSpWidget::ApplyHpSp(int32 Hp, int32 MaxHp, int32 Sp, int32 MaxSp)
|
||||
{
|
||||
LastSnapshot.Hp = Hp;
|
||||
LastSnapshot.MaxHp = MaxHp;
|
||||
LastSnapshot.Sp = Sp;
|
||||
LastSnapshot.MaxSp = MaxSp;
|
||||
|
||||
if (HpBar)
|
||||
{
|
||||
HpBar->SetPercent(MaxHp > 0 ? static_cast<float>(Hp) / static_cast<float>(MaxHp) : 0.f);
|
||||
}
|
||||
if (SpBar)
|
||||
{
|
||||
SpBar->SetPercent(MaxSp > 0 ? static_cast<float>(Sp) / static_cast<float>(MaxSp) : 0.f);
|
||||
}
|
||||
if (HpText)
|
||||
{
|
||||
HpText->SetText(FText::FromString(FString::Printf(TEXT("%d / %d"), Hp, MaxHp)));
|
||||
}
|
||||
if (SpText)
|
||||
{
|
||||
SpText->SetText(FText::FromString(FString::Printf(TEXT("%d / %d"), Sp, MaxSp)));
|
||||
}
|
||||
}
|
||||
|
||||
void UZeusHudHpSpWidget::OnSnapshotApplied_Implementation(const FZeusAttributesSnapshot& Snapshot)
|
||||
{
|
||||
ApplyHpSp(Snapshot.Hp, Snapshot.MaxHp, Snapshot.Sp, Snapshot.MaxSp);
|
||||
if (LevelText)
|
||||
{
|
||||
LevelText->SetText(FText::FromString(FString::Printf(TEXT("Lv %d"), Snapshot.BaseLevel)));
|
||||
}
|
||||
}
|
||||
105
Source/ZeusAttributes/Public/ZeusAttributeComponent.h
Normal file
105
Source/ZeusAttributes/Public/ZeusAttributeComponent.h
Normal file
@@ -0,0 +1,105 @@
|
||||
#pragma once
|
||||
|
||||
#include "CoreMinimal.h"
|
||||
#include "Components/ActorComponent.h"
|
||||
#include "ZeusAttributeTypes.h"
|
||||
#include "ZeusAttributeComponent.generated.h"
|
||||
|
||||
DECLARE_DYNAMIC_MULTICAST_DELEGATE_OneParam(FZeusOnAttributesChanged, const FZeusAttributesSnapshot&, Snapshot);
|
||||
DECLARE_DYNAMIC_MULTICAST_DELEGATE_TwoParams(FZeusOnHpSpChanged, int32, Hp, int32, Sp);
|
||||
DECLARE_DYNAMIC_MULTICAST_DELEGATE_TwoParams(FZeusOnLevelUp, 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(FZeusOnStatAllocReply, bool, bAccepted, int32, Reason);
|
||||
|
||||
/**
|
||||
* Componente de atributos do MMO ligado ao ator que o representa em-jogo
|
||||
* (player local, proxies remotos, NPCs futuros).
|
||||
*
|
||||
* Estado authoritative vive no servidor (CharRuntimeStatus em
|
||||
* Game/MMO/Modules/AttributeSystem). Cliente apenas armazena o ultimo
|
||||
* snapshot recebido e expoe delegates para UI/HUD reagir.
|
||||
*
|
||||
* Roteamento server → componente: `UZeusAttributeNetworkHandler`
|
||||
* (UWorldSubsystem) liga em `UZeusNetworkSubsystem::OnAttributeSnapshotFull`,
|
||||
* resolve `EntityId -> AActor*` via `UZeusWorldSubsystem` e chama
|
||||
* `ApplySnapshot` no componente do ator.
|
||||
*/
|
||||
UCLASS(ClassGroup=(Zeus), meta=(BlueprintSpawnableComponent),
|
||||
hidecategories=(Activation, Collision, Cooking, Object, Replication))
|
||||
class ZEUSATTRIBUTES_API UZeusAttributeComponent : public UActorComponent
|
||||
{
|
||||
GENERATED_BODY()
|
||||
|
||||
public:
|
||||
UZeusAttributeComponent();
|
||||
|
||||
/// Aplica snapshot full recebido do servidor. Substitui `Current` e
|
||||
/// dispara `OnAttributesChanged` + `OnHpSpChanged` (se hp/sp mudaram).
|
||||
UFUNCTION(BlueprintCallable, Category = "Zeus|Attributes")
|
||||
void ApplySnapshot(const FZeusAttributesSnapshot& InSnapshot);
|
||||
|
||||
/// Aplica apenas delta de HP/SP (sem mexer em outros campos).
|
||||
UFUNCTION(BlueprintCallable, Category = "Zeus|Attributes")
|
||||
void ApplyHpSpUpdate(int32 NewHp, int32 NewSp);
|
||||
|
||||
/// Notifica level up — o snapshot subsequente atualiza demais campos.
|
||||
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
|
||||
/// `AZeusCharacter` antes do primeiro S_ATTRIBUTE_SNAPSHOT_FULL
|
||||
/// chegar, garantindo que o NetworkHandler consiga rotear via lookup
|
||||
/// por EntityId desde o inicio.
|
||||
UFUNCTION(BlueprintCallable, Category = "Zeus|Attributes")
|
||||
void SeedEntityId(int64 InEntityId) { Current.EntityId = InEntityId; }
|
||||
|
||||
UFUNCTION(BlueprintPure, Category = "Zeus|Attributes")
|
||||
const FZeusAttributesSnapshot& GetSnapshot() const { return Current; }
|
||||
|
||||
UFUNCTION(BlueprintPure, Category = "Zeus|Attributes")
|
||||
float GetHpRatio() const
|
||||
{
|
||||
return Current.MaxHp > 0 ? static_cast<float>(Current.Hp) / static_cast<float>(Current.MaxHp) : 0.f;
|
||||
}
|
||||
|
||||
UFUNCTION(BlueprintPure, Category = "Zeus|Attributes")
|
||||
float GetSpRatio() const
|
||||
{
|
||||
return Current.MaxSp > 0 ? static_cast<float>(Current.Sp) / static_cast<float>(Current.MaxSp) : 0.f;
|
||||
}
|
||||
|
||||
// === Delegates Blueprint ===
|
||||
|
||||
/** Snapshot novo aplicado. Use isto na UI para refresh geral. */
|
||||
UPROPERTY(BlueprintAssignable, Category = "Zeus|Attributes")
|
||||
FZeusOnAttributesChanged OnAttributesChanged;
|
||||
|
||||
/** HP ou SP mudou (efêmero ou via snapshot). Para barras animadas. */
|
||||
UPROPERTY(BlueprintAssignable, Category = "Zeus|Attributes")
|
||||
FZeusOnHpSpChanged OnHpSpChanged;
|
||||
|
||||
/** Subiu de nível. Para efeitos visuais ("LEVEL UP!" toast). */
|
||||
UPROPERTY(BlueprintAssignable, Category = "Zeus|Attributes")
|
||||
FZeusOnLevelUp OnLevelUp;
|
||||
|
||||
/** Resposta ao C_STAT_ALLOC (aceito ou rejeitado). UI reage. */
|
||||
UPROPERTY(BlueprintAssignable, Category = "Zeus|Attributes")
|
||||
FZeusOnStatAllocReply OnStatAllocReply;
|
||||
|
||||
protected:
|
||||
UPROPERTY(BlueprintReadOnly, Category = "Zeus|Attributes")
|
||||
FZeusAttributesSnapshot Current;
|
||||
};
|
||||
51
Source/ZeusAttributes/Public/ZeusAttributeNetworkHandler.h
Normal file
51
Source/ZeusAttributes/Public/ZeusAttributeNetworkHandler.h
Normal file
@@ -0,0 +1,51 @@
|
||||
#pragma once
|
||||
|
||||
#include "CoreMinimal.h"
|
||||
#include "Subsystems/WorldSubsystem.h"
|
||||
#include "ZeusAttributesPayload.h"
|
||||
#include "ZeusAttributeNetworkHandler.generated.h"
|
||||
|
||||
class UZeusNetworkSubsystem;
|
||||
|
||||
/**
|
||||
* Ponte entre o `UZeusNetworkSubsystem` (GameInstanceSubsystem do plugin
|
||||
* ZeusNetwork) e os `UZeusAttributeComponent` ligados aos atores do mundo.
|
||||
*
|
||||
* Por que UWorldSubsystem (e nao GameInstanceSubsystem)?
|
||||
* - O registry de atores (`UZeusWorldSubsystem::GetActorByEntityId`) e'
|
||||
* World-scoped, reset a cada OpenLevel.
|
||||
* - World subsystem so existe enquanto ha mundo carregado, o que e' exato
|
||||
* quando precisamos rotear snapshots (no front-end nao ha pawn).
|
||||
*
|
||||
* Lifecycle: `Initialize` binda nos 3 delegates do ZeusNetworkSubsystem;
|
||||
* `Deinitialize` unbinda. Conversao `FZeusAttributesPayload ->
|
||||
* FZeusAttributesSnapshot` (USTRUCT Blueprint) acontece aqui.
|
||||
*/
|
||||
UCLASS()
|
||||
class ZEUSATTRIBUTES_API UZeusAttributeNetworkHandler : public UWorldSubsystem
|
||||
{
|
||||
GENERATED_BODY()
|
||||
|
||||
public:
|
||||
virtual void Initialize(FSubsystemCollectionBase& Collection) override;
|
||||
virtual void Deinitialize() override;
|
||||
|
||||
virtual bool ShouldCreateSubsystem(UObject* Outer) const override;
|
||||
|
||||
private:
|
||||
void HandleAttributeSnapshotFull(const FZeusAttributesPayload& Payload);
|
||||
void HandleHpSpUpdate(int64 EntityId, int32 Hp, int32 Sp);
|
||||
void HandleLevelUp(int64 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 UZeusAttributeComponent* FindLocalPlayerAttributeComponent() const;
|
||||
|
||||
FDelegateHandle SnapshotFullHandle;
|
||||
FDelegateHandle HpSpUpdateHandle;
|
||||
FDelegateHandle LevelUpHandle;
|
||||
FDelegateHandle StatAllocReplyHandle;
|
||||
};
|
||||
88
Source/ZeusAttributes/Public/ZeusAttributeTypes.h
Normal file
88
Source/ZeusAttributes/Public/ZeusAttributeTypes.h
Normal file
@@ -0,0 +1,88 @@
|
||||
#pragma once
|
||||
|
||||
#include "CoreMinimal.h"
|
||||
#include "ZeusAttributeTypes.generated.h"
|
||||
|
||||
/**
|
||||
* Snapshot completo dos atributos do char vindo do server via
|
||||
* S_ATTRIBUTE_SNAPSHOT_FULL (opcode 1500).
|
||||
*
|
||||
* Cliente NUNCA recalcula derivados — apenas exibe o que recebe. Os campos
|
||||
* HP/MaxHp/SP/MaxSp ja sao os "efetivos" (base + equip + buff somados no
|
||||
* servidor); o cliente nao ve as 3 camadas separadas.
|
||||
*
|
||||
* Espelha `FZeusAttributesPayload` do plugin ZeusNetwork + a serializacao
|
||||
* em `Server/.../AttributeService.cpp::WriteSnapshotPayload`.
|
||||
*/
|
||||
USTRUCT(BlueprintType)
|
||||
struct ZEUSATTRIBUTES_API FZeusAttributesSnapshot
|
||||
{
|
||||
GENERATED_BODY()
|
||||
|
||||
// === Identidade + classe ===
|
||||
UPROPERTY(BlueprintReadOnly, Category = "Zeus|Attributes") int64 EntityId = 0;
|
||||
UPROPERTY(BlueprintReadOnly, Category = "Zeus|Attributes") int32 ClassId = 0;
|
||||
|
||||
// === 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;
|
||||
UPROPERTY(BlueprintReadOnly, Category = "Zeus|Attributes|Stats") int32 Agi = 1;
|
||||
UPROPERTY(BlueprintReadOnly, Category = "Zeus|Attributes|Stats") int32 Vit = 1;
|
||||
UPROPERTY(BlueprintReadOnly, Category = "Zeus|Attributes|Stats") int32 Int = 1;
|
||||
UPROPERTY(BlueprintReadOnly, Category = "Zeus|Attributes|Stats") int32 Dex = 1;
|
||||
UPROPERTY(BlueprintReadOnly, Category = "Zeus|Attributes|Stats") int32 Luk = 1;
|
||||
|
||||
// === Pontos nao gastos ===
|
||||
UPROPERTY(BlueprintReadOnly, Category = "Zeus|Attributes") int32 StatusPoint = 0;
|
||||
UPROPERTY(BlueprintReadOnly, Category = "Zeus|Attributes") int32 SkillPoint = 0;
|
||||
|
||||
// === Pool efetivo (camadas 1+2+3 somadas no servidor) ===
|
||||
UPROPERTY(BlueprintReadOnly, Category = "Zeus|Attributes") int32 Hp = 0;
|
||||
UPROPERTY(BlueprintReadOnly, Category = "Zeus|Attributes") int32 MaxHp = 0;
|
||||
UPROPERTY(BlueprintReadOnly, Category = "Zeus|Attributes") int32 Sp = 0;
|
||||
UPROPERTY(BlueprintReadOnly, Category = "Zeus|Attributes") int32 MaxSp = 0;
|
||||
|
||||
// === Moeda ===
|
||||
UPROPERTY(BlueprintReadOnly, Category = "Zeus|Attributes") int64 Money = 0;
|
||||
|
||||
// === Derivados split em pares (base, equipBonus) ===
|
||||
// Padrao RO: display mostra "ATK 51 + 0" (base + equip). Cliente NUNCA
|
||||
// recalcula — servidor envia ambos prontos no S_ATTRIBUTE_SNAPSHOT_FULL.
|
||||
UPROPERTY(BlueprintReadOnly, Category = "Zeus|Attributes|Derived") int32 AtkBase = 0;
|
||||
UPROPERTY(BlueprintReadOnly, Category = "Zeus|Attributes|Derived") int32 AtkEquipBonus = 0;
|
||||
|
||||
/** MATK pre-renewal e' um range [min, max] (rathena status_base_matk_min/max).
|
||||
* Display: "MATK min ~ max + equip". */
|
||||
UPROPERTY(BlueprintReadOnly, Category = "Zeus|Attributes|Derived") int32 MatkBaseMin = 0;
|
||||
UPROPERTY(BlueprintReadOnly, Category = "Zeus|Attributes|Derived") int32 MatkBaseMax = 0;
|
||||
UPROPERTY(BlueprintReadOnly, Category = "Zeus|Attributes|Derived") int32 MatkEquipBonus = 0;
|
||||
|
||||
UPROPERTY(BlueprintReadOnly, Category = "Zeus|Attributes|Derived") int32 DefBase = 0;
|
||||
UPROPERTY(BlueprintReadOnly, Category = "Zeus|Attributes|Derived") int32 DefEquipBonus = 0;
|
||||
|
||||
UPROPERTY(BlueprintReadOnly, Category = "Zeus|Attributes|Derived") int32 MdefBase = 0;
|
||||
UPROPERTY(BlueprintReadOnly, Category = "Zeus|Attributes|Derived") int32 MdefEquipBonus = 0;
|
||||
|
||||
UPROPERTY(BlueprintReadOnly, Category = "Zeus|Attributes|Derived") int32 HitBase = 0;
|
||||
UPROPERTY(BlueprintReadOnly, Category = "Zeus|Attributes|Derived") int32 HitEquipBonus = 0;
|
||||
|
||||
UPROPERTY(BlueprintReadOnly, Category = "Zeus|Attributes|Derived") int32 FleeBase = 0;
|
||||
UPROPERTY(BlueprintReadOnly, Category = "Zeus|Attributes|Derived") int32 FleeEquipBonus = 0;
|
||||
|
||||
/** Critico × 10 internamente (10 = 1.0). Divida por 10 para exibir. */
|
||||
UPROPERTY(BlueprintReadOnly, Category = "Zeus|Attributes|Derived") int32 CritBaseX10 = 0;
|
||||
UPROPERTY(BlueprintReadOnly, Category = "Zeus|Attributes|Derived") int32 CritEquipBonusX10 = 0;
|
||||
|
||||
/** ASPD nao splita — calculo nao-linear via aspd_base × stats. Display direto. */
|
||||
UPROPERTY(BlueprintReadOnly, Category = "Zeus|Attributes|Derived") int32 Aspd = 0;
|
||||
};
|
||||
19
Source/ZeusAttributes/Public/ZeusAttributesModule.h
Normal file
19
Source/ZeusAttributes/Public/ZeusAttributesModule.h
Normal file
@@ -0,0 +1,19 @@
|
||||
#pragma once
|
||||
|
||||
#include "CoreMinimal.h"
|
||||
#include "Modules/ModuleInterface.h"
|
||||
|
||||
/**
|
||||
* Sub-modulo cliente do AttributeSystem.
|
||||
*
|
||||
* Espelho de `Server/ZeusServerEngine/Game/MMO/Modules/AttributeSystem/`.
|
||||
* Roda como modulo Runtime carregado em `PreDefault` para que o
|
||||
* `UAttributeComponent` esteja registrado antes do `ZMMO` instanciar o
|
||||
* player character.
|
||||
*/
|
||||
class FZeusAttributesModule : public IModuleInterface
|
||||
{
|
||||
public:
|
||||
virtual void StartupModule() override;
|
||||
virtual void ShutdownModule() override;
|
||||
};
|
||||
76
Source/ZeusAttributes/Public/ZeusHudHpSpWidget.h
Normal file
76
Source/ZeusAttributes/Public/ZeusHudHpSpWidget.h
Normal file
@@ -0,0 +1,76 @@
|
||||
#pragma once
|
||||
|
||||
#include "Blueprint/UserWidget.h"
|
||||
#include "CoreMinimal.h"
|
||||
#include "ZeusAttributeTypes.h"
|
||||
#include "ZeusHudHpSpWidget.generated.h"
|
||||
|
||||
class UProgressBar;
|
||||
class UTextBlock;
|
||||
|
||||
/**
|
||||
* Sub-widget do HUD: barras de HP/SP + label de level. Sem auto-bind no
|
||||
* AttributeComponent — o parent (`UZeusHudWidget`) propaga snapshots via
|
||||
* `ApplySnapshot` / `ApplyHpSpDelta` quando o player local recebe updates.
|
||||
*
|
||||
* Composicao do HUD (ver `UZeusHudWidget`):
|
||||
* WBP_HUD (UZeusHudWidget root)
|
||||
* |-- HpSpBar (UZeusHudHpSpWidget — este)
|
||||
* |-- PlayerInfo (futuro — nome/job/level)
|
||||
* |-- Minimap (futuro)
|
||||
* |-- QuickBar (futuro — hotkeys)
|
||||
* |-- Buffs (futuro — icones SC)
|
||||
* |-- Chat (futuro)
|
||||
* |-- TargetInfo (futuro — mob/player targetado)
|
||||
*
|
||||
* Cada sub-widget tem responsabilidade unica (SRP). O parent (UZeusHudWidget)
|
||||
* faz binding com AttributeComponent e propaga deltas.
|
||||
*
|
||||
* Convencao `meta=(BindWidget)`: o WBP filho deve ter widgets com nomes
|
||||
* exatos (HpBar, SpBar). `BindWidgetOptional` permite omitir.
|
||||
*/
|
||||
UCLASS(Abstract, Blueprintable, BlueprintType)
|
||||
class ZEUSATTRIBUTES_API UZeusHudHpSpWidget : public UUserWidget
|
||||
{
|
||||
GENERATED_BODY()
|
||||
|
||||
public:
|
||||
/** Atualiza todos os campos a partir do snapshot. Chamado pelo UZeusHudWidget. */
|
||||
UFUNCTION(BlueprintCallable, Category = "Zeus|HUD")
|
||||
void ApplySnapshot(const FZeusAttributesSnapshot& Snapshot);
|
||||
|
||||
/** Atualiza apenas HP/SP (sem mexer no level/text). Otimizacao do tick. */
|
||||
UFUNCTION(BlueprintCallable, Category = "Zeus|HUD")
|
||||
void ApplyHpSp(int32 Hp, int32 MaxHp, int32 Sp, int32 MaxSp);
|
||||
|
||||
protected:
|
||||
/**
|
||||
* Hook BlueprintNativeEvent para skin visual reagir a um snapshot novo
|
||||
* (cores, animacao de level up, etc.). Impl default em C++ apenas
|
||||
* atualiza progress bars + textos via BindWidget/BindWidgetOptional.
|
||||
*/
|
||||
UFUNCTION(BlueprintNativeEvent, Category = "Zeus|HUD")
|
||||
void OnSnapshotApplied(const FZeusAttributesSnapshot& Snapshot);
|
||||
virtual void OnSnapshotApplied_Implementation(const FZeusAttributesSnapshot& Snapshot);
|
||||
|
||||
// === BindWidget — devem existir no WBP com EXATAMENTE estes nomes ===
|
||||
UPROPERTY(BlueprintReadOnly, meta = (BindWidget), Category = "Zeus|HUD")
|
||||
UProgressBar* HpBar = nullptr;
|
||||
|
||||
UPROPERTY(BlueprintReadOnly, meta = (BindWidget), Category = "Zeus|HUD")
|
||||
UProgressBar* SpBar = nullptr;
|
||||
|
||||
UPROPERTY(BlueprintReadOnly, meta = (BindWidgetOptional), Category = "Zeus|HUD")
|
||||
UTextBlock* HpText = nullptr;
|
||||
|
||||
UPROPERTY(BlueprintReadOnly, meta = (BindWidgetOptional), Category = "Zeus|HUD")
|
||||
UTextBlock* SpText = nullptr;
|
||||
|
||||
UPROPERTY(BlueprintReadOnly, meta = (BindWidgetOptional), Category = "Zeus|HUD")
|
||||
UTextBlock* LevelText = nullptr;
|
||||
|
||||
private:
|
||||
/// Ultimo snapshot recebido (cache pra ApplyHpSp manter MaxHp/MaxSp). */
|
||||
UPROPERTY(Transient)
|
||||
FZeusAttributesSnapshot LastSnapshot;
|
||||
};
|
||||
31
Source/ZeusAttributes/ZeusAttributes.Build.cs
Normal file
31
Source/ZeusAttributes/ZeusAttributes.Build.cs
Normal file
@@ -0,0 +1,31 @@
|
||||
using UnrealBuildTool;
|
||||
|
||||
public class ZeusAttributes : ModuleRules
|
||||
{
|
||||
public ZeusAttributes(ReadOnlyTargetRules Target) : base(Target)
|
||||
{
|
||||
PCHUsage = PCHUsageMode.UseExplicitOrSharedPCHs;
|
||||
|
||||
PublicDependencyModuleNames.AddRange(new string[] {
|
||||
"Core",
|
||||
"CoreUObject",
|
||||
"Engine",
|
||||
"UMG",
|
||||
"Slate",
|
||||
"CommonUI",
|
||||
"CommonInput",
|
||||
"ZeusNetwork"
|
||||
});
|
||||
|
||||
PrivateDependencyModuleNames.AddRange(new string[] {
|
||||
"SlateCore"
|
||||
});
|
||||
|
||||
// Dependencias entre modulos (vide
|
||||
// Server/ZeusServerEngine/Game/MMO/Modules/AttributeSystem/module.json
|
||||
// — espelho do manifest no cliente):
|
||||
// - engine: Core, CoreUObject, Engine
|
||||
// - modules: [] (este e' modulo de base do MMO)
|
||||
// - plugin ZeusNetwork: para opcodes + delegates
|
||||
}
|
||||
}
|
||||
19
Source/ZeusAttributes/module.json
Normal file
19
Source/ZeusAttributes/module.json
Normal file
@@ -0,0 +1,19 @@
|
||||
{
|
||||
"name": "ZeusAttributes",
|
||||
"gameType": "MMO",
|
||||
"version": "0.1.0",
|
||||
"side": "client",
|
||||
"dependencies": {
|
||||
"engine": ["Core", "CoreUObject", "Engine", "UMG", "Slate", "CommonUI", "CommonInput"],
|
||||
"plugins": ["ZeusNetwork"],
|
||||
"modules": []
|
||||
},
|
||||
"publicHeaders": [
|
||||
"ZeusAttributesModule.h",
|
||||
"ZeusAttributeTypes.h",
|
||||
"ZeusAttributeComponent.h",
|
||||
"ZeusAttributeNetworkHandler.h"
|
||||
],
|
||||
"loadOrder": 100,
|
||||
"description": "Cliente do AttributeSystem do MMO. UActorComponent + UWorldSubsystem que recebe S_ATTRIBUTE_SNAPSHOT_FULL/HP_SP_UPDATE/LEVEL_UP do ZeusNetworkSubsystem e expõe pra UI/HUD."
|
||||
}
|
||||
Reference in New Issue
Block a user