zc
This commit is contained in:
@@ -0,0 +1,181 @@
|
||||
// File: Patches/ColonistBarMechPatch_Fixed.cs
|
||||
using HarmonyLib;
|
||||
using RimWorld;
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
using Verse;
|
||||
using Verse.AI;
|
||||
|
||||
namespace WulaFallenEmpire
|
||||
{
|
||||
[HarmonyPatch(typeof(ColonistBar), "CheckRecacheEntries")]
|
||||
public static class Patch_ColonistBarMech_Fixed
|
||||
{
|
||||
[HarmonyPostfix]
|
||||
public static void Postfix(ref List<ColonistBar.Entry> ___cachedEntries)
|
||||
{
|
||||
// 安全检查:只在玩家派系存在时运行
|
||||
if (Faction.OfPlayer == null)
|
||||
return;
|
||||
|
||||
try
|
||||
{
|
||||
// 建立机甲和驾驶员的映射关系
|
||||
var mechToPilots = new Dictionary<Pawn, List<Pawn>>();
|
||||
var pilotToMech = new Dictionary<Pawn, Pawn>();
|
||||
var mechEntries = new HashSet<Pawn>();
|
||||
|
||||
// 只扫描玩家殖民地的地图
|
||||
foreach (var map in Find.Maps.Where(m => m.IsPlayerHome))
|
||||
{
|
||||
foreach (var pawn in map.mapPawns.AllPawnsSpawned)
|
||||
{
|
||||
// 只处理玩家殖民地的机甲(Wulamechunit)
|
||||
if (pawn is Wulamechunit mech &&
|
||||
(mech.Faction == Faction.OfPlayer || mech.HostFaction == Faction.OfPlayer))
|
||||
{
|
||||
var pilotComp = mech.TryGetComp<CompMechPilotHolder>();
|
||||
if (pilotComp != null && pilotComp.HasPilots)
|
||||
{
|
||||
var pilots = pilotComp.GetPilots()
|
||||
.Where(p => p.Faction == Faction.OfPlayer || p.HostFaction == Faction.OfPlayer)
|
||||
.ToList();
|
||||
|
||||
if (pilots.Count > 0)
|
||||
{
|
||||
mechToPilots[mech] = pilots;
|
||||
foreach (var pilot in pilots)
|
||||
{
|
||||
pilotToMech[pilot] = mech;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// 如果没有找到有机甲驾驶员的机甲,直接返回
|
||||
if (mechToPilots.Count == 0)
|
||||
return;
|
||||
|
||||
// 创建新的条目列表
|
||||
var newEntries = new List<ColonistBar.Entry>();
|
||||
|
||||
// 第一轮:处理原始条目,隐藏驾驶员
|
||||
foreach (var entry in ___cachedEntries)
|
||||
{
|
||||
var pawn = entry.pawn;
|
||||
if (pawn == null)
|
||||
{
|
||||
// 保留空条目
|
||||
newEntries.Add(entry);
|
||||
continue;
|
||||
}
|
||||
|
||||
// 如果是驾驶员,跳过(隐藏)
|
||||
if (pilotToMech.ContainsKey(pawn))
|
||||
{
|
||||
continue;
|
||||
}
|
||||
|
||||
// 如果是机甲
|
||||
if (mechToPilots.ContainsKey(pawn))
|
||||
{
|
||||
// 确保机甲还没有被添加,并且有驾驶员
|
||||
if (!mechEntries.Contains(pawn) && mechToPilots[pawn].Count > 0)
|
||||
{
|
||||
newEntries.Add(entry);
|
||||
mechEntries.Add(pawn);
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
// 普通殖民者(不属于任何机甲的驾驶员)
|
||||
newEntries.Add(entry);
|
||||
}
|
||||
}
|
||||
|
||||
// 第二轮:确保有机甲驾驶员的机甲被添加到列表
|
||||
foreach (var mech in mechToPilots.Keys)
|
||||
{
|
||||
// 如果机甲还没有被添加,且有驾驶员
|
||||
if (!mechEntries.Contains(mech) && mechToPilots[mech].Count > 0)
|
||||
{
|
||||
// 需要创建一个新的Entry
|
||||
var map = mech.MapHeld;
|
||||
if (map != null && map.IsPlayerHome)
|
||||
{
|
||||
// 计算group:需要找到地图对应的group
|
||||
int group = GetGroupForMap(map, ___cachedEntries);
|
||||
newEntries.Add(new ColonistBar.Entry(mech, map, group));
|
||||
mechEntries.Add(mech);
|
||||
}
|
||||
else if (mechToPilots[mech].Count > 0)
|
||||
{
|
||||
// 如果机甲不在任何地图上(可能被携带等),但仍有驾驶员
|
||||
// 使用第一个驾驶员的地图和group作为参考
|
||||
var pilot = mechToPilots[mech].First();
|
||||
var pilotMap = pilot.MapHeld;
|
||||
if (pilotMap != null && pilotMap.IsPlayerHome)
|
||||
{
|
||||
int group = GetGroupForMap(pilotMap, ___cachedEntries);
|
||||
newEntries.Add(new ColonistBar.Entry(mech, pilotMap, group));
|
||||
mechEntries.Add(mech);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// 替换原列表
|
||||
___cachedEntries = newEntries;
|
||||
}
|
||||
catch (System.Exception ex)
|
||||
{
|
||||
Log.Error($"[DD] Error in fixed ColonistBar patch: {ex}");
|
||||
// 出错时不改变原列表
|
||||
}
|
||||
}
|
||||
|
||||
// 辅助方法:获取地图对应的group
|
||||
private static int GetGroupForMap(Map map, List<ColonistBar.Entry> originalEntries)
|
||||
{
|
||||
if (map == null)
|
||||
return 0;
|
||||
|
||||
// 在原始条目中查找第一个属于该地图的条目,返回它的group
|
||||
var entry = originalEntries.FirstOrDefault(e => e.map == map && e.pawn != null);
|
||||
if (entry.pawn != null)
|
||||
{
|
||||
return entry.group;
|
||||
}
|
||||
|
||||
// 如果没有找到有pawn的条目,尝试查找任何属于该地图的条目(包括空条目)
|
||||
entry = originalEntries.FirstOrDefault(e => e.map == map);
|
||||
if (entry.map != null)
|
||||
{
|
||||
return entry.group;
|
||||
}
|
||||
|
||||
// 如果还是找不到,返回0作为默认值
|
||||
return 0;
|
||||
}
|
||||
|
||||
// 备用方案:使用更简单的方法计算group
|
||||
private static int CalculateGroupForMap(Map map)
|
||||
{
|
||||
if (map == null)
|
||||
return 0;
|
||||
|
||||
// 简单的计算:玩家殖民地地图的索引
|
||||
// 注意:这可能不完全准确,但通常工作
|
||||
var playerMaps = Find.Maps.Where(m => m.IsPlayerHome).ToList();
|
||||
int index = playerMaps.IndexOf(map);
|
||||
|
||||
// 如果找不到,返回0
|
||||
if (index < 0)
|
||||
return 0;
|
||||
|
||||
return index;
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,74 @@
|
||||
// File: Patches/Patch_Wulamechunit.cs (修改EquipmentUtility_CanEquip_Patch部分)
|
||||
using HarmonyLib;
|
||||
using RimWorld;
|
||||
using System;
|
||||
using Verse;
|
||||
|
||||
namespace WulaFallenEmpire
|
||||
{
|
||||
[HarmonyPatch(typeof(EquipmentUtility), nameof(EquipmentUtility.CanEquip), new Type[] { typeof(Thing), typeof(Pawn), typeof(string), typeof(bool) }, new ArgumentType[] { ArgumentType.Normal, ArgumentType.Normal, ArgumentType.Out, ArgumentType.Normal })]
|
||||
public static class EquipmentUtility_CanEquip_Patch
|
||||
{
|
||||
[HarmonyPrefix]
|
||||
public static bool CanEquip_Prefix(Thing thing, Pawn pawn, out string cantReason, ref bool __result)
|
||||
{
|
||||
cantReason = null;
|
||||
|
||||
try
|
||||
{
|
||||
// 检查是否有机甲专用武器组件
|
||||
var mechWeapon = thing?.TryGetComp<CompMechOnlyWeapon>();
|
||||
|
||||
// 情况1:这是机甲专用武器
|
||||
if (mechWeapon != null)
|
||||
{
|
||||
// 检查是否是机甲
|
||||
if (pawn is Wulamechunit)
|
||||
{
|
||||
// 检查是否允许此机甲使用
|
||||
if (mechWeapon.CanBeEquippedByMech(pawn))
|
||||
{
|
||||
// 机甲可以使用此专用武器
|
||||
return true;
|
||||
}
|
||||
else
|
||||
{
|
||||
// 此机甲不在允许列表中
|
||||
cantReason = "DD_Equipment_For_Other_Mech".Translate();
|
||||
__result = false;
|
||||
return false;
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
// 非机甲尝试装备专用武器,禁止
|
||||
cantReason = "DD_Human_Cannot_Equip_Mech_Weapon".Translate();
|
||||
__result = false;
|
||||
return false;
|
||||
}
|
||||
}
|
||||
// 情况2:这是普通武器
|
||||
else if (thing?.def?.IsWeapon == true)
|
||||
{
|
||||
// 检查是否是机甲
|
||||
if (pawn is Wulamechunit)
|
||||
{
|
||||
// 机甲不能装备普通武器
|
||||
cantReason = "DD_Equipment_Not_Allow_For_Mech".Translate();
|
||||
__result = false;
|
||||
return false;
|
||||
}
|
||||
// 非机甲可以装备普通武器,继续检查
|
||||
}
|
||||
|
||||
// 情况3:不是武器或不是机甲,按原逻辑处理
|
||||
return true;
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
Log.Error($"[DD] CanEquip patch error: {ex}");
|
||||
return true;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,130 @@
|
||||
// File: Patch_RomanceFix.cs
|
||||
using HarmonyLib;
|
||||
using RimWorld;
|
||||
using System.Collections.Generic;
|
||||
using Verse;
|
||||
|
||||
namespace WulaFallenEmpire
|
||||
{
|
||||
/// <summary>
|
||||
/// 修复浪漫关系菜单相关的空引用异常
|
||||
/// </summary>
|
||||
public static class RomancePatches
|
||||
{
|
||||
/// <summary>
|
||||
/// 补丁:防止对机甲单位显示浪漫菜单
|
||||
/// </summary>
|
||||
[HarmonyPatch(typeof(FloatMenuOptionProvider_Romance))]
|
||||
[HarmonyPatch("GetSingleOptionFor")]
|
||||
public static class Patch_FloatMenuOptionProvider_Romance_GetSingleOptionFor
|
||||
{
|
||||
[HarmonyPrefix]
|
||||
public static bool Prefix(Pawn clickedPawn, ref FloatMenuOption __result)
|
||||
{
|
||||
// 如果是机甲单位,直接返回null
|
||||
if (clickedPawn is Wulamechunit)
|
||||
{
|
||||
__result = null;
|
||||
return false; // 跳过原始方法
|
||||
}
|
||||
|
||||
// 额外检查:如果clickedPawn没有story组件,也跳过
|
||||
if (clickedPawn?.story == null)
|
||||
{
|
||||
__result = null;
|
||||
return false;
|
||||
}
|
||||
|
||||
return true; // 继续执行原始方法
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 补丁:防止在爱情关系检查中出现空引用
|
||||
/// </summary>
|
||||
[HarmonyPatch(typeof(LovePartnerRelationUtility))]
|
||||
[HarmonyPatch("ExistingLovePartners")]
|
||||
public static class Patch_LovePartnerRelationUtility_ExistingLovePartners
|
||||
{
|
||||
[HarmonyPrefix]
|
||||
public static bool Prefix(Pawn pawn, bool allowDead, ref List<DirectPawnRelation> __result)
|
||||
{
|
||||
// 如果pawn是机甲单位,返回空列表
|
||||
if (pawn is Wulamechunit)
|
||||
{
|
||||
__result = new List<DirectPawnRelation>();
|
||||
return false; // 跳过原始方法
|
||||
}
|
||||
|
||||
// 如果pawn没有story组件,返回空列表
|
||||
if (pawn?.story == null)
|
||||
{
|
||||
__result = new List<DirectPawnRelation>();
|
||||
return false;
|
||||
}
|
||||
|
||||
return true; // 继续执行原始方法
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 补丁:防止浪漫关系配对检查中的空引用
|
||||
/// </summary>
|
||||
[HarmonyPatch(typeof(RelationsUtility))]
|
||||
[HarmonyPatch("RomanceEligiblePair")]
|
||||
public static class Patch_RelationsUtility_RomanceEligiblePair
|
||||
{
|
||||
[HarmonyPrefix]
|
||||
public static bool Prefix(Pawn initiator, Pawn target, bool forOpinionExplanation, ref AcceptanceReport __result)
|
||||
{
|
||||
// 如果任一pawn是机甲单位,返回拒绝
|
||||
if (initiator is Wulamechunit || target is Wulamechunit)
|
||||
{
|
||||
__result = new AcceptanceReport("DD_MechCannotRomance".Translate());
|
||||
return false; // 跳过原始方法
|
||||
}
|
||||
|
||||
// 如果任一pawn没有story组件,返回拒绝
|
||||
if (initiator?.story == null || target?.story == null)
|
||||
{
|
||||
__result = new AcceptanceReport("DD_NoStoryComponent".Translate());
|
||||
return false;
|
||||
}
|
||||
|
||||
return true; // 继续执行原始方法
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 补丁:防止浪漫关系检查中的空引用
|
||||
/// </summary>
|
||||
[HarmonyPatch(typeof(RelationsUtility))]
|
||||
[HarmonyPatch("RomanceOption")]
|
||||
public static class Patch_RelationsUtility_RomanceOption
|
||||
{
|
||||
[HarmonyPrefix]
|
||||
public static bool Prefix(Pawn initiator, Pawn romanceTarget, ref FloatMenuOption option, ref float chance, ref bool __result)
|
||||
{
|
||||
// 如果任一pawn是机甲单位,返回false
|
||||
if (initiator is Wulamechunit || romanceTarget is Wulamechunit)
|
||||
{
|
||||
__result = false;
|
||||
option = null;
|
||||
chance = 0f;
|
||||
return false; // 跳过原始方法
|
||||
}
|
||||
|
||||
// 如果任一pawn没有story组件,返回false
|
||||
if (initiator?.story == null || romanceTarget?.story == null)
|
||||
{
|
||||
__result = false;
|
||||
option = null;
|
||||
chance = 0f;
|
||||
return false;
|
||||
}
|
||||
|
||||
return true; // 继续执行原始方法
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,230 @@
|
||||
using HarmonyLib;
|
||||
using RimWorld;
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using UnityEngine;
|
||||
using Verse;
|
||||
using Verse.Sound;
|
||||
|
||||
namespace WulaFallenEmpire.HarmonyPatches
|
||||
{
|
||||
/// <summary>
|
||||
/// 整合的伤害处理系统
|
||||
/// 处理:1. 机甲装甲系统 2. HediffComp_Invulnerable免疫系统
|
||||
/// </summary>
|
||||
[HarmonyPatch(typeof(Thing))]
|
||||
[HarmonyPatch("TakeDamage")]
|
||||
public static class Thing_TakeDamage_Patch
|
||||
{
|
||||
// 缓存装甲值StatDef
|
||||
private static readonly StatDef ArmorStatDef = StatDef.Named("DD_MechArmor");
|
||||
|
||||
// 阻挡效果的MoteDef
|
||||
private static readonly ThingDef BlockMoteDef = DefDatabase<ThingDef>.GetNamedSilentFail("Mote_Spark");
|
||||
|
||||
// 阻挡音效
|
||||
private static readonly SoundDef BlockSoundDef = DefDatabase<SoundDef>.GetNamedSilentFail("ArmorBlock");
|
||||
|
||||
// 免疫效果的MoteDef
|
||||
private static readonly ThingDef ImmuneMoteDef = DefDatabase<ThingDef>.GetNamedSilentFail("Mote_Immunity");
|
||||
|
||||
// 免疫音效
|
||||
private static readonly SoundDef ImmuneSoundDef = DefDatabase<SoundDef>.GetNamedSilentFail("ImmuneSound");
|
||||
|
||||
// 调试统计
|
||||
private static readonly Dictionary<Thing, DamageBlockStats> DebugStats = new Dictionary<Thing, DamageBlockStats>();
|
||||
|
||||
private class DamageBlockStats
|
||||
{
|
||||
public int mechArmorBlocked = 0;
|
||||
public int invulnerableBlocked = 0;
|
||||
public int totalHits = 0;
|
||||
|
||||
public int TotalBlocked => mechArmorBlocked + invulnerableBlocked;
|
||||
|
||||
public override string ToString()
|
||||
{
|
||||
return $"机甲装甲阻挡: {mechArmorBlocked}, 免疫阻挡: {invulnerableBlocked}, 总计阻挡: {TotalBlocked}, 总命中: {totalHits}";
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 前置补丁:在TakeDamage执行前检查伤害免疫
|
||||
/// </summary>
|
||||
[HarmonyPrefix]
|
||||
public static bool Prefix(Thing __instance, ref DamageInfo dinfo, ref DamageWorker.DamageResult __result)
|
||||
{
|
||||
// 更新调试统计
|
||||
if (!DebugStats.ContainsKey(__instance))
|
||||
DebugStats[__instance] = new DamageBlockStats();
|
||||
DebugStats[__instance].totalHits++;
|
||||
|
||||
// 第一步:检查伤害免疫HediffComp
|
||||
if (__instance is Pawn pawn)
|
||||
{
|
||||
bool blockedByInvulnerable = CheckInvulnerableHediff(pawn, dinfo, out HediffComp_Invulnerable invulnerableComp);
|
||||
|
||||
if (blockedByInvulnerable)
|
||||
{
|
||||
// 被HediffComp免疫阻挡
|
||||
DebugStats[__instance].invulnerableBlocked++;
|
||||
|
||||
// 显示免疫效果
|
||||
ShowImmuneEffect(pawn, dinfo);
|
||||
|
||||
// 播放免疫音效
|
||||
PlayImmuneSound(pawn);
|
||||
|
||||
// 调用HediffComp的OnDamageBlocked方法
|
||||
invulnerableComp?.OnDamageBlocked(dinfo);
|
||||
|
||||
// 返回空结果,跳过原方法
|
||||
__result = new DamageWorker.DamageResult();
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
// 第二步:检查机甲装甲系统
|
||||
float armorValue = __instance.GetStatValue(ArmorStatDef);
|
||||
|
||||
// 如果装甲值 <= 0,不启动装甲系统,继续原方法
|
||||
if (armorValue <= 0)
|
||||
return true;
|
||||
|
||||
// 计算穿甲伤害
|
||||
float armorPenetration = dinfo.ArmorPenetrationInt;
|
||||
float piercingDamage = dinfo.Amount * armorPenetration;
|
||||
|
||||
// 判断是否应该阻挡
|
||||
bool shouldBlock = piercingDamage < armorValue;
|
||||
|
||||
if (shouldBlock)
|
||||
{
|
||||
// 机甲装甲阻挡成功
|
||||
DebugStats[__instance].mechArmorBlocked++;
|
||||
|
||||
// 显示阻挡效果
|
||||
ShowBlockEffect(__instance, dinfo);
|
||||
|
||||
// 播放阻挡音效
|
||||
PlayBlockSound(__instance);
|
||||
|
||||
// 返回空结果,跳过原方法
|
||||
__result = new DamageWorker.DamageResult();
|
||||
return false;
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 检查伤害免疫HediffComp
|
||||
/// </summary>
|
||||
private static bool CheckInvulnerableHediff(Pawn pawn, DamageInfo dinfo, out HediffComp_Invulnerable invulnerableComp)
|
||||
{
|
||||
invulnerableComp = null;
|
||||
|
||||
if (pawn == null || pawn.health == null || pawn.health.hediffSet == null)
|
||||
return false;
|
||||
|
||||
// 检查所有Hediff,寻找HediffComp_Invulnerable
|
||||
foreach (Hediff hediff in pawn.health.hediffSet.hediffs)
|
||||
{
|
||||
if (hediff.TryGetComp<HediffComp_Invulnerable>() is HediffComp_Invulnerable comp)
|
||||
{
|
||||
invulnerableComp = comp;
|
||||
return comp.ShouldBlockDamage(dinfo);
|
||||
}
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 显示免疫效果
|
||||
/// </summary>
|
||||
private static void ShowImmuneEffect(Pawn pawn, DamageInfo dinfo)
|
||||
{
|
||||
if (!pawn.Spawned)
|
||||
return;
|
||||
|
||||
// 显示文字效果
|
||||
Vector3 textPos = pawn.DrawPos + new Vector3(0, 0, 1f);
|
||||
MoteMaker.ThrowText(textPos, pawn.Map, "DD_ImmuneToDamage".Translate(), Color.green, 2.5f);
|
||||
|
||||
// 显示粒子效果
|
||||
if (ImmuneMoteDef != null)
|
||||
{
|
||||
MoteMaker.MakeStaticMote(pawn.DrawPos, pawn.Map, ImmuneMoteDef, 1f);
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 播放免疫音效
|
||||
/// </summary>
|
||||
private static void PlayImmuneSound(Pawn pawn)
|
||||
{
|
||||
if (!pawn.Spawned)
|
||||
return;
|
||||
|
||||
if (ImmuneSoundDef != null)
|
||||
{
|
||||
ImmuneSoundDef.PlayOneShot(new TargetInfo(pawn.Position, pawn.Map));
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 显示阻挡效果
|
||||
/// </summary>
|
||||
private static void ShowBlockEffect(Thing target, DamageInfo dinfo)
|
||||
{
|
||||
if (!target.Spawned)
|
||||
return;
|
||||
|
||||
// 显示文字效果
|
||||
Vector3 textPos = target.DrawPos + new Vector3(0, 0, 1f);
|
||||
MoteMaker.ThrowText(textPos, target.Map, "DD_BlockByMechArmor".Translate(), Color.yellow, 2.5f);
|
||||
|
||||
// 显示粒子效果
|
||||
if (BlockMoteDef != null)
|
||||
{
|
||||
MoteMaker.MakeStaticMote(target.DrawPos, target.Map, BlockMoteDef, 1f);
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 播放阻挡音效
|
||||
/// </summary>
|
||||
private static void PlayBlockSound(Thing target)
|
||||
{
|
||||
if (!target.Spawned)
|
||||
return;
|
||||
|
||||
if (BlockSoundDef != null)
|
||||
{
|
||||
BlockSoundDef.PlayOneShot(new TargetInfo(target.Position, target.Map));
|
||||
}
|
||||
else
|
||||
{
|
||||
// 备用音效
|
||||
SoundDefOf.MetalHitImportant.PlayOneShot(new TargetInfo(target.Position, target.Map));
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 获取调试统计信息
|
||||
/// </summary>
|
||||
public static string GetDebugStats()
|
||||
{
|
||||
System.Text.StringBuilder sb = new System.Text.StringBuilder();
|
||||
sb.AppendLine("伤害阻挡统计:");
|
||||
|
||||
foreach (var kvp in DebugStats)
|
||||
{
|
||||
sb.AppendLine($"{kvp.Key.LabelCap}: {kvp.Value}");
|
||||
}
|
||||
|
||||
return sb.ToString();
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,285 @@
|
||||
using HarmonyLib;
|
||||
using RimWorld;
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Reflection;
|
||||
using Verse;
|
||||
using Verse.AI;
|
||||
|
||||
namespace WulaFallenEmpire
|
||||
{
|
||||
[HarmonyPatch(typeof(Pawn), "get_CanTakeOrder")]
|
||||
public class Patch_CanTakeOrder
|
||||
{
|
||||
[HarmonyPostfix]
|
||||
public static void postfix(ref bool __result, Pawn __instance)
|
||||
{
|
||||
if (__instance is Wulamechunit && __instance.Drafted)
|
||||
{
|
||||
__result = true;
|
||||
}
|
||||
}
|
||||
}
|
||||
[HarmonyPatch(typeof(PawnComponentsUtility), "AddAndRemoveDynamicComponents")]
|
||||
public class Patch_PawnTracer
|
||||
{
|
||||
[HarmonyPostfix]
|
||||
public static void postfix(Pawn pawn)
|
||||
{
|
||||
if (pawn is Wulamechunit)
|
||||
{
|
||||
pawn.drafter = new Pawn_DraftController(pawn);
|
||||
pawn.skills = new Pawn_SkillTracker(pawn);
|
||||
}
|
||||
}
|
||||
}
|
||||
[HarmonyPatch(typeof(FloatMenuOptionProvider), "SelectedPawnValid")]
|
||||
public class Patch_GetSingleOption
|
||||
{
|
||||
[HarmonyPostfix]
|
||||
public static void postfix(ref bool __result, Pawn pawn)
|
||||
{
|
||||
if (pawn is Wulamechunit)
|
||||
{
|
||||
__result = true;
|
||||
}
|
||||
}
|
||||
}
|
||||
[HarmonyPatch(typeof(FloatMenuUtility), nameof(FloatMenuUtility.GetMeleeAttackAction))]
|
||||
public static class Patch_FloatMenuUtility_GetMeleeAttackAction
|
||||
{
|
||||
[HarmonyPrefix]
|
||||
public static bool Prefix(Pawn pawn, LocalTargetInfo target, out string failStr, ref Action __result, bool ignoreControlled = false)
|
||||
{
|
||||
failStr = "";
|
||||
|
||||
if (pawn is Wulamechunit)
|
||||
{
|
||||
// 直接返回 true 跳过控制检查
|
||||
ignoreControlled = true;
|
||||
|
||||
// 这里我们直接调用修改后的方法
|
||||
__result = ModifiedGetMeleeAttackAction(pawn, target, out failStr, ignoreControlled);
|
||||
return false; // 跳过原始方法
|
||||
}
|
||||
|
||||
return true; // 没有组件,继续执行原始方法
|
||||
}
|
||||
|
||||
private static Action ModifiedGetMeleeAttackAction(Pawn pawn, LocalTargetInfo target, out string failStr, bool ignoreControlled = false)
|
||||
{
|
||||
failStr = "";
|
||||
|
||||
try
|
||||
{
|
||||
// 直接使用原始代码,但跳过控制检查
|
||||
if (!pawn.Drafted && !ignoreControlled)
|
||||
{
|
||||
failStr = "IsNotDraftedLower".Translate(pawn.LabelShort, pawn);
|
||||
}
|
||||
else if (target.IsValid && !pawn.CanReach(target, PathEndMode.Touch, Danger.Deadly))
|
||||
{
|
||||
failStr = "NoPath".Translate();
|
||||
}
|
||||
else if (pawn.WorkTagIsDisabled(WorkTags.Violent))
|
||||
{
|
||||
failStr = "IsIncapableOfViolenceLower".Translate(pawn.LabelShort, pawn);
|
||||
}
|
||||
else if (pawn.meleeVerbs?.TryGetMeleeVerb(target.Thing) == null)
|
||||
{
|
||||
failStr = "Incapable".Translate();
|
||||
}
|
||||
else if (pawn == target.Thing)
|
||||
{
|
||||
failStr = "CannotAttackSelf".Translate();
|
||||
}
|
||||
else if (target.Thing is Pawn targetPawn && (pawn.InSameExtraFaction(targetPawn, ExtraFactionType.HomeFaction) || pawn.InSameExtraFaction(targetPawn, ExtraFactionType.MiniFaction)))
|
||||
{
|
||||
failStr = "CannotAttackSameFactionMember".Translate();
|
||||
}
|
||||
else
|
||||
{
|
||||
if (!(target.Thing is Pawn pawn2) || !pawn2.RaceProps.Animal || !HistoryEventUtility.IsKillingInnocentAnimal(pawn, pawn2) || new HistoryEvent(HistoryEventDefOf.KilledInnocentAnimal, pawn.Named(HistoryEventArgsNames.Doer)).DoerWillingToDo())
|
||||
{
|
||||
return delegate
|
||||
{
|
||||
Job job = JobMaker.MakeJob(JobDefOf.AttackMelee, target);
|
||||
if (target.Thing is Pawn pawn3)
|
||||
{
|
||||
job.killIncappedTarget = pawn3.Downed;
|
||||
}
|
||||
pawn.jobs.TryTakeOrderedJob(job, JobTag.Misc);
|
||||
};
|
||||
}
|
||||
failStr = "IdeoligionForbids".Translate();
|
||||
}
|
||||
|
||||
failStr = failStr.CapitalizeFirst();
|
||||
return null;
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
Log.Error($"[IRMF] Error in ModifiedGetMeleeAttackAction: {ex}");
|
||||
failStr = "Cannot attack";
|
||||
return null;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
[HarmonyPatch(typeof(FloatMenuUtility), nameof(FloatMenuUtility.GetRangedAttackAction))]
|
||||
public static class Patch_FloatMenuUtility_GetRangedAttackAction
|
||||
{
|
||||
[HarmonyPrefix]
|
||||
public static bool Prefix(Pawn pawn, LocalTargetInfo target, out string failStr, ref Action __result)
|
||||
{
|
||||
failStr = "";
|
||||
|
||||
if (pawn is Wulamechunit)
|
||||
{
|
||||
// 这里我们直接调用修改后的方法
|
||||
__result = ModifiedGetRangedAttackAction(pawn, target, out failStr);
|
||||
return false; // 跳过原始方法
|
||||
}
|
||||
|
||||
return true; // 没有组件,继续执行原始方法
|
||||
}
|
||||
|
||||
private static Action ModifiedGetRangedAttackAction(Pawn pawn, LocalTargetInfo target, out string failStr, bool ignoreControlled = false)
|
||||
{
|
||||
failStr = "";
|
||||
|
||||
try
|
||||
{
|
||||
if (pawn.equipment.Primary == null)
|
||||
{
|
||||
return null;
|
||||
}
|
||||
Verb primaryVerb = pawn.equipment.PrimaryEq.PrimaryVerb;
|
||||
if (primaryVerb.verbProps.IsMeleeAttack)
|
||||
{
|
||||
return null;
|
||||
}
|
||||
if (!pawn.Drafted)
|
||||
{
|
||||
failStr = "IsNotDraftedLower".Translate(pawn.LabelShort, pawn);
|
||||
}
|
||||
else if (pawn.IsColonyMechPlayerControlled && target.IsValid && !MechanitorUtility.InMechanitorCommandRange(pawn, target))
|
||||
{
|
||||
failStr = "OutOfCommandRange".Translate();
|
||||
}
|
||||
else if (target.IsValid && !pawn.equipment.PrimaryEq.PrimaryVerb.CanHitTarget(target))
|
||||
{
|
||||
if (!pawn.Position.InHorDistOf(target.Cell, primaryVerb.EffectiveRange))
|
||||
{
|
||||
failStr = "OutOfRange".Translate();
|
||||
}
|
||||
else
|
||||
{
|
||||
float num = primaryVerb.verbProps.EffectiveMinRange(target, pawn);
|
||||
if ((float)pawn.Position.DistanceToSquared(target.Cell) < num * num)
|
||||
{
|
||||
failStr = "TooClose".Translate();
|
||||
}
|
||||
else
|
||||
{
|
||||
failStr = "CannotHitTarget".Translate();
|
||||
}
|
||||
}
|
||||
}
|
||||
else if (pawn.WorkTagIsDisabled(WorkTags.Violent))
|
||||
{
|
||||
failStr = "IsIncapableOfViolenceLower".Translate(pawn.LabelShort, pawn);
|
||||
}
|
||||
else if (pawn == target.Thing)
|
||||
{
|
||||
failStr = "CannotAttackSelf".Translate();
|
||||
}
|
||||
else if (target.Thing is Pawn target2 && (pawn.InSameExtraFaction(target2, ExtraFactionType.HomeFaction) || pawn.InSameExtraFaction(target2, ExtraFactionType.MiniFaction)))
|
||||
{
|
||||
failStr = "CannotAttackSameFactionMember".Translate();
|
||||
}
|
||||
else if (target.Thing is Pawn victim && HistoryEventUtility.IsKillingInnocentAnimal(pawn, victim) && !new HistoryEvent(HistoryEventDefOf.KilledInnocentAnimal, pawn.Named(HistoryEventArgsNames.Doer)).DoerWillingToDo())
|
||||
{
|
||||
failStr = "IdeoligionForbids".Translate();
|
||||
}
|
||||
else
|
||||
{
|
||||
if (!(target.Thing is Pawn pawn2) || pawn.Ideo == null || !pawn.Ideo.IsVeneratedAnimal(pawn2) || new HistoryEvent(HistoryEventDefOf.HuntedVeneratedAnimal, pawn.Named(HistoryEventArgsNames.Doer)).DoerWillingToDo())
|
||||
{
|
||||
return delegate
|
||||
{
|
||||
Job job = JobMaker.MakeJob(JobDefOf.AttackStatic, target);
|
||||
pawn.jobs.TryTakeOrderedJob(job, JobTag.Misc);
|
||||
};
|
||||
}
|
||||
failStr = "IdeoligionForbids".Translate();
|
||||
}
|
||||
failStr = failStr.CapitalizeFirst();
|
||||
return null;
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
Log.Error($"[IRMF] Error in ModifiedGetRangeAttackAction: {ex}");
|
||||
failStr = "Cannot attack";
|
||||
return null;
|
||||
}
|
||||
}
|
||||
[HarmonyPatch(typeof(FloatMenuOptionProvider_Romance), "GetSingleOptionFor")]
|
||||
[HarmonyPrefix]
|
||||
public static bool GetSingleOptionFor_Prefix(Pawn clickedPawn, ref FloatMenuOption __result)
|
||||
{
|
||||
if (clickedPawn is Wulamechunit)
|
||||
{
|
||||
__result = null;
|
||||
return false; // 跳过原始方法
|
||||
}
|
||||
return true; // 继续执行原始方法
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
[HarmonyPatch]
|
||||
public static class Patch_Pawn_MeleeVerbs_TryMeleeAttack
|
||||
{
|
||||
// 获取要修补的方法
|
||||
[HarmonyTargetMethod]
|
||||
public static MethodBase TargetMethod()
|
||||
{
|
||||
// 查找 Pawn_MeleeVerbs.TryMeleeAttack 方法
|
||||
return AccessTools.Method(typeof(Pawn_MeleeVerbs), nameof(Pawn_MeleeVerbs.TryMeleeAttack));
|
||||
}
|
||||
// 前置补丁:在原始方法执行前检查
|
||||
[HarmonyPrefix]
|
||||
public static bool Prefix(ref bool __result, Pawn_MeleeVerbs __instance, Thing target, Verb verbToUse, bool surpriseAttack)
|
||||
{
|
||||
try
|
||||
{
|
||||
// 获取 Pawn
|
||||
var pawnField = AccessTools.Field(typeof(Pawn_MeleeVerbs), "pawn");
|
||||
if (pawnField == null)
|
||||
return true; // 如果找不到字段,继续执行原方法
|
||||
Pawn pawn = pawnField.GetValue(__instance) as Pawn;
|
||||
if (pawn == null)
|
||||
return true;
|
||||
// 检查是否为机甲
|
||||
if (pawn is Wulamechunit)
|
||||
{
|
||||
// 检查是否有驾驶员
|
||||
var pilotComp = pawn.TryGetComp<CompMechPilotHolder>();
|
||||
if (pilotComp != null && !pilotComp.HasPilots)
|
||||
{
|
||||
// 没有驾驶员,阻止近战攻击
|
||||
__result = false;
|
||||
return false; // 跳过原始方法
|
||||
}
|
||||
}
|
||||
return true; // 继续执行原始方法
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
Log.Error($"[DD] Harmony patch error in TryMeleeAttack: {ex}");
|
||||
return true; // 出错时继续执行原始方法
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,28 @@
|
||||
// File: HarmonyPatches/SkillSystemPatches.cs
|
||||
using HarmonyLib;
|
||||
using RimWorld;
|
||||
using Verse;
|
||||
|
||||
namespace WulaFallenEmpire
|
||||
{
|
||||
/// <summary>
|
||||
/// 针对SkillRecord.Interval()的补丁,防止在机甲上出现空引用
|
||||
/// </summary>
|
||||
[HarmonyPatch(typeof(SkillRecord))]
|
||||
[HarmonyPatch("Interval")]
|
||||
public static class Patch_SkillRecord_Interval
|
||||
{
|
||||
[HarmonyPrefix]
|
||||
public static bool Prefix(SkillRecord __instance)
|
||||
{
|
||||
// 额外检查:如果pawn.story为null,也跳过
|
||||
if (__instance?.Pawn?.story == null)
|
||||
{
|
||||
return false; // 跳过原方法
|
||||
}
|
||||
|
||||
return true; // 执行原方法
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
Reference in New Issue
Block a user