This commit is contained in:
Tourswen
2025-10-19 12:51:06 +08:00
parent 0c45c14570
commit 9dc9d7d6e3
40 changed files with 1837 additions and 423 deletions

View File

@@ -3,13 +3,41 @@
"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\\building_comps\\compnutritiontofuelconverter.cs||{A6C744A8-0E4A-4FC6-886A-064283054674}",
"RelativeMoniker": "D:0:0:{EAE0DB6B-E282-C812-7F5A-6D13E9D24581}|ArachnaeSwarm.csproj|solutionrelative:building_comps\\compnutritiontofuelconverter.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\\building_comps\\ara_compinteractiveproducer\\compinteractiveproducer.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\\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}"
},
{
"AbsoluteMoniker": "D:0:0:{EAE0DB6B-E282-C812-7F5A-6D13E9D24581}|ArachnaeSwarm.csproj|E:\\SteamLibrary\\steamapps\\common\\RimWorld\\Mods\\ArachnaeSwarm\\Source\\ArachnaeSwarm\\powerarmor\\gizmo_structurepanel.cs||{A6C744A8-0E4A-4FC6-886A-064283054674}",
"RelativeMoniker": "D:0:0:{EAE0DB6B-E282-C812-7F5A-6D13E9D24581}|ArachnaeSwarm.csproj|solutionrelative:powerarmor\\gizmo_structurepanel.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\\abilities\\ara_huggingface\\compabilityeffect_possess.cs||{A6C744A8-0E4A-4FC6-886A-064283054674}",
"RelativeMoniker": "D:0:0:{EAE0DB6B-E282-C812-7F5A-6D13E9D24581}|ArachnaeSwarm.csproj|solutionrelative:abilities\\ara_huggingface\\compabilityeffect_possess.cs||{A6C744A8-0E4A-4FC6-886A-064283054674}"
},
{
"AbsoluteMoniker": "D:0:0:{EAE0DB6B-E282-C812-7F5A-6D13E9D24581}|ArachnaeSwarm.csproj|E:\\SteamLibrary\\steamapps\\common\\RimWorld\\Mods\\ArachnaeSwarm\\Source\\ArachnaeSwarm\\building_comps\\ara_nutrientvat\\building_nutrientvat.cs||{A6C744A8-0E4A-4FC6-886A-064283054674}",
"RelativeMoniker": "D:0:0:{EAE0DB6B-E282-C812-7F5A-6D13E9D24581}|ArachnaeSwarm.csproj|solutionrelative:building_comps\\ara_nutrientvat\\building_nutrientvat.cs||{A6C744A8-0E4A-4FC6-886A-064283054674}"
},
{
"AbsoluteMoniker": "D:0:0:{EAE0DB6B-E282-C812-7F5A-6D13E9D24581}|ArachnaeSwarm.csproj|E:\\SteamLibrary\\steamapps\\common\\RimWorld\\Mods\\ArachnaeSwarm\\Source\\ArachnaeSwarm\\building_comps\\ara_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|E:\\SteamLibrary\\steamapps\\common\\RimWorld\\Mods\\ArachnaeSwarm\\Source\\ArachnaeSwarm\\building_comps\\ara_compinteractiveproducer\\compinteractiveproducer.cs||{A6C744A8-0E4A-4FC6-886A-064283054674}",
"RelativeMoniker": "D:0:0:{EAE0DB6B-E282-C812-7F5A-6D13E9D24581}|ArachnaeSwarm.csproj|solutionrelative:building_comps\\ara_compinteractiveproducer\\compinteractiveproducer.cs||{A6C744A8-0E4A-4FC6-886A-064283054674}"
},
{
"AbsoluteMoniker": "D:0:0:{EAE0DB6B-E282-C812-7F5A-6D13E9D24581}|ArachnaeSwarm.csproj|E:\\SteamLibrary\\steamapps\\common\\RimWorld\\Mods\\ArachnaeSwarm\\Source\\ArachnaeSwarm\\building_comps\\compnutritiontofuelconverter.cs||{A6C744A8-0E4A-4FC6-886A-064283054674}",
"RelativeMoniker": "D:0:0:{EAE0DB6B-E282-C812-7F5A-6D13E9D24581}|ArachnaeSwarm.csproj|solutionrelative:building_comps\\compnutritiontofuelconverter.cs||{A6C744A8-0E4A-4FC6-886A-064283054674}"
},
{
"AbsoluteMoniker": "D:0:0:{EAE0DB6B-E282-C812-7F5A-6D13E9D24581}|ArachnaeSwarm.csproj|E:\\SteamLibrary\\steamapps\\common\\RimWorld\\Mods\\ArachnaeSwarm\\Source\\ArachnaeSwarm\\building_comps\\ara_compinteractiveproducer\\compresearchproducer.cs||{A6C744A8-0E4A-4FC6-886A-064283054674}",
"RelativeMoniker": "D:0:0:{EAE0DB6B-E282-C812-7F5A-6D13E9D24581}|ArachnaeSwarm.csproj|solutionrelative:building_comps\\ara_compinteractiveproducer\\compresearchproducer.cs||{A6C744A8-0E4A-4FC6-886A-064283054674}"
@@ -22,28 +50,114 @@
"DocumentGroups": [
{
"DockedWidth": 200,
"SelectedChildIndex": 1,
"SelectedChildIndex": 4,
"Children": [
{
"$type": "Bookmark",
"Name": "ST:0:0:{1c4feeaa-4718-4aa9-859d-94ce25d182ba}"
},
{
"$type": "Document",
"DocumentIndex": 2,
"Title": "CompPowerArmorStation.cs",
"DocumentMoniker": "E:\\SteamLibrary\\steamapps\\common\\RimWorld\\Mods\\ArachnaeSwarm\\Source\\ArachnaeSwarm\\PowerArmor\\CompPowerArmorStation.cs",
"RelativeDocumentMoniker": "PowerArmor\\CompPowerArmorStation.cs",
"ToolTip": "E:\\SteamLibrary\\steamapps\\common\\RimWorld\\Mods\\ArachnaeSwarm\\Source\\ArachnaeSwarm\\PowerArmor\\CompPowerArmorStation.cs",
"RelativeToolTip": "PowerArmor\\CompPowerArmorStation.cs",
"ViewState": "AgIAAAAAAAAAAAAAAAAAAAwAAAAmAAAAAAAAAA==",
"Icon": "ae27a6b0-e345-4288-96df-5eaf394ee369.000738|",
"WhenOpened": "2025-10-18T16:40:43.953Z",
"EditorCaption": ""
},
{
"$type": "Document",
"DocumentIndex": 1,
"Title": "JobDriver_EnterPowerArmor.cs",
"DocumentMoniker": "E:\\SteamLibrary\\steamapps\\common\\RimWorld\\Mods\\ArachnaeSwarm\\Source\\ArachnaeSwarm\\PowerArmor\\JobDriver_EnterPowerArmor.cs",
"RelativeDocumentMoniker": "PowerArmor\\JobDriver_EnterPowerArmor.cs",
"ToolTip": "E:\\SteamLibrary\\steamapps\\common\\RimWorld\\Mods\\ArachnaeSwarm\\Source\\ArachnaeSwarm\\PowerArmor\\JobDriver_EnterPowerArmor.cs",
"RelativeToolTip": "PowerArmor\\JobDriver_EnterPowerArmor.cs",
"ViewState": "AgIAAGUAAAAAAAAAAAAAAIwAAAAAAAAAAAAAAA==",
"Icon": "ae27a6b0-e345-4288-96df-5eaf394ee369.000738|",
"WhenOpened": "2025-10-18T16:33:18.657Z",
"EditorCaption": ""
},
{
"$type": "Document",
"DocumentIndex": 3,
"Title": "Gizmo_StructurePanel.cs",
"DocumentMoniker": "E:\\SteamLibrary\\steamapps\\common\\RimWorld\\Mods\\ArachnaeSwarm\\Source\\ArachnaeSwarm\\PowerArmor\\Gizmo_StructurePanel.cs",
"RelativeDocumentMoniker": "PowerArmor\\Gizmo_StructurePanel.cs",
"ToolTip": "E:\\SteamLibrary\\steamapps\\common\\RimWorld\\Mods\\ArachnaeSwarm\\Source\\ArachnaeSwarm\\PowerArmor\\Gizmo_StructurePanel.cs",
"RelativeToolTip": "PowerArmor\\Gizmo_StructurePanel.cs",
"ViewState": "AgIAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA==",
"Icon": "ae27a6b0-e345-4288-96df-5eaf394ee369.000738|",
"WhenOpened": "2025-10-18T16:32:53.277Z"
},
{
"$type": "Document",
"DocumentIndex": 0,
"Title": "ARA_PowerArmor.cs",
"DocumentMoniker": "E:\\SteamLibrary\\steamapps\\common\\RimWorld\\Mods\\ArachnaeSwarm\\Source\\ArachnaeSwarm\\PowerArmor\\ARA_PowerArmor.cs",
"RelativeDocumentMoniker": "PowerArmor\\ARA_PowerArmor.cs",
"ToolTip": "E:\\SteamLibrary\\steamapps\\common\\RimWorld\\Mods\\ArachnaeSwarm\\Source\\ArachnaeSwarm\\PowerArmor\\ARA_PowerArmor.cs",
"RelativeToolTip": "PowerArmor\\ARA_PowerArmor.cs",
"ViewState": "AgIAAAAAAAAAAAAAAAAAACQAAAAAAAAAAAAAAA==",
"Icon": "ae27a6b0-e345-4288-96df-5eaf394ee369.000738|",
"WhenOpened": "2025-10-18T16:30:55.497Z",
"EditorCaption": ""
},
{
"$type": "Document",
"DocumentIndex": 4,
"Title": "CompAbilityEffect_Possess.cs",
"DocumentMoniker": "E:\\SteamLibrary\\steamapps\\common\\RimWorld\\Mods\\ArachnaeSwarm\\Source\\ArachnaeSwarm\\Abilities\\ARA_HuggingFace\\CompAbilityEffect_Possess.cs",
"RelativeDocumentMoniker": "Abilities\\ARA_HuggingFace\\CompAbilityEffect_Possess.cs",
"ToolTip": "E:\\SteamLibrary\\steamapps\\common\\RimWorld\\Mods\\ArachnaeSwarm\\Source\\ArachnaeSwarm\\Abilities\\ARA_HuggingFace\\CompAbilityEffect_Possess.cs",
"RelativeToolTip": "Abilities\\ARA_HuggingFace\\CompAbilityEffect_Possess.cs",
"ViewState": "AgIAACwAAAAAAAAAAAAkwDsAAAAtAAAAAAAAAA==",
"Icon": "ae27a6b0-e345-4288-96df-5eaf394ee369.000738|",
"WhenOpened": "2025-10-18T13:31:46.288Z"
},
{
"$type": "Document",
"DocumentIndex": 5,
"Title": "Building_NutrientVat.cs",
"DocumentMoniker": "E:\\SteamLibrary\\steamapps\\common\\RimWorld\\Mods\\ArachnaeSwarm\\Source\\ArachnaeSwarm\\Building_Comps\\ARA_NutrientVat\\Building_NutrientVat.cs",
"RelativeDocumentMoniker": "Building_Comps\\ARA_NutrientVat\\Building_NutrientVat.cs",
"ToolTip": "E:\\SteamLibrary\\steamapps\\common\\RimWorld\\Mods\\ArachnaeSwarm\\Source\\ArachnaeSwarm\\Building_Comps\\ARA_NutrientVat\\Building_NutrientVat.cs",
"RelativeToolTip": "Building_Comps\\ARA_NutrientVat\\Building_NutrientVat.cs",
"ViewState": "AgIAAAgAAAAAAAAAAAAQwBAAAAAIAAAAAAAAAA==",
"Icon": "ae27a6b0-e345-4288-96df-5eaf394ee369.000738|",
"WhenOpened": "2025-10-18T11:56:31.022Z"
},
{
"$type": "Document",
"DocumentIndex": 6,
"Title": "Building_RefuelingVat.cs",
"DocumentMoniker": "E:\\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": "E:\\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": "AgIAAGcAAAAAAAAAAAAAAI4AAAAgAAAAAAAAAA==",
"Icon": "ae27a6b0-e345-4288-96df-5eaf394ee369.000738|",
"WhenOpened": "2025-10-18T11:56:12.333Z"
},
{
"$type": "Document",
"DocumentIndex": 8,
"Title": "CompNutritionToFuelConverter.cs",
"DocumentMoniker": "E:\\SteamLibrary\\steamapps\\common\\RimWorld\\Mods\\ArachnaeSwarm\\Source\\ArachnaeSwarm\\Building_Comps\\CompNutritionToFuelConverter.cs",
"RelativeDocumentMoniker": "Building_Comps\\CompNutritionToFuelConverter.cs",
"ToolTip": "E:\\SteamLibrary\\steamapps\\common\\RimWorld\\Mods\\ArachnaeSwarm\\Source\\ArachnaeSwarm\\Building_Comps\\CompNutritionToFuelConverter.cs",
"RelativeToolTip": "Building_Comps\\CompNutritionToFuelConverter.cs",
"ViewState": "AgIAAAAAAAAAAAAAAAAAAKEAAAAcAAAAAAAAAA==",
"ViewState": "AgIAAGEAAAAAAAAAAAAQwHYAAAAJAAAAAAAAAA==",
"Icon": "ae27a6b0-e345-4288-96df-5eaf394ee369.000738|",
"WhenOpened": "2025-10-17T11:32:00.484Z",
"EditorCaption": ""
"WhenOpened": "2025-10-17T11:32:00.484Z"
},
{
"$type": "Document",
"DocumentIndex": 2,
"DocumentIndex": 9,
"Title": "CompResearchProducer.cs",
"DocumentMoniker": "E:\\SteamLibrary\\steamapps\\common\\RimWorld\\Mods\\ArachnaeSwarm\\Source\\ArachnaeSwarm\\Building_Comps\\ARA_CompInteractiveProducer\\CompResearchProducer.cs",
"RelativeDocumentMoniker": "Building_Comps\\ARA_CompInteractiveProducer\\CompResearchProducer.cs",
@@ -55,16 +169,15 @@
},
{
"$type": "Document",
"DocumentIndex": 1,
"DocumentIndex": 7,
"Title": "CompInteractiveProducer.cs",
"DocumentMoniker": "E:\\SteamLibrary\\steamapps\\common\\RimWorld\\Mods\\ArachnaeSwarm\\Source\\ArachnaeSwarm\\Building_Comps\\ARA_CompInteractiveProducer\\CompInteractiveProducer.cs",
"RelativeDocumentMoniker": "Building_Comps\\ARA_CompInteractiveProducer\\CompInteractiveProducer.cs",
"ToolTip": "E:\\SteamLibrary\\steamapps\\common\\RimWorld\\Mods\\ArachnaeSwarm\\Source\\ArachnaeSwarm\\Building_Comps\\ARA_CompInteractiveProducer\\CompInteractiveProducer.cs",
"RelativeToolTip": "Building_Comps\\ARA_CompInteractiveProducer\\CompInteractiveProducer.cs",
"ViewState": "AgIAAJYBAAAAAAAAAAAUwKoBAAAAAAAAAAAAAA==",
"ViewState": "AgIAAAwAAAAAAAAAAAAgwGYAAABWAAAAAAAAAA==",
"Icon": "ae27a6b0-e345-4288-96df-5eaf394ee369.000738|",
"WhenOpened": "2025-10-17T09:00:51.526Z",
"EditorCaption": ""
"WhenOpened": "2025-10-17T09:00:51.526Z"
}
]
}

View File

@@ -40,9 +40,77 @@ namespace ArachnaeSwarm
public override void Apply(LocalTargetInfo target, LocalTargetInfo dest)
{
base.Apply(target, dest);
DoPossession(this.parent.pawn, target.Pawn);
// 新增:检查目标是否无法行动(倒地)
if (target.Pawn != null && IsTargetImmobilized(target.Pawn))
{
Log.Message($"[夺舍] 目标 {target.Pawn.LabelShort} 无法行动,直接执行夺舍");
DoPossession(this.parent.pawn, target.Pawn);
}
else
{
Log.Message($"[夺舍] 目标可以行动,执行标准夺舍流程");
DoPossession(this.parent.pawn, target.Pawn);
}
}
// 新增:检查目标是否无法行动
private bool IsTargetImmobilized(Pawn target)
{
if (target == null) return false;
// 检查是否倒地
if (target.Downed)
{
Log.Message($"[夺舍] 目标 {target.LabelShort} 处于倒地状态");
return true;
}
// 检查是否无法移动
if (!target.health.capacities.CapableOf(PawnCapacityDefOf.Moving))
{
Log.Message($"[夺舍] 目标 {target.LabelShort} 无法移动");
return true;
}
// 检查是否有严重的移动障碍
if (target.health.hediffSet.HasHediff(HediffDefOf.Anesthetic) ||
target.health.hediffSet.HasHediff(HediffDefOf.CryptosleepSickness) ||
target.health.hediffSet.HasHediff(HediffDefOf.FoodPoisoning))
{
Log.Message($"[夺舍] 目标 {target.LabelShort} 有严重的移动障碍");
return true;
}
// 检查是否被束缚或囚禁
if (target.IsPrisoner || target.HostFaction != null)
{
Log.Message($"[夺舍] 目标 {target.LabelShort} 被囚禁或束缚");
return true;
}
return false;
}
// 新增:计算寄生成功率
private float CalculateSuccessChance(Pawn targetPawn, float damageDealt = 0f)
{
// 如果目标无法行动100%成功率
if (IsTargetImmobilized(targetPawn))
{
Log.Message($"[夺舍] 目标 {targetPawn.LabelShort} 无法行动,寄生成功率: 100%");
return 1f;
}
// 正常计算成功率
float baseChance = Props.successChance.RandomInRange;
float bonusFromDamage = damageDealt * Props.successChanceBonusPerDamage;
float finalChance = Mathf.Clamp01(baseChance + bonusFromDamage);
Log.Message($"[夺舍] 目标 {targetPawn.LabelShort} 可以行动,寄生成功率: {finalChance:P0} (基础: {baseChance:P0}, 伤害加成: {bonusFromDamage:P0})");
return finalChance;
}
private void DoPossession(Pawn caster, Pawn targetPawn)
{
if (targetPawn == null || caster == null) return;
@@ -146,10 +214,10 @@ namespace ArachnaeSwarm
if (damageResult.totalDamageDealt > 0)
{
float baseChance = Props.successChance.RandomInRange;
float bonusFromDamage = damageResult.totalDamageDealt * Props.successChanceBonusPerDamage;
float finalChance = Mathf.Clamp01(baseChance + bonusFromDamage);
Log.Message($"[Possess] Base chance: {baseChance}, Bonus: {bonusFromDamage}, Final chance: {finalChance}");
// 修改:使用新的成功率计算方法
float finalChance = CalculateSuccessChance(targetPawn, damageResult.totalDamageDealt);
Log.Message($"[Possess] Final chance: {finalChance:P0}");
if (Rand.Chance(finalChance))
{
@@ -163,4 +231,4 @@ namespace ArachnaeSwarm
}
}
}
}
}

