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 @@ - - -