This commit is contained in:
2026-02-24 12:02:38 +08:00
parent 1af5f0c1d8
commit 96bc1d4c5a
57 changed files with 6595 additions and 1170 deletions

View File

@@ -1,44 +0,0 @@
using Verse;
using RimWorld;
namespace WulaFallenEmpire
{
public class CompProperties_AbilityLaunchMultiProjectile : CompProperties_AbilityLaunchProjectile
{
public int numProjectiles = 1;
public CompProperties_AbilityLaunchMultiProjectile()
{
compClass = typeof(CompAbilityEffect_LaunchMultiProjectile);
}
}
public class CompAbilityEffect_LaunchMultiProjectile : CompAbilityEffect
{
public new CompProperties_AbilityLaunchMultiProjectile Props => (CompProperties_AbilityLaunchMultiProjectile)props;
public override void Apply(LocalTargetInfo target, LocalTargetInfo dest)
{
base.Apply(target, dest);
for (int i = 0; i < Props.numProjectiles; i++)
{
LaunchProjectile(target);
}
}
private void LaunchProjectile(LocalTargetInfo target)
{
if (Props.projectileDef != null)
{
Pawn pawn = parent.pawn;
Projectile projectile = (Projectile)GenSpawn.Spawn(Props.projectileDef, pawn.Position, pawn.Map);
projectile.Launch(pawn, pawn.DrawPos, target, target, ProjectileHitFlags.IntendedTarget, parent.verb.preventFriendlyFire);
}
}
public override bool AICanTargetNow(LocalTargetInfo target)
{
return target.Pawn != null;
}
}
}

View File

