This commit is contained in:
2025-10-27 17:29:13 +08:00
parent 6e27852688
commit 997f1ecaf8
11 changed files with 347 additions and 127 deletions

Binary file not shown.

Binary file not shown.

View File

@@ -119,6 +119,9 @@
<CleaningTimeFactor>0.6</CleaningTimeFactor>
<Beauty>2</Beauty>
</statBases>
<costList>
<ARA_Carapace>1</ARA_Carapace>
</costList>
<pathCost>10</pathCost>
<constructEffect>ConstructMetal</constructEffect>
<constructionSkillPrerequisite>6</constructionSkillPrerequisite>

View File

@@ -740,7 +740,7 @@
<consumeFuelOnlyWhenUsed>true</consumeFuelOnlyWhenUsed>
<targetFuelLevelConfigurable>true</targetFuelLevelConfigurable>
<showAllowAutoRefuelToggle>true</showAllowAutoRefuelToggle>
<showAllowAutoRefuelToggle>false</showAllowAutoRefuelToggle>
</li>
<li Class="ArachnaeSwarm.CompProperties_NutritionToFuelConverter">
<checkInterval>200</checkInterval>

View File

@@ -134,7 +134,7 @@
</thingDefs>
</fuelFilter>
<fuelGizmoLabel>虫蜜</fuelGizmoLabel>
<showAllowAutoRefuelToggle>true</showAllowAutoRefuelToggle>
<showAllowAutoRefuelToggle>false</showAllowAutoRefuelToggle>
<targetFuelLevelConfigurable>true</targetFuelLevelConfigurable>
<consumeFuelOnlyWhenUsed>true</consumeFuelOnlyWhenUsed>
<destroyOnNoFuel>true</destroyOnNoFuel>
@@ -300,7 +300,7 @@
</thingDefs>
</fuelFilter>
<fuelGizmoLabel>虫蜜</fuelGizmoLabel>
<showAllowAutoRefuelToggle>true</showAllowAutoRefuelToggle>
<showAllowAutoRefuelToggle>false</showAllowAutoRefuelToggle>
<targetFuelLevelConfigurable>true</targetFuelLevelConfigurable>
<consumeFuelOnlyWhenUsed>true</consumeFuelOnlyWhenUsed>
</li>
@@ -441,7 +441,7 @@
</thingDefs>
</fuelFilter>
<fuelGizmoLabel>虫蜜</fuelGizmoLabel>
<showAllowAutoRefuelToggle>true</showAllowAutoRefuelToggle>
<showAllowAutoRefuelToggle>false</showAllowAutoRefuelToggle>
<targetFuelLevelConfigurable>true</targetFuelLevelConfigurable>
<consumeFuelOnlyWhenUsed>true</consumeFuelOnlyWhenUsed>
</li>
@@ -604,7 +604,7 @@
</thingDefs>
</fuelFilter>
<fuelGizmoLabel>虫蜜</fuelGizmoLabel>
<showAllowAutoRefuelToggle>true</showAllowAutoRefuelToggle>
<showAllowAutoRefuelToggle>false</showAllowAutoRefuelToggle>
<targetFuelLevelConfigurable>true</targetFuelLevelConfigurable>
<consumeFuelOnlyWhenUsed>true</consumeFuelOnlyWhenUsed>
</li>

View File