View File

@@ -130,7 +130,7 @@ namespace ArachnaeSwarm
DamageInfo acidDamage = new DamageInfo(
acidDamageDef,
0.1f, // 每次0.1点伤害
1.5f, // 每次1.5点伤害
2f, // 护甲穿透
-1f, // 随机角度
instigator: null,

View File

@@ -38,6 +38,11 @@ namespace ArachnaeSwarm
private int spawnTick = -1; // 新增:记录生成时间
private const int COOLDOWN_TICKS = 120; // 新增120 tick冷却时间
// 新增:修复负时间相关字段
private int lastNegativeTimeCheckTick = -1;
private const int NEGATIVE_TIME_CHECK_INTERVAL = 60; // 每60ticks检查一次
private bool hasFixedNegativeTime = false; // 标记是否已经修复过负时间问题
private CompRefuelableNutrition _fuelComp;
private static readonly Texture2D CancelIcon = ContentFinder<Texture2D>.Get("UI/Designators/Cancel");
@@ -79,6 +84,83 @@ namespace ArachnaeSwarm
}
}
// 新增获取剩余生产时间ticks
public int RemainingProductionTicks
{
get
{
if (!InProduction || productionUntilTick <= 0) return 0;
return Mathf.Max(0, productionUntilTick - Find.TickManager.TicksGame);
}
}
// 新增:检查是否存在负时间问题
private bool HasNegativeTimeProblem
{
get
{
if (!InProduction || productionUntilTick <= 0) return false;
int remainingTicks = productionUntilTick - Find.TickManager.TicksGame;
return remainingTicks < -10; // 小于-10ticks认为有问题
}
}
// 新增:修复负时间问题
private void FixNegativeTimeProblem()
{
if (!InProduction || _selectedProcess == null)
{
Log.Warning($"Attempted to fix negative time but no process is selected. Resetting production.");
ResetProduction();
return;
}
int currentTicks = Find.TickManager.TicksGame;
int remainingTicks = productionUntilTick - currentTicks;
Log.Warning($"Detected negative production time for {parent.Label}. " +
$"Current: {currentTicks}, Target: {productionUntilTick}, Remaining: {remainingTicks}. " +
$"Process: {_selectedProcess.thingDef?.defName ?? "Unknown"}, Expected Duration: {_selectedProcess.productionTicks}");
// 计算应该设置的正确结束时间
int correctEndTick = currentTicks + _selectedProcess.productionTicks;
// 如果偏差太大,直接完成生产
if (remainingTicks < -_selectedProcess.productionTicks)
{
Log.Warning($"Negative time too large ({remainingTicks} ticks). Forcing production completion.");
FinishProduction();
return;
}
// 否则重置为正确的剩余时间
productionUntilTick = correctEndTick;
hasFixedNegativeTime = true;
Log.Message($"Fixed negative production time for {parent.Label}. " +
$"New target: {productionUntilTick}, New remaining: {_selectedProcess.productionTicks} ticks");
// 发送消息通知(开发模式)
if (Prefs.DevMode)
{
Messages.Message($"Fixed negative production time for {parent.Label}. Remaining: {_selectedProcess.productionTicks.ToStringTicksToPeriod()}",
MessageTypeDefOf.SilentInput);
}
}
// 新增:定期检查负时间问题
private void CheckForNegativeTime()
{
if (Find.TickManager.TicksGame > lastNegativeTimeCheckTick + NEGATIVE_TIME_CHECK_INTERVAL)
{
if (HasNegativeTimeProblem && !hasFixedNegativeTime)
{
FixNegativeTimeProblem();
}
lastNegativeTimeCheckTick = Find.TickManager.TicksGame;
}
}
public override void PostSpawnSetup(bool respawningAfterLoad)
{
base.PostSpawnSetup(respawningAfterLoad);
@@ -90,6 +172,17 @@ namespace ArachnaeSwarm
{
spawnTick = Find.TickManager.TicksGame;
}
// 新增:初始化负时间检查
lastNegativeTimeCheckTick = Find.TickManager.TicksGame;
hasFixedNegativeTime = false;
// 新增:立即检查一次负时间问题
if (InProduction && HasNegativeTimeProblem)
{
Log.Warning($"Detected negative production time on spawn for {parent.Label}");
FixNegativeTimeProblem();
}
}
public override void PostExposeData()
@@ -104,6 +197,8 @@ namespace ArachnaeSwarm
Scribe_Values.Look(ref ticksUnderOptimalConditions, "ticksUnderOptimalConditions", 0);
Scribe_Values.Look(ref temperaturePenaltyPercent, "temperaturePenaltyPercent", 0f);
Scribe_Values.Look(ref spawnTick, "spawnTick", -1); // 新增:序列化生成时间
Scribe_Values.Look(ref lastNegativeTimeCheckTick, "lastNegativeTimeCheckTick", -1);
Scribe_Values.Look(ref hasFixedNegativeTime, "hasFixedNegativeTime", false);
// 加载时重建 selectedProcess
if (Scribe.mode == LoadSaveMode.LoadingVars && selectedProcessThingDef != null)
@@ -138,6 +233,12 @@ namespace ArachnaeSwarm
Log.Warning($"Abnormal production time detected for {selectedProcessThingDef.defName}. Recalculating.");
productionUntilTick = Find.TickManager.TicksGame + _selectedProcess.productionTicks;
}
// 新增:检查负时间问题
else if (HasNegativeTimeProblem)
{
Log.Warning($"Negative production time detected on load for {selectedProcessThingDef.defName}. Fixing.");
FixNegativeTimeProblem();
}
}
}
}
@@ -241,6 +342,10 @@ namespace ArachnaeSwarm
public override void CompTick()
{
base.CompTick();
// 新增:定期检查负时间问题
CheckForNegativeTime();
if (InProduction && productionUntilTick > 0)
{
// 关键修复:添加时间戳有效性检查
@@ -251,6 +356,13 @@ namespace ArachnaeSwarm
return;
}
// 新增再次检查负时间问题在每次tick时
if (HasNegativeTimeProblem && !hasFixedNegativeTime)
{
FixNegativeTimeProblem();
return; // 修复后跳过本次tick的逻辑
}
if (FuelComp == null) return;
bool hasFuel = FuelComp.HasFuel;
@@ -341,6 +453,9 @@ namespace ArachnaeSwarm
temperaturePenaltyPercent = 0f;
float nutritionPerDay = (_selectedProcess.totalNutritionNeeded / _selectedProcess.productionTicks) * 60000f;
FuelComp.currentConsumptionRate = nutritionPerDay;
// 新增:重置负时间修复标记
hasFixedNegativeTime = false;
}
public (QualityCategory quality, float baseScore, float penalty) GetEstimatedQualityDetails()
@@ -495,6 +610,7 @@ namespace ArachnaeSwarm
productionUntilTick = -1;
ticksUnderOptimalConditions = 0;
temperaturePenaltyPercent = 0f;
hasFixedNegativeTime = false; // 新增:重置修复标记
}
// 新增:绘制进度条的方法
@@ -570,6 +686,13 @@ namespace ArachnaeSwarm
// 生产进度条
float progress = ProductionProgress;
int remainingTicks = productionUntilTick - Find.TickManager.TicksGame;
// 新增:显示负时间警告
if (remainingTicks < -10)
{
sb.AppendLine("<color=red>WARNING: Negative production time detected! Auto-fixing...</color>");
}
sb.AppendLine("Progress".Translate() + ": " + GetProgressBar(progress) + " " + progress.ToStringPercent("F0"));
sb.AppendLine("TimeLeft".Translate() + ": " + remainingTicks.ToStringTicksToPeriod());
@@ -624,12 +747,16 @@ namespace ArachnaeSwarm
drawPos.y += 0.15f; // 稍微抬高一点
float progress = ProductionProgress;
// 新增:负时间警告颜色(红色)
Color barColor = HasNegativeTimeProblem ? Color.red : Color.green;
GenDraw.DrawFillableBar(new GenDraw.FillableBarRequest
{
center = drawPos,
size = new Vector2(1f, 0.15f),
fillPercent = progress,
filledMat = SolidColorMaterials.SimpleSolidColorMaterial(Color.green),
filledMat = SolidColorMaterials.SimpleSolidColorMaterial(barColor),
unfilledMat = SolidColorMaterials.SimpleSolidColorMaterial(Color.gray),
margin = 0.1f,
rotation = Rot4.North
@@ -690,8 +817,53 @@ namespace ArachnaeSwarm
{
float progress = ProductionProgress;
var qualityDetails = GetEstimatedQualityDetails();
Messages.Message($"Progress: {progress:P0}\nBase Score: {qualityDetails.baseScore:P0}\nPenalty: {qualityDetails.penalty:P0}\nFinal: {qualityDetails.quality}",
MessageTypeDefOf.SilentInput);
int remainingTicks = productionUntilTick - Find.TickManager.TicksGame;
string message = $"Progress: {progress:P0}\n" +
$"Base Score: {qualityDetails.baseScore:P0}\n" +
$"Penalty: {qualityDetails.penalty:P0}\n" +
$"Final: {qualityDetails.quality}\n" +
$"Remaining Ticks: {remainingTicks}\n" +
$"Has Negative Time: {HasNegativeTimeProblem}\n" +
$"Fixed Before: {hasFixedNegativeTime}";
Messages.Message(message, MessageTypeDefOf.SilentInput);
}
};
// 新增:调试命令手动触发负时间修复
yield return new Command_Action
{
defaultLabel = "Debug: Fix Negative Time",
action = () =>
{
if (HasNegativeTimeProblem)
{
FixNegativeTimeProblem();
Messages.Message("Manually triggered negative time fix", MessageTypeDefOf.SilentInput);
}
else
{
Messages.Message("No negative time problem detected", MessageTypeDefOf.SilentInput);
}
}
};
// 新增:调试命令模拟负时间问题
yield return new Command_Action
{
defaultLabel = "Debug: Simulate Negative Time",
action = () =>
{
if (InProduction)
{
productionUntilTick = Find.TickManager.TicksGame - 1000; // 设置为1000ticks前
hasFixedNegativeTime = false;
Messages.Message("Simulated negative time problem (-1000 ticks)", MessageTypeDefOf.SilentInput);
}
else
{
Messages.Message("Not in production", MessageTypeDefOf.SilentInput);
}
}
};
}

