This commit is contained in:
Tourswen
2025-10-04 16:20:04 +08:00
27 changed files with 641 additions and 391 deletions

Binary file not shown.

View File

@@ -0,0 +1,11 @@
<?xml version="1.0" encoding="utf-8" ?>
<Defs>
<JobDef>
<defName>ARA_Refuel_Nutrition</defName>
<driverClass>ArachnaeSwarm.JobDriver_Refuel_Nutrition</driverClass>
<reportString>正在为 TargetA 补充营养。</reportString>
<allowOpportunisticPrefix>true</allowOpportunisticPrefix>
</JobDef>
</Defs>

View File

@@ -1628,6 +1628,53 @@
</alienPartGenerator>
</generalSettings>
<raceRestriction>
<!-- 无法拥有的内源性基因 -->
<blackEndoCategories MayRequire="Ludeon.RimWorld.Biotech">
<li MayRequire="Ludeon.RimWorld.Biotech">HairColor</li>
<li MayRequire="Ludeon.RimWorld.Biotech">Melanin</li>
<li MayRequire="Ludeon.RimWorld.Biotech">BodyType</li>
<li MayRequire="Ludeon.RimWorld.Biotech">Ears</li>
<li MayRequire="Ludeon.RimWorld.Biotech">Nose</li>
<li MayRequire="Ludeon.RimWorld.Biotech">Voice</li>
<li MayRequire="Ludeon.RimWorld.Biotech">Headbone</li>
<li MayRequire="Ludeon.RimWorld.Biotech">Head</li>
<li MayRequire="Ludeon.RimWorld.Biotech">Jaw</li>
</blackEndoCategories>
<!-- 无法拥有的Tag基因 -->
<blackGeneTags MayRequire="Ludeon.RimWorld.Biotech">
<li MayRequire="Ludeon.RimWorld.Biotech">EyeColor</li>
<li MayRequire="Ludeon.RimWorld.Biotech">Tail</li>
<li MayRequire="Ludeon.RimWorld.Biotech">BeardStyle</li>
<li MayRequire="Ludeon.RimWorld.Biotech">Fur</li>
</blackGeneTags>
<!-- 无法拥有的基因列表 -->
<blackGeneList>
<li MayRequire="Ludeon.RimWorld.Biotech">Furskin</li>
<li MayRequire="Ludeon.RimWorld.Biotech">Brow_Heavy</li>
</blackGeneList>
<!-- 禁止的异形变种 -->
<blackXenotypeList>
<li MayRequire="Ludeon.RimWorld.Biotech">Dirtmole</li>
<li MayRequire="Ludeon.RimWorld.Biotech">Genie</li>
<li MayRequire="Ludeon.RimWorld.Biotech">Hussar</li>
<li MayRequire="Ludeon.RimWorld.Biotech">Sanguophage</li>
<li MayRequire="Ludeon.RimWorld.Biotech">Neanderthal</li>
<li MayRequire="Ludeon.RimWorld.Biotech">Pigskin</li>
<li MayRequire="Ludeon.RimWorld.Biotech">Impid</li>
<li MayRequire="Ludeon.RimWorld.Biotech">Waster</li>
<li MayRequire="Ludeon.RimWorld.Biotech">Yttakin</li>
<li MayRequire="Ludeon.RimWorld.Biotech">Highmate</li>
</blackXenotypeList>
<!-- 异种类型列表 -->
<xenotypeList>
</xenotypeList>
<onlyUseRaceRestrictedXenotypes>true</onlyUseRaceRestrictedXenotypes>
<!-- 食物列表 -->
<whiteFoodList>
<li>ARA_InsectJelly</li>
<li>ARA_NutrientPasteMeal</li>
<li>ARA_PheromoneSolvent</li>
</whiteFoodList>
<onlyEatRaceRestrictedFood>true</onlyEatRaceRestrictedFood>
</raceRestriction>
</alienRace>

View File

@@ -499,79 +499,6 @@
</li>
</comps>
</ThingDef>
<ThingDef ParentName="BuildingBase">
<defName>ARA_MorphableResearchBench</defName>
<!-- <hasInteractionCell>true</hasInteractionCell>
<interactionCellOffset>(0,0,-1)</interactionCellOffset> -->
<label>阿拉克涅织域种织座</label>
<description>以阿拉克涅织域种为核心形成的活体结构,是推进虫群高级研究的必须建筑。该建筑无法建造,只能通过阿拉克涅织域种变形得到。</description>
<thingClass>ArachnaeSwarm.Building_Morphable</thingClass>
<tickerType>Normal</tickerType>
<size>(2,2)</size>
<graphicData>
<texPath>ArachnaeSwarm/Building/ARA_MorphableResearchBench</texPath>
<graphicClass>Graphic_Single</graphicClass>
<shaderType>CutoutComplex</shaderType>
<drawSize>(2.5,2.5)</drawSize>
</graphicData>
<altitudeLayer>Building</altitudeLayer>
<passability>Impassable</passability>
<castEdgeShadows>false</castEdgeShadows>
<fillPercent>0.8</fillPercent>
<staticSunShadowHeight>0</staticSunShadowHeight>
<terrainAffordanceNeeded>ARA_Creep</terrainAffordanceNeeded>
<statBases>
<MaxHitPoints>1000</MaxHitPoints>
<WorkToBuild>2800</WorkToBuild>
<Flammability>1.0</Flammability>
<ResearchSpeedFactor>1.0</ResearchSpeedFactor>
</statBases>
<uiOrder>2600</uiOrder>
<surfaceType>Item</surfaceType>
<building>
<workTableRoomRole>Laboratory</workTableRoomRole>
<workTableNotInRoomRoleFactor>0.8</workTableNotInRoomRoleFactor>
<deconstructible>false</deconstructible>
</building>
<!-- 不可建造,只能通过变形生成 -->
<comps Inherit="False">
<li Class="ArachnaeSwarm.CompProperties_DelayedTerrainSpawn">
<terrainToSpawn>ARA_InsectCreep</terrainToSpawn>
<spawnRadius>3</spawnRadius>
</li>
<li Class="CompProperties_ReportWorkSpeed">
<workSpeedStat>ResearchSpeedFactor</workSpeedStat>
</li>
<li Class="ArachnaeSwarm.CompProperties_Morphable">
<!-- 休息速度增益例如1.0代表200%的速度 -->
<restGainMultiplier>1.0</restGainMultiplier>
<!-- 操作按钮的自定义标签 -->
<gizmoLabel>离开织座</gizmoLabel>
<!-- 操作按钮的自定义描述 -->
<gizmoDesc>使织域种离开织座,恢复移动状态。</gizmoDesc>
<gizmoIconPath>ArachnaeSwarm/UI/Abilities/ARA_Ability_Morph</gizmoIconPath>
</li>
<li Class="ArachnaeSwarm.CompProperties_RefuelableNutrition">
<fuelFilter>
<thingDefs>
<li>ARA_InsectJelly</li>
</thingDefs>
</fuelFilter>
<fuelCapacity>2.0</fuelCapacity>
<targetFuelLevelConfigurable>false</targetFuelLevelConfigurable>
<fuelGizmoLabel>虫蜜</fuelGizmoLabel>
<outOfFuelMessage>没有营养</outOfFuelMessage>
</li>
<li Class="CompProperties_AffectedByFacilities">
<linkableFacilities>
<li>ARA_NutrientNetworkTower</li>
</linkableFacilities>
</li>
</comps>
</ThingDef>
<ThingDef ParentName="BuildingBase">
<defName>ARA_SmartThermostat</defName>
@@ -643,93 +570,4 @@
</comps>
</ThingDef>
<ThingDef ParentName="BuildingBase">
<defName>ARA_JellyVat</defName> <!-- defName is changed to reflect its purpose -->
<label>生物质酿造池</label>
<description>一个阿拉克涅虫族活体组织,可以充分利用活体钜菌的溶解能力,通过消化生物质,来分泌出营养丰富的阿拉克涅虫蜜。</description>
<thingClass>Building</thingClass>
<graphicData>
<texPath>ArachnaeSwarm/Building/ARA_JellyVat</texPath>
<graphicClass>Graphic_Single</graphicClass>
<shaderType>CutoutComplex</shaderType>
<drawSize>(2.2,2.2)</drawSize>
<shadowData>
<volume>(1.6, 0.5, 1.6)</volume>
<offset>(0,0,-0.1)</offset>
</shadowData>
</graphicData>
<researchPrerequisites>
<li>ARA_Technology_6GUT</li>
</researchPrerequisites>
<size>(2,2)</size>
<tickerType>Normal</tickerType>
<stuffCategories Inherit="False" />
<costStuffCount>0</costStuffCount>
<costList>
<ARA_Carapace>150</ARA_Carapace>
<ARA_Activated_Bacterium>25</ARA_Activated_Bacterium>
</costList>
<castEdgeShadows>false</castEdgeShadows>
<staticSunShadowHeight>0</staticSunShadowHeight>
<altitudeLayer>Building</altitudeLayer>
<passability>PassThroughOnly</passability>
<terrainAffordanceNeeded>ARA_Creep</terrainAffordanceNeeded>
<pathCost>50</pathCost>
<statBases>
<MaxHitPoints>250</MaxHitPoints>
<WorkToBuild>2800</WorkToBuild>
<Flammability>1.0</Flammability>
</statBases>
<placeWorkers>
<li>PlaceWorker_PreventInteractionSpotOverlap</li>
</placeWorkers>
<fillPercent>0.8</fillPercent>
<interactionCellOffset>(0,0,-1)</interactionCellOffset>
<hasInteractionCell>true</hasInteractionCell>
<designationCategory>ARA_Buildings</designationCategory>
<uiOrder>2600</uiOrder>
<surfaceType>Item</surfaceType>
<building>
<workTableRoomRole>Laboratory</workTableRoomRole>
<workTableNotInRoomRoleFactor>0.8</workTableNotInRoomRoleFactor>
</building>
<terrainAffordanceNeeded>ARA_Creep</terrainAffordanceNeeded>
<comps>
<li Class="CompProperties_Flickable"/>
<li Class="ArachnaeSwarm.CompProperties_MultiFuelSpawner">
<spawnIntervalRange>
<min>120000</min> <!-- 2天 -->
<max>120000</max>
</spawnIntervalRange>
<products>
<li>
<thingDef>ARA_InsectJelly</thingDef>
<count>60</count>
</li>
</products>
<showMessageIfOwned>true</showMessageIfOwned>
</li>
<li Class="ArachnaeSwarm.CompProperties_RefuelableNutrition_WithKey">
<saveKeysPrefix>Biomass</saveKeysPrefix>
<fuelLabel>生物质</fuelLabel>
<fuelFilter>
<categories>
<li>MeatRaw</li>
<li>PlantFoodRaw</li>
</categories>
<disallowedThingDefs>
<li>Milk</li>
<li>InsectJelly</li>
</disallowedThingDefs>
</fuelFilter>
<fuelCapacity>50</fuelCapacity>
<fuelConsumptionRate>30</fuelConsumptionRate>
<consumeFuelOnlyWhenUsed>true</consumeFuelOnlyWhenUsed>
</li>
<li Class="ArachnaeSwarm.CompProperties_DelayedTerrainSpawn">
<terrainToSpawn>ARA_InsectCreep</terrainToSpawn>
<spawnRadius>6</spawnRadius>
</li>
</comps>
</ThingDef>
</Defs>

