294 lines
11 KiB
C#
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);
|
|
}
|
|
}
|
|
}
|