This commit is contained in:
2025-09-05 13:35:41 +08:00
parent c87c88a681
commit 3a3c30087a
11 changed files with 284 additions and 17 deletions

Binary file not shown.

View File

@@ -2,12 +2,12 @@
<Defs>
<HediffDef>
<defName>ARA_HiveMindMaster</defName>
<label>阿拉克涅主巢</label>
<label>阿拉克涅女皇</label>
<description>虫群意识的中心节点, 作为主脑统御整个阿拉克涅虫群.</description>
<hediffClass>ArachnaeSwarm.Hediff_HiveMindMaster</hediffClass>
<defaultLabelColor>(0.8, 0.3, 0.8)</defaultLabelColor>
<isBad>false</isBad>
<scenarioCanAdd>true</scenarioCanAdd>
<scenarioCanAdd>false</scenarioCanAdd>
<maxSeverity>100</maxSeverity>
<stages>
<li>
@@ -31,12 +31,12 @@
<HediffDef>
<defName>ARA_HiveMindDrone</defName>
<label>阿拉克涅工蜂</label>
<description>阿拉克涅工蜂通过心灵与阿拉克涅女皇种相链接。如果女皇死亡,工蜂也将停止生命活动。</description>
<label>阿拉克涅督虫</label>
<description>阿拉克涅督虫通过心灵与阿拉克涅女皇种相链接。如果女皇死亡,督虫也将停止生命活动。</description>
<hediffClass>ArachnaeSwarm.Hediff_HiveMindDrone</hediffClass>
<defaultLabelColor>(0.6, 0.4, 0.8)</defaultLabelColor>
<isBad>false</isBad>
<scenarioCanAdd>true</scenarioCanAdd>
<scenarioCanAdd>false</scenarioCanAdd>
<comps>
<li Class="ArachnaeSwarm.HediffCompProperties_HiveMindDrone">
<unlinkedDieDelayTicks>6400</unlinkedDieDelayTicks> <!-- Default to 30 seconds -->
@@ -65,4 +65,18 @@
</li>
</stages>
</HediffDef>
<HediffDef>
<defName>ARA_HiveMindWorker</defName>
<label>阿拉克涅辅虫</label>
<description>阿拉克涅辅虫通过心灵与阿拉克涅督虫种相链接。如果督虫死亡,辅虫也将停止生命活动。</description>
<hediffClass>HediffWithComps</hediffClass>
<defaultLabelColor>(0.6, 0.4, 0.8)</defaultLabelColor>
<isBad>false</isBad>
<scenarioCanAdd>false</scenarioCanAdd>
<comps>
</comps>
<stages>
</stages>
</HediffDef>
</Defs>

View File

@@ -40,12 +40,12 @@
</requiredWorkTags>
<startingHediffs>
</startingHediffs>
<moveSpeedFactorByTerrainTag>
<li>
<key>ARA_Creep</key>
<value>2.0</value>
</li>
</moveSpeedFactorByTerrainTag>
<moveSpeedFactorByTerrainTag>
<li>
<key>ARA_Creep</key>
<value>2.0</value>
</li>
</moveSpeedFactorByTerrainTag>
</PawnKindDef>
<PawnKindDef Name="ArachnaeQueen_Colonist" ParentName="ArachnaeQueenBasePawnKind">
<defName>ARA_ArachnaeQueen</defName>
@@ -101,12 +101,12 @@
</requiredWorkTags>
<startingHediffs>
</startingHediffs>
<moveSpeedFactorByTerrainTag>
<li>
<key>ARA_Creep</key>
<value>2.0</value>
</li>
</moveSpeedFactorByTerrainTag>
<moveSpeedFactorByTerrainTag>
<li>
<key>ARA_Creep</key>
<value>2.0</value>
</li>
</moveSpeedFactorByTerrainTag>
</PawnKindDef>
<PawnKindDef ParentName="ArachnaeNodeABasePawnKind">
<defName>ArachnaeNode_Race_Myrmecocystus</defName>
@@ -182,6 +182,9 @@
<value>8.0</value>
</li>
</moveSpeedFactorByTerrainTag>
<techHediffsRequired>
<li>ARA_HiveMindWorker</li>
</techHediffsRequired>
</PawnKindDef>
<PawnKindDef ParentName="ARA_InsectKindBase">
<defName>ArachnaeBase_Race_Slavey</defName>
@@ -271,4 +274,27 @@
</li>
</lifeStages>
</PawnKindDef>
<PawnKindDef ParentName="ARA_InsectKindBase">
<defName>ArachnaeBase_Race_Maid</defName>
<label>阿拉克涅家政种</label>
<race>ArachnaeBase_Race_Maid</race>
<lifeStages>
<li>
<bodyGraphicData>
<texPath>Things/Pawn/Animal/Spelopede/Spelopede</texPath>
<drawSize>1</drawSize>
<color>(156,148,125)</color>
<shadowData>
<volume>(0.4, 0.5, 0.37)</volume>
<offset>(0,0,-0.15)</offset>
</shadowData>
</bodyGraphicData>
<dessicatedBodyGraphicData>
<texPath>Things/Pawn/Animal/Spelopede/Dessicated_Spelopede</texPath>
<drawSize>1</drawSize>
</dessicatedBodyGraphicData>
</li>
</lifeStages>
</PawnKindDef>
</Defs>

