412 lines
16 KiB
C#
412 lines
16 KiB
C#
using System.Collections.Generic;
|
|
using RimWorld;
|
|
using UnityEngine;
|
|
using Verse;
|
|
|
|
namespace WulaFallenEmpire
|
|
{
|
|
public class CompGroundStrafing : ThingComp
|
|
{
|
|
public CompProperties_GroundStrafing Props => (CompProperties_GroundStrafing)props;
|
|
|
|
// 简化的扫射状态
|
|
private List<IntVec3> confirmedTargetCells = new List<IntVec3>();
|
|
private HashSet<IntVec3> firedCells = new HashSet<IntVec3>();
|
|
|
|
// 横向偏移状态(左右)
|
|
private float currentLateralOffsetAngle = 0f;
|
|
private int shotsFired = 0;
|
|
private Vector3 lastProjectileDirection = Vector3.zero;
|
|
|
|
// 新增:纵向偏移状态(前后)
|
|
private float currentLongitudinalOffset = 0f; // 当前纵向偏移距离
|
|
private bool isForwardPhase = true; // 是否处于向前偏移阶段
|
|
|
|
public override void PostSpawnSetup(bool respawningAfterLoad)
|
|
{
|
|
base.PostSpawnSetup(respawningAfterLoad);
|
|
|
|
// 初始化偏移
|
|
if (!respawningAfterLoad)
|
|
{
|
|
currentLateralOffsetAngle = Props.lateralInitialOffsetAngle;
|
|
currentLongitudinalOffset = Props.longitudinalInitialOffset;
|
|
}
|
|
|
|
Log.Message($"GroundStrafing: Initialized with {confirmedTargetCells.Count} targets, " +
|
|
$"Lateral Offset: {currentLateralOffsetAngle:F1}°, " +
|
|
$"Longitudinal Offset: {currentLongitudinalOffset:F1}");
|
|
}
|
|
|
|
public override void CompTick()
|
|
{
|
|
base.CompTick();
|
|
|
|
if (confirmedTargetCells.Count == 0)
|
|
{
|
|
return;
|
|
}
|
|
|
|
CheckAndFireAtTargets();
|
|
|
|
// 定期状态输出
|
|
if (Find.TickManager.TicksGame % 120 == 0 && confirmedTargetCells.Count > 0)
|
|
{
|
|
Log.Message($"GroundStrafing: {firedCells.Count}/{confirmedTargetCells.Count + firedCells.Count} targets fired, " +
|
|
$"Lateral: {currentLateralOffsetAngle:F1}°, Longitudinal: {currentLongitudinalOffset:F1}");
|
|
}
|
|
}
|
|
|
|
private void CheckAndFireAtTargets()
|
|
{
|
|
Vector3 currentPos = parent.DrawPos;
|
|
|
|
for (int i = confirmedTargetCells.Count - 1; i >= 0; i--)
|
|
{
|
|
IntVec3 targetCell = confirmedTargetCells[i];
|
|
|
|
if (firedCells.Contains(targetCell))
|
|
{
|
|
confirmedTargetCells.RemoveAt(i);
|
|
continue;
|
|
}
|
|
|
|
float horizontalDistance = GetHorizontalDistance(currentPos, targetCell);
|
|
if (horizontalDistance <= Props.range)
|
|
{
|
|
if (LaunchProjectileAt(targetCell))
|
|
{
|
|
firedCells.Add(targetCell);
|
|
confirmedTargetCells.RemoveAt(i);
|
|
|
|
// 更新所有偏移参数
|
|
UpdateOffsets();
|
|
|
|
if (firedCells.Count == 1)
|
|
{
|
|
Log.Message($"First strafing shot at {targetCell}, " +
|
|
$"Lateral offset: {currentLateralOffsetAngle:F1}°, " +
|
|
$"Longitudinal offset: {currentLongitudinalOffset:F1}");
|
|
}
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
// 新增:更新所有偏移参数
|
|
private void UpdateOffsets()
|
|
{
|
|
shotsFired++;
|
|
|
|
// 更新横向偏移
|
|
UpdateLateralOffset();
|
|
|
|
// 更新纵向偏移
|
|
UpdateLongitudinalOffset();
|
|
}
|
|
|
|
// 横向偏移逻辑(左右)
|
|
private void UpdateLateralOffset()
|
|
{
|
|
switch (Props.lateralOffsetMode)
|
|
{
|
|
case OffsetMode.Alternating:
|
|
currentLateralOffsetAngle = (shotsFired % 2 == 0) ? Props.lateralOffsetDistance : -Props.lateralOffsetDistance;
|
|
break;
|
|
|
|
case OffsetMode.Progressive:
|
|
currentLateralOffsetAngle += Props.lateralAngleIncrement;
|
|
if (Mathf.Abs(currentLateralOffsetAngle) > Props.lateralMaxOffsetAngle)
|
|
{
|
|
currentLateralOffsetAngle = Props.lateralInitialOffsetAngle;
|
|
}
|
|
break;
|
|
|
|
case OffsetMode.Random:
|
|
currentLateralOffsetAngle = Random.Range(-Props.lateralMaxOffsetAngle, Props.lateralMaxOffsetAngle);
|
|
break;
|
|
|
|
case OffsetMode.Fixed:
|
|
default:
|
|
break;
|
|
}
|
|
|
|
if (Props.lateralMaxOffsetAngle > 0)
|
|
{
|
|
currentLateralOffsetAngle = Mathf.Clamp(currentLateralOffsetAngle, -Props.lateralMaxOffsetAngle, Props.lateralMaxOffsetAngle);
|
|
}
|
|
}
|
|
|
|
// 新增:纵向偏移逻辑(前后)
|
|
private void UpdateLongitudinalOffset()
|
|
{
|
|
switch (Props.longitudinalOffsetMode)
|
|
{
|
|
case LongitudinalOffsetMode.Alternating:
|
|
// 交替模式:前后交替
|
|
currentLongitudinalOffset = (shotsFired % 2 == 0) ? Props.longitudinalAlternationAmplitude : -Props.longitudinalAlternationAmplitude;
|
|
break;
|
|
|
|
case LongitudinalOffsetMode.Progressive:
|
|
// 渐进模式:逐渐向前然后向后
|
|
if (isForwardPhase)
|
|
{
|
|
currentLongitudinalOffset += Props.longitudinalProgressionStep;
|
|
if (currentLongitudinalOffset >= Props.longitudinalMaxOffset)
|
|
{
|
|
isForwardPhase = false;
|
|
}
|
|
}
|
|
else
|
|
{
|
|
currentLongitudinalOffset -= Props.longitudinalProgressionStep;
|
|
if (currentLongitudinalOffset <= Props.longitudinalMinOffset)
|
|
{
|
|
isForwardPhase = true;
|
|
}
|
|
}
|
|
break;
|
|
|
|
case LongitudinalOffsetMode.Random:
|
|
// 随机模式
|
|
currentLongitudinalOffset = Random.Range(Props.longitudinalMinOffset, Props.longitudinalMaxOffset);
|
|
break;
|
|
|
|
case LongitudinalOffsetMode.Sinusoidal:
|
|
// 正弦波模式:平滑的前后波动
|
|
float time = shotsFired * Props.longitudinalOscillationSpeed;
|
|
currentLongitudinalOffset = Mathf.Sin(time) * Props.longitudinalOscillationAmplitude;
|
|
break;
|
|
|
|
case LongitudinalOffsetMode.Fixed:
|
|
default:
|
|
// 固定模式:保持不变
|
|
break;
|
|
}
|
|
|
|
// 应用限制
|
|
currentLongitudinalOffset = Mathf.Clamp(currentLongitudinalOffset, Props.longitudinalMinOffset, Props.longitudinalMaxOffset);
|
|
}
|
|
|
|
// 修改:计算包含横向和纵向偏移的发射位置
|
|
private Vector3 CalculateOffsetPosition(Vector3 basePosition, Vector3 directionToTarget)
|
|
{
|
|
Vector3 finalPosition = basePosition;
|
|
|
|
// 应用横向偏移(左右)
|
|
if (Mathf.Abs(currentLateralOffsetAngle) > 0.01f)
|
|
{
|
|
Vector3 flyDirection = GetFlyOverDirection();
|
|
Vector3 perpendicular = Vector3.Cross(flyDirection, Vector3.up).normalized;
|
|
float lateralOffsetDistance = Props.lateralOffsetDistance;
|
|
Vector3 lateralOffset = perpendicular * lateralOffsetDistance * Mathf.Sin(currentLateralOffsetAngle * Mathf.Deg2Rad);
|
|
finalPosition += lateralOffset;
|
|
}
|
|
|
|
// 应用纵向偏移(前后)
|
|
if (Mathf.Abs(currentLongitudinalOffset) > 0.01f)
|
|
{
|
|
Vector3 flyDirection = GetFlyOverDirection();
|
|
Vector3 longitudinalOffset = flyDirection * currentLongitudinalOffset;
|
|
finalPosition += longitudinalOffset;
|
|
}
|
|
|
|
return finalPosition;
|
|
}
|
|
|
|
private Vector3 GetFlyOverDirection()
|
|
{
|
|
FlyOver flyOver = parent as FlyOver;
|
|
if (flyOver != null)
|
|
{
|
|
return flyOver.MovementDirection;
|
|
}
|
|
return Vector3.forward;
|
|
}
|
|
|
|
private float GetHorizontalDistance(Vector3 fromPos, IntVec3 toCell)
|
|
{
|
|
Vector2 fromPos2D = new Vector2(fromPos.x, fromPos.z);
|
|
Vector2 toPos2D = new Vector2(toCell.x, toCell.z);
|
|
return Vector2.Distance(fromPos2D, toPos2D);
|
|
}
|
|
|
|
private bool LaunchProjectileAt(IntVec3 targetCell)
|
|
{
|
|
if (Props.projectileDef == null)
|
|
{
|
|
Log.Error("No projectile defined for ground strafing");
|
|
return false;
|
|
}
|
|
|
|
try
|
|
{
|
|
Vector3 spawnPos = parent.DrawPos;
|
|
Vector3 targetPos = targetCell.ToVector3();
|
|
Vector3 directionToTarget = (targetPos - spawnPos).normalized;
|
|
|
|
// 计算偏移后的发射位置
|
|
Vector3 offsetSpawnPos = CalculateOffsetPosition(spawnPos, directionToTarget);
|
|
|
|
IntVec3 spawnCell = offsetSpawnPos.ToIntVec3();
|
|
|
|
// 创建抛射体
|
|
Projectile projectile = (Projectile)GenSpawn.Spawn(Props.projectileDef, spawnCell, parent.Map);
|
|
|
|
if (projectile != null)
|
|
{
|
|
Thing launcher = GetLauncher();
|
|
lastProjectileDirection = directionToTarget;
|
|
|
|
// 发射抛射体
|
|
projectile.Launch(
|
|
launcher,
|
|
offsetSpawnPos,
|
|
new LocalTargetInfo(targetCell),
|
|
new LocalTargetInfo(targetCell),
|
|
ProjectileHitFlags.IntendedTarget,
|
|
false
|
|
);
|
|
|
|
// 播放偏移特效
|
|
if (Props.spawnOffsetEffect)
|
|
{
|
|
CreateOffsetEffect(offsetSpawnPos, directionToTarget);
|
|
}
|
|
|
|
return true;
|
|
}
|
|
}
|
|
catch (System.Exception ex)
|
|
{
|
|
Log.Error($"Error launching ground strafing projectile: {ex}");
|
|
}
|
|
|
|
return false;
|
|
}
|
|
|
|
// 偏移特效
|
|
private void CreateOffsetEffect(Vector3 spawnPos, Vector3 direction)
|
|
{
|
|
if (Props.offsetEffectDef != null)
|
|
{
|
|
MoteMaker.MakeStaticMote(
|
|
spawnPos,
|
|
parent.Map,
|
|
Props.offsetEffectDef,
|
|
1f
|
|
);
|
|
}
|
|
}
|
|
|
|
private Thing GetLauncher()
|
|
{
|
|
FlyOver flyOver = parent as FlyOver;
|
|
//if (flyOver != null && flyOver.caster != null)
|
|
//{
|
|
// return flyOver.caster;
|
|
//}
|
|
return parent;
|
|
}
|
|
|
|
public void SetConfirmedTargets(List<IntVec3> targets)
|
|
{
|
|
confirmedTargetCells.Clear();
|
|
firedCells.Clear();
|
|
shotsFired = 0;
|
|
currentLateralOffsetAngle = Props.lateralInitialOffsetAngle;
|
|
currentLongitudinalOffset = Props.longitudinalInitialOffset;
|
|
isForwardPhase = true;
|
|
|
|
confirmedTargetCells.AddRange(targets);
|
|
|
|
Log.Message($"GroundStrafing: Set {confirmedTargetCells.Count} targets, " +
|
|
$"Lateral Mode: {Props.lateralOffsetMode}, " +
|
|
$"Longitudinal Mode: {Props.longitudinalOffsetMode}");
|
|
|
|
if (confirmedTargetCells.Count > 0)
|
|
{
|
|
Log.Message($"First target: {confirmedTargetCells[0]}, Last target: {confirmedTargetCells[confirmedTargetCells.Count - 1]}");
|
|
}
|
|
}
|
|
|
|
public override void PostExposeData()
|
|
{
|
|
base.PostExposeData();
|
|
|
|
Scribe_Collections.Look(ref confirmedTargetCells, "confirmedTargetCells", LookMode.Value);
|
|
Scribe_Collections.Look(ref firedCells, "firedCells", LookMode.Value);
|
|
Scribe_Values.Look(ref currentLateralOffsetAngle, "currentLateralOffsetAngle", Props.lateralInitialOffsetAngle);
|
|
Scribe_Values.Look(ref currentLongitudinalOffset, "currentLongitudinalOffset", Props.longitudinalInitialOffset);
|
|
Scribe_Values.Look(ref shotsFired, "shotsFired", 0);
|
|
Scribe_Values.Look(ref isForwardPhase, "isForwardPhase", true);
|
|
}
|
|
|
|
// 修改:调试方法
|
|
public void DebugOffsetStatus()
|
|
{
|
|
Log.Message($"GroundStrafing Offset Status:");
|
|
Log.Message($" Lateral - Angle: {currentLateralOffsetAngle:F1}°, Mode: {Props.lateralOffsetMode}");
|
|
Log.Message($" Longitudinal - Offset: {currentLongitudinalOffset:F1}, Mode: {Props.longitudinalOffsetMode}");
|
|
Log.Message($" Shots Fired: {shotsFired}, Forward Phase: {isForwardPhase}");
|
|
}
|
|
}
|
|
|
|
public class CompProperties_GroundStrafing : CompProperties
|
|
{
|
|
public ThingDef projectileDef; // 抛射体定义
|
|
public float range = 15f; // 射程
|
|
|
|
// 横向偏移配置(左右)
|
|
public float lateralOffsetDistance = 2f;
|
|
public float lateralInitialOffsetAngle = 0f;
|
|
public float lateralMaxOffsetAngle = 45f;
|
|
public float lateralAngleIncrement = 5f;
|
|
public OffsetMode lateralOffsetMode = OffsetMode.Alternating;
|
|
|
|
// 纵向偏移配置(前后)
|
|
public float longitudinalInitialOffset = 0f; // 初始纵向偏移
|
|
public float longitudinalMinOffset = -2f; // 最小纵向偏移
|
|
public float longitudinalMaxOffset = 2f; // 最大纵向偏移
|
|
public LongitudinalOffsetMode longitudinalOffsetMode = LongitudinalOffsetMode.Alternating; // 纵向偏移模式
|
|
|
|
// 正弦波模式参数
|
|
public float longitudinalOscillationSpeed = 0.5f; // 振荡速度
|
|
public float longitudinalOscillationAmplitude = 1f; // 振荡幅度
|
|
|
|
// 交替模式参数
|
|
public float longitudinalAlternationAmplitude = 1f; // 交替幅度
|
|
|
|
// 渐进模式参数
|
|
public float longitudinalProgressionStep = 0.1f; // 渐进步长
|
|
|
|
// 视觉效果
|
|
public bool spawnOffsetEffect = false;
|
|
public ThingDef offsetEffectDef;
|
|
|
|
public CompProperties_GroundStrafing()
|
|
{
|
|
compClass = typeof(CompGroundStrafing);
|
|
}
|
|
}
|
|
|
|
// 横向偏移模式枚举
|
|
public enum OffsetMode
|
|
{
|
|
Fixed,
|
|
Alternating,
|
|
Progressive,
|
|
Random
|
|
}
|
|
|
|
// 新增:纵向偏移模式枚举
|
|
public enum LongitudinalOffsetMode
|
|
{
|
|
Fixed, // 固定
|
|
Alternating, // 交替(前后交替)
|
|
Progressive, // 渐进(逐渐变化)
|
|
Random, // 随机
|
|
Sinusoidal // 正弦波(平滑波动)
|
|
}
|
|
}
|