diff --git a/1.6/1.6/Assemblies/ArachnaeSwarm.dll b/1.6/1.6/Assemblies/ArachnaeSwarm.dll
index 3c6968a..658be22 100644
Binary files a/1.6/1.6/Assemblies/ArachnaeSwarm.dll and b/1.6/1.6/Assemblies/ArachnaeSwarm.dll differ
diff --git a/1.6/1.6/Defs/HediffDefs/ARA_GuardianPsyField_Hediff.xml b/1.6/1.6/Defs/HediffDefs/ARA_GuardianPsyField_Hediff.xml
index 0b5e5e1..c197be1 100644
--- a/1.6/1.6/Defs/HediffDefs/ARA_GuardianPsyField_Hediff.xml
+++ b/1.6/1.6/Defs/HediffDefs/ARA_GuardianPsyField_Hediff.xml
@@ -19,18 +19,15 @@
60
- 0.5
+
+ 0.001
0.1
0.01
-
- true
- true
- true
-
- (0.3, 0.8, 0.8)
+ (0.5, 0.3, 0.9, 0.5)
Interceptor_BlockedProjectile
+ Interceptor_BlockedProjectile
Shield_Break
BulletShieldGenerator_Reactivate
diff --git a/Source/ArachnaeSwarm/Hediff_DynamicInterceptor.cs b/Source/ArachnaeSwarm/Hediff_DynamicInterceptor.cs
index 05a3809..3c939c3 100644
--- a/Source/ArachnaeSwarm/Hediff_DynamicInterceptor.cs
+++ b/Source/ArachnaeSwarm/Hediff_DynamicInterceptor.cs
@@ -7,19 +7,12 @@ namespace ArachnaeSwarm
{
public class Hediff_DynamicInterceptor : HediffWithComps
{
- // This Hediff's job is to add and remove our custom ThingComp_GuardianPsyField.
-
public CompProperties_GuardianPsyField GuardianProps
{
get
{
- var compProps = def.comps?.FirstOrDefault(c => c is HediffCompProperties_DynamicInterceptor);
- if (compProps is HediffCompProperties_DynamicInterceptor customProps)
- {
- // Note: The XML now needs to contain CompProperties_GuardianPsyField
- return customProps.guardianProps;
- }
- return null;
+ var hediffCompProps = def.comps?.FirstOrDefault(c => c is HediffCompProperties_DynamicInterceptor) as HediffCompProperties_DynamicInterceptor;
+ return hediffCompProps?.guardianProps;
}
}
@@ -34,6 +27,7 @@ namespace ArachnaeSwarm
Log.Message($"[DynamicInterceptor] Adding ThingComp_GuardianPsyField to {pawn.LabelShort}.");
var newComp = (ThingComp_GuardianPsyField)Activator.CreateInstance(typeof(ThingComp_GuardianPsyField));
newComp.parent = pawn;
+ // Initialize with the actual properties from the HediffDef
newComp.Initialize(props);
pawn.AllComps.Add(newComp);
}
@@ -58,8 +52,7 @@ namespace ArachnaeSwarm
// This comp will hold the properties for our custom interceptor
public class HediffCompProperties_DynamicInterceptor : HediffCompProperties
{
- // This will now point to our custom properties class
- public CompProperties_GuardianPsyField guardianProps;
+ public CompProperties_GuardianPsyField guardianProps; // Nested properties
public HediffCompProperties_DynamicInterceptor()
{
@@ -70,6 +63,6 @@ namespace ArachnaeSwarm
// A simple HediffComp to go with the properties
public class HediffComp_DynamicInterceptor : HediffComp
{
- public HediffCompProperties_DynamicInterceptor Props => (HediffCompProperties_DynamicInterceptor)props;
+ public HediffCompProperties_DynamicInterceptor Props => props as HediffCompProperties_DynamicInterceptor;
}
}
\ No newline at end of file
diff --git a/Source/ArachnaeSwarm/ThingComp_GuardianPsyField.cs b/Source/ArachnaeSwarm/ThingComp_GuardianPsyField.cs
index fde8b4a..d8efac6 100644
--- a/Source/ArachnaeSwarm/ThingComp_GuardianPsyField.cs
+++ b/Source/ArachnaeSwarm/ThingComp_GuardianPsyField.cs
@@ -14,14 +14,14 @@ namespace ArachnaeSwarm
public int rechargeHitPointsIntervalTicks = 60; // Ticks to restore 1 HP
// New properties for psyfocus/entropy mechanics
- public float psyfocusCostForFullRecharge = 0.5f; // 50% psyfocus cost
- public float entropyGainPerDamage = 0.5f; // 1 entropy per 2 damage
+ // Removed psyfocusCostForFullRecharge
+ public float psyfocusCostPerInterval = 0.001f; // e.g., 0.1% of max psyfocus per recharge interval
+ public float entropyGainPerDamage = 0.5f; // For self
+ // Removed entropyGainPerAllyDamage
public float hitPointsPctPerInterval = 0.01f; // Restore 1% of max HP per interval
+ public EffecterDef absorbEffecter;
+ // Removed transferAllyDamage
- public bool interceptGroundProjectiles = true;
- public bool interceptAirProjectiles = true;
- public bool interceptNonHostileProjectiles = false;
-
public EffecterDef interceptEffecter;
public EffecterDef breakEffecter;
public EffecterDef reactivateEffecter;
@@ -57,11 +57,18 @@ namespace ArachnaeSwarm
{
get
{
- if (PawnOwner == null || !PawnOwner.Spawned || PawnOwner.Dead || PawnOwner.Downed || IsOnCooldown || currentHitPoints <= 0)
+ // Shield is active if pawn is valid, hediff is present, not on cooldown, and has psyfocus.
+ // currentHitPoints <= 0 should NOT prevent activation for recharge.
+ if (PawnOwner == null || !PawnOwner.Spawned || PawnOwner.Dead || PawnOwner.Downed || IsOnCooldown)
return false;
var hediff = PawnOwner.health.hediffSet.GetFirstHediffOfDef(HediffDef.Named("ARA_GuardianPsyField"));
- return hediff != null;
+ if (hediff == null) return false;
+
+ // Shield is only active if pawn has psyfocus
+ if (PawnOwner.psychicEntropy == null || PawnOwner.psychicEntropy.CurrentPsyfocus <= 0) return false;
+
+ return true;
}
}
@@ -92,60 +99,134 @@ namespace ArachnaeSwarm
Reset();
}
}
- else if (currentHitPoints < HitPointsMax)
+ // Only allow recharge if the shield is 'Active' (i.e., has psyfocus) and not on cooldown
+ else if (Active && currentHitPoints < HitPointsMax)
{
- wasNotAtFullHp = true; // Mark that the shield was damaged
- if(this.parent.IsHashIntervalTick(Props.rechargeHitPointsIntervalTicks))
+ // Check if there's enough psyfocus to pay for this interval's recharge
+ if (PawnOwner.psychicEntropy != null && PawnOwner.psychicEntropy.CurrentPsyfocus >= Props.psyfocusCostPerInterval)
{
- currentHitPoints += (int)(HitPointsMax * Props.hitPointsPctPerInterval);
- if(currentHitPoints > HitPointsMax) currentHitPoints = HitPointsMax;
+ wasNotAtFullHp = true; // Mark that the shield was damaged
+ if(this.parent.IsHashIntervalTick(Props.rechargeHitPointsIntervalTicks))
+ {
+ currentHitPoints += (int)(HitPointsMax * Props.hitPointsPctPerInterval);
+ if(currentHitPoints > HitPointsMax) currentHitPoints = HitPointsMax;
+
+ // Deduct psyfocus for this interval
+ PawnOwner.psychicEntropy.OffsetPsyfocusDirectly(-Props.psyfocusCostPerInterval);
+ }
+ }
+ else
+ {
+ // Not enough psyfocus to recharge, log for debugging
+ Log.Message($"[GuardianFieldComp] {PawnOwner.LabelShort} has insufficient psyfocus ({PawnOwner.psychicEntropy?.CurrentPsyfocus ?? 0}) to recharge shield (cost: {Props.psyfocusCostPerInterval}).");
}
}
+ // The full recharge psyfocus deduction is removed, as it's now gradual.
+ // The wasNotAtFullHp check is still useful for other potential effects when full.
else if (wasNotAtFullHp && currentHitPoints >= HitPointsMax)
{
- // Shield just reached full charge
- wasNotAtFullHp = false;
- if (PawnOwner.psychicEntropy != null && Props.psyfocusCostForFullRecharge > 0)
- {
- float maxPsyfocus = PawnOwner.GetStatValue(StatDefOf.PsychicEntropyMax);
- PawnOwner.psychicEntropy.OffsetPsyfocusDirectly(-maxPsyfocus * Props.psyfocusCostForFullRecharge);
- }
+ wasNotAtFullHp = false; // Reset flag when full
}
}
+ // Projectile interception logic remains the same, as it's a separate Harmony patch
public bool TryIntercept(Projectile projectile)
{
if (!Active) return false;
- bool isHostile = projectile.Launcher != null && projectile.Launcher.HostileTo(PawnOwner.Faction);
- if (!isHostile && !Props.interceptNonHostileProjectiles) return false;
-
- if (Vector3.Distance(projectile.ExactPosition, PawnOwner.TrueCenter()) > Props.radius) return false;
-
+ // We now intercept all projectiles, the filter will be done by the PostPreApplyDamage on self
+ // and the TryAbsorbDamageForAllyOnly for allies.
+ // This method is only for projectile visual/sound and psyfocus cost.
+
// --- Interception Success ---
lastInterceptTicks = Find.TickManager.TicksGame;
// Spawn effect at the point of interception, not the shield center
Props.interceptEffecter?.Spawn(projectile.ExactPosition.ToIntVec3(), PawnOwner.Map).Cleanup();
- float damageAmount = projectile.DamageAmount;
+ // No longer consuming hitpoints here, PostPreApplyDamage will handle it for self.
+ // The Harmony_DamageWorker for allies will handle it for allies.
- // Add entropy based on damage
+ return true;
+ }
+
+ // --- NEW: PostPreApplyDamage for self-protection (all damage types) ---
+ public override void PostPreApplyDamage(ref DamageInfo dinfo, out bool absorbed)
+ {
+ absorbed = false;
+ if (!Active || PawnOwner == null) return; // Only intercept if shield is active and for the owner
+
+ // We intercept ALL damage types for the owner
+ if (currentHitPoints < dinfo.Amount) return; // Not enough HP to absorb
+
+ // --- Absorption Success for self ---
+ Props.absorbEffecter?.Spawn(PawnOwner.Position, PawnOwner.Map).Cleanup(); // Effect at center for self-damage
+
+ // Add entropy based on damage taken for self
if (PawnOwner.psychicEntropy != null && Props.entropyGainPerDamage > 0)
{
- PawnOwner.psychicEntropy.TryAddEntropy(damageAmount * Props.entropyGainPerDamage, overLimit: true);
+ PawnOwner.psychicEntropy.TryAddEntropy(dinfo.Amount * Props.entropyGainPerDamage, overLimit: true);
}
// Consume Hitpoints
- currentHitPoints -= (int)damageAmount;
+ currentHitPoints -= (int)dinfo.Amount;
if (currentHitPoints <= 0)
{
Break();
}
-
- return true;
+ absorbed = true; // Damage was absorbed
}
+
+ // --- REMOVED: Method for allies, called by Harmony patch ---
+ // public bool TryAbsorbDamageForAllyOnly(DamageInfo dinfo, Pawn allyPawn)
+ // {
+ // Log.Message($"[GuardianFieldComp] TryAbsorbDamageForAllyOnly for {allyPawn.LabelShort} (target) by {PawnOwner.LabelShort} (caster).");
+ // Log.Message($" - transferAllyDamage: {Props.transferAllyDamage}");
+ // Log.Message($" - Active: {Active}");
+ // Log.Message($" - allyPawn == PawnOwner: {allyPawn == PawnOwner}");
+ // Log.Message($" - allyPawn.Faction: {allyPawn.Faction?.Name ?? "Null"}, PawnOwner.Faction: {PawnOwner.Faction?.Name ?? "Null"}, FactionMatch: {allyPawn.Faction == PawnOwner.Faction}");
+ // Log.Message($" - InRange: {Vector3.Distance(allyPawn.TrueCenter(), PawnOwner.TrueCenter()) <= Props.radius}");
+ // Log.Message($" - HasHP: {currentHitPoints >= dinfo.Amount}");
+
+ // // Check if ally damage transfer is enabled
+ // if (!Props.transferAllyDamage) { Log.Message(" -> transferAllyDamage is false."); return false; }
+
+ // // Shield must be active
+ // if (!Active) { Log.Message(" -> Shield is not Active."); return false; }
+
+ // // Cannot absorb damage for self (handled by PostPreApplyDamage)
+ // if (allyPawn == PawnOwner) { Log.Message(" -> Target is self."); return false; }
+
+ // // Only protect friendly pawns (same faction)
+ // if (allyPawn.Faction == null || PawnOwner.Faction == null || allyPawn.Faction != PawnOwner.Faction) { Log.Message(" -> Faction mismatch or null faction."); return false; }
+
+ // // Target must be in range
+ // if (Vector3.Distance(allyPawn.TrueCenter(), PawnOwner.TrueCenter()) > Props.radius) { Log.Message(" -> Target out of range."); return false; }
+
+ // // Check if shield has enough HP
+ // if (currentHitPoints < dinfo.Amount) { Log.Message(" -> Not enough HP to absorb damage."); return false; }
+
+ // // --- Absorption Success for ally ---
+ // Props.absorbEffecter?.Spawn(allyPawn.Position, allyPawn.Map).Cleanup();
+
+ // // Add entropy based on damage taken for ally
+ // if (PawnOwner.psychicEntropy != null && Props.entropyGainPerAllyDamage > 0)
+ // {
+ // PawnOwner.psychicEntropy.TryAddEntropy(dinfo.Amount * Props.entropyGainPerAllyDamage, overLimit: true);
+ // }
+
+ // // Consume Hitpoints
+ // currentHitPoints -= (int)dinfo.Amount;
+ // if (currentHitPoints <= 0)
+ // {
+ // Break();
+ // }
+
+ // Log.Message(" -> Damage absorption successful!");
+ // return true; // Damage was absorbed
+ // }
+
private void Break()
{
Props.breakEffecter?.Spawn(PawnOwner.Position, PawnOwner.Map).Cleanup();