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 StunHandler stunner;
private bool initialized = false;
public bool Active
{
get
{
if (Wearer == null || !Wearer.Spawned || Wearer.Dead || Wearer.Downed || IsOnCooldown)
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) return false;
if (currentHitPoints <= 0) 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; // 销毁抛射体
}
}
///
/// 尝试反射抛射体 - 现在会创建新的抛射体
///
private bool TryReflectProjectile(Projectile originalProjectile, Vector3 lastExactPos, Vector3 newExactPos)
{
if (!Props.canReflect) 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;
}
catch (System.Exception ex)
{
Log.Warning($"Error reflecting projectile: {ex}");
}
return false;
}
///
/// 创建反射后的新抛射体
///
private void CreateReflectedProjectile(Projectile originalProjectile, Vector3 reflectDirection, Vector3 collisionPoint)
{
try
{
// 计算新的发射位置(护盾位置附近)
Vector3 spawnPosition = GetReflectSpawnPosition(collisionPoint);
// 计算新的目标位置
Vector3 targetPosition = spawnPosition + reflectDirection * 30f; // 足够远的距离
// 创建新的抛射体
Projectile newProjectile = (Projectile)GenSpawn.Spawn(originalProjectile.def, spawnPosition.ToIntVec3(), Wearer.Map);
// 设置发射者为原抛射体的发射者
Thing launcher = originalProjectile.Launcher ?? Wearer;
// 发射新抛射体
newProjectile.Launch(
launcher,
spawnPosition,
new LocalTargetInfo(targetPosition.ToIntVec3()),
new LocalTargetInfo(targetPosition.ToIntVec3()),
ProjectileHitFlags.All,
false
);
// 复制重要的属性
CopyProjectileProperties(originalProjectile, newProjectile);
// 销毁原抛射体
originalProjectile.Destroy(DestroyMode.Vanish);
Log.Message($"反射抛射体: 从 {spawnPosition} 向 {targetPosition} 发射");
}
catch (System.Exception ex)
{
Log.Warning($"Error creating reflected projectile: {ex}");
}
}
///
/// 获取反射抛射体的发射位置(护盾边界上)
///
private Vector3 GetReflectSpawnPosition(Vector3 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();
}
///
/// 复制抛射体重要属性
///
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(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)
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 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);
}
}
}