refactor: 移动鼠标定点查看

This commit is contained in:
unanmed 2023-11-12 22:54:19 +08:00
parent 5b36d8977f
commit 5e4a8c1eb3
15 changed files with 264 additions and 165 deletions

View File

@ -3013,7 +3013,7 @@ control.prototype.removeSwitch = function (x, y, floorId, name) {
control.prototype.lockControl = function () { control.prototype.lockControl = function () {
core.status.lockControl = true; core.status.lockControl = true;
if (!main.replayChecking) { if (!main.replayChecking) {
mota.plugin.fixed.showFixed.value = false; // mota.plugin.fixed.showFixed.value = false;
} }
}; };

View File

@ -405,16 +405,16 @@ var functions_d6ad677b_427a_4623_b50f_a445a3b0ef8a = {
break; break;
case 67: // C怪物临界 case 67: // C怪物临界
if (core.getBlockCls(mx, my)?.startsWith('enemy')) { if (core.getBlockCls(mx, my)?.startsWith('enemy')) {
mota.plugin.fixed.showFixed.value = false; // mota.plugin.fixed.showFixed.value = false;
mota.ui.main.open('fixedDetail', void 0, { mota.ui.main.open('fixedDetail', {
panel: 'critical' panel: 'critical'
}); });
} }
break; break;
case 69: // E怪物属性 case 69: // E怪物属性
if (core.getBlockCls(mx, my)?.startsWith('enemy')) { if (core.getBlockCls(mx, my)?.startsWith('enemy')) {
mota.plugin.fixed.showFixed.value = false; // mota.plugin.fixed.showFixed.value = false;
mota.ui.main.open('fixedDetail', void 0, { mota.ui.main.open('fixedDetail', {
panel: 'special' panel: 'special'
}); });
} }

View File

@ -8,6 +8,7 @@ import { GameEvent, hook } from './main/game';
import { fixedUi, mainUi } from './main/init/ui'; import { fixedUi, mainUi } from './main/init/ui';
import { GameStorage } from './main/storage'; import { GameStorage } from './main/storage';
import { resolvePlugin } from './plugin'; import { resolvePlugin } from './plugin';
import './main/init/';
interface AncTePlugin { interface AncTePlugin {
pop: ReturnType<typeof import('../plugin/pop').default>; pop: ReturnType<typeof import('../plugin/pop').default>;

View File

@ -302,8 +302,8 @@ hotkey
const [x, y] = flags.mouseLoc ?? []; const [x, y] = flags.mouseLoc ?? [];
const [mx, my] = getLocFromMouseLoc(x, y); const [mx, my] = getLocFromMouseLoc(x, y);
if (core.getBlockCls(mx, my)?.startsWith('enemy')) { if (core.getBlockCls(mx, my)?.startsWith('enemy')) {
mota.plugin.fixed.showFixed.value = false; // mota.plugin.fixed.showFixed.value = false;
mota.ui.main.open('fixedDetail', void 0, { mota.ui.main.open('fixedDetail', {
panel: 'special' panel: 'special'
}); });
} }
@ -315,8 +315,8 @@ hotkey
const [x, y] = flags.mouseLoc ?? []; const [x, y] = flags.mouseLoc ?? [];
const [mx, my] = getLocFromMouseLoc(x, y); const [mx, my] = getLocFromMouseLoc(x, y);
if (core.getBlockCls(mx, my)?.startsWith('enemy')) { if (core.getBlockCls(mx, my)?.startsWith('enemy')) {
mota.plugin.fixed.showFixed.value = false; // mota.plugin.fixed.showFixed.value = false;
mota.ui.main.open('fixedDetail', void 0, { mota.ui.main.open('fixedDetail', {
panel: 'critical' panel: 'critical'
}); });
} }

View File

@ -150,7 +150,7 @@ export class GameUi extends EventEmitter<GameUiEvent> {
* @param vOn * @param vOn
* @param vBind * @param vBind
*/ */
with(vOn?: UiVOn, vBind?: UiVBind): ShowableGameUi { with(vBind?: UiVBind, vOn?: UiVOn): ShowableGameUi {
return { ui: this, vOn, vBind }; return { ui: this, vOn, vBind };
} }
} }
@ -257,15 +257,16 @@ export class UiController extends Focus<IndexedGameUi> {
* @param vBind * @param vBind
* @returns ui的唯一标识符 * @returns ui的唯一标识符
*/ */
open(id: string, vOn?: UiVOn, vBind?: UiVBind) { open(id: string, vBind?: UiVBind, vOn?: UiVOn) {
const ui = this.get(id); const ui = this.get(id);
if (!ui) return -1; if (!ui) return -1;
const num = this.num++; const num = this.num++;
const sui = ui.with(vOn, { const bind = {
num, num,
ui, ui,
...(vBind ?? {}) ...(vBind ?? {})
}); };
const sui = ui.with(bind, vOn);
this.add({ this.add({
num, num,
...sui ...sui

View File

@ -11,9 +11,12 @@ export interface GameEvent extends EmitableEvent {
export const hook = new EventEmitter<GameEvent>(); export const hook = new EventEmitter<GameEvent>();
interface ListenerEvent extends EmitableEvent { interface ListenerEvent extends EmitableEvent {
hoverBlock: (block: Block) => void; // block
leaveBlock: (block: Block) => void; hoverBlock: (block: Block, ev: MouseEvent) => void;
clickBlock: (block: Block) => void; leaveBlock: (block: Block, ev: MouseEvent, leaveGame: boolean) => void;
clickBlock: (block: Block, ev: MouseEvent) => void;
// mouse
mouseMove: (ev: MouseEvent) => void;
} }
class GameListener extends EventEmitter<ListenerEvent> { class GameListener extends EventEmitter<ListenerEvent> {
@ -24,74 +27,83 @@ class GameListener extends EventEmitter<ListenerEvent> {
constructor() { constructor() {
super(); super();
loading.once('coreInit', () => { if (!!window.core) {
this.init(); this.init();
}); } else {
loading.once('coreInit', () => {
this.init();
});
}
} }
private init() { private init() {
// block // ----- block
let lastHoverX = -1; let lastHoverX = -1;
let lastHoverY = -1; let lastHoverY = -1;
const getBlockLoc = (px: number, py: number) => { const data = core.canvas.data.canvas;
const getBlockLoc = (px: number, py: number, size: number) => {
return [ return [
Math.floor((px - core.bigmap.offsetX) / 32), Math.floor(((px * 32) / size - core.bigmap.offsetX) / 32),
Math.floor((py - core.bigmap.offsetY) / 32) Math.floor(((py * 32) / size - core.bigmap.offsetY) / 32)
]; ];
}; };
core.registerAction( // hover & leave
'onmove', data.addEventListener('mousemove', e => {
`@GameListener_${this.num}_block`, if (core.status.lockControl || !core.isPlaying()) return;
(x, y, px, py) => { this.emit('mouseMove', e);
if (core.status.lockControl || !core.isPlaying()) return false; const {
const [bx, by] = getBlockLoc(px, py); x: px,
const blocks = core.getMapBlocksObj(); y: py,
if (lastHoverX !== bx || lastHoverY !== by) { size
const lastBlock = blocks[`${lastHoverX},${lastHoverY}`]; } = core.actions._getClickLoc(e.clientX, e.clientY);
const block = blocks[`${bx},${by}`]; const [bx, by] = getBlockLoc(px, py, size);
if (!!lastBlock) { const blocks = core.getMapBlocksObj();
this.emit('leaveBlock', lastBlock); if (lastHoverX !== bx || lastHoverY !== by) {
} const lastBlock = blocks[`${lastHoverX},${lastHoverY}`];
if (!!block) { const block = blocks[`${bx},${by}`];
this.emit('hoverBlock', block); if (!!lastBlock) {
lastHoverX = bx; this.emit('leaveBlock', lastBlock, e, false);
lastHoverY = by;
} else {
lastHoverX = -1;
lastHoverY = -1;
}
} }
return false; if (!!block) {
}, this.emit('hoverBlock', block, e);
50 lastHoverX = bx;
); lastHoverY = by;
core.canvas.data.canvas.addEventListener('mouseleave', () => { } else {
lastHoverX = -1;
lastHoverY = -1;
}
}
});
data.addEventListener('mouseleave', e => {
if (core.status.lockControl || !core.isPlaying()) return; if (core.status.lockControl || !core.isPlaying()) return;
const blocks = core.getMapBlocksObj(); const blocks = core.getMapBlocksObj();
const lastBlock = blocks[`${lastHoverX},${lastHoverY}`]; const lastBlock = blocks[`${lastHoverX},${lastHoverY}`];
if (!!lastBlock) { if (!!lastBlock) {
this.emit('leaveBlock', lastBlock); this.emit('leaveBlock', lastBlock, e, true);
} }
lastHoverX = -1; lastHoverX = -1;
lastHoverY = -1; lastHoverY = -1;
}); });
core.registerAction( // click
'onup', data.addEventListener('click', e => {
`@GameListener_${this.num}_block`, if (core.status.lockControl || !core.isPlaying()) return;
(x, y, px, py) => { const {
if (core.status.lockControl || !core.isPlaying()) return false; x: px,
const [bx, by] = getBlockLoc(px, py); y: py,
const blocks = core.getMapBlocksObj(); size
const block = blocks[`${bx},${by}`]; } = core.actions._getClickLoc(e.clientX, e.clientY);
if (!!block) { const [bx, by] = getBlockLoc(px, py, size);
this.emit('clickBlock', block); const blocks = core.getMapBlocksObj();
} const block = blocks[`${bx},${by}`];
return false; if (!!block) {
}, this.emit('clickBlock', block, e);
50 }
); });
// ----- mouse
} }
} }

View File

@ -0,0 +1,55 @@
import { debounce } from 'lodash-es';
import { gameListener } from '../game';
import { fixedUi } from './ui';
import { ref } from 'vue';
import { sleep } from 'mutate-animate';
const close = ref(false);
let cx = 0;
let cy = 0;
/**
*
*/
const showFixed = debounce((block: Block) => {
const e = core.material.enemys[block.event.id as EnemyIds];
if (!e) return;
const enemy = core.status.thisMap.enemy.get(block.x, block.y);
fixedUi.open(
'fixed',
{ enemy, close, loc: [cx, cy] },
{ close: closeFixed }
);
}, 200);
/**
*
*/
const closeFixed = () => {
close.value = true;
sleep(200).then(() => {
fixedUi.closeByName('fixed');
close.value = false;
});
};
let hovered: Block | null;
gameListener.on('hoverBlock', block => {
closeFixed();
hovered = block;
});
gameListener.on('leaveBlock', (_, __, leaveGame) => {
showFixed.cancel();
if (!leaveGame) closeFixed();
hovered = null;
});
gameListener.on('mouseMove', e => {
cx = e.clientX;
cy = e.clientY;
showFixed.cancel();
if (hovered) {
showFixed(hovered);
}
});

View File

@ -0,0 +1 @@
import './fixed';

View File

@ -8,7 +8,6 @@ import mark from '@/plugin/mark';
import chapter from '@/plugin/ui/chapter'; import chapter from '@/plugin/ui/chapter';
import fly from '@/plugin/ui/fly'; import fly from '@/plugin/ui/fly';
import chase from '@/plugin/chase/chase'; import chase from '@/plugin/chase/chase';
import fixed from '@/plugin/ui/fixed';
import webglUtils from '@/plugin/webgl/utils'; import webglUtils from '@/plugin/webgl/utils';
import shadow from '@/plugin/shadow/shadow'; import shadow from '@/plugin/shadow/shadow';
import gameShadow from '@/plugin/shadow/gameShadow'; import gameShadow from '@/plugin/shadow/gameShadow';
@ -33,7 +32,6 @@ export function resolvePlugin() {
['chapter', chapter()], ['chapter', chapter()],
['fly', fly()], ['fly', fly()],
['chase', chase()], ['chase', chase()],
['fixed', fixed()],
['webglUtils', webglUtils()], ['webglUtils', webglUtils()],
['shadow', shadow()], ['shadow', shadow()],
['gameShadow', gameShadow()], ['gameShadow', gameShadow()],

View File

@ -74,6 +74,10 @@ export class EnemyCollection implements RangeCollection<DamageEnemy> {
this.extract(); this.extract();
} }
get(x: number, y: number) {
return this.list.find(v => v.x === x && v.y === y);
}
/** /**
* *
*/ */

View File

@ -11,7 +11,7 @@ export {};
function openItemShop(itemShopId) { function openItemShop(itemShopId) {
if (!core.isReplaying()) { if (!core.isReplaying()) {
mota.ui.main.open('shop', void 0, { mota.ui.main.open('shop', {
shopId: itemShopId shopId: itemShopId
}); });
} }

View File

@ -5,46 +5,46 @@ import { ToShowEnemy, detailInfo } from './book';
import { DamageEnemy } from '../game/enemy/damage'; import { DamageEnemy } from '../game/enemy/damage';
import { isMobile } from '../use'; import { isMobile } from '../use';
export const showFixed = ref(false); // export const showFixed = ref(false);
let lastId: EnemyIds; // let lastId: EnemyIds;
const show = debounce((ev: MouseEvent) => { // const show = debounce((ev: MouseEvent) => {
if (!window.flags) return; // if (!window.flags) return;
if (!flags.mouseLoc) return; // if (!flags.mouseLoc) return;
flags.clientLoc = [ev.clientX, ev.clientY]; // flags.clientLoc = [ev.clientX, ev.clientY];
const [mx, my] = getLocFromMouseLoc(...flags.mouseLoc); // const [mx, my] = getLocFromMouseLoc(...flags.mouseLoc);
const e = core.status.thisMap.enemy.list.find(v => { // const e = core.status.thisMap.enemy.list.find(v => {
return v.x === mx && v.y === my; // return v.x === mx && v.y === my;
}); // });
if (!e) return; // if (!e) return;
lastId = e.id; // lastId = e.id;
const detail = getDetailedEnemy(e); // const detail = getDetailedEnemy(e);
detailInfo.enemy = detail; // detailInfo.enemy = detail;
showFixed.value = true; // showFixed.value = true;
}, 200); // }, 200);
export default function init() { // export default function init() {
const data = core.canvas.data.canvas; // const data = core.canvas.data.canvas;
data.addEventListener('mousemove', ev => { // data.addEventListener('mousemove', ev => {
if (!core.isPlaying() || core.status.lockControl) return; // if (!core.isPlaying() || core.status.lockControl) return;
const [mx, my] = getLocFromMouseLoc(...flags.mouseLoc); // const [mx, my] = getLocFromMouseLoc(...flags.mouseLoc);
const e = core.getBlockId(mx, my); // const e = core.getBlockId(mx, my);
if (e !== lastId) showFixed.value = false; // if (e !== lastId) showFixed.value = false;
if (!e) return; // if (!e) return;
show(ev); // show(ev);
}); // });
data.addEventListener('mousedown', ev => { // data.addEventListener('mousedown', ev => {
showFixed.value = false; // showFixed.value = false;
}); // });
return { // return {
showFixed // showFixed
}; // };
} // }
export function getLocFromMouseLoc(x: number, y: number): LocArr { export function getLocFromMouseLoc(x: number, y: number): LocArr {
const mx = Math.round(x + core.bigmap.offsetX / 32); const mx = Math.round(x + core.bigmap.offsetX / 32);

View File

@ -136,6 +136,15 @@ interface Actions extends VoidedActionFuncs {
_getNextFlyFloor(delta: number, index: number): number; _getNextFlyFloor(delta: number, index: number): number;
_clickGameInfo_openComments(); _clickGameInfo_openComments();
_getClickLoc(
x: number,
y: number
): {
x: number;
y: number;
size: number;
};
} }
declare const actions: new () => Actions; declare const actions: new () => Actions;

View File

@ -177,9 +177,6 @@ interface PluginUis {
/** 是否展示标记的怪物 */ /** 是否展示标记的怪物 */
readonly showMarkedEnemy: Ref<boolean>; readonly showMarkedEnemy: Ref<boolean>;
/** 是否展示移动鼠标显示怪物信息的盒子 */
readonly showFixed: Ref<boolean>;
/** /**
* *
* @param chapter * @param chapter

View File

@ -1,63 +1,94 @@
<template> <template>
<div id="fixed"> <div id="fixed">
<Transition> <Box
<Box v-model:height="height"
v-if="showFixed" v-model:left="left"
v-model:height="height" v-model:top="top"
v-model:left="left" v-model:width="width"
v-model:top="top" >
v-model:width="width" <div id="enemy-fixed">
> <span id="enemy-name">{{ enemy.enemy.name }}</span>
<div id="enemy-fixed"> <div id="enemy-special">
<span id="enemy-name">{{ enemy.enemy.enemy.name }}</span> <span
<div id="enemy-special"> v-for="(text, i) of special"
<span :style="{ color: text[1] }"
v-for="(text, i) of enemy.showSpecial" >{{ text[0] }}</span
:style="{ color: text[2] }" >
>{{ text[0] }}</span
>
</div>
<div class="enemy-attr" v-for="(a, i) of toShowAttrs">
<span class="attr-name" :style="{ color: a[2] }">{{
a[1]
}}</span>
<span class="attr-value" :style="{ color: a[2] }">{{
format(a[0])
}}</span>
</div>
</div> </div>
</Box> <div class="enemy-attr" v-for="(a, i) of detail">
</Transition> <span class="attr-name" :style="{ color: a[2] }">{{
a[1]
}}</span>
<span class="attr-value" :style="{ color: a[2] }">{{
format(a[0])
}}</span>
</div>
</div>
</Box>
</div> </div>
</template> </template>
<script lang="ts" setup> <script lang="ts" setup>
import { ComputedRef, computed, onMounted, onUpdated, ref, watch } from 'vue'; import { onMounted, onUpdated, ref, watch } from 'vue';
import Box from '../components/box.vue'; import Box from '../components/box.vue';
import { showFixed } from '../plugin/ui/fixed'; import { GameUi } from '@/core/main/custom/ui';
import { ToShowEnemy, detailInfo } from '../plugin/ui/book'; import type { DamageEnemy } from '@/plugin/game/enemy/damage';
import { nextFrame } from '@/plugin/utils';
watch(showFixed, n => { const props = defineProps<{
if (n) calHeight(); num: number;
ui: GameUi;
enemy: DamageEnemy;
close: Ref<boolean>;
loc: [x: number, y: number];
}>();
const emits = defineEmits<{
(e: 'close'): void;
}>();
watch(props.close, n => {
if (n) {
fixed.style.opacity = '0';
}
}); });
let main: HTMLDivElement; let main: HTMLDivElement;
let fixed: HTMLDivElement;
const format = core.formatBigNumber; const format = core.formatBigNumber;
const enemy = ref(detailInfo.enemy!); const enemy = props.enemy;
const detail = ((): [number, string, string][] => {
const info = enemy.info;
const data = enemy.calCritical()[0];
const ratio = core.status.thisMap?.ratio ?? 1;
return [
[info.hp, '生命', 'lightgreen'],
[info.atk, '攻击', 'lightcoral'],
[info.def, '防御', 'lightblue'],
[enemy.enemy.money, '金币', 'lightyellow'],
[enemy.enemy.exp, '经验', 'lawgreen'],
[data?.atkDelta ?? 0, '临界', 'lightsalmon'],
[data?.delta ?? 0, '临界减伤', 'lightpink'],
[enemy.calDefDamage(ratio).delta, `${ratio}`, 'cyan']
];
})();
const special = (() => {
const s = enemy.info.special;
const toShowAttrs: ComputedRef<[number | string, string, string][]> = computed( const fromFunc = (
() => [ func: string | ((enemy: Enemy) => string),
[enemy.value.enemy.info.hp, '生命', 'lightgreen'], enemy: Enemy
[enemy.value.enemy.info.atk, '攻击', 'lightcoral'], ) => {
[enemy.value.enemy.info.def, '防御', 'lightblue'], return typeof func === 'string' ? func : func(enemy);
[enemy.value.enemy.enemy.money, '金币', 'lightyellow'], };
[enemy.value.enemy.enemy.exp, '经验', 'lawgreen'],
[enemy.value.critical, '临界', 'lightsalmon'], const show = s.slice(0, 2).map(v => {
[enemy.value.criticalDam, '临界减伤', 'lightpink'], const s = core.plugin.special[v];
[enemy.value.defDam, `${core.status.thisMap?.ratio ?? 1}`, 'cyan'] return [fromFunc(s.name, enemy.enemy), s.color];
] });
); if (s.length > 2) show.push(['...', 'white']);
return show;
})();
const left = ref(0); const left = ref(0);
const top = ref(0); const top = ref(0);
@ -67,19 +98,15 @@ let vh = window.innerHeight;
let vw = window.innerWidth; let vw = window.innerWidth;
async function calHeight() { async function calHeight() {
enemy.value = detailInfo.enemy!;
vh = window.innerHeight; vh = window.innerHeight;
vw = window.innerWidth; vw = window.innerWidth;
width.value = vh * 0.28; width.value = vh * 0.28;
await new Promise(res => requestAnimationFrame(res)); await new Promise(res => requestAnimationFrame(res));
if (mota.ui.main.hasName('fixedDetail')) {
showFixed.value = false;
}
updateMain(); updateMain();
if (!main) return; if (!main) return;
const style = getComputedStyle(main); const style = getComputedStyle(main);
const h = parseFloat(style.height); const h = parseFloat(style.height);
const [cx, cy] = flags.clientLoc; const [cx, cy] = props.loc;
if (cy + h + 10 > vh - 10) top.value = vh - h - 10; if (cy + h + 10 > vh - 10) top.value = vh - h - 10;
else top.value = cy + 10; else top.value = cy + 10;
if (cx + width.value + 10 > vw - 10) left.value = vw - width.value - 10; if (cx + width.value + 10 > vw - 10) left.value = vw - width.value - 10;
@ -89,11 +116,13 @@ async function calHeight() {
function updateMain() { function updateMain() {
main = document.getElementById('enemy-fixed') as HTMLDivElement; main = document.getElementById('enemy-fixed') as HTMLDivElement;
fixed = document.getElementById('fixed') as HTMLDivElement;
if (main) { if (main) {
main.addEventListener('mouseleave', () => { main.addEventListener('mouseleave', () => emits('close'));
showFixed.value = false;
});
} }
nextFrame(() => {
fixed.style.opacity = '1';
});
} }
onUpdated(calHeight); onUpdated(calHeight);
@ -108,16 +137,8 @@ onMounted(() => {
#fixed { #fixed {
font-family: 'normal'; font-family: 'normal';
font-size: 2.5vh; font-size: 2.5vh;
}
.v-enter-active,
.v-leave-active {
transition: opacity 0.2s linear;
}
.v-enter-from,
.v-leave-to {
opacity: 0; opacity: 0;
transition: opacity 0.2s linear;
} }
#enemy-fixed { #enemy-fixed {