- Delete portal_mode_manager.gd, portal_door.gd, portal_door.tscn - Strip all Tekton Doors logic from main.gd, player.gd, lobby.gd, lobby_room.gd, lobby_manager.gd, camera_context_manager.gd, music_manager.gd, tekton.gd, enhanced_gridmap.gd, playerboard_manager.gd, special_tiles_manager.gd - Remove TK enum (TEKTON_DOORS=2), mode_config schema, arena area - Update tests: 3 modes instead of 4 - Strip HowToPlay tab from main.tscn
9.8 KiB
Skin Creation Workflow
How to author a new character skin, register it in the game's shop/gacha catalog, and ship it to players.
Overview
Each skin is defined in two places that must stay in sync:
| Layer | File | What it stores |
|---|---|---|
| Client (visual) | scripts/managers/skin_manager.gd |
Mesh slots, material paths, override/overlay mode |
| Server (shop) | server/nakama/lua/economy.lua |
Item ID, name, category, price (gold/star) |
The item id in economy.lua must match the dictionary key in skin_manager.gd SKIN_CATALOG exactly -- the Godot client looks up the item ID from the wallet/inventory and applies the matching skin data.
Step 1: Create the Skin Material
Open the Skin Shader Generator at res://scenes/tools/skin_shader_generator.tscn.
- Run the scene (F6).
- Import your base albedo and mask textures (PNG with color/alpha channels).
- Use the UI to visualize UV overlays and adjust color channels (Red, Green, Blue, Alpha).
- Export the configured material as a
.tresfile intoassets/materials/skins/or a subfolder:assets/characters/skins/hat/assets/characters/skins/clothing/assets/characters/skins/gloves/
Material path conventions (Oldpop character):
| Category | Example path |
|---|---|
| hat | res://assets/characters/skins/hat/oldpop_mat_hat_blue.tres |
| costume/clothing | res://assets/characters/skins/clothing/oldpop_mat_cloth_red_pant.tres |
| glove | res://assets/characters/skins/gloves/oldpop_mat_gloves_blue.tres |
| accessory | res://assets/characters/skins/accessory/ |
Step 2: Register the Skin in SkinManager (Client)
Open res://scripts/managers/skin_manager.gd and add a new entry inside SKIN_CATALOG (between [BEGIN_SKIN_CATALOG] and [END_SKIN_CATALOG] markers).
Entry format
"item_id": {
"category": "head", # head | costume | glove | accessory
"character": "Oldpop", # node name under CharacterRoot
"slots": [
{
"mesh": "oldpop-hat1", # MeshInstance3D child name
"mode": "override", # "override" | "overlay"
"material": "res://path/to/material.tres"
},
]
}
Slot modes
override--set_surface_override_material(0, mat). Replaces the base material entirely. Preserves the outline shader (next_pass) automatically.overlay--material_overlay = mat. Transparent layer on top of the base material. Good for costume/pant patterns.
Multi-slot skins (costume example)
Costumes typically touch 3 meshes:
"oldpop-grey-pant": {
"category": "costume",
"character": "Oldpop",
"slots": [
{ "mesh": "oldpop-body", "mode": "overlay", "material": "res://assets/characters/skins/clothing/oldpop_mat_cloth_grey_pant.tres" },
{ "mesh": "oldpop-bottom1", "mode": "override", "material": "res://assets/characters/skins/clothing/oldpop_mat_cloth_grey_pant.tres" },
{ "mesh": "oldpop-bottom2", "mode": "override", "material": "res://assets/characters/skins/clothing/oldpop_mat_cloth_grey_pant.tres" },
]
},
Tips
- Leave
"material"as""if the.tresfile is not ready yet. The slot is skipped gracefully. - Use the Skin Catalog Editor (
res://scenes/tools/skin_catalog_editor.tscn) to avoid manual edits. Click Save & Generate to rewrite bothskin_manager.gdandeconomy.lua.
Step 3: Register the Skin in Economy (Server)
Open server/nakama/lua/economy.lua and add a new entry to SHOP_CATALOG_DEFS.
Catalog entry format
{ id = "oldpop-blue-hat", name = "Oldpop Blue Hat", category = "head", gold = 100, star = 0, rarity = "Common", character = "Oldpop" },
| Field | Type | Description |
|---|---|---|
id |
string | Must match the SKIN_CATALOG key in skin_manager.gd exactly |
name |
string | Display name shown in shop |
category |
string | head / costume / glove / accessory |
gold |
number | Gold coin price (0 = not sold for gold) |
star |
number | Star gem price (0 = not sold for stars) |
rarity |
string | "Common" / "Uncommon" / "Rare" -- cosmetic label only |
character |
string | Character this skin belongs to (e.g. "Oldpop") |
Existing catalog (12 items)
oldpop-blue-hat head 100 gold Common Oldpop
oldpop-green-hat head 100 gold Common Oldpop
oldpop-red-hat head 100 gold Common Oldpop
oldpop-yellow-hat head 100 gold Common Oldpop
oldpop-og-pant costume 0 gold Common Oldpop (free)
oldpop-grey-pant costume 150 gold Common Oldpop
oldpop-red-pant costume 150 gold Common Oldpop
oldpop-yellow-pant costume 150 gold Common Oldpop
oldpop-blue-gloves glove 75 gold Common Oldpop
oldpop-green-gloves glove 75 gold Common Oldpop
oldpop-red-gloves glove 75 gold Common Oldpop
oldpop-yellow-gloves glove 75 gold Common Oldpop
Step 4 (Optional): Add Skin as Gacha Prize
Gacha-only skins are registered in server/nakama/lua/gacha.lua inside GACHA_DATA.real_prize_catalog.
Existing gacha skins (4 items)
skin_gacha_rainbow_suit = { name = "Rainbow Suit", category = "costume", rarity = "real_prize", character = "" }
skin_gacha_dragon_hat = { name = "Dragon Hat", category = "head", rarity = "real_prize", character = "" }
skin_gacha_phantom_gloves = { name = "Phantom Gloves", category = "glove", rarity = "real_prize", character = "" }
skin_gacha_neon_acc = { name = "Neon Accessory", category = "accessory", rarity = "real_prize", character = "" }
Gacha skins also need a skin_manager.gd entry (same step 2 format) and a skin_catalog_editor entry so SkinManager.apply_loadout() can render them. The server catalog is optional -- gacha skins are not sold in the shop directly.
The gacha pulls these IDs from GACHA_DATA.pools.real_prize, so add your item_id there too.
pools = {
common = {"frag_common"},
uncommon = {"frag_uncommon"},
rare = {"frag_rare"},
real_prize = {
"skin_gacha_rainbow_suit",
"skin_gacha_dragon_hat",
"skin_gacha_phantom_gloves",
"skin_gacha_neon_acc",
-- add new skin here
}
},
Step 5: Deploy
Hot patch (content only) -- recommended for skins
-
Commit changes to
experimentalbranch:scripts/managers/skin_manager.gdserver/nakama/lua/economy.lua(if shop item)server/nakama/lua/gacha.lua(if gacha prize)- Material
.tresfiles
-
Push to
experimental. -
Trigger
deploy_patch.ymlvia Gitea UI workflow dispatch.- CI runs
--export-packto buildpatch.pck. - CI force-pushes
patch.pck+version.jsontopatchesbranch. - Existing players auto-download on next boot via
GameUpdateManager.
- CI runs
Full binary release (if engine/templates changed)
Tag a version (e.g. v2.5.0) and push. CI builds all platform binaries and uploads to the release.
Full Flow Diagram
┌──────────────────────┐
│ 1. Create Material │
│ skin_shader_generator │
│ ────────────────── │
│ Export .tres file │
└────────┬─────────────┘
v
┌──────────────────────────────┐
│ 2. Register in SkinManager │
│ skin_manager.gd │
│ ────────────────── │
│ Add SKIN_CATALOG entry │
│ (mesh slots + material) │
└────────┬─────────────────────┘
v
┌──────────────────────────────┐
│ 3. Register in Economy │
│ economy.lua │
│ ────────────────── │
│ Add SHOP_CATALOG_DEFS │
│ (price, name, category) │
└────────┬─────────────────────┘
v (optional)
┌──────────────────────────────┐
│ 4. Gacha Prize │
│ gacha.lua │
│ ────────────────── │
│ real_prize_catalog + pools │
└────────┬─────────────────────┘
v
┌──────────────────────┐
│ 5. Git Push & CI │
│ deploy_patch.yml │
│ ────────────────── │
│ patch.pck → players │
└──────────────────────┘
Troubleshooting
| Symptom | Cause | Fix |
|---|---|---|
| Skin visible in editor but not in-game | Material path wrong or .tres not exported |
Verify res:// path in SKIN_CATALOG, run --export-pack |
| Skin purchase fails: "NotEnoughFunds" | Wallet balance insufficient | Check gold/star prices in economy.lua |
| Skin visible on all characters | character field wrong |
Set correct character node name |
| Skin purchase fails: "ItemNotFound" | Item ID not in SHOP_CATALOG_DEFS | Add entry matching SKIN_CATALOG key |
| Player downloads patch but skin missing | econmy.lua change didn't reach server | Nakama hot-reload: restart Nakama container or wait for next restart |
| Outline shader lost on skin | next_pass not preserved | SkinManager preserves it automatically -- verify with latest skin_manager.gd |