feat(ui): sistema de UI Style (tokens C++ por tema) + fontes Aurora Arcana

Camada de estilo data-driven do cliente ZMMO, portando o nucleo do projeto de referencia (Hyper) para C++ conforme ARQUITETURA.md (BP Struct proibido):

- Source/ZMMO/Data/UI: UIStyleTypes.h (enums EUI*), UIStyleTokens.h (tokens primitive/semantic/component), UIStyleRow.h (FUIStyle + FUIStyleRow). - ZMMOThemeSubsystem: GetActiveUIStyle() resolve DT_UI_Styles por ThemeId com fallback Default. - Fontes: 8 UFontFace Inline (Cinzel/Rajdhani) em Content/ZMMO/UI/Fonts + DT_UI_Styles preenchido. - ARQUITETURA.md: prefixo de widget UI_, classes base UUI*_Base (UCLASS Abstract) sem prefixo ZMMO. - Config/DefaultGame.ini: UIStyleTable apontando DT_UI_Styles. - Scripts/import_fonts.py (importador idempotente). Inclui tambem o scaffolding do modulo ZMMO ainda nao versionado (Source/ZMMO/Data, Game/UI, Content/ZMMO, uproject, Build.cs).

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
This commit is contained in:
2026-05-18 21:21:32 -03:00
parent 50471831a2
commit 1c06d69174
124 changed files with 2328 additions and 1 deletions

630
ARQUITETURA.md Normal file
View File

@@ -0,0 +1,630 @@
# 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.
> **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
│ │
│ ├── UI/ ← HUD, Login, CharacterSelect, Inventory,
│ │ │ Chat, Party, Guild, Trade, Quest, Map,
│ │ │ Shared, Icons
│ │ └── 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>/
├── 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
│ ├── Modes/ ← GameMode + GameInstance
│ └── Network/ ← UZMMOWorldSubsystem
└── 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)
└── 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` |
| `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`. O Widget
Blueprint que herda usa prefixo de asset `UI_` (ver §3.2): `UI_Button_Base`
(abstrato) → `UI_Button_Master``UI_Button_<Feature>`.
---
## 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`.
---
## 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,ExternalContent}/...`.
- **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.
- **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.