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;
///
/// This handles logic and interactions related to
///
public abstract partial class SharedTurnstileSystem : EntitySystem
{
[Dependency] private readonly IGameTiming _timing = default!;
[Dependency] private readonly AccessReaderSystem _accessReader = default!;
[Dependency] private readonly SharedAudioSystem _audio = default!;
[Dependency] private readonly EntityWhitelistSystem _entityWhitelist = default!;
[Dependency] private readonly PullingSystem _pulling = default!;
[Dependency] private readonly SharedTransformSystem _transform = default!;
[Dependency] private readonly SharedPopupSystem _popup = default!;
///
public override void Initialize()
{
SubscribeLocalEvent(OnPreventCollide);
SubscribeLocalEvent(OnStartCollide);
SubscribeLocalEvent(OnEndCollide);
}
private void OnPreventCollide(Entity 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.PopupClient(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 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, ent.Comp.DenyState);
}
}
return;
}
// if they passed through:
PlayAnimation(ent, ent.Comp.SpinState);
_audio.PlayPredicted(ent.Comp.TurnSound, ent, args.OtherEntity);
}
private void OnEndCollide(Entity ent, ref EndCollideEvent args)
{
if (!args.OurFixture.Hard)
{
ent.Comp.CollideExceptions.Remove(args.OtherEntity);
Dirty(ent);
}
}
protected bool CanPassDirection(Entity 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 %= MathHelper.TwoPi;
if (diff > Math.PI)
diff = MathHelper.TwoPi - diff;
return diff < Math.PI / 4;
}
protected virtual void PlayAnimation(EntityUid uid, string stateId)
{
}
}