存
This commit is contained in:
Binary file not shown.
@@ -19,18 +19,15 @@
|
|||||||
<rechargeHitPointsIntervalTicks>60</rechargeHitPointsIntervalTicks>
|
<rechargeHitPointsIntervalTicks>60</rechargeHitPointsIntervalTicks>
|
||||||
|
|
||||||
<!-- Psyfocus/Entropy Mechanics -->
|
<!-- Psyfocus/Entropy Mechanics -->
|
||||||
<psyfocusCostForFullRecharge>0.5</psyfocusCostForFullRecharge> <!-- 50% of max psyfocus -->
|
<!-- Removed psyfocusCostForFullRecharge as it's now gradual -->
|
||||||
|
<psyfocusCostPerInterval>0.001</psyfocusCostPerInterval> <!-- e.g., 0.1% of max psyfocus per recharge interval -->
|
||||||
<entropyGainPerDamage>0.1</entropyGainPerDamage> <!-- 1 entropy per 2 damage -->
|
<entropyGainPerDamage>0.1</entropyGainPerDamage> <!-- 1 entropy per 2 damage -->
|
||||||
<hitPointsPctPerInterval>0.01</hitPointsPctPerInterval> <!-- Restore 1% of max HP per interval -->
|
<hitPointsPctPerInterval>0.01</hitPointsPctPerInterval> <!-- Restore 1% of max HP per interval -->
|
||||||
|
|
||||||
<!-- Interception types -->
|
|
||||||
<interceptGroundProjectiles>true</interceptGroundProjectiles>
|
|
||||||
<interceptAirProjectiles>true</interceptAirProjectiles>
|
|
||||||
<interceptNonHostileProjectiles>true</interceptNonHostileProjectiles>
|
|
||||||
|
|
||||||
<!-- Visuals and Sound -->
|
<!-- Visuals and Sound -->
|
||||||
<color>(0.3, 0.8, 0.8)</color>
|
<color>(0.5, 0.3, 0.9, 0.5)</color>
|
||||||
<interceptEffecter>Interceptor_BlockedProjectile</interceptEffecter>
|
<interceptEffecter>Interceptor_BlockedProjectile</interceptEffecter>
|
||||||
|
<absorbEffecter>Interceptor_BlockedProjectile</absorbEffecter> <!-- Reusing an effecter for now -->
|
||||||
<breakEffecter>Shield_Break</breakEffecter>
|
<breakEffecter>Shield_Break</breakEffecter>
|
||||||
<reactivateEffecter>BulletShieldGenerator_Reactivate</reactivateEffecter>
|
<reactivateEffecter>BulletShieldGenerator_Reactivate</reactivateEffecter>
|
||||||
</guardianProps>
|
</guardianProps>
|
||||||
|
|||||||
@@ -7,19 +7,12 @@ namespace ArachnaeSwarm
|
|||||||
{
|
{
|
||||||
public class Hediff_DynamicInterceptor : HediffWithComps
|
public class Hediff_DynamicInterceptor : HediffWithComps
|
||||||
{
|
{
|
||||||
// This Hediff's job is to add and remove our custom ThingComp_GuardianPsyField.
|
|
||||||
|
|
||||||
public CompProperties_GuardianPsyField GuardianProps
|
public CompProperties_GuardianPsyField GuardianProps
|
||||||
{
|
{
|
||||||
get
|
get
|
||||||
{
|
{
|
||||||
var compProps = def.comps?.FirstOrDefault(c => c is HediffCompProperties_DynamicInterceptor);
|
var hediffCompProps = def.comps?.FirstOrDefault(c => c is HediffCompProperties_DynamicInterceptor) as HediffCompProperties_DynamicInterceptor;
|
||||||
if (compProps is HediffCompProperties_DynamicInterceptor customProps)
|
return hediffCompProps?.guardianProps;
|
||||||
{
|
|
||||||
// Note: The XML now needs to contain CompProperties_GuardianPsyField
|
|
||||||
return customProps.guardianProps;
|
|
||||||
}
|
|
||||||
return null;
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -34,6 +27,7 @@ namespace ArachnaeSwarm
|
|||||||
Log.Message($"[DynamicInterceptor] Adding ThingComp_GuardianPsyField to {pawn.LabelShort}.");
|
Log.Message($"[DynamicInterceptor] Adding ThingComp_GuardianPsyField to {pawn.LabelShort}.");
|
||||||
var newComp = (ThingComp_GuardianPsyField)Activator.CreateInstance(typeof(ThingComp_GuardianPsyField));
|
var newComp = (ThingComp_GuardianPsyField)Activator.CreateInstance(typeof(ThingComp_GuardianPsyField));
|
||||||
newComp.parent = pawn;
|
newComp.parent = pawn;
|
||||||
|
// Initialize with the actual properties from the HediffDef
|
||||||
newComp.Initialize(props);
|
newComp.Initialize(props);
|
||||||
pawn.AllComps.Add(newComp);
|
pawn.AllComps.Add(newComp);
|
||||||
}
|
}
|
||||||
@@ -58,8 +52,7 @@ namespace ArachnaeSwarm
|
|||||||
// This comp will hold the properties for our custom interceptor
|
// This comp will hold the properties for our custom interceptor
|
||||||
public class HediffCompProperties_DynamicInterceptor : HediffCompProperties
|
public class HediffCompProperties_DynamicInterceptor : HediffCompProperties
|
||||||
{
|
{
|
||||||
// This will now point to our custom properties class
|
public CompProperties_GuardianPsyField guardianProps; // Nested properties
|
||||||
public CompProperties_GuardianPsyField guardianProps;
|
|
||||||
|
|
||||||
public HediffCompProperties_DynamicInterceptor()
|
public HediffCompProperties_DynamicInterceptor()
|
||||||
{
|
{
|
||||||
@@ -70,6 +63,6 @@ namespace ArachnaeSwarm
|
|||||||
// A simple HediffComp to go with the properties
|
// A simple HediffComp to go with the properties
|
||||||
public class HediffComp_DynamicInterceptor : HediffComp
|
public class HediffComp_DynamicInterceptor : HediffComp
|
||||||
{
|
{
|
||||||
public HediffCompProperties_DynamicInterceptor Props => (HediffCompProperties_DynamicInterceptor)props;
|
public HediffCompProperties_DynamicInterceptor Props => props as HediffCompProperties_DynamicInterceptor;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -14,14 +14,14 @@ namespace ArachnaeSwarm
|
|||||||
public int rechargeHitPointsIntervalTicks = 60; // Ticks to restore 1 HP
|
public int rechargeHitPointsIntervalTicks = 60; // Ticks to restore 1 HP
|
||||||
|
|
||||||
// New properties for psyfocus/entropy mechanics
|
// New properties for psyfocus/entropy mechanics
|
||||||
public float psyfocusCostForFullRecharge = 0.5f; // 50% psyfocus cost
|
// Removed psyfocusCostForFullRecharge
|
||||||
public float entropyGainPerDamage = 0.5f; // 1 entropy per 2 damage
|
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 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 interceptEffecter;
|
||||||
public EffecterDef breakEffecter;
|
public EffecterDef breakEffecter;
|
||||||
public EffecterDef reactivateEffecter;
|
public EffecterDef reactivateEffecter;
|
||||||
@@ -57,11 +57,18 @@ namespace ArachnaeSwarm
|
|||||||
{
|
{
|
||||||
get
|
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;
|
return false;
|
||||||
|
|
||||||
var hediff = PawnOwner.health.hediffSet.GetFirstHediffOfDef(HediffDef.Named("ARA_GuardianPsyField"));
|
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();
|
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
|
// Check if there's enough psyfocus to pay for this interval's recharge
|
||||||
if(this.parent.IsHashIntervalTick(Props.rechargeHitPointsIntervalTicks))
|
if (PawnOwner.psychicEntropy != null && PawnOwner.psychicEntropy.CurrentPsyfocus >= Props.psyfocusCostPerInterval)
|
||||||
{
|
{
|
||||||
currentHitPoints += (int)(HitPointsMax * Props.hitPointsPctPerInterval);
|
wasNotAtFullHp = true; // Mark that the shield was damaged
|
||||||
if(currentHitPoints > HitPointsMax) currentHitPoints = HitPointsMax;
|
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)
|
else if (wasNotAtFullHp && currentHitPoints >= HitPointsMax)
|
||||||
{
|
{
|
||||||
// Shield just reached full charge
|
wasNotAtFullHp = false; // Reset flag when full
|
||||||
wasNotAtFullHp = false;
|
|
||||||
if (PawnOwner.psychicEntropy != null && Props.psyfocusCostForFullRecharge > 0)
|
|
||||||
{
|
|
||||||
float maxPsyfocus = PawnOwner.GetStatValue(StatDefOf.PsychicEntropyMax);
|
|
||||||
PawnOwner.psychicEntropy.OffsetPsyfocusDirectly(-maxPsyfocus * Props.psyfocusCostForFullRecharge);
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Projectile interception logic remains the same, as it's a separate Harmony patch
|
||||||
public bool TryIntercept(Projectile projectile)
|
public bool TryIntercept(Projectile projectile)
|
||||||
{
|
{
|
||||||
if (!Active) return false;
|
if (!Active) return false;
|
||||||
|
|
||||||
bool isHostile = projectile.Launcher != null && projectile.Launcher.HostileTo(PawnOwner.Faction);
|
// We now intercept all projectiles, the filter will be done by the PostPreApplyDamage on self
|
||||||
if (!isHostile && !Props.interceptNonHostileProjectiles) return false;
|
// and the TryAbsorbDamageForAllyOnly for allies.
|
||||||
|
// This method is only for projectile visual/sound and psyfocus cost.
|
||||||
if (Vector3.Distance(projectile.ExactPosition, PawnOwner.TrueCenter()) > Props.radius) return false;
|
|
||||||
|
|
||||||
// --- Interception Success ---
|
// --- Interception Success ---
|
||||||
lastInterceptTicks = Find.TickManager.TicksGame;
|
lastInterceptTicks = Find.TickManager.TicksGame;
|
||||||
|
|
||||||
// Spawn effect at the point of interception, not the shield center
|
// Spawn effect at the point of interception, not the shield center
|
||||||
Props.interceptEffecter?.Spawn(projectile.ExactPosition.ToIntVec3(), PawnOwner.Map).Cleanup();
|
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)
|
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
|
// Consume Hitpoints
|
||||||
currentHitPoints -= (int)damageAmount;
|
currentHitPoints -= (int)dinfo.Amount;
|
||||||
if (currentHitPoints <= 0)
|
if (currentHitPoints <= 0)
|
||||||
{
|
{
|
||||||
Break();
|
Break();
|
||||||
}
|
}
|
||||||
|
absorbed = true; // Damage was absorbed
|
||||||
return true;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
// --- 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()
|
private void Break()
|
||||||
{
|
{
|
||||||
Props.breakEffecter?.Spawn(PawnOwner.Position, PawnOwner.Map).Cleanup();
|
Props.breakEffecter?.Spawn(PawnOwner.Position, PawnOwner.Map).Cleanup();
|
||||||
|
|||||||
Reference in New Issue
Block a user