View File

@@ -54,22 +54,6 @@
<maxDistance>80</maxDistance> <!-- 供能范围 -->
<maxSimultaneous>10</maxSimultaneous>
<maxEfficiency>0.9</maxEfficiency>
</li>
<!-- 全新的、只负责画线的组件 -->
<li Class="ArachnaeSwarm.CompProperties_LineDrawer">
<linkableBuildings>
<li>ARA_BioforgeIncubator</li>
<li>ARA_BioforgeIncubator_Thing</li>
<li>ARA_GrowthVat</li>
<li>ARA_MorphableResearchBench</li>
<li>ARANutrientDispenser</li>
<li>ARA_WormholePortal_A</li>
<li>ARA_Acidling_AutoMortar</li>
<li>CatastropheMissileSilo</li>
<li>ARA_AutoSniperCannon</li>
</linkableBuildings>
<maxDistance>80</maxDistance>
<lineTexturePath>ArachnaeSwarm/Building/Nutrition_Pie</lineTexturePath>
</li>
@@ -511,4 +495,221 @@
</li>
</comps>
</ThingDef>
<ThingDef ParentName="BuildingBase">
<defName>ARA_MorphableResearchBench</defName>
<!-- <hasInteractionCell>true</hasInteractionCell>
<interactionCellOffset>(0,0,-1)</interactionCellOffset> -->
<label>阿拉克涅织域种织座</label>
<description>以阿拉克涅织域种为核心形成的活体结构,是推进虫群高级研究的必须建筑。该建筑无法建造,只能通过阿拉克涅织域种变形得到。</description>
<thingClass>ArachnaeSwarm.Building_Morphable</thingClass>
<tickerType>Normal</tickerType>
<drawerType>RealtimeOnly</drawerType> <!-- ADDED: Force realtime rendering -->
<size>(2,2)</size>
<graphicData>
<texPath>ArachnaeSwarm/Building/ARA_MorphableResearchBench</texPath>
<graphicClass>Graphic_Single</graphicClass>
<shaderType>CutoutComplex</shaderType>
<drawSize>(3,3)</drawSize>
<drawOffset>(0, 0, 0.25)</drawOffset>
</graphicData>
<altitudeLayer>Building</altitudeLayer>
<passability>Impassable</passability>
<castEdgeShadows>false</castEdgeShadows>
<fillPercent>0.8</fillPercent>
<staticSunShadowHeight>0</staticSunShadowHeight>
<terrainAffordanceNeeded>ARA_Creep</terrainAffordanceNeeded>
<statBases>
<MaxHitPoints>1000</MaxHitPoints>
<WorkToBuild>2800</WorkToBuild>
<Flammability>1.0</Flammability>
<ResearchSpeedFactor>1.0</ResearchSpeedFactor>
</statBases>
<uiOrder>2600</uiOrder>
<surfaceType>Item</surfaceType>
<building>
<workTableRoomRole>Laboratory</workTableRoomRole>
<workTableNotInRoomRoleFactor>0.8</workTableNotInRoomRoleFactor>
<deconstructible>false</deconstructible>
</building>
<!-- 不可建造,只能通过变形生成 -->
<comps Inherit="False">
<li Class="ArachnaeSwarm.CompProperties_DelayedTerrainSpawn">
<terrainToSpawn>ARA_InsectCreep</terrainToSpawn>
<spawnRadius>3</spawnRadius>
</li>
<li Class="CompProperties_ReportWorkSpeed">
<workSpeedStat>ResearchSpeedFactor</workSpeedStat>
</li>
<li Class="CompProperties_ProjectileInterceptor">
<radius>5.9</radius>
<hitPoints>300</hitPoints>
<interceptAirProjectiles>true</interceptAirProjectiles>
<interceptGroundProjectiles>true</interceptGroundProjectiles>
<color>(0.5, 0.3, 0.9, 0.5)</color>
<reactivateEffect>BulletShieldGenerator_Reactivate</reactivateEffect>
<activeSound>BulletShield_Ambience</activeSound>
<rechargeHitPointsIntervalTicks>1</rechargeHitPointsIntervalTicks>
<startWithMaxHitPoints>true</startWithMaxHitPoints>
<alwaysShowHitpointsGizmo>true</alwaysShowHitpointsGizmo>
<drawWithNoSelection>true</drawWithNoSelection>
</li>
<li Class="ArachnaeSwarm.CompProperties_Morphable">
<!-- 休息速度增益例如1.0代表200%的速度 -->
<restGainMultiplier>1.0</restGainMultiplier>
<!-- 操作按钮的自定义标签 -->
<gizmoLabel>离开织座</gizmoLabel>
<!-- 操作按钮的自定义描述 -->
<gizmoDesc>使织域种离开织座,恢复移动状态。</gizmoDesc>
<gizmoIconPath>ArachnaeSwarm/UI/Abilities/ARA_Ability_Morph</gizmoIconPath>
</li>
<li Class="ArachnaeSwarm.CompProperties_RefuelableNutrition">
<fuelFilter>
<thingDefs>
<li>ARA_InsectJelly</li>
</thingDefs>
</fuelFilter>
<fuelCapacity>2.0</fuelCapacity>
<targetFuelLevelConfigurable>false</targetFuelLevelConfigurable>
<fuelGizmoLabel>虫蜜</fuelGizmoLabel>
<outOfFuelMessage>没有营养</outOfFuelMessage>
</li>
<li Class="CompProperties_AffectedByFacilities">
<linkableFacilities>
<li>ARA_NutrientNetworkTower</li>
</linkableFacilities>
</li>
</comps>
</ThingDef>
<ThingDef ParentName="BuildingBase">
<defName>ARA_JellyVat</defName> <!-- defName is changed to reflect its purpose -->
<label>生物质酿造池</label>
<description>一个阿拉克涅虫族活体组织,可以充分利用活体钜菌的溶解能力,通过消化生物质,来分泌出营养丰富的阿拉克涅虫蜜。</description>
<thingClass>Building</thingClass>
<graphicData>
<texPath>ArachnaeSwarm/Building/ARA_JellyVat</texPath>
<graphicClass>Graphic_Single</graphicClass>
<shaderType>CutoutComplex</shaderType>
<drawSize>(2.2,2.2)</drawSize>
<shadowData>
<volume>(1.6, 0.5, 1.6)</volume>
<offset>(0,0,-0.1)</offset>
</shadowData>
</graphicData>
<researchPrerequisites>
<li>ARA_Technology_6GUT</li>
</researchPrerequisites>
<size>(2,2)</size>
<tickerType>Normal</tickerType>
<stuffCategories Inherit="False" />
<costStuffCount>0</costStuffCount>
<costList>
<ARA_Carapace>150</ARA_Carapace>
<ARA_Activated_Bacterium>25</ARA_Activated_Bacterium>
</costList>
<castEdgeShadows>false</castEdgeShadows>
<staticSunShadowHeight>0</staticSunShadowHeight>
<altitudeLayer>Building</altitudeLayer>
<passability>PassThroughOnly</passability>
<terrainAffordanceNeeded>ARA_Creep</terrainAffordanceNeeded>
<pathCost>50</pathCost>
<statBases>
<MaxHitPoints>250</MaxHitPoints>
<WorkToBuild>2800</WorkToBuild>
<Flammability>1.0</Flammability>
</statBases>
<placeWorkers>
<li>PlaceWorker_PreventInteractionSpotOverlap</li>
</placeWorkers>
<fillPercent>0.8</fillPercent>
<interactionCellOffset>(0,0,-1)</interactionCellOffset>
<hasInteractionCell>true</hasInteractionCell>
<designationCategory>ARA_Buildings</designationCategory>
<uiOrder>2600</uiOrder>
<surfaceType>Item</surfaceType>
<building>
<workTableRoomRole>Laboratory</workTableRoomRole>
<workTableNotInRoomRoleFactor>0.8</workTableNotInRoomRoleFactor>
</building>
<terrainAffordanceNeeded>ARA_Creep</terrainAffordanceNeeded>
<comps>
<li Class="CompProperties_Flickable"/>
<!--<li Class="ArachnaeSwarm.CompProperties_MultiFuelSpawner">
<spawnIntervalRange>
<min>120000</min>
<max>120000</max>
</spawnIntervalRange>
<products>
<li>
<thingDef>ARA_InsectJelly</thingDef>
<count>60</count>
</li>
</products>
<showMessageIfOwned>true</showMessageIfOwned>
</li>-->
<li Class="ArachnaeSwarm.CompProperties_RefuelableNutrition_WithKey">
<saveKeysPrefix>Biomass</saveKeysPrefix>
<fuelLabel>生物质</fuelLabel>
<fuelGizmoLabel>生物质</fuelGizmoLabel>
<fuelFilter>
<categories>
<li>MeatRaw</li>
<li>PlantFoodRaw</li>
</categories>
<disallowedThingDefs>
<li>Milk</li>
<li>InsectJelly</li>
</disallowedThingDefs>
</fuelFilter>
<fuelCapacity>50</fuelCapacity>
<fuelConsumptionRate>0</fuelConsumptionRate>
<consumeFuelOnlyWhenUsed>true</consumeFuelOnlyWhenUsed>
<targetFuelLevelConfigurable>true</targetFuelLevelConfigurable>
<showAllowAutoRefuelToggle>true</showAllowAutoRefuelToggle>
</li>
<li Class="CompProperties_Refuelable">
<fuelLabel>虫蜜</fuelLabel>
<fuelGizmoLabel>虫蜜</fuelGizmoLabel>
<fuelCapacity>100.0</fuelCapacity>
<fuelConsumptionRate>0</fuelConsumptionRate>
<consumeFuelOnlyWhenUsed>true</consumeFuelOnlyWhenUsed>
<fuelFilter>
<thingDefs>
<li>ARA_InsectJelly</li>
</thingDefs>
</fuelFilter>
<targetFuelLevelConfigurable>true</targetFuelLevelConfigurable>
<showAllowAutoRefuelToggle>true</showAllowAutoRefuelToggle>
<canEjectFuel>true</canEjectFuel>
</li>
<li Class="ArachnaeSwarm.CompProperties_NutritionToFuelConverter">
<checkInterval>200</checkInterval>
<nutritionCost>1</nutritionCost>
<workAmount>2000</workAmount>
<fuelAmount>1</fuelAmount>
</li>
<!-- 燃料满了自动弹出 -->
<li Class="ArachnaeSwarm.CompProperties_AutoEjector">
<!-- 精确指定要监控的燃料组件 -->
<targetComp>CompRefuelable</targetComp>
<!-- 在燃料达到99%时弹出 -->
<ejectAtPercent>0.99</ejectAtPercent>
</li>
<li Class="ArachnaeSwarm.CompProperties_DelayedTerrainSpawn">
<terrainToSpawn>ARA_InsectCreep</terrainToSpawn>
<spawnRadius>6</spawnRadius>
</li>
</comps>
</ThingDef>
</Defs>

