重构怪物手册的一部分

This commit is contained in:
unanmed 2023-07-31 16:49:04 +08:00
parent dae43498ca
commit 112ce4c4ef
15 changed files with 214 additions and 269 deletions

View File

@ -427,24 +427,7 @@ enemys.prototype._getDamage = function (enemy, hero, x, y, floorId) {
////// 获得当前楼层的怪物列表 ////// ////// 获得当前楼层的怪物列表 //////
enemys.prototype.getCurrentEnemys = function (floorId) { enemys.prototype.getCurrentEnemys = function (floorId) {
// todo: 重写这个函数 // Deprecated. See src/plugin/game/enemy/battle.ts
floorId = floorId || core.status.floorId;
var enemys = [],
used = {};
core.extractBlocks(floorId);
core.status.maps[floorId].blocks.forEach(function (block) {
if (!block.disable && block.event.cls.indexOf('enemy') == 0) {
this._getCurrentEnemys_addEnemy(
block.event.id,
enemys,
used,
block.x,
block.y,
floorId
);
}
}, this);
return this._getCurrentEnemys_sort(enemys);
}; };
enemys.prototype._getCurrentEnemys_getEnemy = function (enemyId) { enemys.prototype._getCurrentEnemys_getEnemy = function (enemyId) {

View File

@ -3531,64 +3531,7 @@ maps.prototype.resetMap = function (floorId) {
////// 初始化独立的block canvas ////// ////// 初始化独立的block canvas //////
maps.prototype._initDetachedBlock = function (blockInfo, x, y, displayDamage) { maps.prototype._initDetachedBlock = function (blockInfo, x, y, displayDamage) {
// todo: 不使用 nextCriticals 和 getDamageString // Deprecated. See src/plugin/game/fx/rewrite.ts
var headCanvas = null,
bodyCanvas = '__body_' + x + '_' + y,
damageCanvas = null;
// head
if (!blockInfo.bigImage && blockInfo.height > 32) {
headCanvas = '__head_' + x + '_' + y;
core.createCanvas(headCanvas, 0, 0, 32, blockInfo.height - 32, 55);
}
// body
if (blockInfo.bigImage) {
var bigImageInfo = this._getBigImageInfo(
blockInfo.bigImage,
blockInfo.face,
blockInfo.posX
);
core.createCanvas(
bodyCanvas,
0,
0,
bigImageInfo.per_width,
bigImageInfo.per_height,
35
);
} else {
core.createCanvas(bodyCanvas, 0, 0, 32, 32, 35);
}
// damage
var damage = null,
damageColor = null;
if (
blockInfo.cls.indexOf('enemy') == 0 &&
core.hasItem('book') &&
displayDamage
) {
var damageString = core.enemys.getDamageString(blockInfo.id, x, y);
damage = damageString.damage;
damageColor = damageString.color;
}
if (damage != null) {
damageCanvas = '__damage_' + x + '_' + y;
var ctx = core.createCanvas(damageCanvas, 0, 0, 32, 32, 65);
ctx.textAlign = 'left';
ctx.font = 'bold 11px Arial';
core.fillBoldText(ctx, damage, 1, 31, damageColor);
if (core.flags.displayCritical) {
var critical = core.enemys.nextCriticals(blockInfo.id);
if (critical.length > 0) critical = critical[0];
critical = core.formatBigNumber(critical[0], true);
if (critical == '???') critical = '?';
core.fillBoldText(ctx, critical, 1, 21, '#FFFFFF');
}
}
return {
headCanvas: headCanvas,
bodyCanvas: bodyCanvas,
damageCanvas: damageCanvas
};
}; };
////// 移动独立的block canvas ////// ////// 移动独立的block canvas //////

View File

@ -18,6 +18,7 @@ import { uiStack } from './plugin/uiController';
display: flex; display: flex;
justify-content: center; justify-content: center;
overflow: hidden; overflow: hidden;
font-size: 16px;
} }
@media screen and (max-width: 600px) { @media screen and (max-width: 600px) {

View File

@ -7,9 +7,9 @@
> >
<div class="info"> <div class="info">
<div class="leftbar"> <div class="leftbar">
<span class="name">{{ enemy.name }}</span> <span class="name">{{ enemy.enemy.enemy.name }}</span>
<BoxAnimate <BoxAnimate
:id="enemy.id" :id="enemy.enemy.id"
:width="isMobile ? 32 : w" :width="isMobile ? 32 : w"
:height="isMobile ? 32 : w" :height="isMobile ? 32 : w"
style="margin: 5%" style="margin: 5%"
@ -19,9 +19,9 @@
v-if="has(enemy.special) && enemy.special.length > 0" v-if="has(enemy.special) && enemy.special.length > 0"
> >
<span <span
v-for="(text, i) in enemy.toShowSpecial" v-for="(text, i) in enemy.showSpecial"
:style="{ color: enemy.toShowColor![i] as string }" :style="{ color: text[2] }"
>&nbsp;{{ text }}&nbsp;</span >&nbsp;{{ text[0] }}&nbsp;</span
> >
</div> </div>
<div class="special-text" v-else>无属性</div> <div class="special-text" v-else>无属性</div>
@ -36,42 +36,42 @@
<div class="detail-info"> <div class="detail-info">
<span style="color: lightgreen" <span style="color: lightgreen"
>生命&nbsp;&nbsp;&nbsp;&nbsp;{{ >生命&nbsp;&nbsp;&nbsp;&nbsp;{{
core.formatBigNumber(enemy.hp) core.formatBigNumber(enemy.enemy.info.hp)
}}</span }}</span
> >
</div> </div>
<div class="detail-info"> <div class="detail-info">
<span style="color: lightcoral" <span style="color: lightcoral"
>攻击&nbsp;&nbsp;&nbsp;&nbsp;{{ >攻击&nbsp;&nbsp;&nbsp;&nbsp;{{
core.formatBigNumber(enemy.atk) core.formatBigNumber(enemy.enemy.info.atk)
}}</span }}</span
> >
</div> </div>
<div class="detail-info"> <div class="detail-info">
<span style="color: lightblue" <span style="color: lightblue"
>防御&nbsp;&nbsp;&nbsp;&nbsp;{{ >防御&nbsp;&nbsp;&nbsp;&nbsp;{{
core.formatBigNumber(enemy.def) core.formatBigNumber(enemy.enemy.info.def)
}}</span }}</span
> >
</div> </div>
<div class="detail-info"> <div class="detail-info">
<span style="color: lightyellow" <span style="color: lightyellow"
>金币&nbsp;&nbsp;&nbsp;&nbsp;{{ >金币&nbsp;&nbsp;&nbsp;&nbsp;{{
core.formatBigNumber(enemy.money) core.formatBigNumber(enemy.enemy.enemy.money)
}}</span }}</span
> >
</div> </div>
<div class="detail-info"> <div class="detail-info">
<span style="color: lawngreen" <span style="color: lawngreen"
>经验&nbsp;&nbsp;&nbsp;&nbsp;{{ >经验&nbsp;&nbsp;&nbsp;&nbsp;{{
core.formatBigNumber(enemy.exp) core.formatBigNumber(enemy.enemy.enemy.exp)
}}</span }}</span
> >
</div> </div>
<div class="detail-info"> <div class="detail-info">
<span :style="{color: enemy.damageColor! as string}" <span :style="{ color: enemy.damageColor }"
>伤害&nbsp;&nbsp;&nbsp;&nbsp;{{ >伤害&nbsp;&nbsp;&nbsp;&nbsp;{{
core.formatBigNumber(enemy.damage!) enemy.damage
}}</span }}</span
> >
</div> </div>
@ -79,7 +79,7 @@
<div class="detail-info"> <div class="detail-info">
<span style="color: lightsalmon" <span style="color: lightsalmon"
>临界&nbsp;&nbsp;&nbsp;&nbsp;{{ >临界&nbsp;&nbsp;&nbsp;&nbsp;{{
core.formatBigNumber(enemy.critical) enemy.critical
}}</span }}</span
> >
</div> </div>
@ -87,27 +87,9 @@
<span style="color: lightpink" <span style="color: lightpink"
>减伤&nbsp;&nbsp;&nbsp;&nbsp;<span >减伤&nbsp;&nbsp;&nbsp;&nbsp;<span
:style="{ :style="{
color: color: 'lightpink'
enemy.criticalDamage < 0 &&
!has(enemy.damage)
? 'gold'
: 'lightpink'
}" }"
><span style="font-family: 'Fira Code'">{{ >{{ enemy.criticalDam }}</span
enemy.criticalDamage < 0 &&
!has(enemy.damage)
? isMobile
? '-'
: '=>'
: ''
}}</span
>{{
core.formatBigNumber(
enemy.criticalDamage < 0
? -enemy.criticalDamage
: enemy.criticalDamage
)
}}</span
></span ></span
> >
</div> </div>
@ -116,7 +98,7 @@
>{{ >{{
core.formatBigNumber(core.status.thisMap.ratio) core.formatBigNumber(core.status.thisMap.ratio)
}}&nbsp;&nbsp;&nbsp;&nbsp;{{ }}&nbsp;&nbsp;&nbsp;&nbsp;{{
core.formatBigNumber(enemy.defDamage) core.formatBigNumber(enemy.defDam)
}}</span }}</span
> >
</div> </div>
@ -130,9 +112,10 @@
import { has } from '../plugin/utils'; import { has } from '../plugin/utils';
import BoxAnimate from '../components/boxAnimate.vue'; import BoxAnimate from '../components/boxAnimate.vue';
import { isMobile } from '../plugin/use'; import { isMobile } from '../plugin/use';
import { ToShowEnemy } from '../plugin/ui/book';
const props = defineProps<{ const props = defineProps<{
enemy: DetailedEnemy; enemy: ToShowEnemy;
selected?: boolean; selected?: boolean;
}>(); }>();

View File

@ -88,14 +88,12 @@
<script lang="ts" setup> <script lang="ts" setup>
import { computed, onMounted, ref, watch } from 'vue'; import { computed, onMounted, ref, watch } from 'vue';
import { getCriticalDamage, getDefDamage } from '../plugin/ui/book'; import { detailInfo, getCriticalDamage, getDefDamage } from '../plugin/ui/book';
import Chart, { ChartConfiguration } from 'chart.js/auto'; import Chart, { ChartConfiguration } from 'chart.js/auto';
import { has, setCanvasSize } from '../plugin/utils'; import { has, setCanvasSize } from '../plugin/utils';
import { debounce } from 'lodash-es'; import { debounce } from 'lodash-es';
import { isMobile } from '../plugin/use'; import { isMobile } from '../plugin/use';
// todo: getDamageInfo
const props = defineProps<{ const props = defineProps<{
fromBook?: boolean; fromBook?: boolean;
}>(); }>();
@ -103,7 +101,7 @@ const props = defineProps<{
const critical = ref<HTMLCanvasElement>(); const critical = ref<HTMLCanvasElement>();
const def = ref<HTMLCanvasElement>(); const def = ref<HTMLCanvasElement>();
const enemy = core.plugin.bookDetailEnemy; const enemy = detailInfo.enemy!;
const ceil = Math.ceil; const ceil = Math.ceil;
const x = ref(props.fromBook ? void 0 : flags.mouseLoc[0]); const x = ref(props.fromBook ? void 0 : flags.mouseLoc[0]);
@ -115,8 +113,8 @@ y.value = has(y.value)
? Math.round(y.value + core.bigmap.offsetY / 32) ? Math.round(y.value + core.bigmap.offsetY / 32)
: void 0; : void 0;
let originCri = getCriticalDamage(enemy, 0, 0, x.value, y.value); let originCri = getCriticalDamage(enemy, 0, 0);
let originDef = getDefDamage(enemy, 0, 0, x.value, y.value); let originDef = getDefDamage(enemy, 0, 0);
// //
const allCri = ref(originCri); const allCri = ref(originCri);
@ -126,25 +124,24 @@ const allDef = ref(originDef);
const addAtk = ref(0); const addAtk = ref(0);
const addDef = ref(0); const addDef = ref(0);
const originDamage = core.getDamageInfo(enemy.id, void 0, x.value, y.value); const originDamage = enemy.enemy.calEnemyDamage(core.status.hero, 'none')[0]
.damage;
// core // core
const format = core.formatBigNumber; const format = core.formatBigNumber;
const ratio = core.status.thisMap.ratio; const ratio = core.status.thisMap.ratio;
const nowDamage = computed(() => { const nowDamage = computed(() => {
const dam = core.getDamageInfo( const dam = enemy.enemy.calEnemyDamage(
enemy.id,
{ {
atk: core.getStatus('atk') + addAtk.value * ratio, atk: core.status.hero.atk + addAtk.value * ratio,
def: core.getStatus('def') + addDef.value * ratio def: core.status.hero.def + addDef.value * ratio
}, },
x.value, 'none'
y.value )[0].damage;
); if (!isFinite(dam)) return ['???', '???'];
if (!has(dam)) return ['???', '???']; if (!isFinite(originDamage)) return [-dam, dam];
if (!has(originDamage)) return [-dam.damage, dam.damage]; return [originDamage - dam, dam];
return [originDamage.damage - dam.damage, dam.damage];
}); });
function generateChart(ele: HTMLCanvasElement, data: [number, number][]) { function generateChart(ele: HTMLCanvasElement, data: [number, number][]) {
@ -199,16 +196,12 @@ const update = debounce((atk: Chart, def: Chart) => {
allCri.value = getCriticalDamage( allCri.value = getCriticalDamage(
enemy, enemy,
addAtk.value * ratio, addAtk.value * ratio,
addDef.value * ratio, addDef.value * ratio
x.value,
y.value
); );
allDef.value = getDefDamage( allDef.value = getDefDamage(
enemy, enemy,
addDef.value * ratio, addDef.value * ratio,
addAtk.value * ratio, addAtk.value * ratio
x.value,
y.value
); );
if (allCri.value.length > originCri.length) originCri = allCri.value; if (allCri.value.length > originCri.length) originCri = allCri.value;
if (allDef.value.length > originDef.length) originDef = allDef.value; if (allDef.value.length > originDef.length) originDef = allDef.value;

View File

@ -18,14 +18,9 @@
<span>加攻</span> <span>加攻</span>
<span>减伤</span> <span>减伤</span>
</div> </div>
<div v-for="[atk, dam] of criticals" class="critical"> <div v-for="cri of criticals[0]" class="critical">
<span class="critical-atk">{{ format(atk) }}</span> <span class="critical-atk">{{ format(cri.atkDelta) }}</span>
<span <span>{{ format(cri.delta) }}</span>
><span style="font-family: 'Fira Code'">{{
dam < 0 ? '=>' : ''
}}</span
>{{ dam < 0 ? `${format(-dam)}` : format(dam) }}</span
>
</div> </div>
</div> </div>
</div> </div>
@ -34,23 +29,17 @@
<script lang="ts" setup> <script lang="ts" setup>
import { isMobile } from '../plugin/use'; import { isMobile } from '../plugin/use';
import { getSpecialHint } from '../plugin/ui/book'; import { detailInfo, getSpecialHint } from '../plugin/ui/book';
import { has } from '../plugin/utils';
const props = defineProps<{ const props = defineProps<{
fromBook?: boolean; fromBook?: boolean;
}>(); }>();
const [x, y] = props.fromBook ? [void 0, void 0] : flags.mouseLoc; const enemy = detailInfo.enemy!;
const mx = has(x) ? Math.round(x + core.bigmap.offsetX / 32) : void 0;
const my = has(y) ? Math.round(y + core.bigmap.offsetY / 32) : void 0;
const enemy = core.plugin.bookDetailEnemy;
const info = getSpecialHint(enemy); const info = getSpecialHint(enemy);
// todo: 使 nextCriticals const criticals = enemy.enemy.calCritical(isMobile ? 4 : 8, 'none');
const criticals = core.nextCriticals(enemy, isMobile ? 4 : 8, mx, my);
const format = core.formatBigNumber; const format = core.formatBigNumber;
</script> </script>

View File

@ -3,7 +3,11 @@
<div id="enemy-desc"> <div id="enemy-desc">
<span>怪物描述</span> <span>怪物描述</span>
<Scroll id="enemy-desc-scroll"> <Scroll id="enemy-desc-scroll">
<span>&nbsp;&nbsp;&nbsp;&nbsp;{{ enemy.description }}</span> <span
>&nbsp;&nbsp;&nbsp;&nbsp;{{
enemy.enemy.enemy.description
}}</span
>
</Scroll> </Scroll>
</div> </div>
<a-divider dashed style="border-color: #ddd4"></a-divider> <a-divider dashed style="border-color: #ddd4"></a-divider>
@ -26,14 +30,15 @@
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 { hasMarkedEnemy, markEnemy, unmarkEnemy } from '../plugin/mark';
import { detailInfo } from '../plugin/ui/book';
const enemy = core.plugin.bookDetailEnemy; const enemy = detailInfo.enemy!;
const marked = ref(hasMarkedEnemy(enemy.id)); const marked = ref(hasMarkedEnemy(enemy.enemy.id));
function mark() { function mark() {
if (marked.value) unmarkEnemy(enemy.id); if (marked.value) unmarkEnemy(enemy.enemy.id);
if (!marked.value) markEnemy(enemy.id); if (!marked.value) markEnemy(enemy.enemy.id);
marked.value = hasMarkedEnemy(enemy.id); marked.value = hasMarkedEnemy(enemy.enemy.id);
} }
</script> </script>

View File

@ -1,11 +1,17 @@
import { import {
DamageDir, DamageDir,
DamageEnemy, DamageEnemy,
ensureFloorDamage,
getNeedCalDir, getNeedCalDir,
getSingleEnemy getSingleEnemy
} from './damage'; } from './damage';
import { findDir, has } from '../utils'; import { findDir, has } from '../utils';
export interface CurrentEnemy {
enemy: DamageEnemy;
onMapEnemy: DamageEnemy[];
}
export function getEnemy( export function getEnemy(
x: number, x: number,
y: number, y: number,
@ -228,13 +234,39 @@ core.events._action_battle = function (data, x, y, prefix) {
} }
}; };
core.enemys.getCurrentEnemys = function (floorId = core.status.floorId) {
floorId = floorId || core.status.floorId;
const enemys: CurrentEnemy[] = [];
const used: Record<string, DamageEnemy[]> = {};
ensureFloorDamage(floorId);
const floor = core.status.maps[floorId];
floor.enemy.list.forEach(v => {
if (!(v.id in used)) {
const e = new DamageEnemy(v.enemy);
e.calAttribute();
e.getRealInfo();
e.calDamage();
const curr: CurrentEnemy = {
enemy: e,
onMapEnemy: [v]
};
enemys.push(curr);
used[v.id] = curr.onMapEnemy;
} else {
used[v.id].push(v);
}
});
return enemys.sort((a, b) => {
return (
(a.enemy.damage?.[0]?.damage ?? Infinity) -
(b.enemy.damage?.[0]?.damage ?? Infinity)
);
});
};
declare global { declare global {
interface Events { interface Events {
/**
*
* @param x
* @param y
*/
beforeBattle( beforeBattle(
enemy: DamageEnemy, enemy: DamageEnemy,
x: number, x: number,
@ -242,9 +274,6 @@ declare global {
dir: DamageDir dir: DamageDir
): boolean; ): boolean;
/**
*
*/
afterBattle( afterBattle(
enemy: DamageEnemy, enemy: DamageEnemy,
x: number, x: number,
@ -252,4 +281,8 @@ declare global {
dir: DamageDir dir: DamageDir
): void; ): void;
} }
interface Enemys {
getCurrentEnemys(floorId?: FloorIds): CurrentEnemy[];
}
} }

View File

@ -417,7 +417,7 @@ export class DamageEnemy<T extends EnemyIds = EnemyIds> {
* inject光环之前 * inject光环之前
*/ */
calAttribute() { calAttribute() {
if (this.progress !== 1) return; if (this.progress !== 1 && has(this.x) && has(this.floorId)) return;
this.progress = 2; this.progress = 2;
const special = this.info.special; const special = this.info.special;
const info = this.info; const info = this.info;
@ -448,7 +448,7 @@ export class DamageEnemy<T extends EnemyIds = EnemyIds> {
* inject光环后执行 * inject光环后执行
*/ */
getRealInfo() { getRealInfo() {
if (this.progress < 3) { if (this.progress < 3 && has(this.x) && has(this.floorId)) {
throw new Error( throw new Error(
`Unexpected early real info calculating. Progress: ${this.progress}` `Unexpected early real info calculating. Progress: ${this.progress}`
); );
@ -804,6 +804,7 @@ export class DamageEnemy<T extends EnemyIds = EnemyIds> {
x?: number, x?: number,
y?: number y?: number
): CriticalDamageDelta[] { ): CriticalDamageDelta[] {
// todo: 可以优化,根据之前的计算可以直接确定下一个临界的范围
if (!isFinite(seckill)) return []; if (!isFinite(seckill)) return [];
const res: CriticalDamageDelta[] = []; const res: CriticalDamageDelta[] = [];
const def = hero.def!; const def = hero.def!;

View File

@ -2,8 +2,6 @@ import { getEnemy } from '../enemy/battle';
import { DamageDir, getNeedCalDir } from '../enemy/damage'; import { DamageDir, getNeedCalDir } from '../enemy/damage';
import { formatDamage } from '../utils'; import { formatDamage } from '../utils';
export {};
core.maps._initDetachedBlock = function ( core.maps._initDetachedBlock = function (
info: BlockInfo, info: BlockInfo,
x: number, x: number,

View File

@ -1,39 +1,40 @@
import { CurrentEnemy } from '../game/enemy/battle';
import { has } from '../utils'; import { has } from '../utils';
export interface ToShowEnemy extends CurrentEnemy {
critical: string;
criticalDam: string;
defDam: string;
/** [名称, 描述, 颜色] */
special: [string, string, string][];
damageColor: string;
showSpecial: [string, string, string][];
damage: string;
}
interface BookDetailInfo {
/** 怪物手册详细信息展示的怪物 */
enemy?: ToShowEnemy;
/** 怪物手册的怪物详细信息的初始位置 */
pos?: number;
}
export const detailInfo: BookDetailInfo = {};
/** /**
* *
* @param enemy * @param enemy
*/ */
export function getSpecialHint(enemy: Enemy & DetailedEnemy) { export function getSpecialHint(enemy: ToShowEnemy) {
const all = core
.getSpecials()
.filter(v => enemy.special.includes(v[0]))
.sort((a, b) => a[0] - b[0]);
const des = all.map(v => {
const des = v[2];
if (des instanceof Function) {
return des(enemy);
}
return des;
});
const name = all.map(v => {
const name = v[1];
if (name instanceof Function) {
return name(enemy);
}
return name;
});
return ( return (
<div> <div>
{all.map((v, i) => { {enemy.showSpecial.map((v, i) => {
return ( return (
<div class="special"> <div class="special">
<span style={{ color: core.arrayToRGBA(v[3]) }}> <span style={{ color: v[2] }}>
&nbsp;&nbsp;&nbsp;&nbsp;{name[i]} &nbsp;&nbsp;&nbsp;&nbsp;{v[0]}
</span> </span>
<span innerHTML={des[i]}></span> <span innerHTML={v[1]}></span>
</div> </div>
); );
})} })}
@ -46,12 +47,9 @@ export function getSpecialHint(enemy: Enemy & DetailedEnemy) {
* @param enemy * @param enemy
*/ */
export function getDefDamage( export function getDefDamage(
enemy: DetailedEnemy, enemy: ToShowEnemy,
addDef: number = 0, addDef: number = 0,
addAtk: number = 0, addAtk: number = 0
x?: number,
y?: number,
floorId?: FloorIds
) { ) {
// todo: 删除 getDamageInfo // todo: 删除 getDamageInfo
const ratio = core.status.thisMap.ratio; const ratio = core.status.thisMap.ratio;
@ -63,29 +61,26 @@ export function getDefDamage(
const max = 100 - Math.floor(addDef / ratio); const max = 100 - Math.floor(addDef / ratio);
for (let i = 0; i <= max; i++) { for (let i = 0; i <= max; i++) {
const dam = core.getDamageInfo( const dam = enemy.enemy.calEnemyDamage(
enemy.id,
{ {
def: core.getStatus('def') + ratio * i + addDef, atk: core.status.hero.atk + addAtk,
atk: core.getStatus('atk') + addAtk def: core.status.hero.def + addDef + i * ratio
}, },
x, 'none'
y,
floorId
); );
if (res.length === 0) { if (res.length === 0) {
origin = dam?.damage; origin = dam[0].damage;
if (has(origin)) { if (has(origin)) {
res.push([addDef + i * ratio, origin]); res.push([addDef + i * ratio, origin]);
last = origin; last = origin;
} }
continue; continue;
} }
if (!has(dam)) continue; if (!isFinite(dam[0].damage)) continue;
if (dam.damage === res.at(-1)?.[1]) continue; if (dam[0].damage === res.at(-1)?.[1]) continue;
last = dam.damage; last = dam[0].damage;
res.push([ratio * i + addDef, dam.damage]); res.push([ratio * i + addDef, dam[0].damage]);
} }
return res; return res;
@ -96,12 +91,9 @@ export function getDefDamage(
* @param enemy * @param enemy
*/ */
export function getCriticalDamage( export function getCriticalDamage(
enemy: DetailedEnemy, enemy: ToShowEnemy,
addAtk: number = 0, addAtk: number = 0,
addDef: number = 0, addDef: number = 0
x?: number,
y?: number,
floorId?: FloorIds
): [number, number][] { ): [number, number][] {
// todo: 删除 getDamageInfo // todo: 删除 getDamageInfo
const ratio = core.status.thisMap.ratio; const ratio = core.status.thisMap.ratio;
@ -113,29 +105,26 @@ export function getCriticalDamage(
const max = 100 - Math.floor(addAtk / ratio); const max = 100 - Math.floor(addAtk / ratio);
for (let i = 0; i <= max; i++) { for (let i = 0; i <= max; i++) {
const dam = core.getDamageInfo( const dam = enemy.enemy.calEnemyDamage(
enemy.id,
{ {
atk: core.getStatus('atk') + ratio * i + addAtk, atk: core.status.hero.atk + addAtk + i * ratio,
def: core.getStatus('def') + addDef def: core.status.hero.def + addDef
}, },
x, 'none'
y,
floorId
); );
if (res.length === 0) { if (res.length === 0) {
origin = dam?.damage; origin = dam[0].damage;
if (has(origin)) { if (has(origin)) {
res.push([addAtk + i * ratio, origin]); res.push([addAtk + i * ratio, origin]);
last = origin; last = origin;
} }
continue; continue;
} }
if (!has(dam)) continue; if (!isFinite(dam[0].damage)) continue;
if (dam.damage === res.at(-1)?.[1]) continue; if (dam[0].damage === res.at(-1)?.[1]) continue;
last = dam.damage; last = dam[0].damage;
res.push([ratio * i + addAtk, dam.damage]); res.push([ratio * i + addAtk, dam[0].damage]);
} }
return res; return res;

View File

@ -390,14 +390,6 @@ interface Enemys extends EnemyData {
floorId?: FloorIds floorId?: FloorIds
): number; ): number;
/**
*
* @example core.getCurrentEnemys('MT0') // 主塔0层的敌人集合
* @param floorId id
* @returns
*/
getCurrentEnemys(floorId?: FloorIds): DetailedEnemy[];
/** /**
* *
* @example core.hasEnemyLeft('greenSlime', ['sample0', 'sample1']) // 样板0层和1层是否有漏打的绿头怪 * @example core.hasEnemyLeft('greenSlime', ['sample0', 'sample1']) // 样板0层和1层是否有漏打的绿头怪

View File

@ -168,12 +168,6 @@ interface PluginUtils {
} }
interface PluginUis { interface PluginUis {
/** 怪物手册的怪物详细信息的初始位置 */
bookDetailPos: number;
/** 怪物手册详细信息展示的怪物 */
bookDetailEnemy: DetailedEnemy;
/** 定点查看的界面,特殊属性还是临界 */ /** 定点查看的界面,特殊属性还是临界 */
fixedDetailPanel: 'special' | 'critical'; fixedDetailPanel: 'special' | 'critical';

View File

@ -15,7 +15,7 @@
v-model:now="scroll" v-model:now="scroll"
v-model:drag="drag" v-model:drag="drag"
> >
<div v-for="(e, i) of enemy" class="enemy"> <div v-for="(e, i) of toShow" class="enemy">
<EnemyOne <EnemyOne
:selected="i === selected" :selected="i === selected"
:enemy="e" :enemy="e"
@ -38,7 +38,6 @@
</template> </template>
<script setup lang="tsx"> <script setup lang="tsx">
import { cloneDeep } from 'lodash-es';
import { sleep } from 'mutate-animate'; import { sleep } from 'mutate-animate';
import { onMounted, onUnmounted, ref } from 'vue'; import { onMounted, onUnmounted, ref } from 'vue';
import EnemyOne from '../components/enemyOne.vue'; import EnemyOne from '../components/enemyOne.vue';
@ -48,46 +47,87 @@ import BookDetail from './bookDetail.vue';
import { LeftOutlined } from '@ant-design/icons-vue'; import { LeftOutlined } from '@ant-design/icons-vue';
import { KeyCode } from '../plugin/keyCodes'; import { KeyCode } from '../plugin/keyCodes';
import { noClosePanel } from '../plugin/uiController'; import { noClosePanel } from '../plugin/uiController';
import { ToShowEnemy, detailInfo } from '../plugin/ui/book';
// todo: 使 core.status.checkBlock
const floorId = const floorId =
// @ts-ignore // @ts-ignore
core.floorIds[core.status.event?.ui?.index] ?? core.status.floorId; core.floorIds[core.status.event?.ui?.index] ?? core.status.floorId;
const specials = Object.fromEntries(
core.getSpecials().map(v => {
return [v[0], v.slice(1)];
})
) as Record<
string,
EnemySpecialDeclaration extends [number, ...infer F] ? F : never
>;
const enemy = core.getCurrentEnemys(floorId); const enemy = core.getCurrentEnemys(floorId);
const toShow: ToShowEnemy[] = enemy.map(v => {
const cri = v.enemy.calCritical(1, 'none')[0];
const critical = core.formatBigNumber(cri[0]?.atkDelta);
const criticalDam = core.formatBigNumber(-cri[0]?.delta);
const ratio = core.status.maps[floorId].ratio;
const defDam = core.formatBigNumber(
-v.enemy.calDefDamage(ratio, 'none')[0]?.delta
);
const damage = core.formatBigNumber(
v.enemy.damage?.[0]?.damage ?? Infinity
);
const fromFunc = (
func: string | ((enemy: Enemy) => string),
enemy: Enemy
) => {
return typeof func === 'string' ? func : func(enemy);
};
const special: [string, string, string][] = v.enemy.enemy.special.map(
vv => {
const s = specials[vv];
return [
fromFunc(s[0], v.enemy.enemy),
fromFunc(s[1], v.enemy.enemy),
s[2] as string
];
}
);
const showSpecial =
special.length > 2
? special.slice(0, 2).concat(['...', '', '#fff'])
: special.slice();
const damageColor = getDamageColor(
v.enemy.damage?.[0]?.damage ?? Infinity
) as string;
return {
critical,
criticalDam,
defDam,
special,
damageColor,
showSpecial,
damage,
...v
};
});
const scroll = ref(0); const scroll = ref(0);
const drag = ref(false); const drag = ref(false);
const detail = ref(false); const detail = ref(false);
const selected = ref(0); const selected = ref(0);
//
enemy.forEach(v => {
const l = v.specialText.length;
v.toShowSpecial = cloneDeep(v.specialText);
v.toShowColor = cloneDeep(v.specialColor);
if (l >= 3) {
v.toShowSpecial = v.specialText.slice(0, 2).concat(['...']);
v.toShowColor = v.specialColor.slice(0, 2).concat(['#fff']);
}
v.toShowColor = v.toShowColor.map(v => {
if (typeof v === 'string') return v;
else return core.arrayToRGBA(v);
});
v.damageColor = getDamageColor(v.damage!);
});
/** /**
* 选择怪物展示详细信息 * 选择怪物展示详细信息
* @param enemy 选择的怪物 * @param enemy 选择的怪物
* @param index 选择的怪物索引 * @param index 选择的怪物索引
*/ */
function select(enemy: Enemy & DetailedEnemy, index: number) { function select(enemy: ToShowEnemy, index: number) {
if (drag.value) return; if (drag.value) return;
const h = window.innerHeight; const h = window.innerHeight;
const y = index * h * 0.2 - scroll.value; const y = index * h * 0.2 - scroll.value;
core.plugin.bookDetailEnemy = enemy; detailInfo.enemy = enemy;
core.plugin.bookDetailPos = y; detailInfo.pos = y;
detail.value = true; detail.value = true;
hide(); hide();
} }
@ -161,7 +201,7 @@ function keyup(e: KeyboardEvent) {
(c === KeyCode.Enter || c === KeyCode.KeyC || c === KeyCode.Space) && (c === KeyCode.Enter || c === KeyCode.KeyC || c === KeyCode.Space) &&
!detail.value !detail.value
) { ) {
select(enemy[selected.value], selected.value); select(toShow[selected.value], selected.value);
} }
} }

View File

@ -2,7 +2,7 @@
<template> <template>
<div id="detail"> <div id="detail">
<div id="info" :style="{ top: `${top}px` }"> <div id="info" :style="{ top: `${top}px` }">
<EnemyOne :enemy="enemy"></EnemyOne> <EnemyOne :enemy="enemy!"></EnemyOne>
<a-divider <a-divider
dashed dashed
style="margin: 2vh 0 2vh 0; border-color: #ddd4; width: 100%" style="margin: 2vh 0 2vh 0; border-color: #ddd4; width: 100%"
@ -80,14 +80,15 @@ import { KeyCode } from '../plugin/keyCodes';
import { keycode } from '../plugin/utils'; import { keycode } from '../plugin/utils';
import { sleep } from 'mutate-animate'; import { sleep } from 'mutate-animate';
import EnemyTarget from '../panel/enemyTarget.vue'; import EnemyTarget from '../panel/enemyTarget.vue';
import { detailInfo } from '../plugin/ui/book';
const props = defineProps<{ const props = defineProps<{
fromBook?: boolean; fromBook?: boolean;
defaultPanel?: 'special' | 'critical' | 'target'; defaultPanel?: 'special' | 'critical' | 'target';
}>(); }>();
const enemy = core.plugin.bookDetailEnemy; const enemy = detailInfo.enemy;
const top = ref(core.plugin.bookDetailPos); const top = ref(detailInfo.pos);
const panel = ref<string>(props.defaultPanel ?? 'special'); const panel = ref<string>(props.defaultPanel ?? 'special');
let detail: HTMLDivElement; let detail: HTMLDivElement;
@ -102,7 +103,7 @@ function changePanel(e: MouseEvent, to: string) {
} }
function close() { function close() {
top.value = core.plugin.bookDetailPos; top.value = detailInfo.pos;
detail.style.opacity = '0'; detail.style.opacity = '0';
emits('close'); emits('close');
} }