aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--test-client/assets/item_sprites.ini20
-rw-r--r--test-client/assets/tile_sprites.ini14
-rw-r--r--test-client/main.ts2
-rw-r--r--test-client/map/sprites.ts260
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) { }