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.
137 lines
4.5 KiB
137 lines
4.5 KiB
import type { EntityId } from 'db://assets/mx/module/ecs';
|
|
import type { Skill } from '../skill/Skill';
|
|
import type { SkillPresentationContext } from '../skill/SkillPresentationTypes';
|
|
import type { SkillVisualChannel } from './SkillVisualChannel';
|
|
import type { ISkillPresenter } from './SkillPresenter';
|
|
|
|
/** 待发射飞剑 */
|
|
interface FlySwordPending {
|
|
/** 施法者 */
|
|
caster: EntityId;
|
|
/** Ability id */
|
|
abilityId: string;
|
|
/** 目标 */
|
|
target: EntityId;
|
|
/** 伤害倍率 */
|
|
scale: number;
|
|
/** atk_buff1 */
|
|
buff1: string;
|
|
/** atk_buff2 */
|
|
buff2: string;
|
|
/** 距发射(秒) */
|
|
wait: number;
|
|
/** 表现通道 */
|
|
channel: SkillVisualChannel;
|
|
}
|
|
|
|
let _host: FlySwordHost | null = null;
|
|
|
|
/** 飞剑交错发射宿主 */
|
|
export function getFlySwordHost(): FlySwordHost {
|
|
if (!_host) _host = new FlySwordHost();
|
|
return _host;
|
|
}
|
|
|
|
/**
|
|
* atk_type=10 飞剑线:按间隔依次发射,落地伤由 BowAtkHost 结算。
|
|
*/
|
|
export class FlySwordHost {
|
|
private readonly _pending: FlySwordPending[] = [];
|
|
|
|
/**
|
|
* 排队发射。
|
|
* @param channel 表现通道
|
|
* @param ctx 表现上下文
|
|
* @param scale 伤害倍率
|
|
* @param intervalSec 邻剑间隔
|
|
* @param maxCount 最多发射数
|
|
*/
|
|
begin(channel: SkillVisualChannel, ctx: SkillPresentationContext, scale: number, intervalSec: number, maxCount: number): void {
|
|
const buff1 = String(ctx.params.buff1 ?? '');
|
|
const buff2 = String(ctx.params.buff2 ?? '');
|
|
const targets = ctx.targets;
|
|
let launched = 0;
|
|
for (let i = 0; i < targets.length && launched < maxCount; i++) {
|
|
const tid = targets[i];
|
|
if (!(tid > 0)) continue;
|
|
this._pending.push({
|
|
caster: ctx.caster,
|
|
abilityId: ctx.abilityId,
|
|
target: tid,
|
|
scale,
|
|
buff1,
|
|
buff2,
|
|
wait: launched * Math.max(0, intervalSec),
|
|
channel,
|
|
});
|
|
launched += 1;
|
|
}
|
|
if (launched <= 0) {
|
|
console.log('[FlySword] 无有效目标', ctx.params.skillId);
|
|
return;
|
|
}
|
|
this.tick(0);
|
|
}
|
|
|
|
/**
|
|
* 推进排队发射。
|
|
* @param dt 逻辑秒
|
|
*/
|
|
tick(dt: number): void {
|
|
if (this._pending.length <= 0) return;
|
|
for (let i = this._pending.length - 1; i >= 0; i--) {
|
|
const p = this._pending[i];
|
|
p.wait -= dt;
|
|
if (p.wait > 0) continue;
|
|
this._pending.splice(i, 1);
|
|
if (!p.channel.launchFlySword(p.caster, p.abilityId, p.target, p.scale, p.buff1, p.buff2)) {
|
|
console.log('[FlySword] 发射失败', p.target);
|
|
}
|
|
}
|
|
}
|
|
|
|
/**
|
|
* 卸下施法者排队。
|
|
* @param caster 塔实体
|
|
*/
|
|
cancelCaster(caster: EntityId): void {
|
|
for (let i = this._pending.length - 1; i >= 0; i--) {
|
|
if (this._pending[i].caster === caster) this._pending.splice(i, 1);
|
|
}
|
|
}
|
|
|
|
/** 清空全部排队 */
|
|
abortAll(): void {
|
|
this._pending.length = 0;
|
|
}
|
|
}
|
|
|
|
/**
|
|
* 飞剑武器主动:瞬发,实例化飞剑.prefab 飞向目标,落地结算。
|
|
*/
|
|
export class FlySwordAtkPresenter implements ISkillPresenter {
|
|
present(ctx: SkillPresentationContext, host: Skill): void {
|
|
if (!ctx.isAlive) {
|
|
ctx.finish();
|
|
return;
|
|
}
|
|
const raw = String(ctx.params.atkPara ?? '').split(',');
|
|
const count = Math.floor(Number(String(raw[0] ?? '').trim()) || 0);
|
|
const intervalSec = Number(String(raw[1] ?? '').trim());
|
|
const wan = Number(String(raw[2] ?? '').trim());
|
|
if (!(count > 0) || !Number.isFinite(intervalSec) || intervalSec < 0 || !Number.isFinite(wan) || !(wan > 0)) {
|
|
console.log('[FlySwordAtkPresenter] atk_para 无效', ctx.params.skillId, ctx.params.atkPara);
|
|
ctx.finish();
|
|
return;
|
|
}
|
|
const skillKind = String(ctx.params.skillKind ?? '');
|
|
const followWeapon = skillKind === 'weapon' || host.visualChannel.useWeapon;
|
|
if (followWeapon) host.visualChannel.ensureWeapon();
|
|
const seq = host.beginPresentationCast(ctx);
|
|
if (!(seq > 0)) return;
|
|
host.disarmAnimComplete();
|
|
getFlySwordHost().begin(host.visualChannel, ctx, wan / 10000, intervalSec, count);
|
|
ctx.releasePresentationLock();
|
|
host.finishPresentationCast(seq, false);
|
|
}
|
|
}
|
|
|