master
parent
32d9e005d1
commit
eaf613c5e8
@ -0,0 +1,137 @@ |
|||||||
|
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); |
||||||
|
} |
||||||
|
} |
||||||
@ -0,0 +1,9 @@ |
|||||||
|
{ |
||||||
|
"ver": "4.0.24", |
||||||
|
"importer": "typescript", |
||||||
|
"imported": true, |
||||||
|
"uuid": "aa83d9ec-834f-4235-8777-652362698e71", |
||||||
|
"files": [], |
||||||
|
"subMetas": {}, |
||||||
|
"userData": {} |
||||||
|
} |
||||||
Loading…
Reference in new issue