This commit is contained in:
2025-09-10 01:02:08 +08:00
parent c9fd2e8844
commit 12ea403c97
7 changed files with 435 additions and 14 deletions

Binary file not shown.

View File

@@ -663,8 +663,7 @@
<AlienRace.ThingDef_AlienRace ParentName="ARA_NodeBase_Huge">
<defName>ArachnaeNode_Race_Myrmecocystus</defName>
<label>阿拉克涅蜜罐种</label>
<description>
阿拉克涅督虫之一,拥有广泛食谱的大型虫族,可以吞下许多未经处理的生物并将其分解为阿拉克涅虫蜜,以滋养虫群。\n\n她可以进行搬运、烹饪和种植工作战斗技能非常差。\n\n作为督虫她可以繁育并监管若干阿拉克涅食腐种辅虫以协助巢穴进行收割和播种工作。</description>
<description>阿拉克涅督虫之一,拥有广泛食谱的大型虫族,可以吞下许多未经处理的生物并将其分解为阿拉克涅虫蜜,以滋养虫群。\n\n她可以进行搬运、烹饪和种植工作战斗技能非常差。\n\n作为督虫她可以繁育并监管若干阿拉克涅食腐种辅虫以协助巢穴进行收割和播种工作。</description>
<alienRace>
<generalSettings>
<!-- 各种零件定义 -->
@@ -758,8 +757,7 @@
<AlienRace.ThingDef_AlienRace ParentName="ARA_NodeBase">
<defName>ArachnaeNode_Race_ShieldHead</defName>
<label>阿拉克涅盾头种</label>
<description>
阿拉克涅督虫之一,拥有过度生长而覆盖头部和腿部的甲壳,可以定时剥落甲壳素供虫巢使用。\n\n她可以进行搬运、采矿和建筑工作战斗技能平平无奇但是拥有较好的防御。\n\n作为督虫她可以繁育并监管若干阿拉克涅坚颚种辅虫以协助巢穴开采矿脉。</description>
<description>阿拉克涅督虫之一,拥有过度生长而覆盖头部和腿部的甲壳,可以定时剥落甲壳素供虫巢使用。\n\n她可以进行搬运、采矿和建筑工作战斗技能平平无奇但是拥有较好的防御。\n\n作为督虫她可以繁育并监管若干阿拉克涅坚颚种辅虫以协助巢穴开采矿脉。</description>
<alienRace>
<generalSettings>
<!-- 各种零件定义 -->
@@ -879,8 +877,7 @@
<AlienRace.ThingDef_AlienRace ParentName="ARA_NodeBase">
<defName>ArachnaeNode_Race_WeaponSmith</defName>
<label>阿拉克涅工艺种</label>
<description>
阿拉克涅督虫之一,是少数拥有结茧能力的非女皇种阿拉克涅虫族——她们可以排出一枚器官茧,这枚茧将按照其信息素所标定的方向定向演化出一个武装器官,以供虫群使用。\n\n她可以进行搬运、手工和艺术工作战斗技能平平无奇但本身极度脆弱。\n\n作为督虫她可以繁育并监管若干阿拉克涅家政种辅虫以协助巢穴进行清洁和搬运工作。</description>
<description>阿拉克涅督虫之一,是少数拥有结茧能力的非女皇种阿拉克涅虫族——她们可以排出一枚器官茧,这枚茧将按照其信息素所标定的方向定向演化出一个武装器官,以供虫群使用。\n\n她可以进行搬运、手工和艺术工作战斗技能平平无奇但本身极度脆弱。\n\n作为督虫她可以繁育并监管若干阿拉克涅家政种辅虫以协助巢穴进行清洁和搬运工作。</description>
<alienRace>
<generalSettings>
<!-- 各种零件定义 -->
@@ -964,8 +961,7 @@
<AlienRace.ThingDef_AlienRace ParentName="ARA_NodeBase">
<defName>ArachnaeNode_Race_Fighter</defName>
<label>阿拉克涅战士种</label>
<description>
阿拉克涅督虫之一,是巢穴中真正的战士,其拥有强大的可塑性基因,随着科技的解锁其将获得更多的能力。\n\n她可以进行搬运、狩猎和驯兽工作战斗技能非常亮眼并且移动敏捷。\n\n作为督虫她可以向敌人投射寿命有限但是非常恼人的阿拉克涅酸噬种辅虫以阻止敌人的远程火力开火。</description>
<description>阿拉克涅督虫之一,是巢穴中真正的战士,其拥有强大的可塑性基因,随着科技的解锁其将获得更多的能力。\n\n她可以进行搬运、狩猎和驯兽工作战斗技能非常亮眼并且移动敏捷。\n\n作为督虫她可以向敌人投射寿命有限但是非常恼人的阿拉克涅酸噬种辅虫以阻止敌人的远程火力开火。</description>
<alienRace>
<generalSettings>
<!-- 各种零件定义 -->
@@ -1050,8 +1046,7 @@
<AlienRace.ThingDef_AlienRace ParentName="ARA_NodeBase">
<defName>ArachnaeNode_Race_Facehugger</defName>
<label>阿拉克涅原虫种</label>
<description>
阿拉克涅督虫之一,呈现高度未分化状态的特殊督虫,可以通过独特的神经链接管控制受害者的身体,并释放灵能信号以使得周围的人忽略其存在。然而她本身实在过于脆弱,在完成寄生前难以胜任任何工作。\n\n她可以进行所有非研究工作战斗技能约等于无。\n\n不同于普通阿拉克涅虫族高度未分化的身体决定了其无法孕育任何辅虫。</description>
<description>阿拉克涅督虫之一,呈现高度未分化状态的特殊督虫,可以通过独特的神经链接管控制受害者的身体,并释放灵能信号以使得周围的人忽略其存在。然而她本身实在过于脆弱,在完成寄生前难以胜任任何工作。\n\n她可以进行所有非研究工作战斗技能约等于无。\n\n不同于普通阿拉克涅虫族高度未分化的身体决定了其无法孕育任何辅虫。</description>
<alienRace>
<generalSettings>
<!-- 各种零件定义 -->
@@ -1110,8 +1105,7 @@
<AlienRace.ThingDef_AlienRace ParentName="ARA_NodeBase_Huge">
<defName>ArachnaeNode_Race_Smokepop</defName>
<label>阿拉克涅浓雾种</label>
<description>
阿拉克涅督虫之一,身披厚重甲壳的大型虫族,拥有在大范围内喷射烟雾、阻燃物和铺设菌毯的能力,虽然移动速度比较慢,但是在战斗中就如同装甲车一样难以撼动。\n\n她可以进行搬运和医护工作拥有不错的战斗技能防御力和伤害耐受能力强大。\n\n作为督虫她可以繁育并监管大量阿拉克涅家政种辅虫以协助巢穴进行清洁和搬运工作。</description>
<description>阿拉克涅督虫之一,身披厚重甲壳的大型虫族,拥有在大范围内喷射烟雾、阻燃物和铺设菌毯的能力,虽然移动速度比较慢,但是在战斗中就如同装甲车一样难以撼动。\n\n她可以进行搬运和医护工作拥有不错的战斗技能防御力和伤害耐受能力强大。\n\n作为督虫她可以繁育并监管大量阿拉克涅家政种辅虫以协助巢穴进行清洁和搬运工作。</description>
<alienRace>
<generalSettings>
<!-- 各种零件定义 -->
@@ -1202,8 +1196,7 @@
<AlienRace.ThingDef_AlienRace ParentName="ARA_NodeBase">
<defName>ArachnaeNode_Race_Skyraider</defName>
<label>阿拉克涅空天种</label>
<description>
阿拉克涅督虫之一,众督虫中的精锐,进化出了强大的飞行能力,是巢穴中无可争议的空中霸主。\n\n她可以进行搬运工作拥有不错的战斗技能非常灵活。\n\n作为为数不多拥有飞行能力的虫族她可以从空中掠袭猎物并将其带至千米高空之上俯冲投下只留其余猎物在地面无助的挣扎。</description>
<description>阿拉克涅督虫之一,众督虫中的精锐,进化出了强大的飞行能力,是巢穴中无可争议的空中霸主。\n\n她可以进行搬运工作拥有不错的战斗技能非常灵活。\n\n作为为数不多拥有飞行能力的虫族她可以从空中掠袭猎物并将其带至千米高空之上俯冲投下只留其余猎物在地面无助的挣扎。</description>
<alienRace>
<generalSettings>
<!-- 各种零件定义 -->

