Delta-v/Content.Server/_Starlight/ShadekinSystem.cs

294 lines
11 KiB
C#

using Content.Shared.Humanoid;
using Content.Shared.Alert;
using Robust.Server.GameObjects;
using Content.Shared.Examine;
using Robust.Server.Containers;
using Content.Shared._Starlight;
using Content.Shared.Damage.Components;
using Content.Shared.Mobs;
using Content.Shared.Movement.Systems;
using Content.Shared.Movement.Components;
using Content.Shared.Damage;
using Content.Server.Chat.Managers;
using Content.Shared.Damage.Systems;
using Content.Shared._Goobstation.Overlays;
using Robust.Shared.Timing;
using Content.Server.Body.Components;
using System.Linq;
using Content.Shared._Goobstation.Flashbang;
using Content.Shared._Starlight.Flash.Components;
using Content.Shared.Body.Components;
using Content.Shared.Flash; // Delta V - Flash Work
using Content.Shared._DV.Body.Components; // Delta V - Remove various comps related to breathing
namespace Content.Server._Starlight;
public sealed class ShadekinSystem : EntitySystem
{
[Dependency] private readonly IGameTiming _timing = default!;
[Dependency] private readonly AlertsSystem _alerts = default!;
[Dependency] private readonly SharedTransformSystem _transform = default!;
[Dependency] private readonly EntityLookupSystem _lookup = default!;
[Dependency] private readonly ExamineSystemShared _examine = default!;
[Dependency] private readonly ContainerSystem _container = default!;
[Dependency] private readonly DamageableSystem _damageable = default!;
[Dependency] private readonly MovementSpeedModifierSystem _speed = default!;
[Dependency] private readonly SharedFlashSystem _flashSystem = default!;
private sealed class LightCone
{
public float Direction { get; set; }
public float InnerWidth { get; set; }
public float OuterWidth { get; set; }
}
private readonly Dictionary<string, List<LightCone>> lightMasks = new()
{
["/Textures/Effects/LightMasks/cone.png"] = new List<LightCone>
{
new LightCone { Direction = 0, InnerWidth = 30, OuterWidth = 60 }
},
["/Textures/Effects/LightMasks/double_cone.png"] = new List<LightCone>
{
new LightCone { Direction = 0, InnerWidth = 30, OuterWidth = 60 },
new LightCone { Direction = 180, InnerWidth = 30, OuterWidth = 60 }
}
};
public override void Initialize()
{
base.Initialize();
SubscribeLocalEvent<ShadekinComponent, ComponentStartup>(OnInit);
SubscribeLocalEvent<ShadekinComponent, EyeColorInitEvent>(OnEyeColorChange);
SubscribeLocalEvent<ShadekinComponent, RefreshMovementSpeedModifiersEvent>(OnRefreshMovementSpeedModifiers);
SubscribeLocalEvent<ShadekinComponent, AfterFlashedEvent>(OnShadekinFlashed); // Delta V - Prevent Chain Flashing
SubscribeLocalEvent<ShadekinComponent, FlashDurationMultiplierEvent>(GetFlashModifier); // Delta V - Flash Modifier to Shadekin
}
private void OnInit(EntityUid uid, ShadekinComponent component, ComponentStartup args)
{
UpdateAlert(uid, component, (short)component.CurrentState);
RemComp<InternalsComponent>(uid);
RemComp<RespiratorComponent>(uid);
RemComp<AffectedByCPRComponent>(uid); // No lungs = no CPR
}
private void OnEyeColorChange(EntityUid uid, ShadekinComponent component, EyeColorInitEvent args)
{
if (!TryComp<HumanoidProfileComponent>(uid, out var humanoid))
return;
// humanoid.EyeColor = Color.Black;
Dirty(uid, humanoid);
}
public void UpdateAlert(EntityUid uid, ShadekinComponent component, short state)
{
_alerts.ShowAlert(uid, component.ShadekinAlert, state);
}
private Angle GetAngle(EntityUid lightUid, SharedPointLightComponent lightComp, EntityUid targetUid)
{
var (lightPos, lightRot) = _transform.GetWorldPositionRotation(lightUid);
lightPos += lightRot.RotateVec(lightComp.Offset);
var (targetPos, targetRot) = _transform.GetWorldPositionRotation(targetUid);
var mapDiff = targetPos - lightPos;
var oppositeMapDiff = (-lightRot).RotateVec(mapDiff);
var angle = oppositeMapDiff.ToWorldAngle();
if (angle == double.NaN && _transform.ContainsEntity(targetUid, lightUid) || _transform.ContainsEntity(lightUid, targetUid))
{
angle = 0f;
}
return angle;
}
/// <summary>
/// Return an illumination float value with is how many "energy" of light is hitting our ent.
/// WARNING: This function might be expensive, Avoid calling it too much and CACHE THE RESULT!
/// </summary>
/// <param name="uid"></param>
/// <returns></returns>
public float GetLightExposure(EntityUid uid)
{
var illumination = 0f;
var lightQuery = _lookup.GetEntitiesInRange<PointLightComponent>(Transform(uid).Coordinates, 20, LookupFlags.Uncontained);
foreach (var light in lightQuery)
{
if (!light.Comp.Enabled
|| light.Comp.Radius < 1
|| light.Comp.Energy <= 0)
continue;
var (lightPos, lightRot) = _transform.GetWorldPositionRotation(light);
lightPos += lightRot.RotateVec(light.Comp.Offset);
if (!_examine.InRangeUnOccluded(light, uid, light.Comp.Radius, null))
continue;
Transform(uid).Coordinates.TryDistance(EntityManager, Transform(light).Coordinates, out var dist);
var denom = dist / light.Comp.Radius;
var attenuation = 1 - (denom * denom);
var calculatedLight = 0f;
if (light.Comp.MaskPath is not null)
{
var angleToTarget = GetAngle(light, light.Comp, uid);
foreach (var cone in lightMasks[light.Comp.MaskPath])
{
var coneLight = 0f;
var angleAttenuation = (float)Math.Min((float)Math.Max(cone.OuterWidth - angleToTarget, 0f), cone.InnerWidth) / cone.OuterWidth;
if (angleToTarget.Degrees - cone.Direction > cone.OuterWidth)
continue;
else if (angleToTarget.Degrees - cone.Direction > cone.InnerWidth
&& angleToTarget.Degrees - cone.Direction < cone.OuterWidth)
coneLight = light.Comp.Energy * attenuation * attenuation * angleAttenuation;
else
coneLight = light.Comp.Energy * attenuation * attenuation;
calculatedLight = Math.Max(calculatedLight, coneLight);
}
}
else
calculatedLight = light.Comp.Energy * attenuation * attenuation;
illumination += calculatedLight; //Math.Max(illumination, calculatedLight);
}
return illumination;
}
private void SetPassiveBuff(EntityUid uid, ShadekinState state)
{
if (!TryComp<PassiveDamageComponent>(uid, out var passive))
return;
if (state == ShadekinState.Extreme || state == ShadekinState.Annoying || state == ShadekinState.High)
{
// passive.DamageCap = 1;
}
else if (state == ShadekinState.Low)
{
// passive.DamageCap = 20;
passive.AllowedStates.Clear();
passive.AllowedStates.Add(MobState.Alive);
passive.Interval = 1f;
}
else if (state != ShadekinState.Dark)
{
// passive.DamageCap = 0;
passive.AllowedStates.Clear();
passive.AllowedStates.Add(MobState.Alive);
passive.AllowedStates.Add(MobState.Critical);
passive.AllowedStates.Add(MobState.Dead);
passive.Interval = 0.5f;
}
}
private void ToggleNightVision(EntityUid uid, ShadekinState state)
{
if (state == ShadekinState.Dark)
{
var nightVisionComponent = EnsureComp<NightVisionComponent>(uid);
nightVisionComponent.Color = Color.FromHex("#808080"); // Delta V - Change Night Vision Color
}
else
{
if (TryComp<NightVisionComponent>(uid, out var nightVision) && nightVision.IsActive)
_flashSystem.Flash(uid, uid, uid, TimeSpan.FromSeconds(0.5 * (int)state), 0.5f);
RemComp<NightVisionComponent>(uid);
}
}
private void ApplyLightDamage(EntityUid uid, float dmg)
{
var damage = new DamageSpecifier();
damage.DamageDict.Add("Heat", dmg);
_damageable.TryChangeDamage(uid, damage, true, false);
}
private void OnRefreshMovementSpeedModifiers(EntityUid uid, ShadekinComponent component, RefreshMovementSpeedModifiersEvent args)
{
if (component.CurrentState == ShadekinState.Low || component.CurrentState == ShadekinState.Annoying ||
component.CurrentState == ShadekinState.Dark || component.CurrentState == ShadekinState.Invalid)
return;
if (!TryComp<MovementSpeedModifierComponent>(uid, out var movement))
return;
var sprintDif = movement.BaseWalkSpeed / movement.BaseSprintSpeed;
args.ModifySpeed(1f, sprintDif);
}
private ShadekinState GetStateByThreshold(ShadekinComponent component, float lightExposure)
{
var returnState = ShadekinState.Dark;
foreach (var (threshold, shadekinState) in component.Thresholds.Reverse())
{
if (threshold <= lightExposure)
{
returnState = shadekinState;
break;
}
}
return returnState;
}
// Delta V - Begin Shadekin Flash Changes
private void GetFlashModifier(EntityUid uid, ShadekinComponent comp, FlashDurationMultiplierEvent args)
{
if (!TryComp<FlashModifierComponent>(uid, out var flashModifier))
return;
args.Multiplier = flashModifier.Modifier;
}
private void OnShadekinFlashed(EntityUid uid, ShadekinComponent comp, AfterFlashedEvent ev)
{
RemComp<NightVisionComponent>(uid);
}
// Delta V - End
public override void Update(float frameTime)
{
base.Update(frameTime);
var query = EntityQueryEnumerator<ShadekinComponent>();
while (query.MoveNext(out var uid, out var component))
{
if (_timing.CurTime < component.NextUpdate)
continue;
component.NextUpdate = _timing.CurTime + component.UpdateCooldown;
var lightExposure = 0f;
if (!_container.IsEntityInContainer(uid))
lightExposure = GetLightExposure(uid);
component.CurrentState = GetStateByThreshold(component, lightExposure);
UpdateAlert(uid, component, (short)component.CurrentState);
SetPassiveBuff(uid, component.CurrentState);
ToggleNightVision(uid, component.CurrentState);
_speed.RefreshMovementSpeedModifiers(uid);
if (component.CurrentState == ShadekinState.Extreme)
ApplyLightDamage(uid, 5);
}
}
}