diff --git a/1.6/1.6/Assemblies/ArachnaeSwarm.dll b/1.6/1.6/Assemblies/ArachnaeSwarm.dll
index 29adbd5..7120700 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/AbilityDefs/ARA_Abilities_HiveMind.xml b/1.6/1.6/Defs/AbilityDefs/ARA_Abilities_HiveMind.xml
new file mode 100644
index 0000000..abad623
--- /dev/null
+++ b/1.6/1.6/Defs/AbilityDefs/ARA_Abilities_HiveMind.xml
@@ -0,0 +1,35 @@
+
+
+
+
+ ARA_BindDrone
+ 虫群联结
+ Allows the hive mind master to bind with an unlinked drone, bringing it under direct psychic control.
+ UI/Commands/EggSpew
+
+ CastAbilityOnThing
+ true
+ 60
+
+
+ Verb_CastAbility
+ 0.5
+ 99
+
+ true
+ false
+ true
+ false
+ false
+ false
+
+
+
+
+
+
+
+
+
+
+
\ No newline at end of file
diff --git a/1.6/1.6/Defs/BackstoryDefs/ARA_BackstoryDef.xml b/1.6/1.6/Defs/BackstoryDefs/ARA_BackstoryDef.xml
index 1b755ad..c63cee7 100644
--- a/1.6/1.6/Defs/BackstoryDefs/ARA_BackstoryDef.xml
+++ b/1.6/1.6/Defs/BackstoryDefs/ARA_BackstoryDef.xml
@@ -15,6 +15,9 @@
2
2
+
+ ARA_HiveMindMaster
+
ArachnaeQueen_spawnCategoriesA
@@ -53,9 +56,13 @@
ArachnaeNode_spawnCategoriesA
+
+ ARA_HiveMindDrone
+
+
true
-
+
Arachnae_Node_BS_Adult_1
阿拉克涅督虫种
diff --git a/1.6/Defs/HediffDefs/ARA_Hediffs_HiveMind.xml b/1.6/1.6/Defs/HediffDefs/ARA_Hediffs_HiveMind.xml
similarity index 79%
rename from 1.6/Defs/HediffDefs/ARA_Hediffs_HiveMind.xml
rename to 1.6/1.6/Defs/HediffDefs/ARA_Hediffs_HiveMind.xml
index 02f3fac..421cc2a 100644
--- a/1.6/Defs/HediffDefs/ARA_Hediffs_HiveMind.xml
+++ b/1.6/1.6/Defs/HediffDefs/ARA_Hediffs_HiveMind.xml
@@ -3,7 +3,7 @@
ARA_HiveMindMaster
- hive mind master
+ 阿拉克涅主巢
The central node of a hive mind, connected to multiple drone beings.
ArachnaeSwarm.Hediff_HiveMindMaster
(0.8, 0.3, 0.8)
@@ -19,18 +19,20 @@
-
+
ARA_HiveMindDrone
- hive mind drone
+ 阿拉克涅工蜂
A drone being, psychically linked to a master node. If the master dies, this unit will cease to function.
ArachnaeSwarm.Hediff_HiveMindDrone
(0.6, 0.4, 0.8)
false
true
-
-
- linked
+
+
+ 6400
+
+
diff --git a/1.6/1.6/Defs/PawnKindDef/ARA_PawnKinds.xml b/1.6/1.6/Defs/PawnKindDef/ARA_PawnKinds.xml
index 9388dba..ddc159f 100644
--- a/1.6/1.6/Defs/PawnKindDef/ARA_PawnKinds.xml
+++ b/1.6/1.6/Defs/PawnKindDef/ARA_PawnKinds.xml
@@ -16,6 +16,7 @@
ARA_EggSpew
ARA_AcidSprayBurst
+ ARA_BindDrone
diff --git a/Source/ArachnaeSwarm/ArachnaeSwarm.csproj b/Source/ArachnaeSwarm/ArachnaeSwarm.csproj
index 115002f..73f91ed 100644
--- a/Source/ArachnaeSwarm/ArachnaeSwarm.csproj
+++ b/Source/ArachnaeSwarm/ArachnaeSwarm.csproj
@@ -77,6 +77,12 @@
+
+
+
+
+
+
diff --git a/Source/ArachnaeSwarm/CompAbilityEffect_BindDrone.cs b/Source/ArachnaeSwarm/CompAbilityEffect_BindDrone.cs
new file mode 100644
index 0000000..c206b95
--- /dev/null
+++ b/Source/ArachnaeSwarm/CompAbilityEffect_BindDrone.cs
@@ -0,0 +1,108 @@
+using RimWorld;
+using Verse;
+using System.Linq; // For LINQ operations
+
+namespace ArachnaeSwarm
+{
+ public class CompAbilityEffect_BindDrone : CompAbilityEffect
+ {
+ public override void Apply(LocalTargetInfo target, LocalTargetInfo dest)
+ {
+ base.Apply(target, dest);
+
+ Pawn dronePawn = target.Pawn;
+ Pawn masterPawn = parent.pawn; // The pawn casting the ability
+
+ if (masterPawn != null && dronePawn != null)
+ {
+ Hediff_HiveMindMaster masterHediff = masterPawn.health.hediffSet.GetFirstHediffOfDef(HediffDef.Named("ARA_HiveMindMaster")) as Hediff_HiveMindMaster;
+ if (masterHediff != null)
+ {
+ if (masterHediff.TryBindDrone(dronePawn))
+ {
+ Messages.Message($"Successfully bound {dronePawn.LabelShort} to {masterPawn.LabelShort}'s hive mind.", MessageTypeDefOf.PositiveEvent, historical: false);
+ }
+ else
+ {
+ Messages.Message($"Failed to bind {dronePawn.LabelShort} to {masterPawn.LabelShort}'s hive mind. Check logs for details.", MessageTypeDefOf.NegativeEvent, historical: false);
+ }
+ }
+ else
+ {
+ Log.Error($"[ArachnaeSwarm] Master {masterPawn.LabelShort} tried to bind a drone but does not have Hediff_HiveMindMaster.");
+ }
+ }
+ }
+
+ public override bool Valid(LocalTargetInfo target, bool throwMessages = false)
+ {
+ if (!base.Valid(target, throwMessages))
+ {
+ return false;
+ }
+
+ Pawn dronePawn = target.Pawn;
+ Pawn masterPawn = parent.pawn;
+
+ // Target must be a pawn
+ if (dronePawn == null)
+ {
+ if (throwMessages)
+ {
+ Messages.Message("MustTargetPawn".Translate(parent.def.label), MessageTypeDefOf.RejectInput, historical: false);
+ }
+ return false;
+ }
+
+ // Target must be on the same map as the caster
+ if (dronePawn.Map != masterPawn.Map)
+ {
+ if (throwMessages)
+ {
+ Messages.Message("CannotTargetDifferentMap".Translate(), MessageTypeDefOf.RejectInput, historical: false);
+ }
+ return false;
+ }
+
+ // Target must have ARA_HiveMindDrone hediff
+ Hediff_HiveMindDrone droneHediff = dronePawn.health.hediffSet.GetFirstHediffOfDef(HediffDef.Named("ARA_HiveMindDrone")) as Hediff_HiveMindDrone;
+ if (droneHediff == null)
+ {
+ if (throwMessages)
+ {
+ Messages.Message($"Target {dronePawn.LabelShort} does not have the 'ARA_HiveMindDrone' hediff.", MessageTypeDefOf.RejectInput, historical: false);
+ }
+ return false;
+ }
+
+ // Target must not be already bound to another master
+ if (droneHediff.target != null && droneHediff.target != masterPawn)
+ {
+ if (throwMessages)
+ {
+ Messages.Message($"Target {dronePawn.LabelShort} is already bound to {droneHediff.target.LabelShort}.", MessageTypeDefOf.RejectInput, historical: false);
+ }
+ return false;
+ }
+
+ // Caster must have ARA_HiveMindMaster hediff
+ Hediff_HiveMindMaster masterHediff = masterPawn.health.hediffSet.GetFirstHediffOfDef(HediffDef.Named("ARA_HiveMindMaster")) as Hediff_HiveMindMaster;
+ if (masterHediff == null)
+ {
+ if (throwMessages)
+ {
+ Messages.Message($"Caster {masterPawn.LabelShort} does not have the 'ARA_HiveMindMaster' hediff.", MessageTypeDefOf.RejectInput, historical: false);
+ }
+ return false;
+ }
+
+ // All checks passed
+ return true;
+ }
+
+ public override bool CanApplyOn(LocalTargetInfo target, LocalTargetInfo dest)
+ {
+ return Valid(target);
+ }
+ }
+}
\ No newline at end of file
diff --git a/Source/ArachnaeSwarm/CompProperties_AbilityBindDrone.cs b/Source/ArachnaeSwarm/CompProperties_AbilityBindDrone.cs
new file mode 100644
index 0000000..959f8d2
--- /dev/null
+++ b/Source/ArachnaeSwarm/CompProperties_AbilityBindDrone.cs
@@ -0,0 +1,13 @@
+using Verse;
+using RimWorld;
+
+namespace ArachnaeSwarm
+{
+ public class CompProperties_AbilityBindDrone : CompProperties_AbilityEffect
+ {
+ public CompProperties_AbilityBindDrone()
+ {
+ this.compClass = typeof(CompAbilityEffect_BindDrone);
+ }
+ }
+}
\ No newline at end of file
diff --git a/Source/ArachnaeSwarm/HediffCompProperties_HiveMindDrone.cs b/Source/ArachnaeSwarm/HediffCompProperties_HiveMindDrone.cs
new file mode 100644
index 0000000..921f2ea
--- /dev/null
+++ b/Source/ArachnaeSwarm/HediffCompProperties_HiveMindDrone.cs
@@ -0,0 +1,14 @@
+using Verse;
+
+namespace ArachnaeSwarm
+{
+ public class HediffCompProperties_HiveMindDrone : HediffCompProperties
+ {
+ public int unlinkedDieDelayTicks = 1800; // Default to 30 seconds
+
+ public HediffCompProperties_HiveMindDrone()
+ {
+ this.compClass = typeof(HediffComp_HiveMindDrone); // Reference the Comp class
+ }
+ }
+}
\ No newline at end of file
diff --git a/Source/ArachnaeSwarm/HediffComp_HiveMindDrone.cs b/Source/ArachnaeSwarm/HediffComp_HiveMindDrone.cs
new file mode 100644
index 0000000..09302cd
--- /dev/null
+++ b/Source/ArachnaeSwarm/HediffComp_HiveMindDrone.cs
@@ -0,0 +1,57 @@
+using Verse;
+using RimWorld;
+
+namespace ArachnaeSwarm
+{
+ public class HediffComp_HiveMindDrone : HediffComp
+ {
+ public HediffCompProperties_HiveMindDrone Props => (HediffCompProperties_HiveMindDrone)this.props;
+
+ public int TicksUnlinked => ticksUnlinked; // Expose as public property
+ private int ticksUnlinked = 0;
+
+ public override void CompExposeData()
+ {
+ base.CompExposeData();
+ Scribe_Values.Look(ref ticksUnlinked, "ticksUnlinked", 0);
+ }
+
+ public override void CompPostTick(ref float severityAdjustment)
+ {
+ base.CompPostTick(ref severityAdjustment);
+
+ // Only check if pawn is spawned and on a map
+ if (parent.pawn.Spawned && parent.pawn.Map != null)
+ {
+ // We use parent.pawn.IsHashIntervalTick(60) for performance
+ if (!parent.pawn.IsHashIntervalTick(60))
+ return;
+
+ Hediff_HiveMindDrone droneHediff = parent as Hediff_HiveMindDrone;
+ if (droneHediff == null) return; // Should not happen
+
+ Pawn masterPawn = droneHediff.target as Pawn;
+
+ if (masterPawn == null || masterPawn.Destroyed || masterPawn.Dead || !masterPawn.health.hediffSet.HasHediff(HediffDef.Named("ARA_HiveMindMaster")))
+ {
+ // Master is invalid or unlinked, start/continue unlinked timer
+ ticksUnlinked += 60; // Increment by 60 because we check every 60 ticks
+ if (ticksUnlinked >= Props.unlinkedDieDelayTicks)
+ {
+ Log.Message($"[ArachnaeSwarm] Drone {parent.pawn.LabelShort} was unlinked from master for too long and will die. Forcing death.");
+ // Ensure the pawn is killed only once and prevent further ticks
+ if (!parent.pawn.Dead && !parent.pawn.Destroyed)
+ {
+ parent.pawn.Kill(null, parent);
+ }
+ }
+ }
+ else
+ {
+ // Master is valid, reset unlinked timer
+ ticksUnlinked = 0;
+ }
+ }
+ }
+ }
+}
\ No newline at end of file
diff --git a/Source/ArachnaeSwarm/Hediff_HiveMindDrone.cs b/Source/ArachnaeSwarm/Hediff_HiveMindDrone.cs
new file mode 100644
index 0000000..87760f0
--- /dev/null
+++ b/Source/ArachnaeSwarm/Hediff_HiveMindDrone.cs
@@ -0,0 +1,73 @@
+using System.Linq;
+using RimWorld;
+using Verse;
+
+namespace ArachnaeSwarm
+{
+ public class Hediff_HiveMindDrone : HediffWithTarget
+ {
+ public override string LabelBase
+ {
+ get
+ {
+ string baseLabel = base.LabelBase + " (" + (target != null ? target.LabelShortCap : "未连接") + ")";
+
+ // Get the HediffComp_HiveMindDrone to access ticksUnlinked
+ HediffComp_HiveMindDrone comp = this.TryGetComp();
+ if (comp != null)
+ {
+ // Safely cast target to Pawn for Dead and health checks
+ Pawn masterPawn = target as Pawn;
+
+ if (masterPawn == null || masterPawn.Destroyed || masterPawn.Dead || !masterPawn.health.hediffSet.HasHediff(HediffDef.Named("ARA_HiveMindMaster")))
+ {
+ float timeLeftSecs = (comp.Props.unlinkedDieDelayTicks - comp.TicksUnlinked) / 60f;
+ if (timeLeftSecs > 0)
+ {
+ return baseLabel + " (死亡倒计时: " + timeLeftSecs.ToString("F1") + "s)";
+ }
+ }
+ }
+ return baseLabel;
+ }
+ }
+
+ public override void PostAdd(DamageInfo? dinfo)
+ {
+ base.PostAdd(dinfo);
+ // No direct linking in PostAdd, master will link manually
+ }
+
+ public override bool ShouldRemove
+ {
+ get
+ {
+ // Only remove if base ShouldRemove is true, OR if pawn is dead
+ // We do NOT want to remove it just because target is invalid, as the Comp will handle delayed death
+ if (base.ShouldRemove && pawn.Dead)
+ {
+ return true;
+ }
+ // Also remove if pawn is no longer spawned or on a map
+ if (!pawn.Spawned || pawn.Map == null)
+ {
+ return true;
+ }
+ return false; // Let the Comp handle the unlinked death
+ }
+ }
+
+ public override void PostRemoved()
+ {
+ base.PostRemoved();
+ // Deregister from the master when this hediff is removed
+ // Ensure target is a Pawn and not dead before attempting to deregister
+ if (this.target is Pawn master && master != null && !master.Destroyed && !master.Dead)
+ {
+ var masterHediff = master.health.hediffSet.GetFirstHediffOfDef(HediffDef.Named("ARA_HiveMindMaster")) as Hediff_HiveMindMaster;
+ masterHediff?.DeregisterDrone(this.pawn);
+ }
+ }
+ // PostTick logic moved to HediffComp_HiveMindDrone
+ }
+}
\ No newline at end of file
diff --git a/Source/ArachnaeSwarm/Hediff_HiveMindMaster.cs b/Source/ArachnaeSwarm/Hediff_HiveMindMaster.cs
new file mode 100644
index 0000000..2558b1e
--- /dev/null
+++ b/Source/ArachnaeSwarm/Hediff_HiveMindMaster.cs
@@ -0,0 +1,86 @@
+using System.Collections.Generic;
+using System.Linq;
+using RimWorld;
+using Verse;
+
+namespace ArachnaeSwarm
+{
+ public class Hediff_HiveMindMaster : Hediff
+ {
+ private List drones = new List();
+
+ public override string LabelInBrackets => drones.Count.ToString();
+
+ public override void ExposeData()
+ {
+ base.ExposeData();
+ Scribe_Collections.Look(ref drones, "drones", LookMode.Reference);
+ if (drones == null)
+ {
+ drones = new List();
+ }
+ }
+
+ public bool TryBindDrone(Pawn drone)
+ {
+ if (drone == null || drone.Dead || !drone.Spawned || drone.Map != this.pawn.Map)
+ {
+ Log.Message($"[ArachnaeSwarm] Cannot bind drone {drone?.LabelShort ?? "null"}: Invalid pawn state.");
+ return false;
+ }
+
+ Hediff_HiveMindDrone droneHediff = drone.health.hediffSet.GetFirstHediffOfDef(HediffDef.Named("ARA_HiveMindDrone")) as Hediff_HiveMindDrone;
+ if (droneHediff == null)
+ {
+ Log.Message($"[ArachnaeSwarm] Cannot bind drone {drone.LabelShort}: Does not have ARA_HiveMindDrone hediff.");
+ return false;
+ }
+
+ if (droneHediff.target != null && droneHediff.target != this.pawn)
+ {
+ Log.Message($"[ArachnaeSwarm] Cannot bind drone {drone.LabelShort}: Already bound to another master ({droneHediff.target.LabelShort}).");
+ return false;
+ }
+
+ if (drones.Contains(drone))
+ {
+ Log.Message($"[ArachnaeSwarm] Drone {drone.LabelShort} is already bound to this master.");
+ return false;
+ }
+
+ droneHediff.target = this.pawn; // Set the drone's target to this master
+ drones.Add(drone);
+ UpdateSeverity();
+ Log.Message($"[ArachnaeSwarm] Master {this.pawn.LabelShort} successfully bound drone {drone.LabelShort}.");
+ return true;
+ }
+
+ public void DeregisterDrone(Pawn drone)
+ {
+ if (drones.Contains(drone))
+ {
+ drones.Remove(drone);
+ UpdateSeverity();
+ }
+ }
+
+ private void UpdateSeverity()
+ {
+ this.Severity = drones.Count;
+ }
+
+ public override void PostRemoved()
+ {
+ base.PostRemoved();
+ // Kill all drones when the master hediff is removed (e.g., master dies)
+ foreach (var drone in drones.ToList()) // ToList() to avoid collection modification issues
+ {
+ if (drone != null && !drone.Dead)
+ {
+ Log.Message($"[ArachnaeSwarm] Master {pawn.LabelShort} died, killing drone {drone.LabelShort}.");
+ drone.Kill(null, this);
+ }
+ }
+ }
+ }
+}
\ No newline at end of file