@@ -1,301 +0,0 @@
using System;
using System.Collections.Generic;
using RimWorld;
using UnityEngine;
using Verse;
using Verse.Sound;
namespace WulaFallenEmpire
{
public class CompAbilityEffect_DRM_LightningBombardment : CompAbilityEffect
{
public new CompProperties_AbilityDRM_LightningBombardment Props
{
get => (CompProperties_AbilityDRM_LightningBombardment)props;
}
public override void Apply(LocalTargetInfo target, LocalTargetInfo dest)
{
base.Apply(target, dest);
Map map = parent.pawn.MapHeld;
// 获取或创建地图组件
MapComponent_LightningBombardment comp = GetOrCreateMapComponent(map);
// 启动轰炸任务
comp.StartBombardment(
target: target.Cell,
instigator: parent.pawn,
explosionCount: Props.explosionCount,
bombIntervalTicks: Props.bombIntervalTicks,
impactAreaRadius: Props.impactAreaRadius,
explosionRadiusRange: Props.explosionRadiusRange,
damageDef: Props.damageDef,
damageAmount: Props.damageAmount,
armorPenetration: Props.armorPenetration,
postExplosionSpawnThingDef: Props.postExplosionSpawnThingDef,
postExplosionSpawnChance: Props.postExplosionSpawnChance,
postExplosionSpawnThingCount: Props.postExplosionSpawnThingCount
);
// 播放启动音效
SoundDefOf.Thunder_OffMap.PlayOneShotOnCamera(map);
}
private MapComponent_LightningBombardment GetOrCreateMapComponent(Map map)
{
MapComponent_LightningBombardment comp = map.GetComponent<MapComponent_LightningBombardment>();
if (comp == null)
{
comp = new MapComponent_LightningBombardment(map);
map.components.Add(comp);
}
return comp;
}
public override void DrawEffectPreview(LocalTargetInfo target)
{
// 显示施法范围
float castingRange = parent.verb.verbProps.range;
GenDraw.DrawRadiusRing(parent.pawn.Position, castingRange, Color.white);
// 显示爆炸作用范围
GenDraw.DrawRadiusRing(target.Cell, Props.impactAreaRadius, Color.white);
if (target.IsValid) GenDraw.DrawTargetHighlight(target);
}
}
public class CompProperties_AbilityDRM_LightningBombardment : CompProperties_AbilityEffect
{
public CompProperties_AbilityDRM_LightningBombardment()
{
compClass = typeof(CompAbilityEffect_DRM_LightningBombardment);
}
public float impactAreaRadius = 10f;
public FloatRange explosionRadiusRange = new FloatRange(3f, 4f);
public int bombIntervalTicks = 30;
public int explosionCount = 3;
public DamageDef damageDef;
public int damageAmount = 30;
public float armorPenetration = 0.8f;
public ThingDef postExplosionSpawnThingDef;
public float postExplosionSpawnChance;
public int postExplosionSpawnThingCount;
}
// 地图组件管理轰炸任务
public class MapComponent_LightningBombardment : MapComponent
{
private readonly List<BombardmentCoroutine> activeCoroutines = new List<BombardmentCoroutine>();
private const int MAX_CONCURRENT_BOMBARDMENTS = 5;
public MapComponent_LightningBombardment(Map map) : base(map) { }
public void StartBombardment(
IntVec3 target,
Pawn instigator,
int explosionCount,
int bombIntervalTicks,
float impactAreaRadius,
FloatRange explosionRadiusRange,
DamageDef damageDef,
int damageAmount,
float armorPenetration,
ThingDef postExplosionSpawnThingDef,
float postExplosionSpawnChance,
int postExplosionSpawnThingCount)
{
// 防止过多任务影响性能
if (activeCoroutines.Count >= MAX_CONCURRENT_BOMBARDMENTS)
{
WulaLog.Debug($"Too many concurrent bombardments on map {map}, max is {MAX_CONCURRENT_BOMBARDMENTS}");
return;
}
activeCoroutines.Add(new BombardmentCoroutine(
target: target,
map: map,
instigator: instigator,
explosionCount: explosionCount,
bombIntervalTicks: bombIntervalTicks,
impactAreaRadius: impactAreaRadius,
explosionRadiusRange: explosionRadiusRange,
damageDef: damageDef,
damageAmount: damageAmount,
armorPenetration: armorPenetration,
postExplosionSpawnThingDef: postExplosionSpawnThingDef,
postExplosionSpawnChance: postExplosionSpawnChance,
postExplosionSpawnThingCount: postExplosionSpawnThingCount
));
}
public override void MapComponentTick()
{
try
{
for (int i = activeCoroutines.Count - 1; i >= 0; i--)
{
if (!activeCoroutines[i].Tick())
{
activeCoroutines.RemoveAt(i);
}
}
}
catch (Exception ex)
{
WulaLog.Debug($"Lightning bombardment error: {ex}");
activeCoroutines.Clear();
}
}
}
[StaticConstructorOnStartup]
// 轰炸任务协程
public class BombardmentCoroutine
{
private readonly IntVec3 target;
private readonly Map map;
private readonly Pawn instigator;
private readonly int explosionCount;
private readonly FloatRange explosionRadiusRange;
private readonly float impactAreaRadius;
private readonly int bombIntervalTicks;
public DamageDef damageDef;
public int damageAmount;
public float armorPenetration;
public ThingDef postExplosionSpawnThingDef;
public float postExplosionSpawnChance;
public int postExplosionSpawnThingCount;
private int nextBombTick;
private int explosionsRemaining;
private static readonly Material LightningMat = MatLoader.LoadMat("Weather/LightningBolt", -1);
public BombardmentCoroutine(
IntVec3 target,
Map map,
Pawn instigator,
int explosionCount,
int bombIntervalTicks,
float impactAreaRadius,
FloatRange explosionRadiusRange,
DamageDef damageDef,
int damageAmount,
float armorPenetration,
ThingDef postExplosionSpawnThingDef,
float postExplosionSpawnChance,
int postExplosionSpawnThingCount)
{
this.target = target;
this.map = map;
this.instigator = instigator;
this.explosionCount = explosionCount;
this.bombIntervalTicks = bombIntervalTicks;
this.impactAreaRadius = impactAreaRadius;
this.explosionRadiusRange = explosionRadiusRange;
this.damageDef = damageDef;
this.damageAmount = damageAmount;
this.armorPenetration = armorPenetration;
this.postExplosionSpawnThingDef = postExplosionSpawnThingDef;
this.postExplosionSpawnChance = postExplosionSpawnChance;
this.postExplosionSpawnThingCount = postExplosionSpawnThingCount;
explosionsRemaining = explosionCount;
nextBombTick = Find.TickManager.TicksGame + bombIntervalTicks;
}
public bool Tick()
{
if (Find.TickManager.TicksGame >= nextBombTick)
{
ExecuteBomb();
explosionsRemaining--;
if (explosionsRemaining > 0)
{
nextBombTick += bombIntervalTicks;
}
}
return explosionsRemaining > 0;
}
private void ExecuteBomb()
{
// 在轰炸区域内随机选择目标点
IntVec3 bombTarget = GetRandomCellInRadius(target, map, impactAreaRadius);
// 创建闪电视觉效果
CreateLightningEffect(bombTarget);
// 执行爆炸
GenExplosion.DoExplosion(
center: bombTarget,
map: map,
radius: explosionRadiusRange.RandomInRange,
damType: damageDef,
instigator: instigator,
damAmount: damageAmount,
armorPenetration: armorPenetration,
postExplosionSpawnThingDef: postExplosionSpawnThingDef,
postExplosionSpawnChance: postExplosionSpawnChance,
postExplosionSpawnThingCount: postExplosionSpawnThingCount,
applyDamageToExplosionCellsNeighbors: true,
chanceToStartFire: 0.4f,
damageFalloff: true
);
}
private IntVec3 GetRandomCellInRadius(IntVec3 center, Map map, float radius)
{
if (radius <= 0.0f) return center;
if (CellFinder.TryFindRandomCellNear(
center,
map,
Mathf.CeilToInt(radius),
c => c.DistanceTo(center) <= radius && c.Standable(map),
out IntVec3 result))
{
return result;
}
return center; // 备用方案
}
private void CreateLightningEffect(IntVec3 strikeLoc)
{
if (strikeLoc.Fogged(map)) return;
Vector3 position = strikeLoc.ToVector3Shifted();
// 1. 播放声音
SoundDefOf.Thunder_OffMap.PlayOneShotOnCamera(map);
// 2. 生成粒子效果
for (int i = 0; i < 4; i++)
{
FleckMaker.ThrowSmoke(position, map, 1.5f);
FleckMaker.ThrowMicroSparks(position, map);
FleckMaker.ThrowLightningGlow(position, map, 1.5f);
}
// 3. 绘制闪电网格
Mesh boltMesh = LightningBoltMeshPool.RandomBoltMesh;
Graphics.DrawMesh(
boltMesh,
strikeLoc.ToVector3ShiftedWithAltitude(AltitudeLayer.Weather),
Quaternion.identity,
FadedMaterialPool.FadedVersionOf(LightningMat, 1f),
0
);
// 4. 播放局部雷声
SoundInfo soundInfo = SoundInfo.InMap(new TargetInfo(strikeLoc, map));
SoundDefOf.Thunder_OnMap.PlayOneShot(soundInfo);
}
}
}

