diff --git a/1.6/1.6/Assemblies/ArachnaeSwarm.dll b/1.6/1.6/Assemblies/ArachnaeSwarm.dll index 6d23993..cb33305 100644 Binary files a/1.6/1.6/Assemblies/ArachnaeSwarm.dll and b/1.6/1.6/Assemblies/ArachnaeSwarm.dll differ diff --git a/1.6/1.6/Defs/Rooms/ARA_RoomRoles.xml b/1.6/1.6/Defs/Rooms/ARA_RoomRoles.xml index 5169938..c5652a4 100644 --- a/1.6/1.6/Defs/Rooms/ARA_RoomRoles.xml +++ b/1.6/1.6/Defs/Rooms/ARA_RoomRoles.xml @@ -1,90 +1,12 @@  - - None - - RoomRoleWorker_None - - - - Room - - RoomRoleWorker_Room - - - - Bedroom - - RoomRoleWorker_Bedroom - true + ARA_Incubator_Room + + ArachnaeSwarm.RoomRoleWorker_Incubator -
  • Beauty
  • -
  • Cleanliness
  • -
  • Wealth
  • Space
  • -
  • Impressiveness
  • -
    -
    - - - PrisonCell - - RoomRoleWorker_PrisonCell - true - -
  • Beauty
  • -
  • Cleanliness
  • -
  • Wealth
  • -
  • Space
  • -
  • Impressiveness
  • -
    -
    - - - DiningRoom - - RoomRoleWorker_DiningRoom - -
  • Beauty
  • -
  • Cleanliness
  • -
  • Wealth
  • -
  • Space
  • -
  • Impressiveness
  • -
    -
    - - - RecRoom - - RoomRoleWorker_RecRoom - -
  • Beauty
  • -
  • Cleanliness
  • -
  • Wealth
  • -
  • Space
  • -
  • Impressiveness
  • -
    -
    - - - Hospital - - RoomRoleWorker_Hospital - true - -
  • Beauty
  • -
  • Cleanliness
  • -
  • Space
  • -
    -
    - - - Laboratory - - RoomRoleWorker_Laboratory - -
  • Cleanliness
  • +
  • ARA_IncubatorRateFactor
  • diff --git a/1.6/1.6/Defs/Rooms/ARA_RoomStats.xml b/1.6/1.6/Defs/Rooms/ARA_RoomStats.xml new file mode 100644 index 0000000..5027ede --- /dev/null +++ b/1.6/1.6/Defs/Rooms/ARA_RoomStats.xml @@ -0,0 +1,20 @@ + + + + ARA_IncubatorQualityFactor + + RoomStatWorker_FromStatByCurve + 0 + true + 0.75 + Space + + +
  • (-5.0, 0.65 )
  • +
  • (-2.5, 0.75 )
  • +
  • ( 0.0, 0.85 )
  • +
  • ( 1.0, 1.00 )
  • +
    +
    +
    +
    diff --git a/1.6/1.6/Defs/Rooms/RoomStats.xml b/1.6/1.6/Defs/Rooms/RoomStats.xml deleted file mode 100644 index 5619dd4..0000000 --- a/1.6/1.6/Defs/Rooms/RoomStats.xml +++ /dev/null @@ -1,308 +0,0 @@ - - - - - - - Impressiveness - - RoomStatWorker_Impressiveness - 1 - true - 0 - -
  • - -
  • -
  • - 20 - -
  • -
  • - 30 - -
  • -
  • - 40 - -
  • -
  • - 50 - -
  • -
  • - 65 - -
  • -
  • - 85 - -
  • -
  • - 120 - -
  • -
  • - 170 - -
  • -
  • - 240 - -
  • -
    -
    - - - Wealth - - RoomStatWorker_Wealth - 2 - true - 0 - -
  • - -
  • -
  • - 500 - -
  • -
  • - 700 - -
  • -
  • - 2000 - -
  • -
  • - 4000 - -
  • -
  • - 10000 - -
  • -
  • - 40000 - -
  • -
  • - 100000 - -
  • -
  • - 1000000 - -
  • -
    -
    - - - Space - - RoomStatWorker_Space - 2 - true - 350 - -
  • - -
  • -
  • - 12.5 - -
  • -
  • - 29 - -
  • -
  • - 55 - -
  • -
  • - 70 - -
  • -
  • - 130 - -
  • -
  • - 349.5 - -
  • -
    -
    - - - Beauty - - RoomStatWorker_Beauty - 2 - 0 - -
  • - -
  • -
  • - -3.5 - -
  • -
  • - 0.0 - -
  • -
  • - 2.4 - -
  • -
  • - 5.0 - -
  • -
  • - 15.0 - -
  • -
  • - 50.0 - -
  • -
  • - 100.0 - -
  • -
    -
    - - - Cleanliness - - RoomStatWorker_Cleanliness - 2 - 0 - -
  • - -
  • -
  • - -1.1 - -
  • -
  • - -0.4 - -
  • -
  • - -0.05 - -
  • -
  • - 0.4 - -
  • -
    -
    - - - ReadingBonus - - RoomStatWorker_ReadingBonus - 1 - 0 - true - - - - - - InfectionChanceFactor - - RoomStatWorker_FromStatByCurve - 0 - true - 1.0 - Cleanliness - - -
  • (-5, 1.0)
  • -
  • ( 0, 0.5)
  • -
  • ( 1, 0.2)
  • -
    -
    -
    - - - SurgerySuccessChanceCleanlinessFactor - - RoomStatWorker_FromStatByCurve - 0 - true - 0.6 - Cleanliness - - -
  • (-5, 0.6)
  • -
  • ( 0, 1.0)
  • -
  • ( 1, 1.10)
  • -
  • ( 5, 1.15)
  • -
    -
    -
    - - - ARA_ResearchSpeedFactor - - RoomStatWorker_FromStatByCurve - 0 - true - 0.75 - Space - - -
  • (-5.0, 0.75 )
  • -
  • (-2.5, 0.85 )
  • -
  • ( 0.0, 1.00 )
  • -
  • ( 1.0, 1.15 )
  • -
    -
    -
    - - - GraveVisitingJoyGainFactor - - RoomStatWorker_FromStatByCurve - 0 - true - 1 - Impressiveness - - -
  • (-150, 1.0)
  • -
  • ( 0, 1.0)
  • -
  • ( 150, 1.4)
  • -
    -
    -
    - - - FoodPoisonChance - - RoomStatWorker_FromStatByCurve - 0 - true - 0.02 - Cleanliness - - -
  • (-5, 0.05)
  • -
  • (-3.5, 0.025)
  • -
  • (-2, 0)
  • -
    -
    -
    - -
    diff --git a/1.6/1.6/Defs/Thing_Misc/Apparels/ARA_Apparel.xml b/1.6/1.6/Defs/Thing_Misc/Apparels/ARA_Apparel.xml index a52de07..14efbed 100644 --- a/1.6/1.6/Defs/Thing_Misc/Apparels/ARA_Apparel.xml +++ b/1.6/1.6/Defs/Thing_Misc/Apparels/ARA_Apparel.xml @@ -773,7 +773,6 @@ - ARA_Shield diff --git a/1.6/1.6/Defs/Thing_building/ARA_Incubator.xml b/1.6/1.6/Defs/Thing_building/ARA_Incubator.xml index 21e3f96..1e44266 100644 --- a/1.6/1.6/Defs/Thing_building/ARA_Incubator.xml +++ b/1.6/1.6/Defs/Thing_building/ARA_Incubator.xml @@ -100,6 +100,7 @@ false true false + ARA_Incubator_Room diff --git a/1.6/1.6/Languages/ChineseSimplified (简体中文)/Keyed/ARA_Building_Ootheca.xml b/1.6/1.6/Languages/ChineseSimplified (简体中文)/Keyed/ARA_Building_Ootheca.xml new file mode 100644 index 0000000..724dca0 --- /dev/null +++ b/1.6/1.6/Languages/ChineseSimplified (简体中文)/Keyed/ARA_Building_Ootheca.xml @@ -0,0 +1,100 @@ + + + + 正在孵化 + 进度 + 速度 + 质量 + 目标 + 速度乘数 + 质量乘数 + 幼虫种种进入中 + 剩余秒数 + 幼虫种正在路上 + 剩余时间 + + 小时 + 指派幼虫种 + 取消孵化 + 取消当前的孵化进度,幼虫种将死在卵囊中,卵囊则会重新张开以接纳其他幼虫种 + 孵化 + + 选择要孵化的目标,点击更改目标。 + 未选择目标 + 基础孵化时间 + + 研究 + 已完成 + 需要 + 点击选择不同的孵化目标。 + 已经在孵化中 + 请先取消当前孵化 + 一只幼虫种已经在前往这个卵囊的路上 + 没有找到可用的幼虫种 + 必须是阿拉克涅幼虫种,并且属于你的派系 + 已召唤幼虫种 + 它将很快到达并激活卵囊 + 幼虫种已到达 + 正在激活卵囊... + 已开始孵化 + 过程将在 + 天后完成(基础时间) + 孵化已取消 + 内容已丢失 + 孵化完成 + 已诞生,质量为 + 优秀 + 良好 + 一般 + + 极差 + 孵化速度因子: + ✓ 在孵化间中 (100%) + ✗ 不在孵化间中 (80%速度) + ✓ 营养液 + ✗ 附近没有营养液 + 总速度乘数:{0} + 孵化质量因子: + ✓ 建筑血量: + ✓ 房间因子:100% + 房间因子: + ✗ 附近卵囊: + ✓ 附近没有卵囊 + 总质量乘数:{0} + 召唤一只幼虫种来进入这个卵囊 + 幼虫种将来到卵囊,进入它,然后开始孵化: + 当前速度乘数: + 当前质量乘数: + 取消当前的孵化过程,在其中的幼虫种将会死亡 + + + 孵化:{0} + 点击更改目标 + 选择孵化目标 + 孵化目标: + 需要: + 无描述可用 + 孵化时间 + 孵化时间:{0}天 + + + 孵化状态 + 孵化进度 + 这不是一个卵囊 + 准备孵化: + 未选择孵化目标 + 幼虫种正在激活卵囊... + 剩余秒数 + 质量:低于目标(影响最终结果) + 质量进度 + 孵化进度 + + + 正在激活卵囊... + + + 质量低于目标 + + + 需要研究 + diff --git a/Source/ArachnaeSwarm/.vs/ArachnaeSwarm/v17/.suo b/Source/ArachnaeSwarm/.vs/ArachnaeSwarm/v17/.suo index 8c87dec..51cc3d0 100644 Binary files a/Source/ArachnaeSwarm/.vs/ArachnaeSwarm/v17/.suo and b/Source/ArachnaeSwarm/.vs/ArachnaeSwarm/v17/.suo differ diff --git a/Source/ArachnaeSwarm/.vs/ArachnaeSwarm/v17/DocumentLayout.json b/Source/ArachnaeSwarm/.vs/ArachnaeSwarm/v17/DocumentLayout.json index 625783b..fb5c6e3 100644 --- a/Source/ArachnaeSwarm/.vs/ArachnaeSwarm/v17/DocumentLayout.json +++ b/Source/ArachnaeSwarm/.vs/ArachnaeSwarm/v17/DocumentLayout.json @@ -2,10 +2,6 @@ "Version": 1, "WorkspaceRootPath": "E:\\SteamLibrary\\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\\buildings\\building_ootheca\\itab_ootheca_incubation.cs||{A6C744A8-0E4A-4FC6-886A-064283054674}", - "RelativeMoniker": "D:0:0:{EAE0DB6B-E282-C812-7F5A-6D13E9D24581}|ArachnaeSwarm.csproj|solutionrelative:buildings\\building_ootheca\\itab_ootheca_incubation.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\\buildings\\building_ootheca\\building_ootheca.cs||{A6C744A8-0E4A-4FC6-886A-064283054674}", "RelativeMoniker": "D:0:0:{EAE0DB6B-E282-C812-7F5A-6D13E9D24581}|ArachnaeSwarm.csproj|solutionrelative:buildings\\building_ootheca\\building_ootheca.cs||{A6C744A8-0E4A-4FC6-886A-064283054674}" @@ -14,28 +10,36 @@ "AbsoluteMoniker": "D:0:0:{EAE0DB6B-E282-C812-7F5A-6D13E9D24581}|ArachnaeSwarm.csproj|e:\\steamlibrary\\steamapps\\common\\rimworld\\mods\\arachnaeswarm\\source\\arachnaeswarm\\buildings\\building_ootheca\\compproperties_incubatordata.cs||{A6C744A8-0E4A-4FC6-886A-064283054674}", "RelativeMoniker": "D:0:0:{EAE0DB6B-E282-C812-7F5A-6D13E9D24581}|ArachnaeSwarm.csproj|solutionrelative:buildings\\building_ootheca\\compproperties_incubatordata.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\\buildings\\building_ootheca\\itab_ootheca_incubation.cs||{A6C744A8-0E4A-4FC6-886A-064283054674}", + "RelativeMoniker": "D:0:0:{EAE0DB6B-E282-C812-7F5A-6D13E9D24581}|ArachnaeSwarm.csproj|solutionrelative:buildings\\building_ootheca\\itab_ootheca_incubation.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\\buildings\\building_ootheca\\jobdriver_operateincubator.cs||{A6C744A8-0E4A-4FC6-886A-064283054674}", "RelativeMoniker": "D:0:0:{EAE0DB6B-E282-C812-7F5A-6D13E9D24581}|ArachnaeSwarm.csproj|solutionrelative:buildings\\building_ootheca\\jobdriver_operateincubator.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\\powerarmor\\jobdriver_enterpowerarmor.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\\buildings\\building_ootheca\\roomroleworker_incubator.cs||{A6C744A8-0E4A-4FC6-886A-064283054674}", + "RelativeMoniker": "D:0:0:{EAE0DB6B-E282-C812-7F5A-6D13E9D24581}|ArachnaeSwarm.csproj|solutionrelative:buildings\\building_ootheca\\roomroleworker_incubator.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\\powerarmor\\jobdriver_enterpowerarmor.cs||{A6C744A8-0E4A-4FC6-886A-064283054674}", "RelativeMoniker": "D:0:0:{EAE0DB6B-E282-C812-7F5A-6D13E9D24581}|ArachnaeSwarm.csproj|solutionrelative:powerarmor\\jobdriver_enterpowerarmor.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\\ara_hediffdefof.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\\ara_hediffdefof.cs||{A6C744A8-0E4A-4FC6-886A-064283054674}", "RelativeMoniker": "D:0:0:{EAE0DB6B-E282-C812-7F5A-6D13E9D24581}|ArachnaeSwarm.csproj|solutionrelative:ara_hediffdefof.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\\powerarmor\\ara_powerarmor.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\\powerarmor\\ara_powerarmor.cs||{A6C744A8-0E4A-4FC6-886A-064283054674}", "RelativeMoniker": "D:0:0:{EAE0DB6B-E282-C812-7F5A-6D13E9D24581}|ArachnaeSwarm.csproj|solutionrelative:powerarmor\\ara_powerarmor.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\\buildings\\building_arachnaegravengine.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\\buildings\\building_arachnaegravengine.cs||{A6C744A8-0E4A-4FC6-886A-064283054674}", "RelativeMoniker": "D:0:0:{EAE0DB6B-E282-C812-7F5A-6D13E9D24581}|ArachnaeSwarm.csproj|solutionrelative:buildings\\building_arachnaegravengine.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\\flyover\\ara_groundstrafing\\compgroundstrafing.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\\flyover\\ara_groundstrafing\\compgroundstrafing.cs||{A6C744A8-0E4A-4FC6-886A-064283054674}", "RelativeMoniker": "D:0:0:{EAE0DB6B-E282-C812-7F5A-6D13E9D24581}|ArachnaeSwarm.csproj|solutionrelative:flyover\\ara_groundstrafing\\compgroundstrafing.cs||{A6C744A8-0E4A-4FC6-886A-064283054674}" }, { @@ -86,23 +90,49 @@ "DocumentGroups": [ { "DockedWidth": 200, - "SelectedChildIndex": 1, + "SelectedChildIndex": 2, "Children": [ { "$type": "Bookmark", "Name": "ST:0:0:{1c4feeaa-4718-4aa9-859d-94ce25d182ba}" }, + { + "$type": "Document", + "DocumentIndex": 1, + "Title": "CompProperties_IncubatorData.cs", + "DocumentMoniker": "E:\\SteamLibrary\\steamapps\\common\\RimWorld\\Mods\\ArachnaeSwarm\\Source\\ArachnaeSwarm\\Buildings\\Building_Ootheca\\CompProperties_IncubatorData.cs", + "RelativeDocumentMoniker": "Buildings\\Building_Ootheca\\CompProperties_IncubatorData.cs", + "ToolTip": "E:\\SteamLibrary\\steamapps\\common\\RimWorld\\Mods\\ArachnaeSwarm\\Source\\ArachnaeSwarm\\Buildings\\Building_Ootheca\\CompProperties_IncubatorData.cs", + "RelativeToolTip": "Buildings\\Building_Ootheca\\CompProperties_IncubatorData.cs", + "ViewState": "AgIAAH0AAAAAAAAAAAAAAK8AAAAJAAAAAAAAAA==", + "Icon": "ae27a6b0-e345-4288-96df-5eaf394ee369.000738|", + "WhenOpened": "2025-12-14T12:41:01.454Z", + "EditorCaption": "" + }, { "$type": "Document", "DocumentIndex": 0, + "Title": "Building_Ootheca.cs", + "DocumentMoniker": "E:\\SteamLibrary\\steamapps\\common\\RimWorld\\Mods\\ArachnaeSwarm\\Source\\ArachnaeSwarm\\Buildings\\Building_Ootheca\\Building_Ootheca.cs", + "RelativeDocumentMoniker": "Buildings\\Building_Ootheca\\Building_Ootheca.cs", + "ToolTip": "E:\\SteamLibrary\\steamapps\\common\\RimWorld\\Mods\\ArachnaeSwarm\\Source\\ArachnaeSwarm\\Buildings\\Building_Ootheca\\Building_Ootheca.cs*", + "RelativeToolTip": "Buildings\\Building_Ootheca\\Building_Ootheca.cs*", + "ViewState": "AgIAAK4BAAAAAAAAAAAAAOUBAABIAAAAAAAAAA==", + "Icon": "ae27a6b0-e345-4288-96df-5eaf394ee369.000738|", + "WhenOpened": "2025-12-14T13:27:29.463Z", + "EditorCaption": "" + }, + { + "$type": "Document", + "DocumentIndex": 2, "Title": "ITab_Ootheca_Incubation.cs", "DocumentMoniker": "E:\\SteamLibrary\\steamapps\\common\\RimWorld\\Mods\\ArachnaeSwarm\\Source\\ArachnaeSwarm\\Buildings\\Building_Ootheca\\ITab_Ootheca_Incubation.cs", "RelativeDocumentMoniker": "Buildings\\Building_Ootheca\\ITab_Ootheca_Incubation.cs", "ToolTip": "E:\\SteamLibrary\\steamapps\\common\\RimWorld\\Mods\\ArachnaeSwarm\\Source\\ArachnaeSwarm\\Buildings\\Building_Ootheca\\ITab_Ootheca_Incubation.cs", "RelativeToolTip": "Buildings\\Building_Ootheca\\ITab_Ootheca_Incubation.cs", - "ViewState": "AgIAAAAAAAAAAAAAAAAAAFQAAAAQAAAAAAAAAA==", + "ViewState": "AgIAAKwAAAAAAAAAAADwv+MAAAAAAAAAAAAAAA==", "Icon": "ae27a6b0-e345-4288-96df-5eaf394ee369.000738|", - "WhenOpened": "2025-12-14T15:14:43.715Z", + "WhenOpened": "2025-12-14T16:21:37.25Z", "EditorCaption": "" }, { @@ -113,40 +143,27 @@ "RelativeDocumentMoniker": "Buildings\\Building_Ootheca\\JobDriver_OperateIncubator.cs", "ToolTip": "E:\\SteamLibrary\\steamapps\\common\\RimWorld\\Mods\\ArachnaeSwarm\\Source\\ArachnaeSwarm\\Buildings\\Building_Ootheca\\JobDriver_OperateIncubator.cs", "RelativeToolTip": "Buildings\\Building_Ootheca\\JobDriver_OperateIncubator.cs", - "ViewState": "AgIAABQAAAAAAAAAAAA0wDgAAAAlAAAAAAAAAA==", + "ViewState": "AgIAAA4AAAAAAAAAAAAAwC0AAAAgAAAAAAAAAA==", "Icon": "ae27a6b0-e345-4288-96df-5eaf394ee369.000738|", "WhenOpened": "2025-12-14T13:42:44.544Z", "EditorCaption": "" }, - { - "$type": "Document", - "DocumentIndex": 1, - "Title": "Building_Ootheca.cs", - "DocumentMoniker": "E:\\SteamLibrary\\steamapps\\common\\RimWorld\\Mods\\ArachnaeSwarm\\Source\\ArachnaeSwarm\\Buildings\\Building_Ootheca\\Building_Ootheca.cs", - "RelativeDocumentMoniker": "Buildings\\Building_Ootheca\\Building_Ootheca.cs", - "ToolTip": "E:\\SteamLibrary\\steamapps\\common\\RimWorld\\Mods\\ArachnaeSwarm\\Source\\ArachnaeSwarm\\Buildings\\Building_Ootheca\\Building_Ootheca.cs", - "RelativeToolTip": "Buildings\\Building_Ootheca\\Building_Ootheca.cs", - "ViewState": "AgIAABAAAAAAAAAAAAAAwMsBAAAAAAAAAAAAAA==", - "Icon": "ae27a6b0-e345-4288-96df-5eaf394ee369.000738|", - "WhenOpened": "2025-12-14T13:27:29.463Z", - "EditorCaption": "" - }, - { - "$type": "Document", - "DocumentIndex": 2, - "Title": "CompProperties_IncubatorData.cs", - "DocumentMoniker": "E:\\SteamLibrary\\steamapps\\common\\RimWorld\\Mods\\ArachnaeSwarm\\Source\\ArachnaeSwarm\\Buildings\\Building_Ootheca\\CompProperties_IncubatorData.cs", - "RelativeDocumentMoniker": "Buildings\\Building_Ootheca\\CompProperties_IncubatorData.cs", - "ToolTip": "E:\\SteamLibrary\\steamapps\\common\\RimWorld\\Mods\\ArachnaeSwarm\\Source\\ArachnaeSwarm\\Buildings\\Building_Ootheca\\CompProperties_IncubatorData.cs", - "RelativeToolTip": "Buildings\\Building_Ootheca\\CompProperties_IncubatorData.cs", - "ViewState": "AgIAAFgAAAAAAAAAAIA+wJ8AAAAAAAAAAAAAAA==", - "Icon": "ae27a6b0-e345-4288-96df-5eaf394ee369.000738|", - "WhenOpened": "2025-12-14T12:41:01.454Z", - "EditorCaption": "" - }, { "$type": "Document", "DocumentIndex": 4, + "Title": "RoomRoleWorker_Incubator.cs", + "DocumentMoniker": "E:\\SteamLibrary\\steamapps\\common\\RimWorld\\Mods\\ArachnaeSwarm\\Source\\ArachnaeSwarm\\Buildings\\Building_Ootheca\\RoomRoleWorker_Incubator.cs", + "RelativeDocumentMoniker": "Buildings\\Building_Ootheca\\RoomRoleWorker_Incubator.cs", + "ToolTip": "E:\\SteamLibrary\\steamapps\\common\\RimWorld\\Mods\\ArachnaeSwarm\\Source\\ArachnaeSwarm\\Buildings\\Building_Ootheca\\RoomRoleWorker_Incubator.cs", + "RelativeToolTip": "Buildings\\Building_Ootheca\\RoomRoleWorker_Incubator.cs", + "ViewState": "AgIAAAAAAAAAAAAAAADwvxYAAAA8AAAAAAAAAA==", + "Icon": "ae27a6b0-e345-4288-96df-5eaf394ee369.000738|", + "WhenOpened": "2025-12-14T16:16:56.635Z", + "EditorCaption": "" + }, + { + "$type": "Document", + "DocumentIndex": 5, "Title": "JobDriver_EnterPowerArmor.cs", "DocumentMoniker": "E:\\SteamLibrary\\steamapps\\common\\RimWorld\\Mods\\ArachnaeSwarm\\Source\\ArachnaeSwarm\\PowerArmor\\JobDriver_EnterPowerArmor.cs", "RelativeDocumentMoniker": "PowerArmor\\JobDriver_EnterPowerArmor.cs", @@ -154,12 +171,11 @@ "RelativeToolTip": "PowerArmor\\JobDriver_EnterPowerArmor.cs", "ViewState": "AgIAAAAAAAAAAAAAAADwvwAAAAAAAAAAAAAAAA==", "Icon": "ae27a6b0-e345-4288-96df-5eaf394ee369.000738|", - "WhenOpened": "2025-12-14T13:53:00.443Z", - "EditorCaption": "" + "WhenOpened": "2025-12-14T13:53:00.443Z" }, { "$type": "Document", - "DocumentIndex": 5, + "DocumentIndex": 6, "Title": "ARA_HediffDefOf.cs", "DocumentMoniker": "E:\\SteamLibrary\\steamapps\\common\\RimWorld\\Mods\\ArachnaeSwarm\\Source\\ArachnaeSwarm\\ARA_HediffDefOf.cs", "RelativeDocumentMoniker": "ARA_HediffDefOf.cs", @@ -167,12 +183,11 @@ "RelativeToolTip": "ARA_HediffDefOf.cs", "ViewState": "AgIAAAAAAAAAAAAAAADwvxIAAAAkAAAAAAAAAA==", "Icon": "ae27a6b0-e345-4288-96df-5eaf394ee369.000738|", - "WhenOpened": "2025-12-14T13:29:05.1Z", - "EditorCaption": "" + "WhenOpened": "2025-12-14T13:29:05.1Z" }, { "$type": "Document", - "DocumentIndex": 6, + "DocumentIndex": 7, "Title": "ARA_PowerArmor.cs", "DocumentMoniker": "E:\\SteamLibrary\\steamapps\\common\\RimWorld\\Mods\\ArachnaeSwarm\\Source\\ArachnaeSwarm\\PowerArmor\\ARA_PowerArmor.cs", "RelativeDocumentMoniker": "PowerArmor\\ARA_PowerArmor.cs", @@ -180,12 +195,11 @@ "RelativeToolTip": "PowerArmor\\ARA_PowerArmor.cs", "ViewState": "AgIAAAAAAAAAAAAAAADwvxAAAABMAAAAAAAAAA==", "Icon": "ae27a6b0-e345-4288-96df-5eaf394ee369.000738|", - "WhenOpened": "2025-12-14T12:42:55.951Z", - "EditorCaption": "" + "WhenOpened": "2025-12-14T12:42:55.951Z" }, { "$type": "Document", - "DocumentIndex": 7, + "DocumentIndex": 8, "Title": "Building_ArachnaeGravEngine.cs", "DocumentMoniker": "E:\\SteamLibrary\\steamapps\\common\\RimWorld\\Mods\\ArachnaeSwarm\\Source\\ArachnaeSwarm\\Buildings\\Building_ArachnaeGravEngine.cs", "RelativeDocumentMoniker": "Buildings\\Building_ArachnaeGravEngine.cs", @@ -193,12 +207,11 @@ "RelativeToolTip": "Buildings\\Building_ArachnaeGravEngine.cs", "ViewState": "AgIAAAAAAAAAAAAAAADwvxcAAAAiAAAAAAAAAA==", "Icon": "ae27a6b0-e345-4288-96df-5eaf394ee369.000738|", - "WhenOpened": "2025-12-14T12:37:14.137Z", - "EditorCaption": "" + "WhenOpened": "2025-12-14T12:37:14.137Z" }, { "$type": "Document", - "DocumentIndex": 9, + "DocumentIndex": 10, "Title": "CompSectorSurveillance.cs", "DocumentMoniker": "E:\\SteamLibrary\\steamapps\\common\\RimWorld\\Mods\\ArachnaeSwarm\\Source\\ArachnaeSwarm\\Flyover\\ARA_SectorSurveillance\\CompSectorSurveillance.cs", "RelativeDocumentMoniker": "Flyover\\ARA_SectorSurveillance\\CompSectorSurveillance.cs", @@ -210,7 +223,7 @@ }, { "$type": "Document", - "DocumentIndex": 8, + "DocumentIndex": 9, "Title": "CompGroundStrafing.cs", "DocumentMoniker": "E:\\SteamLibrary\\steamapps\\common\\RimWorld\\Mods\\ArachnaeSwarm\\Source\\ArachnaeSwarm\\Flyover\\ARA_GroundStrafing\\CompGroundStrafing.cs", "RelativeDocumentMoniker": "Flyover\\ARA_GroundStrafing\\CompGroundStrafing.cs", @@ -218,12 +231,11 @@ "RelativeToolTip": "Flyover\\ARA_GroundStrafing\\CompGroundStrafing.cs", "ViewState": "AgIAAAAAAAAAAAAAAAAAABsAAAAMAAAAAAAAAA==", "Icon": "ae27a6b0-e345-4288-96df-5eaf394ee369.000738|", - "WhenOpened": "2025-10-30T13:00:11.18Z", - "EditorCaption": "" + "WhenOpened": "2025-10-30T13:00:11.18Z" }, { "$type": "Document", - "DocumentIndex": 12, + "DocumentIndex": 13, "Title": "CompAbilityEffect_GiveHediffWithSkillDuration.cs", "DocumentMoniker": "E:\\SteamLibrary\\steamapps\\common\\RimWorld\\Mods\\ArachnaeSwarm\\Source\\ArachnaeSwarm\\Abilities\\ARA_GiveHediffWithSkillDuration\\CompAbilityEffect_GiveHediffWithSkillDuration.cs", "RelativeDocumentMoniker": "Abilities\\ARA_GiveHediffWithSkillDuration\\CompAbilityEffect_GiveHediffWithSkillDuration.cs", @@ -235,7 +247,7 @@ }, { "$type": "Document", - "DocumentIndex": 13, + "DocumentIndex": 14, "Title": "CompAbilityEffect_AbilityShowTemperatureRange.cs", "DocumentMoniker": "E:\\SteamLibrary\\steamapps\\common\\RimWorld\\Mods\\ArachnaeSwarm\\Source\\ArachnaeSwarm\\Abilities\\ARA_ShowTemperatureRange\\CompAbilityEffect_AbilityShowTemperatureRange.cs", "RelativeDocumentMoniker": "Abilities\\ARA_ShowTemperatureRange\\CompAbilityEffect_AbilityShowTemperatureRange.cs", @@ -247,7 +259,7 @@ }, { "$type": "Document", - "DocumentIndex": 11, + "DocumentIndex": 12, "Title": "CompAbilityEffect_AircraftStrike.cs", "DocumentMoniker": "E:\\SteamLibrary\\steamapps\\common\\RimWorld\\Mods\\ArachnaeSwarm\\Source\\ArachnaeSwarm\\Flyover\\ARA_AircraftHangar\\CompAbilityEffect_AircraftStrike.cs", "RelativeDocumentMoniker": "Flyover\\ARA_AircraftHangar\\CompAbilityEffect_AircraftStrike.cs", @@ -259,7 +271,7 @@ }, { "$type": "Document", - "DocumentIndex": 10, + "DocumentIndex": 11, "Title": "ThingclassFlyOver.cs", "DocumentMoniker": "E:\\SteamLibrary\\steamapps\\common\\RimWorld\\Mods\\ArachnaeSwarm\\Source\\ArachnaeSwarm\\Flyover\\ThingclassFlyOver.cs", "RelativeDocumentMoniker": "Flyover\\ThingclassFlyOver.cs", @@ -271,7 +283,7 @@ }, { "$type": "Document", - "DocumentIndex": 14, + "DocumentIndex": 15, "Title": "CompAbilityEffect_AbilityShowSpawnablePawns.cs", "DocumentMoniker": "E:\\SteamLibrary\\steamapps\\common\\RimWorld\\Mods\\ArachnaeSwarm\\Source\\ArachnaeSwarm\\Abilities\\ARA_ShowSpawnablePawnsList\\CompAbilityEffect_AbilityShowSpawnablePawns.cs", "RelativeDocumentMoniker": "Abilities\\ARA_ShowSpawnablePawnsList\\CompAbilityEffect_AbilityShowSpawnablePawns.cs", @@ -283,7 +295,7 @@ }, { "$type": "Document", - "DocumentIndex": 16, + "DocumentIndex": 17, "Title": "CompAircraftHangar.cs", "DocumentMoniker": "E:\\SteamLibrary\\steamapps\\common\\RimWorld\\Mods\\ArachnaeSwarm\\Source\\ArachnaeSwarm\\Flyover\\ARA_AircraftHangar\\CompAircraftHangar.cs", "RelativeDocumentMoniker": "Flyover\\ARA_AircraftHangar\\CompAircraftHangar.cs", @@ -295,7 +307,7 @@ }, { "$type": "Document", - "DocumentIndex": 15, + "DocumentIndex": 16, "Title": "CompAbilityEffect_SpawnFlyOver.cs", "DocumentMoniker": "E:\\SteamLibrary\\steamapps\\common\\RimWorld\\Mods\\ArachnaeSwarm\\Source\\ArachnaeSwarm\\Flyover\\ARA_SpawnFlyOver\\CompAbilityEffect_SpawnFlyOver.cs", "RelativeDocumentMoniker": "Flyover\\ARA_SpawnFlyOver\\CompAbilityEffect_SpawnFlyOver.cs", @@ -307,7 +319,7 @@ }, { "$type": "Document", - "DocumentIndex": 17, + "DocumentIndex": 18, "Title": "WorldComponent_AircraftManager.cs", "DocumentMoniker": "E:\\SteamLibrary\\steamapps\\common\\RimWorld\\Mods\\ArachnaeSwarm\\Source\\ArachnaeSwarm\\Flyover\\ARA_AircraftHangar\\WorldComponent_AircraftManager.cs", "RelativeDocumentMoniker": "Flyover\\ARA_AircraftHangar\\WorldComponent_AircraftManager.cs", @@ -319,7 +331,7 @@ }, { "$type": "Document", - "DocumentIndex": 18, + "DocumentIndex": 19, "Title": "CompProperties_FlyOverEscort.cs", "DocumentMoniker": "E:\\SteamLibrary\\steamapps\\common\\RimWorld\\Mods\\ArachnaeSwarm\\Source\\ArachnaeSwarm\\Flyover\\ARA_FlyOverEscort\\CompProperties_FlyOverEscort.cs", "RelativeDocumentMoniker": "Flyover\\ARA_FlyOverEscort\\CompProperties_FlyOverEscort.cs", diff --git a/Source/ArachnaeSwarm/ArachnaeSwarm.csproj b/Source/ArachnaeSwarm/ArachnaeSwarm.csproj index 2131cef..3d6fc9f 100644 --- a/Source/ArachnaeSwarm/ArachnaeSwarm.csproj +++ b/Source/ArachnaeSwarm/ArachnaeSwarm.csproj @@ -119,6 +119,7 @@ + diff --git a/Source/ArachnaeSwarm/Buildings/Building_Ootheca/Building_Ootheca.cs b/Source/ArachnaeSwarm/Buildings/Building_Ootheca/Building_Ootheca.cs new file mode 100644 index 0000000..ee53ef0 --- /dev/null +++ b/Source/ArachnaeSwarm/Buildings/Building_Ootheca/Building_Ootheca.cs @@ -0,0 +1,840 @@ +using RimWorld; +using System.Collections.Generic; +using System.Text; +using UnityEngine; +using Verse; +using Verse.AI; + +namespace ArachnaeSwarm +{ + public class Building_Ootheca : Building + { + // 引用组件 + public CompIncubatorData IncubatorData => this.TryGetComp(); + + // 孵化状态 - 设为public以便ITab访问 + public bool isIncubating = false; + public float incubationProgress = 0f; + public float incubationDuration = 0f; + public PawnKindDef incubatingPawnKind = null; + + // 幼虫交互相关 - 设为public以便ITab访问 + public Pawn assignedLarva = null; + public int larvaOperateTicksRemaining = 0; + + // 速度乘数系统 + private float speedMultiplier = 1.0f; + private int lastMultiplierUpdateTick = -1; + private const int MultiplierUpdateInterval = 250; // 每250ticks更新一次 + + // 质量系统 + private float qualityMultiplier = 1.0f; + private float qualityProgress = 0f; + private float qualityTotal = 0f; + + // 属性访问器 + public float SpeedMultiplier + { + get + { + // 每MultiplierUpdateInterval ticks更新一次 + if (lastMultiplierUpdateTick < 0 || Find.TickManager.TicksGame - lastMultiplierUpdateTick >= MultiplierUpdateInterval) + { + UpdateSpeedMultiplier(); + } + return speedMultiplier; + } + } + + // 质量属性 + public float QualityMultiplier => qualityMultiplier; + public float QualityProgress => qualityProgress; + public float QualityPercent => qualityTotal > 0 ? qualityProgress / qualityTotal : 0f; + + // 获取基于乘数调整后的进度百分比 + public float AdjustedProgressPercent + { + get + { + if (incubationDuration <= 0) return 0f; + return incubationProgress / incubationDuration; + } + } + + public string GetSpeedFactorsDescription() + { + var builder = new StringBuilder(); + + builder.AppendLine("IncubationSpeedFactors".Translate()); + builder.AppendLine(); + + // 1. 检查是否在孵化间中 + bool inIncubatorRoom = IsInIncubatorRoom(); + builder.AppendLine(inIncubatorRoom ? + "InIncubatorRoom".Translate() : + "NotInIncubatorRoom".Translate()); + + // 2. 检查营养液数量 + int nutrientSolutionCount = CountNearbyNutrientSolutions(); + if (nutrientSolutionCount > 0) + { + builder.AppendLine("NutrientSolutions".Translate(nutrientSolutionCount, nutrientSolutionCount)); + } + else + { + builder.AppendLine("NoNutrientSolutionsNearby".Translate()); + } + + builder.AppendLine(); + builder.AppendLine("TotalSpeedMultiplier".Translate(SpeedMultiplier.ToStringPercent())); + + return builder.ToString().TrimEndNewlines(); + } + // 获取质量乘数的详细因子信息(用于工具提示) + public string GetQualityFactorsDescription() + { + var builder = new StringBuilder(); + + builder.AppendLine("IncubationQualityFactors".Translate()); + builder.AppendLine(); + + // 1. 建筑血量损失百分比 + float healthPercent = (float)HitPoints / MaxHitPoints; + builder.AppendLine("BuildingHealth".Translate(healthPercent.ToStringPercent())); + + // 2. 房间的ARA_IncubatorQualityFactor + float roomFactor = GetRoomQualityFactor(); + builder.AppendLine(roomFactor == 1.0f ? + "RoomFactorNormal".Translate() : + $"{(roomFactor > 1.0f ? "✓" : "✗")} {"RoomFactorModified".Translate()}{roomFactor.ToStringPercent()}"); + + // 3. 附近每一个ARA_Pawn_Ootheca,每一个-10% + int nearbyOothecaCount = CountNearbyOtherOothecas(); + if (nearbyOothecaCount > 0) + { + builder.AppendLine("NearbyOothecas".Translate(nearbyOothecaCount, Mathf.Min(nearbyOothecaCount * 10, 100))); + } + else + { + builder.AppendLine("NoNearbyOothecas".Translate()); + } + + builder.AppendLine(); + builder.AppendLine("TotalQualityMultiplier".Translate(QualityMultiplier.ToStringPercent())); + + return builder.ToString().TrimEndNewlines(); + } + // 构建呼叫幼虫描述 + private string BuildCallLarvaDescription(IncubationConfig config) + { + var builder = new StringBuilder(); + + builder.AppendLine("CallALarvaToActivate".Translate()); + builder.AppendLine(); + builder.AppendLine("LarvaWillComeToTheOotheca".Translate()); + builder.AppendLine(config.pawnKind.LabelCap); + builder.AppendLine(); + + return builder.ToString().TrimEndNewlines(); + } + // 呼叫幼虫 + private void CallLarva() + { + // 检查是否已经在孵化中或有幼虫在任务中 + if (isIncubating) + { + Messages.Message("AlreadyIncubating".Translate() + " " + "CancelCurrentIncubationFirst".Translate(), + MessageTypeDefOf.RejectInput); + return; + } + + if (assignedLarva != null) + { + Messages.Message("LarvaAlreadyOnTheWay".Translate(), + MessageTypeDefOf.RejectInput); + return; + } + + // 查找可用的幼虫 + var larva = FindLarva(); + if (larva == null) + { + Messages.Message("NoAvailableLarvaeFound".Translate() + " " + "LarvaMustBeOfRace".Translate(), + MessageTypeDefOf.RejectInput); + return; + } + + // 给幼虫分配任务 + var job = JobMaker.MakeJob(ARA_JobDefOf.ARA_OperateIncubator, this); + job.count = 1; + larva.jobs.TryTakeOrderedJob(job, JobTag.MiscWork); + + assignedLarva = larva; + + Messages.Message("LarvaCalled".Translate() + " " + "ItWillArriveShortly".Translate(), + MessageTypeDefOf.PositiveEvent); + } + // 幼虫开始操作 + public void NotifyLarvaArrived(Pawn larva) + { + // 验证幼虫种族 + if (larva.def.defName != "ArachnaeBase_Race_Larva") + { + Log.Warning($"Invalid larva arrived: {larva.def.defName}"); + return; + } + + // 开始操作计时(3秒 = 180 ticks) + larvaOperateTicksRemaining = 180; + assignedLarva = larva; + + // 显示消息 + Messages.Message("LarvaHasArrived".Translate() + " " + "AndIsActivatingTheOotheca".Translate(), + MessageTypeDefOf.SilentInput); + } + // 幼虫完成操作(由JobDriver调用) + public void NotifyLarvaOperationComplete(Pawn larva) + { + // 验证是当前分配的幼虫 + if (larva != assignedLarva) + { + Log.Warning("Larva operation complete called with wrong larva."); + return; + } + + var config = IncubatorData?.SelectedConfig; + if (config == null) + { + Log.Error("No incubation config selected when larva completed operation."); + return; + } + + // 保存孵化信息 + incubatingPawnKind = config.pawnKind; + incubationDuration = config.daysRequired * 60000f; // RimWorld中1天=60000ticks + incubationProgress = 0f; + isIncubating = true; + + // 初始化质量系统 + qualityTotal = incubationDuration; // 质量总量与孵化时间相同 + qualityProgress = 0f; + UpdateQualityMultiplier(); + + // 更新速度乘数 + UpdateSpeedMultiplier(); + + // 清除幼虫引用(幼虫已在JobDriver中被删除) + assignedLarva = null; + larvaOperateTicksRemaining = 0; + + // 显示消息 + Messages.Message("IncubationStartedFor".Translate() + " " + incubatingPawnKind.LabelCap + ". " + + "ProcessWillCompleteIn".Translate() + " " + config.daysRequired + " " + "DaysBaseTime".Translate(), + MessageTypeDefOf.PositiveEvent); + } + // 取消孵化 + private void CancelIncubation() + { + if (!isIncubating) return; + + isIncubating = false; + incubationProgress = 0f; + incubationDuration = 0f; + incubatingPawnKind = null; + qualityProgress = 0f; + qualityTotal = 0f; + + Messages.Message("IncubationCancelled".Translate() + " " + "ContentsLost".Translate(), + MessageTypeDefOf.NeutralEvent); + } + // 完成孵化 + private void CompleteIncubation() + { + if (incubatingPawnKind == null) return; + + // 计算最终质量百分比 + float finalQualityPercent = QualityPercent; + + // 生成新的pawn + var pawn = PawnGenerator.GeneratePawn(incubatingPawnKind, Faction); + + // 应用质量影响 + ApplyQualityEffects(pawn, finalQualityPercent); + + // 尝试将pawn放置在地图上 + var spawnPos = Position; + if (!pawn.Spawned) + { + GenSpawn.Spawn(pawn, spawnPos, Map); + } + + // 重置孵化状态 + isIncubating = false; + incubationProgress = 0f; + incubationDuration = 0f; + incubatingPawnKind = null; + qualityProgress = 0f; + qualityTotal = 0f; + + // 显示消息 + string qualityText = finalQualityPercent >= 0.9f ? "QualityExcellent".Translate() : + finalQualityPercent >= 0.7f ? "QualityGood".Translate() : + finalQualityPercent >= 0.5f ? "QualityAverage".Translate() : + finalQualityPercent >= 0.3f ? "QualityPoor".Translate() : "QualityVeryPoor".Translate(); + + Messages.Message("IncubationComplete".Translate() + " " + pawn.LabelCap + " " + + "HasEmergedWith".Translate() + " " + qualityText + " " + + "quality".Translate() + " (" + finalQualityPercent.ToStringPercent() + ").", + MessageTypeDefOf.PositiveEvent); + } + // 显示额外信息(简化版本,只显示速率,不显示因子) + public override string GetInspectString() + { + var baseString = base.GetInspectString(); + var builder = new StringBuilder(); + + if (!string.IsNullOrEmpty(baseString)) + { + builder.Append(baseString); + } + + if (isIncubating && incubatingPawnKind != null) + { + float progressPercent = AdjustedProgressPercent; + float daysRemaining = GetRemainingDays(); + float hoursRemaining = GetRemainingHours(); + + if (builder.Length > 0) builder.AppendLine(); + builder.Append("Incubating".Translate() + ": " + incubatingPawnKind.LabelCap); + builder.AppendLine(); + builder.Append("Progress".Translate() + ": " + progressPercent.ToStringPercent()); + builder.AppendLine(); + + // 显示剩余时间 + string timeText = "TimeRemaining".Translate() + ": " + daysRemaining.ToString("F1") + " " + "Days".Translate(); + if (hoursRemaining > 0.1f && daysRemaining < 1f) + { + timeText += " (" + hoursRemaining.ToString("F1") + " " + "Hours".Translate() + ")"; + } + builder.Append(timeText); + + // 显示速度和质量(简化版本) + builder.AppendLine(); + builder.Append("Speed".Translate() + ": " + SpeedMultiplier.ToStringPercent() + ", " + + "Quality".Translate() + ": " + QualityMultiplier.ToStringPercent()); + } + else if (assignedLarva != null) + { + if (builder.Length > 0) builder.AppendLine(); + if (larvaOperateTicksRemaining > 0) + { + float secondsRemaining = larvaOperateTicksRemaining / 60f; + builder.Append("LarvaIsOperating".Translate() + ": " + secondsRemaining.ToString("F1") + " " + "SRemaining".Translate()); + } + else + { + builder.Append("LarvaIsOnTheWay".Translate()); + } + } + else if (!isIncubating) + { + var config = IncubatorData?.SelectedConfig; + if (config != null) + { + if (builder.Length > 0) builder.AppendLine(); + builder.Append("Target".Translate() + ": " + config.pawnKind.LabelCap); + + // 只显示当前乘数,不显示条件详情 + builder.AppendLine(); + builder.Append("SpeedMultiplier".Translate() + ": " + SpeedMultiplier.ToStringPercent() + ", " + + "QualityMultiplier".Translate() + ": " + QualityMultiplier.ToStringPercent()); + } + } + + return builder.ToString().TrimEndNewlines(); + } + + // 获取剩余时间(ticks) + public float GetRemainingTicks() + { + if (!isIncubating || incubationDuration <= incubationProgress) return 0f; + + float remainingProgress = incubationDuration - incubationProgress; + float currentSpeed = SpeedMultiplier; + + // 如果速度为0或负数,返回一个很大的数 + if (currentSpeed <= 0) return float.MaxValue; + + return remainingProgress / currentSpeed; + } + + // 获取剩余天数 + public float GetRemainingDays() + { + return GetRemainingTicks() / 60000f; // RimWorld中1天=60000ticks + } + + // 获取剩余小时 + public float GetRemainingHours() + { + float remainingTicks = GetRemainingTicks(); + return (remainingTicks % 60000f) / 2500f; + } + + // Gizmos + public override IEnumerable GetGizmos() + { + foreach (var gizmo in base.GetGizmos()) + { + yield return gizmo; + } + + // 只有玩家派系才显示Gizmo + if (Faction == Faction.OfPlayer) + { + // 不在孵化中时才显示切换目标按钮 + if (!isIncubating && IncubatorData?.IncubationConfigs?.Count > 0) + { + yield return CreateTargetSwitchGizmo(); + } + + // 不在孵化中且研究完成时才显示呼叫按钮 + var config = IncubatorData?.SelectedConfig; + if (!isIncubating && config != null && config.IsResearchComplete) + { + yield return new Command_Action + { + defaultLabel = "CallLarva".Translate(), + defaultDesc = BuildCallLarvaDescription(config), + icon = ContentFinder.Get("UI/Commands/CallLarva", false) ?? BaseContent.BadTex, + action = CallLarva, + hotKey = KeyBindingDefOf.Misc3 + }; + } + + // 如果正在孵化,显示取消按钮 + if (isIncubating) + { + yield return new Command_Action + { + defaultLabel = "CancelIncubation".Translate(), + defaultDesc = "CancelIncubationDesc".Translate(), + icon = ContentFinder.Get("UI/Commands/Cancel", false) ?? TexCommand.ClearPrioritizedWork, + action = CancelIncubation + }; + } + } + } + + // 创建目标切换Gizmo + private Gizmo CreateTargetSwitchGizmo() + { + var configs = IncubatorData?.IncubationConfigs; + if (configs == null || configs.Count == 0) return null; + + var props = IncubatorData?.props as CompProperties_IncubatorData; + var selectedConfig = IncubatorData?.SelectedConfig; + + var switchButton = new Command_Action + { + defaultLabel = BuildSwitchButtonLabel(selectedConfig, props), + defaultDesc = BuildSwitchButtonDescription(selectedConfig, props), + icon = LoadIcon(selectedConfig?.buttonIconPath ?? props?.defaultIconPath), + action = ShowSelectionMenu, + hotKey = KeyBindingDefOf.Misc2 + }; + + // 如果当前配置不可用(研究未完成),禁用按钮 + if (selectedConfig != null && !selectedConfig.IsResearchComplete) + { + if (selectedConfig.requiredResearch != null) + { + switchButton.Disable($"Requires research: {selectedConfig.requiredResearch.LabelCap}"); + } + } + + return switchButton; + } + + // 构建切换按钮标签 + private string BuildSwitchButtonLabel(IncubationConfig config, CompProperties_IncubatorData props) + { + if (config != null && config.pawnKind != null) + { + return (props?.buttonLabel ?? "Incubate: {0}").Translate(config.pawnKind.LabelCap); + } + return (props?.buttonLabel ?? "Incubate: {0}").Translate("None"); + } + + // 构建切换按钮描述 + private string BuildSwitchButtonDescription(IncubationConfig config, CompProperties_IncubatorData props) + { + var builder = new StringBuilder(); + + // 第一部分:按钮功能说明 + builder.AppendLine((props?.buttonDesc ?? "IncubatorButtonDesc").Translate()); + builder.AppendLine(); + + if (config != null) + { + // 第二部分:当前选择的详细信息 + if (config.pawnKind != null) + { + builder.AppendLine($"IncubatorButtonLabel".Translate(config.pawnKind.LabelCap)); + if (!string.IsNullOrEmpty(config.pawnKind.description)) + { + builder.AppendLine(config.pawnKind.description); + } + } + + builder.AppendLine($"IncubationTime".Translate(config.daysRequired)); + + if (config.requiredResearch != null) + { + if (config.requiredResearch.IsFinished) + builder.AppendLine($"Research: {config.requiredResearch.LabelCap} (Completed)"); + else + builder.AppendLine($"Research: {config.requiredResearch.LabelCap} (Required)"); + } + } + else + { + builder.AppendLine("No target selected"); + } + + builder.AppendLine(); + builder.AppendLine("IncubatorButtonDesc".Translate()); + + return builder.ToString().TrimEndNewlines(); + } + + // 加载图标 + private Texture2D LoadIcon(string path) + { + if (!string.IsNullOrEmpty(path)) + { + var icon = ContentFinder.Get(path, false); + if (icon != null) return icon; + } + return ContentFinder.Get("UI/Commands/Default", false) ?? BaseContent.BadTex; + } + + // 显示选择菜单 + private void ShowSelectionMenu() + { + var configs = IncubatorData?.IncubationConfigs; + var props = IncubatorData?.props as CompProperties_IncubatorData; + if (configs == null || configs.Count == 0) return; + + var options = new List(); + int currentIndex = IncubatorData.GetSelectedIndex(); + + for (int i = 0; i < configs.Count; i++) + { + int index = i; // 捕获索引 + var config = configs[i]; + if (config == null || config.pawnKind == null) continue; + + string label = config.pawnKind.LabelCap; + string description = config.GetDescription(); + + // 标记当前选择的项目 + string prefix = (i == currentIndex) ? "✓ " : " "; + + // 创建选项 + var option = new FloatMenuOption( + prefix + label, + () => SwitchToConfig(index) + ); + + // 设置工具提示 + option.tooltip = description; + + // 如果研究未完成,禁用选项 + if (!config.IsResearchComplete) + { + option.Label = prefix + label; + option.Disabled = true; + option.tooltip = description + "\n\n "+ "ResearchRequired".Translate() + " " + config.requiredResearch.LabelCap; + } + + options.Add(option); + } + + if (options.Count > 0) + { + Find.WindowStack.Add(new FloatMenu(options, + (props?.menuTitle ?? "Select Incubation Target").Translate())); + } + } + + // 切换到特定配置 + private void SwitchToConfig(int index) + { + if (IncubatorData != null) + { + IncubatorData.SwitchToConfig(index); + var config = IncubatorData.SelectedConfig; + if (config != null && config.pawnKind != null) + { + Messages.Message($"Incubation target switched to: {config.pawnKind.LabelCap}", + this, MessageTypeDefOf.SilentInput); + } + } + } + + // 查找幼虫 - 现在通过种族查找 + private Pawn FindLarva() + { + // 查找地图中属于玩家派系的ArachnaeBase_Race_Larva幼虫 + var map = Map; + if (map == null) return null; + + foreach (var pawn in map.mapPawns.SpawnedPawnsInFaction(Faction)) + { + // 检查pawn种族是否为幼虫 + if (pawn.def.defName == "ArachnaeBase_Race_Larva") + { + // 检查pawn是否能够移动且没有其他重要任务 + if (!pawn.Downed && !pawn.InMentalState && + pawn.mindState != null && + (pawn.CurJob == null || pawn.CurJob.def != ARA_JobDefOf.ARA_OperateIncubator)) + { + return pawn; + } + } + } + + return null; + } + + // 孵化进度 - 使用override而不是new + protected override void Tick() + { + base.Tick(); + + // 减少操作剩余时间(仅用于显示) + if (larvaOperateTicksRemaining > 0) + { + larvaOperateTicksRemaining--; + // 注意:孵化启动现在由JobDriver触发,这里只更新显示 + } + + // 更新孵化进度和质量进度 + if (isIncubating) + { + // 更新质量乘数(与速度乘数同步更新) + if (lastMultiplierUpdateTick < 0 || Find.TickManager.TicksGame - lastMultiplierUpdateTick >= MultiplierUpdateInterval) + { + UpdateSpeedMultiplier(); + UpdateQualityMultiplier(); + } + + // 应用速度乘数 + float currentSpeed = SpeedMultiplier; + incubationProgress += currentSpeed; + + // 质量进度与速度同步增长,但乘以质量乘数 + qualityProgress += currentSpeed * QualityMultiplier; + + if (incubationProgress >= incubationDuration) + { + CompleteIncubation(); + } + } + } + + // 应用质量效果到生成的pawn + private void ApplyQualityEffects(Pawn pawn, float qualityPercent) + { + //// 质量影响:根据质量百分比调整pawn的属性 + //if (qualityPercent < 1.0f) + //{ + // // 1. 健康影响 + // float healthFactor = Mathf.Lerp(0.5f, 1.0f, qualityPercent); + // if (healthFactor < 1.0f) + // { + // // 减少最大生命值 + // foreach (var part in pawn.health.hediffSet.GetNotMissingParts()) + // { + // var healthDiff = part.def.GetMaxHealth(pawn) * (1.0f - healthFactor); + // if (healthDiff > 0) + // { + // pawn.health.AddHediff(HediffDefOf.Bruise, part); + // } + // } + // } + + // // 2. 年龄影响(如果质量低,pawn可能会以较大年龄出生) + // if (qualityPercent < 0.6f) + // { + // float ageBonus = (1.0f - qualityPercent) * 5; // 最多增加5岁 + // pawn.ageTracker.AgeBiologicalTicks += (long)(ageBonus * 3600000f); // 每岁约3600000ticks + // } + + // // 3. 能力影响 + // if (pawn.skills != null && qualityPercent < 0.8f) + // { + // float skillFactor = Mathf.Lerp(0.5f, 1.0f, qualityPercent); + // foreach (var skill in pawn.skills.skills) + // { + // skill.levelInt = Mathf.RoundToInt(skill.levelInt * skillFactor); + // } + // } + //} + } + + // 检查是否在孵化间中 + private bool IsInIncubatorRoom() + { + var room = this.GetRoom(); + if (room == null) return false; + + return room.Role != null && room.Role.defName == "ARA_Incubator_Room"; + } + + // 计算周围4格内的营养液数量 + private int CountNearbyNutrientSolutions() + { + var map = Map; + if (map == null) return 0; + + int count = 0; + int radius = 4; // 4格半径 + + // 检查周围4格范围内的所有单元格 + for (int x = -radius; x <= radius; x++) + { + for (int y = -radius; y <= radius; y++) + { + IntVec3 cell = Position + new IntVec3(x, 0, y); + + // 检查单元格是否在地图内 + if (cell.InBounds(map)) + { + // 检查地形是否为营养液 + TerrainDef terrain = map.terrainGrid.TerrainAt(cell); + if (terrain != null && terrain.defName == "ARA_Incubator_Nutrient_Solution") + { + count++; + } + } + } + } + + return count; + } + + // 计算房间质量因子 + private float GetRoomQualityFactor() + { + var room = this.GetRoom(); + if (room == null) return 1.0f; + + // 获取房间的ARA_IncubatorQualityFactor统计值 + // 如果没有这个统计,返回默认值 + var statDef = DefDatabase.GetNamedSilentFail("ARA_IncubatorQualityFactor"); + if (statDef != null) + { + return room.GetStat(statDef); + } + + return 1.0f; + } + + // 计算附近其他Ootheca的数量 + private int CountNearbyOtherOothecas() + { + var map = Map; + if (map == null) return 0; + + int count = 0; + var allBuildings = map.listerThings.ThingsOfDef(this.def); + + foreach (var building in allBuildings) + { + // 排除自己 + if (building == this) continue; + + // 检查是否为ARA_Pawn_Ootheca + if (building.def.defName == "ARA_Pawn_Ootheca") + { + // 检查是否在同一个房间或附近 + var room = building.GetRoom(); + if (room != null) + { + // 如果在同一个房间,或者距离较近(10格内) + float distance = building.Position.DistanceTo(this.Position); + if (room == this.GetRoom() || distance <= 10f) + { + count++; + } + } + } + } + + return count; + } + + // 更新速度乘数 + private void UpdateSpeedMultiplier() + { + float multiplier = 1.0f; + + // 1. 检查是否在孵化间中 + if (!IsInIncubatorRoom()) + { + multiplier *= 0.8f; // 不在孵化间中,速度80% + } + + // 2. 计算周围营养液的加成 + int nutrientSolutionCount = CountNearbyNutrientSolutions(); + float nutrientBonus = 1.0f + (nutrientSolutionCount * 0.01f); // 每个+1% + + multiplier *= nutrientBonus; + + speedMultiplier = multiplier; + lastMultiplierUpdateTick = Find.TickManager.TicksGame; + } + + // 更新质量乘数 + private void UpdateQualityMultiplier() + { + float multiplier = 1.0f; + + // 1. 建筑血量损失百分比 + float healthPercent = (float)HitPoints / MaxHitPoints; + multiplier *= healthPercent; + + // 2. 房间的ARA_IncubatorQualityFactor + float roomFactor = GetRoomQualityFactor(); + multiplier *= roomFactor; + + // 3. 附近每一个ARA_Pawn_Ootheca,每一个-10% + int nearbyOothecaCount = CountNearbyOtherOothecas(); + float oothecaPenalty = Mathf.Max(0f, 1.0f - (nearbyOothecaCount * 0.10f)); // 最多减到0 + multiplier *= oothecaPenalty; + + // 确保乘数在合理范围内(0-1) + qualityMultiplier = Mathf.Clamp(multiplier, 0f, 1.0f); + } + + // 保存/加载 + public override void ExposeData() + { + base.ExposeData(); + + Scribe_Values.Look(ref isIncubating, "isIncubating", false); + Scribe_Values.Look(ref incubationProgress, "incubationProgress", 0f); + Scribe_Values.Look(ref incubationDuration, "incubationDuration", 0f); + Scribe_Defs.Look(ref incubatingPawnKind, "incubatingPawnKind"); + Scribe_References.Look(ref assignedLarva, "assignedLarva"); + Scribe_Values.Look(ref larvaOperateTicksRemaining, "larvaOperateTicksRemaining", 0); + Scribe_Values.Look(ref speedMultiplier, "speedMultiplier", 1.0f); + Scribe_Values.Look(ref lastMultiplierUpdateTick, "lastMultiplierUpdateTick", -1); + Scribe_Values.Look(ref qualityMultiplier, "qualityMultiplier", 1.0f); + Scribe_Values.Look(ref qualityProgress, "qualityProgress", 0f); + Scribe_Values.Look(ref qualityTotal, "qualityTotal", 0f); + } + } +} diff --git a/Source/ArachnaeSwarm/Buildings/Building_Ootheca/CompProperties_IncubatorData.cs b/Source/ArachnaeSwarm/Buildings/Building_Ootheca/CompProperties_IncubatorData.cs new file mode 100644 index 0000000..c4608bb --- /dev/null +++ b/Source/ArachnaeSwarm/Buildings/Building_Ootheca/CompProperties_IncubatorData.cs @@ -0,0 +1,178 @@ +using System.Collections.Generic; +using System.Text; +using RimWorld; +using UnityEngine; +using Verse; + +namespace ArachnaeSwarm +{ + // IncubationConfig 保持不变 + public class IncubationConfig : IExposable + { + public PawnKindDef pawnKind; + public ResearchProjectDef requiredResearch; + public float daysRequired; + public string buttonIconPath; + + public IncubationConfig() { } + + public IncubationConfig(PawnKindDef pawnKind, ResearchProjectDef requiredResearch = null, + float daysRequired = 1f, string buttonIconPath = null) + { + this.pawnKind = pawnKind; + this.requiredResearch = requiredResearch; + this.daysRequired = daysRequired; + this.buttonIconPath = buttonIconPath; + } + + public void ExposeData() + { + Scribe_Defs.Look(ref pawnKind, "pawnKind"); + Scribe_Defs.Look(ref requiredResearch, "requiredResearch"); + Scribe_Values.Look(ref daysRequired, "daysRequired", 1f); + Scribe_Values.Look(ref buttonIconPath, "buttonIconPath"); + } + + // 检查是否满足研究要求 + public bool IsResearchComplete => requiredResearch == null || requiredResearch.IsFinished; + + // 在配置描述中使用翻译 + public string GetDescription() + { + var builder = new StringBuilder(); + builder.AppendLine(pawnKind?.description ?? "NoDescriptionAvailable".Translate()); + builder.AppendLine(); + builder.AppendLine("IncubationTime".Translate() + " " + daysRequired + " " + "DaysRequired".Translate()); + + if (requiredResearch != null) + { + if (requiredResearch.IsFinished) + builder.AppendLine("Research".Translate() + ": " + requiredResearch.LabelCap + " (" + "Completed".Translate() + ")"); + else + builder.AppendLine("Research".Translate() + ": " + requiredResearch.LabelCap + " (" + "Required".Translate() + ")"); + } + + return builder.ToString().TrimEndNewlines(); + } + } + + // 组件属性定义 + public class CompProperties_IncubatorData : CompProperties + { + public List incubationConfigs = new List(); + + // 默认选择索引 + public int defaultIndex = 0; + + // Gizmo相关配置 + public string buttonLabel = "IncubatorButtonLabel"; // 按钮标签翻译键 + public string buttonDesc = "IncubatorButtonDesc"; // 按钮描述翻译键 + public string menuTitle = "IncubatorMenuTitle"; // 菜单标题翻译键 + public string defaultIconPath = "UI/Commands/Default"; + + public CompProperties_IncubatorData() + { + compClass = typeof(CompIncubatorData); + } + + public override void ResolveReferences(ThingDef parentDef) + { + base.ResolveReferences(parentDef); + if (incubationConfigs == null) + incubationConfigs = new List(); + } + } + + // 数据存储组件(仅数据,无Gizmo) + public class CompIncubatorData : ThingComp + { + private CompProperties_IncubatorData Props => (CompProperties_IncubatorData)props; + + // 当前选择的配置索引 + private int selectedIndex = -1; + + // 公开获取孵化配置列表的方法 + public List IncubationConfigs => Props?.incubationConfigs ?? new List(); + + // 获取当前选择的配置 + public IncubationConfig SelectedConfig + { + get + { + var configs = IncubationConfigs; + if (configs.Count == 0) return null; + + // 初始化选择 + if (selectedIndex == -1) + { + selectedIndex = Mathf.Clamp(Props.defaultIndex, 0, configs.Count - 1); + } + + if (selectedIndex < 0 || selectedIndex >= configs.Count) + selectedIndex = 0; + + return configs[selectedIndex]; + } + } + + // 获取当前选择的PawnKind + public PawnKindDef SelectedPawnKind => SelectedConfig?.pawnKind; + + // 切换到特定索引 + public void SwitchToConfig(int index) + { + if (index >= 0 && index < IncubationConfigs.Count) + { + selectedIndex = index; + } + } + + // 检查配置是否可用(研究是否完成) + public bool IsConfigAvailable(int index) + { + if (index < 0 || index >= IncubationConfigs.Count) + return false; + + var config = IncubationConfigs[index]; + return config?.IsResearchComplete ?? false; + } + + // 获取配置索引 + public int GetSelectedIndex() + { + return selectedIndex; + } + + // 存档加载 + public override void PostExposeData() + { + base.PostExposeData(); + Scribe_Values.Look(ref selectedIndex, "selectedIndex", -1); + + if (Scribe.mode == LoadSaveMode.PostLoadInit) + { + // 验证索引有效性 + if (selectedIndex >= IncubationConfigs.Count) + selectedIndex = Mathf.Clamp(Props.defaultIndex, 0, IncubationConfigs.Count - 1); + } + } + + // 在建筑信息中显示额外信息 + public override string CompInspectStringExtra() + { + var current = SelectedConfig; + if (current != null) + { + string status = "IncubationTarget".Translate() + current.pawnKind.LabelCap; + + if (current.requiredResearch != null && !current.requiredResearch.IsFinished) + { + status += " (" + "Requires".Translate() + ": " + current.requiredResearch.LabelCap + ")"; + } + + return status; + } + return base.CompInspectStringExtra(); + } + } +} diff --git a/Source/ArachnaeSwarm/Buildings/Building_Ootheca/ITab_Ootheca_Incubation.cs b/Source/ArachnaeSwarm/Buildings/Building_Ootheca/ITab_Ootheca_Incubation.cs new file mode 100644 index 0000000..6309151 --- /dev/null +++ b/Source/ArachnaeSwarm/Buildings/Building_Ootheca/ITab_Ootheca_Incubation.cs @@ -0,0 +1,369 @@ +using UnityEngine; +using Verse; +using System.Collections.Generic; +using System.Linq; +using RimWorld; +using Verse.Sound; + +namespace ArachnaeSwarm +{ + public class ITab_Ootheca_Incubation : ITab + { + private const float BarHeight = 20f; + private const float Margin = 10f; + private const float LabelHeight = 30f; + private const float SmallLabelHeight = 20f; + private const float ButtonHeight = 25f; + private const float TabWidth = 320f; + private const float TabHeight = 420f; // 增加标签页高度以容纳更多间距 + + private Vector2 scrollPosition = Vector2.zero; + private const float ViewHeight = 450f; // 增加滚动区域高度 + + public override bool IsVisible + { + get + { + // 只显示给玩家派系 + return SelThing.Faction == Faction.OfPlayer; + } + } + + public ITab_Ootheca_Incubation() + { + size = new Vector2(TabWidth, TabHeight); + labelKey = "IncubationTab"; + tutorTag = "Incubation"; + } + + protected override void FillTab() + { + + Rect rect = new Rect(0f, 0f, size.x, size.y).ContractedBy(Margin); + Widgets.DrawMenuSection(rect); + Building_Ootheca ootheca = SelThing as Building_Ootheca; + if (ootheca == null) + { + Widgets.Label(rect, "NotAnOotheca".Translate()); + return; + } + rect = rect.ContractedBy(5f); + + // 创建一个可滚动区域 + Rect viewRect = new Rect(0f, 0f, rect.width - 16f, ViewHeight); + Rect scrollRect = new Rect(rect.x, rect.y, rect.width, rect.height); + + Widgets.BeginScrollView(scrollRect, ref scrollPosition, viewRect); + + float curY = 0f; + + // 显示标题 + Rect titleRect = new Rect(0f, curY, viewRect.width, LabelHeight); + string title = "IncubationProgress".Translate(); + Text.Font = GameFont.Medium; + Widgets.Label(titleRect, title); + Text.Font = GameFont.Small; + curY += LabelHeight + 15f; + // 显示速度和质量乘数按钮 + float buttonWidth = (viewRect.width - 10f) / 2f; + + // 速度按钮 + Rect speedButtonRect = new Rect(0f, curY, buttonWidth, ButtonHeight); + string speedText = "Speed".Translate() + ": " + ootheca.SpeedMultiplier.ToStringPercent(); + + // 设置按钮颜色 + Color speedColor = Color.white; + if (ootheca.SpeedMultiplier != 1.0f) + { + speedColor = ootheca.SpeedMultiplier > 1.0f ? + new Color(0.2f, 0.8f, 0.2f) : + new Color(0.8f, 0.8f, 0.2f); + } + + // 绘制速度按钮 + if (Widgets.ButtonText(speedButtonRect, speedText, true, true, speedColor)) + { + // 按钮点击操作(可选) + // 可以在这里显示详细信息的窗口 + } + + // 为速度按钮添加工具提示 + TooltipHandler.TipRegion(speedButtonRect, () => ootheca.GetSpeedFactorsDescription(), 987654321); + + // 质量按钮 + Rect qualityButtonRect = new Rect(buttonWidth + 10f, curY, buttonWidth, ButtonHeight); + string qualityText = "Quality".Translate() + ": " + ootheca.QualityMultiplier.ToStringPercent(); + + // 设置按钮颜色 + Color qualityColor = Color.white; + float qualityMultiplier = ootheca.QualityMultiplier; + if (qualityMultiplier != 1.0f) + { + if (qualityMultiplier > 0.9f) + qualityColor = new Color(0.2f, 0.8f, 0.2f); + else if (qualityMultiplier > 0.7f) + qualityColor = new Color(0.8f, 0.8f, 0.2f); + else if (qualityMultiplier > 0.5f) + qualityColor = new Color(0.9f, 0.6f, 0.2f); + else + qualityColor = new Color(0.8f, 0.2f, 0.2f); + } + + // 绘制质量按钮 + if (Widgets.ButtonText(qualityButtonRect, qualityText, true, true, qualityColor)) + { + // 按钮点击操作(可选) + // 可以在这里显示详细信息的窗口 + } + + // 为质量按钮添加工具提示 + TooltipHandler.TipRegion(qualityButtonRect, () => ootheca.GetQualityFactorsDescription(), 987654322); + + curY += ButtonHeight + 25f; // 增加按钮与目标显示之间的间距 + + // 如果在孵化中,显示进度条 + if (ootheca.isIncubating && ootheca.incubatingPawnKind != null) + { + // 使用调整后的进度百分比 + float progressPercent = ootheca.AdjustedProgressPercent; + float qualityPercent = ootheca.QualityPercent; + + // 获取调整后的剩余时间 + float daysRemaining = ootheca.GetRemainingDays(); + float hoursRemaining = ootheca.GetRemainingHours(); + + // 显示目标 + Rect targetRect = new Rect(0f, curY, viewRect.width, SmallLabelHeight); + Widgets.Label(targetRect, "Target".Translate() + ": " + ootheca.incubatingPawnKind.LabelCap); + + curY += SmallLabelHeight + 20f; + + // 显示孵化进度条 + Rect progressBarRect = new Rect(0f, curY, viewRect.width, BarHeight); + + // 在进度条正上方居中添加标签 + Rect progressLabelRect = new Rect(progressBarRect.x, progressBarRect.y - 20, progressBarRect.width, 18); + Text.Anchor = TextAnchor.MiddleCenter; + GUI.color = new Color(0.9f, 0.9f, 0.9f, 1f); + Widgets.Label(progressLabelRect, "IncubationProgressLabel".Translate()); + Text.Anchor = TextAnchor.UpperLeft; + GUI.color = Color.white; + + // 绘制进度条 + Widgets.FillableBar(progressBarRect, progressPercent, SolidColorMaterials.NewSolidColorTexture(new Color(0.2f, 0.8f, 0.2f, 0.5f))); + Widgets.FillableBarChangeArrows(progressBarRect, progressPercent); + + // 进度条文字(百分比) + string progressText = $"{progressPercent:P0}"; + Text.Anchor = TextAnchor.MiddleCenter; + Widgets.Label(progressBarRect, progressText); + Text.Anchor = TextAnchor.UpperLeft; + + curY += BarHeight + 30f; // 增加孵化条与质量条之间的间距 + + // 显示质量进度条 + Rect qualityBarRect = new Rect(0f, curY, viewRect.width, BarHeight); + + // 在质量进度条正上方居中添加标签 + Rect qualityLabelRect = new Rect(qualityBarRect.x, qualityBarRect.y - 20, qualityBarRect.width, 18); + Text.Anchor = TextAnchor.MiddleCenter; + GUI.color = new Color(0.9f, 0.9f, 0.9f, 1f); + Widgets.Label(qualityLabelRect, "QualityProgress".Translate()); + Text.Anchor = TextAnchor.UpperLeft; + GUI.color = Color.white; + + // 绘制质量进度条 + Widgets.FillableBar(qualityBarRect, qualityPercent, SolidColorMaterials.NewSolidColorTexture(new Color(0.1f, 0.4f, 0.8f, 0.5f))); + + // 质量进度条文字(百分比) + string qualityProgressText = $"{qualityPercent:P0}"; + Text.Anchor = TextAnchor.MiddleCenter; + Widgets.Label(qualityBarRect, qualityProgressText); + Text.Anchor = TextAnchor.UpperLeft; + + // 在质量进度条上显示目标质量百分比 + Rect targetQualityRect = new Rect(qualityBarRect.x + qualityBarRect.width - 40, qualityBarRect.y, 40, BarHeight); + GUI.color = new Color(0.8f, 0.8f, 0.8f, 0.7f); + Text.Anchor = TextAnchor.MiddleRight; + Widgets.Label(targetQualityRect, $"{ootheca.QualityMultiplier:P0}"); + Text.Anchor = TextAnchor.UpperLeft; + GUI.color = Color.white; + + curY += BarHeight + 25f; // 增加质量条与剩余时间之间的间距 + + // 显示剩余时间 + Rect timeRect = new Rect(0f, curY, viewRect.width, SmallLabelHeight); + string timeText = "TimeRemaining".Translate() + ": " + daysRemaining.ToString("F1") + " " + "Days".Translate(); + if (hoursRemaining > 0.1f && daysRemaining < 1f) + { + timeText += " (" + hoursRemaining.ToString("F1") + " " + "Hours".Translate() + ")"; + } + Widgets.Label(timeRect, timeText); + } + else if (ootheca.assignedLarva != null) + { + // 如果有幼虫正在路上或操作中 + Rect statusRect = new Rect(0f, curY, viewRect.width, SmallLabelHeight * 2); + if (ootheca.larvaOperateTicksRemaining > 0) + { + float secondsRemaining = ootheca.larvaOperateTicksRemaining / 60f; + Widgets.Label(statusRect, "LarvaIsActivatingOotheca".Translate() + "\n" + + secondsRemaining.ToString("F1") + " " + "SecondsRemaining".Translate()); + } + else + { + Widgets.Label(statusRect, "LarvaIsOnTheWay".Translate()); + } + + // 为幼虫状态增加一些间距 + curY += SmallLabelHeight * 2 + 15f; + + // 不在孵化中时,也显示目标配置 + var config = ootheca.IncubatorData?.SelectedConfig; + if (config != null) + { + curY += 10f; + Rect targetRect = new Rect(0f, curY, viewRect.width, SmallLabelHeight * 3); + string targetText = "ReadyToIncubate".Translate() + "\n" + config.pawnKind.LabelCap; + + if (!config.IsResearchComplete && config.requiredResearch != null) + { + targetText += "\n" + "(" + "Requires".Translate() + ": " + config.requiredResearch.LabelCap + ")"; + } + + Widgets.Label(targetRect, targetText); + } + } + else + { + // 不在孵化中,显示当前选择的目标 + var config = ootheca.IncubatorData?.SelectedConfig; + if (config != null) + { + Rect targetRect = new Rect(0f, curY, viewRect.width, SmallLabelHeight * 3); + string targetText = "ReadyToIncubate".Translate() + "\n" + config.pawnKind.LabelCap; + + if (!config.IsResearchComplete && config.requiredResearch != null) + { + targetText += "\n" + "(" + "Requires".Translate() + ": " + config.requiredResearch.LabelCap + ")"; + } + + Widgets.Label(targetRect, targetText); + + // 为静态显示增加一些底部间距 + curY += SmallLabelHeight * 3 + 10f; + } + else + { + Rect noTargetRect = new Rect(0f, curY, viewRect.width, SmallLabelHeight); + Widgets.Label(noTargetRect, "NoIncubationTargetSelected".Translate()); + + curY += SmallLabelHeight + 10f; + } + } + + // 添加一些底部空白区域,让布局看起来不那么拥挤 + curY += 20f; + + // 更新滚动区域的实际高度 + viewRect.height = curY; + + Widgets.EndScrollView(); + } + + // 检查是否在孵化间中(ITab内部使用) + private bool IsInIncubatorRoom(Building_Ootheca ootheca) + { + var room = ootheca.GetRoom(); + if (room == null) return false; + + return room.Role != null && room.Role.defName == "ARA_Incubator_Room"; + } + + // 计算周围4格内的营养液数量(ITab内部使用) + private int CountNearbyNutrientSolutions(Building_Ootheca ootheca) + { + var map = ootheca.Map; + if (map == null) return 0; + + int count = 0; + int radius = 4; // 4格半径 + var position = ootheca.Position; + + // 检查周围4格范围内的所有单元格 + for (int x = -radius; x <= radius; x++) + { + for (int y = -radius; y <= radius; y++) + { + IntVec3 cell = position + new IntVec3(x, 0, y); + + // 检查单元格是否在地图内 + if (cell.InBounds(map)) + { + // 检查地形是否为营养液 + TerrainDef terrain = map.terrainGrid.TerrainAt(cell); + if (terrain != null && terrain.defName == "ARA_Incubator_Nutrient_Solution") + { + count++; + } + } + } + } + + return count; + } + + // 计算房间质量因子(ITab内部使用) + private float GetRoomQualityFactor(Building_Ootheca ootheca) + { + var room = ootheca.GetRoom(); + if (room == null) return 1.0f; + + // 获取房间的ARA_IncubatorQualityFactor统计值 + var statDef = DefDatabase.GetNamedSilentFail("ARA_IncubatorQualityFactor"); + if (statDef != null) + { + return room.GetStat(statDef); + } + + return 1.0f; + } + + // 计算附近其他Ootheca的数量(ITab内部使用) + private int CountNearbyOtherOothecas(Building_Ootheca ootheca) + { + var map = ootheca.Map; + if (map == null) return 0; + + int count = 0; + var position = ootheca.Position; + var room = ootheca.GetRoom(); + + // 查找地图上所有的Ootheca + var allOothecas = map.listerThings.ThingsOfDef(ootheca.def); + + foreach (var thing in allOothecas) + { + // 排除自己 + if (thing == ootheca) continue; + + // 检查是否在同一个房间或附近(10格内) + float distance = thing.Position.DistanceTo(position); + var otherRoom = thing.GetRoom(); + + if ((room != null && room == otherRoom) || distance <= 10f) + { + count++; + } + } + + return count; + } + + protected override void UpdateSize() + { + // 固定大小 + size = new Vector2(TabWidth, TabHeight); + } + } +} diff --git a/Source/ArachnaeSwarm/Buildings/Building_Ootheca/JobDriver_OperateIncubator.cs b/Source/ArachnaeSwarm/Buildings/Building_Ootheca/JobDriver_OperateIncubator.cs new file mode 100644 index 0000000..ae3ccae --- /dev/null +++ b/Source/ArachnaeSwarm/Buildings/Building_Ootheca/JobDriver_OperateIncubator.cs @@ -0,0 +1,80 @@ +using RimWorld; +using System.Collections.Generic; +using Verse; +using Verse.AI; + +namespace ArachnaeSwarm +{ + public class JobDriver_OperateIncubator : JobDriver + { + private const int OperationDuration = 180; // 3 seconds = 180 ticks + + // 目标建筑 + private Building_Ootheca Ootheca => (Building_Ootheca)job.targetA.Thing; + + public override bool TryMakePreToilReservations(bool errorOnFailed) + { + // 动物和殖民者都可以预订 + return pawn.Reserve(job.targetA, job, 1, -1, null, errorOnFailed); + } + + protected override IEnumerable MakeNewToils() + { + // 验证目标建筑 + this.FailOnDespawnedNullOrForbidden(TargetIndex.A); + this.FailOn(() => Ootheca == null); + + // 1. 移动到建筑 + yield return Toils_Goto.GotoThing(TargetIndex.A, PathEndMode.InteractionCell) + .FailOnSomeonePhysicallyInteracting(TargetIndex.A); + + // 2. 等待片刻(让动物有时间转身) + yield return Toils_General.WaitWith(TargetIndex.A, 10, true, true); + + // 3. 开始操作(3秒) + var operate = new Toil(); + operate.initAction = () => + { + // 通知建筑幼虫已到达 + Ootheca?.NotifyLarvaArrived(pawn); + }; + operate.tickAction = () => + { + // 面向建筑 + pawn.rotationTracker.FaceCell(Ootheca.Position); + }; + operate.defaultCompleteMode = ToilCompleteMode.Delay; + operate.defaultDuration = OperationDuration; + operate.WithProgressBar(TargetIndex.A, () => + (float)(OperationDuration - operate.actor.jobs.curDriver.ticksLeftThisToil) / OperationDuration); + yield return operate; + + // 4. 完成操作并删除幼虫 + yield return new Toil + { + initAction = () => + { + // 操作完成,删除幼虫并开始孵化 + if (Ootheca != null && pawn != null && pawn.def.defName == "ArachnaeBase_Race_Larva") + { + // 通知建筑幼虫操作完成 + Ootheca.NotifyLarvaOperationComplete(pawn); + + // 删除幼虫 + pawn.Destroy(DestroyMode.Vanish); + } + }, + defaultCompleteMode = ToilCompleteMode.Instant + }; + } + + public override string GetReport() + { + if (Ootheca != null) + { + return "ActivatingOotheca".Translate(); + } + return base.GetReport(); + } + } +} diff --git a/Source/ArachnaeSwarm/Buildings/Building_Ootheca/RoomRoleWorker_Incubator.cs b/Source/ArachnaeSwarm/Buildings/Building_Ootheca/RoomRoleWorker_Incubator.cs new file mode 100644 index 0000000..24da8eb --- /dev/null +++ b/Source/ArachnaeSwarm/Buildings/Building_Ootheca/RoomRoleWorker_Incubator.cs @@ -0,0 +1,51 @@ +using System.Collections.Generic; +using RimWorld; +using Verse; + +namespace ArachnaeSwarm +{ + public class RoomRoleWorker_Incubator : RoomRoleWorker + { + // 孵化间的评分 - 比重很小,设为10分(实验室是60分) + private const float ScorePerIncubator = 10f; + + // 最低分数要求 - 需要至少1个孵化器才能成为孵化间 + private const float MinimumScore = 10f; + + public override float GetScore(Room room) + { + int incubatorCount = 0; + + // 检查房间内和相邻的所有建筑 + List containedAndAdjacentThings = room.ContainedAndAdjacentThings; + for (int i = 0; i < containedAndAdjacentThings.Count; i++) + { + Thing thing = containedAndAdjacentThings[i]; + + // 检查建筑的工作台房间角色是否为ARA_Incubator_Room + if (thing.def.building?.workTableRoomRole != null && + thing.def.building.workTableRoomRole.defName == "ARA_Incubator_Room") + { + incubatorCount++; + } + } + + // 如果有孵化器,返回分数 + float score = ScorePerIncubator * incubatorCount; + + // 必须有至少一个孵化器才能成为孵化间 + return incubatorCount > 0 ? score : 0f; + } + + public override float GetScoreDeltaIfBuildingPlaced(Room room, ThingDef buildingDef) + { + // 如果放置的建筑具有ARA_Incubator_Room工作台房间角色,返回分数增量 + if (buildingDef.building?.workTableRoomRole != null && + buildingDef.building.workTableRoomRole.defName == "ARA_Incubator_Room") + { + return ScorePerIncubator; + } + return 0f; + } + } +}