@@ -3,32 +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\\thing\\\u793A\u8303.md||{EFC0BB08-EA7D-40C6-A696-C870411A895B}",
"RelativeMoniker": "D:0:0:{EAE0DB6B-E282-C812-7F5A-6D13E9D24581}|ArachnaeSwarm.csproj|solutionrelative:thing\\\u793A\u8303.md||{EFC0BB08-EA7D-40C6-A696-C870411A895B}"
},
{
"AbsoluteMoniker": "D:0:0:{EAE0DB6B-E282-C812-7F5A-6D13E9D24581}|ArachnaeSwarm.csproj|D:\\SteamLibrary\\steamapps\\common\\RimWorld\\Mods\\ArachnaeSwarm\\Source\\ArachnaeSwarm\\thing\\highaltitudeflyover.cs||{A6C744A8-0E4A-4FC6-886A-064283054674}",
"RelativeMoniker": "D:0:0:{EAE0DB6B-E282-C812-7F5A-6D13E9D24581}|ArachnaeSwarm.csproj|solutionrelative:thing\\highaltitudeflyover.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\\thing\\thingclassflyover.cs||{A6C744A8-0E4A-4FC6-886A-064283054674}",
"RelativeMoniker": "D:0:0:{EAE0DB6B-E282-C812-7F5A-6D13E9D24581}|ArachnaeSwarm.csproj|solutionrelative:thing\\thingclassflyover.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\\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|d:\\steamlibrary\\steamapps\\common\\rimworld\\mods\\arachnaeswarm\\source\\arachnaeswarm\\building_comps\\ara_nutrientvat\\building_nutrientvat.cs||{A6C744A8-0E4A-4FC6-886A-064283054674}",
"RelativeMoniker": "D:0:0:{EAE0DB6B-E282-C812-7F5A-6D13E9D24581}|ArachnaeSwarm.csproj|solutionrelative:building_comps\\ara_nutrientvat\\building_nutrientvat.cs||{A6C744A8-0E4A-4FC6-886A-064283054674}"
},
{
"AbsoluteMoniker": "D:0:0:{EAE0DB6B-E282-C812-7F5A-6D13E9D24581}|ArachnaeSwarm.csproj|D:\\SteamLibrary\\steamapps\\common\\RimWorld\\Mods\\ArachnaeSwarm\\Source\\ArachnaeSwarm\\building_comps\\ara_building_refuelingvat\\building_refuelingvat.cs||{A6C744A8-0E4A-4FC6-886A-064283054674}",
"RelativeMoniker": "D:0:0:{EAE0DB6B-E282-C812-7F5A-6D13E9D24581}|ArachnaeSwarm.csproj|solutionrelative:building_comps\\ara_building_refuelingvat\\building_refuelingvat.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\\ara_hediffterrainspawn\\comphediffterrainspawn.cs||{A6C744A8-0E4A-4FC6-886A-064283054674}",
"RelativeMoniker": "D:0:0:{EAE0DB6B-E282-C812-7F5A-6D13E9D24581}|ArachnaeSwarm.csproj|solutionrelative:hediffs\\ara_hediffterrainspawn\\comphediffterrainspawn.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\\compabilityeffect_randomhediff.cs||{A6C744A8-0E4A-4FC6-886A-064283054674}",
"RelativeMoniker": "D:0:0:{EAE0DB6B-E282-C812-7F5A-6D13E9D24581}|ArachnaeSwarm.csproj|solutionrelative:abilities\\compabilityeffect_randomhediff.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\\powerarmor\\comppowerarmorstation.cs||{A6C744A8-0E4A-4FC6-886A-064283054674}",
"RelativeMoniker": "D:0:0:{EAE0DB6B-E282-C812-7F5A-6D13E9D24581}|ArachnaeSwarm.csproj|solutionrelative:powerarmor\\comppowerarmorstation.cs||{A6C744A8-0E4A-4FC6-886A-064283054674}"
}
],
"DocumentGroupContainers": [
@@ -47,87 +31,39 @@
{
"$type": "Document",
"DocumentIndex": 0,
"Title": "\u793A\u8303.md",
"DocumentMoniker": "D:\\SteamLibrary\\steamapps\\common\\RimWorld\\Mods\\ArachnaeSwarm\\Source\\ArachnaeSwarm\\Thing\\\u793A\u8303.md",
"RelativeDocumentMoniker": "Thing\\\u793A\u8303.md",
"ToolTip": "D:\\SteamLibrary\\steamapps\\common\\RimWorld\\Mods\\ArachnaeSwarm\\Source\\ArachnaeSwarm\\Thing\\\u793A\u8303.md",
"RelativeToolTip": "Thing\\\u793A\u8303.md",
"ViewState": "AgIAAAAAAAAAAAAAAAAAABAAAAAkAAAAAAAAAA==",
"Icon": "ae27a6b0-e345-4288-96df-5eaf394ee369.001818|",
"WhenOpened": "2025-10-27T03:36:17.112Z",
"Title": "Building_NutrientVat.cs",
"DocumentMoniker": "D:\\SteamLibrary\\steamapps\\common\\RimWorld\\Mods\\ArachnaeSwarm\\Source\\ArachnaeSwarm\\Building_Comps\\ARA_NutrientVat\\Building_NutrientVat.cs",
"RelativeDocumentMoniker": "Building_Comps\\ARA_NutrientVat\\Building_NutrientVat.cs",
"ToolTip": "D:\\SteamLibrary\\steamapps\\common\\RimWorld\\Mods\\ArachnaeSwarm\\Source\\ArachnaeSwarm\\Building_Comps\\ARA_NutrientVat\\Building_NutrientVat.cs",
"RelativeToolTip": "Building_Comps\\ARA_NutrientVat\\Building_NutrientVat.cs",
"ViewState": "AgIAAMwAAAAAAAAAAAAswMoAAAAuAAAAAAAAAA==",
"Icon": "ae27a6b0-e345-4288-96df-5eaf394ee369.000738|",
"WhenOpened": "2025-10-27T07:14:35.288Z",
"EditorCaption": ""
},
{
"$type": "Document",
"DocumentIndex": 1,
"Title": "HighAltitudeFlyOver.cs",
"DocumentMoniker": "D:\\SteamLibrary\\steamapps\\common\\RimWorld\\Mods\\ArachnaeSwarm\\Source\\ArachnaeSwarm\\Thing\\HighAltitudeFlyOver.cs",
"RelativeDocumentMoniker": "Thing\\HighAltitudeFlyOver.cs",
"ToolTip": "D:\\SteamLibrary\\steamapps\\common\\RimWorld\\Mods\\ArachnaeSwarm\\Source\\ArachnaeSwarm\\Thing\\HighAltitudeFlyOver.cs",
"RelativeToolTip": "Thing\\HighAltitudeFlyOver.cs",
"ViewState": "AgIAAAAAAAAAAAAAAAAAACMAAAARAAAAAAAAAA==",
"Icon": "ae27a6b0-e345-4288-96df-5eaf394ee369.000738|",
"WhenOpened": "2025-10-27T03:32:08.102Z"
},
{
"$type": "Document",
"DocumentIndex": 2,
"Title": "ThingclassFlyOver.cs",
"DocumentMoniker": "D:\\SteamLibrary\\steamapps\\common\\RimWorld\\Mods\\ArachnaeSwarm\\Source\\ArachnaeSwarm\\Thing\\ThingclassFlyOver.cs",
"RelativeDocumentMoniker": "Thing\\ThingclassFlyOver.cs",
"ToolTip": "D:\\SteamLibrary\\steamapps\\common\\RimWorld\\Mods\\ArachnaeSwarm\\Source\\ArachnaeSwarm\\Thing\\ThingclassFlyOver.cs*",
"RelativeToolTip": "Thing\\ThingclassFlyOver.cs*",
"ViewState": "AgIAAAcAAAAAAAAAAAAgwCEAAAAEAAAAAAAAAA==",
"Title": "CompPowerArmorStation.cs",
"DocumentMoniker": "D:\\SteamLibrary\\steamapps\\common\\RimWorld\\Mods\\ArachnaeSwarm\\Source\\ArachnaeSwarm\\PowerArmor\\CompPowerArmorStation.cs",
"RelativeDocumentMoniker": "PowerArmor\\CompPowerArmorStation.cs",
"ToolTip": "D:\\SteamLibrary\\steamapps\\common\\RimWorld\\Mods\\ArachnaeSwarm\\Source\\ArachnaeSwarm\\PowerArmor\\CompPowerArmorStation.cs",
"RelativeToolTip": "PowerArmor\\CompPowerArmorStation.cs",
"ViewState": "AgIAABoAAAAAAAAAAAAAwBoAAABhAAAAAAAAAA==",
"Icon": "ae27a6b0-e345-4288-96df-5eaf394ee369.000738|",
"WhenOpened": "2025-10-27T03:03:30.662Z"
"WhenOpened": "2025-10-27T06:39:11.711Z"
},
{
"$type": "Document",
"DocumentIndex": 4,
"DocumentIndex": 1,
"Title": "Building_RefuelingVat.cs",
"DocumentMoniker": "D:\\SteamLibrary\\steamapps\\common\\RimWorld\\Mods\\ArachnaeSwarm\\Source\\ArachnaeSwarm\\Building_Comps\\ARA_Building_RefuelingVat\\Building_RefuelingVat.cs",
"RelativeDocumentMoniker": "Building_Comps\\ARA_Building_RefuelingVat\\Building_RefuelingVat.cs",
"ToolTip": "D:\\SteamLibrary\\steamapps\\common\\RimWorld\\Mods\\ArachnaeSwarm\\Source\\ArachnaeSwarm\\Building_Comps\\ARA_Building_RefuelingVat\\Building_RefuelingVat.cs",
"RelativeToolTip": "Building_Comps\\ARA_Building_RefuelingVat\\Building_RefuelingVat.cs",
"ViewState": "AgIAAEQAAAAAAAAAAAAAAEABAAAQAAAAAAAAAA==",
"ViewState": "AgIAAMwAAAAAAAAAAAAAwJIAAAAXAAAAAAAAAA==",
"Icon": "ae27a6b0-e345-4288-96df-5eaf394ee369.000738|",
"WhenOpened": "2025-10-24T06:16:14.743Z"
},
{
"$type": "Document",
"DocumentIndex": 3,
"Title": "Building_ArachnaeGravEngine.cs",
"DocumentMoniker": "D:\\SteamLibrary\\steamapps\\common\\RimWorld\\Mods\\ArachnaeSwarm\\Source\\ArachnaeSwarm\\Buildings\\Building_ArachnaeGravEngine.cs",
"RelativeDocumentMoniker": "Buildings\\Building_ArachnaeGravEngine.cs",
"ToolTip": "D:\\SteamLibrary\\steamapps\\common\\RimWorld\\Mods\\ArachnaeSwarm\\Source\\ArachnaeSwarm\\Buildings\\Building_ArachnaeGravEngine.cs",
"RelativeToolTip": "Buildings\\Building_ArachnaeGravEngine.cs",
"ViewState": "AgIAAHwAAAAAAAAAAAAgwJcAAAAJAAAAAAAAAA==",
"Icon": "ae27a6b0-e345-4288-96df-5eaf394ee369.000738|",
"WhenOpened": "2025-10-24T02:30:45.288Z"
},
{
"$type": "Document",
"DocumentIndex": 5,
"Title": "CompHediffTerrainSpawn.cs",
"DocumentMoniker": "D:\\SteamLibrary\\steamapps\\common\\RimWorld\\Mods\\ArachnaeSwarm\\Source\\ArachnaeSwarm\\Hediffs\\ARA_HediffTerrainSpawn\\CompHediffTerrainSpawn.cs",
"RelativeDocumentMoniker": "Hediffs\\ARA_HediffTerrainSpawn\\CompHediffTerrainSpawn.cs",
"ToolTip": "D:\\SteamLibrary\\steamapps\\common\\RimWorld\\Mods\\ArachnaeSwarm\\Source\\ArachnaeSwarm\\Hediffs\\ARA_HediffTerrainSpawn\\CompHediffTerrainSpawn.cs",
"RelativeToolTip": "Hediffs\\ARA_HediffTerrainSpawn\\CompHediffTerrainSpawn.cs",
"ViewState": "AgIAAB0AAAAAAAAAAAAswE0AAABHAAAAAAAAAA==",
"Icon": "ae27a6b0-e345-4288-96df-5eaf394ee369.000738|",
"WhenOpened": "2025-10-23T08:00:28.236Z"
},
{
"$type": "Document",
"DocumentIndex": 6,
"Title": "CompAbilityEffect_RandomHediff.cs",
"DocumentMoniker": "D:\\SteamLibrary\\steamapps\\common\\RimWorld\\Mods\\ArachnaeSwarm\\Source\\ArachnaeSwarm\\Abilities\\CompAbilityEffect_RandomHediff.cs",
"RelativeDocumentMoniker": "Abilities\\CompAbilityEffect_RandomHediff.cs",
"ToolTip": "D:\\SteamLibrary\\steamapps\\common\\RimWorld\\Mods\\ArachnaeSwarm\\Source\\ArachnaeSwarm\\Abilities\\CompAbilityEffect_RandomHediff.cs",
"RelativeToolTip": "Abilities\\CompAbilityEffect_RandomHediff.cs",
"ViewState": "AgIAALoAAAAAAAAAAAAqwNcAAAAZAAAAAAAAAA==",
"Icon": "ae27a6b0-e345-4288-96df-5eaf394ee369.000738|",
"WhenOpened": "2025-10-22T06:34:08.063Z"
}
]
}