View File

@@ -184,6 +184,12 @@
<Compile Include="Projectiles\Projectile_WulaPenetratingBullet.cs" />
<Compile Include="Projectiles\TrackingBulletDef.cs" />
</ItemGroup>
<ItemGroup>
<Compile Include="HediffComp_NecroticTransformation.cs" />
<Compile Include="Hediff_NecroticVirus_Configurable.cs" />
<Compile Include="NecroticTransformationUtility.cs" />
<Compile Include="ProphecyGearEffect.cs" />
</ItemGroup>
<Import Project="$(MSBuildToolsPath)\Microsoft.CSharp.targets" />
<!-- 自定义清理任务删除obj文件夹中的临时文件 -->
<Target Name="CleanDebugFiles" AfterTargets="Build">

View File

@@ -0,0 +1,28 @@
using Verse;
using RimWorld;
namespace ArachnaeSwarm
{
/// <summary>
/// XML配置类用于在HediffDef的comp中指定转化后的MutantDef
/// </summary>
public class HediffCompProperties_NecroticTransformation : HediffCompProperties
{
// 在XML中需要指定的MutantDef的defName
public MutantDef mutantDef;
public HediffCompProperties_NecroticTransformation()
{
compClass = typeof(HediffComp_NecroticTransformation);
}
}
/// <summary>
/// HediffComp用于在运行时存储和提供由XML定义的MutantDef
/// </summary>
public class HediffComp_NecroticTransformation : HediffComp
{
// 属性用于方便地从Comp中获取配置
public HediffCompProperties_NecroticTransformation Props => (HediffCompProperties_NecroticTransformation)props;
}
}

