diff --git a/1.6/Assemblies/WulaFallenEmpire.dll b/1.6/Assemblies/WulaFallenEmpire.dll
index 23dcb6dc..a6de9b87 100644
Binary files a/1.6/Assemblies/WulaFallenEmpire.dll and b/1.6/Assemblies/WulaFallenEmpire.dll differ
diff --git a/1.6/Defs/ThingDefs_Misc/WULA_Apparel.xml b/1.6/Defs/ThingDefs_Misc/WULA_Apparel.xml
index 631c6d3f..8f3f41d6 100644
--- a/1.6/Defs/ThingDefs_Misc/WULA_Apparel.xml
+++ b/1.6/Defs/ThingDefs_Misc/WULA_Apparel.xml
@@ -479,13 +479,31 @@
-
- 3600
- 2.5
- 2.8
- 0.01
- 4.0
- false
+
+
+
+ 2.5
+ 500
+ 2800
+
+
+ true
+ false
+ true
+
+
+ (0.9, 0.8, 0.2, 0.5)
+ Interceptor_BlockedProjectile
+ Shield_Break
+ BulletShieldGenerator_Reactivate
+
+
+ false
+ 600
+
+
+ 60
+
@@ -533,13 +551,31 @@
RewardStandardHighFreq
-
- 3600
- 2.5
- 2.8
- 0.01
- 4.0
- false
+
+
+
+ 2.5
+ 500
+ 2800
+
+
+ true
+ false
+ false
+
+
+ (0.9, 0.8, 0.2, 0.5)
+ Interceptor_BlockedProjectile
+ Shield_Break
+ BulletShieldGenerator_Reactivate
+
+
+ false
+ 600
+
+
+ 60
+
@@ -904,13 +940,31 @@
炮弹
false
-
- 3600
- 2.5
- 2.8
- 0.01
- 4.0
- false
+
+
+
+ 2.5
+ 500
+ 2800
+
+
+ true
+ false
+ false
+
+
+ (0.9, 0.8, 0.2, 0.5)
+ Interceptor_BlockedProjectile
+ Shield_Break
+ BulletShieldGenerator_Reactivate
+
+
+ false
+ 600
+
+
+ 60
+
@@ -1039,13 +1093,31 @@
炮弹
false
-
- 3600
- 2.5
- 2.8
- 0.01
- 4.0
- false
+
+
+
+ 2.5
+ 500
+ 2800
+
+
+ true
+ false
+ false
+
+
+ (0.9, 0.8, 0.2, 0.5)
+ Interceptor_BlockedProjectile
+ Shield_Break
+ BulletShieldGenerator_Reactivate
+
+
+ false
+ 600
+
+
+ 60
+
WULA_Heavy_Infantry_PowerArmor_PowerFist
@@ -1290,13 +1362,31 @@
梭镖
false
-
- 3600
- 2.5
- 2.8
- 0.01
- 4.0
- false
+
+
+
+ 2.5
+ 500
+ 2800
+
+
+ true
+ false
+ false
+
+
+ (0.9, 0.8, 0.2, 0.5)
+ Interceptor_BlockedProjectile
+ Shield_Break
+ BulletShieldGenerator_Reactivate
+
+
+ false
+ 600
+
+
+ 60
+
@@ -1345,4 +1435,4 @@
275
-
\ No newline at end of file
+
diff --git a/Source/WulaFallenEmpire/.vs/WulaFallenEmpire/v17/.suo b/Source/WulaFallenEmpire/.vs/WulaFallenEmpire/v17/.suo
index f4e6a586..d3416850 100644
Binary files a/Source/WulaFallenEmpire/.vs/WulaFallenEmpire/v17/.suo and b/Source/WulaFallenEmpire/.vs/WulaFallenEmpire/v17/.suo differ
diff --git a/Source/WulaFallenEmpire/.vs/WulaFallenEmpire/v17/DocumentLayout.json b/Source/WulaFallenEmpire/.vs/WulaFallenEmpire/v17/DocumentLayout.json
index 225913d5..1d9714bd 100644
--- a/Source/WulaFallenEmpire/.vs/WulaFallenEmpire/v17/DocumentLayout.json
+++ b/Source/WulaFallenEmpire/.vs/WulaFallenEmpire/v17/DocumentLayout.json
@@ -3,20 +3,28 @@
"WorkspaceRootPath": "C:\\Steam\\steamapps\\common\\RimWorld\\Mods\\3516260226\\Source\\WulaFallenEmpire\\",
"Documents": [
{
- "AbsoluteMoniker": "D:0:0:{F5AE8C3B-0221-4C16-A128-9A62D521A8FF}|WulaFallenEmpire.csproj|c:\\steam\\steamapps\\common\\rimworld\\mods\\3516260226\\source\\wulafallenempire\\ingestpatch.cs||{A6C744A8-0E4A-4FC6-886A-064283054674}",
- "RelativeMoniker": "D:0:0:{F5AE8C3B-0221-4C16-A128-9A62D521A8FF}|WulaFallenEmpire.csproj|solutionrelative:ingestpatch.cs||{A6C744A8-0E4A-4FC6-886A-064283054674}"
+ "AbsoluteMoniker": "D:0:0:{F5AE8C3B-0221-4C16-A128-9A62D521A8FF}|WulaFallenEmpire.csproj|c:\\steam\\steamapps\\common\\rimworld\\mods\\3516260226\\source\\wulafallenempire\\compapparelinterceptor.cs||{A6C744A8-0E4A-4FC6-886A-064283054674}",
+ "RelativeMoniker": "D:0:0:{F5AE8C3B-0221-4C16-A128-9A62D521A8FF}|WulaFallenEmpire.csproj|solutionrelative:compapparelinterceptor.cs||{A6C744A8-0E4A-4FC6-886A-064283054674}"
},
{
- "AbsoluteMoniker": "D:0:0:{F5AE8C3B-0221-4C16-A128-9A62D521A8FF}|WulaFallenEmpire.csproj|C:\\Steam\\steamapps\\common\\RimWorld\\Mods\\3516260226\\Source\\WulaFallenEmpire\\hediffcomp_regeneratebackstory.cs||{A6C744A8-0E4A-4FC6-886A-064283054674}",
- "RelativeMoniker": "D:0:0:{F5AE8C3B-0221-4C16-A128-9A62D521A8FF}|WulaFallenEmpire.csproj|solutionrelative:hediffcomp_regeneratebackstory.cs||{A6C744A8-0E4A-4FC6-886A-064283054674}"
+ "AbsoluteMoniker": "D:0:0:{F5AE8C3B-0221-4C16-A128-9A62D521A8FF}|WulaFallenEmpire.csproj|c:\\steam\\steamapps\\common\\rimworld\\mods\\3516260226\\source\\wulafallenempire\\harmonypatches\\projectile_launch_patch.cs||{A6C744A8-0E4A-4FC6-886A-064283054674}",
+ "RelativeMoniker": "D:0:0:{F5AE8C3B-0221-4C16-A128-9A62D521A8FF}|WulaFallenEmpire.csproj|solutionrelative:harmonypatches\\projectile_launch_patch.cs||{A6C744A8-0E4A-4FC6-886A-064283054674}"
+ },
+ {
+ "AbsoluteMoniker": "D:0:0:{F5AE8C3B-0221-4C16-A128-9A62D521A8FF}|WulaFallenEmpire.csproj|C:\\Steam\\steamapps\\common\\RimWorld\\Mods\\3516260226\\Source\\WulaFallenEmpire\\compuseeffect_wulaskilltrainer.cs||{A6C744A8-0E4A-4FC6-886A-064283054674}",
+ "RelativeMoniker": "D:0:0:{F5AE8C3B-0221-4C16-A128-9A62D521A8FF}|WulaFallenEmpire.csproj|solutionrelative:compuseeffect_wulaskilltrainer.cs||{A6C744A8-0E4A-4FC6-886A-064283054674}"
+ },
+ {
+ "AbsoluteMoniker": "D:0:0:{F5AE8C3B-0221-4C16-A128-9A62D521A8FF}|WulaFallenEmpire.csproj|c:\\steam\\steamapps\\common\\rimworld\\mods\\3516260226\\source\\wulafallenempire\\ingestpatch.cs||{A6C744A8-0E4A-4FC6-886A-064283054674}",
+ "RelativeMoniker": "D:0:0:{F5AE8C3B-0221-4C16-A128-9A62D521A8FF}|WulaFallenEmpire.csproj|solutionrelative:ingestpatch.cs||{A6C744A8-0E4A-4FC6-886A-064283054674}"
},
{
"AbsoluteMoniker": "D:0:0:{F5AE8C3B-0221-4C16-A128-9A62D521A8FF}|WulaFallenEmpire.csproj|C:\\Steam\\steamapps\\common\\RimWorld\\Mods\\3516260226\\Source\\WulaFallenEmpire\\building_wula_darkenergy_engine.cs||{A6C744A8-0E4A-4FC6-886A-064283054674}",
"RelativeMoniker": "D:0:0:{F5AE8C3B-0221-4C16-A128-9A62D521A8FF}|WulaFallenEmpire.csproj|solutionrelative:building_wula_darkenergy_engine.cs||{A6C744A8-0E4A-4FC6-886A-064283054674}"
},
{
- "AbsoluteMoniker": "D:0:0:{F5AE8C3B-0221-4C16-A128-9A62D521A8FF}|WulaFallenEmpire.csproj|C:\\Steam\\steamapps\\common\\RimWorld\\Mods\\3516260226\\Source\\WulaFallenEmpire\\compuseeffect_wulaskilltrainer.cs||{A6C744A8-0E4A-4FC6-886A-064283054674}",
- "RelativeMoniker": "D:0:0:{F5AE8C3B-0221-4C16-A128-9A62D521A8FF}|WulaFallenEmpire.csproj|solutionrelative:compuseeffect_wulaskilltrainer.cs||{A6C744A8-0E4A-4FC6-886A-064283054674}"
+ "AbsoluteMoniker": "D:0:0:{F5AE8C3B-0221-4C16-A128-9A62D521A8FF}|WulaFallenEmpire.csproj|C:\\Steam\\steamapps\\common\\RimWorld\\Mods\\3516260226\\Source\\WulaFallenEmpire\\hediffcomp_regeneratebackstory.cs||{A6C744A8-0E4A-4FC6-886A-064283054674}",
+ "RelativeMoniker": "D:0:0:{F5AE8C3B-0221-4C16-A128-9A62D521A8FF}|WulaFallenEmpire.csproj|solutionrelative:hediffcomp_regeneratebackstory.cs||{A6C744A8-0E4A-4FC6-886A-064283054674}"
},
{
"AbsoluteMoniker": "D:0:0:{F5AE8C3B-0221-4C16-A128-9A62D521A8FF}|WulaFallenEmpire.csproj|C:\\Steam\\steamapps\\common\\RimWorld\\Mods\\3516260226\\Source\\WulaFallenEmpire\\wulafallenempiremod.cs||{A6C744A8-0E4A-4FC6-886A-064283054674}",
@@ -34,15 +42,41 @@
"DocumentGroups": [
{
"DockedWidth": 200,
- "SelectedChildIndex": 1,
+ "SelectedChildIndex": 2,
"Children": [
{
"$type": "Bookmark",
"Name": "ST:0:0:{1c4feeaa-4718-4aa9-859d-94ce25d182ba}"
},
+ {
+ "$type": "Document",
+ "DocumentIndex": 1,
+ "Title": "Projectile_Launch_Patch.cs",
+ "DocumentMoniker": "C:\\Steam\\steamapps\\common\\RimWorld\\Mods\\3516260226\\Source\\WulaFallenEmpire\\HarmonyPatches\\Projectile_Launch_Patch.cs",
+ "RelativeDocumentMoniker": "HarmonyPatches\\Projectile_Launch_Patch.cs",
+ "ToolTip": "C:\\Steam\\steamapps\\common\\RimWorld\\Mods\\3516260226\\Source\\WulaFallenEmpire\\HarmonyPatches\\Projectile_Launch_Patch.cs",
+ "RelativeToolTip": "HarmonyPatches\\Projectile_Launch_Patch.cs",
+ "ViewState": "AQIAAAAAAAAAAAAAAAAAABwAAAAAAAAA",
+ "Icon": "ae27a6b0-e345-4288-96df-5eaf394ee369.000738|",
+ "WhenOpened": "2025-07-23T10:23:38.948Z",
+ "EditorCaption": ""
+ },
{
"$type": "Document",
"DocumentIndex": 0,
+ "Title": "CompApparelInterceptor.cs",
+ "DocumentMoniker": "C:\\Steam\\steamapps\\common\\RimWorld\\Mods\\3516260226\\Source\\WulaFallenEmpire\\CompApparelInterceptor.cs",
+ "RelativeDocumentMoniker": "CompApparelInterceptor.cs",
+ "ToolTip": "C:\\Steam\\steamapps\\common\\RimWorld\\Mods\\3516260226\\Source\\WulaFallenEmpire\\CompApparelInterceptor.cs",
+ "RelativeToolTip": "CompApparelInterceptor.cs",
+ "ViewState": "AQIAABoAAAAAAAAAAADwv2UBAAABAAAA",
+ "Icon": "ae27a6b0-e345-4288-96df-5eaf394ee369.000738|",
+ "WhenOpened": "2025-07-23T10:03:31.989Z",
+ "EditorCaption": ""
+ },
+ {
+ "$type": "Document",
+ "DocumentIndex": 3,
"Title": "IngestPatch.cs",
"DocumentMoniker": "C:\\Steam\\steamapps\\common\\RimWorld\\Mods\\3516260226\\Source\\WulaFallenEmpire\\IngestPatch.cs",
"RelativeDocumentMoniker": "IngestPatch.cs",
@@ -55,7 +89,7 @@
},
{
"$type": "Document",
- "DocumentIndex": 3,
+ "DocumentIndex": 2,
"Title": "CompUseEffect_WulaSkillTrainer.cs",
"DocumentMoniker": "C:\\Steam\\steamapps\\common\\RimWorld\\Mods\\3516260226\\Source\\WulaFallenEmpire\\CompUseEffect_WulaSkillTrainer.cs",
"RelativeDocumentMoniker": "CompUseEffect_WulaSkillTrainer.cs",
@@ -63,11 +97,12 @@
"RelativeToolTip": "CompUseEffect_WulaSkillTrainer.cs",
"ViewState": "AQIAAEAAAAAAAAAAAAAUwFMAAAABAAAA",
"Icon": "ae27a6b0-e345-4288-96df-5eaf394ee369.000738|",
- "WhenOpened": "2025-07-22T07:52:56.407Z"
+ "WhenOpened": "2025-07-22T07:52:56.407Z",
+ "EditorCaption": ""
},
{
"$type": "Document",
- "DocumentIndex": 2,
+ "DocumentIndex": 4,
"Title": "Building_Wula_DarkEnergy_Engine.cs",
"DocumentMoniker": "C:\\Steam\\steamapps\\common\\RimWorld\\Mods\\3516260226\\Source\\WulaFallenEmpire\\Building_Wula_DarkEnergy_Engine.cs",
"RelativeDocumentMoniker": "Building_Wula_DarkEnergy_Engine.cs",
@@ -79,7 +114,7 @@
},
{
"$type": "Document",
- "DocumentIndex": 1,
+ "DocumentIndex": 5,
"Title": "HediffComp_RegenerateBackstory.cs",
"DocumentMoniker": "C:\\Steam\\steamapps\\common\\RimWorld\\Mods\\3516260226\\Source\\WulaFallenEmpire\\HediffComp_RegenerateBackstory.cs",
"RelativeDocumentMoniker": "HediffComp_RegenerateBackstory.cs",
@@ -91,7 +126,7 @@
},
{
"$type": "Document",
- "DocumentIndex": 4,
+ "DocumentIndex": 6,
"Title": "WulaFallenEmpireMod.cs",
"DocumentMoniker": "C:\\Steam\\steamapps\\common\\RimWorld\\Mods\\3516260226\\Source\\WulaFallenEmpire\\WulaFallenEmpireMod.cs",
"RelativeDocumentMoniker": "WulaFallenEmpireMod.cs",
@@ -101,13 +136,9 @@
"Icon": "ae27a6b0-e345-4288-96df-5eaf394ee369.000738|",
"WhenOpened": "2025-07-18T10:23:17.898Z"
},
- {
- "$type": "Bookmark",
- "Name": "ST:0:0:{269a02dc-6af8-11d3-bdc4-00c04f688e50}"
- },
{
"$type": "Document",
- "DocumentIndex": 5,
+ "DocumentIndex": 7,
"Title": "MechanitorPatch.cs",
"DocumentMoniker": "C:\\Steam\\steamapps\\common\\RimWorld\\Mods\\3516260226\\Source\\WulaFallenEmpire\\MechanitorPatch.cs",
"RelativeDocumentMoniker": "MechanitorPatch.cs",
@@ -116,6 +147,10 @@
"ViewState": "AQIAAAAAAAAAAAAAAAAAACEAAAAJAAAA",
"Icon": "ae27a6b0-e345-4288-96df-5eaf394ee369.000738|",
"WhenOpened": "2025-07-18T10:20:31.368Z"
+ },
+ {
+ "$type": "Bookmark",
+ "Name": "ST:0:0:{269a02dc-6af8-11d3-bdc4-00c04f688e50}"
}
]
}
diff --git a/Source/WulaFallenEmpire/CompApparelInterceptor.cs b/Source/WulaFallenEmpire/CompApparelInterceptor.cs
new file mode 100644
index 00000000..c4395323
--- /dev/null
+++ b/Source/WulaFallenEmpire/CompApparelInterceptor.cs
@@ -0,0 +1,358 @@
+using HarmonyLib;
+using RimWorld;
+using System;
+using System.Collections.Generic;
+using System.Text;
+using UnityEngine;
+using Verse;
+using Verse.Sound;
+
+namespace WulaFallenEmpire
+{
+ public class CompProperties_ApparelInterceptor : CompProperties
+ {
+ public float radius = 3f;
+ public int startupDelay = 0;
+ public int rechargeDelay = 3200;
+ public int hitPoints = 100;
+
+ public bool interceptGroundProjectiles = false;
+ public bool interceptNonHostileProjectiles = false;
+ public bool interceptAirProjectiles = true;
+
+ public SoundDef soundIntercept;
+ public SoundDef soundBreak;
+ public EffecterDef reactivateEffect;
+
+ public Color color = new Color(0.5f, 0.5f, 0.9f);
+ public bool drawWithNoSelection = true;
+ public bool isImmuneToEMP = false;
+
+ public int cooldownTicks = 0;
+ public int chargeDurationTicks = 0;
+ public int chargeIntervalTicks = 0;
+ public bool startWithMaxHitPoints = true;
+ public bool hitPointsRestoreInstantlyAfterCharge = true;
+ public int rechargeHitPointsIntervalTicks = 60;
+ public bool activated = false;
+ public int activeDuration = 0;
+ public SoundDef activeSound;
+ public bool alwaysShowHitpointsGizmo = false;
+ public float minAlpha = 0f;
+ public float idlePulseSpeed = 0.02f;
+ public float minIdleAlpha = 0.05f;
+ public int disarmedByEmpForTicks = 0;
+
+ public CompProperties_ApparelInterceptor()
+ {
+ compClass = typeof(CompApparelInterceptor);
+ }
+ }
+
+ [StaticConstructorOnStartup]
+ public class CompApparelInterceptor : ThingComp
+ {
+ // ״̬
+ private int lastInterceptTicks = -999999;
+ private int startedChargingTick = -1;
+ private bool shutDown;
+ private StunHandler stunner;
+ private Sustainer sustainer;
+ public int currentHitPoints = -1;
+ private int ticksToReset;
+ private int activatedTick = -999999;
+
+ // ӾЧ
+ private float lastInterceptAngle;
+ private bool drawInterceptCone;
+
+ // ̬Դ
+ 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 static readonly Color InactiveColor = new Color(0.2f, 0.2f, 0.2f);
+
+ //
+ public CompProperties_ApparelInterceptor Props => (CompProperties_ApparelInterceptor)props;
+ private Pawn PawnOwner => (parent as Apparel)?.Wearer;
+
+ public bool Active
+ {
+ get
+ {
+ if (PawnOwner == null || !PawnOwner.Spawned) return false;
+ if (OnCooldown || Charging || stunner.Stunned || shutDown || currentHitPoints <= 0) return false;
+ if (Props.activated && Find.TickManager.TicksGame > activatedTick + Props.activeDuration) return false;
+ return true;
+ }
+ }
+
+ protected bool ShouldDisplay
+ {
+ get
+ {
+ if (PawnOwner == null || !PawnOwner.Spawned || PawnOwner.Dead || PawnOwner.Downed || !Active)
+ {
+ return false;
+ }
+ if (PawnOwner.Drafted || PawnOwner.InAggroMentalState || (PawnOwner.Faction != null && PawnOwner.Faction.HostileTo(Faction.OfPlayer) && !PawnOwner.IsPrisoner))
+ {
+ return true;
+ }
+ if (Find.Selector.IsSelected(PawnOwner))
+ {
+ return true;
+ }
+ return false;
+ }
+ }
+
+ public bool OnCooldown => ticksToReset > 0;
+ public bool Charging => startedChargingTick >= 0 && Find.TickManager.TicksGame < startedChargingTick + Props.startupDelay;
+ public int CooldownTicksLeft => ticksToReset;
+ public int ChargingTicksLeft => (startedChargingTick < 0) ? 0 : Mathf.Max(startedChargingTick + Props.startupDelay - Find.TickManager.TicksGame, 0);
+ public int HitPointsMax => Props.hitPoints;
+ protected virtual int HitPointsPerInterval => 1;
+
+ public override void PostPostMake()
+ {
+ base.PostPostMake();
+ stunner = new StunHandler(parent);
+ if (Props.startupDelay > 0)
+ {
+ startedChargingTick = Find.TickManager.TicksGame;
+ currentHitPoints = 0;
+ }
+ else
+ {
+ currentHitPoints = HitPointsMax;
+ }
+ }
+
+ public override void PostExposeData()
+ {
+ base.PostExposeData();
+ Scribe_Values.Look(ref lastInterceptTicks, "lastInterceptTicks", -999999);
+ Scribe_Values.Look(ref shutDown, "shutDown", defaultValue: false);
+ Scribe_Values.Look(ref startedChargingTick, "startedChargingTick", -1);
+ Scribe_Values.Look(ref currentHitPoints, "currentHitPoints", -1);
+ Scribe_Values.Look(ref ticksToReset, "ticksToReset", 0);
+ Scribe_Values.Look(ref activatedTick, "activatedTick", -999999);
+ Scribe_Deep.Look(ref stunner, "stunner", parent);
+
+ if (Scribe.mode == LoadSaveMode.PostLoadInit)
+ {
+ if (stunner == null) stunner = new StunHandler(parent);
+ if (currentHitPoints == -1) currentHitPoints = HitPointsMax;
+ }
+ }
+
+ public bool TryIntercept(Projectile projectile, Vector3 lastExactPos, Vector3 newExactPos)
+ {
+ if (PawnOwner == null || !PawnOwner.Spawned || !Active)
+ {
+ return false;
+ }
+
+ if (!GenGeo.IntersectLineCircleOutline(PawnOwner.Position.ToVector2(), Props.radius, lastExactPos.ToVector2(), newExactPos.ToVector2()))
+ {
+ return false;
+ }
+
+ if (!InterceptsProjectile(Props, projectile))
+ {
+ return false;
+ }
+
+ bool isHostile = (projectile.Launcher != null && projectile.Launcher.HostileTo(PawnOwner)) || (projectile.Launcher == null && Props.interceptNonHostileProjectiles);
+ if (!isHostile)
+ {
+ return false;
+ }
+
+ // --- Interception Success ---
+ lastInterceptAngle = projectile.ExactPosition.AngleToFlat(PawnOwner.TrueCenter());
+ lastInterceptTicks = Find.TickManager.TicksGame;
+ drawInterceptCone = true;
+ if (Props.soundIntercept != null) Props.soundIntercept.PlayOneShot(new TargetInfo(PawnOwner.Position, PawnOwner.Map));
+ EffecterDefOf.Interceptor_BlockedProjectile.Spawn(PawnOwner.Position, PawnOwner.Map);
+
+ if (projectile.DamageDef == DamageDefOf.EMP && !Props.isImmuneToEMP)
+ {
+ BreakShieldEmp(new DamageInfo(projectile.DamageDef, projectile.DamageAmount, instigator: projectile.Launcher));
+ }
+ else if (HitPointsMax > 0)
+ {
+ currentHitPoints -= projectile.DamageAmount;
+ if (currentHitPoints <= 0)
+ {
+ BreakShieldHitpoints(new DamageInfo(projectile.DamageDef, projectile.DamageAmount, instigator: projectile.Launcher));
+ }
+ }
+ return true;
+ }
+
+ public override void CompTick()
+ {
+ base.CompTick();
+ if (PawnOwner == null || !PawnOwner.Spawned) return;
+
+ stunner.StunHandlerTick();
+
+ if (OnCooldown)
+ {
+ ticksToReset--;
+ if (ticksToReset <= 0) Reset();
+ }
+ else if (Charging)
+ {
+ // Charging logic handled by property
+ }
+ else if (currentHitPoints < HitPointsMax && parent.IsHashIntervalTick(Props.rechargeHitPointsIntervalTicks))
+ {
+ currentHitPoints = Mathf.Clamp(currentHitPoints + HitPointsPerInterval, 0, HitPointsMax);
+ }
+
+ if (Props.activeSound != null)
+ {
+ if (Active && (sustainer == null || sustainer.Ended)) sustainer = Props.activeSound.TrySpawnSustainer(SoundInfo.InMap(parent));
+ sustainer?.Maintain();
+ if (!Active && sustainer != null && !sustainer.Ended) sustainer.End();
+ }
+ }
+
+ public void Reset()
+ {
+ if (PawnOwner.Spawned) Props.reactivateEffect?.Spawn(PawnOwner.Position, PawnOwner.Map).Cleanup();
+ currentHitPoints = HitPointsMax;
+ ticksToReset = 0;
+ }
+
+ private void BreakShieldHitpoints(DamageInfo dinfo)
+ {
+ if (PawnOwner.Spawned)
+ {
+ if (Props.soundBreak != null) Props.soundBreak.PlayOneShot(new TargetInfo(PawnOwner.Position, PawnOwner.Map));
+ EffecterDefOf.Shield_Break.SpawnAttached(PawnOwner, PawnOwner.MapHeld, Props.radius);
+ }
+ currentHitPoints = 0;
+ ticksToReset = Props.rechargeDelay;
+ }
+
+ private void BreakShieldEmp(DamageInfo dinfo)
+ {
+ BreakShieldHitpoints(dinfo);
+ if (Props.disarmedByEmpForTicks > 0) stunner.Notify_DamageApplied(new DamageInfo(DamageDefOf.EMP, (float)Props.disarmedByEmpForTicks / 30f));
+ }
+
+ public static bool InterceptsProjectile(CompProperties_ApparelInterceptor props, Projectile projectile)
+ {
+ if (projectile.def.projectile.flyOverhead) return props.interceptAirProjectiles;
+ return props.interceptGroundProjectiles;
+ }
+
+ // --- DRAWING LOGIC ---
+ public override void CompDrawWornExtras()
+ {
+ base.CompDrawWornExtras();
+ if (PawnOwner == null || !PawnOwner.Spawned || !ShouldDisplay) return;
+
+ Vector3 drawPos = PawnOwner.Drawer.DrawPos;
+ 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(Matrix4x4);
+ matrix.SetTRS(drawPos, Quaternion.identity, new Vector3(Props.radius * 2f * 1.1601562f, 1f, Props.radius * 2f * 1.1601562f));
+ Graphics.DrawMesh(MeshPool.plane10, matrix, ForceFieldMat, 0, null, 0, MatPropertyBlock);
+ }
+
+ float coneAlpha = GetCurrentConeAlpha_RecentlyIntercepted();
+ if (coneAlpha > 0f)
+ {
+ Color color = Props.color;
+ color.a *= coneAlpha;
+ MatPropertyBlock.SetColor(ShaderPropertyIDs.Color, color);
+ Matrix4x4 matrix = default(Matrix4x4);
+ matrix.SetTRS(drawPos, Quaternion.Euler(0f, lastInterceptAngle - 90f, 0f), new Vector3(Props.radius * 2f, 1f, Props.radius * 2f));
+ Graphics.DrawMesh(MeshPool.plane10, matrix, ForceFieldConeMat, 0, null, 0, MatPropertyBlock);
+ }
+ }
+
+ 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_RecentlyIntercepted()
+ {
+ if (!drawInterceptCone) return 0f;
+ return Mathf.Clamp01(1f - (float)(Find.TickManager.TicksGame - lastInterceptTicks) / 40f) * 0.82f;
+ }
+
+ // --- GIZMO ---
+ public override IEnumerable CompGetWornGizmosExtra()
+ {
+ if (PawnOwner != null && Find.Selector.SingleSelectedThing == PawnOwner)
+ {
+ yield return new Gizmo_EnergyShieldStatus { shield = this };
+ }
+ }
+
+ public override string CompInspectStringExtra()
+ {
+ StringBuilder sb = new StringBuilder();
+ if (OnCooldown)
+ {
+ sb.Append("Cooldown: " + CooldownTicksLeft.ToStringTicksToPeriod());
+ }
+ else if (stunner.Stunned)
+ {
+ sb.Append("EMP Shutdown: " + stunner.StunTicksLeft.ToStringTicksToPeriod());
+ }
+ return sb.ToString();
+ }
+ }
+
+ public class Gizmo_EnergyShieldStatus : Gizmo
+ {
+ public CompApparelInterceptor 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;
+
+ public override float GetWidth(float maxWidth) => 140f;
+
+ public override GizmoResult GizmoOnGUI(Vector2 topLeft, float maxWidth, GizmoRenderParms parms)
+ {
+ Rect rect = new Rect(topLeft.x, topLeft.y, GetWidth(maxWidth), 75f);
+ Rect rect2 = rect.ContractedBy(6f);
+ Widgets.DrawWindowBackground(rect);
+
+ Rect labelRect = rect2;
+ labelRect.height = rect.height / 2f;
+ Text.Font = GameFont.Tiny;
+ Widgets.Label(labelRect, shield.parent.LabelCap);
+
+ Rect barRect = rect2;
+ barRect.yMin = rect2.y + rect2.height / 2f;
+ float fillPercent = (float)shield.currentHitPoints / shield.HitPointsMax;
+ Widgets.FillableBar(barRect, fillPercent, FullShieldBarTex, EmptyShieldBarTex, false);
+
+ Text.Font = GameFont.Small;
+ Text.Anchor = TextAnchor.MiddleCenter;
+
+ TaggedString statusText = shield.OnCooldown ? "Broken".Translate() : new TaggedString(shield.currentHitPoints + " / " + shield.HitPointsMax);
+ Widgets.Label(barRect, statusText);
+
+ Text.Anchor = TextAnchor.UpperLeft;
+
+ return new GizmoResult(GizmoState.Clear);
+ }
+ }
+}
\ No newline at end of file
diff --git a/Source/WulaFallenEmpire/HarmonyPatches/Projectile_Launch_Patch.cs b/Source/WulaFallenEmpire/HarmonyPatches/Projectile_Launch_Patch.cs
new file mode 100644
index 00000000..8b094b13
--- /dev/null
+++ b/Source/WulaFallenEmpire/HarmonyPatches/Projectile_Launch_Patch.cs
@@ -0,0 +1,42 @@
+using HarmonyLib;
+using RimWorld;
+using System.Linq;
+using System.Reflection;
+using UnityEngine;
+using Verse;
+
+namespace WulaFallenEmpire.HarmonyPatches
+{
+ [HarmonyPatch(typeof(Projectile), "CheckForFreeInterceptBetween")]
+ public static class Projectile_CheckForFreeInterceptBetween_Patch
+ {
+ private static readonly MethodInfo ImpactMethod = AccessTools.Method(typeof(Projectile), "Impact");
+
+ public static bool Prefix(Projectile __instance, Vector3 lastExactPos, Vector3 newExactPos)
+ {
+ if (__instance.Map == null || __instance.Destroyed) return true;
+
+ foreach (Pawn pawn in __instance.Map.mapPawns.AllPawnsSpawned)
+ {
+ if (pawn.apparel != null)
+ {
+ foreach (Apparel apparel in pawn.apparel.WornApparel)
+ {
+ if (apparel.TryGetComp(out var interceptor))
+ {
+ if (interceptor.TryIntercept(__instance, lastExactPos, newExactPos))
+ {
+
+ ImpactMethod.Invoke(__instance, new object[] { null, true });
+
+ return false;
+ }
+ }
+ }
+ }
+ }
+
+ return true;
+ }
+ }
+}
\ No newline at end of file
diff --git a/Source/WulaFallenEmpire/WulaFallenEmpire.csproj b/Source/WulaFallenEmpire/WulaFallenEmpire.csproj
index 97b2c355..9eb95358 100644
--- a/Source/WulaFallenEmpire/WulaFallenEmpire.csproj
+++ b/Source/WulaFallenEmpire/WulaFallenEmpire.csproj
@@ -62,6 +62,7 @@
+
@@ -86,6 +87,7 @@
+
@@ -94,4 +96,4 @@
-
\ No newline at end of file
+