Files
2026-03-03 17:30:02 +08:00

1021 lines
33 KiB
C#
Raw Permalink Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
using System;
using System.Collections.Generic;
using System.Linq;
using RimWorld;
using UnityEngine;
using Verse;
using Verse.AI;
using Verse.Sound;
namespace WulaFallenEmpire
{
/// <summary>
/// 高速移动撞击组件
/// </summary>
public class CompHighSpeedCollision : ThingComp
{
// === 运行时状态 ===
private enum SpeedStage
{
Stage0, // 0阶段不移动
Stage1, // 1阶段低速碰撞
Stage2 // 2阶段高速击飞
}
private SpeedStage currentStage = SpeedStage.Stage0;
private int stageTransitionCooldown = 0;
// 用于计算速度的帧历史
private Queue<float> speedHistory = new Queue<float>();
private IntVec3 lastPosition = IntVec3.Invalid;
private int lastPositionTick = -1;
// 已处理的敌人记录(避免同一帧重复处理)
private HashSet<Pawn> processedPawnsThisTick = new HashSet<Pawn>();
// === 缓存 ===
private CellRect collisionAreaCache = default;
private int lastAreaRecalculationTick = -1;
// === 地形阻挡相关 ===
private float currentTerrainBlockSeverity = 0f;
private int lastTerrainCheckTick = -1;
public CompProperties_HighSpeedCollision Props => (CompProperties_HighSpeedCollision)props;
public override void PostSpawnSetup(bool respawningAfterLoad)
{
base.PostSpawnSetup(respawningAfterLoad);
// 初始化速度历史
speedHistory.Clear();
for (int i = 0; i < Props.speedHistoryFrameCount; i++)
{
speedHistory.Enqueue(0f);
}
lastPosition = parent.Position;
lastPositionTick = Find.TickManager.TicksGame;
}
public override void CompTick()
{
base.CompTick();
if (!parent.Spawned || parent.Destroyed)
return;
Pawn pawn = parent as Pawn;
if (pawn == null || pawn.Dead || pawn.Downed)
{
ResetToStage0();
return;
}
// 每帧更新
ProcessFrame(pawn);
// 地形阻挡检查(降低频率)
if (Props.narrowTerrainBlocked &&
(Find.TickManager.TicksGame - lastTerrainCheckTick >= Props.terrainCheckInterval))
{
UpdateTerrainBlockStatus(pawn);
lastTerrainCheckTick = Find.TickManager.TicksGame;
}
}
/// <summary>
/// 更新地形阻挡状态
/// </summary>
private void UpdateTerrainBlockStatus(Pawn pawn)
{
if (!Props.narrowTerrainBlocked || Props.maxBlockPenalty <= 0f)
{
currentTerrainBlockSeverity = 0f;
UpdateBlockedHediff(pawn, 0f);
return;
}
// 获取碰撞区域
CellRect collisionArea = GetCollisionArea(pawn);
// 统计空闲格子和被占据格子
int totalCells = 0;
int occupiedCells = 0;
foreach (IntVec3 cell in collisionArea)
{
if (!cell.InBounds(pawn.Map))
continue;
totalCells++;
// 检查是否被不可通行建筑占据
if (IsCellBlockedByImpassable(cell, pawn.Map))
{
occupiedCells++;
}
}
// 计算阻挡严重度
float blockSeverity = CalculateBlockSeverity(totalCells, occupiedCells);
// 立即设置阻挡严重度(移除平滑过渡)
currentTerrainBlockSeverity = blockSeverity;
// 更新Hediff
UpdateBlockedHediff(pawn, currentTerrainBlockSeverity);
// 调试日志
if (Props.enableDebugLogging && currentTerrainBlockSeverity > 0.01f)
{
Log.Message($"[HighSpeedCollision] Terrain Block: {pawn.Label}, " +
$"Occupied: {occupiedCells}/{totalCells}, " +
$"Severity: {currentTerrainBlockSeverity:P0}");
}
}
/// <summary>
/// 检查单元格是否被不可通行建筑占据
/// </summary>
private bool IsCellBlockedByImpassable(IntVec3 cell, Map map)
{
// 获取单元格内的所有建筑
var things = cell.GetThingList(map);
foreach (var thing in things)
{
if (thing is Building building)
{
// 检查是否可通行
if (building.def.passability == Traversability.Impassable)
{
return true;
}
}
}
return false;
}
/// <summary>
/// 计算阻挡严重度
/// </summary>
private float CalculateBlockSeverity(int totalCells, int occupiedCells)
{
if (totalCells == 0 || occupiedCells == 0)
return 0f;
int freeCells = totalCells - occupiedCells;
float freeRatio = (float)freeCells / totalCells;
// 使用公式:(1 - maxBlockPenalty) + maxBlockPenalty * (空闲格子/(空闲格子+被占据格子))
// 简化后1 - maxBlockPenalty + maxBlockPenalty * (freeCells / totalCells)
// 但实际上,我们应该计算减速比例,然后转换为严重度
float speedMultiplier = (1f - Props.maxBlockPenalty) +
Props.maxBlockPenalty * (freeRatio);
// 严重度 = 1 - 速度乘数
return 1f - speedMultiplier;
}
/// <summary>
/// 平滑过渡
/// </summary>
private float SmoothTransition(float current, float target, float speed)
{
if (Mathf.Abs(current - target) < 0.01f)
return target;
return Mathf.Lerp(current, target, speed);
}
/// <summary>
/// 更新阻挡Hediff
/// </summary>
private void UpdateBlockedHediff(Pawn pawn, float severity)
{
if (Props.blockedHediff == null)
return;
// 获取或添加Hediff
Hediff hediff = pawn.health.hediffSet.GetFirstHediffOfDef(Props.blockedHediff);
if (severity <= 0.01f)
{
// 移除Hediff
if (hediff != null)
{
pawn.health.RemoveHediff(hediff);
}
return;
}
// 添加或更新Hediff
if (hediff == null)
{
hediff = HediffMaker.MakeHediff(Props.blockedHediff, pawn);
pawn.health.AddHediff(hediff);
}
// 更新严重度
hediff.Severity = severity;
}
/// <summary>
/// 获取当前地形阻挡严重度供HediffComp使用
/// </summary>
public float GetCurrentTerrainBlockSeverity()
{
return currentTerrainBlockSeverity;
}
/// <summary>
/// 处理每帧逻辑
/// </summary>
private void ProcessFrame(Pawn pawn)
{
int currentTick = Find.TickManager.TicksGame;
// 1. 计算当前速度
float currentSpeed = CalculateCurrentSpeed(pawn, currentTick);
// 2. 更新速度历史
UpdateSpeedHistory(currentSpeed);
// 3. 计算平均速度
float averageSpeed = GetAverageSpeed();
// 4. 确定阶段
DetermineSpeedStage(averageSpeed);
// 5. 根据阶段应用效果
ApplyStageEffects(pawn);
// 6. 清理每帧记录
processedPawnsThisTick.Clear();
// 7. 更新冷却
if (stageTransitionCooldown > 0)
stageTransitionCooldown--;
// 8. 调试绘制
if (Props.enableDebugVisuals && DebugSettings.godMode)
DrawDebugVisuals(pawn);
}
/// <summary>
/// 计算当前速度(每秒格数)
/// </summary>
private float CalculateCurrentSpeed(Pawn pawn, int currentTick)
{
// 如果没有上次位置记录,无法计算速度
if (lastPositionTick < 0 || lastPosition == IntVec3.Invalid)
{
lastPosition = pawn.Position;
lastPositionTick = currentTick;
return 0f;
}
// 计算时间差(秒)
float timeDelta = (currentTick - lastPositionTick) / 60f;
if (timeDelta <= 0f)
return 0f;
// 计算距离(格数)
float distance = pawn.Position.DistanceTo(lastPosition);
// 计算速度(格/秒)
float speed = distance / timeDelta;
// 更新记录
lastPosition = pawn.Position;
lastPositionTick = currentTick;
return speed;
}
/// <summary>
/// 更新速度历史
/// </summary>
private void UpdateSpeedHistory(float currentSpeed)
{
speedHistory.Enqueue(currentSpeed);
while (speedHistory.Count > Props.speedHistoryFrameCount)
{
speedHistory.Dequeue();
}
}
/// <summary>
/// 获取平均速度
/// </summary>
private float GetAverageSpeed()
{
if (speedHistory.Count == 0)
return 0f;
float sum = 0f;
foreach (float speed in speedHistory)
{
sum += speed;
}
return sum / speedHistory.Count;
}
/// <summary>
/// 确定速度阶段
/// </summary>
private void DetermineSpeedStage(float averageSpeed)
{
// 如果有冷却,保持当前阶段
if (stageTransitionCooldown > 0)
return;
SpeedStage newStage;
if (averageSpeed <= Props.minSpeedForStage1)
{
newStage = SpeedStage.Stage0;
}
else if (averageSpeed >= Props.minSpeedForStage2)
{
newStage = SpeedStage.Stage2;
}
else
{
newStage = SpeedStage.Stage1;
}
// 阶段变化时设置冷却
if (newStage != currentStage)
{
currentStage = newStage;
stageTransitionCooldown = Props.stageTransitionCooldownTicks;
if (Props.enableDebugLogging)
{
Log.Message($"[HighSpeedCollision] {parent.Label} transitioned to Stage {(int)currentStage} " +
$"at speed {averageSpeed:F2} cells/sec");
}
}
}
/// <summary>
/// 应用阶段效果
/// </summary>
private void ApplyStageEffects(Pawn pawn)
{
if (currentStage == SpeedStage.Stage0)
return;
// 获取碰撞区域内的所有敌人
List<Pawn> enemiesInArea = GetEnemiesInCollisionArea(pawn);
foreach (Pawn enemy in enemiesInArea)
{
if (enemy == null || enemy.Destroyed || enemy.Dead || processedPawnsThisTick.Contains(enemy))
continue;
switch (currentStage)
{
case SpeedStage.Stage1:
ApplyStage1Effects(pawn, enemy);
break;
case SpeedStage.Stage2:
ApplyStage2Effects(pawn, enemy);
break;
}
processedPawnsThisTick.Add(enemy);
}
}
/// <summary>
/// 应用阶段1效果伤害+hediff
/// </summary>
private void ApplyStage1Effects(Pawn attacker, Pawn target)
{
// 检查目标是否已有hediff
bool alreadyHasHediff = target.health.hediffSet.HasHediff(Props.stage1Hediff);
// 如果已有hediff不造成伤害
if (alreadyHasHediff && Props.stage1HediffPreventsDamage)
return;
// 造成伤害
if (Props.stage1DamageAmount > 0f)
{
ApplyDamage(attacker, target, Props.stage1DamageAmount, Props.stage1DamageDef);
}
// 应用hediff
if (Props.stage1Hediff != null)
{
Hediff hediff = HediffMaker.MakeHediff(Props.stage1Hediff, target);
if (Props.stage1HediffDurationTicks > 0)
{
hediff.Severity = 1f;
hediff.TryGetComp<HediffComp_Disappears>()?.CompPostMake();
}
target.health.AddHediff(hediff);
}
// 播放效果
PlayStage1Effects(attacker, target);
if (Props.enableDebugLogging)
{
Log.Message($"[HighSpeedCollision] Stage1: {attacker.Label} -> {target.Label}, " +
$"Damage: {Props.stage1DamageAmount}, Hediff: {Props.stage1Hediff?.defName}");
}
}
/// <summary>
/// 应用阶段2效果伤害+击飞)
/// </summary>
private void ApplyStage2Effects(Pawn attacker, Pawn target)
{
// 造成伤害
if (Props.stage2DamageAmount > 0f)
{
ApplyDamage(attacker, target, Props.stage2DamageAmount, Props.stage2DamageDef);
}
// 执行击飞
PerformKnockback(attacker, target);
// 播放效果
PlayStage2Effects(attacker, target);
if (Props.enableDebugLogging)
{
Log.Message($"[HighSpeedCollision] Stage2: {attacker.Label} -> {target.Label}, " +
$"Damage: {Props.stage2DamageAmount}, Knockback");
}
}
/// <summary>
/// 执行击飞参考CompAbilityEffect_FanShapedStunKnockback
/// </summary>
private void PerformKnockback(Pawn attacker, Pawn target)
{
if (target == null || target.Destroyed || target.Dead || attacker.Map == null)
return;
// 计算击飞方向(从攻击者指向目标)
IntVec3 knockbackDirection = CalculateKnockbackDirection(attacker, target.Position);
// 寻找击飞位置
IntVec3 knockbackDestination = FindKnockbackDestination(attacker, target, knockbackDirection);
// 如果找到有效位置,执行击飞
if (knockbackDestination.IsValid && knockbackDestination != target.Position)
{
CreateKnockbackFlyer(attacker, target, knockbackDestination);
}
}
/// <summary>
/// 计算击飞方向
/// </summary>
private IntVec3 CalculateKnockbackDirection(Pawn attacker, IntVec3 targetPosition)
{
IntVec3 direction = targetPosition - attacker.Position;
// 标准化方向
if (direction.x != 0 || direction.z != 0)
{
if (Mathf.Abs(direction.x) > Mathf.Abs(direction.z))
{
return new IntVec3(Mathf.Sign(direction.x) > 0 ? 1 : -1, 0, 0);
}
else
{
return new IntVec3(0, 0, Mathf.Sign(direction.z) > 0 ? 1 : -1);
}
}
// 如果攻击者和目标在同一位置,使用随机方向
return new IntVec3(Rand.Value > 0.5f ? 1 : -1, 0, 0);
}
/// <summary>
/// 寻找击飞位置
/// </summary>
private IntVec3 FindKnockbackDestination(Pawn attacker, Pawn target, IntVec3 direction)
{
Map map = attacker.Map;
IntVec3 currentPos = target.Position;
// 从最大距离开始向回找
for (int distance = Props.stage2KnockbackDistance; distance >= 1; distance--)
{
IntVec3 testPos = currentPos + (direction * distance);
if (!IsValidKnockbackDestination(testPos, map, target, attacker))
continue;
return testPos;
}
return currentPos;
}
/// <summary>
/// 检查击飞位置是否有效
/// </summary>
private bool IsValidKnockbackDestination(IntVec3 destination, Map map, Pawn victim, Pawn attacker)
{
if (!destination.IsValid || !destination.InBounds(map))
return false;
if (!destination.Standable(map))
return false;
// 检查是否有其他pawn
Pawn existingPawn = destination.GetFirstPawn(map);
if (existingPawn != null && existingPawn != victim)
return false;
// 检查视线
if (Props.requireLineOfSightForKnockback && !GenSight.LineOfSight(victim.Position, destination, map))
return false;
return true;
}
/// <summary>
/// 创建击飞飞行器
/// </summary>
private void CreateKnockbackFlyer(Pawn attacker, Pawn target, IntVec3 destination)
{
try
{
Map map = attacker.Map;
// 使用自定义飞行器或默认飞行器
ThingDef flyerDef = Props.knockbackFlyerDef ?? ThingDefOf.PawnFlyer;
// 创建飞行器
PawnFlyer flyer = PawnFlyer.MakeFlyer(
flyerDef,
target,
destination,
Props.flightEffecterDef,
Props.landingSound,
false,
null,
null,
new LocalTargetInfo(destination)
);
if (flyer != null)
{
GenSpawn.Spawn(flyer, destination, map);
}
}
catch (Exception ex)
{
Log.Error($"[HighSpeedCollision] Exception creating PawnFlyer: {ex}");
}
}
/// <summary>
/// 应用伤害
/// </summary>
private void ApplyDamage(Pawn attacker, Pawn target, float amount, DamageDef damageDef)
{
if (amount <= 0f || damageDef == null)
return;
DamageInfo damageInfo = new DamageInfo(
damageDef,
amount,
Props.armorPenetration,
-1f,
attacker,
null
);
target.TakeDamage(damageInfo);
}
/// <summary>
/// 播放阶段1效果
/// </summary>
private void PlayStage1Effects(Pawn attacker, Pawn target)
{
if (Props.stage1Effecter != null && attacker.Map != null)
{
Effecter effect = Props.stage1Effecter.Spawn();
effect.Trigger(new TargetInfo(attacker.Position, attacker.Map),
new TargetInfo(target.Position, attacker.Map));
effect.Cleanup();
}
if (Props.stage1Sound != null && attacker.Map != null)
{
Props.stage1Sound.PlayOneShot(new TargetInfo(target.Position, attacker.Map));
}
}
/// <summary>
/// 播放阶段2效果
/// </summary>
private void PlayStage2Effects(Pawn attacker, Pawn target)
{
if (Props.stage2Effecter != null && attacker.Map != null)
{
Effecter effect = Props.stage2Effecter.Spawn();
effect.Trigger(new TargetInfo(attacker.Position, attacker.Map),
new TargetInfo(target.Position, attacker.Map));
effect.Cleanup();
}
if (Props.stage2Sound != null && attacker.Map != null)
{
Props.stage2Sound.PlayOneShot(new TargetInfo(target.Position, attacker.Map));
}
}
/// <summary>
/// 获取碰撞区域内的所有敌人
/// </summary>
private List<Pawn> GetEnemiesInCollisionArea(Pawn pawn)
{
List<Pawn> enemies = new List<Pawn>();
// 获取碰撞区域
CellRect collisionArea = GetCollisionArea(pawn);
// 检查区域内的每个单元格
foreach (IntVec3 cell in collisionArea)
{
if (!cell.InBounds(pawn.Map))
continue;
// 获取单元格内的所有pawn
List<Thing> things = cell.GetThingList(pawn.Map);
foreach (Thing thing in things)
{
if (thing is Pawn otherPawn && otherPawn != pawn)
{
// 检查是否为敌人
if (IsValidTarget(pawn, otherPawn))
{
enemies.Add(otherPawn);
}
}
}
}
return enemies;
}
/// <summary>
/// 获取碰撞区域
/// </summary>
private CellRect GetCollisionArea(Pawn pawn)
{
int currentTick = Find.TickManager.TicksGame;
// 每10帧重新计算一次区域或当位置变化时
if (currentTick - lastAreaRecalculationTick > 10 ||
pawn.Position != collisionAreaCache.CenterCell)
{
int radius = Props.collisionAreaRadius;
IntVec3 center = pawn.Position;
collisionAreaCache = new CellRect(
center.x - radius,
center.z - radius,
radius * 2 + 1,
radius * 2 + 1
);
collisionAreaCache.ClipInsideMap(pawn.Map);
lastAreaRecalculationTick = currentTick;
}
return collisionAreaCache;
}
/// <summary>
/// 检查是否是有效目标
/// </summary>
private bool IsValidTarget(Pawn attacker, Pawn target)
{
if (target == null || target.Destroyed || target.Dead)
return false;
// 检查是否为敌人
if (Props.onlyAffectEnemies && !target.HostileTo(attacker))
return false;
// 检查是否排除友方
if (Props.excludeAlliedPawns && target.Faction == attacker.Faction)
return false;
// 检查是否排除中立
if (Props.excludeNeutralPawns && !target.HostileTo(attacker))
return false;
return true;
}
/// <summary>
/// 检查Pawn是否可以移动
/// </summary>
private bool CanMove(Pawn pawn)
{
if (pawn.Downed || pawn.Dead || pawn.InMentalState)
return false;
if (pawn.stances?.stunner?.Stunned ?? false)
return false;
return true;
}
/// <summary>
/// 重置到阶段0
/// </summary>
private void ResetToStage0()
{
currentStage = SpeedStage.Stage0;
// 清空速度历史
speedHistory.Clear();
for (int i = 0; i < Props.speedHistoryFrameCount; i++)
{
speedHistory.Enqueue(0f);
}
}
/// <summary>
/// 绘制调试视觉效果
/// </summary>
private void DrawDebugVisuals(Pawn pawn)
{
if (!pawn.Spawned)
return;
// 绘制碰撞区域
CellRect area = GetCollisionArea(pawn);
GenDraw.DrawFieldEdges(area.Cells.ToList(), GetStageColor(currentStage));
// 绘制速度指示器
float averageSpeed = GetAverageSpeed();
string speedText = $"Speed: {averageSpeed:F1} cells/sec\nStage: {(int)currentStage}";
Vector3 drawPos = pawn.DrawPos + new Vector3(0, 0, 1f);
GenMapUI.DrawText(drawPos, speedText, GetStageColor(currentStage));
}
/// <summary>
/// 获取阶段颜色
/// </summary>
private Color GetStageColor(SpeedStage stage)
{
switch (stage)
{
case SpeedStage.Stage0: return Color.gray;
case SpeedStage.Stage1: return Color.yellow;
case SpeedStage.Stage2: return Color.red;
default: return Color.white;
}
}
/// <summary>
/// 获取调试信息
/// </summary>
public string GetDebugInfo()
{
float averageSpeed = GetAverageSpeed();
return $"HighSpeedCollision Debug Info:\n" +
$"Current Stage: {(int)currentStage}\n" +
$"Average Speed: {averageSpeed:F2} cells/sec\n" +
$"Stage 1 Threshold: {Props.minSpeedForStage1:F2}\n" +
$"Stage 2 Threshold: {Props.minSpeedForStage2:F2}\n" +
$"Speed History: {speedHistory.Count} frames\n" +
$"Stage Cooldown: {stageTransitionCooldown} ticks";
}
public override void PostExposeData()
{
base.PostExposeData();
Scribe_Values.Look(ref currentStage, "currentStage", SpeedStage.Stage0);
Scribe_Values.Look(ref stageTransitionCooldown, "stageTransitionCooldown", 0);
Scribe_Values.Look(ref lastPosition, "lastPosition", IntVec3.Invalid);
Scribe_Values.Look(ref lastPositionTick, "lastPositionTick", -1);
// 保存速度历史
if (Scribe.mode == LoadSaveMode.Saving)
{
List<float> speedList = speedHistory.ToList();
Scribe_Collections.Look(ref speedList, "speedHistory", LookMode.Value);
}
else if (Scribe.mode == LoadSaveMode.LoadingVars)
{
List<float> speedList = null;
Scribe_Collections.Look(ref speedList, "speedHistory", LookMode.Value);
speedHistory.Clear();
if (speedList != null)
{
foreach (float speed in speedList)
{
speedHistory.Enqueue(speed);
}
}
// 确保有足够的历史数据
while (speedHistory.Count < Props.speedHistoryFrameCount)
{
speedHistory.Enqueue(0f);
}
}
}
}
/// <summary>
/// 高速移动撞击组件属性
/// </summary>
public class CompProperties_HighSpeedCollision : CompProperties
{
// === 速度阈值配置 ===
/// <summary>
/// 进入阶段1所需的最小速度格/秒)
/// </summary>
public float minSpeedForStage1 = 3f;
/// <summary>
/// 进入阶段2所需的最小速度格/秒)
/// </summary>
public float minSpeedForStage2 = 6f;
/// <summary>
/// 速度历史帧数(用于计算平均速度)
/// </summary>
public int speedHistoryFrameCount = 10;
/// <summary>
/// 阶段转换冷却时间tick
/// </summary>
public int stageTransitionCooldownTicks = 5;
// === 碰撞区域配置 ===
/// <summary>
/// 碰撞区域半径以pawn为中心的正方形
/// </summary>
public int collisionAreaRadius = 1;
// === 目标过滤 ===
/// <summary>
/// 只影响敌人
/// </summary>
public bool onlyAffectEnemies = true;
/// <summary>
/// 排除友方单位
/// </summary>
public bool excludeAlliedPawns = true;
/// <summary>
/// 排除中立单位
/// </summary>
public bool excludeNeutralPawns = false;
// === 阶段1效果配置 ===
/// <summary>
/// 阶段1伤害类型
/// </summary>
public DamageDef stage1DamageDef = DamageDefOf.Blunt;
/// <summary>
/// 阶段1伤害量
/// </summary>
public float stage1DamageAmount = 5f;
/// <summary>
/// 阶段1护甲穿透
/// </summary>
public float armorPenetration = 0f;
/// <summary>
/// 阶段1应用的hediff
/// </summary>
public HediffDef stage1Hediff;
/// <summary>
/// 阶段1hediff持续时间tick
/// </summary>
public int stage1HediffDurationTicks = 60;
/// <summary>
/// 拥有hediff的目标是否免疫伤害
/// </summary>
public bool stage1HediffPreventsDamage = true;
/// <summary>
/// 阶段1效果器
/// </summary>
public EffecterDef stage1Effecter;
/// <summary>
/// 阶段1音效
/// </summary>
public SoundDef stage1Sound;
// === 阶段2效果配置 ===
/// <summary>
/// 阶段2伤害类型
/// </summary>
public DamageDef stage2DamageDef = DamageDefOf.Blunt;
/// <summary>
/// 阶段2伤害量
/// </summary>
public float stage2DamageAmount = 10f;
/// <summary>
/// 阶段2击退距离
/// </summary>
public int stage2KnockbackDistance = 3;
/// <summary>
/// 击退是否需要视线
/// </summary>
public bool requireLineOfSightForKnockback = true;
/// <summary>
/// 阶段2效果器
/// </summary>
public EffecterDef stage2Effecter;
/// <summary>
/// 阶段2音效
/// </summary>
public SoundDef stage2Sound;
// === 击飞配置 ===
/// <summary>
/// 击退飞行器定义
/// </summary>
public ThingDef knockbackFlyerDef;
/// <summary>
/// 飞行效果器
/// </summary>
public EffecterDef flightEffecterDef;
/// <summary>
/// 落地音效
/// </summary>
public SoundDef landingSound;
// === 调试配置 ===
/// <summary>
/// 启用调试日志
/// </summary>
public bool enableDebugLogging = false;
/// <summary>
/// 启用调试视觉效果
/// </summary>
public bool enableDebugVisuals = false;
/// <summary>
/// 绘制速度历史图
/// </summary>
public bool debugDrawSpeedHistory = false;
// === 狭窄地形阻挡配置 ===
/// <summary>
/// 是否启用狭窄地形阻挡
/// </summary>
public bool narrowTerrainBlocked = false;
/// <summary>
/// 最高阻挡减益值0-1之间
/// </summary>
public float maxBlockPenalty = 0.5f;
/// <summary>
/// 地形检查间隔ticks
/// </summary>
public int terrainCheckInterval = 60;
/// <summary>
/// 阻挡减益Hediff定义
/// </summary>
public HediffDef blockedHediff;
public CompProperties_HighSpeedCollision()
{
compClass = typeof(CompHighSpeedCollision);
}
}
}