补上传
This commit is contained in:
Binary file not shown.
@@ -4,7 +4,7 @@
|
|||||||
<!-- ==================== Jump Possession ==================== -->
|
<!-- ==================== Jump Possession ==================== -->
|
||||||
<AbilityDef>
|
<AbilityDef>
|
||||||
<defName>ARA_Ability_Possess</defName>
|
<defName>ARA_Ability_Possess</defName>
|
||||||
<label>阿拉克涅突袭寄生</label>
|
<label>原虫种突袭寄生</label>
|
||||||
<description>跃向目标,随后尝试使用长尾刺穿对象并截断其神经中枢信号,以自己的控制信号取而代之,进而完全侵占目标躯体。\n\n被侵占躯体的对象视为阿拉克涅虫族的一员,需要接受女皇种的信息素标记,并且不再拥有感情和高级需求,仅作为躯壳活着。目标的技能熟练度和背景故事会替换为原虫种的技能熟练度和背景故事,一旦宿主死亡,原虫种将从宿主身上逃离。</description>
|
<description>跃向目标,随后尝试使用长尾刺穿对象并截断其神经中枢信号,以自己的控制信号取而代之,进而完全侵占目标躯体。\n\n被侵占躯体的对象视为阿拉克涅虫族的一员,需要接受女皇种的信息素标记,并且不再拥有感情和高级需求,仅作为躯壳活着。目标的技能熟练度和背景故事会替换为原虫种的技能熟练度和背景故事,一旦宿主死亡,原虫种将从宿主身上逃离。</description>
|
||||||
<iconPath>UI/Abilities/Longjump</iconPath>
|
<iconPath>UI/Abilities/Longjump</iconPath>
|
||||||
<cooldownTicksRange>800</cooldownTicksRange>
|
<cooldownTicksRange>800</cooldownTicksRange>
|
||||||
@@ -13,7 +13,7 @@
|
|||||||
<!-- 使用我们新的Verb -->
|
<!-- 使用我们新的Verb -->
|
||||||
<verbClass>ArachnaeSwarm.Verb_JumpAndCastOnLanding</verbClass>
|
<verbClass>ArachnaeSwarm.Verb_JumpAndCastOnLanding</verbClass>
|
||||||
<warmupTime>1.0</warmupTime>
|
<warmupTime>1.0</warmupTime>
|
||||||
<range>19.9</range>
|
<range>9.9</range>
|
||||||
<requireLineOfSight>true</requireLineOfSight>
|
<requireLineOfSight>true</requireLineOfSight>
|
||||||
<soundCast>Longjump_Jump</soundCast>
|
<soundCast>Longjump_Jump</soundCast>
|
||||||
<soundLanding>Longjump_Land</soundLanding>
|
<soundLanding>Longjump_Land</soundLanding>
|
||||||
@@ -26,10 +26,17 @@
|
|||||||
</verbProperties>
|
</verbProperties>
|
||||||
<comps>
|
<comps>
|
||||||
<!-- comps部分现在只包含夺舍效果,因为跳跃由Verb处理 -->
|
<!-- comps部分现在只包含夺舍效果,因为跳跃由Verb处理 -->
|
||||||
<li Class="ArachnaeSwarm.CompProperties_AbilityPossess"/>
|
<li Class="ArachnaeSwarm.CompProperties_AbilityPossess">
|
||||||
<li Class="CompProperties_AbilityGiveHediff">
|
<!-- Base success chance range (60% to 100%) -->
|
||||||
<compClass>CompAbilityEffect_GiveHediff</compClass>
|
<successChance>0.6~1.0</successChance>
|
||||||
<hediffDef>ARA_HiveMindDrone</hediffDef>
|
<!-- Bonus success chance per point of damage dealt (1%) -->
|
||||||
|
<successChanceBonusPerDamage>0.01</successChanceBonusPerDamage>
|
||||||
|
<!-- Optional: Hediff to apply on successful possession -->
|
||||||
|
<hediffToApplyOnSuccess>ARA_HiveMindDrone</hediffToApplyOnSuccess>
|
||||||
|
<!-- Optional: A list of race ThingDefs that cannot be possessed -->
|
||||||
|
<raceBlacklist>
|
||||||
|
<li>ArachnaeQueen_Race</li>
|
||||||
|
</raceBlacklist>
|
||||||
</li>
|
</li>
|
||||||
</comps>
|
</comps>
|
||||||
</AbilityDef>
|
</AbilityDef>
|
||||||
|
|||||||
9
Languages/ChineseSimplified/Keyed/ArachnaeSwarm_Keys.xml
Normal file
9
Languages/ChineseSimplified/Keyed/ArachnaeSwarm_Keys.xml
Normal file
@@ -0,0 +1,9 @@
|
|||||||
|
<?xml version="1.0" encoding="utf-8" ?>
|
||||||
|
<LanguageData>
|
||||||
|
|
||||||
|
<!-- Possession Ability Keys -->
|
||||||
|
<ARA_MustBeHumanlike>目标必须是类人生物。</ARA_MustBeHumanlike>
|
||||||
|
<ARA_AlreadyPossessed>{0} 已被夺舍。</ARA_AlreadyPossessed>
|
||||||
|
<ARA_CannotPossessRace>无法夺舍 {0} 种族。</ARA_CannotPossessRace>
|
||||||
|
|
||||||
|
</LanguageData>
|
||||||
@@ -1,50 +1,110 @@
|
|||||||
using RimWorld;
|
using RimWorld;
|
||||||
using RimWorld.Planet;
|
using RimWorld.Planet;
|
||||||
using Verse;
|
using Verse;
|
||||||
|
using UnityEngine;
|
||||||
|
using System.Linq;
|
||||||
|
|
||||||
namespace ArachnaeSwarm
|
namespace ArachnaeSwarm
|
||||||
{
|
{
|
||||||
public class CompAbilityEffect_Possess : CompAbilityEffect, ICompAbilityEffectOnJumpCompleted
|
public class CompAbilityEffect_Possess : CompAbilityEffect, ICompAbilityEffectOnJumpCompleted
|
||||||
{
|
{
|
||||||
|
public new CompProperties_AbilityPossess Props => (CompProperties_AbilityPossess)props;
|
||||||
|
|
||||||
|
public override bool Valid(LocalTargetInfo target, bool throwMessages = false)
|
||||||
|
{
|
||||||
|
Pawn targetPawn = target.Pawn;
|
||||||
|
if (targetPawn == null) return false;
|
||||||
|
|
||||||
|
if (!targetPawn.RaceProps.Humanlike)
|
||||||
|
{
|
||||||
|
if (throwMessages) Messages.Message("ARA_MustBeHumanlike".Translate(), targetPawn, MessageTypeDefOf.RejectInput, false);
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (targetPawn.health.hediffSet.HasHediff(HediffDef.Named("ARA_Possession")))
|
||||||
|
{
|
||||||
|
if (throwMessages) Messages.Message("ARA_AlreadyPossessed".Translate(targetPawn.LabelShort), targetPawn, MessageTypeDefOf.RejectInput, false);
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (Props.raceBlacklist.Contains(targetPawn.def))
|
||||||
|
{
|
||||||
|
if (throwMessages) Messages.Message("ARA_CannotPossessRace".Translate(targetPawn.def.label), targetPawn, MessageTypeDefOf.RejectInput, false);
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
return base.Valid(target, throwMessages);
|
||||||
|
}
|
||||||
|
|
||||||
public override void Apply(LocalTargetInfo target, LocalTargetInfo dest)
|
public override void Apply(LocalTargetInfo target, LocalTargetInfo dest)
|
||||||
{
|
{
|
||||||
Log.Message($"[CompAbilityEffect_Possess] Apply called. Target: {target.Thing?.LabelShort ?? "null"}");
|
|
||||||
base.Apply(target, dest);
|
base.Apply(target, dest);
|
||||||
|
DoPossession(this.parent.pawn, target.Pawn);
|
||||||
Pawn caster = this.parent.pawn;
|
|
||||||
Pawn targetPawn = target.Pawn;
|
|
||||||
|
|
||||||
if (targetPawn == null || caster == null)
|
|
||||||
{
|
|
||||||
Log.Warning($"[CompAbilityEffect_Possess] Apply aborted. TargetPawn or Caster is null.");
|
|
||||||
return;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private void DoPossession(Pawn caster, Pawn targetPawn)
|
||||||
|
{
|
||||||
|
if (targetPawn == null || caster == null) return;
|
||||||
|
|
||||||
Log.Message($"[夺舍] 开始执行。施法者: {caster.LabelShort}, 目标: {targetPawn.LabelShort}");
|
Log.Message($"[夺舍] 开始执行。施法者: {caster.LabelShort}, 目标: {targetPawn.LabelShort}");
|
||||||
|
|
||||||
Hediff_Possession hediff = (Hediff_Possession)HediffMaker.MakeHediff(HediffDef.Named("ARA_Possession"), targetPawn);
|
Hediff_Possession hediff = (Hediff_Possession)HediffMaker.MakeHediff(HediffDef.Named("ARA_Possession"), targetPawn);
|
||||||
|
|
||||||
if (hediff.GetDirectlyHeldThings().TryAdd(caster.SplitOff(1), true))
|
if (hediff.GetDirectlyHeldThings().TryAdd(caster.SplitOff(1), true))
|
||||||
{
|
{
|
||||||
Log.Message($"[夺舍] 成功将 {caster.LabelShort} 的副本存入Hediff。");
|
Log.Message($"[夺舍] 成功将 {caster.LabelShort} 的副本存入Hediff。");
|
||||||
|
PawnDataUtility.TransferSoul(caster, targetPawn);
|
||||||
|
targetPawn.health.AddHediff(hediff);
|
||||||
|
|
||||||
|
if (Props.hediffToApplyOnSuccess != null)
|
||||||
|
{
|
||||||
|
targetPawn.health.AddHediff(Props.hediffToApplyOnSuccess, null, null);
|
||||||
|
Log.Message($"[夺舍] 成功为 {targetPawn.LabelShort} 添加额外Hediff: {Props.hediffToApplyOnSuccess.defName}");
|
||||||
|
}
|
||||||
|
Log.Message($"[夺舍] {targetPawn.LabelShort} (原 {caster.LabelShort}) 夺舍完成。");
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
Log.Error($"[夺舍] 无法将 {caster.LabelShort} 的副本存入Hediff。中止操作。");
|
Log.Error($"[夺舍] 无法将 {caster.LabelShort} 的副本存入Hediff。中止操作。");
|
||||||
return;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
PawnDataUtility.TransferSoul(caster, targetPawn);
|
|
||||||
|
|
||||||
targetPawn.health.AddHediff(hediff);
|
|
||||||
|
|
||||||
Log.Message($"[夺舍] {targetPawn.LabelShort} (原 {caster.LabelShort}) 夺舍完成。");
|
|
||||||
}
|
}
|
||||||
|
|
||||||
public void OnJumpCompleted(IntVec3 JUMPINPOS_UNUSED, LocalTargetInfo landingTarget)
|
public void OnJumpCompleted(IntVec3 JUMPINPOS_UNUSED, LocalTargetInfo landingTarget)
|
||||||
{
|
{
|
||||||
Log.Message($"[CompAbilityEffect_Possess] OnJumpCompleted called. Landing target: {landingTarget.Thing?.LabelShort ?? "null"}");
|
Pawn caster = this.parent.pawn;
|
||||||
this.Apply(landingTarget, null);
|
if (caster == null || !(landingTarget.Thing is Pawn targetPawn)) return;
|
||||||
|
|
||||||
|
Verb bestMeleeVerb = caster.meleeVerbs.TryGetMeleeVerb(targetPawn);
|
||||||
|
if (bestMeleeVerb == null)
|
||||||
|
{
|
||||||
|
Log.Warning($"[Possess] Caster {caster.LabelShort} has no melee verb.");
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
float damageAmount = bestMeleeVerb.verbProps.AdjustedMeleeDamageAmount(bestMeleeVerb, caster);
|
||||||
|
float armorPenetration = bestMeleeVerb.verbProps.AdjustedArmorPenetration(bestMeleeVerb, caster);
|
||||||
|
DamageDef damageDef = bestMeleeVerb.verbProps.meleeDamageDef;
|
||||||
|
|
||||||
|
var dinfo = new DamageInfo(damageDef, damageAmount, armorPenetration, -1, caster);
|
||||||
|
DamageWorker.DamageResult damageResult = targetPawn.TakeDamage(dinfo);
|
||||||
|
Log.Message($"[Possess] Dealt {damageResult.totalDamageDealt} damage to {targetPawn.LabelShort} using {damageDef.defName}.");
|
||||||
|
|
||||||
|
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}");
|
||||||
|
|
||||||
|
if (Rand.Chance(finalChance))
|
||||||
|
{
|
||||||
|
Log.Message($"[Possess] Success! Applying possession effect.");
|
||||||
|
DoPossession(caster, targetPawn);
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
Log.Message($"[Possess] Failed possession check.");
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -1,9 +1,18 @@
|
|||||||
using RimWorld;
|
using RimWorld;
|
||||||
|
using Verse;
|
||||||
|
using System.Collections.Generic;
|
||||||
|
|
||||||
namespace ArachnaeSwarm
|
namespace ArachnaeSwarm
|
||||||
{
|
{
|
||||||
public class CompProperties_AbilityPossess : CompProperties_AbilityEffect
|
public class CompProperties_AbilityPossess : CompProperties_AbilityEffect
|
||||||
{
|
{
|
||||||
|
public FloatRange successChance = new FloatRange(0.6f, 1.0f);
|
||||||
|
public float successChanceBonusPerDamage = 0.01f;
|
||||||
|
public List<ThingDef> raceBlacklist = new List<ThingDef>();
|
||||||
|
|
||||||
|
// Optional: A hediff to apply to the victim upon successful possession.
|
||||||
|
public HediffDef hediffToApplyOnSuccess;
|
||||||
|
|
||||||
public CompProperties_AbilityPossess()
|
public CompProperties_AbilityPossess()
|
||||||
{
|
{
|
||||||
this.compClass = typeof(CompAbilityEffect_Possess);
|
this.compClass = typeof(CompAbilityEffect_Possess);
|
||||||
|
|||||||
@@ -8,14 +8,18 @@ namespace ArachnaeSwarm
|
|||||||
{
|
{
|
||||||
public class Verb_JumpAndCastOnLanding : Verb_CastAbility
|
public class Verb_JumpAndCastOnLanding : Verb_CastAbility
|
||||||
{
|
{
|
||||||
|
// 1. A private field to store our specific target, separate from what the base class does.
|
||||||
private LocalTargetInfo capturedTarget;
|
private LocalTargetInfo capturedTarget;
|
||||||
|
|
||||||
|
// 2. Capture the reliable target information early.
|
||||||
public override bool TryStartCastOn(LocalTargetInfo castTarg, LocalTargetInfo destTarg, bool surpriseAttack = false, bool canHitNonTargetPawns = true, bool preventFriendlyFire = false, bool nonInterruptingSelfCast = false)
|
public override bool TryStartCastOn(LocalTargetInfo castTarg, LocalTargetInfo destTarg, bool surpriseAttack = false, bool canHitNonTargetPawns = true, bool preventFriendlyFire = false, bool nonInterruptingSelfCast = false)
|
||||||
{
|
{
|
||||||
|
Log.Message($"[Verb_JumpAndCastOnLanding] TryStartCastOn: Capturing our dedicated target: {castTarg.Thing?.LabelShort ?? "null"}.");
|
||||||
this.capturedTarget = castTarg;
|
this.capturedTarget = castTarg;
|
||||||
return base.TryStartCastOn(castTarg, destTarg, surpriseAttack, canHitNonTargetPawns, preventFriendlyFire, nonInterruptingSelfCast);
|
return base.TryStartCastOn(castTarg, destTarg, surpriseAttack, canHitNonTargetPawns, preventFriendlyFire, nonInterruptingSelfCast);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// --- Copied and adapted methods from Verb_CastAbilityJump ---
|
||||||
public virtual ThingDef JumpFlyerDef => ThingDefOf.PawnFlyer;
|
public virtual ThingDef JumpFlyerDef => ThingDefOf.PawnFlyer;
|
||||||
public override float EffectiveRange => this.verbProps.range;
|
public override float EffectiveRange => this.verbProps.range;
|
||||||
|
|
||||||
@@ -35,25 +39,34 @@ namespace ArachnaeSwarm
|
|||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// --- Our custom implementation of TryCastShot ---
|
||||||
protected override bool TryCastShot()
|
protected override bool TryCastShot()
|
||||||
{
|
{
|
||||||
|
// The physical destination for the jump. This might be adjusted by the game. We let it be.
|
||||||
LocalTargetInfo physicalTarget = this.currentTarget;
|
LocalTargetInfo physicalTarget = this.currentTarget;
|
||||||
|
|
||||||
|
// Our dedicated logical target, captured earlier and guaranteed to be the Pawn.
|
||||||
LocalTargetInfo logicalTargetForUs = this.capturedTarget;
|
LocalTargetInfo logicalTargetForUs = this.capturedTarget;
|
||||||
|
|
||||||
if (logicalTargetForUs == null || !logicalTargetForUs.HasThing)
|
if (logicalTargetForUs == null || !logicalTargetForUs.HasThing)
|
||||||
{
|
{
|
||||||
|
Log.Error($"[Verb_JumpAndCastOnLanding] TryCastShot: Our captured target is invalid!");
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
return JumpUtility.DoJump(
|
Log.Message($"[Verb_JumpAndCastOnLanding] TryCastShot: Using our captured target '{logicalTargetForUs.Thing.LabelShort}' for logic, and letting game use '{physicalTarget.Cell}' for jump physics.");
|
||||||
|
|
||||||
|
bool jumpStarted = JumpUtility.DoJump(
|
||||||
CasterPawn,
|
CasterPawn,
|
||||||
physicalTarget,
|
physicalTarget, // For the jump itself
|
||||||
this.ReloadableCompSource,
|
this.ReloadableCompSource,
|
||||||
verbProps,
|
verbProps,
|
||||||
this.ability,
|
this.ability,
|
||||||
logicalTargetForUs,
|
logicalTargetForUs, // For our logic on landing
|
||||||
this.JumpFlyerDef
|
this.JumpFlyerDef
|
||||||
);
|
);
|
||||||
|
|
||||||
|
return jumpStarted;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
Reference in New Issue
Block a user