You can not select more than 25 topics
Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
80 lines
3.1 KiB
80 lines
3.1 KiB
import type { EntityId } from 'db://assets/mx/module/ecs';
|
|
import { createAbilityContext } from 'db://assets/mx/module/battle/combat/AbilityContext';
|
|
import { EntityTag, HealthComp, TagComp } from 'db://assets/mx/module/battle';
|
|
import type { SkillPresentationContext } from '../skill/SkillPresentationTypes';
|
|
import { SkillPresentationEnv } from '../skill/SkillPresentationEnv';
|
|
|
|
/** Boss 不可被秒杀/斩杀 */
|
|
export function isBossEntity(entityId: EntityId): boolean {
|
|
const session = SkillPresentationEnv.getBattleSession();
|
|
if (!session?.world.isAlive(entityId)) return false;
|
|
return !!session.world.getComponent(entityId, TagComp)?.tags.includes(EntityTag.Boss);
|
|
}
|
|
|
|
/** 聚合已选词条 skill_special(含普攻 up_type=1) */
|
|
export function readSkillSpecialWan(caster: EntityId, specialType: number): number {
|
|
let best = 0;
|
|
for (const raw of SkillPresentationEnv.getSkillSpecials(caster)) {
|
|
for (const seg of String(raw).split(';')) {
|
|
const part = seg.trim();
|
|
if (!part) continue;
|
|
const nums = part.split(',').map(Number);
|
|
if ((nums[0] ?? 0) !== specialType || nums.length < 2) continue;
|
|
best = Math.max(best, Math.max(0, Number(nums[1]) || 0));
|
|
}
|
|
}
|
|
return best;
|
|
}
|
|
|
|
function dealExecuteDamage(
|
|
ctx: SkillPresentationContext,
|
|
targetId: EntityId,
|
|
damageType: string,
|
|
): boolean {
|
|
const session = SkillPresentationEnv.getBattleSession();
|
|
if (!session?.world.isAlive(targetId)) return false;
|
|
const floatPos = SkillPresentationEnv.resolveKillFloaterWorldPos(targetId);
|
|
const abilityCtx = createAbilityContext(session, ctx.caster, ctx.abilityId, [targetId]);
|
|
abilityCtx.level = ctx.level;
|
|
const amount = session.damage.dealToTarget(ctx.caster, targetId, {
|
|
percentMaxHpWan: 10000,
|
|
scale: 1,
|
|
damageType,
|
|
skillAtkBuff1: '',
|
|
skillAtkBuff2: '',
|
|
}, abilityCtx);
|
|
if (amount > 0 && floatPos) {
|
|
SkillPresentationEnv.playKillFloater(floatPos, damageType);
|
|
}
|
|
return amount > 0;
|
|
}
|
|
|
|
/** skill_special 类型 10:概率秒杀(Boss 无效) */
|
|
export function tryInstantKill(
|
|
ctx: SkillPresentationContext,
|
|
targetId: EntityId,
|
|
killWan: number,
|
|
damageType = 'instant_kill',
|
|
): boolean {
|
|
if (killWan <= 0 || isBossEntity(targetId)) return false;
|
|
const rng = SkillPresentationEnv.getBattleRng();
|
|
const roll = (rng?.next() ?? Math.random()) * 10000;
|
|
if (roll >= killWan) return false;
|
|
return dealExecuteDamage(ctx, targetId, damageType);
|
|
}
|
|
|
|
/** skill_special 类型 11:HP 比例斩杀(Boss 无效) */
|
|
export function tryExecuteByThreshold(
|
|
ctx: SkillPresentationContext,
|
|
targetId: EntityId,
|
|
thresholdWan: number,
|
|
damageType = 'execute',
|
|
): boolean {
|
|
if (thresholdWan <= 0 || isBossEntity(targetId)) return false;
|
|
const session = SkillPresentationEnv.getBattleSession();
|
|
if (!session?.world.isAlive(targetId)) return false;
|
|
const hp = session.world.getComponent(targetId, HealthComp);
|
|
if (!hp || hp.maxHp <= 0) return false;
|
|
if (hp.hp / hp.maxHp > thresholdWan / 10000) return false;
|
|
return dealExecuteDamage(ctx, targetId, damageType);
|
|
}
|
|
|