diff --git a/1.6/1.6/Assemblies/WulaFallenEmpire.dll b/1.6/1.6/Assemblies/WulaFallenEmpire.dll
index 01c46003..981c5099 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/DesignationCategoryDefs/WULA_DesignationCategoryDefs.xml b/1.6/1.6/Defs/DesignationCategoryDefs/WULA_DesignationCategoryDefs.xml
index 174a5ac3..9799c002 100644
--- a/1.6/1.6/Defs/DesignationCategoryDefs/WULA_DesignationCategoryDefs.xml
+++ b/1.6/1.6/Defs/DesignationCategoryDefs/WULA_DesignationCategoryDefs.xml
@@ -9,6 +9,7 @@
Designator_Cancel
Designator_Deconstruct
+ WulaFallenEmpire.Designator_CallSkyfallerInArea
diff --git a/1.6/1.6/Defs/ThingDefs_Buildings/WULA_Drop_Buildings.xml b/1.6/1.6/Defs/ThingDefs_Buildings/WULA_Drop_Buildings.xml
index b17ca639..8d85ceac 100644
--- a/1.6/1.6/Defs/ThingDefs_Buildings/WULA_Drop_Buildings.xml
+++ b/1.6/1.6/Defs/ThingDefs_Buildings/WULA_Drop_Buildings.xml
@@ -9,6 +9,7 @@
MinifiedThing
Normal
None
+
WulaWall
@@ -154,10 +155,6 @@
Bomb
0.01
-
- Thump
- 0.1
-
@@ -347,15 +344,11 @@
true
-
+
Bomb
0.01
-
- Thump
- 0.01
-
true
@@ -561,6 +554,19 @@
Light
0.20
Misc1
+
+
+ Bomb
+ 0.01
+
+
+
+
+
+ true
+ false
+
+
diff --git a/1.6/1.6/Defs/ThingDefs_Buildings/WULA_Turret_Buildings.xml b/1.6/1.6/Defs/ThingDefs_Buildings/WULA_Turret_Buildings.xml
index c988fd93..aade8b90 100644
--- a/1.6/1.6/Defs/ThingDefs_Buildings/WULA_Turret_Buildings.xml
+++ b/1.6/1.6/Defs/ThingDefs_Buildings/WULA_Turret_Buildings.xml
@@ -1,5 +1,192 @@
+
+
+ Wula_Sonar_Mine_Cleanzone
+
+ 清理出一块场地并准备好资源,使得乌拉帝国可以向此处投放建筑,建造好的信标可以收起或移至他处。\n\nTAm-1"鹅卵石"感应地雷是一种危险的地雷,它们通常由乌拉帝国的工程部队部署到战场上,拥有智能敌我识别能力,在检测到敌军活动时会将自身的战斗部直接向敌人的位置发射。
+ Wula/Building/Wula_Base_ATGun_Turret
+ MinifiedThing
+ Normal
+ None
+
+ Wula_Sonar_Mine
+
+
+ BuildingsMisc
+
+
+ Wula/Building/WULA_Dropping_Building_Cleanzone
+ Graphic_Multi
+ (1,1)
+
+ false
+
+
+ Building
+ PassThroughOnly
+ 0
+ false
+ 0.5
+ false
+ false
+ false
+ Light
+
+ WULA_Turret_Base_AT_Technology
+
+
+ 0
+ 1
+ 0
+ 1
+ 0
+
+ (1,1)
+ 0
+ 0
+
+ 2
+ 5
+
+
+ BuildingDestroyed_Metal_Small
+
+
+ WulaFallenEmpire.PlaceWorker_CustomRadius
+
+ WULA_Buildings
+
+
+ 15
+ (1, 1, 1)
+ 0
+ true
+
+ 在该建筑空降到指定地点时,地雷能够监测敌军动向的最大射程。
+ true
+
+
+ Wula_Sonar_Mine_Incoming
+ true
+ 1
+ true
+ false
+
+
+
+
+ Wula_Sonar_Mine_Incoming
+
+ (1,1)
+
+ Wula/Building/Wula_Base_ATGun_Turret_Incoming
+ Graphic_Single
+ CutoutFlying
+ (1,1)
+
+
+ Accelerate
+ Things/Skyfaller/SkyfallerShadowDropPod
+ (1, 1)
+ DropPod_Fall
+ 100
+ Explosion_Vaporize
+ 0.05
+ 1
+ 1
+
+
+ (0,0)
+ (1, 1)
+
+
+ Wula_Sonar_Mine
+
+
+
+ Smoke_Joint
+
+
+
+
+ Wula_Sonar_Mine
+
+ 一种危险的地雷,它们通常由乌拉帝国的工程部队部署到战场上,拥有智能敌我识别能力,在检测到敌军活动时会将自身的战斗部直接向敌人的位置发射。
+
+ Graphic_Single
+
+ (0.1,0,0.8,0.3)
+
+
+ (0,-0.14)
+ Building
+ false
+ Normal
+ false
+ MinifiedThing
+ (1,1)
+ false
+
+ 2
+ 5
+
+ 40
+
+ BuildingsSecurity
+
+
+ 2
+ 40
+ 1400
+ 1
+ -4
+ 0
+
+ Security
+
+ true
+ false
+ false
+
+
+ PlaceWorker_NeverAdjacentTrap
+
+
+
+ 3.9
+ Bomb
+ 0.2
+ Filth_BlastMark
+ 15
+
+ Bullet
+ Arrow
+ ArrowHighVelocity
+
+
+
+ 15
+ 60
+ Bullet_Wula_AI_Heavy_Panzer_Main_Weapon
+ false
+ true
+ 1
+ false
+
+
+
+
+
+
+ true
+ false
+
+
+
+
WULA_Cat_Bunker_Cleanzone
diff --git a/1.6/1.6/Defs/ThingDefs_Misc/Weapons/WULA_FE_Machine_Weapon.xml b/1.6/1.6/Defs/ThingDefs_Misc/Weapons/WULA_FE_Machine_Weapon.xml
index a824124a..f8f71bb9 100644
--- a/1.6/1.6/Defs/ThingDefs_Misc/Weapons/WULA_FE_Machine_Weapon.xml
+++ b/1.6/1.6/Defs/ThingDefs_Misc/Weapons/WULA_FE_Machine_Weapon.xml
@@ -551,7 +551,7 @@
12
12
7
- true
+ false
8
WULA_RW_Rocket_Shootingsound
GunTail_Heavy
diff --git a/1.6/1.6/Languages/ChineseSimplified (简体中文)/Keyed/Misc_Gameplay.xml b/1.6/1.6/Languages/ChineseSimplified (简体中文)/Keyed/Misc_Gameplay.xml
index 35600bec..48812ac1 100644
--- a/1.6/1.6/Languages/ChineseSimplified (简体中文)/Keyed/Misc_Gameplay.xml
+++ b/1.6/1.6/Languages/ChineseSimplified (简体中文)/Keyed/Misc_Gameplay.xml
@@ -412,4 +412,6 @@
已取出 {0} 白银,它们会随着下一次成品空投一起空投。
无法取出白银
取出白银时发生错误
+
+ 感应地雷已启动!
\ No newline at end of file
diff --git a/Source/WulaFallenEmpire/BuildingComp/WULA_SkyfallerCaller/CompSkyfallerCaller.cs b/Source/WulaFallenEmpire/BuildingComp/WULA_SkyfallerCaller/CompSkyfallerCaller.cs
index d0dac7b0..97387825 100644
--- a/Source/WulaFallenEmpire/BuildingComp/WULA_SkyfallerCaller/CompSkyfallerCaller.cs
+++ b/Source/WulaFallenEmpire/BuildingComp/WULA_SkyfallerCaller/CompSkyfallerCaller.cs
@@ -10,7 +10,7 @@ namespace WulaFallenEmpire
{
public class CompSkyfallerCaller : ThingComp
{
- protected CompProperties_SkyfallerCaller Props => (CompProperties_SkyfallerCaller)props;
+ public CompProperties_SkyfallerCaller Props => (CompProperties_SkyfallerCaller)props;
private WulaSkyfallerWorldComponent _worldComponent;
private WulaSkyfallerWorldComponent WorldComp
@@ -38,9 +38,9 @@ namespace WulaFallenEmpire
}
}
- private bool used = false;
+ public bool used = false;
private int callTick = -1;
- private bool calling = false;
+ public bool calling = false;
private bool usedGlobalStorage = false;
public bool autoCallScheduled = false; // 新增:标记是否已安排自动呼叫
@@ -116,7 +116,7 @@ namespace WulaFallenEmpire
}
}
- private bool CheckRoofConditions
+ public bool CheckRoofConditions
{
get
{
@@ -620,7 +620,7 @@ namespace WulaFallenEmpire
}
// 保留原有的 HasEnoughMaterials 方法用于 Gizmo 显示
- protected bool HasEnoughMaterials()
+ public bool HasEnoughMaterials()
{
if (DebugSettings.godMode) return true;
diff --git a/Source/WulaFallenEmpire/BuildingComp/WULA_TrapLauncher/CompProperties_TrapLauncher.cs b/Source/WulaFallenEmpire/BuildingComp/WULA_TrapLauncher/CompProperties_TrapLauncher.cs
new file mode 100644
index 00000000..e42436b8
--- /dev/null
+++ b/Source/WulaFallenEmpire/BuildingComp/WULA_TrapLauncher/CompProperties_TrapLauncher.cs
@@ -0,0 +1,51 @@
+using System.Collections.Generic;
+using Verse;
+
+namespace WulaFallenEmpire
+{
+ public class CompProperties_TrapLauncher : CompProperties
+ {
+ public float detectionRadius = 10f; // 检测半径
+ public int scanIntervalTicks = 60; // 扫描间隔(ticks)
+ public ThingDef projectileDef; // 抛射体定义
+ public bool ignoreNonHostilePawns = true; // 是否忽略非敌对Pawn
+ public bool requireLineOfSight = true; // 是否需要视线
+ public int maxTargets = 1; // 最大目标数量
+ public int warmupTicks = 30; // 发射前预热ticks
+ public bool showDetectionRadius = true; // 是否显示检测半径
+
+ // 触发时的音效
+ public SoundDef triggerSound;
+ public SoundDef launchSound;
+ public SoundDef selfDestructSound;
+
+ // 视觉效果
+ public EffecterDef triggerEffect;
+ public EffecterDef launchEffect;
+ public EffecterDef selfDestructEffect;
+
+ // 扩展选项
+ public bool canRetarget = false; // 发射后是否可以重新锁定新目标
+ public int burstCount = 1; // 连发数量
+ public float burstDelay = 0.1f; // 连发延迟
+ public bool targetBuildings = false; // 是否瞄准建筑
+
+ public CompProperties_TrapLauncher()
+ {
+ this.compClass = typeof(CompTrapLauncher);
+ }
+
+ public override IEnumerable ConfigErrors(ThingDef parentDef)
+ {
+ foreach (var error in base.ConfigErrors(parentDef))
+ {
+ yield return error;
+ }
+
+ if (projectileDef == null)
+ {
+ yield return $"CompProperties_TrapLauncher: projectileDef must be set for {parentDef.defName}";
+ }
+ }
+ }
+}
diff --git a/Source/WulaFallenEmpire/BuildingComp/WULA_TrapLauncher/CompTrapLauncher.cs b/Source/WulaFallenEmpire/BuildingComp/WULA_TrapLauncher/CompTrapLauncher.cs
new file mode 100644
index 00000000..156d5985
--- /dev/null
+++ b/Source/WulaFallenEmpire/BuildingComp/WULA_TrapLauncher/CompTrapLauncher.cs
@@ -0,0 +1,462 @@
+using RimWorld;
+using System.Collections.Generic;
+using System.Linq;
+using UnityEngine;
+using Verse;
+using Verse.Sound;
+using static UnityEngine.GraphicsBuffer;
+
+namespace WulaFallenEmpire
+{
+ public class CompTrapLauncher : ThingComp
+ {
+ public CompProperties_TrapLauncher Props => (CompProperties_TrapLauncher)props;
+
+ private int scanTickCounter = 0;
+ private bool hasTriggered = false;
+ private Pawn currentTarget = null;
+ private int warmupCounter = 0;
+ private bool isWarmingUp = false;
+ private int burstCounter = 0;
+
+ // 已检测过的目标(避免重复攻击)
+ private HashSet detectedTargets = new HashSet();
+
+ // 用于绘制检测范围
+ private Material cachedRadiusMat;
+ private Material RadiusMat
+ {
+ get
+ {
+ if (cachedRadiusMat == null)
+ {
+ cachedRadiusMat = SolidColorMaterials.SimpleSolidColorMaterial(
+ new Color(1f, 0.2f, 0.2f, 0.1f));
+ }
+ return cachedRadiusMat;
+ }
+ }
+
+ public override void CompTick()
+ {
+ base.CompTick();
+
+ if (!parent.Spawned || hasTriggered)
+ return;
+
+ // 预热计数
+ if (isWarmingUp)
+ {
+ warmupCounter++;
+ if (warmupCounter >= Props.warmupTicks)
+ {
+ LaunchProjectile();
+ }
+ return;
+ }
+
+ // 扫描计数
+ scanTickCounter++;
+ if (scanTickCounter >= Props.scanIntervalTicks)
+ {
+ scanTickCounter = 0;
+ ScanForTargets();
+ }
+ }
+
+ ///
+ /// 扫描范围内的敌对目标
+ ///
+ private void ScanForTargets()
+ {
+ if (!parent.Spawned)
+ return;
+
+ Map map = parent.Map;
+ IntVec3 center = parent.Position;
+
+ // 获取范围内的所有Pawn
+ List pawnsInRange = new List();
+
+ foreach (IntVec3 cell in GenRadial.RadialCellsAround(center, Props.detectionRadius, true))
+ {
+ if (!cell.InBounds(map))
+ continue;
+
+ // 检查视线(如果需要)
+ if (Props.requireLineOfSight)
+ {
+ if (!GenSight.LineOfSight(center, cell, map, skipFirstCell: true))
+ continue;
+ }
+
+ // 获取格子上的所有Pawn
+ List things = map.thingGrid.ThingsListAtFast(cell);
+ foreach (Thing thing in things)
+ {
+ if (thing is Pawn pawn && !detectedTargets.Contains(pawn))
+ {
+ // 检查是否敌对
+ if (IsValidTarget(pawn))
+ {
+ pawnsInRange.Add(pawn);
+ }
+ }
+ }
+ }
+
+ // 如果没有目标,直接返回
+ if (pawnsInRange.Count == 0)
+ return;
+
+ // 选择第一个目标
+ currentTarget = pawnsInRange[0];
+ detectedTargets.Add(currentTarget);
+
+ // 触发陷阱
+ TriggerTrap();
+ }
+
+ ///
+ /// 检查是否为有效目标
+ ///
+ private bool IsValidTarget(Pawn pawn)
+ {
+ if (pawn == null || pawn.Dead || pawn.Downed)
+ return false;
+
+ if (Props.ignoreNonHostilePawns)
+ {
+ // 检查是否为敌对派系
+ if (pawn.Faction == null || !pawn.Faction.HostileTo(Faction.OfPlayer))
+ return false;
+
+ // 检查是否为自然敌对生物
+ if (!pawn.Faction.IsPlayer && !pawn.Faction.HostileTo(Faction.OfPlayer))
+ {
+ // 检查是否为敌对性生物(如人形生物巢穴的敌人)
+ if (!pawn.RaceProps.Humanlike && !pawn.def.race.Animal)
+ return false;
+ }
+ }
+
+ // 检查是否为机械体(如果设定)
+ // 这里可以根据需要添加更多过滤条件
+
+ return true;
+ }
+
+ ///
+ /// 触发陷阱
+ ///
+ private void TriggerTrap()
+ {
+ if (hasTriggered)
+ return;
+
+ // 播放触发音效
+ if (Props.triggerSound != null)
+ {
+ Props.triggerSound.PlayOneShot(new TargetInfo(parent.Position, parent.Map));
+ }
+
+ // 播放触发特效
+ if (Props.triggerEffect != null)
+ {
+ Effecter effecter = Props.triggerEffect.Spawn();
+ effecter.Trigger(parent, parent);
+ effecter.Cleanup();
+ }
+
+ // 发送消息
+ if (currentTarget != null)
+ {
+ Messages.Message("WULA_TrapLauncherTriggered".Translate(
+ parent.Label,
+ currentTarget.LabelShort
+ ), parent, MessageTypeDefOf.ThreatBig);
+ }
+
+ // 开始预热
+ isWarmingUp = true;
+ warmupCounter = 0;
+ burstCounter = 0;
+ }
+
+ ///
+ /// 发射抛射体
+ ///
+ private void LaunchProjectile()
+ {
+ if (currentTarget == null || !currentTarget.Spawned)
+ {
+ // 如果目标无效,尝试寻找新目标
+ if (Props.canRetarget)
+ {
+ currentTarget = FindNewTarget();
+ if (currentTarget == null)
+ {
+ // 没有新目标,取消发射
+ isWarmingUp = false;
+ return;
+ }
+ }
+ else
+ {
+ // 直接自毁
+ SelfDestruct();
+ return;
+ }
+ }
+
+ // 发射抛射体
+ for (int i = 0; i < Props.burstCount; i++)
+ {
+ if (burstCounter >= Props.maxTargets)
+ break;
+
+ // 创建抛射体
+ Projectile projectile = (Projectile)GenSpawn.Spawn(
+ Props.projectileDef,
+ parent.Position,
+ parent.Map
+ );
+
+ // 发射
+ projectile.Launch(parent, parent.DrawPos, currentTarget, currentTarget, ProjectileHitFlags.IntendedTarget, false);
+
+ // 连发延迟
+ if (i < Props.burstCount - 1 && Props.burstDelay > 0)
+ {
+ // 使用简单的延迟实现
+ // 在实际游戏中,可能需要更复杂的实现
+ // 这里我们简化处理
+ }
+ }
+
+ // 播放发射音效
+ if (Props.launchSound != null)
+ {
+ Props.launchSound.PlayOneShot(new TargetInfo(parent.Position, parent.Map));
+ }
+
+ // 播放发射特效
+ if (Props.launchEffect != null)
+ {
+ Effecter effecter = Props.launchEffect.Spawn();
+ effecter.Trigger(parent, parent);
+ effecter.Cleanup();
+ }
+
+ // 检查是否需要继续攻击
+ if (burstCounter >= Props.maxTargets || !Props.canRetarget)
+ {
+ // 自毁
+ SelfDestruct();
+ }
+ else
+ {
+ // 寻找新目标
+ currentTarget = FindNewTarget();
+ if (currentTarget == null)
+ {
+ SelfDestruct();
+ }
+ else
+ {
+ // 重置预热,准备下一次发射
+ warmupCounter = 0;
+ burstCounter = 0;
+ }
+ }
+ }
+
+ ///
+ /// 寻找新目标
+ ///
+ private Pawn FindNewTarget()
+ {
+ Map map = parent.Map;
+ IntVec3 center = parent.Position;
+
+ foreach (IntVec3 cell in GenRadial.RadialCellsAround(center, Props.detectionRadius, true))
+ {
+ if (!cell.InBounds(map))
+ continue;
+
+ if (Props.requireLineOfSight && !GenSight.LineOfSight(center, cell, map, skipFirstCell: true))
+ continue;
+
+ List things = map.thingGrid.ThingsListAtFast(cell);
+ foreach (Thing thing in things)
+ {
+ if (thing is Pawn pawn && !detectedTargets.Contains(pawn) && IsValidTarget(pawn))
+ {
+ detectedTargets.Add(pawn);
+ return pawn;
+ }
+
+ // 如果目标建筑
+ if (Props.targetBuildings && thing is Building building &&
+ building.Faction != null && building.Faction.HostileTo(Faction.OfPlayer))
+ {
+ // 注意:这里需要处理建筑目标,但抛射体可能需要调整
+ // 为了简化,这里只处理Pawn
+ }
+ }
+ }
+
+ return null;
+ }
+
+ ///
+ /// 自毁
+ ///
+ private void SelfDestruct()
+ {
+ if (hasTriggered)
+ return;
+
+ hasTriggered = true;
+
+ // 播放自毁音效
+ if (Props.selfDestructSound != null)
+ {
+ Props.selfDestructSound.PlayOneShot(new TargetInfo(parent.Position, parent.Map));
+ }
+
+ // 播放自毁特效
+ if (Props.selfDestructEffect != null)
+ {
+ Effecter effecter = Props.selfDestructEffect.Spawn();
+ effecter.Trigger(parent, parent);
+ effecter.Cleanup();
+ }
+
+ // 销毁建筑
+ parent.Destroy(DestroyMode.Vanish);
+ }
+
+ ///
+ /// 绘制检测范围(仅在选定和调试模式下)
+ ///
+ public override void PostDrawExtraSelectionOverlays()
+ {
+ base.PostDrawExtraSelectionOverlays();
+
+ if (Props.showDetectionRadius && Props.detectionRadius > 0)
+ {
+ GenDraw.DrawRadiusRing(parent.Position, Props.detectionRadius, Color.red);
+ }
+ }
+
+ ///
+ /// 获取Gizmo按钮
+ ///
+ public override IEnumerable CompGetGizmosExtra()
+ {
+ foreach (Gizmo gizmo in base.CompGetGizmosExtra())
+ {
+ yield return gizmo;
+ }
+
+ if (!hasTriggered && DebugSettings.ShowDevGizmos)
+ {
+ // 调试:手动触发
+ Command_Action debugTrigger = new Command_Action();
+ debugTrigger.defaultLabel = "DEV: Trigger Trap";
+ debugTrigger.action = delegate
+ {
+ currentTarget = FindClosestHostilePawn();
+ if (currentTarget != null)
+ {
+ TriggerTrap();
+ }
+ else
+ {
+ Messages.Message("WULA_TrapLauncherNoTargetFound".Translate(),
+ parent, MessageTypeDefOf.RejectInput);
+ }
+ };
+ yield return debugTrigger;
+
+ // 调试:立即自毁
+ Command_Action debugDestruct = new Command_Action();
+ debugDestruct.defaultLabel = "DEV: Self-Destruct";
+ debugDestruct.action = delegate
+ {
+ SelfDestruct();
+ };
+ yield return debugDestruct;
+ }
+ }
+
+ ///
+ /// 查找最近的敌对Pawn(调试用)
+ ///
+ private Pawn FindClosestHostilePawn()
+ {
+ if (!parent.Spawned)
+ return null;
+
+ Pawn closestPawn = null;
+ float closestDist = float.MaxValue;
+
+ foreach (Pawn pawn in parent.Map.mapPawns.AllPawnsSpawned)
+ {
+ if (IsValidTarget(pawn))
+ {
+ float dist = pawn.Position.DistanceTo(parent.Position);
+ if (dist <= Props.detectionRadius && dist < closestDist)
+ {
+ closestDist = dist;
+ closestPawn = pawn;
+ }
+ }
+ }
+
+ return closestPawn;
+ }
+
+ public override void PostExposeData()
+ {
+ base.PostExposeData();
+
+ Scribe_Values.Look(ref scanTickCounter, "scanTickCounter", 0);
+ Scribe_Values.Look(ref hasTriggered, "hasTriggered", false);
+ Scribe_Values.Look(ref isWarmingUp, "isWarmingUp", false);
+ Scribe_Values.Look(ref warmupCounter, "warmupCounter", 0);
+ Scribe_Values.Look(ref burstCounter, "burstCounter", 0);
+ Scribe_References.Look(ref currentTarget, "currentTarget");
+ Scribe_Collections.Look(ref detectedTargets, "detectedTargets", LookMode.Reference);
+
+ if (Scribe.mode == LoadSaveMode.PostLoadInit)
+ {
+ if (detectedTargets == null)
+ {
+ detectedTargets = new HashSet();
+ }
+
+ // 移除无效的目标引用
+ detectedTargets.RemoveWhere(pawn => pawn == null || pawn.Destroyed);
+
+ // 如果已经在预热但目标无效,尝试恢复或自毁
+ if (isWarmingUp && (currentTarget == null || !currentTarget.Spawned))
+ {
+ if (Props.canRetarget)
+ {
+ currentTarget = FindNewTarget();
+ if (currentTarget == null)
+ {
+ SelfDestruct();
+ }
+ }
+ else
+ {
+ SelfDestruct();
+ }
+ }
+ }
+ }
+ }
+}
diff --git a/Source/WulaFallenEmpire/Designator/Designator_CallSkyfallerInArea.cs b/Source/WulaFallenEmpire/Designator/Designator_CallSkyfallerInArea.cs
new file mode 100644
index 00000000..bffd2b86
--- /dev/null
+++ b/Source/WulaFallenEmpire/Designator/Designator_CallSkyfallerInArea.cs
@@ -0,0 +1,197 @@
+using System.Collections.Generic;
+using System.Linq;
+using RimWorld;
+using UnityEngine;
+using Verse;
+
+namespace WulaFallenEmpire
+{
+ public class Designator_CallSkyfallerInArea : Designator
+ {
+ private readonly new Texture2D icon;
+
+ // 记录已经处理过的建筑(避免重复)
+ private HashSet processedBuildings = new HashSet();
+
+ public Designator_CallSkyfallerInArea()
+ {
+ defaultLabel = "WULA_Designator_CallSkyfallerInArea".Translate();
+ defaultDesc = "WULA_Designator_CallSkyfallerInAreaDesc".Translate();
+ icon = ContentFinder.Get("Wula/UI/Designators/WULA_AreaSkyfaller");
+ soundDragSustain = SoundDefOf.Designate_DragStandard;
+ soundDragChanged = SoundDefOf.Designate_DragStandard_Changed;
+ useMouseIcon = true;
+ soundSucceeded = SoundDefOf.Designate_Claim;
+ hotKey = KeyBindingDefOf.Misc12;
+ tutorTag = "CallSkyfallerInArea";
+ }
+
+ public override AcceptanceReport CanDesignateCell(IntVec3 c)
+ {
+ if (!c.InBounds(Map))
+ return false;
+
+ // 允许在迷雾格上选择(就像拆除和索赔设计器一样)
+ if (c.Fogged(Map))
+ return false;
+
+ // 只要单元格内有玩家建筑,就允许选择
+ var things = Map.thingGrid.ThingsListAt(c);
+ foreach (var thing in things)
+ {
+ if (thing.def.category == ThingCategory.Building &&
+ thing.Faction == Faction.OfPlayer &&
+ thing.TryGetComp() != null)
+ {
+ return true;
+ }
+ }
+
+ // 即使单元格内没有符合条件的建筑,也允许选择(这样用户可以拖动区域)
+ // 但返回一个友好的提示
+ return "WULA_NoCallableBuildingsInCell".Translate();
+ }
+
+ public override void DesignateSingleCell(IntVec3 c)
+ {
+ // 处理单个单元格内的所有建筑
+ ProcessCell(c);
+ }
+
+ public override void DesignateMultiCell(IEnumerable cells)
+ {
+ // 清除已处理的建筑记录
+ processedBuildings.Clear();
+
+ int totalBuildings = 0;
+
+ // 处理所有选中的单元格
+ foreach (var cell in cells)
+ {
+ if (cell.InBounds(Map))
+ {
+ // 统计该单元格处理的建筑数量
+ int cellCount = processedBuildings.Count;
+ ProcessCell(cell);
+ int newBuildings = processedBuildings.Count - cellCount;
+ totalBuildings += newBuildings;
+ }
+ }
+
+ // 计算成功和失败的数量
+ // 这里需要跟踪每个建筑的调用结果
+ // 由于我们直接调用CallSkyfaller,需要知道哪些失败了
+ // 简化处理:在ProcessCell中统计
+
+ // 显示简单的结果消息
+ if (totalBuildings > 0)
+ {
+ Messages.Message("WULA_AreaCallInitiated".Translate(totalBuildings),
+ MessageTypeDefOf.PositiveEvent);
+ }
+ else
+ {
+ Messages.Message("WULA_NoCallableBuildingsInArea".Translate(),
+ MessageTypeDefOf.NeutralEvent);
+ }
+ }
+
+ private void ProcessCell(IntVec3 cell)
+ {
+ var things = Map.thingGrid.ThingsListAt(cell);
+
+ foreach (var thing in things)
+ {
+ // 跳过非建筑
+ if (thing.def.category != ThingCategory.Building)
+ continue;
+
+ // 跳过非玩家建筑
+ if (thing.Faction != Faction.OfPlayer)
+ continue;
+
+ // 检查是否已经处理过(避免重复处理同一个建筑)
+ if (processedBuildings.Contains(thing))
+ continue;
+
+ // 获取空投组件
+ var comp = thing.TryGetComp();
+ if (comp == null)
+ continue;
+
+ // 尝试呼叫空投
+ if (comp.CanCallSkyfaller)
+ {
+ comp.CallSkyfaller(false);
+ processedBuildings.Add(thing);
+ }
+ // 即使不能呼叫,也添加到已处理列表,避免重复尝试
+ else
+ {
+ processedBuildings.Add(thing);
+ }
+ }
+ }
+
+ public override AcceptanceReport CanDesignateThing(Thing t)
+ {
+ // 这里提供单个建筑的反向设计器支持(右键菜单)
+ if (t.def.category != ThingCategory.Building)
+ return false;
+
+ if (t.Faction != Faction.OfPlayer)
+ return false;
+
+ var comp = t.TryGetComp();
+ if (comp == null)
+ return false;
+
+ if (!comp.CanCallSkyfaller)
+ return GetFailureReason(t, comp);
+
+ return true;
+ }
+
+ private string GetFailureReason(Thing building, CompSkyfallerCaller comp)
+ {
+ if (!comp.HasRequiredFlyOver && comp.Props.requireFlyOver)
+ return "WULA_NoBuildingDropperFlyOver".Translate();
+
+ if (!comp.CheckRoofConditions)
+ {
+ var roof = building.Position.GetRoof(building.Map);
+ if (roof?.isThickRoof == true)
+ return "WULA_ThickRoofBlocking".Translate();
+ else
+ return "WULA_RoofBlocking".Translate();
+ }
+
+ if (!comp.HasEnoughMaterials())
+ return "WULA_InsufficientMaterials".Translate();
+
+ if (comp.used)
+ return "WULA_AlreadyUsed".Translate();
+
+ if (comp.calling)
+ return "WULA_AlreadyCalling".Translate();
+
+ return "WULA_CannotCallSkyfaller".Translate();
+ }
+
+ public override void DesignateThing(Thing t)
+ {
+ // 用于反向设计器(右键菜单)
+ var comp = t.TryGetComp();
+ if (comp != null && comp.CanCallSkyfaller)
+ {
+ comp.CallSkyfaller(false);
+ }
+ }
+
+ public override void SelectedUpdate()
+ {
+ // 参考Designator_Deconstruct,只绘制鼠标悬停方框
+ GenUI.RenderMouseoverBracket();
+ }
+ }
+}
diff --git a/Source/WulaFallenEmpire/Projectiles/Projectile_NorthArcTrail.cs b/Source/WulaFallenEmpire/Projectiles/Projectile_NorthArcTrail.cs
index 21a1eaa2..b8af3f12 100644
--- a/Source/WulaFallenEmpire/Projectiles/Projectile_NorthArcTrail.cs
+++ b/Source/WulaFallenEmpire/Projectiles/Projectile_NorthArcTrail.cs
@@ -27,7 +27,10 @@ namespace WulaFallenEmpire
// 新增:绘制相关变量
private float currentArcHeight;
- private const float DRAW_ALTITUDE_OFFSET = 15f; // 增加绘制高度偏移
+ private const float DRAW_ALTITUDE_OFFSET = 0.5f; // 减少偏移量,避免裁剪问题
+
+ // 新增:用于保存真实计算的位置(仅XZ平面)
+ private Vector3 horizontalPosition;
public TrackingBulletDef TrackingDef
{
@@ -45,7 +48,7 @@ namespace WulaFallenEmpire
}
}
- // 修改:重写绘制位置,确保在正确的高度
+ // 修改:简化ExactPosition计算
public override Vector3 ExactPosition
{
get
@@ -53,15 +56,31 @@ namespace WulaFallenEmpire
if (!initialized)
return base.ExactPosition;
- // 返回计算的位置,但保持Y轴为绘制高度
- Vector3 pos = exactPositionInt;
- pos.y = def.Altitude + currentArcHeight + DRAW_ALTITUDE_OFFSET;
- return pos;
+ // 返回水平位置,保持Y轴为定义的高度
+ // RimWorld使用Y轴作为高度层,不应该随意改变
+ return new Vector3(horizontalPosition.x, def.Altitude, horizontalPosition.z);
}
}
+ // 修改:重写ExactRotation以考虑高度变化
public override Quaternion ExactRotation => Quaternion.LookRotation(GetCurrentDirection());
+ // 新增:获取带高度的位置(用于特效绘制)
+ public Vector3 PositionWithHeight
+ {
+ get
+ {
+ if (!initialized)
+ return ExactPosition;
+
+ return new Vector3(
+ horizontalPosition.x,
+ def.Altitude + currentArcHeight * 0.3f, // 适当缩放高度
+ horizontalPosition.z
+ );
+ }
+ }
+
public override void ExposeData()
{
base.ExposeData();
@@ -75,6 +94,7 @@ namespace WulaFallenEmpire
Scribe_Values.Look(ref exactPositionInt, "exactPositionInt", Vector3.zero);
Scribe_Values.Look(ref curveSteepness, "curveSteepness", 1f);
Scribe_Values.Look(ref currentArcHeight, "currentArcHeight", 0f);
+ Scribe_Values.Look(ref horizontalPosition, "horizontalPosition", Vector3.zero);
Scribe_Values.Look(ref Fleck_MakeFleckTick, "Fleck_MakeFleckTick", 0);
Scribe_Values.Look(ref lastTickPosition, "lastTickPosition", Vector3.zero);
@@ -115,6 +135,7 @@ namespace WulaFallenEmpire
bezierControlPoint = 2f * apexPoint - midPoint;
initialized = true;
+ horizontalPosition = origin;
exactPositionInt = origin;
lastTickPosition = origin;
currentArcHeight = 0f;
@@ -147,7 +168,7 @@ namespace WulaFallenEmpire
// 垂直高度 (抛物线)
currentArcHeight = def.projectile.arcHeightFactor * GenMath.InverseParabola(t);
- nextPos.y = 0f; // 水平位置不包含高度
+ horizontalPosition = nextPos; // 保存水平位置
if (!nextPos.ToIntVec3().InBounds(base.Map))
{
@@ -172,7 +193,8 @@ namespace WulaFallenEmpire
if (map != null)
{
int count = TrackingDef.fleckMakeFleckNum.RandomInRange;
- Vector3 currentPosition = this.ExactPosition; // 使用重写后的ExactPosition
+ // 使用带高度的位置生成尾迹
+ Vector3 currentPosition = PositionWithHeight;
Vector3 previousPosition = lastTickPosition;
if ((currentPosition - previousPosition).MagnitudeHorizontalSquared() > 0.0001f)
@@ -195,7 +217,7 @@ namespace WulaFallenEmpire
}
}
- lastTickPosition = ExactPosition; // 使用重写后的ExactPosition
+ lastTickPosition = PositionWithHeight;
if (ticksFlying >= totalTicks)
{
@@ -204,7 +226,7 @@ namespace WulaFallenEmpire
}
}
- // 修改:重写绘制方法,确保正确绘制
+ // 修改:重写绘制方法
protected override void DrawAt(Vector3 drawLoc, bool flip = false)
{
if (!initialized)
@@ -213,13 +235,18 @@ namespace WulaFallenEmpire
return;
}
- // 使用我们计算的位置进行绘制
+ // 使用固定的绘制位置,但考虑高度偏移
Vector3 finalDrawPos = ExactPosition;
+ // 调整绘制位置以考虑抛物线高度
+ // 但保持Y轴在合理范围内,避免被裁剪
+ float heightAdjustment = currentArcHeight * 0.2f; // 缩放高度影响
+ finalDrawPos.y += Mathf.Clamp(heightAdjustment, -0.5f, 2f);
+
// 绘制阴影
if (def.projectile.shadowSize > 0f)
{
- DrawShadow(finalDrawPos, currentArcHeight);
+ DrawShadow(finalDrawPos);
}
Quaternion rotation = ExactRotation;
@@ -232,7 +259,14 @@ namespace WulaFallenEmpire
// 使用正确的绘制方法
if (def.projectile.useGraphicClass)
{
- Graphic.Draw(finalDrawPos, base.Rotation, this, rotation.eulerAngles.y);
+ // 确保图形缩放合适
+ float scaleFactor = 1f + currentArcHeight * 0.1f; // 轻微缩放模拟远近
+ Matrix4x4 matrix = Matrix4x4.TRS(
+ finalDrawPos,
+ rotation,
+ new Vector3(scaleFactor, 1f, scaleFactor)
+ );
+ Graphics.DrawMesh(MeshPool.GridPlane(def.graphicData.drawSize), matrix, DrawMat, 0);
}
else
{
@@ -242,26 +276,32 @@ namespace WulaFallenEmpire
Comps_PostDraw();
}
- // 修改:重写阴影绘制,使用正确的高度
- private void DrawShadow(Vector3 drawLoc, float height)
+ // 修改:简化阴影绘制
+ private void DrawShadow(Vector3 drawLoc)
{
+ if (def.projectile.shadowSize <= 0f)
+ return;
+
Material shadowMat = MaterialPool.MatFrom("Things/Skyfaller/SkyfallerShadowCircle", ShaderDatabase.Transparent);
if (shadowMat == null) return;
- float shadowSize = def.projectile.shadowSize * Mathf.Lerp(1f, 0.6f, height / (def.projectile.arcHeightFactor + 1f));
+ // 根据当前高度调整阴影大小
+ float normalizedHeight = Mathf.Clamp01(currentArcHeight / (def.projectile.arcHeightFactor + 0.01f));
+ float shadowSize = def.projectile.shadowSize * Mathf.Lerp(1f, 0.4f, normalizedHeight);
+
Vector3 scale = new Vector3(shadowSize, 1f, shadowSize);
- Vector3 shadowOffset = new Vector3(0f, -0.01f, 0f);
+ Vector3 shadowOffset = new Vector3(0f, -0.05f, 0f); // 稍微降低阴影位置
Matrix4x4 matrix = Matrix4x4.TRS(drawLoc + shadowOffset, Quaternion.identity, scale);
Graphics.DrawMesh(MeshPool.plane10, matrix, shadowMat, 0);
}
- // 计算当前位置的切线方向
+ // 计算当前位置的切线方向(考虑高度变化)
private Vector3 GetCurrentDirection()
{
if (!initialized || totalTicks <= 0)
{
- return destinationPos - originPos;
+ return (destinationPos - originPos).normalized;
}
float t = (float)ticksFlying / (float)totalTicks;
@@ -275,12 +315,26 @@ namespace WulaFallenEmpire
return (destinationPos - originPos).normalized;
}
- return tangent.normalized;
+ // 添加轻微的上/下方向以模拟抛物线
+ float verticalComponent = GenMath.InverseParabola(t) * def.projectile.arcHeightFactor * 0.3f;
+ return (tangent.normalized + new Vector3(0, verticalComponent, 0)).normalized;
}
protected override void Impact(Thing hitThing, bool blockedByShield = false)
{
base.Impact(hitThing, blockedByShield);
}
+
+ // 新增:确保在保存时位置正确
+ public override void PostMapInit()
+ {
+ base.PostMapInit();
+
+ // 确保位置数据有效
+ if (initialized && horizontalPosition == Vector3.zero)
+ {
+ horizontalPosition = originPos;
+ }
+ }
}
}
diff --git a/Source/WulaFallenEmpire/WulaFallenEmpire.csproj b/Source/WulaFallenEmpire/WulaFallenEmpire.csproj
index 08988ddf..dba895dc 100644
--- a/Source/WulaFallenEmpire/WulaFallenEmpire.csproj
+++ b/Source/WulaFallenEmpire/WulaFallenEmpire.csproj
@@ -134,6 +134,9 @@
+
+
+
@@ -256,9 +259,6 @@
-
-
-