补上传
This commit is contained in:
@@ -1,50 +1,110 @@
|
||||
using RimWorld;
|
||||
using RimWorld.Planet;
|
||||
using Verse;
|
||||
using UnityEngine;
|
||||
using System.Linq;
|
||||
|
||||
namespace ArachnaeSwarm
|
||||
{
|
||||
public class CompAbilityEffect_Possess : CompAbilityEffect, ICompAbilityEffectOnJumpCompleted
|
||||
{
|
||||
public override void Apply(LocalTargetInfo target, LocalTargetInfo dest)
|
||||
public new CompProperties_AbilityPossess Props => (CompProperties_AbilityPossess)props;
|
||||
|
||||
public override bool Valid(LocalTargetInfo target, bool throwMessages = false)
|
||||
{
|
||||
Log.Message($"[CompAbilityEffect_Possess] Apply called. Target: {target.Thing?.LabelShort ?? "null"}");
|
||||
base.Apply(target, dest);
|
||||
|
||||
Pawn caster = this.parent.pawn;
|
||||
Pawn targetPawn = target.Pawn;
|
||||
if (targetPawn == null) return false;
|
||||
|
||||
if (targetPawn == null || caster == null)
|
||||
if (!targetPawn.RaceProps.Humanlike)
|
||||
{
|
||||
Log.Warning($"[CompAbilityEffect_Possess] Apply aborted. TargetPawn or Caster is null.");
|
||||
return;
|
||||
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)
|
||||
{
|
||||
base.Apply(target, dest);
|
||||
DoPossession(this.parent.pawn, target.Pawn);
|
||||
}
|
||||
|
||||
private void DoPossession(Pawn caster, Pawn targetPawn)
|
||||
{
|
||||
if (targetPawn == null || caster == null) return;
|
||||
|
||||
Log.Message($"[夺舍] 开始执行。施法者: {caster.LabelShort}, 目标: {targetPawn.LabelShort}");
|
||||
|
||||
Hediff_Possession hediff = (Hediff_Possession)HediffMaker.MakeHediff(HediffDef.Named("ARA_Possession"), targetPawn);
|
||||
|
||||
if (hediff.GetDirectlyHeldThings().TryAdd(caster.SplitOff(1), true))
|
||||
{
|
||||
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
|
||||
{
|
||||
Log.Error($"[夺舍] 无法将 {caster.LabelShort} 的副本存入Hediff。中止操作。");
|
||||
return;
|
||||
Log.Error($"[夺舍] 无法将 {caster.LabelShort} 的副本存入Hediff。中止操作。");
|
||||
}
|
||||
|
||||
PawnDataUtility.TransferSoul(caster, targetPawn);
|
||||
|
||||
targetPawn.health.AddHediff(hediff);
|
||||
|
||||
Log.Message($"[夺舍] {targetPawn.LabelShort} (原 {caster.LabelShort}) 夺舍完成。");
|
||||
}
|
||||
|
||||
public void OnJumpCompleted(IntVec3 JUMPINPOS_UNUSED, LocalTargetInfo landingTarget)
|
||||
{
|
||||
Log.Message($"[CompAbilityEffect_Possess] OnJumpCompleted called. Landing target: {landingTarget.Thing?.LabelShort ?? "null"}");
|
||||
this.Apply(landingTarget, null);
|
||||
Pawn caster = this.parent.pawn;
|
||||
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 Verse;
|
||||
using System.Collections.Generic;
|
||||
|
||||
namespace ArachnaeSwarm
|
||||
{
|
||||
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()
|
||||
{
|
||||
this.compClass = typeof(CompAbilityEffect_Possess);
|
||||
|
||||
@@ -8,14 +8,18 @@ namespace ArachnaeSwarm
|
||||
{
|
||||
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;
|
||||
|
||||
// 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)
|
||||
{
|
||||
Log.Message($"[Verb_JumpAndCastOnLanding] TryStartCastOn: Capturing our dedicated target: {castTarg.Thing?.LabelShort ?? "null"}.");
|
||||
this.capturedTarget = castTarg;
|
||||
return base.TryStartCastOn(castTarg, destTarg, surpriseAttack, canHitNonTargetPawns, preventFriendlyFire, nonInterruptingSelfCast);
|
||||
}
|
||||
|
||||
// --- Copied and adapted methods from Verb_CastAbilityJump ---
|
||||
public virtual ThingDef JumpFlyerDef => ThingDefOf.PawnFlyer;
|
||||
public override float EffectiveRange => this.verbProps.range;
|
||||
|
||||
@@ -35,25 +39,34 @@ namespace ArachnaeSwarm
|
||||
return true;
|
||||
}
|
||||
|
||||
// --- Our custom implementation of 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;
|
||||
|
||||
// Our dedicated logical target, captured earlier and guaranteed to be the Pawn.
|
||||
LocalTargetInfo logicalTargetForUs = this.capturedTarget;
|
||||
|
||||
if (logicalTargetForUs == null || !logicalTargetForUs.HasThing)
|
||||
{
|
||||
Log.Error($"[Verb_JumpAndCastOnLanding] TryCastShot: Our captured target is invalid!");
|
||||
return false;
|
||||
}
|
||||
|
||||
Log.Message($"[Verb_JumpAndCastOnLanding] TryCastShot: Using our captured target '{logicalTargetForUs.Thing.LabelShort}' for logic, and letting game use '{physicalTarget.Cell}' for jump physics.");
|
||||
|
||||
return JumpUtility.DoJump(
|
||||
bool jumpStarted = JumpUtility.DoJump(
|
||||
CasterPawn,
|
||||
physicalTarget,
|
||||
physicalTarget, // For the jump itself
|
||||
this.ReloadableCompSource,
|
||||
verbProps,
|
||||
this.ability,
|
||||
logicalTargetForUs,
|
||||
logicalTargetForUs, // For our logic on landing
|
||||
this.JumpFlyerDef
|
||||
);
|
||||
|
||||
return jumpStarted;
|
||||
}
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user