aboutsummaryrefslogtreecommitdiff
path: root/test-client/visual.ts
diff options
context:
space:
mode:
Diffstat (limited to 'test-client/visual.ts')
-rw-r--r--test-client/visual.ts172
1 files changed, 172 insertions, 0 deletions
diff --git a/test-client/visual.ts b/test-client/visual.ts
new file mode 100644
index 00000000..4bbfac6c
--- /dev/null
+++ b/test-client/visual.ts
@@ -0,0 +1,172 @@
+import { ItemData, PLAYER_SIZE, camera, canvas, ctx, data, get_interact_target, interact_target_anim, items_removed, keys_down, players, tiles } from "./main.ts";
+import { Message } from "./protocol.ts";
+import { FALLBACK_TILE, ITEMS, TILES, FALLBACK_ITEM } from "./tiles.ts";
+import { V2, ceil_v2, floor_v2 } from "./util.ts";
+
+let camera_zoom = 0.1
+let scale = 0
+
+export function draw_wait(text: string) {
+ ctx.fillStyle = "#444"
+ ctx.fillRect(0, 0, canvas.width, canvas.height)
+ ctx.fillStyle = "#555"
+ ctx.font = "50px sans-serif"
+ ctx.strokeStyle = "black"
+ ctx.fillStyle = "white"
+ ctx.lineWidth = 10
+ ctx.textAlign = "center"
+ ctx.textBaseline = "middle"
+ ctx.lineJoin = "round"
+ ctx.lineCap = "round"
+ ctx.strokeText(text, canvas.width / 2, canvas.height / 2)
+ ctx.fillText(text, canvas.width / 2, canvas.height / 2)
+}
+
+
+export function draw_ingame() {
+ ctx.fillStyle = "#111"
+ ctx.fillRect(0, 0, canvas.width, canvas.height)
+
+ scale = Math.min(canvas.width, canvas.height) * camera_zoom;
+ ctx.save()
+ ctx.translate(canvas.width / 2, canvas.height / 2)
+ ctx.scale(scale, scale)
+ ctx.translate(-camera.x, -camera.y)
+
+ draw_grid()
+
+ for (const [_, tile] of tiles) {
+ ctx.save()
+ ctx.translate(tile.x + 0.5, tile.y + 0.5)
+ const comps = TILES[data.tile_names[tile.kind]] ?? FALLBACK_TILE
+ for (const c of comps) {
+ c(ctx)
+ }
+ ctx.restore()
+ }
+
+ for (const [_, player] of players) {
+ {
+ ctx.save()
+ ctx.translate(player.x, player.y)
+ {
+ ctx.save()
+ ctx.rotate(-player.rot)
+ draw_character(player.character)
+ ctx.restore()
+ }
+ if (player.message) draw_message(player.message)
+ ctx.restore()
+ }
+ if (player.item) draw_item(player.item)
+ }
+
+ for (const item of items_removed) {
+ draw_item(item)
+ }
+ for (const [_, tile] of tiles) {
+ if (tile.item) draw_item(tile.item)
+ }
+
+ draw_interact_target()
+
+ ctx.restore()
+
+ if (keys_down.has("KeyP")) {
+ camera_zoom = 0.05
+ ctx.fillStyle = "white"
+ ctx.textAlign = "left"
+ ctx.textBaseline = "bottom"
+ ctx.font = "20px sans-serif"
+ ctx.fillText(`interact = ${JSON.stringify(get_interact_target())}`, 10, 30)
+ } else { camera_zoom = 0.1 }
+}
+
+function draw_item(item: ItemData) {
+ ctx.save()
+ ctx.translate(item.x, item.y)
+ if (item.remove_anim) ctx.scale(1 - item.remove_anim, 1 - item.remove_anim)
+ const comps = ITEMS[data.item_names[item.kind]] ?? FALLBACK_ITEM
+ for (const c of comps) {
+ c(ctx)
+ }
+ if (item.progress !== null && item.progress !== undefined) {
+ ctx.fillStyle = "rgba(115, 230, 58, 0.66)"
+ ctx.fillRect(-0.5, -0.5, 1, item.progress)
+ }
+ ctx.restore()
+}
+
+function draw_interact_target() {
+ ctx.save()
+ ctx.translate(interact_target_anim.x, interact_target_anim.y)
+
+ ctx.lineCap = "round"
+ ctx.lineJoin = "round"
+ ctx.lineWidth = 0.06 + 0.03 * Math.sin(Date.now() / 100)
+ ctx.strokeStyle = "rgb(84, 122, 236)"
+ ctx.strokeRect(0, 0, 1, 1)
+
+ ctx.restore()
+}
+
+function draw_grid() {
+ ctx.strokeStyle = "#333"
+ ctx.lineWidth = 0.01
+ ctx.beginPath()
+ const min = floor_v2(map_screen_to_world({ x: 0, y: 0 }))
+ const max = ceil_v2(map_screen_to_world({ x: canvas.width, y: canvas.height }))
+ for (let x = min.x; x < max.x; x++) {
+ ctx.moveTo(x, min.y)
+ ctx.lineTo(x, max.y)
+ }
+ for (let y = min.y; y < max.y; y++) {
+ ctx.moveTo(min.x, y)
+ ctx.lineTo(max.x, y)
+ }
+ ctx.stroke()
+}
+
+function draw_character(character: number) {
+ ctx.fillStyle = `hsl(${character}rad, 50%, 50%)`
+ ctx.beginPath()
+ ctx.arc(0, 0, PLAYER_SIZE, 0, Math.PI * 2)
+ ctx.fill()
+
+ ctx.fillStyle = `hsl(${character}rad, 80%, 10%)`
+ ctx.beginPath()
+ ctx.arc(0, -0.2, PLAYER_SIZE, 0, Math.PI * 2)
+ ctx.fill()
+
+ ctx.fillStyle = `hsl(${character}rad, 80%, 70%)`
+ ctx.beginPath()
+ ctx.moveTo(-0.04, 0.25)
+ ctx.lineTo(0.04, 0.25)
+ ctx.lineTo(0, 0.4)
+ ctx.fill()
+}
+
+function draw_message(m: Message) {
+ ctx.save()
+ ctx.translate(0, -1)
+ if ("item" in m) {
+ ctx.fillStyle = "#fffa"
+ ctx.beginPath()
+ ctx.moveTo(0, 0.7)
+ ctx.arc(0, 0, 0.5, Math.PI / 4, Math.PI - Math.PI / 4, true)
+ ctx.closePath()
+ ctx.fill()
+
+ const comps = ITEMS[data.item_names[m.item]] ?? FALLBACK_ITEM
+ for (const c of comps) c(ctx)
+ }
+ ctx.restore()
+}
+
+function map_screen_to_world(screen: V2): V2 {
+ return {
+ x: ((screen.x - canvas.width / 2) / scale) + camera.x,
+ y: ((screen.y - canvas.height / 2) / scale) + camera.y,
+ }
+}
+