diff --git a/1.6/1.6/Assemblies/WulaFallenEmpire.dll b/1.6/1.6/Assemblies/WulaFallenEmpire.dll index 0e1e3c11..7d4ba905 100644 Binary files a/1.6/1.6/Assemblies/WulaFallenEmpire.dll and b/1.6/1.6/Assemblies/WulaFallenEmpire.dll differ diff --git a/1.6/1.6/Defs/AbilityDefs/WULA_Misc_Ability.xml b/1.6/1.6/Defs/AbilityDefs/WULA_Misc_Ability.xml index 507bc130..bee743aa 100644 --- a/1.6/1.6/Defs/AbilityDefs/WULA_Misc_Ability.xml +++ b/1.6/1.6/Defs/AbilityDefs/WULA_Misc_Ability.xml @@ -55,8 +55,6 @@ True False true - true - true 600 Verb_CastAbility @@ -64,7 +62,7 @@ 1 WarqueenWarUrchinsSpawned - true + false true false @@ -86,15 +84,112 @@
  • 18 - 12 + 18 true false false - Fire_SpewShort - 60 - Fire_Spew - 30 + WULA_AreaDestruction_Shockwave + WULA_AreaDestruction_Hit
  • + + WULA_AreaDestruction_Hit + +
  • + SubEffecter_SprayerTriggered + WULA_Mote_ChargeLanceShot + 1~4 + 0.4~0.8 + 20~40 + 135~225 + 0.01 + OnSource +
  • +
  • + SubEffecter_SprayerTriggered + WULA_Mote_ChargeLanceShot + 2~3 + 0.4~0.8 + 10~20 + 135~225 + 0.01 + OnSource +
  • +
    + 0.25~0.25 + 0.1 +
    + + WULA_AreaDestruction_Shockwave + +
  • + SubEffecter_SprayerTriggered + Fleck_BlastMechBandShockwave + 1 + OnSource + true + 0~0 +
  • +
  • + SubEffecter_SprayerTriggered + WULA_AreaDestruction_Mainwave + 1 + 1 + 20 + 0 + 0.01 + OnSource +
  • +
  • + SubEffecter_SprayerTriggered + FlashMechBand + 1 + OnSource + true + 0~0 +
  • +
  • + SubEffecter_SprayerTriggered + Mote_RedFlashStrong + 1 + OnSource + 24 +
  • +
    +
    + + WULA_Mote_ChargeLanceShot + + Things/Projectile/ChargeLanceShot + (0.75,1.5) + MoteGlow + (0.6,0.1,0.6,1) + + Projectile + + 0.2 + 0.4 + 0.2 + -0.8 + true + + + + WULA_AreaDestruction_Mainwave + + Wula/Mote/WULA_AreaDestruction_Mainwave + (15,15) + MoteGlow + (0.6,0.1,0.6,0.5) + + Floor + + 0.2 + 0.4 + 0.2 + 0.25 + true + + \ No newline at end of file diff --git a/1.6/1.6/Defs/PawnKinds/PawnKinds_Wula.xml b/1.6/1.6/Defs/PawnKinds/PawnKinds_Wula.xml index 1b6f3530..56d51b8a 100644 --- a/1.6/1.6/Defs/PawnKinds/PawnKinds_Wula.xml +++ b/1.6/1.6/Defs/PawnKinds/PawnKinds_Wula.xml @@ -231,6 +231,44 @@ 1 9999~9999 + + Wula_Psi_Titan + + Wula_Psi_Titan + PlayerColony + false + false + true + 1000 + + true + 1 + + Wula/Things/Wula_Mech_Mobile_Factory/Flying/Wula_Mech_Mobile_Factory_Flying_ + 1 + 1 + 2 + false + + +
  • + + Wula/Things/Wula_Psi_Titan/Bodies/Naked_Thin + Wula/Things/WULA_Cat/AllegianceOverlays/None + + Graphic_Multi + 10 + +
  • +
    + 0 + + 0.4 + + +
  • WULA_PsiCrusher
  • +
    +
    diff --git a/1.6/1.6/Defs/ThingDefs_Misc/Apperals/WULA_Apparel.xml b/1.6/1.6/Defs/ThingDefs_Misc/Apperals/WULA_Apparel.xml index 77bfc54f..3ccea0d4 100644 --- a/1.6/1.6/Defs/ThingDefs_Misc/Apperals/WULA_Apparel.xml +++ b/1.6/1.6/Defs/ThingDefs_Misc/Apperals/WULA_Apparel.xml @@ -598,8 +598,8 @@
  • - 3 - 50 + 5 + 100 2400 30 @@ -609,7 +609,7 @@ Shield_Break BulletShieldGenerator_Reactivate - (0.9, 0.2, 0.2, 0.2) + (0.9, 0.2, 0.2, 0.5) true diff --git a/1.6/1.6/Defs/ThingDefs_Misc/Weapons/WULA_Weapon.xml b/1.6/1.6/Defs/ThingDefs_Misc/Weapons/WULA_Weapon.xml index fb0d3668..86899356 100644 --- a/1.6/1.6/Defs/ThingDefs_Misc/Weapons/WULA_Weapon.xml +++ b/1.6/1.6/Defs/ThingDefs_Misc/Weapons/WULA_Weapon.xml @@ -48,7 +48,7 @@ Wula/Projectile/WULA_Shrapnel Graphic_Single - (1,2) + (1.5,2)
  • diff --git a/1.6/1.6/Defs/ThingDefs_Races/Races_Wulaspecies.xml b/1.6/1.6/Defs/ThingDefs_Races/Races_Wulaspecies.xml index 9886bb05..f1d78fa9 100644 --- a/1.6/1.6/Defs/ThingDefs_Races/Races_Wulaspecies.xml +++ b/1.6/1.6/Defs/ThingDefs_Races/Races_Wulaspecies.xml @@ -1445,6 +1445,70 @@
  • + + Wula_Psi_Titan + + 由乌拉帝国大教堂所开发的重型灵能机械体,以短距离折跃优雅地穿梭于炮火间,并用灵能盾抵挡敌方射弹侵袭。该机体不仅镌刻了破坏力强大的星光追猎术式用以发起远距离跟踪打击,还拥有一系列改变战局的灵能能力。\n\n但是在近身搏斗中,灵能泰坦是一个可笑的对手,并且它的秘文纹路很容易遭到外力破坏,它的本体无法吸收太多伤害! + Wula/Things/Wula_Psi_Titan/Wula_Psi_Titan_Icon + + 1 + 1 + 5 + + 9999 + 0 + + + Mech_Warqueen + 30 + +
  • + MechanoidFullyFormed + 0 + Pawn_Mech_Warqueen_Wounded + Pawn_Mech_Warqueen_Death + Pawn_Mech_Warqueen_Call +
  • +
    + 25 + + 1 + +
    + +
  • + + +
  • Blunt
  • + + 360 + 8 + Torso + true + +
    + +
  • + 36000 + 8.2 + 8.4 + 0.01 + 4.0 + false +
  • + +
  • + + + Drafted + + + WULA_Hover_FlyNorth + WULA_Hover_FlyEast + WULA_Hover_FlySouth +
  • +
    +
    diff --git a/1.6/1.6/Languages/ChineseSimplified (简体中文)/Keyed/Misc_Gameplay.xml b/1.6/1.6/Languages/ChineseSimplified (简体中文)/Keyed/Misc_Gameplay.xml index e8141d5c..2f7b436b 100644 --- a/1.6/1.6/Languages/ChineseSimplified (简体中文)/Keyed/Misc_Gameplay.xml +++ b/1.6/1.6/Languages/ChineseSimplified (简体中文)/Keyed/Misc_Gameplay.xml @@ -346,15 +346,5 @@ - 逾期 - 冷却中 - 未激活 - - - - 区域护盾状态:\n最大生命值:{0}\n保护半径:{1}格\n破碎冷却:{3}秒 - - 拦截设置:\n- 地面投射物:{0}\n- 空中投射物:{1}\n- 非敌对投射物:{2} - - 冷却剩余时间:{0}秒 - 护盾未激活:\n- 装备未被穿戴\n- 穿戴者死亡或倒下\n- 护盾已破碎 + 未激活-移动中 \ No newline at end of file diff --git a/Content/Textures/Wula/Mote/WULA_AreaDestruction_Mainwave.png b/Content/Textures/Wula/Mote/WULA_AreaDestruction_Mainwave.png new file mode 100644 index 00000000..4a3b3fbc Binary files /dev/null and b/Content/Textures/Wula/Mote/WULA_AreaDestruction_Mainwave.png differ diff --git a/Content/Textures/Wula/Things/Wula_Psi_Titan/Bodies/Naked_Thin_south.png b/Content/Textures/Wula/Things/Wula_Psi_Titan/Bodies/Naked_Thin_south.png index 0fe4b028..1fa5c0cf 100644 Binary files a/Content/Textures/Wula/Things/Wula_Psi_Titan/Bodies/Naked_Thin_south.png and b/Content/Textures/Wula/Things/Wula_Psi_Titan/Bodies/Naked_Thin_south.png differ diff --git a/Source/WulaFallenEmpire/Ability/WULA_AbilityAreaDestruction/CompAbilityEffect_AreaDestruction.cs b/Source/WulaFallenEmpire/Ability/WULA_AbilityAreaDestruction/CompAbilityEffect_AreaDestruction.cs index 55cc81bc..e4ad9ea8 100644 --- a/Source/WulaFallenEmpire/Ability/WULA_AbilityAreaDestruction/CompAbilityEffect_AreaDestruction.cs +++ b/Source/WulaFallenEmpire/Ability/WULA_AbilityAreaDestruction/CompAbilityEffect_AreaDestruction.cs @@ -21,8 +21,8 @@ namespace WulaFallenEmpire Map map = parent.pawn.MapHeld; if (map == null) return; - // 播放发射特效(在施法者位置) - PlayCastEffecter(map); + // 播放发射特效(在施法者位置)- 在释放瞬间播放 + //PlayCastEffecter(target, map); // 获取扇形区域内的所有单元格 List affectedCells = AffectedCells(target); @@ -61,26 +61,26 @@ namespace WulaFallenEmpire } } - private void PlayCastEffecter(Map map) + private void PlayCastEffecter(LocalTargetInfo target, Map map) { try { if (Props.castEffecter == null) return; - // 使用与 CompAbilityEffect_EffecterOnCaster 相同的方法 - Effecter effecter = Props.castEffecter.Spawn(Pawn, map); + // 在释放瞬间创建效果器,确保正确的方向 + Effecter effecter = Props.castEffecter.Spawn(Pawn.Position, target.Cell, map); if (Props.castEffecterMaintainTicks > 0) { // 使用与参考代码相同的方法来维持效果器 - map.effecterMaintainer.AddEffecterToMaintain(effecter, new TargetInfo(Pawn), Pawn, Props.castEffecterMaintainTicks); + parent.AddEffecterToMaintain(effecter, Pawn.Position, target.Cell, Props.castEffecterMaintainTicks, map); } else { effecter.Cleanup(); } - Log.Message($"[AreaDestruction] Played cast effecter on caster at {Pawn.Position}"); + Log.Message($"[AreaDestruction] Played cast effecter from {Pawn.Position} to {target.Cell}"); } catch (System.Exception ex) { @@ -95,28 +95,35 @@ namespace WulaFallenEmpire if (Props.hitEffecter == null) return; if (target == null || target.Destroyed) return; - // 使用与 CompAbilityEffect_EffecterOnTarget 相同的方法 - Effecter effecter; - if (target is Pawn pawnTarget) - { - effecter = Props.hitEffecter.Spawn(pawnTarget, map); - } - else - { - effecter = Props.hitEffecter.Spawn(target.Position, map); - } + // 计算冲击波方向:从施法者到目标的向量 + Vector3 directionFromCaster = (target.Position.ToVector3Shifted() - Pawn.Position.ToVector3Shifted()).normalized; + + // 计算反向位置:目标位置 + 反向向量 * 距离 + // 这样特效会从目标位置向施法者的反方向播放 + IntVec3 reversePosition = target.Position + new IntVec3( + Mathf.RoundToInt(-directionFromCaster.x * 2f), + 0, + Mathf.RoundToInt(-directionFromCaster.z * 2f) + ); + + // 确保反向位置在地图范围内 + reversePosition = reversePosition.ClampInsideMap(map); + + // 使用两个位置参数来设置效果器方向 + // 从目标位置到反向位置,这样特效会向施法者反方向播放 + Effecter effecter = Props.hitEffecter.Spawn(target.Position, reversePosition, map); if (Props.hitEffecterMaintainTicks > 0) { - // 使用与参考代码相同的方法来维持效果器 - parent.AddEffecterToMaintain(effecter, target.Position, Props.hitEffecterMaintainTicks); + // 维持效果器 + parent.AddEffecterToMaintain(effecter, target.Position, reversePosition, Props.hitEffecterMaintainTicks, map); } else { effecter.Cleanup(); } - Log.Message($"[AreaDestruction] Played hit effecter on {target.Label} at {target.Position}"); + Log.Message($"[AreaDestruction] Played hit effecter on {target.Label} at {target.Position} with reverse direction to {reversePosition}"); } catch (System.Exception ex) { @@ -124,6 +131,21 @@ namespace WulaFallenEmpire } } + public override IEnumerable GetPreCastActions() + { + if (Props.castEffecter != null) + { + yield return new PreCastAction + { + action = delegate (LocalTargetInfo a, LocalTargetInfo b) + { + parent.AddEffecterToMaintain(Props.castEffecter.Spawn(parent.pawn.Position, a.Cell, parent.pawn.Map), Pawn.Position, a.Cell, 17, Pawn.MapHeld); + }, + ticksAwayFromCast = 17 + }; + } + } + private void ProcessTarget(Thing target) { if (target is Building building) @@ -254,12 +276,6 @@ namespace WulaFallenEmpire } } - public override IEnumerable GetPreCastActions() - { - // 这里不再预先创建效果器,改为在Apply中创建 - yield break; - } - public override void DrawEffectPreview(LocalTargetInfo target) { GenDraw.DrawFieldEdges(AffectedCells(target), Color.red); diff --git a/Source/WulaFallenEmpire/ThingComp/WULA_AreaShield/AreaShieldManager.cs b/Source/WulaFallenEmpire/ThingComp/WULA_AreaShield/AreaShieldManager.cs index 6916d4ba..3424b995 100644 --- a/Source/WulaFallenEmpire/ThingComp/WULA_AreaShield/AreaShieldManager.cs +++ b/Source/WulaFallenEmpire/ThingComp/WULA_AreaShield/AreaShieldManager.cs @@ -14,46 +14,45 @@ namespace WulaFallenEmpire public static IEnumerable GetActiveShieldsForMap(Map map) { + if (map == null) + yield break; if (Find.TickManager.TicksGame - lastUpdateTick > UPDATE_INTERVAL_TICKS) { UpdateShieldCache(); lastUpdateTick = Find.TickManager.TicksGame; } - if (activeShieldsByMap.TryGetValue(map, out var shields)) { foreach (var shield in shields) { - if (shield?.Active == true) + if (shield?.parent != null && !shield.parent.Destroyed && shield?.Active == true) yield return shield; } } } - private static void UpdateShieldCache() { activeShieldsByMap.Clear(); - foreach (var map in Find.Maps) { + if (map == null) continue; var shieldSet = new HashSet(); - foreach (var pawn in map.mapPawns.AllPawnsSpawned) { - if (pawn.apparel != null) + if (pawn?.apparel == null || pawn.Destroyed) + continue; + foreach (var apparel in pawn.apparel.WornApparel) { - foreach (var apparel in pawn.apparel.WornApparel) + if (apparel == null || apparel.Destroyed) + continue; + var shield = apparel.TryGetComp(); + // 修改:只有立定且激活的护盾才加入缓存 + if (shield != null && shield.Active && !shield.IsWearerMoving) { - // 同时支持普通护盾和反弹护盾 - var shield = apparel.TryGetComp(); - if (shield != null && shield.Active) - { - shieldSet.Add(shield); - } + shieldSet.Add(shield); } } } - activeShieldsByMap[map] = shieldSet; } } diff --git a/Source/WulaFallenEmpire/ThingComp/WULA_AreaShield/Gizmo_AreaShieldStatus.cs b/Source/WulaFallenEmpire/ThingComp/WULA_AreaShield/Gizmo_AreaShieldStatus.cs index 47a89caa..e50718ab 100644 --- a/Source/WulaFallenEmpire/ThingComp/WULA_AreaShield/Gizmo_AreaShieldStatus.cs +++ b/Source/WulaFallenEmpire/ThingComp/WULA_AreaShield/Gizmo_AreaShieldStatus.cs @@ -4,13 +4,14 @@ using Verse; namespace WulaFallenEmpire { - // Gizmo 类保持不变... [StaticConstructorOnStartup] public class Gizmo_AreaShieldStatus : Gizmo { public ThingComp_AreaShield shield; private static readonly Texture2D FullShieldBarTex = SolidColorMaterials.NewSolidColorMaterial(new Color(0.2f, 0.8f, 0.85f), ShaderDatabase.MetaOverlay).mainTexture as Texture2D; private static readonly Texture2D EmptyShieldBarTex = SolidColorMaterials.NewSolidColorMaterial(new Color(0.2f, 0.2f, 0.24f), ShaderDatabase.MetaOverlay).mainTexture as Texture2D; + // 新增:移动状态的颜色 + private static readonly Texture2D MovingShieldBarTex = SolidColorMaterials.NewSolidColorMaterial(new Color(0.5f, 0.5f, 0.5f), ShaderDatabase.MetaOverlay).mainTexture as Texture2D; public override float GetWidth(float maxWidth) => 140f; @@ -28,14 +29,33 @@ namespace WulaFallenEmpire Rect barRect = rect2; barRect.yMin = rect2.y + rect2.height / 2f; float fillPercent = (float)shield.currentHitPoints / shield.HitPointsMax; - Widgets.FillableBar(barRect, fillPercent, FullShieldBarTex, EmptyShieldBarTex, false); + + // 修改:根据状态选择不同的状态条 + Texture2D barTex; + TaggedString statusText; + + if (shield.IsOnCooldown) + { + barTex = EmptyShieldBarTex; + statusText = "ShieldOnCooldown".Translate(); + } + else if (shield.IsWearerMoving) + { + // 移动时显示灰色状态条和"移动中"文本 + barTex = MovingShieldBarTex; + statusText = "ShieldOfflineByMoving".Translate(); // 你可以根据需要修改这个文本 + } + else + { + barTex = FullShieldBarTex; + statusText = new TaggedString(shield.currentHitPoints + " / " + shield.HitPointsMax); + } + + Widgets.FillableBar(barRect, fillPercent, barTex, EmptyShieldBarTex, false); Text.Font = GameFont.Small; Text.Anchor = TextAnchor.MiddleCenter; - - TaggedString statusText = shield.IsOnCooldown ? "ShieldOnCooldown".Translate() : new TaggedString(shield.currentHitPoints + " / " + shield.HitPointsMax); Widgets.Label(barRect, statusText); - Text.Anchor = TextAnchor.UpperLeft; return new GizmoResult(GizmoState.Clear); diff --git a/Source/WulaFallenEmpire/ThingComp/WULA_AreaShield/Harmony_AreaShieldInterceptor.cs b/Source/WulaFallenEmpire/ThingComp/WULA_AreaShield/Harmony_AreaShieldInterceptor.cs index 81acf643..8a835832 100644 --- a/Source/WulaFallenEmpire/ThingComp/WULA_AreaShield/Harmony_AreaShieldInterceptor.cs +++ b/Source/WulaFallenEmpire/ThingComp/WULA_AreaShield/Harmony_AreaShieldInterceptor.cs @@ -2,39 +2,118 @@ using HarmonyLib; using RimWorld; using Verse; using UnityEngine; +using System.Collections.Generic; namespace WulaFallenEmpire { + public static class ReflectedProjectileManager + { + private static Dictionary projectilesToDestroy = new Dictionary(); + private const int DESTROY_DELAY_TICKS = 1; + + public static void MarkForDelayedDestroy(Projectile projectile) + { + if (projectile != null && !projectile.Destroyed) + { + projectilesToDestroy[projectile] = Find.TickManager.TicksGame + DESTROY_DELAY_TICKS; + } + } + + public static void Tick() + { + var toRemove = new List(); + + foreach (var kvp in projectilesToDestroy) + { + if (kvp.Key == null || kvp.Key.Destroyed || Find.TickManager.TicksGame >= kvp.Value) + { + if (kvp.Key != null && !kvp.Key.Destroyed) + { + kvp.Key.Destroy(DestroyMode.Vanish); + } + toRemove.Add(kvp.Key); + } + } + + foreach (var projectile in toRemove) + { + projectilesToDestroy.Remove(projectile); + } + } + + // 在 ReflectedProjectileManager 类中添加这个方法 + public static bool IsMarkedForDestroy(Projectile projectile) + { + return projectile != null && projectilesToDestroy.ContainsKey(projectile); + } + } + [HarmonyPatch(typeof(Projectile), "CheckForFreeInterceptBetween")] public static class Projectile_CheckForFreeInterceptBetween_Patch { - public static bool Prefix(Projectile __instance, Vector3 lastExactPos, Vector3 newExactPos) + public static bool Prefix(Projectile __instance, Vector3 lastExactPos, Vector3 newExactPos, ref bool __result) { - if (__instance.Map == null || __instance.Destroyed) + try { - return true; - } - - bool shouldDestroy = false; - - // 使用缓存系统获取激活的护盾 - foreach (var shield in AreaShieldManager.GetActiveShieldsForMap(__instance.Map)) - { - if (shield?.TryIntercept(__instance, lastExactPos, newExactPos) == true) + // 安全检查 + if (__instance == null || __instance.Map == null || __instance.Destroyed) { - shouldDestroy = true; - break; // 只要有一个护盾吸收就销毁 + return true; // 继续执行原方法 } - // 如果护盾反射了抛射体,继续检查其他护盾(允许多重反射) - } - if (shouldDestroy) + bool shouldDestroy = false; + bool wasReflected = false; + + // 使用缓存系统获取激活的护盾 + foreach (var shield in AreaShieldManager.GetActiveShieldsForMap(__instance.Map)) + { + if (shield == null || shield.parent == null || shield.parent.Destroyed) + continue; + + if (shield?.TryIntercept(__instance, lastExactPos, newExactPos) == true) + { + shouldDestroy = true; + break; + } + + // 检查抛射体是否已经被反射(被标记为延迟销毁) + if (ReflectedProjectileManager.IsMarkedForDestroy(__instance)) + { + wasReflected = true; + break; + } + } + + if (shouldDestroy) + { + __instance.Destroy(DestroyMode.Vanish); + __result = true; // 设置结果为 true 表示已被拦截 + return false; // 跳过原方法 + } + + if (wasReflected) + { + __result = false; // 设置结果为 false 表示未被拦截(因为被反射了) + return false; // 跳过原方法 + } + + return true; // 继续执行原方法 + } + catch (System.Exception ex) { - __instance.Destroy(DestroyMode.Vanish); - return false; + Log.Warning($"AreaShield interception error: {ex}"); + return true; // 出错时继续执行原方法 } + } + } - return true; + // 添加Tick管理器 + [HarmonyPatch(typeof(TickManager), "DoSingleTick")] + public static class TickManager_DoSingleTick_Patch + { + public static void Postfix() + { + ReflectedProjectileManager.Tick(); } } diff --git a/Source/WulaFallenEmpire/ThingComp/WULA_AreaShield/ThingComp_AreaShield.cs b/Source/WulaFallenEmpire/ThingComp/WULA_AreaShield/ThingComp_AreaShield.cs index 8c342477..73985fd7 100644 --- a/Source/WulaFallenEmpire/ThingComp/WULA_AreaShield/ThingComp_AreaShield.cs +++ b/Source/WulaFallenEmpire/ThingComp/WULA_AreaShield/ThingComp_AreaShield.cs @@ -25,15 +25,29 @@ namespace WulaFallenEmpire public bool IsOnCooldown => ticksToReset > 0; public int HitPointsMax => Props.baseHitPoints; - private StunHandler stunner; private bool initialized = false; + private StunHandler stunner; + // 新增:移动状态检测 + public bool IsWearerMoving + { + get + { + if (Wearer == null || !Wearer.Spawned) return false; + return Wearer.pather.Moving; + } + } + + // 修改Active属性:只有在立定时才激活 public bool Active { get { if (Wearer == null || !Wearer.Spawned || Wearer.Dead || Wearer.Downed || IsOnCooldown) return false; + // 新增:只有在立定时才激活 + if (IsWearerMoving) + return false; return true; } } @@ -110,38 +124,51 @@ namespace WulaFallenEmpire public bool TryIntercept(Projectile projectile, Vector3 lastExactPos, Vector3 newExactPos) { - if (!Active) return false; - if (currentHitPoints <= 0) return false; - - if (!GenGeo.IntersectLineCircleOutline(Wearer.Position.ToVector2(), Props.radius, lastExactPos.ToVector2(), newExactPos.ToVector2())) - { + // 增强安全检查 + if (!Active || projectile == null || projectile.Destroyed || Wearer == null || Wearer.Map == null) return false; - } - if (projectile.def.projectile.flyOverhead && !Props.interceptAirProjectiles) return false; - if (!projectile.def.projectile.flyOverhead && !Props.interceptGroundProjectiles) return false; - if (projectile.Launcher != null && !projectile.Launcher.HostileTo(Wearer.Faction) && !Props.interceptNonHostileProjectiles) return false; - - lastInterceptTicks = Find.TickManager.TicksGame; - - // 记录拦截角度用于视觉效果 - lastInterceptAngle = projectile.ExactPosition.AngleToFlat(Wearer.TrueCenter()); - drawInterceptCone = true; - - // 尝试反射 - if (Props.canReflect && TryReflectProjectile(projectile, lastExactPos, newExactPos)) + if (currentHitPoints <= 0) + return false; + try { - // 反射成功,播放反射特效 - Props.reflectEffecter?.Spawn(projectile.ExactPosition.ToIntVec3(), Wearer.Map).Cleanup(); - ApplyCosts(Props.reflectCost); - return false; // 不销毁原抛射体,让它继续飞行(我们会在反射中销毁它) + if (!GenGeo.IntersectLineCircleOutline(Wearer.Position.ToVector2(), Props.radius, lastExactPos.ToVector2(), newExactPos.ToVector2())) + { + return false; + } + if (projectile.def.projectile.flyOverhead && !Props.interceptAirProjectiles) + return false; + if (!projectile.def.projectile.flyOverhead && !Props.interceptGroundProjectiles) + return false; + if (projectile.Launcher != null && !projectile.Launcher.HostileTo(Wearer.Faction) && !Props.interceptNonHostileProjectiles) + return false; + + lastInterceptTicks = Find.TickManager.TicksGame; + + // 记录拦截角度用于视觉效果 + lastInterceptAngle = projectile.ExactPosition.AngleToFlat(Wearer.TrueCenter()); + drawInterceptCone = true; + + // 尝试反射 + if (Props.canReflect && TryReflectProjectile(projectile, lastExactPos, newExactPos)) + { + // 反射成功,播放反射特效 + Props.reflectEffecter?.Spawn(projectile.ExactPosition.ToIntVec3(), Wearer.Map).Cleanup(); + ApplyCosts(Props.reflectCost); + return false; // 不销毁原抛射体,让它继续飞行(我们会在反射中销毁它) + } + else + { + // 普通拦截,播放拦截特效 + Props.interceptEffecter?.Spawn(projectile.ExactPosition.ToIntVec3(), Wearer.Map).Cleanup(); + ApplyCosts(); + return true; // 销毁抛射体 + } } - else + catch (System.Exception ex) { - // 普通拦截,播放拦截特效 - Props.interceptEffecter?.Spawn(projectile.ExactPosition.ToIntVec3(), Wearer.Map).Cleanup(); - ApplyCosts(); - return true; // 销毁抛射体 + Log.Warning($"Error in TryIntercept: {ex}"); + return false; } } @@ -150,79 +177,87 @@ namespace WulaFallenEmpire /// private bool TryReflectProjectile(Projectile originalProjectile, Vector3 lastExactPos, Vector3 newExactPos) { - if (!Props.canReflect) return false; - - // 检查反射概率 - if (Rand.Value > Props.reflectChance) return false; + if (!Props.canReflect || originalProjectile == null || originalProjectile.Destroyed) + return false; + // 检查反射概率 + if (Rand.Value > Props.reflectChance) + return false; try { // 计算入射方向 Vector3 incomingDirection = (newExactPos - lastExactPos).normalized; - + // 计算法线方向(从护盾中心到碰撞点) Vector3 normal = (newExactPos - Wearer.DrawPos).normalized; - + // 计算反射方向(镜面反射) Vector3 reflectDirection = Vector3.Reflect(incomingDirection, normal); - + // 添加随机角度偏移 float randomAngle = Rand.Range(-Props.reflectAngleRange, Props.reflectAngleRange); reflectDirection = Quaternion.Euler(0, randomAngle, 0) * reflectDirection; - + // 创建新的反射抛射体 - CreateReflectedProjectile(originalProjectile, reflectDirection, newExactPos); - - return true; + return CreateReflectedProjectile(originalProjectile, reflectDirection, newExactPos); } catch (System.Exception ex) { Log.Warning($"Error reflecting projectile: {ex}"); } - + return false; } - + /// /// 创建反射后的新抛射体 /// - private void CreateReflectedProjectile(Projectile originalProjectile, Vector3 reflectDirection, Vector3 collisionPoint) + private bool CreateReflectedProjectile(Projectile originalProjectile, Vector3 reflectDirection, Vector3 collisionPoint) { try { + if (originalProjectile == null || originalProjectile.Destroyed || Wearer == null || Wearer.Map == null) + return false; // 计算新的发射位置(护盾位置附近) Vector3 spawnPosition = GetReflectSpawnPosition(collisionPoint); - + // 确保位置在地图内 + IntVec3 spawnCell = spawnPosition.ToIntVec3(); + if (!spawnCell.InBounds(Wearer.Map)) + { + spawnCell = Wearer.Position; + } // 计算新的目标位置 - Vector3 targetPosition = spawnPosition + reflectDirection * 30f; // 足够远的距离 - + Vector3 targetPosition = spawnCell.ToVector3Shifted() + reflectDirection * 30f; + IntVec3 targetCell = targetPosition.ToIntVec3(); // 创建新的抛射体 - Projectile newProjectile = (Projectile)GenSpawn.Spawn(originalProjectile.def, spawnPosition.ToIntVec3(), Wearer.Map); - - // 设置发射者为原抛射体的发射者 - Thing launcher = originalProjectile.Launcher ?? Wearer; - + Projectile newProjectile = (Projectile)GenSpawn.Spawn(originalProjectile.def, spawnCell, Wearer.Map); + if (newProjectile == null) + { + Log.Warning("Failed to spawn reflected projectile"); + return false; + } + // 设置发射者为装备穿戴者 + Thing launcher = Wearer; // 发射新抛射体 newProjectile.Launch( launcher, - spawnPosition, - new LocalTargetInfo(targetPosition.ToIntVec3()), - new LocalTargetInfo(targetPosition.ToIntVec3()), + spawnCell.ToVector3Shifted(), + new LocalTargetInfo(targetCell), + new LocalTargetInfo(targetCell), ProjectileHitFlags.All, false ); - // 复制重要的属性 CopyProjectileProperties(originalProjectile, newProjectile); - - // 销毁原抛射体 - originalProjectile.Destroy(DestroyMode.Vanish); - - Log.Message($"反射抛射体: 从 {spawnPosition} 向 {targetPosition} 发射"); + // 使用延迟销毁而不是立即销毁 + ReflectedProjectileManager.MarkForDelayedDestroy(originalProjectile); + Log.Message($"反射抛射体: 由 {Wearer?.LabelShort} 从 {spawnCell} 向 {targetCell} 发射"); + return true; } catch (System.Exception ex) { Log.Warning($"Error creating reflected projectile: {ex}"); + return false; } } @@ -231,21 +266,17 @@ namespace WulaFallenEmpire /// private Vector3 GetReflectSpawnPosition(Vector3 collisionPoint) { + if (Wearer == null) + return collisionPoint; + // 计算从护盾中心到碰撞点的方向 Vector3 directionFromCenter = (collisionPoint - Wearer.DrawPos).normalized; - + // 在护盾边界上生成(稍微向内一点避免立即再次碰撞) float spawnDistance = Props.radius * 0.9f; Vector3 spawnPosition = Wearer.DrawPos + directionFromCenter * spawnDistance; - - // 确保位置在地图内 - IntVec3 spawnCell = spawnPosition.ToIntVec3(); - if (!spawnCell.InBounds(Wearer.Map)) - { - spawnCell = Wearer.Position; - } - - return spawnCell.ToVector3Shifted(); + + return spawnPosition; } /// @@ -314,12 +345,13 @@ namespace WulaFallenEmpire AreaShieldManager.NotifyShieldStateChanged(this); } - // 护盾绘制方法 + // 护盾绘制方法 - 只有在立定时才绘制 public override void CompDrawWornExtras() { base.CompDrawWornExtras(); - if (!Active || Wearer?.Map == null || !ShouldDisplay) + // 修改:移动时不绘制护盾 + if (!Active || Wearer?.Map == null || !ShouldDisplay || IsWearerMoving) return; Vector3 drawPos = Wearer.Drawer?.DrawPos ?? Wearer.Position.ToVector3Shifted(); diff --git a/美术与文本源文件/Wula/Mote/WULA_AreaDestruction_Mainwave.sai2 b/美术与文本源文件/Wula/Mote/WULA_AreaDestruction_Mainwave.sai2 new file mode 100644 index 00000000..184612b6 Binary files /dev/null and b/美术与文本源文件/Wula/Mote/WULA_AreaDestruction_Mainwave.sai2 differ diff --git a/美术与文本源文件/Wula/Things/Wula_Psi_Titan/Bodies/Naked_Thin_east.png b/美术与文本源文件/Wula/Things/Wula_Psi_Titan/Bodies/Naked_Thin_east.png deleted file mode 100644 index 5f10878a..00000000 Binary files a/美术与文本源文件/Wula/Things/Wula_Psi_Titan/Bodies/Naked_Thin_east.png and /dev/null differ diff --git a/美术与文本源文件/Wula/Things/Wula_Psi_Titan/Bodies/Naked_Thin_east.sai2 b/美术与文本源文件/Wula/Things/Wula_Psi_Titan/Bodies/Naked_Thin_east.sai2 index 89b9aaf9..17773b43 100644 Binary files a/美术与文本源文件/Wula/Things/Wula_Psi_Titan/Bodies/Naked_Thin_east.sai2 and b/美术与文本源文件/Wula/Things/Wula_Psi_Titan/Bodies/Naked_Thin_east.sai2 differ diff --git a/美术与文本源文件/Wula/Things/Wula_Psi_Titan/Bodies/Naked_Thin_north.png b/美术与文本源文件/Wula/Things/Wula_Psi_Titan/Bodies/Naked_Thin_north.png deleted file mode 100644 index b2e9acf2..00000000 Binary files a/美术与文本源文件/Wula/Things/Wula_Psi_Titan/Bodies/Naked_Thin_north.png and /dev/null differ diff --git a/美术与文本源文件/Wula/Things/Wula_Psi_Titan/Bodies/Naked_Thin_south.png b/美术与文本源文件/Wula/Things/Wula_Psi_Titan/Bodies/Naked_Thin_south.png deleted file mode 100644 index 330a287a..00000000 Binary files a/美术与文本源文件/Wula/Things/Wula_Psi_Titan/Bodies/Naked_Thin_south.png and /dev/null differ