View File

@@ -0,0 +1,118 @@
using RimWorld;
using Verse;
using UnityEngine;
using System.Linq;
namespace ArachnaeSwarm
{
public class Hediff_NecroticVirus : HediffWithComps
{
private const int EffectInterval = 600;
// 属性来获取我们的自定义Comp
private HediffComp_NecroticTransformation PropsComp => this.TryGetComp<HediffComp_NecroticTransformation>();
public override void Notify_PawnDied(DamageInfo? dinfo, Hediff culprit = null)
{
base.Notify_PawnDied(dinfo, culprit);
TransformToMutant();
}
public override void PostTick()
{
base.PostTick();
if (pawn.IsHashIntervalTick(EffectInterval) && pawn.Spawned)
{
ShowVirusEffects();
}
}
private void TransformToMutant()
{
var comp = PropsComp;
if (comp == null || comp.Props.mutantDef == null)
{
Log.Error($"[NecroticVirus] HediffComp_NecroticTransformation or its mutantDef is not configured in XML for {this.def.defName}.");
return;
}
try
{
if (pawn.Corpse == null || pawn.Corpse.Destroyed)
{
return;
}
Map map = pawn.Corpse.Map;
IntVec3 position = pawn.Corpse.Position;
if (!MutantUtility.CanResurrectAsShambler(pawn.Corpse))
{
Log.Warning($"[NecroticVirus] Cannot resurrect {pawn.LabelShort} as a shambler-like creature.");
return;
}
Faction faction = GetHostileFaction();
// **调用我们自己的工具方法传入从XML获取的mutantDef**
NecroticTransformationUtility.ResurrectAsCustomMutant(pawn, comp.Props.mutantDef, faction);
// 添加转化特效
FleckMaker.ThrowSmoke(position.ToVector3Shifted(), map, 1.5f);
FleckMaker.ThrowDustPuff(position.ToVector3Shifted(), map, 1.2f);
// 发送转化消息
if (PawnUtility.ShouldSendNotificationAbout(pawn))
{
Messages.Message(
"NecroticVirus_TransformationMessage".Translate(pawn.LabelShortCap, pawn.LabelShortCap),
new LookTargets(position, map),
MessageTypeDefOf.NegativeEvent
);
}
}
catch (System.Exception ex)
{
Log.Error($"[NecroticVirus] Error during transformation: {ex}");
}
}
private Faction GetHostileFaction()
{
if (pawn.Faction != null && pawn.Faction.HostileTo(Faction.OfPlayer))
{
return pawn.Faction;
}
Faction entitiesFaction = Find.FactionManager.AllFactions.FirstOrDefault(f => f.def.defName == "Entities");
if (entitiesFaction != null && !entitiesFaction.defeated)
{
return entitiesFaction;
}
return Find.FactionManager.RandomEnemyFaction(allowNonHumanlike: true, allowHidden: false, allowDefeated: false);
}
private void ShowVirusEffects()
{
if (pawn == null || !pawn.Spawned) return;
Vector3 pos = pawn.DrawPos;
Map map = pawn.Map;
float sizeMultiplier = 1f;
if (CurStageIndex == 1) sizeMultiplier = 1.2f;
if (CurStageIndex == 2) sizeMultiplier = 1.5f;
FleckMaker.ThrowMicroSparks(pos, map);
FleckMaker.ThrowDustPuff(pos, map, 0.8f * sizeMultiplier);
}
public override string TipStringExtra
{
get
{
return base.TipStringExtra + "\n" + "NecroticVirus_EffectTip".Translate();
}
}
}
}

