refactor: 怪物标记

This commit is contained in:
unanmed 2023-11-14 18:21:38 +08:00
parent d178908649
commit 8a8dee3176
13 changed files with 1710 additions and 280 deletions

View File

@ -404,9 +404,6 @@ main.prototype.loadAsync = async function (mode, callback) {
}); });
await core.init(coreData, callback); await core.init(coreData, callback);
if (main.mode === 'play') main.loading.emit('coreInit'); if (main.mode === 'play') main.loading.emit('coreInit');
if (main.mode === 'play') {
mota.plugin.mark.showMarkedEnemy.value = true;
}
core.resize(); core.resize();

View File

@ -420,21 +420,9 @@ var functions_d6ad677b_427a_4623_b50f_a445a3b0ef8a = {
} }
break; break;
case 77: // M快速标记 case 77: // M快速标记
// todo: refactor
const blocks = core.getMapBlocksObj(); const blocks = core.getMapBlocksObj();
const block = blocks[`${mx},${my}`]; const block = blocks[`${mx},${my}`];
if (block.event.cls.startsWith('enemy')) {
const name = core.material.enemys[block.event.id].name;
if (mota.plugin.mark.hasMarkedEnemy(block.event.id)) {
mota.plugin.utils.tip(
'success',
`已取消标记${name}`
);
mota.plugin.mark.unmarkEnemy(block.event.id);
} else {
mota.plugin.utils.tip('success', `已标记${name}`);
mota.plugin.mark.checkMarkedEnemy(block.event.id);
}
}
break; break;
case 78: // N重新开始 case 78: // N重新开始
core.confirmRestart(); core.confirmRestart();

View File

