384 lines
14 KiB
C#
384 lines
14 KiB
C#
using RimWorld;
|
|
using System.Collections.Generic;
|
|
using UnityEngine;
|
|
using Verse;
|
|
|
|
namespace ArachnaeSwarm
|
|
{
|
|
public class CompAbilityEffect_SpawnFlyOver : CompAbilityEffect
|
|
{
|
|
public new CompProperties_AbilitySpawnFlyOver Props => (CompProperties_AbilitySpawnFlyOver)props;
|
|
|
|
public override void Apply(LocalTargetInfo target, LocalTargetInfo dest)
|
|
{
|
|
base.Apply(target, dest);
|
|
|
|
if (parent.pawn == null || parent.pawn.Map == null)
|
|
return;
|
|
|
|
try
|
|
{
|
|
Log.Message($"FlyOver skill activated by {parent.pawn.Label} at position {parent.pawn.Position}");
|
|
Log.Message($"Target cell: {target.Cell}, Dest: {dest.Cell}");
|
|
|
|
// 计算起始和结束位置
|
|
IntVec3 startPos = CalculateStartPosition(target);
|
|
IntVec3 endPos = CalculateEndPosition(target, startPos);
|
|
|
|
Log.Message($"Final positions - Start: {startPos}, End: {endPos}");
|
|
|
|
// 验证位置是否有效
|
|
if (!startPos.InBounds(parent.pawn.Map))
|
|
{
|
|
Log.Warning($"Start position {startPos} is out of bounds, adjusting to map center");
|
|
startPos = parent.pawn.Map.Center;
|
|
}
|
|
|
|
if (!endPos.InBounds(parent.pawn.Map))
|
|
{
|
|
Log.Warning($"End position {endPos} is out of bounds, adjusting to map center");
|
|
endPos = parent.pawn.Map.Center;
|
|
}
|
|
|
|
// 根据类型创建不同的飞越物体
|
|
switch (Props.flyOverType)
|
|
{
|
|
case FlyOverType.Standard:
|
|
default:
|
|
CreateStandardFlyOver(startPos, endPos);
|
|
break;
|
|
}
|
|
|
|
// 显示效果消息
|
|
ShowEffectMessage();
|
|
}
|
|
catch (System.Exception ex)
|
|
{
|
|
Log.Error($"Error spawning fly over: {ex}");
|
|
}
|
|
}
|
|
|
|
private IntVec3 CalculateStartPosition(LocalTargetInfo target)
|
|
{
|
|
Map map = parent.pawn.Map;
|
|
|
|
switch (Props.startPosition)
|
|
{
|
|
case StartPosition.Caster:
|
|
return parent.pawn.Position;
|
|
|
|
case StartPosition.MapEdge:
|
|
return GetMapEdgePosition(map, GetDirectionFromCasterToTarget(target));
|
|
|
|
case StartPosition.CustomOffset:
|
|
return parent.pawn.Position + Props.customStartOffset;
|
|
|
|
case StartPosition.RandomMapEdge:
|
|
return GetRandomMapEdgePosition(map);
|
|
|
|
default:
|
|
return parent.pawn.Position;
|
|
}
|
|
}
|
|
|
|
private IntVec3 CalculateEndPosition(LocalTargetInfo target, IntVec3 startPos)
|
|
{
|
|
Map map = parent.pawn.Map;
|
|
IntVec3 endPos;
|
|
|
|
switch (Props.endPosition)
|
|
{
|
|
case EndPosition.TargetCell:
|
|
endPos = target.Cell;
|
|
break;
|
|
|
|
case EndPosition.OppositeMapEdge:
|
|
endPos = GetOppositeMapEdgeThroughCenter(map, startPos);
|
|
break;
|
|
|
|
case EndPosition.CustomOffset:
|
|
endPos = target.Cell + Props.customEndOffset;
|
|
break;
|
|
|
|
case EndPosition.FixedDistance:
|
|
endPos = GetFixedDistancePosition(startPos, target.Cell);
|
|
break;
|
|
|
|
case EndPosition.RandomMapEdge:
|
|
endPos = GetRandomMapEdgePosition(map);
|
|
Log.Message($"Random map edge selected as end position: {endPos}");
|
|
break;
|
|
|
|
default:
|
|
endPos = target.Cell;
|
|
break;
|
|
}
|
|
|
|
// 关键修复:确保起点和终点不同
|
|
if (startPos == endPos)
|
|
{
|
|
Log.Warning($"FlyOver start and end positions are the same: {startPos}. Adjusting end position.");
|
|
|
|
// 如果相同,将终点向随机方向移动
|
|
IntVec3 randomOffset = new IntVec3(Rand.Range(-10, 11), 0, Rand.Range(-10, 11));
|
|
endPos += randomOffset;
|
|
|
|
// 确保新位置在地图内
|
|
if (!endPos.InBounds(map))
|
|
{
|
|
endPos = map.Center;
|
|
}
|
|
}
|
|
|
|
Log.Message($"Calculated positions - Start: {startPos}, End: {endPos}, Distance: {startPos.DistanceTo(endPos)}");
|
|
return endPos;
|
|
}
|
|
|
|
// 修复的 OppositeMapEdge 逻辑:确保穿过地图中心
|
|
private IntVec3 GetOppositeMapEdgeThroughCenter(Map map, IntVec3 startPos)
|
|
{
|
|
IntVec3 center = map.Center;
|
|
|
|
// 计算从起点到中心的方向
|
|
Vector3 toCenter = (center.ToVector3() - startPos.ToVector3()).normalized;
|
|
|
|
// 如果方向为零向量,使用随机方向
|
|
if (toCenter == Vector3.zero)
|
|
{
|
|
toCenter = new Vector3(Rand.Range(-1f, 1f), 0, Rand.Range(-1f, 1f)).normalized;
|
|
Log.Message($"Using random direction to center: {toCenter}");
|
|
}
|
|
|
|
// 计算从中心到对面边缘的方向(与起点到中心的方向相同)
|
|
Vector3 fromCenter = toCenter;
|
|
|
|
Log.Message($"Calculating opposite edge: Start={startPos}, Center={center}, Direction={fromCenter}");
|
|
|
|
// 从中心出发,沿着相同方向找到对面的地图边缘
|
|
IntVec3 oppositeEdge = GetMapEdgePositionFromCenter(map, fromCenter);
|
|
|
|
Log.Message($"Found opposite edge through center: {oppositeEdge}");
|
|
return oppositeEdge;
|
|
}
|
|
|
|
// 从地图中心出发找到指定方向的地图边缘
|
|
private IntVec3 GetMapEdgePositionFromCenter(Map map, Vector3 direction)
|
|
{
|
|
IntVec3 center = map.Center;
|
|
float maxDist = Mathf.Max(map.Size.x, map.Size.z) * 0.6f;
|
|
|
|
// 从中心向指定方向延伸
|
|
for (int i = 1; i <= maxDist; i++)
|
|
{
|
|
IntVec3 testPos = center + new IntVec3(
|
|
Mathf.RoundToInt(direction.x * i),
|
|
0,
|
|
Mathf.RoundToInt(direction.z * i));
|
|
|
|
if (!testPos.InBounds(map))
|
|
{
|
|
// 找到边界位置
|
|
IntVec3 edgePos = FindClosestValidPosition(testPos, map);
|
|
Log.Message($"Found map edge from center: {edgePos} (direction: {direction}, distance: {i})");
|
|
return edgePos;
|
|
}
|
|
}
|
|
|
|
// 如果没找到边界,使用随机边缘位置
|
|
Log.Warning("Could not find map edge from center, using random edge");
|
|
return GetRandomMapEdgePosition(map);
|
|
}
|
|
|
|
private IntVec3 GetMapEdgePosition(Map map, Vector3 direction)
|
|
{
|
|
if (direction == Vector3.zero)
|
|
{
|
|
direction = new Vector3(Rand.Range(-1f, 1f), 0, Rand.Range(-1f, 1f)).normalized;
|
|
Log.Message($"Using random direction: {direction}");
|
|
}
|
|
|
|
IntVec3 center = map.Center;
|
|
float maxDist = Mathf.Max(map.Size.x, map.Size.z) * 0.6f;
|
|
|
|
// 从中心向指定方向延伸
|
|
for (int i = 1; i <= maxDist; i++)
|
|
{
|
|
IntVec3 testPos = center + new IntVec3(
|
|
Mathf.RoundToInt(direction.x * i),
|
|
0,
|
|
Mathf.RoundToInt(direction.z * i));
|
|
|
|
if (!testPos.InBounds(map))
|
|
{
|
|
// 找到边界位置
|
|
IntVec3 edgePos = FindClosestValidPosition(testPos, map);
|
|
Log.Message($"Found map edge position: {edgePos} (direction: {direction}, distance: {i})");
|
|
return edgePos;
|
|
}
|
|
}
|
|
|
|
// 如果没找到边界,使用随机边缘位置
|
|
Log.Warning("Could not find map edge in direction, using random edge");
|
|
return GetRandomMapEdgePosition(map);
|
|
}
|
|
|
|
private IntVec3 FindClosestValidPosition(IntVec3 invalidPos, Map map)
|
|
{
|
|
// 在无效位置周围寻找有效的边界位置
|
|
for (int radius = 1; radius <= 5; radius++)
|
|
{
|
|
foreach (IntVec3 pos in GenRadial.RadialPatternInRadius(radius))
|
|
{
|
|
IntVec3 testPos = invalidPos + pos;
|
|
if (testPos.InBounds(map))
|
|
{
|
|
return testPos;
|
|
}
|
|
}
|
|
}
|
|
|
|
return map.Center;
|
|
}
|
|
|
|
// 旧的 OppositeMapEdge 逻辑(保留作为参考)
|
|
private IntVec3 GetOppositeMapEdgePosition(Map map, IntVec3 startPos)
|
|
{
|
|
// 计算从起点指向地图中心的方向,然后取反
|
|
Vector3 toCenter = (map.Center.ToVector3() - startPos.ToVector3()).normalized;
|
|
Vector3 oppositeDirection = -toCenter;
|
|
|
|
// 如果方向为零向量,使用随机方向
|
|
if (oppositeDirection == Vector3.zero)
|
|
{
|
|
oppositeDirection = new Vector3(Rand.Range(-1f, 1f), 0, Rand.Range(-1f, 1f)).normalized;
|
|
}
|
|
|
|
Log.Message($"Opposite direction from {startPos}: {oppositeDirection}");
|
|
return GetMapEdgePosition(map, oppositeDirection);
|
|
}
|
|
|
|
private IntVec3 GetRandomMapEdgePosition(Map map)
|
|
{
|
|
// 随机选择一个地图边缘位置
|
|
int edge = Rand.Range(0, 4);
|
|
int x, z;
|
|
|
|
switch (edge)
|
|
{
|
|
case 0: // 上边
|
|
x = Rand.Range(0, map.Size.x);
|
|
z = 0;
|
|
break;
|
|
case 1: // 右边
|
|
x = map.Size.x - 1;
|
|
z = Rand.Range(0, map.Size.z);
|
|
break;
|
|
case 2: // 下边
|
|
x = Rand.Range(0, map.Size.x);
|
|
z = map.Size.z - 1;
|
|
break;
|
|
case 3: // 左边
|
|
default:
|
|
x = 0;
|
|
z = Rand.Range(0, map.Size.z);
|
|
break;
|
|
}
|
|
|
|
IntVec3 edgePos = new IntVec3(x, 0, z);
|
|
Log.Message($"Random map edge position: {edgePos}");
|
|
return edgePos;
|
|
}
|
|
|
|
private IntVec3 GetFixedDistancePosition(IntVec3 startPos, IntVec3 targetPos)
|
|
{
|
|
Vector3 direction = (targetPos.ToVector3() - startPos.ToVector3()).normalized;
|
|
IntVec3 endPos = startPos + new IntVec3(
|
|
(int)(direction.x * Props.flyOverDistance),
|
|
0,
|
|
(int)(direction.z * Props.flyOverDistance));
|
|
|
|
Log.Message($"Fixed distance position: {endPos} (from {startPos}, distance: {Props.flyOverDistance})");
|
|
return endPos;
|
|
}
|
|
|
|
private Vector3 GetDirectionFromCasterToTarget(LocalTargetInfo target)
|
|
{
|
|
Vector3 direction = (target.Cell.ToVector3() - parent.pawn.Position.ToVector3()).normalized;
|
|
|
|
// 如果方向为零向量,使用随机方向
|
|
if (direction == Vector3.zero)
|
|
{
|
|
direction = new Vector3(Rand.Range(-1f, 1f), 0, Rand.Range(-1f, 1f)).normalized;
|
|
Log.Message($"Using random direction: {direction}");
|
|
}
|
|
|
|
return direction;
|
|
}
|
|
|
|
private void CreateStandardFlyOver(IntVec3 startPos, IntVec3 endPos)
|
|
{
|
|
// 确保有默认的飞越定义
|
|
ThingDef flyOverDef = Props.flyOverDef ?? DefDatabase<ThingDef>.GetNamedSilentFail("ARA_HiveShip");
|
|
if (flyOverDef == null)
|
|
{
|
|
Log.Warning("No fly over def specified for standard fly over");
|
|
return;
|
|
}
|
|
|
|
FlyOver flyOver = FlyOver.MakeFlyOver(
|
|
flyOverDef,
|
|
startPos,
|
|
endPos,
|
|
parent.pawn.Map,
|
|
Props.flightSpeed,
|
|
Props.altitude
|
|
);
|
|
|
|
// 设置属性
|
|
flyOver.spawnContentsOnImpact = Props.dropContentsOnImpact;
|
|
flyOver.playFlyOverSound = Props.playFlyOverSound;
|
|
|
|
// 应用自定义音效
|
|
if (Props.customSound != null)
|
|
{
|
|
// 这里需要更复杂的逻辑来替换音效
|
|
}
|
|
|
|
Log.Message($"Standard FlyOver created: {flyOver} from {startPos} to {endPos}");
|
|
}
|
|
|
|
private void ShowEffectMessage()
|
|
{
|
|
string message = GetFlyOverMessage();
|
|
Messages.Message(message, parent.pawn, MessageTypeDefOf.NeutralEvent);
|
|
}
|
|
|
|
private string GetFlyOverMessage()
|
|
{
|
|
switch (Props.flyOverType)
|
|
{
|
|
case FlyOverType.HighAltitude:
|
|
return "HighAltitudeReconArrival".Translate(parent.pawn.LabelShort);
|
|
case FlyOverType.CargoDrop:
|
|
return "CargoDropIncoming".Translate(parent.pawn.LabelShort);
|
|
case FlyOverType.BombingRun:
|
|
return "BombingRunInitiated".Translate(parent.pawn.LabelShort);
|
|
case FlyOverType.Reconnaissance:
|
|
return "ReconnaissanceFlyOver".Translate(parent.pawn.LabelShort);
|
|
case FlyOverType.Standard:
|
|
default:
|
|
return "FlyOverInitiated".Translate(parent.pawn.LabelShort);
|
|
}
|
|
}
|
|
|
|
public override bool Valid(LocalTargetInfo target, bool throwMessages = false)
|
|
{
|
|
return base.Valid(target, throwMessages) &&
|
|
parent.pawn != null &&
|
|
parent.pawn.Map != null &&
|
|
target.Cell.IsValid &&
|
|
target.Cell.InBounds(parent.pawn.Map);
|
|
}
|
|
}
|
|
}
|