diff --git a/1.6/1.6/Assemblies/WulaFallenEmpire.dll b/1.6/1.6/Assemblies/WulaFallenEmpire.dll index 03c30a74..688bbef5 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_Flyover_Ability.xml b/1.6/1.6/Defs/AbilityDefs/WULA_Flyover_Ability.xml index 07d760bf..3bdbe935 100644 --- a/1.6/1.6/Defs/AbilityDefs/WULA_Flyover_Ability.xml +++ b/1.6/1.6/Defs/AbilityDefs/WULA_Flyover_Ability.xml @@ -173,6 +173,7 @@ + WULA_Spawn_BattleShip_Artillery @@ -899,7 +900,7 @@ WULA_Firepower_EnergyLance_Surveillance - 指挥战舰侧弦的副炮,监视一个区域1小时,对任何进入范围的敌对目标进行炮击。 + 指挥战舰侧弦的光矛阵列,监视一个区域1小时,对任何进入范围的敌对目标进行持续照射。 Wula/UI/Abilities/WULA_Firepower_EnergyLance_Surveillance 1 Misc12 @@ -1027,4 +1028,47 @@ + + + + WULA_Support_Shield_Projection + + 以启动拒止舰的止戈立场,在指定地点投射一面球形护盾,阻挡并反射所有的外来射弹。 + Wula/UI/Abilities/WULA_Firepower_Minigun_Strafe + 1 + Misc12 + false + + Verb_CastAbility + false + false + 1 + 120 + true + + false + true + + + +
  • + 1000 + BombardmentFacility +
  • +
  • + 0 + WULA_Support_Shield_Projection_Skyfaller + 12 + (0.6,0.8,0.8,0.5) +
  • +
  • + + FactoryFacility + 需要拥有<color=#BD2F31><i>支援设施</i></color>设施的战舰在地图上才能进行轨道炮击支援 +
  • + +
    +
    \ No newline at end of file diff --git a/1.6/1.6/Defs/HediffDefs/WULA_FM_Hediffs.xml b/1.6/1.6/Defs/HediffDefs/WULA_FM_Hediffs.xml index a63bb23a..d69355a4 100644 --- a/1.6/1.6/Defs/HediffDefs/WULA_FM_Hediffs.xml +++ b/1.6/1.6/Defs/HediffDefs/WULA_FM_Hediffs.xml @@ -167,6 +167,7 @@
  • WULA_CallMotherShip
  • WULA_CallConstructionShip
  • WULA_ClearFlightPath
  • +
  • WULA_Support_Shield_Projection
  • diff --git a/1.6/1.6/Defs/ThingDefs_Buildings/WULA_Misc_Buildings.xml b/1.6/1.6/Defs/ThingDefs_Buildings/WULA_Misc_Buildings.xml index a7920756..e70803ca 100644 --- a/1.6/1.6/Defs/ThingDefs_Buildings/WULA_Misc_Buildings.xml +++ b/1.6/1.6/Defs/ThingDefs_Buildings/WULA_Misc_Buildings.xml @@ -636,10 +636,10 @@ 5 - 600 + 50 0.5 1000 - 125 + 5 0.65 Normal @@ -728,7 +728,6 @@ 50 1.0 330 - 35 2 2991 diff --git a/1.6/1.6/Defs/ThingDefs_Misc/WULA_Flyover_Item.xml b/1.6/1.6/Defs/ThingDefs_Misc/WULA_Flyover_Item.xml index 6be4003b..3e287275 100644 --- a/1.6/1.6/Defs/ThingDefs_Misc/WULA_Flyover_Item.xml +++ b/1.6/1.6/Defs/ThingDefs_Misc/WULA_Flyover_Item.xml @@ -2176,4 +2176,165 @@ + + + WULA_Support_Shield_Projection_Skyfaller + + (1,1) + + Wula/Building/Flag/WULA_Flag_Building_Mount + Graphic_Single + TransparentPostLight + (1,1) + + false + + + + Accelerate + Things/Skyfaller/SkyfallerShadowDropPod + (0, 0) + DropPod_Fall + 100 + + 0.05 + 0 + 0 + + +
  • (0,0)
  • +
  • (1, 1)
  • +
    +
    + WULA_Support_Shield +
    +
    + + WULA_Support_Shield + + 一面大型护盾,反射所有的敌方射弹,己方的开火不受影响。 + WulaFallenEmpire.Building_ExtraGraphics + true + BuildingOnTop + 50 + true + PassThroughOnly + 1 + (1,1) + true + (0.56, 0.62, 0.9) + Wula/Building/Flag/WULA_Flag_Building_A + false + + Wula/Building/Flag/WULA_Flag_Building_Mount + Graphic_Single + TransparentPostLight + (1,1) + + + 5 + + + 100 + 0.5 + 1000 + 125 + 0.65 + + Normal + true + false + North + true + Light + BulletImpact_Metal + true + RealtimeOnly + ConstructMetal + true + WULA_Buildings + + false + BuildingDestroyed_Metal_Big + true + true + + +
  • + 0 + 0 + + +
  • + Wula/Building/Flag/WULA_Flag_Building_A + (3,3) + 0 + (0,1,3) + 1.5 + 0.2 + 0 +
  • +
  • + Wula/Building/Flag/WULA_Flag_Building_Mount + (1,1) + 1 + (0,1,0) + 0 + 0 + 0 +
  • +
  • + Wula/Building/WULA_Support_Shield_Building + (24,24) + 2 + (0,1,0) + 0 + 0 + 0 +
  • + + +
    + +
  • + + true + false +
  • +
  • + 3 + (120,240,252,0) +
  • +
  • + 3600 +
  • + +
  • + 12 + 150000 + 2400 + 30 + + + Interceptor_BlockedProjectile + Interceptor_BlockedProjectile + Shield_Break + BulletShieldGenerator_Reactivate + + (0.9, 0.2, 0.2, 0.5) + + + true + false + true + + + true + 1 + 30 + 0 + Interceptor_BlockedProjectile +
  • +
    +
    \ No newline at end of file diff --git a/Content/Textures/Wula/Building/WULA_Support_Shield_Building.png b/Content/Textures/Wula/Building/WULA_Support_Shield_Building.png new file mode 100644 index 00000000..ec0a3723 Binary files /dev/null and b/Content/Textures/Wula/Building/WULA_Support_Shield_Building.png differ diff --git a/Source/WulaFallenEmpire/ThingComp/WULA_AreaShield/AreaShieldManager.cs b/Source/WulaFallenEmpire/ThingComp/WULA_AreaShield/AreaShieldManager.cs index 3424b995..335ec8ea 100644 --- a/Source/WulaFallenEmpire/ThingComp/WULA_AreaShield/AreaShieldManager.cs +++ b/Source/WulaFallenEmpire/ThingComp/WULA_AreaShield/AreaShieldManager.cs @@ -16,11 +16,13 @@ namespace WulaFallenEmpire { if (map == null) yield break; + if (Find.TickManager.TicksGame - lastUpdateTick > UPDATE_INTERVAL_TICKS) { UpdateShieldCache(); lastUpdateTick = Find.TickManager.TicksGame; } + if (activeShieldsByMap.TryGetValue(map, out var shields)) { foreach (var shield in shields) @@ -30,36 +32,57 @@ namespace WulaFallenEmpire } } } + private static void UpdateShieldCache() { activeShieldsByMap.Clear(); + foreach (var map in Find.Maps) { if (map == null) continue; + var shieldSet = new HashSet(); + + // 搜索装备上的护盾 foreach (var pawn in map.mapPawns.AllPawnsSpawned) { if (pawn?.apparel == null || pawn.Destroyed) continue; + foreach (var apparel in pawn.apparel.WornApparel) { if (apparel == null || apparel.Destroyed) continue; + var shield = apparel.TryGetComp(); // 修改:只有立定且激活的护盾才加入缓存 - if (shield != null && shield.Active && !shield.IsWearerMoving) + if (shield != null && shield.Active && !shield.IsHolderMoving) { shieldSet.Add(shield); } } } + + // 搜索固定物品上的护盾(新增) + foreach (var thing in map.listerThings.AllThings) + { + if (thing == null || thing.Destroyed || thing is Apparel) + continue; + + var shield = thing.TryGetComp(); + if (shield != null && shield.Active) + { + shieldSet.Add(shield); + } + } + activeShieldsByMap[map] = shieldSet; } } public static void NotifyShieldStateChanged(ThingComp_AreaShield shield) { - if (shield?.Wearer?.Map != null) + if (shield?.Holder?.Map != null) { lastUpdateTick = 0; } diff --git a/Source/WulaFallenEmpire/ThingComp/WULA_AreaShield/Gizmo_AreaShieldStatus.cs b/Source/WulaFallenEmpire/ThingComp/WULA_AreaShield/Gizmo_AreaShieldStatus.cs index e50718ab..6ccd2a74 100644 --- a/Source/WulaFallenEmpire/ThingComp/WULA_AreaShield/Gizmo_AreaShieldStatus.cs +++ b/Source/WulaFallenEmpire/ThingComp/WULA_AreaShield/Gizmo_AreaShieldStatus.cs @@ -10,7 +10,8 @@ namespace WulaFallenEmpire public ThingComp_AreaShield shield; private static readonly Texture2D FullShieldBarTex = SolidColorMaterials.NewSolidColorMaterial(new Color(0.2f, 0.8f, 0.85f), ShaderDatabase.MetaOverlay).mainTexture as Texture2D; private static readonly Texture2D EmptyShieldBarTex = SolidColorMaterials.NewSolidColorMaterial(new Color(0.2f, 0.2f, 0.24f), ShaderDatabase.MetaOverlay).mainTexture as Texture2D; - // 新增:移动状态的颜色 + + // 新增:移动状态的颜色(仅对装备有效) private static readonly Texture2D MovingShieldBarTex = SolidColorMaterials.NewSolidColorMaterial(new Color(0.5f, 0.5f, 0.5f), ShaderDatabase.MetaOverlay).mainTexture as Texture2D; public override float GetWidth(float maxWidth) => 140f; @@ -39,11 +40,11 @@ namespace WulaFallenEmpire barTex = EmptyShieldBarTex; statusText = "ShieldOnCooldown".Translate(); } - else if (shield.IsWearerMoving) + else if (shield.IsEquipment && shield.IsHolderMoving) { - // 移动时显示灰色状态条和"移动中"文本 + // 移动时显示灰色状态条和"移动中"文本(仅对装备有效) barTex = MovingShieldBarTex; - statusText = "ShieldOfflineByMoving".Translate(); // 你可以根据需要修改这个文本 + statusText = "ShieldOfflineByMoving".Translate(); } else { diff --git a/Source/WulaFallenEmpire/ThingComp/WULA_AreaShield/ThingComp_AreaShield.cs b/Source/WulaFallenEmpire/ThingComp/WULA_AreaShield/ThingComp_AreaShield.cs index 73985fd7..fd97d7c6 100644 --- a/Source/WulaFallenEmpire/ThingComp/WULA_AreaShield/ThingComp_AreaShield.cs +++ b/Source/WulaFallenEmpire/ThingComp/WULA_AreaShield/ThingComp_AreaShield.cs @@ -21,42 +21,205 @@ namespace WulaFallenEmpire private bool drawInterceptCone; public CompProperties_AreaShield Props => (CompProperties_AreaShield)props; + + // 回退机制:支持装备和普通物品 public Pawn Wearer => (parent as Apparel)?.Wearer; + public bool IsEquipment => parent is Apparel; + public bool IsStandalone => !IsEquipment; + + // 获取护盾持有者(回退机制) + public Thing Holder => IsEquipment ? (Thing)Wearer : parent; + public bool IsOnCooldown => ticksToReset > 0; public int HitPointsMax => Props.baseHitPoints; private bool initialized = false; private StunHandler stunner; - // 新增:移动状态检测 - public bool IsWearerMoving + // 材质定义 + private static readonly Material ForceFieldMat = MaterialPool.MatFrom("Other/ForceField", ShaderDatabase.MoteGlow); + private static readonly Material ForceFieldConeMat = MaterialPool.MatFrom("Other/ForceFieldCone", ShaderDatabase.MoteGlow); + private static readonly MaterialPropertyBlock MatPropertyBlock = new MaterialPropertyBlock(); + private const float TextureActualRingSizeFactor = 1.1601562f; + private static readonly Color InactiveColor = new Color(0.2f, 0.2f, 0.2f); + + // 护盾绘制方法 - 参考原版实现 + public override void CompDrawWornExtras() + { + base.CompDrawWornExtras(); + + if (!IsEquipment) return; // 只有装备使用这个方法 + + DrawShield(); + } + public override void PostDraw() + { + base.PostDraw(); + + if (IsEquipment) return; // 装备使用 CompDrawWornExtras + + DrawShield(); + } + /// + /// 统一的护盾绘制方法 - 参考原版实现 + /// + private void DrawShield() + { + if (!Active || Holder?.Map == null || Holder.Destroyed) + return; + Vector3 drawPos = GetHolderDrawPos(); + drawPos.y = AltitudeLayer.MoteOverhead.AltitudeFor(); + float currentAlpha = GetCurrentAlpha(); + if (currentAlpha > 0f) + { + // 参考原版:未激活但被选中时使用灰色 + Color color = (!Active && Find.Selector.IsSelected(parent)) ? InactiveColor : Props.color; + color.a *= currentAlpha; + MatPropertyBlock.SetColor(ShaderPropertyIDs.Color, color); + + Matrix4x4 matrix = default; + float scale = Props.radius * 2f * TextureActualRingSizeFactor; + matrix.SetTRS(drawPos, Quaternion.identity, new Vector3(scale, 1f, scale)); + Graphics.DrawMesh(MeshPool.plane10, matrix, ForceFieldMat, 0, null, 0, MatPropertyBlock); + } + // 添加拦截锥形效果 + float coneAlpha = GetCurrentConeAlpha(); + if (coneAlpha > 0f) + { + Color color = Props.color; + color.a *= coneAlpha; + MatPropertyBlock.SetColor(ShaderPropertyIDs.Color, color); + + Matrix4x4 matrix = default; + float scale = Props.radius * 2f; + matrix.SetTRS(drawPos, Quaternion.Euler(0f, lastInterceptAngle - 90f, 0f), new Vector3(scale, 1f, scale)); + Graphics.DrawMesh(MeshPool.plane10, matrix, ForceFieldConeMat, 0, null, 0, MatPropertyBlock); + } + } + /// + /// 获取当前透明度 - 参考原版的多状态叠加 + /// + private float GetCurrentAlpha() + { + // 多个透明度来源叠加,取最大值 + return Mathf.Max( + Mathf.Max( + Mathf.Max( + GetCurrentAlpha_Idle(), + GetCurrentAlpha_Selected() + ), + GetCurrentAlpha_RecentlyIntercepted() + ), + 0.1f // 最小透明度 + ); + } + /// + /// 空闲状态透明度 + /// + private float GetCurrentAlpha_Idle() + { + if (!Active) return 0f; + + // 固定物品:始终显示空闲状态 + if (IsStandalone) + { + // 脉冲效果 + return Mathf.Lerp(0.3f, 0.6f, + (Mathf.Sin((float)Gen.HashCombineInt(parent.thingIDNumber, 35990913) + Time.realtimeSinceStartup * 2f) + 1f) / 2f); + } + // 装备:只在特定条件下显示 + else if (IsEquipment) + { + // 装备护盾只在以下情况显示空闲状态: + if (Holder is Pawn pawn) + { + if (pawn.Drafted || pawn.InAggroMentalState || + (pawn.Faction != null && pawn.Faction.HostileTo(Faction.OfPlayer) && !pawn.IsPrisoner)) + { + return Mathf.Lerp(0.3f, 0.6f, + (Mathf.Sin((float)Gen.HashCombineInt(parent.thingIDNumber, 35990913) + Time.realtimeSinceStartup * 2f) + 1f) / 2f); + } + } + } + + return 0f; + } + /// + /// 被选中状态透明度 - 参考原版实现 + /// + private float GetCurrentAlpha_Selected() + { + // 如果被选中,显示更高的透明度 + if (Find.Selector.IsSelected(parent) && Active) + { + return Mathf.Lerp(0.4f, 0.8f, + (Mathf.Sin((float)Gen.HashCombineInt(parent.thingIDNumber, 96804938) + Time.realtimeSinceStartup * 2.5f) + 1f) / 2f); + } + + return 0f; + } + /// + /// 最近拦截状态透明度 + /// + private float GetCurrentAlpha_RecentlyIntercepted() + { + int ticksSinceIntercept = Find.TickManager.TicksGame - lastInterceptTicks; + return Mathf.Clamp01(1f - (float)ticksSinceIntercept / 40f) * 0.3f; + } + /// + /// 拦截锥形透明度 + /// + private float GetCurrentConeAlpha() + { + if (!drawInterceptCone) return 0f; + + int ticksSinceIntercept = Find.TickManager.TicksGame - lastInterceptTicks; + return Mathf.Clamp01(1f - (float)ticksSinceIntercept / 40f) * 0.82f; + } + /// + /// 获取持有者绘制位置(回退机制) + /// + private Vector3 GetHolderDrawPos() + { + if (Holder is Pawn pawn) + return pawn.Drawer?.DrawPos ?? pawn.Position.ToVector3Shifted(); + else + return Holder.DrawPos; + } + + // 移动状态检测(仅对装备有效) + public bool IsHolderMoving { get { + if (IsStandalone) return false; // 固定物品不会移动 if (Wearer == null || !Wearer.Spawned) return false; return Wearer.pather.Moving; } } - // 修改Active属性:只有在立定时才激活 + // 修改Active属性:装备只有在立定时才激活,固定物品始终激活 public bool Active { get { - if (Wearer == null || !Wearer.Spawned || Wearer.Dead || Wearer.Downed || IsOnCooldown) + if (Holder == null || !Holder.Spawned || Holder.Destroyed) return false; - // 新增:只有在立定时才激活 - if (IsWearerMoving) + + if (Holder is Pawn pawn && (pawn.Dead || pawn.Downed)) return false; + + if (IsOnCooldown) + return false; + + // 装备:只有在立定时才激活 + if (IsEquipment && IsHolderMoving) + return false; + return true; } } - // 材质定义 - private static readonly Material ForceFieldMat = MaterialPool.MatFrom("Other/ForceField", ShaderDatabase.MoteGlow); - private static readonly Material ForceFieldConeMat = MaterialPool.MatFrom("Other/ForceFieldCone", ShaderDatabase.MoteGlow); - private static readonly MaterialPropertyBlock MatPropertyBlock = new MaterialPropertyBlock(); - public override void PostPostMake() { base.PostPostMake(); @@ -84,7 +247,7 @@ namespace WulaFallenEmpire wasActiveLastCheck = isActive; } - if (Wearer == null) return; + if (Holder == null) return; if (IsOnCooldown) { @@ -125,14 +288,15 @@ namespace WulaFallenEmpire public bool TryIntercept(Projectile projectile, Vector3 lastExactPos, Vector3 newExactPos) { // 增强安全检查 - if (!Active || projectile == null || projectile.Destroyed || Wearer == null || Wearer.Map == null) + if (!Active || projectile == null || projectile.Destroyed || Holder == null || Holder.Map == null) return false; if (currentHitPoints <= 0) return false; + try { - if (!GenGeo.IntersectLineCircleOutline(Wearer.Position.ToVector2(), Props.radius, lastExactPos.ToVector2(), newExactPos.ToVector2())) + if (!GenGeo.IntersectLineCircleOutline(Holder.Position.ToVector2(), Props.radius, lastExactPos.ToVector2(), newExactPos.ToVector2())) { return false; } @@ -140,27 +304,27 @@ namespace WulaFallenEmpire return false; if (!projectile.def.projectile.flyOverhead && !Props.interceptGroundProjectiles) return false; - if (projectile.Launcher != null && !projectile.Launcher.HostileTo(Wearer.Faction) && !Props.interceptNonHostileProjectiles) + if (projectile.Launcher != null && !projectile.Launcher.HostileTo(Holder.Faction) && !Props.interceptNonHostileProjectiles) return false; lastInterceptTicks = Find.TickManager.TicksGame; // 记录拦截角度用于视觉效果 - lastInterceptAngle = projectile.ExactPosition.AngleToFlat(Wearer.TrueCenter()); + lastInterceptAngle = projectile.ExactPosition.AngleToFlat(GetHolderCenter()); drawInterceptCone = true; // 尝试反射 if (Props.canReflect && TryReflectProjectile(projectile, lastExactPos, newExactPos)) { // 反射成功,播放反射特效 - Props.reflectEffecter?.Spawn(projectile.ExactPosition.ToIntVec3(), Wearer.Map).Cleanup(); + Props.reflectEffecter?.Spawn(projectile.ExactPosition.ToIntVec3(), Holder.Map).Cleanup(); ApplyCosts(Props.reflectCost); return false; // 不销毁原抛射体,让它继续飞行(我们会在反射中销毁它) } else { // 普通拦截,播放拦截特效 - Props.interceptEffecter?.Spawn(projectile.ExactPosition.ToIntVec3(), Wearer.Map).Cleanup(); + Props.interceptEffecter?.Spawn(projectile.ExactPosition.ToIntVec3(), Holder.Map).Cleanup(); ApplyCosts(); return true; // 销毁抛射体 } @@ -172,6 +336,17 @@ namespace WulaFallenEmpire } } + /// + /// 获取持有者中心位置(回退机制) + /// + private Vector3 GetHolderCenter() + { + if (Holder is Pawn pawn) + return pawn.TrueCenter(); + else + return Holder.DrawPos; + } + /// /// 尝试反射抛射体 - 现在会创建新的抛射体 /// @@ -183,13 +358,14 @@ namespace WulaFallenEmpire // 检查反射概率 if (Rand.Value > Props.reflectChance) return false; + try { // 计算入射方向 Vector3 incomingDirection = (newExactPos - lastExactPos).normalized; // 计算法线方向(从护盾中心到碰撞点) - Vector3 normal = (newExactPos - Wearer.DrawPos).normalized; + Vector3 normal = (newExactPos - GetHolderCenter()).normalized; // 计算反射方向(镜面反射) Vector3 reflectDirection = Vector3.Reflect(incomingDirection, normal); @@ -216,28 +392,34 @@ namespace WulaFallenEmpire { try { - if (originalProjectile == null || originalProjectile.Destroyed || Wearer == null || Wearer.Map == null) + if (originalProjectile == null || originalProjectile.Destroyed || Holder == null || Holder.Map == null) return false; + // 计算新的发射位置(护盾位置附近) Vector3 spawnPosition = GetReflectSpawnPosition(collisionPoint); + // 确保位置在地图内 IntVec3 spawnCell = spawnPosition.ToIntVec3(); - if (!spawnCell.InBounds(Wearer.Map)) + if (!spawnCell.InBounds(Holder.Map)) { - spawnCell = Wearer.Position; + spawnCell = Holder.Position; } + // 计算新的目标位置 Vector3 targetPosition = spawnCell.ToVector3Shifted() + reflectDirection * 30f; IntVec3 targetCell = targetPosition.ToIntVec3(); + // 创建新的抛射体 - Projectile newProjectile = (Projectile)GenSpawn.Spawn(originalProjectile.def, spawnCell, Wearer.Map); + Projectile newProjectile = (Projectile)GenSpawn.Spawn(originalProjectile.def, spawnCell, Holder.Map); if (newProjectile == null) { Log.Warning("Failed to spawn reflected projectile"); return false; } - // 设置发射者为装备穿戴者 - Thing launcher = Wearer; + + // 设置发射者为护盾持有者 + Thing launcher = Holder; + // 发射新抛射体 newProjectile.Launch( launcher, @@ -247,11 +429,14 @@ namespace WulaFallenEmpire ProjectileHitFlags.All, false ); + // 复制重要的属性 CopyProjectileProperties(originalProjectile, newProjectile); + // 使用延迟销毁而不是立即销毁 ReflectedProjectileManager.MarkForDelayedDestroy(originalProjectile); - Log.Message($"反射抛射体: 由 {Wearer?.LabelShort} 从 {spawnCell} 向 {targetCell} 发射"); + + Log.Message($"反射抛射体: 由 {Holder?.LabelShort} 从 {spawnCell} 向 {targetCell} 发射"); return true; } catch (System.Exception ex) @@ -266,15 +451,15 @@ namespace WulaFallenEmpire /// private Vector3 GetReflectSpawnPosition(Vector3 collisionPoint) { - if (Wearer == null) + if (Holder == null) return collisionPoint; // 计算从护盾中心到碰撞点的方向 - Vector3 directionFromCenter = (collisionPoint - Wearer.DrawPos).normalized; + Vector3 directionFromCenter = (collisionPoint - GetHolderCenter()).normalized; // 在护盾边界上生成(稍微向内一点避免立即再次碰撞) float spawnDistance = Props.radius * 0.9f; - Vector3 spawnPosition = Wearer.DrawPos + directionFromCenter * spawnDistance; + Vector3 spawnPosition = GetHolderCenter() + directionFromCenter * spawnDistance; return spawnPosition; } @@ -310,26 +495,26 @@ namespace WulaFallenEmpire public override void PostPreApplyDamage(ref DamageInfo dinfo, out bool absorbed) { absorbed = false; - if (!Active || Wearer == null) return; + if (!Active || Holder == null) return; if (dinfo.Def.isRanged) return; if (dinfo.Instigator != null) { - float distance = Wearer.Position.DistanceTo(dinfo.Instigator.Position); + float distance = Holder.Position.DistanceTo(dinfo.Instigator.Position); if (distance > Props.radius) return; } if (currentHitPoints <= 0) return; - Props.absorbEffecter?.Spawn(Wearer.Position, Wearer.Map).Cleanup(); + Props.absorbEffecter?.Spawn(Holder.Position, Holder.Map).Cleanup(); ApplyCosts(); absorbed = true; } private void Break() { - Props.breakEffecter?.Spawn(Wearer.Position, Wearer.Map).Cleanup(); + Props.breakEffecter?.Spawn(Holder.Position, Holder.Map).Cleanup(); ticksToReset = Props.rechargeDelay; currentHitPoints = 0; AreaShieldManager.NotifyShieldStateChanged(this); @@ -337,85 +522,50 @@ namespace WulaFallenEmpire private void Reset() { - if (Wearer != null && Wearer.Spawned) + if (Holder != null && Holder.Spawned) { - Props.reactivateEffecter?.Spawn(Wearer.Position, Wearer.Map).Cleanup(); + Props.reactivateEffecter?.Spawn(Holder.Position, Holder.Map).Cleanup(); } currentHitPoints = HitPointsMax; AreaShieldManager.NotifyShieldStateChanged(this); } - // 护盾绘制方法 - 只有在立定时才绘制 - public override void CompDrawWornExtras() - { - base.CompDrawWornExtras(); - - // 修改:移动时不绘制护盾 - if (!Active || Wearer?.Map == null || !ShouldDisplay || IsWearerMoving) - return; - - Vector3 drawPos = Wearer.Drawer?.DrawPos ?? Wearer.Position.ToVector3Shifted(); - drawPos.y = AltitudeLayer.MoteOverhead.AltitudeFor(); - - float alpha = GetCurrentAlpha(); - if (alpha > 0f) - { - Color color = Props.color; - color.a *= alpha; - MatPropertyBlock.SetColor(ShaderPropertyIDs.Color, color); - Matrix4x4 matrix = default; - - float scale = Props.radius * 2f * 1.1601562f; - matrix.SetTRS(drawPos, Quaternion.identity, new Vector3(scale, 1f, scale)); - Graphics.DrawMesh(MeshPool.plane10, matrix, ForceFieldMat, 0, null, 0, MatPropertyBlock); - } - - // 添加拦截锥形效果 - float coneAlpha = GetCurrentConeAlpha(); - if (coneAlpha > 0f) - { - Color color = Props.color; - color.a *= coneAlpha; - MatPropertyBlock.SetColor(ShaderPropertyIDs.Color, color); - Matrix4x4 matrix = default; - float scale = Props.radius * 2f; - matrix.SetTRS(drawPos, Quaternion.Euler(0f, lastInterceptAngle - 90f, 0f), new Vector3(scale, 1f, scale)); - Graphics.DrawMesh(MeshPool.plane10, matrix, ForceFieldConeMat, 0, null, 0, MatPropertyBlock); - } - } - - // 显示条件 + // 显示条件 - 修改为固定物品始终显示,装备有条件显示 protected bool ShouldDisplay { get { - if (Wearer == null || !Wearer.Spawned || Wearer.Dead || Wearer.Downed || !Active) + if (Holder == null || !Holder.Spawned || Holder.Destroyed || !Active) return false; - - if (Wearer.Drafted || Wearer.InAggroMentalState || - (Wearer.Faction != null && Wearer.Faction.HostileTo(Faction.OfPlayer) && !Wearer.IsPrisoner)) + + // 对于装备:只在特定条件下显示 + if (IsEquipment && Holder is Pawn pawn) + { + if (pawn.Dead || pawn.Downed) + return false; + + // 装备护盾只在以下情况显示: + // 1. 穿戴者被选中 + // 2. 穿戴者处于战斗状态(征召状态或敌对) + // 3. 穿戴者处于攻击性精神状态 + if (Find.Selector.IsSelected(pawn)) + return true; + + if (pawn.Drafted || pawn.InAggroMentalState) + return true; + + if (pawn.Faction != null && pawn.Faction.HostileTo(Faction.OfPlayer) && !pawn.IsPrisoner) + return true; + } + // 对于固定物品:始终显示(只要护盾激活) + else if (IsStandalone) + { return true; - - if (Find.Selector.IsSelected(Wearer)) - return true; - + } + return false; } } - - private float GetCurrentAlpha() - { - float idleAlpha = Mathf.Lerp(0.3f, 0.6f, (Mathf.Sin((float)Gen.HashCombineInt(parent.thingIDNumber, 35990913) + Time.realtimeSinceStartup * 2f) + 1f) / 2f); - float interceptAlpha = Mathf.Clamp01(1f - (float)(Find.TickManager.TicksGame - lastInterceptTicks) / 40f); - return Mathf.Max(idleAlpha, interceptAlpha); - } - - private float GetCurrentConeAlpha() - { - if (!drawInterceptCone) return 0f; - return Mathf.Clamp01(1f - (float)(Find.TickManager.TicksGame - lastInterceptTicks) / 40f) * 0.82f; - } - private void EnsureInitialized() { if (initialized) return; @@ -433,7 +583,18 @@ namespace WulaFallenEmpire { EnsureInitialized(); - if (Wearer != null && Find.Selector.SingleSelectedThing == Wearer) + if (IsEquipment && Wearer != null && Find.Selector.SingleSelectedThing == Wearer) + { + yield return new Gizmo_AreaShieldStatus { shield = this }; + } + } + + public override IEnumerable CompGetGizmosExtra() + { + EnsureInitialized(); + + // 固定物品也显示护盾状态 + if (IsStandalone && Find.Selector.SingleSelectedThing == parent) { yield return new Gizmo_AreaShieldStatus { shield = this }; } diff --git a/美术与文本源文件/Wula/Building/WULA_Support_Shield_Building.sai2 b/美术与文本源文件/Wula/Building/WULA_Support_Shield_Building.sai2 new file mode 100644 index 00000000..b3ec89a6 Binary files /dev/null and b/美术与文本源文件/Wula/Building/WULA_Support_Shield_Building.sai2 differ