View File

@@ -367,7 +367,7 @@ namespace WulaFallenEmpire
Find.BattleLog.Add(battleLogEntry_DamageTaken);
}
DamageInfo damageInfo = new DamageInfo(WulaDamageDefOf.Wula_Dark_Matter_Flame, num, 2f, -1f, instigator, null, weaponDef);
DamageInfo damageInfo = new DamageInfo(Wula_DamageDefOf.Wula_Dark_Matter_Flame, num, 2f, -1f, instigator, null, weaponDef);
tmpThings[i].TakeDamage(damageInfo).AssociateWithLog(battleLogEntry_DamageTaken);
}

View File

@@ -0,0 +1,358 @@
using System.Collections.Generic;
using Verse;
using RimWorld;
using UnityEngine;
using Verse.AI;
namespace WulaFallenEmpire
{
public class CompAbilityEffect_LaunchMultiProjectile : CompAbilityEffect
{
private bool isActive;
private int startTick;
private int nextProjectileTick;
private int projectilesFired;
private IntVec3? targetCell;
private bool parametersInitialized;
// 缓存当前状态的参数
private int currentNumProjectiles;
private ThingDef currentProjectileDef;
private float currentOffsetRadius;
private int currentShotIntervalTicks;
public new CompProperties_AbilityLaunchMultiProjectile Props => (CompProperties_AbilityLaunchMultiProjectile)props;
public bool IsActive => isActive;
public IntVec3? TargetCell => targetCell;
public override void Apply(LocalTargetInfo target, LocalTargetInfo dest)
{
base.Apply(target, dest);
if (parent.pawn == null || parent.pawn.MapHeld == null || !target.IsValid)
{
return;
}
// 初始化参数
InitializeParameters();
// 设置目标
targetCell = target.Cell.ClampInsideMap(parent.pawn.MapHeld);
// 开始发射
isActive = true;
startTick = Find.TickManager.TicksGame;
nextProjectileTick = startTick + Mathf.Max(0, Props.startDelayTicks);
projectilesFired = 0;
// 如果是持续模式需要启动持续Job
if (Props.useSustainedJob)
{
StartSustainedJob(target);
}
}
public override void CompTick()
{
base.CompTick();
if (!isActive)
{
return;
}
// 终止条件检查
if (parent.pawn == null || parent.pawn.Dead || !parent.pawn.Spawned || parent.pawn.MapHeld == null)
{
StopMultiProjectile();
return;
}
// 检查是否还在持续施法状态
if (Props.useSustainedJob && !IsPawnMaintainingJob())
{
StopMultiProjectile();
return;
}
// 检查最大持续时间
if (Props.maxSustainTicks > 0 && (Find.TickManager.TicksGame - startTick) >= Props.maxSustainTicks)
{
StopMultiProjectile();
return;
}
// 检查是否已经发射完所有射弹
if (projectilesFired >= currentNumProjectiles)
{
StopMultiProjectile();
return;
}
int currentTick = Find.TickManager.TicksGame;
// 发射下一发
if (currentTick >= nextProjectileTick)
{
if (targetCell.HasValue)
{
LaunchProjectile(new LocalTargetInfo(targetCell.Value));
}
projectilesFired++;
// 如果还有剩余射弹,设置下一次发射时间
if (projectilesFired < currentNumProjectiles)
{
nextProjectileTick = currentTick + Mathf.Max(1, currentShotIntervalTicks);
}
else
{
// 所有射弹已发射完毕
if (!Props.useSustainedJob)
{
StopMultiProjectile();
}
}
}
}
public override void PostExposeData()
{
base.PostExposeData();
Scribe_Values.Look(ref isActive, "isActive", false);
Scribe_Values.Look(ref startTick, "startTick", 0);
Scribe_Values.Look(ref nextProjectileTick, "nextProjectileTick", 0);
Scribe_Values.Look(ref projectilesFired, "projectilesFired", 0);
if (Scribe.mode == LoadSaveMode.PostLoadInit && isActive)
{
InitializeParameters();
}
}
/// <summary>
/// 初始化当前参数
/// </summary>
private void InitializeParameters()
{
if (parametersInitialized)
return;
var state = GetCurrentState();
currentNumProjectiles = state?.numProjectiles ?? Props.numProjectiles;
currentProjectileDef = state?.projectileDef ?? Props.projectileDef;
currentOffsetRadius = state?.offsetRadius ?? Props.offsetRadius;
currentShotIntervalTicks = state?.shotIntervalTicks ?? Props.shotIntervalTicks;
parametersInitialized = true;
}
/// <summary>
/// 启动持续发射Job
/// </summary>
private void StartSustainedJob(LocalTargetInfo target)
{
// 创建一个持续施法的工作
Job job = JobMaker.MakeJob(Wula_JobDefOf.WULA_Launch_Proj, target);
job.ability = parent;
job.verbToUse = parent.verb;
parent.pawn.jobs.StartJob(job, JobCondition.InterruptForced, null, false, true, null, null, false);
}
/// <summary>
/// 检查pawn是否还在维持发射工作
/// </summary>
private bool IsPawnMaintainingJob()
{
if (parent.pawn?.jobs?.curJob == null)
return false;
var curJob = parent.pawn.jobs.curJob;
return curJob.ability == parent && curJob.def != null && curJob.def.abilityCasting;
}
private void LaunchProjectile(LocalTargetInfo target)
{
if (currentProjectileDef == null)
return;
Pawn pawn = parent.pawn;
LocalTargetInfo finalTarget = target;
// 如果有偏移范围,计算偏移后的目标
if (Props.useRandomOffset && currentOffsetRadius > 0)
{
finalTarget = GetOffsetTarget(target, currentOffsetRadius);
}
Projectile projectile = (Projectile)GenSpawn.Spawn(currentProjectileDef, pawn.Position, pawn.Map);
projectile.Launch(pawn, pawn.DrawPos, finalTarget, finalTarget,
ProjectileHitFlags.IntendedTarget, parent.verb.preventFriendlyFire);
}
/// <summary>
/// 停止多射弹发射
/// </summary>
public void StopMultiProjectile()
{
isActive = false;
startTick = 0;
nextProjectileTick = 0;
projectilesFired = 0;
targetCell = null;
parametersInitialized = false;
}
/// <summary>
/// 获取偏移后的目标点
/// </summary>
private LocalTargetInfo GetOffsetTarget(LocalTargetInfo originalTarget, float offsetRadius)
{
if (!Props.useRandomOffset || offsetRadius <= 0)
return originalTarget;
Vector3 basePos = originalTarget.Cell.ToVector3Shifted();
Map map = parent.pawn.Map;
// 生成随机偏移
Vector3 offset = Vector3.zero;
if (Props.offsetInLineOnly)
{
// 只在线条方向偏移(从施法者到目标的方向)
Vector3 casterPos = parent.pawn.Position.ToVector3Shifted();
Vector3 direction = (basePos - casterPos).normalized;
// 使用offsetRange或offsetRadius
float offsetDistance = Props.offsetRange.RandomInRange;
if (Mathf.Abs(offsetDistance) < Props.minOffsetDistance)
{
offsetDistance = Mathf.Sign(offsetDistance) * Props.minOffsetDistance;
}
offset = direction * offsetDistance;
}
else if (Props.offsetInCircle)
{
// 在圆形范围内随机偏移
float angle = Rand.Range(0f, 360f);
float distance = Rand.Range(0f, offsetRadius);
// 确保最小距离
distance = Mathf.Max(distance, Props.minOffsetDistance);
offset = new Vector3(
Mathf.Cos(angle * Mathf.Deg2Rad) * distance,
0f,
Mathf.Sin(angle * Mathf.Deg2Rad) * distance
);
}
else
{
// 在矩形范围内随机偏移
offset = new Vector3(
Props.offsetRange.RandomInRange,
0f,
Props.offsetRange.RandomInRange
);
// 限制最大偏移距离
if (offsetRadius > 0 && offset.magnitude > offsetRadius)
{
offset = offset.normalized * offsetRadius;
}
}
// 计算最终目标点
IntVec3 targetCell = (basePos + offset).ToIntVec3();
targetCell = targetCell.ClampInsideMap(map);
return new LocalTargetInfo(targetCell);
}
/// <summary>
/// 绘制效果预览(显示偏移范围)
/// </summary>
public override void DrawEffectPreview(LocalTargetInfo target)
{
base.DrawEffectPreview(target);
float currentRadius = GetCurrentOffsetRadius();
if (Props.useRandomOffset && currentRadius > 0)
{
// 绘制偏移范围
GenDraw.DrawRadiusRing(target.Cell, currentRadius);
}
}
public override bool AICanTargetNow(LocalTargetInfo target)
{
return target.Pawn != null;
}
#region
/// <summary>
/// 获取当前状态的射弹数量
/// </summary>
private int GetCurrentNumProjectiles()
{
var state = GetCurrentState();
return state?.numProjectiles ?? Props.numProjectiles;
}
/// <summary>
/// 获取当前状态的射弹类型
/// </summary>
private ThingDef GetCurrentProjectileDef()
{
var state = GetCurrentState();
return state?.projectileDef ?? Props.projectileDef;
}
/// <summary>
/// 获取当前状态的偏移半径
/// </summary>
private float GetCurrentOffsetRadius()
{
var state = GetCurrentState();
return state?.offsetRadius ?? Props.offsetRadius;
}
/// <summary>
/// 获取当前状态的发射间隔
/// </summary>
private int GetCurrentShotIntervalTicks()
{
var state = GetCurrentState();
return state?.shotIntervalTicks ?? Props.shotIntervalTicks;
}
/// <summary>
/// 获取当前状态基于施法者的Hediff
/// </summary>
private ProjectileState GetCurrentState()
{
if (parent.pawn == null || parent.pawn.health?.hediffSet == null || Props.states == null)
return null;
// 检查施法者是否有匹配的Hediff
foreach (var state in Props.states)
{
if (state.hediffDef != null && parent.pawn.health.hediffSet.HasHediff(state.hediffDef))
{
return state;
}
}
return null;
}
#endregion
}
}

