爽
This commit is contained in:
@@ -21,6 +21,9 @@ namespace WulaFallenEmpire
|
||||
Map map = parent.pawn.MapHeld;
|
||||
if (map == null) return;
|
||||
|
||||
// 新增:在施法前清除与施法者重合的物体
|
||||
ClearOverlappingObjects(map);
|
||||
|
||||
// 获取扇形区域内的所有单元格
|
||||
List<IntVec3> affectedCells = AffectedCells(target);
|
||||
|
||||
@@ -62,6 +65,80 @@ namespace WulaFallenEmpire
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 新增:清除与施法者重合的所有物体
|
||||
/// </summary>
|
||||
private void ClearOverlappingObjects(Map map)
|
||||
{
|
||||
try
|
||||
{
|
||||
IntVec3 casterPos = Pawn.Position;
|
||||
|
||||
// 获取施法者位置的所有物体
|
||||
List<Thing> thingsAtCasterPos = casterPos.GetThingList(map);
|
||||
|
||||
foreach (Thing thing in thingsAtCasterPos)
|
||||
{
|
||||
// 跳过施法者自己(除非设置影响施法者)
|
||||
if (thing == Pawn && !Props.affectCaster)
|
||||
continue;
|
||||
|
||||
// 跳过已经销毁的物体
|
||||
if (thing.Destroyed)
|
||||
continue;
|
||||
|
||||
// 只处理建筑和Pawn
|
||||
if (thing is Building || thing is Pawn)
|
||||
{
|
||||
// 检查是否应该影响这个物体
|
||||
if (ShouldAffectThing(thing))
|
||||
{
|
||||
// 播放清除特效
|
||||
PlayClearEffecter(thing, map);
|
||||
|
||||
// 处理目标
|
||||
ProcessTarget(thing);
|
||||
|
||||
Log.Message($"[AreaDestruction] Cleared overlapping object: {thing.Label} at {casterPos}");
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
catch (System.Exception ex)
|
||||
{
|
||||
Log.Warning($"[AreaDestruction] Error clearing overlapping objects: {ex.Message}");
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 新增:播放清除重合物体的特效
|
||||
/// </summary>
|
||||
private void PlayClearEffecter(Thing target, Map map)
|
||||
{
|
||||
try
|
||||
{
|
||||
if (Props.clearEffecter == null) return;
|
||||
if (target == null || target.Destroyed) return;
|
||||
|
||||
// 在目标位置播放清除特效
|
||||
Effecter effecter = Props.clearEffecter.Spawn(target.Position, map);
|
||||
|
||||
if (Props.clearEffecterMaintainTicks > 0)
|
||||
{
|
||||
// 维持效果器
|
||||
parent.AddEffecterToMaintain(effecter, target.Position, Props.clearEffecterMaintainTicks, map);
|
||||
}
|
||||
else
|
||||
{
|
||||
effecter.Cleanup();
|
||||
}
|
||||
}
|
||||
catch (System.Exception ex)
|
||||
{
|
||||
Log.Warning($"[AreaDestruction] Error playing clear effecter: {ex.Message}");
|
||||
}
|
||||
}
|
||||
|
||||
private void PlayHitEffecter(Thing target, Map map)
|
||||
{
|
||||
try
|
||||
@@ -94,9 +171,6 @@ namespace WulaFallenEmpire
|
||||
{
|
||||
effecter.Cleanup();
|
||||
}
|
||||
|
||||
// 可选:记录日志用于调试
|
||||
// Log.Message($"[AreaDestruction] Played hit effecter on {target.Label} at {target.Position}");
|
||||
}
|
||||
catch (System.Exception ex)
|
||||
{
|
||||
@@ -209,9 +283,6 @@ namespace WulaFallenEmpire
|
||||
{
|
||||
// 检查pawn是否还"活着"(没有核心部位缺失时可能还能存活)
|
||||
CheckPawnViability(targetPawn);
|
||||
|
||||
// 可选:记录日志用于调试
|
||||
// Log.Message($"[AreaDestruction] Destroyed {partsDestroyed} body parts on {pawnInfo}");
|
||||
}
|
||||
}
|
||||
catch (System.Exception ex)
|
||||
|
||||
@@ -12,6 +12,10 @@ namespace WulaFallenEmpire
|
||||
public EffecterDef castEffecter;
|
||||
public int castEffecterMaintainTicks = 60;
|
||||
|
||||
// 清除特效(用于清除重合物体)
|
||||
public EffecterDef clearEffecter;
|
||||
public int clearEffecterMaintainTicks = 30;
|
||||
|
||||
// 命中特效(在目标位置)
|
||||
public EffecterDef hitEffecter;
|
||||
public int hitEffecterMaintainTicks = 30;
|
||||
|
||||
@@ -1,4 +1,5 @@
|
||||
using System.Collections.Generic;
|
||||
using System.Linq; // 添加这个 using 指令
|
||||
using RimWorld;
|
||||
using UnityEngine;
|
||||
using Verse;
|
||||
@@ -21,83 +22,101 @@ namespace WulaFallenEmpire
|
||||
Pawn caster = parent.pawn;
|
||||
Map map = caster.Map;
|
||||
|
||||
// 新增:在施法前验证并调整目标位置
|
||||
LocalTargetInfo adjustedTarget = AdjustTargetForBuildings(target, caster, map);
|
||||
|
||||
// 使用调整后的目标位置
|
||||
IntVec3 finalTargetCell = adjustedTarget.Cell;
|
||||
|
||||
// 使用自定义或默认的入口特效
|
||||
if (Props.customEntryFleck != null)
|
||||
{
|
||||
// 自定义入口粒子效果
|
||||
FleckMaker.Static(caster.Position, map, Props.customEntryFleck);
|
||||
}
|
||||
else
|
||||
{
|
||||
// 默认入口粒子效果
|
||||
FleckMaker.Static(caster.Position, map, FleckDefOf.PsycastSkipFlashEntry);
|
||||
}
|
||||
|
||||
// 使用自定义或默认的出口特效
|
||||
if (Props.customExitFleck != null)
|
||||
{
|
||||
// 自定义出口粒子效果
|
||||
FleckMaker.Static(target.Cell, map, Props.customExitFleck);
|
||||
// 如果需要更大的效果,可以创建多个粒子
|
||||
FleckMaker.Static(finalTargetCell, map, Props.customExitFleck);
|
||||
if (Props.effectScale > 1.5f)
|
||||
{
|
||||
for (int i = 0; i < Mathf.FloorToInt(Props.effectScale); i++)
|
||||
{
|
||||
Vector3 offset = new Vector3(Rand.Range(-0.5f, 0.5f), 0f, Rand.Range(-0.5f, 0.5f));
|
||||
FleckMaker.Static(target.Cell.ToVector3Shifted() + offset, map, Props.customExitFleck);
|
||||
FleckMaker.Static(finalTargetCell.ToVector3Shifted() + offset, map, Props.customExitFleck);
|
||||
}
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
// 默认出口粒子效果
|
||||
FleckMaker.Static(target.Cell, map, FleckDefOf.PsycastSkipInnerExit);
|
||||
FleckMaker.Static(target.Cell, map, FleckDefOf.PsycastSkipOuterRingExit);
|
||||
FleckMaker.Static(finalTargetCell, map, FleckDefOf.PsycastSkipInnerExit);
|
||||
FleckMaker.Static(finalTargetCell, map, FleckDefOf.PsycastSkipOuterRingExit);
|
||||
}
|
||||
|
||||
// 播放传送音效
|
||||
SoundDefOf.Psycast_Skip_Entry.PlayOneShot(new TargetInfo(caster.Position, map));
|
||||
SoundDefOf.Psycast_Skip_Exit.PlayOneShot(new TargetInfo(target.Cell, map));
|
||||
SoundDefOf.Psycast_Skip_Exit.PlayOneShot(new TargetInfo(finalTargetCell, map));
|
||||
|
||||
// 存储调整后的目标位置,供Apply方法使用
|
||||
// 注意:这里使用反射或其他方法来设置目标,因为parent.ability可能不可直接访问
|
||||
StoreAdjustedTarget(finalTargetCell);
|
||||
},
|
||||
ticksAwayFromCast = 5
|
||||
};
|
||||
}
|
||||
|
||||
// 新增:存储调整后的目标位置
|
||||
private void StoreAdjustedTarget(IntVec3 targetCell)
|
||||
{
|
||||
// 这里可以使用一个字段来存储调整后的目标
|
||||
// 由于RimWorld的Ability类结构,我们可能需要使用其他方式
|
||||
// 暂时使用一个字段来存储
|
||||
this.adjustedTargetCell = targetCell;
|
||||
}
|
||||
|
||||
private IntVec3 adjustedTargetCell = IntVec3.Invalid;
|
||||
|
||||
public override void Apply(LocalTargetInfo target, LocalTargetInfo dest)
|
||||
{
|
||||
base.Apply(target, dest);
|
||||
|
||||
if (!target.IsValid)
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
Pawn caster = parent.pawn;
|
||||
Map map = caster.Map;
|
||||
|
||||
// 新增:最终目标位置验证 - 优先使用调整后的目标
|
||||
IntVec3 finalTargetCell = this.adjustedTargetCell.IsValid ? this.adjustedTargetCell : target.Cell;
|
||||
finalTargetCell = ValidateAndAdjustTarget(finalTargetCell, caster, map);
|
||||
|
||||
// 重置调整目标
|
||||
this.adjustedTargetCell = IntVec3.Invalid;
|
||||
|
||||
// 如果无法找到有效位置,取消传送
|
||||
if (!CanTeleportTo(finalTargetCell, map))
|
||||
{
|
||||
Messages.Message("TeleportFailedNoValidLocation".Translate(), caster, MessageTypeDefOf.RejectInput);
|
||||
return;
|
||||
}
|
||||
|
||||
// 使用自定义或默认的入口效果器
|
||||
EffecterDef entryEffecter = Props.customEntryEffecter ?? EffecterDefOf.Skip_Entry;
|
||||
Effecter entryEffect = entryEffecter.Spawn(caster, map);
|
||||
|
||||
// 应用效果缩放
|
||||
if (Props.effectScale != 1.0f && entryEffect is Effecter effect)
|
||||
{
|
||||
// 这里可以添加效果缩放的逻辑
|
||||
// 注意:Effecter类可能没有直接的缩放属性,需要根据具体实现调整
|
||||
}
|
||||
|
||||
parent.AddEffecterToMaintain(entryEffect, caster.Position, 60);
|
||||
|
||||
// 使用自定义或默认的出口效果器
|
||||
EffecterDef exitEffecter = Props.customExitEffecter ?? EffecterDefOf.Skip_Exit;
|
||||
Effecter exitEffect = exitEffecter.Spawn(target.Cell, map);
|
||||
parent.AddEffecterToMaintain(exitEffect, target.Cell, 60);
|
||||
Effecter exitEffect = exitEffecter.Spawn(finalTargetCell, map);
|
||||
parent.AddEffecterToMaintain(exitEffect, finalTargetCell, 60);
|
||||
|
||||
// 唤醒可能休眠的组件
|
||||
caster.TryGetComp<CompCanBeDormant>()?.WakeUp();
|
||||
|
||||
// 执行传送
|
||||
caster.Position = target.Cell;
|
||||
caster.Position = finalTargetCell;
|
||||
caster.Notify_Teleported();
|
||||
|
||||
// 如果是玩家阵营,解除战争迷雾
|
||||
@@ -115,10 +134,81 @@ namespace WulaFallenEmpire
|
||||
// 播放到达时的喧嚣效果
|
||||
if (Props.destClamorType != null)
|
||||
{
|
||||
// 根据效果缩放调整喧嚣半径
|
||||
float adjustedRadius = Props.destClamorRadius * Props.effectScale;
|
||||
GenClamor.DoClamor(caster, target.Cell, adjustedRadius, Props.destClamorType);
|
||||
GenClamor.DoClamor(caster, finalTargetCell, adjustedRadius, Props.destClamorType);
|
||||
}
|
||||
|
||||
// 记录传送调整信息(调试用)
|
||||
if (finalTargetCell != target.Cell)
|
||||
{
|
||||
Log.Message($"[TeleportSelf] AI传送位置从 {target.Cell} 调整到 {finalTargetCell}");
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 新增:在施法前调整目标位置,防止传送到建筑上
|
||||
/// </summary>
|
||||
private LocalTargetInfo AdjustTargetForBuildings(LocalTargetInfo originalTarget, Pawn caster, Map map)
|
||||
{
|
||||
IntVec3 originalCell = originalTarget.Cell;
|
||||
|
||||
// 如果目标位置不可传送,寻找最近的可行位置
|
||||
if (!CanTeleportTo(originalCell, map))
|
||||
{
|
||||
IntVec3 adjustedCell = FindNearestValidTeleportPosition(originalCell, caster, map);
|
||||
if (adjustedCell.IsValid)
|
||||
{
|
||||
return new LocalTargetInfo(adjustedCell);
|
||||
}
|
||||
}
|
||||
|
||||
return originalTarget;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 新增:最终目标位置验证
|
||||
/// </summary>
|
||||
private IntVec3 ValidateAndAdjustTarget(IntVec3 targetCell, Pawn caster, Map map)
|
||||
{
|
||||
// 如果目标位置不可传送,寻找替代位置
|
||||
if (!CanTeleportTo(targetCell, map))
|
||||
{
|
||||
IntVec3 adjustedCell = FindNearestValidTeleportPosition(targetCell, caster, map);
|
||||
if (adjustedCell.IsValid)
|
||||
{
|
||||
return adjustedCell;
|
||||
}
|
||||
}
|
||||
|
||||
return targetCell;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 新增:寻找最近的可行传送位置
|
||||
/// </summary>
|
||||
private IntVec3 FindNearestValidTeleportPosition(IntVec3 center, Pawn caster, Map map, int maxRadius = 15)
|
||||
{
|
||||
// 首先检查中心点本身
|
||||
if (CanTeleportTo(center, map))
|
||||
return center;
|
||||
|
||||
// 在逐渐增大的半径内搜索
|
||||
for (int radius = 1; radius <= maxRadius; radius++)
|
||||
{
|
||||
// 使用 GenRadial.RadialPattern 而不是 RadialCellsAround
|
||||
int numCells = GenRadial.NumCellsInRadius(radius);
|
||||
for (int i = 0; i < numCells; i++)
|
||||
{
|
||||
IntVec3 cell = center + GenRadial.RadialPattern[i];
|
||||
if (cell.InBounds(map) && CanTeleportTo(cell, map))
|
||||
{
|
||||
return cell;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// 如果找不到有效位置,返回无效位置
|
||||
return IntVec3.Invalid;
|
||||
}
|
||||
|
||||
public override bool Valid(LocalTargetInfo target, bool showMessages = true)
|
||||
|
||||
@@ -25,6 +25,10 @@ namespace WulaFallenEmpire
|
||||
public FleckDef customExitFleck;
|
||||
public float effectScale = 1.0f; // 效果缩放比例
|
||||
|
||||
// 新增:位置调整设置
|
||||
public int maxPositionAdjustRadius = 15; // 最大位置调整半径
|
||||
public bool allowPositionAdjustment = true; // 是否允许自动调整位置
|
||||
|
||||
public CompProperties_AbilityTeleportSelf()
|
||||
{
|
||||
compClass = typeof(CompAbilityEffect_TeleportSelf);
|
||||
|
||||
@@ -8,10 +8,9 @@ namespace WulaFallenEmpire
|
||||
public class Projectile_NorthArcTrail : Projectile_Explosive
|
||||
{
|
||||
// --- 弹道部分变量 ---
|
||||
// 通过ModExtension配置的向北偏移高度
|
||||
public float northOffsetDistance = 0f;
|
||||
|
||||
private Vector3 exactPositionInt; // 用于存储我们自己计算的位置
|
||||
private Vector3 exactPositionInt;
|
||||
private float curveSteepness = 1f;
|
||||
|
||||
private Vector3 originPos;
|
||||
@@ -24,9 +23,12 @@ namespace WulaFallenEmpire
|
||||
// --- 尾迹部分变量 ---
|
||||
private TrackingBulletDef trackingDefInt;
|
||||
private int Fleck_MakeFleckTick;
|
||||
private Vector3 lastTickPosition; // 记录上一帧位置用于计算拖尾方向
|
||||
private Vector3 lastTickPosition;
|
||||
|
||||
// 新增:绘制相关变量
|
||||
private float currentArcHeight;
|
||||
private const float DRAW_ALTITUDE_OFFSET = 15f; // 增加绘制高度偏移
|
||||
|
||||
// 获取 XML 中的扩展数据
|
||||
public TrackingBulletDef TrackingDef
|
||||
{
|
||||
get
|
||||
@@ -36,7 +38,6 @@ namespace WulaFallenEmpire
|
||||
trackingDefInt = def.GetModExtension<TrackingBulletDef>();
|
||||
if (trackingDefInt == null)
|
||||
{
|
||||
// 如果没配置,给一个空的默认值防止报错,或者只报错一次
|
||||
trackingDefInt = new TrackingBulletDef();
|
||||
}
|
||||
}
|
||||
@@ -44,13 +45,26 @@ namespace WulaFallenEmpire
|
||||
}
|
||||
}
|
||||
|
||||
public override Vector3 ExactPosition => exactPositionInt; // 重写属性,让游戏获取我们计算的位置
|
||||
public override Quaternion ExactRotation => Quaternion.LookRotation(GetCurrentDirection()); // 弹头朝向当前移动方向
|
||||
// 修改:重写绘制位置,确保在正确的高度
|
||||
public override Vector3 ExactPosition
|
||||
{
|
||||
get
|
||||
{
|
||||
if (!initialized)
|
||||
return base.ExactPosition;
|
||||
|
||||
// 返回计算的位置,但保持Y轴为绘制高度
|
||||
Vector3 pos = exactPositionInt;
|
||||
pos.y = def.Altitude + currentArcHeight + DRAW_ALTITUDE_OFFSET;
|
||||
return pos;
|
||||
}
|
||||
}
|
||||
|
||||
public override Quaternion ExactRotation => Quaternion.LookRotation(GetCurrentDirection());
|
||||
|
||||
public override void ExposeData()
|
||||
{
|
||||
base.ExposeData();
|
||||
// 保存弹道数据
|
||||
Scribe_Values.Look(ref originPos, "originPos");
|
||||
Scribe_Values.Look(ref destinationPos, "destinationPos");
|
||||
Scribe_Values.Look(ref bezierControlPoint, "bezierControlPoint");
|
||||
@@ -60,8 +74,8 @@ namespace WulaFallenEmpire
|
||||
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 Fleck_MakeFleckTick, "Fleck_MakeFleckTick", 0);
|
||||
Scribe_Values.Look(ref lastTickPosition, "lastTickPosition", Vector3.zero);
|
||||
}
|
||||
@@ -79,8 +93,7 @@ namespace WulaFallenEmpire
|
||||
}
|
||||
else
|
||||
{
|
||||
// 如果没有配置,则使用默认值,或者从 projectile.arcHeightFactor 获取参考值
|
||||
northOffsetDistance = def.projectile.arcHeightFactor * 3; // 将arcHeightFactor转换为北向偏移距离
|
||||
northOffsetDistance = def.projectile.arcHeightFactor * 3;
|
||||
}
|
||||
|
||||
// --- 初始化弹道 ---
|
||||
@@ -90,36 +103,27 @@ namespace WulaFallenEmpire
|
||||
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;
|
||||
// 顶点 (中点向北偏移 X 格)
|
||||
Vector3 apexPoint = midPoint + new Vector3(0, 0, northOffsetDistance);
|
||||
// 控制点 P1 = 2 * 顶点 - 中点
|
||||
bezierControlPoint = 2f * apexPoint - midPoint;
|
||||
|
||||
initialized = true;
|
||||
|
||||
// 初始化我们自己的位置
|
||||
exactPositionInt = origin;
|
||||
|
||||
// --- 初始化尾迹 ---
|
||||
lastTickPosition = origin;
|
||||
currentArcHeight = 0f;
|
||||
}
|
||||
|
||||
protected override void Tick()
|
||||
{
|
||||
// 首先调用base.Tick(),让它处理组件更新(比如拖尾特效)和ticksToImpact
|
||||
base.Tick();
|
||||
|
||||
// 如果base.Tick()已经处理了撞击,我们就不再继续
|
||||
if (this.Destroyed)
|
||||
{
|
||||
return;
|
||||
@@ -133,61 +137,54 @@ namespace WulaFallenEmpire
|
||||
|
||||
ticksFlying++;
|
||||
|
||||
// 1. 计算当前帧的新位置 (贝塞尔曲线)
|
||||
// 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);
|
||||
|
||||
// 垂直高度 (抛物线)
|
||||
float arcHeight = def.projectile.arcHeightFactor * GenMath.InverseParabola(t);
|
||||
nextPos.y = arcHeight;
|
||||
currentArcHeight = def.projectile.arcHeightFactor * GenMath.InverseParabola(t);
|
||||
nextPos.y = 0f; // 水平位置不包含高度
|
||||
|
||||
// 检查边界
|
||||
if (!nextPos.ToIntVec3().InBounds(base.Map))
|
||||
{
|
||||
this.Destroy();
|
||||
return;
|
||||
}
|
||||
|
||||
// 更新我们自己的位置
|
||||
exactPositionInt = nextPos;
|
||||
|
||||
// 2. 处理拖尾特效
|
||||
// 只有当这一帧移动了,且配置了 DefModExtension 时才生成
|
||||
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 = this.ExactPosition;
|
||||
Vector3 currentPosition = this.ExactPosition; // 使用重写后的ExactPosition
|
||||
Vector3 previousPosition = lastTickPosition;
|
||||
|
||||
// 仅当有位移时才计算角度,防止原地鬼畜
|
||||
if ((currentPosition - previousPosition).MagnitudeHorizontalSquared() > 0.0001f)
|
||||
{
|
||||
float moveAngle = (currentPosition - previousPosition).AngleFlat();
|
||||
|
||||
for (int i = 0; i < count; i++)
|
||||
{
|
||||
// 这里的逻辑完全照搬原来的 BulletWithTrail
|
||||
float velocityAngle = TrackingDef.fleckAngle.RandomInRange + moveAngle;
|
||||
|
||||
FleckCreationData dataStatic = FleckMaker.GetDataStatic(currentPosition, map, TrackingDef.tailFleckDef, TrackingDef.fleckScale.RandomInRange);
|
||||
dataStatic.rotation = moveAngle; // 粒子朝向跟随移动方向
|
||||
dataStatic.rotation = moveAngle;
|
||||
dataStatic.rotationRate = TrackingDef.fleckRotation.RandomInRange;
|
||||
dataStatic.velocityAngle = velocityAngle;
|
||||
dataStatic.velocitySpeed = TrackingDef.fleckSpeed.RandomInRange;
|
||||
@@ -198,10 +195,8 @@ namespace WulaFallenEmpire
|
||||
}
|
||||
}
|
||||
|
||||
// 3. 更新上一帧位置
|
||||
lastTickPosition = nextPos;
|
||||
lastTickPosition = ExactPosition; // 使用重写后的ExactPosition
|
||||
|
||||
// 4. 判定到达目标或倒计时爆炸
|
||||
if (ticksFlying >= totalTicks)
|
||||
{
|
||||
Impact(null);
|
||||
@@ -209,6 +204,58 @@ namespace WulaFallenEmpire
|
||||
}
|
||||
}
|
||||
|
||||
// 修改:重写绘制方法,确保正确绘制
|
||||
protected override void DrawAt(Vector3 drawLoc, bool flip = false)
|
||||
{
|
||||
if (!initialized)
|
||||
{
|
||||
base.DrawAt(drawLoc, flip);
|
||||
return;
|
||||
}
|
||||
|
||||
// 使用我们计算的位置进行绘制
|
||||
Vector3 finalDrawPos = ExactPosition;
|
||||
|
||||
// 绘制阴影
|
||||
if (def.projectile.shadowSize > 0f)
|
||||
{
|
||||
DrawShadow(finalDrawPos, currentArcHeight);
|
||||
}
|
||||
|
||||
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)
|
||||
{
|
||||
Graphic.Draw(finalDrawPos, base.Rotation, this, rotation.eulerAngles.y);
|
||||
}
|
||||
else
|
||||
{
|
||||
Graphics.DrawMesh(MeshPool.GridPlane(def.graphicData.drawSize), finalDrawPos, rotation, DrawMat, 0);
|
||||
}
|
||||
|
||||
Comps_PostDraw();
|
||||
}
|
||||
|
||||
// 修改:重写阴影绘制,使用正确的高度
|
||||
private void DrawShadow(Vector3 drawLoc, float height)
|
||||
{
|
||||
Material shadowMat = MaterialPool.MatFrom("Things/Skyfaller/SkyfallerShadowCircle", ShaderDatabase.Transparent);
|
||||
if (shadowMat == null) return;
|
||||
|
||||
float shadowSize = def.projectile.shadowSize * Mathf.Lerp(1f, 0.6f, height / (def.projectile.arcHeightFactor + 1f));
|
||||
Vector3 scale = new Vector3(shadowSize, 1f, shadowSize);
|
||||
Vector3 shadowOffset = new Vector3(0f, -0.01f, 0f);
|
||||
|
||||
Matrix4x4 matrix = Matrix4x4.TRS(drawLoc + shadowOffset, Quaternion.identity, scale);
|
||||
Graphics.DrawMesh(MeshPool.plane10, matrix, shadowMat, 0);
|
||||
}
|
||||
|
||||
// 计算当前位置的切线方向
|
||||
private Vector3 GetCurrentDirection()
|
||||
{
|
||||
@@ -220,13 +267,9 @@ namespace WulaFallenEmpire
|
||||
float t = (float)ticksFlying / (float)totalTicks;
|
||||
if (t > 1f) t = 1f;
|
||||
|
||||
// 计算贝塞尔曲线的导数(切线向量)
|
||||
// 对于二次贝塞尔曲线 B(t) = (1-t)²P₀ + 2(1-t)tP₁ + t²P₂
|
||||
// 导数 B'(t) = 2(1-t)(P₁-P₀) + 2t(P₂-P₁)
|
||||
float u = 1 - t;
|
||||
Vector3 tangent = 2 * u * (bezierControlPoint - originPos) + 2 * t * (destinationPos - bezierControlPoint);
|
||||
|
||||
// 如果切线向量为零,则使用默认方向
|
||||
if (tangent.MagnitudeHorizontalSquared() < 0.0001f)
|
||||
{
|
||||
return (destinationPos - originPos).normalized;
|
||||
@@ -240,4 +283,4 @@ namespace WulaFallenEmpire
|
||||
base.Impact(hitThing, blockedByShield);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -8,20 +8,23 @@ namespace WulaFallenEmpire
|
||||
public class CompAreaDamage : ThingComp
|
||||
{
|
||||
private int ticksUntilNextDamage;
|
||||
private bool enabled;
|
||||
|
||||
public CompProperties_AreaDamage Props => (CompProperties_AreaDamage)props;
|
||||
public bool Enabled => enabled;
|
||||
|
||||
public override void Initialize(CompProperties props)
|
||||
{
|
||||
base.Initialize(props);
|
||||
ticksUntilNextDamage = Props.damageIntervalTicks;
|
||||
enabled = Props.startEnabled;
|
||||
}
|
||||
|
||||
public override void CompTick()
|
||||
{
|
||||
base.CompTick();
|
||||
|
||||
if (!parent.Spawned)
|
||||
if (!parent.Spawned || !enabled)
|
||||
return;
|
||||
|
||||
ticksUntilNextDamage--;
|
||||
@@ -32,6 +35,16 @@ namespace WulaFallenEmpire
|
||||
}
|
||||
}
|
||||
|
||||
public void Toggle()
|
||||
{
|
||||
enabled = !enabled;
|
||||
}
|
||||
|
||||
public void SetEnabled(bool newState)
|
||||
{
|
||||
enabled = newState;
|
||||
}
|
||||
|
||||
private void DoAreaDamage()
|
||||
{
|
||||
Map map = parent.Map;
|
||||
@@ -227,6 +240,50 @@ namespace WulaFallenEmpire
|
||||
{
|
||||
base.PostExposeData();
|
||||
Scribe_Values.Look(ref ticksUntilNextDamage, "ticksUntilNextDamage", Props.damageIntervalTicks);
|
||||
Scribe_Values.Look(ref enabled, "enabled", Props.startEnabled);
|
||||
}
|
||||
|
||||
public override IEnumerable<Gizmo> CompGetGizmosExtra()
|
||||
{
|
||||
// 只有拥有者可以操作开关
|
||||
if (parent.Faction != null && parent.Faction != Faction.OfPlayer && !parent.Faction.IsPlayer)
|
||||
yield break;
|
||||
|
||||
// 创建切换开关的 Gizmo
|
||||
Command_Toggle toggleCommand = new Command_Toggle
|
||||
{
|
||||
defaultLabel = Props.toggleLabel.Translate(),
|
||||
defaultDesc = Props.toggleDescription.Translate(),
|
||||
icon = LoadToggleIcon(),
|
||||
isActive = () => enabled,
|
||||
toggleAction = () => Toggle()
|
||||
};
|
||||
|
||||
yield return toggleCommand;
|
||||
}
|
||||
|
||||
private Texture2D LoadToggleIcon()
|
||||
{
|
||||
if (!string.IsNullOrEmpty(Props.toggleIconPath))
|
||||
{
|
||||
return ContentFinder<Texture2D>.Get(Props.toggleIconPath, false);
|
||||
}
|
||||
|
||||
// 默认图标
|
||||
return TexCommand.DesirePower;
|
||||
}
|
||||
|
||||
public override string CompInspectStringExtra()
|
||||
{
|
||||
string baseString = base.CompInspectStringExtra();
|
||||
string statusText = enabled ?
|
||||
"AreaDamageEnabled".Translate() :
|
||||
"AreaDamageDisabled".Translate();
|
||||
|
||||
if (string.IsNullOrEmpty(baseString))
|
||||
return statusText;
|
||||
else
|
||||
return baseString + "\n" + statusText;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -33,6 +33,12 @@ namespace WulaFallenEmpire
|
||||
// 特殊效果
|
||||
public bool showDamageNumbers = false; // 显示伤害数值(调试用)
|
||||
|
||||
// 新增:开关设置
|
||||
public bool startEnabled = true; // 初始是否启用
|
||||
public string toggleLabel = "Toggle Area Damage"; // 开关标签
|
||||
public string toggleDescription = "Enable or disable the area damage effect"; // 开关描述
|
||||
public string toggleIconPath; // 开关图标路径
|
||||
|
||||
public CompProperties_AreaDamage()
|
||||
{
|
||||
compClass = typeof(CompAreaDamage);
|
||||
|
||||
Reference in New Issue
Block a user