diff options
author | metamuffin <metamuffin@disroot.org> | 2025-06-08 14:35:57 +0200 |
---|---|---|
committer | metamuffin <metamuffin@disroot.org> | 2025-06-08 14:35:57 +0200 |
commit | 544054658e4ec7cda694e902eebd7494baf448d5 (patch) | |
tree | 0495c1cbb9aacb87cc8297480df58c545c08ba9f /test-client | |
parent | dc2d0509963afb2d0b23aeb8a9085ade5e5bba93 (diff) | |
download | hurrycurry-544054658e4ec7cda694e902eebd7494baf448d5.tar hurrycurry-544054658e4ec7cda694e902eebd7494baf448d5.tar.bz2 hurrycurry-544054658e4ec7cda694e902eebd7494baf448d5.tar.zst |
test-client: proper two handed mode; closes #246
Diffstat (limited to 'test-client')
-rw-r--r-- | test-client/main.ts | 50 | ||||
-rw-r--r-- | test-client/protocol.ts | 1 | ||||
-rw-r--r-- | test-client/visual.ts | 7 |
3 files changed, 38 insertions, 20 deletions
diff --git a/test-client/main.ts b/test-client/main.ts index 8d19be51..18382b04 100644 --- a/test-client/main.ts +++ b/test-client/main.ts @@ -24,7 +24,8 @@ import { Gamedata, ItemIndex, ItemLocation, Message, MessageTimeout, PacketC, Pa import { V2, lerp_exp_v2_mut, normalize, lerp_exp } from "./util.ts"; import { draw_ingame, draw_wait } from "./visual.ts"; -const KEY_INTERACT = "KeyJ" +const KEY_INTERACT_LEFT = "KeyJ" +const KEY_INTERACT_RIGHT = "KeyL" const KEY_BOOST = "KeyK" const KEY_UP = "KeyW" const KEY_DOWN = "KeyS" @@ -32,7 +33,8 @@ const KEY_LEFT = "KeyA" const KEY_RIGHT = "KeyD" const KEY_CHAT = "Enter" const KEY_CLOSE = "Escape" -const HANDLED_KEYS = [KEY_INTERACT, KEY_BOOST, KEY_CHAT, KEY_CLOSE, KEY_DOWN, KEY_UP, KEY_LEFT, KEY_RIGHT] +const KEY_ZOOM = "KeyI" +const HANDLED_KEYS = [KEY_INTERACT_LEFT, KEY_BOOST, KEY_CHAT, KEY_CLOSE, KEY_DOWN, KEY_UP, KEY_LEFT, KEY_RIGHT] export let ctx: CanvasRenderingContext2D; export let canvas: HTMLCanvasElement; @@ -80,10 +82,13 @@ export interface Involvement { speed: number, warn: boolean } +export interface ItemSlot { + item?: ItemData, +} export interface PlayerData extends MovementBase { id: number, name: string, - item?: ItemData, + hands: ItemSlot[], direction: V2, class: PlayerClass, character: number, @@ -93,11 +98,10 @@ export interface PlayerData extends MovementBase { message?: MessageData, } -export interface TileData { +export interface TileData extends ItemSlot { x: number, y: number // tile position position: V2, // center position kind: TileIndex - item?: ItemData } export type MessageStyle = "hint" | "normal" | "error" | "pinned" export interface MessageData { @@ -113,7 +117,7 @@ export const tiles = new Map<string, TileData>() export const items_removed = new Set<ItemData>() export const server_hints = new Map<string, MessageData>() -export let data: Gamedata = { item_names: [], tile_names: [], spawn: [0, 0], tile_collide: [], tile_interact: [], maps: [] } +export let data: Gamedata = { item_names: [], tile_names: [], spawn: [0, 0], tile_collide: [], tile_interact: [], maps: [], hand_count: 0 } export let global_message: MessageData | undefined = undefined export let my_id: PlayerID = -1 @@ -128,9 +132,14 @@ export let is_lobby = false let interacting: V2 | undefined; let last_server_sent_position: V2 = { x: 0, y: 0 } -function get_item_location(loc: ItemLocation): PlayerData | TileData { +function get_item_location(loc: ItemLocation): ItemSlot { if ("tile" in loc) return tiles.get(loc.tile.toString())! - if ("player" in loc) return players.get(loc.player[0])! + if ("player" in loc) return players.get(loc.player[0])!.hands[loc.player[1]] + throw new Error("invalid item location"); +} +function get_item_location_tracking(loc: ItemLocation): V2 { + if ("tile" in loc) return tiles.get(loc.tile.toString())!.position + if ("player" in loc) return players.get(loc.player[0])!.position throw new Error("invalid item location"); } @@ -154,6 +163,7 @@ function packet(p: PacketC) { case "add_player": { players.set(p.id, { id: p.id, + hands: new Array(data.hand_count).fill(null).map(() => ({})), position: { x: p.position[0], y: p.position[1], }, anim_position: { x: p.position[0], y: p.position[1] }, character: p.character.color, @@ -189,16 +199,17 @@ function packet(p: PacketC) { const to = get_item_location(p.to) to.item = from.item - to.item!.tracking = to.position + to.item!.tracking = get_item_location_tracking(p.to) from.item = undefined break; } case "set_item": { const slot = get_item_location(p.location) + const slotpos = get_item_location_tracking(p.location) if (slot.item !== undefined && slot.item !== null) items_removed.add(slot.item) - slot.item = undefined - if (p.item !== undefined && p.item !== null) slot.item = { kind: p.item, x: slot.position.x, y: slot.position.y, tracking: slot.position } + delete slot.item + if (p.item !== undefined && p.item !== null) slot.item = { kind: p.item, x: slotpos.x, y: slotpos.y, tracking: slotpos } break; } case "clear_progress": { @@ -345,8 +356,10 @@ function keyboard(ev: KeyboardEvent, down: boolean) { else if (down && ev.code == KEY_CLOSE && chat) return close_chat() else if (chat) return if (HANDLED_KEYS.includes(ev.code)) ev.preventDefault() - if (!keys_down.has(KEY_INTERACT) && ev.code == KEY_INTERACT && down) set_interact(true) - if (keys_down.has(KEY_INTERACT) && ev.code == KEY_INTERACT && !down) set_interact(false) + if (!keys_down.has(KEY_INTERACT_LEFT) && ev.code == KEY_INTERACT_LEFT && down) set_interact(true, 0) + if (keys_down.has(KEY_INTERACT_LEFT) && ev.code == KEY_INTERACT_LEFT && !down) set_interact(false, 0) + if (!keys_down.has(KEY_INTERACT_RIGHT) && ev.code == KEY_INTERACT_RIGHT && down) set_interact(true, 1) + if (keys_down.has(KEY_INTERACT_RIGHT) && ev.code == KEY_INTERACT_RIGHT && !down) set_interact(false, 1) if (down && ev.code in QUICK_COMMANDS) send({ player: my_id, type: "communicate", message: { text: QUICK_COMMANDS[ev.code] } }) if (down && ev.code == "KeyE") particle_splash(get_interact_target() ?? { x: 0, y: 0 }, 0.8) if (down) keys_down.add(ev.code) @@ -412,9 +425,9 @@ export function get_interact_target(): V2 | undefined { return best } -function set_interact(edge: boolean) { +function set_interact(edge: boolean, hand: number) { if (edge) interacting = get_interact_target() - if (interacting) send({ player: my_id, type: "interact", pos: edge ? [interacting.x, interacting.y] : undefined, hand: 0 }) + if (interacting) send({ player: my_id, type: "interact", pos: edge ? [interacting.x, interacting.y] : undefined, hand }) if (!edge) interacting = undefined } @@ -445,13 +458,12 @@ function frame_update(dt: number) { const update_item = (item: ItemData) => { if (item.tracking) lerp_exp_v2_mut(item, item.tracking, dt * 10.) if (item.active) item.active.position += item.active.speed * dt - } let pin_xo = 0 for (const [pid, player] of players) { if (pid == my_id) player.anim_position.x = player.position.x, player.anim_position.y = player.position.y else lerp_exp_v2_mut(player.anim_position, player.position, dt * 15) - if (player.item !== undefined && player.item !== null) update_item(player.item) + player.hands.forEach(h => { if (h.item) update_item(h.item) }) if (player.message && tick_message(player.message, dt)) delete player.message if (player.message_persist && tick_message(player.message_persist, dt)) delete player.message_persist if (player.message_pinned && tick_message(player.message_pinned, dt)) delete player.message_pinned @@ -482,9 +494,9 @@ function frame_update(dt: number) { interact_possible_anim = lerp_exp(interact_possible_anim, +possible, dt * 18.) interact_active_anim = lerp_exp(interact_active_anim, +!!interacting, dt * 15.) - const zoom_target = Math.min(canvas.width, canvas.height) * (keys_down.has("KeyL") ? 0.05 : 0.1) + const zoom_target = Math.min(canvas.width, canvas.height) * (keys_down.has(KEY_ZOOM) ? 0.05 : 0.1) camera_scale = lerp_exp(camera_scale, zoom_target, dt * 5) - overlay_vis_anim = lerp_exp(overlay_vis_anim, +keys_down.has("KeyL"), dt * 10) + overlay_vis_anim = lerp_exp(overlay_vis_anim, +keys_down.has(KEY_ZOOM), dt * 10) tick_particles(dt) } diff --git a/test-client/protocol.ts b/test-client/protocol.ts index 1cfdab79..fb8c11a8 100644 --- a/test-client/protocol.ts +++ b/test-client/protocol.ts @@ -34,6 +34,7 @@ export interface Gamedata { tile_interact: boolean[], // Look-up table for TileIndex to check if a tile is interactable spawn: Vec2, // Where players spawn when they join. maps: [string, MapMetadata][], // Metadata for most available maps + hand_count: number, } export type PacketS = diff --git a/test-client/visual.ts b/test-client/visual.ts index c5f34c1c..80c36da2 100644 --- a/test-client/visual.ts +++ b/test-client/visual.ts @@ -147,7 +147,12 @@ function draw_player(player: PlayerData) { if (player.boosting) ctx.scale(1.3, 1.3) draw_character(player.class, player.character) ctx.restore() - if (player.item) draw_item(player.item) + ctx.save() + for (const h of player.hands) { + if (h.item) draw_item(h.item) + ctx.translate(0.2, 0.0) + } + ctx.restore() } function draw_player_nametag(player: PlayerData) { |