mirror of
https://github.com/unanmed/HumanBreak.git
synced 2025-02-28 01:17:06 +08:00
feat: 跟进 2.A 的更改
This commit is contained in:
parent
0dfb7e4b99
commit
86e6e76286
@ -8,4 +8,5 @@ public/project/maps.js
|
||||
public/_server/**/*.js
|
||||
script/**/*.js
|
||||
public/editor.html
|
||||
keyCodes.ts
|
||||
keyCodes.ts
|
||||
src/core/main/setting.ts
|
2
components.d.ts
vendored
2
components.d.ts
vendored
@ -10,7 +10,6 @@ declare module '@vue/runtime-core' {
|
||||
AButton: typeof import('ant-design-vue/es')['Button']
|
||||
ADivider: typeof import('ant-design-vue/es')['Divider']
|
||||
AInput: typeof import('ant-design-vue/es')['Input']
|
||||
AInputNumber: typeof import('ant-design-vue/es')['InputNumber']
|
||||
AProgress: typeof import('ant-design-vue/es')['Progress']
|
||||
ASelect: typeof import('ant-design-vue/es')['Select']
|
||||
ASelectOption: typeof import('ant-design-vue/es')['SelectOption']
|
||||
@ -18,7 +17,6 @@ declare module '@vue/runtime-core' {
|
||||
ASwitch: typeof import('ant-design-vue/es')['Switch']
|
||||
Box: typeof import('./src/components/box.vue')['default']
|
||||
BoxAnimate: typeof import('./src/components/boxAnimate.vue')['default']
|
||||
Changable: typeof import('./src/components/changable.vue')['default']
|
||||
Colomn: typeof import('./src/components/colomn.vue')['default']
|
||||
EnemyOne: typeof import('./src/components/enemyOne.vue')['default']
|
||||
Scroll: typeof import('./src/components/scroll.vue')['default']
|
||||
|
@ -602,6 +602,7 @@ core.prototype._afterLoadResources = function (callback) {
|
||||
|
||||
// if (core.plugin._afterLoadResources) core.plugin._afterLoadResources();
|
||||
core.showStartAnimate();
|
||||
Mota.require('var', 'hook').emit('load');
|
||||
if (callback) callback();
|
||||
};
|
||||
|
||||
|
@ -4421,24 +4421,31 @@ events.prototype._checkLvUp_check = function () {
|
||||
};
|
||||
|
||||
////// 尝试使用道具 //////
|
||||
events.prototype.tryUseItem = function (itemId) {
|
||||
events.prototype.tryUseItem = function (itemId, noRoute, callback) {
|
||||
if (itemId == 'book') {
|
||||
core.ui.closePanel();
|
||||
return core.openBook(false);
|
||||
core.openBook(false);
|
||||
callback();
|
||||
return;
|
||||
}
|
||||
if (itemId == 'fly') {
|
||||
core.ui.closePanel();
|
||||
return core.useFly(false);
|
||||
core.useFly(false);
|
||||
callback();
|
||||
return;
|
||||
}
|
||||
if (itemId == 'centerFly') {
|
||||
core.ui.closePanel();
|
||||
return core.ui._drawCenterFly();
|
||||
core.ui._drawCenterFly();
|
||||
callback();
|
||||
return;
|
||||
}
|
||||
if (core.canUseItem(itemId)) {
|
||||
core.ui.closePanel();
|
||||
core.useItem(itemId);
|
||||
core.useItem(itemId, noRoute, callback);
|
||||
} else {
|
||||
core.playSound('操作失败');
|
||||
core.drawTip('当前无法使用' + core.material.items[itemId].name, itemId);
|
||||
}
|
||||
callback();
|
||||
};
|
||||
|
@ -48,6 +48,7 @@ function show(index: number) {
|
||||
position: fixed;
|
||||
overflow: visible;
|
||||
display: block;
|
||||
font-size: 80%;
|
||||
font-family: 'normal';
|
||||
}
|
||||
|
||||
@ -61,7 +62,6 @@ function show(index: number) {
|
||||
top: 0;
|
||||
position: fixed;
|
||||
background-color: #000b;
|
||||
backdrop-filter: blur(5px);
|
||||
z-index: 1;
|
||||
}
|
||||
|
||||
|
@ -43,7 +43,7 @@
|
||||
</template>
|
||||
|
||||
<script lang="ts" setup>
|
||||
import { onMounted, onUnmounted, onUpdated, ref, useSlots, watch } from 'vue';
|
||||
import { onMounted, onUnmounted, onUpdated, ref, watch } from 'vue';
|
||||
import { DragOutlined } from '@ant-design/icons-vue';
|
||||
import { isMobile, useDrag, cancelGlobalDrag } from '../plugin/use';
|
||||
import { has } from '../plugin/utils';
|
||||
@ -231,7 +231,6 @@ onUnmounted(() => {
|
||||
top: 50px;
|
||||
display: flex;
|
||||
overflow: visible;
|
||||
font-family: 'normal';
|
||||
}
|
||||
|
||||
.box-main {
|
||||
|
@ -19,11 +19,14 @@
|
||||
class="special-text"
|
||||
v-if="has(enemy.special) && enemy.special.length > 0"
|
||||
>
|
||||
<span
|
||||
v-for="(text, i) in enemy.showSpecial"
|
||||
:style="{ color: text[2] }"
|
||||
> {{ text[0] }} </span
|
||||
>
|
||||
<template v-for="(text, i) in enemy.showSpecial">
|
||||
<span
|
||||
v-if="i < (isMobile ? 1 : 2)"
|
||||
:style="{ color: text[2] }"
|
||||
> {{ text[0] }} </span
|
||||
>
|
||||
<span v-if="i === (isMobile ? 1 : 2)">...</span>
|
||||
</template>
|
||||
</div>
|
||||
<div class="special-text" v-else>无属性</div>
|
||||
</div>
|
||||
@ -220,12 +223,12 @@ function enter() {
|
||||
@media screen and (max-width: 600px) {
|
||||
.rightbar {
|
||||
width: 80%;
|
||||
font-size: 85%;
|
||||
font-size: 110%;
|
||||
}
|
||||
|
||||
.leftbar {
|
||||
width: 20%;
|
||||
font-size: 80%;
|
||||
font-size: 100%;
|
||||
}
|
||||
|
||||
.enemy-container {
|
||||
|
@ -80,8 +80,8 @@ export class BgmController
|
||||
|
||||
this.playing = true;
|
||||
if (!this.disable) {
|
||||
this.setTransitionAnimate(id, 1);
|
||||
if (this.now) this.setTransitionAnimate(this.now, 0, when);
|
||||
this.setTransitionAnimate(id, 1, when);
|
||||
if (this.now) this.setTransitionAnimate(this.now, 0);
|
||||
}
|
||||
|
||||
if (!noStack) {
|
||||
|
@ -1,6 +1,5 @@
|
||||
import { has } from '@/plugin/utils';
|
||||
import { AudioParamOf, AudioPlayer } from './audio';
|
||||
import resource from '@/data/resource.json';
|
||||
import { ResourceController } from '../loader/controller';
|
||||
|
||||
// todo: 立体声,可设置音源位置
|
||||
@ -24,7 +23,6 @@ export class SoundEffect extends AudioPlayer {
|
||||
|
||||
gain: GainNode = AudioPlayer.ac.createGain();
|
||||
panner: PannerNode | null = null;
|
||||
merger: ChannelMergerNode | null = null;
|
||||
|
||||
set volumn(value: number) {
|
||||
this.gain.gain.value = value * SoundEffect.volume;
|
||||
@ -63,9 +61,7 @@ export class SoundEffect extends AudioPlayer {
|
||||
* 设置音频路由线路
|
||||
* ```txt
|
||||
* 不启用立体声:source -> gain -> destination
|
||||
* 启用立体声:source -> panner -> gain --> destination
|
||||
* 单声道立体声:source -> merger -> panner -> gain -> destination
|
||||
* 单声道立体声指音源为单声道,合成为双声道后模拟为立体声
|
||||
* 启用立体声:source -> panner -> gain -> destination
|
||||
* ```
|
||||
* @param stereo 是否启用立体声
|
||||
*/
|
||||
@ -74,20 +70,10 @@ export class SoundEffect extends AudioPlayer {
|
||||
const ac = AudioPlayer.ac;
|
||||
if (!channel) return;
|
||||
this.panner = null;
|
||||
this.merger = null;
|
||||
if (stereo) {
|
||||
this.panner = ac.createPanner();
|
||||
this.panner.connect(this.gain);
|
||||
if (channel === 1) {
|
||||
this.merger = ac.createChannelMerger();
|
||||
this.merger.connect(this.panner);
|
||||
this.baseNode = [
|
||||
{ node: this.merger, channel: 0 },
|
||||
{ node: this.merger, channel: 1 }
|
||||
];
|
||||
} else {
|
||||
this.baseNode = [{ node: this.panner }];
|
||||
}
|
||||
this.baseNode = [{ node: this.panner }];
|
||||
} else {
|
||||
this.baseNode = [{ node: this.gain }];
|
||||
}
|
||||
@ -136,15 +122,18 @@ export class SoundEffect extends AudioPlayer {
|
||||
* @param source 立体声声源位置与朝向
|
||||
* @param listener 听者的位置、头顶方向、面朝方向
|
||||
*/
|
||||
setPanner(source: Partial<Panner>, listener: Partial<Listener>) {
|
||||
setPanner(source?: Partial<Panner>, listener?: Partial<Listener>) {
|
||||
if (!this.panner) return;
|
||||
console.log(2);
|
||||
for (const [key, value] of Object.entries(source)) {
|
||||
this.panner[key as keyof Panner].value = value;
|
||||
if (source) {
|
||||
for (const [key, value] of Object.entries(source)) {
|
||||
this.panner[key as keyof Panner].value = value;
|
||||
}
|
||||
}
|
||||
const l = AudioPlayer.ac.listener;
|
||||
for (const [key, value] of Object.entries(listener)) {
|
||||
l[key as keyof Listener].value = value;
|
||||
if (listener) {
|
||||
const l = AudioPlayer.ac.listener;
|
||||
for (const [key, value] of Object.entries(listener)) {
|
||||
l[key as keyof Listener].value = value;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@ -161,7 +150,6 @@ export class SoundController extends ResourceController<
|
||||
* @param data 音频的ArrayBuffer信息,会被解析为AudioBuffer
|
||||
*/
|
||||
add(uri: string, data: ArrayBuffer) {
|
||||
const stereo = resource.stereoSE.includes(uri);
|
||||
const se = new SoundEffect(data, true);
|
||||
if (this.list[uri]) {
|
||||
console.warn(`Repeated sound effect: '${uri}'.`);
|
||||
@ -176,6 +164,7 @@ export class SoundController extends ResourceController<
|
||||
*/
|
||||
play(sound: SoundIds, end?: () => void): number {
|
||||
const se = this.get(sound);
|
||||
if (!se) return -1;
|
||||
const index = se.playSE();
|
||||
if (!has(index)) return -1;
|
||||
this.seIndex[index] = se;
|
||||
|
@ -1,13 +1,5 @@
|
||||
import { BgmController, bgm } from './audio/bgm';
|
||||
import { SoundController, SoundEffect, sound } from './audio/sound';
|
||||
import { readyAllResource } from './loader/load';
|
||||
import {
|
||||
Resource,
|
||||
ResourceStore,
|
||||
ZippedResource,
|
||||
resource,
|
||||
zipResource
|
||||
} from './loader/resource';
|
||||
import { Focus, GameUi, UiController } from './main/custom/ui';
|
||||
import { GameStorage } from './main/storage';
|
||||
import './main/init/';
|
||||
@ -20,19 +12,51 @@ import {
|
||||
mainSetting,
|
||||
settingStorage
|
||||
} from './main/setting';
|
||||
import { KeyCode } from '@/plugin/keyCodes';
|
||||
import { KeyCode, ScanCode } from '@/plugin/keyCodes';
|
||||
import { status } from '@/plugin/ui/statusBar';
|
||||
import './plugin';
|
||||
import './package';
|
||||
import { AudioPlayer } from './audio/audio';
|
||||
import { CustomToolbar } from './main/custom/toolbar';
|
||||
import { Hotkey } from './main/custom/hotkey';
|
||||
import { Keyboard } from './main/custom/keyboard';
|
||||
import {
|
||||
Hotkey,
|
||||
checkAssist,
|
||||
isAssist,
|
||||
unwarpBinary
|
||||
} from './main/custom/hotkey';
|
||||
import { Keyboard, generateKeyboardEvent } from './main/custom/keyboard';
|
||||
import './main/layout';
|
||||
|
||||
function ready() {
|
||||
readyAllResource();
|
||||
}
|
||||
import { MComponent, m } from './main/layout';
|
||||
import { createSettingComponents } from './main/init/settings';
|
||||
import {
|
||||
createToolbarComponents,
|
||||
createToolbarEditorComponents
|
||||
} from './main/init/toolbar';
|
||||
import { VirtualKey } from './main/init/misc';
|
||||
import * as utils from '@/plugin/utils';
|
||||
import * as use from '@/plugin/use';
|
||||
import * as mark from '@/plugin/mark';
|
||||
import * as keyCodes from '@/plugin/keyCodes';
|
||||
import { addAnimate, removeAnimate } from '@/plugin/animateController';
|
||||
import * as bookTools from '@/plugin/ui/book';
|
||||
import * as commonTools from '@/plugin/ui/common';
|
||||
import * as equipboxTools from '@/plugin/ui/equipbox';
|
||||
import * as fixedTools from '@/plugin/ui/fixed';
|
||||
import * as flyTools from '@/plugin/ui/fly';
|
||||
import * as statusBarTools from '@/plugin/ui/statusBar';
|
||||
import * as toolboxTools from '@/plugin/ui/toolbox';
|
||||
import * as UI from '@ui/index';
|
||||
import Box from '@/components/box.vue';
|
||||
import BoxAnimate from '@/components/boxAnimate.vue';
|
||||
import Colomn from '@/components/colomn.vue';
|
||||
import EnemyOne from '@/components/enemyOne.vue';
|
||||
import Scroll from '@/components/scroll.vue';
|
||||
import EnemyCritical from '@/panel/enemyCritical.vue';
|
||||
import EnemySpecial from '@/panel/enemySpecial.vue';
|
||||
import EnemyTarget from '@/panel/enemyTarget.vue';
|
||||
import KeyboardPanel from '@/panel/keyboard.vue';
|
||||
import { MCGenerator } from './main/layout';
|
||||
import { ResourceController } from './loader/controller';
|
||||
|
||||
// ----- 类注册
|
||||
Mota.register('class', 'AudioPlayer', AudioPlayer);
|
||||
@ -44,15 +68,20 @@ Mota.register('class', 'GameUi', GameUi);
|
||||
Mota.register('class', 'Hotkey', Hotkey);
|
||||
Mota.register('class', 'Keyboard', Keyboard);
|
||||
Mota.register('class', 'MotaSetting', MotaSetting);
|
||||
Mota.register('class', 'Resource', Resource);
|
||||
Mota.register('class', 'ResourceStore', ResourceStore);
|
||||
Mota.register('class', 'SettingDisplayer', SettingDisplayer);
|
||||
Mota.register('class', 'SoundController', SoundController);
|
||||
Mota.register('class', 'SoundEffect', SoundEffect);
|
||||
Mota.register('class', 'UiController', UiController);
|
||||
Mota.register('class', 'ZippedResource', ZippedResource);
|
||||
Mota.register('class', 'MComponent', MComponent);
|
||||
Mota.register('class', 'ResourceController', ResourceController);
|
||||
// ----- 函数注册
|
||||
|
||||
Mota.register('fn', 'm', m);
|
||||
Mota.register('fn', 'unwrapBinary', unwarpBinary);
|
||||
Mota.register('fn', 'checkAssist', checkAssist);
|
||||
Mota.register('fn', 'isAssist', isAssist);
|
||||
Mota.register('fn', 'generateKeyboardEvent', generateKeyboardEvent);
|
||||
Mota.register('fn', 'addAnimate', addAnimate);
|
||||
Mota.register('fn', 'removeAnimate', removeAnimate);
|
||||
// ----- 变量注册
|
||||
Mota.register('var', 'mainUi', mainUi);
|
||||
Mota.register('var', 'fixedUi', fixedUi);
|
||||
@ -61,11 +90,43 @@ Mota.register('var', 'sound', sound);
|
||||
Mota.register('var', 'gameKey', gameKey);
|
||||
Mota.register('var', 'mainSetting', mainSetting);
|
||||
Mota.register('var', 'KeyCode', KeyCode);
|
||||
Mota.register('var', 'resource', resource);
|
||||
Mota.register('var', 'zipResource', zipResource);
|
||||
Mota.register('var', 'settingStorage', settingStorage);
|
||||
Mota.register('var', 'status', status);
|
||||
|
||||
// ----- 模块注册
|
||||
Mota.register('module', 'CustomComponents', {
|
||||
createSettingComponents,
|
||||
createToolbarComponents,
|
||||
createToolbarEditorComponents
|
||||
});
|
||||
Mota.register('module', 'MiscComponents', {
|
||||
VirtualKey
|
||||
});
|
||||
Mota.register('module', 'RenderUtils', utils);
|
||||
Mota.register('module', 'Use', use);
|
||||
Mota.register('module', 'Mark', mark);
|
||||
Mota.register('module', 'KeyCodes', keyCodes);
|
||||
Mota.register('module', 'UITools', {
|
||||
book: bookTools,
|
||||
common: commonTools,
|
||||
equipbox: equipboxTools,
|
||||
fixed: fixedTools,
|
||||
fly: flyTools,
|
||||
statusBar: statusBarTools,
|
||||
toolbox: toolboxTools
|
||||
});
|
||||
Mota.register('module', 'UI', UI);
|
||||
Mota.register('module', 'UIComponents', {
|
||||
Box,
|
||||
BoxAnimate,
|
||||
Colomn,
|
||||
EnemyOne,
|
||||
Scroll,
|
||||
EnemyCritical,
|
||||
EnemySpecial,
|
||||
EnemyTarget,
|
||||
Keyboard: KeyboardPanel
|
||||
});
|
||||
Mota.register('module', 'MCGenerator', MCGenerator);
|
||||
|
||||
ready();
|
||||
main.renderLoaded = true;
|
||||
Mota.require('var', 'hook').emit('renderLoaded');
|
||||
|
@ -137,7 +137,7 @@ export class Hotkey extends EventEmitter<HotkeyEvent> {
|
||||
* 释放一个作用域,释放后作用域将退回至删除的作用域的上一级
|
||||
* @param symbol 要释放的作用域的symbol
|
||||
*/
|
||||
dispose(symbol: symbol) {
|
||||
dispose(symbol: symbol = this.scopeStack.at(-1) ?? Symbol()) {
|
||||
for (const key of Object.values(this.data)) {
|
||||
key.func.delete(symbol);
|
||||
}
|
||||
|
@ -116,6 +116,14 @@ export class CustomToolbar extends EventEmitter<CustomToolbarEvent> {
|
||||
constructor(id: string) {
|
||||
super();
|
||||
this.id = id;
|
||||
// 按比例设置初始大小
|
||||
const setting = Mota.require('var', 'mainSetting');
|
||||
const scale = setting.getValue('ui.toolbarScale', 100) / 100;
|
||||
this.width *= scale;
|
||||
this.height *= scale;
|
||||
this.x *= scale;
|
||||
this.y *= scale;
|
||||
|
||||
this.show();
|
||||
CustomToolbar.list.push(this);
|
||||
}
|
||||
@ -188,22 +196,31 @@ export class CustomToolbar extends EventEmitter<CustomToolbarEvent> {
|
||||
/**
|
||||
* 强制刷新这个自定义工具栏的所有显示
|
||||
*/
|
||||
refresh() {
|
||||
const items = this.items.splice(0);
|
||||
nextTick(() => {
|
||||
this.items.push(...items);
|
||||
});
|
||||
refresh(reopen: boolean = false) {
|
||||
if (reopen && this.showIds.length > 0) {
|
||||
this.closeAll();
|
||||
nextTick(() => {
|
||||
this.show();
|
||||
});
|
||||
} else {
|
||||
const items = this.items.splice(0);
|
||||
nextTick(() => {
|
||||
this.items.push(...items);
|
||||
});
|
||||
}
|
||||
return this;
|
||||
}
|
||||
|
||||
setPos(x?: number, y?: number) {
|
||||
has(x) && (this.x = x);
|
||||
has(y) && (this.y = y);
|
||||
this.emit('posChange', this);
|
||||
}
|
||||
|
||||
setSize(width?: number, height?: number) {
|
||||
has(width) && (this.width = width);
|
||||
has(height) && (this.height = height);
|
||||
this.emit('posChange', this);
|
||||
}
|
||||
|
||||
/**
|
||||
@ -269,12 +286,14 @@ export class CustomToolbar extends EventEmitter<CustomToolbarEvent> {
|
||||
|
||||
static save() {
|
||||
toolbarStorage.clear();
|
||||
const setting = Mota.require('var', 'mainSetting');
|
||||
const scale = setting.getValue('ui.toolbarScale', 100) / 100;
|
||||
this.list.forEach(v => {
|
||||
const toSave: ToolbarSaveData = {
|
||||
x: v.x,
|
||||
y: v.y,
|
||||
w: v.width,
|
||||
h: v.height,
|
||||
w: v.width / scale,
|
||||
h: v.height / scale,
|
||||
items: []
|
||||
};
|
||||
v.items.forEach(v => {
|
||||
@ -288,7 +307,7 @@ export class CustomToolbar extends EventEmitter<CustomToolbarEvent> {
|
||||
static load() {
|
||||
toolbarStorage.read();
|
||||
for (const [key, value] of Object.entries(toolbarStorage.data)) {
|
||||
const bar = new CustomToolbar(key);
|
||||
const bar = this.get(key) ?? new CustomToolbar(key);
|
||||
bar.x = value.x;
|
||||
bar.y = value.y;
|
||||
bar.width = value.w;
|
||||
@ -299,6 +318,10 @@ export class CustomToolbar extends EventEmitter<CustomToolbarEvent> {
|
||||
}
|
||||
}
|
||||
|
||||
static refreshAll(reopen: boolean = false): void {
|
||||
CustomToolbar.list.forEach(v => v.refresh(reopen));
|
||||
}
|
||||
|
||||
static showAll(): number[] {
|
||||
return CustomToolbar.list.map(v => v.show());
|
||||
}
|
||||
@ -376,16 +399,16 @@ CustomToolbar.register(
|
||||
}
|
||||
);
|
||||
|
||||
window.addEventListener('beforeunload', () => {
|
||||
CustomToolbar.save();
|
||||
});
|
||||
window.addEventListener('blur', () => {
|
||||
CustomToolbar.save();
|
||||
});
|
||||
|
||||
Mota.require('var', 'loading').once('coreInit', () => {
|
||||
CustomToolbar.load();
|
||||
CustomToolbar.closeAll();
|
||||
|
||||
window.addEventListener('beforeunload', e => {
|
||||
CustomToolbar.save();
|
||||
});
|
||||
window.addEventListener('blur', () => {
|
||||
CustomToolbar.save();
|
||||
});
|
||||
});
|
||||
Mota.require('var', 'hook').on('reset', () => {
|
||||
CustomToolbar.showAll();
|
||||
|
@ -9,8 +9,6 @@ interface FocusEvent<T> extends EmitableEvent {
|
||||
unfocus: (before: T | null) => void;
|
||||
add: (item: T) => void;
|
||||
pop: (item: T | null) => void;
|
||||
register: (item: T[]) => void;
|
||||
unregister: (item: T[]) => void;
|
||||
splice: (spliced: T[]) => void;
|
||||
}
|
||||
|
||||
@ -163,7 +161,7 @@ interface IndexedGameUi extends ShowableGameUi {
|
||||
}
|
||||
|
||||
interface HoldOnController {
|
||||
end(): void;
|
||||
end(noClosePanel?: boolean): void;
|
||||
}
|
||||
|
||||
export class UiController extends Focus<IndexedGameUi> {
|
||||
@ -182,7 +180,7 @@ export class UiController extends Focus<IndexedGameUi> {
|
||||
v.ui.emit('close');
|
||||
});
|
||||
if (this.stack.length === 0) {
|
||||
if (!this.hold) this.emit('end');
|
||||
if (!this.hold) this.emit('end', false);
|
||||
this.hold = false;
|
||||
}
|
||||
});
|
||||
@ -227,8 +225,8 @@ export class UiController extends Focus<IndexedGameUi> {
|
||||
this.hold = true;
|
||||
|
||||
return {
|
||||
end: () => {
|
||||
this.emit('end');
|
||||
end: (noClosePanel: boolean = false) => {
|
||||
this.emit('end', noClosePanel);
|
||||
}
|
||||
};
|
||||
}
|
||||
|
@ -9,7 +9,8 @@ interface Components {
|
||||
Number: SettingComponent;
|
||||
HotkeySetting: SettingComponent;
|
||||
ToolbarEditor: SettingComponent;
|
||||
RadioSetting: (items: string[]) => SettingComponent;
|
||||
Radio: (items: string[]) => SettingComponent;
|
||||
Performance: SettingComponent;
|
||||
}
|
||||
|
||||
export type { Components as SettingDisplayComponents };
|
||||
@ -21,7 +22,8 @@ export function createSettingComponents() {
|
||||
Number: NumberSetting,
|
||||
HotkeySetting,
|
||||
ToolbarEditor,
|
||||
RadioSetting
|
||||
Radio: RadioSetting,
|
||||
Performance: PerformanceSetting
|
||||
};
|
||||
return com;
|
||||
}
|
||||
@ -63,7 +65,7 @@ function NumberSetting(props: SettingComponentProps) {
|
||||
if (value < (item.step?.[0] ?? 0) || value > (item.step?.[1] ?? 100)) {
|
||||
return;
|
||||
}
|
||||
setting.setValue(displayer.selectStack.join('.'), value);
|
||||
setting.setValue(displayer.selectStack.join('.'), Math.round(value));
|
||||
displayer.update();
|
||||
};
|
||||
|
||||
@ -180,3 +182,18 @@ function ToolbarEditor(props: SettingComponentProps) {
|
||||
</div>
|
||||
);
|
||||
}
|
||||
|
||||
function PerformanceSetting(props: SettingComponentProps) {
|
||||
return (
|
||||
<div style="display: flex; justify-content: center">
|
||||
<Button
|
||||
style="font-size: 75%"
|
||||
type="primary"
|
||||
size="large"
|
||||
onClick={() => showSpecialSetting('performance')}
|
||||
>
|
||||
打开性能界面
|
||||
</Button>
|
||||
</div>
|
||||
);
|
||||
}
|
||||
|
@ -8,6 +8,7 @@ import { checkAssist } from '../custom/hotkey';
|
||||
import { getVitualKeyOnce } from '@/plugin/utils';
|
||||
import { cloneDeep } from 'lodash-es';
|
||||
import { Select, SelectOption } from 'ant-design-vue';
|
||||
import { mainSetting } from '../setting';
|
||||
|
||||
// todo: 新增更改设置的ToolItem
|
||||
|
||||
@ -53,15 +54,18 @@ function KeyTool(props: CustomToolbarProps<'hotkey'>) {
|
||||
|
||||
function ItemTool(props: CustomToolbarProps<'item'>) {
|
||||
const { item, toolbar } = props;
|
||||
const scale = mainSetting.getValue('ui.toolbarScale', 100) / 100;
|
||||
return (
|
||||
<div
|
||||
style="display: flex; justify-content: center; width: 50px"
|
||||
style={`display: flex; justify-content: center; width: ${
|
||||
50 * scale
|
||||
}px`}
|
||||
onClick={() => toolbar.emitTool(item.id)}
|
||||
>
|
||||
<BoxAnimate
|
||||
noborder={true}
|
||||
width={50}
|
||||
height={50}
|
||||
width={50 * scale}
|
||||
height={50 * scale}
|
||||
id={item.item}
|
||||
></BoxAnimate>
|
||||
</div>
|
||||
|
@ -1,6 +1,7 @@
|
||||
import * as UI from '@ui/.';
|
||||
import * as MiscUI from './misc';
|
||||
import { GameUi, UiController } from '../custom/ui';
|
||||
import { mainSetting } from '../setting';
|
||||
|
||||
export const mainUi = new UiController();
|
||||
mainUi.register(
|
||||
@ -35,20 +36,34 @@ fixedUi.register(
|
||||
);
|
||||
fixedUi.showAll();
|
||||
|
||||
let loaded = false;
|
||||
let mounted = false;
|
||||
|
||||
const hook = Mota.require('var', 'hook');
|
||||
hook.once('mounted', () => {
|
||||
const ui = document.getElementById('ui-main')!;
|
||||
const fixed = document.getElementById('ui-fixed')!;
|
||||
|
||||
const blur = mainSetting.getSetting('screen.blur');
|
||||
|
||||
mainUi.on('start', () => {
|
||||
ui.style.display = 'flex';
|
||||
if (blur?.value) {
|
||||
ui.style.backdropFilter = 'blur(5px)';
|
||||
ui.style.backgroundColor = 'rgba(0,0,0,0.7333)';
|
||||
} else {
|
||||
ui.style.backdropFilter = 'none';
|
||||
ui.style.backgroundColor = 'rgba(0,0,0,0.85)';
|
||||
}
|
||||
core.lockControl();
|
||||
});
|
||||
mainUi.on('end', () => {
|
||||
mainUi.on('end', noClosePanel => {
|
||||
ui.style.display = 'none';
|
||||
try {
|
||||
core.closePanel();
|
||||
} catch {}
|
||||
if (!noClosePanel) {
|
||||
try {
|
||||
core.closePanel();
|
||||
} catch {}
|
||||
}
|
||||
});
|
||||
fixedUi.on('start', () => {
|
||||
fixed.style.display = 'block';
|
||||
@ -57,6 +72,15 @@ hook.once('mounted', () => {
|
||||
fixed.style.display = 'none';
|
||||
});
|
||||
|
||||
// todo: 暂时先这么搞,之后重写加载界面,需要改成先显示加载界面,加载完毕后再打开这个界面
|
||||
fixedUi.open('start');
|
||||
if (loaded && !mounted) {
|
||||
fixedUi.open('start');
|
||||
}
|
||||
mounted = true;
|
||||
});
|
||||
hook.once('load', () => {
|
||||
if (mounted) {
|
||||
// todo: 暂时先这么搞,之后重写加载界面,需要改成先显示加载界面,加载完毕后再打开这个界面
|
||||
fixedUi.open('start');
|
||||
}
|
||||
loaded = true;
|
||||
});
|
||||
|
@ -5,10 +5,12 @@ import {
|
||||
VNode,
|
||||
VNodeChild,
|
||||
defineComponent,
|
||||
h,
|
||||
h as hVue,
|
||||
isVNode,
|
||||
onMounted
|
||||
} from 'vue';
|
||||
import BoxAnimate from '@/components/boxAnimate.vue';
|
||||
import { ensureArray } from '@/plugin/utils';
|
||||
|
||||
interface VForRenderer {
|
||||
type: '@v-for';
|
||||
@ -18,7 +20,7 @@ interface VForRenderer {
|
||||
|
||||
interface MotaComponent extends MotaComponentConfig {
|
||||
type: string;
|
||||
children: MComponent[] | MComponent;
|
||||
children: (MComponent | MotaComponent | VNode)[];
|
||||
}
|
||||
|
||||
interface MotaComponentConfig {
|
||||
@ -32,7 +34,7 @@ interface MotaComponentConfig {
|
||||
velse?: boolean;
|
||||
}
|
||||
|
||||
type OnSetupFunction = (props: Record<string, any>) => void;
|
||||
type OnSetupFunction = (props: Record<string, any>, ctx: SetupContext) => void;
|
||||
type SetupFunction = (
|
||||
props: Record<string, any>,
|
||||
ctx: SetupContext
|
||||
@ -43,6 +45,7 @@ type RetFunction = (
|
||||
) => VNodeChild | VNodeChild[];
|
||||
type OnMountedFunction = (
|
||||
props: Record<string, any>,
|
||||
ctx: SetupContext,
|
||||
canvas: HTMLCanvasElement[]
|
||||
) => void;
|
||||
|
||||
@ -51,6 +54,12 @@ type NonComponentConfig = Omit<
|
||||
'innerText' | 'component' | 'slots' | 'dComponent'
|
||||
>;
|
||||
|
||||
type MComponentChildren =
|
||||
| (MComponent | MotaComponent | VNode)[]
|
||||
| MComponent
|
||||
| MotaComponent
|
||||
| VNode;
|
||||
|
||||
export class MComponent {
|
||||
static mountNum: number = 0;
|
||||
|
||||
@ -88,7 +97,7 @@ export class MComponent {
|
||||
* @param children 渲染内容的子内容
|
||||
* @param config 渲染内容的配置信息,参考 {@link MComponent.h}
|
||||
*/
|
||||
div(children?: MComponent[] | MComponent, config?: NonComponentConfig) {
|
||||
div(children?: MComponentChildren, config?: NonComponentConfig) {
|
||||
return this.h('div', children, config);
|
||||
}
|
||||
|
||||
@ -97,7 +106,7 @@ export class MComponent {
|
||||
* @param children 渲染内容的子内容
|
||||
* @param config 渲染内容的配置信息,参考 {@link MComponent.h}
|
||||
*/
|
||||
span(children?: MComponent[] | MComponent, config?: NonComponentConfig) {
|
||||
span(children?: MComponentChildren, config?: NonComponentConfig) {
|
||||
return this.h('span', children, config);
|
||||
}
|
||||
|
||||
@ -124,7 +133,7 @@ export class MComponent {
|
||||
*/
|
||||
com(
|
||||
component: Component | MComponent,
|
||||
config: Omit<MotaComponentConfig, 'innerText' | 'component'>
|
||||
config?: Omit<MotaComponentConfig, 'innerText' | 'component'>
|
||||
) {
|
||||
return this.h(component, [], config);
|
||||
}
|
||||
@ -136,11 +145,7 @@ export class MComponent {
|
||||
* 或者MComponent.vNode函数生成。
|
||||
*/
|
||||
vfor<T>(items: T[] | (() => T[]), map: (value: T, index: number) => VNode) {
|
||||
this.content.push({
|
||||
type: '@v-for',
|
||||
items,
|
||||
map
|
||||
});
|
||||
this.content.push(MCGenerator.vfor(items, map));
|
||||
return this;
|
||||
}
|
||||
|
||||
@ -180,32 +185,10 @@ export class MComponent {
|
||||
*/
|
||||
h(
|
||||
type: string | Component | MComponent,
|
||||
children?: MComponent[] | MComponent,
|
||||
children?: MComponentChildren,
|
||||
config: MotaComponentConfig = {}
|
||||
): this {
|
||||
if (typeof type === 'string') {
|
||||
this.content.push({
|
||||
type,
|
||||
children: children ?? [],
|
||||
props: config.props,
|
||||
innerText: config.innerText,
|
||||
slots: config.slots,
|
||||
vif: config.vif,
|
||||
velse: config.velse,
|
||||
component: config.component
|
||||
});
|
||||
} else {
|
||||
this.content.push({
|
||||
type: 'component',
|
||||
children: children ?? [],
|
||||
props: config.props,
|
||||
innerText: config.innerText,
|
||||
slots: config.slots,
|
||||
vif: config.vif,
|
||||
velse: config.velse,
|
||||
component: type
|
||||
});
|
||||
}
|
||||
this.content.push(MCGenerator.h(type, children, config));
|
||||
return this;
|
||||
}
|
||||
|
||||
@ -253,11 +236,12 @@ export class MComponent {
|
||||
return defineComponent(
|
||||
(props, ctx) => {
|
||||
const mountNum = MComponent.mountNum++;
|
||||
this.onSetupFn?.(props);
|
||||
this.onSetupFn?.(props, ctx);
|
||||
|
||||
onMounted(() => {
|
||||
this.onMountedFn?.(
|
||||
props,
|
||||
ctx,
|
||||
Array.from(
|
||||
document.getElementsByClassName(
|
||||
`--mota-component-canvas-${mountNum}`
|
||||
@ -269,8 +253,6 @@ export class MComponent {
|
||||
if (this.retFn) return () => this.retFn!(props, ctx);
|
||||
else {
|
||||
return () => {
|
||||
console.log(ctx.slots.default);
|
||||
|
||||
const vNodes = MComponent.vNode(
|
||||
this.content,
|
||||
mountNum
|
||||
@ -312,12 +294,19 @@ export class MComponent {
|
||||
* @param children 要生成VNode的内容列表
|
||||
* @param mount 组件生成时的挂载id,一般不需要填,用于画布获取
|
||||
*/
|
||||
static vNode(children: (MotaComponent | VForRenderer)[], mount?: number) {
|
||||
static vNode(
|
||||
children: (MotaComponent | VForRenderer | VNode)[],
|
||||
mount?: number
|
||||
) {
|
||||
const mountNum = mount ?? this.mountNum++;
|
||||
|
||||
const res: VNode[] = [];
|
||||
const vifRes: Map<number, boolean> = new Map();
|
||||
children.forEach((v, i) => {
|
||||
if (isVNode(v)) {
|
||||
res.push(v);
|
||||
return;
|
||||
}
|
||||
if (v.type === '@v-for') {
|
||||
const node = v as VForRenderer;
|
||||
const items =
|
||||
@ -346,7 +335,7 @@ export class MComponent {
|
||||
);
|
||||
}
|
||||
if (v.dComponent) {
|
||||
res.push(h(v.dComponent(), props, v.slots));
|
||||
res.push(hVue(v.dComponent(), props, v.slots));
|
||||
} else {
|
||||
if (v.component instanceof MComponent) {
|
||||
res.push(
|
||||
@ -356,12 +345,12 @@ export class MComponent {
|
||||
)
|
||||
);
|
||||
} else {
|
||||
res.push(h(v.component!, props, v.slots));
|
||||
res.push(hVue(v.component!, props, v.slots));
|
||||
}
|
||||
}
|
||||
} else if (v.type === 'text') {
|
||||
res.push(
|
||||
h(
|
||||
hVue(
|
||||
'span',
|
||||
typeof v.innerText === 'function'
|
||||
? v.innerText()
|
||||
@ -372,15 +361,17 @@ export class MComponent {
|
||||
const cls = `--mota-component-canvas-${mountNum}`;
|
||||
const mix = !!props.class ? cls + ' ' + props.class : cls;
|
||||
props.class = mix;
|
||||
res.push(h('canvas', props, node.slots));
|
||||
res.push(hVue('canvas', props, node.slots));
|
||||
} else {
|
||||
// 这个时候不可能会有插槽,只会有子内容,因此直接渲染子内容
|
||||
const content = [node.children].flat(2);
|
||||
const content = node.children;
|
||||
const vn = this.vNode(
|
||||
content.map(v => v.content).flat(),
|
||||
content
|
||||
.map(v => (v instanceof MComponent ? v.content : v))
|
||||
.flat(),
|
||||
mountNum
|
||||
);
|
||||
res.push(h(v.type, props, vn));
|
||||
res.push(hVue(v.type, props, vn));
|
||||
}
|
||||
}
|
||||
});
|
||||
@ -406,7 +397,7 @@ export class MComponent {
|
||||
* @param props 要传递的props
|
||||
*/
|
||||
static prop(component: Component, props: Record<string, any>) {
|
||||
return h(component, props);
|
||||
return hVue(component, props);
|
||||
}
|
||||
}
|
||||
|
||||
@ -418,18 +409,101 @@ export function m() {
|
||||
return new MComponent();
|
||||
}
|
||||
|
||||
/**
|
||||
* 生成一个图标的VNode
|
||||
* @param id 图标的id
|
||||
* @param width 显示宽度,单位像素
|
||||
* @param height 显示高度,单位像素
|
||||
* @param noBoarder 显示的时候是否没有边框和背景
|
||||
*/
|
||||
export function icon(
|
||||
id: AllIds,
|
||||
width?: number,
|
||||
height?: number,
|
||||
noBoarder?: number
|
||||
) {
|
||||
return h(BoxAnimate, { id, width, height, noBoarder });
|
||||
export namespace MCGenerator {
|
||||
export function h(
|
||||
type: string | Component | MComponent,
|
||||
children?: MComponentChildren,
|
||||
config: MotaComponentConfig = {}
|
||||
): MotaComponent {
|
||||
if (typeof type === 'string') {
|
||||
return {
|
||||
type,
|
||||
children: ensureArray(children ?? []),
|
||||
props: config.props,
|
||||
innerText: config.innerText,
|
||||
slots: config.slots,
|
||||
vif: config.vif,
|
||||
velse: config.velse,
|
||||
component: config.component
|
||||
};
|
||||
} else {
|
||||
return {
|
||||
type: 'component',
|
||||
children: ensureArray(children ?? []),
|
||||
props: config.props,
|
||||
innerText: config.innerText,
|
||||
slots: config.slots,
|
||||
vif: config.vif,
|
||||
velse: config.velse,
|
||||
component: type
|
||||
};
|
||||
}
|
||||
}
|
||||
|
||||
export function div(
|
||||
children?: MComponentChildren,
|
||||
config?: NonComponentConfig
|
||||
): MotaComponent {
|
||||
return h('div', children, config);
|
||||
}
|
||||
|
||||
export function span(
|
||||
children?: MComponentChildren,
|
||||
config?: NonComponentConfig
|
||||
): MotaComponent {
|
||||
return h('span', children, config);
|
||||
}
|
||||
|
||||
export function canvas(config?: NonComponentConfig): MotaComponent {
|
||||
return h('canvas', [], config);
|
||||
}
|
||||
|
||||
export function text(
|
||||
text: string | (() => string),
|
||||
config: NonComponentConfig = {}
|
||||
): MotaComponent {
|
||||
return h('text', [], { ...config, innerText: text });
|
||||
}
|
||||
|
||||
export function com(
|
||||
component: Component | MComponent,
|
||||
config: Omit<MotaComponentConfig, 'innerText' | 'component'>
|
||||
): MotaComponent {
|
||||
return h(component, [], config);
|
||||
}
|
||||
|
||||
/**
|
||||
* 生成一个图标的VNode
|
||||
* @param id 图标的id
|
||||
* @param width 显示宽度,单位像素
|
||||
* @param height 显示高度,单位像素
|
||||
* @param noBoarder 显示的时候是否没有边框和背景
|
||||
*/
|
||||
export function icon(
|
||||
id: AllIds,
|
||||
width?: number,
|
||||
height?: number,
|
||||
noBoarder?: number
|
||||
): VNode {
|
||||
return hVue(BoxAnimate, { id, width, height, noBoarder });
|
||||
}
|
||||
|
||||
export function vfor<T>(
|
||||
items: T[] | (() => T[]),
|
||||
map: (value: T, index: number) => VNode
|
||||
): VForRenderer {
|
||||
return {
|
||||
type: '@v-for',
|
||||
items,
|
||||
map
|
||||
};
|
||||
}
|
||||
|
||||
/**
|
||||
* 为一个常量创建为一个函数
|
||||
* @param value 要创建成函数的值
|
||||
*/
|
||||
export function f<T>(value: T): () => T {
|
||||
return () => value;
|
||||
}
|
||||
}
|
||||
|
@ -7,6 +7,8 @@ import { bgm } from '../audio/bgm';
|
||||
import { SoundEffect } from '../audio/sound';
|
||||
import settingsText from '@/data/settings.json';
|
||||
import { isMobile } from '@/plugin/use';
|
||||
import { fontSize } from '@/plugin/ui/statusBar';
|
||||
import { CustomToolbar } from './custom/toolbar';
|
||||
|
||||
export interface SettingComponentProps {
|
||||
item: MotaSettingItem;
|
||||
@ -334,6 +336,8 @@ mainSetting.on('valueChange', (key, n, o) => {
|
||||
handleActionSetting(setting, n, o);
|
||||
} else if (root === 'audio') {
|
||||
handleAudioSetting(setting, n, o);
|
||||
} else if (root === 'ui') {
|
||||
handleUiSetting(setting, n, o);
|
||||
}
|
||||
});
|
||||
|
||||
@ -363,6 +367,11 @@ function handleScreenSetting<T extends number | boolean>(
|
||||
} else if (key === 'fontSize') {
|
||||
// 字体大小
|
||||
root.style.fontSize = `${n}px`;
|
||||
const absoluteSize = (n as number) * devicePixelRatio;
|
||||
storage.setValue('@@absoluteFontSize', absoluteSize);
|
||||
storage.write();
|
||||
} else if (key === 'fontSizeStatus') {
|
||||
fontSize.value = n as number;
|
||||
}
|
||||
}
|
||||
|
||||
@ -394,6 +403,16 @@ function handleAudioSetting<T extends number | boolean>(
|
||||
}
|
||||
}
|
||||
|
||||
function handleUiSetting<T extends number | boolean>(key: string, n: T, o: T) {
|
||||
if (key === 'toolbarScale') {
|
||||
const scale = (n as number) / (o as number);
|
||||
CustomToolbar.list.forEach(v => {
|
||||
v.setSize(v.width * scale, v.height * scale);
|
||||
});
|
||||
CustomToolbar.refreshAll(true);
|
||||
}
|
||||
}
|
||||
|
||||
// ----- 游戏的所有设置项
|
||||
// todo: 虚拟键盘缩放,小地图楼传缩放
|
||||
mainSetting
|
||||
@ -407,11 +426,13 @@ mainSetting
|
||||
.register('heroDetail', '勇士显伤', false, COM.Boolean)
|
||||
.register('transition', '界面动画', false, COM.Boolean)
|
||||
.register('antiAlias', '抗锯齿', false, COM.Boolean)
|
||||
.register('fontSize', '字体大小', 16, COM.Number, [8, 28, 1])
|
||||
.register('fontSize', '字体大小', 16, COM.Number, [2, 48, 1])
|
||||
.register('fontSizeStatus', '状态栏字体', 16, COM.Number, [2, 48, 1])
|
||||
.register('smoothView', '平滑镜头', true, COM.Boolean)
|
||||
.register('criticalGem', '临界显示方式', false, COM.Boolean)
|
||||
.setDisplayFunc('criticalGem', value => (value ? '宝石数' : '攻击'))
|
||||
.register('keyScale', '虚拟键盘缩放', 100, COM.Number, [25, 5, 500])
|
||||
.register('blur', '背景虚化', !isMobile, COM.Boolean)
|
||||
)
|
||||
.register(
|
||||
'action',
|
||||
@ -450,13 +471,13 @@ mainSetting
|
||||
.register(
|
||||
'ui',
|
||||
'ui设置',
|
||||
new MotaSetting().register(
|
||||
'mapScale',
|
||||
'小地图楼传缩放',
|
||||
100,
|
||||
COM.Number,
|
||||
[50, 1000, 50]
|
||||
)
|
||||
new MotaSetting()
|
||||
.register('mapScale', '小地图缩放', 100, COM.Number, [50, 1000, 50])
|
||||
.setDisplayFunc('mapScale', value => `${value}%`)
|
||||
.register('toolbarScale', '工具栏缩放', 100, COM.Number, [10, 500, 10])
|
||||
.setDisplayFunc('toolbarScale', value => `${value}%`)
|
||||
.register('bookScale', '怪物手册缩放', 100, COM.Number, [10, 500, 10])
|
||||
.setDisplayFunc('bookScale', value => `${value}%`)
|
||||
);
|
||||
|
||||
const loading = Mota.require('var', 'loading');
|
||||
@ -471,6 +492,7 @@ loading.once('coreInit', () => {
|
||||
'screen.fontSize': storage.getValue('screen.fontSize', 16),
|
||||
'screen.smoothView': !!storage.getValue('screen.smoothView', true),
|
||||
'screen.criticalGem': !!storage.getValue('screen.criticalGem', false),
|
||||
'screen.fontSizeStatus': storage.getValue('screen.fontSizeStatus', 100),
|
||||
'action.fixed': !!storage.getValue('action.fixed', true),
|
||||
'audio.bgmEnabled': !!storage.getValue('audio.bgmEnabled', true),
|
||||
'audio.bgmVolume': storage.getValue('audio.bgmVolume', 80),
|
||||
@ -483,7 +505,12 @@ loading.once('coreInit', () => {
|
||||
'ui.mapScale': storage.getValue(
|
||||
'ui.mapScale',
|
||||
isMobile ? 300 : Math.floor(window.innerWidth / 600) * 50
|
||||
)
|
||||
),
|
||||
'ui.toolbarScale': storage.getValue(
|
||||
'ui.toolbarScale',
|
||||
isMobile ? 50 : Math.floor((window.innerWidth / 1700) * 10) * 10
|
||||
),
|
||||
'ui.bookScale': storage.getValue('ui.bookScale', isMobile ? 100 : 80),
|
||||
});
|
||||
});
|
||||
|
||||
@ -515,4 +542,22 @@ mainSetting
|
||||
.setDescription('audio.bgmVolume', `背景音乐的音量`)
|
||||
.setDescription('audio.soundEnabled', `是否开启音效`)
|
||||
.setDescription('audio.soundVolume', `音效的音量`)
|
||||
.setDescription('ui.mapScale', `楼传小地图的缩放,百分比格式`);
|
||||
.setDescription('ui.mapScale', `楼传小地图的缩放,百分比格式`)
|
||||
.setDescription('ui.toolbarScale', `自定义工具栏的缩放比例`)
|
||||
.setDescription('ui.bookScale', `怪物手册界面中每个怪物框体的高度缩放,最小值限定为 20% 屏幕高度`)
|
||||
.setDescription('screen.fontSizeStatus', `修改状态栏的字体大小`)
|
||||
.setDescription('screen.blur', '打开任意ui界面时是否有背景虚化效果,移动端打开后可能会有掉帧或者发热现象。关闭ui后生效');
|
||||
|
||||
function setFontSize() {
|
||||
const absoluteSize = storage.getValue(
|
||||
'@@absoluteFontSize',
|
||||
16 * devicePixelRatio
|
||||
);
|
||||
const size = Math.round(absoluteSize / devicePixelRatio);
|
||||
mainSetting.setValue('screen.fontSize', size);
|
||||
}
|
||||
setFontSize();
|
||||
|
||||
window.addEventListener('resize', () => {
|
||||
setFontSize();
|
||||
});
|
@ -4,6 +4,7 @@ import { loading } from '../game';
|
||||
|
||||
export interface CurrentEnemy {
|
||||
enemy: DamageEnemy;
|
||||
// 这个是干啥的?
|
||||
onMapEnemy: DamageEnemy[];
|
||||
}
|
||||
|
||||
@ -16,155 +17,64 @@ export function getEnemy(
|
||||
return v.x === x && v.y === y;
|
||||
});
|
||||
if (!enemy) {
|
||||
throw new Error(
|
||||
`Get null when getting enemy on '${x},${y}' in '${floorId}'`
|
||||
);
|
||||
return null;
|
||||
}
|
||||
return enemy;
|
||||
}
|
||||
|
||||
function init() {
|
||||
core.enemys.canBattle = function canBattle(
|
||||
x: number,
|
||||
x: number | DamageEnemy,
|
||||
y: number,
|
||||
floorId: FloorIds = core.status.floorId
|
||||
) {
|
||||
const enemy = getEnemy(x, y, floorId);
|
||||
const enemy = typeof x === 'number' ? getEnemy(x, y, floorId) : x;
|
||||
if (!enemy) {
|
||||
throw new Error(
|
||||
`Cannot get enemy on x:${x}, y:${y}, floor: ${floorId}`
|
||||
);
|
||||
}
|
||||
const { damage } = enemy.calDamage();
|
||||
|
||||
return damage < core.status.hero.hp;
|
||||
};
|
||||
|
||||
core.events.battle = function battle(
|
||||
x: number,
|
||||
x: number | DamageEnemy,
|
||||
y: number,
|
||||
force: boolean = false,
|
||||
callback?: () => void
|
||||
) {
|
||||
core.saveAndStopAutomaticRoute();
|
||||
const enemy = getEnemy(x, y);
|
||||
const isLoc = typeof x === 'number';
|
||||
const enemy = isLoc ? getEnemy(x, y) : x;
|
||||
if (!enemy) {
|
||||
throw new Error(
|
||||
`Cannot battle with enemy since no enemy on ${x},${y}`
|
||||
);
|
||||
}
|
||||
// 非强制战斗
|
||||
// @ts-ignore
|
||||
if (!core.canBattle(x, y) && !force && !core.status.event.id) {
|
||||
core.stopSound();
|
||||
core.playSound('操作失败');
|
||||
core.drawTip('你打不过此怪物!', enemy.id);
|
||||
core.drawTip('你打不过此怪物!', enemy!.id);
|
||||
return core.clearContinueAutomaticRoute(callback);
|
||||
}
|
||||
// 自动存档
|
||||
if (!core.status.event.id) core.autosave(true);
|
||||
// 战前事件
|
||||
// 战后事件
|
||||
core.afterBattle(enemy, x, y);
|
||||
core.afterBattle(enemy, isLoc ? x : enemy.x, y);
|
||||
callback?.();
|
||||
};
|
||||
|
||||
core.events.afterBattle = function afterBattle(
|
||||
enemy: DamageEnemy,
|
||||
x?: number,
|
||||
y?: number
|
||||
) {
|
||||
const floorId = core.status.floorId;
|
||||
const special = enemy.info.special;
|
||||
const getFacedId = (enemy: DamageEnemy) => {
|
||||
const e = enemy.enemy;
|
||||
|
||||
// 播放战斗动画
|
||||
let animate: AnimationIds = 'hand';
|
||||
// 检查当前装备是否存在攻击动画
|
||||
const equipId = core.getEquip(0);
|
||||
if (equipId && (core.material.items[equipId].equip || {}).animate)
|
||||
animate = core.material.items[equipId].equip.animate;
|
||||
|
||||
// 检查该动画是否存在SE,如果不存在则使用默认音效
|
||||
if (!core.material.animates[animate]?.se) core.playSound('attack.mp3');
|
||||
|
||||
// 战斗伤害
|
||||
const info = enemy.calDamage(core.status.hero);
|
||||
const damage = info.damage;
|
||||
// 判定是否致死
|
||||
if (damage >= core.status.hero.hp) {
|
||||
core.status.hero.hp = 0;
|
||||
core.updateStatusBar(false, true);
|
||||
core.events.lose('战斗失败');
|
||||
return;
|
||||
}
|
||||
|
||||
// 扣减体力值并记录统计数据
|
||||
core.status.hero.hp -= damage;
|
||||
core.status.hero.statistics.battleDamage += damage;
|
||||
core.status.hero.statistics.battle++;
|
||||
|
||||
// 智慧之源
|
||||
if (special.includes(14) && flags.hard === 2) {
|
||||
core.addFlag(
|
||||
'inte_' + floorId,
|
||||
Math.ceil((core.status.hero.mdef / 10) * 0.3) * 10
|
||||
);
|
||||
core.status.hero.mdef -=
|
||||
Math.ceil((core.status.hero.mdef / 10) * 0.3) * 10;
|
||||
}
|
||||
|
||||
// 极昼永夜
|
||||
if (special.includes(22)) {
|
||||
flags[`night_${floorId}`] ??= 0;
|
||||
flags[`night_${floorId}`] -= enemy.enemy.night!;
|
||||
}
|
||||
if (special.includes(23)) {
|
||||
flags[`night_${floorId}`] ??= 0;
|
||||
flags[`night_${floorId}`] += enemy.enemy.day;
|
||||
}
|
||||
|
||||
// if (core.plugin.skillTree.getSkillLevel(11) > 0) {
|
||||
// core.plugin.study.declineStudiedSkill();
|
||||
// }
|
||||
|
||||
// 如果是融化怪,需要特殊标记一下
|
||||
if (special.includes(25) && has(x) && has(y)) {
|
||||
flags[`melt_${floorId}`] ??= {};
|
||||
flags[`melt_${floorId}`][`${x},${y}`] = enemy.enemy.melt;
|
||||
}
|
||||
|
||||
// 获得金币
|
||||
const money = enemy.enemy.money;
|
||||
core.status.hero.money += money;
|
||||
core.status.hero.statistics.money += money;
|
||||
|
||||
// 获得经验
|
||||
const exp = enemy.enemy.exp;
|
||||
core.status.hero.exp += exp;
|
||||
core.status.hero.statistics.exp += exp;
|
||||
|
||||
const hint =
|
||||
'打败 ' + enemy.enemy.name + ',金币+' + money + ',经验+' + exp;
|
||||
core.drawTip(hint, enemy.id);
|
||||
|
||||
if (core.getFlag('bladeOn') && core.getFlag('blade')) {
|
||||
core.setFlag('blade', false);
|
||||
}
|
||||
if (core.getFlag('shieldOn') && core.getFlag('shield')) {
|
||||
core.setFlag('shield', false);
|
||||
}
|
||||
|
||||
// 事件的处理
|
||||
const todo: MotaEvent = [];
|
||||
|
||||
// 战后事件
|
||||
if (has(core.status.floorId)) {
|
||||
const loc = `${x},${y}` as LocString;
|
||||
todo.push(
|
||||
...(core.floors[core.status.floorId].afterBattle[loc] ?? [])
|
||||
);
|
||||
}
|
||||
todo.push(...(enemy.enemy.afterBattle ?? []));
|
||||
|
||||
// 如果事件不为空,将其插入
|
||||
if (todo.length > 0) core.insertAction(todo, x, y);
|
||||
|
||||
if (has(x) && has(y)) {
|
||||
core.drawAnimate(animate, x, y);
|
||||
core.removeBlock(x, y);
|
||||
} else core.drawHeroAnimate(animate);
|
||||
|
||||
// 如果已有事件正在处理中
|
||||
if (core.status.event.id == null) core.continueAutomaticRoute();
|
||||
else core.clearContinueAutomaticRoute();
|
||||
if (e.displayIdInBook) return e.displayIdInBook;
|
||||
if (e.faceIds) return e.faceIds.down;
|
||||
return e.id;
|
||||
};
|
||||
|
||||
core.enemys.getCurrentEnemys = function getCurrentEnemys(
|
||||
@ -176,7 +86,8 @@ function init() {
|
||||
ensureFloorDamage(floorId);
|
||||
const floor = core.status.maps[floorId];
|
||||
floor.enemy.list.forEach(v => {
|
||||
if (!(v.id in used)) {
|
||||
const id = getFacedId(v);
|
||||
if (!(id in used)) {
|
||||
const e = new DamageEnemy(v.enemy);
|
||||
e.calAttribute();
|
||||
e.getRealInfo();
|
||||
@ -186,9 +97,9 @@ function init() {
|
||||
onMapEnemy: [v]
|
||||
};
|
||||
enemys.push(curr);
|
||||
used[v.id] = curr.onMapEnemy;
|
||||
used[id] = curr.onMapEnemy;
|
||||
} else {
|
||||
used[v.id].push(v);
|
||||
used[id].push(v);
|
||||
}
|
||||
});
|
||||
|
||||
@ -207,7 +118,7 @@ function init() {
|
||||
const enemy = getEnemy(data.x, data.y);
|
||||
|
||||
beforeBattle.push(...(floor.beforeBattle[loc] ?? []));
|
||||
beforeBattle.push(...(enemy.enemy.beforeBattle ?? []));
|
||||
beforeBattle.push(...(enemy!.enemy.beforeBattle ?? []));
|
||||
|
||||
if (beforeBattle.length > 0) {
|
||||
beforeBattle.push({ type: 'battle', x: data.x, y: data.y });
|
||||
@ -251,5 +162,22 @@ loading.once('coreInit', init);
|
||||
declare global {
|
||||
interface Enemys {
|
||||
getCurrentEnemys(floorId: FloorIds): CurrentEnemy[];
|
||||
canBattle(enemy: DamageEnemy, _?: number, floorId?: FloorIds): boolean;
|
||||
canBattle(x: number, y: number, floorId?: FloorIds): boolean;
|
||||
}
|
||||
|
||||
interface Events {
|
||||
battle(
|
||||
enemy: DamageEnemy,
|
||||
_?: number,
|
||||
force?: boolean,
|
||||
callback?: () => void
|
||||
): void;
|
||||
battle(
|
||||
x: number,
|
||||
y?: number,
|
||||
force?: boolean,
|
||||
callback?: () => void
|
||||
): void;
|
||||
}
|
||||
}
|
||||
|
@ -16,15 +16,15 @@ interface HaloType {
|
||||
};
|
||||
}
|
||||
|
||||
interface EnemyInfo {
|
||||
interface EnemyInfo extends Partial<Enemy> {
|
||||
atk: number;
|
||||
def: number;
|
||||
hp: number;
|
||||
special: number[];
|
||||
damageDecline: number;
|
||||
atkBuff: number;
|
||||
defBuff: number;
|
||||
hpBuff: number;
|
||||
atkBuff_: number;
|
||||
defBuff_: number;
|
||||
hpBuff_: number;
|
||||
enemy: Enemy;
|
||||
x?: number;
|
||||
y?: number;
|
||||
@ -65,7 +65,7 @@ interface CriticalDamageDelta extends Omit<DamageDelta, 'info'> {
|
||||
type HaloFn = (info: EnemyInfo, enemy: Enemy) => void;
|
||||
|
||||
/** 光环属性 */
|
||||
export const haloSpecials: number[] = [8, 21, 25, 26, 27];
|
||||
export const haloSpecials: Set<number> = new Set([8, 21, 25, 26, 27]);
|
||||
|
||||
export class EnemyCollection implements RangeCollection<DamageEnemy> {
|
||||
floorId: FloorIds;
|
||||
@ -302,7 +302,7 @@ export class DamageEnemy<T extends EnemyIds = EnemyIds> {
|
||||
info!: EnemyInfo;
|
||||
|
||||
/** 向其他怪提供过的光环 */
|
||||
providedHalo: number[] = [];
|
||||
providedHalo: Set<number> = new Set();
|
||||
|
||||
/**
|
||||
* 伤害计算进度,0 -> 预平衡光环 -> 1 -> 计算没有光环的属性 -> 2 -> provide inject 光环
|
||||
@ -334,16 +334,23 @@ export class DamageEnemy<T extends EnemyIds = EnemyIds> {
|
||||
def: enemy.def,
|
||||
special: enemy.special.slice(),
|
||||
damageDecline: 0,
|
||||
atkBuff: 0,
|
||||
defBuff: 0,
|
||||
hpBuff: 0,
|
||||
atkBuff_: 0,
|
||||
defBuff_: 0,
|
||||
hpBuff_: 0,
|
||||
enemy: this.enemy,
|
||||
x: this.x,
|
||||
y: this.y,
|
||||
floorId: this.floorId
|
||||
};
|
||||
|
||||
for (const [key, value] of Object.entries(enemy)) {
|
||||
if (!(key in this.info) && has(value)) {
|
||||
// @ts-ignore
|
||||
this.info[key] = value;
|
||||
}
|
||||
}
|
||||
this.progress = 0;
|
||||
this.providedHalo = [];
|
||||
this.providedHalo.clear();
|
||||
}
|
||||
|
||||
/**
|
||||
@ -370,8 +377,8 @@ export class DamageEnemy<T extends EnemyIds = EnemyIds> {
|
||||
for (const [loc, per] of Object.entries(flags[`melt_${floorId}`])) {
|
||||
const [mx, my] = loc.split(',').map(v => parseInt(v));
|
||||
if (Math.abs(mx - this.x) <= 1 && Math.abs(my - this.y) <= 1) {
|
||||
info.atkBuff += per as number;
|
||||
info.defBuff += per as number;
|
||||
info.atkBuff_ += per as number;
|
||||
info.defBuff_ += per as number;
|
||||
}
|
||||
}
|
||||
}
|
||||
@ -392,9 +399,9 @@ export class DamageEnemy<T extends EnemyIds = EnemyIds> {
|
||||
// 此时已经inject光环,因此直接计算真实属性
|
||||
const info = this.info;
|
||||
|
||||
info.atk = Math.floor(info.atk * (info.atkBuff / 100 + 1));
|
||||
info.def = Math.floor(info.def * (info.defBuff / 100 + 1));
|
||||
info.hp = Math.floor(info.hp * (info.hpBuff / 100 + 1));
|
||||
info.atk = Math.floor(info.atk * (info.atkBuff_ / 100 + 1));
|
||||
info.def = Math.floor(info.def * (info.defBuff_ / 100 + 1));
|
||||
info.hp = Math.floor(info.hp * (info.hpBuff_ / 100 + 1));
|
||||
|
||||
return this.info;
|
||||
}
|
||||
@ -404,7 +411,7 @@ export class DamageEnemy<T extends EnemyIds = EnemyIds> {
|
||||
if (!has(this.x) || !has(this.y)) return [];
|
||||
const special = this.info.special ?? this.enemy.special;
|
||||
const filter = special.filter(v => {
|
||||
return haloSpecials.includes(v) && !this.providedHalo.includes(v);
|
||||
return haloSpecials.has(v) && !this.providedHalo.has(v);
|
||||
});
|
||||
if (filter.length === 0) return [];
|
||||
const collection = this.col ?? core.status.maps[this.floorId].enemy;
|
||||
@ -448,11 +455,11 @@ export class DamageEnemy<T extends EnemyIds = EnemyIds> {
|
||||
e.special.includes(8) &&
|
||||
(e.x !== this.x || this.y !== e.y)
|
||||
) {
|
||||
e.atkBuff += enemy.together ?? 0;
|
||||
e.defBuff += enemy.together ?? 0;
|
||||
e.atkBuff_ += enemy.together ?? 0;
|
||||
e.defBuff_ += enemy.together ?? 0;
|
||||
}
|
||||
});
|
||||
this.providedHalo.push(8);
|
||||
this.providedHalo.add(8);
|
||||
}
|
||||
|
||||
// 冰封光环
|
||||
@ -460,7 +467,7 @@ export class DamageEnemy<T extends EnemyIds = EnemyIds> {
|
||||
square7.push(e => {
|
||||
e.damageDecline += this.enemy.iceHalo ?? 0;
|
||||
});
|
||||
this.providedHalo.push(21);
|
||||
this.providedHalo.add(21);
|
||||
col.haloList.push({
|
||||
type: 'square',
|
||||
data: { x: this.x, y: this.y, d: 7 },
|
||||
@ -472,9 +479,9 @@ export class DamageEnemy<T extends EnemyIds = EnemyIds> {
|
||||
// 冰封之核
|
||||
if (special.includes(26)) {
|
||||
square5.push(e => {
|
||||
e.defBuff += this.enemy.iceCore ?? 0;
|
||||
e.defBuff_ += this.enemy.iceCore ?? 0;
|
||||
});
|
||||
this.providedHalo.push(26);
|
||||
this.providedHalo.add(26);
|
||||
col.haloList.push({
|
||||
type: 'square',
|
||||
data: { x: this.x, y: this.y, d: 5 },
|
||||
@ -486,9 +493,9 @@ export class DamageEnemy<T extends EnemyIds = EnemyIds> {
|
||||
// 火焰之核
|
||||
if (special.includes(27)) {
|
||||
square5.push(e => {
|
||||
e.atkBuff += this.enemy.fireCore ?? 0;
|
||||
e.atkBuff_ += this.enemy.fireCore ?? 0;
|
||||
});
|
||||
this.providedHalo.push(27);
|
||||
this.providedHalo.add(27);
|
||||
col.haloList.push({
|
||||
type: 'square',
|
||||
data: { x: this.x, y: this.y, d: 5 },
|
||||
@ -617,7 +624,7 @@ export class DamageEnemy<T extends EnemyIds = EnemyIds> {
|
||||
) {
|
||||
damage[loc] ??= { damage: 0, type: new Set() };
|
||||
damage[loc].damage += dam;
|
||||
damage[loc].type.add(type);
|
||||
if (type) damage[loc].type.add(type);
|
||||
}
|
||||
|
||||
private calEnemyDamageOf(hero: Partial<HeroStatus>, enemy: EnemyInfo) {
|
||||
@ -830,6 +837,9 @@ const skills: [unlock: string, condition: string][] = [
|
||||
['shieldOn', 'shield']
|
||||
];
|
||||
|
||||
const haloValue: Map<number, SelectKey<Enemy, number | undefined>[]> =
|
||||
new Map();
|
||||
|
||||
/**
|
||||
* 计算怪物伤害
|
||||
* @param info 怪物信息
|
||||
@ -946,13 +956,6 @@ export function getSingleEnemy(id: EnemyIds) {
|
||||
}
|
||||
|
||||
declare global {
|
||||
interface PluginDeclaration {
|
||||
damage: {
|
||||
Enemy: typeof DamageEnemy;
|
||||
Collection: typeof EnemyCollection;
|
||||
};
|
||||
}
|
||||
|
||||
interface Floor {
|
||||
enemy: EnemyCollection;
|
||||
}
|
||||
|
@ -84,14 +84,16 @@ export interface GameEvent extends EmitableEvent {
|
||||
mounted: () => void;
|
||||
/** Emitted in plugin/ui.js */
|
||||
statusBarUpdate: () => void;
|
||||
/** Emitted in libs/events.js */
|
||||
afterGetItem: (
|
||||
itemId: AllIdsOf<'items'>,
|
||||
x: number,
|
||||
y: number,
|
||||
isGentleClick: boolean
|
||||
) => void;
|
||||
afterOpenDoor: (doorId: AllIdsOf<'animates'>, x: number, y: number) => void;
|
||||
/** Emitted in core/index.ts */
|
||||
renderLoaded: () => void;
|
||||
// /** Emitted in libs/events.js */
|
||||
// afterGetItem: (
|
||||
// itemId: AllIdsOf<'items'>,
|
||||
// x: number,
|
||||
// y: number,
|
||||
// isGentleClick: boolean
|
||||
// ) => void;
|
||||
// afterOpenDoor: (doorId: AllIdsOf<'animates'>, x: number, y: number) => void;
|
||||
}
|
||||
|
||||
export const hook = new EventEmitter<GameEvent>();
|
||||
@ -112,7 +114,7 @@ class GameListener extends EventEmitter<ListenerEvent> {
|
||||
|
||||
constructor() {
|
||||
super();
|
||||
|
||||
if (main.replayChecking) return;
|
||||
if (!!window.core) {
|
||||
this.init();
|
||||
} else {
|
||||
@ -131,14 +133,19 @@ class GameListener extends EventEmitter<ListenerEvent> {
|
||||
|
||||
const getBlockLoc = (px: number, py: number, size: number) => {
|
||||
return [
|
||||
Math.floor(((px * 32) / size - core.bigmap.offsetX) / 32),
|
||||
Math.floor(((py * 32) / size - core.bigmap.offsetY) / 32)
|
||||
Math.floor(((px * 32) / size + core.bigmap.offsetX) / 32),
|
||||
Math.floor(((py * 32) / size + core.bigmap.offsetY) / 32)
|
||||
];
|
||||
};
|
||||
|
||||
// hover & leave & mouseMove
|
||||
data.addEventListener('mousemove', e => {
|
||||
if (core.status.lockControl || !core.isPlaying()) return;
|
||||
if (
|
||||
core.status.lockControl ||
|
||||
!core.isPlaying() ||
|
||||
!core.status.floorId
|
||||
)
|
||||
return;
|
||||
this.emit('mouseMove', e);
|
||||
const {
|
||||
x: px,
|
||||
@ -164,7 +171,12 @@ class GameListener extends EventEmitter<ListenerEvent> {
|
||||
}
|
||||
});
|
||||
data.addEventListener('mouseleave', e => {
|
||||
if (core.status.lockControl || !core.isPlaying()) return;
|
||||
if (
|
||||
core.status.lockControl ||
|
||||
!core.isPlaying() ||
|
||||
!core.status.floorId
|
||||
)
|
||||
return;
|
||||
const blocks = core.getMapBlocksObj();
|
||||
const lastBlock = blocks[`${lastHoverX},${lastHoverY}`];
|
||||
if (!!lastBlock) {
|
||||
@ -175,7 +187,12 @@ class GameListener extends EventEmitter<ListenerEvent> {
|
||||
});
|
||||
// click
|
||||
data.addEventListener('click', e => {
|
||||
if (core.status.lockControl || !core.isPlaying()) return;
|
||||
if (
|
||||
core.status.lockControl ||
|
||||
!core.isPlaying() ||
|
||||
!core.status.floorId
|
||||
)
|
||||
return;
|
||||
const {
|
||||
x: px,
|
||||
y: py,
|
||||
|
@ -18,11 +18,7 @@ import type { Keyboard } from '@/core/main/custom/keyboard';
|
||||
import type { CustomToolbar } from '@/core/main/custom/toolbar';
|
||||
import type { Focus, GameUi, UiController } from '@/core/main/custom/ui';
|
||||
import type { gameListener, hook } from './game';
|
||||
import type {
|
||||
MotaSetting,
|
||||
SettingDisplayer,
|
||||
SettingStorage
|
||||
} from '@/core/main/setting';
|
||||
import type { MotaSetting, SettingDisplayer } from '@/core/main/setting';
|
||||
import type { GameStorage } from '@/core/main/storage';
|
||||
import type { DamageEnemy, EnemyCollection } from './enemy/damage';
|
||||
import type { specials } from './enemy/special';
|
||||
@ -87,7 +83,7 @@ interface VariableInterface {
|
||||
sound: SoundController;
|
||||
resource: ResourceStore<Exclude<ResourceType, 'zip'>>;
|
||||
zipResource: ResourceStore<'zip'>;
|
||||
settingStorage: GameStorage<SettingStorage>;
|
||||
settingStorage: GameStorage;
|
||||
status: Ref<boolean>;
|
||||
// 定义于游戏进程,渲染进程依然可用
|
||||
haloSpecials: number[];
|
||||
|
@ -8,7 +8,7 @@
|
||||
* Inspired somewhat from https://msdn.microsoft.com/en-us/library/windows/desktop/dd375731(v=vs.85).aspx
|
||||
* But these are "more general", as they should work across browsers & OS`s.
|
||||
*/
|
||||
export enum KeyCode {
|
||||
export enum KeyCode {
|
||||
DependsOnKbLayout = -1,
|
||||
|
||||
/**
|
||||
|
@ -1,3 +1,4 @@
|
||||
import { ref } from 'vue';
|
||||
|
||||
export const status = ref(false);
|
||||
export const fontSize = ref(100);
|
||||
|
5
src/types/enemy.d.ts
vendored
5
src/types/enemy.d.ts
vendored
@ -71,6 +71,11 @@ type Enemy<I extends EnemyIds = EnemyIds> = {
|
||||
*/
|
||||
displayIdInBook: EnemyIds;
|
||||
|
||||
/**
|
||||
* 行走图朝向。在勇士撞上图块时,或图块在移动时,会自动选择最合适的朝向图块(如果存在定义)来进行绘制。
|
||||
*/
|
||||
faceIds: Record<Dir, EnemyIds>;
|
||||
|
||||
/**
|
||||
* 战前事件
|
||||
*/
|
||||
|
26
src/types/event.d.ts
vendored
26
src/types/event.d.ts
vendored
@ -99,22 +99,6 @@ interface Events extends EventData {
|
||||
*/
|
||||
trigger(x: number, y: number, callback?: () => void): void;
|
||||
|
||||
/**
|
||||
* 战斗,如果填写了坐标就会删除该点的敌人并触发战后事件
|
||||
* @example core.battle('greenSlime'); // 和从天而降的绿头怪战斗(如果打得过)
|
||||
* @param id 敌人id,必填
|
||||
* @param x 敌人的横坐标
|
||||
* @param y 敌人的纵坐标
|
||||
* @param force true表示强制战斗
|
||||
* @param callback 回调函数
|
||||
*/
|
||||
battle(
|
||||
x: number,
|
||||
y: number,
|
||||
force: boolean = false,
|
||||
callback?: () => void
|
||||
): void;
|
||||
|
||||
/**
|
||||
* 开门(包括三种基础墙)
|
||||
* @example core.openDoor(0, 0, true, core.jumpHero); // 打开左上角的门,需要钥匙,然后主角原地跳跃半秒
|
||||
@ -428,6 +412,7 @@ interface Events extends EventData {
|
||||
): void;
|
||||
|
||||
/**
|
||||
* @deprecated 已失效(大概
|
||||
* 设置一项敌人属性并计入存档
|
||||
* @example core.setEnemy('greenSlime', 'def', 0); // 把绿头怪的防御设为0
|
||||
* @param id 敌人id
|
||||
@ -447,6 +432,7 @@ interface Events extends EventData {
|
||||
): void;
|
||||
|
||||
/**
|
||||
* @deprecated 已失效(大概
|
||||
* 设置某个点的敌人属性
|
||||
* @param x 横坐标
|
||||
* @param y 纵坐标
|
||||
@ -469,6 +455,7 @@ interface Events extends EventData {
|
||||
): void;
|
||||
|
||||
/**
|
||||
* @deprecated 已失效(大概
|
||||
* 重置某个点的敌人属性
|
||||
* @param x 横坐标
|
||||
* @param y 纵坐标
|
||||
@ -483,6 +470,7 @@ interface Events extends EventData {
|
||||
): void;
|
||||
|
||||
/**
|
||||
* @deprecated 已失效(大概
|
||||
* 将某个点已经设置的敌人属性移动到其他点
|
||||
* @param fromX 起始横坐标
|
||||
* @param fromY 起始纵坐标
|
||||
@ -756,7 +744,11 @@ interface Events extends EventData {
|
||||
* @example core.tryUseItem('pickaxe'); // 尝试使用破墙镐
|
||||
* @param itemId 道具id,其中敌人手册、传送器和飞行器会被特殊处理
|
||||
*/
|
||||
tryUseItem(itemId: ItemIdOf<'tools' | 'constants'>): void;
|
||||
tryUseItem(
|
||||
itemId: ItemIdOf<'tools' | 'constants'>,
|
||||
noRoute?: boolean,
|
||||
callback?: () => void
|
||||
): void;
|
||||
|
||||
_sys_battle(data: Block, callback?: () => void): void;
|
||||
|
||||
|
@ -50,6 +50,8 @@ import { getDetailedEnemy } from '../plugin/ui/fixed';
|
||||
import { GameUi } from '@/core/main/custom/ui';
|
||||
import { gameKey } from '@/core/main/init/hotkey';
|
||||
import { mainUi } from '@/core/main/init/ui';
|
||||
import { mainSetting } from '@/core/main/setting';
|
||||
import { isMobile } from '@/plugin/use';
|
||||
|
||||
const props = defineProps<{
|
||||
num: number;
|
||||
@ -70,6 +72,14 @@ const drag = ref(false);
|
||||
const detail = ref(false);
|
||||
const selected = ref(0);
|
||||
|
||||
const settingScale = mainSetting.getValue('ui.bookScale', 100) / 100;
|
||||
const scale = isMobile
|
||||
? Math.max(settingScale * 15, 20)
|
||||
: Math.max(
|
||||
(window.innerWidth / window.innerHeight) * 15 * settingScale,
|
||||
20
|
||||
);
|
||||
|
||||
/**
|
||||
* 选择怪物,展示详细信息
|
||||
* @param enemy 选择的怪物
|
||||
@ -121,11 +131,13 @@ async function exit() {
|
||||
const hold = mainUi.holdOn();
|
||||
mainUi.close(props.num);
|
||||
if (core.events.recoverEvents(core.status.event.interval)) {
|
||||
hold.end(true);
|
||||
return;
|
||||
} else if (has(core.status.event.ui)) {
|
||||
core.status.boxAnimateObjs = [];
|
||||
// @ts-ignore
|
||||
core.ui._drawViewMaps(core.status.event.ui);
|
||||
hold.end(true);
|
||||
} else hold.end();
|
||||
}
|
||||
|
||||
@ -141,42 +153,44 @@ function checkScroll() {
|
||||
}
|
||||
|
||||
// 按键控制
|
||||
gameKey.use(props.ui.symbol);
|
||||
gameKey
|
||||
.realize('@book_up', () => {
|
||||
if (selected.value > 0) {
|
||||
selected.value--;
|
||||
}
|
||||
checkScroll();
|
||||
})
|
||||
.realize('@book_down', () => {
|
||||
if (selected.value < enemy.length - 1) {
|
||||
selected.value++;
|
||||
}
|
||||
checkScroll();
|
||||
})
|
||||
.realize('@book_pageDown', () => {
|
||||
if (selected.value <= 4) {
|
||||
selected.value = 0;
|
||||
} else {
|
||||
selected.value -= 5;
|
||||
}
|
||||
checkScroll();
|
||||
})
|
||||
.realize('@book_pageUp', () => {
|
||||
if (selected.value >= enemy.length - 5) {
|
||||
selected.value = enemy.length - 1;
|
||||
} else {
|
||||
selected.value += 5;
|
||||
}
|
||||
checkScroll();
|
||||
})
|
||||
.realize('exit', () => {
|
||||
exit();
|
||||
})
|
||||
.realize('confirm', () => {
|
||||
select(toShow[selected.value], selected.value);
|
||||
});
|
||||
setTimeout(() => {
|
||||
gameKey.use(props.ui.symbol);
|
||||
gameKey
|
||||
.realize('@book_up', () => {
|
||||
if (selected.value > 0) {
|
||||
selected.value--;
|
||||
}
|
||||
checkScroll();
|
||||
})
|
||||
.realize('@book_down', () => {
|
||||
if (selected.value < enemy.length - 1) {
|
||||
selected.value++;
|
||||
}
|
||||
checkScroll();
|
||||
})
|
||||
.realize('@book_pageDown', () => {
|
||||
if (selected.value <= 4) {
|
||||
selected.value = 0;
|
||||
} else {
|
||||
selected.value -= 5;
|
||||
}
|
||||
checkScroll();
|
||||
})
|
||||
.realize('@book_pageUp', () => {
|
||||
if (selected.value >= enemy.length - 5) {
|
||||
selected.value = enemy.length - 1;
|
||||
} else {
|
||||
selected.value += 5;
|
||||
}
|
||||
checkScroll();
|
||||
})
|
||||
.realize('exit', () => {
|
||||
exit();
|
||||
})
|
||||
.realize('confirm', () => {
|
||||
select(toShow[selected.value], selected.value);
|
||||
});
|
||||
}, 0);
|
||||
|
||||
onUnmounted(() => {
|
||||
gameKey.dispose(props.ui.symbol);
|
||||
@ -188,7 +202,6 @@ onUnmounted(() => {
|
||||
user-select: none;
|
||||
width: 80%;
|
||||
height: 100%;
|
||||
font-family: 'normal';
|
||||
overflow: hidden;
|
||||
transition: opacity 0.6s linear;
|
||||
display: flex;
|
||||
@ -208,25 +221,28 @@ onUnmounted(() => {
|
||||
display: flex;
|
||||
justify-content: center;
|
||||
align-items: center;
|
||||
font-family: 'normal';
|
||||
}
|
||||
|
||||
.enemy {
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
height: 20vh;
|
||||
height: v-bind('scale + "vh"');
|
||||
width: 100%;
|
||||
padding: 0 1% 0 1%;
|
||||
}
|
||||
|
||||
@media screen and (max-width: 600px) {
|
||||
#tools {
|
||||
transform: translateY(-50%);
|
||||
}
|
||||
|
||||
#book {
|
||||
width: 100%;
|
||||
padding: 5%;
|
||||
}
|
||||
|
||||
.enemy {
|
||||
height: 15vh;
|
||||
height: v-bind('scale * 2 / 3 + "vh"');
|
||||
}
|
||||
}
|
||||
</style>
|
||||
|
@ -1,6 +1,11 @@
|
||||
<!-- 怪物详细信息 -->
|
||||
<template>
|
||||
<div id="detail">
|
||||
<div id="tools">
|
||||
<span id="back" class="button-text tools" @click="close"
|
||||
><left-outlined />返回</span
|
||||
>
|
||||
</div>
|
||||
<div id="info" :style="{ top: `${top}px` }">
|
||||
<EnemyOne :enemy="enemy!"></EnemyOne>
|
||||
<a-divider
|
||||
@ -172,7 +177,7 @@ onUnmounted(() => {
|
||||
flex-direction: column;
|
||||
align-items: center;
|
||||
width: 72%;
|
||||
height: 90%;
|
||||
height: 100%;
|
||||
transition: all 0.6s ease;
|
||||
user-select: none;
|
||||
}
|
||||
@ -208,6 +213,15 @@ onUnmounted(() => {
|
||||
opacity: 0;
|
||||
}
|
||||
|
||||
#tools {
|
||||
position: fixed;
|
||||
height: 6%;
|
||||
font-size: 3.2vh;
|
||||
width: 100%;
|
||||
left: 5%;
|
||||
top: 5%;
|
||||
}
|
||||
|
||||
@media screen and (max-width: 600px) {
|
||||
#detail {
|
||||
width: 100%;
|
||||
@ -220,7 +234,11 @@ onUnmounted(() => {
|
||||
font-size: 4vw;
|
||||
bottom: 5%;
|
||||
left: 5vw;
|
||||
width: 90vw;
|
||||
width: 80vw;
|
||||
}
|
||||
|
||||
#info {
|
||||
transform: translateY(10%);
|
||||
}
|
||||
}
|
||||
</style>
|
||||
|
@ -701,14 +701,9 @@ onUnmounted(() => {
|
||||
}
|
||||
|
||||
@media screen and (max-width: 600px) {
|
||||
#equipbox {
|
||||
padding: 5%;
|
||||
}
|
||||
|
||||
#equipbox-main {
|
||||
height: 90vh;
|
||||
flex-direction: column-reverse;
|
||||
font-size: 100%;
|
||||
font-size: 225%;
|
||||
}
|
||||
|
||||
#equip-now-div {
|
||||
@ -721,7 +716,11 @@ onUnmounted(() => {
|
||||
}
|
||||
|
||||
#equip-list {
|
||||
flex-basis: 50%;
|
||||
flex-basis: 45%;
|
||||
|
||||
#filter #sort-type {
|
||||
font-size: 150%;
|
||||
}
|
||||
}
|
||||
|
||||
.divider {
|
||||
|
@ -56,15 +56,15 @@
|
||||
:type="isMobile ? 'horizontal' : 'vertical'"
|
||||
></a-divider>
|
||||
<div id="fly-right">
|
||||
<canvas id="fly-thumbnail" @click="fly"></canvas>
|
||||
<canvas id="fly-thumbnail" @click="fly" @wheel="wheel"></canvas>
|
||||
<div id="fly-tools">
|
||||
<double-left-outlined
|
||||
@click="changeFloorByDelta(-10)"
|
||||
class="button-text"
|
||||
class="button-text fly-button"
|
||||
/>
|
||||
<left-outlined
|
||||
@click="changeFloorByDelta(-1)"
|
||||
class="button-text"
|
||||
class="button-text fly-button"
|
||||
/>
|
||||
<span
|
||||
class="changable"
|
||||
@ -74,11 +74,11 @@
|
||||
>
|
||||
<right-outlined
|
||||
@click="changeFloorByDelta(1)"
|
||||
class="button-text"
|
||||
class="button-text fly-button"
|
||||
/>
|
||||
<double-right-outlined
|
||||
@click="changeFloorByDelta(10)"
|
||||
class="button-text"
|
||||
class="button-text fly-button"
|
||||
/>
|
||||
</div>
|
||||
</div>
|
||||
@ -472,6 +472,10 @@ function click(e: MouseEvent) {
|
||||
}
|
||||
}
|
||||
|
||||
function wheel(ev: WheelEvent) {
|
||||
changeFloorByDelta(-Math.sign(ev.deltaY));
|
||||
}
|
||||
|
||||
function changeAreaByFloor(id: FloorIds) {
|
||||
nowArea.value = Object.keys(area).find(v => area[v].includes(id))!;
|
||||
}
|
||||
@ -725,6 +729,7 @@ onUnmounted(() => {
|
||||
flex-direction: column;
|
||||
align-items: center;
|
||||
justify-content: space-around;
|
||||
position: relative;
|
||||
}
|
||||
|
||||
#fly-tools {
|
||||
@ -734,11 +739,17 @@ onUnmounted(() => {
|
||||
flex-direction: row;
|
||||
justify-content: space-around;
|
||||
align-items: center;
|
||||
position: absolute;
|
||||
bottom: 0;
|
||||
width: 100%;
|
||||
background-color: #0004;
|
||||
}
|
||||
|
||||
#fly-thumbnail {
|
||||
width: 35vw;
|
||||
height: 35vw;
|
||||
max-height: 75vh;
|
||||
max-width: 75vh;
|
||||
border: 0.1vw solid #ddd4;
|
||||
}
|
||||
|
||||
@ -766,10 +777,27 @@ onUnmounted(() => {
|
||||
background-color: #ddd4;
|
||||
}
|
||||
|
||||
.fly-button {
|
||||
padding: 3%;
|
||||
border-radius: 8px;
|
||||
background-color: rgba(0, 0, 0, 0.301);
|
||||
border: 1px dashed #ddda;
|
||||
filter: drop-shadow(0px 0px 16px black);
|
||||
}
|
||||
|
||||
#fly-now {
|
||||
text-wrap: nowrap;
|
||||
white-space: nowrap;
|
||||
max-width: 50%;
|
||||
text-overflow: ellipsis;
|
||||
overflow: hidden;
|
||||
text-shadow: 1px 1px 1px black, 1px -1px 1px black, -1px 1px 1px black,
|
||||
-1px -1px 1px black;
|
||||
}
|
||||
|
||||
@media screen and (max-width: 600px) {
|
||||
#fly {
|
||||
padding: 5%;
|
||||
font-size: 100%;
|
||||
font-size: 225%;
|
||||
}
|
||||
|
||||
#fly-main {
|
||||
|
@ -242,7 +242,7 @@ onUnmounted(() => {
|
||||
flex-direction: column;
|
||||
width: 50%;
|
||||
text-overflow: clip;
|
||||
align-items: end;
|
||||
align-items: flex-end;
|
||||
text-align: end;
|
||||
|
||||
.hotkey-one-set-item {
|
||||
|
@ -108,7 +108,7 @@ function update() {
|
||||
info.damage = enemy.enemy.calDamage().damage;
|
||||
const critical = enemy.enemy.calCritical()[0];
|
||||
info.critical = critical?.atkDelta ?? 0;
|
||||
info.criticalDam = critical.delta ?? 0;
|
||||
info.criticalDam = critical?.delta ?? 0;
|
||||
info.defDamage = enemy.enemy.calDefDamage(ratio).delta;
|
||||
}
|
||||
|
||||
|
@ -46,15 +46,19 @@
|
||||
</TransitionGroup>
|
||||
</div>
|
||||
<div class="setting-info">
|
||||
<div
|
||||
class="info-text"
|
||||
v-html="splitText(display.at(-1)?.text ?? ['请选择设置'])"
|
||||
></div>
|
||||
<Scroll class="info-text-scroll">
|
||||
<div
|
||||
class="info-text"
|
||||
v-html="
|
||||
splitText(display.at(-1)?.text ?? ['请选择设置'])
|
||||
"
|
||||
></div>
|
||||
</Scroll>
|
||||
<a-divider class="info-divider" dashed></a-divider>
|
||||
<div class="info-editor" v-if="!!selectedItem">
|
||||
<div class="editor-custom">
|
||||
<component
|
||||
:is="selectedItem.controller"
|
||||
:is="(selectedItem.controller as any)"
|
||||
:item="selectedItem"
|
||||
:displayer="displayer"
|
||||
:setting="setting"
|
||||
@ -68,16 +72,14 @@
|
||||
</template>
|
||||
|
||||
<script lang="ts" setup>
|
||||
import { computed, onMounted, onUnmounted, ref, shallowRef } from 'vue';
|
||||
import { computed, onUnmounted, ref, shallowRef } from 'vue';
|
||||
import {
|
||||
mainSetting,
|
||||
MotaSetting,
|
||||
MotaSettingItem,
|
||||
SettingDisplayer,
|
||||
SettingDisplayInfo,
|
||||
SettingText
|
||||
SettingDisplayInfo
|
||||
} from '../core/main/setting';
|
||||
import settingText from '../data/settings.json';
|
||||
import { RightOutlined, LeftOutlined } from '@ant-design/icons-vue';
|
||||
import { splitText } from '../plugin/utils';
|
||||
import Scroll from '../components/scroll.vue';
|
||||
@ -88,13 +90,11 @@ import { mainUi } from '@/core/main/init/ui';
|
||||
|
||||
const props = defineProps<{
|
||||
info?: MotaSetting;
|
||||
text?: SettingText;
|
||||
num: number;
|
||||
ui: GameUi;
|
||||
}>();
|
||||
|
||||
const setting = props.info ?? mainSetting;
|
||||
const text = props.text ?? (settingText as SettingText);
|
||||
const display = shallowRef<SettingDisplayInfo[]>([]);
|
||||
const selectedItem = computed(() => display.value.at(-1)?.item);
|
||||
const update = ref(false);
|
||||
@ -312,6 +312,11 @@ onUnmounted(() => {
|
||||
.info-text {
|
||||
font-size: 85%;
|
||||
min-height: 30%;
|
||||
max-height: 50%;
|
||||
}
|
||||
|
||||
.info-text-scroll {
|
||||
max-height: 50%;
|
||||
}
|
||||
}
|
||||
|
||||
@ -321,7 +326,7 @@ onUnmounted(() => {
|
||||
}
|
||||
|
||||
.setting-main {
|
||||
font-size: 120%;
|
||||
font-size: 225%;
|
||||
|
||||
.setting-container {
|
||||
flex-direction: column;
|
||||
|
@ -460,7 +460,7 @@ onUnmounted(() => {
|
||||
#shop {
|
||||
width: 90vw;
|
||||
padding-top: 5vh;
|
||||
font-size: 100%;
|
||||
font-size: 225%;
|
||||
}
|
||||
|
||||
#item-list {
|
||||
|
@ -130,13 +130,18 @@
|
||||
</template>
|
||||
|
||||
<script lang="ts" setup>
|
||||
import { onMounted, ref, shallowReactive, watch } from 'vue';
|
||||
import { onMounted, onUnmounted, ref, shallowReactive, watch } from 'vue';
|
||||
import Box from '../components/box.vue';
|
||||
import Scroll from '../components/scroll.vue';
|
||||
import { status } from '../plugin/ui/statusBar';
|
||||
import { isMobile } from '../plugin/use';
|
||||
import { has } from '../plugin/utils';
|
||||
import { fontSize } from '../plugin/ui/statusBar';
|
||||
import { has } from '@/plugin/utils';
|
||||
|
||||
let main: HTMLDivElement;
|
||||
|
||||
const items = core.flags.statusBarItems;
|
||||
const icons = core.statusBar.icons;
|
||||
const skillTree = Mota.Plugin.require('skillTree_g');
|
||||
|
||||
const width = ref(
|
||||
@ -148,6 +153,7 @@ const format = core.formatBigNumber;
|
||||
|
||||
watch(width, n => (updateStatus.value = !updateStatus.value));
|
||||
watch(height, n => (updateStatus.value = !updateStatus.value));
|
||||
watch(fontSize, n => (main.style.fontSize = `${isMobile ? n * 1.5 : n}%`));
|
||||
|
||||
const hero = shallowReactive<Partial<HeroStatus>>({});
|
||||
const keys = shallowReactive<number[]>([]);
|
||||
@ -230,8 +236,24 @@ function viewMap() {
|
||||
|
||||
function openStudy() {}
|
||||
|
||||
function resize() {
|
||||
requestAnimationFrame(() => {
|
||||
main.style.fontSize = `${
|
||||
isMobile ? fontSize.value * 1.5 : fontSize.value
|
||||
}%`;
|
||||
});
|
||||
}
|
||||
|
||||
onMounted(() => {
|
||||
update();
|
||||
main = document.getElementById('status-main') as HTMLDivElement;
|
||||
|
||||
window.addEventListener('resize', resize);
|
||||
resize();
|
||||
});
|
||||
|
||||
onUnmounted(() => {
|
||||
window.removeEventListener('resize', resize);
|
||||
});
|
||||
</script>
|
||||
|
||||
@ -241,14 +263,16 @@ onMounted(() => {
|
||||
width: 100%;
|
||||
height: 100%;
|
||||
padding: 1vh 0;
|
||||
font-size: v-bind(fontSize);
|
||||
}
|
||||
|
||||
.status-item {
|
||||
position: relative;
|
||||
display: flex;
|
||||
flex-direction: row;
|
||||
max-width: 17.5vw;
|
||||
font-size: 200%;
|
||||
width: 100%;
|
||||
margin-bottom: 1vh;
|
||||
margin-bottom: 14px;
|
||||
text-shadow: 3px 2px 3px #000, 0px 0px 3px #111;
|
||||
display: flex;
|
||||
flex-direction: row;
|
||||
@ -266,6 +290,10 @@ onMounted(() => {
|
||||
margin-left: 10%;
|
||||
}
|
||||
|
||||
.status-value {
|
||||
transform: translateY(2px);
|
||||
}
|
||||
|
||||
#status-header {
|
||||
width: 100%;
|
||||
display: flex;
|
||||
|
@ -3,41 +3,64 @@
|
||||
<span class="button-text" @click="exit"><left-outlined /> 返回</span>
|
||||
</div>
|
||||
<div id="tool-editor">
|
||||
<Scroll class="tool-list-scroll">
|
||||
<div id="tool-list">
|
||||
<div
|
||||
v-for="(item, i) of list"
|
||||
class="tool-list-item selectable"
|
||||
:selected="i === selected"
|
||||
@click="selected = i"
|
||||
>
|
||||
<span>{{ item.id }}</span>
|
||||
<a-button
|
||||
type="danger"
|
||||
class="tool-list-delete"
|
||||
@click.stop="deleteTool(item.id)"
|
||||
>删除</a-button
|
||||
>
|
||||
</div>
|
||||
<div id="tool-list-add">
|
||||
<div id="tool-left">
|
||||
<Scroll class="tool-list-scroll">
|
||||
<div id="tool-list">
|
||||
<div
|
||||
id="tool-add-div"
|
||||
@click="addingTool = true"
|
||||
v-if="!addingTool"
|
||||
v-for="(item, i) of list"
|
||||
class="tool-list-item selectable"
|
||||
:selected="i === selected"
|
||||
@click="selected = i"
|
||||
>
|
||||
<PlusOutlined></PlusOutlined>
|
||||
<span>新增工具栏</span>
|
||||
<span>{{ item.id }}</span>
|
||||
<a-button
|
||||
type="danger"
|
||||
class="tool-list-delete"
|
||||
@click.stop="deleteTool(item.id)"
|
||||
>删除</a-button
|
||||
>
|
||||
</div>
|
||||
<div v-else>
|
||||
<a-input
|
||||
style="height: 100%; font-size: 80%; width: 100%"
|
||||
v-model:value="addingToolId"
|
||||
@blur="addTool"
|
||||
></a-input>
|
||||
<div id="tool-list-add">
|
||||
<div
|
||||
id="tool-add-div"
|
||||
@click="addingTool = true"
|
||||
v-if="!addingTool"
|
||||
>
|
||||
<PlusOutlined></PlusOutlined>
|
||||
<span>新增工具栏</span>
|
||||
</div>
|
||||
<div v-else>
|
||||
<a-input
|
||||
style="
|
||||
height: 100%;
|
||||
font-size: 80%;
|
||||
width: 100%;
|
||||
"
|
||||
v-model:value="addingToolId"
|
||||
@blur="addTool"
|
||||
></a-input>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</Scroll>
|
||||
<a-divider
|
||||
type="vertical"
|
||||
dashed
|
||||
v-if="isMobile"
|
||||
style="height: 100%; border-color: #ddd4"
|
||||
></a-divider>
|
||||
<div id="tool-preview" v-if="!!bar && isMobile">
|
||||
<div id="tool-preview-container">
|
||||
<div class="tool-preview-item" v-for="item of bar.items">
|
||||
<component
|
||||
:is="(CustomToolbar.info[item.type].show as any)"
|
||||
:item="item"
|
||||
:toolbar="bar"
|
||||
></component>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</Scroll>
|
||||
</div>
|
||||
<a-divider
|
||||
class="divider"
|
||||
dashed
|
||||
@ -151,8 +174,8 @@
|
||||
</div>
|
||||
</Scroll>
|
||||
</div>
|
||||
<a-divider dashed></a-divider>
|
||||
<div id="tool-preview" v-if="!!bar">
|
||||
<a-divider dashed v-if="!isMobile"></a-divider>
|
||||
<div id="tool-preview" v-if="!!bar && !isMobile">
|
||||
<div id="tool-preview-container">
|
||||
<div class="tool-preview-item" v-for="item of bar.items">
|
||||
<component
|
||||
@ -181,12 +204,15 @@ import { isMobile } from '@/plugin/use';
|
||||
import Scroll from '@/components/scroll.vue';
|
||||
import { deleteWith, tip } from '@/plugin/utils';
|
||||
import { Modal } from 'ant-design-vue';
|
||||
import { mainSetting } from '@/core/main/setting';
|
||||
|
||||
const props = defineProps<{
|
||||
ui: GameUi;
|
||||
num: number;
|
||||
}>();
|
||||
|
||||
const scale = mainSetting.getValue('ui.toolbarScale', 100) / 100;
|
||||
|
||||
const list = CustomToolbar.list;
|
||||
|
||||
const selected = ref(0);
|
||||
@ -331,7 +357,13 @@ onUnmounted(() => {
|
||||
|
||||
.tool-list-scroll {
|
||||
height: 100%;
|
||||
width: 30%;
|
||||
width: 100%;
|
||||
}
|
||||
|
||||
#tool-left {
|
||||
flex-basis: 30%;
|
||||
display: flex;
|
||||
height: 100%;
|
||||
}
|
||||
|
||||
#tool-list {
|
||||
@ -478,7 +510,7 @@ onUnmounted(() => {
|
||||
height: 40%;
|
||||
display: flex;
|
||||
justify-content: center;
|
||||
align-items: start;
|
||||
align-items: flex-start;
|
||||
|
||||
#tool-preview-container {
|
||||
width: 90%;
|
||||
@ -491,9 +523,9 @@ onUnmounted(() => {
|
||||
|
||||
.tool-preview-item {
|
||||
display: flex;
|
||||
margin: 5px;
|
||||
min-width: 50px;
|
||||
height: 50px;
|
||||
margin: v-bind('5 * scale + "px"');
|
||||
min-width: v-bind('50 * scale + "px"');
|
||||
height: v-bind('50 * scale + "px"');
|
||||
background-color: #222;
|
||||
border: 1.5px solid #ddd8;
|
||||
justify-content: center;
|
||||
@ -509,4 +541,45 @@ onUnmounted(() => {
|
||||
.divider {
|
||||
height: 100%;
|
||||
}
|
||||
|
||||
@media screen and (max-width: 600px) {
|
||||
#tool-editor {
|
||||
padding-top: 15%;
|
||||
flex-direction: column;
|
||||
width: 100%;
|
||||
height: 100%;
|
||||
}
|
||||
|
||||
#tool-left {
|
||||
width: 100%;
|
||||
flex-basis: 40%;
|
||||
max-height: 40vh;
|
||||
display: flex;
|
||||
flex-direction: row;
|
||||
|
||||
.tool-list-scroll {
|
||||
height: 100%;
|
||||
flex-basis: 50%;
|
||||
}
|
||||
|
||||
#tool-preview {
|
||||
flex-basis: 50%;
|
||||
height: 100%;
|
||||
}
|
||||
}
|
||||
|
||||
#tool-info {
|
||||
width: 100%;
|
||||
flex-basis: 60%;
|
||||
|
||||
#tool-detail {
|
||||
height: 100%;
|
||||
}
|
||||
}
|
||||
|
||||
.divider {
|
||||
height: auto;
|
||||
width: 100%;
|
||||
}
|
||||
}
|
||||
</style>
|
||||
|
@ -28,6 +28,7 @@
|
||||
import Box from '@/components/box.vue';
|
||||
import { CustomToolbar } from '@/core/main/custom/toolbar';
|
||||
import { GameUi } from '@/core/main/custom/ui';
|
||||
import { mainSetting } from '@/core/main/setting';
|
||||
import { onUnmounted, reactive, watch } from 'vue';
|
||||
|
||||
interface BoxData {
|
||||
@ -44,6 +45,7 @@ const props = defineProps<{
|
||||
}>();
|
||||
|
||||
const bar = props.bar;
|
||||
const scale = mainSetting.getValue('ui.toolbarScale', 100) / 100;
|
||||
|
||||
const box = reactive<BoxData>({
|
||||
x: bar.x,
|
||||
@ -80,7 +82,7 @@ onUnmounted(() => {
|
||||
<style lang="less" scoped>
|
||||
.toolbar-container {
|
||||
background-color: #0009;
|
||||
padding: 5px;
|
||||
padding: v-bind('scale * 5 + "px"');
|
||||
}
|
||||
|
||||
.toolbar {
|
||||
@ -93,9 +95,9 @@ onUnmounted(() => {
|
||||
|
||||
.toolbar-item {
|
||||
display: flex;
|
||||
margin: 5px;
|
||||
min-width: 50px;
|
||||
height: 50px;
|
||||
margin: v-bind('scale * 5 + "px"');
|
||||
min-width: v-bind('scale * 50 + "px"');
|
||||
height: v-bind('scale * 50 + "px"');
|
||||
cursor: pointer;
|
||||
background-color: #222;
|
||||
border: 1.5px solid #ddd8;
|
||||
@ -106,7 +108,7 @@ onUnmounted(() => {
|
||||
|
||||
.toolbar-item::v-deep(> *) {
|
||||
height: 100%;
|
||||
min-width: 50px;
|
||||
min-width: v-bind('scale * 50 + "px"');
|
||||
display: flex;
|
||||
justify-content: center;
|
||||
align-items: center;
|
||||
|
@ -182,9 +182,9 @@ function use(id: ShowItemIds) {
|
||||
const hold = mainUi.holdOn();
|
||||
exit();
|
||||
nextTick(() => {
|
||||
core.useItem(id, false, () => {
|
||||
core.tryUseItem(id, false, () => {
|
||||
if (mainUi.stack.length === 0) {
|
||||
hold.end();
|
||||
hold.end(core.status.event.id !== 'toolbox');
|
||||
}
|
||||
});
|
||||
});
|
||||
@ -377,31 +377,23 @@ onUnmounted(() => {
|
||||
align-items: center;
|
||||
width: 100%;
|
||||
height: 100%;
|
||||
flex-basis: 50%;
|
||||
|
||||
#desc-text {
|
||||
margin-top: 2vh;
|
||||
margin-left: 0.5vw;
|
||||
width: 100%;
|
||||
height: 100%;
|
||||
max-height: 100%;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@media screen and (max-width: 600px) {
|
||||
#toolbox {
|
||||
padding: 5%;
|
||||
}
|
||||
|
||||
#tools {
|
||||
span {
|
||||
margin: 0;
|
||||
}
|
||||
}
|
||||
|
||||
#toolbox-main {
|
||||
flex-direction: column-reverse;
|
||||
height: 100%;
|
||||
font-size: 100%;
|
||||
height: 90%;
|
||||
font-size: 225%;
|
||||
margin-top: 10%;
|
||||
}
|
||||
|
||||
.item-list {
|
||||
@ -418,5 +410,16 @@ onUnmounted(() => {
|
||||
display: flex;
|
||||
flex-direction: column-reverse;
|
||||
}
|
||||
|
||||
#detail {
|
||||
flex-basis: 30%;
|
||||
|
||||
#desc {
|
||||
#desc-text {
|
||||
max-height: 10vh;
|
||||
height: 10vh;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
</style>
|
||||
|
Loading…
Reference in New Issue
Block a user