This commit is contained in:
2025-11-20 17:31:17 +08:00
parent 60a4c6de65
commit 6dc949158a
15 changed files with 789 additions and 805 deletions

View File

@@ -17,7 +17,7 @@ namespace WulaFallenEmpire
public ThingDef turretDef;
public float angleOffset;
public bool autoAttack = true;
public bool defaultEnabled = true; // 新增:默认启用状态
public bool defaultEnabled = true;
}
[StaticConstructorOnStartup]
@@ -87,31 +87,28 @@ namespace WulaFallenEmpire
}
}
public override void CompPostTick(ref float severityAdjustment)
{
base.CompPostTick(ref severityAdjustment);
if (!TurretEnabled)
{
ResetCurrentTarget();
return;
}
if (!this.CanShoot)
{
return;
}
// 优先处理手动目标
if (HasManualTarget && CanAttackManualTarget)
{
LocalTargetInfo manualTarget = VolleyTargetManager.GetVolleyTarget(Pawn);
this.currentTarget = manualTarget;
this.curRotation = (manualTarget.Cell.ToVector3Shifted() - this.Pawn.DrawPos).AngleFlat() + this.Props.angleOffset;
}
else if (this.currentTarget.IsValid)
if (this.currentTarget.IsValid)
{
this.curRotation = (this.currentTarget.Cell.ToVector3Shifted() - this.Pawn.DrawPos).AngleFlat() + this.Props.angleOffset;
}
this.AttackVerb.VerbTick();
if (this.AttackVerb.state != VerbState.Bursting)
{
if (this.WarmingUp)
@@ -125,14 +122,6 @@ namespace WulaFallenEmpire
this.lastAttackTargetTick = Find.TickManager.TicksGame;
this.lastAttackedTarget = this.currentTarget;
}
else
{
// 如果手动攻击失败且目标无效,清除手动目标
if (HasManualTarget && !CanAttackManualTarget)
{
VolleyTargetManager.ClearVolleyTarget(Pawn);
}
}
return;
}
}
@@ -142,68 +131,30 @@ namespace WulaFallenEmpire
{
this.burstCooldownTicksLeft--;
}
if (this.burstCooldownTicksLeft <= 0 && this.Pawn.IsHashIntervalTick(10))
{
// 如果手动目标无效,清除它
if (HasManualTarget && !CanAttackManualTarget)
// 自动寻找目标
this.currentTarget = (Thing)AttackTargetFinder.BestShootTargetFromCurrentPosition(this, TargetScanFlags.NeedThreat | TargetScanFlags.NeedAutoTargetable, null, 0f, 9999f);
if (this.currentTarget.IsValid)
{
VolleyTargetManager.ClearVolleyTarget(Pawn);
}
// 只有在没有有效的手动目标时才寻找新目标
if (!HasManualTarget || !CanAttackManualTarget)
{
this.currentTarget = (Thing)AttackTargetFinder.BestShootTargetFromCurrentPosition(this, TargetScanFlags.NeedThreat | TargetScanFlags.NeedAutoTargetable, null, 0f, 9999f);
if (this.currentTarget.IsValid)
{
this.burstWarmupTicksLeft = 1;
return;
}
this.burstWarmupTicksLeft = 1;
return;
}
this.ResetCurrentTarget();
}
}
}
}
// 检查是否有手动目标
private bool HasManualTarget
{
get
{
LocalTargetInfo manualTarget = VolleyTargetManager.GetVolleyTarget(Pawn);
return manualTarget.IsValid;
}
}
// 检查是否可以攻击手动目标
private bool CanAttackManualTarget
{
get
{
LocalTargetInfo manualTarget = VolleyTargetManager.GetVolleyTarget(Pawn);
if (!manualTarget.IsValid)
return false;
// 检查目标是否在射程内
float distance = Pawn.Position.DistanceTo(manualTarget.Cell);
if (distance > AttackVerb.verbProps.range)
return false;
// 检查是否可以命中目标
if (!AttackVerb.CanHitTarget(manualTarget))
return false;
// 检查目标是否还活着(如果是生物)
if (manualTarget.Thing is Pawn targetPawn && (targetPawn.Dead || targetPawn.Downed))
return false;
// 检查目标是否被摧毁(如果是建筑)
if (manualTarget.Thing != null && manualTarget.Thing.Destroyed)
return false;
return true;
}
}
// 简化的Gizmos - 只有设置目标和清除目标按钮
// 简化的Gizmos - 只有开关按钮
public override IEnumerable<Gizmo> CompGetGizmos()
{
// 只有 pawn 被选中且是玩家派系时才显示按钮
if (this.Pawn.Faction == Faction.OfPlayer && Find.Selector.IsSelected(this.Pawn))
{
// 原有开关按钮
yield return new Command_Toggle
{
defaultLabel = "CommandToggleTurret".Translate(),
@@ -213,75 +164,33 @@ namespace WulaFallenEmpire
toggleAction = () => TurretEnabled = !TurretEnabled,
hotKey = KeyBindingDefOf.Misc1
};
// 设置目标按钮
yield return new Command_Action
{
defaultLabel = "CommandSetTarget".Translate(),
defaultDesc = "CommandSetTargetDesc".Translate(),
icon = ContentFinder<Texture2D>.Get("UI/Gizmos/SetTarget"),
action = () =>
{
Find.Targeter.BeginTargeting(
CreateTargetingParameters(),
delegate (LocalTargetInfo target)
{
VolleyTargetManager.SetVolleyTarget(Pawn, target);
},
Pawn, // caster 参数
null, // actionWhenFinished
null, // mouseAttachment
true // requiresCastedSelected
);
},
hotKey = KeyBindingDefOf.Misc2
};
// 清除目标按钮(只在有手动目标时显示)
LocalTargetInfo currentTarget = VolleyTargetManager.GetVolleyTarget(Pawn);
if (currentTarget.IsValid)
{
yield return new Command_Action
{
defaultLabel = "CommandClearTarget".Translate(),
defaultDesc = "CommandClearTargetDesc".Translate(),
icon = ContentFinder<Texture2D>.Get("UI/Gizmos/ClearTarget"),
action = () => VolleyTargetManager.ClearVolleyTarget(Pawn),
hotKey = KeyBindingDefOf.Misc3
};
}
}
}
// 创建目标参数
private TargetingParameters CreateTargetingParameters()
{
return TargetingParameters.ForThing();
}
// 在提示中显示目标状态
// 简化的提示信息
public override string CompTipStringExtra
{
get
{
string baseString = base.CompTipStringExtra;
string turretStatus = TurretEnabled ? "Turret: Active" : "Turret: Inactive";
string targetStatus = "Manual Target: ";
LocalTargetInfo manualTarget = VolleyTargetManager.GetVolleyTarget(Pawn);
if (manualTarget.IsValid)
string targetStatus = "Target: ";
if (this.currentTarget.IsValid)
{
targetStatus += $"{manualTarget.Thing?.LabelCap ?? manualTarget.Cell.ToString()}";
if (!CanAttackManualTarget)
{
targetStatus += " (Unreachable)";
}
targetStatus += $"{this.currentTarget.Thing?.LabelCap ?? this.currentTarget.Cell.ToString()}";
}
else
{
targetStatus += "None";
}
string result = turretStatus + "\n" + targetStatus;
return string.IsNullOrEmpty(baseString) ? result : baseString + "\n" + result;
}
}
// 新增:炮塔启用状态
// 炮塔启用状态
public bool TurretEnabled
{
get { return turretEnabled; }
@@ -299,7 +208,7 @@ namespace WulaFallenEmpire
{
get
{
// 新增:检查炮塔是否启用
// 检查炮塔是否启用
if (!TurretEnabled)
return false;
@@ -361,7 +270,7 @@ namespace WulaFallenEmpire
{
base.CompPostMake();
this.MakeGun();
// 新增:设置默认启用状态
// 设置默认启用状态
TurretEnabled = Props.defaultEnabled;
}
@@ -384,6 +293,7 @@ namespace WulaFallenEmpire
};
}
}
private void ResetCurrentTarget()
{
this.currentTarget = LocalTargetInfo.Invalid;
@@ -398,7 +308,7 @@ namespace WulaFallenEmpire
Scribe_TargetInfo.Look(ref this.currentTarget, "currentTarget");
Scribe_Deep.Look<Thing>(ref this.gun, "gun", Array.Empty<object>());
Scribe_Values.Look<bool>(ref this.fireAtWill, "fireAtWill", true, false);
// 新增:保存启用状态
// 保存启用状态
Scribe_Values.Look<bool>(ref this.turretEnabled, "turretEnabled", Props.defaultEnabled, false);
if (Scribe.mode == LoadSaveMode.PostLoadInit)
@@ -415,8 +325,6 @@ namespace WulaFallenEmpire
private const int StartShootIntervalTicks = 10;
private static readonly CachedTexture ToggleTurretIcon = new CachedTexture("UI/Gizmos/ToggleTurret");
public Thing gun;
protected int burstCooldownTicksLeft;
protected int burstWarmupTicksLeft;
@@ -426,10 +334,10 @@ namespace WulaFallenEmpire
private int lastAttackTargetTick;
private float curRotation;
// 新增:炮塔启用状态字段
// 炮塔启用状态字段
private bool turretEnabled = true;
[Unsaved(false)]
public Material turretMat;
}
}
}

