mirror of
https://github.com/unanmed/HumanBreak.git
synced 2025-04-18 17:48:52 +08:00
365 lines
12 KiB
TypeScript
365 lines
12 KiB
TypeScript
import { DefaultProps, ElementLocator, Font } from '@motajs/render';
|
|
import { computed, defineComponent, ref } from 'vue';
|
|
import { SetupComponentOptions } from '../components';
|
|
import {
|
|
DanmakuIcon,
|
|
DoubleArrow,
|
|
NumpadIcon,
|
|
PauseIcon,
|
|
PlayIcon,
|
|
ReplayIcon,
|
|
RetweetIcon,
|
|
RollbackIcon,
|
|
StepForward,
|
|
ViewMapIcon
|
|
} from '../components/icons';
|
|
import {
|
|
generateBinary,
|
|
getVitualKeyOnce,
|
|
openDanmakuPoster
|
|
} from '@motajs/legacy-ui';
|
|
import { gameKey } from '@motajs/system-action';
|
|
import { generateKeyboardEvent } from '@motajs/system-action';
|
|
import { transitioned } from '../use';
|
|
import { linear } from 'mutate-animate';
|
|
import { KeyCode } from '@motajs/client-base';
|
|
import { Progress } from '../components/misc';
|
|
|
|
interface ToolbarProps extends DefaultProps {
|
|
loc?: ElementLocator;
|
|
}
|
|
|
|
type ToolbarEmits = {
|
|
numpad: () => void;
|
|
};
|
|
|
|
const toolbarProps = {
|
|
props: ['loc'],
|
|
emits: ['numpad']
|
|
} satisfies SetupComponentOptions<
|
|
ToolbarProps,
|
|
ToolbarEmits,
|
|
keyof ToolbarEmits
|
|
>;
|
|
|
|
const im = (col: number, row: number): ElementLocator => {
|
|
return [5 + 34 * col, 5 + 36 * row, 32, 32];
|
|
};
|
|
|
|
const ic = (col: number, row: number): ElementLocator => {
|
|
return [7 + 34 * col, 7 + 36 * row, 28, 28];
|
|
};
|
|
|
|
const ic2 = (col: number, row: number): ElementLocator => {
|
|
return [9 + 34 * col, 9 + 36 * row, 24, 24];
|
|
};
|
|
|
|
const middle = (col: number, row: number): ElementLocator => {
|
|
return [21 + 34 * col, 21 + 36 * row, void 0, void 0, 0.5, 0.5];
|
|
};
|
|
|
|
const middle2 = (
|
|
col: number,
|
|
row: number,
|
|
width: number,
|
|
height: number
|
|
): ElementLocator => {
|
|
return [21 + 34 * col, 21 + 36 * row, width, height, 0.5, 0.5];
|
|
};
|
|
|
|
export const PlayingToolbar = defineComponent<
|
|
ToolbarProps,
|
|
ToolbarEmits,
|
|
keyof ToolbarEmits
|
|
>((props, { emit }) => {
|
|
const bookIcon = core.statusBar.icons.book;
|
|
const flyIcon = core.statusBar.icons.fly;
|
|
const toolIcon = core.statusBar.icons.toolbox;
|
|
const equipIcon = core.statusBar.icons.equipbox;
|
|
const keyIcon = core.statusBar.icons.keyboard;
|
|
const shopIcon = core.statusBar.icons.shop;
|
|
const saveIcon = core.statusBar.icons.save;
|
|
const loadIcon = core.statusBar.icons.load;
|
|
const setIcon = core.statusBar.icons.settings;
|
|
|
|
const iconFont = new Font('Verdana', 12);
|
|
|
|
const book = () => core.openBook(true);
|
|
const tool = () => core.openEquipbox(true);
|
|
const fly = () => core.useFly(true);
|
|
const save = () => core.save(true);
|
|
const load = () => core.load(true);
|
|
const equip = () => core.openEquipbox(true);
|
|
const shop = () => core.openQuickShop(true);
|
|
const key = () => {
|
|
getVitualKeyOnce().then(value => {
|
|
gameKey.emitKey(
|
|
value.key,
|
|
value.assist,
|
|
'up',
|
|
generateKeyboardEvent(value.key, value.assist)
|
|
);
|
|
});
|
|
};
|
|
const undo = () => core.doSL('autoSave', 'load');
|
|
const redo = () => core.doSL('autoSave', 'reload');
|
|
const numpad = () => emit('numpad');
|
|
const view = () => {
|
|
if (core.isPlaying() && !core.isMoving() && !core.status.lockControl) {
|
|
core.ui._drawViewMaps();
|
|
}
|
|
};
|
|
const danmaku = () => requestAnimationFrame(openDanmakuPoster);
|
|
const replay = () => core.ui._drawReplay();
|
|
const settings = () => core.openSettings(true);
|
|
|
|
return () => (
|
|
<container loc={props.loc} cursor="pointer">
|
|
<image image={bookIcon} loc={im(0, 0)} noanti onClick={book} />
|
|
<image image={toolIcon} loc={im(1, 0)} noanti onClick={tool} />
|
|
<image image={flyIcon} loc={im(2, 0)} noanti onClick={fly} />
|
|
<image image={saveIcon} loc={im(3, 0)} noanti onClick={save} />
|
|
<image image={loadIcon} loc={im(4, 0)} noanti onClick={load} />
|
|
<image image={equipIcon} loc={im(0, 1)} noanti onClick={equip} />
|
|
<image image={shopIcon} loc={im(1, 1)} noanti onClick={shop} />
|
|
<image image={keyIcon} loc={im(2, 1)} noanti onClick={key} />
|
|
<RollbackIcon loc={ic(3, 1)} strokeStyle="#eee" onClick={undo} />
|
|
<RetweetIcon loc={ic(4, 1)} strokeStyle="#eee" onClick={redo} />
|
|
<NumpadIcon loc={ic(0, 2)} strokeStyle="#eee" onClick={numpad} />
|
|
<ViewMapIcon loc={ic(1, 2)} strokeStyle="#eee" onClick={view} />
|
|
<DanmakuIcon loc={ic(2, 2)} strokeStyle="#eee" onClick={danmaku} />
|
|
<ReplayIcon loc={ic(3, 2)} strokeStyle="#eee" onClick={replay} />
|
|
<text text="R" loc={middle(3, 2)} font={iconFont} noevent />
|
|
<image image={setIcon} loc={im(4, 2)} noanti onClick={settings} />
|
|
</container>
|
|
);
|
|
}, toolbarProps);
|
|
|
|
export interface ReplayingStatus {
|
|
/** 是否正在播放 */
|
|
playing: boolean;
|
|
/** 录像播放速度 */
|
|
speed: number;
|
|
/** 已播放的长度 */
|
|
played: number;
|
|
/** 总长度 */
|
|
total: number;
|
|
}
|
|
|
|
export interface ReplayingProps extends ToolbarProps {
|
|
/** 录像播放状态 */
|
|
status: ReplayingStatus;
|
|
}
|
|
|
|
const replayingProps = {
|
|
props: ['status', 'loc']
|
|
} satisfies SetupComponentOptions<ReplayingProps>;
|
|
|
|
export const ReplayingToolbar = defineComponent<ReplayingProps>(props => {
|
|
const status = props.status;
|
|
|
|
const bookIcon = core.statusBar.icons.book;
|
|
const saveIcon = core.statusBar.icons.save;
|
|
const font1 = new Font('normal', 16);
|
|
const font2 = new Font('Verdana', 12);
|
|
|
|
const speedText = computed(() => `${status.speed}速`);
|
|
const progress = computed(() => status.played / status.total);
|
|
const progressText1 = computed(() => `${status.played}/${status.total}`);
|
|
const progressText2 = computed(
|
|
() => `${(progress.value * 100).toFixed(2)}%`
|
|
);
|
|
|
|
const play = () => core.resumeReplay();
|
|
const pause = () => core.pauseReplay();
|
|
const stop = () => core.stopReplay(true);
|
|
const speedDown = () => core.speedDownReplay();
|
|
const speedUp = () => core.speedUpReplay();
|
|
const book = () => core.openBook(true);
|
|
const save = () => core.save(true);
|
|
const view = () => {
|
|
if (core.isPlaying() && !core.isMoving() && !core.status.lockControl) {
|
|
core.ui._drawViewMaps();
|
|
}
|
|
};
|
|
const rewind = () => core.rewindReplay();
|
|
const step = () => core.stepReplay();
|
|
|
|
return () => {
|
|
return (
|
|
<container loc={props.loc} cursor="pointer">
|
|
{status.playing ? (
|
|
<PauseIcon loc={ic(0, 0)} onClick={pause} />
|
|
) : (
|
|
<PlayIcon loc={ic(0, 0)} onClick={play} />
|
|
)}
|
|
<g-rectr loc={[47, 13, 16, 16]} circle={[2]} onClick={stop} />
|
|
<DoubleArrow
|
|
loc={middle2(2, 0, 28, 28)}
|
|
scale={[-1, 1]}
|
|
onClick={speedDown}
|
|
/>
|
|
<text text={speedText.value} loc={middle(3, 0)} font={font1} />
|
|
<DoubleArrow loc={ic(4, 0)} onClick={speedUp} />
|
|
<image image={bookIcon} loc={im(0, 1)} noanti onClick={book} />
|
|
<image image={saveIcon} loc={im(1, 1)} noanti onClick={save} />
|
|
<ViewMapIcon loc={ic(2, 1)} onClick={view} />
|
|
<StepForward
|
|
loc={middle2(3, 1, 28, 28)}
|
|
scale={[-1, 1]}
|
|
onClick={rewind}
|
|
/>
|
|
<StepForward loc={ic(4, 1)} onClick={step} />
|
|
<text
|
|
text={progressText1.value}
|
|
loc={[12, 98, void 0, void 0, 0, 1]}
|
|
font={font2}
|
|
/>
|
|
<text
|
|
text={progressText2.value}
|
|
loc={[168, 98, void 0, void 0, 1, 1]}
|
|
font={font2}
|
|
/>
|
|
<Progress
|
|
loc={[12, 101, 156, 4]}
|
|
progress={progress.value}
|
|
success="lightgreen"
|
|
/>
|
|
</container>
|
|
);
|
|
};
|
|
}, replayingProps);
|
|
|
|
export const NumpadToolbar = defineComponent<
|
|
ToolbarProps,
|
|
ToolbarEmits,
|
|
keyof ToolbarEmits
|
|
>((props, { emit }) => {
|
|
const numpad = () => emit('numpad');
|
|
const nums = [1, 2, 3, 4, 5, 6, 7, 8, 9, 0];
|
|
|
|
const ctrlEnabled = ref(false);
|
|
const shiftEnabled = ref(false);
|
|
const altEnabled = ref(false);
|
|
|
|
const ctrlAlpha = transitioned(0, 100, linear())!;
|
|
const shiftAlpha = transitioned(0, 100, linear())!;
|
|
const altAlpha = transitioned(0, 100, linear())!;
|
|
|
|
const ctrlColor = computed(
|
|
() => `rgba(221,221,221,${ctrlAlpha.ref.value})`
|
|
);
|
|
const ctrlTextColor = computed(() => {
|
|
const rgb = Math.floor(255 - ctrlAlpha.ref.value * 255);
|
|
return `rgba(${rgb},${rgb},${rgb},1)`;
|
|
});
|
|
|
|
const shiftColor = computed(
|
|
() => `rgba(221,221,221,${shiftAlpha.ref.value})`
|
|
);
|
|
const shiftTextColor = computed(() => {
|
|
const rgb = Math.floor(255 - shiftAlpha.ref.value * 255);
|
|
return `rgba(${rgb},${rgb},${rgb},1)`;
|
|
});
|
|
|
|
const altColor = computed(() => `rgba(221,221,221,${altAlpha.ref.value})`);
|
|
const altTextColor = computed(() => {
|
|
const rgb = Math.floor(255 - altAlpha.ref.value * 255);
|
|
return `rgba(${rgb},${rgb},${rgb},1)`;
|
|
});
|
|
|
|
const clickCtrl = () => {
|
|
ctrlEnabled.value = !ctrlEnabled.value;
|
|
ctrlAlpha.set(ctrlEnabled.value ? 1 : 0);
|
|
};
|
|
|
|
const clickShift = () => {
|
|
shiftEnabled.value = !shiftEnabled.value;
|
|
shiftAlpha.set(shiftEnabled.value ? 1 : 0);
|
|
};
|
|
|
|
const clickAlt = () => {
|
|
altEnabled.value = !altEnabled.value;
|
|
altAlpha.set(altEnabled.value ? 1 : 0);
|
|
};
|
|
|
|
const clickNum = (num: number) => {
|
|
const bin = generateBinary([
|
|
ctrlEnabled.value,
|
|
shiftEnabled.value,
|
|
altEnabled.value
|
|
]);
|
|
const code = (KeyCode.Digit0 + num) as KeyCode;
|
|
gameKey.emitKey(code, bin, 'up', generateKeyboardEvent(code, bin));
|
|
};
|
|
|
|
return () => (
|
|
<container loc={props.loc} cursor="pointer">
|
|
<container loc={[0, 0, 180, 81]}>
|
|
{nums
|
|
.map((v, i) => {
|
|
const col = i % 5;
|
|
const row = Math.floor(i / 5);
|
|
return [
|
|
<g-rectr
|
|
loc={ic2(col, row)}
|
|
circle={[4]}
|
|
stroke
|
|
onClick={() => clickNum(v)}
|
|
/>,
|
|
<text
|
|
text={v.toString()}
|
|
loc={middle(col, row)}
|
|
noevent
|
|
/>
|
|
];
|
|
})
|
|
.flat()}
|
|
</container>
|
|
<g-rectr
|
|
loc={[41, 81, 36, 24]}
|
|
circle={[4]}
|
|
stroke
|
|
fill
|
|
fillStyle={ctrlColor.value}
|
|
onClick={clickCtrl}
|
|
></g-rectr>
|
|
<text
|
|
text="Ctrl"
|
|
loc={[59, 93, void 0, void 0, 0.5, 0.5]}
|
|
fillStyle={ctrlTextColor.value}
|
|
noevent
|
|
/>
|
|
<g-rectr
|
|
loc={[86, 81, 44, 24]}
|
|
circle={[4]}
|
|
stroke
|
|
fill
|
|
fillStyle={shiftColor.value}
|
|
onClick={clickShift}
|
|
></g-rectr>
|
|
<text
|
|
text="Shift"
|
|
loc={[108, 93, void 0, void 0, 0.5, 0.5]}
|
|
fillStyle={shiftTextColor.value}
|
|
noevent
|
|
/>
|
|
<g-rectr
|
|
loc={[139, 81, 30, 24]}
|
|
circle={[4]}
|
|
stroke
|
|
fill
|
|
fillStyle={altColor.value}
|
|
onClick={clickAlt}
|
|
></g-rectr>
|
|
<text
|
|
text="Alt"
|
|
loc={[154, 93, void 0, void 0, 0.5, 0.5]}
|
|
fillStyle={altTextColor.value}
|
|
noevent
|
|
/>
|
|
<NumpadIcon loc={ic(0, 2)} strokeStyle="gold" onClick={numpad} />
|
|
</container>
|
|
);
|
|
}, toolbarProps);
|