View File

@@ -38,6 +38,9 @@
<HintPath>..\..\..\..\..\..\workshop\content\294100\2009463077\1.5\Assemblies\0Harmony.dll</HintPath>
<Private>False</Private>
</Reference>
<Reference Include="AlienRace">
<HintPath>..\..\..\..\..\..\workshop\content\294100\839005762\1.6\Assemblies\AlienRace.dll</HintPath>
</Reference>
<Reference Include="Assembly-CSharp">
<HintPath>..\..\..\..\..\..\common\RimWorld\RimWorldWin64_Data\Managed\Assembly-CSharp.dll</HintPath>
<Private>False</Private>

View File

@@ -17,6 +17,9 @@ namespace ArachnaeSwarm
private CompAutoEjector cachedAutoEjectorComp;
private Graphic cachedTopGraphic;
// 新增字段跟踪被建筑杀死的pawn
private HashSet<Pawn> pawnsKilledByVat = new HashSet<Pawn>();
// IThingHolderWithDrawnPawn implementation
public float HeldPawnDrawPos_Y => DrawPos.y + 0.03658537f;
public float HeldPawnBodyAngle => base.Rotation.AsAngle;
@@ -134,12 +137,21 @@ namespace ArachnaeSwarm
1.5f, // 每次1.5点伤害
2f, // 护甲穿透
-1f, // 随机角度
instigator: null,
instigator: this, // 将建筑设为伤害来源
hitPart: targetPart
);
// 标记这个pawn将被建筑杀死
pawnsKilledByVat.Add(pawn);
// 应用伤害
pawn.TakeDamage(acidDamage);
// 立即检查pawn是否死亡
if (pawn.Dead)
{
HandlePawnDeath(pawn);
}
}
catch (Exception ex)
{
@@ -147,6 +159,48 @@ namespace ArachnaeSwarm
}
}
// 新增方法处理pawn死亡
private void HandlePawnDeath(Pawn pawn)
{
try
{
// 检查是否是被建筑杀死的
if (pawnsKilledByVat.Contains(pawn))
{
Log.Message($"Pawn {pawn.Label} killed by RefuelingVat, destroying corpse.");
// 从容器中移除pawn
if (innerContainer.Contains(pawn))
{
innerContainer.Remove(pawn);
}
// 销毁pawn的尸体
if (!pawn.Destroyed)
{
pawn.Destroy();
}
// 从跟踪列表中移除
pawnsKilledByVat.Remove(pawn);
pawnTickCounters.Remove(pawn);
// 立即调用完成逻辑
if (selectedPawn == pawn)
{
selectedPawn = null;
startTick = -1;
}
return; // 直接返回,不执行后续弹出逻辑
}
}
catch (Exception ex)
{
Log.Error($"Error handling pawn death for {pawn}: {ex}");
}
}
private BodyPartRecord GetRandomVulnerablePart(Pawn pawn)
{
// 优先选择外部身体部位
@@ -176,7 +230,15 @@ namespace ArachnaeSwarm
// 检查俘虏是否死亡 - 增加更严格的检查
if (selectedPawn.Dead || selectedPawn.Destroyed)
{
Finish();
// 检查是否是被建筑杀死的
if (pawnsKilledByVat.Contains(selectedPawn))
{
HandlePawnDeath(selectedPawn);
}
else
{
Finish(); // 其他原因的死亡正常弹出
}
return;
}
@@ -246,6 +308,9 @@ namespace ArachnaeSwarm
{
startTick = Find.TickManager.TicksGame;
pawnTickCounters[pawn] = 0; // 初始化伤害计数器
// 确保pawn不在死亡跟踪列表中
pawnsKilledByVat.Remove(pawn);
}
if (deselected)
{
@@ -259,20 +324,27 @@ namespace ArachnaeSwarm
{
try
{
// 检查pawn是否还活着如果已经死亡且是被建筑杀死的则跳过弹出
if (selectedPawn.Dead && pawnsKilledByVat.Contains(selectedPawn))
{
HandlePawnDeath(selectedPawn);
return;
}
Notify_PawnRemoved();
bool ejected = false;
string ejectionMethod = "None";
// 方法1标准弹出 - 在交互单元格附近
if (innerContainer.Contains(selectedPawn))
if (innerContainer.Contains(selectedPawn) && !selectedPawn.Dead)
{
ejected = innerContainer.TryDrop(selectedPawn, InteractionCell, base.Map, ThingPlaceMode.Near, 1, out var _);
if (ejected) ejectionMethod = "Standard";
}
// 方法2尝试随机相邻单元格
if (!ejected && innerContainer.Contains(selectedPawn))
if (!ejected && innerContainer.Contains(selectedPawn) && !selectedPawn.Dead)
{
var adjacentCells = GenAdj.CellsAdjacent8Way(this).Where(c => c.Walkable(base.Map) && c.InBounds(base.Map)).ToList();
if (adjacentCells.Count > 0)
@@ -283,10 +355,10 @@ namespace ArachnaeSwarm
}
}
// 方法3强制移除
if (!ejected && innerContainer.Contains(selectedPawn))
// 方法3强制移除仅对活着的pawn
if (!ejected && innerContainer.Contains(selectedPawn) && !selectedPawn.Dead)
{
Log.Warning($"Forcing removal of dead pawn {selectedPawn} from RefuelingVat");
Log.Warning($"Forcing removal of pawn {selectedPawn} from RefuelingVat");
innerContainer.Remove(selectedPawn);
GenPlace.TryPlaceThing(selectedPawn, this.Position, base.Map, ThingPlaceMode.Near);
ejected = true;
@@ -297,7 +369,7 @@ namespace ArachnaeSwarm
{
Log.Message($"Successfully ejected {selectedPawn} using method: {ejectionMethod}");
}
else
else if (!selectedPawn.Dead) // 只有活着的pawn弹出失败才报错
{
Log.Error($"Failed to eject {selectedPawn} from RefuelingVat");
}
@@ -317,10 +389,12 @@ namespace ArachnaeSwarm
{
if (selectedPawn != null)
{
// 从跟踪列表中移除
pawnsKilledByVat.Remove(selectedPawn);
pawnTickCounters.Remove(selectedPawn);
// 确保pawn不在容器中
if (innerContainer.Contains(selectedPawn))
// 确保pawn不在容器中(除非是被建筑杀死的)
if (innerContainer.Contains(selectedPawn) && !(selectedPawn.Dead && pawnsKilledByVat.Contains(selectedPawn)))
{
Log.Warning($"Pawn {selectedPawn} still in container during OnStop, forcing removal.");
innerContainer.Remove(selectedPawn);
@@ -597,6 +671,22 @@ namespace ArachnaeSwarm
{
base.ExposeData();
Scribe_Collections.Look(ref pawnTickCounters, "pawnTickCounters", LookMode.Reference, LookMode.Value);
Scribe_Collections.Look(ref pawnsKilledByVat, "pawnsKilledByVat", LookMode.Reference);
// 确保集合不为null
if (Scribe.mode == LoadSaveMode.PostLoadInit)
{
pawnsKilledByVat ??= new HashSet<Pawn>();
pawnTickCounters ??= new Dictionary<Pawn, int>();
// 清理可能已销毁的pawn引用
pawnsKilledByVat.RemoveWhere(pawn => pawn == null || pawn.Destroyed);
var deadPawns = pawnTickCounters.Keys.Where(pawn => pawn == null || pawn.Destroyed).ToList();
foreach (var deadPawn in deadPawns)
{
pawnTickCounters.Remove(deadPawn);
}
}
}
}
}

View File

@@ -15,6 +15,9 @@ namespace ArachnaeSwarm
private CompRefuelableNutrition cachedRefuelableComp;
private Graphic cachedTopGraphic;
// 新增字段跟踪被建筑杀死的pawn
private HashSet<Pawn> pawnsKilledByVat = new HashSet<Pawn>();
// IThingHolderWithDrawnPawn implementation
public float HeldPawnDrawPos_Y => DrawPos.y + 0.03658537f;
public float HeldPawnBodyAngle => base.Rotation.AsAngle;
@@ -124,12 +127,21 @@ namespace ArachnaeSwarm
3f, // 每次3点伤害
2f, // 护甲穿透
-1f, // 随机角度
instigator: null,
instigator: this, // 将建筑设为伤害来源
hitPart: targetPart
);
// 标记这个pawn将被建筑杀死
pawnsKilledByVat.Add(pawn);
// 应用伤害
pawn.TakeDamage(acidDamage);
// 立即检查pawn是否死亡
if (pawn.Dead)
{
HandlePawnDeath(pawn);
}
}
catch (Exception ex)
{
@@ -137,6 +149,48 @@ namespace ArachnaeSwarm
}
}
// 新增方法处理pawn死亡与RefuelingVat相同
private void HandlePawnDeath(Pawn pawn)
{
try
{
// 检查是否是被建筑杀死的
if (pawnsKilledByVat.Contains(pawn))
{
Log.Message($"Pawn {pawn.Label} killed by NutrientVat, destroying corpse.");
// 从容器中移除pawn
if (innerContainer.Contains(pawn))
{
innerContainer.Remove(pawn);
}
// 销毁pawn的尸体
if (!pawn.Destroyed)
{
pawn.Destroy();
}
// 从跟踪列表中移除
pawnsKilledByVat.Remove(pawn);
pawnTickCounters.Remove(pawn);
// 立即调用完成逻辑
if (selectedPawn == pawn)
{
selectedPawn = null;
startTick = -1;
}
return; // 直接返回,不执行后续弹出逻辑
}
}
catch (Exception ex)
{
Log.Error($"Error handling pawn death for {pawn}: {ex}");
}
}
private BodyPartRecord GetRandomVulnerablePart(Pawn pawn)
{
// 优先选择外部身体部位
@@ -166,7 +220,15 @@ namespace ArachnaeSwarm
// 检查俘虏是否死亡 - 增加更严格的检查
if (selectedPawn.Dead || selectedPawn.Destroyed)
{
Finish();
// 检查是否是被建筑杀死的
if (pawnsKilledByVat.Contains(selectedPawn))
{
HandlePawnDeath(selectedPawn);
}
else
{
Finish(); // 其他原因的死亡正常弹出
}
return;
}
@@ -270,6 +332,9 @@ namespace ArachnaeSwarm
{
startTick = Find.TickManager.TicksGame;
pawnTickCounters[pawn] = 0; // 初始化伤害计数器
// 确保pawn不在死亡跟踪列表中
pawnsKilledByVat.Remove(pawn);
}
if (deselected)
{
@@ -283,20 +348,27 @@ namespace ArachnaeSwarm
{
try
{
// 检查pawn是否还活着如果已经死亡且是被建筑杀死的则跳过弹出
if (selectedPawn.Dead && pawnsKilledByVat.Contains(selectedPawn))
{
HandlePawnDeath(selectedPawn);
return;
}
Notify_PawnRemoved();
bool ejected = false;
string ejectionMethod = "None";
// 方法1标准弹出 - 在交互单元格附近
if (innerContainer.Contains(selectedPawn))
if (innerContainer.Contains(selectedPawn) && !selectedPawn.Dead)
{
ejected = innerContainer.TryDrop(selectedPawn, InteractionCell, base.Map, ThingPlaceMode.Near, 1, out var _);
if (ejected) ejectionMethod = "Standard";
}
// 方法2尝试随机相邻单元格
if (!ejected && innerContainer.Contains(selectedPawn))
if (!ejected && innerContainer.Contains(selectedPawn) && !selectedPawn.Dead)
{
var adjacentCells = GenAdj.CellsAdjacent8Way(this).Where(c => c.Walkable(base.Map) && c.InBounds(base.Map)).ToList();
if (adjacentCells.Count > 0)
@@ -307,8 +379,8 @@ namespace ArachnaeSwarm
}
}
// 方法3强制移除
if (!ejected && innerContainer.Contains(selectedPawn))
// 方法3强制移除仅对活着的pawn
if (!ejected && innerContainer.Contains(selectedPawn) && !selectedPawn.Dead)
{
Log.Warning($"Forcing removal of pawn {selectedPawn} from NutrientVat");
innerContainer.Remove(selectedPawn);
@@ -321,7 +393,7 @@ namespace ArachnaeSwarm
{
Log.Message($"Successfully ejected {selectedPawn} using method: {ejectionMethod}");
}
else
else if (!selectedPawn.Dead) // 只有活着的pawn弹出失败才报错
{
Log.Error($"Failed to eject {selectedPawn} from NutrientVat");
}
@@ -343,20 +415,27 @@ namespace ArachnaeSwarm
{
try
{
// 检查pawn是否还活着如果已经死亡且是被建筑杀死的则跳过弹出
if (selectedPawn.Dead && pawnsKilledByVat.Contains(selectedPawn))
{
HandlePawnDeath(selectedPawn);
return;
}
Notify_PawnRemoved();
bool ejected = false;
string ejectionMethod = "None";
// 方法1标准弹出 - 在交互单元格附近
if (innerContainer.Contains(selectedPawn))
if (innerContainer.Contains(selectedPawn) && !selectedPawn.Dead)
{
ejected = innerContainer.TryDrop(selectedPawn, InteractionCell, base.Map, ThingPlaceMode.Near, 1, out var _);
if (ejected) ejectionMethod = "Standard";
}
// 方法2尝试随机相邻单元格
if (!ejected && innerContainer.Contains(selectedPawn))
if (!ejected && innerContainer.Contains(selectedPawn) && !selectedPawn.Dead)
{
var adjacentCells = GenAdj.CellsAdjacent8Way(this).Where(c => c.Walkable(base.Map) && c.InBounds(base.Map)).ToList();
if (adjacentCells.Count > 0)
@@ -367,8 +446,8 @@ namespace ArachnaeSwarm
}
}
// 方法3强制移除
if (!ejected && innerContainer.Contains(selectedPawn))
// 方法3强制移除仅对活着的pawn
if (!ejected && innerContainer.Contains(selectedPawn) && !selectedPawn.Dead)
{
Log.Warning($"Forcing removal of failed pawn {selectedPawn} from NutrientVat");
innerContainer.Remove(selectedPawn);
@@ -384,7 +463,7 @@ namespace ArachnaeSwarm
Hediff firstHediffOfDef = selectedPawn.health.hediffSet.GetFirstHediffOfDef(HediffDefOf.BioStarvation);
selectedPawn.Kill(null, firstHediffOfDef);
}
else
else if (!selectedPawn.Dead) // 只有活着的pawn弹出失败才报错
{
Log.Error($"Failed to eject failed pawn {selectedPawn} from NutrientVat");
// 即使弹出失败也要杀死俘虏
@@ -407,10 +486,12 @@ namespace ArachnaeSwarm
{
if (selectedPawn != null)
{
// 从跟踪列表中移除
pawnsKilledByVat.Remove(selectedPawn);
pawnTickCounters.Remove(selectedPawn);
// 确保pawn不在容器中
if (innerContainer.Contains(selectedPawn))
// 确保pawn不在容器中(除非是被建筑杀死的)
if (innerContainer.Contains(selectedPawn) && !(selectedPawn.Dead && pawnsKilledByVat.Contains(selectedPawn)))
{
Log.Warning($"Pawn {selectedPawn} still in container during OnStop, forcing removal.");
innerContainer.Remove(selectedPawn);
@@ -671,6 +752,22 @@ namespace ArachnaeSwarm
{
base.ExposeData();
Scribe_Collections.Look(ref pawnTickCounters, "pawnTickCounters", LookMode.Reference, LookMode.Value);
Scribe_Collections.Look(ref pawnsKilledByVat, "pawnsKilledByVat", LookMode.Reference);
// 确保集合不为null
if (Scribe.mode == LoadSaveMode.PostLoadInit)
{
pawnsKilledByVat ??= new HashSet<Pawn>();
pawnTickCounters ??= new Dictionary<Pawn, int>();
// 清理可能已销毁的pawn引用
pawnsKilledByVat.RemoveWhere(pawn => pawn == null || pawn.Destroyed);
var deadPawns = pawnTickCounters.Keys.Where(pawn => pawn == null || pawn.Destroyed).ToList();
foreach (var deadPawn in deadPawns)
{
pawnTickCounters.Remove(deadPawn);
}
}
}
}
}

View File

@@ -1,14 +1,20 @@
using RimWorld;
using Verse;
using System.Collections.Generic;
using Verse.AI; // For PathEndMode and Danger
using UnityEngine; // For Texture2D
using Verse.AI;
using UnityEngine;
using AlienRace;
using System; // 添加 System 命名空间用于 Action 类型
using System.Text; // 添加 System.Text 命名空间用于 StringBuilder
using System.Linq; // 添加 System.Linq 命名空间用于 Any() 方法
namespace ArachnaeSwarm
{
public class CompProperties_PowerArmorStation : CompProperties
{
public ThingDef apparelDef;
public bool enableRaceRestrictionCheck = true;
public string customRestrictionMessage;
public CompProperties_PowerArmorStation()
{
@@ -26,11 +32,64 @@ namespace ArachnaeSwarm
var fuelComp = parent.GetComp<CompRefuelableNutrition>();
if (fuelComp != null)
{
// Set consumption rate to 0 when in building form
fuelComp.currentConsumptionRate = 0f;
}
}
// 使用 Alien Race 框架提供的静态方法检查装备限制
private AcceptanceReport CheckRaceRestriction(Pawn pawn, ThingDef apparelDef)
{
if (pawn == null || apparelDef == null)
return true;
if (!Props.enableRaceRestrictionCheck)
return true;
// 使用 Alien Race 框架的 CanWear 方法
bool canWear = RaceRestrictionSettings.CanWear(apparelDef, pawn.def);
if (!canWear)
{
if (!Props.customRestrictionMessage.NullOrEmpty())
{
return Props.customRestrictionMessage.Translate(pawn.def.label, apparelDef.label);
}
return "ARA_PowerArmorForbiddenByRace".Translate(pawn.def.label, apparelDef.label);
}
return true;
}
public AcceptanceReport CanPawnEnter(Pawn pawn)
{
// 基础可达性检查
if (!pawn.CanReserveAndReach(parent, PathEndMode.InteractionCell, Danger.Deadly))
{
return "CannotReach".Translate();
}
if (Props.apparelDef == null)
{
return "ARA_NoApparelDefined".Translate();
}
// 使用 Alien Race 框架的方法检查种族限制
AcceptanceReport raceCheck = CheckRaceRestriction(pawn, Props.apparelDef);
if (!raceCheck.Accepted)
{
return raceCheck;
}
// 检查是否已经穿戴了同类型装备
if (pawn.apparel?.WornApparel?.Any(a => a.def == Props.apparelDef) == true)
{
return "ARA_AlreadyWearingSameApparel".Translate(Props.apparelDef.label);
}
return true;
}
public override IEnumerable<FloatMenuOption> CompFloatMenuOptions(Pawn selPawn)
{
foreach (FloatMenuOption option in base.CompFloatMenuOptions(selPawn))
@@ -38,26 +97,58 @@ namespace ArachnaeSwarm
yield return option;
}
// Check if there's an apparelDef defined
if (Props.apparelDef == null)
{
yield break; // No apparel to wear
yield break;
}
// Check if the pawn can interact with the building
if (!selPawn.CanReserveAndReach(parent, PathEndMode.InteractionCell, Danger.Deadly))
AcceptanceReport canEnter = CanPawnEnter(selPawn);
string label = "ARA_EnterPowerArmor".Translate(parent.Label);
if (!canEnter.Accepted)
{
yield return new FloatMenuOption("CannotEnterPowerArmor".Translate() + ": " + "CannotReach".Translate(), null);
label += ": " + canEnter.Reason;
}
else
Action enterAction = null;
if (canEnter.Accepted)
{
void enterAction()
enterAction = () =>
{
Job job = JobMaker.MakeJob(DefDatabase<JobDef>.GetNamed("ARA_EnterPowerArmor"), parent);
selPawn.jobs.TryTakeOrderedJob(job, JobTag.Misc);
}
yield return new FloatMenuOption("ARA_EnterPowerArmor".Translate(parent.Label), enterAction);
};
}
yield return new FloatMenuOption(label, enterAction)
{
Disabled = !canEnter.Accepted
};
}
public override string CompInspectStringExtra()
{
StringBuilder sb = new StringBuilder();
string baseString = base.CompInspectStringExtra();
if (!string.IsNullOrEmpty(baseString))
sb.Append(baseString);
if (Props.apparelDef != null)
{
if (sb.Length > 0)
sb.AppendLine();
sb.Append("ARA_PowerArmorStationApparel".Translate(Props.apparelDef.label));
if (Props.enableRaceRestrictionCheck)
{
sb.AppendLine();
sb.Append("ARA_RaceRestrictionEnabled".Translate());
}
}
return sb.ToString();
}
}
}
}