76 lines
2.4 KiB
TypeScript
76 lines
2.4 KiB
TypeScript
import { gameConfig } from '../../config/gameConfig'
|
|
import type { Player } from '../../core/types'
|
|
import type { InputCommand, LocalBufferSource } from '../InputSource'
|
|
import { firstToken } from '../InputSource'
|
|
|
|
/** Тестовый источник без Twitch: локальная клавиатура, как в оригинале. */
|
|
export class LocalKeyboardInputSource implements LocalBufferSource {
|
|
readonly origin = 'local' as const
|
|
private buffer = ''
|
|
private attached = false
|
|
private readonly commandHandlers = new Set<(command: InputCommand) => void>()
|
|
private readonly bufferHandlers = new Set<(text: string) => void>()
|
|
|
|
constructor(private readonly channelName: string) {}
|
|
|
|
start(): void {
|
|
if (this.attached) return
|
|
window.addEventListener('keydown', this.onKeyDown)
|
|
this.attached = true
|
|
}
|
|
|
|
stop(): void {
|
|
if (!this.attached) return
|
|
window.removeEventListener('keydown', this.onKeyDown)
|
|
this.attached = false
|
|
}
|
|
|
|
onCommand(handler: (command: InputCommand) => void): () => void {
|
|
this.commandHandlers.add(handler)
|
|
return () => this.commandHandlers.delete(handler)
|
|
}
|
|
|
|
onBuffer(handler: (text: string) => void): () => void {
|
|
this.bufferHandlers.add(handler)
|
|
return () => this.bufferHandlers.delete(handler)
|
|
}
|
|
|
|
private onKeyDown = (event: KeyboardEvent): void => {
|
|
if (event.key === 'Backspace') {
|
|
event.preventDefault()
|
|
this.buffer = this.buffer.slice(0, -1)
|
|
this.emitBuffer()
|
|
} else if (event.key === 'Enter') {
|
|
event.preventDefault()
|
|
const line = this.buffer
|
|
this.buffer = ''
|
|
this.submit(line)
|
|
} else if (event.key === 'Escape') {
|
|
this.buffer = ''
|
|
this.emitCommand({ kind: 'clear' })
|
|
} else if (event.key.length === 1 && !event.ctrlKey && !event.altKey && !event.metaKey) {
|
|
this.buffer += event.key
|
|
this.emitBuffer()
|
|
}
|
|
}
|
|
|
|
private submit(line: string): void {
|
|
const word = firstToken(line)
|
|
if (!word) return
|
|
if (word === gameConfig.commands.play) {
|
|
this.emitCommand({ kind: 'start' })
|
|
return
|
|
}
|
|
const player: Player = { id: 'local', name: this.channelName }
|
|
this.emitCommand({ kind: 'word', word, player })
|
|
}
|
|
|
|
private emitCommand(command: InputCommand): void {
|
|
for (const handler of [...this.commandHandlers]) handler(command)
|
|
}
|
|
|
|
private emitBuffer(): void {
|
|
for (const handler of [...this.bufferHandlers]) handler(this.buffer)
|
|
}
|
|
}
|