View File

@@ -183,4 +183,27 @@
</li>
</tools>
</ThingDef>
<ThingDef ParentName="ArachnaeBase_Race">
<defName>ArachnaeBase_Race_Maid</defName>
<label>阿拉克涅家政种</label>
<description>阿拉克涅辅虫之一,智力低下,可以执行清洁工作,注定在度过短暂的时光后死亡。</description>
<race>
<specialTrainables>
<li>ARA_Cleaning</li>
</specialTrainables>
</race>
<comps>
<li Class="ArachnaeSwarm.CompProperties_AdvancedTraining">
<trainables>
<li>
<trainable>ARA_Cleaning</trainable>
<trainInstantly>true</trainInstantly>
<setWanted>true</setWanted>
</li>
</trainables>
<disableAllSkillDecay>true</disableAllSkillDecay> <!-- 阻止这个动物的所有技能衰减 -->
</li>
</comps>
</ThingDef>
</Defs>

View File

@@ -332,6 +332,13 @@
<li Class="ArachnaeSwarm.JobGiver_Grower" />
</subNodes>
</li>
<!-- OUR CUSTOM LOGIC INJECTION FOR CLEANING -->
<li Class="ArachnaeSwarm.ThinkNode_ConditionalAnimalShouldDoCleaningWork">
<subNodes>
<li Class="ArachnaeSwarm.JobGiver_Cleaner" />
</subNodes>
</li>
</subNodes>
</li>

View File

@@ -0,0 +1,22 @@
<?xml version="1.0" encoding="utf-8" ?>
<Defs>
<TrainableDef>
<defName>ARA_Cleaning</defName>
<label>清洁</label>
<description>允许该生物执行清洁任务。</description>
<!-- 标记为特殊训练,这样它就会被 PawnColumnWorker_Trainable_Special 统一管理 -->
<specialTrainable>true</specialTrainable>
<!-- 训练难度和所需智力 -->
<difficulty>5</difficulty>
<requiredTrainability>Advanced</requiredTrainability>
<!-- 训练所需步骤 -->
<steps>3</steps>
<!-- 在UI中的排序 -->
<listPriority>100</listPriority>
</TrainableDef>
</Defs>

View File

@@ -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));
}
}
}

View File

@@ -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;
}
}
}

View File

@@ -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);
}
}
}

View File

@@ -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<Thing> 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);
}
}
}

View File

@@ -118,6 +118,12 @@
<Compile Include="ARA_HuggingFace\CompAbilityEffect_Possess.cs" />
<Compile Include="ARA_HuggingFace\CompProperties_AbilityPossess.cs" />
</ItemGroup>
<ItemGroup>
<Compile Include="ARA_TrainingWork\JobClean\ARA_TrainableDefOf_Cleaning.cs" />
<Compile Include="ARA_TrainingWork\JobClean\JobGiver_Cleaner.cs" />
<Compile Include="ARA_TrainingWork\JobClean\ThinkNode_ConditionalAnimalShouldDoCleaningWork.cs" />
<Compile Include="ARA_TrainingWork\JobClean\WorkGiver_ArachnaeClean.cs" />
</ItemGroup>
<Import Project="$(MSBuildToolsPath)\Microsoft.CSharp.targets" />
<!-- 自定义清理任务删除obj文件夹中的临时文件 -->
<Target Name="CleanDebugFiles" AfterTargets="Build">