using System.Collections.Generic; using RimWorld; using UnityEngine; using Verse; namespace WulaFallenEmpire { public class Projectile_NorthArcTrail : Projectile_Explosive { // --- 弹道部分变量 --- public float northOffsetDistance = 0f; private Vector3 exactPositionInt; private float curveSteepness = 1f; private Vector3 originPos; private Vector3 destinationPos; private Vector3 bezierControlPoint; private int ticksFlying; private int totalTicks; private bool initialized = false; // --- 尾迹部分变量 --- private TrackingBulletDef trackingDefInt; private int Fleck_MakeFleckTick; private Vector3 lastTickPosition; // 新增:绘制相关变量 private float currentArcHeight; private const float DRAW_ALTITUDE_OFFSET = 0.5f; // 减少偏移量,避免裁剪问题 // 新增:用于保存真实计算的位置(仅XZ平面) private Vector3 horizontalPosition; public TrackingBulletDef TrackingDef { get { if (trackingDefInt == null) { trackingDefInt = def.GetModExtension(); if (trackingDefInt == null) { trackingDefInt = new TrackingBulletDef(); } } return trackingDefInt; } } // 修改:简化ExactPosition计算 public override Vector3 ExactPosition { get { if (!initialized) return base.ExactPosition; // 返回水平位置,保持Y轴为定义的高度 // RimWorld使用Y轴作为高度层,不应该随意改变 return new Vector3(horizontalPosition.x, def.Altitude, horizontalPosition.z); } } // 修改:重写ExactRotation以考虑高度变化 public override Quaternion ExactRotation => Quaternion.LookRotation(GetCurrentDirection()); // 新增:获取带高度的位置(用于特效绘制) public Vector3 PositionWithHeight { get { if (!initialized) return ExactPosition; return new Vector3( horizontalPosition.x, def.Altitude + currentArcHeight * 0.3f, // 适当缩放高度 horizontalPosition.z ); } } public override void ExposeData() { base.ExposeData(); Scribe_Values.Look(ref originPos, "originPos"); Scribe_Values.Look(ref destinationPos, "destinationPos"); Scribe_Values.Look(ref bezierControlPoint, "bezierControlPoint"); Scribe_Values.Look(ref ticksFlying, "ticksFlying", 0); Scribe_Values.Look(ref totalTicks, "totalTicks", 0); Scribe_Values.Look(ref initialized, "initialized", false); Scribe_Values.Look(ref northOffsetDistance, "northOffsetDistance", 0f); Scribe_Values.Look(ref exactPositionInt, "exactPositionInt", Vector3.zero); Scribe_Values.Look(ref curveSteepness, "curveSteepness", 1f); Scribe_Values.Look(ref currentArcHeight, "currentArcHeight", 0f); Scribe_Values.Look(ref horizontalPosition, "horizontalPosition", Vector3.zero); Scribe_Values.Look(ref Fleck_MakeFleckTick, "Fleck_MakeFleckTick", 0); Scribe_Values.Look(ref lastTickPosition, "lastTickPosition", Vector3.zero); } public override void Launch(Thing launcher, Vector3 origin, LocalTargetInfo usedTarget, LocalTargetInfo intendedTarget, ProjectileHitFlags hitFlags, bool preventFriendlyFire = false, Thing equipment = null, ThingDef targetCoverDef = null) { base.Launch(launcher, origin, usedTarget, intendedTarget, hitFlags, preventFriendlyFire, equipment, targetCoverDef); // 获取北向偏移配置 NorthArcModExtension arcExtension = def.GetModExtension(); if (arcExtension != null) { northOffsetDistance = arcExtension.northOffsetDistance; curveSteepness = arcExtension.curveSteepness; } else { northOffsetDistance = def.projectile.arcHeightFactor * 3; } // --- 初始化弹道 --- originPos = origin; destinationPos = usedTarget.CenterVector3; float speed = def.projectile.speed; if (speed <= 0) speed = 1f; float distance = (originPos - destinationPos).MagnitudeHorizontal(); totalTicks = Mathf.CeilToInt(distance / speed * 100f); if (totalTicks < 1) totalTicks = 1; ticksFlying = 0; // 贝塞尔曲线计算 Vector3 midPoint = (originPos + destinationPos) / 2f; Vector3 apexPoint = midPoint + new Vector3(0, 0, northOffsetDistance); bezierControlPoint = 2f * apexPoint - midPoint; initialized = true; horizontalPosition = origin; exactPositionInt = origin; lastTickPosition = origin; currentArcHeight = 0f; } protected override void Tick() { base.Tick(); if (this.Destroyed) { return; } if (!initialized) { base.Tick(); return; } ticksFlying++; // 1. 计算当前帧的新位置 float t = (float)ticksFlying / (float)totalTicks; if (t > 1f) t = 1f; float u = 1 - t; // 水平位移 (贝塞尔) Vector3 nextPos = (u * u * originPos) + (2 * u * t * bezierControlPoint) + (t * t * destinationPos); // 垂直高度 (抛物线) currentArcHeight = def.projectile.arcHeightFactor * GenMath.InverseParabola(t); horizontalPosition = nextPos; // 保存水平位置 if (!nextPos.ToIntVec3().InBounds(base.Map)) { this.Destroy(); return; } exactPositionInt = nextPos; // 2. 处理拖尾特效 if (TrackingDef != null && TrackingDef.tailFleckDef != null) { Fleck_MakeFleckTick++; if (Fleck_MakeFleckTick >= TrackingDef.fleckDelayTicks) { if (Fleck_MakeFleckTick >= (TrackingDef.fleckDelayTicks + TrackingDef.fleckMakeFleckTickMax)) { Fleck_MakeFleckTick = TrackingDef.fleckDelayTicks; } Map map = base.Map; if (map != null) { int count = TrackingDef.fleckMakeFleckNum.RandomInRange; // 使用带高度的位置生成尾迹 Vector3 currentPosition = PositionWithHeight; Vector3 previousPosition = lastTickPosition; if ((currentPosition - previousPosition).MagnitudeHorizontalSquared() > 0.0001f) { float moveAngle = (currentPosition - previousPosition).AngleFlat(); for (int i = 0; i < count; i++) { float velocityAngle = TrackingDef.fleckAngle.RandomInRange + moveAngle; FleckCreationData dataStatic = FleckMaker.GetDataStatic(currentPosition, map, TrackingDef.tailFleckDef, TrackingDef.fleckScale.RandomInRange); dataStatic.rotation = moveAngle; dataStatic.rotationRate = TrackingDef.fleckRotation.RandomInRange; dataStatic.velocityAngle = velocityAngle; dataStatic.velocitySpeed = TrackingDef.fleckSpeed.RandomInRange; map.flecks.CreateFleck(dataStatic); } } } } } lastTickPosition = PositionWithHeight; if (ticksFlying >= totalTicks) { Impact(null); return; } } // 修改:重写绘制方法 protected override void DrawAt(Vector3 drawLoc, bool flip = false) { if (!initialized) { base.DrawAt(drawLoc, flip); return; } // 使用固定的绘制位置,但考虑高度偏移 Vector3 finalDrawPos = ExactPosition; // 调整绘制位置以考虑抛物线高度 // 但保持Y轴在合理范围内,避免被裁剪 float heightAdjustment = currentArcHeight * 0.2f; // 缩放高度影响 finalDrawPos.y += Mathf.Clamp(heightAdjustment, -0.5f, 2f); // 绘制阴影 if (def.projectile.shadowSize > 0f) { DrawShadow(finalDrawPos); } Quaternion rotation = ExactRotation; if (def.projectile.spinRate != 0f) { float spinAngle = 60f / def.projectile.spinRate; rotation = Quaternion.AngleAxis((float)Find.TickManager.TicksGame % spinAngle / spinAngle * 360f, Vector3.up); } // 使用正确的绘制方法 if (def.projectile.useGraphicClass) { // 确保图形缩放合适 float scaleFactor = 1f + currentArcHeight * 0.1f; // 轻微缩放模拟远近 Matrix4x4 matrix = Matrix4x4.TRS( finalDrawPos, rotation, new Vector3(scaleFactor, 1f, scaleFactor) ); Graphics.DrawMesh(MeshPool.GridPlane(def.graphicData.drawSize), matrix, DrawMat, 0); } else { Graphics.DrawMesh(MeshPool.GridPlane(def.graphicData.drawSize), finalDrawPos, rotation, DrawMat, 0); } Comps_PostDraw(); } // 修改:简化阴影绘制 private void DrawShadow(Vector3 drawLoc) { if (def.projectile.shadowSize <= 0f) return; Material shadowMat = MaterialPool.MatFrom("Things/Skyfaller/SkyfallerShadowCircle", ShaderDatabase.Transparent); if (shadowMat == null) return; // 根据当前高度调整阴影大小 float normalizedHeight = Mathf.Clamp01(currentArcHeight / (def.projectile.arcHeightFactor + 0.01f)); float shadowSize = def.projectile.shadowSize * Mathf.Lerp(1f, 0.4f, normalizedHeight); Vector3 scale = new Vector3(shadowSize, 1f, shadowSize); Vector3 shadowOffset = new Vector3(0f, -0.05f, 0f); // 稍微降低阴影位置 Matrix4x4 matrix = Matrix4x4.TRS(drawLoc + shadowOffset, Quaternion.identity, scale); Graphics.DrawMesh(MeshPool.plane10, matrix, shadowMat, 0); } // 计算当前位置的切线方向(考虑高度变化) private Vector3 GetCurrentDirection() { if (!initialized || totalTicks <= 0) { return (destinationPos - originPos).normalized; } float t = (float)ticksFlying / (float)totalTicks; if (t > 1f) t = 1f; float u = 1 - t; Vector3 tangent = 2 * u * (bezierControlPoint - originPos) + 2 * t * (destinationPos - bezierControlPoint); if (tangent.MagnitudeHorizontalSquared() < 0.0001f) { return (destinationPos - originPos).normalized; } // 添加轻微的上/下方向以模拟抛物线 float verticalComponent = GenMath.InverseParabola(t) * def.projectile.arcHeightFactor * 0.3f; return (tangent.normalized + new Vector3(0, verticalComponent, 0)).normalized; } protected override void Impact(Thing hitThing, bool blockedByShield = false) { base.Impact(hitThing, blockedByShield); } // 新增:确保在保存时位置正确 public override void PostMapInit() { base.PostMapInit(); // 确保位置数据有效 if (initialized && horizontalPosition == Vector3.zero) { horizontalPosition = originPos; } } } }