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 () {
core.status.lockControl = true;
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;
case 67: // C怪物临界
if (core.getBlockCls(mx, my)?.startsWith('enemy')) {
mota.plugin.fixed.showFixed.value = false;
mota.ui.main.open('fixedDetail', void 0, {
// mota.plugin.fixed.showFixed.value = false;
mota.ui.main.open('fixedDetail', {
panel: 'critical'
});
}
break;
case 69: // E怪物属性
if (core.getBlockCls(mx, my)?.startsWith('enemy')) {
mota.plugin.fixed.showFixed.value = false;
mota.ui.main.open('fixedDetail', void 0, {
// mota.plugin.fixed.showFixed.value = false;
mota.ui.main.open('fixedDetail', {
panel: 'special'
});
}

View File

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

View File

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

View File

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

View File

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

View File

@ -74,6 +74,10 @@ export class EnemyCollection implements RangeCollection<DamageEnemy> {
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) {
if (!core.isReplaying()) {
mota.ui.main.open('shop', void 0, {
mota.ui.main.open('shop', {
shopId: itemShopId
});
}

View File

@ -5,46 +5,46 @@ import { ToShowEnemy, detailInfo } from './book';
import { DamageEnemy } from '../game/enemy/damage';
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) => {
if (!window.flags) return;
if (!flags.mouseLoc) return;
flags.clientLoc = [ev.clientX, ev.clientY];
const [mx, my] = getLocFromMouseLoc(...flags.mouseLoc);
// const show = debounce((ev: MouseEvent) => {
// if (!window.flags) return;
// if (!flags.mouseLoc) return;
// flags.clientLoc = [ev.clientX, ev.clientY];
// const [mx, my] = getLocFromMouseLoc(...flags.mouseLoc);
const e = core.status.thisMap.enemy.list.find(v => {
return v.x === mx && v.y === my;
});
// const e = core.status.thisMap.enemy.list.find(v => {
// return v.x === mx && v.y === my;
// });
if (!e) return;
// if (!e) return;
lastId = e.id;
const detail = getDetailedEnemy(e);
detailInfo.enemy = detail;
showFixed.value = true;
}, 200);
// lastId = e.id;
// const detail = getDetailedEnemy(e);
// detailInfo.enemy = detail;
// showFixed.value = true;
// }, 200);
export default function init() {
const data = core.canvas.data.canvas;
data.addEventListener('mousemove', ev => {
if (!core.isPlaying() || core.status.lockControl) return;
const [mx, my] = getLocFromMouseLoc(...flags.mouseLoc);
const e = core.getBlockId(mx, my);
if (e !== lastId) showFixed.value = false;
if (!e) return;
show(ev);
});
data.addEventListener('mousedown', ev => {
showFixed.value = false;
});
// export default function init() {
// const data = core.canvas.data.canvas;
// data.addEventListener('mousemove', ev => {
// if (!core.isPlaying() || core.status.lockControl) return;
// const [mx, my] = getLocFromMouseLoc(...flags.mouseLoc);
// const e = core.getBlockId(mx, my);
// if (e !== lastId) showFixed.value = false;
// if (!e) return;
// show(ev);
// });
// data.addEventListener('mousedown', ev => {
// showFixed.value = false;
// });
return {
showFixed
};
}
// return {
// showFixed
// };
// }
export function getLocFromMouseLoc(x: number, y: number): LocArr {
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;
_clickGameInfo_openComments();
_getClickLoc(
x: number,
y: number
): {
x: number;
y: number;
size: number;
};
}
declare const actions: new () => Actions;

View File

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

View File

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