feat: 显示自定义工具栏

This commit is contained in:
unanmed 2023-12-17 18:11:58 +08:00
parent 9adcb5543f
commit 56ff2098f7
8 changed files with 330 additions and 1 deletions

View File

@ -105,3 +105,4 @@ dam4.png ---- 存档 59
[] 完善加载系统
[] 不同怪物可以在怪物手册中添加一些不同的边框
[] 按住一个按键时显示怪物的攻防血
[] 新的事件系统?

View File

@ -271,3 +271,10 @@ export function unwarpBinary(bin: number): AssistHoykey {
alt: !!(bin & (1 << 2))
};
}
export function checkAssist(bin: number, key: KeyCode) {
return !!(
(1 << (key === KeyCode.Ctrl ? 0 : key === KeyCode.Shift ? 1 : 2)) &
bin
);
}

View File

@ -0,0 +1,180 @@
import { EmitableEvent, EventEmitter } from '@/core/common/eventEmitter';
import { KeyCode } from '@/plugin/keyCodes';
import { flipBinary, has } from '@/plugin/utils';
import { FunctionalComponent, reactive } from 'vue';
import { createToolbarComponents } from '../init/toolbar';
import { gameKey } from '../init/hotkey';
import { unwarpBinary } from './hotkey';
interface CustomToolbarEvent extends EmitableEvent {
add: (item: ValueOf<ToolbarItemMap>) => void;
delete: (item: ValueOf<ToolbarItemMap>) => void;
set: (id: string, data: Partial<SettableItemData>) => void;
emit: (id: string) => void;
}
type ToolbarItemType = 'hotkey' | 'item' | 'assistKey';
interface ToolbarItemBase<T extends ToolbarItemType> {
type: T;
id: string;
com: CustomToolbarComponent<T>;
}
// 快捷键
interface HotkeyToolbarItem extends ToolbarItemBase<'hotkey'> {
key: KeyCode;
assist: number;
}
// 使用道具
interface ItemToolbarItem extends ToolbarItemBase<'item'> {
item: ItemIdOf<'tools' | 'constants'>;
}
// 切换辅助按键 ctrl alt shift
interface AssistKeyToolbarItem extends ToolbarItemBase<'assistKey'> {
assist: KeyCode.Ctrl | KeyCode.Shift | KeyCode.Alt;
}
interface ToolbarItemMap {
hotkey: HotkeyToolbarItem;
item: ItemToolbarItem;
assistKey: AssistKeyToolbarItem;
}
export type SettableItemData<T extends ToolbarItemType = ToolbarItemType> =
Omit<ToolbarItemMap[T], 'id' | 'type'>;
export interface CustomToolbarProps<
T extends ToolbarItemType = ToolbarItemType
> {
item: ToolbarItemMap[T];
toolbar: CustomToolbar;
}
export type CustomToolbarComponent<
T extends ToolbarItemType = ToolbarItemType
> = FunctionalComponent<CustomToolbarProps<T>>;
const COM = createToolbarComponents();
const comMap: {
[P in ToolbarItemType]: CustomToolbarComponent<P>;
} = {
hotkey: COM.KeyTool,
item: COM.ItemTool,
assistKey: COM.AssistKeyTool
};
export class CustomToolbar extends EventEmitter<CustomToolbarEvent> {
static num: number = 0;
static list: CustomToolbar[] = [];
items: ValueOf<ToolbarItemMap>[] = reactive([]);
num: number = CustomToolbar.num++;
id: string;
// ----- size
x: number = 300;
y: number = 300;
width: number = 300;
height: number = 100;
// ----- other
assistKey: number = 0;
constructor(id: string) {
super();
this.id = id;
CustomToolbar.list.push(this);
}
/**
*
* @param item
*/
add<K extends ToolbarItemType>(item: Omit<ToolbarItemMap[K], 'com'>) {
// @ts-ignore
const data: ToolbarItemMap[K] = {
com: comMap[item.type],
...item
} as ToolbarItemMap[K];
this.items.push(data);
this.emit('add', data);
}
/**
*
* @param id id
*/
delete(id: string) {
const index = this.items.findIndex(v => v.id === id);
if (index === -1) return;
const item = this.items[index];
this.items.splice(index, 1);
this.emit('delete', item);
}
/**
*
* @param id id
* @param item
*/
set<T extends ToolbarItemType>(
id: string,
item: Partial<SettableItemData<T>>
) {
const toSet = this.items.find(v => v.id === id);
if (!toSet) return;
Object.assign(toSet, item);
this.emit('set', id, item);
}
/**
*
* @param id id
*/
emitTool(id: string) {
const item = this.items.find(v => v.id === id);
if (!item) return;
this.emit(id);
if (item.type === 'hotkey') {
// 按键
const { ctrl, shift, alt } = unwarpBinary(
item.assist | this.assistKey
);
const ev = new KeyboardEvent('keyup', {
ctrlKey: ctrl,
shiftKey: shift,
altKey: alt
});
// todo: Advanced KeyboardEvent simulate
gameKey.emitKey(item.key, item.assist, 'up', ev);
} else if (item.type === 'item') {
// 道具
core.tryUseItem(item.item);
} else if (item.type === 'assistKey') {
// 辅助按键
if (item.assist === KeyCode.Ctrl) {
this.assistKey = flipBinary(this.assistKey, 0);
} else if (item.assist === KeyCode.Shift) {
this.assistKey = flipBinary(this.assistKey, 1);
} else if (item.assist === KeyCode.Alt) {
this.assistKey = flipBinary(this.assistKey, 2);
}
}
}
setPos(x?: number, y?: number) {
has(x) && (this.x = x);
has(y) && (this.y = y);
}
setSize(width?: number, height?: number) {
has(width) && (this.width = width);
has(height) && (this.height = height);
}
static get(id: string) {
return this.list.find(v => v.id === id);
}
}

