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>
745 lines
37 KiB
Markdown
745 lines
37 KiB
Markdown
# 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)
|
||
│ └── <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 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.
|