|
|
|
@ -103,6 +103,12 @@ const WATER_BALL_SPEED = BOW_ATK_ARROW_SPEED; |
|
|
|
const FLY_SWORD_PREFAB = '飞剑'; |
|
|
|
const FLY_SWORD_PREFAB = '飞剑'; |
|
|
|
/** 飞剑默认 Spine 动画 */ |
|
|
|
/** 飞剑默认 Spine 动画 */ |
|
|
|
const FLY_SWORD_ANIM = '飞剑'; |
|
|
|
const FLY_SWORD_ANIM = '飞剑'; |
|
|
|
|
|
|
|
/** 飞剑弧高占射程比例(弦中点上下偏移) */ |
|
|
|
|
|
|
|
const FLY_SWORD_ARC_T = 0.32; |
|
|
|
|
|
|
|
/** 飞剑弧高下限(像素) */ |
|
|
|
|
|
|
|
const FLY_SWORD_ARC_MIN = 56; |
|
|
|
|
|
|
|
/** 同向高阶剑弧高相对基础的递增值 */ |
|
|
|
|
|
|
|
const FLY_SWORD_ARC_TIER = 0.38; |
|
|
|
/** battleHero/prefab/skill/玉帝飞剑 */ |
|
|
|
/** battleHero/prefab/skill/玉帝飞剑 */ |
|
|
|
const YUDI_FLY_SWORD_PREFAB = '玉帝飞剑'; |
|
|
|
const YUDI_FLY_SWORD_PREFAB = '玉帝飞剑'; |
|
|
|
/** 飞剑预制体默认动画(带光效) */ |
|
|
|
/** 飞剑预制体默认动画(带光效) */ |
|
|
|
@ -182,6 +188,49 @@ class WaterBallRecycle { |
|
|
|
} |
|
|
|
} |
|
|
|
} |
|
|
|
} |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
/** |
|
|
|
|
|
|
|
* 飞剑飞行绑定:追踪落点 + 落地回池。 |
|
|
|
|
|
|
|
*/ |
|
|
|
|
|
|
|
class FlySwordRecycle { |
|
|
|
|
|
|
|
private static readonly _pool: FlySwordRecycle[] = []; |
|
|
|
|
|
|
|
private _channel: SkillVisualChannel | null = null; |
|
|
|
|
|
|
|
private _node: Node | null = null; |
|
|
|
|
|
|
|
private _target: EntityId = 0; |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
/** 每帧刷新终点;目标失效则沿用上次 */ |
|
|
|
|
|
|
|
readonly resolveToWorld = (_node: Node): Vec3 | null => { |
|
|
|
|
|
|
|
if (!(this._target > 0)) return null; |
|
|
|
|
|
|
|
return SkillPresentationEnv.getTargetWorldPos(this._target); |
|
|
|
|
|
|
|
}; |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
/** 落地:回池飞剑 */ |
|
|
|
|
|
|
|
readonly onLand = (): void => { |
|
|
|
|
|
|
|
const channel = this._channel; |
|
|
|
|
|
|
|
const node = this._node; |
|
|
|
|
|
|
|
this._channel = null; |
|
|
|
|
|
|
|
this._node = null; |
|
|
|
|
|
|
|
this._target = 0; |
|
|
|
|
|
|
|
FlySwordRecycle._pool.push(this); |
|
|
|
|
|
|
|
if (channel && node) channel.recycleWaterBall(node); |
|
|
|
|
|
|
|
}; |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
/** |
|
|
|
|
|
|
|
* 取得绑定。 |
|
|
|
|
|
|
|
* @param channel 表现通道 |
|
|
|
|
|
|
|
* @param node 飞剑 |
|
|
|
|
|
|
|
* @param target 锁定目标 |
|
|
|
|
|
|
|
*/ |
|
|
|
|
|
|
|
static obtain(channel: SkillVisualChannel, node: Node, target: EntityId): FlySwordRecycle { |
|
|
|
|
|
|
|
const rec = FlySwordRecycle._pool.length > 0 |
|
|
|
|
|
|
|
? FlySwordRecycle._pool.pop()! |
|
|
|
|
|
|
|
: new FlySwordRecycle(); |
|
|
|
|
|
|
|
rec._channel = channel; |
|
|
|
|
|
|
|
rec._node = node; |
|
|
|
|
|
|
|
rec._target = target; |
|
|
|
|
|
|
|
return rec; |
|
|
|
|
|
|
|
} |
|
|
|
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
|
|
/** 待发射的弓箭 */ |
|
|
|
/** 待发射的弓箭 */ |
|
|
|
interface BowArrowLaunch { |
|
|
|
interface BowArrowLaunch { |
|
|
|
/** 代数,过期则丢弃 */ |
|
|
|
/** 代数,过期则丢弃 */ |
|
|
|
@ -1382,16 +1431,17 @@ export class SkillVisualChannel { |
|
|
|
} |
|
|
|
} |
|
|
|
|
|
|
|
|
|
|
|
/** |
|
|
|
/** |
|
|
|
* 飞剑武器主动:实例化「飞剑」直线飞向目标,落地结算。 |
|
|
|
* 飞剑武器主动:弧线追踪飞向目标;多发时上下弧高交错。 |
|
|
|
* @param caster 施法者 |
|
|
|
* @param caster 施法者 |
|
|
|
* @param abilityId Ability id |
|
|
|
* @param abilityId Ability id |
|
|
|
* @param target 锁定目标 |
|
|
|
* @param target 锁定目标 |
|
|
|
* @param scale 伤害倍率 |
|
|
|
* @param scale 伤害倍率 |
|
|
|
* @param buff1 atk_buff1 |
|
|
|
* @param buff1 atk_buff1 |
|
|
|
* @param buff2 atk_buff2 |
|
|
|
* @param buff2 atk_buff2 |
|
|
|
|
|
|
|
* @param shotIndex 发次(0 起,奇偶上下弧) |
|
|
|
* @returns 是否发出 |
|
|
|
* @returns 是否发出 |
|
|
|
*/ |
|
|
|
*/ |
|
|
|
launchFlySword(caster: EntityId, abilityId: string, target: EntityId, scale: number, buff1: string, buff2: string): boolean { |
|
|
|
launchFlySword(caster: EntityId, abilityId: string, target: EntityId, scale: number, buff1: string, buff2: string, shotIndex = 0): boolean { |
|
|
|
const fromNode = this._weapon.skinAnim?.node ?? this._weapon.aim ?? this._weapon.root; |
|
|
|
const fromNode = this._weapon.skinAnim?.node ?? this._weapon.aim ?? this._weapon.root; |
|
|
|
const fromWorld = fromNode?.isValid |
|
|
|
const fromWorld = fromNode?.isValid |
|
|
|
? fromNode.worldPosition |
|
|
|
? fromNode.worldPosition |
|
|
|
@ -1411,17 +1461,19 @@ export class SkillVisualChannel { |
|
|
|
const duration = Math.max(0.12, dist / WATER_BALL_SPEED); |
|
|
|
const duration = Math.max(0.12, dist / WATER_BALL_SPEED); |
|
|
|
const node = this._spawnFlySwordPrefab(this._arrowFrom); |
|
|
|
const node = this._spawnFlySwordPrefab(this._arrowFrom); |
|
|
|
if (!node) return false; |
|
|
|
if (!node) return false; |
|
|
|
|
|
|
|
const arcHeight = this._flySwordArcHeight(dist, shotIndex); |
|
|
|
|
|
|
|
const land = FlySwordRecycle.obtain(this, node, target); |
|
|
|
SkillTransformDriver.animateParabola({ |
|
|
|
SkillTransformDriver.animateParabola({ |
|
|
|
node, |
|
|
|
node, |
|
|
|
duration, |
|
|
|
duration, |
|
|
|
fromWorld: this._arrowFrom, |
|
|
|
fromWorld: this._arrowFrom, |
|
|
|
toWorld: this._arrowTo, |
|
|
|
toWorld: this._arrowTo, |
|
|
|
arcHeight: 0, |
|
|
|
arcHeight, |
|
|
|
|
|
|
|
resolveToWorld: land.resolveToWorld, |
|
|
|
easing: SkillTransformDriver.easeLinear, |
|
|
|
easing: SkillTransformDriver.easeLinear, |
|
|
|
faceTangent: true, |
|
|
|
faceTangent: true, |
|
|
|
}); |
|
|
|
}); |
|
|
|
SkillTransformDriver.stepNow(); |
|
|
|
SkillTransformDriver.stepNow(); |
|
|
|
const land = WaterBallRecycle.obtain(this, node); |
|
|
|
|
|
|
|
getBowAtkHost().spawnVolley({ |
|
|
|
getBowAtkHost().spawnVolley({ |
|
|
|
caster, |
|
|
|
caster, |
|
|
|
target, |
|
|
|
target, |
|
|
|
@ -1435,6 +1487,19 @@ export class SkillVisualChannel { |
|
|
|
return true; |
|
|
|
return true; |
|
|
|
} |
|
|
|
} |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
/** |
|
|
|
|
|
|
|
* 飞剑弧高:奇偶上下交错,同向越高阶弧越大。 |
|
|
|
|
|
|
|
* @param dist 射程 |
|
|
|
|
|
|
|
* @param shotIndex 发次 |
|
|
|
|
|
|
|
*/ |
|
|
|
|
|
|
|
private _flySwordArcHeight(dist: number, shotIndex: number): number { |
|
|
|
|
|
|
|
const idx = Math.max(0, Math.floor(shotIndex)); |
|
|
|
|
|
|
|
const base = Math.max(FLY_SWORD_ARC_MIN, dist * FLY_SWORD_ARC_T); |
|
|
|
|
|
|
|
const sign = (idx % 2 === 0) ? 1 : -1; |
|
|
|
|
|
|
|
const tier = Math.floor(idx / 2); |
|
|
|
|
|
|
|
return sign * base * (1 + tier * FLY_SWORD_ARC_TIER); |
|
|
|
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
|
|
launchVolleyShot(caster: EntityId, abilityId: string, target: EntityId, scale: number, buff1: string, buff2: string, shotIndex: number, aimWorld: Readonly<Vec3>): boolean { |
|
|
|
launchVolleyShot(caster: EntityId, abilityId: string, target: EntityId, scale: number, buff1: string, buff2: string, shotIndex: number, aimWorld: Readonly<Vec3>): boolean { |
|
|
|
const castAnim = String(this._castAnimParams?.animation ?? '').trim(); |
|
|
|
const castAnim = String(this._castAnimParams?.animation ?? '').trim(); |
|
|
|
if (this._isStaffWeapon() || this._isStaffProjectileAnim(castAnim)) { |
|
|
|
if (this._isStaffWeapon() || this._isStaffProjectileAnim(castAnim)) { |
|
|
|
|