View File

@@ -36,6 +36,11 @@ namespace ArachnaeSwarm
private int spawnTick = -1;
private const int COOLDOWN_TICKS = 120;
// 新增:修复负时间相关字段
private int lastNegativeTimeCheckTick = -1;
private const int NEGATIVE_TIME_CHECK_INTERVAL = 60; // 每60ticks检查一次
private bool hasFixedNegativeTime = false; // 标记是否已经修复过负时间问题
private CompRefuelableNutrition _fuelComp;
private static readonly Texture2D CancelIcon = ContentFinder<Texture2D>.Get("UI/Designators/Cancel");
@@ -77,6 +82,83 @@ namespace ArachnaeSwarm
}
}
// 新增获取剩余生产时间ticks
public int RemainingProductionTicks
{
get
{
if (!InProduction || productionUntilTick <= 0) return 0;
return Mathf.Max(0, productionUntilTick - Find.TickManager.TicksGame);
}
}
// 新增:检查是否存在负时间问题
private bool HasNegativeTimeProblem
{
get
{
if (!InProduction || productionUntilTick <= 0) return false;
int remainingTicks = productionUntilTick - Find.TickManager.TicksGame;
return remainingTicks < -10; // 小于-10ticks认为有问题
}
}
// 新增:修复负时间问题
private void FixNegativeTimeProblem()
{
if (!InProduction || _selectedProcess == null)
{
Log.Warning($"Attempted to fix negative time but no process is selected. Resetting production.");
ResetProduction();
return;
}
int currentTicks = Find.TickManager.TicksGame;
int remainingTicks = productionUntilTick - currentTicks;
Log.Warning($"Detected negative research production time for {parent.Label}. " +
$"Current: {currentTicks}, Target: {productionUntilTick}, Remaining: {remainingTicks}. " +
$"Research: {_selectedProcess.researchDef?.defName ?? "Unknown"}, Expected Duration: {_selectedProcess.productionTicks}");
// 计算应该设置的正确结束时间
int correctEndTick = currentTicks + _selectedProcess.productionTicks;
// 如果偏差太大,直接完成生产
if (remainingTicks < -_selectedProcess.productionTicks)
{
Log.Warning($"Negative time too large ({remainingTicks} ticks). Forcing production completion.");
FinishProduction();
return;
}
// 否则重置为正确的剩余时间
productionUntilTick = correctEndTick;
hasFixedNegativeTime = true;
Log.Message($"Fixed negative research production time for {parent.Label}. " +
$"New target: {productionUntilTick}, New remaining: {_selectedProcess.productionTicks} ticks");
// 发送消息通知(开发模式)
if (Prefs.DevMode)
{
Messages.Message($"Fixed negative research production time for {parent.Label}. Remaining: {_selectedProcess.productionTicks.ToStringTicksToPeriod()}",
MessageTypeDefOf.SilentInput);
}
}
// 新增:定期检查负时间问题
private void CheckForNegativeTime()
{
if (Find.TickManager.TicksGame > lastNegativeTimeCheckTick + NEGATIVE_TIME_CHECK_INTERVAL)
{
if (HasNegativeTimeProblem && !hasFixedNegativeTime)
{
FixNegativeTimeProblem();
}
lastNegativeTimeCheckTick = Find.TickManager.TicksGame;
}
}
public override void PostSpawnSetup(bool respawningAfterLoad)
{
base.PostSpawnSetup(respawningAfterLoad);
@@ -87,6 +169,17 @@ namespace ArachnaeSwarm
{
spawnTick = Find.TickManager.TicksGame;
}
// 新增:初始化负时间检查
lastNegativeTimeCheckTick = Find.TickManager.TicksGame;
hasFixedNegativeTime = false;
// 新增:立即检查一次负时间问题
if (InProduction && HasNegativeTimeProblem)
{
Log.Warning($"Detected negative research production time on spawn for {parent.Label}");
FixNegativeTimeProblem();
}
}
public override void PostExposeData()
@@ -99,6 +192,8 @@ namespace ArachnaeSwarm
Scribe_Values.Look(ref productionUntilTick, "productionUntilTick", -1);
Scribe_Values.Look(ref spawnTick, "spawnTick", -1);
Scribe_Values.Look(ref lastNegativeTimeCheckTick, "lastNegativeTimeCheckTick", -1);
Scribe_Values.Look(ref hasFixedNegativeTime, "hasFixedNegativeTime", false);
// 加载时重建 selectedProcess
if (Scribe.mode == LoadSaveMode.LoadingVars && selectedProcessResearchDef != null)
@@ -127,6 +222,12 @@ namespace ArachnaeSwarm
Log.Warning($"Abnormal production time detected for {selectedProcessResearchDef.defName}. Recalculating.");
productionUntilTick = Find.TickManager.TicksGame + _selectedProcess.productionTicks;
}
// 新增:检查负时间问题
else if (HasNegativeTimeProblem)
{
Log.Warning($"Negative production time detected on load for {selectedProcessResearchDef.defName}. Fixing.");
FixNegativeTimeProblem();
}
}
}
}
@@ -231,6 +332,9 @@ namespace ArachnaeSwarm
{
base.CompTick();
// 新增:定期检查负时间问题
CheckForNegativeTime();
if (InProduction && productionUntilTick > 0)
{
if (productionUntilTick <= 0)
@@ -240,6 +344,13 @@ namespace ArachnaeSwarm
return;
}
// 新增再次检查负时间问题在每次tick时
if (HasNegativeTimeProblem && !hasFixedNegativeTime)
{
FixNegativeTimeProblem();
return; // 修复后跳过本次tick的逻辑
}
if (FuelComp == null) return;
bool hasFuel = FuelComp.HasFuel;
@@ -315,6 +426,9 @@ namespace ArachnaeSwarm
float nutritionPerDay = (_selectedProcess.totalNutritionNeeded / _selectedProcess.productionTicks) * 60000f;
FuelComp.currentConsumptionRate = nutritionPerDay;
// 新增:重置负时间修复标记
hasFixedNegativeTime = false;
}
private void FinishProduction()
@@ -417,6 +531,7 @@ namespace ArachnaeSwarm
if (FuelComp != null) FuelComp.currentConsumptionRate = 0f;
_selectedProcess = null;
productionUntilTick = -1;
hasFixedNegativeTime = false; // 新增:重置修复标记
}
// 进度条显示
@@ -449,6 +564,13 @@ namespace ArachnaeSwarm
float progress = ProductionProgress;
int remainingTicks = productionUntilTick - Find.TickManager.TicksGame;
// 新增:显示负时间警告
if (remainingTicks < -10)
{
sb.AppendLine("<color=red>WARNING: Negative production time detected! Auto-fixing...</color>");
}
sb.AppendLine("Progress".Translate() + ": " + GetProgressBar(progress) + " " + progress.ToStringPercent("F0"));
sb.AppendLine("TimeLeft".Translate() + ": " + remainingTicks.ToStringTicksToPeriod());
sb.AppendLine("TechprintsToProduce".Translate() + ": " + _selectedProcess.techprintCount);
@@ -487,12 +609,16 @@ namespace ArachnaeSwarm
drawPos.y += 0.15f;
float progress = ProductionProgress;
// 新增:负时间警告颜色(红色)
Color barColor = HasNegativeTimeProblem ? Color.red : Color.green;
GenDraw.DrawFillableBar(new GenDraw.FillableBarRequest
{
center = drawPos,
size = new Vector2(1f, 0.15f),
fillPercent = progress,
filledMat = SolidColorMaterials.SimpleSolidColorMaterial(Color.green),
filledMat = SolidColorMaterials.SimpleSolidColorMaterial(barColor),
unfilledMat = SolidColorMaterials.SimpleSolidColorMaterial(Color.gray),
margin = 0.1f,
rotation = Rot4.North
@@ -522,6 +648,62 @@ namespace ArachnaeSwarm
defaultLabel = "Debug: Force Finish",
action = () => FinishProduction()
};
// 新增:调试命令显示详细进度信息
yield return new Command_Action
{
defaultLabel = "Debug: Show Progress Info",
action = () =>
{
float progress = ProductionProgress;
int remainingTicks = productionUntilTick - Find.TickManager.TicksGame;
string message = $"Progress: {progress:P0}\n" +
$"Remaining Ticks: {remainingTicks}\n" +
$"Has Negative Time: {HasNegativeTimeProblem}\n" +
$"Fixed Before: {hasFixedNegativeTime}\n" +
$"Research: {_selectedProcess.researchDef?.defName ?? "Unknown"}\n" +
$"Techprints: {_selectedProcess.techprintCount}";
Messages.Message(message, MessageTypeDefOf.SilentInput);
}
};
// 新增:调试命令手动触发负时间修复
yield return new Command_Action
{
defaultLabel = "Debug: Fix Negative Time",
action = () =>
{
if (HasNegativeTimeProblem)
{
FixNegativeTimeProblem();
Messages.Message("Manually triggered negative time fix", MessageTypeDefOf.SilentInput);
}
else
{
Messages.Message("No negative time problem detected", MessageTypeDefOf.SilentInput);
}
}
};
// 新增:调试命令模拟负时间问题
yield return new Command_Action
{
defaultLabel = "Debug: Simulate Negative Time",
action = () =>
{
if (InProduction)
{
productionUntilTick = Find.TickManager.TicksGame - 1000; // 设置为1000ticks前
hasFixedNegativeTime = false;
Messages.Message("Simulated negative time problem (-1000 ticks)", MessageTypeDefOf.SilentInput);
}
else
{
Messages.Message("Not in production", MessageTypeDefOf.SilentInput);
}
}
};
}
}
}

