This commit is contained in:
2025-09-17 14:53:26 +08:00
parent ee9ada0edb
commit 0c118ca8a6
5 changed files with 99 additions and 49 deletions

Binary file not shown.

View File

@@ -5,17 +5,17 @@
<defName>ARA_NutrientNetworkTower</defName> <defName>ARA_NutrientNetworkTower</defName>
<label>阿拉克涅营养供给塔</label> <label>阿拉克涅营养供给塔</label>
<description>一个中央营养供给设施。它可以自动为链接到的、需要营养的建筑补充燃料。它本身需要被手动填充大量的生物质。</description> <description>一个中央营养供给设施。它可以自动为链接到的、需要营养的建筑补充燃料。它本身需要被手动填充大量的生物质。</description>
<size>(3,3)</size> <size>(5,5)</size>
<graphicData> <graphicData>
<texPath>ArachnaeSwarm/Building/ARA_ResearchBench</texPath> <texPath>ArachnaeSwarm/Building/ARA_ResearchBench</texPath>
<graphicClass>Graphic_Multi</graphicClass> <graphicClass>Graphic_Multi</graphicClass>
<shaderType>CutoutComplex</shaderType> <shaderType>CutoutComplex</shaderType>
<drawSize>(3,4.5)</drawSize> <drawSize>(5,6.7)</drawSize>
</graphicData> </graphicData>
<altitudeLayer>Building</altitudeLayer> <altitudeLayer>Building</altitudeLayer>
<passability>PassThroughOnly</passability> <passability>PassThroughOnly</passability>
<pathCost>70</pathCost> <pathCost>70</pathCost>
<tickerType>Rare</tickerType> <tickerType>Normal</tickerType> <!-- 改为 Normal 以匹配 CompRefuelable 的要求 -->
<fillPercent>0.5</fillPercent> <fillPercent>0.5</fillPercent>
<statBases> <statBases>
<MaxHitPoints>300</MaxHitPoints> <MaxHitPoints>300</MaxHitPoints>
@@ -57,8 +57,6 @@
<!-- 开关 --> <!-- 开关 -->
<li Class="CompProperties_Flickable"/> <li Class="CompProperties_Flickable"/>
<!-- 接收增效器的加成 -->
<li Class="CompProperties_AffectedByFacilities"> <li Class="CompProperties_AffectedByFacilities">
<linkableFacilities> <linkableFacilities>
<li>ARA_GrowthVat</li> <li>ARA_GrowthVat</li>
@@ -370,12 +368,6 @@
<consumeFuelOnlyWhenUsed>true</consumeFuelOnlyWhenUsed> <consumeFuelOnlyWhenUsed>true</consumeFuelOnlyWhenUsed>
</li> </li>
<li Class="CompProperties_AffectedByFacilities">
<linkableFacilities>
<li>ARA_NutrientNetworkTower</li>
</linkableFacilities>
</li>
<!-- 燃料槽 2: 肉类 --> <!-- 燃料槽 2: 肉类 -->
<li Class="ArachnaeSwarm.CompProperties_RefuelableNutrition_WithKey"> <li Class="ArachnaeSwarm.CompProperties_RefuelableNutrition_WithKey">
<saveKeysPrefix>meat_vat</saveKeysPrefix> <saveKeysPrefix>meat_vat</saveKeysPrefix>
@@ -390,6 +382,12 @@
<fuelConsumptionRate>12.5</fuelConsumptionRate> <fuelConsumptionRate>12.5</fuelConsumptionRate>
<consumeFuelOnlyWhenUsed>true</consumeFuelOnlyWhenUsed> <consumeFuelOnlyWhenUsed>true</consumeFuelOnlyWhenUsed>
</li> </li>
<li Class="CompProperties_AffectedByFacilities">
<linkableFacilities>
<li>ARA_NutrientNetworkTower</li>
</linkableFacilities>
</li>
</comps> </comps>
</ThingDef> </ThingDef>
@@ -448,8 +446,8 @@
<workTableRoomRole>Laboratory</workTableRoomRole> <workTableRoomRole>Laboratory</workTableRoomRole>
</building> </building>
<constructionSkillPrerequisite>4</constructionSkillPrerequisite> <constructionSkillPrerequisite>4</constructionSkillPrerequisite>
<!-- ... 其他建筑属性 ... -->
<comps> <comps>
<!-- 1. 作为消费者,自己也需要燃料 -->
<li Class="ArachnaeSwarm.CompProperties_RefuelableNutrition"> <li Class="ArachnaeSwarm.CompProperties_RefuelableNutrition">
<fuelCapacity>100.0</fuelCapacity> <fuelCapacity>100.0</fuelCapacity>
<fuelFilter> <fuelFilter>
@@ -461,25 +459,21 @@
<showAllowAutoRefuelToggle>true</showAllowAutoRefuelToggle> <showAllowAutoRefuelToggle>true</showAllowAutoRefuelToggle>
<targetFuelLevelConfigurable>true</targetFuelLevelConfigurable> <targetFuelLevelConfigurable>true</targetFuelLevelConfigurable>
</li> </li>
<!-- 2. 作为动态增效器,为其他建筑提供加成 -->
<li Class="ArachnaeSwarm.CompProperties_GrowthVatBooster"> <li Class="ArachnaeSwarm.CompProperties_GrowthVatBooster">
<linkableBuildings> <linkableBuildings>
<li>ARA_NutrientNetworkTower</li> <li>ARA_NutrientNetworkTower</li>
</linkableBuildings>
<statOffset>
<stat>NutrientTransmissionEfficiency</stat>
<value>0.05</value>
</statOffset>
</li>
<li Class="ArachnaeSwarm.CompProperties_GrowthVatBooster">
<linkableBuildings>
<li>ARA_BioforgeIncubator</li> <li>ARA_BioforgeIncubator</li>
<li>ARA_BioforgeIncubator_Thing</li> <li>ARA_BioforgeIncubator_Thing</li>
</linkableBuildings> </linkableBuildings>
<statOffset> <statOffsets>
<stat>ARA_IncubationSpeedFactor</stat> <NutrientTransmissionEfficiency>0.05</NutrientTransmissionEfficiency>
<value>0.1</value> <ARA_IncubationSpeedFactor>0.1</ARA_IncubationSpeedFactor>
</statOffset> </statOffsets>
</li> </li>
<!-- 3. 作为消费者,接收来自供能塔的燃料 -->
<li Class="CompProperties_AffectedByFacilities"> <li Class="CompProperties_AffectedByFacilities">
<linkableFacilities> <linkableFacilities>
<li>ARA_NutrientNetworkTower</li> <li>ARA_NutrientNetworkTower</li>

