diff options
| -rw-r--r-- | test-client/assets/item_sprites.ini | 20 | ||||
| -rw-r--r-- | test-client/assets/tile_sprites.ini | 14 | ||||
| -rw-r--r-- | test-client/main.ts | 2 | ||||
| -rw-r--r-- | test-client/map/sprites.ts | 260 |
4 files changed, 63 insertions, 233 deletions
diff --git a/test-client/assets/item_sprites.ini b/test-client/assets/item_sprites.ini new file mode 100644 index 00000000..b54264c5 --- /dev/null +++ b/test-client/assets/item_sprites.ini @@ -0,0 +1,20 @@ +# Hurry Curry! - a game about cooking +# Copyright (C) 2026 Hurry Curry! Contributors +# +# This program is free software: you can redistribute it and/or modify +# it under the terms of the GNU Affero General Public License as published by +# the Free Software Foundation, version 3 of the License only. +# +# This program is distributed in the hope that it will be useful, +# but WITHOUT ANY WARRANTY; without even the implied warranty of +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +# GNU Affero General Public License for more details. +# +# You should have received a copy of the GNU Affero General Public License +# along with this program. If not, see <https://www.gnu.org/licenses/>. + +[sliced-mushroom] + fi rgb(98, 50, 25)|st|sc 0.3|pa M-3 0A1 1 0 013 0H1V3H-1V0Z +[mushroom] + fi rgb(202, 178, 165)|st|pa M-1 0H1V3H-1Z + fi rgb(98, 50, 25)|st|pa M-3 0A1 1 0 013 0Z diff --git a/test-client/assets/tile_sprites.ini b/test-client/assets/tile_sprites.ini new file mode 100644 index 00000000..2cfbd96c --- /dev/null +++ b/test-client/assets/tile_sprites.ini @@ -0,0 +1,14 @@ +# Hurry Curry! - a game about cooking +# Copyright (C) 2026 Hurry Curry! Contributors +# +# This program is free software: you can redistribute it and/or modify +# it under the terms of the GNU Affero General Public License as published by +# the Free Software Foundation, version 3 of the License only. +# +# This program is distributed in the hope that it will be useful, +# but WITHOUT ANY WARRANTY; without even the implied warranty of +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +# GNU Affero General Public License for more details. +# +# You should have received a copy of the GNU Affero General Public License +# along with this program. If not, see <https://www.gnu.org/licenses/>. diff --git a/test-client/main.ts b/test-client/main.ts index 0c47062e..b2eeac48 100644 --- a/test-client/main.ts +++ b/test-client/main.ts @@ -37,12 +37,14 @@ import { SPinnedMessages } from "./pinned_messages.ts"; import { GridLayer } from "./map/mod.ts"; import { SConnectScreen } from "./connect_screen.ts"; import { SAnnouncement } from "./announcement.ts"; +import { load_sprites } from "./map/sprites.ts"; export let ctx: CanvasRenderingContext2D; export let canvas: HTMLCanvasElement; export let ws: WebSocket; document.addEventListener("DOMContentLoaded", async () => { await init_locale() + await load_sprites() const ws_uri = globalThis.location.protocol.endsWith("s:") ? `wss://${globalThis.location.host}/` : `ws://${globalThis.location.hostname}:27032/` diff --git a/test-client/map/sprites.ts b/test-client/map/sprites.ts index c7cd769d..ef9343a8 100644 --- a/test-client/map/sprites.ts +++ b/test-client/map/sprites.ts @@ -16,244 +16,38 @@ */ -type Component = (ctx: CanvasRenderingContext2D) => void -function base(fill: string, stroke?: string, stroke_width?: number): Component { - return c => { - c.fillStyle = fill; - c.strokeStyle = stroke ?? "black"; - c.lineWidth = stroke_width ?? 0.05 - c.lineJoin = "miter" - c.lineCap = "square" - c.fillRect( - -0.5, - -0.5, - 1, - 1 - ) - if (stroke) c.strokeRect( - -0.5 + c.lineWidth / 2, - -0.5 + c.lineWidth / 2, - 1 - c.lineWidth, - 1 - c.lineWidth - ) - } -} -function rect(inset: number, fill: string, stroke?: string, stroke_width?: number): Component { - return c => { - c.fillStyle = fill; - c.strokeStyle = stroke ?? "black"; - c.lineWidth = stroke_width ?? 0.05 - c.lineJoin = "round" - c.lineCap = "round" - c.fillRect( - -0.5 + inset, - -0.5 + inset, - 1 - inset * 2, - 1 - inset * 2 - ) - if (stroke) c.strokeRect( - -0.5 + inset, - -0.5 + inset, - 1 - inset * 2, - 1 - inset * 2 - ) - } -} -function circle(radius: number, fill: string, stroke?: string, stroke_width?: number): Component { - return c => { - c.fillStyle = fill; - c.strokeStyle = stroke ?? "black"; - c.lineWidth = stroke_width ?? 0.05 - c.beginPath() - c.arc(0.0, 0.0, radius, 0, Math.PI * 2) - if (stroke) c.stroke() - c.fill() - } -} -function cross(size: number, stroke: string, stroke_width = 0.05): Component { - return c => { - c.strokeStyle = stroke - c.lineWidth = stroke_width - c.lineCap = "round" - c.beginPath() - c.moveTo(-size, -size) - c.lineTo(size, size) - c.moveTo(size, -size) - c.lineTo(-size, size) - c.stroke() - } -} -function checkmark(size: number, stroke: string, stroke_width = 0.05): Component { - return c => { - c.strokeStyle = stroke - c.lineWidth = stroke_width - c.lineCap = "round" - c.beginPath() - c.moveTo(-size, 0) - c.lineTo(-size * 0.3, size) - c.lineTo(size, -size) - c.stroke() - } -} -function text(s: string): Component { - return c => { - c.font = "0.8px sans-serif" - c.strokeStyle = "#e38242" - c.fillStyle = "white" - c.lineWidth = 0.05 - c.textAlign = "center" - c.textBaseline = "middle" - c.lineJoin = "round" - c.lineCap = "round" - c.strokeText(s, 0, 0) - c.fillText(s, 0, 0) - } -} +import { Gamedata } from "../protocol.ts"; -function arrange_items(...items: ItemName[]): Component { - return c => { - for (let index = 0; index < items.length; index++) { - const item = items[index]; - - const t = index / items.length * Math.PI * 2. - const radius = items.length == 1 ? 0 : (0.4 / items.length) - const off_x = Math.sin(t) * radius - const off_y = Math.cos(t) * radius - const scale = 1. / Math.sqrt(items.length) +let item_sprites_raw = "" +let tile_sprites_raw = "" +export async function load_sprites() { + const r1 = await fetch("assets/item_sprites.ini") + const r2 = await fetch("assets/tile_sprites.ini") + if (!r1.ok || !r2.ok) throw "http fail" + item_sprites_raw = await r1.text() + tile_sprites_raw = await r2.text() +} - c.save() - c.translate(off_x, off_y) - c.scale(scale, scale) - iref(item)(c) - c.restore() +function parse_sprite_file(f: string): { [key: string]: string[] } { + let item = "" + const out: { [key: string]: string[] } = {} + for (const line of f.split("\n")) { + if (line.startsWith("#")) continue + else if (!line.trim().length) continue + else if (line.startsWith("[") && line.endsWith("]")) + item = line.substring(1, line.length - 2) + else { + out[item] = out[item] ?? [] + out[item].push(line.trim()) } } + return out } -const door: Component = c => { - c.fillStyle = "#ff9843" - c.fillRect(-0.5, -0.1, 1, 0.2) +function prepare_sprites(data: Gamedata) { + const items = parse_sprite_file(item_sprites_raw) + const tiles = parse_sprite_file(tile_sprites_raw) } -const iref = (name: ItemName): Component => c => draw_item_sprite(c, name) -const tref = (name: ItemName): Component => c => draw_tile_sprite(c, name) - -export type ItemName = string -export type TileName = string - -const ITEMS: { [key in ItemName]: (c: string[]) => Component } = { - "bun": () => circle(0.3, "#853e20"), - "burned": () => c => (circle(0.3, "rgb(61, 18, 0)")(c), cross(0.2, "red", 0.02)(c)), - "coconut": () => circle(0.3, "rgb(75, 49, 25)"), - "cooked-rice": () => circle(0.3, "rgb(233, 233, 233)"), - "curry": () => circle(0.3, "rgb(185, 67, 37)"), - "dough": () => circle(0.3, "#b38d7d"), - "fish": () => circle(0.3, "rgb(62, 66, 104)"), - "flour": () => circle(0.3, "#d8c9c2"), - "leek": () => circle(0.3, "rgb(50, 133, 17)"), - "milk": () => circle(0.3, "rgb(252, 243, 208)"), - "nigiri": () => circle(0.25, "rgb(233, 233, 233)", "salmon"), - "rice": () => circle(0.3, "rgb(163, 163, 163)"), - "seared-steak": () => circle(0.3, "#702200"), - "sliced-bun": () => circle(0.3, "#853e20"), - "sliced-fish": () => circle(0.3, "salmon", "rgb(62, 66, 104)"), - "steak": () => circle(0.3, "#ca3510"), - "patty": () => circle(0.3, "#c26149"), - "seared-patty": () => circle(0.3, "#502c23"), - "strawberry-icecream": () => circle(0.2, "rgb(250, 148, 236)"), - "strawberry-mochi": () => circle(0.2, "rgb(161, 111, 132)"), - "strawberry-shake": () => circle(0.3, "rgb(255, 180, 180)"), - "strawberry": () => circle(0.3, "rgb(228, 79, 111)"), - "tomato-juice": () => circle(0.3, "#b80000"), - "tomato-soup": () => circle(0.3, "#ff2600"), - "water": () => circle(0.3, "rgb(86, 92, 206)"), - "tomato": () => circle(0.3, "#d63838"), - "sliced-tomato": () => circle(0.3, "#d16363", "#d63838", 0.08), - "lettuce": () => circle(0.3, "#64a30b"), - "sliced-lettuce": () => circle(0.3, "#a0da4f", "#64a30b", 0.08), - "cheese": () => circle(0.3, "#b3b615"), - "sliced-cheese": () => rect(0.25, "#dcdf29", "#b3b615", 0.05), - "dirty-plate": () => circle(0.4, "#947a6f", "#d3a187", 0.02), - "mochi-dough": () => circle(0.3, "rgb(172, 162, 151)"), - "rice-flour": () => iref("rice"), - "unknown-order": () => text("!"), - - "pan": i => c => (circle(0.35, "rgb(29, 29, 29)", "rgb(39, 39, 39)", 0.04)(c), arrange_items(...i)(c)), - "pot": i => c => (circle(0.27, "rgb(29, 29, 29)", "rgb(56, 56, 56)", 0.2)(c), arrange_items(...i)(c)), - "foodprocessor": i => c => (circle(0.35, "rgb(86, 168, 189)", "rgb(88, 222, 255)", 0.04)(c), arrange_items(...i)(c)), - "plate": i => c => (circle(0.4, "#b6b6b6", "#f7f7f7", 0.02)(c), arrange_items(...i)(c)), - "glass": i => c => (circle(0.35, "rgb(150, 255, 237)", "rgb(52, 129, 155)", 0.02), arrange_items(...i)(c)), - -} - -const counter = tref("counter"); -const TILES: { [key in TileName]: (param: string) => Component } = { - "floor": () => base("#333", "#222", 0.05), - "street": () => base("rgb(19, 19, 19)"), - "table": () => base("rgb(133, 76, 38)"), - "door": () => door, - "chair": () => circle(0.45, "rgb(136, 83, 41)"), - "wall": () => base("rgb(0, 14, 56)"), - "wall-window": () => base("rgb(19, 40, 102)"), - "counter": () => base("rgb(182, 172, 164)"), - "counter-window": () => base("rgb(233, 233, 233)"), - "grass": () => base("rgb(0, 107, 4)"), - "path": () => base("rgb(100, 80, 55)"), - "conveyor": () => base("rgb(107, 62, 128)"), - "tree": () => base("rgb(1, 82, 4)"), - "cutting-board": () => rect(0.3, "#9eec44ff", "#9eec44ff", 0.2), - "rolling-board": () => rect(0.3, "#ece644ff", "#ece644ff", 0.2), - "trash": () => c => (circle(0.4, "rgb(20, 20, 20)")(c), cross(0.3, "rgb(90, 36, 36)")(c)), - "sink": () => base("rgb(131, 129, 161)", "rgb(177, 174, 226)", 0.2), - "oven": () => base("rgb(241, 97, 61)", "rgb(109, 84, 84)", 0.3), - "freezer": () => base("rgb(61, 97, 241)", "rgb(84, 88, 109)", 0.3), - "stove": () => c => (counter(c), circle(0.4, "#444", "#999")(c)), - "book": () => c => (counter(c), rect(0.2, "rgb(88, 44, 7)")(c)), - "lamp": () => rect(0.3, "rgb(255, 217, 127)", "rgb(32, 32, 32)", 0.1), - "crate": name => c => (base("#60701e", "#b9da37", 0.05)(c), iref(name)(c)), - "button-base": () => base("#272727", "#272727", 0.05), - "button": name => BUTTON_ICONS[name], - "map-selector": () => rect(0.2, "rgb(56, 99, 239)", "rgb(44, 79, 194)", 0.1), - "screen": () => () => { } -} -const BUTTON_ICONS: { [key: string]: Component } = { - "accept": c => (circle(0.35, "rgb(81, 255, 0)", "rgb(57, 179, 0)")(c), checkmark(0.2, "#fff", 0.05)(c)), - "reject": c => (circle(0.35, "rgb(255, 0, 0)", "rgb(181, 0, 0)")(c), cross(0.2, "#fff", 0.05)(c)), -} - - -function debug_label(ctx: CanvasRenderingContext2D, name: string) { - ctx.save() - ctx.font = "10px sans-serif" - ctx.fillStyle = "white" - ctx.strokeStyle = "black" - ctx.lineWidth = 1 - ctx.textAlign = "center" - ctx.textBaseline = "middle" - ctx.scale(0.03, 0.03) - ctx.strokeText(name, 0, 0) - ctx.fillText(name, 0, 0) - ctx.restore() -} - -// TODO performance -export function draw_item_sprite(ctx: CanvasRenderingContext2D, name: ItemName) { - const [base, cont] = name.split(":") - if (ITEMS[base]) { - ITEMS[base](cont?.split(",") ?? [])(ctx) - } else { - circle(0.4, "#f0f")(ctx) - debug_label(ctx, name) - } -} -export function draw_tile_sprite(ctx: CanvasRenderingContext2D, name: TileName) { - if (!name) return - const [kind, param] = name.split(":", 2) - if (TILES[kind]) { - TILES[kind](param)(ctx) - } else { - base("#f0f")(ctx) - debug_label(ctx, name) - } - -} +export function draw_item_sprite(ctx: CanvasRenderingContext2D, name: string) { } +export function draw_tile_sprite(ctx: CanvasRenderingContext2D, name: string) { } |