View File

@@ -1,87 +0,0 @@
using System.Collections.Generic;
using Verse;
using RimWorld;
namespace WulaFallenEmpire
{
public static class VolleyTargetManager
{
private static Dictionary<Pawn, LocalTargetInfo> volleyTargets = new Dictionary<Pawn, LocalTargetInfo>();
private static Dictionary<Pawn, bool> volleyEnabled = new Dictionary<Pawn, bool>();
public static void SetVolleyTarget(Pawn pawn, LocalTargetInfo target)
{
if (pawn == null) return;
volleyTargets[pawn] = target;
volleyEnabled[pawn] = target.IsValid;
Log.Message($"Set volley target for {pawn.Label}: {target.Thing?.Label ?? target.Cell.ToString()}");
}
public static void ClearVolleyTarget(Pawn pawn)
{
if (pawn == null) return;
volleyTargets.Remove(pawn);
volleyEnabled.Remove(pawn);
Log.Message($"Cleared volley target for {pawn.Label}");
}
public static LocalTargetInfo GetVolleyTarget(Pawn pawn)
{
if (pawn == null || !volleyTargets.ContainsKey(pawn))
return LocalTargetInfo.Invalid;
return volleyTargets[pawn];
}
public static bool IsVolleyEnabled(Pawn pawn)
{
if (pawn == null || !volleyEnabled.ContainsKey(pawn))
return false;
return volleyEnabled[pawn];
}
public static void ToggleVolley(Pawn pawn)
{
if (pawn == null) return;
bool current = IsVolleyEnabled(pawn);
volleyEnabled[pawn] = !current;
Log.Message($"Toggled volley for {pawn.Label}: {!current}");
// 如果禁用齐射,清除目标
if (!volleyEnabled[pawn])
{
ClearVolleyTarget(pawn);
}
}
// 新增:检查齐射目标是否仍然有效
public static bool IsVolleyTargetValid(Pawn pawn)
{
if (!IsVolleyEnabled(pawn))
return false;
LocalTargetInfo target = GetVolleyTarget(pawn);
if (!target.IsValid)
return false;
// 检查目标是否还活着/存在
if (target.Thing != null)
{
if (target.Thing.Destroyed)
return false;
if (target.Thing is Pawn targetPawn && (targetPawn.Dead || targetPawn.Downed))
return false;
}
return true;
}
}
}