View File

@@ -0,0 +1,65 @@
using RimWorld;
using Verse;
namespace ArachnaeSwarm
{
public static class NecroticTransformationUtility
{
/// <summary>
/// 将一个Pawn复活为指定的自定义变异体.
/// 这个方法是模仿原版 MutantUtility.ResurrectAsShambler,
/// 但允许传入一个自定义的 MutantDef.
/// </summary>
public static void ResurrectAsCustomMutant(Pawn pawn, MutantDef mutantDef, Faction faction = null, int lifespanTicks = -1)
{
if (pawn?.Corpse == null || mutantDef == null)
{
return;
}
RotStage rotStage = pawn.Corpse.GetRotStage();
// 创建并附加Pawn_MutantTracker使用我们从XML传入的mutantDef
pawn.mutant = new Pawn_MutantTracker(pawn, mutantDef, rotStage);
// 添加变异体核心Hediff
Hediff hediff = pawn.health.AddHediff(mutantDef.hediff);
// 如果是蹒跚者类型的Hediff则调用其上升动画
if (hediff is Hediff_Shambler shamblerHediff)
{
shamblerHediff.StartRising(lifespanTicks);
}
// 设置生命周期
var disappearsComp = hediff.TryGetComp<HediffComp_DisappearsAndKills>();
if (disappearsComp != null)
{
if (lifespanTicks > 0)
{
disappearsComp.disappearsAfterTicks = lifespanTicks;
disappearsComp.ticksToDisappear = lifespanTicks;
}
else
{
disappearsComp.disabled = true;
}
}
// 如果没有提供派系,则尝试使用变异体定义的默认派系
if (faction == null && mutantDef.defaultFaction != null)
{
faction = Find.FactionManager.FirstFactionOfDef(mutantDef.defaultFaction);
}
// 设置派系
if (faction != null && pawn.Faction != faction)
{
pawn.SetFaction(faction);
}
// 立即执行转变
pawn.mutant.Turn(clearLord: true);
}
}
}

View File

