Files
ZMMO/ARQUITETURA.md
Mateus Rodrigues abeec244d5 feat(ui): UI_Button_Master estilizado (FUIStyle gold) + style transparente; remove WBP base redundante
- UMG nao permite WBP-com-arvore herdar de WBP-com-arvore: a base abstrata e SO a classe C++ UUIButton_Base. UI_Button_Master reparentado direto p/ /Script/ZMMO.UIButton_Base; WBP UI_Button_Base (redundante) deletado. - UI_Button_Master: arvore propria (SizeBox_Master/Bg/ContentOverlay/ButtonText/Number_Text/Image_Icon/InputActionWidget) via set_widget_tree; ButtonTextValue='Button Text'. - BS_Button_Transparent (novo CommonButtonStyle: brushes NoDraw + padding 0) atribuido ao Style -> remove o fundo branco padrao do UCommonButtonBase; so o Bg (FUIStyle) aparece. - DT_UI_Styles linha Default: Button agora com cores Aurora Arcana (Primary dourado etc.). - ARQUITETURA.md §3.3 corrigido (regra UMG: C++ abstrato -> WBP concreto, variantes sao WBPs irmaos). Compila 0 erros; visual confirmado (dourado + BUTTON TEXT, sem moldura).

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
2026-05-18 22:58:10 -03:00

638 lines
30 KiB
Markdown
Raw 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.
> **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`.
**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`.
---
## 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.