彻底修正伤害计算

This commit is contained in:
unanmed 2023-07-12 18:34:55 +08:00
parent 061e25e8d1
commit b9c1142d14
4 changed files with 72 additions and 111 deletions

View File

@ -29,6 +29,9 @@ interface EnemyInfo {
defBuff: number; defBuff: number;
hpBuff: number; hpBuff: number;
enemy: Enemy; enemy: Enemy;
x?: number;
y?: number;
floorId?: FloorIds;
} }
interface DamageInfo { interface DamageInfo {
@ -71,7 +74,7 @@ interface CriticalDamageDelta extends Omit<DamageDelta, 'info'> {
type HaloFn = (info: EnemyInfo, enemy: Enemy) => void; type HaloFn = (info: EnemyInfo, enemy: Enemy) => void;
type DamageDir = Dir | 'none'; type DamageDir = Dir | 'none';
export const haloSpecials: number[] = [21, 25, 26, 27]; export const haloSpecials: number[] = [8, 21, 25, 26, 27];
export class EnemyCollection implements RangeCollection<DamageEnemy> { export class EnemyCollection implements RangeCollection<DamageEnemy> {
floorId: FloorIds; floorId: FloorIds;
@ -105,9 +108,17 @@ export class EnemyCollection implements RangeCollection<DamageEnemy> {
* @param noCache 使 * @param noCache 使
*/ */
calRealAttribute(noCache: boolean = false) { calRealAttribute(noCache: boolean = false) {
if (!noCache) return;
this.list.forEach(v => { this.list.forEach(v => {
if (noCache) v.reset(); v.reset();
v.calRealAttribute(); v.preProvideHalo();
});
this.list.forEach(v => {
v.calAttribute();
v.provideHalo();
});
this.list.forEach(v => {
v.getRealInfo();
}); });
} }
@ -116,11 +127,8 @@ export class EnemyCollection implements RangeCollection<DamageEnemy> {
* @param noCache 使 * @param noCache 使
*/ */
calDamage(noCache: boolean = false, onMap: boolean = false) { calDamage(noCache: boolean = false, onMap: boolean = false) {
if (noCache) this.calRealAttribute(noCache);
this.list.forEach(v => { this.list.forEach(v => {
if (noCache || v.needCalculate) {
v.reset();
v.calRealAttribute();
}
v.calDamage(void 0, onMap); v.calDamage(void 0, onMap);
}); });
} }
@ -161,13 +169,13 @@ export class EnemyCollection implements RangeCollection<DamageEnemy> {
if (!recursion) { if (!recursion) {
arr.forEach(v => { arr.forEach(v => {
enemy.forEach(e => { enemy.forEach(e => {
e.injectHalo(v); e.injectHalo(v, e.enemy);
}); });
}); });
} else { } else {
enemy.forEach(e => { enemy.forEach(e => {
arr.forEach(v => { arr.forEach(v => {
e.injectHalo(v); e.injectHalo(v, e.enemy);
e.preProvideHalo(); e.preProvideHalo();
}); });
}); });
@ -333,8 +341,6 @@ export class DamageEnemy<T extends EnemyIds = EnemyIds> {
* -> provide inject -> -> * -> provide inject -> ->
*/ */
info!: EnemyInfo; info!: EnemyInfo;
/** 是否需要计算属性 */
needCalculate: boolean = true;
/** 怪物伤害 */ /** 怪物伤害 */
damage?: DamageInfo[]; damage?: DamageInfo[];
/** 是否需要计算伤害 */ /** 是否需要计算伤害 */
@ -376,38 +382,25 @@ export class DamageEnemy<T extends EnemyIds = EnemyIds> {
atkBuff: 0, atkBuff: 0,
defBuff: 0, defBuff: 0,
hpBuff: 0, hpBuff: 0,
enemy: this.enemy enemy: this.enemy,
x: this.x,
y: this.y,
floorId: this.floorId
}; };
this.progress = 0; this.progress = 0;
this.needCalculate = true;
this.needCalDamage = true; this.needCalDamage = true;
this.providedHalo = [];
} }
/** /**
* inject光环之前 * inject光环之前
* @param hero
* @param getReal
*/ */
calAttribute( calAttribute() {
hero: Partial<HeroStatus> = core.status.hero,
getReal: boolean = true
) {
if (this.progress !== 1) return; if (this.progress !== 1) 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;
const enemy = this.enemy;
const floorId = this.floorId ?? core.status.floorId; const floorId = this.floorId ?? core.status.floorId;
const { atk } = getReal
? getHeroStatusOf(hero, ['atk'], hero.x, hero.y, hero.floorId)
: hero;
if (!has(atk)) return;
// 饥渴
if (special.includes(7)) {
info.atk += (atk * (enemy.hungry ?? 0)) / 100;
}
// 智慧之源 // 智慧之源
if (flags.hard === 2 && special.includes(14)) { if (flags.hard === 2 && special.includes(14)) {
@ -418,11 +411,6 @@ export class DamageEnemy<T extends EnemyIds = EnemyIds> {
info.atk -= flags[`night_${floorId}`] ?? 0; info.atk -= flags[`night_${floorId}`] ?? 0;
info.def -= flags[`night_${floorId}`] ?? 0; info.def -= flags[`night_${floorId}`] ?? 0;
// 坚固
if (special.includes(3) && enemy.def < atk - 1) {
info.def = atk - 1;
}
// 融化融化不属于怪物光环因此不能用provide和inject计算需要在这里计算 // 融化融化不属于怪物光环因此不能用provide和inject计算需要在这里计算
if (has(flags[`melt_${floorId}`]) && has(this.x) && has(this.y)) { if (has(flags[`melt_${floorId}`]) && has(this.x) && has(this.y)) {
for (const [loc, per] of Object.entries(flags[`melt_${floorId}`])) { for (const [loc, per] of Object.entries(flags[`melt_${floorId}`])) {
@ -439,42 +427,24 @@ export class DamageEnemy<T extends EnemyIds = EnemyIds> {
* inject光环后执行 * inject光环后执行
*/ */
getRealInfo() { getRealInfo() {
if (this.progress < 3) {
throw new Error(
`Unexpected early real info calculating. Progress: ${this.progress}`
);
}
if (this.progress === 4) return this.info; if (this.progress === 4) return this.info;
if (this.progress <= 3) this.ensureCaled(3);
this.progress = 4; this.progress = 4;
// 此时已经inject光环因此直接计算真实属性 // 此时已经inject光环因此直接计算真实属性
const info = this.info; const info = this.info;
info.atk = Math.floor(info.atk * (info.atkBuff / 100 + 1)); info.atk = Math.floor(info.atk * (info.atkBuff / 100 + 1));
info.def = Math.floor(info.def * (info.defBuff / 100 + 1)); info.def = Math.floor(info.def * (info.defBuff / 100 + 1));
info.hp = Math.floor(info.hp * (info.hpBuff / 100 + 1)); info.hp = Math.floor(info.hp * (info.hpBuff / 100 + 1));
this.needCalculate = false;
return this.info; return this.info;
} }
/**
*
*/
calRealAttribute() {
this.preProvideHalo();
this.calAttribute();
this.provideHalo();
this.getRealInfo();
}
/**
*
* @param progress
*/
ensureCaled(progress: number) {
if (progress >= 1) this.preProvideHalo();
if (progress >= 2) this.calAttribute();
if (progress >= 3) this.provideHalo();
if (progress >= 4) this.getRealInfo();
}
getHaloSpecials(): number[] { getHaloSpecials(): number[] {
if (!this.floorId) return []; if (!this.floorId) return [];
if (!core.has(this.x) || !core.has(this.y)) return []; if (!core.has(this.x) || !core.has(this.y)) return [];
@ -510,15 +480,17 @@ export class DamageEnemy<T extends EnemyIds = EnemyIds> {
if (!core.has(this.x) || !core.has(this.y)) return; if (!core.has(this.x) || !core.has(this.y)) return;
const col = this.col ?? core.status.maps[this.floorId].enemy; const col = this.col ?? core.status.maps[this.floorId].enemy;
if (!col) return; if (!col) return;
const speical = this.getHaloSpecials(); const special = this.getHaloSpecials();
const square7: HaloFn[] = []; const square7: HaloFn[] = [];
const square5: HaloFn[] = []; const square5: HaloFn[] = [];
// e 是被加成怪的属性enemy 是施加光环的怪
// 抱团 // 抱团
if (speical.includes(8)) { if (special.includes(8)) {
square5.push((e, enemy) => { square5.push((e, enemy) => {
if (e.special.includes(8)) { if (e.special.includes(8) && e.x !== this.x && this.y !== e.y) {
e.atkBuff += enemy.together ?? 0; e.atkBuff += enemy.together ?? 0;
e.defBuff += enemy.together ?? 0; e.defBuff += enemy.together ?? 0;
} }
@ -527,25 +499,25 @@ export class DamageEnemy<T extends EnemyIds = EnemyIds> {
} }
// 冰封光环 // 冰封光环
if (speical.includes(21)) { if (special.includes(21)) {
square7.push((e, enemy) => { square7.push(e => {
e.damageDecline += enemy.iceDecline ?? 0; e.damageDecline += this.enemy.iceHalo ?? 0;
}); });
this.providedHalo.push(21); this.providedHalo.push(21);
} }
// 冰封之核 // 冰封之核
if (speical.includes(26)) { if (special.includes(26)) {
square5.push((e, enemy) => { square5.push(e => {
e.defBuff += enemy.iceCore ?? 0; e.defBuff += this.enemy.iceCore ?? 0;
}); });
this.providedHalo.push(26); this.providedHalo.push(26);
} }
// 火焰之核 // 火焰之核
if (speical.includes(27)) { if (special.includes(27)) {
square5.push((e, enemy) => { square5.push(e => {
e.atkBuff += enemy.fireCore ?? 0; e.atkBuff += this.enemy.fireCore ?? 0;
}); });
this.providedHalo.push(27); this.providedHalo.push(27);
} }
@ -557,8 +529,8 @@ export class DamageEnemy<T extends EnemyIds = EnemyIds> {
/** /**
* *
*/ */
injectHalo(halo: HaloFn) { injectHalo(halo: HaloFn, enemy: Enemy) {
halo.call(this, this.info, this.enemy); halo(this.info, enemy);
} }
/** /**
@ -687,14 +659,10 @@ export class DamageEnemy<T extends EnemyIds = EnemyIds> {
): DamageInfo[] { ): DamageInfo[] {
const damageCache: Record<string, number> = {}; const damageCache: Record<string, number> = {};
const dirs = ensureArray(dir); const dirs = ensureArray(dir);
const enemy = this.getRealInfo();
return dirs.map(dir => { return dirs.map(dir => {
const status = getHeroStatusOf(hero, realStatus); const status = getHeroStatusOf(hero, realStatus);
let enemy: EnemyInfo;
this.reset();
this.preProvideHalo();
this.calAttribute(status, false);
enemy = this.getRealInfo();
let damage = calDamageWith(enemy, status) ?? Infinity; let damage = calDamageWith(enemy, status) ?? Infinity;
let skill = -1; let skill = -1;
@ -705,13 +673,6 @@ export class DamageEnemy<T extends EnemyIds = EnemyIds> {
if (!flags[unlock]) continue; if (!flags[unlock]) continue;
flags[condition] = true; flags[condition] = true;
const status = getHeroStatusOf(hero, realStatus); const status = getHeroStatusOf(hero, realStatus);
// 这几个技能会导致怪物属性也改变,需要重新计算怪物属性
if (needCalSpecial.some(v => enemy.special.includes(v))) {
this.reset();
this.preProvideHalo();
this.calAttribute(status, false);
enemy = this.getRealInfo();
}
const id = `${status.atk},${status.def}`; const id = `${status.atk},${status.def}`;
const d = const d =
@ -727,12 +688,6 @@ export class DamageEnemy<T extends EnemyIds = EnemyIds> {
damageCache[id] = d; damageCache[id] = d;
} }
} }
if (needCalSpecial.some(v => enemy.special.includes(v))) {
this.reset();
this.preProvideHalo();
this.calAttribute(status, false);
this.getRealInfo();
}
let x: number | undefined; let x: number | undefined;
let y: number | undefined; let y: number | undefined;
@ -960,10 +915,6 @@ const skills: [unlock: string, condition: string][] = [
['bladeOn', 'blade'], ['bladeOn', 'blade'],
['shieldOn', 'shield'] ['shieldOn', 'shield']
]; ];
/**
*
*/
const needCalSpecial = [3, 7];
/** /**
* *
@ -1017,13 +968,15 @@ export function calDamageWith(
): number | null { ): number | null {
const { hp, hpmax, mana, mdef } = core.status.hero; const { hp, hpmax, mana, mdef } = core.status.hero;
let { atk, def } = hero as HeroStatus; let { atk, def } = hero as HeroStatus;
const { hp: monHp, atk: monAtk, def: monDef, special, enemy } = info; let { hp: monHp, atk: monAtk, def: monDef, special, enemy } = info;
let damage = 0; let damage = 0;
// 饥渴 // 饥渴
if (special.includes(7)) { if (special.includes(7)) {
atk *= 1 - enemy.hungry! / 100; const delta = Math.floor((atk * enemy.hungry!) / 100);
atk -= delta;
monAtk += delta;
} }
let heroPerDamage: number; let heroPerDamage: number;
@ -1032,12 +985,23 @@ export function calDamageWith(
if (special.includes(9)) { if (special.includes(9)) {
heroPerDamage = atk + mana - monDef; heroPerDamage = atk + mana - monDef;
if (heroPerDamage <= 0) return null; if (heroPerDamage <= 0) return null;
} else if (special.includes(3)) {
// 由于坚固的特性,只能放到这来计算了
if (atk > enemy.def) heroPerDamage = 1 + mana;
else return null;
} else { } else {
heroPerDamage = atk - monDef; heroPerDamage = atk - monDef;
if (heroPerDamage > 0) heroPerDamage += mana; if (heroPerDamage > 0) heroPerDamage += mana;
else return null; else return null;
} }
// 霜冻
if (special.includes(20) && !core.hasEquip('I589')) {
heroPerDamage *= 1 - enemy.ice! / 100;
}
heroPerDamage *= 1 - info.damageDecline / 100;
let enemyPerDamage: number; let enemyPerDamage: number;
// 魔攻 // 魔攻
@ -1048,17 +1012,16 @@ export function calDamageWith(
if (enemyPerDamage < 0) enemyPerDamage = 0; if (enemyPerDamage < 0) enemyPerDamage = 0;
} }
// 先攻
if (special.includes(17)) {
damage += enemyPerDamage;
}
// 连击 // 连击
if (special.includes(4)) enemyPerDamage *= 2; if (special.includes(4)) enemyPerDamage *= 2;
if (special.includes(5)) enemyPerDamage *= 3; if (special.includes(5)) enemyPerDamage *= 3;
if (special.includes(6)) enemyPerDamage *= enemy.n!; if (special.includes(6)) enemyPerDamage *= enemy.n!;
// 霜冻
if (special.includes(20) && !core.hasEquip('I589')) {
heroPerDamage *= 1 - enemy.ice! / 100;
}
heroPerDamage *= 1 - info.damageDecline;
// 苍蓝刻 // 苍蓝刻
if (special.includes(28)) { if (special.includes(28)) {
heroPerDamage *= 1 - enemy.paleShield! / 100; heroPerDamage *= 1 - enemy.paleShield! / 100;

View File

@ -17,6 +17,7 @@ core.control.updateDamage = function (floorId = core.status.floorId, ctx) {
} }
// 计算伤害 // 计算伤害
core.plugin.damage.ensureFloorDamage(floorId); core.plugin.damage.ensureFloorDamage(floorId);
floor.enemy.calDamage(true, onMap); floor.enemy.calDamage(true, onMap);
floor.enemy.calMapDamage(true); floor.enemy.calMapDamage(true);
core.status.damage.data = []; core.status.damage.data = [];

View File

@ -72,21 +72,17 @@ export class Range<C extends Partial<Loc>> {
Range.registerRangeType( Range.registerRangeType(
'square', 'square',
(col, { x, y, d }) => { (col, { x, y, d }) => {
const cache = (col.cache.square ??= {});
const index = `${x},${y},${d}`;
if (index in cache) return cache[index];
const list = col.collection.list; const list = col.collection.list;
const r = Math.floor(d / 2);
const r = Math.floor(d); return list.filter(v => {
return (cache[index] = list.filter(v => {
return ( return (
core.has(v.x) && core.has(v.x) &&
core.has(v.y) && core.has(v.y) &&
Math.abs(v.x - x) <= r && Math.abs(v.x - x) <= r &&
Math.abs(v.y - y) <= r Math.abs(v.y - y) <= r
); );
})); });
}, },
(col, { x, y, d }, item) => { (col, { x, y, d }, item) => {
const r = Math.floor(d / 2); const r = Math.floor(d / 2);

View File

@ -25,7 +25,8 @@ type PartialNumbericEnemyProperty =
| 'crit' | 'crit'
| 'courage' | 'courage'
| 'charge' | 'charge'
| 'paleShield'; | 'paleShield'
| 'iceHalo';
type BooleanEnemyProperty = type BooleanEnemyProperty =
| 'zoneSquare' | 'zoneSquare'