@@ -0,0 +1,211 @@
using System;
using System.Collections.Generic;
using System.Reflection;
using HarmonyLib;
using RimWorld;
using UnityEngine;
using Verse;
namespace ArachnaeSwarm
{
public class ProphecyGearEffect : DefModExtension
{
public HediffDef hediffToApply;
public float severityPerDamage = 0f;
public float minSeverity = 0f;
public float maxSeverity = 1f;
public bool enableHediffEffect = true;
public float extraDamageFactor = 0f;
public DamageDef extraDamageType;
public float armorPenetration = 0f;
public BodyPartDef targetBodyPart;
public bool enableExtraDamage = true;
// 新添加:触发效果所需的仿生体 Hediff
public HediffDef requiredBionicHediff;
}
[HarmonyPatch(typeof(Pawn), "PostApplyDamage")]
public static class DRM_ProphecyGear_Patch
{
private static readonly FieldInfo damageInfoWeaponField =
typeof(DamageInfo).GetField("weapon", BindingFlags.Instance | BindingFlags.NonPublic);
private static bool isApplyingExtraDamage = false;
public static void Postfix(Pawn __instance, DamageInfo dinfo, float totalDamageDealt)
{
if (isApplyingExtraDamage) return;
try
{
if (__instance == null || __instance.Dead || __instance.health == null) return;
if (!(dinfo.Instigator is Pawn attacker) || attacker.apparel == null) return;
if (totalDamageDealt <= 0) return;
// 收集所有可能的效果来源
var effectsToApply = new List<ProphecyGearEffect>();
// 1. 从装备收集效果
foreach (var apparel in attacker.apparel.WornApparel)
{
var effect = apparel.def.GetModExtension<ProphecyGearEffect>();
if (effect != null && IsEffectValid(effect, attacker))
{
effectsToApply.Add(effect);
}
}
// 2. 从仿生体 Hediff 收集效果
foreach (var hediff in attacker.health.hediffSet.hediffs)
{
var effect = hediff.def.GetModExtension<ProphecyGearEffect>();
if (effect != null && IsEffectValid(effect, attacker))
{
effectsToApply.Add(effect);
}
}
// 应用所有效果
foreach (var effect in effectsToApply)
{
if (effect.enableHediffEffect && effect.hediffToApply != null)
{
ApplyHediffEffect(__instance, effect, totalDamageDealt);
}
if (effect.enableExtraDamage &&
effect.extraDamageFactor > 0 &&
effect.extraDamageType != null)
{
isApplyingExtraDamage = true;
ApplyExtraDamage(__instance, effect, totalDamageDealt, dinfo);
isApplyingExtraDamage = false;
}
}
}
catch (Exception ex)
{
isApplyingExtraDamage = false;
Log.Error($"[ArachnaeSwarm] Error in PostApplyDamage patch: {ex}");
}
}
// 检查效果是否有效(满足仿生体要求)
private static bool IsEffectValid(ProphecyGearEffect effect, Pawn attacker)
{
// 如果没有要求仿生体,则始终有效
if (effect.requiredBionicHediff == null)
return true;
// 检查攻击者是否拥有所需的仿生体
return attacker.health.hediffSet.HasHediff(effect.requiredBionicHediff);
}
private static void ApplyHediffEffect(Pawn target, ProphecyGearEffect effect, float damageDealt)
{
try
{
double severityToAdd = (double)damageDealt * (double)effect.severityPerDamage;
float finalSeverity = (float)severityToAdd;
if (finalSeverity < 0.001f) return;
var existingHediff = target.health.hediffSet.GetFirstHediffOfDef(effect.hediffToApply);
if (existingHediff == null)
{
Hediff newHediff = HediffMaker.MakeHediff(effect.hediffToApply, target);
if (newHediff != null)
{
newHediff.Severity = finalSeverity;
if (effect.minSeverity > 0)
newHediff.Severity = Math.Max(newHediff.Severity, effect.minSeverity);
if (effect.maxSeverity > 0)
newHediff.Severity = Math.Min(newHediff.Severity, effect.maxSeverity);
target.health.AddHediff(newHediff);
}
}
else
{
float newSeverity = existingHediff.Severity + finalSeverity;
if (effect.minSeverity > 0)
newSeverity = Math.Max(newSeverity, effect.minSeverity);
if (effect.maxSeverity > 0)
newSeverity = Math.Min(newSeverity, effect.maxSeverity);
existingHediff.Severity = newSeverity;
if (existingHediff is HediffWithComps hwc)
{
var comp = hwc.TryGetComp<HediffComp_Disappears>();
if (comp != null)
{
comp.ticksToDisappear = comp.Props.disappearsAfterTicks.max;
}
}
}
}
catch (Exception ex)
{
Log.Error($"[ArachnaeSwarm] Error in ApplyHediffEffect: {ex}");
}
}
private static void ApplyExtraDamage(
Pawn target,
ProphecyGearEffect effect,
float baseDamage,
DamageInfo originalDinfo)
{
try
{
float extraDamageAmount = baseDamage * effect.extraDamageFactor;
if (extraDamageAmount < 0.5f) extraDamageAmount = 0.5f;
if (extraDamageAmount <= 0) return;
Thing weapon = null;
if (damageInfoWeaponField != null)
{
weapon = damageInfoWeaponField.GetValue(originalDinfo) as Thing;
}
BodyPartRecord hitPart = null;
if (effect.targetBodyPart != null)
{
var bodyPartDef = DefDatabase<BodyPartDef>.GetNamedSilentFail(effect.targetBodyPart.defName);
if (bodyPartDef != null)
{
hitPart = target.RaceProps.body.GetPartsWithDef(bodyPartDef).FirstOrFallback();
}
}
else
{
hitPart = originalDinfo.HitPart;
}
DamageInfo extraDinfo = new DamageInfo(
def: effect.extraDamageType,
amount: extraDamageAmount,
armorPenetration: effect.armorPenetration,
angle: -1f,
instigator: originalDinfo.Instigator,
hitPart: hitPart,
weapon: originalDinfo.Weapon,
category: DamageInfo.SourceCategory.ThingOrUnknown,
intendedTarget: target
);
target.TakeDamage(extraDinfo);
}
catch (Exception ex)
{
Log.Error($"[ArachnaeSwarm] Error in ApplyExtraDamage: {ex}");
}
}
}
}