环形轰炸,区域监视,光矛扫射
This commit is contained in:
@@ -0,0 +1,83 @@
|
||||
using RimWorld;
|
||||
using Verse;
|
||||
using UnityEngine;
|
||||
using System.Collections.Generic;
|
||||
|
||||
namespace WulaFallenEmpire
|
||||
{
|
||||
public class CompAbilityEffect_CallSkyfaller : CompAbilityEffect
|
||||
{
|
||||
public new CompProperties_AbilityCallSkyfaller Props => (CompProperties_AbilityCallSkyfaller)props;
|
||||
|
||||
public override void Apply(LocalTargetInfo target, LocalTargetInfo dest)
|
||||
{
|
||||
base.Apply(target, dest);
|
||||
|
||||
if (parent.pawn == null || parent.pawn.Map == null || !target.IsValid)
|
||||
return;
|
||||
|
||||
try
|
||||
{
|
||||
// 创建延时召唤
|
||||
CallSkyfallerDelayed(target.Cell);
|
||||
|
||||
Log.Message($"[CallSkyfaller] Scheduled skyfaller at {target.Cell} with {Props.delayTicks} ticks delay");
|
||||
}
|
||||
catch (System.Exception ex)
|
||||
{
|
||||
Log.Error($"[CallSkyfaller] Error calling skyfaller: {ex}");
|
||||
}
|
||||
}
|
||||
|
||||
private void CallSkyfallerDelayed(IntVec3 targetCell)
|
||||
{
|
||||
// 使用延时动作来召唤skyfaller
|
||||
parent.pawn.Map.GetComponent<MapComponent_SkyfallerDelayed>()?
|
||||
.ScheduleSkyfaller(Props.skyfallerDef, targetCell, Props.delayTicks, parent.pawn);
|
||||
}
|
||||
|
||||
// 绘制预览效果
|
||||
public override void DrawEffectPreview(LocalTargetInfo target)
|
||||
{
|
||||
base.DrawEffectPreview(target);
|
||||
|
||||
if (parent.pawn == null || parent.pawn.Map == null || !target.IsValid)
|
||||
return;
|
||||
|
||||
try
|
||||
{
|
||||
// 绘制圆形预览区域
|
||||
DrawCircularPreview(target.Cell);
|
||||
}
|
||||
catch (System.Exception)
|
||||
{
|
||||
// 忽略预览绘制错误
|
||||
}
|
||||
}
|
||||
|
||||
private void DrawCircularPreview(IntVec3 center)
|
||||
{
|
||||
Map map = parent.pawn.Map;
|
||||
|
||||
// 获取圆形区域内的所有单元格
|
||||
var previewCells = GenRadial.RadialCellsAround(center, Props.previewRadius, true);
|
||||
|
||||
// 绘制预览区域
|
||||
foreach (var cell in previewCells)
|
||||
{
|
||||
if (cell.InBounds(map))
|
||||
{
|
||||
GenDraw.DrawFieldEdges(new List<IntVec3> { cell }, Props.previewColor, 0.2f);
|
||||
}
|
||||
}
|
||||
|
||||
// 绘制目标点高亮
|
||||
GenDraw.DrawTargetHighlight(center);
|
||||
}
|
||||
|
||||
public override string ExtraLabelMouseAttachment(LocalTargetInfo target)
|
||||
{
|
||||
return $"召唤空投舱: {Props.delayTicks}刻后到达";
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,234 @@
|
||||
using RimWorld;
|
||||
using Verse;
|
||||
using UnityEngine;
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
|
||||
namespace WulaFallenEmpire
|
||||
{
|
||||
public class CompBuildingBombardment : ThingComp
|
||||
{
|
||||
public CompProperties_BuildingBombardment Props => (CompProperties_BuildingBombardment)props;
|
||||
|
||||
// 轰炸状态
|
||||
private BuildingBombardmentState currentState = BuildingBombardmentState.Idle;
|
||||
private int nextBurstTick = 0;
|
||||
private int currentBurstCount = 0;
|
||||
private int nextInnerBurstTick = 0;
|
||||
private List<LocalTargetInfo> currentTargets = new List<LocalTargetInfo>();
|
||||
|
||||
public override void PostSpawnSetup(bool respawningAfterLoad)
|
||||
{
|
||||
base.PostSpawnSetup(respawningAfterLoad);
|
||||
|
||||
if (!respawningAfterLoad)
|
||||
{
|
||||
// 新生成时立即开始第一轮轰炸
|
||||
StartNextBurst();
|
||||
}
|
||||
}
|
||||
|
||||
private void StartNextBurst()
|
||||
{
|
||||
currentState = BuildingBombardmentState.Targeting;
|
||||
currentBurstCount = 0;
|
||||
currentTargets.Clear();
|
||||
|
||||
// 选择目标
|
||||
SelectTargets();
|
||||
|
||||
if (currentTargets.Count > 0)
|
||||
{
|
||||
currentState = BuildingBombardmentState.Bursting;
|
||||
nextInnerBurstTick = Find.TickManager.TicksGame;
|
||||
Log.Message($"[BuildingBombardment] Starting burst with {currentTargets.Count} targets");
|
||||
}
|
||||
else
|
||||
{
|
||||
// 没有找到目标,等待下一轮
|
||||
currentState = BuildingBombardmentState.Idle;
|
||||
nextBurstTick = Find.TickManager.TicksGame + Props.burstIntervalTicks;
|
||||
Log.Message($"[BuildingBombardment] No targets found, waiting for next burst");
|
||||
}
|
||||
}
|
||||
|
||||
private void SelectTargets()
|
||||
{
|
||||
Map map = parent.Map;
|
||||
if (map == null) return;
|
||||
|
||||
// 获取范围内的所有pawn
|
||||
var potentialTargets = new List<Pawn>();
|
||||
|
||||
foreach (var pawn in map.mapPawns.AllPawnsSpawned)
|
||||
{
|
||||
if (IsValidTarget(pawn) && IsInRange(pawn.Position))
|
||||
{
|
||||
potentialTargets.Add(pawn);
|
||||
}
|
||||
}
|
||||
|
||||
// 随机选择目标,最多burstCount个
|
||||
int targetCount = Mathf.Min(Props.burstCount, potentialTargets.Count);
|
||||
currentTargets = potentialTargets
|
||||
.InRandomOrder()
|
||||
.Take(targetCount)
|
||||
.Select(p => new LocalTargetInfo(p))
|
||||
.ToList();
|
||||
}
|
||||
|
||||
private bool IsValidTarget(Pawn pawn)
|
||||
{
|
||||
if (pawn == null || pawn.Downed || pawn.Dead) return false;
|
||||
|
||||
// 检查目标类型
|
||||
if (Props.targetEnemies && pawn.HostileTo(parent.Faction))
|
||||
return true;
|
||||
|
||||
if (Props.targetNeutrals && !pawn.HostileTo(parent.Faction) && pawn.Faction != parent.Faction)
|
||||
return true;
|
||||
|
||||
if (Props.targetAnimals && pawn.RaceProps.Animal)
|
||||
return true;
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
private bool IsInRange(IntVec3 position)
|
||||
{
|
||||
float distance = Vector3.Distance(parent.Position.ToVector3(), position.ToVector3());
|
||||
return distance <= Props.radius;
|
||||
}
|
||||
|
||||
private void UpdateBursting()
|
||||
{
|
||||
if (Find.TickManager.TicksGame < nextInnerBurstTick)
|
||||
return;
|
||||
|
||||
if (currentBurstCount >= currentTargets.Count)
|
||||
{
|
||||
// 当前组发射完毕
|
||||
currentState = BuildingBombardmentState.Idle;
|
||||
nextBurstTick = Find.TickManager.TicksGame + Props.burstIntervalTicks;
|
||||
Log.Message($"[BuildingBombardment] Burst completed, waiting for next burst");
|
||||
return;
|
||||
}
|
||||
|
||||
// 发射当前目标
|
||||
var target = currentTargets[currentBurstCount];
|
||||
LaunchBombardment(target);
|
||||
currentBurstCount++;
|
||||
|
||||
// 设置下一个组内发射时间
|
||||
if (currentBurstCount < currentTargets.Count)
|
||||
{
|
||||
nextInnerBurstTick = Find.TickManager.TicksGame + Props.innerBurstIntervalTicks;
|
||||
}
|
||||
|
||||
Log.Message($"[BuildingBombardment] Launched bombardment {currentBurstCount}/{currentTargets.Count}");
|
||||
}
|
||||
|
||||
private void LaunchBombardment(LocalTargetInfo target)
|
||||
{
|
||||
try
|
||||
{
|
||||
// 应用随机偏移
|
||||
IntVec3 targetCell = ApplyRandomOffset(target.Cell);
|
||||
|
||||
if (Props.skyfallerDef != null)
|
||||
{
|
||||
// 使用 Skyfaller
|
||||
Skyfaller skyfaller = SkyfallerMaker.MakeSkyfaller(Props.skyfallerDef);
|
||||
GenSpawn.Spawn(skyfaller, targetCell, parent.Map);
|
||||
}
|
||||
else if (Props.projectileDef != null)
|
||||
{
|
||||
// 使用抛射体作为备用
|
||||
LaunchProjectileAt(targetCell);
|
||||
}
|
||||
}
|
||||
catch (System.Exception ex)
|
||||
{
|
||||
Log.Error($"[BuildingBombardment] Error launching bombardment: {ex}");
|
||||
}
|
||||
}
|
||||
|
||||
private IntVec3 ApplyRandomOffset(IntVec3 originalCell)
|
||||
{
|
||||
if (Props.randomOffset <= 0f)
|
||||
return originalCell;
|
||||
|
||||
// 在随机偏移范围内选择一个位置
|
||||
float offsetX = Rand.Range(-Props.randomOffset, Props.randomOffset);
|
||||
float offsetZ = Rand.Range(-Props.randomOffset, Props.randomOffset);
|
||||
|
||||
IntVec3 offsetCell = new IntVec3(
|
||||
Mathf.RoundToInt(originalCell.x + offsetX),
|
||||
originalCell.y,
|
||||
Mathf.RoundToInt(originalCell.z + offsetZ)
|
||||
);
|
||||
|
||||
// 确保位置有效
|
||||
if (offsetCell.InBounds(parent.Map))
|
||||
return offsetCell;
|
||||
else
|
||||
return originalCell;
|
||||
}
|
||||
|
||||
private void LaunchProjectileAt(IntVec3 targetCell)
|
||||
{
|
||||
// 从建筑位置发射抛射体
|
||||
Vector3 spawnPos = parent.Position.ToVector3Shifted();
|
||||
|
||||
Projectile projectile = (Projectile)GenSpawn.Spawn(Props.projectileDef, parent.Position, parent.Map);
|
||||
if (projectile != null)
|
||||
{
|
||||
projectile.Launch(
|
||||
parent,
|
||||
spawnPos,
|
||||
new LocalTargetInfo(targetCell),
|
||||
new LocalTargetInfo(targetCell),
|
||||
ProjectileHitFlags.All,
|
||||
false
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
public override void CompTick()
|
||||
{
|
||||
base.CompTick();
|
||||
|
||||
switch (currentState)
|
||||
{
|
||||
case BuildingBombardmentState.Idle:
|
||||
if (Find.TickManager.TicksGame >= nextBurstTick)
|
||||
{
|
||||
StartNextBurst();
|
||||
}
|
||||
break;
|
||||
|
||||
case BuildingBombardmentState.Bursting:
|
||||
UpdateBursting();
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
public override void PostExposeData()
|
||||
{
|
||||
base.PostExposeData();
|
||||
|
||||
Scribe_Values.Look(ref currentState, "currentState", BuildingBombardmentState.Idle);
|
||||
Scribe_Values.Look(ref nextBurstTick, "nextBurstTick", 0);
|
||||
Scribe_Values.Look(ref currentBurstCount, "currentBurstCount", 0);
|
||||
Scribe_Values.Look(ref nextInnerBurstTick, "nextInnerBurstTick", 0);
|
||||
Scribe_Collections.Look(ref currentTargets, "currentTargets", LookMode.LocalTargetInfo);
|
||||
}
|
||||
}
|
||||
|
||||
public enum BuildingBombardmentState
|
||||
{
|
||||
Idle,
|
||||
Targeting,
|
||||
Bursting
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,22 @@
|
||||
using RimWorld;
|
||||
using Verse;
|
||||
using UnityEngine;
|
||||
|
||||
namespace WulaFallenEmpire
|
||||
{
|
||||
public class CompProperties_AbilityCallSkyfaller : CompProperties_AbilityEffect
|
||||
{
|
||||
// 基础配置
|
||||
public int delayTicks = 120; // 延时(刻)
|
||||
public ThingDef skyfallerDef; // 使用的 Skyfaller
|
||||
|
||||
// 预览配置
|
||||
public float previewRadius = 5f; // 预览半径
|
||||
public Color previewColor = new Color(1f, 0.5f, 0.1f, 0.3f); // 预览颜色
|
||||
|
||||
public CompProperties_AbilityCallSkyfaller()
|
||||
{
|
||||
this.compClass = typeof(CompAbilityEffect_CallSkyfaller);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,31 @@
|
||||
using RimWorld;
|
||||
using Verse;
|
||||
|
||||
namespace WulaFallenEmpire
|
||||
{
|
||||
public class CompProperties_BuildingBombardment : CompProperties
|
||||
{
|
||||
// 轰炸区域配置
|
||||
public float radius = 15f; // 作用半径
|
||||
|
||||
// 目标选择配置
|
||||
public bool targetEnemies = true; // 是否以敌人为目标
|
||||
public bool targetNeutrals = false; // 是否以中立单位为目标
|
||||
public bool targetAnimals = false; // 是否以动物为目标
|
||||
|
||||
// 召唤配置
|
||||
public int burstCount = 3; // 单组召唤数量
|
||||
public int innerBurstIntervalTicks = 10; // 同组召唤间隔
|
||||
public int burstIntervalTicks = 60; // 组间召唤间隔
|
||||
public float randomOffset = 2f; // 随机偏移量
|
||||
|
||||
// Skyfaller 配置
|
||||
public ThingDef skyfallerDef; // 使用的 Skyfaller
|
||||
public ThingDef projectileDef; // 备用的抛射体定义
|
||||
|
||||
public CompProperties_BuildingBombardment()
|
||||
{
|
||||
this.compClass = typeof(CompBuildingBombardment);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,81 @@
|
||||
using RimWorld;
|
||||
using Verse;
|
||||
using System.Collections.Generic;
|
||||
|
||||
namespace WulaFallenEmpire
|
||||
{
|
||||
public class MapComponent_SkyfallerDelayed : MapComponent
|
||||
{
|
||||
private List<DelayedSkyfaller> scheduledSkyfallers = new List<DelayedSkyfaller>();
|
||||
|
||||
public MapComponent_SkyfallerDelayed(Map map) : base(map) { }
|
||||
|
||||
public void ScheduleSkyfaller(ThingDef skyfallerDef, IntVec3 targetCell, int delayTicks, Pawn caster = null)
|
||||
{
|
||||
scheduledSkyfallers.Add(new DelayedSkyfaller
|
||||
{
|
||||
skyfallerDef = skyfallerDef,
|
||||
targetCell = targetCell,
|
||||
spawnTick = Find.TickManager.TicksGame + delayTicks,
|
||||
caster = caster
|
||||
});
|
||||
}
|
||||
|
||||
public override void MapComponentTick()
|
||||
{
|
||||
base.MapComponentTick();
|
||||
|
||||
int currentTick = Find.TickManager.TicksGame;
|
||||
|
||||
// 检查并执行到期的召唤
|
||||
for (int i = scheduledSkyfallers.Count - 1; i >= 0; i--)
|
||||
{
|
||||
var skyfaller = scheduledSkyfallers[i];
|
||||
if (currentTick >= skyfaller.spawnTick)
|
||||
{
|
||||
SpawnSkyfaller(skyfaller);
|
||||
scheduledSkyfallers.RemoveAt(i);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private void SpawnSkyfaller(DelayedSkyfaller delayedSkyfaller)
|
||||
{
|
||||
try
|
||||
{
|
||||
if (delayedSkyfaller.skyfallerDef != null && delayedSkyfaller.targetCell.IsValid && delayedSkyfaller.targetCell.InBounds(map))
|
||||
{
|
||||
Skyfaller skyfaller = SkyfallerMaker.MakeSkyfaller(delayedSkyfaller.skyfallerDef);
|
||||
GenSpawn.Spawn(skyfaller, delayedSkyfaller.targetCell, map);
|
||||
Log.Message($"[DelayedSkyfaller] Spawned skyfaller at {delayedSkyfaller.targetCell}");
|
||||
}
|
||||
}
|
||||
catch (System.Exception ex)
|
||||
{
|
||||
Log.Error($"[DelayedSkyfaller] Error spawning skyfaller: {ex}");
|
||||
}
|
||||
}
|
||||
|
||||
public override void ExposeData()
|
||||
{
|
||||
base.ExposeData();
|
||||
Scribe_Collections.Look(ref scheduledSkyfallers, "scheduledSkyfallers", LookMode.Deep);
|
||||
}
|
||||
}
|
||||
|
||||
public class DelayedSkyfaller : IExposable
|
||||
{
|
||||
public ThingDef skyfallerDef;
|
||||
public IntVec3 targetCell;
|
||||
public int spawnTick;
|
||||
public Pawn caster;
|
||||
|
||||
public void ExposeData()
|
||||
{
|
||||
Scribe_Defs.Look(ref skyfallerDef, "skyfallerDef");
|
||||
Scribe_Values.Look(ref targetCell, "targetCell");
|
||||
Scribe_Values.Look(ref spawnTick, "spawnTick");
|
||||
Scribe_References.Look(ref caster, "caster");
|
||||
}
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user