diff options
Diffstat (limited to 'test-client/visual.ts')
| -rw-r--r-- | test-client/visual.ts | 172 | 
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, +    } +} + | 
