mirror of
https://github.com/xmanrui/OpenClaw-bot-review.git
synced 2026-07-31 04:06:49 +00:00
Add emoji-based furniture rendering support. Camera placed at center of right room desk with a radial flash effect every 10 seconds. Fix surface item z-sorting for fractional positions.
1176 lines
53 KiB
TypeScript
1176 lines
53 KiB
TypeScript
import type { Direction, SpriteData, FloorColor } from '../types'
|
|
import { Direction as Dir } from '../types'
|
|
import { adjustSprite } from '../colorize'
|
|
|
|
// ── Color Palettes ──────────────────────────────────────────────
|
|
const _ = '' // transparent
|
|
|
|
// ── Furniture Sprites ───────────────────────────────────────────
|
|
|
|
/** Square desk: 32x32 pixels (2x2 tiles) — top-down wood surface */
|
|
export const DESK_SQUARE_SPRITE: SpriteData = (() => {
|
|
const W = '#8B6914' // wood edge
|
|
const L = '#A07828' // lighter wood
|
|
const S = '#B8922E' // surface
|
|
const D = '#6B4E0A' // dark edge
|
|
const rows: string[][] = []
|
|
// Row 0: empty
|
|
rows.push(new Array(32).fill(_))
|
|
// Row 1: top edge
|
|
rows.push([_, ...new Array(30).fill(W), _])
|
|
// Rows 2-5: top surface
|
|
for (let r = 0; r < 4; r++) {
|
|
rows.push([_, W, ...new Array(28).fill(r < 1 ? L : S), W, _])
|
|
}
|
|
// Row 6: horizontal divider
|
|
rows.push([_, D, ...new Array(28).fill(W), D, _])
|
|
// Rows 7-12: middle surface area
|
|
for (let r = 0; r < 6; r++) {
|
|
rows.push([_, W, ...new Array(28).fill(S), W, _])
|
|
}
|
|
// Row 13: center line
|
|
rows.push([_, W, ...new Array(28).fill(L), W, _])
|
|
// Rows 14-19: lower surface
|
|
for (let r = 0; r < 6; r++) {
|
|
rows.push([_, W, ...new Array(28).fill(S), W, _])
|
|
}
|
|
// Row 20: horizontal divider
|
|
rows.push([_, D, ...new Array(28).fill(W), D, _])
|
|
// Rows 21-24: bottom surface
|
|
for (let r = 0; r < 4; r++) {
|
|
rows.push([_, W, ...new Array(28).fill(r > 2 ? L : S), W, _])
|
|
}
|
|
// Row 25: bottom edge
|
|
rows.push([_, ...new Array(30).fill(W), _])
|
|
// Rows 26-31: legs/shadow
|
|
for (let r = 0; r < 4; r++) {
|
|
const row = new Array(32).fill(_) as string[]
|
|
row[1] = D; row[2] = D; row[29] = D; row[30] = D
|
|
rows.push(row)
|
|
}
|
|
rows.push(new Array(32).fill(_))
|
|
rows.push(new Array(32).fill(_))
|
|
return rows
|
|
})()
|
|
|
|
/** Plant in pot: 16x24 */
|
|
export const PLANT_SPRITE: SpriteData = (() => {
|
|
const G = '#3D8B37'
|
|
const D = '#2D6B27'
|
|
const T = '#6B4E0A'
|
|
const P = '#B85C3A'
|
|
const R = '#8B4422'
|
|
return [
|
|
[_, _, _, _, _, _, G, G, _, _, _, _, _, _, _, _],
|
|
[_, _, _, _, _, G, G, G, G, _, _, _, _, _, _, _],
|
|
[_, _, _, _, G, G, D, G, G, G, _, _, _, _, _, _],
|
|
[_, _, _, G, G, D, G, G, D, G, G, _, _, _, _, _],
|
|
[_, _, G, G, G, G, G, G, G, G, G, G, _, _, _, _],
|
|
[_, G, G, D, G, G, G, G, G, G, D, G, G, _, _, _],
|
|
[_, G, G, G, G, D, G, G, D, G, G, G, G, _, _, _],
|
|
[_, _, G, G, G, G, G, G, G, G, G, G, _, _, _, _],
|
|
[_, _, _, G, G, G, D, G, G, G, G, _, _, _, _, _],
|
|
[_, _, _, _, G, G, G, G, G, G, _, _, _, _, _, _],
|
|
[_, _, _, _, _, G, G, G, G, _, _, _, _, _, _, _],
|
|
[_, _, _, _, _, _, T, T, _, _, _, _, _, _, _, _],
|
|
[_, _, _, _, _, _, T, T, _, _, _, _, _, _, _, _],
|
|
[_, _, _, _, _, _, T, T, _, _, _, _, _, _, _, _],
|
|
[_, _, _, _, _, R, R, R, R, R, _, _, _, _, _, _],
|
|
[_, _, _, _, R, P, P, P, P, P, R, _, _, _, _, _],
|
|
[_, _, _, _, R, P, P, P, P, P, R, _, _, _, _, _],
|
|
[_, _, _, _, R, P, P, P, P, P, R, _, _, _, _, _],
|
|
[_, _, _, _, R, P, P, P, P, P, R, _, _, _, _, _],
|
|
[_, _, _, _, R, P, P, P, P, P, R, _, _, _, _, _],
|
|
[_, _, _, _, R, P, P, P, P, P, R, _, _, _, _, _],
|
|
[_, _, _, _, _, R, P, P, P, R, _, _, _, _, _, _],
|
|
[_, _, _, _, _, _, R, R, R, _, _, _, _, _, _, _],
|
|
[_, _, _, _, _, _, _, _, _, _, _, _, _, _, _, _],
|
|
]
|
|
})()
|
|
|
|
/** Bookshelf: 16x32 (1 tile wide, 2 tiles tall) */
|
|
export const BOOKSHELF_SPRITE: SpriteData = (() => {
|
|
const W = '#8B6914'
|
|
const D = '#6B4E0A'
|
|
const R = '#CC4444'
|
|
const B = '#4477AA'
|
|
const G = '#44AA66'
|
|
const Y = '#CCAA33'
|
|
const P = '#9955AA'
|
|
return [
|
|
[_, W, W, W, W, W, W, W, W, W, W, W, W, W, W, _],
|
|
[W, D, D, D, D, D, D, D, D, D, D, D, D, D, D, W],
|
|
[W, D, R, R, B, B, G, G, Y, Y, R, R, B, B, D, W],
|
|
[W, D, R, R, B, B, G, G, Y, Y, R, R, B, B, D, W],
|
|
[W, D, R, R, B, B, G, G, Y, Y, R, R, B, B, D, W],
|
|
[W, D, R, R, B, B, G, G, Y, Y, R, R, B, B, D, W],
|
|
[W, D, R, R, B, B, G, G, Y, Y, R, R, B, B, D, W],
|
|
[W, W, W, W, W, W, W, W, W, W, W, W, W, W, W, W],
|
|
[W, D, D, D, D, D, D, D, D, D, D, D, D, D, D, W],
|
|
[W, D, P, P, Y, Y, B, B, G, G, P, P, R, R, D, W],
|
|
[W, D, P, P, Y, Y, B, B, G, G, P, P, R, R, D, W],
|
|
[W, D, P, P, Y, Y, B, B, G, G, P, P, R, R, D, W],
|
|
[W, D, P, P, Y, Y, B, B, G, G, P, P, R, R, D, W],
|
|
[W, D, P, P, Y, Y, B, B, G, G, P, P, R, R, D, W],
|
|
[W, D, P, P, Y, Y, B, B, G, G, P, P, R, R, D, W],
|
|
[W, W, W, W, W, W, W, W, W, W, W, W, W, W, W, W],
|
|
[W, D, D, D, D, D, D, D, D, D, D, D, D, D, D, W],
|
|
[W, D, G, G, R, R, P, P, B, B, Y, Y, G, G, D, W],
|
|
[W, D, G, G, R, R, P, P, B, B, Y, Y, G, G, D, W],
|
|
[W, D, G, G, R, R, P, P, B, B, Y, Y, G, G, D, W],
|
|
[W, D, G, G, R, R, P, P, B, B, Y, Y, G, G, D, W],
|
|
[W, D, G, G, R, R, P, P, B, B, Y, Y, G, G, D, W],
|
|
[W, D, G, G, R, R, P, P, B, B, Y, Y, G, G, D, W],
|
|
[W, W, W, W, W, W, W, W, W, W, W, W, W, W, W, W],
|
|
[W, D, D, D, D, D, D, D, D, D, D, D, D, D, D, W],
|
|
[W, D, D, D, D, D, D, D, D, D, D, D, D, D, D, W],
|
|
[W, D, D, D, D, D, D, D, D, D, D, D, D, D, D, W],
|
|
[W, D, D, D, D, D, D, D, D, D, D, D, D, D, D, W],
|
|
[W, D, D, D, D, D, D, D, D, D, D, D, D, D, D, W],
|
|
[W, D, D, D, D, D, D, D, D, D, D, D, D, D, D, W],
|
|
[W, W, W, W, W, W, W, W, W, W, W, W, W, W, W, W],
|
|
[_, W, W, W, W, W, W, W, W, W, W, W, W, W, W, _],
|
|
]
|
|
})()
|
|
|
|
/** Water cooler: 16x24 */
|
|
export const COOLER_SPRITE: SpriteData = (() => {
|
|
const W = '#CCDDEE'
|
|
const L = '#88BBDD'
|
|
const D = '#999999'
|
|
const B = '#666666'
|
|
return [
|
|
[_, _, _, _, _, _, _, _, _, _, _, _, _, _, _, _],
|
|
[_, _, _, _, _, D, D, D, D, D, D, _, _, _, _, _],
|
|
[_, _, _, _, D, L, L, L, L, L, L, D, _, _, _, _],
|
|
[_, _, _, _, D, L, L, L, L, L, L, D, _, _, _, _],
|
|
[_, _, _, _, D, L, L, L, L, L, L, D, _, _, _, _],
|
|
[_, _, _, _, D, L, L, L, L, L, L, D, _, _, _, _],
|
|
[_, _, _, _, D, L, L, L, L, L, L, D, _, _, _, _],
|
|
[_, _, _, _, _, D, D, D, D, D, D, _, _, _, _, _],
|
|
[_, _, _, _, _, D, W, W, W, W, D, _, _, _, _, _],
|
|
[_, _, _, _, _, D, W, W, W, W, D, _, _, _, _, _],
|
|
[_, _, _, _, _, D, W, W, W, W, D, _, _, _, _, _],
|
|
[_, _, _, _, _, D, W, W, W, W, D, _, _, _, _, _],
|
|
[_, _, _, _, _, D, W, W, W, W, D, _, _, _, _, _],
|
|
[_, _, _, _, D, D, W, W, W, W, D, D, _, _, _, _],
|
|
[_, _, _, _, D, W, W, W, W, W, W, D, _, _, _, _],
|
|
[_, _, _, _, D, W, W, W, W, W, W, D, _, _, _, _],
|
|
[_, _, _, _, D, D, D, D, D, D, D, D, _, _, _, _],
|
|
[_, _, _, _, _, D, B, B, B, B, D, _, _, _, _, _],
|
|
[_, _, _, _, _, D, B, B, B, B, D, _, _, _, _, _],
|
|
[_, _, _, _, _, D, B, B, B, B, D, _, _, _, _, _],
|
|
[_, _, _, _, D, D, B, B, B, B, D, D, _, _, _, _],
|
|
[_, _, _, _, D, B, B, B, B, B, B, D, _, _, _, _],
|
|
[_, _, _, _, D, D, D, D, D, D, D, D, _, _, _, _],
|
|
[_, _, _, _, _, _, _, _, _, _, _, _, _, _, _, _],
|
|
]
|
|
})()
|
|
|
|
/** Whiteboard: 32x16 (2 tiles wide, 1 tile tall) — hangs on wall */
|
|
export const WHITEBOARD_SPRITE: SpriteData = (() => {
|
|
const F = '#AAAAAA'
|
|
const W = '#EEEEFF'
|
|
const M = '#CC4444'
|
|
const B = '#4477AA'
|
|
return [
|
|
[_, _, _, _, _, _, _, _, _, _, _, _, _, _, _, _, _, _, _, _, _, _, _, _, _, _, _, _, _, _, _, _],
|
|
[_, F, F, F, F, F, F, F, F, F, F, F, F, F, F, F, F, F, F, F, F, F, F, F, F, F, F, F, F, F, F, _],
|
|
[_, F, W, W, W, W, W, W, W, W, W, W, W, W, W, W, W, W, W, W, W, W, W, W, W, W, W, W, W, W, F, _],
|
|
[_, F, W, W, M, M, M, W, W, W, W, W, B, B, B, B, W, W, W, W, W, W, W, M, W, W, W, W, W, W, F, _],
|
|
[_, F, W, W, W, W, W, W, W, W, W, W, W, W, W, W, W, W, W, B, B, W, W, M, W, W, W, W, W, W, F, _],
|
|
[_, F, W, W, W, W, M, M, M, M, W, W, W, W, W, W, W, W, W, W, W, W, W, W, W, W, B, B, W, W, F, _],
|
|
[_, F, W, W, W, W, W, W, W, W, W, W, W, B, B, B, W, W, W, W, W, W, W, W, W, W, W, W, W, W, F, _],
|
|
[_, F, W, W, W, W, W, W, W, W, W, W, W, W, W, W, W, W, W, W, M, M, M, W, W, W, W, W, W, W, F, _],
|
|
[_, F, W, M, M, W, W, W, W, W, W, W, W, W, W, W, B, B, W, W, W, W, W, W, W, W, W, W, W, W, F, _],
|
|
[_, F, W, W, W, W, W, W, B, B, B, W, W, W, W, W, W, W, W, W, W, W, W, W, M, M, M, M, W, W, F, _],
|
|
[_, F, W, W, W, W, W, W, W, W, W, W, W, W, W, W, W, W, W, W, W, W, W, W, W, W, W, W, W, W, F, _],
|
|
[_, F, W, W, W, W, W, W, W, W, W, W, W, W, W, W, W, W, W, W, W, W, W, W, W, W, W, W, W, W, F, _],
|
|
[_, F, F, F, F, F, F, F, F, F, F, F, F, F, F, F, F, F, F, F, F, F, F, F, F, F, F, F, F, F, F, _],
|
|
[_, _, _, _, _, _, _, _, _, _, _, _, _, _, _, _, _, _, _, _, _, _, _, _, _, _, _, _, _, _, _, _],
|
|
[_, _, _, _, _, _, _, _, _, _, _, _, _, _, _, _, _, _, _, _, _, _, _, _, _, _, _, _, _, _, _, _],
|
|
[_, _, _, _, _, _, _, _, _, _, _, _, _, _, _, _, _, _, _, _, _, _, _, _, _, _, _, _, _, _, _, _],
|
|
]
|
|
})()
|
|
|
|
/** Chair: 16x16 — top-down desk chair */
|
|
export const CHAIR_SPRITE: SpriteData = (() => {
|
|
const W = '#8B6914'
|
|
const D = '#6B4E0A'
|
|
const B = '#5C3D0A'
|
|
const S = '#A07828'
|
|
return [
|
|
[_, _, _, _, _, D, D, D, D, D, D, _, _, _, _, _],
|
|
[_, _, _, _, D, B, B, B, B, B, B, D, _, _, _, _],
|
|
[_, _, _, _, D, B, S, S, S, S, B, D, _, _, _, _],
|
|
[_, _, _, _, D, B, S, S, S, S, B, D, _, _, _, _],
|
|
[_, _, _, _, D, B, S, S, S, S, B, D, _, _, _, _],
|
|
[_, _, _, _, D, B, S, S, S, S, B, D, _, _, _, _],
|
|
[_, _, _, _, D, B, S, S, S, S, B, D, _, _, _, _],
|
|
[_, _, _, _, D, B, S, S, S, S, B, D, _, _, _, _],
|
|
[_, _, _, _, D, B, S, S, S, S, B, D, _, _, _, _],
|
|
[_, _, _, _, D, B, B, B, B, B, B, D, _, _, _, _],
|
|
[_, _, _, _, _, D, D, D, D, D, D, _, _, _, _, _],
|
|
[_, _, _, _, _, _, D, W, W, D, _, _, _, _, _, _],
|
|
[_, _, _, _, _, _, D, W, W, D, _, _, _, _, _, _],
|
|
[_, _, _, _, _, D, D, D, D, D, D, _, _, _, _, _],
|
|
[_, _, _, _, _, D, _, _, _, _, D, _, _, _, _, _],
|
|
[_, _, _, _, _, D, _, _, _, _, D, _, _, _, _, _],
|
|
]
|
|
})()
|
|
|
|
/** PC monitor: 16x16 — top-down monitor on stand */
|
|
export const PC_SPRITE: SpriteData = (() => {
|
|
const F = '#555555'
|
|
const S = '#3A3A5C'
|
|
const B = '#6688CC'
|
|
const D = '#444444'
|
|
return [
|
|
[_, _, _, F, F, F, F, F, F, F, F, F, F, _, _, _],
|
|
[_, _, _, F, S, S, S, S, S, S, S, S, F, _, _, _],
|
|
[_, _, _, F, S, B, B, B, B, B, B, S, F, _, _, _],
|
|
[_, _, _, F, S, B, B, B, B, B, B, S, F, _, _, _],
|
|
[_, _, _, F, S, B, B, B, B, B, B, S, F, _, _, _],
|
|
[_, _, _, F, S, B, B, B, B, B, B, S, F, _, _, _],
|
|
[_, _, _, F, S, B, B, B, B, B, B, S, F, _, _, _],
|
|
[_, _, _, F, S, B, B, B, B, B, B, S, F, _, _, _],
|
|
[_, _, _, F, S, S, S, S, S, S, S, S, F, _, _, _],
|
|
[_, _, _, F, F, F, F, F, F, F, F, F, F, _, _, _],
|
|
[_, _, _, _, _, _, _, D, D, _, _, _, _, _, _, _],
|
|
[_, _, _, _, _, _, _, D, D, _, _, _, _, _, _, _],
|
|
[_, _, _, _, _, _, D, D, D, D, _, _, _, _, _, _],
|
|
[_, _, _, _, _, D, D, D, D, D, D, _, _, _, _, _],
|
|
[_, _, _, _, _, D, D, D, D, D, D, _, _, _, _, _],
|
|
[_, _, _, _, _, _, _, _, _, _, _, _, _, _, _, _],
|
|
]
|
|
})()
|
|
|
|
/** PC monitor back: 16x16 — top-down monitor viewed from behind */
|
|
export const PC_BACK_SPRITE: SpriteData = (() => {
|
|
const F = '#555555'
|
|
const K = '#4A4A4A' // back panel
|
|
const D = '#444444'
|
|
return [
|
|
[_, _, _, _, _, _, _, _, _, _, _, _, _, _, _, _],
|
|
[_, _, _, _, _, D, D, D, D, D, D, _, _, _, _, _],
|
|
[_, _, _, _, _, D, D, D, D, D, D, _, _, _, _, _],
|
|
[_, _, _, _, _, _, D, D, D, D, _, _, _, _, _, _],
|
|
[_, _, _, _, _, _, _, D, D, _, _, _, _, _, _, _],
|
|
[_, _, _, _, _, _, _, D, D, _, _, _, _, _, _, _],
|
|
[_, _, _, F, F, F, F, F, F, F, F, F, F, _, _, _],
|
|
[_, _, _, F, K, K, K, K, K, K, K, K, F, _, _, _],
|
|
[_, _, _, F, K, K, K, K, K, K, K, K, F, _, _, _],
|
|
[_, _, _, F, K, K, K, K, K, K, K, K, F, _, _, _],
|
|
[_, _, _, F, K, K, K, K, K, K, K, K, F, _, _, _],
|
|
[_, _, _, F, K, K, K, K, K, K, K, K, F, _, _, _],
|
|
[_, _, _, F, K, K, K, K, K, K, K, K, F, _, _, _],
|
|
[_, _, _, F, K, K, K, K, K, K, K, K, F, _, _, _],
|
|
[_, _, _, F, F, F, F, F, F, F, F, F, F, _, _, _],
|
|
[_, _, _, _, _, _, _, _, _, _, _, _, _, _, _, _],
|
|
]
|
|
})()
|
|
|
|
/** Sony mirrorless camera: 16x16 — top-down view */
|
|
export const CAMERA_SPRITE: SpriteData = (() => {
|
|
const B = '#222222' // body
|
|
const G = '#333333' // grip
|
|
const L = '#444466' // lens ring
|
|
const R = '#5566AA' // lens glass
|
|
const S = '#888888' // silver accent
|
|
const D = '#1A1A1A' // dark
|
|
return [
|
|
[_, _, _, _, _, _, _, _, _, _, _, _, _, _, _, _],
|
|
[_, _, _, _, _, _, _, _, _, _, _, _, _, _, _, _],
|
|
[_, _, _, _, _, B, B, B, B, B, B, B, _, _, _, _],
|
|
[_, _, _, G, G, B, S, S, S, S, S, B, B, _, _, _],
|
|
[_, _, _, G, G, B, B, B, B, B, B, B, B, _, _, _],
|
|
[_, _, _, G, G, B, B, L, L, L, B, B, B, _, _, _],
|
|
[_, _, _, G, G, B, L, R, R, R, L, B, B, _, _, _],
|
|
[_, _, _, G, G, B, L, R, R, R, L, B, B, _, _, _],
|
|
[_, _, _, G, G, B, L, R, R, R, L, B, B, _, _, _],
|
|
[_, _, _, G, G, B, B, L, L, L, B, B, B, _, _, _],
|
|
[_, _, _, G, G, B, B, B, B, B, B, B, B, _, _, _],
|
|
[_, _, _, D, D, B, B, B, B, B, B, B, D, _, _, _],
|
|
[_, _, _, _, _, _, _, _, _, _, _, _, _, _, _, _],
|
|
[_, _, _, _, _, _, _, _, _, _, _, _, _, _, _, _],
|
|
[_, _, _, _, _, _, _, _, _, _, _, _, _, _, _, _],
|
|
[_, _, _, _, _, _, _, _, _, _, _, _, _, _, _, _],
|
|
]
|
|
})()
|
|
|
|
/** Desk lamp: 16x16 — top-down lamp with light cone */
|
|
export const LAMP_SPRITE: SpriteData = (() => {
|
|
const Y = '#FFDD55'
|
|
const L = '#FFEE88'
|
|
const D = '#888888'
|
|
const B = '#555555'
|
|
const G = '#FFFFCC'
|
|
return [
|
|
[_, _, _, _, _, _, _, _, _, _, _, _, _, _, _, _],
|
|
[_, _, _, _, _, _, G, G, G, G, _, _, _, _, _, _],
|
|
[_, _, _, _, _, G, Y, Y, Y, Y, G, _, _, _, _, _],
|
|
[_, _, _, _, G, Y, Y, L, L, Y, Y, G, _, _, _, _],
|
|
[_, _, _, _, Y, Y, L, L, L, L, Y, Y, _, _, _, _],
|
|
[_, _, _, _, Y, Y, L, L, L, L, Y, Y, _, _, _, _],
|
|
[_, _, _, _, _, Y, Y, Y, Y, Y, Y, _, _, _, _, _],
|
|
[_, _, _, _, _, _, D, D, D, D, _, _, _, _, _, _],
|
|
[_, _, _, _, _, _, _, D, D, _, _, _, _, _, _, _],
|
|
[_, _, _, _, _, _, _, D, D, _, _, _, _, _, _, _],
|
|
[_, _, _, _, _, _, _, D, D, _, _, _, _, _, _, _],
|
|
[_, _, _, _, _, _, _, D, D, _, _, _, _, _, _, _],
|
|
[_, _, _, _, _, _, D, D, D, D, _, _, _, _, _, _],
|
|
[_, _, _, _, _, B, B, B, B, B, B, _, _, _, _, _],
|
|
[_, _, _, _, _, B, B, B, B, B, B, _, _, _, _, _],
|
|
[_, _, _, _, _, _, _, _, _, _, _, _, _, _, _, _],
|
|
]
|
|
})()
|
|
|
|
// ── Speech Bubble Sprites ───────────────────────────────────────
|
|
|
|
/** Permission bubble: white square with "..." in amber, and a tail pointer (11x13) */
|
|
export const BUBBLE_PERMISSION_SPRITE: SpriteData = (() => {
|
|
const B = '#555566' // border
|
|
const F = '#EEEEFF' // fill
|
|
const A = '#CCA700' // amber dots
|
|
return [
|
|
[B, B, B, B, B, B, B, B, B, B, B],
|
|
[B, F, F, F, F, F, F, F, F, F, B],
|
|
[B, F, F, F, F, F, F, F, F, F, B],
|
|
[B, F, F, F, F, F, F, F, F, F, B],
|
|
[B, F, F, F, F, F, F, F, F, F, B],
|
|
[B, F, F, A, F, A, F, A, F, F, B],
|
|
[B, F, F, F, F, F, F, F, F, F, B],
|
|
[B, F, F, F, F, F, F, F, F, F, B],
|
|
[B, F, F, F, F, F, F, F, F, F, B],
|
|
[B, B, B, B, B, B, B, B, B, B, B],
|
|
[_, _, _, _, B, B, B, _, _, _, _],
|
|
[_, _, _, _, _, B, _, _, _, _, _],
|
|
[_, _, _, _, _, _, _, _, _, _, _],
|
|
]
|
|
})()
|
|
|
|
/** Waiting bubble: white square with green checkmark, and a tail pointer (11x13) */
|
|
export const BUBBLE_WAITING_SPRITE: SpriteData = (() => {
|
|
const B = '#555566' // border
|
|
const F = '#EEEEFF' // fill
|
|
const G = '#44BB66' // green check
|
|
return [
|
|
[_, B, B, B, B, B, B, B, B, B, _],
|
|
[B, F, F, F, F, F, F, F, F, F, B],
|
|
[B, F, F, F, F, F, F, F, F, F, B],
|
|
[B, F, F, F, F, F, F, F, G, F, B],
|
|
[B, F, F, F, F, F, F, G, F, F, B],
|
|
[B, F, F, G, F, F, G, F, F, F, B],
|
|
[B, F, F, F, G, G, F, F, F, F, B],
|
|
[B, F, F, F, F, F, F, F, F, F, B],
|
|
[B, F, F, F, F, F, F, F, F, F, B],
|
|
[_, B, B, B, B, B, B, B, B, B, _],
|
|
[_, _, _, _, B, B, B, _, _, _, _],
|
|
[_, _, _, _, _, B, _, _, _, _, _],
|
|
[_, _, _, _, _, _, _, _, _, _, _],
|
|
]
|
|
})()
|
|
|
|
// ── Character Sprites ───────────────────────────────────────────
|
|
// 16x24 characters with palette substitution
|
|
|
|
/** Palette colors for 6 distinct agent characters */
|
|
export const CHARACTER_PALETTES = [
|
|
{ skin: '#FFCC99', shirt: '#4488CC', pants: '#334466', hair: '#553322', shoes: '#222222' },
|
|
{ skin: '#FFCC99', shirt: '#CC4444', pants: '#333333', hair: '#FFD700', shoes: '#222222' },
|
|
{ skin: '#DEB887', shirt: '#44AA66', pants: '#334444', hair: '#222222', shoes: '#333333' },
|
|
{ skin: '#FFCC99', shirt: '#AA55CC', pants: '#443355', hair: '#AA4422', shoes: '#222222' },
|
|
{ skin: '#DEB887', shirt: '#CCAA33', pants: '#444433', hair: '#553322', shoes: '#333333' },
|
|
{ skin: '#FFCC99', shirt: '#FF8844', pants: '#443322', hair: '#111111', shoes: '#222222' },
|
|
] as const
|
|
|
|
interface CharPalette {
|
|
skin: string
|
|
shirt: string
|
|
pants: string
|
|
hair: string
|
|
shoes: string
|
|
}
|
|
|
|
// Template keys for character pixel data
|
|
const H = 'hair'
|
|
const K = 'skin'
|
|
const S = 'shirt'
|
|
const P = 'pants'
|
|
const O = 'shoes'
|
|
const E = '#FFFFFF' // eyes
|
|
|
|
type TemplateCell = typeof H | typeof K | typeof S | typeof P | typeof O | typeof E | typeof _
|
|
|
|
/** Resolve a template to SpriteData using a palette */
|
|
function resolveTemplate(template: TemplateCell[][], palette: CharPalette): SpriteData {
|
|
return template.map((row) =>
|
|
row.map((cell) => {
|
|
if (cell === _) return ''
|
|
if (cell === E) return E
|
|
if (cell === H) return palette.hair
|
|
if (cell === K) return palette.skin
|
|
if (cell === S) return palette.shirt
|
|
if (cell === P) return palette.pants
|
|
if (cell === O) return palette.shoes
|
|
return cell
|
|
}),
|
|
)
|
|
}
|
|
|
|
/** Flip a template horizontally (for generating left sprites from right) */
|
|
function flipHorizontal(template: TemplateCell[][]): TemplateCell[][] {
|
|
return template.map((row) => [...row].reverse())
|
|
}
|
|
|
|
// ════════════════════════════════════════════════════════════════
|
|
// DOWN-FACING SPRITES
|
|
// ════════════════════════════════════════════════════════════════
|
|
|
|
// Walk down: 4 frames (1, 2=standing, 3=mirror legs, 2 again)
|
|
const CHAR_WALK_DOWN_1: TemplateCell[][] = [
|
|
[_, _, _, _, _, _, _, _, _, _, _, _, _, _, _, _],
|
|
[_, _, _, _, _, _, H, H, H, H, _, _, _, _, _, _],
|
|
[_, _, _, _, _, H, H, H, H, H, H, _, _, _, _, _],
|
|
[_, _, _, _, _, H, H, H, H, H, H, _, _, _, _, _],
|
|
[_, _, _, _, _, K, K, K, K, K, K, _, _, _, _, _],
|
|
[_, _, _, _, _, K, E, K, K, E, K, _, _, _, _, _],
|
|
[_, _, _, _, _, K, K, K, K, K, K, _, _, _, _, _],
|
|
[_, _, _, _, _, K, K, K, K, K, K, _, _, _, _, _],
|
|
[_, _, _, _, _, _, S, S, S, S, _, _, _, _, _, _],
|
|
[_, _, _, _, _, S, S, S, S, S, S, _, _, _, _, _],
|
|
[_, _, _, _, S, S, S, S, S, S, S, S, _, _, _, _],
|
|
[_, _, _, _, S, S, S, S, S, S, S, S, _, _, _, _],
|
|
[_, _, _, _, K, S, S, S, S, S, S, K, _, _, _, _],
|
|
[_, _, _, _, _, S, S, S, S, S, S, _, _, _, _, _],
|
|
[_, _, _, _, _, _, P, P, P, P, _, _, _, _, _, _],
|
|
[_, _, _, _, _, P, P, P, P, P, P, _, _, _, _, _],
|
|
[_, _, _, _, _, P, P, P, P, P, P, _, _, _, _, _],
|
|
[_, _, _, _, P, P, _, _, _, _, P, P, _, _, _, _],
|
|
[_, _, _, _, P, P, _, _, _, _, P, P, _, _, _, _],
|
|
[_, _, _, _, O, O, _, _, _, _, _, O, O, _, _, _],
|
|
[_, _, _, _, O, O, _, _, _, _, _, O, O, _, _, _],
|
|
[_, _, _, _, _, _, _, _, _, _, _, _, _, _, _, _],
|
|
[_, _, _, _, _, _, _, _, _, _, _, _, _, _, _, _],
|
|
[_, _, _, _, _, _, _, _, _, _, _, _, _, _, _, _],
|
|
]
|
|
|
|
const CHAR_WALK_DOWN_2: TemplateCell[][] = [
|
|
[_, _, _, _, _, _, _, _, _, _, _, _, _, _, _, _],
|
|
[_, _, _, _, _, _, _, _, _, _, _, _, _, _, _, _],
|
|
[_, _, _, _, _, _, H, H, H, H, _, _, _, _, _, _],
|
|
[_, _, _, _, _, H, H, H, H, H, H, _, _, _, _, _],
|
|
[_, _, _, _, _, H, H, H, H, H, H, _, _, _, _, _],
|
|
[_, _, _, _, _, K, K, K, K, K, K, _, _, _, _, _],
|
|
[_, _, _, _, _, K, E, K, K, E, K, _, _, _, _, _],
|
|
[_, _, _, _, _, K, K, K, K, K, K, _, _, _, _, _],
|
|
[_, _, _, _, _, K, K, K, K, K, K, _, _, _, _, _],
|
|
[_, _, _, _, _, _, S, S, S, S, _, _, _, _, _, _],
|
|
[_, _, _, _, _, S, S, S, S, S, S, _, _, _, _, _],
|
|
[_, _, _, _, S, S, S, S, S, S, S, S, _, _, _, _],
|
|
[_, _, _, _, S, S, S, S, S, S, S, S, _, _, _, _],
|
|
[_, _, _, _, K, S, S, S, S, S, S, K, _, _, _, _],
|
|
[_, _, _, _, _, S, S, S, S, S, S, _, _, _, _, _],
|
|
[_, _, _, _, _, _, P, P, P, P, _, _, _, _, _, _],
|
|
[_, _, _, _, _, P, P, P, P, P, P, _, _, _, _, _],
|
|
[_, _, _, _, _, P, P, _, _, P, P, _, _, _, _, _],
|
|
[_, _, _, _, _, P, P, _, _, P, P, _, _, _, _, _],
|
|
[_, _, _, _, _, P, P, _, _, P, P, _, _, _, _, _],
|
|
[_, _, _, _, _, O, O, _, _, O, O, _, _, _, _, _],
|
|
[_, _, _, _, _, O, O, _, _, O, O, _, _, _, _, _],
|
|
[_, _, _, _, _, _, _, _, _, _, _, _, _, _, _, _],
|
|
[_, _, _, _, _, _, _, _, _, _, _, _, _, _, _, _],
|
|
]
|
|
|
|
const CHAR_WALK_DOWN_3: TemplateCell[][] = [
|
|
[_, _, _, _, _, _, _, _, _, _, _, _, _, _, _, _],
|
|
[_, _, _, _, _, _, H, H, H, H, _, _, _, _, _, _],
|
|
[_, _, _, _, _, H, H, H, H, H, H, _, _, _, _, _],
|
|
[_, _, _, _, _, H, H, H, H, H, H, _, _, _, _, _],
|
|
[_, _, _, _, _, K, K, K, K, K, K, _, _, _, _, _],
|
|
[_, _, _, _, _, K, E, K, K, E, K, _, _, _, _, _],
|
|
[_, _, _, _, _, K, K, K, K, K, K, _, _, _, _, _],
|
|
[_, _, _, _, _, K, K, K, K, K, K, _, _, _, _, _],
|
|
[_, _, _, _, _, _, S, S, S, S, _, _, _, _, _, _],
|
|
[_, _, _, _, _, S, S, S, S, S, S, _, _, _, _, _],
|
|
[_, _, _, _, S, S, S, S, S, S, S, S, _, _, _, _],
|
|
[_, _, _, _, S, S, S, S, S, S, S, S, _, _, _, _],
|
|
[_, _, _, _, K, S, S, S, S, S, S, K, _, _, _, _],
|
|
[_, _, _, _, _, S, S, S, S, S, S, _, _, _, _, _],
|
|
[_, _, _, _, _, _, P, P, P, P, _, _, _, _, _, _],
|
|
[_, _, _, _, _, P, P, P, P, P, P, _, _, _, _, _],
|
|
[_, _, _, _, _, P, P, P, P, P, P, _, _, _, _, _],
|
|
[_, _, _, O, O, _, _, _, _, _, _, P, P, _, _, _],
|
|
[_, _, _, O, O, _, _, _, _, _, _, P, P, _, _, _],
|
|
[_, _, _, _, _, _, _, _, _, _, O, O, _, _, _, _],
|
|
[_, _, _, _, _, _, _, _, _, _, O, O, _, _, _, _],
|
|
[_, _, _, _, _, _, _, _, _, _, _, _, _, _, _, _],
|
|
[_, _, _, _, _, _, _, _, _, _, _, _, _, _, _, _],
|
|
[_, _, _, _, _, _, _, _, _, _, _, _, _, _, _, _],
|
|
]
|
|
|
|
// Down typing: front-facing sitting, arms on keyboard
|
|
const CHAR_DOWN_TYPE_1: TemplateCell[][] = [
|
|
[_, _, _, _, _, _, _, _, _, _, _, _, _, _, _, _],
|
|
[_, _, _, _, _, _, _, _, _, _, _, _, _, _, _, _],
|
|
[_, _, _, _, _, _, H, H, H, H, _, _, _, _, _, _],
|
|
[_, _, _, _, _, H, H, H, H, H, H, _, _, _, _, _],
|
|
[_, _, _, _, _, H, H, H, H, H, H, _, _, _, _, _],
|
|
[_, _, _, _, _, K, K, K, K, K, K, _, _, _, _, _],
|
|
[_, _, _, _, _, K, E, K, K, E, K, _, _, _, _, _],
|
|
[_, _, _, _, _, K, K, K, K, K, K, _, _, _, _, _],
|
|
[_, _, _, _, _, K, K, K, K, K, K, _, _, _, _, _],
|
|
[_, _, _, _, _, _, S, S, S, S, _, _, _, _, _, _],
|
|
[_, _, _, _, _, S, S, S, S, S, S, _, _, _, _, _],
|
|
[_, _, _, _, S, S, S, S, S, S, S, S, _, _, _, _],
|
|
[_, _, _, K, K, S, S, S, S, S, S, K, K, _, _, _],
|
|
[_, _, _, _, K, S, S, S, S, S, S, K, _, _, _, _],
|
|
[_, _, _, _, _, S, S, S, S, S, S, _, _, _, _, _],
|
|
[_, _, _, _, _, _, P, P, P, P, _, _, _, _, _, _],
|
|
[_, _, _, _, _, P, P, P, P, P, P, _, _, _, _, _],
|
|
[_, _, _, _, _, P, P, P, P, P, P, _, _, _, _, _],
|
|
[_, _, _, _, _, P, P, _, _, P, P, _, _, _, _, _],
|
|
[_, _, _, _, _, O, O, _, _, O, O, _, _, _, _, _],
|
|
[_, _, _, _, _, _, _, _, _, _, _, _, _, _, _, _],
|
|
[_, _, _, _, _, _, _, _, _, _, _, _, _, _, _, _],
|
|
[_, _, _, _, _, _, _, _, _, _, _, _, _, _, _, _],
|
|
[_, _, _, _, _, _, _, _, _, _, _, _, _, _, _, _],
|
|
]
|
|
|
|
const CHAR_DOWN_TYPE_2: TemplateCell[][] = [
|
|
[_, _, _, _, _, _, _, _, _, _, _, _, _, _, _, _],
|
|
[_, _, _, _, _, _, _, _, _, _, _, _, _, _, _, _],
|
|
[_, _, _, _, _, _, H, H, H, H, _, _, _, _, _, _],
|
|
[_, _, _, _, _, H, H, H, H, H, H, _, _, _, _, _],
|
|
[_, _, _, _, _, H, H, H, H, H, H, _, _, _, _, _],
|
|
[_, _, _, _, _, K, K, K, K, K, K, _, _, _, _, _],
|
|
[_, _, _, _, _, K, E, K, K, E, K, _, _, _, _, _],
|
|
[_, _, _, _, _, K, K, K, K, K, K, _, _, _, _, _],
|
|
[_, _, _, _, _, K, K, K, K, K, K, _, _, _, _, _],
|
|
[_, _, _, _, _, _, S, S, S, S, _, _, _, _, _, _],
|
|
[_, _, _, _, _, S, S, S, S, S, S, _, _, _, _, _],
|
|
[_, _, _, _, S, S, S, S, S, S, S, S, _, _, _, _],
|
|
[_, _, _, _, K, S, S, S, S, S, S, K, K, _, _, _],
|
|
[_, _, _, _, K, S, S, S, S, S, S, _, K, _, _, _],
|
|
[_, _, _, _, _, S, S, S, S, S, S, _, _, _, _, _],
|
|
[_, _, _, _, _, _, P, P, P, P, _, _, _, _, _, _],
|
|
[_, _, _, _, _, P, P, P, P, P, P, _, _, _, _, _],
|
|
[_, _, _, _, _, P, P, P, P, P, P, _, _, _, _, _],
|
|
[_, _, _, _, _, P, P, _, _, P, P, _, _, _, _, _],
|
|
[_, _, _, _, _, O, O, _, _, O, O, _, _, _, _, _],
|
|
[_, _, _, _, _, _, _, _, _, _, _, _, _, _, _, _],
|
|
[_, _, _, _, _, _, _, _, _, _, _, _, _, _, _, _],
|
|
[_, _, _, _, _, _, _, _, _, _, _, _, _, _, _, _],
|
|
[_, _, _, _, _, _, _, _, _, _, _, _, _, _, _, _],
|
|
]
|
|
|
|
// Down reading: front-facing sitting, arms at sides, looking at screen
|
|
const CHAR_DOWN_READ_1: TemplateCell[][] = [
|
|
[_, _, _, _, _, _, _, _, _, _, _, _, _, _, _, _],
|
|
[_, _, _, _, _, _, _, _, _, _, _, _, _, _, _, _],
|
|
[_, _, _, _, _, _, H, H, H, H, _, _, _, _, _, _],
|
|
[_, _, _, _, _, H, H, H, H, H, H, _, _, _, _, _],
|
|
[_, _, _, _, _, H, H, H, H, H, H, _, _, _, _, _],
|
|
[_, _, _, _, _, K, K, K, K, K, K, _, _, _, _, _],
|
|
[_, _, _, _, _, K, E, K, K, E, K, _, _, _, _, _],
|
|
[_, _, _, _, _, K, K, K, K, K, K, _, _, _, _, _],
|
|
[_, _, _, _, _, K, K, K, K, K, K, _, _, _, _, _],
|
|
[_, _, _, _, _, _, S, S, S, S, _, _, _, _, _, _],
|
|
[_, _, _, _, _, S, S, S, S, S, S, _, _, _, _, _],
|
|
[_, _, _, _, S, S, S, S, S, S, S, S, _, _, _, _],
|
|
[_, _, _, _, S, S, S, S, S, S, S, S, _, _, _, _],
|
|
[_, _, _, _, K, S, S, S, S, S, S, K, _, _, _, _],
|
|
[_, _, _, _, _, S, S, S, S, S, S, _, _, _, _, _],
|
|
[_, _, _, _, _, _, P, P, P, P, _, _, _, _, _, _],
|
|
[_, _, _, _, _, P, P, P, P, P, P, _, _, _, _, _],
|
|
[_, _, _, _, _, P, P, P, P, P, P, _, _, _, _, _],
|
|
[_, _, _, _, _, P, P, _, _, P, P, _, _, _, _, _],
|
|
[_, _, _, _, _, O, O, _, _, O, O, _, _, _, _, _],
|
|
[_, _, _, _, _, _, _, _, _, _, _, _, _, _, _, _],
|
|
[_, _, _, _, _, _, _, _, _, _, _, _, _, _, _, _],
|
|
[_, _, _, _, _, _, _, _, _, _, _, _, _, _, _, _],
|
|
[_, _, _, _, _, _, _, _, _, _, _, _, _, _, _, _],
|
|
]
|
|
|
|
const CHAR_DOWN_READ_2: TemplateCell[][] = [
|
|
[_, _, _, _, _, _, _, _, _, _, _, _, _, _, _, _],
|
|
[_, _, _, _, _, _, _, _, _, _, _, _, _, _, _, _],
|
|
[_, _, _, _, _, _, _, _, _, _, _, _, _, _, _, _],
|
|
[_, _, _, _, _, _, H, H, H, H, _, _, _, _, _, _],
|
|
[_, _, _, _, _, H, H, H, H, H, H, _, _, _, _, _],
|
|
[_, _, _, _, _, H, H, H, H, H, H, _, _, _, _, _],
|
|
[_, _, _, _, _, K, K, K, K, K, K, _, _, _, _, _],
|
|
[_, _, _, _, _, K, E, K, K, E, K, _, _, _, _, _],
|
|
[_, _, _, _, _, K, K, K, K, K, K, _, _, _, _, _],
|
|
[_, _, _, _, _, K, K, K, K, K, K, _, _, _, _, _],
|
|
[_, _, _, _, _, _, S, S, S, S, _, _, _, _, _, _],
|
|
[_, _, _, _, _, S, S, S, S, S, S, _, _, _, _, _],
|
|
[_, _, _, _, S, S, S, S, S, S, S, S, _, _, _, _],
|
|
[_, _, _, _, S, S, S, S, S, S, S, S, _, _, _, _],
|
|
[_, _, _, _, K, S, S, S, S, S, S, K, _, _, _, _],
|
|
[_, _, _, _, _, S, S, S, S, S, S, _, _, _, _, _],
|
|
[_, _, _, _, _, _, P, P, P, P, _, _, _, _, _, _],
|
|
[_, _, _, _, _, P, P, P, P, P, P, _, _, _, _, _],
|
|
[_, _, _, _, _, P, P, _, _, P, P, _, _, _, _, _],
|
|
[_, _, _, _, _, O, O, _, _, O, O, _, _, _, _, _],
|
|
[_, _, _, _, _, _, _, _, _, _, _, _, _, _, _, _],
|
|
[_, _, _, _, _, _, _, _, _, _, _, _, _, _, _, _],
|
|
[_, _, _, _, _, _, _, _, _, _, _, _, _, _, _, _],
|
|
[_, _, _, _, _, _, _, _, _, _, _, _, _, _, _, _],
|
|
]
|
|
|
|
// ════════════════════════════════════════════════════════════════
|
|
// UP-FACING SPRITES (back of head, no face)
|
|
// ════════════════════════════════════════════════════════════════
|
|
|
|
// Walk up: back view, legs alternate
|
|
const CHAR_WALK_UP_1: TemplateCell[][] = [
|
|
[_, _, _, _, _, _, _, _, _, _, _, _, _, _, _, _],
|
|
[_, _, _, _, _, _, H, H, H, H, _, _, _, _, _, _],
|
|
[_, _, _, _, _, H, H, H, H, H, H, _, _, _, _, _],
|
|
[_, _, _, _, _, H, H, H, H, H, H, _, _, _, _, _],
|
|
[_, _, _, _, _, H, H, H, H, H, H, _, _, _, _, _],
|
|
[_, _, _, _, _, K, K, K, K, K, K, _, _, _, _, _],
|
|
[_, _, _, _, _, K, K, K, K, K, K, _, _, _, _, _],
|
|
[_, _, _, _, _, K, K, K, K, K, K, _, _, _, _, _],
|
|
[_, _, _, _, _, _, S, S, S, S, _, _, _, _, _, _],
|
|
[_, _, _, _, _, S, S, S, S, S, S, _, _, _, _, _],
|
|
[_, _, _, _, S, S, S, S, S, S, S, S, _, _, _, _],
|
|
[_, _, _, _, S, S, S, S, S, S, S, S, _, _, _, _],
|
|
[_, _, _, _, K, S, S, S, S, S, S, K, _, _, _, _],
|
|
[_, _, _, _, _, S, S, S, S, S, S, _, _, _, _, _],
|
|
[_, _, _, _, _, _, P, P, P, P, _, _, _, _, _, _],
|
|
[_, _, _, _, _, P, P, P, P, P, P, _, _, _, _, _],
|
|
[_, _, _, _, _, P, P, P, P, P, P, _, _, _, _, _],
|
|
[_, _, _, _, P, P, _, _, _, _, P, P, _, _, _, _],
|
|
[_, _, _, _, P, P, _, _, _, _, P, P, _, _, _, _],
|
|
[_, _, _, O, O, _, _, _, _, _, _, O, O, _, _, _],
|
|
[_, _, _, O, O, _, _, _, _, _, _, O, O, _, _, _],
|
|
[_, _, _, _, _, _, _, _, _, _, _, _, _, _, _, _],
|
|
[_, _, _, _, _, _, _, _, _, _, _, _, _, _, _, _],
|
|
[_, _, _, _, _, _, _, _, _, _, _, _, _, _, _, _],
|
|
]
|
|
|
|
const CHAR_WALK_UP_2: TemplateCell[][] = [
|
|
[_, _, _, _, _, _, _, _, _, _, _, _, _, _, _, _],
|
|
[_, _, _, _, _, _, _, _, _, _, _, _, _, _, _, _],
|
|
[_, _, _, _, _, _, H, H, H, H, _, _, _, _, _, _],
|
|
[_, _, _, _, _, H, H, H, H, H, H, _, _, _, _, _],
|
|
[_, _, _, _, _, H, H, H, H, H, H, _, _, _, _, _],
|
|
[_, _, _, _, _, H, H, H, H, H, H, _, _, _, _, _],
|
|
[_, _, _, _, _, K, K, K, K, K, K, _, _, _, _, _],
|
|
[_, _, _, _, _, K, K, K, K, K, K, _, _, _, _, _],
|
|
[_, _, _, _, _, K, K, K, K, K, K, _, _, _, _, _],
|
|
[_, _, _, _, _, _, S, S, S, S, _, _, _, _, _, _],
|
|
[_, _, _, _, _, S, S, S, S, S, S, _, _, _, _, _],
|
|
[_, _, _, _, S, S, S, S, S, S, S, S, _, _, _, _],
|
|
[_, _, _, _, S, S, S, S, S, S, S, S, _, _, _, _],
|
|
[_, _, _, _, K, S, S, S, S, S, S, K, _, _, _, _],
|
|
[_, _, _, _, _, S, S, S, S, S, S, _, _, _, _, _],
|
|
[_, _, _, _, _, _, P, P, P, P, _, _, _, _, _, _],
|
|
[_, _, _, _, _, P, P, P, P, P, P, _, _, _, _, _],
|
|
[_, _, _, _, _, P, P, _, _, P, P, _, _, _, _, _],
|
|
[_, _, _, _, _, P, P, _, _, P, P, _, _, _, _, _],
|
|
[_, _, _, _, _, P, P, _, _, P, P, _, _, _, _, _],
|
|
[_, _, _, _, _, O, O, _, _, O, O, _, _, _, _, _],
|
|
[_, _, _, _, _, O, O, _, _, O, O, _, _, _, _, _],
|
|
[_, _, _, _, _, _, _, _, _, _, _, _, _, _, _, _],
|
|
[_, _, _, _, _, _, _, _, _, _, _, _, _, _, _, _],
|
|
]
|
|
|
|
const CHAR_WALK_UP_3: TemplateCell[][] = [
|
|
[_, _, _, _, _, _, _, _, _, _, _, _, _, _, _, _],
|
|
[_, _, _, _, _, _, H, H, H, H, _, _, _, _, _, _],
|
|
[_, _, _, _, _, H, H, H, H, H, H, _, _, _, _, _],
|
|
[_, _, _, _, _, H, H, H, H, H, H, _, _, _, _, _],
|
|
[_, _, _, _, _, H, H, H, H, H, H, _, _, _, _, _],
|
|
[_, _, _, _, _, K, K, K, K, K, K, _, _, _, _, _],
|
|
[_, _, _, _, _, K, K, K, K, K, K, _, _, _, _, _],
|
|
[_, _, _, _, _, K, K, K, K, K, K, _, _, _, _, _],
|
|
[_, _, _, _, _, _, S, S, S, S, _, _, _, _, _, _],
|
|
[_, _, _, _, _, S, S, S, S, S, S, _, _, _, _, _],
|
|
[_, _, _, _, S, S, S, S, S, S, S, S, _, _, _, _],
|
|
[_, _, _, _, S, S, S, S, S, S, S, S, _, _, _, _],
|
|
[_, _, _, _, K, S, S, S, S, S, S, K, _, _, _, _],
|
|
[_, _, _, _, _, S, S, S, S, S, S, _, _, _, _, _],
|
|
[_, _, _, _, _, _, P, P, P, P, _, _, _, _, _, _],
|
|
[_, _, _, _, _, P, P, P, P, P, P, _, _, _, _, _],
|
|
[_, _, _, _, _, P, P, P, P, P, P, _, _, _, _, _],
|
|
[_, _, _, O, O, _, _, _, _, _, _, P, P, _, _, _],
|
|
[_, _, _, O, O, _, _, _, _, _, _, P, P, _, _, _],
|
|
[_, _, _, _, _, _, _, _, _, _, O, O, _, _, _, _],
|
|
[_, _, _, _, _, _, _, _, _, _, O, O, _, _, _, _],
|
|
[_, _, _, _, _, _, _, _, _, _, _, _, _, _, _, _],
|
|
[_, _, _, _, _, _, _, _, _, _, _, _, _, _, _, _],
|
|
[_, _, _, _, _, _, _, _, _, _, _, _, _, _, _, _],
|
|
]
|
|
|
|
// Up typing: back view, arms out to keyboard
|
|
const CHAR_UP_TYPE_1: TemplateCell[][] = [
|
|
[_, _, _, _, _, _, _, _, _, _, _, _, _, _, _, _],
|
|
[_, _, _, _, _, _, _, _, _, _, _, _, _, _, _, _],
|
|
[_, _, _, _, _, _, H, H, H, H, _, _, _, _, _, _],
|
|
[_, _, _, _, _, H, H, H, H, H, H, _, _, _, _, _],
|
|
[_, _, _, _, _, H, H, H, H, H, H, _, _, _, _, _],
|
|
[_, _, _, _, _, H, H, H, H, H, H, _, _, _, _, _],
|
|
[_, _, _, _, _, K, K, K, K, K, K, _, _, _, _, _],
|
|
[_, _, _, _, _, K, K, K, K, K, K, _, _, _, _, _],
|
|
[_, _, _, _, _, _, S, S, S, S, _, _, _, _, _, _],
|
|
[_, _, _, _, _, S, S, S, S, S, S, _, _, _, _, _],
|
|
[_, _, _, _, S, S, S, S, S, S, S, S, _, _, _, _],
|
|
[_, _, _, K, K, S, S, S, S, S, S, K, K, _, _, _],
|
|
[_, _, _, _, K, S, S, S, S, S, S, K, _, _, _, _],
|
|
[_, _, _, _, _, S, S, S, S, S, S, _, _, _, _, _],
|
|
[_, _, _, _, _, _, P, P, P, P, _, _, _, _, _, _],
|
|
[_, _, _, _, _, P, P, P, P, P, P, _, _, _, _, _],
|
|
[_, _, _, _, _, P, P, P, P, P, P, _, _, _, _, _],
|
|
[_, _, _, _, _, P, P, _, _, P, P, _, _, _, _, _],
|
|
[_, _, _, _, _, O, O, _, _, O, O, _, _, _, _, _],
|
|
[_, _, _, _, _, _, _, _, _, _, _, _, _, _, _, _],
|
|
[_, _, _, _, _, _, _, _, _, _, _, _, _, _, _, _],
|
|
[_, _, _, _, _, _, _, _, _, _, _, _, _, _, _, _],
|
|
[_, _, _, _, _, _, _, _, _, _, _, _, _, _, _, _],
|
|
[_, _, _, _, _, _, _, _, _, _, _, _, _, _, _, _],
|
|
]
|
|
|
|
const CHAR_UP_TYPE_2: TemplateCell[][] = [
|
|
[_, _, _, _, _, _, _, _, _, _, _, _, _, _, _, _],
|
|
[_, _, _, _, _, _, _, _, _, _, _, _, _, _, _, _],
|
|
[_, _, _, _, _, _, H, H, H, H, _, _, _, _, _, _],
|
|
[_, _, _, _, _, H, H, H, H, H, H, _, _, _, _, _],
|
|
[_, _, _, _, _, H, H, H, H, H, H, _, _, _, _, _],
|
|
[_, _, _, _, _, H, H, H, H, H, H, _, _, _, _, _],
|
|
[_, _, _, _, _, K, K, K, K, K, K, _, _, _, _, _],
|
|
[_, _, _, _, _, K, K, K, K, K, K, _, _, _, _, _],
|
|
[_, _, _, _, _, _, S, S, S, S, _, _, _, _, _, _],
|
|
[_, _, _, _, _, S, S, S, S, S, S, _, _, _, _, _],
|
|
[_, _, _, _, S, S, S, S, S, S, S, S, _, _, _, _],
|
|
[_, _, _, _, K, S, S, S, S, S, S, K, K, _, _, _],
|
|
[_, _, _, _, K, S, S, S, S, S, S, _, K, _, _, _],
|
|
[_, _, _, _, _, S, S, S, S, S, S, _, _, _, _, _],
|
|
[_, _, _, _, _, _, P, P, P, P, _, _, _, _, _, _],
|
|
[_, _, _, _, _, P, P, P, P, P, P, _, _, _, _, _],
|
|
[_, _, _, _, _, P, P, P, P, P, P, _, _, _, _, _],
|
|
[_, _, _, _, _, P, P, _, _, P, P, _, _, _, _, _],
|
|
[_, _, _, _, _, O, O, _, _, O, O, _, _, _, _, _],
|
|
[_, _, _, _, _, _, _, _, _, _, _, _, _, _, _, _],
|
|
[_, _, _, _, _, _, _, _, _, _, _, _, _, _, _, _],
|
|
[_, _, _, _, _, _, _, _, _, _, _, _, _, _, _, _],
|
|
[_, _, _, _, _, _, _, _, _, _, _, _, _, _, _, _],
|
|
[_, _, _, _, _, _, _, _, _, _, _, _, _, _, _, _],
|
|
]
|
|
|
|
// Up reading: back view, arms at sides
|
|
const CHAR_UP_READ_1: TemplateCell[][] = [
|
|
[_, _, _, _, _, _, _, _, _, _, _, _, _, _, _, _],
|
|
[_, _, _, _, _, _, _, _, _, _, _, _, _, _, _, _],
|
|
[_, _, _, _, _, _, H, H, H, H, _, _, _, _, _, _],
|
|
[_, _, _, _, _, H, H, H, H, H, H, _, _, _, _, _],
|
|
[_, _, _, _, _, H, H, H, H, H, H, _, _, _, _, _],
|
|
[_, _, _, _, _, H, H, H, H, H, H, _, _, _, _, _],
|
|
[_, _, _, _, _, K, K, K, K, K, K, _, _, _, _, _],
|
|
[_, _, _, _, _, K, K, K, K, K, K, _, _, _, _, _],
|
|
[_, _, _, _, _, _, S, S, S, S, _, _, _, _, _, _],
|
|
[_, _, _, _, _, S, S, S, S, S, S, _, _, _, _, _],
|
|
[_, _, _, _, S, S, S, S, S, S, S, S, _, _, _, _],
|
|
[_, _, _, _, S, S, S, S, S, S, S, S, _, _, _, _],
|
|
[_, _, _, _, K, S, S, S, S, S, S, K, _, _, _, _],
|
|
[_, _, _, _, _, S, S, S, S, S, S, _, _, _, _, _],
|
|
[_, _, _, _, _, _, P, P, P, P, _, _, _, _, _, _],
|
|
[_, _, _, _, _, P, P, P, P, P, P, _, _, _, _, _],
|
|
[_, _, _, _, _, P, P, P, P, P, P, _, _, _, _, _],
|
|
[_, _, _, _, _, P, P, _, _, P, P, _, _, _, _, _],
|
|
[_, _, _, _, _, O, O, _, _, O, O, _, _, _, _, _],
|
|
[_, _, _, _, _, _, _, _, _, _, _, _, _, _, _, _],
|
|
[_, _, _, _, _, _, _, _, _, _, _, _, _, _, _, _],
|
|
[_, _, _, _, _, _, _, _, _, _, _, _, _, _, _, _],
|
|
[_, _, _, _, _, _, _, _, _, _, _, _, _, _, _, _],
|
|
[_, _, _, _, _, _, _, _, _, _, _, _, _, _, _, _],
|
|
]
|
|
|
|
const CHAR_UP_READ_2: TemplateCell[][] = [
|
|
[_, _, _, _, _, _, _, _, _, _, _, _, _, _, _, _],
|
|
[_, _, _, _, _, _, _, _, _, _, _, _, _, _, _, _],
|
|
[_, _, _, _, _, _, _, _, _, _, _, _, _, _, _, _],
|
|
[_, _, _, _, _, _, H, H, H, H, _, _, _, _, _, _],
|
|
[_, _, _, _, _, H, H, H, H, H, H, _, _, _, _, _],
|
|
[_, _, _, _, _, H, H, H, H, H, H, _, _, _, _, _],
|
|
[_, _, _, _, _, H, H, H, H, H, H, _, _, _, _, _],
|
|
[_, _, _, _, _, K, K, K, K, K, K, _, _, _, _, _],
|
|
[_, _, _, _, _, K, K, K, K, K, K, _, _, _, _, _],
|
|
[_, _, _, _, _, _, S, S, S, S, _, _, _, _, _, _],
|
|
[_, _, _, _, _, S, S, S, S, S, S, _, _, _, _, _],
|
|
[_, _, _, _, S, S, S, S, S, S, S, S, _, _, _, _],
|
|
[_, _, _, _, S, S, S, S, S, S, S, S, _, _, _, _],
|
|
[_, _, _, _, K, S, S, S, S, S, S, K, _, _, _, _],
|
|
[_, _, _, _, _, S, S, S, S, S, S, _, _, _, _, _],
|
|
[_, _, _, _, _, _, P, P, P, P, _, _, _, _, _, _],
|
|
[_, _, _, _, _, P, P, P, P, P, P, _, _, _, _, _],
|
|
[_, _, _, _, _, P, P, _, _, P, P, _, _, _, _, _],
|
|
[_, _, _, _, _, O, O, _, _, O, O, _, _, _, _, _],
|
|
[_, _, _, _, _, _, _, _, _, _, _, _, _, _, _, _],
|
|
[_, _, _, _, _, _, _, _, _, _, _, _, _, _, _, _],
|
|
[_, _, _, _, _, _, _, _, _, _, _, _, _, _, _, _],
|
|
[_, _, _, _, _, _, _, _, _, _, _, _, _, _, _, _],
|
|
[_, _, _, _, _, _, _, _, _, _, _, _, _, _, _, _],
|
|
]
|
|
|
|
// ════════════════════════════════════════════════════════════════
|
|
// RIGHT-FACING SPRITES (side profile, one eye visible)
|
|
// Left sprites are generated by flipHorizontal()
|
|
// ════════════════════════════════════════════════════════════════
|
|
|
|
// Right walk: side view, legs step
|
|
const CHAR_WALK_RIGHT_1: TemplateCell[][] = [
|
|
[_, _, _, _, _, _, _, _, _, _, _, _, _, _, _, _],
|
|
[_, _, _, _, _, _, _, H, H, H, H, _, _, _, _, _],
|
|
[_, _, _, _, _, _, H, H, H, H, H, _, _, _, _, _],
|
|
[_, _, _, _, _, _, H, H, H, H, H, _, _, _, _, _],
|
|
[_, _, _, _, _, _, K, K, K, K, K, _, _, _, _, _],
|
|
[_, _, _, _, _, _, K, K, K, E, K, _, _, _, _, _],
|
|
[_, _, _, _, _, _, K, K, K, K, K, _, _, _, _, _],
|
|
[_, _, _, _, _, _, K, K, K, K, _, _, _, _, _, _],
|
|
[_, _, _, _, _, _, _, S, S, S, _, _, _, _, _, _],
|
|
[_, _, _, _, _, _, S, S, S, S, S, _, _, _, _, _],
|
|
[_, _, _, _, _, S, S, S, S, S, S, _, _, _, _, _],
|
|
[_, _, _, _, _, S, S, S, S, S, S, _, _, _, _, _],
|
|
[_, _, _, _, _, K, S, S, S, S, K, _, _, _, _, _],
|
|
[_, _, _, _, _, _, S, S, S, S, _, _, _, _, _, _],
|
|
[_, _, _, _, _, _, _, P, P, _, _, _, _, _, _, _],
|
|
[_, _, _, _, _, _, P, P, P, P, _, _, _, _, _, _],
|
|
[_, _, _, _, _, _, P, P, P, P, _, _, _, _, _, _],
|
|
[_, _, _, _, _, P, P, _, _, _, P, P, _, _, _, _],
|
|
[_, _, _, _, _, P, P, _, _, _, P, P, _, _, _, _],
|
|
[_, _, _, _, _, O, O, _, _, _, _, O, O, _, _, _],
|
|
[_, _, _, _, _, O, O, _, _, _, _, O, O, _, _, _],
|
|
[_, _, _, _, _, _, _, _, _, _, _, _, _, _, _, _],
|
|
[_, _, _, _, _, _, _, _, _, _, _, _, _, _, _, _],
|
|
[_, _, _, _, _, _, _, _, _, _, _, _, _, _, _, _],
|
|
]
|
|
|
|
const CHAR_WALK_RIGHT_2: TemplateCell[][] = [
|
|
[_, _, _, _, _, _, _, _, _, _, _, _, _, _, _, _],
|
|
[_, _, _, _, _, _, _, _, _, _, _, _, _, _, _, _],
|
|
[_, _, _, _, _, _, _, H, H, H, H, _, _, _, _, _],
|
|
[_, _, _, _, _, _, H, H, H, H, H, _, _, _, _, _],
|
|
[_, _, _, _, _, _, H, H, H, H, H, _, _, _, _, _],
|
|
[_, _, _, _, _, _, K, K, K, K, K, _, _, _, _, _],
|
|
[_, _, _, _, _, _, K, K, K, E, K, _, _, _, _, _],
|
|
[_, _, _, _, _, _, K, K, K, K, K, _, _, _, _, _],
|
|
[_, _, _, _, _, _, K, K, K, K, _, _, _, _, _, _],
|
|
[_, _, _, _, _, _, _, S, S, S, _, _, _, _, _, _],
|
|
[_, _, _, _, _, _, S, S, S, S, S, _, _, _, _, _],
|
|
[_, _, _, _, _, S, S, S, S, S, S, _, _, _, _, _],
|
|
[_, _, _, _, _, S, S, S, S, S, S, _, _, _, _, _],
|
|
[_, _, _, _, _, K, S, S, S, S, K, _, _, _, _, _],
|
|
[_, _, _, _, _, _, S, S, S, S, _, _, _, _, _, _],
|
|
[_, _, _, _, _, _, _, P, P, _, _, _, _, _, _, _],
|
|
[_, _, _, _, _, _, P, P, P, P, _, _, _, _, _, _],
|
|
[_, _, _, _, _, _, P, P, _, P, P, _, _, _, _, _],
|
|
[_, _, _, _, _, _, P, P, _, P, P, _, _, _, _, _],
|
|
[_, _, _, _, _, _, P, P, _, P, P, _, _, _, _, _],
|
|
[_, _, _, _, _, _, O, O, _, O, O, _, _, _, _, _],
|
|
[_, _, _, _, _, _, O, O, _, O, O, _, _, _, _, _],
|
|
[_, _, _, _, _, _, _, _, _, _, _, _, _, _, _, _],
|
|
[_, _, _, _, _, _, _, _, _, _, _, _, _, _, _, _],
|
|
]
|
|
|
|
const CHAR_WALK_RIGHT_3: TemplateCell[][] = [
|
|
[_, _, _, _, _, _, _, _, _, _, _, _, _, _, _, _],
|
|
[_, _, _, _, _, _, _, H, H, H, H, _, _, _, _, _],
|
|
[_, _, _, _, _, _, H, H, H, H, H, _, _, _, _, _],
|
|
[_, _, _, _, _, _, H, H, H, H, H, _, _, _, _, _],
|
|
[_, _, _, _, _, _, K, K, K, K, K, _, _, _, _, _],
|
|
[_, _, _, _, _, _, K, K, K, E, K, _, _, _, _, _],
|
|
[_, _, _, _, _, _, K, K, K, K, K, _, _, _, _, _],
|
|
[_, _, _, _, _, _, K, K, K, K, _, _, _, _, _, _],
|
|
[_, _, _, _, _, _, _, S, S, S, _, _, _, _, _, _],
|
|
[_, _, _, _, _, _, S, S, S, S, S, _, _, _, _, _],
|
|
[_, _, _, _, _, S, S, S, S, S, S, _, _, _, _, _],
|
|
[_, _, _, _, _, S, S, S, S, S, S, _, _, _, _, _],
|
|
[_, _, _, _, _, K, S, S, S, S, K, _, _, _, _, _],
|
|
[_, _, _, _, _, _, S, S, S, S, _, _, _, _, _, _],
|
|
[_, _, _, _, _, _, _, P, P, _, _, _, _, _, _, _],
|
|
[_, _, _, _, _, _, P, P, P, P, _, _, _, _, _, _],
|
|
[_, _, _, _, _, _, P, P, P, P, _, _, _, _, _, _],
|
|
[_, _, _, _, _, _, _, _, P, P, P, _, _, _, _, _],
|
|
[_, _, _, _, _, _, _, _, P, P, P, _, _, _, _, _],
|
|
[_, _, _, _, _, O, O, _, _, O, O, _, _, _, _, _],
|
|
[_, _, _, _, _, O, O, _, _, _, _, _, _, _, _, _],
|
|
[_, _, _, _, _, _, _, _, _, _, _, _, _, _, _, _],
|
|
[_, _, _, _, _, _, _, _, _, _, _, _, _, _, _, _],
|
|
[_, _, _, _, _, _, _, _, _, _, _, _, _, _, _, _],
|
|
]
|
|
|
|
// Right typing: side profile sitting, one arm on keyboard
|
|
const CHAR_RIGHT_TYPE_1: TemplateCell[][] = [
|
|
[_, _, _, _, _, _, _, _, _, _, _, _, _, _, _, _],
|
|
[_, _, _, _, _, _, _, _, _, _, _, _, _, _, _, _],
|
|
[_, _, _, _, _, _, _, H, H, H, H, _, _, _, _, _],
|
|
[_, _, _, _, _, _, H, H, H, H, H, _, _, _, _, _],
|
|
[_, _, _, _, _, _, H, H, H, H, H, _, _, _, _, _],
|
|
[_, _, _, _, _, _, K, K, K, K, K, _, _, _, _, _],
|
|
[_, _, _, _, _, _, K, K, K, E, K, _, _, _, _, _],
|
|
[_, _, _, _, _, _, K, K, K, K, K, _, _, _, _, _],
|
|
[_, _, _, _, _, _, K, K, K, K, _, _, _, _, _, _],
|
|
[_, _, _, _, _, _, _, S, S, S, _, _, _, _, _, _],
|
|
[_, _, _, _, _, _, S, S, S, S, S, _, _, _, _, _],
|
|
[_, _, _, _, _, S, S, S, S, S, S, K, _, _, _, _],
|
|
[_, _, _, _, _, S, S, S, S, S, K, _, _, _, _, _],
|
|
[_, _, _, _, _, _, S, S, S, S, _, _, _, _, _, _],
|
|
[_, _, _, _, _, _, S, S, S, S, _, _, _, _, _, _],
|
|
[_, _, _, _, _, _, _, P, P, _, _, _, _, _, _, _],
|
|
[_, _, _, _, _, _, P, P, P, P, _, _, _, _, _, _],
|
|
[_, _, _, _, _, _, P, P, P, P, _, _, _, _, _, _],
|
|
[_, _, _, _, _, _, P, P, _, P, P, _, _, _, _, _],
|
|
[_, _, _, _, _, _, O, O, _, O, O, _, _, _, _, _],
|
|
[_, _, _, _, _, _, _, _, _, _, _, _, _, _, _, _],
|
|
[_, _, _, _, _, _, _, _, _, _, _, _, _, _, _, _],
|
|
[_, _, _, _, _, _, _, _, _, _, _, _, _, _, _, _],
|
|
[_, _, _, _, _, _, _, _, _, _, _, _, _, _, _, _],
|
|
]
|
|
|
|
const CHAR_RIGHT_TYPE_2: TemplateCell[][] = [
|
|
[_, _, _, _, _, _, _, _, _, _, _, _, _, _, _, _],
|
|
[_, _, _, _, _, _, _, _, _, _, _, _, _, _, _, _],
|
|
[_, _, _, _, _, _, _, H, H, H, H, _, _, _, _, _],
|
|
[_, _, _, _, _, _, H, H, H, H, H, _, _, _, _, _],
|
|
[_, _, _, _, _, _, H, H, H, H, H, _, _, _, _, _],
|
|
[_, _, _, _, _, _, K, K, K, K, K, _, _, _, _, _],
|
|
[_, _, _, _, _, _, K, K, K, E, K, _, _, _, _, _],
|
|
[_, _, _, _, _, _, K, K, K, K, K, _, _, _, _, _],
|
|
[_, _, _, _, _, _, K, K, K, K, _, _, _, _, _, _],
|
|
[_, _, _, _, _, _, _, S, S, S, _, _, _, _, _, _],
|
|
[_, _, _, _, _, _, S, S, S, S, S, _, _, _, _, _],
|
|
[_, _, _, _, _, S, S, S, S, S, S, _, K, _, _, _],
|
|
[_, _, _, _, _, S, S, S, S, S, _, _, K, _, _, _],
|
|
[_, _, _, _, _, _, S, S, S, S, _, _, _, _, _, _],
|
|
[_, _, _, _, _, _, S, S, S, S, _, _, _, _, _, _],
|
|
[_, _, _, _, _, _, _, P, P, _, _, _, _, _, _, _],
|
|
[_, _, _, _, _, _, P, P, P, P, _, _, _, _, _, _],
|
|
[_, _, _, _, _, _, P, P, P, P, _, _, _, _, _, _],
|
|
[_, _, _, _, _, _, P, P, _, P, P, _, _, _, _, _],
|
|
[_, _, _, _, _, _, O, O, _, O, O, _, _, _, _, _],
|
|
[_, _, _, _, _, _, _, _, _, _, _, _, _, _, _, _],
|
|
[_, _, _, _, _, _, _, _, _, _, _, _, _, _, _, _],
|
|
[_, _, _, _, _, _, _, _, _, _, _, _, _, _, _, _],
|
|
[_, _, _, _, _, _, _, _, _, _, _, _, _, _, _, _],
|
|
]
|
|
|
|
// Right reading: side sitting, arms at side
|
|
const CHAR_RIGHT_READ_1: TemplateCell[][] = [
|
|
[_, _, _, _, _, _, _, _, _, _, _, _, _, _, _, _],
|
|
[_, _, _, _, _, _, _, _, _, _, _, _, _, _, _, _],
|
|
[_, _, _, _, _, _, _, H, H, H, H, _, _, _, _, _],
|
|
[_, _, _, _, _, _, H, H, H, H, H, _, _, _, _, _],
|
|
[_, _, _, _, _, _, H, H, H, H, H, _, _, _, _, _],
|
|
[_, _, _, _, _, _, K, K, K, K, K, _, _, _, _, _],
|
|
[_, _, _, _, _, _, K, K, K, E, K, _, _, _, _, _],
|
|
[_, _, _, _, _, _, K, K, K, K, K, _, _, _, _, _],
|
|
[_, _, _, _, _, _, K, K, K, K, _, _, _, _, _, _],
|
|
[_, _, _, _, _, _, _, S, S, S, _, _, _, _, _, _],
|
|
[_, _, _, _, _, _, S, S, S, S, S, _, _, _, _, _],
|
|
[_, _, _, _, _, S, S, S, S, S, S, _, _, _, _, _],
|
|
[_, _, _, _, _, S, S, S, S, S, S, _, _, _, _, _],
|
|
[_, _, _, _, _, K, S, S, S, S, K, _, _, _, _, _],
|
|
[_, _, _, _, _, _, S, S, S, S, _, _, _, _, _, _],
|
|
[_, _, _, _, _, _, _, P, P, _, _, _, _, _, _, _],
|
|
[_, _, _, _, _, _, P, P, P, P, _, _, _, _, _, _],
|
|
[_, _, _, _, _, _, P, P, P, P, _, _, _, _, _, _],
|
|
[_, _, _, _, _, _, P, P, _, P, P, _, _, _, _, _],
|
|
[_, _, _, _, _, _, O, O, _, O, O, _, _, _, _, _],
|
|
[_, _, _, _, _, _, _, _, _, _, _, _, _, _, _, _],
|
|
[_, _, _, _, _, _, _, _, _, _, _, _, _, _, _, _],
|
|
[_, _, _, _, _, _, _, _, _, _, _, _, _, _, _, _],
|
|
[_, _, _, _, _, _, _, _, _, _, _, _, _, _, _, _],
|
|
]
|
|
|
|
const CHAR_RIGHT_READ_2: TemplateCell[][] = [
|
|
[_, _, _, _, _, _, _, _, _, _, _, _, _, _, _, _],
|
|
[_, _, _, _, _, _, _, _, _, _, _, _, _, _, _, _],
|
|
[_, _, _, _, _, _, _, _, _, _, _, _, _, _, _, _],
|
|
[_, _, _, _, _, _, _, H, H, H, H, _, _, _, _, _],
|
|
[_, _, _, _, _, _, H, H, H, H, H, _, _, _, _, _],
|
|
[_, _, _, _, _, _, H, H, H, H, H, _, _, _, _, _],
|
|
[_, _, _, _, _, _, K, K, K, K, K, _, _, _, _, _],
|
|
[_, _, _, _, _, _, K, K, K, E, K, _, _, _, _, _],
|
|
[_, _, _, _, _, _, K, K, K, K, K, _, _, _, _, _],
|
|
[_, _, _, _, _, _, K, K, K, K, _, _, _, _, _, _],
|
|
[_, _, _, _, _, _, _, S, S, S, _, _, _, _, _, _],
|
|
[_, _, _, _, _, _, S, S, S, S, S, _, _, _, _, _],
|
|
[_, _, _, _, _, S, S, S, S, S, S, _, _, _, _, _],
|
|
[_, _, _, _, _, S, S, S, S, S, S, _, _, _, _, _],
|
|
[_, _, _, _, _, K, S, S, S, S, K, _, _, _, _, _],
|
|
[_, _, _, _, _, _, S, S, S, S, _, _, _, _, _, _],
|
|
[_, _, _, _, _, _, _, P, P, _, _, _, _, _, _, _],
|
|
[_, _, _, _, _, _, P, P, P, P, _, _, _, _, _, _],
|
|
[_, _, _, _, _, _, P, P, _, P, P, _, _, _, _, _],
|
|
[_, _, _, _, _, _, O, O, _, O, O, _, _, _, _, _],
|
|
[_, _, _, _, _, _, _, _, _, _, _, _, _, _, _, _],
|
|
[_, _, _, _, _, _, _, _, _, _, _, _, _, _, _, _],
|
|
[_, _, _, _, _, _, _, _, _, _, _, _, _, _, _, _],
|
|
[_, _, _, _, _, _, _, _, _, _, _, _, _, _, _, _],
|
|
]
|
|
|
|
// ════════════════════════════════════════════════════════════════
|
|
// Template export (for export-characters script)
|
|
// ════════════════════════════════════════════════════════════════
|
|
|
|
/** All character templates grouped by direction, for use by the export script.
|
|
* Frame order per direction: walk1, walk2, walk3, type1, type2, read1, read2 */
|
|
export const CHARACTER_TEMPLATES = {
|
|
down: [
|
|
CHAR_WALK_DOWN_1, CHAR_WALK_DOWN_2, CHAR_WALK_DOWN_3,
|
|
CHAR_DOWN_TYPE_1, CHAR_DOWN_TYPE_2,
|
|
CHAR_DOWN_READ_1, CHAR_DOWN_READ_2,
|
|
],
|
|
up: [
|
|
CHAR_WALK_UP_1, CHAR_WALK_UP_2, CHAR_WALK_UP_3,
|
|
CHAR_UP_TYPE_1, CHAR_UP_TYPE_2,
|
|
CHAR_UP_READ_1, CHAR_UP_READ_2,
|
|
],
|
|
right: [
|
|
CHAR_WALK_RIGHT_1, CHAR_WALK_RIGHT_2, CHAR_WALK_RIGHT_3,
|
|
CHAR_RIGHT_TYPE_1, CHAR_RIGHT_TYPE_2,
|
|
CHAR_RIGHT_READ_1, CHAR_RIGHT_READ_2,
|
|
],
|
|
} as const
|
|
|
|
// ════════════════════════════════════════════════════════════════
|
|
// Loaded character sprites (from PNG assets)
|
|
// ════════════════════════════════════════════════════════════════
|
|
|
|
export interface LoadedCharacterData {
|
|
down: SpriteData[]
|
|
up: SpriteData[]
|
|
right: SpriteData[]
|
|
}
|
|
|
|
let loadedCharacters: LoadedCharacterData[] | null = null
|
|
|
|
/** Set pre-colored character sprites loaded from PNG assets. Call this when characterSpritesLoaded message arrives. */
|
|
export function setCharacterTemplates(data: LoadedCharacterData[]): void {
|
|
loadedCharacters = data
|
|
// Clear cache so sprites are rebuilt from loaded data
|
|
spriteCache.clear()
|
|
}
|
|
|
|
/** Flip a SpriteData horizontally (for generating left sprites from right) */
|
|
function flipSpriteHorizontal(sprite: SpriteData): SpriteData {
|
|
return sprite.map((row) => [...row].reverse())
|
|
}
|
|
|
|
// ════════════════════════════════════════════════════════════════
|
|
// Sprite resolution + caching
|
|
// ════════════════════════════════════════════════════════════════
|
|
|
|
export interface CharacterSprites {
|
|
walk: Record<Direction, [SpriteData, SpriteData, SpriteData, SpriteData]>
|
|
typing: Record<Direction, [SpriteData, SpriteData]>
|
|
reading: Record<Direction, [SpriteData, SpriteData]>
|
|
}
|
|
|
|
const spriteCache = new Map<string, CharacterSprites>()
|
|
|
|
/** Apply hue shift to every sprite in a CharacterSprites set */
|
|
function hueShiftSprites(sprites: CharacterSprites, hueShift: number): CharacterSprites {
|
|
const color: FloorColor = { h: hueShift, s: 0, b: 0, c: 0 }
|
|
const shift = (s: SpriteData) => adjustSprite(s, color)
|
|
const shiftWalk = (arr: [SpriteData, SpriteData, SpriteData, SpriteData]): [SpriteData, SpriteData, SpriteData, SpriteData] =>
|
|
[shift(arr[0]), shift(arr[1]), shift(arr[2]), shift(arr[3])]
|
|
const shiftPair = (arr: [SpriteData, SpriteData]): [SpriteData, SpriteData] =>
|
|
[shift(arr[0]), shift(arr[1])]
|
|
return {
|
|
walk: {
|
|
[Dir.DOWN]: shiftWalk(sprites.walk[Dir.DOWN]),
|
|
[Dir.UP]: shiftWalk(sprites.walk[Dir.UP]),
|
|
[Dir.RIGHT]: shiftWalk(sprites.walk[Dir.RIGHT]),
|
|
[Dir.LEFT]: shiftWalk(sprites.walk[Dir.LEFT]),
|
|
} as Record<Direction, [SpriteData, SpriteData, SpriteData, SpriteData]>,
|
|
typing: {
|
|
[Dir.DOWN]: shiftPair(sprites.typing[Dir.DOWN]),
|
|
[Dir.UP]: shiftPair(sprites.typing[Dir.UP]),
|
|
[Dir.RIGHT]: shiftPair(sprites.typing[Dir.RIGHT]),
|
|
[Dir.LEFT]: shiftPair(sprites.typing[Dir.LEFT]),
|
|
} as Record<Direction, [SpriteData, SpriteData]>,
|
|
reading: {
|
|
[Dir.DOWN]: shiftPair(sprites.reading[Dir.DOWN]),
|
|
[Dir.UP]: shiftPair(sprites.reading[Dir.UP]),
|
|
[Dir.RIGHT]: shiftPair(sprites.reading[Dir.RIGHT]),
|
|
[Dir.LEFT]: shiftPair(sprites.reading[Dir.LEFT]),
|
|
} as Record<Direction, [SpriteData, SpriteData]>,
|
|
}
|
|
}
|
|
|
|
export function getCharacterSprites(paletteIndex: number, hueShift = 0): CharacterSprites {
|
|
const cacheKey = `${paletteIndex}:${hueShift}`
|
|
const cached = spriteCache.get(cacheKey)
|
|
if (cached) return cached
|
|
|
|
let sprites: CharacterSprites
|
|
|
|
if (loadedCharacters) {
|
|
// Use pre-colored character sprites directly (no palette swapping)
|
|
const char = loadedCharacters[paletteIndex % loadedCharacters.length]
|
|
const d = char.down
|
|
const u = char.up
|
|
const rt = char.right
|
|
const flip = flipSpriteHorizontal
|
|
|
|
sprites = {
|
|
walk: {
|
|
[Dir.DOWN]: [d[0], d[1], d[2], d[1]],
|
|
[Dir.UP]: [u[0], u[1], u[2], u[1]],
|
|
[Dir.RIGHT]: [rt[0], rt[1], rt[2], rt[1]],
|
|
[Dir.LEFT]: [flip(rt[0]), flip(rt[1]), flip(rt[2]), flip(rt[1])],
|
|
},
|
|
typing: {
|
|
[Dir.DOWN]: [d[3], d[4]],
|
|
[Dir.UP]: [u[3], u[4]],
|
|
[Dir.RIGHT]: [rt[3], rt[4]],
|
|
[Dir.LEFT]: [flip(rt[3]), flip(rt[4])],
|
|
},
|
|
reading: {
|
|
[Dir.DOWN]: [d[5], d[6]],
|
|
[Dir.UP]: [u[5], u[6]],
|
|
[Dir.RIGHT]: [rt[5], rt[6]],
|
|
[Dir.LEFT]: [flip(rt[5]), flip(rt[6])],
|
|
},
|
|
}
|
|
} else {
|
|
// Fallback: use hardcoded templates with palette swapping
|
|
const pal = CHARACTER_PALETTES[paletteIndex % CHARACTER_PALETTES.length]
|
|
const r = (t: TemplateCell[][]) => resolveTemplate(t, pal)
|
|
const rf = (t: TemplateCell[][]) => resolveTemplate(flipHorizontal(t), pal)
|
|
|
|
sprites = {
|
|
walk: {
|
|
[Dir.DOWN]: [r(CHAR_WALK_DOWN_1), r(CHAR_WALK_DOWN_2), r(CHAR_WALK_DOWN_3), r(CHAR_WALK_DOWN_2)],
|
|
[Dir.UP]: [r(CHAR_WALK_UP_1), r(CHAR_WALK_UP_2), r(CHAR_WALK_UP_3), r(CHAR_WALK_UP_2)],
|
|
[Dir.RIGHT]: [r(CHAR_WALK_RIGHT_1), r(CHAR_WALK_RIGHT_2), r(CHAR_WALK_RIGHT_3), r(CHAR_WALK_RIGHT_2)],
|
|
[Dir.LEFT]: [rf(CHAR_WALK_RIGHT_1), rf(CHAR_WALK_RIGHT_2), rf(CHAR_WALK_RIGHT_3), rf(CHAR_WALK_RIGHT_2)],
|
|
},
|
|
typing: {
|
|
[Dir.DOWN]: [r(CHAR_DOWN_TYPE_1), r(CHAR_DOWN_TYPE_2)],
|
|
[Dir.UP]: [r(CHAR_UP_TYPE_1), r(CHAR_UP_TYPE_2)],
|
|
[Dir.RIGHT]: [r(CHAR_RIGHT_TYPE_1), r(CHAR_RIGHT_TYPE_2)],
|
|
[Dir.LEFT]: [rf(CHAR_RIGHT_TYPE_1), rf(CHAR_RIGHT_TYPE_2)],
|
|
},
|
|
reading: {
|
|
[Dir.DOWN]: [r(CHAR_DOWN_READ_1), r(CHAR_DOWN_READ_2)],
|
|
[Dir.UP]: [r(CHAR_UP_READ_1), r(CHAR_UP_READ_2)],
|
|
[Dir.RIGHT]: [r(CHAR_RIGHT_READ_1), r(CHAR_RIGHT_READ_2)],
|
|
[Dir.LEFT]: [rf(CHAR_RIGHT_READ_1), rf(CHAR_RIGHT_READ_2)],
|
|
},
|
|
}
|
|
}
|
|
|
|
// Apply hue shift if non-zero
|
|
if (hueShift !== 0) {
|
|
sprites = hueShiftSprites(sprites, hueShift)
|
|
}
|
|
|
|
spriteCache.set(cacheKey, sprites)
|
|
return sprites
|
|
}
|