diff --git a/.codex/config.toml b/.codex/config.toml new file mode 100644 index 000000000..92f3a4317 --- /dev/null +++ b/.codex/config.toml @@ -0,0 +1,9 @@ +[shell_environment_policy] +inherit = "core" + +[shell_environment_policy.set] +GIT_TERMINAL_PROMPT = "0" +GIT_EDITOR = "true" +GIT_PAGER = "cat" +PAGER = "cat" +GIT_SSH_COMMAND = "ssh -oBatchMode=yes" diff --git a/.codex/hooks.json b/.codex/hooks.json new file mode 100644 index 000000000..6b4430313 --- /dev/null +++ b/.codex/hooks.json @@ -0,0 +1,43 @@ +{ + "hooks": { + "PostToolUse": [ + { + "matcher": "Edit", + "hooks": [ + { + "type": "command", + "command": "bun 'C:\\Users\\Jk101\\Desktop\\jgengine\\.codex\\hooks\\repetitive-edit-watch.mjs'" + } + ] + } + ], + "SessionStart": [ + { + "hooks": [ + { + "type": "command", + "command": "bun 'C:\\Users\\Jk101\\Desktop\\jgengine\\.codex\\hooks\\session-start.mjs'" + } + ] + } + ], + "Stop": [ + { + "hooks": [ + { + "type": "command", + "command": "bun 'C:\\Users\\Jk101\\Desktop\\jgengine\\.codex\\hooks\\warn-unpushed.mjs'" + } + ] + }, + { + "hooks": [ + { + "type": "command", + "command": "bun 'C:\\Users\\Jk101\\Desktop\\jgengine\\.codex\\hooks\\papercut-reminder.mjs'" + } + ] + } + ] + } +} diff --git a/.codex/hooks/papercut-reminder.mjs b/.codex/hooks/papercut-reminder.mjs new file mode 100644 index 000000000..7ac573f1f --- /dev/null +++ b/.codex/hooks/papercut-reminder.mjs @@ -0,0 +1,152 @@ +import { readFileSync, existsSync } from "node:fs"; +import { homedir } from "node:os"; +import path from "node:path"; + +let input = {}; +try { + input = JSON.parse(readFileSync(0, "utf8")); +} catch { + /* no stdin — treat as empty */ +} +if (input.stop_hook_active) process.exit(0); + +function resolveTranscriptPath() { + if (typeof input.transcript_path === "string" && existsSync(input.transcript_path)) { + return input.transcript_path; + } + if (typeof input.session_id !== "string") return null; + const cwd = typeof input.cwd === "string" ? input.cwd : process.cwd(); + const sanitized = cwd.replace(/[/\\]/g, "-"); + const guess = path.join(homedir(), ".claude", "projects", sanitized, `${input.session_id}.jsonl`); + return existsSync(guess) ? guess : null; +} + +const transcriptPath = resolveTranscriptPath(); +if (transcriptPath === null) process.exit(0); + +let entries; +try { + entries = readFileSync(transcriptPath, "utf8") + .split("\n") + .filter(Boolean) + .map((line) => { + try { + return JSON.parse(line); + } catch { + return null; + } + }) + .filter((entry) => entry !== null); +} catch { + process.exit(0); +} + +function messageContent(entry) { + const content = entry?.message?.content; + return Array.isArray(content) ? content : []; +} + +// Already logged a papercut this session — nothing to remind about. +const alreadyLogged = entries.some((entry) => + messageContent(entry).some( + (block) => + block.type === "tool_use" && + block.name === "Bash" && + typeof block.input?.command === "string" && + /\bpapercut\b/i.test(block.input.command), + ), +); +if (alreadyLogged) process.exit(0); + +const toolUses = []; +const resultById = new Map(); +for (const entry of entries) { + for (const block of messageContent(entry)) { + if (block.type === "tool_use") toolUses.push(block); + if (block.type === "tool_result") resultById.set(block.tool_use_id, block); + } +} + +function isError(toolUse) { + return resultById.get(toolUse.id)?.is_error === true; +} + +// Signal A: the exact same non-trivial Bash command was run twice in a row +// with no Edit/Write/NotebookEdit between the two runs, and at least one run +// failed — a genuine dead-end retry. Requiring "no edit in between" is what +// excludes a normal fix-test-fix loop (there, the same test command reruns +// on purpose after code changed, and that's fine). +const EDIT_TOOLS = new Set(["Edit", "Write", "NotebookEdit"]); +const commandPositions = new Map(); +toolUses.forEach((toolUse, index) => { + if (toolUse.name !== "Bash") return; + const command = (toolUse.input?.command ?? "").trim(); + if (command.length < 8) return; + const positions = commandPositions.get(command) ?? []; + positions.push(index); + commandPositions.set(command, positions); +}); + +let retriedFailedCommand = null; +outerRetry: for (const [command, positions] of commandPositions) { + for (let k = 0; k < positions.length - 1; k += 1) { + const [from, to] = [positions[k], positions[k + 1]]; + const editedBetween = toolUses.slice(from + 1, to).some((toolUse) => EDIT_TOOLS.has(toolUse.name)); + if (editedBetween) continue; + if (isError(toolUses[from]) || isError(toolUses[to])) { + retriedFailedCommand = command; + break outerRetry; + } + } +} + +// Signal B: a later Agent/Task call whose prompt heavily overlaps an earlier +// one's and explicitly corrects it (e.g. "run synchronously", "don't +// background") — the shape of a relaunch after an unusable first result. +const RELAUNCH_HINTS = + /(instead of backgrounding|re-?launch|try (that |it )?again|unusable (first )?result|(last|previous) (attempt|run|worker) (failed|returned nothing|stalled))/i; + +function wordSet(text) { + return new Set((text ?? "").toLowerCase().match(/[a-z]{4,}/g) ?? []); +} +function overlapRatio(a, b) { + if (a.size === 0 || b.size === 0) return 0; + let shared = 0; + for (const word of a) if (b.has(word)) shared += 1; + return shared / Math.min(a.size, b.size); +} + +const agentCalls = toolUses.filter((toolUse) => toolUse.name === "Agent" || toolUse.name === "Task"); +let relaunchedAgent = null; +outer: for (let i = 0; i < agentCalls.length; i += 1) { + for (let j = i + 1; j < agentCalls.length; j += 1) { + const promptA = agentCalls[i].input?.prompt ?? ""; + const promptB = agentCalls[j].input?.prompt ?? ""; + if ( + overlapRatio(wordSet(promptA), wordSet(promptB)) > 0.5 && + RELAUNCH_HINTS.test(promptB) && + !RELAUNCH_HINTS.test(promptA) + ) { + relaunchedAgent = agentCalls[j].input?.description ?? "a subagent"; + break outer; + } + } +} + +if (retriedFailedCommand === null && relaunchedAgent === null) process.exit(0); + +const bits = []; +if (retriedFailedCommand !== null) { + bits.push(`a Bash command was retried after failing: \`${retriedFailedCommand.slice(0, 120)}\``); +} +if (relaunchedAgent !== null) { + bits.push(`a subagent ("${relaunchedAgent}") looks like it was relaunched after an unusable first result`); +} + +const reason = + `This session hit friction worth logging as a papercut: ${bits.join("; ")}.\n\n` + + `Per CLAUDE.md, log it now before stopping (proactively, no need to ask first):\n` + + ` bun run papercut -m "what you were doing → what got in the way"`; + +process.stdout.write(JSON.stringify({ decision: "block", reason })); +process.exit(0); diff --git a/.codex/hooks/repetitive-edit-watch.mjs b/.codex/hooks/repetitive-edit-watch.mjs new file mode 100644 index 000000000..b0084b831 --- /dev/null +++ b/.codex/hooks/repetitive-edit-watch.mjs @@ -0,0 +1,28 @@ +import { appendFileSync, readFileSync } from "node:fs"; +import { tmpdir } from "node:os"; +import { join } from "node:path"; + +const input = JSON.parse(readFileSync(0, "utf8")); +const { session_id, tool_input } = input; +if (!tool_input?.old_string || !tool_input?.new_string) process.exit(0); + +const key = JSON.stringify([ + tool_input.old_string.slice(0, 200), + tool_input.new_string.slice(0, 200), +]); +const logPath = join(tmpdir(), `jg-edit-watch-${session_id}.jsonl`); +appendFileSync(logPath, JSON.stringify({ key, file: tool_input.file_path }) + "\n"); + +const lines = readFileSync(logPath, "utf8").trim().split("\n").map((l) => JSON.parse(l)); +const files = new Set(lines.filter((l) => l.key === key).map((l) => l.file)); + +if (files.size >= 3) { + console.log( + JSON.stringify({ + hookSpecificOutput: { + hookEventName: "PostToolUse", + additionalContext: `You have now applied the same edit manually in ${files.size} files. Stop hand-editing: build one regex find-and-replace instead — dry-run with rg '', then rg -l '' | xargs sed -i -E 's///g' (or perl -pi -e for multiline), then git diff --stat to confirm scope. See CLAUDE.md → Style.`, + }, + }), + ); +} diff --git a/.codex/hooks/session-start.mjs b/.codex/hooks/session-start.mjs new file mode 100644 index 000000000..7e3b6e57d --- /dev/null +++ b/.codex/hooks/session-start.mjs @@ -0,0 +1,146 @@ +import { execFileSync } from "node:child_process"; + +const git = (...args) => { + try { + return execFileSync("git", args, { stdio: ["ignore", "pipe", "ignore"], timeout: 30000 }) + .toString() + .trim(); + } catch { + return null; + } +}; + +const gitIn = (input, ...args) => { + try { + return execFileSync("git", args, { input, stdio: ["pipe", "pipe", "ignore"], timeout: 30000 }) + .toString() + .trim(); + } catch { + return null; + } +}; + +const patchIds = (diffText) => { + if (!diffText) return []; + const out = gitIn(diffText, "patch-id", "--stable"); + if (!out) return []; + return out + .split("\n") + .map((line) => line.trim().split(/\s+/)[0]) + .filter(Boolean); +}; + +const contentAlreadyUpstream = (remoteRef, branch) => { + const base = git("merge-base", remoteRef, branch); + if (!base) return false; + const upstream = new Set(patchIds(git("log", "-p", "--no-color", `${base}..${remoteRef}`))); + if (upstream.size === 0) return false; + const combined = patchIds(git("diff", "--no-color", base, branch))[0]; + if (combined && upstream.has(combined)) return true; + const local = patchIds(git("log", "-p", "--no-color", `${remoteRef}..${branch}`)); + return local.length > 0 && local.every((id) => upstream.has(id)); +}; + +const emit = (context) => { + process.stdout.write( + JSON.stringify({ + hookSpecificOutput: { hookEventName: "SessionStart", additionalContext: context }, + }), + ); + process.exit(0); +}; + +const gitDir = git("rev-parse", "--absolute-git-dir"); +if (!gitDir) process.exit(0); + +const unshallow = () => { + if (git("rev-parse", "--is-shallow-repository") !== "true") return ""; + try { + execFileSync("git", ["fetch", "--unshallow", "--quiet", "origin"], { + stdio: "ignore", + timeout: 180000, + }); + return ""; + } catch { + return ( + `\n\n⚠️ This clone is SHALLOW and un-shallowing failed (network/timeout). History ` + + `comparisons against origin are unreliable — run git fetch --unshallow origin before ` + + `diagnosing any branch state.` + ); + } +}; +const shallowNote = unshallow(); + +git("fetch", "origin", "--prune", "--quiet"); + +const branch = git("rev-parse", "--abbrev-ref", "HEAD") ?? "?"; +const defaultBranch = + (git("symbolic-ref", "--quiet", "refs/remotes/origin/HEAD") ?? "").split("/").pop() || "main"; +const remoteMain = `origin/${defaultBranch}`; +const dirtyTracked = git("status", "--porcelain", "--untracked-files=no"); +const notes = []; + +if (branch === defaultBranch) { + if (!dirtyTracked && git("merge", "--ff-only", remoteMain) !== null) { + notes.push(`Fast-forwarded ${defaultBranch} to ${remoteMain}.`); + } +} else if (branch !== "HEAD") { + const remoteBranch = `origin/${branch}`; + const hasRemoteBranch = git("rev-parse", "--verify", "--quiet", remoteBranch) !== null; + + if (hasRemoteBranch && !dirtyTracked) { + const counts = git("rev-list", "--left-right", "--count", `${branch}...${remoteBranch}`); + const [ahead, behind] = (counts ?? "0\t0").split(/\s+/).map(Number); + if (behind > 0 && ahead === 0 && git("merge", "--ff-only", remoteBranch) !== null) { + notes.push(`Fast-forwarded ${branch} to ${remoteBranch}.`); + } + } + + const unpushed = Number(git("rev-list", "--count", "HEAD", "--not", "--remotes")) || 0; + const headSha = git("rev-parse", "HEAD"); + const mainSha = git("rev-parse", remoteMain); + const behindMain = + headSha !== mainSha && git("merge-base", "--is-ancestor", "HEAD", remoteMain) === ""; + if (!dirtyTracked && behindMain) { + if (git("reset", "--hard", remoteMain) !== null) { + notes.push( + `Self-healed: "${branch}" pointed at history already contained in ${remoteMain} — ` + + `restarted it from ${remoteMain}. Start committing on top of this fresh base.`, + ); + } + } else if (!dirtyTracked && contentAlreadyUpstream(remoteMain, branch)) { + if (git("reset", "--hard", remoteMain) !== null) { + notes.push( + `Self-healed: every commit on "${branch}" was already squash-merged into ${remoteMain} ` + + `(patch-id match). Restarted the branch from ${remoteMain} so new work does not stack ` + + `on merged history — this is what used to cause the every-session merge conflicts. ` + + `The old tip is recoverable via reflog. When you push, use --force-with-lease if the ` + + `remote branch still carries the old history.`, + ); + } + } else if (unpushed > 0) { + notes.push( + `⚠️ "${branch}" carries ${unpushed} commit(s) not on any remote and not yet merged. ` + + `This container is ephemeral — push early (git push -u origin ${branch}).`, + ); + } +} + +emit( + [ + `Cloud session on branch "${branch}" (default: ${defaultBranch}).`, + `Flow: commit here, push with git push -u origin ${branch}, open a PR via the GitHub MCP ` + + `tools (ready for review), subscribe_pr_activity on it, report the PR link, and END ` + + `THE TURN. Never wait or poll on CI — the subscription delivers failures as events, ` + + `silence is green (PRs run only the ~30s quick job). NEVER merge — no ` + + `merge_pull_request, no enable_pr_auto_merge. The user merges PRs themselves by asking ` + + `in chat; the PR sits parked until then, and a user-requested merge ends at "merged" — ` + + `no post-merge babysitting of ${defaultBranch}. A CI failure event → fix on the same ` + + `branch, push, end turn. ` + + `New task in the same session → fresh claude/... branch off origin/${defaultBranch}, ` + + `separate PR; never stack it on a parked branch. Never arm send_later check-ins or ` + + `scheduled remote sessions for CI. No worktrees — every session is its own isolated ` + + `cloud container.`, + ...notes, + ].join("\n\n") + shallowNote, +); diff --git a/.codex/hooks/warn-unpushed.mjs b/.codex/hooks/warn-unpushed.mjs new file mode 100644 index 000000000..5f91afc08 --- /dev/null +++ b/.codex/hooks/warn-unpushed.mjs @@ -0,0 +1,85 @@ +import { readFileSync, writeFileSync, statSync } from "node:fs"; +import path from "node:path"; +import { execFileSync } from "node:child_process"; + +const EDIT_QUIET_MS = 5 * 60 * 1000; +const WARN_COOLDOWN_MS = 30 * 60 * 1000; + +const gitRaw = (...args) => { + try { + return execFileSync("git", args, { stdio: ["ignore", "pipe", "ignore"], timeout: 15000 }).toString(); + } catch { + return null; + } +}; + +const git = (...args) => gitRaw(...args)?.trim() ?? null; + +let input = {}; +try { + input = JSON.parse(readFileSync(0, "utf8")); +} catch { + /* no stdin — treat as empty */ +} +if (input.stop_hook_active) process.exit(0); + +const branch = git("rev-parse", "--abbrev-ref", "HEAD"); +if (!branch || branch === "HEAD") process.exit(0); + +const count = Number(git("rev-list", "--count", "HEAD", "--not", "--remotes")); +if (!Number.isFinite(count) || count <= 0) process.exit(0); + +const defaultBranch = + (git("symbolic-ref", "--quiet", "refs/remotes/origin/HEAD") ?? "").split("/").pop() || "main"; +const remoteMain = `origin/${defaultBranch}`; +if (git("merge-base", "--is-ancestor", "HEAD", remoteMain) !== null) process.exit(0); +if (git("diff", "--quiet", `${remoteMain}...HEAD`) !== null) process.exit(0); + +const now = Date.now(); +const repoRoot = git("rev-parse", "--show-toplevel"); +const statusRecords = (gitRaw("status", "--porcelain", "-z") ?? "").split("\0").filter(Boolean); +const dirtyPaths = []; +for (let i = 0; i < statusRecords.length; i += 1) { + const record = statusRecords[i]; + dirtyPaths.push(record.slice(3)); + if (record[0] === "R" || record[0] === "C") i += 1; +} +const editsStillInFlight = + repoRoot !== null && + dirtyPaths.some((relPath) => { + try { + return now - statSync(path.join(repoRoot, relPath)).mtimeMs < EDIT_QUIET_MS; + } catch { + return false; + } + }); +if (editsStillInFlight) process.exit(0); + +const gitDir = git("rev-parse", "--absolute-git-dir"); +const lastWarnFile = gitDir === null ? null : path.join(gitDir, "warn-unpushed-last.json"); +if (lastWarnFile !== null) { + try { + const lastWarn = JSON.parse(readFileSync(lastWarnFile, "utf8")); + if (lastWarn.branch === branch && now - lastWarn.at < WARN_COOLDOWN_MS) process.exit(0); + } catch { + /* no prior warning recorded */ + } +} + +const reason = + `⚠️ ${count} commit${count === 1 ? "" : "s"} on branch "${branch}" ` + + `are not on any remote. This is an ephemeral cloud container — if it is reclaimed, that work is lost.\n\n` + + `Push before ending: git push -u origin ${branch}\n` + + `If that branch name is already taken on the remote with different history, back it up instead: ` + + `git push origin ${branch}:backup/${branch}`; + +if (lastWarnFile !== null) { + try { + writeFileSync(lastWarnFile, JSON.stringify({ branch, at: now })); + } catch { + /* best effort */ + } +} + +process.stdout.write(JSON.stringify({ decision: "block", reason })); +process.exit(0); diff --git a/.gitignore b/.gitignore index 17c066ba0..c19e2dbb7 100644 --- a/.gitignore +++ b/.gitignore @@ -6,17 +6,18 @@ shots/ .turbo/ **/public/models/* !apps/dev/public/models/claudecraft/ +!apps/dev/public/models/kaykit-adventurers/ !apps/dev/public/models/kaykit-city-builder/ +!apps/dev/public/models/kaykit-dungeon/ !apps/dev/public/models/quaternius-stylized-nature/ -!apps/dev/public/models/kaykit-adventurers/ **/.jgengine-assets-tmp/ *.tsbuildinfo # CHANGELOG.md is authored once at the repo root and copied into each package -# at publish time (see publish.yml) — the per-package copies are build artifacts. +# at publish time (see publish.yml) +GGǥ the per-package copies are build artifacts. packages/*/CHANGELOG.md -# Compiled output must never sit beside TS source — Vite resolves .js over .ts, +# Compiled output must never sit beside TS source +GGǥ Vite resolves .js over .ts, # so a stray src/*.js silently shadows its .ts sibling and breaks dev/shoot. # Source dirs are pure TypeScript; emit only ever goes to dist/. packages/*/src/**/*.js @@ -26,11 +27,11 @@ packages/*/src/**/*.js.map packages/*/src/**/*.d.ts.map # Packaged skills and the API-surface JSON are staged from .claude/skills and -# packages/*/src by scripts/stage-skills.ts and scripts/gen-api-docs.ts — +# packages/*/src by scripts/stage-skills.ts and scripts/gen-api-docs.ts +GGǥ # never author or commit these by hand. packages/*/skills/ apps/web/src/generated/ # Ephemeral git worktrees created by fan-out shipping subagents -# (Agent({ isolation: "worktree" })) — transient, never committed. +# (Agent({ isolation: "worktree" })) +GGǥ transient, never committed. .claude/worktrees/ diff --git a/Games/duet-keys/src/game.config.ts b/Games/duet-keys/src/game.config.ts index 18fd5ff21..4127a6d9e 100644 --- a/Games/duet-keys/src/game.config.ts +++ b/Games/duet-keys/src/game.config.ts @@ -5,7 +5,7 @@ import { assets } from "./game/assets"; import { content } from "./game/content"; import { keybinds } from "./game/keybinds"; import { objectStyles } from "./game/objects/catalog"; -import { DuetEnvironment, DuetVfx, objectModels, renderDuetObject, renderHero } from "./game/render"; +import { DuetEnvironment, DuetVfx, renderDuetObject, renderHero } from "./game/render"; import { ROOMS } from "./game/rooms/catalog"; import { isWalkable } from "./game/rooms/engine"; import { currentRoomState } from "./game/rooms/setup"; @@ -37,7 +37,6 @@ export const game = defineGame({ WorldOverlay: DuetVfx, renderEntity: renderHero, renderObject: renderDuetObject, - objectModels, objectStyles, shadows: true, camera: { diff --git a/Games/duet-keys/src/game/assets.ts b/Games/duet-keys/src/game/assets.ts index e2e25379b..5cff2ff7b 100644 --- a/Games/duet-keys/src/game/assets.ts +++ b/Games/duet-keys/src/game/assets.ts @@ -1,31 +1,26 @@ import { buildCatalog } from "@jgengine/assets/catalogs/build"; import type { AssetCatalog } from "@jgengine/core/scene/assetCatalog"; -/** KayKit dungeon + adventurers — never Kenney. */ export const assets: AssetCatalog = buildCatalog({ basePath: "/models", sources: ["kaykit-dungeon", "kaykit-adventurers"], }); -const DUN = "kaykit-dungeon"; -const CHAR = "kaykit-adventurers"; - export const DUNGEON = { - wall: `${DUN}/wall`, - floor: `${DUN}/floor_tile_large`, - floorDetail: `${DUN}/floor_tile_small_decorated`, - dirt: `${DUN}/floor_dirt_large`, - gate: `${DUN}/wall_gated`, - trap: `${DUN}/floor_tile_big_spikes`, - column: `${DUN}/pillar`, - stairs: `${DUN}/stairs`, - rocks: `${DUN}/barrel_large`, - stones: `${DUN}/box_large`, - coin: `${DUN}/coin`, - humanHero: `${CHAR}/Mage`, - orcHero: `${CHAR}/Barbarian`, - chest: `${DUN}/chest`, - torch: `${DUN}/torch_lit`, + wall: "kaykit-dungeon/pillar", + floor: "kaykit-dungeon/floor_tile_small", + floorDetail: "kaykit-dungeon/floor_tile_small_broken_A", + dirt: "kaykit-dungeon/floor_tile_small_broken_B", + plate: "kaykit-dungeon/floor_tile_small_decorated", + gate: "kaykit-dungeon/barrier_half", + trap: "kaykit-dungeon/floor_tile_big_spikes", + column: "kaykit-dungeon/torch_mounted", + stairs: "kaykit-dungeon/stairs", + torch: "kaykit-dungeon/torch", + trunk: "kaykit-dungeon/trunk_small_A", + coin: "kaykit-dungeon/coin", + humanHero: "kaykit-adventurers/Mage", + orcHero: "kaykit-adventurers/Barbarian", } as const; export const FLOOR_VARIANTS = [DUNGEON.floor, DUNGEON.floorDetail, DUNGEON.dirt] as const; diff --git a/Games/duet-keys/src/game/objects/catalog.ts b/Games/duet-keys/src/game/objects/catalog.ts index bf2a28a02..fb4e7890e 100644 --- a/Games/duet-keys/src/game/objects/catalog.ts +++ b/Games/duet-keys/src/game/objects/catalog.ts @@ -11,7 +11,8 @@ export const OBJECT_IDS = [ export type ObjectId = (typeof OBJECT_IDS)[number]; -/** Fallback tint per catalog id if a GLB fails; real look comes from KayKit models. */ +/** Fallback tint per catalog id — read by `objectStyles` as a safety net if a GLB ever fails to load; the + * real look comes from the dungeon-kit models wired in `render.tsx` via `objectModels`/`renderObject`. */ export const objectStyles: Record = { wall: { color: "#2b3350" }, plate: { color: "#5a6685" }, diff --git a/Games/duet-keys/src/game/render.tsx b/Games/duet-keys/src/game/render.tsx index a13e99e8a..11ccd6466 100644 --- a/Games/duet-keys/src/game/render.tsx +++ b/Games/duet-keys/src/game/render.tsx @@ -25,62 +25,85 @@ const VOID_BASE = "#0b0f1e"; const VOID_HIGH = "#141a30"; const ROOM_BASE = "#232a44"; +/** + * A dedicated `LoadingManager`, not the implicit `THREE.DefaultLoadingManager` a bare + * `useLoader(GLTFLoader, url, ...)` constructs — matches the fix in `packages/shell/GamePlayerShell.tsx`. + * The shared default manager is process-wide singleton state — under this dev server's repeated + * navigations it can end up with an already-aborted internal `AbortController`, which + * `FileLoader.load()` composes into every future request's abort signal, silently stalling + * `GLTFLoader.load()` forever with no thrown error (parsing already-fetched bytes still works fine + * — it's a fetch-stage-only hang). A private manager sidesteps the shared state. + */ const gltfLoader = new GLTFLoader(new THREE.LoadingManager()); gltfLoader.setMeshoptDecoder(MeshoptDecoder); -function resolveModel(id: string): ModelAssetRef | null { - return assets.resolve(id); -} - -function mustResolve(id: string): ModelAssetRef { - const entry = resolveModel(id); +function resolveModel(id: string): ModelAssetRef { + const entry = assets.resolve(id); if (entry === null) throw new Error(`duet-keys: unknown model id "${id}"`); return entry; } -function tinted(id: string, material: ModelMaterialOverride): ModelConfig | null { - const entry = resolveModel(id); - if (entry === null) return null; - return { url: entry.url, dims: entry.dims, material }; -} - +/** kaykit-dungeon ships on its own ~4-unit tile grid; these scales bring each piece down to this + * game's 1-unit grid (a pillar's 1.5-unit footprint * 0.6 ≈ 0.9, comfortably inside one cell). */ +const WALL_SCALE = 0.6; +const FLOOR_SCALE = 0.5; +const GATE_SCALE = 0.55; +const SPIKE_SCALE = 0.25; +const RECEIVER_SCALE = 1.05; +const EXIT_SCALE = 0.2; +const EMITTER_SCALE = 1.05; +const HERO_SCALE = 0.4; +const PRISM_SCALE = 4.4; +const WEIGHT_SCALE = 1.6; + +/** Warm every dungeon-kit id into `useLoader`'s cache before the Canvas's render loop starts + * competing for the main thread — `useLoader` inside an active `useFrame` loop can starve the + * fetch/parse continuation for a very long time on a busy machine; preloading while the page is + * still quiet avoids that entirely. */ for (const id of Object.values(DUNGEON)) { - const entry = resolveModel(id); - if (entry !== null) useLoader.preload(gltfLoader, entry.url); + useLoader.preload(gltfLoader, resolveModel(id).url); } -/** Wall/emitter/exit — static objectModels when catalog is live. */ -export const objectModels: Record = {}; -{ - const wall = resolveModel(DUNGEON.wall); - if (wall !== null) objectModels.wall = DUNGEON.wall; - const emitter = tinted(DUNGEON.rocks, { - color: objectStyles.emitter.color, - emissive: "#bafcff", - emissiveIntensity: 0.55, - }); - if (emitter !== null) objectModels.emitter = emitter; - const exitL = tinted(DUNGEON.stairs, { - color: HEROES.lumen.color, - emissive: HEROES.lumen.glow, - emissiveIntensity: 0.45, - }); - if (exitL !== null) objectModels.exit_lumen = exitL; - const exitA = tinted(DUNGEON.stairs, { - color: HEROES.anchor.color, - emissive: HEROES.anchor.glow, - emissiveIntensity: 0.45, - }); - if (exitA !== null) objectModels.exit_anchor = exitA; -} - -const TINTED_OBJECT_MODELS: Partial> = { +/** + * Every placed object routes through this one preloaded `gltfLoader` via `renderObject` — including + * wall/emitter/exit, which have no per-instance tint and would otherwise be the obvious fit for the + * engine's static `objectModels` map. `objectModels` resolves through the shell's own separate + * `sharedGltfLoader` instance (`packages/shell/GamePlayerShell.tsx`), which this game's module-level + * `useLoader.preload` never warms — that split cache reintroduces the exact main-thread-starvation + * hang the preload exists to avoid. Keeping every model on this game's one loader keeps the preload + * meaningful for all of them. + */ +const OBJECT_MODEL_IDS: Record = { + wall: DUNGEON.wall, + emitter: DUNGEON.torch, + exit_lumen: DUNGEON.stairs, + exit_anchor: DUNGEON.stairs, gate: DUNGEON.gate, - plate: DUNGEON.floorDetail, + plate: DUNGEON.plate, receiver: DUNGEON.column, spike: DUNGEON.trap, }; +const OBJECT_SCALE: Record = { + wall: WALL_SCALE, + emitter: EMITTER_SCALE, + exit_lumen: EXIT_SCALE, + exit_anchor: EXIT_SCALE, + gate: GATE_SCALE, + plate: FLOOR_SCALE, + receiver: RECEIVER_SCALE, + spike: SPIKE_SCALE, +}; + +/** Objects with no dynamic `object.visual` (wall) or whose tint never changes at runtime + * (emitter/exit) get their look fixed here; gate/plate/receiver/spike fall through to + * `object.visual.color`, set live by `applyRoomVisuals`. */ +const OBJECT_STATIC_MATERIAL: Partial> = { + emitter: { color: objectStyles.emitter.color, emissive: "#bafcff", emissiveIntensity: 0.55 }, + exit_lumen: { color: HEROES.lumen.color, emissive: HEROES.lumen.glow, emissiveIntensity: 0.45 }, + exit_anchor: { color: HEROES.anchor.color, emissive: HEROES.anchor.glow, emissiveIntensity: 0.45 }, +}; + class ModelErrorBoundary extends Component<{ children: ReactNode }, { failed: boolean }> { override state = { failed: false }; static getDerivedStateFromError(): { failed: boolean } { @@ -106,25 +129,29 @@ function centeredPosition(entry: ModelAssetRef, baseY = 0, scale = 1): [number, } function useDungeonScene(id: string, material: ModelMaterialOverride | undefined, poseClip?: string) { - const entry = mustResolve(id); + const entry = resolveModel(id); const gltf = useLoader(gltfLoader, entry.url); const materialKey = material === undefined ? "" : `${material.color ?? ""}|${material.emissive ?? ""}|${material.emissiveIntensity ?? ""}`; - const scene = useMemo(() => { - const cloned = cloneModelScene(gltf.scene); - if (material !== undefined) applyMaterialOverride(cloned, material, { clone: false }); - if (poseClip !== undefined) { - const clip = THREE.AnimationClip.findByName(gltf.animations, poseClip); - if (clip !== null) { - const mixer = new THREE.AnimationMixer(cloned); - mixer.clipAction(clip).play(); - mixer.update(0); + const scene = useMemo( + () => { + const cloned = cloneModelScene(gltf.scene); + if (material !== undefined) applyMaterialOverride(cloned, material, { clone: false }); + if (poseClip !== undefined) { + const clip = THREE.AnimationClip.findByName(gltf.animations, poseClip); + if (clip !== null) { + const mixer = new THREE.AnimationMixer(cloned); + mixer.clipAction(clip).play(); + mixer.update(0); + } } - } - return cloned; - }, [gltf, materialKey, poseClip]); + return cloned; + }, + // eslint-disable-next-line react-hooks/exhaustive-deps + [gltf, materialKey, poseClip], + ); useEffect(() => () => disposeClonedMaterials(scene), [scene]); return { scene, entry }; } @@ -142,68 +169,34 @@ function setOpacity(root: THREE.Object3D, opacity: number): void { }); } -function HeroRing({ glow, isActive }: { glow: string; isActive: boolean }) { - return ( - - - - - ); -} - -function HeroPrimitive({ hero, isActive }: { hero: (typeof HEROES)[keyof typeof HEROES]; isActive: boolean }) { - return ( - - - - - - - - ); -} - -function HeroGlb({ - modelId, - hero, - isActive, -}: { - modelId: string; - hero: (typeof HEROES)[keyof typeof HEROES]; - isActive: boolean; -}) { +/** The two heroes, rendered as real kaykit-adventurers characters — cyan-tinted Mage (Lumen) and + * orange-tinted Barbarian (Anchor) — with a floor ring marking which one the player is driving. */ +function HeroMesh({ entity }: { entity: SceneEntity }) { + const active = useStore(duetStore, (state) => state.active); + const hero = entity.name === "anchor" ? HEROES.anchor : HEROES.lumen; + const isActive = active === hero.id; + const modelId = hero.id === "anchor" ? DUNGEON.orcHero : DUNGEON.humanHero; const { scene, entry } = useDungeonScene( modelId, { color: hero.color, emissive: hero.glow, - emissiveIntensity: isActive ? 0.85 : 0.3, + emissiveIntensity: isActive ? 0.35 : 0.12, }, "Idle", ); - const heroScale = 1.5; - const position = centeredPosition(entry, 0, heroScale); + const position = centeredPosition(entry, 0, HERO_SCALE); return ( - - + + + + + ); } -function HeroMesh({ entity }: { entity: SceneEntity }) { - const active = useStore(duetStore, (state) => state.active); - const hero = entity.name === "anchor" ? HEROES.anchor : HEROES.lumen; - const isActive = active === hero.id; - const modelId = hero.id === "anchor" ? DUNGEON.orcHero : DUNGEON.humanHero; - if (resolveModel(modelId) === null) return ; - return ; -} - export function renderHero(entity: SceneEntity): ReactNode { if (entity.name !== "lumen" && entity.name !== "anchor") return null; return ( @@ -213,22 +206,27 @@ export function renderHero(entity: SceneEntity): ReactNode { ); } +/** Every placed object — wall/emitter/exit statically tinted, gate/plate/receiver/spike live-tinted + * (and, via the object's own position — sunk/raised by `applyRoomVisuals`) from real dungeon-kit + * models on this game's single preloaded loader. */ function DungeonObjectMesh({ object }: { object: SceneObject }) { - const modelId = TINTED_OBJECT_MODELS[object.catalogId as ObjectId]!; - if (resolveModel(modelId) === null) return null; - const color = object.visual?.color; + const catalogId = object.catalogId as ObjectId; + const modelId = OBJECT_MODEL_IDS[catalogId]; + const scale = OBJECT_SCALE[catalogId]; + const dynamicColor = object.visual?.color; + const material = + dynamicColor !== undefined + ? { color: dynamicColor, emissive: dynamicColor, emissiveIntensity: 0.5 } + : OBJECT_STATIC_MATERIAL[catalogId]; const opacity = object.visual?.opacity ?? 1; - const { scene, entry } = useDungeonScene( - modelId, - color === undefined ? undefined : { color, emissive: color, emissiveIntensity: 0.35 }, - ); + const { scene, entry } = useDungeonScene(modelId, material); useEffect(() => setOpacity(scene, opacity), [scene, opacity]); - const position = centeredPosition(entry); - return ; + const position = centeredPosition(entry, 0, scale); + return ; } export function renderDuetObject(object: SceneObject): ReactNode { - if (!(object.catalogId in TINTED_OBJECT_MODELS)) return null; + if (!(object.catalogId in OBJECT_MODEL_IDS)) return null; return ( @@ -242,33 +240,29 @@ function hashCell({ x, z }: V2): number { const FLOOR_VARIANT_IDS = [DUNGEON.floor, DUNGEON.floorDetail, DUNGEON.dirt] as const; +/** Tiles the room's walkable footprint with real dungeon-kit floor GLBs (three variants, hashed + * per cell for texture variety) instead of one flat color plane. */ function FloorTiles({ room }: { room: RoomDef }) { - const urls = FLOOR_VARIANT_IDS.map((id) => resolveModel(id)?.url).filter((u): u is string => u !== undefined); - if (urls.length === 0) { - return ( - - - - - ); - } - const floor = useLoader(gltfLoader, urls[0]!); - const detail = useLoader(gltfLoader, urls[Math.min(1, urls.length - 1)]!); - const dirt = useLoader(gltfLoader, urls[Math.min(2, urls.length - 1)]!); - - const tiles = useMemo(() => { - const variants = [floor, detail, dirt]; - return room.floor.map((cell) => { - const hash = hashCell(cell); - const gltf = variants[hash % variants.length]!; - return { - key: `${cell.x}:${cell.z}`, - cell, - scene: cloneModelScene(gltf.scene), - rotationY: (hash % 4) * (Math.PI / 2), - }; - }); - }, [room, floor, detail, dirt]); + const floor = useLoader(gltfLoader, resolveModel(FLOOR_VARIANT_IDS[0]).url); + const detail = useLoader(gltfLoader, resolveModel(FLOOR_VARIANT_IDS[1]).url); + const dirt = useLoader(gltfLoader, resolveModel(FLOOR_VARIANT_IDS[2]).url); + + const tiles = useMemo( + () => { + const variants = [floor, detail, dirt]; + return room.floor.map((cell) => { + const hash = hashCell(cell); + const gltf = variants[hash % variants.length]!; + return { + key: `${cell.x}:${cell.z}`, + cell, + scene: cloneModelScene(gltf.scene), + rotationY: (hash % 4) * (Math.PI / 2), + }; + }); + }, + [room, floor, detail, dirt], + ); useEffect( () => () => { @@ -285,6 +279,7 @@ function FloorTiles({ room }: { room: RoomDef }) { object={tile.scene} position={[tile.cell.x, 0, tile.cell.z]} rotation-y={tile.rotationY} + scale={FLOOR_SCALE} /> ))} @@ -333,10 +328,10 @@ export function DuetEnvironment() { ); } +/** Dynamic devices the heroes latch: Lumen's spinning prism crystal + light beam, Anchor's dropped + * weight — real dungeon-kit props instead of primitive octahedron/cylinder geometry. */ function PrismProp({ position }: { position: [number, number, number] }) { - const modelId = resolveModel(DUNGEON.coin) !== null ? DUNGEON.coin : DUNGEON.torch; - if (resolveModel(modelId) === null) return null; - const { scene, entry } = useDungeonScene(modelId, { + const { scene, entry } = useDungeonScene(DUNGEON.coin, { color: HEROES.lumen.glow, emissive: HEROES.lumen.color, emissiveIntensity: 1.1, @@ -345,27 +340,24 @@ function PrismProp({ position }: { position: [number, number, number] }) { useFrame((_state, delta) => { if (groupRef.current !== null) groupRef.current.rotation.y += delta * 1.6; }); - const propScale = 2.4; - const local = centeredPosition(entry, 0.55, propScale); + const local = centeredPosition(entry, 0.55, PRISM_SCALE); return ( - + ); } function AnchorWeightProp({ position }: { position: [number, number, number] }) { - if (resolveModel(DUNGEON.stones) === null) return null; - const { scene, entry } = useDungeonScene(DUNGEON.stones, { + const { scene, entry } = useDungeonScene(DUNGEON.trunk, { color: "#2a2f3d", metalness: 0.5, roughness: 0.5, }); - const weightScale = 1.35; - const local = centeredPosition(entry, 0, weightScale); + const local = centeredPosition(entry, 0, WEIGHT_SCALE); return ( - + ); } diff --git a/Games/duet-keys/src/game/rooms/setup.ts b/Games/duet-keys/src/game/rooms/setup.ts index 22ce39986..11c10709b 100644 --- a/Games/duet-keys/src/game/rooms/setup.ts +++ b/Games/duet-keys/src/game/rooms/setup.ts @@ -1,4 +1,4 @@ -import type { GameContext } from "@jgengine/core/runtime/gameContext"; +import type { GameContext } from "@jgengine/core/runtime/gameContext"; import type { ObjectVisual } from "@jgengine/core/scene/objectStore"; import { HEROES } from "../entities/players/catalog"; @@ -67,7 +67,7 @@ const PLATE_PRESSED_Y = -0.05; const SPIKE_UP_Y = 0; const SPIKE_RETRACTED_Y = -0.55; -/** Repaint stateful objects to match the derived room signals ΓÇö a gate sinks into the floor when +/** Repaint stateful objects to match the derived room signals — a gate sinks into the floor when * open, a plate settles under a held weight, spikes rise/retract; color communicates the same * state for the top-down read. */ export function applyRoomVisuals(ctx: GameContext, room: RoomDef, state: RoomState): void { diff --git a/apps/dev/public/models/kaykit-dungeon/banner_blue.glb b/apps/dev/public/models/kaykit-dungeon/banner_blue.glb new file mode 100644 index 000000000..0137c7ba6 Binary files /dev/null and b/apps/dev/public/models/kaykit-dungeon/banner_blue.glb differ diff --git a/apps/dev/public/models/kaykit-dungeon/banner_brown.glb b/apps/dev/public/models/kaykit-dungeon/banner_brown.glb new file mode 100644 index 000000000..148a91721 Binary files /dev/null and b/apps/dev/public/models/kaykit-dungeon/banner_brown.glb differ diff --git a/apps/dev/public/models/kaykit-dungeon/banner_green.glb b/apps/dev/public/models/kaykit-dungeon/banner_green.glb new file mode 100644 index 000000000..f3a9d1c78 Binary files /dev/null and b/apps/dev/public/models/kaykit-dungeon/banner_green.glb differ diff --git a/apps/dev/public/models/kaykit-dungeon/banner_patternA_blue.glb b/apps/dev/public/models/kaykit-dungeon/banner_patternA_blue.glb new file mode 100644 index 000000000..099f1bb03 Binary files /dev/null and b/apps/dev/public/models/kaykit-dungeon/banner_patternA_blue.glb differ diff --git a/apps/dev/public/models/kaykit-dungeon/banner_patternA_brown.glb b/apps/dev/public/models/kaykit-dungeon/banner_patternA_brown.glb new file mode 100644 index 000000000..5c6145ee9 Binary files /dev/null and b/apps/dev/public/models/kaykit-dungeon/banner_patternA_brown.glb differ diff --git a/apps/dev/public/models/kaykit-dungeon/banner_patternA_green.glb b/apps/dev/public/models/kaykit-dungeon/banner_patternA_green.glb new file mode 100644 index 000000000..0c29db5f0 Binary files /dev/null and b/apps/dev/public/models/kaykit-dungeon/banner_patternA_green.glb differ diff --git a/apps/dev/public/models/kaykit-dungeon/banner_patternA_red.glb b/apps/dev/public/models/kaykit-dungeon/banner_patternA_red.glb new file mode 100644 index 000000000..e7185427c Binary files /dev/null and b/apps/dev/public/models/kaykit-dungeon/banner_patternA_red.glb differ diff --git a/apps/dev/public/models/kaykit-dungeon/banner_patternA_white.glb b/apps/dev/public/models/kaykit-dungeon/banner_patternA_white.glb new file mode 100644 index 000000000..8d4490627 Binary files /dev/null and b/apps/dev/public/models/kaykit-dungeon/banner_patternA_white.glb differ diff --git a/apps/dev/public/models/kaykit-dungeon/banner_patternA_yellow.glb b/apps/dev/public/models/kaykit-dungeon/banner_patternA_yellow.glb new file mode 100644 index 000000000..f134bdbdb Binary files /dev/null and b/apps/dev/public/models/kaykit-dungeon/banner_patternA_yellow.glb differ diff --git a/apps/dev/public/models/kaykit-dungeon/banner_patternB_blue.glb b/apps/dev/public/models/kaykit-dungeon/banner_patternB_blue.glb new file mode 100644 index 000000000..6c3e36445 Binary files /dev/null and b/apps/dev/public/models/kaykit-dungeon/banner_patternB_blue.glb differ diff --git a/apps/dev/public/models/kaykit-dungeon/banner_patternB_brown.glb b/apps/dev/public/models/kaykit-dungeon/banner_patternB_brown.glb new file mode 100644 index 000000000..17cc1375a Binary files /dev/null and b/apps/dev/public/models/kaykit-dungeon/banner_patternB_brown.glb differ diff --git a/apps/dev/public/models/kaykit-dungeon/banner_patternB_green.glb b/apps/dev/public/models/kaykit-dungeon/banner_patternB_green.glb new file mode 100644 index 000000000..080d68db8 Binary files /dev/null and b/apps/dev/public/models/kaykit-dungeon/banner_patternB_green.glb differ diff --git a/apps/dev/public/models/kaykit-dungeon/banner_patternB_red.glb b/apps/dev/public/models/kaykit-dungeon/banner_patternB_red.glb new file mode 100644 index 000000000..cf2987ed7 Binary files /dev/null and b/apps/dev/public/models/kaykit-dungeon/banner_patternB_red.glb differ diff --git a/apps/dev/public/models/kaykit-dungeon/banner_patternB_white.glb b/apps/dev/public/models/kaykit-dungeon/banner_patternB_white.glb new file mode 100644 index 000000000..be2071a15 Binary files /dev/null and b/apps/dev/public/models/kaykit-dungeon/banner_patternB_white.glb differ diff --git a/apps/dev/public/models/kaykit-dungeon/banner_patternB_yellow.glb b/apps/dev/public/models/kaykit-dungeon/banner_patternB_yellow.glb new file mode 100644 index 000000000..3cb2080f6 Binary files /dev/null and b/apps/dev/public/models/kaykit-dungeon/banner_patternB_yellow.glb differ diff --git a/apps/dev/public/models/kaykit-dungeon/banner_patternC_blue.glb b/apps/dev/public/models/kaykit-dungeon/banner_patternC_blue.glb new file mode 100644 index 000000000..c273c9d10 Binary files /dev/null and b/apps/dev/public/models/kaykit-dungeon/banner_patternC_blue.glb differ diff --git a/apps/dev/public/models/kaykit-dungeon/banner_patternC_brown.glb b/apps/dev/public/models/kaykit-dungeon/banner_patternC_brown.glb new file mode 100644 index 000000000..809ac1e76 Binary files /dev/null and b/apps/dev/public/models/kaykit-dungeon/banner_patternC_brown.glb differ diff --git a/apps/dev/public/models/kaykit-dungeon/banner_patternC_green.glb b/apps/dev/public/models/kaykit-dungeon/banner_patternC_green.glb new file mode 100644 index 000000000..beba2a40c Binary files /dev/null and b/apps/dev/public/models/kaykit-dungeon/banner_patternC_green.glb differ diff --git a/apps/dev/public/models/kaykit-dungeon/banner_patternC_red.glb b/apps/dev/public/models/kaykit-dungeon/banner_patternC_red.glb new file mode 100644 index 000000000..c1b65e8fd Binary files /dev/null and b/apps/dev/public/models/kaykit-dungeon/banner_patternC_red.glb differ diff --git a/apps/dev/public/models/kaykit-dungeon/banner_patternC_white.glb b/apps/dev/public/models/kaykit-dungeon/banner_patternC_white.glb new file mode 100644 index 000000000..8eeaeda4f Binary files /dev/null and b/apps/dev/public/models/kaykit-dungeon/banner_patternC_white.glb differ diff --git a/apps/dev/public/models/kaykit-dungeon/banner_patternC_yellow.glb b/apps/dev/public/models/kaykit-dungeon/banner_patternC_yellow.glb new file mode 100644 index 000000000..a07366d74 Binary files /dev/null and b/apps/dev/public/models/kaykit-dungeon/banner_patternC_yellow.glb differ diff --git a/apps/dev/public/models/kaykit-dungeon/banner_red.glb b/apps/dev/public/models/kaykit-dungeon/banner_red.glb new file mode 100644 index 000000000..aa438c014 Binary files /dev/null and b/apps/dev/public/models/kaykit-dungeon/banner_red.glb differ diff --git a/apps/dev/public/models/kaykit-dungeon/banner_shield_blue.glb b/apps/dev/public/models/kaykit-dungeon/banner_shield_blue.glb new file mode 100644 index 000000000..ee9cb3f45 Binary files /dev/null and b/apps/dev/public/models/kaykit-dungeon/banner_shield_blue.glb differ diff --git a/apps/dev/public/models/kaykit-dungeon/banner_shield_brown.glb b/apps/dev/public/models/kaykit-dungeon/banner_shield_brown.glb new file mode 100644 index 000000000..03140b1cd Binary files /dev/null and b/apps/dev/public/models/kaykit-dungeon/banner_shield_brown.glb differ diff --git a/apps/dev/public/models/kaykit-dungeon/banner_shield_green.glb b/apps/dev/public/models/kaykit-dungeon/banner_shield_green.glb new file mode 100644 index 000000000..ab301a74e Binary files /dev/null and b/apps/dev/public/models/kaykit-dungeon/banner_shield_green.glb differ diff --git a/apps/dev/public/models/kaykit-dungeon/banner_shield_red.glb b/apps/dev/public/models/kaykit-dungeon/banner_shield_red.glb new file mode 100644 index 000000000..7e81382d8 Binary files /dev/null and b/apps/dev/public/models/kaykit-dungeon/banner_shield_red.glb differ diff --git a/apps/dev/public/models/kaykit-dungeon/banner_shield_white.glb b/apps/dev/public/models/kaykit-dungeon/banner_shield_white.glb new file mode 100644 index 000000000..5d10e0a1e Binary files /dev/null and b/apps/dev/public/models/kaykit-dungeon/banner_shield_white.glb differ diff --git a/apps/dev/public/models/kaykit-dungeon/banner_shield_yellow.glb b/apps/dev/public/models/kaykit-dungeon/banner_shield_yellow.glb new file mode 100644 index 000000000..33e673630 Binary files /dev/null and b/apps/dev/public/models/kaykit-dungeon/banner_shield_yellow.glb differ diff --git a/apps/dev/public/models/kaykit-dungeon/banner_thin_blue.glb b/apps/dev/public/models/kaykit-dungeon/banner_thin_blue.glb new file mode 100644 index 000000000..703c989f8 Binary files /dev/null and b/apps/dev/public/models/kaykit-dungeon/banner_thin_blue.glb differ diff --git a/apps/dev/public/models/kaykit-dungeon/banner_thin_brown.glb b/apps/dev/public/models/kaykit-dungeon/banner_thin_brown.glb new file mode 100644 index 000000000..f421673ff Binary files /dev/null and b/apps/dev/public/models/kaykit-dungeon/banner_thin_brown.glb differ diff --git a/apps/dev/public/models/kaykit-dungeon/banner_thin_green.glb b/apps/dev/public/models/kaykit-dungeon/banner_thin_green.glb new file mode 100644 index 000000000..ce8357fc9 Binary files /dev/null and b/apps/dev/public/models/kaykit-dungeon/banner_thin_green.glb differ diff --git a/apps/dev/public/models/kaykit-dungeon/banner_thin_red.glb b/apps/dev/public/models/kaykit-dungeon/banner_thin_red.glb new file mode 100644 index 000000000..f7472d75b Binary files /dev/null and b/apps/dev/public/models/kaykit-dungeon/banner_thin_red.glb differ diff --git a/apps/dev/public/models/kaykit-dungeon/banner_thin_white.glb b/apps/dev/public/models/kaykit-dungeon/banner_thin_white.glb new file mode 100644 index 000000000..895a0368e Binary files /dev/null and b/apps/dev/public/models/kaykit-dungeon/banner_thin_white.glb differ diff --git a/apps/dev/public/models/kaykit-dungeon/banner_thin_yellow.glb b/apps/dev/public/models/kaykit-dungeon/banner_thin_yellow.glb new file mode 100644 index 000000000..50297c70c Binary files /dev/null and b/apps/dev/public/models/kaykit-dungeon/banner_thin_yellow.glb differ diff --git a/apps/dev/public/models/kaykit-dungeon/banner_triple_blue.glb b/apps/dev/public/models/kaykit-dungeon/banner_triple_blue.glb new file mode 100644 index 000000000..37144eeb5 Binary files /dev/null and b/apps/dev/public/models/kaykit-dungeon/banner_triple_blue.glb differ diff --git a/apps/dev/public/models/kaykit-dungeon/banner_triple_brown.glb b/apps/dev/public/models/kaykit-dungeon/banner_triple_brown.glb new file mode 100644 index 000000000..1d1cadb6e Binary files /dev/null and b/apps/dev/public/models/kaykit-dungeon/banner_triple_brown.glb differ diff --git a/apps/dev/public/models/kaykit-dungeon/banner_triple_green.glb b/apps/dev/public/models/kaykit-dungeon/banner_triple_green.glb new file mode 100644 index 000000000..d9a6f6954 Binary files /dev/null and b/apps/dev/public/models/kaykit-dungeon/banner_triple_green.glb differ diff --git a/apps/dev/public/models/kaykit-dungeon/banner_triple_red.glb b/apps/dev/public/models/kaykit-dungeon/banner_triple_red.glb new file mode 100644 index 000000000..6cae20d93 Binary files /dev/null and b/apps/dev/public/models/kaykit-dungeon/banner_triple_red.glb differ diff --git a/apps/dev/public/models/kaykit-dungeon/banner_triple_white.glb b/apps/dev/public/models/kaykit-dungeon/banner_triple_white.glb new file mode 100644 index 000000000..67896b834 Binary files /dev/null and b/apps/dev/public/models/kaykit-dungeon/banner_triple_white.glb differ diff --git a/apps/dev/public/models/kaykit-dungeon/banner_triple_yellow.glb b/apps/dev/public/models/kaykit-dungeon/banner_triple_yellow.glb new file mode 100644 index 000000000..f8fbca4bf Binary files /dev/null and b/apps/dev/public/models/kaykit-dungeon/banner_triple_yellow.glb differ diff --git a/apps/dev/public/models/kaykit-dungeon/banner_white.glb b/apps/dev/public/models/kaykit-dungeon/banner_white.glb new file mode 100644 index 000000000..f00bf0406 Binary files /dev/null and b/apps/dev/public/models/kaykit-dungeon/banner_white.glb differ diff --git a/apps/dev/public/models/kaykit-dungeon/banner_yellow.glb b/apps/dev/public/models/kaykit-dungeon/banner_yellow.glb new file mode 100644 index 000000000..78041b681 Binary files /dev/null and b/apps/dev/public/models/kaykit-dungeon/banner_yellow.glb differ diff --git a/apps/dev/public/models/kaykit-dungeon/barrel_large.glb b/apps/dev/public/models/kaykit-dungeon/barrel_large.glb new file mode 100644 index 000000000..7a88d9bcc Binary files /dev/null and b/apps/dev/public/models/kaykit-dungeon/barrel_large.glb differ diff --git a/apps/dev/public/models/kaykit-dungeon/barrel_large_decorated.glb b/apps/dev/public/models/kaykit-dungeon/barrel_large_decorated.glb new file mode 100644 index 000000000..dd8b97dde Binary files /dev/null and b/apps/dev/public/models/kaykit-dungeon/barrel_large_decorated.glb differ diff --git a/apps/dev/public/models/kaykit-dungeon/barrel_small.glb b/apps/dev/public/models/kaykit-dungeon/barrel_small.glb new file mode 100644 index 000000000..327119c21 Binary files /dev/null and b/apps/dev/public/models/kaykit-dungeon/barrel_small.glb differ diff --git a/apps/dev/public/models/kaykit-dungeon/barrel_small_stack.glb b/apps/dev/public/models/kaykit-dungeon/barrel_small_stack.glb new file mode 100644 index 000000000..8e34afa28 Binary files /dev/null and b/apps/dev/public/models/kaykit-dungeon/barrel_small_stack.glb differ diff --git a/apps/dev/public/models/kaykit-dungeon/barrier.glb b/apps/dev/public/models/kaykit-dungeon/barrier.glb new file mode 100644 index 000000000..94b189369 Binary files /dev/null and b/apps/dev/public/models/kaykit-dungeon/barrier.glb differ diff --git a/apps/dev/public/models/kaykit-dungeon/barrier_colum_half.glb b/apps/dev/public/models/kaykit-dungeon/barrier_colum_half.glb new file mode 100644 index 000000000..0c15ea789 Binary files /dev/null and b/apps/dev/public/models/kaykit-dungeon/barrier_colum_half.glb differ diff --git a/apps/dev/public/models/kaykit-dungeon/barrier_column.glb b/apps/dev/public/models/kaykit-dungeon/barrier_column.glb new file mode 100644 index 000000000..30776b706 Binary files /dev/null and b/apps/dev/public/models/kaykit-dungeon/barrier_column.glb differ diff --git a/apps/dev/public/models/kaykit-dungeon/barrier_corner.glb b/apps/dev/public/models/kaykit-dungeon/barrier_corner.glb new file mode 100644 index 000000000..9222c56f8 Binary files /dev/null and b/apps/dev/public/models/kaykit-dungeon/barrier_corner.glb differ diff --git a/apps/dev/public/models/kaykit-dungeon/barrier_half.glb b/apps/dev/public/models/kaykit-dungeon/barrier_half.glb new file mode 100644 index 000000000..72bd5f54a Binary files /dev/null and b/apps/dev/public/models/kaykit-dungeon/barrier_half.glb differ diff --git a/apps/dev/public/models/kaykit-dungeon/bed_decorated.glb b/apps/dev/public/models/kaykit-dungeon/bed_decorated.glb new file mode 100644 index 000000000..b7195b9fa Binary files /dev/null and b/apps/dev/public/models/kaykit-dungeon/bed_decorated.glb differ diff --git a/apps/dev/public/models/kaykit-dungeon/bed_floor.glb b/apps/dev/public/models/kaykit-dungeon/bed_floor.glb new file mode 100644 index 000000000..101a79b77 Binary files /dev/null and b/apps/dev/public/models/kaykit-dungeon/bed_floor.glb differ diff --git a/apps/dev/public/models/kaykit-dungeon/bed_frame.glb b/apps/dev/public/models/kaykit-dungeon/bed_frame.glb new file mode 100644 index 000000000..bcc4963a6 Binary files /dev/null and b/apps/dev/public/models/kaykit-dungeon/bed_frame.glb differ diff --git a/apps/dev/public/models/kaykit-dungeon/bottle_A_brown.glb b/apps/dev/public/models/kaykit-dungeon/bottle_A_brown.glb new file mode 100644 index 000000000..34209142a Binary files /dev/null and b/apps/dev/public/models/kaykit-dungeon/bottle_A_brown.glb differ diff --git a/apps/dev/public/models/kaykit-dungeon/bottle_A_green.glb b/apps/dev/public/models/kaykit-dungeon/bottle_A_green.glb new file mode 100644 index 000000000..7c65aca05 Binary files /dev/null and b/apps/dev/public/models/kaykit-dungeon/bottle_A_green.glb differ diff --git a/apps/dev/public/models/kaykit-dungeon/bottle_A_labeled_brown.glb b/apps/dev/public/models/kaykit-dungeon/bottle_A_labeled_brown.glb new file mode 100644 index 000000000..1863eccff Binary files /dev/null and b/apps/dev/public/models/kaykit-dungeon/bottle_A_labeled_brown.glb differ diff --git a/apps/dev/public/models/kaykit-dungeon/bottle_A_labeled_green.glb b/apps/dev/public/models/kaykit-dungeon/bottle_A_labeled_green.glb new file mode 100644 index 000000000..aae096063 Binary files /dev/null and b/apps/dev/public/models/kaykit-dungeon/bottle_A_labeled_green.glb differ diff --git a/apps/dev/public/models/kaykit-dungeon/bottle_B_brown.glb b/apps/dev/public/models/kaykit-dungeon/bottle_B_brown.glb new file mode 100644 index 000000000..51dd2ddf2 Binary files /dev/null and b/apps/dev/public/models/kaykit-dungeon/bottle_B_brown.glb differ diff --git a/apps/dev/public/models/kaykit-dungeon/bottle_B_green.glb b/apps/dev/public/models/kaykit-dungeon/bottle_B_green.glb new file mode 100644 index 000000000..7300b1c56 Binary files /dev/null and b/apps/dev/public/models/kaykit-dungeon/bottle_B_green.glb differ diff --git a/apps/dev/public/models/kaykit-dungeon/bottle_C_brown.glb b/apps/dev/public/models/kaykit-dungeon/bottle_C_brown.glb new file mode 100644 index 000000000..503269db1 Binary files /dev/null and b/apps/dev/public/models/kaykit-dungeon/bottle_C_brown.glb differ diff --git a/apps/dev/public/models/kaykit-dungeon/bottle_C_green.glb b/apps/dev/public/models/kaykit-dungeon/bottle_C_green.glb new file mode 100644 index 000000000..9d48472db Binary files /dev/null and b/apps/dev/public/models/kaykit-dungeon/bottle_C_green.glb differ diff --git a/apps/dev/public/models/kaykit-dungeon/box_large.glb b/apps/dev/public/models/kaykit-dungeon/box_large.glb new file mode 100644 index 000000000..ba2666dcf Binary files /dev/null and b/apps/dev/public/models/kaykit-dungeon/box_large.glb differ diff --git a/apps/dev/public/models/kaykit-dungeon/box_small.glb b/apps/dev/public/models/kaykit-dungeon/box_small.glb new file mode 100644 index 000000000..afe14e93c Binary files /dev/null and b/apps/dev/public/models/kaykit-dungeon/box_small.glb differ diff --git a/apps/dev/public/models/kaykit-dungeon/box_small_decorated.glb b/apps/dev/public/models/kaykit-dungeon/box_small_decorated.glb new file mode 100644 index 000000000..7ce90474d Binary files /dev/null and b/apps/dev/public/models/kaykit-dungeon/box_small_decorated.glb differ diff --git a/apps/dev/public/models/kaykit-dungeon/box_stacked.glb b/apps/dev/public/models/kaykit-dungeon/box_stacked.glb new file mode 100644 index 000000000..e12a56efe Binary files /dev/null and b/apps/dev/public/models/kaykit-dungeon/box_stacked.glb differ diff --git a/apps/dev/public/models/kaykit-dungeon/candle.glb b/apps/dev/public/models/kaykit-dungeon/candle.glb new file mode 100644 index 000000000..a991df53d Binary files /dev/null and b/apps/dev/public/models/kaykit-dungeon/candle.glb differ diff --git a/apps/dev/public/models/kaykit-dungeon/candle_lit.glb b/apps/dev/public/models/kaykit-dungeon/candle_lit.glb new file mode 100644 index 000000000..b79333506 Binary files /dev/null and b/apps/dev/public/models/kaykit-dungeon/candle_lit.glb differ diff --git a/apps/dev/public/models/kaykit-dungeon/candle_melted.glb b/apps/dev/public/models/kaykit-dungeon/candle_melted.glb new file mode 100644 index 000000000..13a9de780 Binary files /dev/null and b/apps/dev/public/models/kaykit-dungeon/candle_melted.glb differ diff --git a/apps/dev/public/models/kaykit-dungeon/candle_thin.glb b/apps/dev/public/models/kaykit-dungeon/candle_thin.glb new file mode 100644 index 000000000..fc09366b9 Binary files /dev/null and b/apps/dev/public/models/kaykit-dungeon/candle_thin.glb differ diff --git a/apps/dev/public/models/kaykit-dungeon/candle_thin_lit.glb b/apps/dev/public/models/kaykit-dungeon/candle_thin_lit.glb new file mode 100644 index 000000000..cfe90d36e Binary files /dev/null and b/apps/dev/public/models/kaykit-dungeon/candle_thin_lit.glb differ diff --git a/apps/dev/public/models/kaykit-dungeon/candle_triple.glb b/apps/dev/public/models/kaykit-dungeon/candle_triple.glb new file mode 100644 index 000000000..c8c7e6330 Binary files /dev/null and b/apps/dev/public/models/kaykit-dungeon/candle_triple.glb differ diff --git a/apps/dev/public/models/kaykit-dungeon/chair.glb b/apps/dev/public/models/kaykit-dungeon/chair.glb new file mode 100644 index 000000000..d714e3eac Binary files /dev/null and b/apps/dev/public/models/kaykit-dungeon/chair.glb differ diff --git a/apps/dev/public/models/kaykit-dungeon/chest.glb b/apps/dev/public/models/kaykit-dungeon/chest.glb new file mode 100644 index 000000000..534c76a79 Binary files /dev/null and b/apps/dev/public/models/kaykit-dungeon/chest.glb differ diff --git a/apps/dev/public/models/kaykit-dungeon/chest_gold.glb b/apps/dev/public/models/kaykit-dungeon/chest_gold.glb new file mode 100644 index 000000000..8dd05d691 Binary files /dev/null and b/apps/dev/public/models/kaykit-dungeon/chest_gold.glb differ diff --git a/apps/dev/public/models/kaykit-dungeon/coin.glb b/apps/dev/public/models/kaykit-dungeon/coin.glb new file mode 100644 index 000000000..f7cdef3c8 Binary files /dev/null and b/apps/dev/public/models/kaykit-dungeon/coin.glb differ diff --git a/apps/dev/public/models/kaykit-dungeon/coin_stack_large.glb b/apps/dev/public/models/kaykit-dungeon/coin_stack_large.glb new file mode 100644 index 000000000..a806b1238 Binary files /dev/null and b/apps/dev/public/models/kaykit-dungeon/coin_stack_large.glb differ diff --git a/apps/dev/public/models/kaykit-dungeon/coin_stack_medium.glb b/apps/dev/public/models/kaykit-dungeon/coin_stack_medium.glb new file mode 100644 index 000000000..8d371ebea Binary files /dev/null and b/apps/dev/public/models/kaykit-dungeon/coin_stack_medium.glb differ diff --git a/apps/dev/public/models/kaykit-dungeon/coin_stack_small.glb b/apps/dev/public/models/kaykit-dungeon/coin_stack_small.glb new file mode 100644 index 000000000..0a07fbd0b Binary files /dev/null and b/apps/dev/public/models/kaykit-dungeon/coin_stack_small.glb differ diff --git a/apps/dev/public/models/kaykit-dungeon/column.glb b/apps/dev/public/models/kaykit-dungeon/column.glb new file mode 100644 index 000000000..a4b5779f5 Binary files /dev/null and b/apps/dev/public/models/kaykit-dungeon/column.glb differ diff --git a/apps/dev/public/models/kaykit-dungeon/crates_stacked.glb b/apps/dev/public/models/kaykit-dungeon/crates_stacked.glb new file mode 100644 index 000000000..7f273be5e Binary files /dev/null and b/apps/dev/public/models/kaykit-dungeon/crates_stacked.glb differ diff --git a/apps/dev/public/models/kaykit-dungeon/floor_dirt_large.glb b/apps/dev/public/models/kaykit-dungeon/floor_dirt_large.glb new file mode 100644 index 000000000..9e0cd900a Binary files /dev/null and b/apps/dev/public/models/kaykit-dungeon/floor_dirt_large.glb differ diff --git a/apps/dev/public/models/kaykit-dungeon/floor_dirt_large_rocky.glb b/apps/dev/public/models/kaykit-dungeon/floor_dirt_large_rocky.glb new file mode 100644 index 000000000..280dd6f91 Binary files /dev/null and b/apps/dev/public/models/kaykit-dungeon/floor_dirt_large_rocky.glb differ diff --git a/apps/dev/public/models/kaykit-dungeon/floor_dirt_small_A.glb b/apps/dev/public/models/kaykit-dungeon/floor_dirt_small_A.glb new file mode 100644 index 000000000..afcbbd099 Binary files /dev/null and b/apps/dev/public/models/kaykit-dungeon/floor_dirt_small_A.glb differ diff --git a/apps/dev/public/models/kaykit-dungeon/floor_dirt_small_B.glb b/apps/dev/public/models/kaykit-dungeon/floor_dirt_small_B.glb new file mode 100644 index 000000000..675a471b7 Binary files /dev/null and b/apps/dev/public/models/kaykit-dungeon/floor_dirt_small_B.glb differ diff --git a/apps/dev/public/models/kaykit-dungeon/floor_dirt_small_C.glb b/apps/dev/public/models/kaykit-dungeon/floor_dirt_small_C.glb new file mode 100644 index 000000000..8a2cc6ab4 Binary files /dev/null and b/apps/dev/public/models/kaykit-dungeon/floor_dirt_small_C.glb differ diff --git a/apps/dev/public/models/kaykit-dungeon/floor_dirt_small_D.glb b/apps/dev/public/models/kaykit-dungeon/floor_dirt_small_D.glb new file mode 100644 index 000000000..82a0e7ff1 Binary files /dev/null and b/apps/dev/public/models/kaykit-dungeon/floor_dirt_small_D.glb differ diff --git a/apps/dev/public/models/kaykit-dungeon/floor_dirt_small_corner.glb b/apps/dev/public/models/kaykit-dungeon/floor_dirt_small_corner.glb new file mode 100644 index 000000000..a07fcc4c2 Binary files /dev/null and b/apps/dev/public/models/kaykit-dungeon/floor_dirt_small_corner.glb differ diff --git a/apps/dev/public/models/kaykit-dungeon/floor_dirt_small_weeds.glb b/apps/dev/public/models/kaykit-dungeon/floor_dirt_small_weeds.glb new file mode 100644 index 000000000..ba39f5f8c Binary files /dev/null and b/apps/dev/public/models/kaykit-dungeon/floor_dirt_small_weeds.glb differ diff --git a/apps/dev/public/models/kaykit-dungeon/floor_foundation_allsides.glb b/apps/dev/public/models/kaykit-dungeon/floor_foundation_allsides.glb new file mode 100644 index 000000000..7d9366690 Binary files /dev/null and b/apps/dev/public/models/kaykit-dungeon/floor_foundation_allsides.glb differ diff --git a/apps/dev/public/models/kaykit-dungeon/floor_foundation_corner.glb b/apps/dev/public/models/kaykit-dungeon/floor_foundation_corner.glb new file mode 100644 index 000000000..d0c8b22bb Binary files /dev/null and b/apps/dev/public/models/kaykit-dungeon/floor_foundation_corner.glb differ diff --git a/apps/dev/public/models/kaykit-dungeon/floor_foundation_diagonal_corner.glb b/apps/dev/public/models/kaykit-dungeon/floor_foundation_diagonal_corner.glb new file mode 100644 index 000000000..9dbb542d2 Binary files /dev/null and b/apps/dev/public/models/kaykit-dungeon/floor_foundation_diagonal_corner.glb differ diff --git a/apps/dev/public/models/kaykit-dungeon/floor_foundation_front.glb b/apps/dev/public/models/kaykit-dungeon/floor_foundation_front.glb new file mode 100644 index 000000000..4a699d17f Binary files /dev/null and b/apps/dev/public/models/kaykit-dungeon/floor_foundation_front.glb differ diff --git a/apps/dev/public/models/kaykit-dungeon/floor_foundation_front_and_back.glb b/apps/dev/public/models/kaykit-dungeon/floor_foundation_front_and_back.glb new file mode 100644 index 000000000..6408b2cc5 Binary files /dev/null and b/apps/dev/public/models/kaykit-dungeon/floor_foundation_front_and_back.glb differ diff --git a/apps/dev/public/models/kaykit-dungeon/floor_foundation_front_and_sides.glb b/apps/dev/public/models/kaykit-dungeon/floor_foundation_front_and_sides.glb new file mode 100644 index 000000000..eb7113865 Binary files /dev/null and b/apps/dev/public/models/kaykit-dungeon/floor_foundation_front_and_sides.glb differ diff --git a/apps/dev/public/models/kaykit-dungeon/floor_tile_big_grate.glb b/apps/dev/public/models/kaykit-dungeon/floor_tile_big_grate.glb new file mode 100644 index 000000000..56ff3f57b Binary files /dev/null and b/apps/dev/public/models/kaykit-dungeon/floor_tile_big_grate.glb differ diff --git a/apps/dev/public/models/kaykit-dungeon/floor_tile_big_grate_open.glb b/apps/dev/public/models/kaykit-dungeon/floor_tile_big_grate_open.glb new file mode 100644 index 000000000..4d55430ad Binary files /dev/null and b/apps/dev/public/models/kaykit-dungeon/floor_tile_big_grate_open.glb differ diff --git a/apps/dev/public/models/kaykit-dungeon/floor_tile_big_spikes.glb b/apps/dev/public/models/kaykit-dungeon/floor_tile_big_spikes.glb new file mode 100644 index 000000000..e4ea4b93d Binary files /dev/null and b/apps/dev/public/models/kaykit-dungeon/floor_tile_big_spikes.glb differ diff --git a/apps/dev/public/models/kaykit-dungeon/floor_tile_extralarge_grates.glb b/apps/dev/public/models/kaykit-dungeon/floor_tile_extralarge_grates.glb new file mode 100644 index 000000000..e094df83c Binary files /dev/null and b/apps/dev/public/models/kaykit-dungeon/floor_tile_extralarge_grates.glb differ diff --git a/apps/dev/public/models/kaykit-dungeon/floor_tile_extralarge_grates_open.glb b/apps/dev/public/models/kaykit-dungeon/floor_tile_extralarge_grates_open.glb new file mode 100644 index 000000000..7128b3543 Binary files /dev/null and b/apps/dev/public/models/kaykit-dungeon/floor_tile_extralarge_grates_open.glb differ diff --git a/apps/dev/public/models/kaykit-dungeon/floor_tile_grate.glb b/apps/dev/public/models/kaykit-dungeon/floor_tile_grate.glb new file mode 100644 index 000000000..762bf0120 Binary files /dev/null and b/apps/dev/public/models/kaykit-dungeon/floor_tile_grate.glb differ diff --git a/apps/dev/public/models/kaykit-dungeon/floor_tile_grate_open.glb b/apps/dev/public/models/kaykit-dungeon/floor_tile_grate_open.glb new file mode 100644 index 000000000..47be00492 Binary files /dev/null and b/apps/dev/public/models/kaykit-dungeon/floor_tile_grate_open.glb differ diff --git a/apps/dev/public/models/kaykit-dungeon/floor_tile_large.glb b/apps/dev/public/models/kaykit-dungeon/floor_tile_large.glb new file mode 100644 index 000000000..953468a2f Binary files /dev/null and b/apps/dev/public/models/kaykit-dungeon/floor_tile_large.glb differ diff --git a/apps/dev/public/models/kaykit-dungeon/floor_tile_large_rocks.glb b/apps/dev/public/models/kaykit-dungeon/floor_tile_large_rocks.glb new file mode 100644 index 000000000..5c1379a20 Binary files /dev/null and b/apps/dev/public/models/kaykit-dungeon/floor_tile_large_rocks.glb differ diff --git a/apps/dev/public/models/kaykit-dungeon/floor_tile_small.glb b/apps/dev/public/models/kaykit-dungeon/floor_tile_small.glb new file mode 100644 index 000000000..18fd07c66 Binary files /dev/null and b/apps/dev/public/models/kaykit-dungeon/floor_tile_small.glb differ diff --git a/apps/dev/public/models/kaykit-dungeon/floor_tile_small_broken_A.glb b/apps/dev/public/models/kaykit-dungeon/floor_tile_small_broken_A.glb new file mode 100644 index 000000000..11bcf371c Binary files /dev/null and b/apps/dev/public/models/kaykit-dungeon/floor_tile_small_broken_A.glb differ diff --git a/apps/dev/public/models/kaykit-dungeon/floor_tile_small_broken_B.glb b/apps/dev/public/models/kaykit-dungeon/floor_tile_small_broken_B.glb new file mode 100644 index 000000000..73f41f5fd Binary files /dev/null and b/apps/dev/public/models/kaykit-dungeon/floor_tile_small_broken_B.glb differ diff --git a/apps/dev/public/models/kaykit-dungeon/floor_tile_small_corner.glb b/apps/dev/public/models/kaykit-dungeon/floor_tile_small_corner.glb new file mode 100644 index 000000000..623395cdd Binary files /dev/null and b/apps/dev/public/models/kaykit-dungeon/floor_tile_small_corner.glb differ diff --git a/apps/dev/public/models/kaykit-dungeon/floor_tile_small_decorated.glb b/apps/dev/public/models/kaykit-dungeon/floor_tile_small_decorated.glb new file mode 100644 index 000000000..47e9eebb5 Binary files /dev/null and b/apps/dev/public/models/kaykit-dungeon/floor_tile_small_decorated.glb differ diff --git a/apps/dev/public/models/kaykit-dungeon/floor_tile_small_weeds_A.glb b/apps/dev/public/models/kaykit-dungeon/floor_tile_small_weeds_A.glb new file mode 100644 index 000000000..ac5027c60 Binary files /dev/null and b/apps/dev/public/models/kaykit-dungeon/floor_tile_small_weeds_A.glb differ diff --git a/apps/dev/public/models/kaykit-dungeon/floor_tile_small_weeds_B.glb b/apps/dev/public/models/kaykit-dungeon/floor_tile_small_weeds_B.glb new file mode 100644 index 000000000..58bd57d3e Binary files /dev/null and b/apps/dev/public/models/kaykit-dungeon/floor_tile_small_weeds_B.glb differ diff --git a/apps/dev/public/models/kaykit-dungeon/floor_wood_large.glb b/apps/dev/public/models/kaykit-dungeon/floor_wood_large.glb new file mode 100644 index 000000000..8a883f189 Binary files /dev/null and b/apps/dev/public/models/kaykit-dungeon/floor_wood_large.glb differ diff --git a/apps/dev/public/models/kaykit-dungeon/floor_wood_large_dark.glb b/apps/dev/public/models/kaykit-dungeon/floor_wood_large_dark.glb new file mode 100644 index 000000000..613aa0855 Binary files /dev/null and b/apps/dev/public/models/kaykit-dungeon/floor_wood_large_dark.glb differ diff --git a/apps/dev/public/models/kaykit-dungeon/floor_wood_small.glb b/apps/dev/public/models/kaykit-dungeon/floor_wood_small.glb new file mode 100644 index 000000000..be492dd1e Binary files /dev/null and b/apps/dev/public/models/kaykit-dungeon/floor_wood_small.glb differ diff --git a/apps/dev/public/models/kaykit-dungeon/floor_wood_small_dark.glb b/apps/dev/public/models/kaykit-dungeon/floor_wood_small_dark.glb new file mode 100644 index 000000000..f7be70fad Binary files /dev/null and b/apps/dev/public/models/kaykit-dungeon/floor_wood_small_dark.glb differ diff --git a/apps/dev/public/models/kaykit-dungeon/keg.glb b/apps/dev/public/models/kaykit-dungeon/keg.glb new file mode 100644 index 000000000..47a84fa90 Binary files /dev/null and b/apps/dev/public/models/kaykit-dungeon/keg.glb differ diff --git a/apps/dev/public/models/kaykit-dungeon/keg_decorated.glb b/apps/dev/public/models/kaykit-dungeon/keg_decorated.glb new file mode 100644 index 000000000..6420a0e39 Binary files /dev/null and b/apps/dev/public/models/kaykit-dungeon/keg_decorated.glb differ diff --git a/apps/dev/public/models/kaykit-dungeon/key.glb b/apps/dev/public/models/kaykit-dungeon/key.glb new file mode 100644 index 000000000..4686f074e Binary files /dev/null and b/apps/dev/public/models/kaykit-dungeon/key.glb differ diff --git a/apps/dev/public/models/kaykit-dungeon/keyring.glb b/apps/dev/public/models/kaykit-dungeon/keyring.glb new file mode 100644 index 000000000..02a14d068 Binary files /dev/null and b/apps/dev/public/models/kaykit-dungeon/keyring.glb differ diff --git a/apps/dev/public/models/kaykit-dungeon/keyring_hanging.glb b/apps/dev/public/models/kaykit-dungeon/keyring_hanging.glb new file mode 100644 index 000000000..51c110f8f Binary files /dev/null and b/apps/dev/public/models/kaykit-dungeon/keyring_hanging.glb differ diff --git a/apps/dev/public/models/kaykit-dungeon/pillar.glb b/apps/dev/public/models/kaykit-dungeon/pillar.glb new file mode 100644 index 000000000..3b228d747 Binary files /dev/null and b/apps/dev/public/models/kaykit-dungeon/pillar.glb differ diff --git a/apps/dev/public/models/kaykit-dungeon/pillar_decorated.glb b/apps/dev/public/models/kaykit-dungeon/pillar_decorated.glb new file mode 100644 index 000000000..7eda56c42 Binary files /dev/null and b/apps/dev/public/models/kaykit-dungeon/pillar_decorated.glb differ diff --git a/apps/dev/public/models/kaykit-dungeon/plate.glb b/apps/dev/public/models/kaykit-dungeon/plate.glb new file mode 100644 index 000000000..8dcec4f87 Binary files /dev/null and b/apps/dev/public/models/kaykit-dungeon/plate.glb differ diff --git a/apps/dev/public/models/kaykit-dungeon/plate_food_A.glb b/apps/dev/public/models/kaykit-dungeon/plate_food_A.glb new file mode 100644 index 000000000..365d53423 Binary files /dev/null and b/apps/dev/public/models/kaykit-dungeon/plate_food_A.glb differ diff --git a/apps/dev/public/models/kaykit-dungeon/plate_food_B.glb b/apps/dev/public/models/kaykit-dungeon/plate_food_B.glb new file mode 100644 index 000000000..da81a5002 Binary files /dev/null and b/apps/dev/public/models/kaykit-dungeon/plate_food_B.glb differ diff --git a/apps/dev/public/models/kaykit-dungeon/plate_small.glb b/apps/dev/public/models/kaykit-dungeon/plate_small.glb new file mode 100644 index 000000000..0e6c0c9f1 Binary files /dev/null and b/apps/dev/public/models/kaykit-dungeon/plate_small.glb differ diff --git a/apps/dev/public/models/kaykit-dungeon/plate_stack.glb b/apps/dev/public/models/kaykit-dungeon/plate_stack.glb new file mode 100644 index 000000000..f32ccc4df Binary files /dev/null and b/apps/dev/public/models/kaykit-dungeon/plate_stack.glb differ diff --git a/apps/dev/public/models/kaykit-dungeon/rubble_half.glb b/apps/dev/public/models/kaykit-dungeon/rubble_half.glb new file mode 100644 index 000000000..4a4c48135 Binary files /dev/null and b/apps/dev/public/models/kaykit-dungeon/rubble_half.glb differ diff --git a/apps/dev/public/models/kaykit-dungeon/rubble_large.glb b/apps/dev/public/models/kaykit-dungeon/rubble_large.glb new file mode 100644 index 000000000..4ddb08100 Binary files /dev/null and b/apps/dev/public/models/kaykit-dungeon/rubble_large.glb differ diff --git a/apps/dev/public/models/kaykit-dungeon/shelf_large.glb b/apps/dev/public/models/kaykit-dungeon/shelf_large.glb new file mode 100644 index 000000000..e1b7e9752 Binary files /dev/null and b/apps/dev/public/models/kaykit-dungeon/shelf_large.glb differ diff --git a/apps/dev/public/models/kaykit-dungeon/shelf_small.glb b/apps/dev/public/models/kaykit-dungeon/shelf_small.glb new file mode 100644 index 000000000..b4af23b46 Binary files /dev/null and b/apps/dev/public/models/kaykit-dungeon/shelf_small.glb differ diff --git a/apps/dev/public/models/kaykit-dungeon/shelf_small_candles.glb b/apps/dev/public/models/kaykit-dungeon/shelf_small_candles.glb new file mode 100644 index 000000000..8d4603c8f Binary files /dev/null and b/apps/dev/public/models/kaykit-dungeon/shelf_small_candles.glb differ diff --git a/apps/dev/public/models/kaykit-dungeon/shelves.glb b/apps/dev/public/models/kaykit-dungeon/shelves.glb new file mode 100644 index 000000000..fea1c9db7 Binary files /dev/null and b/apps/dev/public/models/kaykit-dungeon/shelves.glb differ diff --git a/apps/dev/public/models/kaykit-dungeon/stairs.glb b/apps/dev/public/models/kaykit-dungeon/stairs.glb new file mode 100644 index 000000000..e780d3d62 Binary files /dev/null and b/apps/dev/public/models/kaykit-dungeon/stairs.glb differ diff --git a/apps/dev/public/models/kaykit-dungeon/stairs_narrow.glb b/apps/dev/public/models/kaykit-dungeon/stairs_narrow.glb new file mode 100644 index 000000000..437acb175 Binary files /dev/null and b/apps/dev/public/models/kaykit-dungeon/stairs_narrow.glb differ diff --git a/apps/dev/public/models/kaykit-dungeon/stairs_wall_left.glb b/apps/dev/public/models/kaykit-dungeon/stairs_wall_left.glb new file mode 100644 index 000000000..4afe3002f Binary files /dev/null and b/apps/dev/public/models/kaykit-dungeon/stairs_wall_left.glb differ diff --git a/apps/dev/public/models/kaykit-dungeon/stairs_wall_right.glb b/apps/dev/public/models/kaykit-dungeon/stairs_wall_right.glb new file mode 100644 index 000000000..e461d579d Binary files /dev/null and b/apps/dev/public/models/kaykit-dungeon/stairs_wall_right.glb differ diff --git a/apps/dev/public/models/kaykit-dungeon/stairs_walled.glb b/apps/dev/public/models/kaykit-dungeon/stairs_walled.glb new file mode 100644 index 000000000..1b7da3c55 Binary files /dev/null and b/apps/dev/public/models/kaykit-dungeon/stairs_walled.glb differ diff --git a/apps/dev/public/models/kaykit-dungeon/stairs_wide.glb b/apps/dev/public/models/kaykit-dungeon/stairs_wide.glb new file mode 100644 index 000000000..71189ad7b Binary files /dev/null and b/apps/dev/public/models/kaykit-dungeon/stairs_wide.glb differ diff --git a/apps/dev/public/models/kaykit-dungeon/stairs_wood.glb b/apps/dev/public/models/kaykit-dungeon/stairs_wood.glb new file mode 100644 index 000000000..1852cd6ec Binary files /dev/null and b/apps/dev/public/models/kaykit-dungeon/stairs_wood.glb differ diff --git a/apps/dev/public/models/kaykit-dungeon/stairs_wood_decorated.glb b/apps/dev/public/models/kaykit-dungeon/stairs_wood_decorated.glb new file mode 100644 index 000000000..86f0e8d90 Binary files /dev/null and b/apps/dev/public/models/kaykit-dungeon/stairs_wood_decorated.glb differ diff --git a/apps/dev/public/models/kaykit-dungeon/stool.glb b/apps/dev/public/models/kaykit-dungeon/stool.glb new file mode 100644 index 000000000..cbac1e0eb Binary files /dev/null and b/apps/dev/public/models/kaykit-dungeon/stool.glb differ diff --git a/apps/dev/public/models/kaykit-dungeon/sword_shield.glb b/apps/dev/public/models/kaykit-dungeon/sword_shield.glb new file mode 100644 index 000000000..156a15e16 Binary files /dev/null and b/apps/dev/public/models/kaykit-dungeon/sword_shield.glb differ diff --git a/apps/dev/public/models/kaykit-dungeon/sword_shield_broken.glb b/apps/dev/public/models/kaykit-dungeon/sword_shield_broken.glb new file mode 100644 index 000000000..b57c464a5 Binary files /dev/null and b/apps/dev/public/models/kaykit-dungeon/sword_shield_broken.glb differ diff --git a/apps/dev/public/models/kaykit-dungeon/sword_shield_gold.glb b/apps/dev/public/models/kaykit-dungeon/sword_shield_gold.glb new file mode 100644 index 000000000..7d67e13d2 Binary files /dev/null and b/apps/dev/public/models/kaykit-dungeon/sword_shield_gold.glb differ diff --git a/apps/dev/public/models/kaykit-dungeon/table_long.glb b/apps/dev/public/models/kaykit-dungeon/table_long.glb new file mode 100644 index 000000000..5b364d5c5 Binary files /dev/null and b/apps/dev/public/models/kaykit-dungeon/table_long.glb differ diff --git a/apps/dev/public/models/kaykit-dungeon/table_long_broken.glb b/apps/dev/public/models/kaykit-dungeon/table_long_broken.glb new file mode 100644 index 000000000..44d324294 Binary files /dev/null and b/apps/dev/public/models/kaykit-dungeon/table_long_broken.glb differ diff --git a/apps/dev/public/models/kaykit-dungeon/table_long_decorated_A.glb b/apps/dev/public/models/kaykit-dungeon/table_long_decorated_A.glb new file mode 100644 index 000000000..ad1b253c1 Binary files /dev/null and b/apps/dev/public/models/kaykit-dungeon/table_long_decorated_A.glb differ diff --git a/apps/dev/public/models/kaykit-dungeon/table_long_decorated_C.glb b/apps/dev/public/models/kaykit-dungeon/table_long_decorated_C.glb new file mode 100644 index 000000000..9b93de01a Binary files /dev/null and b/apps/dev/public/models/kaykit-dungeon/table_long_decorated_C.glb differ diff --git a/apps/dev/public/models/kaykit-dungeon/table_long_tablecloth.glb b/apps/dev/public/models/kaykit-dungeon/table_long_tablecloth.glb new file mode 100644 index 000000000..8219a1e10 Binary files /dev/null and b/apps/dev/public/models/kaykit-dungeon/table_long_tablecloth.glb differ diff --git a/apps/dev/public/models/kaykit-dungeon/table_long_tablecloth_decorated_A.glb b/apps/dev/public/models/kaykit-dungeon/table_long_tablecloth_decorated_A.glb new file mode 100644 index 000000000..58d3970ef Binary files /dev/null and b/apps/dev/public/models/kaykit-dungeon/table_long_tablecloth_decorated_A.glb differ diff --git a/apps/dev/public/models/kaykit-dungeon/table_medium.glb b/apps/dev/public/models/kaykit-dungeon/table_medium.glb new file mode 100644 index 000000000..eee20e627 Binary files /dev/null and b/apps/dev/public/models/kaykit-dungeon/table_medium.glb differ diff --git a/apps/dev/public/models/kaykit-dungeon/table_medium_broken.glb b/apps/dev/public/models/kaykit-dungeon/table_medium_broken.glb new file mode 100644 index 000000000..ff80a3a34 Binary files /dev/null and b/apps/dev/public/models/kaykit-dungeon/table_medium_broken.glb differ diff --git a/apps/dev/public/models/kaykit-dungeon/table_medium_decorated_A.glb b/apps/dev/public/models/kaykit-dungeon/table_medium_decorated_A.glb new file mode 100644 index 000000000..7f03c5737 Binary files /dev/null and b/apps/dev/public/models/kaykit-dungeon/table_medium_decorated_A.glb differ diff --git a/apps/dev/public/models/kaykit-dungeon/table_medium_tablecloth.glb b/apps/dev/public/models/kaykit-dungeon/table_medium_tablecloth.glb new file mode 100644 index 000000000..82101b761 Binary files /dev/null and b/apps/dev/public/models/kaykit-dungeon/table_medium_tablecloth.glb differ diff --git a/apps/dev/public/models/kaykit-dungeon/table_medium_tablecloth_decorated_B.glb b/apps/dev/public/models/kaykit-dungeon/table_medium_tablecloth_decorated_B.glb new file mode 100644 index 000000000..0042fd205 Binary files /dev/null and b/apps/dev/public/models/kaykit-dungeon/table_medium_tablecloth_decorated_B.glb differ diff --git a/apps/dev/public/models/kaykit-dungeon/table_small.glb b/apps/dev/public/models/kaykit-dungeon/table_small.glb new file mode 100644 index 000000000..f17e5b255 Binary files /dev/null and b/apps/dev/public/models/kaykit-dungeon/table_small.glb differ diff --git a/apps/dev/public/models/kaykit-dungeon/table_small_decorated_A.glb b/apps/dev/public/models/kaykit-dungeon/table_small_decorated_A.glb new file mode 100644 index 000000000..b9cc0649c Binary files /dev/null and b/apps/dev/public/models/kaykit-dungeon/table_small_decorated_A.glb differ diff --git a/apps/dev/public/models/kaykit-dungeon/table_small_decorated_B.glb b/apps/dev/public/models/kaykit-dungeon/table_small_decorated_B.glb new file mode 100644 index 000000000..f02a130be Binary files /dev/null and b/apps/dev/public/models/kaykit-dungeon/table_small_decorated_B.glb differ diff --git a/apps/dev/public/models/kaykit-dungeon/torch.glb b/apps/dev/public/models/kaykit-dungeon/torch.glb new file mode 100644 index 000000000..5fcf9a8ab Binary files /dev/null and b/apps/dev/public/models/kaykit-dungeon/torch.glb differ diff --git a/apps/dev/public/models/kaykit-dungeon/torch_lit.glb b/apps/dev/public/models/kaykit-dungeon/torch_lit.glb new file mode 100644 index 000000000..7976a0301 Binary files /dev/null and b/apps/dev/public/models/kaykit-dungeon/torch_lit.glb differ diff --git a/apps/dev/public/models/kaykit-dungeon/torch_mounted.glb b/apps/dev/public/models/kaykit-dungeon/torch_mounted.glb new file mode 100644 index 000000000..5c3892703 Binary files /dev/null and b/apps/dev/public/models/kaykit-dungeon/torch_mounted.glb differ diff --git a/apps/dev/public/models/kaykit-dungeon/trunk_large_A.glb b/apps/dev/public/models/kaykit-dungeon/trunk_large_A.glb new file mode 100644 index 000000000..0b1beabe9 Binary files /dev/null and b/apps/dev/public/models/kaykit-dungeon/trunk_large_A.glb differ diff --git a/apps/dev/public/models/kaykit-dungeon/trunk_large_B.glb b/apps/dev/public/models/kaykit-dungeon/trunk_large_B.glb new file mode 100644 index 000000000..01bb3fb0a Binary files /dev/null and b/apps/dev/public/models/kaykit-dungeon/trunk_large_B.glb differ diff --git a/apps/dev/public/models/kaykit-dungeon/trunk_large_C.glb b/apps/dev/public/models/kaykit-dungeon/trunk_large_C.glb new file mode 100644 index 000000000..983821f3d Binary files /dev/null and b/apps/dev/public/models/kaykit-dungeon/trunk_large_C.glb differ diff --git a/apps/dev/public/models/kaykit-dungeon/trunk_medium_A.glb b/apps/dev/public/models/kaykit-dungeon/trunk_medium_A.glb new file mode 100644 index 000000000..642868e66 Binary files /dev/null and b/apps/dev/public/models/kaykit-dungeon/trunk_medium_A.glb differ diff --git a/apps/dev/public/models/kaykit-dungeon/trunk_medium_B.glb b/apps/dev/public/models/kaykit-dungeon/trunk_medium_B.glb new file mode 100644 index 000000000..579a5046c Binary files /dev/null and b/apps/dev/public/models/kaykit-dungeon/trunk_medium_B.glb differ diff --git a/apps/dev/public/models/kaykit-dungeon/trunk_medium_C.glb b/apps/dev/public/models/kaykit-dungeon/trunk_medium_C.glb new file mode 100644 index 000000000..daca478d8 Binary files /dev/null and b/apps/dev/public/models/kaykit-dungeon/trunk_medium_C.glb differ diff --git a/apps/dev/public/models/kaykit-dungeon/trunk_small_A.glb b/apps/dev/public/models/kaykit-dungeon/trunk_small_A.glb new file mode 100644 index 000000000..869fa48ef Binary files /dev/null and b/apps/dev/public/models/kaykit-dungeon/trunk_small_A.glb differ diff --git a/apps/dev/public/models/kaykit-dungeon/trunk_small_B.glb b/apps/dev/public/models/kaykit-dungeon/trunk_small_B.glb new file mode 100644 index 000000000..fa54b78ab Binary files /dev/null and b/apps/dev/public/models/kaykit-dungeon/trunk_small_B.glb differ diff --git a/apps/dev/public/models/kaykit-dungeon/trunk_small_C.glb b/apps/dev/public/models/kaykit-dungeon/trunk_small_C.glb new file mode 100644 index 000000000..23618e67f Binary files /dev/null and b/apps/dev/public/models/kaykit-dungeon/trunk_small_C.glb differ diff --git a/apps/dev/public/models/kaykit-dungeon/wall.glb b/apps/dev/public/models/kaykit-dungeon/wall.glb new file mode 100644 index 000000000..5bb502fe4 Binary files /dev/null and b/apps/dev/public/models/kaykit-dungeon/wall.glb differ diff --git a/apps/dev/public/models/kaykit-dungeon/wall_Tsplit.glb b/apps/dev/public/models/kaykit-dungeon/wall_Tsplit.glb new file mode 100644 index 000000000..0c6477b8a Binary files /dev/null and b/apps/dev/public/models/kaykit-dungeon/wall_Tsplit.glb differ diff --git a/apps/dev/public/models/kaykit-dungeon/wall_Tsplit_sloped.glb b/apps/dev/public/models/kaykit-dungeon/wall_Tsplit_sloped.glb new file mode 100644 index 000000000..c93284738 Binary files /dev/null and b/apps/dev/public/models/kaykit-dungeon/wall_Tsplit_sloped.glb differ diff --git a/apps/dev/public/models/kaykit-dungeon/wall_arched.glb b/apps/dev/public/models/kaykit-dungeon/wall_arched.glb new file mode 100644 index 000000000..d049ace88 Binary files /dev/null and b/apps/dev/public/models/kaykit-dungeon/wall_arched.glb differ diff --git a/apps/dev/public/models/kaykit-dungeon/wall_archedwindow_gated.glb b/apps/dev/public/models/kaykit-dungeon/wall_archedwindow_gated.glb new file mode 100644 index 000000000..5ebea6d49 Binary files /dev/null and b/apps/dev/public/models/kaykit-dungeon/wall_archedwindow_gated.glb differ diff --git a/apps/dev/public/models/kaykit-dungeon/wall_archedwindow_gated_scaffold.glb b/apps/dev/public/models/kaykit-dungeon/wall_archedwindow_gated_scaffold.glb new file mode 100644 index 000000000..f594bb45a Binary files /dev/null and b/apps/dev/public/models/kaykit-dungeon/wall_archedwindow_gated_scaffold.glb differ diff --git a/apps/dev/public/models/kaykit-dungeon/wall_archedwindow_open.glb b/apps/dev/public/models/kaykit-dungeon/wall_archedwindow_open.glb new file mode 100644 index 000000000..14621d098 Binary files /dev/null and b/apps/dev/public/models/kaykit-dungeon/wall_archedwindow_open.glb differ diff --git a/apps/dev/public/models/kaykit-dungeon/wall_broken.glb b/apps/dev/public/models/kaykit-dungeon/wall_broken.glb new file mode 100644 index 000000000..c67687c36 Binary files /dev/null and b/apps/dev/public/models/kaykit-dungeon/wall_broken.glb differ diff --git a/apps/dev/public/models/kaykit-dungeon/wall_corner.glb b/apps/dev/public/models/kaykit-dungeon/wall_corner.glb new file mode 100644 index 000000000..e594aa8eb Binary files /dev/null and b/apps/dev/public/models/kaykit-dungeon/wall_corner.glb differ diff --git a/apps/dev/public/models/kaykit-dungeon/wall_corner_gated.glb b/apps/dev/public/models/kaykit-dungeon/wall_corner_gated.glb new file mode 100644 index 000000000..d83efd31c Binary files /dev/null and b/apps/dev/public/models/kaykit-dungeon/wall_corner_gated.glb differ diff --git a/apps/dev/public/models/kaykit-dungeon/wall_corner_scaffold.glb b/apps/dev/public/models/kaykit-dungeon/wall_corner_scaffold.glb new file mode 100644 index 000000000..5da5a21a3 Binary files /dev/null and b/apps/dev/public/models/kaykit-dungeon/wall_corner_scaffold.glb differ diff --git a/apps/dev/public/models/kaykit-dungeon/wall_corner_small.glb b/apps/dev/public/models/kaykit-dungeon/wall_corner_small.glb new file mode 100644 index 000000000..ead903157 Binary files /dev/null and b/apps/dev/public/models/kaykit-dungeon/wall_corner_small.glb differ diff --git a/apps/dev/public/models/kaykit-dungeon/wall_cracked.glb b/apps/dev/public/models/kaykit-dungeon/wall_cracked.glb new file mode 100644 index 000000000..13440aaa2 Binary files /dev/null and b/apps/dev/public/models/kaykit-dungeon/wall_cracked.glb differ diff --git a/apps/dev/public/models/kaykit-dungeon/wall_crossing.glb b/apps/dev/public/models/kaykit-dungeon/wall_crossing.glb new file mode 100644 index 000000000..bcf02be1a Binary files /dev/null and b/apps/dev/public/models/kaykit-dungeon/wall_crossing.glb differ diff --git a/apps/dev/public/models/kaykit-dungeon/wall_doorway.glb b/apps/dev/public/models/kaykit-dungeon/wall_doorway.glb new file mode 100644 index 000000000..bce214e25 Binary files /dev/null and b/apps/dev/public/models/kaykit-dungeon/wall_doorway.glb differ diff --git a/apps/dev/public/models/kaykit-dungeon/wall_doorway_Tsplit.glb b/apps/dev/public/models/kaykit-dungeon/wall_doorway_Tsplit.glb new file mode 100644 index 000000000..30a615633 Binary files /dev/null and b/apps/dev/public/models/kaykit-dungeon/wall_doorway_Tsplit.glb differ diff --git a/apps/dev/public/models/kaykit-dungeon/wall_doorway_scaffold.glb b/apps/dev/public/models/kaykit-dungeon/wall_doorway_scaffold.glb new file mode 100644 index 000000000..0ca509f6b Binary files /dev/null and b/apps/dev/public/models/kaykit-dungeon/wall_doorway_scaffold.glb differ diff --git a/apps/dev/public/models/kaykit-dungeon/wall_doorway_sides.glb b/apps/dev/public/models/kaykit-dungeon/wall_doorway_sides.glb new file mode 100644 index 000000000..e28414d3a Binary files /dev/null and b/apps/dev/public/models/kaykit-dungeon/wall_doorway_sides.glb differ diff --git a/apps/dev/public/models/kaykit-dungeon/wall_endcap.glb b/apps/dev/public/models/kaykit-dungeon/wall_endcap.glb new file mode 100644 index 000000000..f386e524a Binary files /dev/null and b/apps/dev/public/models/kaykit-dungeon/wall_endcap.glb differ diff --git a/apps/dev/public/models/kaykit-dungeon/wall_gated.glb b/apps/dev/public/models/kaykit-dungeon/wall_gated.glb new file mode 100644 index 000000000..9213fa89f Binary files /dev/null and b/apps/dev/public/models/kaykit-dungeon/wall_gated.glb differ diff --git a/apps/dev/public/models/kaykit-dungeon/wall_half.glb b/apps/dev/public/models/kaykit-dungeon/wall_half.glb new file mode 100644 index 000000000..e1bfabe58 Binary files /dev/null and b/apps/dev/public/models/kaykit-dungeon/wall_half.glb differ diff --git a/apps/dev/public/models/kaykit-dungeon/wall_half_endcap.glb b/apps/dev/public/models/kaykit-dungeon/wall_half_endcap.glb new file mode 100644 index 000000000..561bf0776 Binary files /dev/null and b/apps/dev/public/models/kaykit-dungeon/wall_half_endcap.glb differ diff --git a/apps/dev/public/models/kaykit-dungeon/wall_half_endcap_sloped.glb b/apps/dev/public/models/kaykit-dungeon/wall_half_endcap_sloped.glb new file mode 100644 index 000000000..cfc1c5ec8 Binary files /dev/null and b/apps/dev/public/models/kaykit-dungeon/wall_half_endcap_sloped.glb differ diff --git a/apps/dev/public/models/kaykit-dungeon/wall_open_scaffold.glb b/apps/dev/public/models/kaykit-dungeon/wall_open_scaffold.glb new file mode 100644 index 000000000..207531fa1 Binary files /dev/null and b/apps/dev/public/models/kaykit-dungeon/wall_open_scaffold.glb differ diff --git a/apps/dev/public/models/kaykit-dungeon/wall_pillar.glb b/apps/dev/public/models/kaykit-dungeon/wall_pillar.glb new file mode 100644 index 000000000..6f50b79de Binary files /dev/null and b/apps/dev/public/models/kaykit-dungeon/wall_pillar.glb differ diff --git a/apps/dev/public/models/kaykit-dungeon/wall_scaffold.glb b/apps/dev/public/models/kaykit-dungeon/wall_scaffold.glb new file mode 100644 index 000000000..f6dcff62b Binary files /dev/null and b/apps/dev/public/models/kaykit-dungeon/wall_scaffold.glb differ diff --git a/apps/dev/public/models/kaykit-dungeon/wall_shelves.glb b/apps/dev/public/models/kaykit-dungeon/wall_shelves.glb new file mode 100644 index 000000000..a9c0c666d Binary files /dev/null and b/apps/dev/public/models/kaykit-dungeon/wall_shelves.glb differ diff --git a/apps/dev/public/models/kaykit-dungeon/wall_sloped.glb b/apps/dev/public/models/kaykit-dungeon/wall_sloped.glb new file mode 100644 index 000000000..471dbfbd4 Binary files /dev/null and b/apps/dev/public/models/kaykit-dungeon/wall_sloped.glb differ diff --git a/apps/dev/public/models/kaykit-dungeon/wall_window_closed.glb b/apps/dev/public/models/kaykit-dungeon/wall_window_closed.glb new file mode 100644 index 000000000..7b8894bef Binary files /dev/null and b/apps/dev/public/models/kaykit-dungeon/wall_window_closed.glb differ diff --git a/apps/dev/public/models/kaykit-dungeon/wall_window_closed_scaffold.glb b/apps/dev/public/models/kaykit-dungeon/wall_window_closed_scaffold.glb new file mode 100644 index 000000000..da5ac7233 Binary files /dev/null and b/apps/dev/public/models/kaykit-dungeon/wall_window_closed_scaffold.glb differ diff --git a/apps/dev/public/models/kaykit-dungeon/wall_window_open.glb b/apps/dev/public/models/kaykit-dungeon/wall_window_open.glb new file mode 100644 index 000000000..b3f82fabb Binary files /dev/null and b/apps/dev/public/models/kaykit-dungeon/wall_window_open.glb differ diff --git a/apps/dev/public/models/kaykit-dungeon/wall_window_open_scaffold.glb b/apps/dev/public/models/kaykit-dungeon/wall_window_open_scaffold.glb new file mode 100644 index 000000000..85c498d0e Binary files /dev/null and b/apps/dev/public/models/kaykit-dungeon/wall_window_open_scaffold.glb differ