From bacf987e0b8d308829fd487de14cc336b1a6235d Mon Sep 17 00:00:00 2001 From: Mateus Rodrigues Date: Sat, 23 May 2026 19:19:41 -0300 Subject: [PATCH] =?UTF-8?q?feat(jobs):=20novo=20m=C3=B3dulo=20ZMMOJobs=20(?= =?UTF-8?q?peer=20de=20ZMMOAttributes)=20+=20DA=5FJob=5FNovice?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Sub-módulo cliente do JobsSystem do server. Pavimenta dados visuais/UI por job (DisplayName localizado, ícone, anim override, som de level up) sem acoplar ao ZMMOAttributes. ARQUITETURA — espelha o peer-module pattern do server (PR #9): Server: Game/MMO/Modules/JobsSystem (módulo núcleo, LoadOrder=50) Client: Source/ZMMOJobs/ (LoadingPhase=PreDefault, peer de ZMMOAttributes) ZMMOAttributes NÃO depende de ZMMOJobs — server envia classId no snapshot, cliente faz lookup local via Library/Subsystem. NOVOS ARQUIVOS C++ module (Source/ZMMOJobs/): - module.json + ZMMOJobs.Build.cs (deps: Core/Engine/AssetRegistry) - ZMMOJobsModule.h/.cpp — IMPLEMENT_MODULE - ZMMOJobDataAsset.h/.cpp — UPrimaryDataAsset (BlueprintType) campos: ClassId, TechnicalName, DisplayName, JobIcon (soft), DefaultAnimInstanceClass, JobChangeMontage (soft), LevelUpSoundCue (soft) - ZMMOJobsSubsystem.h/.cpp — UGameInstanceSubsystem scan AssetRegistry no Initialize + cache TMap por ClassId O(1) GetJobData(ClassId) - ZMMOJobsLibrary.h/.cpp — UBlueprintFunctionLibrary GetJobDisplayName/GetJobData/IsJobRegistered fallback "Classe N" se ClassId desconhecido Content: - Content/ZMMO/Data/Jobs/DA_Job_Novice.uasset (ClassId=0, TechnicalName="Novice", DisplayName="Aprendiz") MODIFICADOS - ZMMO.uproject: + Module "ZMMOJobs" (LoadingPhase PreDefault) - Source/ZMMO/ZMMO.Build.cs: + "ZMMOJobs" em PublicDependencyModuleNames - Source/ZMMO/Game/UI/FrontEnd/UICharCard_Base.cpp:51 — primeiro consumer do Library: "Classe %d" hardcoded → UZMMOJobsLibrary::GetJobDisplayName(this, ClassId) Card da seleção de personagem agora mostra "Aprendiz" via DA lookup. BLUEPRINT ACCESS Toda a API exposta pra BP via UFUNCTION(BlueprintPure): - UZMMOJobsLibrary::GetJobDisplayName(Self, ClassId) → FText - UZMMOJobsLibrary::GetJobData(Self, ClassId) → UZMMOJobDataAsset* - UZMMOJobsSubsystem::GetJobData(ClassId) - UZMMOJobDataAsset propriedades BlueprintReadOnly (UMG binding direto) LOOKUP AssetRegistry varre /Game/ por UZMMOJobDataAsset no boot do GameInstance. Chave = campo ClassId do asset (não nome do arquivo — renomear .uasset OK). Lookup O(1) via TMap. Custo de scan amortizado em 1 vez por sessão (~50ms pra 50 jobs futuro). VALIDAÇÃO Editor: [Log] DA class found: [Log] Subsystem class found: [Log] Library class found: [Log] AssetRegistry encontrou 1 ZMMOJobDataAsset(s): - DA_Job_Novice @ /Game/ZMMO/Data/Jobs/DA_Job_Novice Próximo módulo cliente: 1 linha no .uproject + .Build.cs + ZMMOJobs.Build.cs não muda. Pattern estabelecido. Co-Authored-By: Claude Opus 4.7 --- Content/ZMMO/Data/Jobs/DA_Job_Novice.uasset | Bin 0 -> 1422 bytes .../ZMMO/Game/UI/FrontEnd/UICharCard_Base.cpp | 3 +- Source/ZMMO/ZMMO.Build.cs | 3 +- Source/ZMMOJobs/Private/ZMMOJobDataAsset.cpp | 5 ++ Source/ZMMOJobs/Private/ZMMOJobsLibrary.cpp | 54 +++++++++++++ Source/ZMMOJobs/Private/ZMMOJobsModule.cpp | 13 +++ Source/ZMMOJobs/Private/ZMMOJobsSubsystem.cpp | 67 ++++++++++++++++ Source/ZMMOJobs/Public/ZMMOJobDataAsset.h | 75 ++++++++++++++++++ Source/ZMMOJobs/Public/ZMMOJobsLibrary.h | 39 +++++++++ Source/ZMMOJobs/Public/ZMMOJobsModule.h | 23 ++++++ Source/ZMMOJobs/Public/ZMMOJobsSubsystem.h | 52 ++++++++++++ Source/ZMMOJobs/ZMMOJobs.Build.cs | 26 ++++++ Source/ZMMOJobs/module.json | 18 +++++ ZMMO.uproject | 5 ++ 14 files changed, 381 insertions(+), 2 deletions(-) create mode 100644 Content/ZMMO/Data/Jobs/DA_Job_Novice.uasset create mode 100644 Source/ZMMOJobs/Private/ZMMOJobDataAsset.cpp create mode 100644 Source/ZMMOJobs/Private/ZMMOJobsLibrary.cpp create mode 100644 Source/ZMMOJobs/Private/ZMMOJobsModule.cpp create mode 100644 Source/ZMMOJobs/Private/ZMMOJobsSubsystem.cpp create mode 100644 Source/ZMMOJobs/Public/ZMMOJobDataAsset.h create mode 100644 Source/ZMMOJobs/Public/ZMMOJobsLibrary.h create mode 100644 Source/ZMMOJobs/Public/ZMMOJobsModule.h create mode 100644 Source/ZMMOJobs/Public/ZMMOJobsSubsystem.h create mode 100644 Source/ZMMOJobs/ZMMOJobs.Build.cs create mode 100644 Source/ZMMOJobs/module.json diff --git a/Content/ZMMO/Data/Jobs/DA_Job_Novice.uasset b/Content/ZMMO/Data/Jobs/DA_Job_Novice.uasset new file mode 100644 index 0000000000000000000000000000000000000000..e6a0629d9c2c27ed60b863f85cf8c1f8f3802177 GIT binary patch literal 1422 zcma)6O-K|`9Dl8vr73CJ$quDpxws$cre+`P?(9mTtELi(rQ`U>$#rL&nKsu`AcfbU zkS-mfZb4B8p$@rdmmr}-hk`oQLQ4^ZRCsXv|7K@w`~v^*=Dq*td+-1I%}Y(y-T#x# zW{2_^D{?TlOf%%e`LPTA_b%@1E?T^G;**q#q#dc{e8zT=@5kuVFW&-pmWn@mANXH} zU5Ck5O~c(Lj&pZ^M@Oey5OvXgRvFgZf`5?a!HANOB~C$%mC%?*9g-KcC6KU6I77JSpn35<{>S7pb-7dU6_7dUUl*RpMOLldLtR)|8U1mrJ8DnX-)9 zSCcbo+_#rc=&PX%$MxB2GHwIJ`L8Q_1wwlyRgUZKprZ1=&fzOu(%IiL^{2{gKCl5i zVBzl#30yiD6*Z0N*C%e{U~AdY&Yo56BCB~%rnOC1qL^6(r1EPT*V$vQqmJwbawZA3 zN25Yyk?hV*Y=z)J(;%RMC01OqWuO@+gV^H$XU>}Y7MTk2hRikK-byU`!N0ziJ!inK ztXreTrd`_D;8|j1#yk$vHbxvGtA(qDoohq?QDQF*1C7A7*hrKE9I`Nmc1DPs37&)c zB%u!kSJALb;&XF% N{+m}vJf;!x{{U4~EIj}K literal 0 HcmV?d00001 diff --git a/Source/ZMMO/Game/UI/FrontEnd/UICharCard_Base.cpp b/Source/ZMMO/Game/UI/FrontEnd/UICharCard_Base.cpp index 8263248..853bd01 100644 --- a/Source/ZMMO/Game/UI/FrontEnd/UICharCard_Base.cpp +++ b/Source/ZMMO/Game/UI/FrontEnd/UICharCard_Base.cpp @@ -5,6 +5,7 @@ #include "Engine/World.h" #include "TimerManager.h" #include "UI/Widgets/UIButton_Base.h" +#include "ZMMOJobsLibrary.h" void UUICharCard_Base::NativeConstruct() { @@ -48,7 +49,7 @@ void UUICharCard_Base::SetFromSummary(const FZMMOCharSummary& InSummary) } if (Text_Class) { - Text_Class->SetText(FText::FromString(FString::Printf(TEXT("Classe %d"), Summary.ClassId))); + Text_Class->SetText(UZMMOJobsLibrary::GetJobDisplayName(this, Summary.ClassId)); } const ESlateVisibility ActionVis = bDeleteScheduled ? ESlateVisibility::Collapsed : ESlateVisibility::Visible; diff --git a/Source/ZMMO/ZMMO.Build.cs b/Source/ZMMO/ZMMO.Build.cs index 80ae3e4..f911655 100644 --- a/Source/ZMMO/ZMMO.Build.cs +++ b/Source/ZMMO/ZMMO.Build.cs @@ -19,7 +19,8 @@ public class ZMMO : ModuleRules "CommonInput", "GameplayTags", "ZeusNetwork", - "ZMMOAttributes" + "ZMMOAttributes", + "ZMMOJobs" }); PrivateDependencyModuleNames.AddRange(new string[] { diff --git a/Source/ZMMOJobs/Private/ZMMOJobDataAsset.cpp b/Source/ZMMOJobs/Private/ZMMOJobDataAsset.cpp new file mode 100644 index 0000000..2631308 --- /dev/null +++ b/Source/ZMMOJobs/Private/ZMMOJobDataAsset.cpp @@ -0,0 +1,5 @@ +#include "ZMMOJobDataAsset.h" + +// Implementacao vazia — todas as props sao UPROPERTY EditDefaultsOnly e +// vivem inline no .uasset. Override de GetPrimaryAssetId() esta no header +// (constexpr-friendly). diff --git a/Source/ZMMOJobs/Private/ZMMOJobsLibrary.cpp b/Source/ZMMOJobs/Private/ZMMOJobsLibrary.cpp new file mode 100644 index 0000000..0f4b861 --- /dev/null +++ b/Source/ZMMOJobs/Private/ZMMOJobsLibrary.cpp @@ -0,0 +1,54 @@ +#include "ZMMOJobsLibrary.h" + +#include "Engine/GameInstance.h" +#include "Engine/World.h" +#include "ZMMOJobDataAsset.h" +#include "ZMMOJobsSubsystem.h" + +namespace +{ +/// Resolve Subsystem via WorldContextObject. Retorna nullptr em contextos +/// invalidos (CDO em editor, world em destruicao). +UZMMOJobsSubsystem* ResolveSubsystem(const UObject* WorldContextObject) +{ + if (WorldContextObject == nullptr) { return nullptr; } + const UWorld* World = WorldContextObject->GetWorld(); + if (World == nullptr) { return nullptr; } + UGameInstance* GI = World->GetGameInstance(); + if (GI == nullptr) { return nullptr; } + return GI->GetSubsystem(); +} +} // namespace + +FText UZMMOJobsLibrary::GetJobDisplayName(const UObject* WorldContextObject, int32 ClassId) +{ + if (UZMMOJobsSubsystem* Sub = ResolveSubsystem(WorldContextObject)) + { + if (UZMMOJobDataAsset* Job = Sub->GetJobData(ClassId)) + { + return Job->DisplayName.IsEmpty() + ? FText::FromString(Job->TechnicalName) + : Job->DisplayName; + } + } + // Fallback: nao crasha UI se ClassId desconhecido. + return FText::FromString(FString::Printf(TEXT("Classe %d"), ClassId)); +} + +UZMMOJobDataAsset* UZMMOJobsLibrary::GetJobData(const UObject* WorldContextObject, int32 ClassId) +{ + if (UZMMOJobsSubsystem* Sub = ResolveSubsystem(WorldContextObject)) + { + return Sub->GetJobData(ClassId); + } + return nullptr; +} + +bool UZMMOJobsLibrary::IsJobRegistered(const UObject* WorldContextObject, int32 ClassId) +{ + if (UZMMOJobsSubsystem* Sub = ResolveSubsystem(WorldContextObject)) + { + return Sub->GetJobData(ClassId) != nullptr; + } + return false; +} diff --git a/Source/ZMMOJobs/Private/ZMMOJobsModule.cpp b/Source/ZMMOJobs/Private/ZMMOJobsModule.cpp new file mode 100644 index 0000000..4bfc787 --- /dev/null +++ b/Source/ZMMOJobs/Private/ZMMOJobsModule.cpp @@ -0,0 +1,13 @@ +#include "ZMMOJobsModule.h" + +#include "Modules/ModuleManager.h" + +void FZMMOJobsModule::StartupModule() +{ +} + +void FZMMOJobsModule::ShutdownModule() +{ +} + +IMPLEMENT_MODULE(FZMMOJobsModule, ZMMOJobs) diff --git a/Source/ZMMOJobs/Private/ZMMOJobsSubsystem.cpp b/Source/ZMMOJobs/Private/ZMMOJobsSubsystem.cpp new file mode 100644 index 0000000..bcdac0a --- /dev/null +++ b/Source/ZMMOJobs/Private/ZMMOJobsSubsystem.cpp @@ -0,0 +1,67 @@ +#include "ZMMOJobsSubsystem.h" + +#include "AssetRegistry/AssetRegistryModule.h" +#include "Modules/ModuleManager.h" +#include "ZMMOJobDataAsset.h" + +DEFINE_LOG_CATEGORY_STATIC(LogZMMOJobs, Log, All); + +void UZMMOJobsSubsystem::Initialize(FSubsystemCollectionBase& Collection) +{ + Super::Initialize(Collection); + LoadJobsFromAssetRegistry(); + UE_LOG(LogZMMOJobs, Log, TEXT("[ZMMOJobsSubsystem] Initialize: %d job(s) carregados"), CachedJobs.Num()); +} + +void UZMMOJobsSubsystem::Deinitialize() +{ + CachedJobs.Empty(); + Super::Deinitialize(); +} + +void UZMMOJobsSubsystem::LoadJobsFromAssetRegistry() +{ + FAssetRegistryModule& AssetRegistryModule = + FModuleManager::LoadModuleChecked("AssetRegistry"); + IAssetRegistry& AR = AssetRegistryModule.Get(); + + // Editor pode chamar antes de scan inicial. Standalone/PIE scan ja' rodou. + // SearchAllAssets(false) = nao-blocking; assume cache populado em runtime. +#if WITH_EDITOR + AR.SearchAllAssets(true); // bloqueia editor; ok em Initialize +#endif + + TArray Assets; + const FTopLevelAssetPath ClassPath(UZMMOJobDataAsset::StaticClass()->GetClassPathName()); + AR.GetAssetsByClass(ClassPath, Assets, /*bSearchSubClasses*/ true); + + for (const FAssetData& AssetData : Assets) + { + UZMMOJobDataAsset* Job = Cast(AssetData.GetAsset()); + if (Job == nullptr) { continue; } + + if (CachedJobs.Contains(Job->ClassId)) + { + UE_LOG(LogZMMOJobs, Warning, + TEXT("[ZMMOJobsSubsystem] Duplicate ClassId=%d (existing=%s, new=%s) — ignorando segundo"), + Job->ClassId, + *CachedJobs[Job->ClassId]->GetName(), + *Job->GetName()); + continue; + } + + CachedJobs.Add(Job->ClassId, Job); + UE_LOG(LogZMMOJobs, Verbose, + TEXT("[ZMMOJobsSubsystem] Cached classId=%d name=%s display=%s"), + Job->ClassId, *Job->TechnicalName, *Job->DisplayName.ToString()); + } +} + +UZMMOJobDataAsset* UZMMOJobsSubsystem::GetJobData(int32 ClassId) const +{ + if (const TObjectPtr* Found = CachedJobs.Find(ClassId)) + { + return Found->Get(); + } + return nullptr; +} diff --git a/Source/ZMMOJobs/Public/ZMMOJobDataAsset.h b/Source/ZMMOJobs/Public/ZMMOJobDataAsset.h new file mode 100644 index 0000000..dcb6c3a --- /dev/null +++ b/Source/ZMMOJobs/Public/ZMMOJobDataAsset.h @@ -0,0 +1,75 @@ +#pragma once + +#include "CoreMinimal.h" +#include "Engine/DataAsset.h" +#include "ZMMOJobDataAsset.generated.h" + +class UTexture2D; +class USoundBase; +class UAnimInstance; +class UAnimMontage; + +/** + * Dados visuais/UI de UM job (classe). 1 .uasset por job em + * `Content/ZMMO/Data/Jobs/DA_Job_.uasset`. + * + * ClassId eh a CHAVE — bate com `classId` do snapshot do server (rathena + * pc.hpp `job_class`, ex: Novice=0, Swordsman=1, Mage=2, ...). + * + * Soft pointers (TSoftObjectPtr) sao usados pra assets pesados (textures, + * sounds) — lazy-loaded quando precisar (level up, hover icon). DataAsset + * em si fica em memoria depois do scan do Subsystem. + * + * Async pointers (TSubclassOf) sao sync — anim BP precisa estar carregada + * quando o player spawna. + */ +UCLASS(BlueprintType) +class ZMMOJOBS_API UZMMOJobDataAsset : public UPrimaryDataAsset +{ + GENERATED_BODY() + +public: + /// Chave estavel (bate com `Config/Jobs/.json:Id` no server e com + /// `FZMMOAttributesSnapshot::ClassId` recebido do server). + UPROPERTY(EditDefaultsOnly, BlueprintReadOnly, Category = "Zeus|Job") + int32 ClassId = 0; + + /// Nome tecnico estavel (en-US). Ex: "Novice", "Swordsman". Aparece em + /// logs e telemetria — NUNCA exibido pro player. + UPROPERTY(EditDefaultsOnly, BlueprintReadOnly, Category = "Zeus|Job") + FString TechnicalName; + + /// Nome localizado exibido na UI (pt-BR no projeto base). Ex: "Aprendiz". + /// Pode usar FText Localization tables pra multi-idioma futuro. + UPROPERTY(EditDefaultsOnly, BlueprintReadOnly, Category = "Zeus|Job") + FText DisplayName; + + /// Icone do job — usado em StatusWindow, CharacterCard, JobChangeUI. + /// Soft ptr: nao carrega ate alguem chamar LoadSynchronous() ou async load. + UPROPERTY(EditDefaultsOnly, BlueprintReadOnly, Category = "Zeus|Job|Assets") + TSoftObjectPtr JobIcon; + + /// AnimInstance class override (opcional). Quando nao-null, AnimBP + /// padrao do PlayerCharacter eh substituido por esta classe no spawn + /// (jobs com move set diferente — Monge bate, Assassin furtivo, etc.). + UPROPERTY(EditDefaultsOnly, BlueprintReadOnly, Category = "Zeus|Job|Anim") + TSubclassOf DefaultAnimInstanceClass; + + /// Montage de "job change" (animacao de transformacao). Tocada quando + /// player promove de Novice → 1a classe. V1 nao implementado. + UPROPERTY(EditDefaultsOnly, BlueprintReadOnly, Category = "Zeus|Job|Anim") + TSoftObjectPtr JobChangeMontage; + + /// Som de level up override. Quando null, HUD usa som generico do projeto. + /// Soft ptr: lazy load no primeiro level up. + UPROPERTY(EditDefaultsOnly, BlueprintReadOnly, Category = "Zeus|Job|Audio") + TSoftObjectPtr LevelUpSoundCue; + + /// PrimaryAssetType pro AssetManager (precisa pra GetPrimaryAssetIdList). + /// Hardcoded "ZMMOJob" — registry classifica todos os DAs deste tipo. + virtual FPrimaryAssetId GetPrimaryAssetId() const override + { + return FPrimaryAssetId(FPrimaryAssetType(TEXT("ZMMOJob")), + *FString::Printf(TEXT("Job_%d_%s"), ClassId, *TechnicalName)); + } +}; diff --git a/Source/ZMMOJobs/Public/ZMMOJobsLibrary.h b/Source/ZMMOJobs/Public/ZMMOJobsLibrary.h new file mode 100644 index 0000000..825b348 --- /dev/null +++ b/Source/ZMMOJobs/Public/ZMMOJobsLibrary.h @@ -0,0 +1,39 @@ +#pragma once + +#include "CoreMinimal.h" +#include "Kismet/BlueprintFunctionLibrary.h" +#include "ZMMOJobsLibrary.generated.h" + +class UZMMOJobDataAsset; + +/** + * Helpers Blueprint pra UI/HUD acessar dados de job sem precisar pegar o + * Subsystem manualmente. Todos usam WorldContextObject pra resolver + * GameInstance → Subsystem internamente. + * + * Padrao de uso em UMG: + * - Text binding: `JobsLibrary::GetJobDisplayName(Self, AttrComp.Current.ClassId)` + * - Image binding: `JobsLibrary::GetJobIcon(Self, ClassId).LoadSynchronous()` + */ +UCLASS() +class ZMMOJOBS_API UZMMOJobsLibrary : public UBlueprintFunctionLibrary +{ + GENERATED_BODY() + +public: + /// Nome localizado do job. Fallback "Classe N" se ClassId nao tem DA + /// correspondente (job nao registrado no Content/ZMMO/Data/Jobs/). + UFUNCTION(BlueprintPure, Category = "Zeus|Jobs", + meta = (WorldContext = "WorldContextObject")) + static FText GetJobDisplayName(const UObject* WorldContextObject, int32 ClassId); + + /// DataAsset completo (pode ser nullptr — caller checa). + UFUNCTION(BlueprintPure, Category = "Zeus|Jobs", + meta = (WorldContext = "WorldContextObject")) + static UZMMOJobDataAsset* GetJobData(const UObject* WorldContextObject, int32 ClassId); + + /// Helper de UI: retorna true se ClassId tem DA registrado. + UFUNCTION(BlueprintPure, Category = "Zeus|Jobs", + meta = (WorldContext = "WorldContextObject")) + static bool IsJobRegistered(const UObject* WorldContextObject, int32 ClassId); +}; diff --git a/Source/ZMMOJobs/Public/ZMMOJobsModule.h b/Source/ZMMOJobs/Public/ZMMOJobsModule.h new file mode 100644 index 0000000..89817bb --- /dev/null +++ b/Source/ZMMOJobs/Public/ZMMOJobsModule.h @@ -0,0 +1,23 @@ +#pragma once + +#include "CoreMinimal.h" +#include "Modules/ModuleInterface.h" + +/** + * Sub-modulo cliente do JobsSystem. + * + * Espelho de `Server/ZeusServerEngine/Game/MMO/Modules/JobsSystem/`. Roda + * como modulo Runtime em LoadingPhase PreDefault para que o + * `UZMMOJobsSubsystem` esteja registrado antes do ZMMO core spawnar a HUD. + * + * Responsabilidades: + * - Dados visuais por job: DisplayName localizado, JobIcon, anim override + * - Lookup classId → UZMMOJobDataAsset via Subsystem + * - Library Blueprint pra HUD/UI consumir + */ +class FZMMOJobsModule : public IModuleInterface +{ +public: + virtual void StartupModule() override; + virtual void ShutdownModule() override; +}; diff --git a/Source/ZMMOJobs/Public/ZMMOJobsSubsystem.h b/Source/ZMMOJobs/Public/ZMMOJobsSubsystem.h new file mode 100644 index 0000000..7f8a103 --- /dev/null +++ b/Source/ZMMOJobs/Public/ZMMOJobsSubsystem.h @@ -0,0 +1,52 @@ +#pragma once + +#include "CoreMinimal.h" +#include "Subsystems/GameInstanceSubsystem.h" +#include "ZMMOJobsSubsystem.generated.h" + +class UZMMOJobDataAsset; + +/** + * Cache central de UZMMOJobDataAsset por ClassId. Vive como GameInstance + * Subsystem — startup automatico quando GameInstance inicializa (PIE, + * standalone, packaged). Sobrevive a map transitions. + * + * Initialize: + * 1. Carrega via AssetRegistry: scan de UZMMOJobDataAsset em todo Content + * 2. Cacheia em TMap por ClassId + * 3. Log: quantos jobs encontrou + * + * Uso: + * - C++: GameInstance->GetSubsystem()->GetJobData(0) + * - BP: UZMMOJobsLibrary::GetJobData(WorldContext, 0) + * + * Fase 7+ (Mod support): SCANEAR PrimaryAssetType="ZMMOJob" do AssetManager + * permite mods adicionarem DAs proprios sem mexer no source. + */ +UCLASS() +class ZMMOJOBS_API UZMMOJobsSubsystem : public UGameInstanceSubsystem +{ + GENERATED_BODY() + +public: + virtual void Initialize(FSubsystemCollectionBase& Collection) override; + virtual void Deinitialize() override; + + /// Lookup por classId. Retorna nullptr se ClassId nao tem DA correspondente + /// (caller usa Library com fallback FText). + UFUNCTION(BlueprintPure, Category = "Zeus|Jobs") + UZMMOJobDataAsset* GetJobData(int32 ClassId) const; + + /// Total de jobs cacheados (diagnostico). + UFUNCTION(BlueprintPure, Category = "Zeus|Jobs") + int32 GetJobCount() const { return CachedJobs.Num(); } + +private: + /// Scan AssetRegistry + popula CachedJobs. Sincronizo (V1) — chamado UMA + /// vez no Initialize. Pra 50 jobs eh aceitavel; se crescer pra 200+ pode + /// virar async no futuro. + void LoadJobsFromAssetRegistry(); + + UPROPERTY() + TMap> CachedJobs; +}; diff --git a/Source/ZMMOJobs/ZMMOJobs.Build.cs b/Source/ZMMOJobs/ZMMOJobs.Build.cs new file mode 100644 index 0000000..f5f200f --- /dev/null +++ b/Source/ZMMOJobs/ZMMOJobs.Build.cs @@ -0,0 +1,26 @@ +using UnrealBuildTool; + +public class ZMMOJobs : ModuleRules +{ + public ZMMOJobs(ReadOnlyTargetRules Target) : base(Target) + { + PCHUsage = PCHUsageMode.UseExplicitOrSharedPCHs; + + PublicDependencyModuleNames.AddRange(new string[] { + "Core", + "CoreUObject", + "Engine" + }); + + PrivateDependencyModuleNames.AddRange(new string[] { + "AssetRegistry" // FAssetRegistryModule pra scan de DA_Job_* + }); + + // Espelho do `Server/.../JobsSystem/module.json` (modulo nucleo MMO). + // Cliente roda em LoadingPhase PreDefault — Subsystem inicializa + // ANTES de o ZMMO core criar a player character / HUD. + // + // Sem dependencia em ZMMOAttributes: server envia classId no snapshot, + // cliente faz lookup local. Mantem o peer-module pattern. + } +} diff --git a/Source/ZMMOJobs/module.json b/Source/ZMMOJobs/module.json new file mode 100644 index 0000000..9afae77 --- /dev/null +++ b/Source/ZMMOJobs/module.json @@ -0,0 +1,18 @@ +{ + "name": "ZMMOJobs", + "gameType": "MMO", + "version": "0.1.0", + "side": "client", + "dependencies": { + "engine": ["Core", "CoreUObject", "Engine", "AssetRegistry"], + "modules": [] + }, + "publicHeaders": [ + "ZMMOJobsModule.h", + "ZMMOJobDataAsset.h", + "ZMMOJobsSubsystem.h", + "ZMMOJobsLibrary.h" + ], + "loadingPhase": "PreDefault", + "description": "Sub-modulo cliente do GameType MMO — dados visuais/UI por job (DisplayName localizado, ícone, anim override, som de level up). Espelha JobsSystem do server mas focado em apresentacao. NAO depende de ZMMOAttributes — server envia classId no snapshot, cliente faz lookup local. Pattern peer-module estabelecido no PR #9." +} diff --git a/ZMMO.uproject b/ZMMO.uproject index 7d4ee6e..a06e717 100644 --- a/ZMMO.uproject +++ b/ZMMO.uproject @@ -13,6 +13,11 @@ "Name": "ZMMOAttributes", "Type": "Runtime", "LoadingPhase": "PreDefault" + }, + { + "Name": "ZMMOJobs", + "Type": "Runtime", + "LoadingPhase": "PreDefault" } ], "Plugins": [