Files
WulaFallenEmpireRW/Source/WulaFallenEmpire/BuildingComp/WULA_TrapLauncher/CompTrapLauncher.cs
2025-12-02 11:15:13 +08:00

463 lines
15 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 System.Collections.Generic;
using System.Linq;
using UnityEngine;
using Verse;
using Verse.Sound;
using static UnityEngine.GraphicsBuffer;
namespace WulaFallenEmpire
{
public class CompTrapLauncher : ThingComp
{
public CompProperties_TrapLauncher Props => (CompProperties_TrapLauncher)props;
private int scanTickCounter = 0;
private bool hasTriggered = false;
private Pawn currentTarget = null;
private int warmupCounter = 0;
private bool isWarmingUp = false;
private int burstCounter = 0;
// 已检测过的目标(避免重复攻击)
private HashSet<Pawn> detectedTargets = new HashSet<Pawn>();
// 用于绘制检测范围
private Material cachedRadiusMat;
private Material RadiusMat
{
get
{
if (cachedRadiusMat == null)
{
cachedRadiusMat = SolidColorMaterials.SimpleSolidColorMaterial(
new Color(1f, 0.2f, 0.2f, 0.1f));
}
return cachedRadiusMat;
}
}
public override void CompTick()
{
base.CompTick();
if (!parent.Spawned || hasTriggered)
return;
// 预热计数
if (isWarmingUp)
{
warmupCounter++;
if (warmupCounter >= Props.warmupTicks)
{
LaunchProjectile();
}
return;
}
// 扫描计数
scanTickCounter++;
if (scanTickCounter >= Props.scanIntervalTicks)
{
scanTickCounter = 0;
ScanForTargets();
}
}
/// <summary>
/// 扫描范围内的敌对目标
/// </summary>
private void ScanForTargets()
{
if (!parent.Spawned)
return;
Map map = parent.Map;
IntVec3 center = parent.Position;
// 获取范围内的所有Pawn
List<Pawn> pawnsInRange = new List<Pawn>();
foreach (IntVec3 cell in GenRadial.RadialCellsAround(center, Props.detectionRadius, true))
{
if (!cell.InBounds(map))
continue;
// 检查视线(如果需要)
if (Props.requireLineOfSight)
{
if (!GenSight.LineOfSight(center, cell, map, skipFirstCell: true))
continue;
}
// 获取格子上的所有Pawn
List<Thing> things = map.thingGrid.ThingsListAtFast(cell);
foreach (Thing thing in things)
{
if (thing is Pawn pawn && !detectedTargets.Contains(pawn))
{
// 检查是否敌对
if (IsValidTarget(pawn))
{
pawnsInRange.Add(pawn);
}
}
}
}
// 如果没有目标,直接返回
if (pawnsInRange.Count == 0)
return;
// 选择第一个目标
currentTarget = pawnsInRange[0];
detectedTargets.Add(currentTarget);
// 触发陷阱
TriggerTrap();
}
/// <summary>
/// 检查是否为有效目标
/// </summary>
private bool IsValidTarget(Pawn pawn)
{
if (pawn == null || pawn.Dead || pawn.Downed)
return false;
if (Props.ignoreNonHostilePawns)
{
// 检查是否为敌对派系
if (pawn.Faction == null || !pawn.Faction.HostileTo(Faction.OfPlayer))
return false;
// 检查是否为自然敌对生物
if (!pawn.Faction.IsPlayer && !pawn.Faction.HostileTo(Faction.OfPlayer))
{
// 检查是否为敌对性生物(如人形生物巢穴的敌人)
if (!pawn.RaceProps.Humanlike && !pawn.def.race.Animal)
return false;
}
}
// 检查是否为机械体(如果设定)
// 这里可以根据需要添加更多过滤条件
return true;
}
/// <summary>
/// 触发陷阱
/// </summary>
private void TriggerTrap()
{
if (hasTriggered)
return;
// 播放触发音效
if (Props.triggerSound != null)
{
Props.triggerSound.PlayOneShot(new TargetInfo(parent.Position, parent.Map));
}
// 播放触发特效
if (Props.triggerEffect != null)
{
Effecter effecter = Props.triggerEffect.Spawn();
effecter.Trigger(parent, parent);
effecter.Cleanup();
}
// 发送消息
if (currentTarget != null)
{
Messages.Message("WULA_TrapLauncherTriggered".Translate(
parent.Label,
currentTarget.LabelShort
), parent, MessageTypeDefOf.ThreatBig);
}
// 开始预热
isWarmingUp = true;
warmupCounter = 0;
burstCounter = 0;
}
/// <summary>
/// 发射抛射体
/// </summary>
private void LaunchProjectile()
{
if (currentTarget == null || !currentTarget.Spawned)
{
// 如果目标无效,尝试寻找新目标
if (Props.canRetarget)
{
currentTarget = FindNewTarget();
if (currentTarget == null)
{
// 没有新目标,取消发射
isWarmingUp = false;
return;
}
}
else
{
// 直接自毁
SelfDestruct();
return;
}
}
// 发射抛射体
for (int i = 0; i < Props.burstCount; i++)
{
if (burstCounter >= Props.maxTargets)
break;
// 创建抛射体
Projectile projectile = (Projectile)GenSpawn.Spawn(
Props.projectileDef,
parent.Position,
parent.Map
);
// 发射
projectile.Launch(parent, parent.DrawPos, currentTarget, currentTarget, ProjectileHitFlags.IntendedTarget, false);
// 连发延迟
if (i < Props.burstCount - 1 && Props.burstDelay > 0)
{
// 使用简单的延迟实现
// 在实际游戏中,可能需要更复杂的实现
// 这里我们简化处理
}
}
// 播放发射音效
if (Props.launchSound != null)
{
Props.launchSound.PlayOneShot(new TargetInfo(parent.Position, parent.Map));
}
// 播放发射特效
if (Props.launchEffect != null)
{
Effecter effecter = Props.launchEffect.Spawn();
effecter.Trigger(parent, parent);
effecter.Cleanup();
}
// 检查是否需要继续攻击
if (burstCounter >= Props.maxTargets || !Props.canRetarget)
{
// 自毁
SelfDestruct();
}
else
{
// 寻找新目标
currentTarget = FindNewTarget();
if (currentTarget == null)
{
SelfDestruct();
}
else
{
// 重置预热,准备下一次发射
warmupCounter = 0;
burstCounter = 0;
}
}
}
/// <summary>
/// 寻找新目标
/// </summary>
private Pawn FindNewTarget()
{
Map map = parent.Map;
IntVec3 center = parent.Position;
foreach (IntVec3 cell in GenRadial.RadialCellsAround(center, Props.detectionRadius, true))
{
if (!cell.InBounds(map))
continue;
if (Props.requireLineOfSight && !GenSight.LineOfSight(center, cell, map, skipFirstCell: true))
continue;
List<Thing> things = map.thingGrid.ThingsListAtFast(cell);
foreach (Thing thing in things)
{
if (thing is Pawn pawn && !detectedTargets.Contains(pawn) && IsValidTarget(pawn))
{
detectedTargets.Add(pawn);
return pawn;
}
// 如果目标建筑
if (Props.targetBuildings && thing is Building building &&
building.Faction != null && building.Faction.HostileTo(Faction.OfPlayer))
{
// 注意:这里需要处理建筑目标,但抛射体可能需要调整
// 为了简化这里只处理Pawn
}
}
}
return null;
}
/// <summary>
/// 自毁
/// </summary>
private void SelfDestruct()
{
if (hasTriggered)
return;
hasTriggered = true;
// 播放自毁音效
if (Props.selfDestructSound != null)
{
Props.selfDestructSound.PlayOneShot(new TargetInfo(parent.Position, parent.Map));
}
// 播放自毁特效
if (Props.selfDestructEffect != null)
{
Effecter effecter = Props.selfDestructEffect.Spawn();
effecter.Trigger(parent, parent);
effecter.Cleanup();
}
// 销毁建筑
parent.Destroy(DestroyMode.Vanish);
}
/// <summary>
/// 绘制检测范围(仅在选定和调试模式下)
/// </summary>
public override void PostDrawExtraSelectionOverlays()
{
base.PostDrawExtraSelectionOverlays();
if (Props.showDetectionRadius && Props.detectionRadius > 0)
{
GenDraw.DrawRadiusRing(parent.Position, Props.detectionRadius, Color.red);
}
}
/// <summary>
/// 获取Gizmo按钮
/// </summary>
public override IEnumerable<Gizmo> CompGetGizmosExtra()
{
foreach (Gizmo gizmo in base.CompGetGizmosExtra())
{
yield return gizmo;
}
if (!hasTriggered && DebugSettings.ShowDevGizmos)
{
// 调试:手动触发
Command_Action debugTrigger = new Command_Action();
debugTrigger.defaultLabel = "DEV: Trigger Trap";
debugTrigger.action = delegate
{
currentTarget = FindClosestHostilePawn();
if (currentTarget != null)
{
TriggerTrap();
}
else
{
Messages.Message("WULA_TrapLauncherNoTargetFound".Translate(),
parent, MessageTypeDefOf.RejectInput);
}
};
yield return debugTrigger;
// 调试:立即自毁
Command_Action debugDestruct = new Command_Action();
debugDestruct.defaultLabel = "DEV: Self-Destruct";
debugDestruct.action = delegate
{
SelfDestruct();
};
yield return debugDestruct;
}
}
/// <summary>
/// 查找最近的敌对Pawn调试用
/// </summary>
private Pawn FindClosestHostilePawn()
{
if (!parent.Spawned)
return null;
Pawn closestPawn = null;
float closestDist = float.MaxValue;
foreach (Pawn pawn in parent.Map.mapPawns.AllPawnsSpawned)
{
if (IsValidTarget(pawn))
{
float dist = pawn.Position.DistanceTo(parent.Position);
if (dist <= Props.detectionRadius && dist < closestDist)
{
closestDist = dist;
closestPawn = pawn;
}
}
}
return closestPawn;
}
public override void PostExposeData()
{
base.PostExposeData();
Scribe_Values.Look(ref scanTickCounter, "scanTickCounter", 0);
Scribe_Values.Look(ref hasTriggered, "hasTriggered", false);
Scribe_Values.Look(ref isWarmingUp, "isWarmingUp", false);
Scribe_Values.Look(ref warmupCounter, "warmupCounter", 0);
Scribe_Values.Look(ref burstCounter, "burstCounter", 0);
Scribe_References.Look(ref currentTarget, "currentTarget");
Scribe_Collections.Look(ref detectedTargets, "detectedTargets", LookMode.Reference);
if (Scribe.mode == LoadSaveMode.PostLoadInit)
{
if (detectedTargets == null)
{
detectedTargets = new HashSet<Pawn>();
}
// 移除无效的目标引用
detectedTargets.RemoveWhere(pawn => pawn == null || pawn.Destroyed);
// 如果已经在预热但目标无效,尝试恢复或自毁
if (isWarmingUp && (currentTarget == null || !currentTarget.Spawned))
{
if (Props.canRetarget)
{
currentTarget = FindNewTarget();
if (currentTarget == null)
{
SelfDestruct();
}
}
else
{
SelfDestruct();
}
}
}
}
}
}