@ -17,7 +17,6 @@ interface AncTePlugin {
animate: ReturnType<typeof import('../plugin/animateController').default>; animate: ReturnType<typeof import('../plugin/animateController').default>;
utils: ReturnType<typeof import('../plugin/utils').default>; utils: ReturnType<typeof import('../plugin/utils').default>;
status: ReturnType<typeof import('../plugin/ui/statusBar').default>; status: ReturnType<typeof import('../plugin/ui/statusBar').default>;
mark: ReturnType<typeof import('../plugin/mark').default>;
fly: ReturnType<typeof import('../plugin/ui/fly').default>; fly: ReturnType<typeof import('../plugin/ui/fly').default>;
chase: ReturnType<typeof import('../plugin/chase/chase').default>; chase: ReturnType<typeof import('../plugin/chase/chase').default>;
webglUtils: ReturnType<typeof import('../plugin/webgl/utils').default>; webglUtils: ReturnType<typeof import('../plugin/webgl/utils').default>;

View File

@ -267,21 +267,9 @@ hotkey
.register('mark', '标记怪物', { .register('mark', '标记怪物', {
defaults: KeyCode.KeyM, defaults: KeyCode.KeyM,
func: () => { func: () => {
// todo: refactor
const [x, y] = flags.mouseLoc ?? []; const [x, y] = flags.mouseLoc ?? [];
const [mx, my] = getLocFromMouseLoc(x, y); const [mx, my] = getLocFromMouseLoc(x, y);
const blocks = core.getMapBlocksObj();
const block = blocks[`${mx},${my}`];
if (block.event.cls.startsWith('enemy')) {
const id = block.event.id as EnemyIds;
const name = core.material.enemys[id].name;
if (mota.plugin.mark.hasMarkedEnemy(id)) {
tip('success', `已取消标记${name}`);
mota.plugin.mark.unmarkEnemy(id);
} else {
tip('success', `已标记${name}`);
mota.plugin.mark.checkMarkedEnemy(false);
}
}
} }
}) })
.register('skillTree', '技能树', { .register('skillTree', '技能树', {

View File

@ -6,6 +6,8 @@ export interface GameEvent extends EmitableEvent {
reset: () => void; reset: () => void;
/** Emitted in src/App.vue setup. */ /** Emitted in src/App.vue setup. */
mounted: () => void; mounted: () => void;
/** Emitted in plugin/ui.js */
statusBarUpdate: () => void;
} }
export const hook = new EventEmitter<GameEvent>(); export const hook = new EventEmitter<GameEvent>();
@ -50,7 +52,7 @@ class GameListener extends EventEmitter<ListenerEvent> {
]; ];
}; };
// hover & leave // hover & leave & mouseMove
data.addEventListener('mousemove', e => { data.addEventListener('mousemove', e => {
if (core.status.lockControl || !core.isPlaying()) return; if (core.status.lockControl || !core.isPlaying()) return;
this.emit('mouseMove', e); this.emit('mouseMove', e);

View File

@ -4,7 +4,6 @@ import use from '@/plugin/use';
import animate from '@/plugin/animateController'; import animate from '@/plugin/animateController';
import utils from '@/plugin/utils'; import utils from '@/plugin/utils';
import status from '@/plugin/ui/statusBar'; import status from '@/plugin/ui/statusBar';
import mark from '@/plugin/mark';
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 webglUtils from '@/plugin/webgl/utils'; import webglUtils from '@/plugin/webgl/utils';
@ -27,7 +26,6 @@ export function resolvePlugin() {
['animate', animate()], ['animate', animate()],
['utils', utils()], ['utils', utils()],
['status', status()], ['status', status()],
['mark', mark()],
['fly', fly()], ['fly', fly()],
['chase', chase()], ['chase', chase()],
['webglUtils', webglUtils()], ['webglUtils', webglUtils()],

View File

@ -29,15 +29,14 @@
<script lang="ts" setup> <script lang="ts" setup>
import { ref } from 'vue'; import { ref } from 'vue';
import Scroll from '../components/scroll.vue'; import Scroll from '../components/scroll.vue';
import { hasMarkedEnemy, markEnemy, unmarkEnemy } from '../plugin/mark';
import { detailInfo } from '../plugin/ui/book'; import { detailInfo } from '../plugin/ui/book';
import { hasMarkedEnemy, markEnemy } from '@/plugin/mark';
const enemy = detailInfo.enemy!; const enemy = detailInfo.enemy!;
const marked = ref(hasMarkedEnemy(enemy.enemy.id)); const marked = ref(hasMarkedEnemy(enemy.enemy.id));
function mark() { function mark() {
if (marked.value) unmarkEnemy(enemy.enemy.id); markEnemy(enemy.enemy.id);
if (!marked.value) markEnemy(enemy.enemy.id);
marked.value = hasMarkedEnemy(enemy.enemy.id); marked.value = hasMarkedEnemy(enemy.enemy.id);
} }
</script> </script>

View File

@ -20,7 +20,6 @@ export {};
if (main.replayChecking) return; if (main.replayChecking) return;
mota.plugin.status.statusBarStatus.value = mota.plugin.status.statusBarStatus.value =
!mota.plugin.status.statusBarStatus.value; !mota.plugin.status.statusBarStatus.value;
mota.plugin.mark.checkMarkedEnemy();
} }
ui.prototype.drawBook = function () { ui.prototype.drawBook = function () {
@ -49,6 +48,7 @@ export {};
core.control.noAutoEvents = true; core.control.noAutoEvents = true;
// 更新vue状态栏 // 更新vue状态栏
updateVueStatusBar(); updateVueStatusBar();
mota.game.hook.emit('statusBarUpdate');
}; };
// todo: 多个状态栏分离与控制 // todo: 多个状态栏分离与控制

View File

@ -1,156 +1,159 @@
import { reactive, ref } from 'vue'; import { fixedUi } from '@/core/main/init/ui';
import { tip } from './utils';
import type { DamageEnemy } from './game/enemy/damage'; import type { DamageEnemy } from './game/enemy/damage';
import { tip } from './utils';
import { ref, Ref } from 'vue';
import { hook } from '@/core/main/game';
export const showMarkedEnemy = ref(false); export interface MarkInfo<T extends EnemyIds> {
id: T;
const markedEnemy = reactive<EnemyIds[]>([]); enemy: DamageEnemy<T>;
/**
interface MarkInfo { *
enemy: DamageEnemy; * 1.
nextCritical: number; * 2.
* 3. 2/3
* 4. 1/3
* 5.
* 6.
*/
mode: number;
/** 当前提示状态,提示模式的 2-6 */
status: number;
lastAtk: number;
lastDamage: number;
markDamage?: number;
/** 数据更新用,取反更新标记信息 */
update: Ref<boolean>;
} }
export const markInfo: Partial<Record<EnemyIds, MarkInfo>> = {}; const uiMap = new Map<EnemyIds, number>();
const criticalReached: Partial<Record<EnemyIds, Record<number, boolean>>> = {}; const marked: MarkInfo<EnemyIds>[] = [];
const enemyDamageInfo: Partial<Record<EnemyIds, Record<number, boolean>>> = {};
/** /**
* 2/31/3 * 2/31/3
* @param id id * @param id id
*/ */
export function markEnemy(id: EnemyIds) { export function markEnemy(id: EnemyIds) {
const { Enemy } = core.plugin.damage;
if (hasMarkedEnemy(id)) return; if (hasMarkedEnemy(id)) return;
markedEnemy.push(id); const { Enemy } = core.plugin.damage;
const enemy = new Enemy(core.material.enemys[id]); const enemy = new Enemy(core.material.enemys[id]);
enemy.calAttribute(); enemy.calAttribute();
enemy.getRealInfo(); enemy.getRealInfo();
markInfo[id] = {
nextCritical: const info: MarkInfo<EnemyIds> = {
enemy.calCritical(1)[0]?.atkDelta ?? 0 + core.status.hero.atk, id,
enemy enemy,
mode: 0b011111,
lastAtk: core.plugin.hero.getHeroStatusOn('atk', 'empty'),
lastDamage: enemy.calDamage().damage,
status: 0b0,
update: ref(true)
}; };
criticalReached[id] = { 0: true }; marked.push(info);
enemyDamageInfo[id] = { 1: false, 2: false, 3: false };
getMarkInfo(id, true); uiMap.set(id, fixedUi.open('markedEnemy', { enemy: info }));
checkMarkedEnemy(true);
} }
/**
*
*/
export function hasMarkedEnemy(id: EnemyIds) {
return markedEnemy.includes(id);
}
/**
*
*/
export function unmarkEnemy(id: EnemyIds) { export function unmarkEnemy(id: EnemyIds) {
const index = markedEnemy.indexOf(id); fixedUi.close(uiMap.get(id) ?? -1);
if (index === -1) return; uiMap.delete(id);
markedEnemy.splice(index, 1); const index = marked.findIndex(v => v.id === id);
checkMarkedEnemy(); marked.splice(index, 1);
} }
export function unmarkAll() { export function checkMarkedEnemy() {
markedEnemy.splice(0); marked.forEach(v => {
checkMarkedEnemy(); const { id, enemy, mode, lastAtk, lastDamage, markDamage } = v;
} const atk = core.plugin.hero.getHeroStatusOn('atk', 'empty');
let tip = 0;
/** if (mode & 0b11110) {
* const damage = enemy.calDamage().damage;
*/ const hp = core.status.hero.hp;
export function getMarkedEnemy() { v.lastDamage = damage;
return markedEnemy; if (damage > lastDamage) return;
} // 重置标记状态
if (damage > hp) {
/** v.status &= 0b100001;
*
* @param id id
*/
export function getMarkInfo(id: EnemyIds, noMessage: boolean = false) {
const reached = criticalReached[id]!;
const info = markInfo[id]!;
if (core.status.hero.atk >= info.nextCritical) {
if (!reached[info.nextCritical] && !noMessage) {
tip('success', `踩到了${core.material.enemys[id].name}的临界!`);
}
reached[info.nextCritical] = true;
const n = info.enemy.calCritical(1)[0]?.atkDelta;
const next = (n ?? 0) + core.status.hero.atk;
info.nextCritical = next;
}
}
/**
*
*/
export function checkMarkedEnemy(noMessage: boolean = false) {
checkMarkedStatus.value = !checkMarkedStatus.value;
const hp = core.status.hero.hp;
getMarkedEnemy().forEach(v => {
getMarkInfo(v);
const { enemy } = markInfo[v]!;
const damage = enemy.calDamage().damage;
if (!isFinite(damage)) return;
const info = enemyDamageInfo[v]!;
const name = core.material.enemys[v].name;
let res = 0;
if (damage <= 0) {
if (!noMessage) tip('success', `${name}已经零伤了!`);
} else if (damage < hp / 3) {
if (!info[3] && !noMessage) {
tip('success', `${name}的伤害已降至勇士生命值的1/3`);
} }
res = 0b111; if (damage > (markDamage ?? Infinity)) {
} else if (damage < (hp / 3) * 2) { v.status &= 0b1;
if (!info[2] && !noMessage) {
tip('success', `${name}的伤害已降至勇士生命值的2/3`);
} }
res = 0b110; // 能打过怪物提示、2/3提示、1/3提示、零伤提示、指定伤害提示
} else if (damage < hp) { if (mode & (1 << 1) && damage < hp && damage > (hp * 2) / 3) {
if (!info[1] && !noMessage) { if (!(v.status & (1 << 1))) {
tip('success', `你已经能打过${name}了!`); v.status &= 0b100001;
v.status |= 1 << 1;
tip |= 1 << 1;
}
} else if (mode & (1 << 2) && damage > hp / 3) {
if (!(v.status & (1 << 2))) {
v.status &= 0b100011;
v.status |= 1 << 2;
tip |= 1 << 2;
}
} else if (mode & (1 << 3) && damage > 0) {
if (!(v.status & (1 << 3))) {
v.status &= 0b100111;
v.status |= 1 << 3;
tip |= 1 << 3;
}
} else if (mode & (1 << 4)) {
if (!(v.status & (1 << 4))) {
v.status &= 0b101111;
v.status |= 1 << 4;
tip |= 1 << 4;
}
}
if (mode & (1 << 5) && damage < (markDamage ?? Infinity)) {
if (!(v.status & (1 << 5))) {
if (damage < (markDamage ?? Infinity)) {
v.status |= 1 << 5;
} else {
v.status &= 0b011111;
}
}
} }
res = 0b100;
} }
info[1] = info[2] = info[3] = false; // 临界提示
if (res & 0b100) { if (mode & (1 << 0)) {
info[1] = true; const critical = enemy.calCritical(1)[0]?.atkDelta ?? Infinity;
} v.lastAtk = atk + critical;
if (res & 0b010) { if (critical + atk > lastAtk) {
info[2] = true; tip |= 1 << 0;
} }
if (res & 0b001) {
info[3] = true;
} }
makeTip(id, tip, v);
v.update.value = !v.update.value;
}); });
} }
export const checkMarkedStatus = ref(false); function makeTip(enemy: EnemyIds, mode: number, info: MarkInfo<EnemyIds>) {
const name = core.material.enemys[enemy].name;
export default function init() { if (mode & (1 << 0)) {
// 鼠标移动时进行监听按下M时进行快速标记 tip('success', `已踩到 ${name} 的临界!`);
core.registerAction( }
'onmove', if (mode & (1 << 1)) {
'mark', tip('success', `已能打过 ${name}`);
(x, y) => { }
if (core.isPlaying()) { if (mode & (1 << 2)) {
flags.mouseLoc = [x, y]; tip('success', `${name} 的伤害已降至 2/3`);
} }
return false; if (mode & (1 << 3)) {
}, tip('success', `${name} 的伤害已降至 1/3`);
150 }
); if (mode & (1 << 4)) {
return { tip('success', `${name} 已零伤!`);
checkMarkedEnemy, }
checkStatus: checkMarkedStatus, if (mode & (1 << 5)) {
markEnemy, const damage = core.formatBigNumber(info.markDamage ?? Infinity);
hasMarkedEnemy, tip('success', `${name} 的伤害已降至 ${damage}`);
unmarkEnemy, }
showMarkedEnemy,
unmarkAll
};
} }
export function hasMarkedEnemy(id: EnemyIds) {
return marked.some(v => v.id === id);
}
hook.on('statusBarUpdate', () => {
checkMarkedEnemy();
});

View File

@ -8,6 +8,7 @@
z-index: 1000; z-index: 1000;
overflow: visible; overflow: visible;
font-size: 16px; font-size: 16px;
user-select: none;
} }
.antdv-message { .antdv-message {

24
src/types/plugin.d.ts vendored
View File

@ -48,9 +48,6 @@ interface PluginDeclaration
/** 状态栏信息,取反后刷新状态栏 */ /** 状态栏信息,取反后刷新状态栏 */
readonly statusBarStatus: Ref<boolean>; readonly statusBarStatus: Ref<boolean>;
/** 检查标记的怪物,取反后更新显示信息 */
readonly checkMarkedStatus: Ref<boolean>;
/** /**
* *
* @param fn * @param fn
@ -63,27 +60,6 @@ interface PluginDeclaration
*/ */
removeAnimate(fn: (time: number) => void); removeAnimate(fn: (time: number) => void);
/**
*
*/
checkMarkedEnemy(): void;
/**
*
* @param id id
*/
markEnemy(id: EnemyIds): void;
/**
*
*/
hasMarkedEnemy(id: EnemyIds): void;
/**
*
*/
unmarkEnemy(id: EnemyIds): void;
/** /**
* undefined或null * undefined或null
* @param value * @param value

View File

@ -1,122 +1,118 @@
<template> <template>
<div id="marked-enemy"> <div id="marked-enemy">
<div v-for="v of all"> <Box
<Box v-model:left="boxPos.left"
:key="v" v-model:top="boxPos.top"
v-if="!getBoxPos(v).hidden" v-model:width="boxPos.width"
v-model:left="getBoxPos(v).left" v-model:height="boxPos.height"
v-model:top="getBoxPos(v).top" :resizable="true"
v-model:width="getBoxPos(v).width" :dragable="true"
v-model:height="getBoxPos(v).height" >
:resizable="true" <Scroll class="box-scroll" :no-scroll="true">
:dragable="true" <div class="marked-main">
<div class="marked-info">
<BoxAnimate
:id="enemy.id"
:width="24"
:height="24"
></BoxAnimate>
<span class="marked-name marked-item">{{
getName()
}}</span>
</div>
<span class="marked-damage marked-item"
>伤害{{ format(info.damage) }}</span
>
<span class="marked-critical marked-item"
>临界{{ format(info.critical) }}</span
>
<span class="marked-critical-damage marked-item"
>减伤{{ format(info.criticalDam) }}</span
>
<span class="marked-def marked-item"
>{{ ratio }}{{ format(info.defDamage) }}</span
>
<div class="marked-button">
<span
class="marked-hide button-text"
@click.stop="hidden = true"
>隐藏盒子</span
>
<span
class="marked-cancel button-text"
@click.stop="unmarkEnemy(enemy.id)"
>取消标记</span
>
</div>
</div></Scroll
> >
<Scroll class="box-scroll" :no-scroll="true"> </Box>
<div class="marked-main">
<div class="marked-info">
<BoxAnimate
:id="v"
:width="24"
:height="24"
></BoxAnimate>
<span class="marked-name marked-item">{{
getName(v)
}}</span>
</div>
<span class="marked-damage marked-item"
>伤害{{ getDamage(v) }}</span
>
<span class="marked-critical marked-item"
>临界{{ getCritical(v)[0] }}</span
>
<span class="marked-critical-damage marked-item"
>减伤{{ getCritical(v)[1] }}</span
>
<span class="marked-def marked-item"
>{{ ratio }}{{ getDefDamage(v) }}</span
>
<div class="marked-button">
<span
class="marked-hide button-text"
@click.stop="getBoxPos(v).hidden = true"
>隐藏盒子</span
>
<span
class="marked-cancel button-text"
@click.stop="unmarkEnemy(v)"
>取消标记</span
>
</div>
</div></Scroll
>
</Box>
</div>
</div> </div>
</template> </template>
<script lang="ts" setup> <script lang="ts" setup>
import { reactive, ref, watch } from 'vue'; import { reactive, ref, watch } from 'vue';
import { import { MarkInfo, unmarkEnemy } from '../plugin/mark';
checkMarkedStatus,
getMarkedEnemy,
markInfo,
unmarkEnemy
} from '../plugin/mark';
import { has } from '../plugin/utils';
import Box from '../components/box.vue'; import Box from '../components/box.vue';
import Scroll from '../components/scroll.vue'; import Scroll from '../components/scroll.vue';
import BoxAnimate from '../components/boxAnimate.vue'; import BoxAnimate from '../components/boxAnimate.vue';
import { GameUi } from '@/core/main/custom/ui';
const props = defineProps<{
num: number;
ui: GameUi;
enemy: MarkInfo<EnemyIds>;
}>();
interface BoxPos { interface BoxPos {
left: number; left: number;
top: number; top: number;
width: number; width: number;
height: number; height: number;
hidden: boolean;
} }
interface MarkedEnemy {
damage: number;
critical: number;
criticalDam: number;
defDamage: number;
}
const enemy = props.enemy;
const ratio = core.status.thisMap?.ratio ?? 1; const ratio = core.status.thisMap?.ratio ?? 1;
let all = getMarkedEnemy(); const format = core.formatBigNumber;
watch(checkMarkedStatus, update); const boxPos = reactive<BoxPos>({
left: window.innerWidth - 300,
top: 100,
width: 200,
height: 150
});
const info = reactive<MarkedEnemy>({
damage: 0,
critical: 0,
criticalDam: 0,
defDamage: 0
});
const boxPos = reactive<Partial<Record<EnemyIds, BoxPos>>>({}); const hidden = ref(false);
watch(hidden, n => {
if (n) mota.ui.fixed.close(props.num);
});
watch(enemy.update, update);
function update() { function update() {
all.push(...all.splice(0, all.length)); info.damage = enemy.enemy.calDamage().damage;
for (const id in boxPos) { const critical = enemy.enemy.calCritical()[0];
if (!all.includes(id as EnemyIds)) delete boxPos[id as EnemyIds]; info.critical = critical?.atkDelta ?? 0;
} info.criticalDam = critical.delta ?? 0;
info.defDamage = enemy.enemy.calDefDamage(ratio).delta;
} }
function getBoxPos(id: EnemyIds) { function getName() {
if (has(boxPos[id])) return boxPos[id]!; return enemy.enemy.enemy.name;
boxPos[id] = {
left: window.innerWidth - 300,
top: 100,
width: 200,
height: 150,
hidden: false
};
return boxPos[id]!;
}
function getName(id: EnemyIds) {
return core.material.enemys[id].name;
}
function getDamage(id: EnemyIds) {
return core.formatBigNumber(markInfo[id]!.enemy.calDamage().damage);
}
function getCritical(id: EnemyIds) {
const { delta, atkDelta } = markInfo[id]!.enemy.calCritical(1)[0];
return [-delta, atkDelta];
}
function getDefDamage(id: EnemyIds) {
return core.formatBigNumber(markInfo[id]!.enemy.calDefDamage(ratio).delta);
} }
</script> </script>

1483
回合制.js Normal file

File diff suppressed because it is too large Load Diff