Files
WulaFallenEmpireRW/Source/WulaFallenEmpire/ThingComp/WULA_AreaShield/ThingComp_AreaShield.cs
Tourswen 22f03e2e05 1
2025-11-20 22:57:25 +08:00

455 lines
17 KiB
C#
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
using RimWorld;
using Verse;
using UnityEngine;
using Verse.Sound;
using System.Collections.Generic;
using HarmonyLib;
namespace WulaFallenEmpire
{
[StaticConstructorOnStartup]
public class ThingComp_AreaShield : ThingComp
{
private int lastInterceptTicks = -999999;
public int ticksToReset = 0;
public int currentHitPoints;
private bool wasNotAtFullHp = false;
private bool wasActiveLastCheck = false;
// 视觉效果变量
private float lastInterceptAngle;
private bool drawInterceptCone;
public CompProperties_AreaShield Props => (CompProperties_AreaShield)props;
public Pawn Wearer => (parent as Apparel)?.Wearer;
public bool IsOnCooldown => ticksToReset > 0;
public int HitPointsMax => Props.baseHitPoints;
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;
}
}
// 材质定义
private static readonly Material ForceFieldMat = MaterialPool.MatFrom("Other/ForceField", ShaderDatabase.MoteGlow);
private static readonly Material ForceFieldConeMat = MaterialPool.MatFrom("Other/ForceFieldCone", ShaderDatabase.MoteGlow);
private static readonly MaterialPropertyBlock MatPropertyBlock = new MaterialPropertyBlock();
public override void PostPostMake()
{
base.PostPostMake();
currentHitPoints = HitPointsMax;
}
public override void PostExposeData()
{
base.PostExposeData();
Scribe_Values.Look(ref lastInterceptTicks, "lastInterceptTicks", -999999);
Scribe_Values.Look(ref ticksToReset, "ticksToReset", 0);
Scribe_Values.Look(ref currentHitPoints, "currentHitPoints", 0);
}
public override void CompTick()
{
base.CompTick();
bool isActive = Active;
// 检查状态变化并通知管理器
if (isActive != wasActiveLastCheck)
{
AreaShieldManager.NotifyShieldStateChanged(this);
wasActiveLastCheck = isActive;
}
if (Wearer == null) return;
if (IsOnCooldown)
{
ticksToReset--;
if (ticksToReset <= 0)
{
Reset();
}
}
else if (isActive && currentHitPoints < HitPointsMax)
{
wasNotAtFullHp = true;
if (parent.IsHashIntervalTick(Props.rechargeHitPointsIntervalTicks))
{
currentHitPoints += 1;
if (currentHitPoints > HitPointsMax)
currentHitPoints = HitPointsMax;
}
}
else if (wasNotAtFullHp && currentHitPoints >= HitPointsMax)
{
wasNotAtFullHp = false;
}
}
private void ApplyCosts(int cost = 1)
{
currentHitPoints -= cost;
if (currentHitPoints <= 0)
{
Break();
}
// 护盾值变化时通知管理器
AreaShieldManager.NotifyShieldStateChanged(this);
}
public bool TryIntercept(Projectile projectile, Vector3 lastExactPos, Vector3 newExactPos)
{
// 增强安全检查
if (!Active || projectile == null || projectile.Destroyed || Wearer == null || Wearer.Map == null)
return false;
if (currentHitPoints <= 0)
return false;
try
{
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; // 销毁抛射体
}
}
catch (System.Exception ex)
{
Log.Warning($"Error in TryIntercept: {ex}");
return false;
}
}
/// <summary>
/// 尝试反射抛射体 - 现在会创建新的抛射体
/// </summary>
private bool TryReflectProjectile(Projectile originalProjectile, Vector3 lastExactPos, Vector3 newExactPos)
{
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;
// 创建新的反射抛射体
return CreateReflectedProjectile(originalProjectile, reflectDirection, newExactPos);
}
catch (System.Exception ex)
{
Log.Warning($"Error reflecting projectile: {ex}");
}
return false;
}
/// <summary>
/// 创建反射后的新抛射体
/// </summary>
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 = spawnCell.ToVector3Shifted() + reflectDirection * 30f;
IntVec3 targetCell = targetPosition.ToIntVec3();
// 创建新的抛射体
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,
spawnCell.ToVector3Shifted(),
new LocalTargetInfo(targetCell),
new LocalTargetInfo(targetCell),
ProjectileHitFlags.All,
false
);
// 复制重要的属性
CopyProjectileProperties(originalProjectile, newProjectile);
// 使用延迟销毁而不是立即销毁
ReflectedProjectileManager.MarkForDelayedDestroy(originalProjectile);
Log.Message($"反射抛射体: 由 {Wearer?.LabelShort} 从 {spawnCell} 向 {targetCell} 发射");
return true;
}
catch (System.Exception ex)
{
Log.Warning($"Error creating reflected projectile: {ex}");
return false;
}
}
/// <summary>
/// 获取反射抛射体的发射位置(护盾边界上)
/// </summary>
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;
return spawnPosition;
}
/// <summary>
/// 复制抛射体重要属性
/// </summary>
private void CopyProjectileProperties(Projectile source, Projectile destination)
{
try
{
var sourceTraverse = Traverse.Create(source);
var destTraverse = Traverse.Create(destination);
// 复制伤害属性
destTraverse.Field("damageDefOverride").SetValue(source.damageDefOverride);
// 复制额外伤害
if (source.extraDamages != null)
{
destTraverse.Field("extraDamages").SetValue(new List<ExtraDamage>(source.extraDamages));
}
// 复制停止力
destTraverse.Field("stoppingPower").SetValue(source.stoppingPower);
}
catch (System.Exception ex)
{
Log.Warning($"Error copying projectile properties: {ex}");
}
}
public override void PostPreApplyDamage(ref DamageInfo dinfo, out bool absorbed)
{
absorbed = false;
if (!Active || Wearer == null) return;
if (dinfo.Def.isRanged) return;
if (dinfo.Instigator != null)
{
float distance = Wearer.Position.DistanceTo(dinfo.Instigator.Position);
if (distance > Props.radius) return;
}
if (currentHitPoints <= 0) return;
Props.absorbEffecter?.Spawn(Wearer.Position, Wearer.Map).Cleanup();
ApplyCosts();
absorbed = true;
}
private void Break()
{
Props.breakEffecter?.Spawn(Wearer.Position, Wearer.Map).Cleanup();
ticksToReset = Props.rechargeDelay;
currentHitPoints = 0;
AreaShieldManager.NotifyShieldStateChanged(this);
}
private void Reset()
{
if (Wearer != null && Wearer.Spawned)
{
Props.reactivateEffecter?.Spawn(Wearer.Position, Wearer.Map).Cleanup();
}
currentHitPoints = HitPointsMax;
AreaShieldManager.NotifyShieldStateChanged(this);
}
// 护盾绘制方法 - 只有在立定时才绘制
public override void CompDrawWornExtras()
{
base.CompDrawWornExtras();
// 修改:移动时不绘制护盾
if (!Active || Wearer?.Map == null || !ShouldDisplay || IsWearerMoving)
return;
Vector3 drawPos = Wearer.Drawer?.DrawPos ?? Wearer.Position.ToVector3Shifted();
drawPos.y = AltitudeLayer.MoteOverhead.AltitudeFor();
float alpha = GetCurrentAlpha();
if (alpha > 0f)
{
Color color = Props.color;
color.a *= alpha;
MatPropertyBlock.SetColor(ShaderPropertyIDs.Color, color);
Matrix4x4 matrix = default;
float scale = Props.radius * 2f * 1.1601562f;
matrix.SetTRS(drawPos, Quaternion.identity, new Vector3(scale, 1f, scale));
Graphics.DrawMesh(MeshPool.plane10, matrix, ForceFieldMat, 0, null, 0, MatPropertyBlock);
}
// 添加拦截锥形效果
float coneAlpha = GetCurrentConeAlpha();
if (coneAlpha > 0f)
{
Color color = Props.color;
color.a *= coneAlpha;
MatPropertyBlock.SetColor(ShaderPropertyIDs.Color, color);
Matrix4x4 matrix = default;
float scale = Props.radius * 2f;
matrix.SetTRS(drawPos, Quaternion.Euler(0f, lastInterceptAngle - 90f, 0f), new Vector3(scale, 1f, scale));
Graphics.DrawMesh(MeshPool.plane10, matrix, ForceFieldConeMat, 0, null, 0, MatPropertyBlock);
}
}
// 显示条件
protected bool ShouldDisplay
{
get
{
if (Wearer == null || !Wearer.Spawned || Wearer.Dead || Wearer.Downed || !Active)
return false;
if (Wearer.Drafted || Wearer.InAggroMentalState ||
(Wearer.Faction != null && Wearer.Faction.HostileTo(Faction.OfPlayer) && !Wearer.IsPrisoner))
return true;
if (Find.Selector.IsSelected(Wearer))
return true;
return false;
}
}
private float GetCurrentAlpha()
{
float idleAlpha = Mathf.Lerp(0.3f, 0.6f, (Mathf.Sin((float)Gen.HashCombineInt(parent.thingIDNumber, 35990913) + Time.realtimeSinceStartup * 2f) + 1f) / 2f);
float interceptAlpha = Mathf.Clamp01(1f - (float)(Find.TickManager.TicksGame - lastInterceptTicks) / 40f);
return Mathf.Max(idleAlpha, interceptAlpha);
}
private float GetCurrentConeAlpha()
{
if (!drawInterceptCone) return 0f;
return Mathf.Clamp01(1f - (float)(Find.TickManager.TicksGame - lastInterceptTicks) / 40f) * 0.82f;
}
private void EnsureInitialized()
{
if (initialized) return;
if (stunner == null)
stunner = new StunHandler(parent);
if (currentHitPoints == -1)
currentHitPoints = Props.startupDelay > 0 ? 0 : HitPointsMax;
initialized = true;
}
public override IEnumerable<Gizmo> CompGetWornGizmosExtra()
{
EnsureInitialized();
if (Wearer != null && Find.Selector.SingleSelectedThing == Wearer)
{
yield return new Gizmo_AreaShieldStatus { shield = this };
}
}
public override void Notify_Equipped(Pawn pawn)
{
base.Notify_Equipped(pawn);
AreaShieldManager.NotifyShieldStateChanged(this);
}
public override void Notify_Unequipped(Pawn pawn)
{
base.Notify_Unequipped(pawn);
AreaShieldManager.NotifyShieldStateChanged(this);
}
}
}