diff options
Diffstat (limited to 'test-client')
| -rw-r--r-- | test-client/main.ts | 161 | ||||
| -rw-r--r-- | test-client/protocol.ts | 13 | ||||
| -rw-r--r-- | test-client/tiles.ts | 25 | ||||
| -rw-r--r-- | test-client/util.ts | 13 | 
4 files changed, 152 insertions, 60 deletions
| diff --git a/test-client/main.ts b/test-client/main.ts index e0cc9b3c..4c755e55 100644 --- a/test-client/main.ts +++ b/test-client/main.ts @@ -1,9 +1,11 @@  /// <reference lib="dom" /> -import { Gamedata, ItemID, ItemIndex, PacketC, PacketS, PlayerID, TileIndex } from "./protocol.ts"; +import { Gamedata, ItemIndex, PacketC, PacketS, PlayerID, TileIndex } from "./protocol.ts";  import { FALLBACK_ITEM } from "./tiles.ts";  import { FALLBACK_TILE, ITEMS, TILES } from "./tiles.ts"; -import { V2, add_v2, ceil_v2, floor_v2, length, lerp_exp_v2_mut, normalize } from "./util.ts"; +import { V2, add_v2, ceil_v2, floor_v2, length, lerp_exp_v2_mut, normalize, aabb_circle_distance } from "./util.ts"; + +const PLAYER_SIZE = 0.4;  let ctx: CanvasRenderingContext2D;  let canvas: HTMLCanvasElement; @@ -33,14 +35,31 @@ document.addEventListener("DOMContentLoaded", () => {      setInterval(tick_update, 1000 / 25);  }) -interface PlayerData { x: number; y: number, name: string, rot: number, hand?: ItemID, facing: V2 } +interface ItemData { +    kind: ItemIndex, +    x: number, +    y: number, +    progress?: number +} +interface PlayerData { +    x: number, +    y: number, +    name: string, +    rot: number, +    item?: ItemData, +    facing: V2, +    vel: { x: number, y: number } +} +interface TileData { +    x: number +    y: number +    kind: TileIndex +    item?: ItemData +}  const players = new Map<PlayerID, PlayerData>() -interface ItemData { kind: ItemIndex, tile?: V2, player?: PlayerID, tracking_player: boolean, x: number, y: number } -const items = new Map<ItemID, ItemData>() -interface TileData { x: number; y: number, kind: TileIndex, items: ItemID[], active_progress?: number }  const tiles = new Map<string, TileData>() -let data: Gamedata = { item_names: [], tile_names: [] } +let data: Gamedata = { item_names: [], tile_names: [], spawn: [0, 0] }  let my_id: PlayerID = -1  const camera: V2 = { x: 0, y: 0 } @@ -55,10 +74,12 @@ function packet(p: PacketC) {              my_id = p.id              data = p.data              break; -        case "add_player": -            if (p.hand) items.set(p.hand[0], { kind: p.hand[1], player: p.id, tracking_player: true, x: 0, y: 0 }) -            players.set(p.id, { x: 0, y: 0, name: p.name, rot: 0, hand: p.hand?.[0], facing: { x: 0, y: 1 } }) +        case "add_player": { +            let item = undefined +            if (p.item) item = { kind: p.item, x: 0, y: 0 }; +            players.set(p.id, { x: data.spawn[0], y: data.spawn[1], name: p.name, rot: 0, item, facing: { x: 0, y: 1 }, vel: { x: 0, y: 0 } })              break; +        }          case "remove_player":              players.delete(p.id)              break; @@ -71,33 +92,34 @@ function packet(p: PacketC) {              break;          }          case "take_item": { -            const item = items.get(p.item)! -            item.tracking_player = true -            item.player = p.player +            const player = players.get(p.player)! +            const tile = tiles.get(p.tile.toString())! +            player.item = tile.item; +            tile.item = undefined              break;          }          case "put_item": { -            const item = items.get(p.item)! -            item.tracking_player = false -            item.tile = { x: p.pos[0], y: p.pos[1] } +            const player = players.get(p.player)! +            const tile = tiles.get(p.tile.toString())! +            tile.item = player.item; +            player.item = undefined              break;          } -        case "produce_item": -            items.set(p.id, { kind: p.kind, x: p.pos[0] + 0.5, y: p.pos[1] + 0.5, tracking_player: false, tile: { x: p.pos[0], y: p.pos[1] } }) -            tiles.get(p.pos.toString())!.items.push(p.id) +        case "produce_item": { +            const item = { kind: p.item, x: p.tile[0] + 0.5, y: p.tile[1] + 0.5 } +            tiles.get(p.tile.toString())!.item = item              break; +        }          case "consume_item": { -            const t = tiles.get(p.pos.toString())! -            t.items.splice(t.items.indexOf(p.id)) -            items.delete(p.id) +            tiles.get(p.tile.toString())!.item = undefined              break;          }          case "set_active": { -            tiles.get(p.tile.toString())!.active_progress = p.progress +            tiles.get(p.tile.toString())!.item!.progress = p.progress              break;          }          case "update_map": -            tiles.set(p.pos.toString(), { x: p.pos[0], y: p.pos[1], kind: p.tile, items: [] }) +            tiles.set(p.pos.toString(), { x: p.pos[0], y: p.pos[1], kind: p.tile })              break;          default:              console.warn("unknown packet", p); @@ -136,6 +158,7 @@ function tick_update() {      send({ type: "position", pos: [p.x, p.y], rot: p.rot })  } +  function frame_update(dt: number) {      const p = players.get(my_id)      if (!p) return @@ -147,26 +170,37 @@ function frame_update(dt: number) {      if (length(input) > 0.1) lerp_exp_v2_mut(p.facing, input, dt * 10.)      p.rot = Math.atan2(p.facing.x, p.facing.y) -    p.x += input.x * dt * 5 -    p.y += input.y * dt * 5 +    p.vel.x += input.x * dt * 0.5 +    p.vel.y += input.y * dt * 0.5 +    p.x += p.vel.x +    p.y += p.vel.y +    collide_player(p) +    lerp_exp_v2_mut(p.vel, { x: 0, y: 0 }, dt * 5.) + -    for (const [_, i] of items) { -        lerp_exp_v2_mut(i, i.tracking_player ? players.get(i.player!)! : add_v2(i.tile!, 0.5), dt * 10.) +    const update_item = (item: ItemData, parent: V2) => { +        lerp_exp_v2_mut(item, parent, dt * 10.) +    } +    for (const [_, player] of players) { +        if (player.item) update_item(player.item, player)      } +    for (const [_, tile] of tiles) { +        if (tile.item) update_item(tile.item, add_v2(tile, 0.5)) +    } +      lerp_exp_v2_mut(interact_target_anim, get_interact_target() ?? { x: 0, y: 0 }, dt * 15.)      lerp_exp_v2_mut(camera, p, dt * 10.)  } -  function resize() {      canvas.width = globalThis.innerWidth      canvas.height = globalThis.innerHeight  } -let last_frame = Date.now() +let last_frame = performance.now()  function draw() { -    const now = Date.now() +    const now = performance.now()      frame_update((now - last_frame) / 1000)      last_frame = now;      if (ws.readyState == ws.CONNECTING) draw_wait("Connecting...") @@ -226,29 +260,26 @@ function draw_ingame() {          ctx.save()          ctx.translate(player.x, player.y)          ctx.rotate(-player.rot) +          ctx.fillStyle = "rgb(226, 176, 26)" -        const psize = 0.6; -        ctx.fillRect(-psize / 2, -psize / 2, psize, psize) -        ctx.restore() -    } +        ctx.beginPath() +        ctx.arc(0, 0, PLAYER_SIZE, 0, Math.PI * 2) +        ctx.fill() + +        ctx.fillStyle = "rgb(103, 79, 7)" +        ctx.beginPath() +        ctx.arc(0, -0.2, PLAYER_SIZE, 0, Math.PI * 2) +        ctx.fill() -    for (const [_, item] of items) { -        ctx.save() -        ctx.translate(item.x, item.y) -        const comps = ITEMS[data.item_names[item.kind]] ?? FALLBACK_ITEM -        for (const c of comps) { -            c(ctx) -        }          ctx.restore() + +        if (player.item) draw_item(player.item)      }      for (const [_, tile] of tiles) {          ctx.save()          ctx.translate(tile.x, tile.y) -        if (tile.active_progress !== null && tile.active_progress !== undefined) { -            ctx.fillStyle = "rgba(115, 230, 58, 0.66)" -            ctx.fillRect(0, 0, 1, tile.active_progress) -        } +        if (tile.item) draw_item(tile.item)          ctx.restore()      } @@ -257,6 +288,20 @@ function draw_ingame() {      ctx.restore()  } +function draw_item(item: ItemData) { +    ctx.save() +    ctx.translate(item.x, item.y) +    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, 0, 1, item.progress) +    } +    ctx.restore() +} +  function draw_interact_target() {      ctx.save()      ctx.translate(interact_target_anim.x, interact_target_anim.y) @@ -286,3 +331,27 @@ function draw_grid() {      }      ctx.stroke()  } + +function collide_player(p: PlayerData) { +    const tiles_ignored = ["floor", "door", "chair"].map(t => data.tile_names.indexOf(t)) +    for (const [_, tile] of tiles) { +        if (tiles_ignored.includes(tile.kind)) continue +        const d = aabb_circle_distance(tile.x, tile.y, tile.x + 1, tile.y + 1, p.x, p.y) +        if (d > PLAYER_SIZE) continue + +        const h = 0.01 +        const d_sample_x = aabb_circle_distance(tile.x, tile.y, tile.x + 1, tile.y + 1, p.x + h, p.y) +        const d_sample_y = aabb_circle_distance(tile.x, tile.y, tile.x + 1, tile.y + 1, p.x, p.y + h) +        const grad_x = (d_sample_x - d) / h +        const grad_y = (d_sample_y - d) / h + +        p.x += (PLAYER_SIZE - d) * grad_x +        p.y += (PLAYER_SIZE - d) * grad_y + +        const vdotn = (grad_x * p.vel.x) + (grad_y * p.vel.y) +        p.vel.x -= grad_x * vdotn +        p.vel.y -= grad_y * vdotn + + +    } +} diff --git a/test-client/protocol.ts b/test-client/protocol.ts index 96657db7..98fcbf39 100644 --- a/test-client/protocol.ts +++ b/test-client/protocol.ts @@ -1,12 +1,12 @@  export type Vec2 = [number, number] // x, y  export type PlayerID = number -export type ItemID = number  export type ItemIndex = number  export type TileIndex = number  export interface Gamedata {      item_names: string[], // Look-up table for ItemIndex      tile_names: string[], // Look-up table for TileIndex +    spawn: Vec2, // Where players spawn when they join.  }  export type PacketS = @@ -17,13 +17,12 @@ export type PacketS =  export type PacketC =      { type: "init", id: PlayerID, data: Gamedata } // You joined -    | { type: "add_player", id: PlayerID, name: string, hand?: [ItemID, ItemIndex] } // Somebody else joined (or was already in the game) +    | { type: "add_player", id: PlayerID, name: string, item?: ItemIndex } // Somebody else joined (or was already in the game)      | { type: "remove_player", id: PlayerID }  // Somebody left      | { type: "position", player: PlayerID, pos: Vec2, rot: number } // Update the position of a players (your own position is included here) -    | { type: "take_item", item: ItemID, player: PlayerID }  // An item was taken from a tile -    | { type: "put_item", item: ItemID, pos: Vec2 } // An item was put on a tile -    | { type: "produce_item", id: ItemID, pos: Vec2, kind: ItemIndex } // A tile generated a new item -    | { type: "consume_item", id: ItemID, pos: Vec2 } // A tile removed an item +    | { type: "take_item", tile: Vec2, player: PlayerID }  // An item was taken from a tile +    | { type: "put_item", tile: Vec2, player: PlayerID } // An item was put on a tile +    | { type: "produce_item", tile: Vec2, item: ItemIndex } // A tile generated a new item +    | { type: "consume_item", tile: Vec2 } // A tile removed an item      | { type: "set_active", tile: Vec2, progress?: number } // A tile is doing something. progress goes from 0 to 1, then null when finished      | { type: "update_map", pos: Vec2, tile: TileIndex } // A map tile was changed - diff --git a/test-client/tiles.ts b/test-client/tiles.ts index 57d96c0f..5eee2b32 100644 --- a/test-client/tiles.ts +++ b/test-client/tiles.ts @@ -55,6 +55,11 @@ function arrange_items(...items: string[]): Component[] {      })  } +const door: Component = c => { +    c.fillStyle = "#ff9843" +    c.fillRect(-0.5, -0.1, 1, 0.2) +} +  const plate = [circle(0.4, "#b6b6b6", "#f7f7f7", 0.02)];  export const FALLBACK_ITEM: Component[] = [circle(0.3, "#f0f")]; @@ -76,19 +81,25 @@ export const ITEMS: { [key: string]: Component[] } = {  const table = [base("rgb(133, 76, 38)")];  const floor = [base("#333", "#222", 0.05)]; -const spawn = (i: string) => [base("#60701e", "#b9da37", 0.05), ...ITEMS[i]]; +const counter = [base("rgb(182, 172, 164)")]; +const crate = (i: string) => [base("#60701e", "#b9da37", 0.05), ...ITEMS[i]];  export const FALLBACK_TILE: Component[] = [base("#f0f")];  export const TILES: { [key: string]: Component[] } = {      "floor": floor,      "table": table, -    "counter": [base("rgb(182, 172, 164)")], +    "counter": counter, +    "door": [...floor, door], +    "chair": [...floor, circle(0.45, "rgb(136, 83, 41)")], +    "wall": [base("rgb(0, 14, 56)")], +    "window": [base("rgb(233, 233, 233)")], +    "watercooler": [...floor, circle(0.4, "rgb(64, 226, 207)")],      "trash": [...floor, circle(0.4, "rgb(20, 20, 20)"), cross(0.3, "rgb(90, 36, 36)")],      "sink": [base("rgb(131, 129, 161)", "rgb(177, 174, 226)", 0.2)],      "oven": [base("rgb(241, 97, 61)", "rgb(109, 84, 84)", 0.3)], -    "pan": [...table, circle(0.4, "#444", "#999")], -    "flour-spawn": spawn("flour"), -    "dirty-plate-spawn": spawn("dirty-plate"), -    "raw-steak-spawn": spawn("raw-steak"), -    "tomato-spawn": spawn("tomato"), +    "pan": [...counter, circle(0.4, "#444", "#999")], +    "flour-crate": crate("flour"), +    "dirty-plate-crate": crate("dirty-plate"), +    "raw-steak-crate": crate("raw-steak"), +    "tomato-crate": crate("tomato"),  } diff --git a/test-client/util.ts b/test-client/util.ts index baebc61e..23679974 100644 --- a/test-client/util.ts +++ b/test-client/util.ts @@ -21,3 +21,16 @@ export function add_v2(p: V2, o: V2 | number) {      if (typeof o == "number") return { x: p.x + o, y: p.y + o }      else return { x: p.x + o.x, y: p.y + o.y }  } + +export function aabb_circle_distance( +    min_x: number, +    min_y: number, +    max_x: number, +    max_y: number, +    px: number, +    py: number +): number { +    const dx = px - Math.max(min_x, Math.min(max_x, px)) +    const dy = py - Math.max(min_y, Math.min(max_y, py)) +    return Math.sqrt(dx * dx + dy * dy) +} | 
