几个comps修改

This commit is contained in:
2025-10-09 12:02:11 +08:00
parent d16f1cee31
commit 64847d6e88
16 changed files with 681 additions and 77 deletions

Binary file not shown.

View File

@@ -1388,7 +1388,7 @@
<iconPath>ArachnaeSwarm/UI/Abilities/ARA_Suicide_Ability</iconPath>
<cooldownTicksRange>1</cooldownTicksRange>
<aiCanUse>true</aiCanUse>
<displayOrder>300</displayOrder>
<displayOrder>9900</displayOrder>
<displayGizmoWhileUndrafted>true</displayGizmoWhileUndrafted>
<disableGizmoWhileUndrafted>false</disableGizmoWhileUndrafted>
<warmupStartSound>AcidSpray_Warmup</warmupStartSound>
@@ -1419,7 +1419,7 @@
<cooldownTicksRange>144000</cooldownTicksRange>
<targetRequired>false</targetRequired>
<casterMustBeCapableOfViolence>false</casterMustBeCapableOfViolence>
<displayOrder>301</displayOrder>
<displayOrder>9901</displayOrder>
<verbProperties>
<verbClass>Verb_CastAbility</verbClass>
<drawAimPie>false</drawAimPie>

View File

@@ -229,6 +229,13 @@
<customLabel>温度要求</customLabel>
<showCurrentTemperature>true</showCurrentTemperature>
</li>
<li Class="ArachnaeSwarm.CompProperties_AbilityShowInteractiveThing">
<cocoonBuildingDef>ARA_Cocoon_Cloth</cocoonBuildingDef>
<customLabel>可孵化物品列表</customLabel>
<showResearchRequirements>true</showResearchRequirements>
<showNutritionCost>true</showNutritionCost>
<showIncubationTime>true</showIncubationTime>
</li>
</comps>
</AbilityDef>
<AbilityDef ParentName="ARA_CocoonSpew_Base">
@@ -245,6 +252,13 @@
<customLabel>温度要求</customLabel>
<showCurrentTemperature>true</showCurrentTemperature>
</li>
<li Class="ArachnaeSwarm.CompProperties_AbilityShowInteractiveThing">
<cocoonBuildingDef>ARA_Cocoon_Weapon</cocoonBuildingDef>
<customLabel>可孵化物品列表</customLabel>
<showResearchRequirements>true</showResearchRequirements>
<showNutritionCost>true</showNutritionCost>
<showIncubationTime>true</showIncubationTime>
</li>
</comps>
</AbilityDef>
<!-- T1 -->
@@ -262,6 +276,13 @@
<customLabel>温度要求</customLabel>
<showCurrentTemperature>true</showCurrentTemperature>
</li>
<li Class="ArachnaeSwarm.CompProperties_AbilityShowInteractiveThing">
<cocoonBuildingDef>ARA_Cocoon_Cloth_1Stage</cocoonBuildingDef>
<customLabel>可孵化物品列表</customLabel>
<showResearchRequirements>true</showResearchRequirements>
<showNutritionCost>true</showNutritionCost>
<showIncubationTime>true</showIncubationTime>
</li>
</comps>
</AbilityDef>
<AbilityDef ParentName="ARA_CocoonSpew_Base">
@@ -278,6 +299,13 @@
<customLabel>温度要求</customLabel>
<showCurrentTemperature>true</showCurrentTemperature>
</li>
<li Class="ArachnaeSwarm.CompProperties_AbilityShowInteractiveThing">
<cocoonBuildingDef>ARA_Cocoon_Weapon_1Stage</cocoonBuildingDef>
<customLabel>可孵化物品列表</customLabel>
<showResearchRequirements>true</showResearchRequirements>
<showNutritionCost>true</showNutritionCost>
<showIncubationTime>true</showIncubationTime>
</li>
</comps>
</AbilityDef>
<!-- T2 -->
@@ -295,10 +323,17 @@
<customLabel>温度要求</customLabel>
<showCurrentTemperature>true</showCurrentTemperature>
</li>
<li Class="ArachnaeSwarm.CompProperties_AbilityShowInteractiveThing">
<cocoonBuildingDef>ARA_Cocoon_Cloth_2Stage</cocoonBuildingDef>
<customLabel>可孵化物品列表</customLabel>
<showResearchRequirements>true</showResearchRequirements>
<showNutritionCost>true</showNutritionCost>
<showIncubationTime>true</showIncubationTime>
</li>
</comps>
</AbilityDef>
<AbilityDef ParentName="ARA_CocoonSpew_Base">
<defName>ARA_Cocoon_Weapon_2Stage_Ability</defName>
<defName>ARA_Cocoon_Weapon_2Stage</defName>
<label>武装器官孵化茧——等级2</label>
<description>投放一枚武装器官茧,内含可以孵化一套基础武装器官的营养和遗传物质——参阅茧的超链接,了解其能生产的所有装备的特点。</description>
<iconPath>ArachnaeSwarm/UI/Abilities/ARA_Cocoon_Weapon_2Stage</iconPath>
@@ -311,6 +346,13 @@
<customLabel>温度要求</customLabel>
<showCurrentTemperature>true</showCurrentTemperature>
</li>
<li Class="ArachnaeSwarm.CompProperties_AbilityShowInteractiveThing">
<cocoonBuildingDef>ARA_Cocoon_Weapon_2Stage</cocoonBuildingDef>
<customLabel>可孵化物品列表</customLabel>
<showResearchRequirements>true</showResearchRequirements>
<showNutritionCost>true</showNutritionCost>
<showIncubationTime>true</showIncubationTime>
</li>
</comps>
</AbilityDef>

View File

@@ -319,7 +319,7 @@
<li Class="HediffCompProperties_GiveAbility">
<abilityDefs>
<li>ARA_Cocoon_Cloth_2Stage</li>
<li>ARA_Cocoon_Weapon_2Stage_Ability</li>
<li>ARA_Cocoon_Weapon_2Stage</li>
</abilityDefs>
</li>
</comps>

View File

