+
+
\ No newline at end of file
diff --git a/1.6/1.6/Defs/ThingDef_Races/ARA_RaceBaseSwarm.xml b/1.6/1.6/Defs/ThingDef_Races/ARA_RaceBaseSwarm.xml
index 130db61..dd9ee0e 100644
--- a/1.6/1.6/Defs/ThingDef_Races/ARA_RaceBaseSwarm.xml
+++ b/1.6/1.6/Defs/ThingDef_Races/ARA_RaceBaseSwarm.xml
@@ -183,4 +183,27 @@
+
+
+ ArachnaeBase_Race_Maid
+
+ 阿拉克涅辅虫之一,智力低下,可以执行清洁工作,注定在度过短暂的时光后死亡。
+
+
+
ARA_Cleaning
+
+
+
+
+
+
+ ARA_Cleaning
+ true
+ true
+
+
+ true
+
+
+
\ No newline at end of file
diff --git a/1.6/1.6/Defs/ThinkTreeDefs/ARA_ThinkTrees.xml b/1.6/1.6/Defs/ThinkTreeDefs/ARA_ThinkTrees.xml
index 3218afc..5b65f4d 100644
--- a/1.6/1.6/Defs/ThinkTreeDefs/ARA_ThinkTrees.xml
+++ b/1.6/1.6/Defs/ThinkTreeDefs/ARA_ThinkTrees.xml
@@ -332,6 +332,13 @@
+
+
+
+
+
+
+
diff --git a/1.6/1.6/Defs/TrainableDefs/ARA_Cleaning.xml b/1.6/1.6/Defs/TrainableDefs/ARA_Cleaning.xml
new file mode 100644
index 0000000..8ed9fcc
--- /dev/null
+++ b/1.6/1.6/Defs/TrainableDefs/ARA_Cleaning.xml
@@ -0,0 +1,22 @@
+
+
+
+
+ ARA_Cleaning
+
+ 允许该生物执行清洁任务。
+
+ true
+
+
+ 5
+ Advanced
+
+
+ 3
+
+
+ 100
+
+
+
\ No newline at end of file
diff --git a/Source/ArachnaeSwarm/ARA_TrainingWork/JobClean/ARA_TrainableDefOf_Cleaning.cs b/Source/ArachnaeSwarm/ARA_TrainingWork/JobClean/ARA_TrainableDefOf_Cleaning.cs
new file mode 100644
index 0000000..1ec09fe
--- /dev/null
+++ b/Source/ArachnaeSwarm/ARA_TrainingWork/JobClean/ARA_TrainableDefOf_Cleaning.cs
@@ -0,0 +1,16 @@
+using Verse;
+using RimWorld;
+
+namespace ArachnaeSwarm
+{
+ [DefOf]
+ public static class ARA_TrainableDefOf_Cleaning
+ {
+ public static TrainableDef ARA_Cleaning;
+
+ static ARA_TrainableDefOf_Cleaning()
+ {
+ DefOfHelper.EnsureInitializedInCtor(typeof(ARA_TrainableDefOf_Cleaning));
+ }
+ }
+}
\ No newline at end of file
diff --git a/Source/ArachnaeSwarm/ARA_TrainingWork/JobClean/JobGiver_Cleaner.cs b/Source/ArachnaeSwarm/ARA_TrainingWork/JobClean/JobGiver_Cleaner.cs
new file mode 100644
index 0000000..f99aedb
--- /dev/null
+++ b/Source/ArachnaeSwarm/ARA_TrainingWork/JobClean/JobGiver_Cleaner.cs
@@ -0,0 +1,50 @@
+using Verse;
+using Verse.AI;
+using RimWorld;
+
+namespace ArachnaeSwarm
+{
+ public class JobGiver_Cleaner : ThinkNode_JobGiver
+ {
+ private WorkGiver_ArachnaeClean _workGiver;
+
+ public override void ResolveReferences()
+ {
+ base.ResolveReferences();
+ // We instantiate our custom WorkGiver here.
+ // It doesn't need a Def because it's not a standard game WorkGiver.
+ _workGiver = new WorkGiver_ArachnaeClean();
+ }
+
+ protected override Job TryGiveJob(Pawn pawn)
+ {
+ if (_workGiver == null)
+ {
+ Log.ErrorOnce("JobGiver_Cleaner's WorkGiver is null. ResolveReferences was not called.", 91354);
+ return null;
+ }
+
+ // The WorkGiver will handle both filth and snow.
+ // We just need to find the closest potential job.
+ // The logic is simplified here; the real work is in the WorkGiver.
+
+ // Find the closest filth to clean
+ Thing closestFilth = _workGiver.FindClosestFilth(pawn);
+ if (closestFilth != null)
+ {
+ Job filthJob = _workGiver.JobOnThing(pawn, closestFilth);
+ if (filthJob != null) return filthJob;
+ }
+
+ // If no filth, find the closest snow/sand to clear
+ IntVec3 closestSnowCell = _workGiver.FindClosestSnow(pawn);
+ if (closestSnowCell.IsValid)
+ {
+ Job snowJob = _workGiver.JobOnCell(pawn, closestSnowCell);
+ if (snowJob != null) return snowJob;
+ }
+
+ return null;
+ }
+ }
+}
\ No newline at end of file
diff --git a/Source/ArachnaeSwarm/ARA_TrainingWork/JobClean/ThinkNode_ConditionalAnimalShouldDoCleaningWork.cs b/Source/ArachnaeSwarm/ARA_TrainingWork/JobClean/ThinkNode_ConditionalAnimalShouldDoCleaningWork.cs
new file mode 100644
index 0000000..b94475a
--- /dev/null
+++ b/Source/ArachnaeSwarm/ARA_TrainingWork/JobClean/ThinkNode_ConditionalAnimalShouldDoCleaningWork.cs
@@ -0,0 +1,21 @@
+using Verse;
+using Verse.AI;
+using RimWorld;
+
+namespace ArachnaeSwarm
+{
+ public class ThinkNode_ConditionalAnimalShouldDoCleaningWork : ThinkNode_Conditional
+ {
+ protected override bool Satisfied(Pawn pawn)
+ {
+ if (pawn.training == null)
+ {
+ return false;
+ }
+
+ // Check if the animal has learned and is set to perform "Cleaning"
+ return pawn.training.HasLearned(ARA_TrainableDefOf_Cleaning.ARA_Cleaning) &&
+ pawn.training.GetWanted(ARA_TrainableDefOf_Cleaning.ARA_Cleaning);
+ }
+ }
+}
\ No newline at end of file
diff --git a/Source/ArachnaeSwarm/ARA_TrainingWork/JobClean/WorkGiver_ArachnaeClean.cs b/Source/ArachnaeSwarm/ARA_TrainingWork/JobClean/WorkGiver_ArachnaeClean.cs
new file mode 100644
index 0000000..ddfb1c6
--- /dev/null
+++ b/Source/ArachnaeSwarm/ARA_TrainingWork/JobClean/WorkGiver_ArachnaeClean.cs
@@ -0,0 +1,82 @@
+using System.Collections.Generic;
+using Verse;
+using Verse.AI;
+using RimWorld;
+
+namespace ArachnaeSwarm
+{
+ public class WorkGiver_ArachnaeClean : WorkGiver_Scanner
+ {
+ private const int MinTicksSinceThickened = 600;
+
+ public override PathEndMode PathEndMode => PathEndMode.Touch;
+
+ // --- Filth Cleaning Logic ---
+
+ public Thing FindClosestFilth(Pawn pawn)
+ {
+ return GenClosest.ClosestThing_Global(
+ pawn.Position,
+ pawn.Map.listerFilthInHomeArea.FilthInHomeArea,
+ maxDistance: 9999f,
+ validator: t => HasJobOnThing(pawn, t)
+ );
+ }
+
+ public override bool HasJobOnThing(Pawn pawn, Thing t, bool forced = false)
+ {
+ if (!(t is Filth filth)) return false;
+ if (!filth.Map.areaManager.Home[filth.Position]) return false;
+ if (filth.Fogged() || !pawn.CanReserve(t, 1, -1, null, forced)) return false;
+ if (filth.TicksSinceThickened < MinTicksSinceThickened) return false;
+ return true;
+ }
+
+ public override Job JobOnThing(Pawn pawn, Thing t, bool forced = false)
+ {
+ Job job = JobMaker.MakeJob(JobDefOf.Clean);
+ job.AddQueuedTarget(TargetIndex.A, t);
+
+ // Simplified multi-clean logic from original WorkGiver_CleanFilth
+ int num = 15;
+ for (int i = 0; i < 100; i++)
+ {
+ IntVec3 c = t.Position + GenRadial.RadialPattern[i];
+ if (c.InBounds(pawn.Map))
+ {
+ List thingList = c.GetThingList(pawn.Map);
+ foreach(var thing in thingList)
+ {
+ if (thing != t && HasJobOnThing(pawn, thing, forced))
+ {
+ job.AddQueuedTarget(TargetIndex.A, thing);
+ }
+ }
+ }
+ if (job.GetTargetQueue(TargetIndex.A).Count >= num) break;
+ }
+ return job;
+ }
+
+ // --- Snow Clearing Logic ---
+
+ public IntVec3 FindClosestSnow(Pawn pawn)
+ {
+ return CellFinder.RandomClosewalkCellNear(pawn.Position, pawn.Map, 100,
+ c => HasJobOnCell(pawn, c) && pawn.CanReach(c, PathEndMode.Touch, Danger.Deadly));
+ }
+
+ public override bool HasJobOnCell(Pawn pawn, IntVec3 c, bool forced = false)
+ {
+ if (pawn.Map.snowGrid.GetDepth(c) < 0.2f) return false;
+ if (!pawn.Map.areaManager.SnowOrSandClear[c]) return false; // Must be in the clear zone
+ if (!pawn.CanReserve(c, 1, -1, null, forced)) return false;
+ return true;
+ }
+
+ public override Job JobOnCell(Pawn pawn, IntVec3 c, bool forced = false)
+ {
+ return JobMaker.MakeJob(JobDefOf.ClearSnow, c);
+ }
+ }
+}
\ No newline at end of file
diff --git a/Source/ArachnaeSwarm/ArachnaeSwarm.csproj b/Source/ArachnaeSwarm/ArachnaeSwarm.csproj
index 32f2be4..4532ea1 100644
--- a/Source/ArachnaeSwarm/ArachnaeSwarm.csproj
+++ b/Source/ArachnaeSwarm/ArachnaeSwarm.csproj
@@ -118,6 +118,12 @@
+
+
+
+
+
+