View File

@@ -299,8 +299,9 @@
<terrainAffordanceNeeded>Heavy</terrainAffordanceNeeded>
<comps>
<li Class="CompProperties_Forbiddable" />
<li Class="ArachnaeSwarm.CompProperties_RefuelableNutrition">
<fuelCapacity>100</fuelCapacity>
<li Class="ArachnaeSwarm.CompProperties_RefuelableNutrition_WithKey">
<saveKeysPrefix>nutrition</saveKeysPrefix>
<fuelCapacity>5.0</fuelCapacity>
<fuelFilter>
<thingDefs>
<li>ARA_InsectJelly</li>
@@ -451,8 +452,9 @@
<terrainAffordanceNeeded>Heavy</terrainAffordanceNeeded>
<comps>
<li Class="CompProperties_Forbiddable" />
<li Class="ArachnaeSwarm.CompProperties_RefuelableNutrition">
<fuelCapacity>20</fuelCapacity>
<li Class="ArachnaeSwarm.CompProperties_RefuelableNutrition_WithKey">
<saveKeysPrefix>nutrition</saveKeysPrefix>
<fuelCapacity>5.0</fuelCapacity>
<fuelFilter>
<thingDefs>
<li>ARA_InsectJelly</li>
@@ -623,8 +625,9 @@
</statBases>
<tickerType>Normal</tickerType>
<comps>
<li Class="ArachnaeSwarm.CompProperties_RefuelableNutrition">
<fuelCapacity>10</fuelCapacity>
<li Class="ArachnaeSwarm.CompProperties_RefuelableNutrition_WithKey">
<saveKeysPrefix>nutrition</saveKeysPrefix>
<fuelCapacity>5.0</fuelCapacity>
<fuelFilter>
<thingDefs>
<li>ARA_InsectJelly</li>

