✅ 创建了 ArachnaeLog.cs - 中央化日志类,仅检查mod设置(不检查DevMode) ✅ 创建了 ArachnaeSwarmMod.cs - Mod主类,提供UI设置选项 ✅ 修改了 MainHarmony.cs - 移除重复的Harmony初始化(现在由ArachnaeSwarmMod处理) ✅ 修改了 .csproj - 添加了3个新文件到编译列表 ✅ 替换了所有582个 Log.Message/Error/Warning 调用为 ArachnaeLog.Debug()
575 lines
22 KiB
C#
575 lines
22 KiB
C#
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; }
|
||
float StructurePointsMax { get; }
|
||
float StructurePointsPercent { get; }
|
||
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 List<PowerArmorWeaponSet> weaponSets = new List<PowerArmorWeaponSet>();
|
||
|
||
// 默认武装方案索引
|
||
public int defaultWeaponSetIndex = 0;
|
||
}
|
||
|
||
[StaticConstructorOnStartup]
|
||
public class ARA_PowerArmor : Apparel, IStructurePoints
|
||
{
|
||
#region Properties
|
||
private PowerArmorExtension ext;
|
||
public PowerArmorExtension Ext => ext ??= def.GetModExtension<PowerArmorExtension>();
|
||
public override string Label => base.Label;
|
||
|
||
private float structurePoints = -1f;
|
||
public float StructurePointsMax => Ext?.structurePointsMax ?? 500f;
|
||
|
||
public float StructurePoints
|
||
{
|
||
get
|
||
{
|
||
if (structurePoints < 0)
|
||
{
|
||
structurePoints = StructurePointsMax;
|
||
}
|
||
return structurePoints;
|
||
}
|
||
set
|
||
{
|
||
structurePoints = Mathf.Clamp(value, 0, StructurePointsMax);
|
||
}
|
||
}
|
||
|
||
public float StructurePointsPercent => StructurePoints / StructurePointsMax;
|
||
|
||
public Building sourceBuilding;
|
||
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)
|
||
{
|
||
originalWeapon = weapon;
|
||
}
|
||
|
||
public void SetCurrentPowerArmorWeapon(ThingWithComps weapon)
|
||
{
|
||
currentPowerArmorWeapon = weapon;
|
||
}
|
||
#endregion
|
||
|
||
#region 装备/卸下处理
|
||
public override void Notify_Equipped(Pawn pawn)
|
||
{
|
||
base.Notify_Equipped(pawn);
|
||
|
||
// 设置默认武装方案
|
||
currentWeaponSetIndex = Ext?.defaultWeaponSetIndex ?? 0;
|
||
|
||
// 应用初始武装方案
|
||
ApplyWeaponSet(pawn, CurrentWeaponSet);
|
||
|
||
ArachnaeLog.Debug($"[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();
|
||
ArachnaeLog.Debug($"[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);
|
||
ArachnaeLog.Debug($"[PA_Debug] Notify_Unequipped: 恢复原始武器 {originalWeaponLabel}.");
|
||
originalWeapon = null;
|
||
}
|
||
}
|
||
|
||
Building building = sourceBuilding;
|
||
|
||
// 如果源建筑引用丢失,创建新建筑作为回退
|
||
if (building == null)
|
||
{
|
||
ThingDef buildingDef = Ext?.buildingDef;
|
||
if (buildingDef == null)
|
||
{
|
||
ArachnaeLog.Debug($"[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);
|
||
}
|
||
|
||
ArachnaeLog.Debug($"[PA_Debug] Notify_Unequipped: 生成建筑前 (ID: {building.thingIDNumber}) - HitPoints: {building.HitPoints}, StackCount: {building.stackCount}");
|
||
|
||
// 确保建筑堆叠数至少为1
|
||
if (building.stackCount <= 0)
|
||
{
|
||
building.stackCount = 1;
|
||
ArachnaeLog.Debug($"[PA_Debug] Notify_Unequipped: 修正建筑 (ID: {building.thingIDNumber}) 堆叠数为1,因为原为0。");
|
||
}
|
||
|
||
// 设置派系
|
||
building.SetFaction(pawn.Faction);
|
||
|
||
// 重新生成原始建筑实例
|
||
GenPlace.TryPlaceThing(building, pawn.Position, pawn.Map, ThingPlaceMode.Near);
|
||
ArachnaeLog.Debug($"[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;
|
||
|
||
ArachnaeLog.Debug($"[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;
|
||
|
||
ArachnaeLog.Debug($"[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);
|
||
ArachnaeLog.Debug($"[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;
|
||
ArachnaeLog.Debug($"[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);
|
||
|
||
ArachnaeLog.Debug($"[PA_Debug] 装备新武器: {weapon.Label}");
|
||
}
|
||
// 如果没有武器,恢复原始武器
|
||
else if (originalWeapon != null)
|
||
{
|
||
pawn.equipment.MakeRoomFor(originalWeapon);
|
||
pawn.equipment.AddEquipment(originalWeapon);
|
||
ArachnaeLog.Debug($"[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()
|
||
{
|
||
base.Tick();
|
||
if (this.Wearer == null)
|
||
{
|
||
if (!this.Destroyed)
|
||
{
|
||
this.Destroy();
|
||
}
|
||
return;
|
||
}
|
||
|
||
var fuelComp = this.GetComp<CompRefuelableNutrition>();
|
||
if (fuelComp != null)
|
||
{
|
||
// 设置消耗率
|
||
fuelComp.currentConsumptionRate = Ext?.fuelConsumptionRate ?? 0f;
|
||
fuelComp.CompTick();
|
||
|
||
// 处理空燃料Hediff
|
||
if (this.Wearer.IsHashIntervalTick(60))
|
||
{
|
||
var hediffDef = Ext?.hediffOnEmptyFuel;
|
||
if (hediffDef != null)
|
||
{
|
||
var hediff = this.Wearer.health.hediffSet.GetFirstHediffOfDef(hediffDef);
|
||
if (!fuelComp.HasFuel)
|
||
{
|
||
if (hediff == null)
|
||
{
|
||
this.Wearer.health.AddHediff(hediffDef);
|
||
}
|
||
}
|
||
else
|
||
{
|
||
if (hediff != null)
|
||
{
|
||
this.Wearer.health.RemoveHediff(hediff);
|
||
}
|
||
}
|
||
}
|
||
}
|
||
}
|
||
}
|
||
#endregion
|
||
|
||
#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");
|
||
Scribe_Values.Look(ref currentWeaponSetIndex, "currentWeaponSetIndex", 0);
|
||
|
||
// 注意:Hediff不需要保存,因为会在加载时重新应用
|
||
}
|
||
#endregion
|
||
|
||
#region Gizmo
|
||
public override IEnumerable<Gizmo> GetWornGizmos()
|
||
{
|
||
// 基础Gizmo
|
||
foreach (var gizmo in base.GetWornGizmos())
|
||
{
|
||
yield return gizmo;
|
||
}
|
||
|
||
// 结构点面板
|
||
yield return new Gizmo_StructurePanel(this);
|
||
|
||
// 武装方案切换按钮(只有在有多个方案时显示)
|
||
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)
|
||
{
|
||
foreach (var gizmo in fuelComp.CompGetGizmosExtra())
|
||
{
|
||
yield return gizmo;
|
||
}
|
||
}
|
||
}
|
||
#endregion
|
||
|
||
#region 伤害处理
|
||
public override bool CheckPreAbsorbDamage(DamageInfo dinfo)
|
||
{
|
||
if (this.Wearer == null || !dinfo.Def.harmsHealth || dinfo.Amount <= 0.001f)
|
||
{
|
||
return false;
|
||
}
|
||
|
||
float finalDamage = GetPostArmorDamage(ref dinfo);
|
||
|
||
if (finalDamage > 0)
|
||
{
|
||
this.StructurePoints -= finalDamage;
|
||
EffecterDefOf.DamageDiminished_Metal.SpawnAttached(this.Wearer, this.Wearer.Map, 1f);
|
||
|
||
if (this.StructurePoints <= 0)
|
||
{
|
||
Messages.Message(ARA_Translations.PowerArmorDamaged.Translate(this.Label, this.Wearer.LabelShort), this, MessageTypeDefOf.NegativeEvent);
|
||
this.Destroy(DestroyMode.KillFinalize);
|
||
}
|
||
}
|
||
else
|
||
{
|
||
EffecterDefOf.Deflect_Metal_Bullet.SpawnAttached(this.Wearer, this.Wearer.Map, 1f);
|
||
}
|
||
|
||
// 返回true表示伤害已被完全处理,不应继续传递给pawn
|
||
return true;
|
||
}
|
||
|
||
private float GetPostArmorDamage(ref DamageInfo dinfo)
|
||
{
|
||
float amount = dinfo.Amount;
|
||
if (dinfo.Def.armorCategory != null)
|
||
{
|
||
StatDef armorRatingStat = dinfo.Def.armorCategory.armorRatingStat;
|
||
float armorRating = this.GetStatValue(armorRatingStat);
|
||
|
||
float armorPenetration = dinfo.ArmorPenetrationInt;
|
||
float num = Mathf.Max(armorRating - armorPenetration, 0f);
|
||
float value = Rand.Value;
|
||
float num2 = num * 0.5f;
|
||
float num3 = num;
|
||
if (value < num2)
|
||
{
|
||
amount = 0f;
|
||
}
|
||
else if (value < num3)
|
||
{
|
||
amount = GenMath.RoundRandom(amount / 2f);
|
||
if (dinfo.Def.armorCategory == DamageArmorCategoryDefOf.Sharp)
|
||
{
|
||
dinfo.Def = DamageDefOf.Blunt;
|
||
}
|
||
}
|
||
}
|
||
return amount;
|
||
}
|
||
#endregion
|
||
}
|
||
}
|