// File: CompMoteEmitterNorthward.cs using RimWorld; using System; using UnityEngine; using System.Collections.Generic; using Verse; using Verse.Sound; namespace WulaFallenEmpire { /// /// 组件:持续产生向上(北向)移动的Mote /// public class CompMoteEmitterNorthward : ThingComp { private CompProperties_MoteEmitterNorthward Props => (CompProperties_MoteEmitterNorthward)props; private int ticksUntilNextEmit; // 移动状态跟踪 private Vector3 lastPosition; private bool isMoving; private int positionUpdateCooldown = 0; // 缓存引用 private CompMechPilotHolder pilotHolder; // Pawn引用 private Pawn pawnParent; // 是否已销毁标记 private bool isDestroyed = false; public override void Initialize(CompProperties props) { base.Initialize(props); // 随机化初始计时器,避免所有发射器同时发射 ticksUntilNextEmit = Rand.Range(0, Props.emitIntervalTicks); // 获取Pawn引用 pawnParent = parent as Pawn; } public override void PostSpawnSetup(bool respawningAfterLoad) { base.PostSpawnSetup(respawningAfterLoad); // 重置销毁标记 isDestroyed = false; // 获取驾驶员容器组件 pilotHolder = parent.TryGetComp(); // 如果需要驾驶员但组件不存在,发出警告 if (Props.requirePilot && pilotHolder == null) { Log.Warning($"[WULA] CompMoteEmitterNorthward on {parent} requires pilot but no CompMechPilotHolder found"); } // 初始化位置 if (parent.Spawned) { lastPosition = GetSafePosition(); } } public override void CompTick() { base.CompTick(); // 如果已标记销毁,跳过所有处理 if (isDestroyed || parent == null) return; if (!parent.Spawned || parent.Map == null) return; // 更新移动状态 if (positionUpdateCooldown <= 0) { UpdateMovementState(); positionUpdateCooldown = 10; // 每10ticks更新一次位置,减少开销 } else { positionUpdateCooldown--; } // 检查是否满足发射条件 if (!CanEmit()) return; ticksUntilNextEmit--; if (ticksUntilNextEmit <= 0) { EmitMote(); // 根据移动状态设置下次发射间隔 ticksUntilNextEmit = isMoving ? Props.emitIntervalMovingTicks : Props.emitIntervalTicks; } } /// /// 安全获取当前位置 /// private Vector3 GetSafePosition() { try { if (parent == null || !parent.Spawned) return parent?.Position.ToVector3Shifted() ?? Vector3.zero; // 如果是Pawn且绘制器可用,使用DrawPos if (pawnParent != null && pawnParent.Drawer != null) { return pawnParent.DrawPos; } // 否则使用网格位置 return parent.Position.ToVector3Shifted(); } catch (NullReferenceException) { // 发生异常时返回网格位置 return parent?.Position.ToVector3Shifted() ?? Vector3.zero; } } /// /// 更新移动状态 /// private void UpdateMovementState() { if (!parent.Spawned || parent.Destroyed) { isMoving = false; return; } try { Vector3 currentPos = GetSafePosition(); float distanceMoved = Vector3.Distance(currentPos, lastPosition); // 简单移动检测:如果位置有变化就算移动 isMoving = distanceMoved > 0.01f; lastPosition = currentPos; } catch (NullReferenceException ex) { // 发生异常时重置状态 Log.Warning($"[WULA] Error updating movement state for {parent}: {ex.Message}"); isMoving = false; } } /// /// 检查是否可以发射Mote /// private bool CanEmit() { // 基础检查 if (parent == null || !parent.Spawned || parent.Map == null || Props.moteDef == null) return false; // 如果Pawn状态异常,不发射 if (pawnParent != null) { if (pawnParent.Dead || pawnParent.Downed || pawnParent.InMentalState) return false; // 检查绘制器是否可用 if (pawnParent.Drawer == null) return false; } // 新增:检查驾驶员条件 if (Props.requirePilot) { // 需要至少一个驾驶员 if (pilotHolder == null || !pilotHolder.HasPilots) return false; // 可选:检查驾驶员是否存活 if (Props.requirePilotAlive) { foreach (var pilot in pilotHolder.GetPilots()) { if (pilot.Dead || pilot.Downed) return false; } } } // 检查电源条件 if (Props.onlyWhenPowered) { var powerComp = parent.TryGetComp(); if (powerComp != null && !powerComp.PowerOn) return false; } // 检查天气条件 if (!string.IsNullOrEmpty(Props.onlyInWeather)) { var currentWeather = parent.Map.weatherManager.curWeather; if (currentWeather == null || currentWeather.defName != Props.onlyInWeather) return false; } // 检查地形条件 if (Props.onlyOnTerrain != null) { var terrain = parent.Position.GetTerrain(parent.Map); if (terrain != Props.onlyOnTerrain) return false; } return true; } private void EmitMote() { try { // 计算发射位置(根据朝向调整偏移) Vector3 emitPos = GetSafePosition() + GetOffsetForFacing(); // 如果父物体是Pawn,可以添加一些随机偏移 if (pawnParent != null && Props.randomOffsetRadius > 0f) { emitPos += new Vector3( Rand.Range(-Props.randomOffsetRadius, Props.randomOffsetRadius), 0f, Rand.Range(-Props.randomOffsetRadius, Props.randomOffsetRadius) ); } // 创建Mote Mote mote = (Mote)ThingMaker.MakeThing(Props.moteDef); if (mote is MoteThrown moteThrown) { // 设置初始位置 moteThrown.exactPosition = emitPos; // 设置向北移动的速度 moteThrown.SetVelocity( angle: 0f, // 0度 = 北向 speed: Props.moveSpeed ); // 设置旋转 moteThrown.exactRotation = Props.rotation; moteThrown.rotationRate = Props.rotationRate; // 设置缩放 moteThrown.Scale = Props.scale; // 设置存活时间 moteThrown.airTimeLeft = Props.lifetimeTicks; // 添加到地图 GenSpawn.Spawn(mote, parent.Position, parent.Map); } else { // 不是MoteThrown类型,使用基础设置 mote.exactPosition = emitPos; mote.Scale = Props.scale; GenSpawn.Spawn(mote, parent.Position, parent.Map); } // 播放发射音效 if (Props.soundOnEmit != null) { Props.soundOnEmit.PlayOneShot(parent); } } catch (Exception ex) { Log.Error($"[WULA] Error emitting mote: {ex}"); } } /// /// 根据朝向获取偏移位置 /// private Vector3 GetOffsetForFacing() { Vector3 offset = Props.offset; // 如果不是Pawn,返回基础偏移 if (pawnParent == null) return offset; // 检查Pawn是否可用 if (pawnParent.Destroyed || !pawnParent.Spawned) return offset; try { // 根据朝向调整偏移 switch (pawnParent.Rotation.AsInt) { case 0: // 北 return offset; case 1: // 东 return new Vector3(-offset.z, offset.y, offset.x); case 2: // 南 return new Vector3(-offset.x, offset.y, offset.z); case 3: // 西 return new Vector3(offset.z, offset.y, -offset.x); default: return offset; } } catch (NullReferenceException) { // 如果访问Rotation失败,返回基础偏移 return offset; } } public override void PostExposeData() { base.PostExposeData(); Scribe_Values.Look(ref ticksUntilNextEmit, "ticksUntilNextEmit", 0); Scribe_Values.Look(ref lastPosition, "lastPosition", Vector3.zero); Scribe_Values.Look(ref isMoving, "isMoving", false); } // 添加销毁相关的清理 public override void PostDestroy(DestroyMode mode, Map previousMap) { base.PostDestroy(mode, previousMap); isDestroyed = true; } public void PostDeSpawn(Map map) { base.PostDeSpawn(map); isDestroyed = true; } /// /// 获取组件状态信息(用于调试) /// public string GetStatusInfo() { if (parent == null || isDestroyed) return "Component destroyed"; string pilotStatus = "N/A"; if (pilotHolder != null) { pilotStatus = pilotHolder.HasPilots ? $"Has {pilotHolder.CurrentPilotCount} pilot(s)" : "No pilots"; } return $"Mote Emitter Status:\n" + $" Active: {!isDestroyed}\n" + $" Can Emit: {CanEmit()}\n" + $" Moving: {isMoving}\n" + $" Pilot Status: {pilotStatus}\n" + $" Next Emit: {ticksUntilNextEmit} ticks\n" + $" Powered: {(Props.onlyWhenPowered ? CheckPowerStatus() : "N/A")}"; } private string CheckPowerStatus() { var powerComp = parent.TryGetComp(); if (powerComp == null) return "No power comp"; return powerComp.PowerOn ? "Powered" : "No power"; } } /// /// 组件属性(更新版) /// public class CompProperties_MoteEmitterNorthward : CompProperties { /// Mote定义 public ThingDef moteDef; /// 发射间隔(ticks)- 静止时 public int emitIntervalTicks = 60; // 默认1秒 /// 发射间隔(ticks)- 移动时 public int emitIntervalMovingTicks = 30; // 移动时默认0.5秒 /// 移动速度 public float moveSpeed = 1f; /// Mote生命周期(ticks) public float lifetimeTicks = 120f; // 默认2秒 /// 初始旋转角度 public float rotation = 0f; /// 旋转速度(度/秒) public float rotationRate = 0f; /// 缩放大小 public float scale = 1f; /// 偏移位置(相对于父物体)- 默认朝北时的偏移 public Vector3 offset = Vector3.zero; /// 随机偏移半径 public float randomOffsetRadius = 0f; /// 发射时的音效 public SoundDef soundOnEmit; /// 是否只在启用的状态发射 public bool onlyWhenPowered = false; /// 是否只在至少有一个驾驶员时发射 public bool requirePilot = true; // 新增:驾驶员条件 /// 天气条件:只在指定天气发射(用逗号分隔) public string onlyInWeather; /// 地形条件:只在指定地形发射 public TerrainDef onlyOnTerrain; /// 驾驶员条件:只在驾驶员存活时发射 public bool requirePilotAlive = true; // 新增:要求驾驶员存活 public CompProperties_MoteEmitterNorthward() { compClass = typeof(CompMoteEmitterNorthward); } public override IEnumerable ConfigErrors(ThingDef parentDef) { foreach (string error in base.ConfigErrors(parentDef)) { yield return error; } if (moteDef == null) { yield return $"moteDef is not defined for {parentDef.defName}"; } if (emitIntervalTicks <= 0) { yield return $"emitIntervalTicks must be greater than 0 for {parentDef.defName}"; } if (emitIntervalMovingTicks <= 0) { yield return $"emitIntervalMovingTicks must be greater than 0 for {parentDef.defName}"; } if (lifetimeTicks <= 0) { yield return $"lifetimeTicks must be greater than 0 for {parentDef.defName}"; } if (requirePilot && parentDef.GetCompProperties() == null) { yield return $"requirePilot is true but no CompProperties_MechPilotHolder found for {parentDef.defName}"; } } } }