diff --git a/1.6/1.6/Assemblies/WulaFallenEmpire.dll b/1.6/1.6/Assemblies/WulaFallenEmpire.dll
index 103f7c27..e6153256 100644
Binary files a/1.6/1.6/Assemblies/WulaFallenEmpire.dll and b/1.6/1.6/Assemblies/WulaFallenEmpire.dll differ
diff --git a/1.6/1.6/Defs/AbilityDefs/WULA_Misc_Ability.xml b/1.6/1.6/Defs/AbilityDefs/WULA_Misc_Ability.xml
new file mode 100644
index 00000000..6cbc6ff9
--- /dev/null
+++ b/1.6/1.6/Defs/AbilityDefs/WULA_Misc_Ability.xml
@@ -0,0 +1,98 @@
+
+
+
+ Wula_Mech_Mobile_Factory_Produce
+
+ 立刻生产10台CRm-51"兵蚁",快速组织一道近战阵线。
+ Wula/UI/Abilities/Wula_Mech_Mobile_Factory_Produce
+ 5000
+ 5
+ true
+ true
+ 300
+ true
+ false
+
+ Verb_CastAbility
+ 24
+ 0
+ WarqueenWarUrchinsSpawned
+ false
+ false
+
+ true
+
+
+
+
+ Wula_Mech_Mobile_Factory_Produce_Proj
+ 10
+
+
+
+
+ Wula_Mech_Mobile_Factory_Produce_Proj
+
+ Projectile_SpawnsPawnZeroAge
+
+ Wula/Things/WULA_Mech_Flyer/WULA_Mech_Flyer_south
+ Graphic_Single
+
+
+ 41
+ WULA_Mech_Flyer
+ true
+ Bullet
+ 1
+
+
+
+
+ WULA_PsiCrusher
+
+ 释放纯净的灵能能量,直接摧毁面前扇形区域内的所有目标。
+ UI/Abilities/FireSpew
+ True
+ False
+ true
+ true
+ true
+ 600
+
+ Verb_CastAbility
+ 24
+ 0
+ WarqueenWarUrchinsSpawned
+
+ true
+ true
+
+ false
+
+ Mote_HellsphereCannon_Aim
+ Mote_HellsphereCannon_Charge
+ 1.07
+ 32
+ Mote_HellsphereCannon_Target
+
+
+ true
+ false
+ true
+ true
+ true
+
+
+
+
+ 12
+ 5
+ true
+ false
+ false
+ Fire_Spew
+ Fire_SpewShort
+
+
+
+
\ No newline at end of file
diff --git a/1.6/1.6/Defs/PawnKinds/PawnKinds_Wula.xml b/1.6/1.6/Defs/PawnKinds/PawnKinds_Wula.xml
index 53f61fd3..434798fd 100644
--- a/1.6/1.6/Defs/PawnKinds/PawnKinds_Wula.xml
+++ b/1.6/1.6/Defs/PawnKinds/PawnKinds_Wula.xml
@@ -231,52 +231,45 @@
1
9999~9999
-
- Wula_Mech_Mobile_Factory_Produce
-
- 立刻生产10台CRm-51"兵蚁",快速组织一道近战阵线。
- Wula/UI/Abilities/Wula_Mech_Mobile_Factory_Produce
- 5000
- 5
- true
- true
- 300
- true
- false
-
- Verb_CastAbility
- 24
- 0
- WarqueenWarUrchinsSpawned
- false
- false
-
- true
-
-
-
-
- Wula_Mech_Mobile_Factory_Produce_Proj
- 10
+
+
+ Wula_Psi_Titan
+
+ Wula_Psi_Titan
+ 1000
+ false
+ PlayerColony
+ false
+ true
+
+ Wula/Things/Wula_Mech_Mobile_Factory/Flying/Wula_Mech_Mobile_Factory_Flying_
+ 1
+ 1
+ 2
+ false
+
+
+
+
+ Wula/Things/Wula_Psi_Titan/Bodies/Naked_Thin
+ Wula/Things/WULA_Cat/AllegianceOverlays/None
+ CutoutWithOverlay
+ Graphic_Multi
+ 9
+
+ (1.4, 1.8, 1.4)
+
+
-
-
-
- Wula_Mech_Mobile_Factory_Produce_Proj
-
- Projectile_SpawnsPawnZeroAge
-
- Wula/Things/WULA_Mech_Flyer/WULA_Mech_Flyer_south
- Graphic_Single
-
-
- 41
- WULA_Mech_Flyer
- true
- Bullet
- 1
-
-
+
+ 99999~99999
+
+ 0.7
+
+
+ Wula_Mech_Mobile_Factory_Produce
+
+
diff --git a/1.6/1.6/Defs/QuestScriptDefs/WULA_Scripts_Utility.xml b/1.6/1.6/Defs/QuestScriptDefs/WULA_Scripts_Utility.xml
deleted file mode 100644
index d7377068..00000000
--- a/1.6/1.6/Defs/QuestScriptDefs/WULA_Scripts_Utility.xml
+++ /dev/null
@@ -1,21 +0,0 @@
-
-
-
-
- Util_Wula_GlobalResourceCheck
-
-
-
-
- $resourceDef
- $requiredCount
- $retryDelayTicks
- $successSignal
- $failSignal
- $deductOnSuccess
- $useInputStorage
-
-
-
-
-
\ No newline at end of file
diff --git a/1.6/1.6/Defs/ThingDefs_Races/Races_Wulaspecies.xml b/1.6/1.6/Defs/ThingDefs_Races/Races_Wulaspecies.xml
index ad153e41..66f82379 100644
--- a/1.6/1.6/Defs/ThingDefs_Races/Races_Wulaspecies.xml
+++ b/1.6/1.6/Defs/ThingDefs_Races/Races_Wulaspecies.xml
@@ -896,6 +896,10 @@
1~2
+
+ 80
+ 1
+
WULA_Bunker_Drop_Technology
@@ -1441,6 +1445,69 @@
+
+ Wula_Psi_Titan
+
+ 骇人之物
+
+ 1
+ 1
+ 35
+
+ 9999
+ 0
+
+
+ Mech_Warqueen
+ 30
+
+
+ MechanoidFullyFormed
+ 0
+ Pawn_Mech_Warqueen_Wounded
+ Pawn_Mech_Warqueen_Death
+ Pawn_Mech_Warqueen_Call
+
+
+ 25
+
+ 1
+
+
+
+
+
+
+ Blunt
+
+ 360
+ 8
+ Torso
+ true
+
+
+
+
+ 36000
+ 8.2
+ 8.4
+ 0.01
+ 4.0
+ false
+
+
+
+
+
+ Drafted
+
+
+ WULA_Hover_FlyNorth
+ WULA_Hover_FlyEast
+ WULA_Hover_FlySouth
+
+
+
diff --git a/1.6/1.6/Languages/ChineseSimplified (简体中文)/DefInjected/QuestScriptDef/Wula_Base_QuestScript.xml b/1.6/1.6/Languages/ChineseSimplified (简体中文)/DefInjected/QuestScriptDef/Wula_Base_QuestScript.xml
index 7294f7de..538507d4 100644
--- a/1.6/1.6/Languages/ChineseSimplified (简体中文)/DefInjected/QuestScriptDef/Wula_Base_QuestScript.xml
+++ b/1.6/1.6/Languages/ChineseSimplified (简体中文)/DefInjected/QuestScriptDef/Wula_Base_QuestScript.xml
@@ -60,6 +60,6 @@
questName->什一税税收
- questDescription->唯死亡和税收不可避免——按时上交什一税是乌拉帝国殖民地的光荣义务。\n\n乌拉帝国的什一税会从殖民地储存在舰队中的资产里面扣除,你可以建造<color=#6BB7B7><i>乌拉帝国物资输送舱</i></color>来将物资输送到位于轨道上的舰队。\n\n你可以快速地准备好税金,乌拉帝国对积极纳税的殖民地会给予更多关照——但是如果一直拖延,则会惹其不快,甚至有可能被定性为叛国!
+ questDescription->唯死亡和税收不可避免——按时上交什一税是乌拉帝国殖民地的光荣义务。\n\n乌拉帝国的什一税会从殖民地储存在舰队中的资产里面扣除,你可以建造<color=#6BB7B7><i>乌拉帝国物资输送舱</i></color>来将物资输送到位于轨道上的舰队。\n\n乌拉帝国对积极纳税的殖民地会给予更多关照——但是如果拖延,则每延期一天都会惹其不快,最后甚至有可能被定性为叛国!
\ No newline at end of file
diff --git a/Source/WulaFallenEmpire/Ability/WULA_AbilityAreaDestruction/CompAbilityEffect_AreaDestruction.cs b/Source/WulaFallenEmpire/Ability/WULA_AbilityAreaDestruction/CompAbilityEffect_AreaDestruction.cs
new file mode 100644
index 00000000..be21297e
--- /dev/null
+++ b/Source/WulaFallenEmpire/Ability/WULA_AbilityAreaDestruction/CompAbilityEffect_AreaDestruction.cs
@@ -0,0 +1,338 @@
+using System.Collections.Generic;
+using RimWorld;
+using UnityEngine;
+using Verse;
+
+namespace WulaFallenEmpire
+{
+ public class CompAbilityEffect_AreaDestruction : CompAbilityEffect
+ {
+ private readonly List tmpCells = new List();
+ private readonly List tmpThings = new List();
+
+ private new CompProperties_AbilityAreaDestruction Props => (CompProperties_AbilityAreaDestruction)props;
+
+ private Pawn Pawn => parent.pawn;
+
+ public override void Apply(LocalTargetInfo target, LocalTargetInfo dest)
+ {
+ base.Apply(target, dest);
+
+ Map map = parent.pawn.MapHeld;
+ if (map == null) return;
+
+ // 获取扇形区域内的所有单元格
+ List affectedCells = AffectedCells(target);
+
+ // 记录所有被影响的目标
+ List affectedTargets = new List();
+
+ foreach (IntVec3 cell in affectedCells)
+ {
+ if (!cell.InBounds(map)) continue;
+
+ // 处理该单元格中的所有事物
+ tmpThings.Clear();
+ tmpThings.AddRange(cell.GetThingList(map));
+
+ foreach (Thing thing in tmpThings)
+ {
+ if (thing == null || thing.Destroyed) continue;
+
+ // 检查是否应该影响这个目标
+ if (!ShouldAffectThing(thing)) continue;
+
+ // 添加到受影响目标列表
+ if (!affectedTargets.Contains(thing))
+ {
+ affectedTargets.Add(thing);
+ }
+
+ // 根据事物类型进行处理
+ if (thing is Building building)
+ {
+ DestroyBuilding(building);
+ }
+ else if (thing is Pawn targetPawn)
+ {
+ DestroyAllBodyParts(targetPawn);
+ }
+ }
+ }
+
+ // 为每个受影响的目标播放命中效果器
+ foreach (Thing affectedThing in affectedTargets)
+ {
+ PlayHitEffecter(affectedThing, map);
+ }
+
+ // 播放主要效果
+ if (Props.effecterDef != null)
+ {
+ Props.effecterDef.Spawn(target.Cell, map).Cleanup();
+ }
+ }
+
+ private void PlayHitEffecter(Thing target, Map map)
+ {
+ try
+ {
+ if (Props.hitEffecter == null) return;
+ if (target == null || target.Destroyed) return;
+
+ // 创建效果器
+ Effecter effecter = Props.hitEffecter.Spawn();
+
+ // 计算效果器方向(从目标指向施法者)
+ TargetInfo targetInfo = new TargetInfo(target.Position, map, false);
+ TargetInfo casterInfo = new TargetInfo(Pawn.Position, map, false);
+
+ // 触发效果器,方向朝向施法者
+ effecter.Trigger(targetInfo, casterInfo);
+
+ // 如果效果器需要持续维护,添加到维护列表
+ if (Props.hitEffecter.maintainTicks > 0)
+ {
+ map.effecterMaintainer.AddEffecterToMaintain(effecter, target, Props.hitEffecter.maintainTicks);
+ }
+ else
+ {
+ // 否则在适当时间后清理
+ LongEventHandler.ExecuteWhenFinished(delegate
+ {
+ effecter.Cleanup();
+ });
+ }
+
+ Log.Message($"[AreaDestruction] Played hit effecter on {target.Label} at {target.Position}");
+ }
+ catch (System.Exception ex)
+ {
+ Log.Warning($"[AreaDestruction] Error playing hit effecter on {target?.Label}: {ex.Message}");
+ }
+ }
+
+ private bool ShouldAffectThing(Thing thing)
+ {
+ // 检查是否影响施法者自己
+ if (thing == Pawn && !Props.affectCaster)
+ return false;
+
+ // 检查是否影响友方单位
+ if (thing is Pawn targetPawn && targetPawn.Faction != null)
+ {
+ if (!Props.affectAllies && targetPawn.Faction == Pawn.Faction)
+ return false;
+
+ // 不攻击囚犯(除非设置影响友方)
+ if (targetPawn.IsPrisoner && targetPawn.HostFaction == Pawn.Faction && !Props.affectAllies)
+ return false;
+ }
+
+ return true;
+ }
+
+ private void DestroyBuilding(Building building)
+ {
+ try
+ {
+ if (building.Destroyed || !building.Spawned) return;
+
+ // 记录建筑信息用于日志
+ string buildingInfo = $"{building.Label} at {building.Position}";
+
+ // 直接销毁建筑
+ building.Destroy(DestroyMode.Vanish);
+
+ Log.Message($"[AreaDestruction] Destroyed building: {buildingInfo}");
+ }
+ catch (System.Exception ex)
+ {
+ Log.Warning($"[AreaDestruction] Error destroying building {building?.Label}: {ex.Message}");
+ }
+ }
+
+ private void DestroyAllBodyParts(Pawn targetPawn)
+ {
+ try
+ {
+ if (targetPawn.Destroyed || !targetPawn.Spawned || targetPawn.Dead) return;
+
+ // 记录pawn信息
+ string pawnInfo = $"{targetPawn.Label} at {targetPawn.Position}";
+
+ // 获取所有身体部位(不包括核心部位如躯干、头部)
+ var bodyPartRecords = targetPawn.def.race.body.AllParts;
+
+ int partsDestroyed = 0;
+ foreach (var bodyPartRecord in bodyPartRecords)
+ {
+ // 跳过核心部位以避免立即死亡(可选,根据需求调整)
+ if (IsCoreBodyPart(bodyPartRecord)) continue;
+
+ // 检查该部位是否已经缺失
+ if (!targetPawn.health.hediffSet.PartIsMissing(bodyPartRecord))
+ {
+ // 添加缺失部位hediff
+ targetPawn.health.AddHediff(HediffDefOf.MissingBodyPart, bodyPartRecord);
+ partsDestroyed++;
+ }
+ }
+
+ // 如果摧毁了任何部位,检查是否应该杀死pawn
+ if (partsDestroyed > 0)
+ {
+ // 检查pawn是否还"活着"(没有核心部位缺失时可能还能存活)
+ CheckPawnViability(targetPawn);
+
+ Log.Message($"[AreaDestruction] Destroyed {partsDestroyed} body parts on {pawnInfo}");
+ }
+ }
+ catch (System.Exception ex)
+ {
+ Log.Warning($"[AreaDestruction] Error destroying body parts on {targetPawn?.Label}: {ex.Message}");
+ }
+ }
+
+ private bool IsCoreBodyPart(BodyPartRecord bodyPart)
+ {
+ // 定义核心部位,这些部位缺失会导致立即死亡
+ return bodyPart.def.tags.Contains(BodyPartTagDefOf.ConsciousnessSource) || // 大脑
+ bodyPart.def.tags.Contains(BodyPartTagDefOf.BloodPumpingSource) || // 心脏
+ bodyPart.def == BodyPartDefOf.Torso; // 躯干
+ }
+
+ private void CheckPawnViability(Pawn pawn)
+ {
+ // 检查pawn是否还能存活
+ if (pawn.Dead) return;
+
+ // 如果失去了所有肢体,pawn可能会倒下但不会立即死亡
+ bool hasAnyLimbs = false;
+ var allParts = pawn.def.race.body.AllParts;
+
+ foreach (var part in allParts)
+ {
+ if ((part.def.tags.Contains(BodyPartTagDefOf.MovingLimbCore) ||
+ part.def.tags.Contains(BodyPartTagDefOf.ManipulationLimbCore)) &&
+ !pawn.health.hediffSet.PartIsMissing(part))
+ {
+ hasAnyLimbs = true;
+ break;
+ }
+ }
+
+ // 如果没有肢体了,让pawn倒下
+ if (!hasAnyLimbs && pawn.health.capacities.CapableOf(PawnCapacityDefOf.Moving))
+ {
+ pawn.health.forceDowned = true;
+ pawn.health.CheckForStateChange(null, null);
+ }
+ }
+
+ public override IEnumerable GetPreCastActions()
+ {
+ if (Props.effecterDef != null)
+ {
+ yield return new PreCastAction
+ {
+ action = delegate(LocalTargetInfo a, LocalTargetInfo b)
+ {
+ parent.AddEffecterToMaintain(Props.effecterDef.Spawn(parent.pawn.Position, a.Cell, parent.pawn.Map),
+ Pawn.Position, a.Cell, 17, Pawn.MapHeld);
+ },
+ ticksAwayFromCast = 17
+ };
+ }
+ }
+
+ public override void DrawEffectPreview(LocalTargetInfo target)
+ {
+ GenDraw.DrawFieldEdges(AffectedCells(target), Color.red);
+ }
+
+ public override bool AICanTargetNow(LocalTargetInfo target)
+ {
+ if (Pawn.Faction != null && !Props.affectAllies)
+ {
+ foreach (IntVec3 cell in AffectedCells(target))
+ {
+ List thingList = cell.GetThingList(Pawn.Map);
+ for (int i = 0; i < thingList.Count; i++)
+ {
+ if (thingList[i].Faction == Pawn.Faction)
+ {
+ return false;
+ }
+ }
+ }
+ }
+ return true;
+ }
+
+ private List AffectedCells(LocalTargetInfo target)
+ {
+ tmpCells.Clear();
+ Vector3 casterPos = Pawn.Position.ToVector3Shifted().Yto0();
+ IntVec3 targetCell = target.Cell.ClampInsideMap(Pawn.Map);
+
+ if (Pawn.Position == targetCell)
+ {
+ return tmpCells;
+ }
+
+ float distance = (targetCell - Pawn.Position).LengthHorizontal;
+ float xRatio = (float)(targetCell.x - Pawn.Position.x) / distance;
+ float zRatio = (float)(targetCell.z - Pawn.Position.z) / distance;
+
+ // 计算扇形末端位置
+ targetCell.x = Mathf.RoundToInt((float)Pawn.Position.x + xRatio * Props.range);
+ targetCell.z = Mathf.RoundToInt((float)Pawn.Position.z + zRatio * Props.range);
+
+ float targetAngle = Vector3.SignedAngle(targetCell.ToVector3Shifted().Yto0() - casterPos, Vector3.right, Vector3.up);
+ float halfWidth = Props.lineWidthEnd / 2f;
+ float coneLength = Mathf.Sqrt(Mathf.Pow((targetCell - Pawn.Position).LengthHorizontal, 2f) + Mathf.Pow(halfWidth, 2f));
+ float coneAngle = 57.29578f * Mathf.Asin(halfWidth / coneLength);
+
+ // 遍历范围内的所有单元格
+ int radialCellCount = GenRadial.NumCellsInRadius(Props.range);
+ for (int i = 0; i < radialCellCount; i++)
+ {
+ IntVec3 cell = Pawn.Position + GenRadial.RadialPattern[i];
+ if (CanUseCell(cell) &&
+ Mathf.Abs(Mathf.DeltaAngle(Vector3.SignedAngle(cell.ToVector3Shifted().Yto0() - casterPos, Vector3.right, Vector3.up), targetAngle)) <= coneAngle)
+ {
+ tmpCells.Add(cell);
+ }
+ }
+
+ // 添加从施法者到目标直线上的单元格
+ List lineCells = GenSight.BresenhamCellsBetween(Pawn.Position, targetCell);
+ for (int j = 0; j < lineCells.Count; j++)
+ {
+ IntVec3 lineCell = lineCells[j];
+ if (!tmpCells.Contains(lineCell) && CanUseCell(lineCell))
+ {
+ tmpCells.Add(lineCell);
+ }
+ }
+
+ return tmpCells;
+
+ bool CanUseCell(IntVec3 c)
+ {
+ if (!c.InBounds(Pawn.Map))
+ return false;
+ if (c == Pawn.Position && !Props.affectCaster)
+ return false;
+ if (!Props.canHitFilledCells && c.Filled(Pawn.Map))
+ return false;
+ if (!c.InHorDistOf(Pawn.Position, Props.range))
+ return false;
+
+ ShootLine resultingLine;
+ return parent.verb.TryFindShootLineFromTo(Pawn.Position, c, out resultingLine);
+ }
+ }
+ }
+}
diff --git a/Source/WulaFallenEmpire/Ability/WULA_AbilityAreaDestruction/CompProperties_AbilityAreaDestruction.cs b/Source/WulaFallenEmpire/Ability/WULA_AbilityAreaDestruction/CompProperties_AbilityAreaDestruction.cs
new file mode 100644
index 00000000..f30deb0e
--- /dev/null
+++ b/Source/WulaFallenEmpire/Ability/WULA_AbilityAreaDestruction/CompProperties_AbilityAreaDestruction.cs
@@ -0,0 +1,27 @@
+using RimWorld;
+using Verse;
+
+namespace WulaFallenEmpire
+{
+ public class CompProperties_AbilityAreaDestruction : CompProperties_AbilityEffect
+ {
+ public float range;
+ public float lineWidthEnd;
+ public EffecterDef effecterDef;
+ public bool canHitFilledCells;
+
+ // 新增:命中效果器
+ public EffecterDef hitEffecter;
+
+ // 新增:是否影响友方单位
+ public bool affectAllies = false;
+
+ // 新增:是否影响施法者自己
+ public bool affectCaster = false;
+
+ public CompProperties_AbilityAreaDestruction()
+ {
+ compClass = typeof(CompAbilityEffect_AreaDestruction);
+ }
+ }
+}
diff --git a/Source/WulaFallenEmpire/HarmonyPatches/Projectile_Launch_Patch.cs b/Source/WulaFallenEmpire/HarmonyPatches/Projectile_Launch_Patch.cs
index bad0bcd3..8830ca7f 100644
--- a/Source/WulaFallenEmpire/HarmonyPatches/Projectile_Launch_Patch.cs
+++ b/Source/WulaFallenEmpire/HarmonyPatches/Projectile_Launch_Patch.cs
@@ -82,7 +82,7 @@ namespace WulaFallenEmpire.HarmonyPatches
return result;
}
- // 清理反弹计数 - 修复:使用 Thing 的 Destroy 方法
+ // 清理反弹计数
[HarmonyPatch(typeof(Thing), "Destroy")]
public static class Thing_Destroy_Patch
{
diff --git a/Source/WulaFallenEmpire/HediffComp/WULA_HediffComp_TopTurret/HediffComp_TopTurret.cs b/Source/WulaFallenEmpire/HediffComp/WULA_HediffComp_TopTurret/HediffComp_TopTurret.cs
index 3ed7b89d..3bbabbea 100644
--- a/Source/WulaFallenEmpire/HediffComp/WULA_HediffComp_TopTurret/HediffComp_TopTurret.cs
+++ b/Source/WulaFallenEmpire/HediffComp/WULA_HediffComp_TopTurret/HediffComp_TopTurret.cs
@@ -87,6 +87,200 @@ 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)
+ {
+ 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)
+ {
+ bool attackSuccess = this.AttackVerb.TryStartCastOn(this.currentTarget, false, true, false, true);
+ if (attackSuccess)
+ {
+ this.lastAttackTargetTick = Find.TickManager.TicksGame;
+ this.lastAttackedTarget = this.currentTarget;
+ }
+ else
+ {
+ // 如果手动攻击失败且目标无效,清除手动目标
+ if (HasManualTarget && !CanAttackManualTarget)
+ {
+ VolleyTargetManager.ClearVolleyTarget(Pawn);
+ }
+ }
+ return;
+ }
+ }
+ else
+ {
+ if (this.burstCooldownTicksLeft > 0)
+ {
+ this.burstCooldownTicksLeft--;
+ }
+ if (this.burstCooldownTicksLeft <= 0 && this.Pawn.IsHashIntervalTick(10))
+ {
+ // 如果手动目标无效,清除它
+ if (HasManualTarget && !CanAttackManualTarget)
+ {
+ 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.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 - 只有设置目标和清除目标按钮
+ public override IEnumerable 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.Get("UI/Gizmos/ToggleTurret"),
+ isActive = () => TurretEnabled,
+ toggleAction = () => TurretEnabled = !TurretEnabled,
+ hotKey = KeyBindingDefOf.Misc1
+ };
+ // 设置目标按钮
+ yield return new Command_Action
+ {
+ defaultLabel = "CommandSetTarget".Translate(),
+ defaultDesc = "CommandSetTargetDesc".Translate(),
+ icon = ContentFinder.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.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)
+ {
+ targetStatus += $"{manualTarget.Thing?.LabelCap ?? manualTarget.Cell.ToString()}";
+ if (!CanAttackManualTarget)
+ {
+ targetStatus += " (Unreachable)";
+ }
+ }
+ else
+ {
+ targetStatus += "None";
+ }
+ string result = turretStatus + "\n" + targetStatus;
+ return string.IsNullOrEmpty(baseString) ? result : baseString + "\n" + result;
+ }
+ }
+
// 新增:炮塔启用状态
public bool TurretEnabled
{
@@ -190,60 +384,6 @@ namespace WulaFallenEmpire
};
}
}
-
- public override void CompPostTick(ref float severityAdjustment)
- {
- base.CompPostTick(ref severityAdjustment);
-
- // 新增:只在启用状态下执行攻击逻辑
- if (!TurretEnabled)
- {
- ResetCurrentTarget();
- return;
- }
-
- if (!this.CanShoot)
- {
- return;
- }
- 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))
- {
- this.currentTarget = (Thing)AttackTargetFinder.BestShootTargetFromCurrentPosition(this, TargetScanFlags.NeedThreat | TargetScanFlags.NeedAutoTargetable, null, 0f, 9999f);
- if (this.currentTarget.IsValid)
- {
- this.burstWarmupTicksLeft = 1;
- return;
- }
- this.ResetCurrentTarget();
- }
- }
- }
- }
-
private void ResetCurrentTarget()
{
this.currentTarget = LocalTargetInfo.Invalid;
@@ -273,35 +413,6 @@ namespace WulaFallenEmpire
}
}
- // 新增:实现 Gizmo 接口
- public override IEnumerable 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.Get("UI/Gizmos/ToggleTurret"),
- isActive = () => TurretEnabled,
- toggleAction = () => TurretEnabled = !TurretEnabled,
- hotKey = KeyBindingDefOf.Misc1
- };
- }
- }
-
- // 新增:在绘制时显示状态
- public override string CompTipStringExtra
- {
- get
- {
- string baseString = base.CompTipStringExtra;
- string status = TurretEnabled ? "Turret: Active" : "Turret: Inactive";
- return string.IsNullOrEmpty(baseString) ? status : baseString + "\n" + status;
- }
- }
-
private const int StartShootIntervalTicks = 10;
private static readonly CachedTexture ToggleTurretIcon = new CachedTexture("UI/Gizmos/ToggleTurret");
diff --git a/Source/WulaFallenEmpire/HediffComp/WULA_HediffComp_TopTurret/VolleyTargetManager.cs b/Source/WulaFallenEmpire/HediffComp/WULA_HediffComp_TopTurret/VolleyTargetManager.cs
index de05cc74..61adfccc 100644
--- a/Source/WulaFallenEmpire/HediffComp/WULA_HediffComp_TopTurret/VolleyTargetManager.cs
+++ b/Source/WulaFallenEmpire/HediffComp/WULA_HediffComp_TopTurret/VolleyTargetManager.cs
@@ -15,6 +15,8 @@ namespace WulaFallenEmpire
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)
@@ -23,6 +25,8 @@ namespace WulaFallenEmpire
volleyTargets.Remove(pawn);
volleyEnabled.Remove(pawn);
+
+ Log.Message($"Cleared volley target for {pawn.Label}");
}
public static LocalTargetInfo GetVolleyTarget(Pawn pawn)
@@ -48,11 +52,36 @@ namespace WulaFallenEmpire
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;
+ }
}
}
diff --git a/Source/WulaFallenEmpire/ThingComp/CompApparelInterceptor.cs b/Source/WulaFallenEmpire/ThingComp/CompApparelInterceptor.cs
index 6b243f44..eef15cdc 100644
--- a/Source/WulaFallenEmpire/ThingComp/CompApparelInterceptor.cs
+++ b/Source/WulaFallenEmpire/ThingComp/CompApparelInterceptor.cs
@@ -2,6 +2,7 @@ using HarmonyLib;
using RimWorld;
using System;
using System.Collections.Generic;
+using System.Reflection;
using System.Text;
using UnityEngine;
using Verse;
@@ -11,11 +12,12 @@ namespace WulaFallenEmpire
{
public class CompProperties_ApparelInterceptor : CompProperties
{
- public float radius = 3f; // 仅用于视觉效果
+ public float radius = 3f;
public int startupDelay = 0;
public int rechargeDelay = 3200;
public int hitPoints = 100;
public int maxBounces = 3;
+ public float bounceRange = 15f; // 反弹射程
public bool interceptGroundProjectiles = false;
public bool interceptNonHostileProjectiles = false;
@@ -78,6 +80,267 @@ namespace WulaFallenEmpire
public CompProperties_ApparelInterceptor Props => (CompProperties_ApparelInterceptor)props;
public Pawn PawnOwner => (parent as Apparel)?.Wearer;
+
+ private void BounceProjectileNew(Projectile originalProjectile)
+ {
+ try
+ {
+ if (originalProjectile == null || originalProjectile.Destroyed)
+ return;
+ // 计算反弹方向 - 朝穿戴者前方发射
+ Vector3 bounceDirection = CalculateForwardBounceDirection();
+
+ // 计算新目标位置
+ Vector3 newDestination = PawnOwner.Position.ToVector3Shifted() + bounceDirection * Props.bounceRange;
+ IntVec3 targetCell = newDestination.ToIntVec3();
+ // 创建新的抛射体
+ Projectile newProjectile = (Projectile)ThingMaker.MakeThing(originalProjectile.def, null);
+
+ // 使用 Traverse 复制字段
+ CopyProjectileFieldsUsingTraverse(newProjectile, originalProjectile);
+ // 生成新抛射体
+ GenSpawn.Spawn(newProjectile, PawnOwner.Position, PawnOwner.Map);
+
+ // 使用 Traverse 调用 Launch 方法
+ LaunchProjectileUsingTraverse(newProjectile, targetCell, originalProjectile);
+ // 销毁原抛射体
+ originalProjectile.Destroy(DestroyMode.Vanish);
+ // 播放反弹效果
+ PlayBounceEffect(originalProjectile);
+ Log.Message($"[Interceptor] Projectile bounced forward to {targetCell}");
+ }
+ catch (Exception ex)
+ {
+ Log.Error($"Error in BounceProjectileNew: {ex}");
+ }
+ }
+ // 使用 Traverse 复制字段
+ private void CopyProjectileFieldsUsingTraverse(Projectile newProjectile, Projectile originalProjectile)
+ {
+ try
+ {
+ Traverse newTraverse = Traverse.Create(newProjectile);
+ Traverse originalTraverse = Traverse.Create(originalProjectile);
+ // 复制所有重要字段
+ newTraverse.Field("launcher").SetValue(originalTraverse.Field("launcher").GetValue());
+ newTraverse.Field("equipment").SetValue(originalTraverse.Field("equipment").GetValue());
+ newTraverse.Field("equipmentDef").SetValue(originalTraverse.Field("equipmentDef").GetValue());
+ newTraverse.Field("damageDefOverride").SetValue(originalTraverse.Field("damageDefOverride").GetValue());
+ newTraverse.Field("targetCoverDef").SetValue(originalTraverse.Field("targetCoverDef").GetValue());
+
+ // 复制额外伤害列表
+ List originalExtraDamages = originalTraverse.Field("extraDamages").GetValue>();
+ if (originalExtraDamages != null)
+ {
+ newTraverse.Field("extraDamages").SetValue(new List(originalExtraDamages));
+ }
+ }
+ catch (Exception ex)
+ {
+ Log.Warning($"Error copying projectile fields with Traverse: {ex}");
+ }
+ }
+ // 使用 Traverse 调用 Launch 方法
+ private void LaunchProjectileUsingTraverse(Projectile projectile, IntVec3 targetCell, Projectile originalProjectile)
+ {
+ try
+ {
+ Traverse projectileTraverse = Traverse.Create(projectile);
+ Traverse originalTraverse = Traverse.Create(originalProjectile);
+ // 获取 Launch 方法
+ var launchMethod = projectileTraverse.Method("Launch", new object[]
+ {
+ PawnOwner, // 发射者
+ PawnOwner.Position.ToVector3Shifted(), // 发射位置
+ new LocalTargetInfo(targetCell), // 目标位置
+ new LocalTargetInfo(targetCell), // 预期目标
+ originalProjectile.HitFlags, // 命中标志
+ originalTraverse.Field("preventFriendlyFire").GetValue(), // 防止友军伤害
+ originalTraverse.Field("equipment").GetValue(), // 装备
+ originalTraverse.Field("targetCoverDef").GetValue() // 目标覆盖定义
+ });
+ if (launchMethod.MethodExists())
+ {
+ launchMethod.GetValue();
+ }
+ else
+ {
+ Log.Error("Launch method not found using Traverse");
+ }
+ }
+ catch (Exception ex)
+ {
+ Log.Error($"Error launching projectile with Traverse: {ex}");
+ }
+ }
+ // 使用反射设置抛射体字段
+ private void SetProjectileFields(Projectile newProjectile, Projectile originalProjectile)
+ {
+ try
+ {
+ // 获取 Projectile 类型
+ Type projectileType = typeof(Projectile);
+
+ // 设置发射者
+ FieldInfo launcherField = projectileType.GetField("launcher", BindingFlags.Instance | BindingFlags.NonPublic);
+ launcherField?.SetValue(newProjectile, originalProjectile.Launcher);
+
+ // 设置装备
+ FieldInfo equipmentField = projectileType.GetField("equipment", BindingFlags.Instance | BindingFlags.NonPublic);
+ equipmentField?.SetValue(newProjectile, GetEquipment(originalProjectile));
+
+ // 设置装备定义
+ FieldInfo equipmentDefField = projectileType.GetField("equipmentDef", BindingFlags.Instance | BindingFlags.NonPublic);
+ equipmentDefField?.SetValue(newProjectile, GetEquipmentDef(originalProjectile));
+
+ // 设置伤害定义覆盖
+ FieldInfo damageDefOverrideField = projectileType.GetField("damageDefOverride", BindingFlags.Instance | BindingFlags.NonPublic);
+ damageDefOverrideField?.SetValue(newProjectile, GetDamageDefOverride(originalProjectile));
+
+ // 设置额外伤害
+ FieldInfo extraDamagesField = projectileType.GetField("extraDamages", BindingFlags.Instance | BindingFlags.NonPublic);
+ if (extraDamagesField != null)
+ {
+ List originalExtraDamages = (List)extraDamagesField.GetValue(originalProjectile);
+ if (originalExtraDamages != null)
+ {
+ List newExtraDamages = new List(originalExtraDamages);
+ extraDamagesField.SetValue(newProjectile, newExtraDamages);
+ }
+ }
+
+ // 设置目标覆盖定义
+ FieldInfo targetCoverDefField = projectileType.GetField("targetCoverDef", BindingFlags.Instance | BindingFlags.NonPublic);
+ targetCoverDefField?.SetValue(newProjectile, GetTargetCoverDef(originalProjectile));
+ }
+ catch (Exception ex)
+ {
+ Log.Warning($"Error setting projectile fields: {ex}");
+ }
+ }
+ // 使用反射调用 Launch 方法
+ private void LaunchProjectile(Projectile projectile, IntVec3 targetCell, Projectile originalProjectile)
+ {
+ try
+ {
+ Type projectileType = typeof(Projectile);
+
+ // 获取 Launch 方法
+ MethodInfo launchMethod = projectileType.GetMethod("Launch", new Type[]
+ {
+ typeof(Thing),
+ typeof(Vector3),
+ typeof(LocalTargetInfo),
+ typeof(LocalTargetInfo),
+ typeof(ProjectileHitFlags),
+ typeof(bool),
+ typeof(Thing),
+ typeof(ThingDef)
+ });
+ if (launchMethod != null)
+ {
+ // 获取原抛射体的命中标志
+ ProjectileHitFlags hitFlags = GetHitFlags(originalProjectile);
+
+ // 获取防止友军伤害设置
+ bool preventFriendlyFire = GetPreventFriendlyFire(originalProjectile);
+
+ // 调用 Launch 方法
+ launchMethod.Invoke(projectile, new object[]
+ {
+ PawnOwner, // 发射者改为护盾穿戴者
+ PawnOwner.Position.ToVector3Shifted(), // 发射位置
+ new LocalTargetInfo(targetCell), // 目标位置
+ new LocalTargetInfo(targetCell), // 预期目标
+ hitFlags,
+ preventFriendlyFire,
+ GetEquipment(originalProjectile), // 装备
+ GetTargetCoverDef(originalProjectile) // 目标覆盖定义
+ });
+ }
+ else
+ {
+ Log.Error("Could not find Launch method on Projectile");
+ }
+ }
+ catch (Exception ex)
+ {
+ Log.Error($"Error launching projectile: {ex}");
+ }
+ }
+ // 使用反射获取私有字段值
+ private Thing GetEquipment(Projectile projectile)
+ {
+ try
+ {
+ FieldInfo equipmentField = typeof(Projectile).GetField("equipment", BindingFlags.Instance | BindingFlags.NonPublic);
+ return (Thing)equipmentField?.GetValue(projectile);
+ }
+ catch
+ {
+ return null;
+ }
+ }
+ private ThingDef GetEquipmentDef(Projectile projectile)
+ {
+ try
+ {
+ FieldInfo equipmentDefField = typeof(Projectile).GetField("equipmentDef", BindingFlags.Instance | BindingFlags.NonPublic);
+ return (ThingDef)equipmentDefField?.GetValue(projectile);
+ }
+ catch
+ {
+ return null;
+ }
+ }
+ private DamageDef GetDamageDefOverride(Projectile projectile)
+ {
+ try
+ {
+ FieldInfo damageDefOverrideField = typeof(Projectile).GetField("damageDefOverride", BindingFlags.Instance | BindingFlags.NonPublic);
+ return (DamageDef)damageDefOverrideField?.GetValue(projectile);
+ }
+ catch
+ {
+ return null;
+ }
+ }
+ private ThingDef GetTargetCoverDef(Projectile projectile)
+ {
+ try
+ {
+ FieldInfo targetCoverDefField = typeof(Projectile).GetField("targetCoverDef", BindingFlags.Instance | BindingFlags.NonPublic);
+ return (ThingDef)targetCoverDefField?.GetValue(projectile);
+ }
+ catch
+ {
+ return null;
+ }
+ }
+ private ProjectileHitFlags GetHitFlags(Projectile projectile)
+ {
+ try
+ {
+ return projectile.HitFlags;
+ }
+ catch
+ {
+ return ProjectileHitFlags.All;
+ }
+ }
+ private bool GetPreventFriendlyFire(Projectile projectile)
+ {
+ try
+ {
+ FieldInfo preventFriendlyFireField = typeof(Projectile).GetField("preventFriendlyFire", BindingFlags.Instance | BindingFlags.NonPublic);
+ return preventFriendlyFireField != null && (bool)preventFriendlyFireField.GetValue(projectile);
+ }
+ catch
+ {
+ return false;
+ }
+ }
+
// 主要拦截方法
public bool TryInterceptProjectile(Projectile projectile, Thing hitThing)
{
@@ -156,8 +419,8 @@ namespace WulaFallenEmpire
}
}
- // 反弹抛射体
- BounceProjectile(projectile);
+ // 反弹抛射体 - 新方法:销毁原抛射体并创建新的
+ BounceProjectileNew(projectile);
}
catch (Exception ex)
{
@@ -165,61 +428,17 @@ namespace WulaFallenEmpire
}
}
- private void BounceProjectile(Projectile projectile)
+ private Vector3 CalculateForwardBounceDirection()
{
- try
- {
- if (projectile == null || projectile.Destroyed)
- return;
-
- // 计算反弹方向
- Vector3 bounceDirection = CalculateBounceDirection(projectile);
-
- // 使用 Traverse 修改抛射体方向
- var traverse = Traverse.Create(projectile);
-
- // 修改目的地 - 随机弹射
- Vector3 newDestination = projectile.ExactPosition + bounceDirection * 30f;
- traverse.Field("destination").SetValue(newDestination);
-
- // 重置起点为当前位置
- traverse.Field("origin").SetValue(projectile.ExactPosition);
-
- // 重新计算飞行时间
- float distance = (newDestination - projectile.ExactPosition).MagnitudeHorizontal();
- int newTicks = Mathf.CeilToInt(distance / projectile.def.projectile.SpeedTilesPerTick);
- traverse.Field("ticksToImpact").SetValue(newTicks);
-
- // 播放反弹效果
- PlayBounceEffect(projectile);
-
- Log.Message($"[Interceptor] Projectile bounced towards {bounceDirection}");
- }
- catch (Exception ex)
- {
- Log.Error($"Error in BounceProjectile: {ex}");
- }
- }
-
- private Vector3 CalculateBounceDirection(Projectile projectile)
- {
- // 如果有发射者,尝试弹向发射者
- if (projectile.Launcher != null && projectile.Launcher.Spawned)
- {
- try
- {
- Vector3 toLauncher = (projectile.Launcher.Position.ToVector3() - projectile.ExactPosition).normalized;
- return toLauncher;
- }
- catch
- {
- // 如果计算失败,使用随机方向
- }
- }
-
- // 随机弹射方向
- float angle = Rand.Range(0f, 360f);
- return Quaternion.AngleAxis(angle, Vector3.up) * Vector3.forward;
+ // 获取穿戴者的朝向
+ float pawnRotation = PawnOwner.Rotation.AsAngle;
+
+ // 添加一些随机偏移,使反弹更自然
+ float randomOffset = Rand.Range(-30f, 30f);
+ float finalAngle = pawnRotation + randomOffset;
+
+ // 转换为方向向量
+ return Quaternion.AngleAxis(finalAngle, Vector3.up) * Vector3.forward;
}
private void PlayBounceEffect(Projectile projectile)