Files
WulaFallenEmpireRW/Source/WulaFallenEmpire/Ability/WULA_AbilityGuidedEnergyLance/GuidedEnergyLance.cs

256 lines
9.4 KiB
C#
Raw 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 RimWorld;
using Verse;
using UnityEngine;
using System.Collections.Generic;
using System.Linq;
namespace WulaFallenEmpire
{
public class GuidedEnergyLance : OrbitalStrike
{
// 引导相关属性
public Thing controlBuilding; // 控制建筑
private IntVec3 currentTarget = IntVec3.Invalid; // 当前目标位置
private int lastTargetUpdateTick = -1; // 最后收到目标更新的刻
private int maxNoUpdateTicks = 60; // 无更新时的最大存活时间
// 移动配置
public float moveSpeed = 2.0f; // 移动速度(格/秒)
public float turnSpeed = 90f; // 转向速度(度/秒)
// 状态
private Vector3 currentVelocity;
private bool hasValidTarget = false;
// ModExtension引用
private EnergyLanceExtension extension;
private static List<Thing> tmpThings = new List<Thing>();
public override void StartStrike()
{
base.StartStrike();
// 获取ModExtension
extension = def.GetModExtension<EnergyLanceExtension>();
if (extension == null)
{
Log.Error($"[GuidedEnergyLance] No EnergyLanceExtension found on {def.defName}");
return;
}
lastTargetUpdateTick = Find.TickManager.TicksGame;
// 创建视觉效果
CreateVisualEffect();
Log.Message($"[GuidedEnergyLance] Guided EnergyLance started, controlled by {controlBuilding?.Label ?? "None"}");
}
private void CreateVisualEffect()
{
// 使用ModExtension中定义的Mote如果没有则使用默认的PowerBeam
if (extension.moteDef != null)
{
Mote mote = MoteMaker.MakeStaticMote(base.Position, base.Map, extension.moteDef);
}
else
{
// 使用原版PowerBeam的视觉效果
MoteMaker.MakePowerBeamMote(base.Position, base.Map);
}
}
protected override void Tick()
{
base.Tick();
if (!base.Destroyed && extension != null)
{
// 检查目标更新状态
CheckTargetStatus();
// 移动光束
MoveBeam();
// 造成伤害
for (int i = 0; i < firesPerTick; i++)
{
DoBeamDamage();
}
}
}
private void CheckTargetStatus()
{
int currentTick = Find.TickManager.TicksGame;
int ticksSinceUpdate = currentTick - lastTargetUpdateTick;
// 检查是否长时间未收到更新
if (ticksSinceUpdate >= maxNoUpdateTicks)
{
Log.Message($"[GuidedEnergyLance] No target updates for {ticksSinceUpdate} ticks, destroying");
Destroy();
return;
}
// 检查控制建筑状态
if (controlBuilding == null || controlBuilding.Destroyed || !controlBuilding.Spawned)
{
Log.Message($"[GuidedEnergyLance] Control building lost, destroying");
Destroy();
return;
}
}
private void MoveBeam()
{
if (!hasValidTarget || !currentTarget.IsValid)
{
// 没有有效目标,缓慢移动或保持原地
ApplyMinimalMovement();
return;
}
// 计算移动方向
Vector3 targetDirection = (currentTarget.ToVector3() - base.Position.ToVector3()).normalized;
// 平滑转向
if (currentVelocity.magnitude > 0.1f)
{
float maxTurnAngle = turnSpeed * 0.0167f; // 每帧最大转向角度假设60FPS
currentVelocity = Vector3.RotateTowards(currentVelocity, targetDirection, maxTurnAngle * Mathf.Deg2Rad, moveSpeed * 0.0167f);
}
else
{
currentVelocity = targetDirection * moveSpeed * 0.0167f;
}
// 应用移动
Vector3 newPos = base.Position.ToVector3() + currentVelocity;
IntVec3 newCell = new IntVec3(Mathf.RoundToInt(newPos.x), 0, Mathf.RoundToInt(newPos.z));
if (newCell != base.Position && newCell.InBounds(base.Map))
{
base.Position = newCell;
}
// 检查是否接近目标
float distanceToTarget = Vector3.Distance(base.Position.ToVector3(), currentTarget.ToVector3());
if (distanceToTarget < 1.5f)
{
// 到达目标附近,可以减速或保持位置
currentVelocity *= 0.8f;
}
Log.Message($"[GuidedEnergyLance] Moving to {currentTarget}, distance: {distanceToTarget:F1}");
}
private void ApplyMinimalMovement()
{
// 无目标时的最小移动,防止完全静止
if (currentVelocity.magnitude < 0.1f)
{
// 随机轻微移动
currentVelocity = new Vector3(Rand.Range(-0.1f, 0.1f), 0f, Rand.Range(-0.1f, 0.1f));
}
else
{
// 缓慢减速
currentVelocity *= 0.95f;
}
Vector3 newPos = base.Position.ToVector3() + currentVelocity;
IntVec3 newCell = new IntVec3(Mathf.RoundToInt(newPos.x), 0, Mathf.RoundToInt(newPos.z));
if (newCell != base.Position && newCell.InBounds(base.Map))
{
base.Position = newCell;
}
}
private void DoBeamDamage()
{
if (extension == null) return;
// 在当前光束位置周围随机选择一个单元格
IntVec3 targetCell = (from x in GenRadial.RadialCellsAround(base.Position, 2f, useCenter: true)
where x.InBounds(base.Map)
select x).RandomElementByWeight((IntVec3 x) => 1f - Mathf.Min(x.DistanceTo(base.Position) / 2f, 1f) + 0.05f);
// 尝试在该单元格点火(如果配置了点火)
if (extension.igniteFires)
{
FireUtility.TryStartFireIn(targetCell, base.Map, Rand.Range(0.1f, extension.fireIgniteChance), instigator);
}
// 对该单元格内的物体造成伤害
tmpThings.Clear();
tmpThings.AddRange(targetCell.GetThingList(base.Map));
for (int i = 0; i < tmpThings.Count; i++)
{
Thing thing = tmpThings[i];
// 检查是否对尸体造成伤害
if (!extension.applyDamageToCorpses && thing is Corpse)
continue;
// 计算伤害量
int damageAmount = (thing is Corpse) ?
extension.corpseDamageAmountRange.RandomInRange :
extension.damageAmountRange.RandomInRange;
Pawn pawn = thing as Pawn;
BattleLogEntry_DamageTaken battleLogEntry = null;
if (pawn != null)
{
battleLogEntry = new BattleLogEntry_DamageTaken(pawn, RulePackDefOf.DamageEvent_PowerBeam, instigator as Pawn);
Find.BattleLog.Add(battleLogEntry);
}
// 使用ModExtension中定义的伤害类型
DamageInfo damageInfo = new DamageInfo(extension.damageDef, damageAmount, 0f, -1f, instigator, null, weaponDef);
thing.TakeDamage(damageInfo).AssociateWithLog(battleLogEntry);
}
tmpThings.Clear();
}
// 外部调用:更新目标位置
public void UpdateTarget(IntVec3 newTarget)
{
lastTargetUpdateTick = Find.TickManager.TicksGame;
if (newTarget.IsValid && newTarget.InBounds(base.Map))
{
currentTarget = newTarget;
hasValidTarget = true;
Log.Message($"[GuidedEnergyLance] Target updated to {newTarget}");
}
else
{
hasValidTarget = false;
currentTarget = IntVec3.Invalid;
Log.Message($"[GuidedEnergyLance] Target cleared");
}
}
public override void ExposeData()
{
base.ExposeData();
Scribe_References.Look(ref controlBuilding, "controlBuilding");
Scribe_Values.Look(ref currentTarget, "currentTarget", IntVec3.Invalid);
Scribe_Values.Look(ref lastTargetUpdateTick, "lastTargetUpdateTick", -1);
Scribe_Values.Look(ref maxNoUpdateTicks, "maxNoUpdateTicks", 60);
Scribe_Values.Look(ref moveSpeed, "moveSpeed", 2.0f);
Scribe_Values.Look(ref turnSpeed, "turnSpeed", 90f);
Scribe_Values.Look(ref firesPerTick, "firesPerTick", 3);
}
}
}