Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
129 commits
Select commit Hold shift + click to select a range
32155bc
Replace Mixamo character with grudge6 CDN kit and magic anims
MolochDaGod Aug 5, 2026
6f62c6b
docs: add live Vercel URL; ignore .env.local
MolochDaGod Aug 5, 2026
602052c
Toon RTS equip, cast loop, attack, inventory panel
MolochDaGod Aug 5, 2026
b35ad48
Replace air ball with windsurf ride + sail/deck IK sockets
MolochDaGod Aug 5, 2026
f0ad8f1
Enhance DRC weapon skills, TPS camera, and gait animations
MolochDaGod Aug 5, 2026
516314f
Add Rapier CCT and vfxgrudge beauty VFX for DRC weapon skills
MolochDaGod Aug 5, 2026
02108b7
Consume three-generator fleet catalog for baked props
MolochDaGod Aug 5, 2026
496ff8d
Fix grudge6 character mesh/scale/equip and clear console noise.
MolochDaGod Aug 5, 2026
73525e6
Use Multiverse grudge6 deploy path for mesh, atlas, and scale.
MolochDaGod Aug 5, 2026
78cc237
Fix exploded grudge6 mesh: unify multi-skeleton kits before anim.
MolochDaGod Aug 5, 2026
45fe63a
Purge broken player path; combat-first Toon RTS + DRC action bar.
MolochDaGod Aug 5, 2026
f60b84e
Lock Bip001 18-bone SSOT and force-hide bag/wood/quiver on combat equip.
MolochDaGod Aug 6, 2026
e792b62
Add missing grudge6 scaffold: hide-all exclusive equip + Open fit math.
MolochDaGod Aug 6, 2026
80c8d5e
Fix boot crash: countSkeletons import + tolerant Bip001 18 validation.
MolochDaGod Aug 6, 2026
3685fa8
Fix invisible hero: preserve equip after scaffold; snap TPS + physics…
MolochDaGod Aug 6, 2026
d0120d0
Fix hero scale/visibility: GLB SI fit band + never hide equipped meshes.
MolochDaGod Aug 6, 2026
a2fd7c8
Migrate 4 elements + arcane tree into fleet-shaped weapon skills.
MolochDaGod Aug 6, 2026
304935d
fix(casting): purge to Toon RTS ★ GLB loader + simple SI deploy
MolochDaGod Aug 6, 2026
bec32e1
feat(ride): wire RideIK to windsurf board deck and boom sockets
MolochDaGod Aug 6, 2026
186ca6c
fix(casting): player = Toon RTS GLB only
MolochDaGod Aug 7, 2026
9c2f2b1
fix(casting): stop head-at-feet — pose widest skeleton only, bones-on…
MolochDaGod Aug 7, 2026
ec93e5a
fix(casting): use ObjectStore loadRaceKit path — bone SI, no unify/pose
MolochDaGod Aug 7, 2026
e5a5187
docs(deploy): Toon RTS + windsurf asset SSOT; ride cache headers
MolochDaGod Aug 7, 2026
f754d06
purge: kill wrong grudge6 play systems (scaffold throw, no fallbacks)
MolochDaGod Aug 7, 2026
1021736
harden: stamp warlordsPlayContract on Toon play kit
MolochDaGod Aug 7, 2026
6daec00
fix(anim): prefer prod magic idle with hand tracks
MolochDaGod Aug 7, 2026
ae7b3f4
fix(ride): parent mount to deck, IK + physics glue until dismount
MolochDaGod Aug 7, 2026
ac98561
fix(loco): A/D camera-right, jump + S+Space backflip double-jump
MolochDaGod Aug 7, 2026
0a2f487
docs(deploy): Cloudflare DNS steps for casting.grudge-studio.com
MolochDaGod Aug 7, 2026
dee3c4b
feat(world): SI mapScale 1.5x + StageWater layer for 2m heroes
MolochDaGod Aug 7, 2026
7b4b795
docs: Casting Lab SSOT for Warlords UX and effect primitives
MolochDaGod Aug 7, 2026
755f1f2
feat(lab): EffectPrefab export + F melee residual from weapon tip
MolochDaGod Aug 7, 2026
508d643
feat(lab): HUD frames + left Lab Panel for race/equip/anims/API
MolochDaGod Aug 7, 2026
0b4dba1
feat(hud): Danger Room tight bar 6+6 + fleet combat hotkeys
MolochDaGod Aug 7, 2026
63e1373
feat(combat): longbow directional dodges + staff path cast modes
MolochDaGod Aug 7, 2026
6435443
feat(combat): mouse-aim face/move + weapon equip anim packs
MolochDaGod Aug 7, 2026
5b8b55f
fix(combat): A/D body-relative strafe left/right
MolochDaGod Aug 7, 2026
2f13156
chore: single Toon play path — Casting deploy is Warlords character SSOT
MolochDaGod Aug 7, 2026
a1d87ca
feat(lab): Showcase for race/weapon/anim review + bind master skills
MolochDaGod Aug 7, 2026
133296f
feat(loot): drop tier rates — max T5, no T6–8, level + difficulty
MolochDaGod Aug 7, 2026
2acbdf0
feat(world): prefab world drops — icon sprite, glow border, throw fro…
MolochDaGod Aug 8, 2026
0bc540a
feat(locomotion): MM dodge afterimage + anim library SSOT
MolochDaGod Aug 8, 2026
621ef6f
feat(ride): frontflip windsurf freeride + model-color vapor dodge
MolochDaGod Aug 8, 2026
f6f4024
docs(walk): MODE_META freeride blurb for Space deploy
MolochDaGod Aug 8, 2026
8c2efdb
feat(stage): island shore + staff element labels + ride IK order
MolochDaGod Aug 8, 2026
4448eb5
fix(ride): parent player under windsurf vehicle until dismount
MolochDaGod Aug 8, 2026
978708c
feat(core): SessionState SSOT for mode, ride, combat gates
MolochDaGod Aug 8, 2026
16d7927
feat(combat): mana+stamina hold intensity, staff signatures, gait blend
MolochDaGod Aug 8, 2026
4364d80
feat(camera): production TPS angles + soft-lock RMB focus
MolochDaGod Aug 8, 2026
e0b0bf1
fix(input): focus couples body to camera; free A/D turn; Shift sprint…
MolochDaGod Aug 8, 2026
00ed158
fix(input): Shift hold sprint, Shift+Ctrl slide, Ctrl roll with dir
MolochDaGod Aug 8, 2026
fe07a08
fix(input): LMB attack in focus, select free with unlocked mouse
MolochDaGod Aug 8, 2026
504ee13
feat(elements): fire storm ice nature holy arcane
MolochDaGod Aug 8, 2026
9984c73
fix(spells): Holy Light uses holy element not arcane
MolochDaGod Aug 8, 2026
e1ff697
feat(vfx): soft shake + creative element presentations
MolochDaGod Aug 8, 2026
348d3fd
feat(storm): narrow fast chain lightning + wind residual
MolochDaGod Aug 8, 2026
cd9f39b
feat(staff): transit mastery SSOT + all STAFF skill binds + nature trap
MolochDaGod Aug 8, 2026
905925c
feat(lab): equip weapons, T0 wand kit, F interact, mythic drop sources
MolochDaGod Aug 8, 2026
04bcd2b
fix(staff): catalog-only STAFF binds, no invented skills
MolochDaGod Aug 8, 2026
389110e
feat(t0): import Apprentice Wand + Sapling Staff from WEAPON_SKILLS
MolochDaGod Aug 8, 2026
90d1505
feat(lab): Prefabs tab for all game item categories from ObjectStore
MolochDaGod Aug 8, 2026
580b7f5
docs(audit): asset library half-done R2/D1/catalog conflicts
MolochDaGod Aug 8, 2026
aa87ec5
fix(deploy): T0 wand/sapling CDN meshes + SI hand attach
MolochDaGod Aug 8, 2026
d536137
docs: casting.grudge-studio.com DNS NXDOMAIN fix instructions
MolochDaGod Aug 8, 2026
64adab3
feat(casting): Warlords dev island, radials, UI skins, windsurf fix
MolochDaGod Aug 10, 2026
e8a5013
fix(casting): windsurf 180 yaw, small cursors, LMB/RMB tooltips
MolochDaGod Aug 10, 2026
7a8419c
fix(casting): action soft-lock focus camera and aim assist
MolochDaGod Aug 10, 2026
9c4cf9b
fix(casting): live path audit, anim CDN fallbacks, richer dev island map
MolochDaGod Aug 10, 2026
0f6ec6b
feat(casting): port LinearAbility skillshots into skillshot kit
MolochDaGod Aug 10, 2026
e0e520b
Mastery: XState activity, tool stow/draw, GRUDOX camera, spell variants
MolochDaGod Aug 11, 2026
d1f9f23
fix(windsurf): nose along travel + open-sea island ring
MolochDaGod Aug 11, 2026
8f9ada6
fix(windsurf): swap A/D freeride turn directions
MolochDaGod Aug 11, 2026
32bc85e
Windsurf back stow: quiver-scale mesh + cloth sail wind
MolochDaGod Aug 11, 2026
016f757
Ocean wind indicators from wind-attack silk (nearly invisible)
MolochDaGod Aug 11, 2026
e9854ca
Land/water heightfield physics + growing forest harvestables
MolochDaGod Aug 11, 2026
fde9cc2
Terrain aim/path: one projectToTerrain SSOT, no y=0 plane only
MolochDaGod Aug 11, 2026
ffe902c
feat(devnode): world node authoring page for biomes, harvest, PvE
MolochDaGod Aug 11, 2026
eb47f6d
feat(map): Training Room = DevIsland = DevNode one island
MolochDaGod Aug 11, 2026
2cbf39f
feat(deploy): Training Room production path for Vercel, R2, D1
MolochDaGod Aug 11, 2026
14ac3f8
feat(ui): MMO Main Panel RMB menus, asset catalog, fleet equipment API
MolochDaGod Aug 11, 2026
4dc0f1e
feat(ui): icon resolve, harvest→bag, fleet inventory deposit API
MolochDaGod Aug 11, 2026
affbcf9
feat(lab): production skills, shared glTF pipeline, terrain L2, eleme…
MolochDaGod Aug 11, 2026
227c800
chore(lab): consolidate SDK export, terrain mount, static imports, de…
MolochDaGod Aug 11, 2026
854d104
feat(pistol): flintlock t0-gun wire, Styloo bullets, blood vs terrain
MolochDaGod Aug 11, 2026
da8d7e2
feat(flintlock): wire T0 weapon skill kit runtime on casting
MolochDaGod Aug 11, 2026
a1379a3
feat(flintlock): muzzle spawn, fire timing, soft-lock assist, powder …
MolochDaGod Aug 11, 2026
a634c85
fix(pistol): apply fire/draw/reload timeScale in requestOneShot
MolochDaGod Aug 11, 2026
a93dba7
feat(flintlock): chamber empty = key 1 reload, baked reload clip, Ope…
MolochDaGod Aug 11, 2026
f77daf8
feat(combat): Charged Shot hold UX with ticks, rest, blend
MolochDaGod Aug 11, 2026
12719b4
feat(loco): fall loop/land/roll FBX for deterministic jump-fall blends
MolochDaGod Aug 11, 2026
fe70a63
feat(back-slot): water-only windsurf parts map + wing prefabs SSOT
MolochDaGod Aug 11, 2026
1497382
feat(equip): RMB T0-T1 catalog, mesh appearance, full paperdoll equip…
MolochDaGod Aug 11, 2026
413c25a
feat(casting): master merge contract 1.2.0 Linear + three-layer islan…
MolochDaGod Aug 11, 2026
9485889
feat(combat): dual weapon loadouts + Q-tap set swap
MolochDaGod Aug 11, 2026
a6ab7a5
feat(hud): bars pack player frame + enemy/ally overhead HP
MolochDaGod Aug 11, 2026
02f2b00
docs(weapons): T0 play stack SSOT + scaffold; product language weapon…
MolochDaGod Aug 11, 2026
0edc565
chore(weapons): play-stack manifests under docs/play-stack (skills/ i…
MolochDaGod Aug 11, 2026
e1ec03a
fix(ui): English staff/mode glyphs (no alchemy emoji)
MolochDaGod Aug 11, 2026
3385230
feat(hold): wire applyWeaponHoldPose after mixer on play equip
MolochDaGod Aug 11, 2026
5e47ed6
feat(audio): weapon skill + ability SFX from user WAVs
MolochDaGod Aug 11, 2026
f148a44
fix(audio): parry on attempt; soft burn loop only while player burning
MolochDaGod Aug 11, 2026
96a9fb1
feat(audio): magical parry SFX (wizard lightning) for staff/ward
MolochDaGod Aug 11, 2026
e72714f
feat(audio): magical impact SFX pack (3 variants, random)
MolochDaGod Aug 11, 2026
b4401d8
feat(combat): weapon mesh cylinder colliders, tip trail, apex residual
MolochDaGod Aug 11, 2026
5733a06
fix(play): exclusive equip, CDN-first anims, absolute HUD CSS URLs
MolochDaGod Aug 11, 2026
c4e2aea
fix(boot): non-blocking secondary anim packs; bars HUD origin URLs
MolochDaGod Aug 11, 2026
59ac7ac
fix(boot): hide loader fully; rescue empty equip; bars origin URLs
MolochDaGod Aug 11, 2026
42b3b0d
feat(anim): 2H melee idle from 2hand Idle.fbx for sword_shield pack
MolochDaGod Aug 11, 2026
6deff54
fix(assets): absolute HUD CSS urls + same-origin waternormals
MolochDaGod Aug 11, 2026
b3cfb53
fix(assets): restore overhead exports; absolute HUD + waternormals
MolochDaGod Aug 11, 2026
4fd5658
fix(combat): wire tryParryBlock on player hit path (P0)
MolochDaGod Aug 11, 2026
4a5e6f2
fix(lab): readable stage, spell SFX, same-origin HUD, layer SSOT
MolochDaGod Aug 11, 2026
a590bde
fix(render): OutputPass last in composer — restore visible stage
MolochDaGod Aug 11, 2026
e537659
feat(vfx-studio): singular tabbed skill authoring shell
MolochDaGod Aug 11, 2026
ed12e28
fix(gameplay): Fortnite shoulder TPS, feet on heightfield, UX gates
MolochDaGod Aug 11, 2026
b5c01ce
feat(fishing): 3D profession — pole cast, SCUM snag, Palworld fight bar
MolochDaGod Aug 11, 2026
f82b720
feat(fishing): Grudge Angler rods, harvest skill tree, SWG meals, nau…
MolochDaGod Aug 11, 2026
4dd0181
feat(fishing): game-pack fish SI scale, lure/rod size gates, big-game…
MolochDaGod Aug 11, 2026
a170469
fix(world): waterY=0, seafloor -5, sand, weld islands, island town, r…
MolochDaGod Aug 11, 2026
1b535bb
fix(world): plant river town on island, seafloor -5, water 0
MolochDaGod Aug 11, 2026
d0cd8ea
feat(anim): auto sheath weapons on traversal and mount
MolochDaGod Aug 11, 2026
4bb34a9
feat(lab): weapon prefab scale/import + shore bathymetry to -50m
MolochDaGod Aug 11, 2026
b2a5236
feat(fishing): super-rare titans, aetherwing recipes, claim docks T1-3
MolochDaGod Aug 11, 2026
55412de
feat(back): shark fin — 2x swim, no shark aggro, underwater breath
MolochDaGod Aug 11, 2026
2f7167d
docs(casting): data-plane audit — one Railway player DB
MolochDaGod Aug 18, 2026
bde00bd
docs(casting): stop dead objectstore catalog 404s + ignore dump blobs
MolochDaGod Aug 18, 2026
6eead4e
fix(casting): wire same-origin CDN proxy + live catalogs
MolochDaGod Aug 18, 2026
29df3bd
fix(ride): swap mirrored L/R windsurf foot and hand IK
MolochDaGod Aug 18, 2026
79112da
fix(casting): bind Bip001 clips via /api/assets/prod/anims
MolochDaGod Aug 18, 2026
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
The table of contents is too big for display.
Diff view
Diff view
  •  
  •  
  •  
31 changes: 31 additions & 0 deletions .gitignore
Original file line number Diff line number Diff line change
Expand Up @@ -134,3 +134,34 @@ dist
.yarn/build-state.yml
.yarn/install-state.gz
.pnp.*
.vercel
.env*

# Author multipacks (regen via scripts/* — do not ship in Vercel)
public/models/vfx/**/*_src.glb
public/models/vfx/**/*_src.gltf

# Duplicate override mirror (runtime SSOT is public/skills/production)
/skills/

# Large local weapon sources until R2 (keep on disk for lab)
public/models/weapons/t0-flintlock.glb


# >100MB — keep local / CDN later
public/models/fish/species/rare/ocean_creature.glb

# Local QA dumps (never ship)
_qa_*
deploy-out*.txt
tmp/
.tmp/

# Loose fish extracts (tracked SSOT lives in poles/lures/docks/species)
public/models/fish/*.glb
public/models/fish/_bundle/

# WIP scenery / class-form dumps until convert + R2
public/models/class-forms/
public/models/island-scenery/

592 changes: 344 additions & 248 deletions README.md

Large diffs are not rendered by default.

Binary file removed Standing Idle.fbx
Binary file not shown.
141 changes: 141 additions & 0 deletions api/cdn-proxy.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,141 @@
/**
* Same-origin R2 proxy — classic Vercel Node (req, res).
* Pattern from Warlords api/cdn-proxy.ts — do not invent a second CDN.
*
* vercel.json:
* /api/assets/:path* → /api/cdn-proxy?key=:path*
*
* node:https with same-host Referer. Do not use fetch() — Vercel forwards
* the incoming Referer and Cloudflare Hotlink Protection 1011s PNGs.
*/
import https from 'node:https';

const CDN_HOST = 'assets.grudge-studio.com';
const MAX_PATH = 512;

function setCors(res) {
res.setHeader('Access-Control-Allow-Origin', '*');
res.setHeader('Access-Control-Allow-Methods', 'GET, HEAD, OPTIONS');
res.setHeader('Access-Control-Allow-Headers', 'Content-Type, Range');
res.setHeader(
'Access-Control-Expose-Headers',
'ETag, Accept-Ranges, Content-Length, Content-Type, Content-Range, X-Upstream-Status'
);
res.setHeader('X-Grudge-Asset-Proxy', 'cdn-proxy');
}

function sanitizeKey(raw) {
let key = String(raw || '').trim();
try {
key = decodeURIComponent(key);
} catch {
return null;
}
key = key.replace(/^\/+/, '');
if (!key || key.length > MAX_PATH) return null;
if (key.includes('..') || key.includes('\\') || key.includes('://')) return null;
if (key.includes('//')) return null;
if (!/^[A-Za-z0-9._\-/% ]+$/.test(key)) return null;
return key;
}

function keyFromReq(req) {
const host = req.headers.host || 'localhost';
const url = new URL(req.url || '/', `http://${host}`);
const q = url.searchParams.get('key');
if (q) return sanitizeKey(q);
const path = url.pathname;
for (const prefix of ['/api/cdn-proxy/', '/api/assets/']) {
if (path.startsWith(prefix)) return sanitizeKey(path.slice(prefix.length));
}
return null;
}

export default function handler(req, res) {
setCors(res);

if (req.method === 'OPTIONS') {
res.statusCode = 204;
res.end();
return;
}
if (req.method !== 'GET' && req.method !== 'HEAD') {
res.statusCode = 405;
res.end('Method Not Allowed');
return;
}

const key = keyFromReq(req);
if (!key) {
res.statusCode = 400;
res.end('Bad Request');
return;
}

const host = req.headers.host || 'localhost';
const url = new URL(req.url || '/', `http://${host}`);
const extra = [...url.searchParams.entries()]
.filter(([k]) => k !== 'key')
.map(([k, v]) => `${encodeURIComponent(k)}=${encodeURIComponent(v)}`)
.join('&');
const path = `/${key}${extra ? `?${extra}` : ''}`;

const headers = {
Host: CDN_HOST,
Accept: typeof req.headers.accept === 'string' ? req.headers.accept : '*/*',
'User-Agent': 'grudge-vercel-asset-proxy',
Referer: `https://${CDN_HOST}/`
};
if (typeof req.headers.range === 'string') headers.Range = req.headers.range;
if (typeof req.headers['if-none-match'] === 'string') {
headers['If-None-Match'] = req.headers['if-none-match'];
}

const up = https.request(
{
protocol: 'https:',
hostname: CDN_HOST,
path,
method: req.method,
headers
},
(upRes) => {
res.statusCode = upRes.statusCode || 502;
res.setHeader('X-Upstream-Status', String(upRes.statusCode || 502));
for (const name of [
'content-type',
'content-length',
'content-range',
'accept-ranges',
'etag',
'last-modified',
'cache-control'
]) {
const v = upRes.headers[name];
if (typeof v === 'string') res.setHeader(name, v);
else if (Array.isArray(v)) res.setHeader(name, v.join(', '));
}
if (!res.getHeader('cache-control')) {
res.setHeader('Cache-Control', 'public, max-age=86400, stale-while-revalidate=3600');
}
if (req.method === 'HEAD') {
upRes.resume();
res.end();
return;
}
upRes.pipe(res);
}
);
up.setTimeout(20000, () => {
up.destroy(new Error('cdn timeout'));
});
up.on('error', () => {
if (!res.headersSent) {
res.statusCode = 502;
res.end('Bad Gateway');
} else {
res.end();
}
});
up.end();
}
48 changes: 48 additions & 0 deletions devnode.html
Original file line number Diff line number Diff line change
@@ -0,0 +1,48 @@
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8" />
<meta name="viewport" content="width=device-width, initial-scale=1.0" />
<title>Grudge · Training Room · DevNode</title>
<meta
name="description"
content="Author the Training Room / DevIsland map: biomes, terrain, cliffs, trees, rocks, flowers, ore, animals, PvE — same map as play."
/>
<link rel="icon" type="image/svg+xml" href="./favicon.svg" />
<link rel="stylesheet" href="./src/devnode/devnode.css" />
</head>
<body>
<div id="devnode-app">
<aside class="dn-panel" id="dn-side">
<header class="dn-head">
<h1>Training Room</h1>
<p class="dn-sub">DevIsland · DevNode · one map</p>
<a class="dn-link" href="./index.html">← Play Training Room</a>
</header>

<label class="dn-label">Biome</label>
<select id="dn-biome" class="dn-select"></select>

<label class="dn-label">Family</label>
<div id="dn-families" class="dn-chips"></div>

<label class="dn-label">Palette</label>
<div id="dn-palette" class="dn-palette"></div>

<div class="dn-actions">
<button type="button" id="dn-export" class="dn-btn">Export JSON</button>
<button type="button" id="dn-import" class="dn-btn dn-btn--ghost">Import JSON</button>
<button type="button" id="dn-clear" class="dn-btn dn-btn--ghost">Clear nodes</button>
<input type="file" id="dn-file" accept="application/json,.json" hidden />
</div>

<p class="dn-hint" id="dn-hint">
LMB place · RMB remove · MMB orbit · Scroll zoom · Shift+LMB skips place
</p>
<pre class="dn-stats" id="dn-stats">nodes: 0</pre>
</aside>
<canvas id="dn-canvas"></canvas>
</div>
<script type="module" src="./src/devnode/main.js"></script>
</body>
</html>
136 changes: 136 additions & 0 deletions docs/ADMIN_HUB_F1_F5_SSOT.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,136 @@
# Admin Hub F1–F4 · ] World — tools, editors, deployables

**Code:** `src/ui/AdminHub.js` · `src/api/deployableContract.js` · `src/api/prefabDraftStore.js`
**Host:** casting.grudge.studio (lab control plane)
**Related:** `PREFAB_SCAFFOLD_CONTROL_SSOT.md` · `WEAPON_PREFAB_UUID_SSOT.md` · `GAME_ITEM_PREFAB_PRODUCTION_SSOT.md`

---

## Hotkeys

| Key | Tab | Owns |
|-----|-----|------|
| **F1** | Player | Hero session, race kit, controller, equip shortcuts, Foundry/ID links |
| **F2** | Assets | Buildables · harvestables · vehicles — **scripts + purpose** |
| **F3** | Creatures | Enemy · ally · boss · NPC · vendor · Grudge race kits · AI brain |
| **F4** | Prefabs | Weapons · armour · T0 scaffold · craft · export |
| **]** | World | Stage notes · loot spawn · deploy surface map (**not F5**) |
| **`** | — | Auto run / freeride sail-row (toggle) |
| **Esc** | — | Close Admin Hub |
| **?** / **H** | — | Keyboard help (not F1) |

**F5 is unbound** (browser refresh elsewhere). Same admin key **toggles close** if that tab is already open. Top chips + TightBar **Admin** also open F4.

---

## Product intent

Continue **tools, editors, and systems** for:

1. **Prefab creation + saves** (local drafts → export JSON)
2. **Buildable assets** — mesh + **script** + **purpose** (structure, camp, craft, storage, harvest node…)
3. **Everything deployable** — weapons, armour, enemies, Grudge characters, vehicles, props

Do **not** invent a second catalog. Read ObjectStore / info SSOT; mint UUIDs only via ObjectStore pipelines.

---

## Deployable kinds

| Kind | Admin tab | Layers (have) | Jobs (do) |
|------|-----------|---------------|-----------|
| weapon | F4 | identity · stats · skills · assets · runtime · loadout | bag · equip · controller · hotbar · combat · craft · export |
| armour | F4 | identity · stats · assets · runtime · slots | bag · mesh_ids · stats · export |
| buildable | F2 | identity · **purpose** · **script** · assets · placement · runtime | place · snap · interact · use · save instance · export |
| harvestable | F2 | + drops | spawn · E harvest · loot · export |
| vehicle | F2 | + seats | spawn · mount · drive · export |
| enemy / ally / npc | F3 | identity · kit · anims · ai · combat · assets · runtime | spawn · aggro/follow · skills · loot · export |
| character_kit | F3 | race · mesh_ids · anims | load kit · equip · export |

Contract API: `createDeployableDraft` · `validateDeployableDraft` · `exportDeployableSnapshot`.

---

## Create + save flow

```
Admin F2/F3/F4 → Create draft
localStorage grudge.casting.deployableDrafts.v1
Export JSON (deployable-*.json)
ObjectStore pipeline / R2 CDN (real ITEM- / PREFAB- / NPC- uuids)
Fleet clients consume catalog
```

| Store | What |
|-------|------|
| **localStorage drafts** | Lab authoring only |
| **Download JSON** | Handoff pack + validation |
| **ObjectStore** | Catalog mint (`build:weapon-pipeline`, `generate:master`) |
| **Railway** | Player instances (bag, equip) — never drafts |
| **R2 CDN** | GLB / icons binaries only |

---

## Buildable purpose + script

Purpose tags (`BUILDABLE_PURPOSES`): structure · camp · crafting · storage · defense · decoration · resource_node · spawn_pad · transport.

Script stub (not eval’d):

```js
{
id, type, purpose,
onPlace, onInteract, onDestroy, tick?, stats?
}
```

Interact routes by purpose (craft UI · depot · harvest swing · inspect).

---

## Creatures (F3)

Each entry ties:

- **Model** — grudge6 race GLB (`…/toon-rts-characters/glb/characters/{short}.glb`)
- **Anims** — pack + roles (idle/walk/attack/death…)
- **AI brain** — passive · guard · patrol · hunter · boss_phase · ally_follow · vendor_idle
- **Role** — enemy · monster · boss · ally · commander · npc · vendor · training_dummy

---

## Prefabs (F4)

- Load live T0 / equippable catalog
- One-click **scaffold pack** (existing `buildItemScaffoldPack`)
- New weapon/armour drafts
- Link to Inventory **Prefabs** tab for full browser equip

---

## Agent rules

1. Extend **this** hub + `deployableContract` — do not fork `AdminPanel2` / parallel F-keys.
2. Never mint GRUDGE UUIDs in the lab.
3. Buildables must declare **purpose + script**, not mesh-only.
4. Enemies use grudge6 kits + SI heights (human ~1.8 m).
5. Saves are local drafts until ObjectStore register.
6. Help stays on **?** — F1 is Player.

---

## Files

| File | Role |
|------|------|
| `src/ui/AdminHub.js` | Panel UI |
| `src/ui/adminHub.css` | Styles + top chips |
| `src/api/deployableContract.js` | Kinds · layers · create · validate · export |
| `src/api/prefabDraftStore.js` | localStorage CRUD · import/export |
| `src/api/prefabScaffold.js` | Weapon scaffold (F4) |
| `src/input/InputManager.js` | F1–F4 · ] World · ` auto · Esc |
| `src/core/App.js` | Wire AdminHub |
Loading