View File

@@ -0,0 +1,47 @@
using Verse;
using RimWorld;
using System.Collections.Generic;
namespace WulaFallenEmpire
{
public class CompProperties_AbilityLaunchMultiProjectile : CompProperties_AbilityLaunchProjectile
{
public int numProjectiles = 1;
// 发射间隔控制
public int shotIntervalTicks = 0; // 每两发射弹的间隔时间tick
public bool useSustainedJob = false; // 是否使用持续Job
// 偏移范围
public float offsetRadius = 0f;
public bool useRandomOffset = false;
public FloatRange offsetRange = new FloatRange(-1f, 1f);
public bool offsetInLineOnly = false;
public bool offsetInCircle = true;
public float minOffsetDistance = 0f;
public bool avoidOverlap = false;
public float minProjectileSpacing = 1f;
// 持续发射控制
public int maxSustainTicks = 180; // 最大持续时间(参考火焰技能)
public int startDelayTicks = 10; // 开始发射前的延迟
// 状态定义
public List<ProjectileState> states;
public CompProperties_AbilityLaunchMultiProjectile()
{
compClass = typeof(CompAbilityEffect_LaunchMultiProjectile);
}
}
// 射弹状态参数
public class ProjectileState
{
public HediffDef hediffDef; // 触发的hediff
public int numProjectiles = 1; // 射弹数量(可选)
public ThingDef projectileDef; // 射弹类型(可选)
public float offsetRadius = 0f; // 偏移半径(可选)
public int shotIntervalTicks = 0; // 发射间隔(可选)
}
}