@@ -26,7 +26,7 @@
<takeFootprints>True</takeFootprints>
<avoidWander>false</avoidWander>
<statBases>
<Beauty>-6</Beauty>
<Beauty>0</Beauty>
<CleaningTimeFactor>0.25</CleaningTimeFactor>
<Cleanliness>0</Cleanliness>
<Flammability>0</Flammability>
@@ -75,7 +75,7 @@
<li>Substructure</li>
</affordances>
<statBases>
<Beauty>-6</Beauty>
<Beauty>0</Beauty>
<CleaningTimeFactor>0.25</CleaningTimeFactor>
<Cleanliness>0</Cleanliness>
<Flammability>0</Flammability>
@@ -122,7 +122,7 @@
<staticSunShadowHeight>0</staticSunShadowHeight>
<statBases>
<MarketValue>0</MarketValue>
<Beauty>-6</Beauty>
<Beauty>0</Beauty>
<MaxHitPoints>1000</MaxHitPoints>
<WorkToBuild>1500</WorkToBuild>
<Flammability>0</Flammability>
@@ -198,7 +198,7 @@
<MaxHitPoints>1200</MaxHitPoints>
<Flammability>0</Flammability>
<WorkToBuild>1000</WorkToBuild>
<Beauty>-10</Beauty>
<Beauty>0</Beauty>
<DoorOpenSpeed>2</DoorOpenSpeed>
</statBases>
<costStuffCount>10</costStuffCount>
@@ -687,7 +687,7 @@
<WorkToBuild>750</WorkToBuild>
<Mass>5</Mass>
<Flammability>1.0</Flammability>
<Beauty>-5</Beauty>
<Beauty>0</Beauty>
<Comfort>0.70</Comfort>
<StyleDominance MayRequire="Ludeon.RimWorld.Ideology">5</StyleDominance>
</statBases>
@@ -760,7 +760,7 @@
<WorkToBuild>750</WorkToBuild>
<Mass>10</Mass>
<Flammability>1.0</Flammability>
<Beauty>5</Beauty>
<Beauty>1</Beauty>
<StyleDominance MayRequire="Ludeon.RimWorld.Ideology">10</StyleDominance>
</statBases>
<researchPrerequisites>

View File

@@ -727,7 +727,7 @@
<li>InsectJelly</li>
</disallowedThingDefs>
</fuelFilter>
<fuelCapacity>50</fuelCapacity>
<fuelCapacity>20</fuelCapacity>
<fuelConsumptionRate>0</fuelConsumptionRate>
<consumeFuelOnlyWhenUsed>true</consumeFuelOnlyWhenUsed>
@@ -757,7 +757,7 @@
</li>
<li Class="ArachnaeSwarm.CompProperties_NutritionToFuelConverter">
<checkInterval>200</checkInterval>
<nutritionCost>1</nutritionCost>
<nutritionCost>0.25</nutritionCost>
<workAmount>2000</workAmount>
<fuelAmount>1</fuelAmount>
</li>

View File

