From 4cb9a14ffefbdfb320251e8443d2e1b7edd92cab Mon Sep 17 00:00:00 2001 From: humingxing <930621236@qq.com> Date: Fri, 9 Oct 2026 11:41:58 +0800 Subject: [PATCH] =?UTF-8?q?=E8=B0=83=E6=8A=80=E8=83=BD?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../battleHero/prefab/skill/水弹.prefab | 6 +- assets/bundles/first/config/Skill.json | 16 +-- .../spine/武器/法杖/冰锥.png | Bin 0 -> 4096 bytes .../spine/武器/法杖/冰锥.png.meta | 134 ++++++++++++++++++ .../spine/武器/长枪/土块溅射.png | Bin 0 -> 3912 bytes .../spine/武器/长枪/土块溅射.png.meta | 134 ++++++++++++++++++ .../config/baseAtk/SkillAbilityBuilder.ts | 16 +-- .../battle/presentation/AtkTypePresenters.ts | 12 +- assets/script/battle/skill/Skill.ts | 78 ++++++++-- .../battle/view/CocosBattleCombatPresenter.ts | 5 +- assets/script/ui/BattleGame/Monster.ts | 66 +++++---- assets/script/ui/BattleGame/UIBattleGame.ts | 22 ++- docs/方案-出怪逻辑实现.md | 2 +- docs/方案-战斗技能逻辑层.md | 6 +- docs/方案-战斗表现数据驱动.md | 2 + 配置表/excelList/技能表.xlsx | Bin 21569 -> 21562 bytes 16 files changed, 437 insertions(+), 62 deletions(-) create mode 100644 assets/res/battleGameWeapon/spine/武器/法杖/冰锥.png create mode 100644 assets/res/battleGameWeapon/spine/武器/法杖/冰锥.png.meta create mode 100644 assets/res/battleGameWeapon/spine/武器/长枪/土块溅射.png create mode 100644 assets/res/battleGameWeapon/spine/武器/长枪/土块溅射.png.meta diff --git a/assets/bundles/battleHero/prefab/skill/水弹.prefab b/assets/bundles/battleHero/prefab/skill/水弹.prefab index 8cdd929..bf71a1b 100644 --- a/assets/bundles/battleHero/prefab/skill/水弹.prefab +++ b/assets/bundles/battleHero/prefab/skill/水弹.prefab @@ -124,8 +124,8 @@ }, "_contentSize": { "__type__": "cc.Size", - "width": 322, - "height": 122 + "width": 139, + "height": 50 }, "_anchorPoint": { "__type__": "cc.Vec2", @@ -161,7 +161,7 @@ "a": 255 }, "_spriteFrame": { - "__uuid__": "d0abd767-3ed4-49ca-b7dd-e295c41cd319@f9941", + "__uuid__": "4ad93541-2be9-43ab-8d9b-6f7fbd86a297@f9941", "__expectedType__": "cc.SpriteFrame" }, "_type": 0, diff --git a/assets/bundles/first/config/Skill.json b/assets/bundles/first/config/Skill.json index 2c73bbc..d5915dd 100644 --- a/assets/bundles/first/config/Skill.json +++ b/assets/bundles/first/config/Skill.json @@ -888,7 +888,7 @@ "level": 0, "cd": 20, "priority": 100, - "animation": "悟空专属技能效果", + "animation": "原地旋转大风车", "showeffect": "", "target_stage": 0, "range": 0, @@ -910,7 +910,7 @@ "level": 0, "cd": 20, "priority": 100, - "animation": "攻击1", + "animation": "悟空专属技能效果", "showeffect": "", "target_stage": 0, "range": 0, @@ -954,7 +954,7 @@ "level": 0, "cd": 20, "priority": 100, - "animation": "悟空专属技能效果", + "animation": "原地旋转大风车", "showeffect": "", "target_stage": 0, "range": 0, @@ -976,7 +976,7 @@ "level": 0, "cd": 20, "priority": 100, - "animation": "攻击1", + "animation": "悟空专属技能效果", "showeffect": "", "target_stage": 0, "range": 0, @@ -1020,7 +1020,7 @@ "level": 0, "cd": 20, "priority": 100, - "animation": "悟空专属技能效果", + "animation": "原地旋转大风车", "showeffect": "", "target_stage": 0, "range": 0, @@ -1042,7 +1042,7 @@ "level": 0, "cd": 20, "priority": 100, - "animation": "攻击1", + "animation": "悟空专属技能效果击1", "showeffect": "", "target_stage": 0, "range": 0, @@ -1086,7 +1086,7 @@ "level": 0, "cd": 20, "priority": 100, - "animation": "悟空专属技能效果", + "animation": "原地旋转大风车", "showeffect": "", "target_stage": 0, "range": 0, @@ -1108,7 +1108,7 @@ "level": 0, "cd": 0, "priority": 100, - "animation": "攻击1", + "animation": "悟空专属技能效果", "showeffect": "", "target_stage": 0, "range": 0, diff --git a/assets/res/battleGameWeapon/spine/武器/法杖/冰锥.png b/assets/res/battleGameWeapon/spine/武器/法杖/冰锥.png new file mode 100644 index 0000000000000000000000000000000000000000..3151db01805f24c161f30cc3cc693466243da5ab GIT binary patch literal 4096 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 Wb5oL`Uyri@0000 { + private static _countIntervalHitParams(row: ITableSkill, atkParts: number[], atkHead: number): Pick { const parsed = this._parseCountIntervalWan(row, atkParts, atkHead); if (!parsed) return {}; - return { hitCount: parsed.hitCount, hitIntervalSec: parsed.intervalSec }; + return { hitCount: parsed.hitCount, hitIntervalSec: parsed.intervalSec, damageWan: parsed.wan }; } /** @@ -664,9 +664,9 @@ export class SkillAbilityBuilder { } // 12 散射 / 13 连发 / 17 不穿透散射 / 19 冰锥:伤害由 BowAtkHost 弹道结算,无 deferred 段 - // 18 / 20 目标圆多段:每段按落点圆重新索敌结算,无 deferred 段 + // 14 360°多段 / 18 / 20:每段按圆重新索敌结算,无 deferred 段 // 飞剑主动(atk_type=10 子集):落地结算,无 deferred 段 - if (atkHead === 12 || atkHead === 13 || atkHead === 17 || atkHead === 18 || atkHead === 19 || atkHead === 20 + if (atkHead === 12 || atkHead === 13 || atkHead === 14 || atkHead === 17 || atkHead === 18 || atkHead === 19 || atkHead === 20 || isFlySwordSkillId(row.id)) { return []; } @@ -675,9 +675,9 @@ export class SkillAbilityBuilder { return []; } - if (atkHead === 5 || atkHead === 10 || atkHead === 14 || atkHead === 16) { + if (atkHead === 5 || atkHead === 10 || atkHead === 16) { let hitCount = Math.max(1, Math.floor(atkParts[2] || 1)); - if (atkHead === 10 || atkHead === 14) { + if (atkHead === 10) { const parsed = this._parseCountIntervalWan(row, atkParts, atkHead); if (!parsed) return []; hitCount = parsed.hitCount; diff --git a/assets/script/battle/presentation/AtkTypePresenters.ts b/assets/script/battle/presentation/AtkTypePresenters.ts index 2707300..2cd82c3 100644 --- a/assets/script/battle/presentation/AtkTypePresenters.ts +++ b/assets/script/battle/presentation/AtkTypePresenters.ts @@ -63,6 +63,7 @@ export class LineRectAtkPresenter implements ISkillPresenter { /** * atk_type=5 直线多段 / 14 360°多段 / 16 扇形多段:按 hitIntervalSec 多次 hit,每段独立结算范围内伤害。 + * atk_type=14:武器动画循环至全部段结束(与伤害时长同步,如原地旋转大风车)。 */ export class LineMultiHitAtkPresenter implements ISkillPresenter { present(ctx: SkillPresentationContext, host: Skill): void { @@ -72,19 +73,28 @@ export class LineMultiHitAtkPresenter implements ISkillPresenter { } const skillKind = String(ctx.params.skillKind ?? ''); const followWeapon = skillKind === 'weapon' || host.visualChannel.useWeapon; + const atkHead = Math.floor(Number(ctx.params.atkTypeHead ?? ctx.params.atkType) || 0); + const loopCast = atkHead === 14; const seq = host.beginPresentationCast(ctx); if (!(seq > 0)) return; + if (loopCast) host.disarmAnimComplete(); const animTs = host.visualChannel.playCastAnim({ caster: ctx.caster, animation: String(ctx.params.animation ?? ''), followWeapon, skillKind, primaryTarget: ctx.targets[0] ?? null, - onAnimComplete: host.onPresentationAnimCompleteBound, + holdCasting: loopCast, + loopWeapon: loopCast, + onAnimComplete: loopCast + ? LineMultiHitAtkPresenter._noopAnimComplete + : host.onPresentationAnimCompleteBound, }); const firstDelay = PRES_HIT_AT_BASE / Math.max(0.01, animTs); host.schedulePresentationMultiHit(seq, ctx, firstDelay); } + + private static _noopAnimComplete = (): void => {}; } /** diff --git a/assets/script/battle/skill/Skill.ts b/assets/script/battle/skill/Skill.ts index 35b2e5b..58ca5f2 100644 --- a/assets/script/battle/skill/Skill.ts +++ b/assets/script/battle/skill/Skill.ts @@ -28,7 +28,7 @@ import { SkillPresenterRouter } from '../presentation/SkillPresenterRouter'; import { getAllySkillExtraHost } from '../presentation/AllySkillExtraHost'; import { getFaXiangHost } from '../presentation/FaXiangHost'; import { SkillVisualChannel } from '../presentation/SkillVisualChannel'; -import { getGroundCircleHost } from '../presentation/ThrownCircleAtkPresenter'; +import { getGroundCircleHost, logicPosOf, pulseHostileCircle } from '../presentation/ThrownCircleAtkPresenter'; import { getStaffSmashHost } from '../presentation/StaffSmashPresenter'; import { SkillPresentationEnv } from './SkillPresentationEnv'; import { SkillShowEffectPlayer } from './SkillShowEffect'; @@ -471,6 +471,10 @@ export class Skill extends Component { private _presMultiHitSeq = 0; private _presMultiHitIndex = 0; private _presMultiHitCtx: SkillPresentationContext | null = null; + /** atk_type=14 当前脉冲用的表现上下文 */ + private _presCirclePulseCtx: SkillPresentationContext | null = null; + /** atk_type=14 当前脉冲伤害倍率 */ + private _presCirclePulseScale = 0; /** 12 散射 / 13 连发 */ private _presBowShape = 0; /** 散射是否已出手 */ @@ -504,22 +508,21 @@ export class Skill extends Component { private _onPresMultiHitTick = (): void => { const seq = this._presMultiHitSeq; const ctx = this._presMultiHitCtx; - if (!ctx || !this.isPresentationCast(seq) || !ctx.isAlive) return; + if (!ctx || !this.isPresentationCast(seq) || !ctx.isAlive) { + this._finishMultiHitCast(seq, ctx); + return; + } const atkHead = Math.floor(Number(ctx.params.atkTypeHead ?? ctx.params.atkType) || 0); if (atkHead === 10 && !this._prepareSequentialSmash(ctx)) { - this._presMultiHitCtx = null; - getStaffSmashHost().stopSpawning(ctx.caster); - if (this._presAnimSeq === seq && !this._presAnimComplete) return; - this.finishPresentationCast(seq, false); + this._finishMultiHitCast(seq, ctx, true); return; } - ctx.hit(); + if (atkHead === 14) this._pulseCasterCircleHit(ctx); + else ctx.hit(); const hitCount = Math.max(1, Math.floor(Number(ctx.params.hitCount) || 1)); const next = this._presMultiHitIndex + 1; if (next >= hitCount) { - this._presMultiHitCtx = null; - if (this._presAnimSeq === seq && !this._presAnimComplete) return; - this.finishPresentationCast(seq, false); + this._finishMultiHitCast(seq, ctx); return; } this._presMultiHitIndex = next; @@ -527,6 +530,61 @@ export class Skill extends Component { this.schedulePresentation(this._onPresMultiHitTick, interval); }; + /** + * atk_type=14:以施法者为圆心、射程为半径,本段重新索敌并结算。 + * @param ctx 表现上下文 + */ + private _pulseCasterCircleHit(ctx: SkillPresentationContext): void { + const session = SkillPresentationEnv.getBattleSession(); + if (!session || !session.world.isAlive(ctx.caster)) return; + const center = logicPosOf(session, ctx.caster); + if (!center) { + console.log('[Skill] atk_type=14 无释放点', ctx.params.skillId); + return; + } + let radius = Math.max(0, Number(ctx.params.stageRangePx) || 0); + if (!(radius > 0)) { + radius = Math.max(0, session.attributes.getAttr(ctx.caster, AttrId.Range) || 0); + } + if (!(radius > 0)) { + console.log('[Skill] atk_type=14 无有效射程', ctx.params.skillId); + return; + } + const wan = Number(ctx.params.damageWan); + if (!Number.isFinite(wan)) { + console.log('[Skill] atk_type=14 伤害万分比无效', ctx.params.skillId, ctx.params.atkPara); + return; + } + this._presCirclePulseCtx = ctx; + this._presCirclePulseScale = wan / 10000; + pulseHostileCircle(session, ctx.caster, center.x, center.y, radius, this._onCasterCircleHit); + this._presCirclePulseCtx = null; + } + + private readonly _onCasterCircleHit = (id: EntityId): void => { + const ctx = this._presCirclePulseCtx; + if (!ctx?.isAlive) return; + ctx.dealDamage(id, { scale: this._presCirclePulseScale, damageType: 'caster_circle' }); + }; + + /** + * 多段命中结束:atk_type=14 先停循环武器动画再收招。 + * @param seq 播控序号 + * @param ctx 表现上下文(可空) + * @param stopSmash 是否停连砸生成 + */ + private _finishMultiHitCast(seq: number, ctx: SkillPresentationContext | null, stopSmash = false): void { + const atkHead = ctx + ? Math.floor(Number(ctx.params.atkTypeHead ?? ctx.params.atkType) || 0) + : 0; + this._presMultiHitCtx = null; + if (stopSmash && ctx) getStaffSmashHost().stopSpawning(ctx.caster); + if (atkHead === 14) this.visualChannel.endHoldCast(); + if (!this.isPresentationCast(seq)) return; + if (this._presAnimSeq === seq && !this._presAnimComplete) return; + this.finishPresentationCast(seq, false); + } + /** * atk_type=10:本段只打一个存活单体。无存活目标返回 false。 * @param ctx 表现上下文 diff --git a/assets/script/battle/view/CocosBattleCombatPresenter.ts b/assets/script/battle/view/CocosBattleCombatPresenter.ts index 4d14582..9ed3cc5 100644 --- a/assets/script/battle/view/CocosBattleCombatPresenter.ts +++ b/assets/script/battle/view/CocosBattleCombatPresenter.ts @@ -48,6 +48,7 @@ type MonsterView = IBattleMonsterView & { clearSlowFx?(): void; showFreezeFx?(durationSec: number): void; clearFreezeFx?(): void; + clearFreezeFxImmediate?(): void; showBurnFx?(durationSec: number): void; clearBurnFx?(): void; showBleedFx?(durationSec: number): void; @@ -223,7 +224,7 @@ export class CocosBattleCombatPresenter { case BattleEventType.EntityKilled: this._onStunRemoved(event.victim); this._onSlowRemoved(event.victim); - this._onFreezeRemoved(event.victim); + this._getMonsterView(event.victim)?.clearFreezeFxImmediate?.(); this._onBurnRemoved(event.victim); this._onBleedRemoved(event.victim); this._onGhostBiteRemoved(event.victim); @@ -232,7 +233,7 @@ export class CocosBattleCombatPresenter { case BattleEventType.UnitDespawned: this._onStunRemoved(event.entity); this._onSlowRemoved(event.entity); - this._onFreezeRemoved(event.entity); + this._getMonsterView(event.entity)?.clearFreezeFxImmediate?.(); this._onBurnRemoved(event.entity); this._onBleedRemoved(event.entity); this._onGhostBiteRemoved(event.entity); diff --git a/assets/script/ui/BattleGame/Monster.ts b/assets/script/ui/BattleGame/Monster.ts index a6414b1..a226ada 100644 --- a/assets/script/ui/BattleGame/Monster.ts +++ b/assets/script/ui/BattleGame/Monster.ts @@ -873,15 +873,25 @@ export class Monster extends Component { }; /** - * 显示冰冻覆盖(prefab/冰冻),挂怪体播「怪物冻结」;按时长或 clear 时播「怪物冻结2」再回收。 - * @param durationSec 持续秒 + * 显示冰冻覆盖(prefab/冰冻):定格「怪物冻结」首帧冰块;刷新时长不重播,避免闪。 + * 解冻由 clearFreezeFx 播「怪物冻结2」。 + * @param _durationSec 持续秒(表现由 BuffRemoved 驱动结束,参数仅兼容调用方) */ - showFreezeFx(durationSec: number): void { + showFreezeFx(_durationSec: number): void { if (!this.node?.isValid || this.IsDead) return; const parent = this.skin?.isValid ? this.skin : this.node; + + if (this._freezeFx?.isValid && !this._freezeFxThawing) { + if (this._freezeFx.parent !== parent) { + this._freezeFx.parent = parent; + this._freezeFx.setPosition(0, 0, 0); + } + this._freezeFx.active = true; + return; + } + ++this._freezeFxSeq; this._freezeFxThawing = false; - this.unschedule(this._onFreezeFxTimeout); if (!this._freezeFx?.isValid) { let node: Node | null = null; @@ -899,27 +909,24 @@ export class Monster extends Component { node.setPosition(0, 0, 0); node.active = true; this._freezeFx = node; - } else if (this._freezeFx.parent !== parent) { + } else { this._freezeFx.parent = parent; this._freezeFx.setPosition(0, 0, 0); this._freezeFx.active = true; } const sk = this._freezeFx.getComponentInChildren(sp.Skeleton); - if (sk?.isValid) { - sk.setAnimationCacheMode(sp.SpineAnimationCacheMode.SHARED_CACHE); - BattleSpinePlayback.play(sk, FREEZE_FX_ANIM, { loop: true }); - } - - const dur = Math.max(0.05, durationSec); - this.scheduleOnce(this._onFreezeFxTimeout, dur); + if (sk?.isValid) this._holdFreezeIcePose(sk); } /** - * 解冻:播「怪物冻结2」后回池;无节点则直接结束。 + * 解冻:播「怪物冻结2」破冰后回池;死亡则立即回收。 */ clearFreezeFx(): void { - this.unschedule(this._onFreezeFxTimeout); + if (this.IsDead) { + this.clearFreezeFxImmediate(); + return; + } if (this._freezeFxThawing) return; const node = this._freezeFx; if (!node?.isValid) { @@ -933,33 +940,39 @@ export class Monster extends Component { return; } this._freezeFxThawing = true; - const seq = ++this._freezeFxSeq; + ++this._freezeFxSeq; + // 附件切换动画须 REALTIME;破冰前恢复倍速 + sk.setAnimationCacheMode(sp.SpineAnimationCacheMode.REALTIME); + sk.timeScale = BattleSpinePlayback.resolveCombinedTimeScale(1); const ok = BattleSpinePlayback.play(sk, FREEZE_THAW_ANIM, { loop: false, onComplete: this._onFreezeThawComplete, }); - if (!ok) { - if (seq !== this._freezeFxSeq) return; - this._dropFreezeFx(); - } + if (!ok) this._dropFreezeFx(); } /** 立即回池冰冻特效(死亡/清池,不解冻动画) */ clearFreezeFxImmediate(): void { this._freezeFxSeq++; - this.unschedule(this._onFreezeFxTimeout); this._dropFreezeFx(); } - private _onFreezeFxTimeout = (): void => { - this.clearFreezeFx(); - }; - private readonly _onFreezeThawComplete = (): void => { if (!this._freezeFxThawing) return; this._dropFreezeFx(); }; + /** + * 「怪物冻结」末帧会卸附件;定格首帧保持冰块,禁止 loop。 + * @param sk 冰冻 Spine + */ + private _holdFreezeIcePose(sk: sp.Skeleton): void { + BattleSpinePlayback.clearPendingComplete(sk); + sk.setAnimationCacheMode(sp.SpineAnimationCacheMode.REALTIME); + sk.setAnimation(0, FREEZE_FX_ANIM, false); + sk.timeScale = 0; + } + /** 回池并清空冰冻节点引用 */ private _dropFreezeFx(): void { this._freezeFxThawing = false; @@ -968,7 +981,10 @@ export class Monster extends Component { if (!node?.isValid) return; const sk = node.getComponentInChildren(sp.Skeleton); BattleSpinePlayback.clearPendingComplete(sk); - BattleSpinePlayback.resetTracks(sk); + if (sk?.isValid) { + sk.timeScale = 1; + BattleSpinePlayback.resetTracks(sk); + } gg.res.putNode(node); } diff --git a/assets/script/ui/BattleGame/UIBattleGame.ts b/assets/script/ui/BattleGame/UIBattleGame.ts index 79017f6..57f1f3e 100644 --- a/assets/script/ui/BattleGame/UIBattleGame.ts +++ b/assets/script/ui/BattleGame/UIBattleGame.ts @@ -81,6 +81,8 @@ const REFRESH_FILL_RANGE_MIN = -0.1; const REFRESH_BOUNCE_AMP = 0.1; /** 单段弹起/回落时长(秒) */ const REFRESH_BOUNCE_SEC = 0.22; +/** 开局未放置数字则强制开怪的等待秒数 */ +const FIRST_PLACE_COMBAT_TIMEOUT_SEC = 10; /** 砚台 Spine:资源里名字取反,出字过程播「待机」 */ const INKSTONE_SPINE_SPAWN = '待机'; @@ -440,7 +442,9 @@ export class UIBattleGame extends Auto_BattleGame { this._refreshWaveUi(); } - /** 开局不立刻出怪:等本机玩家第一次把数字放到地图后再 startCombat */ + /** + * 开局不立刻出怪:本机首次放数字开怪,或超时后强制开怪。 + */ private _listenFirstPlaceCombat(): void { this._unbindFirstPlaceCombat(); const bag = this._battle?.bagSession; @@ -449,10 +453,12 @@ export class UIBattleGame extends Auto_BattleGame { return; } bag.on(MergeEventType.TilePlaced, this._onFirstPlayerNumberPlaced); + this.scheduleOnce(this._onFirstPlaceCombatTimeout, FIRST_PLACE_COMBAT_TIMEOUT_SEC); } - /** 卸掉首次放字开怪监听 */ + /** 卸掉首次放字开怪监听与超时 */ private _unbindFirstPlaceCombat(): void { + this.unschedule(this._onFirstPlaceCombatTimeout); this._battle?.bagSession?.off(MergeEventType.TilePlaced, this._onFirstPlayerNumberPlaced); } @@ -465,6 +471,18 @@ export class UIBattleGame extends Auto_BattleGame { if (event.type !== MergeEventType.TilePlaced) return; const tileType = Math.floor(Number(WeaponComposeConfigProvider.instance.getWeapon2Type(event.weapon2Id))); if (!isElementGlyphType(tileType)) return; + this._startCombatFromFirstPlaceGate(); + }; + + /** 超时仍未放数字:强制开怪 */ + private readonly _onFirstPlaceCombatTimeout = (): void => { + if (this._combatStarted) return; + this._startCombatFromFirstPlaceGate(); + }; + + /** 首次放字 / 超时共用:开波并卸监听 */ + private _startCombatFromFirstPlaceGate(): void { + if (this._combatStarted) return; this._unbindFirstPlaceCombat(); this._combatStarted = true; gg.battle.startCombat(); diff --git a/docs/方案-出怪逻辑实现.md b/docs/方案-出怪逻辑实现.md index ac7018e..6b6ec93 100644 --- a/docs/方案-出怪逻辑实现.md +++ b/docs/方案-出怪逻辑实现.md @@ -103,7 +103,7 @@ UIPanelHome ← pickRandomBattleLevelId 进关 2. `GameController.enterBattle` → 预载 → `gg.battle.start` 3. `ChapterLevelBuilder.build` → `LevelWaveParser.toWaveDefs` 4. `UIBattleGame` → `loadMap` → `applyMapTopologyToSession(..., 'pvp')` -5. `CocosBattleMonsterPresenter.bind`;**本机玩家第一次把数字放到地图后** `startCombat` → 双口出怪 + 寻路表现 +5. `CocosBattleMonsterPresenter.bind`;**本机玩家第一次把数字放到地图后**,或开局 **10 秒仍未放置**,`startCombat` → 双口出怪 + 寻路表现 ## 4. 默认参数 diff --git a/docs/方案-战斗技能逻辑层.md b/docs/方案-战斗技能逻辑层.md index d80b932..a8098bf 100644 --- a/docs/方案-战斗技能逻辑层.md +++ b/docs/方案-战斗技能逻辑层.md @@ -109,7 +109,7 @@ Hero(合成等级 level) ### atk_type 14 360° 多段 -圈半径=`Skill.range`(≤0 则塔射程)。`atk_para`=`段数,间隔秒,伤害万分比`(例 `5,0.2,5000` = 5 段、间隔 0.2s、每段 50%)。开战锁圈内敌,每段 `hit()` 结算一段。有 `showeffect` 也只播预兆,**多段伤害出手即按 `PRES_HIT_AT_BASE` 起跳**,不等预兆。 +圈半径=`Skill.range`(≤0 则塔射程)。`atk_para`=`段数,间隔秒,伤害万分比`(例 `5,0.2,5000` = 5 段、间隔 0.2s、每段 50%)。**无 deferred 段**;开战仅作施法门槛,**每段伤害以施法者为圆心 `pulseHostileCircle` 重新索敌**后 `dealDamage`(含 `atk_buff`)。有 `showeffect` 也只播预兆,**多段伤害出手即按 `PRES_HIT_AT_BASE` 起跳**,不等预兆。**武器动画 `loopWeapon` 循环至全部段结束**(与伤害时长同步,如棍神通「原地旋转大风车」),段尽 `endHoldCast` 收招。 ### atk_type 10 连续多个单体 @@ -140,7 +140,7 @@ Hero(合成等级 level) ### atk_buff 13 冰冻 -参数:`概率,冰冻秒`。挂 `debuff_13`(定身 + 禁施法)。表现用 `冰冻` 预制体覆盖怪物播「怪物冻结」,解冻播「怪物冻结2」并飘「冰冻」。部署 loadout 不施加。 +参数:`概率,冰冻秒`。挂 `debuff_13`(定身 + 禁施法)。表现用 `冰冻` 预制体覆盖怪物:REALTIME 定格「怪物冻结」首帧冰块(不 loop,避免末帧卸附件闪烁);`BuffRemoved` 时播「怪物冻结2」破冰再回池;飘「冰冻」。部署 loadout 不施加。 ### atk_buff 14 点燃 @@ -199,3 +199,5 @@ Hero(合成等级 level) | 2026-10-08 | atk_type=18:丢出 `飞棍.prefab`(按半径缩放),落地后持续圈伤 | | 2026-10-08 | atk_type=20:主目标圆心放置 `暴风雪.prefab`;占锁并循环播「暴风雪技能」至圈伤结束 | | 2026-10-09 | 冰锥发射物改「冰锥」;冰冻覆盖「冰冻」播怪物冻结/解冻怪物冻结2 | +| 2026-10-09 | atk_type=14:武器动画循环至多段伤害结束(棍神通大风车与时长同步) | +| 2026-10-09 | atk_type=14:每段按施法者射程圆重新索敌,非开战锁定名单 | diff --git a/docs/方案-战斗表现数据驱动.md b/docs/方案-战斗表现数据驱动.md index d39ea7f..aca2643 100644 --- a/docs/方案-战斗表现数据驱动.md +++ b/docs/方案-战斗表现数据驱动.md @@ -144,6 +144,8 @@ M| 1 / 3 / 6 / 7 / 15 | 通道播动画 → drain hit(15:逻辑已锁主目 | 2026-09-30 | 补注册 atk_type=11 召唤(10051~10055)→ SummonAtkPresenter;表内 1~17 表现已全覆盖,仅 9(将武器形 30406)按兜底结算待策划定义攻效 | | 2026-10-08 | atk_type=19 冰锥 → IceConeAtkPresenter(水弹直线落地);18/20 已有独立 Presenter | | 2026-10-09 | atk_type=19 / 分裂冰锥发射物改「冰锥」;debuff_13 用「冰冻」覆盖播怪物冻结/解冻 | +| 2026-10-09 | atk_type=14:holdCasting+loopWeapon 至多段结束(棍神通「原地旋转大风车」) | +| 2026-10-09 | atk_type=14:每段 `pulseHostileCircle` 重扫施法者射程圆结算 | | 2026-10-08 | 飞剑主动 skillId 210101~250101 → FlySwordAtkPresenter(飞剑.prefab 落地伤);同 atk_type=10 的飞棍仍 StaffSmash | | 2026-10-08 | atk_type=18 ThrownCircle:改丢 `飞棍.prefab`(半径缩放=半径/175),落地后圈伤 | | 2026-10-08 | atk_type=20 GroundCircle:主目标圆心放置 `暴风雪.prefab`;占锁循环「暴风雪技能」至圈伤结束 | diff --git a/配置表/excelList/技能表.xlsx b/配置表/excelList/技能表.xlsx index 1d373d9be7cf1ed61fbaa39e1d8187a0e1817e45..17648cec2bf8c9af6f2ce645e83f1f1171467b29 100644 GIT binary patch delta 13391 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

pVM-7)HPwWNx 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