View File

@@ -0,0 +1,115 @@
using System.Collections.Generic;
using Verse;
using Verse.AI;
namespace WulaFallenEmpire
{
public class JobDriver_CastAbilityMaintainMultiProjectile : JobDriver_CastAbility
{
private CompAbilityEffect_LaunchMultiProjectile MultiProjectileComp
{
get
{
if (job?.ability?.EffectComps == null)
{
return null;
}
foreach (var comp in job.ability.EffectComps)
{
if (comp is CompAbilityEffect_LaunchMultiProjectile multiComp)
{
return multiComp;
}
}
return null;
}
}
protected override IEnumerable<Toil> MakeNewToils()
{
this.FailOnDespawnedOrNull(TargetIndex.A);
this.FailOn(() => job.ability == null || (!job.ability.CanCast && !job.ability.Casting));
AddFinishAction(delegate
{
if (job.ability != null && job.def.abilityCasting)
{
job.ability.StartCooldown(job.ability.def.cooldownTicksRange.RandomInRange);
}
// 停止多射弹发射
MultiProjectileComp?.StopMultiProjectile();
});
// 停止移动
Toil stopToil = ToilMaker.MakeToil("StopBeforeMultiProjectileCast");
stopToil.initAction = delegate
{
pawn.pather.StopDead();
};
stopToil.defaultCompleteMode = ToilCompleteMode.Instant;
yield return stopToil;
// 施法动作
Toil castToil = Toils_Combat.CastVerb(TargetIndex.A, TargetIndex.B, canHitNonTargetPawns: false);
if (job.ability != null && job.ability.def.showCastingProgressBar && job.verbToUse != null)
{
castToil.WithProgressBar(TargetIndex.A, () => job.verbToUse.WarmupProgress);
}
yield return castToil;
// 持续发射阶段
Toil maintainToil = ToilMaker.MakeToil("MaintainMultiProjectile");
maintainToil.initAction = delegate
{
pawn.pather.StopDead();
rotateToFace = TargetIndex.A;
// 如果组件有目标单元格更新job的目标
var multiComp = MultiProjectileComp;
if (multiComp != null && multiComp.TargetCell.HasValue)
{
job.targetA = new LocalTargetInfo(multiComp.TargetCell.Value);
}
};
maintainToil.tickAction = delegate
{
pawn.pather.StopDead();
var multiComp = MultiProjectileComp;
if (multiComp == null || !multiComp.IsActive)
{
EndJobWith(JobCondition.Succeeded);
return;
}
// 更新目标(如果有必要)- 修正类型转换
if (multiComp.TargetCell.HasValue && multiComp.TargetCell.Value.IsValid)
{
// 将 IntVec3? 转换为 LocalTargetInfo
job.targetA = new LocalTargetInfo(multiComp.TargetCell.Value);
}
// 继续发射射弹通过组件的Tick方法
// 组件会在其CompTick中处理发射逻辑
};
maintainToil.FailOn(() => pawn.Dead || pawn.Downed || !pawn.Spawned);
maintainToil.handlingFacing = true;
maintainToil.defaultCompleteMode = ToilCompleteMode.Never;
yield return maintainToil;
}
public override string GetReport()
{
if (job?.ability != null)
{
return "UsingVerbNoTarget".Translate(job.verbToUse.ReportLabel);
}
return base.GetReport();
}
}
}