View File

@ -0,0 +1,71 @@
import { KeyCodeUtils } from '@/plugin/keyCodes';
import type {
CustomToolbarComponent,
CustomToolbarProps
} from '../custom/toolbar';
import BoxAnimate from '@/components/boxAnimate.vue';
import { onUnmounted, ref } from 'vue';
import { checkAssist } from '../custom/hotkey';
interface Components {
DefaultTool: CustomToolbarComponent;
KeyTool: CustomToolbarComponent<'hotkey'>;
ItemTool: CustomToolbarComponent<'item'>;
AssistKeyTool: CustomToolbarComponent<'assistKey'>;
}
export function createToolbarComponents() {
const com: Components = {
DefaultTool,
KeyTool,
ItemTool,
AssistKeyTool
};
return com;
}
function DefaultTool(props: CustomToolbarProps) {
return <span></span>;
}
function KeyTool(props: CustomToolbarProps<'hotkey'>) {
const { item, toolbar } = props;
return (
<span onClick={() => toolbar.emitTool(item.id)}>
{KeyCodeUtils.toString(item.key)}
</span>
);
}
function ItemTool(props: CustomToolbarProps<'item'>) {
const { item, toolbar } = props;
return (
<div onClick={() => toolbar.emitTool(item.id)}>
<BoxAnimate id={item.item}></BoxAnimate>
</div>
);
}
function AssistKeyTool(props: CustomToolbarProps<'assistKey'>) {
const { item, toolbar } = props;
const pressed = ref(checkAssist(toolbar.assistKey, item.assist));
const listen = () => {
pressed.value = checkAssist(toolbar.assistKey, item.assist);
};
toolbar.on('emit', listen);
onUnmounted(() => {
toolbar.off('emit', listen);
});
return (
<span
class="button-text"
// @ts-ignore
active={pressed.value}
onClick={() => toolbar.emitTool(item.id)}
>
{KeyCodeUtils.toString(item.assist)}
</span>
);
}

View File

@ -28,7 +28,8 @@ fixedUi.register(
new GameUi('fixed', UI.Fixed),
new GameUi('chapter', UI.Chapter),
new GameUi('completeAchi', UI.CompleteAchi),
new GameUi('start', UI.Start)
new GameUi('start', UI.Start),
new GameUi('toolbar', UI.Toolbar)
);
fixedUi.showAll();

View File

@ -333,3 +333,9 @@ export function getStatusLabel(name: string) {
}[name] || name
);
}
export function flipBinary(num: number, col: number) {
const n = 1 << col;
if (num & col) return num & ~n;
else return num | n;
}

View File

@ -20,3 +20,4 @@ export { default as Studied } from './studied.vue';
export { default as Study } from './study.vue';
export { default as Toolbox } from './toolbox.vue';
export { default as Hotkey } from './hotkey.vue';
export { default as Toolbar } from './toolbar.vue';

62
src/ui/toolbar.vue Normal file
View File

@ -0,0 +1,62 @@
<template>
<Box
:dragable="true"
:resizable="true"
v-model:left="box.x"
v-model:top="box.y"
v-model:height="box.height"
v-model:width="box.width"
>
<div class="toolbar">
<div v-for="item of bar.items">
<component
:is="(item.com as any)"
:item="item"
:toolbar="bar"
></component>
</div>
</div>
</Box>
</template>
<script lang="ts" setup>
import Box from '@/components/box.vue';
import { CustomToolbar } from '@/core/main/custom/toolbar';
import { GameUi } from '@/core/main/custom/ui';
import { reactive, watch } from 'vue';
interface BoxData {
x: number;
y: number;
width: number;
height: number;
}
const props = defineProps<{
num: number;
ui: GameUi;
bar: CustomToolbar;
}>();
const bar = props.bar;
const box = reactive<BoxData>({
x: bar.x,
y: bar.y,
width: bar.width,
height: bar.height
});
watch(box, ({ x, y, width, height }) => {
bar.x = x;
bar.y = y;
bar.width = width;
bar.height = height;
});
</script>
<style lang="less" scoped>
.toolbar {
display: flex;
flex-direction: row;
}
</style>