no WYDConverter eu fiz uma área de tutorial e um deles é a criação de novos itens/mobs, veja se te ajuda. copia e crie um .md pode ser no próprio VS que vai ficar bem mais legível.
# Guia: Criar Mobs, Objetos e Meshes Novos para o WYD
> Passo a passo completo para criar conteudo 3D novo e integrar no cliente WYD.
> Cobre mobs (MSH skinned), objetos estaticos (MSA), modelagem, exportacao,
> conversao e registro nos arquivos do cliente.
---
## 1. Sistema de Arquivos do WYD
### 1.1 Arquivos 3D por mob
| Arquivo | Exemplo | Conteudo | Formato |
|---|---|---|---|
| `.msh` | `nm010101.msh` | Mesh skinned (vertices, indices, bones, skinning) | Binario 32B header |
| `.bon` | `nm01.bon` | Hierarquia do esqueleto (pares parentID/boneID) | Binario 8B por bone |
| `.ani` | `nm010101.ani` | Animacao (matrizes 4x4 locais por bone por frame) | Binario 8B header + matrices |
| `.wys` | `nm010101.wys` | Textura DXT1 comprimida | Binario WYS |
| `.wyt` | `nm010101.wyt` | Textura BGRA raw (formato antigo) | Binario WYT |
### 1.2 Convencao de nomes
```
{prefixo}{PPVV}.ext
prefixo = skeleton type (4 chars): nm01, ch01, ch02, tr09, hs01...
PP = parte/slot (2 digitos): 01=corpo, 02=helm, 03=armor...
VV = visual/variante (2 digitos): 01=base, 02=alt...
```
**Regra critica:** os primeiros 4 caracteres definem o skeleton. Todos os arquivos
com mesmo prefixo compartilham o mesmo `.bon` e os mesmos `.ani`.
Exemplos:
- `nm010101.msh`, `nm010102.msh` → usam `nm01.bon`
- `ch010597.msh` → usa `ch01.bon`, slot 05 (luva), visual 97
### 1.3 Arquivos de registro do cliente
Estes arquivos precisam ser editados para o cliente reconhecer mobs novos.
| Arquivo | Path | Funcao | Editar quando |
|---|---|---|---|
| `BoneAni4.txt` | `Mesh\BoneAni4.txt` | Registra skeletons: index, num ANIs, num partes, prefixo | Skeleton NOVO |
| `ValidIndex.bin` | `Mesh\ValidIndex.bin` | Mapeia quais ANIs existem para cada skeleton | Skeleton NOVO |
| `MeshTextureList.bin` | `Mesh\MeshTextureList.bin` | Tabela de texturas de meshes (nome → index) | Textura NOVA |
| `MeshList.txt` | `Mesh\MeshList.txt` | Registra objetos estaticos MSA: index → path | MSA NOVO |
| `AniSound4.txt` | `AniSound4.txt` | Tabela de animacoes/velocidades/sons por skeleton type | Skeleton NOVO |
| `ItemList.bin` | Server-side | Define nClass do mob → mapeia para skeleton via `BASE_DefineSkinMeshType` | Mob NOVO |
| `Basedef.cpp` | Compilado no .exe | `BASE_DefineSkinMeshType(nClass)` → nBoneAniIndex (hardcoded switch) | Skeleton NOVO (recompilacao) |
**Para mob com skeleton EXISTENTE:** nenhum destes arquivos precisa ser editado.
Basta colocar o MSH + WYS na pasta `Mesh\` com o prefixo correto.
**Para mob com skeleton NOVO:** todos os arquivos acima precisam ser editados.
---
## 2. Formatos Binarios Detalhados
### 2.1 MSH (Mesh Skinned)
```
Header (32 bytes):
[00] DWORD dwParentID — ID do bone pai (0xFFFFFFFF = root)
[04] DWORD dwMeshID — ID do bone deste mesh
[08] DWORD dwFVF — Flexible Vertex Format (D3D9)
[0C] DWORD sizeVertex — Bytes por vertice
[10] DWORD numFaceInfluence — Numero de face influences
[14] DWORD numPalette — Numero de bones na palette
[18] DWORD vertexCount — Numero de vertices
[1C] DWORD faceCount — Numero de INDICES (nao faces!)
Bones (se numPalette > 0):
XMFLOAT4X4[numPalette] — Offset matrices (64 bytes cada)
DWORD[numPalette] — Bone IDs (4 bytes cada)
Vertices:
Layout depende do FVF. Tipico para skinned:
Position XYZ (12B) + BlendWeights (4-12B) + PackedBoneIndices (4B)
+ Normal (12B) + UV (8B)
Indices:
uint16[faceCount] — Index buffer 16-bit
```
### 2.2 BON (Bone Hierarchy)
```
Arquivo = array de pares (8 bytes cada):
[parentID: DWORD] [boneID: DWORD]
parentID = 0xFFFFFFFF ou 0 → root (sem pai)
parentID = N → bone filho de bone N
Tamanho total = numBones * 8 bytes
```
Exemplo para esqueleto com 5 bones:
```
[0xFFFFFFFF] [0] — Bone 0 e root
[0] [1] — Bone 1 filho de Bone 0
[0] [2] — Bone 2 filho de Bone 0
[1] [3] — Bone 3 filho de Bone 1
[1] [4] — Bone 4 filho de Bone 1
```
### 2.3 ANI (Animation)
```
Header (8 bytes):
[00] DWORD numTicks — Numero de keyframes (frames de animacao)
[04] DWORD numBones — Numero de bones no skeleton
Dados:
Para cada tick (0..numTicks-1):
XMFLOAT4X4[numBones] — Matriz LOCAL de cada bone neste frame (64 bytes cada)
Tamanho total = 8 + (numTicks * numBones * 64) bytes
```
As matrizes sao LOCAL (relativas ao pai na hierarquia BON).
O cliente propaga pela hierarquia para obter as combined (world) matrices:
`combined[bone] = local[bone] * combined[parent]`
### 2.4 BoneAni4.txt
```
Formato texto, uma linha por skeleton:
{index} {numAniTypes} {numParts} {szAniName}
Campos:
index — Indice no array m_BoneAnimationList (0-99)
numAniTypes — Quantos ANIs diferentes este skeleton tem
numParts — Quantos slots de equipamento (1 para mobs simples, 7 para players)
szAniName — Prefixo completo com path (ex: mesh\nm01, mesh\ch01)
```
Exemplo:
```
0 38 7 mesh\ch01 <- personagem masculino, 38 animacoes, 7 partes
1 37 7 mesh\ch02 <- personagem feminino
2 1 1 mesh\hs01 <- cavalo, 1 animacao, 1 parte
3 2 1 mesh\nm01 <- mob tipo 1, 2 animacoes, 1 parte
...
```
### 2.5 ValidIndex.bin
```
Array binario fixo:
stValidAniList[100][186]
stValidAniList = struct { int nI; } — 4 bytes
Tamanho total = 100 * 186 * 4 = 74.400 bytes
Indexacao:
m_stValidAniList[skeletonIndex][aniSlot].nI = aniFileIndex
O cliente usa nI+1 para montar o nome do ANI:
sprintf(szTemp, "%s%04d.ani", szAniName, m_stValidAniList[nCount][nFI].nI + 1)
Exemplo:
m_stValidAniList[3][0].nI = 0 → mesh\nm010001.ani (idle)
m_stValidAniList[3][1].nI = 1 → mesh\nm010002.ani (walk)
m_stValidAniList[3][2].nI = 100 → mesh\nm010101.ani (arma 01, idle)
```
Codificacao dos 4 digitos do ANI:
```
XXYY onde:
XX = tipo de arma (weapon): nArrayIndex / 100 - 1
YY = tipo de acao (motion): nArrayIndex % 100 - 1
Exemplos (nI+1 → arquivo):
0001 → arma 0, acao 0 (idle)
0002 → arma 0, acao 1 (walk)
0003 → arma 0, acao 2 (run)
0004 → arma 0, acao 3 (attack1)
0101 → arma 1, acao 0 (idle com espada)
```
### 2.6 MeshList.txt
```
Formato texto, uma linha por objeto estatico:
{index} {path_relativo}
Campos:
index — Indice no array m_stCommonMesh (0-3047)
path_relativo — Caminho do MSA relativo ao diretorio do cliente
```
Exemplo:
```
0 mesh\cas001.msa
1 mesh\cas002.msa
50 mesh\tree01.msa
100 mesh\sword01.msa
```
O engine chama `GetCommonMesh(dwObjType)` onde dwObjType = index.
Mapas (.trn) referenciam objetos pelo dwObjType.
### 2.7 MeshTextureList.bin
```
Array binario fixo de stTextureListInfo (264 bytes cada):
char szFileName[255] — path relativo da textura (ex: "mesh\nm010101.wys")
char cAlpha — tipo de alpha: 'N'=none, 'A'=alpha, 'a'=1bit, 'C'=compressed
DWORD dwLastUsedTime — runtime (ignorar, preencher com 0)
DWORD dwShowTime — runtime (ignorar, preencher com 0)
Tamanho por versao:
TMProject classico: 2048 slots = 540.672 bytes
WYD Global: 6096 slots = 1.609.344 bytes
```
O cliente busca textura por nome (`GetModelTextureIndex`): percorre TODOS os slots
comparando `szFileName` com o nome calculado pelo `TMSkinMesh`. Se nao encontra,
o mesh **NAO CARREGA** (retorna erro).
**Para adicionar textura nova:**
1. Encontrar um slot vazio (szFileName[0] == '\0')
2. Preencher szFileName com path relativo (ex: `mesh\xx010101.wys`)
3. Preencher cAlpha com 'N' (sem alpha) ou 'A' (com alpha)
4. dwLastUsedTime e dwShowTime = 0
### 2.8 AniSound4.txt
```
Formato texto. Uma secao por skeleton type (MAX_ANI_TYPE = 60 skeletons).
Cada secao tem 56 linhas (MAX_ANI_MOTION), uma por tipo de acao.
Para skeleton types 0 e 1 (players):
{nomeAcao} {ani0} {speed0} {ani1} {speed1} {ani2} {speed2} {ani3} {speed3} {sound}
Para skeleton types 2+ (mobs/NPCs):
{nomeAcao} {aniIndex} {speed} {sound}
Campos:
nomeAcao — label (ex: "IDLE", "WALK", "ATK1") — ignorado pelo parser
aniIndex — indice na lista de animacoes do skeleton (posicao no array concatenado)
speed — velocidade da animacao (ticks por frame)
sound — indice do som a tocar
```
Controla qual animacao tocar para cada acao do mob (idle, walk, attack, death, etc.)
e a velocidade de reproducao. Se o skeleton novo for um mob simples, basta preencher
as acoes basicas (idle=0, walk=1, attack=2, death=3).
### 2.9 Fluxo de resolucao no cliente
```
Servidor envia pacote com mob → nClass (tipo do mob)
↓
BASE_DefineSkinMeshType(nClass) → nBoneAniIndex (switch hardcoded em Basedef.cpp)
↓
BoneAni4.txt[nBoneAniIndex] → szAniName (ex: "mesh\nm01"), numParts, numAniTypes
↓
Skeleton: szAniName + ".bon" → mesh\nm01.bon
Mesh: szAniName + PPVV + ".msh" → mesh\nm010101.msh
Textura: szAniName + PPVV + ".wys" → mesh\nm010101.wys
→ GetModelTextureIndex(texName) → busca em MeshTextureList.bin
Animacao: ValidIndex.bin[nBoneAniIndex][slot] → nI+1 → mesh\nm010001.ani
→ AniSound4.txt[nBoneAniIndex] → mapeia acao → indice de animacao
```
### 2.10 STRUCT_ITEMLIST (ItemList.bin)
```
Struct STRUCT_ITEMLIST — 120 bytes por item:
char Name[64] — Nome do item
short nIndexMesh — Indice visual do mesh
short nIndexTexture — Indice visual da textura
short nIndexVisualEffect — Efeito visual (brilho, aura)
short nReqLvl — Nivel minimo
short nReqStr / nReqInt / — Requisitos de atributo
nReqDex / nReqCon
STRUCT_STATICEFFECT stEffect[12] — 12 efeitos estaticos (4 bytes cada)
int nPrice — Preco do item
short nUnique — Tipo de raridade
short nPos — Bitmask de slots onde pode equipar
short nExtra — Dados extras
short nGrade — Grau/rank do item
```
**nIndexMesh e nIndexTexture** sao os campos que conectam o item ao mesh visual.
O significado muda COMPLETAMENTE dependendo do tipo de item:
**ARMAS E ESCUDOS (slots 6 e 7 — MSA):**
nIndexMesh = indice DIRETO no MeshList.txt
O cliente chama: GetCommonMesh(nIndexMesh) → LoadMsa(MeshList[nIndexMesh])
Nenhuma formula intermediaria. Se a arma esta no index 500 do MeshList,
nIndexMesh = 500.
nIndexTexture = preenche LOOK_INFO.SkinX mas NAO e usado para renderizar
a arma 3D. A textura da arma vem do nome embeddado no MSA → MeshTextureList.
Na pratica, para armas, nIndexTexture = 0.
**EQUIPAMENTOS SKINNED (slots 0-5 — MSH):**
nIndexMesh = variante visual usada na FORMULA do nome do MSH:
sprintf("%s%02d%02d.msh", szAniName, slot+1, nIndexMesh + offset + 1)
NAO e um indice de MeshList. E um numero de variante (0, 1, 2...).
Exemplo: nIndexMesh=5 no slot coat → ch010306.msh (prefixo + slot03 + visual06)
nIndexTexture = modificador de textura na FORMULA do nome da WYS:
sprintf("%s%02d%02d.wys", szAniName, slot+1, (nIndexTexture & 0xFFF) + nIndexMesh + offset + 1)
Permite usar texturas diferentes para o mesmo mesh visual.
Exemplo: nIndexMesh=5, nIndexTexture=2 + 1 → ch010308.wys
**MONTARIAS (slot 14 — MSH skinned, skeleton proprio):**
O item de montaria fica em Equip[14].
sIndex do item → transformado (sIndex - 2045, com overrides hardcoded)
→ sIndex final usado como indice no g_pItemList:
nIndexMesh = g_pItemList[sIndex].nIndexMesh → Mesh0 (variante visual)
nIndexTexture = g_pItemList[sIndex].nIndexTexture → Skin0 (variante textura)
nClass (efeito 18) → BASE_DefineSkinMeshType(nClass) → skeleton da montaria
Funciona igual a equipamentos skinned: nIndexMesh e variante visual,
NAO indice do MeshList. Vem do ItemList.bin.
**nPos** — bitmask de slots de equipamento:
```
Bit 0 (0x01) = Slot 0: Face/Corpo
Bit 1 (0x02) = Slot 1: Helm (elmo)
Bit 2 (0x04) = Slot 2: Coat (armadura)
Bit 3 (0x08) = Slot 3: Pants (calca)
Bit 4 (0x10) = Slot 4: Gloves (luva)
Bit 5 (0x20) = Slot 5: Boots (bota)
Bit 6 (0x40) = Slot 6: Left (mao esq/escudo)
Bit 7 (0x80) = Slot 7: Right (mao dir/arma)
```
**nClass (efeito 18)** — tipo de classe que determina o skeleton:
Usado por `BASE_DefineSkinMeshType(nClass)` para mobs e montarias.
Para players: nClass 1/4 = masculino (ch01), 2/8 = feminino (ch02).
Para mobs: nClass 16-74 mapeiam para indices 2-57 no BoneAni4.txt.
### 2.11 Fluxo visual de armas
```
Item no Equip[7] (mao direita) ou Equip[6] (mao esquerda)
↓
g_pItemList[sIndex].nIndexMesh → nIndexMesh
↓
GetCommonMesh(nIndexMesh) ← DIRETO, sem formula intermediaria!
↓
MeshList.txt[nIndexMesh] → path do MSA (ex: mesh\wp001.msa)
↓
TMMesh::LoadMsa(path) → carrega MSA
↓
Textura: nome embeddado no MSA → GetModelTextureIndex → MeshTextureList.bin
```
### 2.12 Fluxo visual de equipamentos (armaduras, helmos, etc.)
```
Item no Equip[0-5] (face, helm, coat, pants, gloves, boots)
↓
g_pItemList[sIndex].nIndexMesh → LOOK_INFO.MeshX (variante)
g_pItemList[sIndex].nIndexTexture → LOOK_INFO.SkinX (textura mod)
↓
TMSkinMesh monta nome do arquivo:
Mesh: sprintf("%s%02d%02d.msh", szAniName, slot+1, MeshX + offset + 1)
Tex: sprintf("%s%02d%02d.wys", szAniName, slot+1, (SkinX & 0xFFF) + MeshX + offset + 1)
↓
Exemplo: ch01, slot 3 (coat), nIndexMesh=5, nIndexTexture=0
Mesh: ch010306.msh
Tex: ch010306.wys
```
---
## 3. Restricoes Tecnicas do Cliente
### 3.1 MSH (Skinned Meshes)
| Item | Limite | Origem |
|---|---|---|
| Bones por mesh | 40 max | `safePalette = min(n, 40)` em CMesh |
| Influences por vertice | 4 max | FVF XYZB1-B4 + LASTBETA_UBYTE4 |
| Vertices por mesh | 65535 max | Indices 16-bit no MSH |
| Textura por mesh | 1 | MSH nao tem attribute ranges |
| Textura dimensao | Potencia de 2 | DXT1 |
| Coordenadas | Z-Up | Nativo WYD |
| UV | V=0 no topo (D3D) | Nativo WYD e 3ds Max |
| Skeletons registrados | 100 max | MAX_BONE_ANIMATION_LIST |
| ANIs por skeleton | 186 max | Array fixo ValidAniList |
| Bones por skeleton | Sem limite hard | Limitado pela RAM |
### 3.2 MSA (Static Meshes)
| Item | Limite | Origem |
|---|---|---|
| Vertices por mesh | 65535 max | Indices 16-bit |
| Materiais por mesh | 32 max | dwAttCount no header |
| Texturas por material | 1 | Uma WYS por attribute range |
| Textura dimensao | Potencia de 2 | DXT1 |
| Coordenadas | Z-Up | Nativo WYD |
| UV | V=0 no topo (D3D) | Nativo WYD |
| Objetos em MeshList | 3048 max | MAX_COMMON_MESH |
| FVF suportados | 274 (normal) ou 322 (diffuse) | Engine hardcoded |
---
## 4. Workflow: Novo Mob com Skeleton Existente
**Cenario:** criar um mob novo que reutiliza o esqueleto e animacoes de um mob existente.
Este e o caminho mais rapido e seguro.
### Passo 1 — Exportar mob de referencia
No WYDConverter, abrir o MSH do mob que quer reusar (ex: `nm010101.msh`).
Exportar como FBX (target 3ds Max). O FBX contem:
- Geometria do mob original
- Esqueleto completo com bone IDs
- Animacao do ANI companion
- Textura companion como PNG
### Passo 2 — Importar no 3ds Max
Importar o FBX. O mesh aparece:
- Z-Up (nativo do Max e do WYD — sem conversao)
- Escala amplificada (conversor aplica x100 para edicao confortavel)
- Bones com nomes `Bone_000`, `Bone_001`, etc.
**NAO mover, rotacionar ou escalar o Armature.** A posicao dos bones e o bind pose
que as animacoes existentes esperam.
### Passo 3 — Modelar
- Deletar a geometria do mob original (so poligonos, NAO os bones)
- Modelar o novo mob ao redor do esqueleto existente
- Manter o mesh alinhado com os bones na posicao de bind
Regras:
- Apenas triangulos (sem quads/n-gons)
- Max 65535 vertices
- 1 material/textura por mesh
- Polycount razoavel (~500-3000 tris para mobs)
### Passo 4 — Skinning
- Aplicar Skin modifier no mesh
- Adicionar os bones do esqueleto importado
- Pintar weights: max 4 influences por vertice
- Weights devem somar 1.0
- Todo vertice deve ter pelo menos 1 bone com peso > 0
### Passo 5 — UV e Textura
- UV Unwrap no Max (UV convention D3D: V=0 topo — default do Max)
- Textura PNG/TGA em potencia de 2 (256x256 ou 512x512)
- O WYDConverter converte para WYS automaticamente
### Passo 6 — Exportar FBX
File → Export → FBX:
- Formato: FBX Binary
- Eixos: Z-Up (default Max)
- Smoothing Groups: ON
- Skin/Deformers: ON
- Embed Media: OFF
### Passo 7 — Converter
No WYDConverter:
1. Abrir o FBX exportado
2. Converter para MSH
3. Nomear: `nm010199.msh` (mesmo prefixo, numero livre)
4. Converter textura PNG → WYS: `nm010199.wys`
5. Copiar para `Mesh\` do cliente
### Passo 8 — Testar
O cliente carrega automaticamente:
- `nm010199.msh` — mesh novo
- `nm01.bon` — hierarquia (ja existe)
- `nm010001.ani` etc — animacoes (ja existem)
- `nm010199.wys` — textura nova
Nenhuma edicao de BoneAni4.txt ou ValidIndex.bin necessaria (mesmo skeleton).
---
## 5. Workflow: Mob com Skeleton e Animacoes Novas
**Cenario:** criar um mob completamente novo com esqueleto e animacoes proprias.
Requer ferramentas adicionais e edicao de arquivos de registro.
### Passo 1 — Modelar e Riggar no 3ds Max
- Modelar o mob
- Criar esqueleto com bones (usar nomes `Bone_000`, `Bone_001`, etc.)
- Bone 0 deve ser o root
- Max 40 bones
- Skin modifier com max 4 influences
### Passo 2 — Criar Animacoes
No 3ds Max, criar as animacoes necessarias:
- Idle (parado)
- Walk (andando)
- Run (correndo)
- Attack (atacando)
- Death (morrendo)
- etc.
Cada animacao sera exportada como um FBX separado (ou ranges no mesmo FBX).
### Passo 3 — Exportar
Exportar cada animacao como arquivo separado com mesh + skeleton + animation.
Formatos suportados:
- **FBX Binary** (3ds Max, Maya, Blender via plugin)
- **GLB/GLTF** (Blender nativo, qualquer DCC moderno)
### Passo 4 — Converter
Usar o WYDConverter para gerar:
| De | Para | Ferramenta | Status |
|---|---|---|---|
| FBX mesh | `.msh` | MSHEncoder (FBX→MSH) | ✅ Funciona |
| GLB/GLTF mesh | `.msh` | MSHEncoder (GLTF→MSH) | ✅ Funciona |
| FBX skeleton | `.bon` | **BONEncoder (FBX)** | ❌ NAO EXISTE |
| GLB/GLTF skeleton | `.bon` | **BONEncoder (GLTF)** | ❌ NAO EXISTE |
| FBX animation | `.ani` | **ANIEncoder (FBX)** | ❌ NAO EXISTE |
| GLB/GLTF animation | `.ani` | **ANIEncoder (GLTF)** | ❌ NAO EXISTE |
| PNG textura | `.wys` | WYSEncoder | ✅ Funciona |
### Passo 5 — Registrar no cliente
Para um skeleton completamente novo, TODOS estes arquivos precisam ser editados:
#### 5.1 Editar BoneAni4.txt
Adicionar uma linha para o novo skeleton:
```
{novoIndex} {numAnis} {numParts} mesh\{prefixo}
```
Exemplo: adicionar mob `xx01` com 3 animacoes, 1 parte:
```
50 3 1 mesh\xx01
```
O index (50) deve ser um slot livre no array (0-99).
#### 5.2 Editar ValidIndex.bin
Adicionar as entradas para mapear os ANIs do novo skeleton:
```
m_stValidAniList[50][0].nI = 0 → mesh\xx010001.ani (idle)
m_stValidAniList[50][1].nI = 1 → mesh\xx010002.ani (walk)
m_stValidAniList[50][2].nI = 2 → mesh\xx010003.ani (attack)
```
O ValidIndex.bin e um array binario fixo de 74400 bytes.
Offset para skeleton index N = N * 186 * 4 bytes.
Cada slot = 4 bytes (int).
#### 5.3 Editar MeshTextureList.bin
Adicionar a textura do novo mob em um slot vazio:
```
Slot livre → szFileName = "mesh\xx010101.wys", cAlpha = 'N'
```
**CRITICO:** sem esta entrada, o cliente NAO encontra a textura e o mesh nao carrega.
O `GetModelTextureIndex` faz busca linear por nome — se nao encontra, retorna -1,
e o CMesh aborta o carregamento.
Para encontrar slots vazios: qualquer entrada com `szFileName[0] == '\0'`.
Cada slot tem 264 bytes. Para editar diretamente no binario:
```
Offset = slotIndex * 264
[+0..+254] = szFileName (null-terminated, zero-padded)
[+255] = cAlpha ('N' ou 'A')
[+256..+259] = dwLastUsedTime (0)
[+260..+263] = dwShowTime (0)
```
#### 5.4 Editar AniSound4.txt
Adicionar uma secao para o novo skeleton type.
A posicao na lista corresponde ao nBoneAniIndex.
Para mob simples (56 linhas, formato `{nome} {aniIdx} {speed} {sound}`):
```
TYPE50
IDLE 0 30 0
WALK 1 30 0
RUN 1 20 0
ATK1 2 25 0
ATK2 2 25 0
DEATH 2 30 0
... (preencher restante com 0 0 0)
```
#### 5.5 Editar BASE_DefineSkinMeshType (recompilacao)
Em `Basedef.cpp`, a funcao `BASE_DefineSkinMeshType(nClass)` e um switch hardcoded
que mapeia o nClass do item/mob → nBoneAniIndex.
Para adicionar um novo mob com nClass=75:
```cpp
case 75:
return 50; // nosso novo skeleton xx01, index 50 no BoneAni4.txt
```
**Isto requer recompilacao do cliente.** Nao ha como evitar — o mapeamento e
hardcoded no executavel.
#### 5.6 Registrar no servidor (ItemList)
O servidor define o `nClass` de cada mob/NPC no `ItemList.bin` ou na
tabela de mobs. O nClass e o que o cliente recebe no pacote e usa para
chamar `BASE_DefineSkinMeshType`.
#### 5.7 Colocar arquivos
```
Mesh\xx01.bon — hierarquia
Mesh\xx010001.ani — idle
Mesh\xx010002.ani — walk
Mesh\xx010003.ani — attack
Mesh\xx010101.msh — mesh
Mesh\xx010101.wys — textura
```
---
## 6. Plano de Implementacao: Encoders Faltantes
### 6.1 BONEncoder (FBX/GLTF/GLB → BON)
**Complexidade:** Baixa (formato trivial: pares de DWORDs)
**Input:** FBX ou GLTF/GLB com skeleton
**Output:** arquivo .bon
**Algoritmo (FBX path):**
1. Percorrer todos os FbxNode com FbxSkeleton attribute
2. Para cada bone, extrair o boneID do nome (`Bone_XXX` → XXX)
3. Para cada bone, determinar o parentID:
- Root: parentID = 0xFFFFFFFF
- Outros: parentID = boneID do pai
4. Escrever pares [parentID, boneID] em ordem (root primeiro, BFS)
**Algoritmo (GLTF/GLB path):**
1. Carregar documento GLTF com glTF SDK
2. Ler `doc.skins[0].jointIds` — lista de nodes que sao joints
3. Para cada joint node, extrair boneID do nome (`Bone_XXX` → XXX)
4. Para cada joint, determinar parentID pela hierarquia de nodes:
- Se parent node nao esta em jointIds → root (0xFFFFFFFF)
- Senao → boneID do parent node
5. Escrever pares [parentID, boneID]
**Arquivos a criar:**
- `Conversores/BON/BONEncoder.h` — declaracao
- `Conversores/BON/BONEncoder.cpp` — `FromFBX()` e `FromGLTF()` + `SaveBON()` compartilhado
**Estimativa:** ~120 linhas de codigo, 2-3 horas.
### 6.2 ANIEncoder (FBX/GLTF/GLB → ANI)
**Complexidade:** Media (precisa avaliar animacao por frame e extrair matrizes locais)
**Input:** FBX ou GLTF/GLB com skeleton + animation
**Output:** arquivo .ani
**Algoritmo (FBX path):**
1. Ler o FBX com FBX SDK
2. Determinar numBones (do skeleton) e numTicks (do animation stack)
3. Para cada tick (frame):
a. Setar o tempo: `FbxTime::SetFrame(tick, eFrames30)`
b. Para cada bone (por boneID, 0..numBones-1):
- Avaliar `node->EvaluateLocalTransform(time)` → FbxAMatrix
- Converter FbxAMatrix para XMFLOAT4X4
- Se o FBX veio do WYDConverter: reverter anchor correction e scale
- Se e novo conteudo: usar local transforms direto
c. Gravar as numBones matrizes para este tick
4. Escrever header (numTicks, numBones) + todas as matrizes
**Algoritmo (GLTF/GLB path):**
1. Carregar documento GLTF com glTF SDK
2. Ler `doc.animations[0]` — channels e samplers
3. Determinar numTicks pelo max timestamp / (1/30) + 1
4. Para cada tick:
a. Calcular tempo: `t = tick / 30.0f`
b. Para cada bone:
- Interpolar TRS dos samplers (translation, rotation, scale) no tempo t
- Rotation: quaternion → matrix (GLTF usa quaternions nativamente)
- Compor local matrix: S * R * T
- Converter Y-Up → Z-Up (GLTF e Y-Up, ANI e Z-Up)
c. Gravar as numBones matrizes
5. Escrever header + matrizes
**Cuidados comuns:**
- As matrizes no ANI sao LOCAL (relativas ao pai), nao world
- FBX: `EvaluateLocalTransform` ja retorna local
- GLTF: channels ja sao local (por spec)
- FBX Z-Up (nosso export) = WYD Z-Up → sem conversao
- GLTF Y-Up → precisa converter para Z-Up: conjugar cada local matrix
- Scale: matrizes ANI sao puras (rotacao + translacao)
- Normalizar rotation columns para magnitude 1.0 antes de salvar
- Frame rate: WYD usa 30fps
- GLTF: samplers podem ter interpolacao LINEAR ou STEP
- LINEAR: interpolar entre keyframes
- STEP: usar valor anterior
**Arquivos a criar:**
- `Conversores/ANI/ANIEncoder.h` — declaracao
- `Conversores/ANI/ANIEncoder.cpp` — `FromFBX()`, `FromGLTF()` + `SaveANI()` compartilhado
**Estimativa:** ~350 linhas de codigo, 5-8 horas.
### 6.3 ValidIndexEditor (editor de ValidIndex.bin)
**Complexidade:** Baixa (array binario fixo, UI simples)
**Funcionalidade:**
- Carregar ValidIndex.bin (74400 bytes)
- Mostrar grid: skeleton index × ani slot → nI value
- Editar nI values
- Salvar ValidIndex.bin
**Integracao:** nova tab ou dialog no WYDConverter.
**Estimativa:** ~150 linhas, 2-3 horas.
### 6.4 BoneAni4Editor (editor de BoneAni4.txt)
**Complexidade:** Baixa (texto simples, UI simples)
**Funcionalidade:**
- Carregar BoneAni4.txt
- Mostrar tabela: index, numAniTypes, numParts, szAniName
- Adicionar/remover/editar linhas
- Salvar BoneAni4.txt
**Integracao:** mesma tab do ValidIndexEditor.
**Estimativa:** ~100 linhas, 1-2 horas.
### 6.5 MeshTextureListEditor (editor de MeshTextureList.bin)
**Complexidade:** Baixa (array binario fixo, ja temos decoder)
**Funcionalidade:**
- Carregar MeshTextureList.bin (auto-detecta classico 2048 vs Global 6096 slots)
- Mostrar tabela: index, szFileName, cAlpha
- Busca por nome (filtro)
- Adicionar textura em slot vazio (auto-encontrar proximo livre)
- Editar/remover entradas
- Salvar MeshTextureList.bin
**Nota:** Ja temos `MeshTextureListDecoder` com `LoadBIN`, `SaveBIN`, `LoadTXT`, `SaveTXT`.
O editor so precisa de UI — a logica de I/O ja existe.
**Integracao:** tab no DataEditor do WYDConverter.
**Estimativa:** ~120 linhas (UI apenas), 2-3 horas.
### 6.6 AniSound4Editor (editor de AniSound4.txt)
**Complexidade:** Baixa (texto formatado, parser simples)
**Funcionalidade:**
- Carregar AniSound4.txt
- Mostrar: skeleton type → 56 linhas (acao, aniIndex, speed, sound)
- Editar valores por celula
- Duplicar secao de skeleton existente (template para novo mob)
- Salvar AniSound4.txt
**Integracao:** mesma tab dos editores de registro.
**Estimativa:** ~150 linhas, 2-3 horas.
### 6.7 Ordem de Implementacao
```
Fase 1 — BONEncoder FBX + GLTF/GLB (2-3h)
Permite: criar hierarquia de skeleton novo de qualquer formato
Dependencias: nenhuma
Teste: exportar mob existente MSH→FBX→BON e MSH→GLB→BON, comparar com original
Fase 2 — ANIEncoder FBX (3-5h)
Permite: criar animacoes novas a partir de FBX
Dependencias: nenhuma
Teste: exportar mob existente MSH+ANI→FBX→ANI, comparar com original
Fase 3 — ANIEncoder GLTF/GLB (3-5h)
Permite: criar animacoes novas a partir de GLTF/GLB
Dependencias: SaveANI() da Fase 2
Teste: exportar mob existente MSH+ANI→GLB→ANI, comparar com original
Nota: GLTF usa quaternions + timestamps flutuantes (vs FBX Euler + frames fixos)
Precisa: interpolacao de samplers, quat→matrix, Y-Up→Z-Up
Fase 4 — Editores de registro (5-8h)
4a. BoneAni4Editor (1-2h) — ja temos parser
4b. ValidIndexEditor (2-3h) — binario fixo
4c. MeshTextureListEditor (2-3h) — ja temos LoadBIN/SaveBIN
4d. AniSound4Editor (2-3h) — parser texto
Permite: registrar skeleton, animacoes, texturas e sons
Teste: adicionar entradas, verificar que cliente carrega
Fase 5 — Workflow integrado (3-4h)
Batch: FBX/GLB → MSH + BON + ANI + WYS em um click
Auto-registro: adiciona entradas nos 4 arquivos de registro
UI: wizard "Novo Mob" no WYDConverter
Validacao: checar limites (40 bones, 65535 verts, etc.)
Suporte: arrastar FBX ou GLB, detectar conteudo automaticamente
```
**Total estimado: 18-27 horas de desenvolvimento.**
---
## 7. Checklist Final
### Para mob com skeleton existente:
- [ ] Exportar mob de referencia como FBX ou GLB
- [ ] Modelar novo mesh no DCC ao redor do skeleton
- [ ] Skin com max 4 influences, weights somam 1.0
- [ ] UV unwrap, textura potencia de 2
- [ ] Exportar FBX Binary Z-Up ou GLB
- [ ] Converter FBX/GLB → MSH no WYDConverter
- [ ] Converter textura → WYS
- [ ] Adicionar textura nova em MeshTextureList.bin (se textura nova)
- [ ] Copiar MSH + WYS para Mesh\ do cliente
- [ ] Testar in-game
### Para mob com skeleton novo:
- [ ] Tudo acima +
- [ ] Criar skeleton com bones `Bone_000` a `Bone_NNN`
- [ ] Criar animacoes (idle, walk, attack, death)
- [ ] Exportar FBX ou GLB por animacao
- [ ] Gerar BON via BONEncoder (FBX ou GLTF)
- [ ] Gerar ANIs via ANIEncoder (FBX ou GLTF)
- [ ] Editar BoneAni4.txt (adicionar linha com index, numAnis, numParts, prefixo)
- [ ] Editar ValidIndex.bin (mapear slots → nI para cada ANI)
- [ ] Editar MeshTextureList.bin (adicionar textura em slot vazio)
- [ ] Editar AniSound4.txt (adicionar secao com aniIndex/speed/sound por acao)
- [ ] Editar BASE_DefineSkinMeshType em Basedef.cpp (nClass → nBoneAniIndex)
- [ ] Registrar mob no servidor (ItemList: nClass + LOOK_INFO)
- [ ] Copiar BON + ANIs + MSH + WYS para Mesh\ do cliente
- [ ] Testar in-game
---
## 8. Criar Objetos Estaticos Novos (MSA)
Objetos MSA sao meshes estaticos SEM bones nem animacao. Usados para:
- Construcoes (casas, torres, portais, altares)
- Decoracoes de cenario (arvores, pedras, caixas, barris)
- Armas e itens de chao (espadas, escudos, pocoes)
- Efeitos estaticos (circulos, plataformas)
### 8.1 Diferenca entre MSH e MSA
| Aspecto | MSH (Skinned) | MSA (Static) |
|---|---|---|
| Bones/Skeleton | Sim (max 40) | Nao |
| Animacao | Sim (.ani + .bon) | Nao |
| Skinning/Weights | Sim (max 4 por vertice) | Nao |
| Multi-material | Nao (1 textura) | Sim (1-32 attribute ranges) |
| Diffuse color | Nao (FVF 274) | Opcional (FVF 322) |
| Uso tipico | Personagens, mobs, montarias | Cenario, itens, armas |
### 8.2 Sistema de registro MSA
MSA sao registrados via **MeshList.txt** — uma tabela texto simples:
```
{index} {path_relativo}
Exemplo:
0 mesh\cas001.msa
1 mesh\cas002.msa
2 mesh\tree01.msa
...
```
O index e o `dwObjType` que o engine usa para referenciar o objeto.
`GetCommonMesh(dwObjType)` busca o path no MeshList e chama `LoadMsa()`.
As texturas sao resolvidas de 2 formas:
1. Nome embeddado no MSA (campo de 11 bytes por attribute range)
2. Busca em MeshTextureList.bin por `mesh\{nomeBase}.wys`
### 8.3 Formatos de vertice MSA (FVF)
| FVF | Hex | Layout | Bytes | Uso |
|---|---|---|---|---|
| 274 | 0x112 | Position(12) + Normal(12) + UV(8) | 32 | Maioria dos objetos |
| 322 | 0x142 | Position(12) + Diffuse(4) + UV(8) | 24 | Objetos com vertex color |
**CRITICO:** o FVF deve ser preservado no round-trip. Converter um MSA de FVF 322
para FVF 274 QUEBRA a renderizacao no cliente (o engine trata 322 de forma especial).
### 8.4 Workflow: Criar MSA Novo
#### Passo 1 — Modelar
Crie o objeto no 3ds Max ou Blender:
- Apenas geometria (sem bones, sem skeleton)
- Triangulos apenas (sem quads/n-gons)
- Max 65.535 vertices
- Escala: 1 unidade = proporcional ao mundo WYD (exporte um objeto existente como referencia)
#### Passo 2 — Multi-material (opcional)
MSA suporta ate 32 materiais/texturas por mesh:
- Atribua materiais diferentes a grupos de faces
- Cada material pode ter sua propria textura
- Os attribute ranges sao preservados no round-trip
Para objetos simples, 1 material e suficiente.
#### Passo 3 — UV e Textura
- UV Unwrap normal
- Textura PNG ou TGA em potencia de 2 (256x256, 512x512)
- Para multi-material: 1 textura por material
- O WYDConverter converte PNG → WYS
#### Passo 4 — Exportar
No 3ds Max:
File > Export > FBX Binary, Z-Up, Smoothing Groups ON
No Blender:
File > Export > glTF 2.0 (.glb), +Y Up, Apply Modifiers ON
**NAO precisa** incluir Skin/Deformers (MSA e estatico).
#### Passo 5 — Converter no WYDConverter
1. Abrir o FBX ou GLB no WYDConverter
2. Dropdown "MSA" > "FBX -> MSA" (ou "GLB -> MSA")
3. Nomear: `cas999.msa` (ou qualquer nome livre)
4. Converter textura: PNG -> WYS: `cas999.wys`
#### Passo 6 — Registrar
**MeshList.txt** — adicionar linha:
```
{indexLivre} mesh\cas999.msa
```
O index deve ser um slot livre (0-3047, MAX_COMMON_MESH = 3048).
**MeshTextureList.bin** — adicionar textura:
```
Slot vazio → szFileName = "mesh\cas999.wys", cAlpha = 'N'
```
Para multi-material, adicionar CADA textura separadamente.
#### Passo 7 — Colocar e testar
```
Mesh\cas999.msa ← mesh estatico
Mesh\cas999.wys ← textura
```
O engine carrega via `GetCommonMesh(index)` quando o terreno
ou objeto referencia o `dwObjType` correspondente ao index.
Para testar: colocar o objeto em um mapa editando o .trn
(terrain file) com o index do MeshList.txt.
### 8.5 Checklist MSA
- [ ] Modelar objeto (sem bones, triangulos apenas)
- [ ] UV unwrap, textura potencia de 2
- [ ] Exportar FBX ou GLB (sem skeleton)
- [ ] Converter FBX/GLB → MSA no WYDConverter
- [ ] Converter textura → WYS
- [ ] Adicionar textura em MeshTextureList.bin
- [ ] Adicionar entrada em MeshList.txt (index + path)
- [ ] Copiar MSA + WYS para Mesh\ do cliente
- [ ] Testar no mapa (colocar objeto no .trn)