View File

@@ -6,7 +6,8 @@ namespace ArachnaeSwarm
{ {
public class CompProperties_GrowthVatBooster : CompProperties_Facility public class CompProperties_GrowthVatBooster : CompProperties_Facility
{ {
public StatModifier statOffset; // 从XML读取 // We will use the base class's statOffsets field.
// public List<StatModifier> statOffsets;
public CompProperties_GrowthVatBooster() public CompProperties_GrowthVatBooster()
{ {
@@ -16,30 +17,31 @@ namespace ArachnaeSwarm
public class CompFacility_GrowthVatBooster : CompFacility public class CompFacility_GrowthVatBooster : CompFacility
{ {
private new CompProperties_GrowthVatBooster Props => (CompProperties_GrowthVatBooster)props;
private List<StatModifier> cachedStatOffsets = new List<StatModifier>();
private bool lastHadPawn = false;
private Building_NutrientVat Vat => parent as Building_NutrientVat; private Building_NutrientVat Vat => parent as Building_NutrientVat;
public override List<StatModifier> StatOffsets public override List<StatModifier> StatOffsets
{ {
get get
{ {
// Building_Enterable 使用 innerContainer 存放 Pawn // If the vat contains a pawn, return the stat offsets defined in XML.
bool hasPawn = Vat != null && Vat.innerContainer.Any; if (Vat != null && Vat.innerContainer.Any)
if (cachedStatOffsets.Count == 0 || hasPawn != lastHadPawn)
{ {
cachedStatOffsets.Clear(); return base.StatOffsets; // Returns the list from Props.statOffsets
if (hasPawn && Props.statOffset != null)
{
cachedStatOffsets.Add(Props.statOffset);
}
lastHadPawn = hasPawn;
} }
return cachedStatOffsets;
// Otherwise, return an empty list, providing no bonus.
return new List<StatModifier>();
} }
} }
public override string CompInspectStringExtra()
{
// If we are not providing any bonus, don't call the base method to avoid empty lines.
if (StatOffsets.NullOrEmpty())
{
return null;
}
return base.CompInspectStringExtra();
}
} }
} }

View File

@@ -2,6 +2,7 @@ using RimWorld;
using Verse; using Verse;
using System.Linq; using System.Linq;
using UnityEngine; using UnityEngine;
using System.Text;
namespace ArachnaeSwarm namespace ArachnaeSwarm
{ {
@@ -15,37 +16,90 @@ namespace ArachnaeSwarm
selfRefuelable = parent.GetComp<CompRefuelableNutrition>(); selfRefuelable = parent.GetComp<CompRefuelableNutrition>();
} }
public override void CompTickRare() public override string CompInspectStringExtra()
{ {
base.CompTickRare(); StringBuilder sb = new StringBuilder();
float efficiency = Mathf.Clamp(parent.GetStatValue(StatDef.Named("NutrientTransmissionEfficiency")), 0f, 0.1f);
sb.AppendLine("生物质传输效率".Translate() + ": " + efficiency.ToStringPercent());
sb.AppendLine("链接的建筑".Translate() + ":");
if (LinkedBuildings.NullOrEmpty())
{
sb.AppendLine(" (" + "None".Translate() + ")");
}
else
{
foreach (var building in LinkedBuildings)
{
var comp = (building as ThingWithComps)?.GetComp<CompRefuelableNutrition>();
if (comp != null)
{
bool needsFuel = NeedsFuel(comp);
sb.AppendLine($" - {building.LabelCap}: {comp.Fuel:F0}/{comp.Props.fuelCapacity:F0} (目标: {comp.TargetFuelLevel:F0}) -> {(needsFuel ? "" : "")}");
}
}
}
return sb.ToString().TrimEnd();
}
public override void CompTick()
{
base.CompTick();
// Log.Message($"[NutrientProvider] CompTick called for {parent.Label}."); // This will be too spammy
if (!parent.IsHashIntervalTick(250))
{
return;
}
Log.Message($"[NutrientProvider] Rare Tick Logic executing for {parent.Label}.");
if (selfRefuelable == null || !selfRefuelable.HasFuel) if (selfRefuelable == null || !selfRefuelable.HasFuel)
{ {
return; return;
} }
// 找到最需要燃料的已连接建筑
var targetBuilding = LinkedBuildings var targetBuilding = LinkedBuildings
.Select(b => new { Building = b, Comp = (b as ThingWithComps)?.GetComp<CompRefuelableNutrition>() }) .Select(b => new { Building = b, Comp = (b as ThingWithComps)?.GetComp<CompRefuelableNutrition>() })
.Where(x => x.Comp != null && x.Comp.Fuel < x.Comp.TargetFuelLevel) .Where(x => x.Comp != null && NeedsFuel(x.Comp))
.OrderBy(x => x.Comp.Fuel / x.Comp.Props.fuelCapacity) // 按燃料百分比排序 .OrderBy(x => x.Comp.Fuel / x.Comp.Props.fuelCapacity)
.FirstOrDefault(); .FirstOrDefault();
if (targetBuilding != null) if (targetBuilding != null)
{ {
var consumerComp = targetBuilding.Comp; var consumerComp = targetBuilding.Comp;
float fuelNeeded = consumerComp.TargetFuelLevel - consumerComp.Fuel; float desiredLevel = GetDesiredFuelLevel(consumerComp);
float fuelNeeded = desiredLevel - consumerComp.Fuel;
float fuelToTransfer = Mathf.Min(fuelNeeded, selfRefuelable.Fuel); float fuelToTransfer = Mathf.Min(fuelNeeded, selfRefuelable.Fuel);
// 计算效率加成,并设置上限为 10%
float efficiency = Mathf.Clamp(parent.GetStatValue(StatDef.Named("NutrientTransmissionEfficiency")), 0f, 0.1f); float efficiency = Mathf.Clamp(parent.GetStatValue(StatDef.Named("NutrientTransmissionEfficiency")), 0f, 0.1f);
float finalCost = fuelToTransfer * (1 - efficiency); float finalCost = fuelToTransfer * (1 - efficiency);
Log.Message($"[NutrientProvider] Found target: {targetBuilding.Building.Label}. Fuel: {consumerComp.Fuel:F0}/{consumerComp.Props.fuelCapacity:F0}. Needs: {fuelNeeded:F2}. Transferring: {fuelToTransfer:F2}. Final cost: {finalCost:F2}");
selfRefuelable.ConsumeFuel(finalCost); selfRefuelable.ConsumeFuel(finalCost);
consumerComp.ReceiveFuel(fuelToTransfer); consumerComp.ReceiveFuel(fuelToTransfer);
// 可以在这里添加一个 Mote 来显示传输效果
} }
} }
private bool NeedsFuel(CompRefuelableNutrition comp)
{
if (comp == null) return false;
return comp.Fuel < GetDesiredFuelLevel(comp);
}
private float GetDesiredFuelLevel(CompRefuelableNutrition comp)
{
// If target is 0 (initial default) or max (UI default), treat as "fill to full".
if (comp.TargetFuelLevel <= 0.01f || comp.TargetFuelLevel >= comp.Props.fuelCapacity * 0.99f)
{
return comp.Props.fuelCapacity;
}
// Otherwise, respect the player's custom setting.
return comp.TargetFuelLevel;
}
} }
} }