From d3013b10539e44a8ff38e7e655110b0abe2d55e0 Mon Sep 17 00:00:00 2001 From: Joywayer Date: Thu, 30 Jul 2026 16:39:57 +0800 Subject: [PATCH] =?UTF-8?q?refactor(enemy)!:=20=E5=88=A0=E9=99=A4=E6=97=A7?= =?UTF-8?q?=20Boss=20=E8=BD=A8=209=20=E4=B8=AA=E8=84=9A=E6=9C=AC?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit BossSkillSO / AttackPatternSO / SkillSequenceSO / BossSkillExecutor / BossSkillTypes / WeakPointSystem / TelegraphSystem / BossSkillModule / BossSkillSequenceWindow,连同 Patterns 独立程序集(BaseGames.Enemies.Boss.Patterns.asmdef, 已核实无任何 asmdef 按名或按 GUID 引用它)整包移除。 删前先清两处引用: - ChaoFengKnockdownCounter 击落打断改走 _boss.Abilities.InterruptAll(ExternalRequest) - EditorScaffoldUtils 命名前缀表删掉两条指向已删类型的死条目(还与 AssetFolderSpec 的 ABL_ 冲突) 判据「有更优替代才删」逐项对应: - 编排 → EnemyAbilitySO + EnemyAttackSO(归一化时机 / 槽位 HitBox) - 阶段门 → BossPhaseAbilityGate - 选招 → EnemyAttackSelector + WeightedRandomAntiRepeat - 可弹反 → DamageSourceSO.Flags(CanBeParried),HurtBox 已在判定 - 弱点倍率 → HurtBox 自带倍率(原实现的 GetDamageMultiplier 零调用者) - 预警 → clip 姿态 + 动画事件(TriggerFeedback / PlaySFX 已补接) - 竞技场联动 → EnemySpawnerOnEvent - 霸体窗口 → EnemyAttackSO.hasPoiseWindow 四字段 能力资产的编辑器总览视图随 BossSkillModule 一并消失, 后续补 EnemyAbilityModule(优先级低于 AiDefinitionValidator)。 Co-Authored-By: Claude Opus 5 (1M context) --- .../Editor/Enemies/BossSkillSequenceWindow.cs | 306 --------------- .../Enemies/BossSkillSequenceWindow.cs.meta | 11 - .../Scripts/Editor/Modules/BossSkillModule.cs | 209 ---------- .../Editor/Modules/BossSkillModule.cs.meta | 11 - .../Editor/Shared/EditorScaffoldUtils.cs | 2 - .../Scripts/Enemies/Boss/AttackPatternSO.cs | 33 -- .../Enemies/Boss/AttackPatternSO.cs.meta | 11 - .../Scripts/Enemies/Boss/BossSkillExecutor.cs | 359 ------------------ .../Enemies/Boss/BossSkillExecutor.cs.meta | 11 - .../_Game/Scripts/Enemies/Boss/BossSkillSO.cs | 69 ---- .../Scripts/Enemies/Boss/BossSkillSO.cs.meta | 11 - .../Scripts/Enemies/Boss/BossSkillTypes.cs | 184 --------- .../Enemies/Boss/BossSkillTypes.cs.meta | 11 - .../Enemies/Boss/ChaoFengKnockdownCounter.cs | 4 +- .../_Game/Scripts/Enemies/Boss/Patterns.meta | 8 - .../BaseGames.Enemies.Boss.Patterns.asmdef | 19 - ...aseGames.Enemies.Boss.Patterns.asmdef.meta | 7 - .../Enemies/Boss/Patterns/TelegraphSystem.cs | 50 --- .../Boss/Patterns/TelegraphSystem.cs.meta | 11 - .../Scripts/Enemies/Boss/SkillSequenceSO.cs | 26 -- .../Enemies/Boss/SkillSequenceSO.cs.meta | 11 - .../Scripts/Enemies/Boss/WeakPointSystem.cs | 51 --- .../Enemies/Boss/WeakPointSystem.cs.meta | 11 - zeling_v2.sln | 18 +- 24 files changed, 9 insertions(+), 1435 deletions(-) delete mode 100644 Assets/_Game/Scripts/Editor/Enemies/BossSkillSequenceWindow.cs delete mode 100644 Assets/_Game/Scripts/Editor/Enemies/BossSkillSequenceWindow.cs.meta delete mode 100644 Assets/_Game/Scripts/Editor/Modules/BossSkillModule.cs delete mode 100644 Assets/_Game/Scripts/Editor/Modules/BossSkillModule.cs.meta delete mode 100644 Assets/_Game/Scripts/Enemies/Boss/AttackPatternSO.cs delete mode 100644 Assets/_Game/Scripts/Enemies/Boss/AttackPatternSO.cs.meta delete mode 100644 Assets/_Game/Scripts/Enemies/Boss/BossSkillExecutor.cs delete mode 100644 Assets/_Game/Scripts/Enemies/Boss/BossSkillExecutor.cs.meta delete mode 100644 Assets/_Game/Scripts/Enemies/Boss/BossSkillSO.cs delete mode 100644 Assets/_Game/Scripts/Enemies/Boss/BossSkillSO.cs.meta delete mode 100644 Assets/_Game/Scripts/Enemies/Boss/BossSkillTypes.cs delete mode 100644 Assets/_Game/Scripts/Enemies/Boss/BossSkillTypes.cs.meta delete mode 100644 Assets/_Game/Scripts/Enemies/Boss/Patterns.meta delete mode 100644 Assets/_Game/Scripts/Enemies/Boss/Patterns/BaseGames.Enemies.Boss.Patterns.asmdef delete mode 100644 Assets/_Game/Scripts/Enemies/Boss/Patterns/BaseGames.Enemies.Boss.Patterns.asmdef.meta delete mode 100644 Assets/_Game/Scripts/Enemies/Boss/Patterns/TelegraphSystem.cs delete mode 100644 Assets/_Game/Scripts/Enemies/Boss/Patterns/TelegraphSystem.cs.meta delete mode 100644 Assets/_Game/Scripts/Enemies/Boss/SkillSequenceSO.cs delete mode 100644 Assets/_Game/Scripts/Enemies/Boss/SkillSequenceSO.cs.meta delete mode 100644 Assets/_Game/Scripts/Enemies/Boss/WeakPointSystem.cs delete mode 100644 Assets/_Game/Scripts/Enemies/Boss/WeakPointSystem.cs.meta diff --git a/Assets/_Game/Scripts/Editor/Enemies/BossSkillSequenceWindow.cs b/Assets/_Game/Scripts/Editor/Enemies/BossSkillSequenceWindow.cs deleted file mode 100644 index 70b5fe72..00000000 --- a/Assets/_Game/Scripts/Editor/Enemies/BossSkillSequenceWindow.cs +++ /dev/null @@ -1,306 +0,0 @@ -using System.Collections.Generic; -using UnityEditor; -using UnityEngine; -using BaseGames.Boss; - -namespace BaseGames.Editor -{ - /// - /// Boss 技能序列甘特图可视化窗口(架构 23_BossSkillModule §12)。 - /// 菜单:BaseGames/Tools/Boss Skill Sequence Viewer - /// - /// 功能: - /// - 拖放 BossSkillSO 或 SkillSequenceSO 资产加载 - /// - 甘特图:Windup(黄色)→ Active(红色)→ Recovery(灰色)各阶段时序条 - /// - VulnerabilityWindow 绿色覆盖层(TriggerDelay 偏移 + Duration 宽度) - /// - DurationNormalized < 0.1 时阶段条变红警告 - /// - 点击阶段条高亮对应 AttackPatternSO(EditorGUIUtility.PingObject) - /// - public class BossSkillSequenceWindow : EditorWindow - { - // ── State ────────────────────────────────────────────────────────── - private BossSkillSO _loadedSkill; - private SkillSequenceSO _loadedSequence; - private Vector2 _scrollPos; - - // ── Layout ───────────────────────────────────────────────────────── - private const float HeaderH = 24f; - private const float RowH = 28f; - private const float LabelW = 180f; - private const float MinBarWidth = 6f; - // 时间轴宽度随窗口宽度动态调整,最小 300px - private float TimelineW => Mathf.Max(300f, position.width - LabelW - 30f); - - // ── Colors ───────────────────────────────────────────────────────── - private static readonly Color ColWindup = new Color(0.95f, 0.80f, 0.10f, 0.85f); - private static readonly Color ColActive = new Color(0.90f, 0.20f, 0.15f, 0.85f); - private static readonly Color ColRecovery = new Color(0.50f, 0.50f, 0.55f, 0.70f); - private static readonly Color ColVuln = new Color(0.10f, 0.90f, 0.30f, 0.45f); - private static readonly Color ColDelay = new Color(0.25f, 0.25f, 0.30f, 0.50f); - private static readonly Color ColWarn = new Color(0.95f, 0.10f, 0.10f, 0.85f); - - [MenuItem("BaseGames/Data/Boss Skill Sequence", priority = 110)] - public static void OpenWindow() - { - var win = GetWindow("Boss Skill Sequence"); - win.minSize = new Vector2(900, 400); - win.Show(); - } - - // ── GUI ──────────────────────────────────────────────────────────── - - private void OnGUI() - { - DrawToolbar(); - - if (_loadedSkill == null && _loadedSequence == null) - { - EditorGUILayout.HelpBox( - "将 BossSkillSO 或 SkillSequenceSO 资产拖放到此处,或使用上方字段加载。", - MessageType.Info); - HandleDragDrop(); - return; - } - - _scrollPos = EditorGUILayout.BeginScrollView(_scrollPos); - - if (_loadedSkill != null) - DrawSkillTimeline(_loadedSkill); - else if (_loadedSequence != null) - DrawSequenceTimeline(_loadedSequence); - - EditorGUILayout.EndScrollView(); - } - - // ── Toolbar ─────────────────────────────────────────────────────── - - private void DrawToolbar() - { - EditorGUILayout.BeginHorizontal(EditorStyles.toolbar); - - EditorGUILayout.LabelField("技能:", GUILayout.Width(36)); - var newSkill = (BossSkillSO)EditorGUILayout.ObjectField( - _loadedSkill, typeof(BossSkillSO), false, GUILayout.Width(200)); - if (newSkill != _loadedSkill) - { - _loadedSkill = newSkill; - _loadedSequence = null; - } - - GUILayout.Space(12); - EditorGUILayout.LabelField("序列:", GUILayout.Width(36)); - var newSeq = (SkillSequenceSO)EditorGUILayout.ObjectField( - _loadedSequence, typeof(SkillSequenceSO), false, GUILayout.Width(200)); - if (newSeq != _loadedSequence) - { - _loadedSequence = newSeq; - _loadedSkill = null; - } - - GUILayout.FlexibleSpace(); - - if (GUILayout.Button("清除", EditorStyles.toolbarButton, GUILayout.Width(50))) - { - _loadedSkill = null; - _loadedSequence = null; - } - - EditorGUILayout.EndHorizontal(); - } - - // ── BossSkillSO 时间轴 ──────────────────────────────────────────── - - private void DrawSkillTimeline(BossSkillSO skill) - { - EditorGUILayout.LabelField($"技能:{skill.displayName} [{skill.skillId}]", - EditorStyles.boldLabel); - EditorGUILayout.Space(4); - - if (skill.attackPatterns == null || skill.attackPatterns.Length == 0) - { - EditorGUILayout.HelpBox("此技能没有 AttackPattern。", MessageType.Warning); - return; - } - - // 计算总时长 - float totalDuration = 0f; - foreach (var p in skill.attackPatterns) - if (p != null) totalDuration += p.WindupDuration + p.ActiveDuration + p.RecoveryDuration; - - if (totalDuration <= 0f) totalDuration = 1f; - - DrawTimelineHeader(totalDuration); - - float cursor = 0f; - for (int i = 0; i < skill.attackPatterns.Length; i++) - { - var pattern = skill.attackPatterns[i]; - if (pattern == null) continue; - DrawPatternRow($"[{i}] {pattern.name}", pattern, ref cursor, totalDuration); - } - - // 绘制 VulnerabilityWindows - if (skill.vulnerabilityWindows != null && skill.vulnerabilityWindows.Length > 0) - { - EditorGUILayout.Space(4); - EditorGUILayout.LabelField("弱点窗口(Vulnerability Windows)", EditorStyles.miniBoldLabel); - foreach (var vw in skill.vulnerabilityWindows) - DrawVulnWindowRow(vw, totalDuration); - } - } - - // ── SkillSequenceSO 时间轴 ──────────────────────────────────────── - - private void DrawSequenceTimeline(SkillSequenceSO sequence) - { - EditorGUILayout.LabelField($"序列:{sequence.name}", EditorStyles.boldLabel); - EditorGUILayout.Space(4); - - if (sequence.steps == null || sequence.steps.Length == 0) - { - EditorGUILayout.HelpBox("此序列没有步骤。", MessageType.Warning); - return; - } - - // 计算总时长 - float totalDuration = 0f; - foreach (var step in sequence.steps) - { - totalDuration += step.delayBeforeStep; - if (step.pattern != null) - totalDuration += step.pattern.WindupDuration + step.pattern.ActiveDuration + step.pattern.RecoveryDuration; - } - if (totalDuration <= 0f) totalDuration = 1f; - - DrawTimelineHeader(totalDuration); - - float cursor = 0f; - for (int i = 0; i < sequence.steps.Length; i++) - { - var step = sequence.steps[i]; - - // 延迟条 - if (step.delayBeforeStep > 0f) - { - DrawBar($"延迟 {step.delayBeforeStep:F2}s", cursor, step.delayBeforeStep, - totalDuration, ColDelay, null); - cursor += step.delayBeforeStep; - } - - if (step.pattern != null) - DrawPatternRow($"[{i}] {step.pattern.name}", step.pattern, ref cursor, totalDuration); - } - } - - // ── 共用绘制方法 ────────────────────────────────────────────────── - - private void DrawTimelineHeader(float totalDuration) - { - Rect headerRect = EditorGUILayout.GetControlRect(false, HeaderH); - headerRect.x += LabelW; - headerRect.width -= LabelW; - - EditorGUI.DrawRect(headerRect, new Color(0.18f, 0.18f, 0.20f)); - - // 刻度线(每 0.5s 一条) - float step = 0.5f; - for (float t = 0; t <= totalDuration + 0.001f; t += step) - { - float x = headerRect.x + (t / totalDuration) * headerRect.width; - EditorGUI.DrawRect(new Rect(x, headerRect.y, 1f, HeaderH * 0.6f), Color.gray); - EditorGUI.LabelField(new Rect(x + 2f, headerRect.y, 40f, HeaderH), - $"{t:F1}s", new GUIStyle(EditorStyles.miniLabel) { normal = { textColor = Color.gray } }); - } - } - - private void DrawPatternRow(string label, AttackPatternSO pattern, ref float cursor, float totalDuration) - { - float windupDur = pattern.WindupDuration; - float activeDur = pattern.ActiveDuration; - float recoveryDur = pattern.RecoveryDuration; - - float rowStart = cursor; - EditorGUILayout.BeginHorizontal(GUILayout.Height(RowH)); - - // 标签 + Ping - if (GUILayout.Button(label, EditorStyles.miniLabel, GUILayout.Width(LabelW), GUILayout.Height(RowH))) - EditorGUIUtility.PingObject(pattern); - - Rect timelineRect = EditorGUILayout.GetControlRect(false, RowH, - GUILayout.Width(TimelineW)); - - // Windup - if (windupDur > 0f) - DrawBarInRect(timelineRect, cursor, windupDur, totalDuration, - windupDur / (windupDur + activeDur + recoveryDur) < 0.1f ? ColWarn : ColWindup); - cursor += windupDur; - - // Active - if (activeDur > 0f) - DrawBarInRect(timelineRect, cursor, activeDur, totalDuration, - activeDur / (windupDur + activeDur + recoveryDur) < 0.1f ? ColWarn : ColActive); - cursor += activeDur; - - // Recovery - if (recoveryDur > 0f) - DrawBarInRect(timelineRect, cursor, recoveryDur, totalDuration, - recoveryDur / (windupDur + activeDur + recoveryDur) < 0.1f ? ColWarn : ColRecovery); - cursor += recoveryDur; - - _ = rowStart; // suppress unused warning - EditorGUILayout.EndHorizontal(); - } - - private void DrawVulnWindowRow(VulnerabilityWindow vw, float totalDuration) - { - string label = $"弱点:{vw.TriggerType} +{vw.TriggerDelay:F2}s / {vw.Duration:F2}s"; - DrawBar(label, vw.TriggerDelay, vw.Duration, totalDuration, ColVuln, null); - } - - private void DrawBar(string label, float start, float duration, float totalDuration, - Color color, AttackPatternSO pingTarget) - { - EditorGUILayout.BeginHorizontal(GUILayout.Height(RowH)); - - if (GUILayout.Button(label, EditorStyles.miniLabel, GUILayout.Width(LabelW), GUILayout.Height(RowH))) - { - if (pingTarget != null) EditorGUIUtility.PingObject(pingTarget); - } - - Rect timelineRect = EditorGUILayout.GetControlRect(false, RowH, GUILayout.Width(TimelineW)); - DrawBarInRect(timelineRect, start, duration, totalDuration, color); - - EditorGUILayout.EndHorizontal(); - } - - private static void DrawBarInRect(Rect timeline, float start, float duration, - float totalDuration, Color color) - { - float x = timeline.x + (start / totalDuration) * timeline.width; - float w = Mathf.Max(MinBarWidth, (duration / totalDuration) * timeline.width); - EditorGUI.DrawRect(new Rect(x, timeline.y + 2f, w, timeline.height - 4f), color); - } - - // ── Drag & Drop ─────────────────────────────────────────────────── - - private void HandleDragDrop() - { - var evt = Event.current; - if (evt.type != EventType.DragUpdated && evt.type != EventType.DragPerform) return; - - DragAndDrop.visualMode = DragAndDropVisualMode.Copy; - - if (evt.type == EventType.DragPerform) - { - DragAndDrop.AcceptDrag(); - foreach (var obj in DragAndDrop.objectReferences) - { - if (obj is BossSkillSO skill) { _loadedSkill = skill; _loadedSequence = null; break; } - if (obj is SkillSequenceSO seq) { _loadedSequence = seq; _loadedSkill = null; break; } - } - Repaint(); - } - evt.Use(); - } - } -} diff --git a/Assets/_Game/Scripts/Editor/Enemies/BossSkillSequenceWindow.cs.meta b/Assets/_Game/Scripts/Editor/Enemies/BossSkillSequenceWindow.cs.meta deleted file mode 100644 index 397db5f6..00000000 --- a/Assets/_Game/Scripts/Editor/Enemies/BossSkillSequenceWindow.cs.meta +++ /dev/null @@ -1,11 +0,0 @@ -fileFormatVersion: 2 -guid: d47145d394333184eb3ff822e3c4aa4d -MonoImporter: - externalObjects: {} - serializedVersion: 2 - defaultReferences: [] - executionOrder: 0 - icon: {instanceID: 0} - userData: - assetBundleName: - assetBundleVariant: diff --git a/Assets/_Game/Scripts/Editor/Modules/BossSkillModule.cs b/Assets/_Game/Scripts/Editor/Modules/BossSkillModule.cs deleted file mode 100644 index efca1016..00000000 --- a/Assets/_Game/Scripts/Editor/Modules/BossSkillModule.cs +++ /dev/null @@ -1,209 +0,0 @@ -using System; -using UnityEditor; -using UnityEditor.UIElements; -using UnityEngine; -using UnityEngine.UIElements; -using BaseGames.Boss; - -namespace BaseGames.Editor.Modules -{ - /// - /// DataHub Boss技能模块 —— Tab 切换管理 BossSkillSO 和 SkillSequenceSO。 - /// - public class BossSkillModule : IDataModule, IDataModuleOrdered - { - private const string SkillFolder = "Assets/_Game/Data/Boss/Skills"; - private const string SeqFolder = "Assets/_Game/Data/Boss/Sequences"; - - public string ModuleId => "boss"; - public string DisplayName => "Boss技能"; - public string IconName => null; - public int DisplayOrder => 50; - - private int _activeTab = 0; - - private SoListPane _skillPane; - private SoListPane _seqPane; - private Action _onSelected; - - private DetailHeader _header; - private BossSkillSO _selectedSkill; - private SkillSequenceSO _selectedSeq; - - public void Initialize() - { - _skillPane = new SoListPane( - SkillFolder, "ABL_Boss_", - s => s.category.ToString()); - _skillPane.SelectionChanged = s => { _selectedSkill = s; _onSelected?.Invoke(s); }; - - _seqPane = new SoListPane(SeqFolder, "ABL_Seq_"); - _seqPane.SelectionChanged = s => { _selectedSeq = s; _onSelected?.Invoke(s); }; - } - - public void BuildListPane(VisualElement container, Action onSelected) - { - _onSelected = onSelected; - container.style.flexDirection = FlexDirection.Column; - - // Tab bar - var tabBar = new VisualElement(); - tabBar.style.flexDirection = FlexDirection.Row; - tabBar.style.borderBottomWidth = 1; - tabBar.style.borderBottomColor = new StyleColor(new Color(0.5f, 0.5f, 0.5f, 0.3f)); - container.Add(tabBar); - - var btnSkill = BuildTabBtn("技能 (Skill)", 0, tabBar); - var btnSeq = BuildTabBtn("序列 (Seq)", 1, tabBar); - - var listArea = new VisualElement(); - listArea.style.flexGrow = 1; - container.Add(listArea); - - ShowTab(0, listArea, new[] { btnSkill, btnSeq }); - btnSkill.clicked += () => ShowTab(0, listArea, new[] { btnSkill, btnSeq }); - btnSeq.clicked += () => ShowTab(1, listArea, new[] { btnSkill, btnSeq }); - - _skillPane.Refresh(); - _seqPane.Refresh(); - } - - public void BuildDetailPane(VisualElement container, UnityEngine.Object selected) - { - _header = new DetailHeader(); - _header.SetAsset(selected); - _header.RenameRequested += name => OnRenameRequested(selected, name); - container.Add(_header); - - if (selected == null) return; - - if (selected is BossSkillSO skill) - { - container.Add(BuildSkillCard(skill)); - container.Add(BuildActionBar(skill, SkillFolder, _skillPane)); - container.Add(SkillModule.MakeDivider()); - var insp = new InspectorElement(skill); container.Add(insp); - } - else if (selected is SkillSequenceSO seq) - { - container.Add(BuildSeqCard(seq)); - container.Add(BuildActionBar(seq, SeqFolder, _seqPane)); - container.Add(SkillModule.MakeDivider()); - var insp = new InspectorElement(seq); container.Add(insp); - } - } - - public void OnActivated() - { - _skillPane?.Refresh(); - _seqPane?.Refresh(); - } - - // ── 内部 ───────────────────────────────────────────────────────────── - - private Button BuildTabBtn(string text, int tabIdx, VisualElement bar) - { - var btn = new Button { text = text }; - btn.style.flexGrow = 1; - btn.style.paddingTop = 5; - btn.style.paddingBottom = 5; - btn.style.borderTopLeftRadius = 0; - btn.style.borderTopRightRadius = 0; - btn.style.borderBottomLeftRadius = 0; - btn.style.borderBottomRightRadius = 0; - btn.style.borderLeftWidth = 0; - btn.style.borderRightWidth = 0; - btn.style.borderTopWidth = 0; - btn.style.borderBottomWidth = 0; - btn.style.backgroundColor = new StyleColor(Color.clear); - btn.userData = tabIdx; - bar.Add(btn); - return btn; - } - - private void ShowTab(int tab, VisualElement area, Button[] tabBtns) - { - _activeTab = tab; - area.Clear(); - - for (int i = 0; i < tabBtns.Length; i++) - { - if (i == tab) - { - tabBtns[i].style.borderBottomWidth = 2; - tabBtns[i].style.borderBottomColor = new StyleColor(new Color(0.4f, 0.65f, 1f, 1f)); - tabBtns[i].style.opacity = 1f; - } - else - { - tabBtns[i].style.borderBottomWidth = 0; - tabBtns[i].style.opacity = 0.65f; - } - } - - if (tab == 0) { _skillPane.style.flexGrow = 1; area.Add(_skillPane); } - else { _seqPane.style.flexGrow = 1; area.Add(_seqPane); } - } - - private void OnRenameRequested(UnityEngine.Object asset, string newName) - { - var (ok, err) = AssetOperations.Rename(asset, newName); - if (!ok) EditorUtility.DisplayDialog("重命名失败", err, "确定"); - else - { - _header.SetAsset(asset); - if (_activeTab == 0) _skillPane.Invalidate(); - else _seqPane.Invalidate(); - } - } - - private static VisualElement BuildSkillCard(BossSkillSO s) - { - var card = SkillModule.MakeCard(); - SkillModule.AddChip(card, "分类", s.category.ToString()); - SkillModule.AddChip(card, "类型", s.skillType.ToString()); - SkillModule.AddChip(card, "模式数", (s.attackPatterns?.Length ?? 0).ToString()); - SkillModule.AddChip(card, "弱点窗口", (s.vulnerabilityWindows?.Length ?? 0).ToString()); - if (!string.IsNullOrEmpty(s.skillId)) - SkillModule.AddChip(card, "ID", s.skillId); - return card; - } - - private static VisualElement BuildSeqCard(SkillSequenceSO s) - { - var card = SkillModule.MakeCard(); - SkillModule.AddChip(card, "步骤数", (s.steps?.Length ?? 0).ToString()); - SkillModule.AddChip(card, "循环", s.RepeatIfPlayerInRange ? "是" : "否"); - SkillModule.AddChip(card, "最大循环次数", s.MaxRepeatCount.ToString()); - return card; - } - - private VisualElement BuildActionBar(T asset, string folder, SoListPane pane) - where T : ScriptableObject - { - var bar = SkillModule.MakeActionBar(); - new Button(() => { EditorGUIUtility.PingObject(asset); Selection.activeObject = asset; }) - { text = "定位" }.AlsoAddTo(bar); - new Button(() => - { - var c = AssetOperations.Clone(asset, folder); - if (c != null) pane.Refresh(c); - }) { text = "克隆..." }.AlsoAddTo(bar); - var del = new Button(() => - { - if (AssetOperations.Delete(asset)) pane.Refresh(null); - }) { text = "删除" }; - del.style.borderLeftColor = new StyleColor(new Color(0.8f, 0.3f, 0.3f, 0.6f)); - del.style.borderRightColor = new StyleColor(new Color(0.8f, 0.3f, 0.3f, 0.6f)); - del.style.borderTopColor = new StyleColor(new Color(0.8f, 0.3f, 0.3f, 0.6f)); - del.style.borderBottomColor = new StyleColor(new Color(0.8f, 0.3f, 0.3f, 0.6f)); - del.style.borderLeftWidth = 1; - del.style.borderRightWidth = 1; - del.style.borderTopWidth = 1; - del.style.borderBottomWidth = 1; - del.style.marginLeft = 8; - del.AlsoAddTo(bar); - return bar; - } - } -} diff --git a/Assets/_Game/Scripts/Editor/Modules/BossSkillModule.cs.meta b/Assets/_Game/Scripts/Editor/Modules/BossSkillModule.cs.meta deleted file mode 100644 index 3f77eaa3..00000000 --- a/Assets/_Game/Scripts/Editor/Modules/BossSkillModule.cs.meta +++ /dev/null @@ -1,11 +0,0 @@ -fileFormatVersion: 2 -guid: f0d0425e529293e469da3762fe3bf8f0 -MonoImporter: - externalObjects: {} - serializedVersion: 2 - defaultReferences: [] - executionOrder: 0 - icon: {instanceID: 0} - userData: - assetBundleName: - assetBundleVariant: diff --git a/Assets/_Game/Scripts/Editor/Shared/EditorScaffoldUtils.cs b/Assets/_Game/Scripts/Editor/Shared/EditorScaffoldUtils.cs index 7c627512..b380af19 100644 --- a/Assets/_Game/Scripts/Editor/Shared/EditorScaffoldUtils.cs +++ b/Assets/_Game/Scripts/Editor/Shared/EditorScaffoldUtils.cs @@ -18,8 +18,6 @@ namespace BaseGames.Editor { { "WeaponSO", ("WPN_", "WPN_{ID},例:WPN_SkyBlade") }, { "FormSkillSO", ("SKL_", "SKL_{Name},例:SKL_SoulBlade") }, - { "BossSkillSO", ("SKL_", "SKL_{Name},例:SKL_BossRage") }, - { "SkillSequenceSO", ("SKL_", "SKL_Seq_{Name},例:SKL_Seq_RageCombo") }, { "EnemyStatsSO", ("ENM_", "ENM_E{ID}_Stats,例:ENM_E001_Stats") }, { "LootTableSO", ("ENM_", "ENM_E{ID}_Loot,例:ENM_E001_Loot") }, { "FormConfigSO", ("PLY_", "PLY_{FormID},例:PLY_Player01") }, diff --git a/Assets/_Game/Scripts/Enemies/Boss/AttackPatternSO.cs b/Assets/_Game/Scripts/Enemies/Boss/AttackPatternSO.cs deleted file mode 100644 index ee62b7ab..00000000 --- a/Assets/_Game/Scripts/Enemies/Boss/AttackPatternSO.cs +++ /dev/null @@ -1,33 +0,0 @@ -using System; -using UnityEngine; -using UnityEngine.AddressableAssets; -using BaseGames.Combat; - -namespace BaseGames.Boss -{ - /// - /// 单个攻击图案的数据。伤害参数只写在此处,BossSkillSO 不存参数。 - /// - [CreateAssetMenu(menuName = "BaseGames/Boss/AttackPattern")] - public class AttackPatternSO : ScriptableObject - { - [Header("输出")] - public DamageSourceSO DamageSource; - public float KnockbackAngle; - - [Header("弹幕(若为弹幕类型)")] - public AssetReferenceGameObject ProjectilePrefab; - public int ProjectileCount = 1; - public float SpreadAngle = 0f; - public float ProjectileSpeed = 8f; - - [Header("范围攻击(若为 AoE 类型)")] - public float AoERadius; - public Vector2 AoEOffset; - - [Header("时序")] - [Min(0f)] public float WindupDuration; - [Min(0f)] public float ActiveDuration; - [Min(0f)] public float RecoveryDuration; - } -} diff --git a/Assets/_Game/Scripts/Enemies/Boss/AttackPatternSO.cs.meta b/Assets/_Game/Scripts/Enemies/Boss/AttackPatternSO.cs.meta deleted file mode 100644 index 6e0a41d7..00000000 --- a/Assets/_Game/Scripts/Enemies/Boss/AttackPatternSO.cs.meta +++ /dev/null @@ -1,11 +0,0 @@ -fileFormatVersion: 2 -guid: 81f89b6e2f8f2774ab7cedbe45dcb810 -MonoImporter: - externalObjects: {} - serializedVersion: 2 - defaultReferences: [] - executionOrder: 0 - icon: {instanceID: 0} - userData: - assetBundleName: - assetBundleVariant: diff --git a/Assets/_Game/Scripts/Enemies/Boss/BossSkillExecutor.cs b/Assets/_Game/Scripts/Enemies/Boss/BossSkillExecutor.cs deleted file mode 100644 index 9be6312a..00000000 --- a/Assets/_Game/Scripts/Enemies/Boss/BossSkillExecutor.cs +++ /dev/null @@ -1,359 +0,0 @@ -using System.Collections; -using System.Collections.Generic; -using UnityEngine; -using Animancer; -using BaseGames.Combat; -using BaseGames.Core.Events; - -namespace BaseGames.Boss -{ - /// - /// 挂在 Boss GameObject 上,接收 BossOrchestrator 的指令执行指定 BossSkillSO。 - /// 管理 VulnerabilityWindow 计时和 WeakPointSystem 激活。 - /// - public class BossSkillExecutor : MonoBehaviour - { - [SerializeField] private HitBox[] _hitBoxes; - [SerializeField] private WeakPointSystem _weakPointSystem; - [SerializeField] private AnimancerComponent _animancer; - [SerializeField] private string _bossId; - [SerializeField] private BossSkillEventChannelSO _onBossSkillStarted; - [SerializeField] private BossSkillEventChannelSO _onBossSkillEnded; - /// PlayerController 无 Instance(架构 05 §2),由 Inspector 指定。 - [SerializeField] private Transform _playerTransform; - - [SerializeField] private BossSkillSO[] _skills; - - [Header("技能重复检测范围")] - [Tooltip("SkillSequence RepeatIfPlayerInRange 的检测半径(m)")] - [SerializeField, Min(1f)] private float _repeatRangeCheck = 8f; - - private BossSkillSO _currentSkill; - private bool _isExecuting; - private Coroutine _activeCoroutine; - private Coroutine _vulnCoroutine; // 弱点窗口协程(中断时需同步停止) - private bool _patternHitConfirmed; // 本次技能执行期间是否有 HitBox 命中 - - // 技能冷却:skillId → 冷却结束的 Time.time 时刻 - private readonly Dictionary _skillCooldownEndTimes = new(); - - public bool IsExecuting => _isExecuting; - - /// 检查指定技能是否冷却就绪(无冷却记录或已过冷却时间)。 - public bool CanUseSkill(string skillId) - { - if (string.IsNullOrEmpty(skillId)) return false; - if (_skillCooldownEndTimes.TryGetValue(skillId, out float endTime)) - return Time.time >= endTime; - return true; - } - - /// 强制重置指定技能的冷却(阶段切换、复活等场景使用)。 - public void ResetSkillCooldown(string skillId) - { - _skillCooldownEndTimes.Remove(skillId); - } - - /// 重置所有技能冷却。 - public void ResetAllCooldowns() => _skillCooldownEndTimes.Clear(); - - private void Awake() - { -#if UNITY_EDITOR - ValidateSkillConfig(); -#endif - } - -#if UNITY_EDITOR - private void OnValidate() => ValidateSkillConfig(); - - private void ValidateSkillConfig() - { - if (_skills == null || _skills.Length == 0) - { - Debug.LogError($"[BossSkillExecutor] Boss '{_bossId}' ({gameObject.name}) 未配置任何技能 SO。", this); - return; - } - foreach (var skill in _skills) - { - if (skill == null) - Debug.LogError($"[BossSkillExecutor] Boss '{_bossId}' ({gameObject.name}) _skills 数组含 null 元素。", this); - else if (string.IsNullOrEmpty(skill.skillId)) - Debug.LogError($"[BossSkillExecutor] Boss '{_bossId}' ({gameObject.name}) 技能 '{skill.name}' 缺少 skillId。", this); - } - } -#endif - - /// - /// 按 float 值复用 WaitForSeconds 实例,消除协程中每次 new WaitForSeconds 的 GC 分配。 - /// Domain Reload 禁用时静态缓存跨 PlayMode 会话保留,但 WaitForSeconds 是幂等值对象, - /// 不会引发功能错误;[RuntimeInitializeOnLoadMethod] 确保每次进入 Play 时清空。 - /// - private static readonly Dictionary _wfsCache = new(); - - [UnityEngine.RuntimeInitializeOnLoadMethod(UnityEngine.RuntimeInitializeLoadType.SubsystemRegistration)] - private static void ClearWFSCache() => _wfsCache.Clear(); - - private const int MaxWFSCacheSize = 64; - - private static WaitForSeconds GetWFS(float t) - { - if (!_wfsCache.TryGetValue(t, out var wfs)) - { - if (_wfsCache.Count < MaxWFSCacheSize) - _wfsCache[t] = wfs = new WaitForSeconds(t); - else - return new WaitForSeconds(t); - } - return wfs; - } - - // ── 公共 API ─────────────────────────────────────────────────────────── - - /// - /// 按 skillId 查找已在 Inspector 注册的技能 SO。未找到返回 null。 - /// - public BossSkillSO FindSkill(string skillId) - { - if (_skills == null) return null; - foreach (var s in _skills) - if (s != null && s.skillId == skillId) return s; - return null; - } - - /// 返回当前正在执行的技能 SO,未执行时返回 null。 - public BossSkillSO FindCurrentSkill() => _isExecuting ? _currentSkill : null; - - /// Inspector 中注册的全部技能 SO(只读)。 - public BossSkillSO[] Skills => _skills; - - /// - /// 执行一个 Boss 技能。若当前正在执行或技能冷却未就绪则返回。 - /// - public void ExecuteSkill(BossSkillSO skill) - { - if (_isExecuting || skill == null) return; - if (!CanUseSkill(skill.skillId)) - { - Debug.Log($"[BossSkillExecutor] 技能 '{skill.skillId}' 冷却中,无法执行。", this); - return; - } - // 提前订阅,确保 InterruptCurrentSkill() 中断时 FinishExecution() 能正常取消 - SubscribeHitCallbacks(); - _activeCoroutine = StartCoroutine(ExecuteSkillCoroutine(skill)); - } - - /// - /// 立即打断正在执行的技能(阶段切换时调用)。 - /// - public void InterruptCurrentSkill() - { - // 同步停止弱点窗口协程,防止中断后继续激活 WeakPointSystem - if (_vulnCoroutine != null) - { - StopCoroutine(_vulnCoroutine); - _vulnCoroutine = null; - } - if (_activeCoroutine != null) - { - StopCoroutine(_activeCoroutine); - _activeCoroutine = null; - } - FinishExecution(); - } - - // 等待事件触发的 VulnWindow(事件驱动类型,存储后由 NotifyVulnTrigger 逐个激活) - private readonly List _pendingEventWindows = new(); - - /// - /// 通知执行器某一外部事件已发生(如格挡成功、反制命中等), - /// 激活所有注册该触发类型的弱点窗口。 - /// 由 BossBase.HandleParrySuccess / ApplyCounterResponse 等调用。 - /// - public void NotifyVulnTrigger(VulnTriggerType triggerType) - { - for (int i = _pendingEventWindows.Count - 1; i >= 0; i--) - { - var w = _pendingEventWindows[i]; - if (w.TriggerType == triggerType) - { - _pendingEventWindows.RemoveAt(i); - StartCoroutine(OpenWindowCoroutine(w)); - } - } - } - - private void OnHitConfirmedCallback(DamageInfo _) => _patternHitConfirmed = true; - - private void SubscribeHitCallbacks() - { - if (_hitBoxes == null) return; - foreach (var hb in _hitBoxes) if (hb != null) hb.OnHitConfirmed += OnHitConfirmedCallback; - } - - private void UnsubscribeHitCallbacks() - { - if (_hitBoxes == null) return; - foreach (var hb in _hitBoxes) if (hb != null) hb.OnHitConfirmed -= OnHitConfirmedCallback; - } - - private IEnumerator ExecuteSkillCoroutine(BossSkillSO skill) - { - _isExecuting = true; - _currentSkill = skill; - _patternHitConfirmed = false; - - // HitBox 订阅已在 ExecuteSkill() 入口完成(确保 Interrupt 中断时能在 FinishExecution 取消) - - _onBossSkillStarted?.Raise(new BossSkillEvent { BossId = _bossId, SkillId = skill.skillId }); - - // 播放技能动画 - if (skill.skillAnimation != null) - _animancer.Play(skill.skillAnimation); - - // 启动 VulnerabilityWindow 协程(与主序列并行) - _vulnCoroutine = null; - if (skill.vulnerabilityWindows != null && skill.vulnerabilityWindows.Length > 0) - _vulnCoroutine = StartCoroutine(ActivateVulnerabilityWindowsCoroutine(skill)); - - // 执行主攻击序列(始终执行 sequenceOnMiss;sequenceOnHit 是命中后的追加序列) - if (skill.sequenceOnMiss != null) - yield return ExecuteSequenceCoroutine(skill.sequenceOnMiss); - - // 若本次有命中确认且配置了 sequenceOnHit,执行追加序列(连段、击倒追击等) - if (_patternHitConfirmed && skill.sequenceOnHit != null) - yield return ExecuteSequenceCoroutine(skill.sequenceOnHit); - - // 若弱点协程还在运行则等待其结束(避免孤立协程) - if (_vulnCoroutine != null) - yield return _vulnCoroutine; - - FinishExecution(); - } - - private void FinishExecution() - { - UnsubscribeHitCallbacks(); // 无论正常结束还是被 Interrupt,均在此取消订阅 - _pendingEventWindows.Clear(); // 清除未触发的事件驱动弱点窗口,防止跨技能积压 - _vulnCoroutine = null; // 正常结束时已自然结束,仅清除引用 - _isExecuting = false; - if (_currentSkill != null) - { - // 记录冷却结束时刻 - if (_currentSkill.cooldown > 0f) - _skillCooldownEndTimes[_currentSkill.skillId] = Time.time + _currentSkill.cooldown; - - _onBossSkillEnded?.Raise(new BossSkillEvent { BossId = _bossId, SkillId = _currentSkill.skillId }); - _currentSkill = null; - } - } - - // ── 序列协程 ──────────────────────────────────────────────────────────── - - private IEnumerator ExecuteSequenceCoroutine(SkillSequenceSO seq) - { - int repeatCount = 0; - do - { - foreach (var step in seq.steps) - { - if (step.delayBeforeStep > 0f) - yield return GetWFS(step.delayBeforeStep); - - if (step.pattern != null) - yield return ExecutePatternCoroutine(step.pattern); - } - - repeatCount++; - - if (seq.RepeatIfPlayerInRange && seq.RepeatDelay > 0f) - yield return GetWFS(seq.RepeatDelay); - } - while (seq.RepeatIfPlayerInRange - && (seq.MaxRepeatCount == 0 || repeatCount < seq.MaxRepeatCount) - && IsPlayerInRange()); - } - - private IEnumerator ExecutePatternCoroutine(AttackPatternSO pattern) - { - // 预备 - if (pattern.WindupDuration > 0f) - yield return GetWFS(pattern.WindupDuration); - - // 激活 HitBox(架构 06 §4:Activate(DamageSourceSO, Transform)) - if (_hitBoxes != null && _hitBoxes.Length > 0) - foreach (var hb in _hitBoxes) - if (hb != null) hb.Activate(pattern.DamageSource, transform); - - if (pattern.ActiveDuration > 0f) - yield return GetWFS(pattern.ActiveDuration); - - // 关闭 HitBox - if (_hitBoxes != null && _hitBoxes.Length > 0) - foreach (var hb in _hitBoxes) - if (hb != null) hb.Deactivate(); - - // 后摇 - if (pattern.RecoveryDuration > 0f) - yield return GetWFS(pattern.RecoveryDuration); - } - - // ── VulnerabilityWindow 协程 ───────────────────────────────────────────── - - private IEnumerator ActivateVulnerabilityWindowsCoroutine(BossSkillSO skill) - { - _pendingEventWindows.Clear(); - - foreach (var window in skill.vulnerabilityWindows) - { - if (window.TriggerType == VulnTriggerType.OnAttackRecovery) - { - // 时间驱动:按 TriggerDelay 延迟后自动激活 - if (window.TriggerDelay > 0f) - yield return GetWFS(window.TriggerDelay); - StartCoroutine(OpenWindowCoroutine(window)); - } - else - { - // 事件驱动(OnParriedSuccess / Manual 等):注册到待触发列表 - _pendingEventWindows.Add(window); - } - } - } - - /// 实际开启并持续弱点窗口,支持独立并行运行。 - private IEnumerator OpenWindowCoroutine(VulnerabilityWindow window) - { - bool activateSpecific = window.ActivateWeakPointHurtBox; - _weakPointSystem?.SetActive(true, window.DamageMultiplier, activateSpecific); - window.OpenFeedback?.Play(); - - yield return GetWFS(window.Duration); - - _weakPointSystem?.SetActive(false, 1f, activateSpecific); - window.CloseFeedback?.Play(); - } - - // ── 工具 ─────────────────────────────────────────────────────────────── - - /// - /// 在指定时长内开启弱点窗口(格挡/闪避反制时调用,独立于技能 VulnerabilityWindow 序列)。 - /// - public void OpenVulnerabilityWindow(float duration, float damageMultiplier) - { - if (_weakPointSystem == null || duration <= 0f) return; - StartCoroutine(VulnWindowOverride(duration, damageMultiplier)); - } - - private IEnumerator VulnWindowOverride(float duration, float multiplier) - { - _weakPointSystem.SetActive(true, multiplier, false); - yield return GetWFS(duration); - _weakPointSystem.SetActive(false, 1f, false); - } - - private bool IsPlayerInRange() => - _playerTransform != null && - Vector2.Distance(transform.position, _playerTransform.position) < _repeatRangeCheck; - } -} diff --git a/Assets/_Game/Scripts/Enemies/Boss/BossSkillExecutor.cs.meta b/Assets/_Game/Scripts/Enemies/Boss/BossSkillExecutor.cs.meta deleted file mode 100644 index cce54276..00000000 --- a/Assets/_Game/Scripts/Enemies/Boss/BossSkillExecutor.cs.meta +++ /dev/null @@ -1,11 +0,0 @@ -fileFormatVersion: 2 -guid: 4dfa1c525eaca5640b3cfe945626a466 -MonoImporter: - externalObjects: {} - serializedVersion: 2 - defaultReferences: [] - executionOrder: 0 - icon: {instanceID: 0} - userData: - assetBundleName: - assetBundleVariant: diff --git a/Assets/_Game/Scripts/Enemies/Boss/BossSkillSO.cs b/Assets/_Game/Scripts/Enemies/Boss/BossSkillSO.cs deleted file mode 100644 index 4c67271b..00000000 --- a/Assets/_Game/Scripts/Enemies/Boss/BossSkillSO.cs +++ /dev/null @@ -1,69 +0,0 @@ -using System; -using UnityEngine; -using Animancer; -using BaseGames.Combat; - -namespace BaseGames.Boss -{ - /// - /// Boss 单个技能的所有数据,包括攻击模式、弱点窗口、互动标签等。 - /// - [CreateAssetMenu(menuName = "BaseGames/Boss/BossSkill")] - public class BossSkillSO : ScriptableObject - { - [Header("元信息")] - [Tooltip("技能唯一标识符(全小写英文 + 下划线,如 'slash_combo'、'phase_dash')。BD_UseBossSkill 通过此 Id 引用")] - public string skillId; - [Tooltip("编辑器中显示的可读名称,不影响运行逻辑")] - public string displayName; - [TextArea(1, 4)] - [Tooltip("设计备注(仅供编辑器参考,不影响运行)")] - public string designNote; - - [Header("技能分类")] - public BossSkillCategory category; - public BossSkillType skillType; - - [Header("阶段可用性")] - [Tooltip("空数组 = 全阶段可用")] - public int[] availablePhaseIndices; - - [Header("核心攻击动作引用")] - public AttackPatternSO[] attackPatterns; - - [Header("弱点窗口(至少 1 个)")] - public VulnerabilityWindow[] vulnerabilityWindows; - - [Header("互动标签")] - public InteractionTag interactionTags; - - [Header("连段")] - public SkillSequenceSO sequenceOnHit; - public SkillSequenceSO sequenceOnMiss; - - [Header("玩家反制接口")] - public PlayerCounterResponse[] counterResponses; - - [Header("场景联动")] - public ArenaEventTrigger[] arenaEvents; - - [Header("Boss 资源")] - public BossResourceCost resourceCost; - public bool buildsRage; - - [Header("霸体配置")] - public PoiseWindowConfig poiseWindow; - - [Header("动画")] - public ClipTransition skillAnimation; - - [Header("冷却")] - [Min(0f)] - public float cooldown; - - [Header("权重随机(UseBossSkillWeighted 使用)")] - [Tooltip("相对权重,数值越大被随机选中的概率越高;0 = 禁用随机选择")] - [Min(0f)] - public float weight = 1f; - } -} diff --git a/Assets/_Game/Scripts/Enemies/Boss/BossSkillSO.cs.meta b/Assets/_Game/Scripts/Enemies/Boss/BossSkillSO.cs.meta deleted file mode 100644 index aaceeee8..00000000 --- a/Assets/_Game/Scripts/Enemies/Boss/BossSkillSO.cs.meta +++ /dev/null @@ -1,11 +0,0 @@ -fileFormatVersion: 2 -guid: de92221c7c3fb4a42a7cd122a8f97632 -MonoImporter: - externalObjects: {} - serializedVersion: 2 - defaultReferences: [] - executionOrder: 0 - icon: {instanceID: 0} - userData: - assetBundleName: - assetBundleVariant: diff --git a/Assets/_Game/Scripts/Enemies/Boss/BossSkillTypes.cs b/Assets/_Game/Scripts/Enemies/Boss/BossSkillTypes.cs deleted file mode 100644 index a612f79a..00000000 --- a/Assets/_Game/Scripts/Enemies/Boss/BossSkillTypes.cs +++ /dev/null @@ -1,184 +0,0 @@ -using System; -using UnityEngine; -using BaseGames.Feedback; - -namespace BaseGames.Boss -{ - // ── 分类枚举 ─────────────────────────────────────────────────────────────── - - public enum BossSkillCategory - { - Melee, Ranged, Charge, AoE, Environmental, Summon, - Buff, Debuff, Phase, Passive, Reactive - } - - public enum BossSkillType - { - MeleeSlash, - ChargeAttack, - LeapSlam, - ProjectileVolley, - LaserBeam, - PhaseTransition, - SummonMinion, - ArenaTrap, - SpeedBurst, - DefensiveShell, - } - - [Flags] - public enum InteractionTag - { - None = 0, - Parryable = 1 << 0, - PerfectParryOnly = 1 << 1, - DodgeWindow = 1 << 2, - Unblockable = 1 << 3, - CanBeReflected = 1 << 4, - ExposesWeakPoint = 1 << 5, - GrantsPlayerReso = 1 << 6, - ArenaHazard = 1 << 7, - PhaseGate = 1 << 8, - } - - // ── VulnerabilityWindow ──────────────────────────────────────────────────── - - public enum VulnTriggerType - { - OnAttackRecovery, - OnParriedSuccess, - OnCounterSkillHit, - OnPhaseTransition, - OnHazardBackfire, - OnSummonDefeated, - Manual, - } - - public enum WeakPointType - { - FullBody, - HeadOnly, - BackOnly, - CoreExposed, - CustomPoint, - } - - [Serializable] - public struct VulnerabilityWindow - { - [Tooltip("弱点触发方式")] - public VulnTriggerType TriggerType; - - [Tooltip("触发后延迟出现(秒)")] - [Min(0f)] - public float TriggerDelay; - - [Tooltip("弱点持续时长(秒)")] - [Min(0.1f)] - public float Duration; - - public WeakPointType WeakPointType; - - [Tooltip("弱点激活时 Boss 受击乘数")] - [Min(0.1f)] - public float DamageMultiplier; - - public bool ForceStagger; - - [Min(0f)] - public float StaggerDuration; - - public SceneFeedback OpenFeedback; - public SceneFeedback CloseFeedback; - public Color HighlightColor; - - public bool ActivateWeakPointHurtBox => WeakPointType != WeakPointType.FullBody; - } - - // ── PlayerCounterResponse ───────────────────────────────────────────────── - - public enum CounterType - { - Parry, - PerfectParry, - DodgeThrough, - SpecificSkill, - WeakPointHit, - HazardBackfire, - SummonKill, - } - - [Serializable] - public struct PlayerCounterResponse - { - [Header("反制条件")] - public CounterType counterType; - public string requiredSkillId; - - [Header("反制效果(对 Boss)")] - public float bossStaggerDuration; - public float bossDamageBonus; - public bool openVulnWindow; - public bool interruptSkill; - - [Header("反制收益(对玩家)")] - public int soulPowerGrant; - public int spiritPowerGrant; - public SceneFeedback counterFeedback; - } - - // ── ArenaEvent ──────────────────────────────────────────────────────────── - - public enum ArenaEventType - { - DestroyPlatform, - ActivateHazard, - DeactivateHazard, - SpawnHazardArea, - ShakeArena, - ToggleLighting, - SpawnPlatform, - TriggerCutscene, - } - - [Serializable] - public struct ArenaEventParams - { - public float duration; - public float intensity; - public bool revertsOnPhaseEnd; - } - - [Serializable] - public struct ArenaEventTrigger - { - public string targetArenaObjectId; - public ArenaEventType eventType; - public float delay; - public ArenaEventParams parameters; - } - - [Serializable] - public struct ArenaEventData - { - public ArenaEventType type; - public ArenaEventParams parameters; - public string sourceSkillId; - } - - public interface IArenaInteractable - { - string ArenaObjectId { get; } - void OnBossArenaEvent(ArenaEventData data); - } - - // ── BossResourceCost ────────────────────────────────────────────────────── - - [Serializable] - public struct BossResourceCost - { - public string resourceId; - public float cost; - public float minRequired; - } -} diff --git a/Assets/_Game/Scripts/Enemies/Boss/BossSkillTypes.cs.meta b/Assets/_Game/Scripts/Enemies/Boss/BossSkillTypes.cs.meta deleted file mode 100644 index 2f8a3962..00000000 --- a/Assets/_Game/Scripts/Enemies/Boss/BossSkillTypes.cs.meta +++ /dev/null @@ -1,11 +0,0 @@ -fileFormatVersion: 2 -guid: a1beb8f8f7958b84c9ab60abe5f8c4ed -MonoImporter: - externalObjects: {} - serializedVersion: 2 - defaultReferences: [] - executionOrder: 0 - icon: {instanceID: 0} - userData: - assetBundleName: - assetBundleVariant: diff --git a/Assets/_Game/Scripts/Enemies/Boss/ChaoFengKnockdownCounter.cs b/Assets/_Game/Scripts/Enemies/Boss/ChaoFengKnockdownCounter.cs index 9ed20228..69f9f910 100644 --- a/Assets/_Game/Scripts/Enemies/Boss/ChaoFengKnockdownCounter.cs +++ b/Assets/_Game/Scripts/Enemies/Boss/ChaoFengKnockdownCounter.cs @@ -2,6 +2,7 @@ using System.Collections; using Animancer; using BaseGames.Boss; using BaseGames.Combat; +using BaseGames.Enemies.Abilities; using UnityEngine; namespace BaseGames.Enemies.Boss @@ -79,7 +80,8 @@ namespace BaseGames.Enemies.Boss { _inKnockdown = true; - _boss.GetComponentInChildren()?.InterruptCurrentSkill(); + // 击落打断当前正在执行的所有能力(招式统一由能力注册表管理) + _boss.Abilities.InterruptAll(InterruptReason.ExternalRequest); if (_knockdownHitClip.Clip != null) _boss.Animancer.Play(_knockdownHitClip); diff --git a/Assets/_Game/Scripts/Enemies/Boss/Patterns.meta b/Assets/_Game/Scripts/Enemies/Boss/Patterns.meta deleted file mode 100644 index a881eff4..00000000 --- a/Assets/_Game/Scripts/Enemies/Boss/Patterns.meta +++ /dev/null @@ -1,8 +0,0 @@ -fileFormatVersion: 2 -guid: 6d8f5f23ee1dde046b1a7361ac1b6386 -folderAsset: yes -DefaultImporter: - externalObjects: {} - userData: - assetBundleName: - assetBundleVariant: diff --git a/Assets/_Game/Scripts/Enemies/Boss/Patterns/BaseGames.Enemies.Boss.Patterns.asmdef b/Assets/_Game/Scripts/Enemies/Boss/Patterns/BaseGames.Enemies.Boss.Patterns.asmdef deleted file mode 100644 index 757971ee..00000000 --- a/Assets/_Game/Scripts/Enemies/Boss/Patterns/BaseGames.Enemies.Boss.Patterns.asmdef +++ /dev/null @@ -1,19 +0,0 @@ -{ - "excludePlatforms": [], - "allowUnsafeCode": false, - "precompiledReferences": [], - "name": "BaseGames.Enemies.Boss.Patterns", - "defineConstraints": [], - "noEngineReferences": false, - "versionDefines": [], - "rootNamespace": "BaseGames.Enemies.Boss.Patterns", - "references": [ - "BaseGames.Core", - "BaseGames.Core.Events", - "BaseGames.Enemies", - "BaseGames.Combat" - ], - "autoReferenced": true, - "overrideReferences": false, - "includePlatforms": [] -} diff --git a/Assets/_Game/Scripts/Enemies/Boss/Patterns/BaseGames.Enemies.Boss.Patterns.asmdef.meta b/Assets/_Game/Scripts/Enemies/Boss/Patterns/BaseGames.Enemies.Boss.Patterns.asmdef.meta deleted file mode 100644 index 0bab4b52..00000000 --- a/Assets/_Game/Scripts/Enemies/Boss/Patterns/BaseGames.Enemies.Boss.Patterns.asmdef.meta +++ /dev/null @@ -1,7 +0,0 @@ -fileFormatVersion: 2 -guid: 8bc3529e552a34a45998814c7cd056e6 -AssemblyDefinitionImporter: - externalObjects: {} - userData: - assetBundleName: - assetBundleVariant: diff --git a/Assets/_Game/Scripts/Enemies/Boss/Patterns/TelegraphSystem.cs b/Assets/_Game/Scripts/Enemies/Boss/Patterns/TelegraphSystem.cs deleted file mode 100644 index 64e2991c..00000000 --- a/Assets/_Game/Scripts/Enemies/Boss/Patterns/TelegraphSystem.cs +++ /dev/null @@ -1,50 +0,0 @@ -using System.Collections; -using UnityEngine; -using BaseGames.Core; -using BaseGames.Core.Pool; - -namespace BaseGames.Enemies.Boss.Patterns -{ - /// - /// 攻击预警系统(架构 07_EnemyModule §11)。 - /// 在攻击前若干帧显示视觉提示(VFX 从对象池取出,到期归还)。 - /// 由 BD_TelegraphAttack 通过协程调用 ShowTelegraph。 - /// - public class TelegraphSystem : MonoBehaviour - { - /// - /// 开始预警:从对象池取出 vfxKey 对应预警 VFX,等待 duration 秒后归还。 - /// 由 BD_TelegraphAttack.OnStart 通过 StartCoroutine 调用。 - /// - public IEnumerator ShowTelegraph(string vfxKey, float duration, Vector2 position) - { - if (string.IsNullOrEmpty(vfxKey) || duration <= 0f) - { - yield return null; - yield break; - } - - GameObject vfx = null; - var pool = ServiceLocator.GetOrDefault(); - if (pool != null) - vfx = pool.Spawn(vfxKey, new Vector3(position.x, position.y, 0f), Quaternion.identity); - else - Debug.LogWarning($"[TelegraphSystem] IObjectPoolService 未就绪,预警 VFX '{vfxKey}' 无法显示。"); - - yield return new WaitForSeconds(duration); - - if (vfx != null && pool != null) - { - var po = vfx.GetComponent(); - if (po != null) po.ReturnToPool(); - else vfx.SetActive(false); - } - } - - /// 立即隐藏所有活跃预警 VFX(技能被打断时调用)。 - public void CancelTelegraph() - { - StopAllCoroutines(); - } - } -} diff --git a/Assets/_Game/Scripts/Enemies/Boss/Patterns/TelegraphSystem.cs.meta b/Assets/_Game/Scripts/Enemies/Boss/Patterns/TelegraphSystem.cs.meta deleted file mode 100644 index 7c15784a..00000000 --- a/Assets/_Game/Scripts/Enemies/Boss/Patterns/TelegraphSystem.cs.meta +++ /dev/null @@ -1,11 +0,0 @@ -fileFormatVersion: 2 -guid: e6f4987894dfe1648909b6863c003c31 -MonoImporter: - externalObjects: {} - serializedVersion: 2 - defaultReferences: [] - executionOrder: 0 - icon: {instanceID: 0} - userData: - assetBundleName: - assetBundleVariant: diff --git a/Assets/_Game/Scripts/Enemies/Boss/SkillSequenceSO.cs b/Assets/_Game/Scripts/Enemies/Boss/SkillSequenceSO.cs deleted file mode 100644 index 978598e4..00000000 --- a/Assets/_Game/Scripts/Enemies/Boss/SkillSequenceSO.cs +++ /dev/null @@ -1,26 +0,0 @@ -using System; -using UnityEngine; - -namespace BaseGames.Boss -{ - /// - /// 有序攻击序列(一个技能内的多段连段)。 - /// - [CreateAssetMenu(menuName = "BaseGames/Boss/SkillSequence")] - public class SkillSequenceSO : ScriptableObject - { - [Serializable] - public struct SequenceStep - { - public AttackPatternSO pattern; - [Min(0f)] public float delayBeforeStep; - } - - public SequenceStep[] steps; - - [Header("序列完成后的行为")] - public bool RepeatIfPlayerInRange; - [Min(0f)] public float RepeatDelay; - [Range(0, 10)] public int MaxRepeatCount; - } -} diff --git a/Assets/_Game/Scripts/Enemies/Boss/SkillSequenceSO.cs.meta b/Assets/_Game/Scripts/Enemies/Boss/SkillSequenceSO.cs.meta deleted file mode 100644 index 8a1169fc..00000000 --- a/Assets/_Game/Scripts/Enemies/Boss/SkillSequenceSO.cs.meta +++ /dev/null @@ -1,11 +0,0 @@ -fileFormatVersion: 2 -guid: ab2ec01e225283d4face08cef0d72c87 -MonoImporter: - externalObjects: {} - serializedVersion: 2 - defaultReferences: [] - executionOrder: 0 - icon: {instanceID: 0} - userData: - assetBundleName: - assetBundleVariant: diff --git a/Assets/_Game/Scripts/Enemies/Boss/WeakPointSystem.cs b/Assets/_Game/Scripts/Enemies/Boss/WeakPointSystem.cs deleted file mode 100644 index d89df133..00000000 --- a/Assets/_Game/Scripts/Enemies/Boss/WeakPointSystem.cs +++ /dev/null @@ -1,51 +0,0 @@ -using UnityEngine; -using BaseGames.Combat; -using BaseGames.Core.Events; - -namespace BaseGames.Boss -{ - /// - /// 管理 Boss 的专属弱点 HurtBox(如核心、眼睛等)。 - /// 弱点激活期间受到的伤害会乘以 DamageMultiplier。 - /// - public class WeakPointSystem : MonoBehaviour - { - [System.Serializable] - public struct WeakPoint - { - public HurtBox hurtBox; - public GameObject visualIndicator; - } - - [SerializeField] private WeakPoint[] _weakPoints; - [SerializeField] private string _bossId; - [SerializeField] private StringEventChannelSO _onVulnerabilityWindowOpened; - - private float _damageMultiplier = 1f; - - /// 激活或关闭弱点 HurtBox 及视觉指示器。 - /// 是否激活。 - /// 激活时的受击伤害乘数。 - /// true = 仅激活弱点专属 HurtBox;false = 全身视为弱点(不改变 HurtBox 状态)。 - public void SetActive(bool active, float multiplier = 1f, bool activateSpecific = false) - { - _damageMultiplier = active ? multiplier : 1f; - - if (activateSpecific) - { - foreach (var wp in _weakPoints) - { - wp.hurtBox.gameObject.SetActive(active); - if (wp.visualIndicator != null) - wp.visualIndicator.SetActive(active); - } - } - - if (active) - _onVulnerabilityWindowOpened?.Raise(_bossId); - } - - /// 弱点 HurtBox 受击时,由 BossStats 调用此方法获取最终伤害系数。 - public float GetDamageMultiplier() => _damageMultiplier; - } -} diff --git a/Assets/_Game/Scripts/Enemies/Boss/WeakPointSystem.cs.meta b/Assets/_Game/Scripts/Enemies/Boss/WeakPointSystem.cs.meta deleted file mode 100644 index 2c79579d..00000000 --- a/Assets/_Game/Scripts/Enemies/Boss/WeakPointSystem.cs.meta +++ /dev/null @@ -1,11 +0,0 @@ -fileFormatVersion: 2 -guid: 96ffe91642a6ccc4ea4c6076d80f5e27 -MonoImporter: - externalObjects: {} - serializedVersion: 2 - defaultReferences: [] - executionOrder: 0 - icon: {instanceID: 0} - userData: - assetBundleName: - assetBundleVariant: diff --git a/zeling_v2.sln b/zeling_v2.sln index 4f8bc59e..09257a25 100644 --- a/zeling_v2.sln +++ b/zeling_v2.sln @@ -23,14 +23,14 @@ Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "BaseGames.Combat", "BaseGam EndProject Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "BaseGames.Dialogue", "BaseGames.Dialogue.csproj", "{4595E198-DE11-AAF7-3388-915563EBFE41}" EndProject -Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "BaseGames.Enemies", "BaseGames.Enemies.csproj", "{5E00F025-ED00-233A-3B2F-BAFF76D883F0}" -EndProject Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "BaseGames.VFX", "BaseGames.VFX.csproj", "{31DBC108-839C-1442-F9AC-A39596C9E06A}" EndProject Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "BaseGames.UI", "BaseGames.UI.csproj", "{7D3E9996-D17C-52A7-538D-3AEBAAF35DF3}" EndProject Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Micosmo.SensorToolkit.Editor", "Micosmo.SensorToolkit.Editor.csproj", "{DF93C827-75A1-E6E4-D7D2-206550211A63}" EndProject +Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "BaseGames.Enemies", "BaseGames.Enemies.csproj", "{5E00F025-ED00-233A-3B2F-BAFF76D883F0}" +EndProject Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "BaseGames.World.Map", "BaseGames.World.Map.csproj", "{16BB97E7-3EA9-4707-2D93-441D9C908404}" EndProject Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "BaseGames.Tests.EditMode", "BaseGames.Tests.EditMode.csproj", "{0CFAE763-03B9-0921-E4ED-03289E7D499F}" @@ -93,8 +93,6 @@ Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "BaseGames.Spells", "BaseGam EndProject Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "BaseGames.World.Streaming", "BaseGames.World.Streaming.csproj", "{8FA0AF4D-7EF6-D3CD-F2A1-9C291AA06F3C}" EndProject -Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "BaseGames.Enemies.Boss.Patterns", "BaseGames.Enemies.Boss.Patterns.csproj", "{FABE4470-F5D8-FF10-6CF9-C03E9D2A8DBD}" -EndProject Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "BaseGames.Tutorial", "BaseGames.Tutorial.csproj", "{0A1566C3-6032-C8A1-D015-8EF75B3F7099}" EndProject Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "BaseGames.World.Shop", "BaseGames.World.Shop.csproj", "{3022C488-D174-AD8B-A390-675C8CB13DEF}" @@ -157,10 +155,6 @@ Global {4595E198-DE11-AAF7-3388-915563EBFE41}.Debug|Any CPU.Build.0 = Debug|Any CPU {4595E198-DE11-AAF7-3388-915563EBFE41}.Release|Any CPU.ActiveCfg = Release|Any CPU {4595E198-DE11-AAF7-3388-915563EBFE41}.Release|Any CPU.Build.0 = Release|Any CPU - {5E00F025-ED00-233A-3B2F-BAFF76D883F0}.Debug|Any CPU.ActiveCfg = Debug|Any CPU - {5E00F025-ED00-233A-3B2F-BAFF76D883F0}.Debug|Any CPU.Build.0 = Debug|Any CPU - {5E00F025-ED00-233A-3B2F-BAFF76D883F0}.Release|Any CPU.ActiveCfg = Release|Any CPU - {5E00F025-ED00-233A-3B2F-BAFF76D883F0}.Release|Any CPU.Build.0 = Release|Any CPU {31DBC108-839C-1442-F9AC-A39596C9E06A}.Debug|Any CPU.ActiveCfg = Debug|Any CPU {31DBC108-839C-1442-F9AC-A39596C9E06A}.Debug|Any CPU.Build.0 = Debug|Any CPU {31DBC108-839C-1442-F9AC-A39596C9E06A}.Release|Any CPU.ActiveCfg = Release|Any CPU @@ -173,6 +167,10 @@ Global {DF93C827-75A1-E6E4-D7D2-206550211A63}.Debug|Any CPU.Build.0 = Debug|Any CPU {DF93C827-75A1-E6E4-D7D2-206550211A63}.Release|Any CPU.ActiveCfg = Release|Any CPU {DF93C827-75A1-E6E4-D7D2-206550211A63}.Release|Any CPU.Build.0 = Release|Any CPU + {5E00F025-ED00-233A-3B2F-BAFF76D883F0}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {5E00F025-ED00-233A-3B2F-BAFF76D883F0}.Debug|Any CPU.Build.0 = Debug|Any CPU + {5E00F025-ED00-233A-3B2F-BAFF76D883F0}.Release|Any CPU.ActiveCfg = Release|Any CPU + {5E00F025-ED00-233A-3B2F-BAFF76D883F0}.Release|Any CPU.Build.0 = Release|Any CPU {16BB97E7-3EA9-4707-2D93-441D9C908404}.Debug|Any CPU.ActiveCfg = Debug|Any CPU {16BB97E7-3EA9-4707-2D93-441D9C908404}.Debug|Any CPU.Build.0 = Debug|Any CPU {16BB97E7-3EA9-4707-2D93-441D9C908404}.Release|Any CPU.ActiveCfg = Release|Any CPU @@ -297,10 +295,6 @@ Global {8FA0AF4D-7EF6-D3CD-F2A1-9C291AA06F3C}.Debug|Any CPU.Build.0 = Debug|Any CPU {8FA0AF4D-7EF6-D3CD-F2A1-9C291AA06F3C}.Release|Any CPU.ActiveCfg = Release|Any CPU {8FA0AF4D-7EF6-D3CD-F2A1-9C291AA06F3C}.Release|Any CPU.Build.0 = Release|Any CPU - {FABE4470-F5D8-FF10-6CF9-C03E9D2A8DBD}.Debug|Any CPU.ActiveCfg = Debug|Any CPU - {FABE4470-F5D8-FF10-6CF9-C03E9D2A8DBD}.Debug|Any CPU.Build.0 = Debug|Any CPU - {FABE4470-F5D8-FF10-6CF9-C03E9D2A8DBD}.Release|Any CPU.ActiveCfg = Release|Any CPU - {FABE4470-F5D8-FF10-6CF9-C03E9D2A8DBD}.Release|Any CPU.Build.0 = Release|Any CPU {0A1566C3-6032-C8A1-D015-8EF75B3F7099}.Debug|Any CPU.ActiveCfg = Debug|Any CPU {0A1566C3-6032-C8A1-D015-8EF75B3F7099}.Debug|Any CPU.Build.0 = Debug|Any CPU {0A1566C3-6032-C8A1-D015-8EF75B3F7099}.Release|Any CPU.ActiveCfg = Release|Any CPU