View File

@@ -123,8 +123,8 @@ namespace ArachnaeSwarm
DamageInfo acidDamage = new DamageInfo(
acidDamageDef,
1f, // 每次1点伤害
0.1f, // 轻微护甲穿透
3f, // 每次3点伤害
2f, // 轻微护甲穿透
-1f, // 随机角度
instigator: null,
hitPart: targetPart

View File

@@ -80,6 +80,13 @@ namespace ArachnaeSwarm
Pawn pawn = parent as Pawn;
if (pawn == null) return;
// 添加:如果有关闭 Hediff不处理缺失的寿命 Hediff
if (HasShutdownHediff)
{
Log.Message($"Lifespan hediff missing for {pawn.Label}, but shutdown hediff is present. This is expected behavior.");
return;
}
Log.Warning($"Lifespan hediff missing for {pawn.Label}. This should not happen. Forcing death.");
// 立即处死pawn
@@ -105,6 +112,20 @@ namespace ArachnaeSwarm
Pawn pawn = parent as Pawn;
if (pawn == null) return null;
// 添加:如果有关闭 Hediff不创建或返回寿命 Hediff
if (HasShutdownHediff)
{
// 如果已经存在寿命 Hediff可以选择移除它
var existingHediff = pawn.health.hediffSet.GetFirstHediffOfDef(Props.lifespanHediff);
if (existingHediff != null)
{
Log.Message($"Shutdown hediff present for {pawn.Label}. Removing lifespan hediff.");
pawn.health.RemoveHediff(existingHediff);
lifespanHediff = null;
}
return null;
}
// 首先尝试查找现有的寿命Hediff
if (lifespanHediff == null || lifespanHediff.pawn != pawn)
{
@@ -132,6 +153,19 @@ namespace ArachnaeSwarm
Pawn pawn = parent as Pawn;
if (pawn == null) return;
// 添加:如果有关闭 Hediff不进行同步
if (HasShutdownHediff)
{
// 确保移除可能存在的寿命 Hediff
var existingHediff = pawn.health.hediffSet.GetFirstHediffOfDef(Props.lifespanHediff);
if (existingHediff != null)
{
pawn.health.RemoveHediff(existingHediff);
lifespanHediff = null;
}
return;
}
var hediff = GetOrCreateLifespanHediff();
if (hediff == null)
{
@@ -167,6 +201,13 @@ namespace ArachnaeSwarm
Pawn pawn = parent as Pawn;
if (pawn == null) return;
// 添加:如果有关闭 Hediff不写入数据
if (HasShutdownHediff)
{
Log.Message($"Cannot write comp data to hediff: Shutdown hediff is present for {pawn.Label}");
return;
}
var hediff = GetOrCreateLifespanHediff();
if (hediff == null)
{
@@ -185,6 +226,20 @@ namespace ArachnaeSwarm
Pawn pawn = parent as Pawn;
if (pawn == null) return;
// 添加:如果有关闭 Hediff不进行 Hediff 校验
if (HasShutdownHediff)
{
// 确保移除可能存在的寿命 Hediff
var existingHediff = pawn.health.hediffSet.GetFirstHediffOfDef(Props.lifespanHediff);
if (existingHediff != null)
{
pawn.health.RemoveHediff(existingHediff);
lifespanHediff = null;
Log.Message($"Removed lifespan hediff for {pawn.Label} due to shutdown hediff presence");
}
return;
}
// 检查寿命Hediff是否存在
var currentHediff = pawn.health.hediffSet.GetFirstHediffOfDef(Props.lifespanHediff);
@@ -201,6 +256,19 @@ namespace ArachnaeSwarm
}
}
// 新增:检查是否需要创建寿命 Hediff
private bool ShouldCreateLifespanHediff()
{
// 如果有关闭 Hediff不应该创建寿命 Hediff
if (HasShutdownHediff)
{
return false;
}
// 其他创建条件...
return true;
}
public override void PostSpawnSetup(bool respawningAfterLoad)
{
if (!ModLister.CheckBiotechOrAnomalyOrOdyssey("Node swarm lifetime"))
@@ -222,11 +290,19 @@ namespace ArachnaeSwarm
lastHediffSyncTick = Find.TickManager.TicksGame;
lastHediffMissingCheckTick = Find.TickManager.TicksGame;
// 确保寿命Hediff存在
GetOrCreateLifespanHediff();
// 初始同步
SyncLifespanWithHediff();
// 添加:检查是否需要创建寿命 Hediff
if (ShouldCreateLifespanHediff())
{
// 确保寿命Hediff存在
GetOrCreateLifespanHediff();
// 初始同步
SyncLifespanWithHediff();
}
else
{
Log.Message($"Skipping lifespan hediff creation for {parent.Label} due to shutdown hediff");
}
}
public override IEnumerable<Gizmo> CompGetGizmosExtra()
@@ -254,12 +330,38 @@ namespace ArachnaeSwarm
}
if (DebugSettings.ShowDevGizmos)
{
// 新增:显示关闭 Hediff 状态
yield return new Command_Action
{
defaultLabel = "DEV: 显示关闭Hediff状态",
action = delegate
{
Pawn pawn = parent as Pawn;
if (pawn != null)
{
string message = $"关闭Hediff状态:\n" +
$"HasShutdownHediff: {HasShutdownHediff}\n" +
$"Shutdown Hediff Def: {Props.immuteHediff?.defName ?? "None"}\n" +
$"实际存在: {(Props.immuteHediff != null ? pawn.health.hediffSet.HasHediff(Props.immuteHediff).ToString() : "N/A")}";
Messages.Message(message, MessageTypeDefOf.SilentInput);
Log.Message(message);
}
}
};
// 新增:补满寿命调试命令
yield return new Command_Action
{
defaultLabel = "DEV: 补满寿命",
action = delegate
{
// 添加:检查关闭 Hediff
if (HasShutdownHediff)
{
Messages.Message("无法补满寿命:存在关闭系统的 Hediff", MessageTypeDefOf.RejectInput);
return;
}
powerTicksLeft = (int)(Props.lifetimeDays * 60000);
WriteCompDataToHediff(); // 使用新的写入方法
Log.Message($"已补满寿命: {parent.Label} 剩余 {DaysLeft:F1} 天");
@@ -273,6 +375,13 @@ namespace ArachnaeSwarm
defaultLabel = "DEV: 剩余0.01%寿命",
action = delegate
{
// 添加:检查关闭 Hediff
if (HasShutdownHediff)
{
Messages.Message("无法设置寿命:存在关闭系统的 Hediff", MessageTypeDefOf.RejectInput);
return;
}
int totalTicks = (int)(Props.lifetimeDays * 60000);
powerTicksLeft = (int)(totalTicks * 0.0001f); // 0.01%
WriteCompDataToHediff(); // 使用新的写入方法
@@ -288,6 +397,13 @@ namespace ArachnaeSwarm
defaultLabel = "DEV: 归零寿命",
action = delegate
{
// 添加:检查关闭 Hediff
if (HasShutdownHediff)
{
Messages.Message("无法归零寿命:存在关闭系统的 Hediff", MessageTypeDefOf.RejectInput);
return;
}
powerTicksLeft = 0;
WriteCompDataToHediff(); // 使用新的写入方法
Log.Message($"已归零寿命: {parent.Label} 将立即死亡");
@@ -320,13 +436,16 @@ namespace ArachnaeSwarm
$"剩余天数: {DaysLeft:F4}\n" +
$"总天数: {TotalDays:F1}\n" +
$"百分比: {PercentFull:P4}\n" +
$"已耗尽: {depleted}";
$"已耗尽: {depleted}\n" +
$"关闭Hediff存在: {HasShutdownHediff}";
Messages.Message(message, MessageTypeDefOf.SilentInput);
Log.Message(message);
}
else
{
Messages.Message("未找到寿命hediff", MessageTypeDefOf.SilentInput);
string message = $"未找到寿命hediff\n" +
$"关闭Hediff存在: {HasShutdownHediff}";
Messages.Message(message, MessageTypeDefOf.SilentInput);
}
}
};
@@ -362,6 +481,13 @@ namespace ArachnaeSwarm
defaultLabel = "DEV: 强制同步",
action = delegate
{
// 添加:检查关闭 Hediff
if (HasShutdownHediff)
{
Messages.Message("无法同步:存在关闭系统的 Hediff", MessageTypeDefOf.RejectInput);
return;
}
SyncLifespanWithHediff();
var hediff = GetOrCreateLifespanHediff();
if (hediff != null)
@@ -482,9 +608,17 @@ namespace ArachnaeSwarm
// 立即检查hediff是否存在
ValidateHediffState();
// 确保寿命Hediff存在并同步
GetOrCreateLifespanHediff();
SyncLifespanWithHediff();
// 添加:检查是否需要创建寿命 Hediff
if (ShouldCreateLifespanHediff())
{
// 确保寿命Hediff存在并同步
GetOrCreateLifespanHediff();
SyncLifespanWithHediff();
}
else
{
Log.Message($"Skipping lifespan hediff creation for {parent.Label} after load due to shutdown hediff");
}
}
}
}

View File

@@ -2,9 +2,31 @@ using RimWorld;
using UnityEngine;
using Verse;
using System.Collections.Generic;
using System.Linq;
namespace ArachnaeSwarm
{
public static class ARA_Translations
{
// 武装方案相关
public static readonly string UnknownScheme = "ARA_UnknownScheme";
public static readonly string SchemeFormat = "ARA_SchemeFormat";
// 消息提示
public static readonly string PowerArmorDamaged = "ARA_PowerArmorDamaged";
public static readonly string SwitchedToMode = "ARA_SwitchedToMode";
public static readonly string EquipPowerArmorFailed = "ARA_EquipPowerArmorFailed";
public static readonly string AlreadySelectedScheme = "ARA_AlreadySelectedScheme";
// Gizmo 按钮文本
public static readonly string SwitchWeapon = "ARA_SwitchWeapon";
public static readonly string SwitchWeaponDesc = "ARA_SwitchWeaponDesc";
public static readonly string SwitchToScheme = "ARA_SwitchToScheme";
// 默认值
public static readonly string Default = "ARA_Default";
}
public interface IStructurePoints
{
float StructurePoints { get; }
@@ -13,13 +35,28 @@ namespace ArachnaeSwarm
string Label { get; }
}
public class PowerArmorWeaponSet
{
public string label; // 方案名称显示在UI上
public string description; // 方案描述显示在按钮的desc上
public ThingDef weapon; // 武器定义
public List<HediffDef> hediffsToAdd; // 切换到此方案时添加的Hediff
public List<HediffDef> hediffsToRemove; // 切换到此方案时移除的Hediff
public string iconPath; // 可选的图标路径
}
public class PowerArmorExtension : DefModExtension
{
public ThingDef buildingDef;
public float structurePointsMax = 500f;
public HediffDef hediffOnEmptyFuel;
public float fuelConsumptionRate = 0.5f; // Nutrition per day
public ThingDef powerArmorWeapon;
// 废弃原来的单个武器,改用列表
public List<PowerArmorWeaponSet> weaponSets = new List<PowerArmorWeaponSet>();
// 默认武装方案索引
public int defaultWeaponSetIndex = 0;
}
[StaticConstructorOnStartup]
@@ -52,8 +89,18 @@ namespace ArachnaeSwarm
public float StructurePointsPercent => StructurePoints / StructurePointsMax;
public Building sourceBuilding;
private ThingWithComps originalWeapon; // Still needed to store pawn's original weapon
private ThingWithComps currentPowerArmorWeapon; // Track the currently equipped power armor weapon
private ThingWithComps originalWeapon;
private ThingWithComps currentPowerArmorWeapon;
// 新增字段:武装方案管理
private int currentWeaponSetIndex = 0;
private List<Hediff> activeHediffs = new List<Hediff>();
public int CurrentWeaponSetIndex => currentWeaponSetIndex;
public PowerArmorWeaponSet CurrentWeaponSet =>
Ext?.weaponSets != null && Ext.weaponSets.Count > currentWeaponSetIndex ?
Ext.weaponSets[currentWeaponSetIndex] : null;
public int WeaponSetCount => Ext?.weaponSets?.Count ?? 0;
public void SetOriginalWeapon(ThingWithComps weapon)
{
@@ -66,6 +113,280 @@ namespace ArachnaeSwarm
}
#endregion
#region /
public override void Notify_Equipped(Pawn pawn)
{
base.Notify_Equipped(pawn);
// 设置默认武装方案
currentWeaponSetIndex = Ext?.defaultWeaponSetIndex ?? 0;
// 应用初始武装方案
ApplyWeaponSet(pawn, CurrentWeaponSet);
Log.Message($"[PA_Debug] 装备动力装甲,应用武装方案: {CurrentWeaponSet?.label ?? ""}");
}
public override void Notify_Unequipped(Pawn pawn)
{
// 清除所有激活的Hediff
foreach (var hediff in activeHediffs)
{
if (pawn.health.hediffSet.hediffs.Contains(hediff))
{
pawn.health.RemoveHediff(hediff);
}
}
activeHediffs.Clear();
base.Notify_Unequipped(pawn);
// 原有卸下逻辑保持不变...
if (Ext?.weaponSets != null && Ext.weaponSets.Count > 0)
{
// 销毁当前动力装甲武器
if (currentPowerArmorWeapon != null)
{
if (pawn?.equipment != null && pawn.equipment.Contains(currentPowerArmorWeapon))
{
pawn.equipment.Remove(currentPowerArmorWeapon);
}
else if (pawn?.inventory?.innerContainer != null && pawn.inventory.innerContainer.Contains(currentPowerArmorWeapon))
{
pawn.inventory.innerContainer.Remove(currentPowerArmorWeapon);
}
else if (currentPowerArmorWeapon.Spawned)
{
currentPowerArmorWeapon.DeSpawn();
}
string destroyedWeaponLabel = currentPowerArmorWeapon.Label;
currentPowerArmorWeapon.Destroy();
Log.Message($"[PA_Debug] Notify_Unequipped: 销毁动力装甲武器 {destroyedWeaponLabel}.");
currentPowerArmorWeapon = null;
}
// 恢复原始武器
if (originalWeapon != null && pawn?.equipment != null)
{
string originalWeaponLabel = originalWeapon.Label;
pawn.equipment.MakeRoomFor(originalWeapon);
pawn.equipment.AddEquipment(originalWeapon);
Log.Message($"[PA_Debug] Notify_Unequipped: 恢复原始武器 {originalWeaponLabel}.");
originalWeapon = null;
}
}
Building building = sourceBuilding;
// 如果源建筑引用丢失,创建新建筑作为回退
if (building == null)
{
ThingDef buildingDef = Ext?.buildingDef;
if (buildingDef == null)
{
Log.Error($"[ArachnaeSwarm] 动力装甲 {this.def.defName} 卸下但在其PowerArmorExtension中未定义buildingDef且源建筑引用丢失。");
this.Destroy(DestroyMode.Vanish);
return;
}
building = (Building)ThingMaker.MakeThing(buildingDef);
}
// 同步健康值回建筑
building.HitPoints = Mathf.Max(1, Mathf.RoundToInt(this.StructurePoints));
// 同步燃料回建筑
var apparelFuelComp = this.GetComp<CompRefuelableNutrition>();
var buildingFuelComp = building.GetComp<CompRefuelableNutrition>();
if (apparelFuelComp != null && buildingFuelComp != null)
{
buildingFuelComp.ConsumeFuel(buildingFuelComp.Fuel);
buildingFuelComp.ReceiveFuel(apparelFuelComp.Fuel);
}
// 同步质量回建筑
if (this.TryGetComp<CompQuality>() is CompQuality apparelQuality && building.TryGetComp<CompQuality>() is CompQuality buildingQuality)
{
buildingQuality.SetQuality(apparelQuality.Quality, ArtGenerationContext.Colony);
}
Log.Message($"[PA_Debug] Notify_Unequipped: 生成建筑前 (ID: {building.thingIDNumber}) - HitPoints: {building.HitPoints}, StackCount: {building.stackCount}");
// 确保建筑堆叠数至少为1
if (building.stackCount <= 0)
{
building.stackCount = 1;
Log.Warning($"[PA_Debug] Notify_Unequipped: 修正建筑 (ID: {building.thingIDNumber}) 堆叠数为1因为原为0。");
}
// 设置派系
building.SetFaction(pawn.Faction);
// 重新生成原始建筑实例
GenPlace.TryPlaceThing(building, pawn.Position, pawn.Map, ThingPlaceMode.Near);
Log.Message($"[PA_Debug] Notify_Unequipped: 生成建筑后 (ID: {building.thingIDNumber}) - HitPoints: {building.HitPoints}, StackCount: {building.stackCount}");
}
#endregion
#region
/// <summary>
/// 切换到下一个武装方案
/// </summary>
public void CycleToNextWeaponSet()
{
if (WeaponSetCount <= 1) return;
var oldSet = CurrentWeaponSet;
currentWeaponSetIndex = (currentWeaponSetIndex + 1) % WeaponSetCount;
var newSet = CurrentWeaponSet;
Log.Message($"[PA_Debug] 切换武装方案: {oldSet?.label ?? ""} -> {newSet?.label ?? ""}");
ApplyWeaponSet(Wearer, newSet, oldSet);
}
/// <summary>
/// 切换到指定索引的武装方案
/// </summary>
public void SwitchToWeaponSet(int index)
{
if (index < 0 || index >= WeaponSetCount) return;
var oldSet = CurrentWeaponSet;
currentWeaponSetIndex = index;
var newSet = CurrentWeaponSet;
Log.Message($"[PA_Debug] 切换到武装方案: {oldSet?.label ?? ""} -> {newSet?.label ?? ""}");
ApplyWeaponSet(Wearer, newSet, oldSet);
}
/// <summary>
/// 应用指定的武装方案
/// </summary>
private void ApplyWeaponSet(Pawn pawn, PowerArmorWeaponSet newSet, PowerArmorWeaponSet oldSet = null)
{
if (pawn == null) return;
// 移除旧方案的Hediff
if (oldSet?.hediffsToRemove != null)
{
foreach (var hediffDef in oldSet.hediffsToRemove)
{
var hediff = pawn.health.hediffSet.GetFirstHediffOfDef(hediffDef);
if (hediff != null)
{
pawn.health.RemoveHediff(hediff);
}
}
}
// 清除当前激活的Hediff
foreach (var hediff in activeHediffs.ToList())
{
if (pawn.health.hediffSet.hediffs.Contains(hediff))
{
pawn.health.RemoveHediff(hediff);
}
}
activeHediffs.Clear();
// 添加新方案的Hediff
if (newSet?.hediffsToAdd != null)
{
foreach (var hediffDef in newSet.hediffsToAdd)
{
var hediff = pawn.health.GetOrAddHediff(hediffDef);
activeHediffs.Add(hediff);
Log.Message($"[PA_Debug] 添加Hediff: {hediffDef.defName}");
}
}
// 处理武器切换
HandleWeaponSwitch(pawn, newSet);
// 显示切换消息
if (newSet != null)
{
Messages.Message(ARA_Translations.SwitchedToMode.Translate(newSet.label), pawn, MessageTypeDefOf.NeutralEvent);
}
}
/// <summary>
/// 处理武器切换逻辑
/// </summary>
private void HandleWeaponSwitch(Pawn pawn, PowerArmorWeaponSet newSet)
{
if (pawn?.equipment == null) return;
// 销毁当前动力装甲武器
if (currentPowerArmorWeapon != null)
{
if (pawn.equipment.Contains(currentPowerArmorWeapon))
{
pawn.equipment.Remove(currentPowerArmorWeapon);
}
currentPowerArmorWeapon.Destroy();
currentPowerArmorWeapon = null;
Log.Message($"[PA_Debug] 销毁当前动力装甲武器");
}
// 如果新方案有武器,装备新武器
if (newSet?.weapon != null)
{
ThingWithComps weapon = (ThingWithComps)ThingMaker.MakeThing(newSet.weapon);
// 同步武器质量
if (this.TryGetComp<CompQuality>() is CompQuality apparelQuality &&
weapon.TryGetComp<CompQuality>() is CompQuality weaponQuality)
{
weaponQuality.SetQuality(apparelQuality.Quality, ArtGenerationContext.Colony);
}
pawn.equipment.MakeRoomFor(weapon);
pawn.equipment.AddEquipment(weapon);
SetCurrentPowerArmorWeapon(weapon);
Log.Message($"[PA_Debug] 装备新武器: {weapon.Label}");
}
// 如果没有武器,恢复原始武器
else if (originalWeapon != null)
{
pawn.equipment.MakeRoomFor(originalWeapon);
pawn.equipment.AddEquipment(originalWeapon);
Log.Message($"[PA_Debug] 恢复原始武器: {originalWeapon.Label}");
}
}
/// <summary>
/// 获取武装方案显示名称
/// </summary>
public string GetWeaponSetDisplayName(int index)
{
if (index < 0 || index >= WeaponSetCount) return ARA_Translations.UnknownScheme.Translate();
var set = Ext.weaponSets[index];
return set.label ?? ARA_Translations.SchemeFormat.Translate(index + 1);
}
/// <summary>
/// 获取武装方案描述
/// </summary>
public string GetWeaponSetDescription(int index)
{
if (index < 0 || index >= WeaponSetCount) return string.Empty;
var set = Ext.weaponSets[index];
// 如果设置了自定义描述,使用自定义描述
if (!string.IsNullOrEmpty(set.description))
{
return set.description;
}
// 否则使用默认的翻译文本
return ARA_Translations.SwitchToScheme.Translate(
set.label ?? ARA_Translations.SchemeFormat.Translate(index + 1));
}
#endregion
#region Ticker
protected override void Tick()
{
@@ -82,15 +403,11 @@ namespace ArachnaeSwarm
var fuelComp = this.GetComp<CompRefuelableNutrition>();
if (fuelComp != null)
{
// Set the consumption rate on every tick. This value is then used by CompRefuelableNutrition's CompTick.
// We use the value from our PowerArmorExtension, ensuring it's always active.
// 设置消耗率
fuelComp.currentConsumptionRate = Ext?.fuelConsumptionRate ?? 0f;
// We must explicitly call the component's Tick, as Apparel's base Tick does not.
// This will trigger the consumption logic inside CompRefuelableNutrition.
fuelComp.CompTick();
// Handle hediff for empty fuel, checked frequently for responsiveness.
// 处理空燃料Hediff
if (this.Wearer.IsHashIntervalTick(60))
{
var hediffDef = Ext?.hediffOnEmptyFuel;
@@ -117,31 +434,72 @@ namespace ArachnaeSwarm
}
#endregion
#region Data
#region
public override void ExposeData()
{
base.ExposeData();
Scribe_Values.Look(ref structurePoints, "structurePoints", -1f);
Scribe_References.Look(ref sourceBuilding, "sourceBuilding");
Scribe_References.Look(ref originalWeapon, "originalWeapon");
Scribe_References.Look(ref currentPowerArmorWeapon, "currentPowerArmorWeapon"); // Save/load current power armor weapon
Scribe_References.Look(ref currentPowerArmorWeapon, "currentPowerArmorWeapon");
Scribe_Values.Look(ref currentWeaponSetIndex, "currentWeaponSetIndex", 0);
// 注意Hediff不需要保存因为会在加载时重新应用
}
#endregion
#region Gizmo
public override IEnumerable<Gizmo> GetWornGizmos()
{
// Always yield base gizmos first.
// 基础Gizmo
foreach (var gizmo in base.GetWornGizmos())
{
yield return gizmo;
}
// Yield our custom structure points gizmo.
// 结构点面板
yield return new Gizmo_StructurePanel(this);
// Yield the default gizmos from the CompRefuelable component.
// This will provide the standard RimWorld fuel gizmo.
// 武装方案切换按钮(只有在有多个方案时显示)
if (WeaponSetCount > 1)
{
// 主切换按钮(已注释,但保留翻译)
/*
yield return new Command_Action
{
defaultLabel = ARA_Translations.SwitchWeapon.Translate(CurrentWeaponSet?.label ?? ARA_Translations.Default.Translate()),
defaultDesc = ARA_Translations.SwitchWeaponDesc.Translate(
CurrentWeaponSet?.label ?? ARA_Translations.Default.Translate(),
WeaponSetCount),
icon = ContentFinder<Texture2D>.Get("UI/Commands/Attack") ?? BaseContent.BadTex,
action = CycleToNextWeaponSet
};
*/
// 为每个武装方案提供单独的切换按钮
for (int i = 0; i < WeaponSetCount; i++)
{
int currentIndex = i; // 创建局部变量捕获当前索引
var set = Ext.weaponSets[currentIndex];
var command = new Command_Action
{
defaultLabel = set.label ?? ARA_Translations.SchemeFormat.Translate(currentIndex + 1),
defaultDesc = GetWeaponSetDescription(currentIndex), // 使用新的描述获取方法
icon = set.iconPath != null ? ContentFinder<Texture2D>.Get(set.iconPath) : ContentFinder<Texture2D>.Get("UI/Commands/Attack"),
action = () => SwitchToWeaponSet(currentIndex) // 使用局部变量
};
// 正确禁用当前已选择的方案按钮
if (currentIndex == currentWeaponSetIndex)
{
command.Disable(ARA_Translations.AlreadySelectedScheme.Translate());
}
yield return command;
}
}
// 燃料组件Gizmo
var fuelComp = this.GetComp<CompRefuelableNutrition>();
if (fuelComp != null)
{
@@ -152,105 +510,8 @@ namespace ArachnaeSwarm
}
}
#endregion
#region State-Switching
public override void Notify_Unequipped(Pawn pawn)
{
base.Notify_Unequipped(pawn);
// Handle power armor weapon destruction and original weapon restoration
if (Ext?.powerArmorWeapon != null)
{
// Destroy the power armor weapon, wherever it might be.
// We track it with currentPowerArmorWeapon, so we don't rely on pawn.equipment.Primary.
if (currentPowerArmorWeapon != null)
{
if (pawn?.equipment != null && pawn.equipment.Contains(currentPowerArmorWeapon))
{
pawn.equipment.Remove(currentPowerArmorWeapon);
}
else if (pawn?.inventory?.innerContainer != null && pawn.inventory.innerContainer.Contains(currentPowerArmorWeapon))
{
pawn.inventory.innerContainer.Remove(currentPowerArmorWeapon);
}
// If it's on the map, destroy it there.
else if (currentPowerArmorWeapon.Spawned)
{
currentPowerArmorWeapon.DeSpawn();
}
string destroyedWeaponLabel = currentPowerArmorWeapon.Label;
currentPowerArmorWeapon.Destroy();
Log.Message($"[PA_Debug] Notify_Unequipped: Destroyed power armor weapon {destroyedWeaponLabel}.");
currentPowerArmorWeapon = null;
}
// Restore original weapon if saved
if (originalWeapon != null && pawn?.equipment != null)
{
string originalWeaponLabel = originalWeapon.Label;
pawn.equipment.MakeRoomFor(originalWeapon);
pawn.equipment.AddEquipment(originalWeapon);
Log.Message($"[PA_Debug] Notify_Unequipped: Restored original weapon {originalWeaponLabel}.");
originalWeapon = null;
}
}
Building building = sourceBuilding;
// If the source building reference is lost, create a new one as a fallback.
if (building == null)
{
ThingDef buildingDef = Ext?.buildingDef;
if (buildingDef == null)
{
Log.Error($"[ArachnaeSwarm] Power Armor {this.def.defName} unequipped, but has no buildingDef defined in its PowerArmorExtension and the source building reference was lost.");
this.Destroy(DestroyMode.Vanish);
return;
}
building = (Building)ThingMaker.MakeThing(buildingDef);
}
// Sync health back to the building, ensuring it's at least 1 to prevent it from being destroyed instantly.
building.HitPoints = Mathf.Max(1, Mathf.RoundToInt(this.StructurePoints));
// Sync fuel back to the building
var apparelFuelComp = this.GetComp<CompRefuelableNutrition>();
var buildingFuelComp = building.GetComp<CompRefuelableNutrition>();
if (apparelFuelComp != null && buildingFuelComp != null)
{
// Reset building fuel and then set it to the apparel's current fuel to avoid overflow.
buildingFuelComp.ConsumeFuel(buildingFuelComp.Fuel);
buildingFuelComp.ReceiveFuel(apparelFuelComp.Fuel);
}
// Sync quality back to building
if (this.TryGetComp<CompQuality>() is CompQuality apparelQuality && building.TryGetComp<CompQuality>() is CompQuality buildingQuality)
{
buildingQuality.SetQuality(apparelQuality.Quality, ArtGenerationContext.Colony);
}
Log.Message($"[PA_Debug] Notify_Unequipped: Before spawning building (ID: {building.thingIDNumber}) - HitPoints: {building.HitPoints}, StackCount: {building.stackCount}");
// Ensure stackCount is at least 1 for buildings, as 0 stackCount causes errors during spawning
if (building.stackCount <= 0)
{
building.stackCount = 1;
Log.Warning($"[PA_Debug] Notify_Unequipped: Corrected building (ID: {building.thingIDNumber}) stackCount to 1 as it was 0.");
}
// Set the faction to the pawn's faction before spawning
building.SetFaction(pawn.Faction);
// Re-spawn the original building instance
GenPlace.TryPlaceThing(building, pawn.Position, pawn.Map, ThingPlaceMode.Near);
Log.Message($"[PA_Debug] Notify_Unequipped: After spawning building (ID: {building.thingIDNumber}) - HitPoints: {building.HitPoints}, StackCount: {building.stackCount}");
// The destruction is now handled by the Tick() method when it detects Wearer is null.
// This is the safest way to implement delayed self-destruction.
}
#endregion
#region Damage Handling - THE FINAL, CORRECT IMPLEMENTATION
#region
public override bool CheckPreAbsorbDamage(DamageInfo dinfo)
{
if (this.Wearer == null || !dinfo.Def.harmsHealth || dinfo.Amount <= 0.001f)
@@ -267,7 +528,7 @@ namespace ArachnaeSwarm
if (this.StructurePoints <= 0)
{
Messages.Message("PowerArmorBroken".Translate(this.Label, this.Wearer.LabelShort), this, MessageTypeDefOf.NegativeEvent);
Messages.Message(ARA_Translations.PowerArmorDamaged.Translate(this.Label, this.Wearer.LabelShort), this, MessageTypeDefOf.NegativeEvent);
this.Destroy(DestroyMode.KillFinalize);
}
}
@@ -276,7 +537,7 @@ namespace ArachnaeSwarm
EffecterDefOf.Deflect_Metal_Bullet.SpawnAttached(this.Wearer, this.Wearer.Map, 1f);
}
// By returning true, we tell the game the damage has been fully handled and should not proceed to the pawn.
// 返回true表示伤害已被完全处理不应继续传递给pawn
return true;
}
@@ -310,4 +571,4 @@ namespace ArachnaeSwarm
}
#endregion
}
}
}