View File

@@ -1,462 +0,0 @@
## 🤖 Assistant
我来帮您为这两个炮塔类添加齐射目标功能。我们需要创建一个共享的目标系统,让所有炮塔可以协同攻击同一个目标。
## 1. 首先创建共享目标管理器
```csharp
using System.Collections.Generic;
using Verse;
using RimWorld;
namespace WulaFallenEmpire
{
public static class VolleyTargetManager
{
private static Dictionary<Pawn, LocalTargetInfo> volleyTargets = new Dictionary<Pawn, LocalTargetInfo>();
private static Dictionary<Pawn, bool> volleyEnabled = new Dictionary<Pawn, bool>();
public static void SetVolleyTarget(Pawn pawn, LocalTargetInfo target)
{
if (pawn == null) return;
volleyTargets[pawn] = target;
volleyEnabled[pawn] = target.IsValid;
}
public static void ClearVolleyTarget(Pawn pawn)
{
if (pawn == null) return;
volleyTargets.Remove(pawn);
volleyEnabled.Remove(pawn);
}
public static LocalTargetInfo GetVolleyTarget(Pawn pawn)
{
if (pawn == null || !volleyTargets.ContainsKey(pawn))
return LocalTargetInfo.Invalid;
return volleyTargets[pawn];
}
public static bool IsVolleyEnabled(Pawn pawn)
{
if (pawn == null || !volleyEnabled.ContainsKey(pawn))
return false;
return volleyEnabled[pawn];
}
public static void ToggleVolley(Pawn pawn)
{
if (pawn == null) return;
bool current = IsVolleyEnabled(pawn);
volleyEnabled[pawn] = !current;
// 如果禁用齐射,清除目标
if (!volleyEnabled[pawn])
{
ClearVolleyTarget(pawn);
}
}
}
}
```
## 2. 修改 HediffComp_TopTurret 添加齐射功能
```csharp
using System;
using System.Collections.Generic;
using UnityEngine;
using Verse;
using Verse.AI;
using RimWorld;
namespace WulaFallenEmpire
{
[StaticConstructorOnStartup]
public class HediffComp_TopTurret : HediffComp, IAttackTargetSearcher
{
// ... 保留原有属性和字段 ...
// 新增:检查是否应该攻击齐射目标
private bool ShouldAttackVolleyTarget
{
get
{
if (!VolleyTargetManager.IsVolleyEnabled(Pawn))
return false;
LocalTargetInfo volleyTarget = VolleyTargetManager.GetVolleyTarget(Pawn);
if (!volleyTarget.IsValid)
return false;
// 检查目标是否在射程内
float distance = Pawn.Position.DistanceTo(volleyTarget.Cell);
if (distance > AttackVerb.verbProps.range)
return false;
// 检查是否可以命中目标
return AttackVerb.CanHitTarget(volleyTarget);
}
}
public override void CompPostTick(ref float severityAdjustment)
{
base.CompPostTick(ref severityAdjustment);
if (!TurretEnabled)
{
ResetCurrentTarget();
return;
}
if (!this.CanShoot)
{
return;
}
// 新增:优先处理齐射目标
if (ShouldAttackVolleyTarget)
{
LocalTargetInfo volleyTarget = VolleyTargetManager.GetVolleyTarget(Pawn);
this.currentTarget = volleyTarget;
this.curRotation = (volleyTarget.Cell.ToVector3Shifted() - this.Pawn.DrawPos).AngleFlat() + this.Props.angleOffset;
}
else if (this.currentTarget.IsValid)
{
this.curRotation = (this.currentTarget.Cell.ToVector3Shifted() - this.Pawn.DrawPos).AngleFlat() + this.Props.angleOffset;
}
this.AttackVerb.VerbTick();
if (this.AttackVerb.state != VerbState.Bursting)
{
if (this.WarmingUp)
{
this.burstWarmupTicksLeft--;
if (this.burstWarmupTicksLeft == 0)
{
this.AttackVerb.TryStartCastOn(this.currentTarget, false, true, false, true);
this.lastAttackTargetTick = Find.TickManager.TicksGame;
this.lastAttackedTarget = this.currentTarget;
return;
}
}
else
{
if (this.burstCooldownTicksLeft > 0)
{
this.burstCooldownTicksLeft--;
}
if (this.burstCooldownTicksLeft <= 0 && this.Pawn.IsHashIntervalTick(10))
{
// 只有在没有齐射目标时才寻找新目标
if (!ShouldAttackVolleyTarget)
{
this.currentTarget = (Thing)AttackTargetFinder.BestShootTargetFromCurrentPosition(this, TargetScanFlags.NeedThreat | TargetScanFlags.NeedAutoTargetable, null, 0f, 9999f);
if (this.currentTarget.IsValid)
{
this.burstWarmupTicksLeft = 1;
return;
}
}
this.ResetCurrentTarget();
}
}
}
}
// 新增齐射Gizmos
public override IEnumerable<Gizmo> CompGetGizmos()
{
// 只有 pawn 被选中且是玩家派系时才显示按钮
if (this.Pawn.Faction == Faction.OfPlayer && Find.Selector.IsSelected(this.Pawn))
{
// 原有开关按钮
yield return new Command_Toggle
{
defaultLabel = "CommandToggleTurret".Translate(),
defaultDesc = "CommandToggleTurretDesc".Translate(),
icon = ContentFinder<Texture2D>.Get("UI/Gizmos/ToggleTurret"),
isActive = () => TurretEnabled,
toggleAction = () => TurretEnabled = !TurretEnabled,
hotKey = KeyBindingDefOf.Misc1
};
// 新增:齐射开关按钮
yield return new Command_Toggle
{
defaultLabel = "CommandToggleVolley".Translate(),
defaultDesc = "CommandToggleVolleyDesc".Translate(),
icon = ContentFinder<Texture2D>.Get("UI/Gizmos/VolleyFire"),
isActive = () => VolleyTargetManager.IsVolleyEnabled(Pawn),
toggleAction = () => VolleyTargetManager.ToggleVolley(Pawn),
hotKey = KeyBindingDefOf.Misc2
};
// 新增:设置齐射目标按钮(只在齐射启用时显示)
if (VolleyTargetManager.IsVolleyEnabled(Pawn))
{
yield return new Command_Action
{
defaultLabel = "CommandSetVolleyTarget".Translate(),
defaultDesc = "CommandSetVolleyTargetDesc".Translate(),
icon = ContentFinder<Texture2D>.Get("UI/Gizmos/SetTarget"),
action = () => Find.Targeter.BeginTargeting(TargetingParameters.ForAttack(),
delegate(LocalTargetInfo target)
{
VolleyTargetManager.SetVolleyTarget(Pawn, target);
},
null,
null,
"SetVolleyTarget".Translate()),
hotKey = KeyBindingDefOf.Misc3
};
// 新增:清除齐射目标按钮
LocalTargetInfo currentVolleyTarget = VolleyTargetManager.GetVolleyTarget(Pawn);
if (currentVolleyTarget.IsValid)
{
yield return new Command_Action
{
defaultLabel = "CommandClearVolleyTarget".Translate(),
defaultDesc = "CommandClearVolleyTargetDesc".Translate(),
icon = ContentFinder<Texture2D>.Get("UI/Gizmos/ClearTarget"),
action = () => VolleyTargetManager.ClearVolleyTarget(Pawn),
hotKey = KeyBindingDefOf.Misc4
};
}
}
}
}
// 新增:在提示中显示齐射状态
public override string CompTipStringExtra
{
get
{
string baseString = base.CompTipStringExtra;
string turretStatus = TurretEnabled ? "Turret: Active" : "Turret: Inactive";
string volleyStatus = "Volley: ";
if (VolleyTargetManager.IsVolleyEnabled(Pawn))
{
LocalTargetInfo volleyTarget = VolleyTargetManager.GetVolleyTarget(Pawn);
if (volleyTarget.IsValid)
{
volleyStatus += $"Targeting {volleyTarget.Thing?.LabelCap ?? volleyTarget.Cell.ToString()}";
}
else
{
volleyStatus += "Enabled (No Target)";
}
}
else
{
volleyStatus += "Disabled";
}
string result = turretStatus + "\n" + volleyStatus;
return string.IsNullOrEmpty(baseString) ? result : baseString + "\n" + result;
}
}
// ... 保留其他原有代码 ...
}
}
```
## 3. 修改 Comp_MultiTurretGun 添加齐射功能
```csharp
using System;
using System.Collections.Generic;
using Verse;
using Verse.AI;
using RimWorld;
using UnityEngine;
namespace WulaFallenEmpire
{
public class Comp_MultiTurretGun : CompTurretGun
{
// ... 保留原有属性和字段 ...
// 新增:检查是否应该攻击齐射目标
private bool ShouldAttackVolleyTarget
{
get
{
if (parent is not Pawn pawn)
return false;
if (!VolleyTargetManager.IsVolleyEnabled(pawn))
return false;
LocalTargetInfo volleyTarget = VolleyTargetManager.GetVolleyTarget(pawn);
if (!volleyTarget.IsValid)
return false;
// 检查目标是否在射程内
float distance = parent.Position.DistanceTo(volleyTarget.Cell);
if (distance > AttackVerb.verbProps.range)
return false;
// 检查是否可以命中目标
return AttackVerb.CanHitTarget(volleyTarget);
}
}
public override void CompTick()
{
base.CompTick();
// 新增:优先处理齐射目标
if (ShouldAttackVolleyTarget && parent is Pawn pawn)
{
LocalTargetInfo volleyTarget = VolleyTargetManager.GetVolleyTarget(pawn);
currentTarget = volleyTarget;
curRotation = (volleyTarget.Cell.ToVector3Shifted() - parent.DrawPos).AngleFlat() + Props.angleOffset;
}
else if (!currentTarget.IsValid && burstCooldownTicksLeft <= 0)
{
// 在其他情况下没有目标且冷却结束时也回正
curRotation = parent.Rotation.AsAngle + Props.angleOffset;
}
}
// 新增:重写目标选择逻辑以支持齐射
protected override void TryStartShootSomething(bool canBeginBurstImmediately)
{
// 优先检查齐射目标
if (ShouldAttackVolleyTarget && parent is Pawn pawn)
{
LocalTargetInfo volleyTarget = VolleyTargetManager.GetVolleyTarget(pawn);
if (volleyTarget.IsValid)
{
currentTarget = volleyTarget;
if (canBeginBurstImmediately)
{
BeginBurst();
}
else
{
burstWarmupTicksLeft = AttackVerb.verbProps.warmupTime.SecondsToTicks();
}
return;
}
}
// 如果没有齐射目标,使用原逻辑
base.TryStartShootSomething(canBeginBurstImmediately);
}
// 新增Gizmos
public override IEnumerable<Gizmo> CompGetGizmos()
{
foreach (Gizmo gizmo in base.CompGetGizmos())
{
yield return gizmo;
}
// 只有 parent 是 pawn 且是玩家派系时才显示齐射按钮
if (parent is Pawn pawn && pawn.Faction == Faction.OfPlayer && Find.Selector.IsSelected(parent))
{
// 齐射开关按钮
yield return new Command_Toggle
{
defaultLabel = "CommandToggleVolley".Translate(),
defaultDesc = "CommandToggleVolleyDesc".Translate(),
icon = ContentFinder<Texture2D>.Get("UI/Gizmos/VolleyFire"),
isActive = () => VolleyTargetManager.IsVolleyEnabled(pawn),
toggleAction = () => VolleyTargetManager.ToggleVolley(pawn),
hotKey = KeyBindingDefOf.Misc2
};
// 设置齐射目标按钮(只在齐射启用时显示)
if (VolleyTargetManager.IsVolleyEnabled(pawn))
{
yield return new Command_Action
{
defaultLabel = "CommandSetVolleyTarget".Translate(),
defaultDesc = "CommandSetVolleyTargetDesc".Translate(),
icon = ContentFinder<Texture2D>.Get("UI/Gizmos/SetTarget"),
action = () => Find.Targeter.BeginTargeting(TargetingParameters.ForAttack(),
delegate(LocalTargetInfo target)
{
VolleyTargetManager.SetVolleyTarget(pawn, target);
},
null,
null,
"SetVolleyTarget".Translate()),
hotKey = KeyBindingDefOf.Misc3
};
// 清除齐射目标按钮
LocalTargetInfo currentVolleyTarget = VolleyTargetManager.GetVolleyTarget(pawn);
if (currentVolleyTarget.IsValid)
{
yield return new Command_Action
{
defaultLabel = "CommandClearVolleyTarget".Translate(),
defaultDesc = "CommandClearVolleyTargetDesc".Translate(),
icon = ContentFinder<Texture2D>.Get("UI/Gizmos/ClearTarget"),
action = () => VolleyTargetManager.ClearVolleyTarget(pawn),
hotKey = KeyBindingDefOf.Misc4
};
}
}
}
}
// ... 保留其他原有代码 ...
}
}
```
## 4. 添加翻译字符串
在您的语言文件中添加:
```xml
<!-- 在 Keyed 文件夹的 XML 文件中 -->
<CommandToggleVolley>Volley Fire</CommandToggleVolley>
<CommandToggleVolleyDesc>Enable or disable coordinated volley fire for all turrets.</CommandToggleVolleyDesc>
<CommandSetVolleyTarget>Set Volley Target</CommandSetVolleyTarget>
<CommandSetVolleyTargetDesc>Set a coordinated target for all turrets to focus fire.</CommandSetVolleyTargetDesc>
<CommandClearVolleyTarget>Clear Volley Target</CommandClearVolleyTarget>
<CommandClearVolleyTargetDesc>Clear the coordinated volley target.</CommandClearVolleyTargetDesc>
<SetVolleyTarget>Set volley target</SetVolleyTarget>
```
## 5. 需要准备的图标
您需要在 `Textures/UI/Gizmos/` 文件夹中准备以下图标:
- `VolleyFire.png` - 齐射开关图标
- `SetTarget.png` - 设置目标图标
- `ClearTarget.png` - 清除目标图标
## 功能说明
### 工作流程:
1. **启用齐射模式**:点击齐射开关按钮启用协同攻击
2. **设置目标**:在齐射模式下点击设置目标按钮,选择要攻击的目标
3. **自动协同**:所有炮塔会优先攻击齐射目标,如果目标在射程内且可攻击
4. **智能回退**:如果齐射目标无效或不在射程内,炮塔会恢复原来自动攻击逻辑
5. **独立控制**:每个 pawn 有独立的齐射目标和开关状态
### 优先级系统:
1. 齐射目标(如果启用且有效)
2. 原有自动攻击目标
3. 空闲状态
### 优势:
- **协同攻击**:所有炮塔集中火力攻击同一目标
- **灵活控制**:可以随时启用/禁用齐射模式
- **兼容性**:不影响原有的单个炮塔开关功能
- **智能判断**:自动检查射程和攻击可行性
这样您就为两个炮塔类添加了完整的齐射目标功能,让多个炮塔可以协同攻击同一个目标!