删除不同位置攻击怪物伤害不同的机制

This commit is contained in:
unanmed 2023-08-02 10:43:00 +08:00
parent b14e4edcc7
commit 4999452cb6
11 changed files with 79 additions and 353 deletions

View File

@ -2822,6 +2822,7 @@ control.prototype.getRealStatus = function (name) {
////// 从status中获得实际属性增幅后的如果不存在则从勇士属性中获取 //////
control.prototype.getRealStatusOrDefault = function (status, name) {
// todo: 删除
return core.plugin.hero.getHeroStatusOf(status, name);
};

View File

@ -331,6 +331,7 @@ var functions_d6ad677b_427a_4623_b50f_a445a3b0ef8a = {
},
enemys: {
getSpecials: function () {
// todo: 重写
// 获得怪物的特殊属性,每一行定义一个特殊属性。
// 分为五项,第一项为该特殊属性的数字,第二项为特殊属性的名字,第三项为特殊属性的描述
// 第四项为该特殊属性的颜色,可以写十六进制 #RRGGBB 或者 [r,g,b,a] 四元数组

View File

@ -1,10 +1,4 @@
import {
DamageDir,
DamageEnemy,
ensureFloorDamage,
getNeedCalDir,
getSingleEnemy
} from './damage';
import { DamageEnemy, ensureFloorDamage, getSingleEnemy } from './damage';
import { findDir, has } from '../utils';
export interface CurrentEnemy {
@ -31,32 +25,23 @@ export function getEnemy(
core.enemys.canBattle = function (
x: number,
y: number,
floorId: FloorIds = core.status.floorId,
dir: DamageDir | DamageDir[] = getNeedCalDir(x, y, floorId)
floorId: FloorIds = core.status.floorId
) {
const enemy = getEnemy(x, y, floorId);
const damage = enemy.calEnemyDamage(core.status.hero, dir);
return damage.some(v => {
return v.damage < core.status.hero.hp;
});
const { damage } = enemy.calDamage();
return damage < core.status.hero.hp;
};
core.events.battle = function (
x: number,
y: number,
dir: DamageDir,
force: boolean = false,
callback?: () => void
) {
core.saveAndStopAutomaticRoute();
const enemy = getEnemy(x, y);
// 非强制战斗
if (
!core.enemys.canBattle(x, y, void 0, dir) &&
!force &&
!core.status.event.id
) {
if (!core.enemys.canBattle(x, y) && !force && !core.status.event.id) {
core.stopSound();
core.playSound('操作失败');
core.drawTip('你打不过此怪物!', enemy.id);
@ -65,10 +50,10 @@ core.events.battle = function (
// 自动存档
if (!core.status.event.id) core.autosave(true);
// 战前事件
if (!this.beforeBattle(enemy, x, y, dir))
if (!this.beforeBattle(enemy, x, y))
return core.clearContinueAutomaticRoute(callback);
// 战后事件
this.afterBattle(enemy, x, y, dir);
this.afterBattle(enemy, x, y);
callback?.();
};
@ -79,8 +64,7 @@ core.events.beforeBattle = function () {
core.events.afterBattle = function (
enemy: DamageEnemy,
x?: number,
y?: number,
dir: DamageDir = 'none'
y?: number
) {
const floorId = core.status.floorId;
const special = enemy.info.special;
@ -96,7 +80,7 @@ core.events.afterBattle = function (
if (!core.material.animates[animate]?.se) core.playSound('attack.mp3');
// 战斗伤害
const info = enemy.calEnemyDamage(core.status.hero, dir)[0];
const info = enemy.calDamage(core.status.hero);
const damage = info.damage;
// 判定是否致死
if (damage >= core.status.hero.hp) {
@ -238,8 +222,7 @@ core.events._sys_battle = function (data: Block, callback?: () => void) {
core.insertAction(beforeBattle, data.x, data.y, callback);
}
} else {
const dir = findDir(data, core.status.hero.loc) as DamageDir;
this.battle(data.x, data.y, dir, false, callback);
this.battle(data.x, data.y, false, callback);
}
};
@ -253,11 +236,7 @@ core.events._action_battle = function (data, x, y, prefix) {
return;
}
const [ex, ey] = this.__action_getLoc(data.loc, x, y, prefix) as LocArr;
const dir = findDir(core.status.hero.loc, {
x: ex,
y: ey
}) as DamageDir;
this.battle(ex, ey, dir, true, core.doAction);
this.battle(ex, ey, true, core.doAction);
}
};
@ -285,28 +264,16 @@ core.enemys.getCurrentEnemys = function (floorId = core.status.floorId) {
});
return enemys.sort((a, b) => {
return (
(a.enemy.damage?.[0]?.damage ?? Infinity) -
(b.enemy.damage?.[0]?.damage ?? Infinity)
);
const ad = a.enemy.calDamage().damage;
const bd = b.enemy.calDamage().damage;
return ad - bd;
});
};
declare global {
interface Events {
beforeBattle(
enemy: DamageEnemy,
x: number,
y: number,
dir: DamageDir
): boolean;
afterBattle(
enemy: DamageEnemy,
x: number,
y: number,
dir: DamageDir
): void;
beforeBattle(enemy: DamageEnemy, x: number, y: number): boolean;
afterBattle(enemy: DamageEnemy, x: number, y: number): void;
}
interface Enemys {

View File

@ -17,7 +17,7 @@ control.prototype.checkBlock = function (forceMockery: boolean = false) {
);
}
core.status.hero.hp -= damage;
const type = Array.from(info.type.keys());
const type = [...info.type];
const text = type.join('') || '伤害';
core.drawTip('受到' + text + damage + '点');
core.drawHeroAnimate('zone');

View File

@ -1,4 +1,3 @@
import { equal } from '../utils';
import { getHeroStatusOf, getHeroStatusOn } from '../hero';
import { Range, RangeCollection } from '../range';
import {
@ -36,10 +35,6 @@ interface EnemyInfo {
interface DamageInfo {
damage: number;
/** 从怪物位置指向勇士的方向 */
dir: Dir | 'none';
x?: number;
y?: number;
/** 自动切换技能时使用的技能 */
skill?: number;
}
@ -58,12 +53,9 @@ interface HaloData<T extends keyof HaloType = keyof HaloType> {
}
interface DamageDelta {
dir: DamageDir;
/** 跟最小伤害值的减伤 */
delta: number;
damage: number;
/** 跟当前方向的减伤 */
dirDelta: number;
info: DamageInfo;
}
@ -73,7 +65,6 @@ interface CriticalDamageDelta extends Omit<DamageDelta, 'info'> {
}
type HaloFn = (info: EnemyInfo, enemy: Enemy) => void;
export type DamageDir = Dir | 'none';
/** 光环属性 */
export const haloSpecials: number[] = [8, 21, 25, 26, 27];
@ -129,10 +120,10 @@ export class EnemyCollection implements RangeCollection<DamageEnemy> {
*
* @param noCache 使
*/
calDamage(noCache: boolean = false, onMap: boolean = false) {
calDamage(noCache: boolean = false) {
if (noCache) this.calRealAttribute();
this.list.forEach(v => {
v.calDamage(void 0, onMap);
v.calDamage(void 0);
});
}
@ -141,12 +132,7 @@ export class EnemyCollection implements RangeCollection<DamageEnemy> {
*/
calMapDamage() {
this.mapDamage = {};
const hero = getHeroStatusOn(
realStatus,
core.status.hero.loc.x,
core.status.hero.loc.y,
this.floorId
);
const hero = getHeroStatusOn(realStatus, this.floorId);
this.list.forEach(v => {
v.calMapDamage(this.mapDamage, hero);
});
@ -192,15 +178,8 @@ export class EnemyCollection implements RangeCollection<DamageEnemy> {
});
}
render(onMap?: boolean): void;
render(onMap: boolean, cal: boolean, noCache: boolean): void;
render(
onMap: boolean = false,
cal: boolean = false,
noCache: boolean = false
) {
render(onMap: boolean = false, cal: boolean = false) {
if (cal) {
this.calDamage(noCache, true);
this.calMapDamage();
}
core.status.damage.data = [];
@ -211,72 +190,23 @@ export class EnemyCollection implements RangeCollection<DamageEnemy> {
this.list.forEach(v => {
if (onMap && !checkV2(v.x, v.y)) return;
if (!v.damage) {
throw new Error(
`Unexpected null of enemy's damage. Loc: '${v.x},${v.y}'. Floor: ${v.floorId}`
);
}
if (equal(v.damage, 'damage')) {
// 伤害全部相等,绘制在怪物本身所在位置
const { damage, color } = formatDamage(v.damage[0].damage);
const critical = v.calCritical(1)[0]?.[0];
core.status.damage.data.push({
text: damage,
px: 32 * v.x! + 1,
py: 32 * (v.y! + 1) - 1,
color: color
});
core.status.damage.data.push({
text: critical?.atkDelta.toString() ?? '?',
px: 32 * v.x! + 1,
py: 32 * (v.y! + 1) - 11,
color: '#fff'
});
} else {
let min = v.damage[0].damage;
let max = min;
let minI = 0;
for (let i = 1; i < v.damage.length; i++) {
const dam = v.damage[i].damage;
if (dam < min) {
min = dam;
minI = i;
}
if (dam > max) {
max = dam;
}
}
const delta = max - min;
const { damage, color } = formatDamage(min);
// 在怪物位置绘制最低的伤害
core.status.damage.data.push({
text: damage,
px: 32 * v.x! + 1,
py: 32 * (v.y! + 1) - 1,
color: color
});
// 绘制临界
const critical = v.calCritical(1, v.damage[minI].dir)[0]?.[0];
core.status.damage.data.push({
text: critical?.atkDelta.toString() ?? '?',
px: 32 * v.x! + 1,
py: 32 * (v.y! + 1) - 11,
color: '#fff'
});
// 然后根据位置依次绘制对应位置的伤害
for (const dam of v.damage) {
if (dam.dir === 'none') continue;
const d = ((dam.damage - min) / delta) * 255;
const color = core.arrayToRGB([d, 255 - d, 0]);
const { damage } = v.calDamage();
core.status.damage.dir.push({
x: v.x!,
y: v.y!,
dir: dam.dir,
color: color
});
}
}
// 伤害全部相等,绘制在怪物本身所在位置
const { damage: dam, color } = formatDamage(damage);
const critical = v.calCritical(1)[0];
core.status.damage.data.push({
text: dam,
px: 32 * v.x! + 1,
py: 32 * (v.y! + 1) - 1,
color: color
});
core.status.damage.data.push({
text: critical?.atkDelta.toString() ?? '?',
px: 32 * v.x! + 1,
py: 32 * (v.y! + 1) - 11,
color: '#fff'
});
});
// 地图伤害
@ -362,10 +292,6 @@ export class DamageEnemy<T extends EnemyIds = EnemyIds> {
* -> provide inject -> ->
*/
info!: EnemyInfo;
/** 怪物伤害 */
damage?: DamageInfo[];
/** 是否需要计算伤害 */
needCalDamage: boolean = true;
/** 向其他怪提供过的光环 */
providedHalo: number[] = [];
@ -409,7 +335,6 @@ export class DamageEnemy<T extends EnemyIds = EnemyIds> {
floorId: this.floorId
};
this.progress = 0;
this.needCalDamage = true;
this.providedHalo = [];
}
@ -578,17 +503,9 @@ export class DamageEnemy<T extends EnemyIds = EnemyIds> {
/**
*
*/
calDamage(
hero: Partial<HeroStatus> = core.status.hero,
onMap: boolean = false
) {
if (onMap && !checkV2(this.x, this.y)) return this.damage!;
if (!this.needCalDamage) return this.damage!;
const dirs = getNeedCalDir(this.x, this.y, this.floorId, hero);
this.needCalDamage = false;
return (this.damage = this.calEnemyDamage(hero, dirs));
calDamage(hero: Partial<HeroStatus> = core.status.hero) {
const enemy = this.getRealInfo();
return this.calEnemyDamageOf(hero, enemy);
}
/**
@ -695,44 +612,8 @@ export class DamageEnemy<T extends EnemyIds = EnemyIds> {
damage[loc].type.add(type);
}
calEnemyDamage(
hero: Partial<HeroStatus> = core.status.hero,
dir: DamageDir | DamageDir[]
): DamageInfo[] {
const dirs = ensureArray(dir);
const enemy = this.getRealInfo();
return dirs.map(dir => {
let x: number | undefined;
let y: number | undefined;
if (has(this.x) && has(this.y)) {
if (dir !== 'none') {
[x, y] = ofDir(this.x, this.y, dir);
} else {
x = hero.x ?? this.x;
y = hero.y ?? this.y;
}
}
const { damage, skill } = this.calEnemyDamageOf(hero, enemy, x, y);
return {
damage,
dir,
skill,
x,
y
};
});
}
private calEnemyDamageOf(
hero: Partial<HeroStatus>,
enemy: EnemyInfo,
x?: number,
y?: number
) {
const status = getHeroStatusOf(hero, realStatus, x, y, this.floorId);
private calEnemyDamageOf(hero: Partial<HeroStatus>, enemy: EnemyInfo) {
const status = getHeroStatusOf(hero, realStatus, this.floorId);
let damage = calDamageWith(enemy, status) ?? Infinity;
let skill = -1;
@ -765,27 +646,11 @@ export class DamageEnemy<T extends EnemyIds = EnemyIds> {
*/
calCritical(
num: number = 1,
dir: DamageDir | DamageDir[] = 'none',
hero: Partial<HeroStatus> = core.status.hero
): CriticalDamageDelta[][] {
const origin = this.calEnemyDamage(hero, dir);
const min = Math.min(...origin.map(v => v.damage));
): CriticalDamageDelta[] {
const origin = this.calDamage(hero);
const seckill = this.getSeckillAtk();
return origin.map(v => {
const dir = v.dir;
if (
dir === 'none' ||
!has(this.x) ||
!has(this.y) ||
!has(this.floorId)
) {
return this.calCriticalWith(num, min, seckill, v, hero);
} else {
const [x, y] = ofDir(this.x, this.y, dir);
return this.calCriticalWith(num, min, seckill, v, hero, x, y);
}
});
return this.calCriticalWith(num, seckill, origin, hero);
}
/**
@ -797,12 +662,9 @@ export class DamageEnemy<T extends EnemyIds = EnemyIds> {
*/
private calCriticalWith(
num: number,
min: number,
seckill: number,
origin: DamageInfo,
hero: Partial<HeroStatus>,
x?: number,
y?: number
hero: Partial<HeroStatus>
): CriticalDamageDelta[] {
// todo: 可以优化,根据之前的计算可以直接确定下一个临界的范围
if (!isFinite(seckill)) return [];
@ -819,8 +681,7 @@ export class DamageEnemy<T extends EnemyIds = EnemyIds> {
let ori = origin.damage;
const calDam = () => {
return this.calEnemyDamageOf({ atk: curr, def }, enemy, x, y)
.damage;
return this.calEnemyDamageOf({ atk: curr, def }, enemy).damage;
};
let i = 0;
@ -835,9 +696,7 @@ export class DamageEnemy<T extends EnemyIds = EnemyIds> {
res.push({
damage: dam,
atkDelta: Math.ceil(v - hero.atk!),
dir: origin.dir,
delta: dam - min,
dirDelta: dam - origin.damage
delta: dam - ori
});
start = v;
@ -872,9 +731,7 @@ export class DamageEnemy<T extends EnemyIds = EnemyIds> {
res.push({
damage: dam,
atkDelta: 0,
dir: origin.dir,
delta: dam - min,
dirDelta: 0
delta: 0
});
}
@ -889,26 +746,19 @@ export class DamageEnemy<T extends EnemyIds = EnemyIds> {
*/
calDefDamage(
num: number = 1,
dir: DamageDir | DamageDir[] = 'none',
hero: Partial<HeroStatus> = core.status.hero
): DamageDelta[] {
const damage = this.calEnemyDamage(
{ def: (hero.def ?? core.status.hero.def) + num },
dir
);
const origin = this.calEnemyDamage(hero, dir);
const min = Math.min(...origin.map(v => v.damage));
return damage.map((v, i) => {
const finite = isFinite(v.damage);
return {
dir: v.dir,
damage: v.damage,
info: v,
delta: finite ? v.damage - min : Infinity,
dirDelta: finite ? v.damage - origin[i].damage : Infinity
};
): DamageDelta {
const damage = this.calDamage({
def: (hero.def ?? core.status.hero.def) + num
});
const origin = this.calDamage(hero);
const finite = isFinite(damage.damage);
return {
damage: damage.damage,
info: damage,
delta: finite ? damage.damage - origin.damage : Infinity
};
}
/**
@ -923,7 +773,7 @@ export class DamageEnemy<T extends EnemyIds = EnemyIds> {
return Infinity;
}
// 列方程求解
// 列方程求解,拿笔算一下就知道了
// 饥渴,会偷取勇士攻击
if (info.special.includes(7)) {
if (info.damageDecline === 0) {
@ -971,47 +821,6 @@ const skills: [unlock: string, condition: string][] = [
['shieldOn', 'shield']
];
/**
*
* @param x
* @param y
* @param floorId
* @returns
*/
export function getNeedCalDir(
x?: number,
y?: number,
floorId?: FloorIds,
hero: Partial<HeroStatus> = core.status.hero
): (Dir | 'none')[] {
// 第一章或序章,或者没有指定怪物位置,或者没开自动定位,用不到这个函数
if (flags.chapter < 2 || !has(x) || !has(y) || !floorId) {
return ['none'];
}
// 如果指定了勇士坐标
if (has(hero.x) && has(hero.y)) {
return ['none'];
}
const needMap: Dir[] = ['left', 'down', 'right', 'up'];
const { width, height } = core.status.maps[floorId];
const blocks = core.getMapBlocksObj(floorId);
const res = needMap.filter(v => {
const [tx, ty] = ofDir(x, y, v);
if (tx < 0 || ty < 0 || tx >= width || ty >= height) return false;
const index = `${tx},${ty}` as LocString;
if (!core.canMoveHero(tx, ty, backDir(v), floorId)) return false;
const block = blocks[index];
if (!block) return true;
if (block.event.noPass || block.event.cls === 'items') return false;
return true;
});
return res.length === 0 ? ['none'] : res;
}
/**
*
* @param info
@ -1123,7 +932,7 @@ export function getSingleEnemy(id: EnemyIds) {
const enemy = new DamageEnemy(e);
enemy.calAttribute();
enemy.getRealInfo();
enemy.calDamage(core.status.hero, false)[0];
enemy.calDamage(core.status.hero);
return enemy;
}

View File

@ -19,7 +19,7 @@ core.control.updateDamage = function (floorId = core.status.floorId, ctx) {
ensureFloorDamage(floorId);
floor.enemy.extract();
floor.enemy.calDamage(true, onMap);
floor.enemy.calDamage(true);
floor.enemy.calMapDamage();
core.status.damage.data = [];

View File

@ -1,5 +1,4 @@
import { getEnemy } from '../enemy/battle';
import { DamageDir, getNeedCalDir } from '../enemy/damage';
import { formatDamage } from '../utils';
core.maps._initDetachedBlock = function (
@ -37,16 +36,8 @@ core.maps._initDetachedBlock = function (
displayDamage
) {
const enemy = getEnemy(x, y);
const dam = enemy.calEnemyDamage(core.status.hero, getNeedCalDir(x, y));
let min = Infinity;
let minDir: DamageDir = 'none';
for (const d of dam) {
if (d.damage < min) {
min = d.damage;
minDir = d.dir;
}
}
const { damage, color } = formatDamage(min);
const dam = enemy.calDamage();
const { damage, color } = formatDamage(dam.damage);
damageCanvas = '__damage_' + x + '_' + y;
const ctx = core.createCanvas(damageCanvas, 0, 0, 32, 32, 65);
@ -54,8 +45,8 @@ core.maps._initDetachedBlock = function (
ctx.font = '14px normal';
core.fillBoldText(ctx, damage, 1, 31, color as string);
if (core.flags.displayCritical) {
const critical = enemy.calCritical(1, minDir)[0];
const atk = core.formatBigNumber(critical[0].dirDelta, true);
const critical = enemy.calCritical(1);
const atk = core.formatBigNumber(critical[0]?.delta, true);
const display = atk === '???' ? '?' : atk;
core.fillBoldText(ctx, display, 1, 21, '#FFFFFF');
}

View File

@ -5,32 +5,21 @@
* @param y
* @param floorId
*/
export function getHeroStatusOn(
name: 'all',
x?: number,
y?: number,
floorId?: FloorIds
): HeroStatus;
export function getHeroStatusOn(name: 'all', floorId?: FloorIds): HeroStatus;
export function getHeroStatusOn(
name: (keyof HeroStatus)[],
x?: number,
y?: number,
floorId?: FloorIds
): Partial<HeroStatus>;
export function getHeroStatusOn<K extends keyof HeroStatus>(
name: K,
x?: number,
y?: number,
floorId?: FloorIds
): HeroStatus[K];
export function getHeroStatusOn(
name: keyof HeroStatus | 'all' | (keyof HeroStatus)[],
x?: number,
y?: number,
floorId?: FloorIds
) {
// @ts-ignore
return getHeroStatusOf(core.status.hero, name, x, y, floorId);
return getHeroStatusOf(core.status.hero, name, floorId);
}
/**
@ -44,44 +33,34 @@ export function getHeroStatusOn(
export function getHeroStatusOf(
status: Partial<HeroStatus>,
name: 'all',
x?: number,
y?: number,
floorId?: FloorIds
): HeroStatus;
export function getHeroStatusOf(
status: Partial<HeroStatus>,
name: (keyof HeroStatus)[],
x?: number,
y?: number,
floorId?: FloorIds
): Partial<HeroStatus>;
export function getHeroStatusOf<K extends keyof HeroStatus>(
status: Partial<HeroStatus>,
name: K,
x?: number,
y?: number,
floorId?: FloorIds
): HeroStatus[K];
export function getHeroStatusOf(
status: DeepPartial<HeroStatus>,
name: keyof HeroStatus | 'all' | (keyof HeroStatus)[],
x?: number,
y?: number,
floorId?: FloorIds
) {
return getRealStatus(status, name, x, y, floorId);
return getRealStatus(status, name, floorId);
}
function getRealStatus(
status: DeepPartial<HeroStatus>,
name: keyof HeroStatus | 'all' | (keyof HeroStatus)[],
x?: number,
y?: number,
floorId?: FloorIds
floorId: FloorIds = core.status.floorId
): any {
if (name instanceof Array) {
return Object.fromEntries(
name.map(v => [v, getRealStatus(status, v, x, y, floorId)])
name.map(v => [v, getRealStatus(status, v, floorId)])
);
}
@ -90,7 +69,7 @@ function getRealStatus(
Object.keys(core.status.hero).map(v => [
v,
v !== 'all' &&
getRealStatus(status, v as keyof HeroStatus, x, y, floorId)
getRealStatus(status, v as keyof HeroStatus, floorId)
])
);
}
@ -102,10 +81,6 @@ function getRealStatus(
);
}
x ??= core.status.hero.loc.x;
y ??= core.status.hero.loc.y;
floorId ??= core.status.floorId;
// 永夜、极昼
if (name === 'atk' || name === 'def') {
s += window.flags?.[`night_${floorId}`] ?? 0;

View File

@ -65,13 +65,11 @@ export function getDetailedEnemy(
const ratio = core.status.maps[floorId].ratio;
const dam = enemy.calEnemyDamage(core.status.hero, 'none')[0].damage;
const cri = enemy.calCritical(1, 'none')[0]?.[0];
const dam = enemy.calDamage().damage;
const cri = enemy.calCritical(1)[0];
const critical = core.formatBigNumber(cri?.atkDelta);
const criticalDam = core.formatBigNumber(cri?.delta);
const defDam = core.formatBigNumber(
enemy.calDefDamage(ratio, 'none')[0].damage
);
const defDam = core.formatBigNumber(enemy.calDefDamage(ratio).damage);
const damage = core.formatBigNumber(dam);
const fromFunc = (

View File

@ -111,7 +111,6 @@ interface Events extends EventData {
battle(
x: number,
y: number,
dir: Dir | 'none',
force: boolean = false,
callback?: () => void
): void;

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

@ -363,22 +363,13 @@ interface GamePluginHeroRealStatus {
* @param y
* @param floorId
*/
getHeroStatusOn(
name: 'all',
x?: number,
y?: number,
floorId?: FloorIds
): HeroStatus;
getHeroStatusOn(name: 'all', floorId?: FloorIds): HeroStatus;
getHeroStatusOn(
name: (keyof HeroStatus)[],
x?: number,
y?: number,
floorId?: FloorIds
): Partial<HeroStatus>;
getHeroStatusOn<K extends keyof HeroStatus>(
name: K,
x?: number,
y?: number,
floorId?: FloorIds
): HeroStatus[K];
@ -393,22 +384,16 @@ interface GamePluginHeroRealStatus {
getHeroStatusOf(
status: Partial<HeroStatus>,
name: 'all',
x?: number,
y?: number,
floorId?: FloorIds
): HeroStatus;
getHeroStatusOf(
status: Partial<HeroStatus>,
name: (keyof HeroStatus)[],
x?: number,
y?: number,
floorId?: FloorIds
): Partial<HeroStatus>;
getHeroStatusOf<K extends keyof HeroStatus>(
status: Partial<HeroStatus>,
name: K,
x?: number,
y?: number,
floorId?: FloorIds
): HeroStatus[K];
}