伤害计算函数

This commit is contained in:
unanmed 2023-05-14 21:59:36 +08:00
parent f96ce0f8b0
commit b72bf33815
3 changed files with 174 additions and 33 deletions

View File

@ -5,7 +5,7 @@ import {
ServerResponse, ServerResponse,
createServer as http createServer as http
} from 'http'; } from 'http';
import { isNil, some } from 'lodash-es'; import { isNil } from 'lodash-es';
import config from '../mota.config.js'; import config from '../mota.config.js';
import fs from 'fs-extra'; import fs from 'fs-extra';
import { resolve, basename } from 'path'; import { resolve, basename } from 'path';

View File

@ -20,10 +20,17 @@ interface EnemyInfo {
defBuff: number; defBuff: number;
hpBuff: number; hpBuff: number;
together: number; together: number;
enemy: Enemy;
} }
interface DamageInfo { interface DamageInfo {
damage: number; damage: number;
/** 从勇士位置指向怪物的方向 */
dir: Dir | 'none';
x?: number;
y?: number;
/** 自动切换技能时使用的技能 */
skill?: number;
} }
type HaloFn = (info: EnemyInfo, enemy: Enemy) => void; type HaloFn = (info: EnemyInfo, enemy: Enemy) => void;
@ -53,6 +60,8 @@ export class EnemyCollection implements RangeCollection<DamageEnemy> {
calDamage(noCache: boolean = false) {} calDamage(noCache: boolean = false) {}
calMapDamage(noCache: boolean = false) {}
/** /**
* *
* @param type * @param type
@ -110,7 +119,7 @@ export class DamageEnemy<T extends EnemyIds = EnemyIds> {
/** 是否需要计算属性 */ /** 是否需要计算属性 */
needCalculate: boolean = true; needCalculate: boolean = true;
/** 怪物伤害 */ /** 怪物伤害 */
damage?: DamageInfo; damage?: DamageInfo[];
/** 是否需要计算伤害 */ /** 是否需要计算伤害 */
needCalDamage: boolean = true; needCalDamage: boolean = true;
@ -137,7 +146,8 @@ export class DamageEnemy<T extends EnemyIds = EnemyIds> {
atkBuff: 0, atkBuff: 0,
defBuff: 0, defBuff: 0,
hpBuff: 0, hpBuff: 0,
together: 0 together: 0,
enemy: this.enemy
}; };
this.needCalculate = true; this.needCalculate = true;
this.needCalDamage = true; this.needCalDamage = true;
@ -290,16 +300,70 @@ export class DamageEnemy<T extends EnemyIds = EnemyIds> {
/** /**
* *
*/ */
calDamage() { calDamage(hero: Partial<HeroStatus> = core.status.hero) {
if (!this.needCalDamage) return this.damage!; if (!this.needCalDamage) return this.damage!;
const info = this.getRealInfo(); const info = this.getRealInfo();
const dirs = getNeedCalDir(this.x, this.y, this.floorId, hero);
const damageCache: Record<string, number> = {};
return (this.damage = dirs.map(dir => {
const status = getHeroStatusOf(hero, realStatus);
let damage = calDamageWith(info, status) ?? Infinity;
let skill = -1;
if (flags.autoSkill) {
for (let i = 0; i < skills.length; i++) {
const [unlock, condition] = skills[i];
if (!flags[unlock]) continue;
flags[condition] = true;
const status = getHeroStatusOf(hero, realStatus);
const id = `${status.atk},${status.def}`;
const d =
id in damageCache
? damageCache[id]
: calDamageWith(info, status) ?? Infinity;
if (d < damage) {
damage = d;
skill = i;
}
flags[condition] = false;
damageCache[id] = d;
}
}
let x = this.x;
let y = this.y;
if (has(this.x) && has(this.y)) {
if (dir !== 'none') {
[x, y] = ofDir(this.x, this.y, dir);
}
}
return {
damage,
dir,
skill,
x,
y
};
}));
} }
calMapDamage() {}
} }
/** /**
* buff加成core.status.hero取 * buff加成core.status.hero取
*/ */
const realStatus: (keyof HeroStatus)[] = ['atk', 'def']; const realStatus: (keyof HeroStatus)[] = ['atk', 'def'];
/**
*
*/
const skills = [
['bladeOn', 'blade'],
['shieldOn', 'shield']
];
/** /**
* *
@ -308,46 +372,119 @@ const realStatus: (keyof HeroStatus)[] = ['atk', 'def'];
* @param floorId * @param floorId
*/ */
export function getNeedCalDir( export function getNeedCalDir(
x: number, x?: number,
y: number, y?: number,
floorId: FloorIds, floorId: FloorIds = core.status.floorId,
hero: Partial<HeroStatus> = core.status.hero hero: Partial<HeroStatus> = core.status.hero
): [Dir | 'none', Partial<HeroStatus>][] { ): (Dir | 'none')[] {
// 第一章或序章,用不到这个函数 // 第一章或序章,或者没有指定怪物位置,或者没开自动定位,用不到这个函数
if (flags.chapter < 2) { if (flags.chapter < 2 || !has(x) || !has(y)) {
return [['none', getHeroStatusOf(hero, realStatus, x, y, floorId)]]; return ['none'];
} }
// 如果指定了勇士坐标 // 如果指定了勇士坐标
if (has(hero.x) && has(hero.y)) { if (has(hero.x) && has(hero.y)) {
const { x, y, floorId } = hero; return ['none'];
if (has(floorId)) {
return [['none', getHeroStatusOf(hero, realStatus, x, y, floorId)]];
} else {
return [['none', getHeroStatusOf(hero, realStatus, x, y, floorId)]];
}
} }
const needMap: Dir[] = ['left', 'down', 'right', 'up']; const needMap: Dir[] = ['left', 'down', 'right', 'up'];
const { width, height } = core.status.maps[floorId]; const { width, height } = core.status.maps[floorId];
const blocks = core.getMapBlocksObj(floorId); const blocks = core.getMapBlocksObj(floorId);
return needMap return needMap.filter(v => {
.filter(v => { const [tx, ty] = ofDir(x, y, v);
const [tx, ty] = ofDir(x, y, v); if (tx < 0 || ty < 0 || tx >= width || ty >= height) return false;
if (tx < 0 || ty < 0 || tx >= width || ty >= height) return false; const index = `${tx},${ty}` as LocString;
const index = `${tx},${ty}` as LocString; const block = blocks[index];
const block = blocks[index]; if (block.event.noPass) return false;
if (block.event.noPass) return false; if (!core.canMoveHero(tx, ty, backDir(v), floorId)) return false;
if (!core.canMoveHero(tx, ty, backDir(v), floorId)) return false;
return true; return true;
}) });
.map(v => { }
const [tx, ty] = ofDir(x, y, v);
const status = getHeroStatusOf(hero, realStatus, tx, ty, floorId); /**
return [v, status]; *
}); * @param info
* @param hero
*/
export function calDamageWith(
info: EnemyInfo,
hero: Partial<HeroStatus>
): number | null {
const { hp, hpmax, mana, mdef } = core.status.hero;
let { atk, def } = hero as HeroStatus;
const { hp: monHp, atk: monAtk, def: monDef, special, enemy } = info;
let damage = 0;
// 饥渴
if (special.includes(7)) {
atk *= 1 - enemy.hungry! / 100;
}
let heroPerDamage: number;
// 绝对防御
if (special.includes(9)) {
heroPerDamage = atk + mana - monDef;
if (heroPerDamage <= 0) return null;
} else {
heroPerDamage = atk - monDef;
if (heroPerDamage > 0) heroPerDamage += mana;
else return null;
}
let enemyPerDamage: number;
// 魔攻
if (special.includes(2) || special.includes(13)) {
enemyPerDamage = monAtk;
} else {
enemyPerDamage = monAtk - def;
if (enemyPerDamage < 0) enemyPerDamage = 0;
}
// 连击
if (special.includes(4)) enemyPerDamage *= 2;
if (special.includes(5)) enemyPerDamage *= 3;
if (special.includes(6)) enemyPerDamage *= enemy.n!;
// 霜冻
if (special.includes(20) && !core.hasEquip('I589')) {
heroPerDamage *= 1 - enemy.ice! / 100;
}
heroPerDamage *= 1 - info.damageDecline;
let turn = Math.ceil(monHp / heroPerDamage);
// 致命一击
if (special.includes(1)) {
const times = Math.floor(turn / 5);
damage += ((times * (enemy.crit! - 100)) / 100) * enemyPerDamage;
}
// 勇气之刃
if (turn > 1 && special.includes(10)) {
damage += (enemy.courage! / 100 - 1) * enemyPerDamage;
}
// 勇气冲锋
if (special.includes(11)) {
damage += (enemy.charge! / 100) * enemyPerDamage;
turn += 5;
}
damage += (turn - 1) * enemyPerDamage;
// 无上之盾
if (core.hasFlag('superSheild')) {
damage -= mdef / 10;
}
// 生命回复
damage -= hpmax * turn;
if (flags.hard === 1) damage *= 0.9;
return damage;
} }
declare global { declare global {

View File

@ -20,7 +20,11 @@ type PartialNumbericEnemyProperty =
| 'iceCore' | 'iceCore'
| 'fireCore' | 'fireCore'
| 'together' | 'together'
| 'hungry'; | 'hungry'
| 'ice'
| 'crit'
| 'courage'
| 'charge';
type BooleanEnemyProperty = type BooleanEnemyProperty =
| 'zoneSquare' | 'zoneSquare'