CatStation/Content.Shared/Doors/Systems/SharedTurnstileSystem.cs
metalgearsloth 0908314c98
Update turnstiles to use Math instead of MathHelper (#45543)
Co-authored-by: Pok245 <113675512+Pok27@users.noreply.github.com>
2026-10-02 18:38:59 +00:00

135 lines
4.5 KiB
C#

using Content.Shared.Access.Systems;
using Content.Shared.Doors.Components;
using Content.Shared.Movement.Pulling.Systems;
using Content.Shared.Popups;
using Content.Shared.Whitelist;
using Robust.Shared.Audio.Systems;
using Robust.Shared.Physics.Events;
using Robust.Shared.Timing;
namespace Content.Shared.Doors.Systems;
/// <summary>
/// This handles logic and interactions related to <see cref="TurnstileComponent"/>
/// </summary>
public abstract partial class SharedTurnstileSystem : EntitySystem
{
[Dependency] private IGameTiming _timing = default!;
[Dependency] private AccessReaderSystem _accessReader = default!;
[Dependency] private SharedAudioSystem _audio = default!;
[Dependency] private EntityWhitelistSystem _entityWhitelist = default!;
[Dependency] private PullingSystem _pulling = default!;
[Dependency] private SharedTransformSystem _transform = default!;
[Dependency] private SharedPopupSystem _popup = default!;
/// <inheritdoc/>
public override void Initialize()
{
SubscribeLocalEvent<TurnstileComponent, PreventCollideEvent>(OnPreventCollide);
SubscribeLocalEvent<TurnstileComponent, StartCollideEvent>(OnStartCollide);
SubscribeLocalEvent<TurnstileComponent, EndCollideEvent>(OnEndCollide);
}
private void OnPreventCollide(Entity<TurnstileComponent> ent, ref PreventCollideEvent args)
{
if (args.Cancelled || !args.OurFixture.Hard || !args.OtherFixture.Hard)
return;
if (ent.Comp.CollideExceptions.Contains(args.OtherEntity))
{
args.Cancelled = true;
return;
}
// We need to add this in here too for chain pulls
if (_pulling.GetPuller(args.OtherEntity) is { } puller && ent.Comp.CollideExceptions.Contains(puller))
{
ent.Comp.CollideExceptions.Add(args.OtherEntity);
Dirty(ent);
args.Cancelled = true;
return;
}
// unblockables go through for free.
if (_entityWhitelist.IsWhitelistFail(ent.Comp.ProcessWhitelist, args.OtherEntity))
{
args.Cancelled = true;
return;
}
if (CanPassDirection(ent, args.OtherEntity))
{
if (!_accessReader.IsAllowed(args.OtherEntity, ent))
return;
ent.Comp.CollideExceptions.Add(args.OtherEntity);
if (_pulling.GetPulling(args.OtherEntity) is { } uid)
ent.Comp.CollideExceptions.Add(uid);
args.Cancelled = true;
Dirty(ent);
}
else
{
if (_timing.CurTime >= ent.Comp.NextResistTime)
{
_popup.PopupEntity(Loc.GetString("turnstile-component-popup-resist", ("turnstile", ent.Owner)), ent, args.OtherEntity);
ent.Comp.NextResistTime = _timing.CurTime + TimeSpan.FromSeconds(0.1);
Dirty(ent);
}
}
}
private void OnStartCollide(Entity<TurnstileComponent> ent, ref StartCollideEvent args)
{
if (!ent.Comp.CollideExceptions.Contains(args.OtherEntity))
{
if (CanPassDirection(ent, args.OtherEntity))
{
if (!_accessReader.IsAllowed(args.OtherEntity, ent))
{
_audio.PlayPredicted(ent.Comp.DenySound, ent, args.OtherEntity);
PlayAnimation(ent, TurnstileStates.Deny);
}
}
return;
}
// if they passed through:
PlayAnimation(ent, TurnstileStates.Spin);
_audio.PlayPredicted(ent.Comp.TurnSound, ent, args.OtherEntity);
}
private void OnEndCollide(Entity<TurnstileComponent> ent, ref EndCollideEvent args)
{
if (!args.OurFixture.Hard)
{
ent.Comp.CollideExceptions.Remove(args.OtherEntity);
Dirty(ent);
}
}
protected bool CanPassDirection(Entity<TurnstileComponent> ent, EntityUid other)
{
var xform = Transform(ent);
var otherXform = Transform(other);
var (pos, rot) = _transform.GetWorldPositionRotation(xform);
var otherPos = _transform.GetWorldPosition(otherXform);
var approachAngle = (pos - otherPos).ToAngle();
var rotateAngle = rot.ToWorldVec().ToAngle();
var diff = Math.Abs(approachAngle - rotateAngle);
diff %= Math.Tau;
if (diff > Math.PI)
diff = Math.Tau - diff;
return diff < Math.PI / 4;
}
protected virtual void PlayAnimation(EntityUid uid, TurnstileStates state)
{
}
}