HumanBreak/src/core/main/custom/keyboard.ts

202 lines
5.5 KiB
TypeScript

import { EventEmitter, Listener } from '@motajs/legacy-common';
import { KeyCode } from '@motajs/client-base';
import { gameKey } from './hotkey';
import { unwarpBinary } from './hotkey';
import { deleteWith, flipBinary } from '@motajs/legacy-ui';
import { cloneDeep } from 'lodash-es';
import { shallowReactive } from 'vue';
export interface KeyboardEmits {
key: KeyCode;
assist: number;
}
interface KeyboardItem {
key: KeyCode;
text?: string;
x: number;
y: number;
width: number;
height: number;
}
interface VirtualKeyEmit {
preventDefault(): void;
preventAssist(): void;
}
type VirtualKeyEmitFn = (
item: KeyboardItem,
assist: number,
index: number,
ev: VirtualKeyEmit
) => void;
interface VirtualKeyboardEvent {
add: (item: KeyboardItem) => void;
remove: (item: KeyboardItem) => void;
extend: (extended: Keyboard) => void;
emit: VirtualKeyEmitFn;
scopeCreate: (scope: symbol) => void;
scopeDispose: (scope: symbol) => void;
}
/**
* 虚拟按键,同一个虚拟按键实例应当只能同时操作一个,但可以显示多个
*/
export class Keyboard extends EventEmitter<VirtualKeyboardEvent> {
static list: Keyboard[] = [];
id: string;
keys: KeyboardItem[] = [];
assist: number = 0;
fontSize: number = 18;
scope: symbol = Symbol();
private scopeStack: symbol[] = [];
private onEmitKey: Record<symbol, Listener<VirtualKeyEmitFn>[]> = {};
private scopeAssist: Record<symbol, number> = {};
constructor(id: string) {
super();
this.id = id;
Keyboard.list.push(this);
}
/**
* 给虚拟键盘添加一个按键
* @param item 按键信息
*/
add(item: KeyboardItem) {
const i = shallowReactive(item);
this.keys.push(i);
this.emit('add', i);
return this;
}
/**
* 移除一个按键
* @param item 按键信息
*/
remove(item: KeyboardItem) {
deleteWith(this.keys, item);
this.emit('remove', item);
return this;
}
/**
* 创造一个在某些辅助按键已经按下的情况下的作用域,这些被按下的辅助按键还可以被玩家手动取消
* @param assist 辅助按键
*/
withAssist(assist: number) {
const symbol = this.createScope();
this.assist = assist;
return symbol;
}
/**
* 创造一个虚拟按键作用域,所有监听的事件与其他作用域不冲突
* @returns 作用域的唯一标识符
*/
createScope() {
const last = this.scopeStack.at(-1);
const symbol = Symbol();
this.scope = symbol;
this.scopeStack.push(symbol);
const ev: Listener<VirtualKeyEmitFn>[] = [];
this.onEmitKey[symbol] = ev;
// @ts-ignore
this.events = ev;
this.emit('scopeCreate', symbol);
if (!!last) {
this.scopeAssist[symbol] = this.assist;
}
this.assist = 0;
this.scopeAssist[symbol] = 0;
return symbol;
}
/**
* 释放一个作用域,同时删除其中的所有监听器
*/
disposeScope() {
if (this.scopeStack.length === 0) {
throw new Error(
`Cannot dispose virtual key scope since there's no scope to be disposed.`
);
}
const now = this.scopeStack.pop()!;
delete this.onEmitKey[now];
delete this.scopeAssist[now];
const symbol = this.scopeStack.at(-1);
this.emit('scopeDispose', now);
if (!symbol) return;
this.scope = symbol;
this.assist = this.scopeAssist[symbol];
// @ts-ignore
this.events = this.onEmitKey[symbol];
}
/**
* 继承一个按键的按键信息
* @param keyboard 要被继承的按键
* @param offsetX 被继承的按键的横坐标偏移量
* @param offsetY 被继承的按键的纵坐标偏移量
*/
extend(keyboard: Keyboard, offsetX: number = 0, offsetY: number = 0) {
const toClone = cloneDeep(keyboard.keys);
toClone.forEach(v => {
v.x += offsetX;
v.y += offsetY;
});
this.keys.push(...toClone);
this.emit('extend', keyboard);
return this;
}
/**
* 触发按键
* @param key 要触发的按键
*/
emitKey(key: KeyboardItem, index: number) {
let prevent = false;
let preventAss = false;
const preventDefault = () => (prevent = true);
const preventAssist = () => (preventAss = true);
this.emit('emit', key, this.assist, index, {
preventDefault,
preventAssist
});
if (!preventAss) {
if (key.key === KeyCode.Ctrl) {
this.assist = flipBinary(this.assist, 0);
} else if (key.key === KeyCode.Shift) {
this.assist = flipBinary(this.assist, 1);
} else if (key.key === KeyCode.Alt) {
this.assist = flipBinary(this.assist, 2);
}
}
if (!prevent) {
const ev = generateKeyboardEvent(key.key, this.assist);
gameKey.emitKey(key.key, this.assist, 'up', ev);
}
}
static get(id: string) {
return this.list.find(v => v.id === id);
}
}
export function generateKeyboardEvent(key: KeyCode, assist: number) {
const { ctrl, alt, shift } = unwarpBinary(assist);
const ev = new KeyboardEvent('keyup', {
ctrlKey: ctrl,
shiftKey: shift,
altKey: alt
});
return ev;
}