View File

@@ -0,0 +1,14 @@
<?xml version="1.0" encoding="utf-8" ?>
<Defs>
<WorkGiverDef>
<defName>ARA_Refuel_Nutrition</defName>
<label>补充营养</label>
<giverClass>ArachnaeSwarm.WorkGiver_Refuel_Nutrition</giverClass>
<workType>Hauling</workType>
<priorityInType>60</priorityInType>
<verb>补充</verb>
<gerund>补充</gerund>
<scanThings>true</scanThings>
<scanCells>false</scanCells>
</WorkGiverDef>
</Defs>

View File

@@ -1,22 +1,29 @@
{
"Version": 1,
"WorkspaceRootPath": "E:\\SteamLibrary\\steamapps\\common\\RimWorld\\Mods\\ArachnaeSwarm\\Source\\ArachnaeSwarm\\",
"WorkspaceRootPath": "C:\\Steam\\steamapps\\common\\RimWorld\\Mods\\ArachnaeSwarm\\Source\\ArachnaeSwarm\\",
"Documents": [
{
"AbsoluteMoniker": "D:0:0:{EAE0DB6B-E282-C812-7F5A-6D13E9D24581}|ArachnaeSwarm.csproj|e:\\steamlibrary\\steamapps\\common\\rimworld\\mods\\arachnaeswarm\\source\\arachnaeswarm\\thingcomp_guardianpsyfield.cs||{A6C744A8-0E4A-4FC6-886A-064283054674}",
"RelativeMoniker": "D:0:0:{EAE0DB6B-E282-C812-7F5A-6D13E9D24581}|ArachnaeSwarm.csproj|solutionrelative:thingcomp_guardianpsyfield.cs||{A6C744A8-0E4A-4FC6-886A-064283054674}"
"AbsoluteMoniker": "D:0:0:{EAE0DB6B-E282-C812-7F5A-6D13E9D24581}|ArachnaeSwarm.csproj|c:\\steam\\steamapps\\common\\rimworld\\mods\\arachnaeswarm\\source\\arachnaeswarm\\jobs\\jobdriver_supercarry\\floatmenuoptionprovider_supercarry.cs||{A6C744A8-0E4A-4FC6-886A-064283054674}",
"RelativeMoniker": "D:0:0:{EAE0DB6B-E282-C812-7F5A-6D13E9D24581}|ArachnaeSwarm.csproj|solutionrelative:jobs\\jobdriver_supercarry\\floatmenuoptionprovider_supercarry.cs||{A6C744A8-0E4A-4FC6-886A-064283054674}"
},
{
"AbsoluteMoniker": "D:0:0:{EAE0DB6B-E282-C812-7F5A-6D13E9D24581}|ArachnaeSwarm.csproj|E:\\SteamLibrary\\steamapps\\common\\RimWorld\\Mods\\ArachnaeSwarm\\Source\\ArachnaeSwarm\\building_comps\\ara_nutrientvat\\building_nutrientvat.cs||{A6C744A8-0E4A-4FC6-886A-064283054674}",
"AbsoluteMoniker": "D:0:0:{EAE0DB6B-E282-C812-7F5A-6D13E9D24581}|ArachnaeSwarm.csproj|c:\\steam\\steamapps\\common\\rimworld\\mods\\arachnaeswarm\\source\\arachnaeswarm\\thing_comps\\ara_thingcomp_guardianpsyfield\\harmony_projectileinterceptor.cs||{A6C744A8-0E4A-4FC6-886A-064283054674}",
"RelativeMoniker": "D:0:0:{EAE0DB6B-E282-C812-7F5A-6D13E9D24581}|ArachnaeSwarm.csproj|solutionrelative:thing_comps\\ara_thingcomp_guardianpsyfield\\harmony_projectileinterceptor.cs||{A6C744A8-0E4A-4FC6-886A-064283054674}"
},
{
"AbsoluteMoniker": "D:0:0:{EAE0DB6B-E282-C812-7F5A-6D13E9D24581}|ArachnaeSwarm.csproj|c:\\steam\\steamapps\\common\\rimworld\\mods\\arachnaeswarm\\source\\arachnaeswarm\\thing_comps\\ara_thingcomp_guardianpsyfield\\thingcomp_guardianpsyfield.cs||{A6C744A8-0E4A-4FC6-886A-064283054674}",
"RelativeMoniker": "D:0:0:{EAE0DB6B-E282-C812-7F5A-6D13E9D24581}|ArachnaeSwarm.csproj|solutionrelative:thing_comps\\ara_thingcomp_guardianpsyfield\\thingcomp_guardianpsyfield.cs||{A6C744A8-0E4A-4FC6-886A-064283054674}"
},
{
"AbsoluteMoniker": "D:0:0:{EAE0DB6B-E282-C812-7F5A-6D13E9D24581}|ArachnaeSwarm.csproj|C:\\Steam\\steamapps\\common\\RimWorld\\Mods\\ArachnaeSwarm\\Source\\ArachnaeSwarm\\building_comps\\ara_nutrientvat\\building_nutrientvat.cs||{A6C744A8-0E4A-4FC6-886A-064283054674}",
"RelativeMoniker": "D:0:0:{EAE0DB6B-E282-C812-7F5A-6D13E9D24581}|ArachnaeSwarm.csproj|solutionrelative:building_comps\\ara_nutrientvat\\building_nutrientvat.cs||{A6C744A8-0E4A-4FC6-886A-064283054674}"
},
{
"AbsoluteMoniker": "D:0:0:{EAE0DB6B-E282-C812-7F5A-6D13E9D24581}|ArachnaeSwarm.csproj|E:\\SteamLibrary\\steamapps\\common\\RimWorld\\Mods\\ArachnaeSwarm\\Source\\ArachnaeSwarm\\building_comps\\ara_nutrientnetwork\\compnutrientprovider.cs||{A6C744A8-0E4A-4FC6-886A-064283054674}",
"AbsoluteMoniker": "D:0:0:{EAE0DB6B-E282-C812-7F5A-6D13E9D24581}|ArachnaeSwarm.csproj|C:\\Steam\\steamapps\\common\\RimWorld\\Mods\\ArachnaeSwarm\\Source\\ArachnaeSwarm\\building_comps\\ara_nutrientnetwork\\compnutrientprovider.cs||{A6C744A8-0E4A-4FC6-886A-064283054674}",
"RelativeMoniker": "D:0:0:{EAE0DB6B-E282-C812-7F5A-6D13E9D24581}|ArachnaeSwarm.csproj|solutionrelative:building_comps\\ara_nutrientnetwork\\compnutrientprovider.cs||{A6C744A8-0E4A-4FC6-886A-064283054674}"
},
{
"AbsoluteMoniker": "D:0:0:{EAE0DB6B-E282-C812-7F5A-6D13E9D24581}|ArachnaeSwarm.csproj|E:\\SteamLibrary\\steamapps\\common\\RimWorld\\Mods\\ArachnaeSwarm\\Source\\ArachnaeSwarm\\building_comps\\ara_nutrientnetwork\\complinedrawer.cs||{A6C744A8-0E4A-4FC6-886A-064283054674}",
"RelativeMoniker": "D:0:0:{EAE0DB6B-E282-C812-7F5A-6D13E9D24581}|ArachnaeSwarm.csproj|solutionrelative:building_comps\\ara_nutrientnetwork\\complinedrawer.cs||{A6C744A8-0E4A-4FC6-886A-064283054674}"
"AbsoluteMoniker": "D:0:0:{A2FE74E1-B743-11D0-AE1A-00A0C90FFFC3}|\u003CMiscFiles\u003E|E:\\SteamLibrary\\steamapps\\common\\RimWorld\\Mods\\ArachnaeSwarm\\Source\\ArachnaeSwarm\\Building_Comps\\ARA_NutrientNetwork\\CompLineDrawer.cs||{A6C744A8-0E4A-4FC6-886A-064283054674}"
}
],
"DocumentGroupContainers": [
@@ -35,23 +42,49 @@
{
"$type": "Document",
"DocumentIndex": 0,
"Title": "FloatMenuOptionProvider_SuperCarry.cs",
"DocumentMoniker": "C:\\Steam\\steamapps\\common\\RimWorld\\Mods\\ArachnaeSwarm\\Source\\ArachnaeSwarm\\Jobs\\JobDriver_SuperCarry\\FloatMenuOptionProvider_SuperCarry.cs",
"RelativeDocumentMoniker": "Jobs\\JobDriver_SuperCarry\\FloatMenuOptionProvider_SuperCarry.cs",
"ToolTip": "C:\\Steam\\steamapps\\common\\RimWorld\\Mods\\ArachnaeSwarm\\Source\\ArachnaeSwarm\\Jobs\\JobDriver_SuperCarry\\FloatMenuOptionProvider_SuperCarry.cs",
"RelativeToolTip": "Jobs\\JobDriver_SuperCarry\\FloatMenuOptionProvider_SuperCarry.cs",
"ViewState": "AQIAAAAAAAAAAAAAAADwvwAAAAAAAAAA",
"Icon": "ae27a6b0-e345-4288-96df-5eaf394ee369.000738|",
"WhenOpened": "2025-10-04T07:00:14.106Z",
"EditorCaption": ""
},
{
"$type": "Document",
"DocumentIndex": 1,
"Title": "Harmony_ProjectileInterceptor.cs",
"DocumentMoniker": "C:\\Steam\\steamapps\\common\\RimWorld\\Mods\\ArachnaeSwarm\\Source\\ArachnaeSwarm\\Thing_Comps\\ARA_ThingComp_GuardianPsyField\\Harmony_ProjectileInterceptor.cs",
"RelativeDocumentMoniker": "Thing_Comps\\ARA_ThingComp_GuardianPsyField\\Harmony_ProjectileInterceptor.cs",
"ToolTip": "C:\\Steam\\steamapps\\common\\RimWorld\\Mods\\ArachnaeSwarm\\Source\\ArachnaeSwarm\\Thing_Comps\\ARA_ThingComp_GuardianPsyField\\Harmony_ProjectileInterceptor.cs",
"RelativeToolTip": "Thing_Comps\\ARA_ThingComp_GuardianPsyField\\Harmony_ProjectileInterceptor.cs",
"ViewState": "AQIAAAAAAAAAAAAAAADwvwAAAAAAAAAA",
"Icon": "ae27a6b0-e345-4288-96df-5eaf394ee369.000738|",
"WhenOpened": "2025-10-04T07:00:14.029Z",
"EditorCaption": ""
},
{
"$type": "Document",
"DocumentIndex": 2,
"Title": "ThingComp_GuardianPsyField.cs",
"DocumentMoniker": "E:\\SteamLibrary\\steamapps\\common\\RimWorld\\Mods\\ArachnaeSwarm\\Source\\ArachnaeSwarm\\ThingComp_GuardianPsyField.cs",
"RelativeDocumentMoniker": "ThingComp_GuardianPsyField.cs",
"ToolTip": "E:\\SteamLibrary\\steamapps\\common\\RimWorld\\Mods\\ArachnaeSwarm\\Source\\ArachnaeSwarm\\ThingComp_GuardianPsyField.cs",
"RelativeToolTip": "ThingComp_GuardianPsyField.cs",
"ViewState": "AgIAAAAAAAAAAAAAAAAAAAoAAAAgAAAAAAAAAA==",
"DocumentMoniker": "C:\\Steam\\steamapps\\common\\RimWorld\\Mods\\ArachnaeSwarm\\Source\\ArachnaeSwarm\\Thing_Comps\\ARA_ThingComp_GuardianPsyField\\ThingComp_GuardianPsyField.cs",
"RelativeDocumentMoniker": "Thing_Comps\\ARA_ThingComp_GuardianPsyField\\ThingComp_GuardianPsyField.cs",
"ToolTip": "C:\\Steam\\steamapps\\common\\RimWorld\\Mods\\ArachnaeSwarm\\Source\\ArachnaeSwarm\\Thing_Comps\\ARA_ThingComp_GuardianPsyField\\ThingComp_GuardianPsyField.cs",
"RelativeToolTip": "Thing_Comps\\ARA_ThingComp_GuardianPsyField\\ThingComp_GuardianPsyField.cs",
"ViewState": "AQIAAAAAAAAAAAAAAADwvwAAAAAAAAAA",
"Icon": "ae27a6b0-e345-4288-96df-5eaf394ee369.000738|",
"WhenOpened": "2025-10-02T16:24:06.176Z",
"EditorCaption": ""
},
{
"$type": "Document",
"DocumentIndex": 1,
"DocumentIndex": 3,
"Title": "Building_NutrientVat.cs",
"DocumentMoniker": "E:\\SteamLibrary\\steamapps\\common\\RimWorld\\Mods\\ArachnaeSwarm\\Source\\ArachnaeSwarm\\Building_Comps\\ARA_NutrientVat\\Building_NutrientVat.cs",
"DocumentMoniker": "C:\\Steam\\steamapps\\common\\RimWorld\\Mods\\ArachnaeSwarm\\Source\\ArachnaeSwarm\\Building_Comps\\ARA_NutrientVat\\Building_NutrientVat.cs",
"RelativeDocumentMoniker": "Building_Comps\\ARA_NutrientVat\\Building_NutrientVat.cs",
"ToolTip": "E:\\SteamLibrary\\steamapps\\common\\RimWorld\\Mods\\ArachnaeSwarm\\Source\\ArachnaeSwarm\\Building_Comps\\ARA_NutrientVat\\Building_NutrientVat.cs",
"ToolTip": "C:\\Steam\\steamapps\\common\\RimWorld\\Mods\\ArachnaeSwarm\\Source\\ArachnaeSwarm\\Building_Comps\\ARA_NutrientVat\\Building_NutrientVat.cs",
"RelativeToolTip": "Building_Comps\\ARA_NutrientVat\\Building_NutrientVat.cs",
"ViewState": "AgIAAF8AAAAAAAAAAAAswJkBAAANAAAAAAAAAA==",
"Icon": "ae27a6b0-e345-4288-96df-5eaf394ee369.000738|",
@@ -59,11 +92,11 @@
},
{
"$type": "Document",
"DocumentIndex": 2,
"DocumentIndex": 4,
"Title": "CompNutrientProvider.cs",
"DocumentMoniker": "E:\\SteamLibrary\\steamapps\\common\\RimWorld\\Mods\\ArachnaeSwarm\\Source\\ArachnaeSwarm\\Building_Comps\\ARA_NutrientNetwork\\CompNutrientProvider.cs",
"DocumentMoniker": "C:\\Steam\\steamapps\\common\\RimWorld\\Mods\\ArachnaeSwarm\\Source\\ArachnaeSwarm\\Building_Comps\\ARA_NutrientNetwork\\CompNutrientProvider.cs",
"RelativeDocumentMoniker": "Building_Comps\\ARA_NutrientNetwork\\CompNutrientProvider.cs",
"ToolTip": "E:\\SteamLibrary\\steamapps\\common\\RimWorld\\Mods\\ArachnaeSwarm\\Source\\ArachnaeSwarm\\Building_Comps\\ARA_NutrientNetwork\\CompNutrientProvider.cs",
"ToolTip": "C:\\Steam\\steamapps\\common\\RimWorld\\Mods\\ArachnaeSwarm\\Source\\ArachnaeSwarm\\Building_Comps\\ARA_NutrientNetwork\\CompNutrientProvider.cs",
"RelativeToolTip": "Building_Comps\\ARA_NutrientNetwork\\CompNutrientProvider.cs",
"ViewState": "AgIAAAAAAAAAAAAAAAAAABkAAAAZAAAAAAAAAA==",
"Icon": "ae27a6b0-e345-4288-96df-5eaf394ee369.000738|",
@@ -71,12 +104,10 @@
},
{
"$type": "Document",
"DocumentIndex": 3,
"DocumentIndex": 5,
"Title": "CompLineDrawer.cs",
"DocumentMoniker": "E:\\SteamLibrary\\steamapps\\common\\RimWorld\\Mods\\ArachnaeSwarm\\Source\\ArachnaeSwarm\\Building_Comps\\ARA_NutrientNetwork\\CompLineDrawer.cs",
"RelativeDocumentMoniker": "Building_Comps\\ARA_NutrientNetwork\\CompLineDrawer.cs",
"ToolTip": "E:\\SteamLibrary\\steamapps\\common\\RimWorld\\Mods\\ArachnaeSwarm\\Source\\ArachnaeSwarm\\Building_Comps\\ARA_NutrientNetwork\\CompLineDrawer.cs",
"RelativeToolTip": "Building_Comps\\ARA_NutrientNetwork\\CompLineDrawer.cs",
"ViewState": "AgIAAAAAAAAAAAAAAAAAABkAAAAAAAAAAAAAAA==",
"Icon": "ae27a6b0-e345-4288-96df-5eaf394ee369.000738|",
"WhenOpened": "2025-10-02T15:11:03.083Z"

View File

@@ -128,7 +128,6 @@
<Compile Include="Building_Comps\ARA_HunterTrap\CompProperties_HunterExplosion.cs" />
<Compile Include="Building_Comps\ARA_HunterTrap\TrapReleaseRandomExtension.cs" />
<Compile Include="Building_Comps\ARA_NutrientNetwork\CompFacility_GrowthVatBooster.cs" />
<Compile Include="Building_Comps\ARA_NutrientNetwork\CompLineDrawer.cs" />
<Compile Include="Building_Comps\ARA_NutrientNetwork\CompNutrientProvider.cs" />
<Compile Include="Building_Comps\ARA_NutrientNetwork\CompProperties_NutrientProvider.cs" />
<Compile Include="Building_Comps\ARA_NutrientVat\Building_NutrientVat.cs" />
@@ -146,6 +145,9 @@
<Compile Include="Building_Comps\WULA_MutiFuelSpawner\IFuelSource.cs" />
<Compile Include="Building_Comps\WULA_MutiFuelSpawner\Patch_CompRefuelableWithKey.cs" />
<Compile Include="Building_Comps\CompNutritionToFuelConverter.cs" />
<Compile Include="Building_Comps\CompAutoEjector.cs" />
<Compile Include="Jobs\JobDriver_Refuel_Nutrition.cs" />
<Compile Include="WorkGivers\WorkGiver_Refuel_Nutrition.cs" />
<Compile Include="Hediffs\ARA_ConfigurableMutant\Hediff_ConfigurableMutant.cs" />
<Compile Include="Hediffs\ARA_ConfigurableMutant\Hediff_NecroticVirus_Configurable.cs" />
<Compile Include="Hediffs\ARA_ConfigurableMutant\HediffComp_NecroticTransformation.cs" />
@@ -171,13 +173,13 @@
<Compile Include="Hediffs\ProphecyGearEffect.cs" />
<Compile Include="Hediffs\WULA_HediffDamgeShield\DRMDamageShield.cs" />
<Compile Include="Hediffs\WULA_HediffDamgeShield\Hediff_DamageShield.cs" />
<Compile Include="Hediff_DynamicInterceptor.cs" />
<Compile Include="JobDriver_CarryPrisonerToNutrientVat.cs" />
<Compile Include="Thing_Comps\ARA_ThingComp_GuardianPsyField\Hediff_DynamicInterceptor.cs" />
<Compile Include="Jobs\JobDriver_CarryPrisonerToNutrientVat.cs" />
<Compile Include="Pawn_Comps\ARA_CompSkillExperienceGiver\CompProperties_SkillExperienceGiver.cs" />
<Compile Include="Pawn_Comps\ARA_CompSkillExperienceGiver\CompSkillExperienceGiver.cs" />
<Compile Include="Stat\StatWorker_IncubationInfo.cs" />
<Compile Include="ThingComp_GuardianPsyField.cs" />
<Compile Include="Harmony_ProjectileInterceptor.cs" />
<Compile Include="Thing_Comps\ARA_ThingComp_GuardianPsyField\ThingComp_GuardianPsyField.cs" />
<Compile Include="Thing_Comps\ARA_ThingComp_GuardianPsyField\Harmony_ProjectileInterceptor.cs" />
<Compile Include="MainHarmony.cs" />
<Compile Include="Thing_Comps\ARA_CompExtraIncubationInfo\CompExtraIncubationInfo.cs" />
<Compile Include="Thing_Comps\ARA_CompExtraIncubationInfo\CompProperties_ExtraIncubationInfo.cs" />
@@ -250,9 +252,9 @@
<Compile Include="Building_Comps\CompForceTargetable.cs" />
</ItemGroup>
<ItemGroup>
<Compile Include="SuperCarryExtension.cs" />
<Compile Include="FloatMenuOptionProvider_SuperCarry.cs" />
<Compile Include="JobDriver_SuperCarry.cs" />
<Compile Include="Jobs\JobDriver_SuperCarry\SuperCarryExtension.cs" />
<Compile Include="Jobs\JobDriver_SuperCarry\FloatMenuOptionProvider_SuperCarry.cs" />
<Compile Include="Jobs\JobDriver_SuperCarry\JobDriver_SuperCarry.cs" />
</ItemGroup>
<ItemGroup>
<Compile Include="Buildings\Wormhole\Building_WormholePortal_A.cs" />

View File

@@ -1,172 +0,0 @@
using RimWorld;
using Verse;
using UnityEngine;
using System.Collections.Generic;
using System.Linq;
namespace ArachnaeSwarm
{
public class CompProperties_LineDrawer : CompProperties
{
public List<ThingDef> linkableBuildings;
public float maxDistance = 999f;
public string lineTexturePath = "Things/Special/Power/Wire";
public CompProperties_LineDrawer()
{
compClass = typeof(CompLineDrawer);
}
}
public class CompLineDrawer : ThingComp
{
private CompProperties_LineDrawer Props => (CompProperties_LineDrawer)props;
private List<Thing> linkedBuildings = new List<Thing>();
private Material lineMat;
private Material LineMat
{
get
{
if (lineMat == null)
{
lineMat = MaterialPool.MatFrom(Props.lineTexturePath, ShaderDatabase.Transparent, Color.white);
}
return lineMat;
}
}
public override void PostSpawnSetup(bool respawningAfterLoad)
{
base.PostSpawnSetup(respawningAfterLoad);
FindAndLinkBuildings();
}
public override void CompTick()
{
base.CompTick();
if (parent.IsHashIntervalTick(120))
{
FindAndLinkBuildings();
}
}
private void FindAndLinkBuildings()
{
int previousCount = linkedBuildings.Count;
linkedBuildings.Clear();
if (Props.linkableBuildings.NullOrEmpty()) return;
var potentialTargets = parent.Map.listerBuildings.allBuildingsColonist.Where(b =>
b != parent &&
Props.linkableBuildings.Contains(b.def) &&
parent.Position.DistanceTo(b.Position) <= Props.maxDistance &&
HasLineOfSight(parent, b) // 新增:检查视线
);
linkedBuildings.AddRange(potentialTargets);
if (linkedBuildings.Count != previousCount)
{
parent.Map.mapDrawer.MapMeshDirty(parent.Position, MapMeshFlagDefOf.Things, true, false);
}
}
// 修复后的方法:检查两点之间是否有墙体遮挡
private bool HasLineOfSight(Thing from, Thing to)
{
IntVec3 fromPos = from.Position;
IntVec3 toPos = to.Position;
// 如果两个建筑在同一位置直接返回true
if (fromPos == toPos) return true;
// 使用更兼容的视线检查方法
if (!GenSight.LineOfSight(fromPos, toPos, parent.Map))
{
return false;
}
// 额外检查:确保路径上没有阻挡视线的建筑
foreach (IntVec3 cell in GetLineCells(fromPos, toPos))
{
// 跳过起点和终点
if (cell == fromPos || cell == toPos) continue;
// 检查该单元格是否有阻挡视线的建筑
List<Thing> things = parent.Map.thingGrid.ThingsListAt(cell);
foreach (Thing thing in things)
{
// 如果是建筑且阻挡视线
if (thing.def.passability == Traversability.Impassable &&
thing.def.category == ThingCategory.Building &&
!IsPassableBuilding(thing.def)) // 排除可通行的建筑类型
{
return false;
}
}
}
return true;
}
// 获取两点之间的直线上的所有单元格
private IEnumerable<IntVec3> GetLineCells(IntVec3 start, IntVec3 end)
{
// 使用简单的Bresenham直线算法
int x0 = start.x;
int y0 = start.z;
int x1 = end.x;
int y1 = end.z;
int dx = Mathf.Abs(x1 - x0);
int dy = Mathf.Abs(y1 - y0);
int sx = x0 < x1 ? 1 : -1;
int sy = y0 < y1 ? 1 : -1;
int err = dx - dy;
while (true)
{
yield return new IntVec3(x0, 0, y0);
if (x0 == x1 && y0 == y1) break;
int e2 = 2 * err;
if (e2 > -dy)
{
err -= dy;
x0 += sx;
}
if (e2 < dx)
{
err += dx;
y0 += sy;
}
}
}
// 检查建筑类型是否允许管线通过
private bool IsPassableBuilding(ThingDef def)
{
// 门、栅栏门等可通行的建筑不算阻挡
return def.IsDoor ||
def.passability == Traversability.PassThroughOnly ||
def.passability == Traversability.Standable;
}
public override void PostPrintOnto(SectionLayer layer)
{
base.PostPrintOnto(layer);
foreach (var building in linkedBuildings)
{
Vector3 center = (this.parent.TrueCenter() + this.parent.Graphic.DrawOffset(this.parent.Rotation) + building.TrueCenter() + building.Graphic.DrawOffset(building.Rotation)) / 2f;
center.y = AltitudeLayer.SmallWire.AltitudeFor();
Vector3 v = building.TrueCenter() - this.parent.TrueCenter();
Vector2 size = new Vector2(1f, v.MagnitudeHorizontal());
float rot = v.AngleFlat();
Printer_Plane.PrintPlane(layer, center, size, this.LineMat, rot);
}
}
}
}

View File

@@ -11,6 +11,19 @@ namespace ArachnaeSwarm
{
private CompRefuelableNutrition selfRefuelable;
private new CompProperties_NutrientProvider Props => (CompProperties_NutrientProvider)props;
private Material lineMat;
private Material LineMat
{
get
{
if (lineMat == null)
{
lineMat = MaterialPool.MatFrom(Props.lineTexturePath, ShaderDatabase.Transparent, Color.white);
}
return lineMat;
}
}
public override void PostSpawnSetup(bool respawningAfterLoad)
{
@@ -102,5 +115,20 @@ namespace ArachnaeSwarm
}
return comp.TargetFuelLevel;
}
public override void PostPrintOnto(SectionLayer layer)
{
base.PostPrintOnto(layer);
foreach (var building in LinkedBuildings)
{
if (building == null) continue;
Vector3 center = (this.parent.TrueCenter() + this.parent.Graphic.DrawOffset(this.parent.Rotation) + building.TrueCenter() + building.Graphic.DrawOffset(building.Rotation)) / 2f;
center.y = AltitudeLayer.SmallWire.AltitudeFor();
Vector3 v = building.TrueCenter() - this.parent.TrueCenter();
Vector2 size = new Vector2(1f, v.MagnitudeHorizontal());
float rot = v.AngleFlat();
Printer_Plane.PrintPlane(layer, center, size, this.LineMat, rot);
}
}
}
}

View File

@@ -5,6 +5,8 @@ namespace ArachnaeSwarm
public class CompProperties_NutrientProvider : CompProperties_Facility
{
public float maxEfficiency = 0.9f;
public string lineTexturePath = "Things/Special/Power/Wire";
public CompProperties_NutrientProvider()
{
compClass = typeof(CompNutrientProvider);

View File

@@ -0,0 +1,58 @@
using System;
using System.Linq;
using RimWorld;
using Verse;
namespace ArachnaeSwarm
{
// Properties for the new component
public class CompProperties_AutoEjector : CompProperties
{
public int checkInterval = 250; // Check roughly every 4 seconds
public float ejectAtPercent = 1.0f; // Eject when fuel reaches this percentage of max capacity (1.0 = 100%)
public Type targetComp = typeof(CompRefuelable); // The specific CompRefuelable class to target, can be overridden in XML
public CompProperties_AutoEjector()
{
this.compClass = typeof(CompAutoEjector);
}
}
// The component logic
public class CompAutoEjector : ThingComp
{
private CompProperties_AutoEjector Props => (CompProperties_AutoEjector)this.props;
private CompRefuelable refuelableComp;
public override void PostSpawnSetup(bool respawningAfterLoad)
{
base.PostSpawnSetup(respawningAfterLoad);
// Find the specific refuelable component specified in XML
this.refuelableComp = this.parent.GetComps<CompRefuelable>()
.FirstOrDefault(comp => comp.GetType() == this.Props.targetComp);
if (this.refuelableComp == null)
{
Log.Warning($"[ArachnaeSwarm] CompAutoEjector on {parent.def.defName} could not find a CompRefuelable of type '{this.Props.targetComp.FullName}' to monitor.");
}
}
public override void CompTick()
{
base.CompTick();
// Check if we have a valid refuelable comp and it's time to check
if (this.refuelableComp != null &&
this.parent.IsHashIntervalTick(this.Props.checkInterval))
{
// Check if fuel has reached or exceeded the configured percentage of the MAX capacity
if (this.refuelableComp.FuelPercentOfMax >= this.Props.ejectAtPercent)
{
// Call the public EjectFuel() method.
this.refuelableComp.EjectFuel();
}
}
}
}
}

View File

@@ -63,10 +63,7 @@ namespace ArachnaeSwarm
fuelNeeded = TargetFuelLevel - Fuel;
}
if (totalNutritionGained > 0 && Props.fuelGizmoLabel != null)
{
Messages.Message("MessageRefueled".Translate(parent.LabelShort, totalNutritionGained.ToString("0.##"), Props.fuelGizmoLabel), parent, MessageTypeDefOf.PositiveEvent);
}
// Let the job driver handle the message. This component should only handle logic.
}
public void ReceiveFuel(float amount)

View File

@@ -1,5 +1,7 @@
using RimWorld;
using Verse;
using System.Reflection;
using HarmonyLib;
namespace ArachnaeSwarm
{
@@ -17,6 +19,36 @@ namespace ArachnaeSwarm
{
public new CompProperties_RefuelableWithKey Props => (CompProperties_RefuelableWithKey)props;
public override void PostExposeData()
{
string prefix = Props.saveKeysPrefix;
if (prefix.NullOrEmpty())
{
Log.ErrorOnce($"CompRefuelableWithKey on {parent.def.defName} has a null or empty saveKeysPrefix. Defaulting to standard save.", GetHashCode());
base.PostExposeData();
return;
}
FieldInfo fuelField = AccessTools.Field(typeof(CompRefuelable), "fuel");
FieldInfo configuredTargetFuelLevelField = AccessTools.Field(typeof(CompRefuelable), "configuredTargetFuelLevel");
FieldInfo allowAutoRefuelField = AccessTools.Field(typeof(CompRefuelable), "allowAutoRefuel");
float currentFuel = (float)fuelField.GetValue(this);
float currentConfiguredLevel = (float)configuredTargetFuelLevelField.GetValue(this);
bool currentAllowAuto = (bool)allowAutoRefuelField.GetValue(this);
Scribe_Values.Look(ref currentFuel, prefix + "_fuel", 0f);
Scribe_Values.Look(ref currentConfiguredLevel, prefix + "_configuredTargetFuelLevel", -1f);
Scribe_Values.Look(ref currentAllowAuto, prefix + "_allowAutoRefuel", true);
if (Scribe.mode == LoadSaveMode.LoadingVars)
{
fuelField.SetValue(this, currentFuel);
configuredTargetFuelLevelField.SetValue(this, currentConfiguredLevel);
allowAutoRefuelField.SetValue(this, currentAllowAuto);
}
}
public new void Notify_UsedThisTick()
{
if (Props.consumeFuelOnlyWhenUsed)

View File

@@ -0,0 +1,88 @@
using System.Collections.Generic;
using RimWorld;
using Verse;
using Verse.AI;
namespace ArachnaeSwarm
{
public class JobDriver_Refuel_Nutrition : JobDriver
{
private const TargetIndex RefuelableInd = TargetIndex.A;
private const TargetIndex FuelInd = TargetIndex.B;
private const int RefuelingDuration = 240;
protected Thing Refuelable => job.GetTarget(RefuelableInd).Thing;
// --- KEY CHANGE HERE ---
// We specifically target CompRefuelableNutrition and its children.
protected CompRefuelableNutrition RefuelableComp => Refuelable.TryGetComp<CompRefuelableNutrition>();
protected Thing Fuel => job.GetTarget(FuelInd).Thing;
public override bool TryMakePreToilReservations(bool errorOnFailed)
{
if (pawn.Reserve(Refuelable, job, 1, -1, null, errorOnFailed))
{
return pawn.Reserve(Fuel, job, 1, -1, null, errorOnFailed);
}
return false;
}
protected override IEnumerable<Toil> MakeNewToils()
{
this.FailOnDespawnedNullOrForbidden(RefuelableInd);
AddEndCondition(() => !RefuelableComp.IsFull ? JobCondition.Ongoing : JobCondition.Succeeded);
AddFailCondition(() => !job.playerForced && !RefuelableComp.ShouldAutoRefuelNowIgnoringFuelPct);
AddFailCondition(() => !RefuelableComp.allowAutoRefuel && !job.playerForced);
yield return Toils_General.DoAtomic(delegate
{
job.count = RefuelableComp.GetFuelCountToFullyRefuel();
});
Toil reserveFuel = Toils_Reserve.Reserve(FuelInd);
yield return reserveFuel;
yield return Toils_Goto.GotoThing(FuelInd, PathEndMode.ClosestTouch)
.FailOnDespawnedNullOrForbidden(FuelInd)
.FailOnSomeonePhysicallyInteracting(FuelInd);
yield return Toils_Haul.StartCarryThing(FuelInd, putRemainderInQueue: false, subtractNumTakenFromJobCount: true)
.FailOnDestroyedNullOrForbidden(FuelInd);
yield return Toils_Haul.CheckForGetOpportunityDuplicate(reserveFuel, FuelInd, TargetIndex.None, takeFromValidStorage: true);
yield return Toils_Goto.GotoThing(RefuelableInd, PathEndMode.Touch);
yield return Toils_General.Wait(RefuelingDuration)
.FailOnDestroyedNullOrForbidden(FuelInd)
.FailOnDestroyedNullOrForbidden(RefuelableInd)
.FailOnCannotTouch(RefuelableInd, PathEndMode.Touch)
.WithProgressBarToilDelay(RefuelableInd);
// Use our custom Refuel toil for CompRefuelableNutrition
yield return FinalizeRefueling_Nutrition();
}
// Custom Finalize Toil that uses the specific Refuel method from CompRefuelableNutrition
public Toil FinalizeRefueling_Nutrition()
{
Toil toil = new Toil();
toil.initAction = delegate()
{
Pawn actor = toil.GetActor();
Job curJob = actor.jobs.curJob;
Thing refuelableThing = curJob.GetTarget(RefuelableInd).Thing;
CompRefuelableNutrition refuelableComp = refuelableThing.TryGetComp<CompRefuelableNutrition>();
if (actor.carryTracker.CarriedThing == null)
{
Log.Error(actor + " is not carrying anything to refuel with.");
// The correct way to end the job from within a Toil's action.
actor.jobs.EndCurrentJob(JobCondition.Incompletable);
return;
}
// Call the specific Refuel method from our custom component
refuelableComp.Refuel(new List<Thing> { actor.carryTracker.CarriedThing });
};
toil.defaultCompleteMode = ToilCompleteMode.Instant;
return toil;
}
}
}

View File

@@ -0,0 +1,70 @@
using System.Collections.Generic;
using System.Linq;
using RimWorld;
using Verse;
using Verse.AI;
namespace ArachnaeSwarm
{
public class WorkGiver_Refuel_Nutrition : WorkGiver_Scanner
{
public override ThingRequest PotentialWorkThingRequest => ThingRequest.ForGroup(ThingRequestGroup.BuildingArtificial);
public override PathEndMode PathEndMode => PathEndMode.Touch;
public override IEnumerable<Thing> PotentialWorkThingsGlobal(Pawn pawn)
{
// Find all buildings that have our special component.
return pawn.Map.listerBuildings.allBuildingsColonist.Where(b =>
b.GetComp<CompRefuelableNutrition>() != null);
}
public override bool HasJobOnThing(Pawn pawn, Thing t, bool forced = false)
{
var building = t as Building;
if (building == null) return false;
// --- KEY CHANGE HERE ---
// Target the specific nutrition component
var refuelableComp = building.GetComp<CompRefuelableNutrition>();
if (refuelableComp == null || refuelableComp.IsFull) return false;
if (!refuelableComp.ShouldAutoRefuelNow && !forced) return false;
if (building.IsForbidden(pawn) || !pawn.CanReserve(building, 1, -1, null, forced)) return false;
var fuel = FindBestFuel(pawn, refuelableComp);
if (fuel == null)
{
JobFailReason.Is("NoFuelToRefuel".Translate(refuelableComp.Props.fuelFilter.Summary));
return false;
}
return true;
}
public override Job JobOnThing(Pawn pawn, Thing t, bool forced = false)
{
var building = t as Building;
var refuelableComp = building.GetComp<CompRefuelableNutrition>();
var fuel = FindBestFuel(pawn, refuelableComp);
// Return our custom Job
return new Job(DefDatabase<JobDef>.GetNamed("ARA_Refuel_Nutrition"), building, fuel);
}
private Thing FindBestFuel(Pawn pawn, CompRefuelable refuelable)
{
var fuelFilter = refuelable.Props.fuelFilter;
return GenClosest.ClosestThingReachable(
pawn.Position,
pawn.Map,
ThingRequest.ForGroup(ThingRequestGroup.HaulableEver),
PathEndMode.ClosestTouch,
TraverseParms.For(pawn),
9999f,
thing => !thing.IsForbidden(pawn) && fuelFilter.Allows(thing) && pawn.CanReserve(thing)
);
}
}
}