Files
ZMMO/ARQUITETURA.md
Mateus Rodrigues a354d9eefc chore(jobs): renomeia display "Aprendiz" -> "Novato"
DA_Job_Novice DisplayName e docs de arquitetura (3 ARQUITETURA*.md)
sincronizados com a nova terminologia PT-BR. Server (Novice.json +
JobsDatabase.cpp) sera commitado separado no repo do ZeusServerEngine.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
2026-05-23 21:29:12 -03:00

745 lines
37 KiB
Markdown
Raw Permalink Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
# 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 56.
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)
│ └── <nick-do-dev>/
├── AutoCreated/ ← saída CRUA do Zeus UMG Forge (HTML→WBP).
│ └── <batch>/ 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: `F<Nome>Row` quando é linha de DataTable. Ex: `FItemRow`, `FMobRow`.
- Struct genérica (não-DT): `F<Nome>`. Ex: `FStatBlock`.
- Enum: `E<Nome>`. 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<Componente>_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<X>_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_<Feature>` : `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<FName>` 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<FName>` 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<FName> 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<UDataTable> MobsTable;
//
// UPROPERTY(EditAnywhere, Category="Data")
// FName MobId;
//
// void AZMMOMobBase::BeginPlay()
// {
// Super::BeginPlay();
// if (UDataTable* Table = MobsTable.LoadSynchronous())
// {
// if (FMobRow* Row = Table->FindRow<FMobRow>(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<EZMMOThemeKey, FThemeEntry>` 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<UTexture2D> HudFrameTex;
//
// Faz:
// const UZMMOThemeSubsystem* Theme = GetGameInstance()->GetSubsystem<UZMMOThemeSubsystem>();
// 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:<n>`
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/<batch>/`.
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<UTexture2D> 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 56 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.