View File

@@ -20,80 +20,167 @@ namespace ArachnaeSwarm
this.FailOnDespawnedNullOrForbidden(BuildingInd);
this.FailOnSomeonePhysicallyInteracting(BuildingInd);
// 第一步:移动到动力装甲站
yield return Toils_Goto.GotoThing(BuildingInd, PathEndMode.InteractionCell);
// 第二步等待装备过程120 ticks = 2秒
yield return Toils_General.Wait(120, BuildingInd).WithProgressBarToilDelay(BuildingInd);
// 第三步:执行装备动力装甲
Toil enter = new Toil();
enter.initAction = () =>
{
Pawn actor = enter.actor;
var building = (Building)actor.CurJob.GetTarget(BuildingInd).Thing;
// Get the CompProperties from the building's new component
// 获取动力装甲站的组件属性
var compProps = building.GetComp<CompPowerArmorStation>()?.Props;
if (compProps != null && compProps.apparelDef != null)
{
// Create the apparel
// 创建动力装甲装备
ARA_PowerArmor apparel = (ARA_PowerArmor)ThingMaker.MakeThing(compProps.apparelDef);
// CRITICAL STEP: Link the apparel back to the building
// 关键步骤:将装备链接回建筑
apparel.sourceBuilding = building;
// Sync health from building to apparel
// 从建筑同步健康值到装备
apparel.StructurePoints = building.HitPoints;
// Sync fuel from building to apparel
// 从建筑同步燃料到装备
var buildingFuelComp = building.GetComp<CompRefuelableNutrition>();
var apparelFuelComp = apparel.GetComp<CompRefuelableNutrition>();
if (buildingFuelComp != null && apparelFuelComp != null)
{
// 重置装备燃料并同步建筑燃料
apparelFuelComp.ConsumeFuel(apparelFuelComp.Fuel); // 先清空
apparelFuelComp.ReceiveFuel(buildingFuelComp.Fuel);
Log.Message($"[PA_Debug] 同步燃料: 建筑 {buildingFuelComp.Fuel} -> 装备 {apparelFuelComp.Fuel}");
}
// Sync quality
if (building.TryGetComp<CompQuality>() is CompQuality buildingQuality && apparel.TryGetComp<CompQuality>() is CompQuality apparelQuality)
// 同步质量
if (building.TryGetComp<CompQuality>() is CompQuality buildingQuality &&
apparel.TryGetComp<CompQuality>() is CompQuality apparelQuality)
{
apparelQuality.SetQuality(buildingQuality.Quality, ArtGenerationContext.Colony);
Log.Message($"[PA_Debug] 同步质量: {buildingQuality.Quality}");
}
// Wear the apparel. The second argument 'false' is lockWhileWorn.
// The third argument 'false' is playerForced, which is CRITICAL.
// If playerForced is true, the game automatically locks the apparel.
actor.apparel.Wear(apparel, false, false);
// Handle weapon switching
if (apparel.Ext.powerArmorWeapon != null)
// 保存原始武器(如果有)
ThingWithComps originalWeapon = actor.equipment?.Primary;
if (originalWeapon != null)
{
if (actor.equipment.Primary != null)
{
apparel.SetOriginalWeapon(actor.equipment.Primary);
actor.equipment.TryDropEquipment(actor.equipment.Primary, out _, actor.Position, false);
}
ThingWithComps weapon = (ThingWithComps)ThingMaker.MakeThing(apparel.Ext.powerArmorWeapon);
// Sync weapon quality with armor quality
if (apparel.TryGetComp<CompQuality>() is CompQuality existingApparelQuality && weapon.TryGetComp<CompQuality>() is CompQuality weaponQuality)
{
weaponQuality.SetQuality(existingApparelQuality.Quality, ArtGenerationContext.Colony);
}
actor.equipment.MakeRoomFor(weapon);
actor.equipment.AddEquipment(weapon);
// Track the power armor weapon so it can be destroyed later
apparel.SetCurrentPowerArmorWeapon(weapon);
apparel.SetOriginalWeapon(originalWeapon);
Log.Message($"[PA_Debug] 保存原始武器: {originalWeapon.Label}");
}
// Despawn the building
// 获取动力装甲扩展和默认武装方案
var powerArmorExt = apparel.Ext;
PowerArmorWeaponSet defaultSet = null;
// 确定要使用的武装方案
if (powerArmorExt?.weaponSets != null && powerArmorExt.weaponSets.Count > 0)
{
// 使用默认方案索引,确保在有效范围内
int defaultIndex = powerArmorExt.defaultWeaponSetIndex;
if (defaultIndex < 0 || defaultIndex >= powerArmorExt.weaponSets.Count)
{
defaultIndex = 0; // 回退到第一个方案
}
defaultSet = powerArmorExt.weaponSets[defaultIndex];
Log.Message($"[PA_Debug] 使用武装方案: {defaultSet?.label ?? ""} (索引: {defaultIndex})");
// 如果默认方案有武器,需要卸下原始武器
if (defaultSet?.weapon != null && originalWeapon != null)
{
// 卸下原始武器,为动力装甲武器腾出位置
if (actor.equipment.Contains(originalWeapon))
{
actor.equipment.Remove(originalWeapon);
Log.Message($"[PA_Debug] 卸下原始武器: {originalWeapon.Label}");
}
// 创建并装备动力装甲武器
ThingWithComps powerArmorWeapon = (ThingWithComps)ThingMaker.MakeThing(defaultSet.weapon);
// 同步武器质量与装甲质量
if (apparel.TryGetComp<CompQuality>() is CompQuality existingApparelQuality &&
powerArmorWeapon.TryGetComp<CompQuality>() is CompQuality weaponQuality)
{
weaponQuality.SetQuality(existingApparelQuality.Quality, ArtGenerationContext.Colony);
}
// 装备动力装甲武器
actor.equipment.MakeRoomFor(powerArmorWeapon);
actor.equipment.AddEquipment(powerArmorWeapon);
apparel.SetCurrentPowerArmorWeapon(powerArmorWeapon);
Log.Message($"[PA_Debug] 装备动力装甲武器: {powerArmorWeapon.Label}");
}
// 如果默认方案没有武器,但原始武器存在,保留原始武器
else if (originalWeapon != null)
{
Log.Message($"[PA_Debug] 默认方案无武器,保留原始武器: {originalWeapon.Label}");
}
}
else
{
Log.Message($"[PA_Debug] 无武装方案配置,使用原有逻辑");
// 如果没有武装方案配置,就不进行武器切换
// 保留殖民者的原始武器
if (originalWeapon != null)
{
Log.Message($"[PA_Debug] 无武装方案,保留原始武器: {originalWeapon.Label}");
}
else
{
Log.Message($"[PA_Debug] 无武装方案,且无原始武器");
}
}
// 穿戴装备
// 第三个参数 'false' 是 playerForced这很关键
// 如果 playerForced 为 true游戏会自动锁定装备
actor.apparel.Wear(apparel, false, false);
Log.Message($"[PA_Debug] 成功穿戴动力装甲: {apparel.Label}");
// 销毁建筑
building.DeSpawn();
Log.Message($"[PA_Debug] 销毁动力装甲站建筑");
// 显示成功消息
Messages.Message($"{actor.LabelShort} 已装备{apparel.Label}", actor, MessageTypeDefOf.PositiveEvent);
}
else
{
Log.Error($"[ArachnaeSwarm] Power Armor Building {building.def.defName} is missing CompProperties_PowerArmorStation or apparelDef.");
Log.Error($"[ArachnaeSwarm] 动力装甲建筑 {building.def.defName} 缺少 CompProperties_PowerArmorStation apparelDef");
// 显示错误消息
Messages.Message("装备动力装甲失败:配置错误", actor, MessageTypeDefOf.NegativeEvent);
}
};
enter.defaultCompleteMode = ToilCompleteMode.Instant;
yield return enter;
}
// 添加失败处理
public override string GetReport()
{
return "正在装备动力装甲...";
}
}
}
// 可选的:为动力装甲站添加工作定义
[DefOf]
public static class JobDefOf_ARA
{
public static JobDef ARA_EnterPowerArmor;
static JobDefOf_ARA()
{
DefOfHelper.EnsureInitializedInCtor(typeof(JobDefOf_ARA));
}
}
}

View File

@@ -79,4 +79,24 @@ namespace ArachnaeSwarm
disappearsComp?.ResetElapsedTicks();
}
}
}
// 新增补丁:为 Verb_ShootBeamExplosive 添加支持
[HarmonyPatch(typeof(Verb_ShootBeamExplosive), "TryCastShot")]
public static class Patch_Verb_ShootBeamExplosive_TryCastShot
{
public static void Postfix(Verb_ShootBeamExplosive __instance, bool __result)
{
if (!__result) return;
if (__instance.CasterPawn == null || __instance.EquipmentSource == null) return;
CompGiveHediffOnShot comp = __instance.EquipmentSource.GetComp<CompGiveHediffOnShot>();
if (comp == null || comp.Props.hediffDef == null) return;
Hediff hediff = __instance.CasterPawn.health.GetOrAddHediff(comp.Props.hediffDef);
hediff.Severity += comp.Props.severityToAdd;
var disappearsComp = hediff.TryGetComp<HediffComp_Disappears>();
disappearsComp?.ResetElapsedTicks();
}
}
}