diff --git a/1.6/1.6/Assemblies/ArachnaeSwarm.dll b/1.6/1.6/Assemblies/ArachnaeSwarm.dll index e2ba914..cc40cab 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/Thing_Misc/Weapons/ARA_Missile_Weapon.xml b/1.6/1.6/Defs/Thing_Misc/Weapons/ARA_Missile_Weapon.xml index 7f4a573..923bccc 100644 --- a/1.6/1.6/Defs/Thing_Misc/Weapons/ARA_Missile_Weapon.xml +++ b/1.6/1.6/Defs/Thing_Misc/Weapons/ARA_Missile_Weapon.xml @@ -141,7 +141,7 @@ 25 - +
  • ARA_Armed_Organ
  • ARA_Armed_Organ_Ranged
  • ARA_Armed_Organ_T1
  • @@ -234,7 +234,7 @@ 50 - +
  • ARA_Armed_Organ
  • ARA_Armed_Organ_Ranged
  • ARA_Armed_Organ_T2
  • diff --git a/1.6/1.6/Defs/Thing_Misc/Weapons/ARA_Weapon.xml b/1.6/1.6/Defs/Thing_Misc/Weapons/ARA_Weapon.xml index b9ace96..ae011f5 100644 --- a/1.6/1.6/Defs/Thing_Misc/Weapons/ARA_Weapon.xml +++ b/1.6/1.6/Defs/Thing_Misc/Weapons/ARA_Weapon.xml @@ -8,7 +8,7 @@ ARA_Cocoon_Weapon - +
  • ARA_Armed_Organ
  • ARA_Armed_Organ_Melee
  • ARA_Armed_Organ_T1
  • @@ -81,7 +81,7 @@ ARA_Cocoon_Weapon_1Stage - +
  • ARA_Armed_Organ
  • ARA_Armed_Organ_Melee
  • ARA_Armed_Organ_T2
  • @@ -164,7 +164,7 @@ ARA_Cocoon_Weapon_2Stage - +
  • ARA_Armed_Organ
  • ARA_Armed_Organ_Melee
  • ARA_Armed_Organ_T3
  • @@ -302,7 +302,7 @@ 25 - +
  • ARA_Armed_Organ
  • ARA_Armed_Organ_Ranged
  • ARA_Armed_Organ_T1
  • @@ -420,7 +420,7 @@ 25 - +
  • ARA_Armed_Organ
  • ARA_Armed_Organ_Ranged
  • ARA_Armed_Organ_T2
  • @@ -493,7 +493,7 @@ 25 - +
  • ARA_Armed_Organ
  • ARA_Armed_Organ_Ranged
  • ARA_Armed_Organ_T2
  • @@ -563,7 +563,7 @@ 50 - +
  • ARA_Armed_Organ
  • ARA_Armed_Organ_Ranged
  • ARA_Armed_Organ_T2
  • @@ -664,7 +664,7 @@ 50 - +
  • ARA_Armed_Organ
  • ARA_Armed_Organ_Ranged
  • ARA_Armed_Organ_T1
  • @@ -772,7 +772,7 @@ 50 - +
  • ARA_Armed_Organ
  • ARA_Armed_Organ_Ranged
  • ARA_Armed_Organ_T2
  • @@ -852,7 +852,7 @@ 50 - +
  • ARA_Armed_Organ
  • ARA_Armed_Organ_Ranged
  • ARA_Armed_Organ_T2
  • @@ -969,7 +969,7 @@ 50 - +
  • ARA_Armed_Organ
  • ARA_Armed_Organ_Ranged
  • ARA_Armed_Organ_T1
  • @@ -1045,7 +1045,7 @@ ARA_MW_Mimic_Niddle 阿拉克涅虫群督虫使用基础近战武装器官,通过多根外露神经束与督虫的辅肢相连。这根毒针中藏有休眠中的阿拉克涅拟线种虫卵,攻击将感染受害者使其最终成为被拟线虫操控的寄生体。 - +
  • ARA_Armed_Organ
  • ARA_Armed_Organ_Melee
  • ARA_Armed_Organ_T1
  • diff --git a/1.6/1.6/Defs/Thing_Misc/Weapons/ARA_Weapon_FireSpew.xml b/1.6/1.6/Defs/Thing_Misc/Weapons/ARA_Weapon_FireSpew.xml index f239743..d4ad35b 100644 --- a/1.6/1.6/Defs/Thing_Misc/Weapons/ARA_Weapon_FireSpew.xml +++ b/1.6/1.6/Defs/Thing_Misc/Weapons/ARA_Weapon_FireSpew.xml @@ -57,7 +57,7 @@ 50 - +
  • ARA_Armed_Organ
  • ARA_Armed_Organ_Ranged
  • ARA_Armed_Organ_T1
  • diff --git a/1.6/1.6/Languages/ChineseSimplified (简体中文)/Keyed/GizmoLabels.xml b/1.6/1.6/Languages/ChineseSimplified (简体中文)/Keyed/GizmoLabels.xml new file mode 100644 index 0000000..0b549d1 --- /dev/null +++ b/1.6/1.6/Languages/ChineseSimplified (简体中文)/Keyed/GizmoLabels.xml @@ -0,0 +1,7 @@ + + + + + 守护者力场 + + \ No newline at end of file diff --git a/Source/ArachnaeSwarm/Harmony_ProjectileInterceptor.cs b/Source/ArachnaeSwarm/Harmony_ProjectileInterceptor.cs index 43f34bb..52e019a 100644 --- a/Source/ArachnaeSwarm/Harmony_ProjectileInterceptor.cs +++ b/Source/ArachnaeSwarm/Harmony_ProjectileInterceptor.cs @@ -22,8 +22,8 @@ namespace ArachnaeSwarm // Our comp is directly on the pawn, not on apparel if (pawn.TryGetComp(out var interceptor)) { - // Call our custom intercept method - if (interceptor.TryIntercept(__instance)) + // Call our custom intercept method, passing in the projectile's path + if (interceptor.TryIntercept(__instance, lastExactPos, newExactPos)) { // If interception is successful, destroy the projectile __instance.Destroy(DestroyMode.Vanish); diff --git a/Source/ArachnaeSwarm/ThingComp_GuardianPsyField.cs b/Source/ArachnaeSwarm/ThingComp_GuardianPsyField.cs index 33dde60..932e349 100644 --- a/Source/ArachnaeSwarm/ThingComp_GuardianPsyField.cs +++ b/Source/ArachnaeSwarm/ThingComp_GuardianPsyField.cs @@ -5,7 +5,6 @@ using Verse.Sound; namespace ArachnaeSwarm { - // 1. Expanded CompProperties to match the user's example public class CompProperties_GuardianPsyField : CompProperties { public float radius = 5.9f; @@ -13,16 +12,12 @@ namespace ArachnaeSwarm public int rechargeDelay = 3200; // Ticks after breaking public int rechargeHitPointsIntervalTicks = 60; // Ticks to restore 1 HP - // New properties for psyfocus/entropy mechanics - 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 psyfocusCostPerInterval = 0.001f; + public float entropyGainPerDamage = 0.5f; + public float hitPointsPctPerInterval = 0.01f; public EffecterDef absorbEffecter; - // Removed transferAllyDamage - - // Projectile interception properties - public bool interceptGroundProjectiles = false; + + public bool interceptGroundProjectiles = true; public bool interceptNonHostileProjectiles = false; public bool interceptAirProjectiles = true; @@ -41,19 +36,16 @@ namespace ArachnaeSwarm [StaticConstructorOnStartup] public class ThingComp_GuardianPsyField : ThingComp { - // --- State Variables --- private int lastInterceptTicks = -999999; - private int ticksToReset = 0; // Cooldown timer + private int ticksToReset = 0; public int currentHitPoints; - private bool wasNotAtFullHp = false; // Tracks if shield was damaged before recharge + private bool wasNotAtFullHp = false; - // --- Properties --- public CompProperties_GuardianPsyField Props => (CompProperties_GuardianPsyField)props; private Pawn PawnOwner => parent as Pawn; public bool IsOnCooldown => ticksToReset > 0; public int HitPointsMax => Props.hitPoints; - // --- Visuals --- private static readonly Material ForceFieldMat = MaterialPool.MatFrom("Other/ForceField", ShaderDatabase.MoteGlow); private static readonly MaterialPropertyBlock MatPropertyBlock = new MaterialPropertyBlock(); @@ -61,15 +53,12 @@ namespace ArachnaeSwarm { get { - // 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")); 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; @@ -103,134 +92,79 @@ namespace ArachnaeSwarm Reset(); } } - // Only allow recharge if the shield is 'Active' (i.e., has psyfocus) and not on cooldown else if (Active && currentHitPoints < HitPointsMax) { - // Check if there's enough psyfocus to pay for this interval's recharge if (PawnOwner.psychicEntropy != null && PawnOwner.psychicEntropy.CurrentPsyfocus >= Props.psyfocusCostPerInterval) { - wasNotAtFullHp = true; // Mark that the shield was damaged + wasNotAtFullHp = true; 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) { - wasNotAtFullHp = false; // Reset flag when full + wasNotAtFullHp = false; } } - // Projectile interception logic remains the same, as it's a separate Harmony patch - public bool TryIntercept(Projectile projectile) + // Helper method to apply costs + private void ApplyCosts(float damageAmount) { - if (!Active) return false; - - // Filter projectile types based on properties - if (projectile.def.projectile.flyOverhead && !Props.interceptAirProjectiles) return false; - if (!projectile.def.projectile.flyOverhead && !Props.interceptGroundProjectiles) return false; - if (projectile.Launcher != null && !projectile.Launcher.HostileTo(PawnOwner.Faction) && !Props.interceptNonHostileProjectiles) return false; - - // --- 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(); - - // No longer consuming hitpoints here, PostPreApplyDamage will handle it for self. - // The Harmony_DamageWorker for allies will handle it for allies. - - 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(dinfo.Amount * Props.entropyGainPerDamage, overLimit: true); + PawnOwner.psychicEntropy.TryAddEntropy(damageAmount * Props.entropyGainPerDamage, overLimit: true); } - - // Consume Hitpoints - currentHitPoints -= (int)dinfo.Amount; + currentHitPoints -= (int)damageAmount; if (currentHitPoints <= 0) { Break(); } - absorbed = true; // Damage was absorbed } + public bool TryIntercept(Projectile projectile, Vector3 lastExactPos, Vector3 newExactPos) + { + if (!Active) return false; + if (currentHitPoints <= 0) return false; - // --- 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}"); + if (!GenGeo.IntersectLineCircleOutline(PawnOwner.Position.ToVector2(), Props.radius, lastExactPos.ToVector2(), newExactPos.ToVector2())) + { + return false; + } - // // 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(); - // } + if (projectile.def.projectile.flyOverhead && !Props.interceptAirProjectiles) return false; + if (!projectile.def.projectile.flyOverhead && !Props.interceptGroundProjectiles) return false; + if (projectile.Launcher != null && !projectile.Launcher.HostileTo(PawnOwner.Faction) && !Props.interceptNonHostileProjectiles) return false; - // Log.Message(" -> Damage absorption successful!"); - // return true; // Damage was absorbed - // } + lastInterceptTicks = Find.TickManager.TicksGame; + Props.interceptEffecter?.Spawn(projectile.ExactPosition.ToIntVec3(), PawnOwner.Map).Cleanup(); + + // Apply costs for intercepting projectile + ApplyCosts(projectile.DamageAmount); + + return true; + } + + public override void PostPreApplyDamage(ref DamageInfo dinfo, out bool absorbed) + { + absorbed = false; + if (!Active || PawnOwner == null) return; + + // We only handle non-projectile damage here, as projectiles are handled by TryIntercept + if (dinfo.Def.isRanged) return; + + if (currentHitPoints < dinfo.Amount) return; + + Props.absorbEffecter?.Spawn(PawnOwner.Position, PawnOwner.Map).Cleanup(); + + // Apply costs for absorbing non-projectile damage + ApplyCosts(dinfo.Amount); + + absorbed = true; + } private void Break() { @@ -275,7 +209,6 @@ namespace ArachnaeSwarm return Mathf.Max(idleAlpha, interceptAlpha); } - // --- GIZMO --- public override System.Collections.Generic.IEnumerable CompGetGizmosExtra() { if (PawnOwner != null && Find.Selector.SingleSelectedThing == PawnOwner) @@ -285,7 +218,6 @@ namespace ArachnaeSwarm } } - // Gizmo class copied from the user's example and adapted [StaticConstructorOnStartup] public class Gizmo_GuardianShieldStatus : Gizmo { @@ -304,7 +236,7 @@ namespace ArachnaeSwarm Rect labelRect = rect2; labelRect.height = rect.height / 2f; Text.Font = GameFont.Tiny; - Widgets.Label(labelRect, "Guardian Field"); + Widgets.Label(labelRect, "ARA_GuardianFieldGizmoLabel".Translate()); Rect barRect = rect2; barRect.yMin = rect2.y + rect2.height / 2f;