# Arquitectura do Cliente ZMMO > Regras de organização do projecto Unreal `ZMMO` (Content + Source) e tutorial > de como adicionar gameplay novo seguindo o padrão. **Este documento é > normativo.** Qualquer estrutura nova no projecto tem de respeitar estas > regras, ou justificar a excepção por escrito. Última actualização: 2026-05-11. > **Extensões da arquitetura** (documentos normativos paralelos, escopo específico): > - [`ARQUITETURA_SERVER_SELECT.md`](ARQUITETURA_SERVER_SELECT.md) — Server Select, lista de mundos, handoff CharServer↔WorldServer, multi-region, queue, Valkey. Roadmap em 7 fases. > - [`ARQUITETURA_CHARACTER_MODEL.md`](ARQUITETURA_CHARACTER_MODEL.md) — Stats primários (STR/AGI/VIT/INT/DEX/LUK), classes (Novato → especializações), fórmulas de stats derivados (ATK/MATK/DEF/...), `jobs.yml` data-driven, stat allocation server-side. Espelha rathena. > **Sumário de mudanças (2026-05-11):** adicionada secção de **temas sazonais > de UI** (`UI/Themes/`), com tutorial §4.7, regra de "no hard ref a textura > em widget" em §5 e política de activação em §1.10. --- ## 1. Princípios fundamentais Estas são as regras que **não negociamos**: 1. **Namespace do projecto.** Todo o conteúdo do jogo vive sob `Content/ZMMO/`. Nada de assets soltos na raiz `Content/`. 2. **Organizar por feature, não por tipo de asset.** Não criamos pastas `Meshes/`, `Textures/`, `Materials/` no nível alto. O Content Browser já filtra por prefixo (`SM_`, `T_`, `M_`...). Pastas existem para refletir **domínio de gameplay**. 3. **Self-contained por entidade.** Cada Mob, NPC ou Item vive numa pasta própria que contém **todos os seus assets** (malha, animações, materiais, texturas, áudio, VFX). Apagar a pasta = remover a entidade do jogo, sem referências órfãs espalhadas. 4. **Plano (máximo 3 níveis úteis).** Se está a aninhar mais que isso, provavelmente está a organizar errado. Excepção: dentro de uma entidade self-contained, sub-pasta `Animations/` é permitida quando o número de anims ultrapassa 5–6. 5. **DataTable manda em catálogo, Blueprint manda em comportamento.** Dados tabulares (stats, drops, preços, descrições) vão em `Content/ZMMO/Data/**/DT_*.uasset`. Comportamento vai em Blueprints herdados das bases em `Core/`. Nunca misturar. 6. **Structs e Enums vivem em C++.** Toda `USTRUCT` (row de DataTable inclusive) e toda `UENUM` é declarada em `Source/ZMMO/Data/`. Blueprint Structs estão **proibidos** — eles corrompem em refactor e não dão tipos fortes. 7. **Hierarquia de Entity.** Tudo que vive no mundo herda da mesma raiz: `BP_Entity_Base` → (`BP_Player_Base` | `BP_NPC_Base` | `BP_Mob_Base`) → instâncias concretas. Espelha a classe C++ `AZMMOEntity` em `Source/ZMMO/Game/Entity/`. 8. **Lazy loading via `TSoftObjectPtr`.** Linhas de DataTable e referências entre features usam soft pointers. Nada de hard ref para BPs de mob/item a partir da DT mestre — caso contrário carregamos o mundo inteiro ao abrir o inventário. 9. **`Developers/` e `ExternalContent/` fora do namespace `ZMMO/`.** Não entram no cook (`Developers/`) e não poluem o nosso namespace (`ExternalContent/` para Fab/Megascans/Marketplace). 10. **Temas de UI são data-driven, não hard-coded.** Widgets não têm hard ref para textura/som sazonal — pedem ao `UZMMOThemeSubsystem` via uma chave declarada em `EZMMOThemeKey` (C++). Veja §1.10 abaixo para a política de activação. ### 1.10. Política de activação de temas A UI suporta **temas** (`Default`, `Christmas`, `Halloween`, etc.). A escolha do tema activo segue esta **ordem de precedência** em runtime: 1. **Override de desenvolvimento** (`DefaultGame.ini`, chave `ActiveTheme=`). Só lido fora de builds Shipping. Para QA testar Natal em Maio. 2. **Resposta autoritativa do servidor.** Quando o servidor Zeus envia o opcode `ServerHello` com `ThemeId`, ele vence. É o único caminho válido em produção. 3. **Calendário local do cliente** (`DT_ThemeCalendar`). Fallback antes do login (tela de login/character-select aparece antes do servidor falar) e se o servidor não enviar `ThemeId`. 4. **`DA_Theme_Default`.** Último recurso. Sempre presente, sempre carregado. **Por que servidor vence:** MMO precisa ligar Halloween fora da data canónica, fazer evento-surpresa, A/B test de tema sem patch do cliente. **Por que data local existe:** sem ela, jogador que abre o jogo em 24 de Dezembro vê tema Default até autenticar — quebra a magia sazonal. **Por que .ini só fora de Shipping:** evita que um jogador edite o ficheiro e force um tema que não devia ter acesso (cosméticos pagos, por exemplo). --- ## 2. Estrutura de pastas ### 2.1. `Clients/ZMMO/Content/` ``` Content/ │ ├── ZMMO/ ← namespace do projecto │ │ │ ├── Core/ ← bases que outros assets herdam │ │ ├── Entity/ │ │ │ ├── BP_Entity_Base.uasset ← herda AZMMOEntity (C++) │ │ │ └── BPI_Entity.uasset ← interface comum │ │ ├── Player/ │ │ │ └── BP_Player_Base.uasset │ │ ├── NPC/ │ │ │ └── BP_NPC_Base.uasset │ │ ├── Mob/ │ │ │ ├── BP_Mob_Base.uasset │ │ │ ├── BT_Mob_Default.uasset ← Behavior Tree base │ │ │ └── BTT_Mob_*.uasset ← Tasks compartilhadas │ │ ├── Item/ │ │ │ ├── BP_Item_Base.uasset │ │ │ └── BPI_Item.uasset │ │ └── Network/ ← debug do UZMMOWorldSubsystem │ │ │ ├── Entities/ ← TODAS as instâncias concretas │ │ ├── Players/ │ │ │ └── Classes/ │ │ │ ├── Warrior/ │ │ │ │ ├── BP_Player_Warrior.uasset │ │ │ │ ├── SK_Warrior.uasset │ │ │ │ ├── ABP_Warrior.uasset │ │ │ │ ├── M_Warrior.uasset │ │ │ │ ├── T_Warrior_*.uasset │ │ │ │ └── Animations/ │ │ │ ├── Mage/ │ │ │ └── Archer/ │ │ │ │ │ ├── NPCs/ │ │ │ ├── Vendor_Blacksmith/ │ │ │ │ ├── BP_NPC_Blacksmith.uasset │ │ │ │ ├── SK_Blacksmith.uasset │ │ │ │ ├── ABP_Blacksmith.uasset │ │ │ │ └── ... │ │ │ └── Questgiver_Elder/ │ │ │ │ │ └── Mobs/ │ │ ├── Beasts/ ← família (organiza, não herda) │ │ │ ├── Wolf_Gray/ ← pacote completo do mob │ │ │ │ ├── BP_Mob_Wolf_Gray.uasset │ │ │ │ ├── SK_Wolf.uasset │ │ │ │ ├── PHY_Wolf.uasset │ │ │ │ ├── M_Wolf.uasset │ │ │ │ ├── MI_Wolf_Gray.uasset │ │ │ │ ├── T_Wolf_*.uasset │ │ │ │ ├── Animations/ │ │ │ │ │ ├── ABP_Wolf.uasset │ │ │ │ │ ├── AS_Wolf_Skel.uasset │ │ │ │ │ ├── AM_Wolf_AttackBite.uasset │ │ │ │ │ └── A_Wolf_{Idle,Run,Hit,Death}.uasset │ │ │ │ ├── Audio/ │ │ │ │ └── VFX/ │ │ │ ├── Bear_Brown/ │ │ │ └── Boar_Wild/ │ │ ├── Undead/ │ │ ├── Humanoids/ │ │ ├── Dragons/ │ │ └── Bosses/ │ │ │ ├── Data/ ← apenas DataTables (.uasset) │ │ ├── Items/ │ │ │ ├── DT_Items_Master.uasset │ │ │ ├── DT_Items_Weapons.uasset │ │ │ ├── DT_Items_Armor.uasset │ │ │ ├── DT_Items_Consumables.uasset │ │ │ └── DT_ItemDrops.uasset │ │ ├── Mobs/ │ │ │ ├── DT_Mobs_Master.uasset │ │ │ └── DT_MobSpawns.uasset │ │ ├── Players/ │ │ │ ├── DT_Classes.uasset │ │ │ └── DT_ExpCurve.uasset │ │ ├── NPCs/ │ │ │ ├── DT_NPCs.uasset │ │ │ └── DT_Dialogs.uasset │ │ ├── Abilities/ │ │ │ ├── DT_Abilities.uasset │ │ │ └── DT_StatusEffects.uasset │ │ ├── Quests/ │ │ │ ├── DT_Quests.uasset │ │ │ └── DT_QuestRewards.uasset │ │ └── World/ │ │ └── DT_Zones.uasset │ │ │ ├── Items/ ← assets visuais (modelo/icon dos itens) │ │ ├── Equipment/ │ │ │ ├── Weapons/ │ │ │ │ ├── Sword_Iron/ │ │ │ │ │ ├── SM_Sword_Iron.uasset │ │ │ │ │ ├── M_Sword_Iron.uasset │ │ │ │ │ ├── T_Sword_Iron_*.uasset │ │ │ │ │ └── T_Icon_Sword_Iron.uasset │ │ │ │ └── Bow_Hunter/ │ │ │ ├── Armor/ │ │ │ └── Accessories/ │ │ ├── Consumables/ │ │ │ ├── Potion_Health/ │ │ │ └── Potion_Mana/ │ │ ├── Materials/ │ │ └── Quest/ │ │ │ ├── Abilities/ ← VFX/Cues/Montagens (data fica em Data/) │ │ ├── Combat/ Buffs/ Heals/ Movement/ │ │ └── Shared/ │ │ │ ├── World/ │ │ ├── Maps/ ← L_World_Open, L_City_*, L_Dungeon_* │ │ ├── Instances/ │ │ ├── Environment/ ← Architecture/ Nature/ Props/ │ │ └── Interactables/ ← baús, portas, warps, recursos │ │ │ ├── Maps/ ← mapas que NÃO são de mundo │ │ └── FrontEnd/ ← L_FrontEnd (boot/menu; GameMode via │ │ World Settings = AZMMOFrontEndGameMode) │ │ │ ├── UI/ ← HUD, Login, CharacterSelect, Inventory, │ │ │ Chat, Party, Guild, Trade, Quest, Map, │ │ │ Shared, Icons │ │ ├── Shared/ ← WBPs reutilizáveis (UI_Button_Master, │ │ │ UI_Panel_Master, UI_Spinner_Master, │ │ │ UI_CheckBox_Master, UI_Input_Master) │ │ ├── FrontEnd/ ← WBP_PrimaryGameLayout, DA_FrontEndScreenSet, │ │ │ telas refinadas (Boot/Login/… vindas do Forge) │ │ ├── Fonts/ ← Font_*/FF_* (Cinzel, Rajdhani) │ │ ├── CommonUI/Style/ ← estilos CommonUI (espelha o Hyper) │ │ │ ├── Button/ ← BSB_* (UCommonButtonStyle) │ │ │ └── Text/ ← BST_* (UCommonTextStyle) │ │ └── Themes/ ← temas sazonais (data-driven, ver §1.10) │ │ ├── DA_Theme_Default.uasset ← sempre carregado, fallback │ │ ├── DA_Theme_Christmas.uasset │ │ ├── DA_Theme_Halloween.uasset │ │ ├── DT_ThemeCalendar.uasset ← datas → ThemeId (fallback local) │ │ ├── _Shared/ ← assets reutilizados entre temas │ │ ├── Default/ ← assets do tema base │ │ │ ├── HUD/ Login/ Inventory/ │ │ │ ├── Audio/ MS_MainMenu.uasset │ │ │ └── VFX/ │ │ ├── Christmas/ ← sazonal │ │ │ ├── HUD/ Login/ Inventory/ │ │ │ ├── Audio/ MS_MainMenu_Christmas.uasset │ │ │ └── VFX/ NS_Snow.uasset │ │ └── Halloween/ ... ← mesma estrutura │ ├── Effects/ ← VFX globais (Niagara) │ ├── Audio/ ← Music, Ambient, SFX, Voice │ ├── Input/ ← Enhanced Input │ ├── Cinematics/ ← Sequencer │ ├── Movement/ ← Mounts, Transport │ ├── Localization/ ← StringTables │ └── Debug/ ← Maps de smoke test │ ├── Developers/ ← rascunho por dev (não cooka) │ └── / ├── AutoCreated/ ← saída CRUA do Zeus UMG Forge (HTML→WBP). │ └── / Quarentena: refina-se e MOVE p/ ZMMO/UI/ │ FrontEnd/ antes de produção (não cooka cru) ├── ExternalContent/ ← Fab / Megascans / Marketplace │ ├── Fab/ Megascans/ Marketplace/ ├── Collections/ └── __ExternalActors__/ __ExternalObjects__/ ← World Partition (não mexer) ``` ### 2.2. `Clients/ZMMO/Source/ZMMO/` ``` Source/ZMMO/ ├── ZMMO.Build.cs ├── ZMMO.h ├── ZMMO.cpp │ ├── Game/ ← gameplay runtime (já existe) │ ├── Entity/ ← AZMMOEntity + derivados │ ├── Controller/ ← AZMMOPlayerController, │ │ AZMMOFrontEndPlayerController │ ├── Modes/ ← GameMode + GameInstance, │ │ AZMMOFrontEndGameMode │ ├── Network/ ← UZMMOWorldSubsystem │ └── UI/ ← runtime de UI (não é contrato de dados) │ ├── ZMMOThemeSubsystem.* ← resolução de tema │ ├── Widgets/ ← UUIButton_Base, UUIPanel_Base, │ │ UUISpinner_Base, UUICheckBox_Base, │ │ UUIInput_Base (variantes Box/Outline/ │ │ Underline/Search), UUICommonText_Base │ │ (EUITextRole → FUIStyle.Text) │ └── FrontEnd/ ← infra de navegação CommonUI: │ UUIActivatableScreen_Base, │ UUIBootScreen_Base (1ª tela), │ UUILoginScreen_Base (2ª tela), │ CharServerOpcodes.h (faixa 2000-2099), │ UUIPrimaryGameLayout_Base, │ UUIManagerSubsystem (LocalPlayer), │ UUIFrontEndFlowSubsystem (GameInstance), │ UUIFrontEndScreenSet (UDataAsset) │ └── Data/ ← contrato de dados (USTRUCT + UENUM) ├── ZMMOData.h ← header agregador (opcional) ├── Items/ │ ├── ItemRow.h ← FItemRow : public FTableRowBase │ └── ItemTypes.h ← E_ItemType, E_ItemRarity, E_EquipSlot ├── Mobs/ │ ├── MobRow.h ← FMobRow │ └── MobTypes.h ← E_MobFaction, E_MobBehavior ├── Players/ │ ├── ClassRow.h ← FClassRow │ └── StatBlock.h ← FStatBlock (HP/MP/Sta/Atk/Def…) ├── NPCs/ │ ├── NPCRow.h │ └── DialogRow.h ├── Abilities/ │ ├── AbilityRow.h │ └── StatusEffectRow.h ├── Quests/ │ └── QuestRow.h ├── UI/ │ ├── ThemeKeys.h ← EZMMOThemeKey (HUD_Frame, Login_BG…) │ ├── ThemeRow.h ← FThemeEntry (Key → SoftAsset) │ ├── ThemeCalendarRow.h ← FThemeCalendarRow (data → ThemeId) │ ├── FrontEndTypes.h ← EZMMOFrontEndState, EZMMOLobbyPage │ └── UILayerTags.h/.cpp ← GameplayTags nativos UI.Layer.* └── World/ └── ZoneRow.h ``` Regra: **toda DT em `Content/ZMMO/Data/X/`** tem a `Row Structure` definida no `.h` correspondente em **`Source/ZMMO/Data/X/`**. Espelhamento 1-para-1 nos nomes das sub-pastas. O subsystem do tema (`UZMMOThemeSubsystem`) vive em `Source/ZMMO/Game/UI/` — é gameplay/runtime, não contrato de dados, por isso não fica em `Data/`. --- ## 3. Convenções de nomenclatura ### 3.1. Pastas - `PascalCase`, sem espaços, sem acentos. - Nome no singular para categoria conceitual (`Mob`, `Item`, `Quest`). - Nome no plural para colecção de instâncias (`Mobs/`, `Items/`, `Quests/`). - Família de mob/item usa nome temático: `Beasts/`, `Undead/`, `Weapons/`. - Instância individual usa `Tipo_Variante`: `Wolf_Gray`, `Sword_Iron`, `Vendor_Blacksmith`. ### 3.2. Assets (prefixo obrigatório) | Prefixo | Tipo | Exemplo | |---------|-----------------------------------|-------------------------------| | `BP_` | Blueprint Class | `BP_Mob_Wolf_Gray` | | `BPI_` | Blueprint Interface | `BPI_Entity` | | `UI_` | Widget Blueprint (estilo Hyper) | `UI_Button_Base`, `UI_Inventory_Slot` | | `ABP_` | Animation Blueprint | `ABP_Wolf` | | `BT_` | Behavior Tree | `BT_Mob_Default` | | `BTT_` | Behavior Tree Task | `BTT_Mob_ChasePlayer` | | `BTS_` | Behavior Tree Service | `BTS_Mob_FindTarget` | | `BTD_` | Behavior Tree Decorator | `BTD_Mob_CanSeeTarget` | | `BB_` | Blackboard | `BB_Mob_Default` | | `SK_` | Skeletal Mesh | `SK_Wolf` | | `SKEL_` | Skeleton | `SKEL_Wolf` | | `SM_` | Static Mesh | `SM_Sword_Iron` | | `PHY_` | Physics Asset | `PHY_Wolf` | | `M_` | Material | `M_Wolf` | | `MI_` | Material Instance | `MI_Wolf_Gray` | | `MF_` | Material Function | `MF_NormalBlend` | | `T_` | Texture | `T_Wolf_Diffuse` | | `RT_` | Render Target | `RT_MiniMap` | | `Font_` | Font (UFont, Composite) | `Font_Cinzel` | | `FF_` | Font Face (UFontFace, Inline) | `FF_Cinzel_Bold` | | `BSB_` | Common Button Style (UCommonButtonStyle) | `BSB_Button_Transparent` | | `BST_` | Common Text Style (UCommonTextStyle) | `BST_Button` | | `NS_` | Niagara System | `NS_Wolf_Death` | | `NE_` | Niagara Emitter | `NE_Sparks_Small` | | `SC_` | Sound Cue | `SC_Wolf_Growl` | | `SW_` | Sound Wave | `SW_Wolf_Growl_01` | | `MS_` | MetaSound Source | `MS_AmbientForest` | | `MSA_` | Mix MetaSound Asset | `MSA_CombatMix` | | `A_` | Animation Sequence | `A_Wolf_Idle` | | `AM_` | Animation Montage | `AM_Wolf_AttackBite` | | `AS_` | Anim State / Skeleton ref | `AS_Wolf_Skel` | | `IA_` | Input Action | `IA_Move` | | `IMC_` | Input Mapping Context | `IMC_Default` | | `DA_` | Data Asset (PrimaryDataAsset) | `DA_Quest_FirstSteps` | | `DT_` | Data Table | `DT_Items_Master` | | `CT_` | Curve Table | `CT_DamageFalloff` | | `C_` | Curve | `C_AttackPower` | | `L_` | Level / Map | `L_World_Open` | | `LS_` | Level Sequence | `LS_OpeningCinematic` | | `GE_` | Gameplay Effect *(se GAS)* | `GE_StatusBleed` | | `GA_` | Gameplay Ability *(se GAS)* | `GA_Fireball` | | `GC_` | Gameplay Cue *(se GAS)* | `GC_Hit_Blood` | ### 3.3. C++ (`Source/ZMMO/Data/`) - Struct: `FRow` quando é linha de DataTable. Ex: `FItemRow`, `FMobRow`. - Struct genérica (não-DT): `F`. Ex: `FStatBlock`. - Enum: `E`. Sempre `UENUM(BlueprintType)` e tipado: `enum class EItemType : uint8`. - Header path espelha a pasta: `Source/ZMMO/Data/Items/ItemRow.h` → `#include "Data/Items/ItemRow.h"`. **Exceção — classes base de UI (widgets):** a família de UI **não** leva o prefixo `ZMMO` (ao contrário de `AZMMOEntity`/`UZMMOGameInstance`). Classe C++ base de widget = `UUI_Base`, sempre `UCLASS(Abstract)` (não instanciável direto; só os filhos derivam — padrão `_Abstract` do Hyper). Ex.: `UUIButton_Base`, `UUIPanel_Base`, `UUIProgressBar_Base`. **Sub-regra — infra de navegação de front-end:** as classes que orquestram a navegação CommonUI seguem o mesmo prefixo `UUI` (sem `ZMMO`) por coerência com a família de UI, **mesmo não sendo widgets**. Assim: o layout raiz é `UUIPrimaryGameLayout_Base` (Abstract, widget), a base de tela é `UUIActivatableScreen_Base` (Abstract, widget), e os subsistemas de UI são `UUIManagerSubsystem` (`ULocalPlayerSubsystem`) e `UUIFrontEndFlowSubsystem` (`UGameInstanceSubsystem`). Atores de gameplay do front-end (GameMode, PlayerController) **mantêm** o prefixo `AZMMO` — não são UI, espelham `AZMMOGameMode`: `AZMMOFrontEndGameMode`, `AZMMOFrontEndPlayerController`. **Regra do UMG (importante):** um Widget Blueprint **não pode** herdar de outro Widget Blueprint que tenha árvore de widgets (UMG aborta a compilação: "apenas um deles deve ter árvore"). Portanto a camada abstrata é **só a classe C++** `UUI_Base`. O WBP concreto (prefixo `UI_`, ver §3.2) herda **direto da classe C++**: `UUIButton_Base` (C++ Abstract) → `UI_Button_Master` (WBP, árvore própria). Variantes por feature são **WBPs irmãos** sobre a mesma classe C++ (`UI_Button_` : `UUIButton_Base`), cada um com a própria árvore (montada via `set_widget_tree`) — nunca encadeando WBPs. --- ## 4. Tutoriais ### 4.1. Adicionar um Mob novo (ex: `Wolf_Gray`) **Pré-requisito:** já existe `FMobRow` em `Source/ZMMO/Data/Mobs/MobRow.h` e `DT_Mobs_Master` em `Content/ZMMO/Data/Mobs/`. 1. Criar a pasta `Content/ZMMO/Entities/Mobs/Beasts/Wolf_Gray/`. 2. Importar **todos** os assets do lobo dentro dela: - `SK_Wolf`, `SKEL_Wolf`, `PHY_Wolf`. - `T_Wolf_Diffuse`, `T_Wolf_Normal`, `T_Wolf_ORM`. - `M_Wolf` e a variante `MI_Wolf_Gray`. - `Animations/`: `ABP_Wolf`, `AS_Wolf_Skel`, `A_Wolf_*`, `AM_Wolf_*`. - `Audio/`: `SC_Wolf_Growl`, `SC_Wolf_Footstep`. - `VFX/`: `NS_Wolf_Death`. 3. Criar `BP_Mob_Wolf_Gray` **herdando** de `Core/Mob/BP_Mob_Base`. 4. Atribuir no BP: `SK_Wolf` no Mesh, `ABP_Wolf` no AnimClass, `BT_Mob_Default` (ou um BT próprio se necessário) no AIController. 5. Abrir `Content/ZMMO/Data/Mobs/DT_Mobs_Master.uasset`. Adicionar linha `Wolf_Gray`: - `BlueprintRef` = `BP_Mob_Wolf_Gray` (via `TSoftObjectPtr` — soft). - `Stats` = `FStatBlock` com HP/Atk/Def adequados. - `Faction` = `EMobFaction::Hostile`. - `DropTableRow` = `WolfDrops` (linha em `DT_ItemDrops`). 6. Se for dropar loot novo, abrir `DT_ItemDrops` e adicionar a linha `WolfDrops` com array de `FItemRow` ids + chances. **Regra:** nunca colocar um asset de `Wolf_Gray` fora de `Entities/Mobs/Beasts/Wolf_Gray/`. Apagar a pasta tem de remover o mob por inteiro. ### 4.2. Adicionar um Item novo (ex: `Sword_Iron`) **Pré-requisito:** `FItemRow` em `Source/ZMMO/Data/Items/ItemRow.h`, `DT_Items_Master` em `Content/ZMMO/Data/Items/`. 1. Criar `Content/ZMMO/Items/Equipment/Weapons/Sword_Iron/`. 2. Importar `SM_Sword_Iron`, texturas `T_Sword_Iron_*`, material `M_Sword_Iron`, e o ícone `T_Icon_Sword_Iron`. 3. Abrir `DT_Items_Master`. Adicionar linha `Sword_Iron`: - `Type` = `EItemType::Weapon`. - `Rarity` = `EItemRarity::Common`. - `EquipSlot` = `EEquipSlot::MainHand`. - `WorldMesh` = `SM_Sword_Iron` (soft). - `Icon` = `T_Icon_Sword_Iron` (soft). - `DisplayName`, `Description` via `StringTable` em `Localization/`. **Sem Blueprint por item.** O item é "data only" — quando precisar de comportamento especial (ex: poção que cura), criar `BP_Item_Potion_Health` herdando de `Core/Item/BP_Item_Base` e apontar a coluna `BehaviorClass` da linha para esse BP. A maioria dos itens **não** tem BP próprio. ### 4.3. Adicionar uma Classe de Player (ex: `Warrior`) 1. Criar `Content/ZMMO/Entities/Players/Classes/Warrior/`. 2. Importar `SK_Warrior`, anims, materiais, etc. (mesmo padrão self-contained do mob). 3. Criar `BP_Player_Warrior` herdando de `Core/Player/BP_Player_Base`. 4. Adicionar linha `Warrior` em `DT_Classes`: - `BlueprintRef` = `BP_Player_Warrior` (soft). - `BaseStats` = `FStatBlock`. - `LevelUpStats` = `FStatBlock` (delta por nível). - `StartingAbilities` = `TArray` de ids em `DT_Abilities`. ### 4.4. Adicionar um NPC (ex: `Vendor_Blacksmith`) 1. Criar `Content/ZMMO/Entities/NPCs/Vendor_Blacksmith/` (self-contained). 2. Criar `BP_NPC_Blacksmith` herdando de `Core/NPC/BP_NPC_Base`. 3. Adicionar linha `Vendor_Blacksmith` em `DT_NPCs`: - `BlueprintRef` = `BP_NPC_Blacksmith` (soft). - `Role` = `ENPCRole::Vendor`. - `ShopInventory` = `TArray` de ids em `DT_Items_Master`. - `DialogRoot` = id em `DT_Dialogs`. ### 4.5. Adicionar uma Row Struct em C++ Exemplo: nova struct `FCraftRecipeRow` para receitas de crafting. 1. Criar `Source/ZMMO/Data/Crafting/CraftRecipeRow.h`: ```cpp #pragma once #include "CoreMinimal.h" #include "Engine/DataTable.h" #include "CraftRecipeRow.generated.h" USTRUCT(BlueprintType) struct ZMMO_API FCraftRecipeRow : public FTableRowBase { GENERATED_BODY() UPROPERTY(EditAnywhere, BlueprintReadOnly) FName OutputItem; UPROPERTY(EditAnywhere, BlueprintReadOnly) int32 OutputQuantity = 1; UPROPERTY(EditAnywhere, BlueprintReadOnly) TArray Ingredients; }; ``` 2. Recompilar o módulo `ZMMO`. 3. No editor: `Content/ZMMO/Data/Crafting/` → `Add → Miscellaneous → Data Table` → escolher `CraftRecipeRow` como Row Structure → salvar como `DT_Crafting_Recipes`. ### 4.6. Conectar uma DataTable a um Blueprint em runtime ```cpp // Em BP_Mob_Base (ou na sua classe C++ AZMMOMobBase): // // UPROPERTY(EditDefaultsOnly, Category="Data") // TSoftObjectPtr MobsTable; // // UPROPERTY(EditAnywhere, Category="Data") // FName MobId; // // void AZMMOMobBase::BeginPlay() // { // Super::BeginPlay(); // if (UDataTable* Table = MobsTable.LoadSynchronous()) // { // if (FMobRow* Row = Table->FindRow(MobId, TEXT("Mob init"))) // { // ApplyStats(Row->Stats); // Faction = Row->Faction; // DropTableRowId = Row->DropTableRow; // } // } // } ``` Regra: o `MobId` é setado pelo `UZMMOWorldSubsystem` quando o servidor envia o opcode de spawn. O cliente nunca instancia mob "por nome" — sempre via id. ### 4.7. Adicionar um tema sazonal (ex: `Christmas`) **Pré-requisito:** já existem `EZMMOThemeKey` em `Source/ZMMO/Data/UI/ThemeKeys.h`, `FThemeEntry` em `ThemeRow.h`, e o `UZMMOThemeSubsystem` em `Source/ZMMO/Game/UI/`. 1. Criar a pasta `Content/ZMMO/UI/Themes/Christmas/` com sub-pastas que espelham as features de UI a substituir: `HUD/`, `Login/`, `Inventory/`, `Audio/`, `VFX/`. Não precisa criar sub-pastas para features que o tema **não** altera. 2. Importar os assets variantes lá dentro. Manter os **mesmos nomes** que os equivalentes em `Themes/Default/` ajuda a comparar: - `Themes/Default/HUD/T_HUD_Frame.uasset` ↔ `Themes/Christmas/HUD/T_HUD_Frame.uasset`. 3. Criar `DA_Theme_Christmas.uasset` em `Content/ZMMO/UI/Themes/` como `PrimaryDataAsset` herdando da classe `UZMMOThemeDataAsset` (a classe vive em `Source/ZMMO/Game/UI/`). 4. Preencher o `TMap` do data asset **apenas com as chaves que o tema sobrescreve**. As que ficam vazias caem automaticamente para `DA_Theme_Default`. 5. Adicionar linha em `DT_ThemeCalendar`: - `ThemeId` = `Christmas`. - `StartDate` = `12-15`. - `EndDate` = `01-05`. - `Priority` = `10` (caso duas datas se sobreponham). 6. No servidor Zeus, quando quiser activar o tema (autoritativo), enviar o opcode `ServerHello` com `ThemeId = "Christmas"`. Em ausência disso, o cliente usa o calendário do passo 5. **Adicionar uma chave nova de tema** (ex: tela de loading sazonal): 1. Abrir `Source/ZMMO/Data/UI/ThemeKeys.h` e adicionar o valor ao enum: ```cpp UENUM(BlueprintType) enum class EZMMOThemeKey : uint8 { HUD_Frame, Login_Background, Login_Logo, Inventory_SlotEmpty, LoadingScreen, // <-- nova chave MainMenu_Music, }; ``` 2. Recompilar o módulo `ZMMO`. 3. Abrir `DA_Theme_Default` no editor e preencher a nova chave **obrigatoriamente** (Default tem de ter todas). 4. Preencher nos temas sazonais apenas se quiser substituir. **Regra de ouro do tema:** widget consome via subsystem, nunca por hard ref. ```cpp // Em vez de: // UPROPERTY(EditAnywhere) TObjectPtr HudFrameTex; // // Faz: // const UZMMOThemeSubsystem* Theme = GetGameInstance()->GetSubsystem(); // SetBrushFromSoftTexture(Theme->GetTexture(EZMMOThemeKey::HUD_Frame)); ``` O subsystem aplica fallback internamente: pergunta ao tema activo; se a chave não estiver preenchida, devolve a do `Default`. ### 4.8. Adicionar uma tela de front-end (ex: `Login`) **Pré-requisito:** já existem `UUIActivatableScreen_Base` e `UUIFrontEndFlowSubsystem` em `Source/ZMMO/Game/UI/FrontEnd/`, o `WBP_PrimaryGameLayout` e o `DA_FrontEndScreenSet` em `Content/ZMMO/UI/FrontEnd/`. Fluxo geral: **Boot → Connecting → Login → ServerSelect → Lobby → EnteringWorld → InWorld**. O **Lobby** é o hub principal logado; suas páginas internas (CharacterSelect, CharacterCreate, Shop, Settings…) são `EZMMOLobbyPage`, navegadas por um stack CommonUI **dentro** da tela do Lobby — não são estados de topo do fluxo. **Rede do pré-login:** o `UUIFrontEndFlowSubsystem` dirige a conexão ao **`UZeusCharServerSubsystem`** (WebSocket, `CharServerUrl` de Project Settings, default `ws://127.0.0.1:7100/zeus`) ao entrar em **Boot** — NÃO o `UZeusNetworkSubsystem` (UDP, world server, usado só no travel EnteringWorld→InWorld). A tela Boot (`UUIBootScreen_Base`) observa o CharServer: libera o botão "Iniciar" no `OnConnected`; o clique chama `UUIFrontEndFlowSubsystem::RequestEnterLogin()` → estado Login. Tela nunca segura hard-ref de asset de tema (cores via `FUIStyle`; §5). **Login:** a tela `UUILoginScreen_Base` autentica pelo MESMO WebSocket do CharServer enviando `C_CHAR_AUTH_REQUEST` (opcode 2000, ver `CharServerOpcodes.h`) com um token. Escopo atual = **dev**: token `dev:` aceito pelo `StubTokenValidator` (senha ignorada no stub). `S_CHAR_AUTH_OK` → `RequestEnterServerSelect()` → estado ServerSelect; `S_CHAR_AUTH_REJECT` → mensagem de erro na própria tela. Auth real (HTTP `/auth/login` + JWT) fica para tarefa dedicada. "Voltar" → `RequestBack()` (Login→Boot, CharServer permanece conectado). 1. Prototipar a tela em HTML/CSS em `Tools/Templates/MMO_Widget/` (ou usar os templates já existentes). 2. Rodar o Zeus UMG Forge (HTML→UMG). Saída crua: `Content/AutoCreated//`. 3. Abrir o `WBP_*` gerado e **reparent** a classe pai para `UUIActivatableScreen_Base` (a base é `UCommonActivatableWidget`; o WBP tem árvore própria — respeita §3.3, nunca encadeia WBP→WBP). 4. **Reconectar tema:** o Forge gera hard ref de textura (proibido §5). Substituir por consumo via `UZMMOThemeSubsystem` por `EZMMOThemeKey` no override de `RefreshUIStyle`; remontar botões/painéis com os widgets compartilhados (`UI_Button_Master`, `UI_Panel_Master`). 5. **Mover** o asset refinado de `Content/AutoCreated/…` para `Content/ZMMO/UI/FrontEnd/` (o cru não cooka). 6. Registrar no `DA_FrontEndScreenSet`: `StateScreens[EZMMOFrontEndState::Login]` = soft class do WBP refinado (páginas do Lobby vão no map de `EZMMOLobbyPage` da tela de Lobby). Sem registro, o flow faz no-op + log. **Regra:** uma tela nunca segura hard ref a asset de tema (textura/som/VFX) — sempre via `UZMMOThemeSubsystem` por `EZMMOThemeKey` (igual §4.7 e §5). --- ## 5. O que **NÃO** fazer - **Não** criar pastas `Meshes/`, `Textures/`, `Materials/` no nível alto. - **Não** criar `Blueprint Struct` no editor. Toda struct é C++. - **Não** referenciar BPs de mob/item com `TObjectPtr<>` (hard ref) a partir de DataTables ou Subsystems globais — usar `TSoftObjectPtr<>`. - **Não** misturar assets de uma entidade com assets de outra. Cada Wolf_Gray, Sword_Iron, etc. é uma pasta isolada. - **Não** colocar nada na raiz `Content/`. Tudo é `Content/ZMMO/...` ou `Content/{Developers,AutoCreated,ExternalContent}/...`. `AutoCreated/` é quarentena do Zeus UMG Forge: refina-se e **move** para `ZMMO/UI/FrontEnd/` antes de produção; o cru não cooka. - **Não** importar conteúdo do Fab/Megascans directo para dentro de `ZMMO/`. Vai sempre para `ExternalContent/` e, se quiser usar, **referencia** dali. - **Não** colocar dados tabulares em Blueprint (lista hardcoded de itens, array de stats no ABP). Move para DataTable. - **Não** duplicar `FStatBlock` em C++ se já existe — importar de `Source/ZMMO/Data/Players/StatBlock.h`. - **Não** colocar hard ref de textura/som de tema dentro de um Widget (`TObjectPtr ChristmasFrame` é proibido). Widgets pedem ao `UZMMOThemeSubsystem` via `EZMMOThemeKey`. Hard ref a asset de tema bloqueia a troca em runtime e quebra a política de fallback. Vale também para **telas de front-end** (Boot/Login/ServerSelect/Lobby/…): a saída crua do Forge traz hard ref — tem de ser reconectada ao subsystem antes do merge (ver §4.8). - **Não** criar chave de tema "ad-hoc" como `FName("HUD.Frame")`. Toda chave é entrada do enum C++ `EZMMOThemeKey`. String literal é proibida — o compilador tem de quebrar quando alguém esquecer de adicionar a chave. - **Não** preencher um tema sazonal com **todas** as chaves só porque o Default preenche. Tema sazonal só lista o que **substitui**; o resto cai no Default automaticamente. --- ## 6. Excepções permitidas - Dentro de uma entidade self-contained, sub-pasta `Animations/` é OK quando passa de 5–6 anims. O mesmo vale para `Audio/` e `VFX/` próprios da entidade. - `Items/.../Equipment/Weapons/Sword_Iron/` mantém o ícone `T_Icon_*` junto do mesh — é a única excepção em que textura e mesh ficam na mesma pasta raiz da entidade (porque o ícone é parte do item, não um asset solto). - Skeleton compartilhado entre variantes (ex: `Wolf_Gray`, `Wolf_Black`) pode viver em `Entities/Mobs/Beasts/_Shared/Wolf/` se as variantes reutilizarem o mesmo `SK_` e `Animations/`. Pasta começa com `_` para ordenar primeiro no Content Browser. --- ## 7. Referências externas - [Allar UE5 Style Guide](https://github.com/Allar/ue5-style-guide) — base das nossas convenções de nomenclatura e estrutura. - [Epic – Unreal Engine Directory Structure](https://dev.epicgames.com/documentation/en-us/unreal-engine/unreal-engine-directory-structure). - [Lyra Project Structure](https://github.com/iliags/LyraGameVault/blob/main/Lyra%20Project%20Structure.md) — modelo Epic para projecto multiplayer escalável. - [Hyperdense – UE5 folder structure best practices](https://medium.com/@sarah.hyperdense/ue5-project-folder-structure-and-organization-best-practices-b9e487c330a3). - [Epic Forum – Using DataTables, Structs and DataAssets for Inventory](https://forums.unrealengine.com/t/using-data-tables-structs-and-data-assets-for-an-inventory/2381408). - [Epic Forum – Item system: how to structure/store data](https://forums.unrealengine.com/t/item-system-how-to-structure-store-data/2170364). --- ## 8. Como mudar este documento Este `.md` é a fonte de verdade da arquitectura do cliente. Mudanças estruturais (nova pasta no nível alto, mudança de convenção de nome, novo tipo de asset) **precisam de PR alterando este ficheiro primeiro**. Sem PR neste documento, não se mexe na árvore real.