From 98dce2f8553623809ff736d6201272deddc094bc Mon Sep 17 00:00:00 2001 From: Mateus Rodrigues Date: Wed, 3 Jun 2026 01:26:04 -0300 Subject: [PATCH] feat(zeus-gas): M7 - migrate HUD/Status to UZeusGASComponent + delete legacy MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit - ZeusHudWidget: porta de UZeusAttributeComponent legacy pra UZeusGASComponent (delegate OnSnapshotAppliedRaw via AddUObject; FZeusAttributesSnapshot construido pelo helper estatico FromPayload do plugin ZeusGAS). Logs diagnosticos no bind. - UIPlayerStatus_Window: mesmo porte (snapshot raw + StatAllocReply). Lock de spam-click preservado. - ZeusHudHpSpWidget: movido do plugin ZeusAttributes (deletado) pra ZMMO /Game/UI/InGame/. API preservada (WBP filhos nao precisam mudar). - ZeusCharacter: tira o dual-seed legacy; so' seedea UZeusGASComponent via FindComponentByClass no HandleLocalSpawnReady. - ZeusPlayerState/ZeusCharacter: comentarios atualizados pra refletir o UZeusGASComponent como dono unico. - ZMMO.Build.cs: remove dep "ZeusAttributes", adiciona "ZeusGAS" + "GameplayAbilities". - ZMMO.uproject: remove module "ZeusAttributes" (delete completo). - DefaultGame.ini: remove linha legacy comentada do Component Registry. - DefaultEngine.ini: adiciona [CoreRedirects] pra: * ZMMOJobs module + ZMMOJobDataAsset class (Package+Class) — 7 DA_Job_* * ZMMO.ZMMOMapDef struct (DT_Maps row) * ZMMO.ZMMOLoadingTipRow struct (DT_LoadingTips row) * ZMMO.ZMMOLoadingProfile/StepDef structs + EZMMOLoadingContext/StepStatus enums (DA_LoadingProfiles) - DELETE: Source/ZeusAttributes/ inteiro (5 .cpp + 5 .h + Build.cs + module.json). Pegadinha aprendida: ActiveClassRedirects so' renomeia classe (sem package); CoreRedirects (ClassRedirects/StructRedirects/EnumRedirects/ PackageRedirects) e' obrigatorio quando muda modulo OU quando o asset referencia USTRUCT/UENUM. Tipo errado nao da erro mas tambem nao funciona. Co-Authored-By: Claude Opus 4.7 --- Config/DefaultEngine.ini | 22 +- Config/DefaultGame.ini | 6 +- Source/ZMMO/Game/Entity/ZeusCharacter.cpp | 17 +- Source/ZMMO/Game/Entity/ZeusCharacter.h | 4 +- Source/ZMMO/Game/Modes/ZeusPlayerState.h | 8 +- .../Game/UI/InGame}/ZeusHudHpSpWidget.cpp | 0 .../Game/UI/InGame}/ZeusHudHpSpWidget.h | 24 +- .../UI/InGame/ZeusHudPlayerVitalsWidget.h | 2 +- Source/ZMMO/Game/UI/InGame/ZeusHudWidget.cpp | 81 ++++--- Source/ZMMO/Game/UI/InGame/ZeusHudWidget.h | 31 ++- .../Game/UI/Widgets/UIPlayerStatus_Window.cpp | 38 ++- .../Game/UI/Widgets/UIPlayerStatus_Window.h | 17 +- Source/ZMMO/ZMMO.Build.cs | 3 +- .../Private/ZeusAttributeComponent.cpp | 55 ----- .../Private/ZeusAttributeNetworkHandler.cpp | 218 ------------------ .../Private/ZeusAttributesModule.cpp | 13 -- .../Public/ZeusAttributeComponent.h | 105 --------- .../Public/ZeusAttributeNetworkHandler.h | 51 ---- .../Public/ZeusAttributeTypes.h | 88 ------- .../Public/ZeusAttributesModule.h | 19 -- Source/ZeusAttributes/ZeusAttributes.Build.cs | 31 --- Source/ZeusAttributes/module.json | 19 -- ZMMO.uproject | 5 - 23 files changed, 161 insertions(+), 696 deletions(-) rename Source/{ZeusAttributes/Private => ZMMO/Game/UI/InGame}/ZeusHudHpSpWidget.cpp (100%) rename Source/{ZeusAttributes/Public => ZMMO/Game/UI/InGame}/ZeusHudHpSpWidget.h (72%) delete mode 100644 Source/ZeusAttributes/Private/ZeusAttributeComponent.cpp delete mode 100644 Source/ZeusAttributes/Private/ZeusAttributeNetworkHandler.cpp delete mode 100644 Source/ZeusAttributes/Private/ZeusAttributesModule.cpp delete mode 100644 Source/ZeusAttributes/Public/ZeusAttributeComponent.h delete mode 100644 Source/ZeusAttributes/Public/ZeusAttributeNetworkHandler.h delete mode 100644 Source/ZeusAttributes/Public/ZeusAttributeTypes.h delete mode 100644 Source/ZeusAttributes/Public/ZeusAttributesModule.h delete mode 100644 Source/ZeusAttributes/ZeusAttributes.Build.cs delete mode 100644 Source/ZeusAttributes/module.json diff --git a/Config/DefaultEngine.ini b/Config/DefaultEngine.ini index e2f127d..402f4f8 100644 --- a/Config/DefaultEngine.ini +++ b/Config/DefaultEngine.ini @@ -104,7 +104,6 @@ GameViewportClientClassName=/Script/CommonUI.CommonGameViewportClient +ActiveClassRedirects=(OldClassName="ZMMOHudWidget",NewClassName="ZeusHudWidget") +ActiveClassRedirects=(OldClassName="ZMMOHudPlayerVitalsWidget",NewClassName="ZeusHudPlayerVitalsWidget") +ActiveClassRedirects=(OldClassName="ZMMOLoadingProfilesDataAsset",NewClassName="ZeusLoadingProfilesDataAsset") - [CoreRedirects] ; Migração v1→v2 do FUIStylePanel: TMap (Enum) → TMap. ; Os campos *_DEPRECATED preservam a leitura do asset antigo; PostSerialize copia @@ -114,6 +113,27 @@ GameViewportClientClassName=/Script/CommonUI.CommonGameViewportClient +PropertyRedirects=(OldName="/Script/ZMMO.UIStylePanel.PanelOutline",NewName="/Script/ZMMO.UIStylePanel.PanelOutline_DEPRECATED") +PropertyRedirects=(OldName="/Script/ZMMO.UIStylePanel.PanelOutlineEffect",NewName="/Script/ZMMO.UIStylePanel.PanelOutlineEffect_DEPRECATED") +; --- M7: Modulo cliente "ZMMOJobs" foi renomeado pra "ZeusJobs" + classe +; UZMMOJobDataAsset -> UZeusJobDataAsset. Os 7 DA_Job_* em /Game/ZMMO/Data/Jobs/ +; foram salvos apontando pra /Script/ZMMOJobs.ZMMOJobDataAsset (modulo + classe +; antigos). ActiveClassRedirects so' troca o NOME da classe, nao o package; +; precisamos de CoreRedirects com path completo + PackageRedirect. ++PackageRedirects=(OldName="/Script/ZMMOJobs",NewName="/Script/ZeusJobs") ++ClassRedirects=(OldName="/Script/ZMMOJobs.ZMMOJobDataAsset",NewName="/Script/ZeusJobs.ZeusJobDataAsset") + +; --- M7: USTRUCTs renomeadas (mesmo modulo ZMMO). DTs salvos com Row Structure +; apontando pro nome antigo (ZMMOMapDef, ZMMOLoadingTipRow). USTRUCT usa +; +StructRedirects (nao +ClassRedirects). ++StructRedirects=(OldName="/Script/ZMMO.ZMMOMapDef",NewName="/Script/ZMMO.ZeusMapDef") ++StructRedirects=(OldName="/Script/ZMMO.ZMMOLoadingTipRow",NewName="/Script/ZMMO.ZeusLoadingTipRow") +; DA_LoadingProfiles salvo com TMap; +; FZMMOLoadingProfile contem TArray. Precisa de 3 redirects +; (1 enum + 2 struct) pra carregar sem warnings nem perda de dados. ++EnumRedirects=(OldName="/Script/ZMMO.EZMMOLoadingContext",NewName="/Script/ZMMO.EZeusLoadingContext") ++EnumRedirects=(OldName="/Script/ZMMO.EZMMOLoadingStepStatus",NewName="/Script/ZMMO.EZeusLoadingStepStatus") ++StructRedirects=(OldName="/Script/ZMMO.ZMMOLoadingProfile",NewName="/Script/ZMMO.ZeusLoadingProfile") ++StructRedirects=(OldName="/Script/ZMMO.ZMMOLoadingStepDef",NewName="/Script/ZMMO.ZeusLoadingStepDef") + [/Script/AndroidFileServerEditor.AndroidFileServerRuntimeSettings] bEnablePlugin=True bAllowNetworkConnection=True diff --git a/Config/DefaultGame.ini b/Config/DefaultGame.ini index 1ba23a4..8a9018c 100644 --- a/Config/DefaultGame.ini +++ b/Config/DefaultGame.ini @@ -65,7 +65,11 @@ ScreenSetAsset=/Game/ZMMO/UI/InGame/DA_InGameScreenSet.DA_InGameScreenSet ; ----------------------------------------------------------------------------- [/Script/ZMMO.ZeusPlayerState] -+ComponentClasses=/Script/ZeusAttributes.ZeusAttributeComponent +; UZeusGASComponent (plugin ZeusGAS) — dono unico dos atributos do char (M7). +; Owns UZeusAbilitySystemComponent + UZeusAttributeSet (26 atributos +; espelhados do server CharAttributeSet). Bridge via UZeusGASNetworkHandler +; (UWorldSubsystem) subscribe nos delegates do UZeusNetworkSubsystem. ++ComponentClasses=/Script/ZeusGAS.ZeusGASComponent [/Script/Engine.AssetManagerSettings] -PrimaryAssetTypesToScan=(PrimaryAssetType="Map",AssetBaseClass=/Script/Engine.World,bHasBlueprintClasses=False,bIsEditorOnly=True,Directories=((Path="/Game/Maps")),SpecificAssets=,Rules=(Priority=-1,ChunkId=-1,bApplyRecursively=True,CookRule=Unknown)) diff --git a/Source/ZMMO/Game/Entity/ZeusCharacter.cpp b/Source/ZMMO/Game/Entity/ZeusCharacter.cpp index d4fae78..8752dd6 100644 --- a/Source/ZMMO/Game/Entity/ZeusCharacter.cpp +++ b/Source/ZMMO/Game/Entity/ZeusCharacter.cpp @@ -17,7 +17,7 @@ #include "Subsystems/WorldSubsystem.h" #include "UObject/ConstructorHelpers.h" #include "ZMMO.h" -#include "ZeusAttributeComponent.h" +#include "ZeusGASComponent.h" #include "ZeusPlayerState.h" #include "ZeusWorldSubsystem.h" #include "ZeusNetworkSubsystem.h" @@ -355,10 +355,11 @@ void AZeusCharacter::HandleLocalSpawnReady(const int64 InEntityId, const FVector } } - // Identidade publica (EntityId) + seed no AttributeComponent. CharName/Guild - // vem separado via S_CHAR_INFO -> HandleZeusCharInfo. AttributeComponent vive - // como subobject e recebe seed pra que o ZeusAttributeNetworkHandler consiga - // rotear o snapshot por EntityId desde o primeiro pacote. + // Identidade publica (EntityId) + seed no GAS Component. CharName/Guild + // vem separado via S_CHAR_INFO -> HandleZeusCharInfo. UZeusGASComponent + // vive como subobject do PlayerState (Component Registry) e recebe seed + // pra que o UZeusGASNetworkHandler consiga rotear o snapshot por + // EntityId desde o primeiro pacote. if (APlayerState* PS = GetPlayerState()) { if (AZeusPlayerState* ZeusPs = Cast(PS)) @@ -366,9 +367,9 @@ void AZeusCharacter::HandleLocalSpawnReady(const int64 InEntityId, const FVector ZeusPs->SetPublicIdentity(InEntityId, /*CharId*/ FString(), /*BaseLevel*/ 1, /*ClassId*/ 0); } - if (UZeusAttributeComponent* AttrComp = PS->FindComponentByClass()) + if (UZeusGASComponent* GASComp = PS->FindComponentByClass()) { - AttrComp->SeedEntityId(InEntityId); + GASComp->SeedEntityId(InEntityId); } } @@ -378,7 +379,7 @@ void AZeusCharacter::HandleLocalSpawnReady(const int64 InEntityId, const FVector // UI in-game e' responsabilidade do AZeusHUD (GameMode.HUDClass), nao do // Pawn. AHUD::BeginPlay chama UUIInGameFlowSubsystem::StartInGame, que // pushea WBP_HUD em UI.Layer.Game. UZeusHudWidget::NativeOnActivated auto-binda - // no UZeusAttributeComponent via PlayerState. + // no UZeusGASComponent via PlayerState. } void AZeusCharacter::HandleZeusCharInfo(const int64 InEntityId, const FString& CharName, const FString& GuildName) diff --git a/Source/ZMMO/Game/Entity/ZeusCharacter.h b/Source/ZMMO/Game/Entity/ZeusCharacter.h index c86ae0a..3589812 100644 --- a/Source/ZMMO/Game/Entity/ZeusCharacter.h +++ b/Source/ZMMO/Game/Entity/ZeusCharacter.h @@ -46,8 +46,8 @@ class ZMMO_API AZeusCharacter : public ACharacter, public IZeusEntityInterface UPROPERTY(VisibleAnywhere, BlueprintReadOnly, Category = "Components", meta = (AllowPrivateAccess = "true")) UCameraComponent* FollowCamera; - // AttributeComponent vive no PlayerState (AZeusPlayerState + Component Registry). - // Acesso: GetPlayerState()->GetZeusComponent(). + // GAS Component vive no PlayerState (AZeusPlayerState + Component Registry). + // Acesso: GetPlayerState()->GetZeusComponent(). protected: UPROPERTY(EditAnywhere, Category = "Input") diff --git a/Source/ZMMO/Game/Modes/ZeusPlayerState.h b/Source/ZMMO/Game/Modes/ZeusPlayerState.h index 26b2107..aed645d 100644 --- a/Source/ZMMO/Game/Modes/ZeusPlayerState.h +++ b/Source/ZMMO/Game/Modes/ZeusPlayerState.h @@ -28,11 +28,11 @@ class UActorComponent; * * ## Component Registry (Open-Closed) * - * Cada modulo (ZeusAttributes, ZeusInventory, ZeusSkills futuros) declara + * Cada modulo (ZeusGAS, ZeusInventory, ZeusSkills futuros) declara * sua component class em DefaultGame.ini: * * [/Script/ZMMO.ZeusPlayerState] - * +ComponentClasses=/Script/ZeusAttributes.ZeusAttributeComponent + * +ComponentClasses=/Script/ZeusGAS.ZeusGASComponent * +ComponentClasses=/Script/ZeusInventory.ZeusInventoryComponent * +ComponentClasses=/Script/ZeusSkills.ZeusSkillComponent * @@ -41,7 +41,7 @@ class UActorComponent; * modulos abertos pra extensao — adicionar Inventory NAO requer * recompilar/editar AZeusPlayerState. * - * Acesso: `PlayerState->FindComponentByClass()` + * Acesso: `PlayerState->FindComponentByClass()` * ou o helper `GetZeusComponent()`. */ UCLASS(Config = Game, BlueprintType) @@ -102,7 +102,7 @@ public: UFUNCTION(BlueprintCallable, Category = "Zeus|Identity") void SetCharInfo(const FString& InCharName, const FString& InGuildName); - /// Template helper: PS->GetZeusComponent() + /// Template helper: PS->GetZeusComponent() template T* GetZeusComponent() const { diff --git a/Source/ZeusAttributes/Private/ZeusHudHpSpWidget.cpp b/Source/ZMMO/Game/UI/InGame/ZeusHudHpSpWidget.cpp similarity index 100% rename from Source/ZeusAttributes/Private/ZeusHudHpSpWidget.cpp rename to Source/ZMMO/Game/UI/InGame/ZeusHudHpSpWidget.cpp diff --git a/Source/ZeusAttributes/Public/ZeusHudHpSpWidget.h b/Source/ZMMO/Game/UI/InGame/ZeusHudHpSpWidget.h similarity index 72% rename from Source/ZeusAttributes/Public/ZeusHudHpSpWidget.h rename to Source/ZMMO/Game/UI/InGame/ZeusHudHpSpWidget.h index 107fcbf..7fc6d60 100644 --- a/Source/ZeusAttributes/Public/ZeusHudHpSpWidget.h +++ b/Source/ZMMO/Game/UI/InGame/ZeusHudHpSpWidget.h @@ -2,7 +2,7 @@ #include "Blueprint/UserWidget.h" #include "CoreMinimal.h" -#include "ZeusAttributeTypes.h" +#include "ZeusAttributesSnapshot.h" #include "ZeusHudHpSpWidget.generated.h" class UProgressBar; @@ -10,27 +10,17 @@ 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. + * GAS Component — o parent (`UZeusHudWidget`) propaga snapshots via + * `ApplySnapshot` / `ApplyHpSp` 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. + * Migrado de ZeusAttributes plugin pra ZMMO em M7 (delecao do plugin + * legacy). API preservada — WBP que herda dele nao precisa mudar. * * 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 +class ZMMO_API UZeusHudHpSpWidget : public UUserWidget { GENERATED_BODY() @@ -70,7 +60,7 @@ protected: UTextBlock* LevelText = nullptr; private: - /// Ultimo snapshot recebido (cache pra ApplyHpSp manter MaxHp/MaxSp). */ + /// Ultimo snapshot recebido (cache pra ApplyHpSp manter MaxHp/MaxSp). UPROPERTY(Transient) FZeusAttributesSnapshot LastSnapshot; }; diff --git a/Source/ZMMO/Game/UI/InGame/ZeusHudPlayerVitalsWidget.h b/Source/ZMMO/Game/UI/InGame/ZeusHudPlayerVitalsWidget.h index 0cfeaea..167eae4 100644 --- a/Source/ZMMO/Game/UI/InGame/ZeusHudPlayerVitalsWidget.h +++ b/Source/ZMMO/Game/UI/InGame/ZeusHudPlayerVitalsWidget.h @@ -2,7 +2,7 @@ #include "CommonUserWidget.h" #include "CoreMinimal.h" -#include "ZeusAttributeTypes.h" +#include "ZeusAttributesSnapshot.h" #include "ZeusHudPlayerVitalsWidget.generated.h" class UUIProgressBar_Base; diff --git a/Source/ZMMO/Game/UI/InGame/ZeusHudWidget.cpp b/Source/ZMMO/Game/UI/InGame/ZeusHudWidget.cpp index 8543cd7..b7c426f 100644 --- a/Source/ZMMO/Game/UI/InGame/ZeusHudWidget.cpp +++ b/Source/ZMMO/Game/UI/InGame/ZeusHudWidget.cpp @@ -8,7 +8,8 @@ #include "GameFramework/PlayerState.h" #include "TimerManager.h" #include "ZMMO.h" -#include "ZeusAttributeComponent.h" +#include "ZeusAttributesPayload.h" // FZeusAttributesPayload (ZeusNetwork) +#include "ZeusGASComponent.h" // UZeusGASComponent (ZeusGAS) #include "ZeusHudHpSpWidget.h" #include "ZeusHudPlayerVitalsWidget.h" #include "ZeusJobsLibrary.h" @@ -68,40 +69,61 @@ void UZeusHudWidget::BindToLocalPlayer() if (!PS) { // PlayerState pode nao existir ainda — race com GameMode setup. - // AZeusCharacter::HandleLocalSpawnReady chama BindToAttributeComponent - // manualmente como fallback (seed do EntityId). - UE_LOG(LogZMMO, Log, TEXT("ZeusHudWidget: PlayerState ausente no activate (race fix via HandleLocalSpawnReady esperado)")); + UE_LOG(LogZMMO, Log, TEXT("ZeusHudWidget: PlayerState ausente no activate (esperado se ativou antes do replicar)")); + return; + } + UZeusGASComponent* Comp = PS->FindComponentByClass(); + if (!Comp) + { + UE_LOG(LogZMMO, Warning, TEXT("ZeusHudWidget::BindToLocalPlayer: UZeusGASComponent NAO encontrado em PS=%s"), + *PS->GetName()); return; } - UZeusAttributeComponent* Comp = PS->FindComponentByClass(); - if (!Comp) { return; } if (Comp == BoundComponent.Get()) { return; } // idempotente UnbindFromComponent(); BoundComponent = Comp; - Comp->OnAttributesChanged.AddDynamic(this, &UZeusHudWidget::HandleAttributesChanged); - Comp->OnHpSpChanged.AddDynamic(this, &UZeusHudWidget::HandleHpSpChanged); + // Raw e' non-dynamic (FZeusAttributesPayload nao e' BlueprintType) — AddUObject. + SnapshotRawHandle = Comp->OnSnapshotAppliedRaw.AddUObject(this, &UZeusHudWidget::HandleSnapshotRaw); + Comp->OnHpSpUpdated.AddDynamic(this, &UZeusHudWidget::HandleHpSpUpdated); Comp->OnLevelUp.AddDynamic(this, &UZeusHudWidget::HandleLevelUp); - // Refresh imediato — caso o snapshot ja' tenha chegado. - HandleAttributesChanged(Comp->GetSnapshot()); + UE_LOG(LogZMMO, Log, TEXT("ZeusHudWidget::BindToLocalPlayer: bound to %s (HasSnapshot=%d, PlayerVitals=%s, HpSpBar=%s)"), + *Comp->GetName(), Comp->HasSnapshot() ? 1 : 0, + PlayerVitals ? *PlayerVitals->GetName() : TEXT("nullptr"), + HpSpBar ? *HpSpBar->GetName() : TEXT("nullptr")); + + // Refresh imediato — caso o snapshot ja' tenha chegado antes do bind. + if (Comp->HasSnapshot()) + { + HandleSnapshotRaw(Comp->GetLastSnapshot()); + } } void UZeusHudWidget::UnbindFromComponent() { - if (UZeusAttributeComponent* Old = BoundComponent.Get()) + if (UZeusGASComponent* Old = BoundComponent.Get()) { - Old->OnAttributesChanged.RemoveDynamic(this, &UZeusHudWidget::HandleAttributesChanged); - Old->OnHpSpChanged.RemoveDynamic(this, &UZeusHudWidget::HandleHpSpChanged); + if (SnapshotRawHandle.IsValid()) + { + Old->OnSnapshotAppliedRaw.Remove(SnapshotRawHandle); + SnapshotRawHandle.Reset(); + } + Old->OnHpSpUpdated.RemoveDynamic(this, &UZeusHudWidget::HandleHpSpUpdated); Old->OnLevelUp.RemoveDynamic(this, &UZeusHudWidget::HandleLevelUp); } BoundComponent.Reset(); } -void UZeusHudWidget::HandleAttributesChanged(const FZeusAttributesSnapshot& Snapshot) +void UZeusHudWidget::HandleSnapshotRaw(const FZeusAttributesPayload& Payload) { - // Propaga snapshot aos sub-widgets que ja existem. + const FZeusAttributesSnapshot Snapshot = FZeusAttributesSnapshot::FromPayload(Payload); + + UE_LOG(LogZMMO, Log, TEXT("ZeusHudWidget::HandleSnapshotRaw: entity=%lld level=%d hp=%d/%d (PlayerVitals=%s)"), + Snapshot.EntityId, Snapshot.BaseLevel, Snapshot.Hp, Snapshot.MaxHp, + PlayerVitals ? TEXT("ok") : TEXT("NULLPTR")); + if (HpSpBar) { HpSpBar->ApplySnapshot(Snapshot); @@ -110,9 +132,8 @@ void UZeusHudWidget::HandleAttributesChanged(const FZeusAttributesSnapshot& Snap { PlayerVitals->ApplySnapshot(Snapshot); - // Identidade: nome vem do PlayerState (não está no snapshot); job vem - // do JobsSubsystem resolvendo ClassId -> display name. - if (UZeusAttributeComponent* Comp = BoundComponent.Get()) + // Identidade: nome vem do PlayerState; job vem do JobsSubsystem. + if (UZeusGASComponent* Comp = BoundComponent.Get()) { if (const AZeusPlayerState* PS = Cast(Comp->GetOwner())) { @@ -125,19 +146,21 @@ void UZeusHudWidget::HandleAttributesChanged(const FZeusAttributesSnapshot& Snap // Futuros: Buffs->RefreshIcons, etc. } -void UZeusHudWidget::HandleHpSpChanged(int32 Hp, int32 Sp) +void UZeusHudWidget::HandleHpSpUpdated(float Hp, float Sp) { - if (UZeusAttributeComponent* Comp = BoundComponent.Get()) + UZeusGASComponent* Comp = BoundComponent.Get(); + if (!Comp || !Comp->HasSnapshot()) { return; } + + const FZeusAttributesPayload& P = Comp->GetLastSnapshot(); + const int32 HpInt = static_cast(Hp); + const int32 SpInt = static_cast(Sp); + if (HpSpBar) { - const FZeusAttributesSnapshot& Snap = Comp->GetSnapshot(); - if (HpSpBar) - { - HpSpBar->ApplyHpSp(Hp, Snap.MaxHp, Sp, Snap.MaxSp); - } - if (PlayerVitals) - { - PlayerVitals->ApplyHpSp(Hp, Snap.MaxHp, Sp, Snap.MaxSp); - } + HpSpBar->ApplyHpSp(HpInt, P.MaxHp, SpInt, P.MaxSp); + } + if (PlayerVitals) + { + PlayerVitals->ApplyHpSp(HpInt, P.MaxHp, SpInt, P.MaxSp); } } diff --git a/Source/ZMMO/Game/UI/InGame/ZeusHudWidget.h b/Source/ZMMO/Game/UI/InGame/ZeusHudWidget.h index 2639ee3..6376c1a 100644 --- a/Source/ZMMO/Game/UI/InGame/ZeusHudWidget.h +++ b/Source/ZMMO/Game/UI/InGame/ZeusHudWidget.h @@ -3,14 +3,15 @@ #include "CommonActivatableWidget.h" #include "CoreMinimal.h" #include "Engine/TimerHandle.h" -#include "ZeusAttributeTypes.h" +#include "ZeusAttributesSnapshot.h" #include "ZeusHudWidget.generated.h" class UTextBlock; class UWidget; -class UZeusAttributeComponent; +class UZeusGASComponent; class UZeusHudHpSpWidget; class UZeusHudPlayerVitalsWidget; +struct FZeusAttributesPayload; /** * Root da HUD de gameplay. UCommonActivatableWidget pushed pelo @@ -18,7 +19,7 @@ class UZeusHudPlayerVitalsWidget; * * **Vive em ZMMO core** (UI/InGame/) porque o HUD root e' generico pra * qualquer GameType — sub-widgets vem dos modulos especializados - * (HpSpBar do sub-modulo ZeusAttributes, Minimap de ZeusMap futuro, + * (HpSpBar local do ZMMO, Minimap de ZeusMap futuro, * Chat de ZeusChat futuro, etc.). * * Padrao **composite**: o WBP_HUD (filho) declara as PECAS visuais como @@ -32,7 +33,7 @@ class UZeusHudPlayerVitalsWidget; * * Composicao prevista (sub-widgets — todos `BindWidgetOptional` pra * permitir GameTypes sem modulos opcionais — ex.: Survival sem stats RO): - * - HpSpBar (UZeusHudHpSpWidget — sub-modulo ZeusAttributes) — impl + * - HpSpBar (UZeusHudHpSpWidget — local UI/InGame/) — impl * - PlayerInfo (futuro — nome/job/level/exp bar) * - Minimap (futuro) * - QuickBar (futuro — hotkeys de skills) @@ -70,7 +71,7 @@ protected: */ virtual TOptional GetDesiredInputConfig() const override; - /// Sub-widget HP/SP/level (sub-modulo ZeusAttributes). BindWidgetOptional + /// Sub-widget HP/SP/level (local UI/InGame/). BindWidgetOptional /// — WBP_HUD pode omitir se o GameType nao usa Attributes (Survival, etc.). UPROPERTY(BlueprintReadOnly, meta = (BindWidgetOptional), Category = "Zeus|HUD") UZeusHudHpSpWidget* HpSpBar = nullptr; @@ -106,25 +107,31 @@ protected: // UPROPERTY(BlueprintReadOnly, meta = (BindWidgetOptional)) UZeusHudTargetInfoWidget* TargetInfo = nullptr; UFUNCTION(BlueprintPure, Category = "Zeus|HUD") - UZeusAttributeComponent* GetBoundComponent() const { return BoundComponent.Get(); } + UZeusGASComponent* GetBoundComponent() const { return BoundComponent.Get(); } private: - /// Tenta achar o UZeusAttributeComponent do player local + subscrever - /// nos delegates. Idempotente. + /// Tenta achar o UZeusGASComponent do player local + subscrever nos + /// delegates. Idempotente. void BindToLocalPlayer(); void UnbindFromComponent(); - UFUNCTION() - void HandleAttributesChanged(const FZeusAttributesSnapshot& Snapshot); + /// Listener C++ no payload bruto do UZeusGASComponent (nao e' UFUNCTION + /// porque FZeusAttributesPayload nao e' BlueprintType — delegate multicast + /// nativo). + void HandleSnapshotRaw(const FZeusAttributesPayload& Payload); UFUNCTION() - void HandleHpSpChanged(int32 Hp, int32 Sp); + void HandleHpSpUpdated(float Hp, float Sp); UFUNCTION() void HandleLevelUp(int32 NewBaseLevel, int32 StatusPointDelta); UPROPERTY(Transient) - TWeakObjectPtr BoundComponent; + TWeakObjectPtr BoundComponent; + + /// Handle do listener nao-dynamic em OnSnapshotAppliedRaw — guardar pra remover + /// em UnbindFromComponent (AddUObject nao auto-cleanup como AddDynamic). + FDelegateHandle SnapshotRawHandle; void ShowLevelUpToast(int32 NewBaseLevel); void HideLevelUpToast(); diff --git a/Source/ZMMO/Game/UI/Widgets/UIPlayerStatus_Window.cpp b/Source/ZMMO/Game/UI/Widgets/UIPlayerStatus_Window.cpp index 1152be7..3e29827 100644 --- a/Source/ZMMO/Game/UI/Widgets/UIPlayerStatus_Window.cpp +++ b/Source/ZMMO/Game/UI/Widgets/UIPlayerStatus_Window.cpp @@ -10,7 +10,8 @@ #include "GameFramework/PlayerState.h" #include "Internationalization/Text.h" #include "ZMMO.h" -#include "ZeusAttributeComponent.h" +#include "ZeusAttributesPayload.h" // FZeusAttributesPayload (ZeusNetwork) +#include "ZeusGASComponent.h" // UZeusGASComponent (ZeusGAS) #include "ZeusJobDataAsset.h" #include "ZeusJobsSubsystem.h" #include "ZeusPlayerState.h" @@ -123,30 +124,40 @@ void UUIPlayerStatus_Window::BindToLocalPlayer() Text_CharName->SetText(FText::FromString(DisplayName)); } - UZeusAttributeComponent* Comp = PC->PlayerState->FindComponentByClass(); + UZeusGASComponent* Comp = PC->PlayerState->FindComponentByClass(); if (!Comp || Comp == BoundComponent.Get()) return; UnbindFromComponent(); BoundComponent = Comp; - Comp->OnAttributesChanged.AddDynamic(this, &UUIPlayerStatus_Window::HandleAttributesChanged); + // Raw e' non-dynamic (FZeusAttributesPayload nao e' BlueprintType) — AddUObject. + SnapshotRawHandle = Comp->OnSnapshotAppliedRaw.AddUObject(this, &UUIPlayerStatus_Window::HandleSnapshotRaw); Comp->OnStatAllocReply.AddDynamic(this, &UUIPlayerStatus_Window::HandleStatAllocReply); - RefreshFromSnapshot(Comp->GetSnapshot()); + + // Refresh imediato — caso o snapshot ja' tenha chegado antes do bind. + if (Comp->HasSnapshot()) + { + HandleSnapshotRaw(Comp->GetLastSnapshot()); + } } void UUIPlayerStatus_Window::UnbindFromComponent() { - if (UZeusAttributeComponent* Old = BoundComponent.Get()) + if (UZeusGASComponent* Old = BoundComponent.Get()) { - Old->OnAttributesChanged.RemoveDynamic(this, &UUIPlayerStatus_Window::HandleAttributesChanged); + if (SnapshotRawHandle.IsValid()) + { + Old->OnSnapshotAppliedRaw.Remove(SnapshotRawHandle); + SnapshotRawHandle.Reset(); + } Old->OnStatAllocReply.RemoveDynamic(this, &UUIPlayerStatus_Window::HandleStatAllocReply); } BoundComponent.Reset(); } -void UUIPlayerStatus_Window::HandleAttributesChanged(const FZeusAttributesSnapshot& Snapshot) +void UUIPlayerStatus_Window::HandleSnapshotRaw(const FZeusAttributesPayload& Payload) { bPlusButtonsLocked = false; - RefreshFromSnapshot(Snapshot); + RefreshFromSnapshot(FZeusAttributesSnapshot::FromPayload(Payload)); } void UUIPlayerStatus_Window::HandleStatAllocReply(bool bAccepted, int32 Reason) @@ -155,17 +166,20 @@ void UUIPlayerStatus_Window::HandleStatAllocReply(bool bAccepted, int32 Reason) bAccepted ? 1 : 0, Reason); bPlusButtonsLocked = false; - if (UZeusAttributeComponent* Comp = BoundComponent.Get()) + if (UZeusGASComponent* Comp = BoundComponent.Get()) { - const FZeusAttributesSnapshot& S = Comp->GetSnapshot(); - SetAllPrimaryRowsPlusEnabled(S.StatusPoint > 0); + if (Comp->HasSnapshot()) + { + const FZeusAttributesPayload& P = Comp->GetLastSnapshot(); + SetAllPrimaryRowsPlusEnabled(P.StatusPoint > 0); + } } } void UUIPlayerStatus_Window::HandleRowPlusClicked(int32 StatId) { if (bPlusButtonsLocked) return; - UZeusAttributeComponent* Comp = BoundComponent.Get(); + UZeusGASComponent* Comp = BoundComponent.Get(); if (!Comp) return; bPlusButtonsLocked = true; diff --git a/Source/ZMMO/Game/UI/Widgets/UIPlayerStatus_Window.h b/Source/ZMMO/Game/UI/Widgets/UIPlayerStatus_Window.h index c5a5a17..9a2ab21 100644 --- a/Source/ZMMO/Game/UI/Widgets/UIPlayerStatus_Window.h +++ b/Source/ZMMO/Game/UI/Widgets/UIPlayerStatus_Window.h @@ -2,7 +2,7 @@ #include "CoreMinimal.h" #include "UI/Common/UIDraggableWindow_Base.h" -#include "ZeusAttributeTypes.h" +#include "ZeusAttributesSnapshot.h" #include "UIPlayerStatus_Window.generated.h" class UButton; @@ -10,7 +10,8 @@ class UTextBlock; class UHorizontalBox; class UUIPlayerStatus_BonusRow; class UUIPlayerStatus_StatRow; -class UZeusAttributeComponent; +class UZeusGASComponent; +struct FZeusAttributesPayload; /** * Janela de Status do player — composta por instâncias dos sub-widgets @@ -98,13 +99,21 @@ private: void SetAllPrimaryRowsPlusEnabled(bool bEnabled); void BindRow(UUIPlayerStatus_StatRow* Row, int32 StatId, const TCHAR* IconLetter, const TCHAR* DisplayName); - UFUNCTION() void HandleAttributesChanged(const FZeusAttributesSnapshot& Snapshot); + /// Listener C++ no payload bruto (FZeusAttributesPayload nao e' BlueprintType + /// — delegate multicast nativo). Converte pra FZeusAttributesSnapshot e + /// chama RefreshFromSnapshot. + void HandleSnapshotRaw(const FZeusAttributesPayload& Payload); + UFUNCTION() void HandleStatAllocReply(bool bAccepted, int32 Reason); UFUNCTION() void HandleRowPlusClicked(int32 StatId); UFUNCTION() void HandleCloseClicked(); UPROPERTY(Transient) - TWeakObjectPtr BoundComponent; + TWeakObjectPtr BoundComponent; + + /// Handle do listener nao-dynamic em OnSnapshotAppliedRaw — guardar pra remover + /// em UnbindFromComponent (AddUObject nao auto-cleanup como AddDynamic). + FDelegateHandle SnapshotRawHandle; /** Trava local pra evitar spam-click: vira true ao clicar, false ao * receber StatAllocReply (ou snapshot novo, o que vier primeiro). */ diff --git a/Source/ZMMO/ZMMO.Build.cs b/Source/ZMMO/ZMMO.Build.cs index 7e9999e..c509c55 100644 --- a/Source/ZMMO/ZMMO.Build.cs +++ b/Source/ZMMO/ZMMO.Build.cs @@ -18,8 +18,9 @@ public class ZMMO : ModuleRules "CommonUI", "CommonInput", "GameplayTags", + "GameplayAbilities", // GAS: UAttributeSet, FGameplayAttributeData "ZeusNetwork", - "ZeusAttributes", + "ZeusGAS", // UZeusGASComponent, UZeusAttributeSet, FZeusAttributesSnapshot "ZeusJobs" }); diff --git a/Source/ZeusAttributes/Private/ZeusAttributeComponent.cpp b/Source/ZeusAttributes/Private/ZeusAttributeComponent.cpp deleted file mode 100644 index 1ac6ed0..0000000 --- a/Source/ZeusAttributes/Private/ZeusAttributeComponent.cpp +++ /dev/null @@ -1,55 +0,0 @@ -#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(); - if (!Net) { return; } - Net->SendStatAlloc(StatId, Amount); -} diff --git a/Source/ZeusAttributes/Private/ZeusAttributeNetworkHandler.cpp b/Source/ZeusAttributes/Private/ZeusAttributeNetworkHandler.cpp deleted file mode 100644 index af3a201..0000000 --- a/Source/ZeusAttributes/Private/ZeusAttributeNetworkHandler.cpp +++ /dev/null @@ -1,218 +0,0 @@ -#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(); - 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(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(); -} - -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(); - 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(); -} diff --git a/Source/ZeusAttributes/Private/ZeusAttributesModule.cpp b/Source/ZeusAttributes/Private/ZeusAttributesModule.cpp deleted file mode 100644 index cdfcbd3..0000000 --- a/Source/ZeusAttributes/Private/ZeusAttributesModule.cpp +++ /dev/null @@ -1,13 +0,0 @@ -#include "ZeusAttributesModule.h" - -#include "Modules/ModuleManager.h" - -void FZeusAttributesModule::StartupModule() -{ -} - -void FZeusAttributesModule::ShutdownModule() -{ -} - -IMPLEMENT_MODULE(FZeusAttributesModule, ZeusAttributes) diff --git a/Source/ZeusAttributes/Public/ZeusAttributeComponent.h b/Source/ZeusAttributes/Public/ZeusAttributeComponent.h deleted file mode 100644 index 39ff4e2..0000000 --- a/Source/ZeusAttributes/Public/ZeusAttributeComponent.h +++ /dev/null @@ -1,105 +0,0 @@ -#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(Current.Hp) / static_cast(Current.MaxHp) : 0.f; - } - - UFUNCTION(BlueprintPure, Category = "Zeus|Attributes") - float GetSpRatio() const - { - return Current.MaxSp > 0 ? static_cast(Current.Sp) / static_cast(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; -}; diff --git a/Source/ZeusAttributes/Public/ZeusAttributeNetworkHandler.h b/Source/ZeusAttributes/Public/ZeusAttributeNetworkHandler.h deleted file mode 100644 index 12ca4cd..0000000 --- a/Source/ZeusAttributes/Public/ZeusAttributeNetworkHandler.h +++ /dev/null @@ -1,51 +0,0 @@ -#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; -}; diff --git a/Source/ZeusAttributes/Public/ZeusAttributeTypes.h b/Source/ZeusAttributes/Public/ZeusAttributeTypes.h deleted file mode 100644 index 7e34477..0000000 --- a/Source/ZeusAttributes/Public/ZeusAttributeTypes.h +++ /dev/null @@ -1,88 +0,0 @@ -#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; -}; diff --git a/Source/ZeusAttributes/Public/ZeusAttributesModule.h b/Source/ZeusAttributes/Public/ZeusAttributesModule.h deleted file mode 100644 index 0878053..0000000 --- a/Source/ZeusAttributes/Public/ZeusAttributesModule.h +++ /dev/null @@ -1,19 +0,0 @@ -#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; -}; diff --git a/Source/ZeusAttributes/ZeusAttributes.Build.cs b/Source/ZeusAttributes/ZeusAttributes.Build.cs deleted file mode 100644 index f5b942b..0000000 --- a/Source/ZeusAttributes/ZeusAttributes.Build.cs +++ /dev/null @@ -1,31 +0,0 @@ -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 - } -} diff --git a/Source/ZeusAttributes/module.json b/Source/ZeusAttributes/module.json deleted file mode 100644 index 5971739..0000000 --- a/Source/ZeusAttributes/module.json +++ /dev/null @@ -1,19 +0,0 @@ -{ - "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." -} diff --git a/ZMMO.uproject b/ZMMO.uproject index d8902ed..1c695b2 100644 --- a/ZMMO.uproject +++ b/ZMMO.uproject @@ -9,11 +9,6 @@ "Type": "Runtime", "LoadingPhase": "Default" }, - { - "Name": "ZeusAttributes", - "Type": "Runtime", - "LoadingPhase": "PreDefault" - }, { "Name": "ZeusJobs", "Type": "Runtime",