@@ -1,6 +1,5 @@
<?xml version="1.0" encoding="utf-8"?>
<LanguageData>
<EstimatedQuality>预计品质</EstimatedQuality>
<QualityScore>品质评分</QualityScore>
<TemperaturePenalty>温度惩罚</TemperaturePenalty>
@@ -8,17 +7,20 @@
<SafeTemperatureRange>安全范围</SafeTemperatureRange>
<CannotStartProduction>无法开始生产</CannotStartProduction>
<NoFuel>无燃料</NoFuel>
<StartProduction>开始生产 {0}</StartProduction>
<StartProduction>开始生产 {0}{1} 营养)</StartProduction>
<CommandCancelProduction>取消生产</CommandCancelProduction>
<CommandCancelProductionDesc>停止当前的生产流程。</CommandCancelProductionDesc>
<Producing>正在生产 {0}</Producing>
<TimeLeft>剩余时间</TimeLeft>
<ProjectedQuality>预计品质</ProjectedQuality>
<NotProducing>未在生产</NotProducing>
<ARA_NeedArachnaeToStartIncubation>未孵化,需要阿拉克涅工艺种交互</ARA_NeedArachnaeToStartIncubation>
<ARA_NeedSpecificArachnaeToStartIncubation>未孵化,需要 {0} 交互({1}</ARA_NeedSpecificArachnaeToStartIncubation>
<ARA_AnyArachnaeRace>任何阿拉克涅虫族</ARA_AnyArachnaeRace>
<ARA_ItemsAvailable>{0} 个物品可用</ARA_ItemsAvailable>
<!-- Added for CompQueuedPawnSpawner -->
<NutritionNeeded>所需营养</NutritionNeeded>
<ARA_Incubate>孵化 {0}</ARA_Incubate>
<ARA_CocoonCooldown>茧正在稳定中:{0}</ARA_CocoonCooldown>
</LanguageData>

View File

@@ -3,8 +3,16 @@
"WorkspaceRootPath": "D:\\SteamLibrary\\steamapps\\common\\RimWorld\\Mods\\ArachnaeSwarm\\Source\\ArachnaeSwarm\\",
"Documents": [
{
"AbsoluteMoniker": "D:0:0:{EAE0DB6B-E282-C812-7F5A-6D13E9D24581}|ArachnaeSwarm.csproj|d:\\steamlibrary\\steamapps\\common\\rimworld\\mods\\arachnaeswarm\\source\\arachnaeswarm\\abilities\\ara_queenability\\compabilityeffect_needcost.cs||{A6C744A8-0E4A-4FC6-886A-064283054674}",
"RelativeMoniker": "D:0:0:{EAE0DB6B-E282-C812-7F5A-6D13E9D24581}|ArachnaeSwarm.csproj|solutionrelative:abilities\\ara_queenability\\compabilityeffect_needcost.cs||{A6C744A8-0E4A-4FC6-886A-064283054674}"
"AbsoluteMoniker": "D:0:0:{EAE0DB6B-E282-C812-7F5A-6D13E9D24581}|ArachnaeSwarm.csproj|d:\\steamlibrary\\steamapps\\common\\rimworld\\mods\\arachnaeswarm\\source\\arachnaeswarm\\building_comps\\ara_compinteractiveproducer\\compinteractiveproducer.cs||{A6C744A8-0E4A-4FC6-886A-064283054674}",
"RelativeMoniker": "D:0:0:{EAE0DB6B-E282-C812-7F5A-6D13E9D24581}|ArachnaeSwarm.csproj|solutionrelative:building_comps\\ara_compinteractiveproducer\\compinteractiveproducer.cs||{A6C744A8-0E4A-4FC6-886A-064283054674}"
},
{
"AbsoluteMoniker": "D:0:0:{EAE0DB6B-E282-C812-7F5A-6D13E9D24581}|ArachnaeSwarm.csproj|D:\\SteamLibrary\\steamapps\\common\\RimWorld\\Mods\\ArachnaeSwarm\\Source\\ArachnaeSwarm\\hediffs\\moharhediffs\\hediffcomp_spawner.cs||{A6C744A8-0E4A-4FC6-886A-064283054674}",
"RelativeMoniker": "D:0:0:{EAE0DB6B-E282-C812-7F5A-6D13E9D24581}|ArachnaeSwarm.csproj|solutionrelative:hediffs\\moharhediffs\\hediffcomp_spawner.cs||{A6C744A8-0E4A-4FC6-886A-064283054674}"
},
{
"AbsoluteMoniker": "D:0:0:{EAE0DB6B-E282-C812-7F5A-6D13E9D24581}|ArachnaeSwarm.csproj|D:\\SteamLibrary\\steamapps\\common\\RimWorld\\Mods\\ArachnaeSwarm\\Source\\ArachnaeSwarm\\abilities\\ara_showtemperaturerange\\compabilityeffect_abilityshowtemperaturerange.cs||{A6C744A8-0E4A-4FC6-886A-064283054674}",
"RelativeMoniker": "D:0:0:{EAE0DB6B-E282-C812-7F5A-6D13E9D24581}|ArachnaeSwarm.csproj|solutionrelative:abilities\\ara_showtemperaturerange\\compabilityeffect_abilityshowtemperaturerange.cs||{A6C744A8-0E4A-4FC6-886A-064283054674}"
}
],
"DocumentGroupContainers": [
@@ -23,14 +31,40 @@
{
"$type": "Document",
"DocumentIndex": 0,
"Title": "CompAbilityEffect_NeedCost.cs",
"DocumentMoniker": "D:\\SteamLibrary\\steamapps\\common\\RimWorld\\Mods\\ArachnaeSwarm\\Source\\ArachnaeSwarm\\Abilities\\ARA_QueenAbility\\CompAbilityEffect_NeedCost.cs",
"RelativeDocumentMoniker": "Abilities\\ARA_QueenAbility\\CompAbilityEffect_NeedCost.cs",
"ToolTip": "D:\\SteamLibrary\\steamapps\\common\\RimWorld\\Mods\\ArachnaeSwarm\\Source\\ArachnaeSwarm\\Abilities\\ARA_QueenAbility\\CompAbilityEffect_NeedCost.cs",
"RelativeToolTip": "Abilities\\ARA_QueenAbility\\CompAbilityEffect_NeedCost.cs",
"ViewState": "AgIAACUAAAAAAAAAAAAAAEgAAAAAAAAAAAAAAA==",
"Title": "CompInteractiveProducer.cs",
"DocumentMoniker": "D:\\SteamLibrary\\steamapps\\common\\RimWorld\\Mods\\ArachnaeSwarm\\Source\\ArachnaeSwarm\\Building_Comps\\ARA_CompInteractiveProducer\\CompInteractiveProducer.cs",
"RelativeDocumentMoniker": "Building_Comps\\ARA_CompInteractiveProducer\\CompInteractiveProducer.cs",
"ToolTip": "D:\\SteamLibrary\\steamapps\\common\\RimWorld\\Mods\\ArachnaeSwarm\\Source\\ArachnaeSwarm\\Building_Comps\\ARA_CompInteractiveProducer\\CompInteractiveProducer.cs",
"RelativeToolTip": "Building_Comps\\ARA_CompInteractiveProducer\\CompInteractiveProducer.cs",
"ViewState": "AgIAAEgAAAAAAAAAAAA4wDcAAABHAAAAAAAAAA==",
"Icon": "ae27a6b0-e345-4288-96df-5eaf394ee369.000738|",
"WhenOpened": "2025-10-08T08:23:27.533Z",
"WhenOpened": "2025-10-09T03:57:32.708Z",
"EditorCaption": ""
},
{
"$type": "Document",
"DocumentIndex": 1,
"Title": "HediffComp_Spawner.cs",
"DocumentMoniker": "D:\\SteamLibrary\\steamapps\\common\\RimWorld\\Mods\\ArachnaeSwarm\\Source\\ArachnaeSwarm\\Hediffs\\MoharHediffs\\HediffComp_Spawner.cs",
"RelativeDocumentMoniker": "Hediffs\\MoharHediffs\\HediffComp_Spawner.cs",
"ToolTip": "D:\\SteamLibrary\\steamapps\\common\\RimWorld\\Mods\\ArachnaeSwarm\\Source\\ArachnaeSwarm\\Hediffs\\MoharHediffs\\HediffComp_Spawner.cs",
"RelativeToolTip": "Hediffs\\MoharHediffs\\HediffComp_Spawner.cs",
"ViewState": "AgIAAAQAAAAAAAAAAAAswBIAAAAAAAAAAAAAAA==",
"Icon": "ae27a6b0-e345-4288-96df-5eaf394ee369.000738|",
"WhenOpened": "2025-10-09T03:45:03.599Z",
"EditorCaption": ""
},
{
"$type": "Document",
"DocumentIndex": 2,
"Title": "CompAbilityEffect_AbilityShowTemperatureRange.cs",
"DocumentMoniker": "D:\\SteamLibrary\\steamapps\\common\\RimWorld\\Mods\\ArachnaeSwarm\\Source\\ArachnaeSwarm\\Abilities\\ARA_ShowTemperatureRange\\CompAbilityEffect_AbilityShowTemperatureRange.cs",
"RelativeDocumentMoniker": "Abilities\\ARA_ShowTemperatureRange\\CompAbilityEffect_AbilityShowTemperatureRange.cs",
"ToolTip": "D:\\SteamLibrary\\steamapps\\common\\RimWorld\\Mods\\ArachnaeSwarm\\Source\\ArachnaeSwarm\\Abilities\\ARA_ShowTemperatureRange\\CompAbilityEffect_AbilityShowTemperatureRange.cs",
"RelativeToolTip": "Abilities\\ARA_ShowTemperatureRange\\CompAbilityEffect_AbilityShowTemperatureRange.cs",
"ViewState": "AgIAAC8AAAAAAAAAAAAUwAYAAAAXAAAAAAAAAA==",
"Icon": "ae27a6b0-e345-4288-96df-5eaf394ee369.000738|",
"WhenOpened": "2025-10-09T02:18:27.532Z",
"EditorCaption": ""
}
]

View File

@@ -0,0 +1,214 @@
using RimWorld;
using Verse;
using System.Text;
using System.Collections.Generic;
using System.Linq;
using UnityEngine;
namespace ArachnaeSwarm
{
public class CompAbilityEffect_ShowInteractiveThing : CompAbilityEffect
{
public new CompProperties_AbilityShowInteractiveThing Props => (CompProperties_AbilityShowInteractiveThing)props;
public override void Apply(LocalTargetInfo target, LocalTargetInfo dest)
{
// 这个组件只用于显示信息,不执行实际效果
}
public override bool Valid(LocalTargetInfo target, bool throwMessages = false)
{
return true;
}
// 重写工具提示方法,显示可孵化的物品列表
public override string ExtraTooltipPart()
{
StringBuilder stringBuilder = new StringBuilder();
// 获取所有可孵化的物品
var incubatableItems = GetIncubatableItems();
if (incubatableItems.Count == 0)
{
return null; // 没有可显示的内容
}
stringBuilder.AppendLine(Props.customLabel + ":");
stringBuilder.AppendLine();
// 显示每个可孵化的物品
foreach (var item in incubatableItems)
{
string entryText = $" • {item.thingDef.LabelCap}";
// 显示营养需求
if (Props.showNutritionCost)
{
float nutritionCost = GetIncubationCost(item.thingDef);
entryText += $" ({nutritionCost.ToString("F1")} 营养)";
}
// 显示孵化时间
if (Props.showIncubationTime)
{
int incubationTicks = GetIncubationTimeTicks(item.thingDef);
entryText += $" [{incubationTicks.ToStringTicksToPeriod()}]";
}
// 显示科技需求
if (Props.showResearchRequirements && item.requiredResearch != null)
{
bool researched = item.requiredResearch.IsFinished;
string researchStatus = researched ? "✓" : "✗";
entryText += $" <color={(researched ? "green" : "red")}>[{researchStatus}{item.requiredResearch.LabelCap}]</color>";
}
stringBuilder.AppendLine(entryText);
}
return stringBuilder.ToString().TrimEndNewlines();
}
// 获取所有可孵化的物品(基于 CompExtraIncubationInfo
private List<ProcessDef> GetIncubatableItems()
{
var result = new List<ProcessDef>();
if (Props.cocoonBuildingDef == null)
{
Log.Error("CompAbilityEffect_ShowInteractiveThing: cocoonBuildingDef is null");
return result;
}
// 扫描所有定义了 CompExtraIncubationInfo 的物品
foreach (ThingDef thingDef in DefDatabase<ThingDef>.AllDefs)
{
var incubationCompProps = thingDef.GetCompProperties<CompProperties_ExtraIncubationInfo>();
if (incubationCompProps != null)
{
bool isMatch = false;
if (!incubationCompProps.cocoonDefs.NullOrEmpty())
{
isMatch = incubationCompProps.cocoonDefs.Contains(Props.cocoonBuildingDef);
}
else if (incubationCompProps.cocoonDef != null)
{
isMatch = incubationCompProps.cocoonDef == Props.cocoonBuildingDef;
}
if (isMatch)
{
// 获取研究前提
ResearchProjectDef researchPrerequisite = GetResearchPrerequisite(thingDef);
// 创建 ProcessDef与 CompInteractiveProducer 中的结构一致)
ProcessDef process = new ProcessDef
{
thingDef = thingDef,
productionTicks = GetIncubationTimeTicks(thingDef),
totalNutritionNeeded = GetIncubationCost(thingDef),
requiredResearch = researchPrerequisite
};
result.Add(process);
}
}
}
// 按物品名称排序
result.SortBy(p => p.thingDef.label);
return result;
}
// 获取研究前提(与 CompInteractiveProducer 中的逻辑一致)
private ResearchProjectDef GetResearchPrerequisite(ThingDef thingDef)
{
ResearchProjectDef researchPrerequisite = null;
// 方法1从 recipeMaker.researchPrerequisite 获取
if (thingDef.recipeMaker?.researchPrerequisite != null)
{
researchPrerequisite = thingDef.recipeMaker.researchPrerequisite;
}
// 方法2从 recipeMaker.researchPrerequisites 获取第一个
else if (thingDef.recipeMaker?.researchPrerequisites?.Count > 0)
{
researchPrerequisite = thingDef.recipeMaker.researchPrerequisites[0];
}
// 方法3从 thingDef.researchPrerequisites 获取(备用)
else if (thingDef.researchPrerequisites?.Count > 0)
{
researchPrerequisite = thingDef.researchPrerequisites[0];
}
return researchPrerequisite;
}
// 获取孵化时间(与 CompInteractiveProducer 中的逻辑一致)
private int GetIncubationTimeTicks(ThingDef thingDef)
{
StatDef incubationTimeStat = DefDatabase<StatDef>.GetNamedSilentFail("ARA_IncubationTime");
if (incubationTimeStat != null && thingDef.statBases != null)
{
var statValue = thingDef.statBases.FirstOrDefault(s => s.stat == incubationTimeStat);
if (statValue != null)
{
// ARA_IncubationTime 是以天为单位,转换为 ticks (1天 = 60000 ticks)
return Mathf.RoundToInt(statValue.value * 60000f);
}
}
// 默认值1 天
return 60000;
}
// 获取孵化所需营养(与 CompInteractiveProducer 中的逻辑一致)
private float GetIncubationCost(ThingDef thingDef)
{
StatDef incubationCostStat = DefDatabase<StatDef>.GetNamedSilentFail("ARA_IncubationCost");
if (incubationCostStat != null && thingDef.statBases != null)
{
var statValue = thingDef.statBases.FirstOrDefault(s => s.stat == incubationCostStat);
if (statValue != null)
{
return statValue.value;
}
}
// 默认值10 营养
return 10f;
}
// 获取所有可孵化的物品种类(用于其他用途)
public List<ThingDef> GetIncubatableThingDefs()
{
var incubatableItems = GetIncubatableItems();
var result = new List<ThingDef>();
foreach (var item in incubatableItems)
{
if (item.thingDef != null && (item.requiredResearch == null || item.requiredResearch.IsFinished))
{
result.Add(item.thingDef);
}
}
return result;
}
// 检查特定物品是否可孵化
public bool CanIncubateThingDef(ThingDef thingDef)
{
var incubatableItems = GetIncubatableItems();
foreach (var item in incubatableItems)
{
if (item.thingDef == thingDef &&
(item.requiredResearch == null || item.requiredResearch.IsFinished))
{
return true;
}
}
return false;
}
}
}

View File

@@ -0,0 +1,19 @@
using RimWorld;
using Verse;
namespace ArachnaeSwarm
{
public class CompProperties_AbilityShowInteractiveThing : CompProperties_AbilityEffect
{
public ThingDef cocoonBuildingDef; // 指向的茧建筑定义
public string customLabel = "可孵化物品"; // 自定义标签
public bool showResearchRequirements = true; // 是否显示科技需求
public bool showNutritionCost = true; // 是否显示营养需求
public bool showIncubationTime = true; // 是否显示孵化时间
public CompProperties_AbilityShowInteractiveThing()
{
this.compClass = typeof(CompAbilityEffect_ShowInteractiveThing);
}
}
}

View File

@@ -2,6 +2,7 @@ using RimWorld;
using Verse;
using System.Text;
using System.Collections.Generic;
using UnityEngine;
namespace ArachnaeSwarm
{
@@ -19,7 +20,7 @@ namespace ArachnaeSwarm
return true;
}
// Override tooltip method to display temperature range information
// Override tooltip method to display temperature range information with progress bar
public override string ExtraTooltipPart()
{
StringBuilder stringBuilder = new StringBuilder();
@@ -31,23 +32,84 @@ namespace ArachnaeSwarm
return null; // No content to display
}
// 第一行:安全温度范围
stringBuilder.AppendLine(Props.customLabel.Translate() + ":");
stringBuilder.AppendLine();
// Display temperature range
stringBuilder.AppendLine(" " + "AbilityCheckSafeTemperatureRange".Translate(tempCompProps.minSafeTemperature, tempCompProps.maxSafeTemperature));
// 第二行:当前温度
float currentTemp = parent.pawn != null && parent.pawn.Map != null ?
GenTemperature.GetTemperatureForCell(parent.pawn.Position, parent.pawn.Map) : 0f;
stringBuilder.AppendLine($" 当前温度: {currentTemp:0.0}°C");
// Display current temperature (if enabled)
if (Props.showCurrentTemperature && parent.pawn != null && parent.pawn.Map != null)
{
float currentTemp = GenTemperature.GetTemperatureForCell(parent.pawn.Position, parent.pawn.Map);
// 第三行和第四行:温度标签和进度条
var (tempLabels, progressBar) = GetTemperatureProgressBar(currentTemp, tempCompProps.minSafeTemperature, tempCompProps.maxSafeTemperature);
stringBuilder.AppendLine(tempLabels);
stringBuilder.AppendLine(progressBar);
// 第五行:温度状态
string tempStatus = GetTemperatureStatus(currentTemp, tempCompProps.minSafeTemperature, tempCompProps.maxSafeTemperature);
stringBuilder.AppendLine();
stringBuilder.AppendLine("AbilityCheckCurrentTemperature".Translate(currentTemp.ToString("F1"), tempStatus));
}
string statusColor = GetTemperatureStatusColor(currentTemp, tempCompProps.minSafeTemperature, tempCompProps.maxSafeTemperature);
stringBuilder.AppendLine($" <color={statusColor}>{tempStatus}</color>");
return stringBuilder.ToString().TrimEndNewlines();
}
// Get temperature progress bar display
private (string tempLabels, string progressBar) GetTemperatureProgressBar(float currentTemp, float minSafe, float maxSafe)
{
const int barLength = 20;
// Use only the safe temperature range for display
float displayMin = minSafe;
float displayMax = maxSafe;
// Calculate current position in the bar
float normalizedPos = Mathf.Clamp01((currentTemp - displayMin) / (displayMax - displayMin));
int currentPos = Mathf.RoundToInt(normalizedPos * (barLength - 1));
// Build temperature labels line (第三行)
string minTempLabel = $"{displayMin:0}°C";
string maxTempLabel = $"{displayMax:0}°C";
// Calculate spacing to align labels with progress bar
int totalSpaces = barLength + 2 - minTempLabel.Length - maxTempLabel.Length; // +2 for brackets
string spacing = new string(' ', Mathf.Max(1, totalSpaces));
string tempLabels = $" {minTempLabel}{spacing}{maxTempLabel}";
// Build progress bar (第四行)
StringBuilder bar = new StringBuilder();
bar.Append(" [");
for (int i = 0; i < barLength; i++)
{
if (i == currentPos)
{
// Current temperature indicator - color based on position
string markerColor = GetMarkerColor(currentTemp, minSafe, maxSafe);
bar.Append($"<color={markerColor}>|</color>");
}
else
{
// Regular segment - always green since we're only showing the safe range
bar.Append("<color=green>─</color>");
}
}
bar.Append("]");
return (tempLabels, bar.ToString());
}
// Get color for the current temperature marker
private string GetMarkerColor(float currentTemp, float minSafe, float maxSafe)
{
if (currentTemp < minSafe)
return "blue"; // Below minimum - blue
else if (currentTemp > maxSafe)
return "red"; // Above maximum - red
else
return "green"; // Within safe range - green
}
// Get temperature status description
private string GetTemperatureStatus(float currentTemp, float minSafe, float maxSafe)
{
@@ -59,6 +121,17 @@ namespace ArachnaeSwarm
return "AbilityCheckTemperatureSafe".Translate();
}
// Get color for temperature status
private string GetTemperatureStatusColor(float currentTemp, float minSafe, float maxSafe)
{
if (currentTemp < minSafe)
return "blue";
else if (currentTemp > maxSafe)
return "red";
else
return "green";
}
// Get temperature control component from building definition
private CompProperties_TemperatureRuinableDamage GetTemperatureCompFromBuilding()
{

View File

@@ -95,6 +95,8 @@
<Compile Include="Abilities\ARA_QueenAbility\CompAbilityEffect_ResearchPrereq.cs" />
<Compile Include="Abilities\ARA_QueenAbility\CompAbilityEffect_SprayLiquidMulti.cs" />
<Compile Include="Abilities\ARA_QueenAbility\CompProperties_AbilitySprayLiquidMulti.cs" />
<Compile Include="Abilities\ARA_ShowInteractiveThing\CompAbilityEffect_ShowInteractiveThing.cs" />
<Compile Include="Abilities\ARA_ShowInteractiveThing\CompProperties_AbilityShowInteractiveThing.cs" />
<Compile Include="Abilities\ARA_ShowSpawnablePawnsList\CompAbilityEffect_AbilityShowSpawnablePawns.cs" />
<Compile Include="Abilities\ARA_ShowSpawnablePawnsList\CompProperties_AbilityShowSpawnablePawns.cs" />
<Compile Include="Buildings\Building_TurretGunHasSpeed.cs" />

View File

@@ -35,13 +35,28 @@ namespace ArachnaeSwarm
private int ticksUnderOptimalConditions;
private float temperaturePenaltyPercent;
private List<ProcessDef> _cachedProcesses;
private int spawnTick = -1; // 新增:记录生成时间
private const int COOLDOWN_TICKS = 120; // 新增120 tick冷却时间
private CompRefuelableNutrition _fuelComp;
private static readonly Texture2D CancelIcon = ContentFinder<Texture2D>.Get("UI/Designators/Cancel");
public bool InProduction => _selectedProcess != null;
public bool InCooldown => spawnTick > 0 && Find.TickManager.TicksGame < spawnTick + COOLDOWN_TICKS; // 新增:冷却状态检查
public CompProperties_InteractiveProducer Props => (CompProperties_InteractiveProducer)props;
// 生产进度0-1
public float ProductionProgress
{
get
{
if (!InProduction || productionUntilTick <= 0) return 0f;
int totalTicks = _selectedProcess.productionTicks;
int elapsedTicks = totalTicks - (productionUntilTick - Find.TickManager.TicksGame);
return Mathf.Clamp01((float)elapsedTicks / totalTicks);
}
}
// 自动生成的 ProcessDef 列表
public List<ProcessDef> Processes
{
@@ -69,6 +84,12 @@ namespace ArachnaeSwarm
base.PostSpawnSetup(respawningAfterLoad);
_fuelComp = parent.GetComp<CompRefuelableNutrition>();
BuildProcessList(); // 确保进程列表在生成时构建
// 新增:记录生成时间(仅在首次生成时,不是加载后)
if (!respawningAfterLoad)
{
spawnTick = Find.TickManager.TicksGame;
}
}
public override void PostExposeData()
@@ -82,6 +103,7 @@ namespace ArachnaeSwarm
Scribe_Values.Look(ref productionUntilTick, "productionUntilTick", -1);
Scribe_Values.Look(ref ticksUnderOptimalConditions, "ticksUnderOptimalConditions", 0);
Scribe_Values.Look(ref temperaturePenaltyPercent, "temperaturePenaltyPercent", 0f);
Scribe_Values.Look(ref spawnTick, "spawnTick", -1); // 新增:序列化生成时间
// 加载时重建 selectedProcess
if (Scribe.mode == LoadSaveMode.LoadingVars && selectedProcessThingDef != null)
@@ -264,27 +286,38 @@ namespace ArachnaeSwarm
public override IEnumerable<FloatMenuOption> CompFloatMenuOptions(Pawn selPawn)
{
// 新增:冷却期内不提供任何交互选项
if (InCooldown)
{
int remainingTicks = (spawnTick + COOLDOWN_TICKS) - Find.TickManager.TicksGame;
yield return new FloatMenuOption($"ARA_CocoonCooldown".Translate(remainingTicks.ToStringTicksToPeriod()), null);
yield break;
}
if (InProduction || !selPawn.CanReach(parent, PathEndMode.InteractionCell, Danger.Deadly)) yield break;
if (Props.whitelist != null && !Props.whitelist.Contains(selPawn.kindDef)) yield break;
if (FuelComp == null) yield break;
if (!FuelComp.HasFuel || FuelComp.NutritionStored < Props.minNutritionToStart)
{
yield return new FloatMenuOption("CannotStartProduction".Translate() + ": " + "NoFuel".Translate(), null);
yield break;
}
// 使用自动生成的 Processes 列表
foreach (var process in Processes)
{
// 获取营养需求 - 直接从 ProcessDef 中读取
float nutritionCost = process.totalNutritionNeeded;
if (process.requiredResearch != null && !process.requiredResearch.IsFinished)
{
string disabledText = "StartProduction".Translate(process.thingDef.label) + " (" + "Requires".Translate() + ": " + process.requiredResearch.label + ")";
// 修改:在未研究完成的情况下也显示营养需求
string disabledText = "StartProduction".Translate(process.thingDef.label, nutritionCost.ToString("F1")) +
" (" + "Requires".Translate() + ": " + process.requiredResearch.label + ")";
yield return new FloatMenuOption(disabledText, null);
}
else
{
yield return new FloatMenuOption("StartProduction".Translate(process.thingDef.label), () =>
// 修改:使用新的翻译键显示营养需求
yield return new FloatMenuOption("StartProduction".Translate(process.thingDef.label, nutritionCost.ToString("F1")), () =>
{
this._selectedProcess = process;
Job job = JobMaker.MakeJob(DefDatabase<JobDef>.GetNamed("ARA_StartInteractiveProduction"), parent);
@@ -296,6 +329,13 @@ namespace ArachnaeSwarm
public void StartProduction()
{
// 新增:冷却期内不允许开始生产
if (InCooldown)
{
Log.Warning("Attempted to start production during cooldown period.");
return;
}
if (_selectedProcess == null) return;
productionUntilTick = Find.TickManager.TicksGame + _selectedProcess.productionTicks;
ticksUnderOptimalConditions = 0;
@@ -377,38 +417,173 @@ namespace ArachnaeSwarm
temperaturePenaltyPercent = 0f;
}
// 新增:绘制进度条的方法
private string GetProgressBar(float progress, int barLength = 20)
{
int filledLength = Mathf.RoundToInt(progress * barLength);
int emptyLength = barLength - filledLength;
StringBuilder bar = new StringBuilder();
bar.Append("[");
for (int i = 0; i < filledLength; i++)
{
bar.Append("=");
}
for (int i = 0; i < emptyLength; i++)
{
bar.Append("-");
}
bar.Append("]");
return bar.ToString();
}
// 新增:绘制质量进度条的方法
private string GetQualityProgressBar(float baseScore, float penalty, int barLength = 20)
{
int baseLength = Mathf.RoundToInt(baseScore * barLength);
int penaltyLength = Mathf.RoundToInt(penalty * barLength);
int actualLength = Mathf.Max(0, baseLength - penaltyLength);
int penaltyStart = actualLength;
int emptyLength = barLength - baseLength;
StringBuilder bar = new StringBuilder();
bar.Append("[");
// 实际质量部分(绿色)
for (int i = 0; i < actualLength; i++)
{
bar.Append("=");
}
// 温度惩罚部分(黄色)
for (int i = penaltyStart; i < baseLength; i++)
{
bar.Append("-");
}
// 剩余部分(灰色)
for (int i = baseLength; i < barLength; i++)
{
bar.Append(".");
}
bar.Append("]");
return bar.ToString();
}
public override string CompInspectStringExtra()
{
// 新增:显示冷却信息
if (InCooldown)
{
int remainingTicks = (spawnTick + COOLDOWN_TICKS) - Find.TickManager.TicksGame;
return "ARA_CocoonCooldown".Translate(remainingTicks.ToStringTicksToPeriod());
}
if (InProduction)
{
StringBuilder sb = new StringBuilder();
sb.AppendLine("Producing".Translate(this._selectedProcess.thingDef.label));
// 生产进度条
float progress = ProductionProgress;
int remainingTicks = productionUntilTick - Find.TickManager.TicksGame;
sb.AppendLine("Progress".Translate() + ": " + GetProgressBar(progress) + " " + progress.ToStringPercent("F0"));
sb.AppendLine("TimeLeft".Translate() + ": " + remainingTicks.ToStringTicksToPeriod());
// 质量进度条
var qualityDetails = GetEstimatedQualityDetails();
sb.AppendLine("EstimatedQuality".Translate() + ": " + qualityDetails.quality.GetLabel());
sb.AppendLine($" {"QualityScore".Translate()}: {qualityDetails.baseScore.ToStringPercent("F0")}");
sb.AppendLine($" {"TemperaturePenalty".Translate()}: -{qualityDetails.penalty.ToStringPercent("F0")}");
sb.AppendLine("QualityProgress".Translate() + ": " +
GetQualityProgressBar(qualityDetails.baseScore, qualityDetails.penalty) + " " +
(qualityDetails.baseScore - qualityDetails.penalty).ToStringPercent("F0"));
// 温度信息
string tempStr = "CurrentTemperature".Translate(parent.AmbientTemperature.ToStringTemperature("F0"));
tempStr += $" ({"SafeTemperatureRange".Translate()}: {Props.minSafeTemperature.ToStringTemperature("F0")} ~ {Props.maxSafeTemperature.ToStringTemperature("F0")})";
sb.AppendLine(tempStr);
return sb.ToString().TrimEnd();
}
if (!InProduction)
{
// 显示可生产的物品数量
int availableProcesses = Processes.Count(p => p.requiredResearch == null || p.requiredResearch.IsFinished);
return "ARA_NeedArachnaeToStartIncubation".Translate() + $" ({availableProcesses} items available)";
// 构建可交互的种族名称列表
string allowedRaces = "";
if (Props.whitelist != null && Props.whitelist.Count > 0)
{
// 获取所有可交互种族的显示名称
var raceNames = Props.whitelist.Select(pkd => pkd.LabelCap).Distinct();
allowedRaces = string.Join(", ", raceNames);
}
else
{
allowedRaces = "ARA_AnyArachnaeRace".Translate(); // 如果没有白名单,显示"任何阿拉克涅虫族"
}
// 使用新的翻译键,包含种族信息和物品数量
return "ARA_NeedSpecificArachnaeToStartIncubation".Translate(allowedRaces, availableProcesses);
}
return null;
}
// 新增:在物体上方绘制进度条
public override void PostDraw()
{
base.PostDraw();
if (InProduction)
{
// 在物体上方绘制生产进度条
Vector3 drawPos = parent.DrawPos;
drawPos.y += 0.15f; // 稍微抬高一点
float progress = ProductionProgress;
GenDraw.DrawFillableBar(new GenDraw.FillableBarRequest
{
center = drawPos,
size = new Vector2(1f, 0.15f),
fillPercent = progress,
filledMat = SolidColorMaterials.SimpleSolidColorMaterial(Color.green),
unfilledMat = SolidColorMaterials.SimpleSolidColorMaterial(Color.gray),
margin = 0.1f,
rotation = Rot4.North
});
// 在进度条上方绘制质量进度条
if (Props.qualityThresholds != null && Props.qualityThresholds.Count > 0)
{
var qualityDetails = GetEstimatedQualityDetails();
float qualityProgress = Mathf.Clamp01(qualityDetails.baseScore - qualityDetails.penalty);
drawPos.y += 0.2f;
GenDraw.DrawFillableBar(new GenDraw.FillableBarRequest
{
center = drawPos,
size = new Vector2(1f, 0.1f),
fillPercent = qualityProgress,
filledMat = SolidColorMaterials.SimpleSolidColorMaterial(Color.blue),
unfilledMat = SolidColorMaterials.SimpleSolidColorMaterial(Color.gray),
margin = 0.1f,
rotation = Rot4.North
});
}
}
}
public override IEnumerable<Gizmo> CompGetGizmosExtra()
{
foreach (var g in base.CompGetGizmosExtra()) yield return g;
// 新增冷却期内不显示任何Gizmo
if (InCooldown) yield break;
if (InProduction)
{
yield return new Command_Action
@@ -426,6 +601,19 @@ namespace ArachnaeSwarm
defaultLabel = "Debug: Force Finish",
action = () => FinishProduction()
};
// 调试命令:显示详细进度信息
yield return new Command_Action
{
defaultLabel = "Debug: Show Progress Info",
action = () =>
{
float progress = ProductionProgress;
var qualityDetails = GetEstimatedQualityDetails();
Messages.Message($"Progress: {progress:P0}\nBase Score: {qualityDetails.baseScore:P0}\nPenalty: {qualityDetails.penalty:P0}\nFinal: {qualityDetails.quality}",
MessageTypeDefOf.SilentInput);
}
};
}
}
}

View File

@@ -350,6 +350,10 @@ namespace ArachnaeSwarm.MoharHediffs
public bool TryDoSpawn()
{
Pawn pawn = this.parent.pawn;
if (this.Props.animalThing)
{
// 动物生成逻辑保持不变
if (this.Props.spawnMaxAdjacent > 0 && pawn.Map.mapPawns.AllPawns.Where(delegate(Pawn mP)
{
ThingDef defToCompare = this.Props.animalThing ? this.Props.animalToSpawn?.race : this.Props.thingToSpawn;
@@ -362,12 +366,12 @@ namespace ArachnaeSwarm.MoharHediffs
{
return false;
}
if (this.Props.animalThing)
{
if (this.Props.animalToSpawn == null)
{
return false;
}
Faction faction = this.Props.factionOfPlayerAnimal ? Faction.OfPlayer : null;
int i = 0;
while (i < this.calculatedQuantity)
@@ -401,22 +405,47 @@ namespace ArachnaeSwarm.MoharHediffs
}
else
{
IntVec3 intVec2;
if (!this.TryFindSpawnCell(out intVec2))
{
return false;
}
// 修改物品直接添加到pawn的物品栏中
Thing thing = ThingMaker.MakeThing(this.Props.thingToSpawn, null);
if (thing == null)
{
return false;
}
thing.stackCount = this.calculatedQuantity;
// 检查pawn是否有物品栏
if (pawn.inventory == null)
{
Tools.Warn($"Pawn {pawn.Label} does not have an inventory to receive spawned items", this.myDebug);
return false;
}
// 尝试将物品添加到pawn的物品栏
if (pawn.inventory.innerContainer.TryAdd(thing))
{
if (PawnUtility.ShouldSendNotificationAbout(pawn))
{
Messages.Message(this.Props.spawnVerb.Translate(pawn.Named("PAWN"), thing.Named("THING")), pawn, MessageTypeDefOf.PositiveEvent, true);
}
return true;
}
else
{
// 如果物品栏添加失败,回退到原来的地面生成方式
Tools.Warn($"Failed to add {thing.Label} to {pawn.Label}'s inventory, falling back to ground spawn", this.myDebug);
IntVec3 intVec2;
if (!this.TryFindSpawnCell(out intVec2))
{
return false;
}
if (this.Props.spawnForbidden)
{
thing.SetForbidden(true, true);
}
GenPlace.TryPlaceThing(thing, intVec2, pawn.Map, ThingPlaceMode.Direct, null, null, default(Rot4));
if (PawnUtility.ShouldSendNotificationAbout(pawn))
{
Messages.Message(this.Props.spawnVerb.Translate(pawn.Named("PAWN"), thing.Named("THING")), thing, MessageTypeDefOf.PositiveEvent, true);
@@ -424,6 +453,7 @@ namespace ArachnaeSwarm.MoharHediffs
return true;
}
}
}
private bool TryFindSpawnCell(out IntVec3 result)
{
@@ -442,19 +472,19 @@ namespace ArachnaeSwarm.MoharHediffs
}
else
{
// 修改这里将半径从5减少到2让生成位置更靠近pawn
// 修改这里将半径从5减少到2让生成位置更靠近pawn
int searchRadius = 2;
// 首先尝试在pawn的相邻单元格生成半径为1
// 首先尝试在pawn的相邻单元格生成半径为1
result = CellFinder.RandomClosewalkCellNear(this.pawn.Position, map, 1, null);
// 如果相邻单元格找不到合适位置再尝试稍远一点半径为2
// 如果相邻单元格找不到合适位置再尝试稍远一点半径为2
if (!result.IsValid)
{
result = CellFinder.RandomClosewalkCellNear(this.pawn.Position, map, searchRadius, null);
}
// 如果还是找不到尝试pawn当前位置作为最后手段
// 如果还是找不到尝试pawn当前位置作为最后手段
if (!result.IsValid && this.pawn.Position.IsValid && this.pawn.Position.Walkable(map))
{
result = this.pawn.Position;