6
2026-01-24 12:49:55 +03:00

862 lines
35 KiB
C#

using System.Numerics;
using Content.Shared._RMC14.Attachable.Components;
using Content.Shared._RMC14.Attachable.Events;
// using Content.Shared._RMC14.Slow;
using Content.Shared._RMC14.Weapons.Ranged;
using Content.Shared.Actions;
using Content.Shared.Actions.Components;
using Content.Shared.Actions.Events;
using Content.Shared.DoAfter;
using Content.Shared.Fluids;
using Content.Shared.Hands;
using Content.Shared.Hands.EntitySystems;
using Content.Shared.Interaction;
using Content.Shared.Interaction.Events;
using Content.Shared.Light;
using Content.Shared.Movement.Events;
using Content.Shared.Physics;
using Content.Shared.Popups;
using Content.Shared.Timing;
using Content.Shared.Toggleable;
using Content.Shared.Weapons.Ranged.Systems;
using Content.Shared.Whitelist;
using Content.Shared.Wieldable.Components;
using Robust.Shared.Audio.Systems;
using Robust.Shared.Map;
using Robust.Shared.Physics;
using Robust.Shared.Timing;
namespace Content.Shared._RMC14.Attachable.Systems;
public sealed class AttachableToggleableSystem : EntitySystem
{
[Dependency] private readonly IGameTiming _gameTiming = default!;
[Dependency] private readonly ActionContainerSystem _actionContainerSystem = default!;
[Dependency] private readonly EntityLookupSystem _entityLookupSystem = default!;
[Dependency] private readonly EntityWhitelistSystem _entityWhitelistSystem = default!;
[Dependency] private readonly MetaDataSystem _metaDataSystem = default!;
[Dependency] private readonly SharedActionsSystem _actionsSystem = default!;
[Dependency] private readonly AttachableHolderSystem _attachableHolderSystem = default!;
[Dependency] private readonly SharedAudioSystem _audioSystem = default!;
[Dependency] private readonly SharedDoAfterSystem _doAfterSystem = default!;
[Dependency] private readonly SharedHandsSystem _handsSystem = default!;
[Dependency] private readonly SharedPopupSystem _popupSystem = default!;
[Dependency] private readonly SharedTransformSystem _transformSystem = default!;
[Dependency] private readonly UseDelaySystem _useDelaySystem = default!;
// [Dependency] private readonly RMCSlowSystem _slow = default!;
private const string attachableToggleUseDelayID = "RMCAttachableToggle";
private const int bracingInvalidCollisionGroup = (int)CollisionGroup.ThrownItem;
private const int bracingRequiredCollisionGroup = (int)CollisionGroup.MidImpassable;
public override void Initialize()
{
SubscribeLocalEvent<AttachableToggleableComponent, ActivateInWorldEvent>(OnActivateInWorld,
after: new[] { typeof(CMGunSystem) });
SubscribeLocalEvent<AttachableToggleableComponent, AttachableAlteredEvent>(OnAttachableAltered,
after: new[] { typeof(AttachableModifiersSystem) });
SubscribeLocalEvent<AttachableToggleableComponent, AttachableToggleableInterruptEvent>(OnAttachableToggleableInterrupt);
SubscribeLocalEvent<AttachableToggleableComponent, AttachableToggleActionEvent>(OnAttachableToggleAction);
SubscribeLocalEvent<AttachableToggleableComponent, AttachableToggleDoAfterEvent>(OnAttachableToggleDoAfter);
SubscribeLocalEvent<AttachableToggleableComponent, AttachableToggleStartedEvent>(OnAttachableToggleStarted);
SubscribeLocalEvent<AttachableToggleableComponent, AttemptShootEvent>(OnAttemptShoot);
SubscribeLocalEvent<AttachableToggleableComponent, AttachableRelayedEvent<GunShotEvent>>(OnGunShot);
SubscribeLocalEvent<AttachableToggleableComponent, GunShotEvent>(OnGunShot);
SubscribeLocalEvent<AttachableToggleableComponent, ToggleActionEvent>(OnToggleAction,
before: new[] { typeof(SharedHandheldLightSystem) });
//SubscribeLocalEvent<AttachableToggleableComponent, UniqueActionEvent>(OnUniqueAction);
SubscribeLocalEvent<AttachableToggleableComponent, GrantAttachableActionsEvent>(OnGrantAttachableActions);
SubscribeLocalEvent<AttachableToggleableComponent, RemoveAttachableActionsEvent>(OnRemoveAttachableActions);
SubscribeLocalEvent<AttachableToggleableComponent, AttachableRelayedEvent<HandDeselectedEvent>>(OnHandDeselected);
SubscribeLocalEvent<AttachableToggleableComponent, AttachableRelayedEvent<GotEquippedHandEvent>>(OnGotEquippedHand);
SubscribeLocalEvent<AttachableToggleableComponent, AttachableRelayedEvent<GotUnequippedHandEvent>>(OnGotUnequippedHand);
SubscribeLocalEvent<AttachableToggleableComponent, SprayAttemptEvent>(OnSprayAttempt);
SubscribeLocalEvent<AttachableToggleableComponent, DroppedEvent>(OnDropped);
SubscribeLocalEvent<AttachableToggleableComponent, AttachableRelayedEvent<DroppedEvent>>(OnDropped);
SubscribeLocalEvent<AttachableMovementLockedComponent, MoveInputEvent>(OnAttachableMovementLockedMoveInput);
SubscribeLocalEvent<AttachableToggleableSimpleActivateComponent, AttachableAlteredEvent>(OnAttachableAltered,
after: new[] { typeof(AttachableModifiersSystem) });
SubscribeLocalEvent<AttachableToggleablePreventShootComponent, AttachableAlteredEvent>(OnAttachableAltered,
after: new[] { typeof(AttachableModifiersSystem) });
SubscribeLocalEvent<AttachableGunPreventShootComponent, AttemptShootEvent>(OnAttemptShoot);
}
#region AttachableAlteredEvent handling
private void OnAttachableAltered(Entity<AttachableToggleableComponent> attachable, ref AttachableAlteredEvent args)
{
switch (args.Alteration)
{
case AttachableAlteredType.Detached:
if (attachable.Comp.SupercedeHolder &&
TryComp(args.Holder, out AttachableHolderComponent? holderComponent) &&
holderComponent.SupercedingAttachable == attachable.Owner)
{
_attachableHolderSystem.SetSupercedingAttachable((args.Holder, holderComponent), null);
}
if (attachable.Comp.Active)
{
var ev = args with { Alteration = AttachableAlteredType.DetachedDeactivated };
RaiseLocalEvent(attachable.Owner, ref ev);
}
attachable.Comp.Attached = false;
attachable.Comp.Active = false;
Dirty(attachable);
break;
case AttachableAlteredType.Attached:
attachable.Comp.Attached = true;
break;
case AttachableAlteredType.Unwielded:
if (!attachable.Comp.WieldedOnly || !attachable.Comp.Active)
break;
Toggle(attachable, args.User, attachable.Comp.DoInterrupt);
break;
}
if (attachable.Comp.Action == null ||
!TryComp(attachable.Comp.Action, out ActionComponent? actionComponent))
{
return;
}
_actionsSystem.SetToggled(attachable.Comp.Action, attachable.Comp.Active);
_actionsSystem.SetEnabled((attachable.Comp.Action.Value, actionComponent), attachable.Comp.Attached);
}
private void OnAttachableAltered(Entity<AttachableToggleableSimpleActivateComponent> attachable, ref AttachableAlteredEvent args)
{
if (args.User == null)
return;
switch (args.Alteration)
{
case AttachableAlteredType.Activated:
RaiseLocalEvent(attachable.Owner, new ActivateInWorldEvent(args.User.Value, args.Holder, true));
break;
case AttachableAlteredType.Deactivated:
RaiseLocalEvent(attachable.Owner, new ActivateInWorldEvent(args.User.Value, args.Holder, true));
break;
case AttachableAlteredType.DetachedDeactivated:
RaiseLocalEvent(attachable.Owner, new ActivateInWorldEvent(args.User.Value, args.Holder, true));
break;
}
}
private void OnAttachableAltered(Entity<AttachableToggleablePreventShootComponent> attachable, ref AttachableAlteredEvent args)
{
if (!TryComp(attachable.Owner, out AttachableToggleableComponent? toggleableComponent))
return;
EnsureComp(args.Holder, out AttachableGunPreventShootComponent preventShootComponent);
switch (args.Alteration)
{
case AttachableAlteredType.Attached:
preventShootComponent.Message = attachable.Comp.Message;
preventShootComponent.PreventShoot = attachable.Comp.ShootWhenActive && !toggleableComponent.Active || !attachable.Comp.ShootWhenActive && toggleableComponent.Active;
break;
case AttachableAlteredType.Detached:
preventShootComponent.Message = "";
preventShootComponent.PreventShoot = false;
break;
case AttachableAlteredType.Activated:
preventShootComponent.PreventShoot = !attachable.Comp.ShootWhenActive;
break;
case AttachableAlteredType.Deactivated:
preventShootComponent.PreventShoot = attachable.Comp.ShootWhenActive;
break;
case AttachableAlteredType.DetachedDeactivated:
preventShootComponent.PreventShoot = false;
break;
}
Dirty(args.Holder, preventShootComponent);
}
#endregion
private void OnGotEquippedHand(Entity<AttachableToggleableComponent> attachable, ref AttachableRelayedEvent<GotEquippedHandEvent> args)
{
if (!attachable.Comp.Attached)
return;
args.Args.Handled = true;
var addEv = new GrantAttachableActionsEvent(args.Args.User);
RaiseLocalEvent(attachable, ref addEv);
}
#region Lockouts and interrupts
private void OnActivateInWorld(Entity<AttachableToggleableComponent> attachable, ref ActivateInWorldEvent args)
{
if (attachable.Comp.AttachedOnly && !attachable.Comp.Attached)
args.Handled = true;
}
private void OnAttemptShoot(Entity<AttachableToggleableComponent> attachable, ref AttemptShootEvent args)
{
if (args.Cancelled)
return;
if (attachable.Comp.AttachedOnly && !attachable.Comp.Attached)
{
args.Cancelled = true;
return;
}
if (attachable.Comp.WieldedUseOnly &&
(!_attachableHolderSystem.TryGetHolder(attachable.Owner, out EntityUid? holderUid) ||
!TryComp(holderUid, out WieldableComponent? wieldableComponent) ||
!wieldableComponent.Wielded))
{
args.Cancelled = true;
if (holderUid == null)
return;
_popupSystem.PopupClient(
Loc.GetString("rmc-attachable-shoot-fail-not-wielded", ("holder", holderUid), ("attachable", attachable)),
args.User,
args.User);
return;
}
}
private void OnGunShot(Entity<AttachableToggleableComponent> attachable, ref AttachableRelayedEvent<GunShotEvent> args)
{
CheckUserBreakOnRotate(args.Args.User);
CheckUserBreakOnFullRotate(args.Args.User, args.Args.FromCoordinates, args.Args.ToCoordinates);
}
private void OnGunShot(Entity<AttachableToggleableComponent> attachable, ref GunShotEvent args)
{
CheckUserBreakOnRotate(args.User);
CheckUserBreakOnFullRotate(args.User, args.FromCoordinates, args.ToCoordinates);
}
private void OnAttemptShoot(Entity<AttachableGunPreventShootComponent> gun, ref AttemptShootEvent args)
{
if (args.Cancelled || !gun.Comp.PreventShoot)
return;
args.Cancelled = true;
_popupSystem.PopupClient(gun.Comp.Message, args.User, args.User);
}
/* private void OnUniqueAction(Entity<AttachableToggleableComponent> attachable, ref UniqueActionEvent args)
{
if (attachable.Comp.AttachedOnly && !attachable.Comp.Attached)
args.Handled = true;
}*/
private void OnHandDeselected(Entity<AttachableToggleableComponent> attachable, ref AttachableRelayedEvent<HandDeselectedEvent> args)
{
if (!attachable.Comp.Attached)
return;
args.Args.Handled = true;
if (!attachable.Comp.NeedHand || !attachable.Comp.Active)
return;
Toggle(attachable, args.Args.User, attachable.Comp.DoInterrupt);
}
private void OnAttachableToggleableInterrupt(Entity<AttachableToggleableComponent> attachable, ref AttachableToggleableInterruptEvent args)
{
if (!attachable.Comp.Active)
return;
Toggle(attachable, args.User, attachable.Comp.DoInterrupt);
}
private void OnGotUnequippedHand(Entity<AttachableToggleableComponent> attachable, ref AttachableRelayedEvent<GotUnequippedHandEvent> args)
{
if (!attachable.Comp.Attached)
return;
args.Args.Handled = true;
if ((attachable.Comp.NeedHand || attachable.Comp.BreakOnDrop) && attachable.Comp.Active)
{
Toggle(attachable, args.Args.User, attachable.Comp.DoInterrupt);
}
var removeEv = new RemoveAttachableActionsEvent(args.Args.User);
RaiseLocalEvent(attachable, ref removeEv);
}
private void OnSprayAttempt(Entity<AttachableToggleableComponent> attachable, ref SprayAttemptEvent args)
{
if (args.Cancelled)
return;
if (attachable.Comp.AttachedOnly && !attachable.Comp.Attached)
args.Cancelled = true;
}
private void OnDropped(Entity<AttachableToggleableComponent> attachable, ref DroppedEvent args)
{
if (attachable.Comp.AttachedOnly && !attachable.Comp.Attached)
return;
if (!attachable.Comp.BreakOnDrop || !attachable.Comp.Active)
return;
Toggle(attachable, args.User);
}
private void OnDropped(Entity<AttachableToggleableComponent> attachable, ref AttachableRelayedEvent<DroppedEvent> args)
{
if (attachable.Comp.AttachedOnly && !attachable.Comp.Attached)
return;
if (!attachable.Comp.BreakOnDrop || !attachable.Comp.Active)
return;
Toggle(attachable, args.Args.User);
}
private void OnAttachableMovementLockedMoveInput(Entity<AttachableMovementLockedComponent> user, ref MoveInputEvent args)
{
if (!args.HasDirectionalMovement)
return;
for (var i = user.Comp.AttachableList.Count - 1; i >= 0; i--)
{
var attachableUid = user.Comp.AttachableList[i];
if (!TryComp(attachableUid, out AttachableToggleableComponent? toggleableComponent) ||
!toggleableComponent.Active ||
!toggleableComponent.BreakOnMove)
{
continue;
}
Toggle((attachableUid, toggleableComponent), user.Owner, toggleableComponent.DoInterrupt);
}
}
private void CheckUserBreakOnRotate(Entity<AttachableDirectionLockedComponent?> user)
{
if (user.Comp == null)
{
if (!TryComp(user.Owner, out AttachableDirectionLockedComponent? lockedComponent))
return;
user.Comp = lockedComponent;
}
if (Transform(user.Owner).LocalRotation.GetCardinalDir() == user.Comp.LockedDirection)
return;
for (var i = user.Comp.AttachableList.Count - 1; i >= 0; i--)
{
var attachableUid = user.Comp.AttachableList[i];
if (!TryComp(attachableUid, out AttachableToggleableComponent? toggleableComponent) ||
!toggleableComponent.Active ||
!toggleableComponent.BreakOnRotate)
{
continue;
}
Toggle((attachableUid, toggleableComponent), user.Owner, toggleableComponent.DoInterrupt);
}
}
private void CheckUserBreakOnFullRotate(Entity<AttachableSideLockedComponent?> user, EntityCoordinates playerPos, EntityCoordinates targetPos)
{
if (user.Comp == null)
{
if (!TryComp(user.Owner, out AttachableSideLockedComponent? lockedComponent))
return;
user.Comp = lockedComponent;
}
if (user.Comp.LockedDirection == null)
return;
var initialAngle = user.Comp.LockedDirection.Value.ToAngle();
var playerMapPos = _transformSystem.ToMapCoordinates(playerPos);
var targetMapPos = _transformSystem.ToMapCoordinates(targetPos);
var currentAngle = (targetMapPos.Position - playerMapPos.Position).ToWorldAngle();
var differenceFromLockedAngle = (currentAngle.Degrees - initialAngle.Degrees + 180 + 360) % 360 - 180;
if (differenceFromLockedAngle > -90 && differenceFromLockedAngle < 90)
return;
for (var i = user.Comp.AttachableList.Count - 1; i >= 0; i--)
{
var attachableUid = user.Comp.AttachableList[i];
if (!TryComp(attachableUid, out AttachableToggleableComponent? toggleableComponent) ||
!toggleableComponent.Active ||
!toggleableComponent.BreakOnFullRotate)
{
continue;
}
Toggle((attachableUid, toggleableComponent), user.Owner, toggleableComponent.DoInterrupt);
}
}
#endregion
#region Toggling
private void OnAttachableToggleStarted(Entity<AttachableToggleableComponent> attachable, ref AttachableToggleStartedEvent args)
{
if (!CanStartToggleDoAfter(attachable, ref args))
return;
var popupText = Loc.GetString(attachable.Comp.Active ? attachable.Comp.DeactivatePopupText : attachable.Comp.ActivatePopupText, ("attachable", attachable.Owner));
_doAfterSystem.TryStartDoAfter(new DoAfterArgs(
EntityManager,
args.User,
GetToggleDoAfter(attachable, args.Holder, args.User, ref popupText),
new AttachableToggleDoAfterEvent(args.SlotId, popupText),
attachable,
target: attachable.Owner,
used: args.Holder)
{
NeedHand = attachable.Comp.DoAfterNeedHand,
BreakOnMove = attachable.Comp.DoAfterBreakOnMove
});
Dirty(attachable);
}
private bool CanStartToggleDoAfter(Entity<AttachableToggleableComponent> attachable, ref AttachableToggleStartedEvent args, bool silent = false)
{
if (TryComp(attachable.Owner, out UseDelayComponent? useDelayComponent) &&
_useDelaySystem.IsDelayed((attachable.Owner, useDelayComponent), attachableToggleUseDelayID))
{
return false;
}
_attachableHolderSystem.TryGetUser(attachable.Owner, out var userUid);
if (attachable.Comp.HeldOnlyActivate && !attachable.Comp.Active && (userUid == null || !_handsSystem.IsHolding(userUid.Value, args.Holder, out _)))
{
if (!silent)
_popupSystem.PopupClient(
Loc.GetString("rmc-attachable-activation-fail-not-held", ("holder", args.Holder), ("attachable", attachable)),
args.User,
args.User);
return false;
}
if (attachable.Comp.UserOnly && userUid != args.User)
{
if (!silent)
_popupSystem.PopupClient(
Loc.GetString("rmc-attachable-activation-fail-not-owned", ("holder", args.Holder), ("attachable", attachable)),
args.User,
args.User);
return false;
}
if (!attachable.Comp.Active && attachable.Comp.WieldedOnly && (!TryComp(args.Holder, out WieldableComponent? wieldableComponent) || !wieldableComponent.Wielded))
{
if (!silent)
_popupSystem.PopupClient(
Loc.GetString("rmc-attachable-activation-fail-not-wielded", ("holder", args.Holder), ("attachable", attachable)),
args.User,
args.User);
return false;
}
return true;
}
private TimeSpan GetToggleDoAfter(Entity<AttachableToggleableComponent> attachable, EntityUid holderUid, EntityUid userUid, ref string popupText)
{
if (!TryComp(holderUid, out TransformComponent? transformComponent) || !transformComponent.ParentUid.Valid)
return TimeSpan.FromSeconds(0f);
var extraDoAfter = transformComponent.ParentUid == userUid ? 0f : 0.5f;
switch (attachable.Comp.InstantToggle)
{
case AttachableInstantToggleConditions.Brace:
if (attachable.Comp.Active || transformComponent.ParentUid != userUid || !TryComp(userUid, out TransformComponent? userTransform))
break;
TimeSpan? doAfter;
var coords = userTransform.Coordinates;
Func<EntityCoordinates, EntityCoordinates, bool> comparer = (EntityCoordinates userCoords, EntityCoordinates entCoords) => { return false; };
var coordsShift = new Vector2(0f, 0f);
Func<HashSet<EntityUid>, EntityUid?> GetBracingSurface = (HashSet<EntityUid> ents) =>
{
foreach (var entity in ents)
{
if (!TryComp(entity, out FixturesComponent? fixturesComponent) || !Transform(entity).Anchored)
continue;
foreach (var fixture in fixturesComponent.Fixtures.Values)
{
if ((fixture.CollisionLayer & bracingInvalidCollisionGroup) != 0 || (fixture.CollisionLayer & bracingRequiredCollisionGroup) == 0)
continue;
if (!comparer(coords, Transform(entity).Coordinates))
continue;
return entity;
}
}
return null;
};
switch (userTransform.LocalRotation.GetCardinalDir())
{
case Direction.South:
comparer = (EntityCoordinates userCoords, EntityCoordinates entCoords) => { return entCoords.Y < userCoords.Y; };
coordsShift = new Vector2(0f, -0.7f);
break;
case Direction.North:
comparer = (EntityCoordinates userCoords, EntityCoordinates entCoords) => { return entCoords.Y > userCoords.Y; };
coordsShift = new Vector2(0f, 0.7f);
break;
case Direction.East:
comparer = (EntityCoordinates userCoords, EntityCoordinates entCoords) => { return entCoords.X > userCoords.X; };
coordsShift = new Vector2(0.7f, 0f);
break;
case Direction.West:
comparer = (EntityCoordinates userCoords, EntityCoordinates entCoords) => { return entCoords.X < userCoords.X; };
coordsShift = new Vector2(-0.7f, 0f);
break;
default:
break;
}
var surface = GetBracingSurface(_entityLookupSystem.GetEntitiesInRange(coords, 0.5f, LookupFlags.Dynamic | LookupFlags.Static));
if (surface != null)
{
popupText = Loc.GetString("attachable-popup-activate-deploy-on-generic", ("attachable", attachable.Owner), ("surface", surface));
return TimeSpan.FromSeconds(0f);
}
coords = new EntityCoordinates(coords.EntityId, coords.Position + coordsShift);
surface = GetBracingSurface(_entityLookupSystem.GetEntitiesInRange(coords, 0.5f, LookupFlags.Dynamic | LookupFlags.Static));
if (surface != null)
{
popupText = Loc.GetString("attachable-popup-activate-deploy-on-generic", ("attachable", attachable.Owner), ("surface", surface));
return TimeSpan.FromSeconds(0f);
}
popupText = Loc.GetString("attachable-popup-activate-deploy-on-ground", ("attachable", attachable.Owner));
break;
default:
break;
}
return TimeSpan.FromSeconds(Math.Max(
(attachable.Comp.DeactivateDoAfter != null && attachable.Comp.Active
? attachable.Comp.DeactivateDoAfter.Value
: attachable.Comp.DoAfter
) + extraDoAfter,
0));
}
private void OnAttachableToggleDoAfter(Entity<AttachableToggleableComponent> attachable,
ref AttachableToggleDoAfterEvent args)
{
if (args.Cancelled || args.Handled)
return;
if (args.Target is not { } target || args.Used is not { } used)
return;
if (!HasComp<AttachableToggleableComponent>(target))
return;
if (!TryComp(args.Used, out AttachableHolderComponent? holderComponent))
return;
FinishToggle(attachable, (used, holderComponent), args.SlotId, args.User, args.PopupText);
args.Handled = true;
Dirty(attachable);
}
private void RemoveUnusedLocks(Entity<AttachableToggleableComponent> attachable, EntityUid? userUid)
{
if (userUid == null)
return;
if (attachable.Comp.BreakOnMove && TryComp<AttachableMovementLockedComponent>(userUid.Value, out var movementLockedComponent))
{
movementLockedComponent.AttachableList.Remove(attachable.Owner);
if (movementLockedComponent.AttachableList.Count == 0)
RemCompDeferred<AttachableMovementLockedComponent>(userUid.Value);
}
if (attachable.Comp.BreakOnRotate && TryComp<AttachableDirectionLockedComponent>(userUid.Value, out var directionLockedComponent))
{
directionLockedComponent.AttachableList.Remove(attachable.Owner);
if (directionLockedComponent.AttachableList.Count == 0)
RemCompDeferred<AttachableDirectionLockedComponent>(userUid.Value);
}
if (attachable.Comp.BreakOnFullRotate && TryComp<AttachableSideLockedComponent>(userUid.Value, out var sideLockedComponent))
{
sideLockedComponent.AttachableList.Remove(attachable.Owner);
if (sideLockedComponent.AttachableList.Count == 0)
RemCompDeferred<AttachableSideLockedComponent>(userUid.Value);
}
}
private void FinishToggle(
Entity<AttachableToggleableComponent> attachable,
Entity<AttachableHolderComponent> holder,
string slotId,
EntityUid? userUid,
string popupText,
bool interrupted = false)
{
attachable.Comp.Active = !attachable.Comp.Active;
var mode = attachable.Comp.Active
? AttachableAlteredType.Activated
: interrupted ? AttachableAlteredType.Interrupted : AttachableAlteredType.Deactivated;
var ev = new AttachableAlteredEvent(holder.Owner, mode, userUid);
RaiseLocalEvent(attachable.Owner, ref ev);
var holderEv = new AttachableHolderAttachablesAlteredEvent(attachable.Owner, slotId, mode);
RaiseLocalEvent(holder.Owner, ref holderEv);
_useDelaySystem.SetLength(attachable.Owner, attachable.Comp.UseDelay, attachableToggleUseDelayID);
_useDelaySystem.TryResetDelay(attachable.Owner, id: attachableToggleUseDelayID);
_actionsSystem.StartUseDelay(attachable.Comp.Action);
if (attachable.Comp.ShowTogglePopup && userUid != null)
_popupSystem.PopupClient(popupText, userUid.Value, userUid.Value);
_audioSystem.PlayPredicted(
attachable.Comp.Active ? attachable.Comp.ActivateSound : attachable.Comp.DeactivateSound,
attachable,
userUid);
if (!attachable.Comp.Active)
{
if (attachable.Comp.SupercedeHolder && holder.Comp.SupercedingAttachable == attachable.Owner)
_attachableHolderSystem.SetSupercedingAttachable(holder, null);
RemoveUnusedLocks(attachable, userUid);
return;
}
if (attachable.Comp.BreakOnMove && userUid != null)
{
var movementLockedComponent = EnsureComp<AttachableMovementLockedComponent>(userUid.Value);
movementLockedComponent.AttachableList.Add(attachable.Owner);
}
if (attachable.Comp.BreakOnRotate && userUid != null)
{
var directionLockedComponent = EnsureComp<AttachableDirectionLockedComponent>(userUid.Value);
directionLockedComponent.AttachableList.Add(attachable.Owner);
if (directionLockedComponent.LockedDirection == null)
directionLockedComponent.LockedDirection = Transform(userUid.Value).LocalRotation.GetCardinalDir();
}
if (attachable.Comp.BreakOnFullRotate && userUid != null)
{
var sideLockedComponent = EnsureComp<AttachableSideLockedComponent>(userUid.Value);
sideLockedComponent.AttachableList.Add(attachable.Owner);
if (sideLockedComponent.LockedDirection == null)
sideLockedComponent.LockedDirection = Transform(userUid.Value).LocalRotation.GetCardinalDir();
}
if (!attachable.Comp.SupercedeHolder)
return;
if (holder.Comp.SupercedingAttachable != null &&
TryComp(holder.Comp.SupercedingAttachable, out AttachableToggleableComponent? toggleableComponent))
{
toggleableComponent.Active = false;
ev = new AttachableAlteredEvent(holder.Owner, AttachableAlteredType.Deactivated);
RaiseLocalEvent(holder.Comp.SupercedingAttachable.Value, ref ev);
if (_attachableHolderSystem.TryGetSlotId(holder.Owner, attachable.Owner, out var deactivatedSlot))
{
holderEv = new AttachableHolderAttachablesAlteredEvent(holder.Comp.SupercedingAttachable.Value,
deactivatedSlot,
AttachableAlteredType.Deactivated);
RaiseLocalEvent(holder.Owner, ref holderEv);
}
}
_attachableHolderSystem.SetSupercedingAttachable(holder, attachable.Owner);
}
private void Toggle(Entity<AttachableToggleableComponent> attachable, EntityUid? user, bool interrupted = false)
{
if (!_attachableHolderSystem.TryGetHolder(attachable.Owner, out var holderUid) ||
!TryComp(holderUid, out AttachableHolderComponent? holderComponent) ||
!_attachableHolderSystem.TryGetSlotId(holderUid.Value, attachable.Owner, out var slotId))
{
return;
}
FinishToggle(
attachable,
(holderUid.Value, holderComponent),
slotId,
user,
Loc.GetString(attachable.Comp.Active ? attachable.Comp.DeactivatePopupText : attachable.Comp.ActivatePopupText, ("attachable", attachable.Owner)),
interrupted);
Dirty(attachable);
}
#endregion
#region Actions
private void OnGrantAttachableActions(Entity<AttachableToggleableComponent> ent, ref GrantAttachableActionsEvent args)
{
GrantAttachableActions(ent, args.User);
RelayAttachableActions(ent, args.User);
}
private void GrantAttachableActions(Entity<AttachableToggleableComponent> ent, EntityUid user, bool doSecondTry = true)
{
// This is to prevent ActionContainerSystem from shitting itself if the attachment has actions other than its attachment toggle.
if (!TryComp(ent.Owner, out ActionsContainerComponent? actionsContainerComponent) || actionsContainerComponent.Container == null)
{
EnsureComp<ActionsContainerComponent>(ent.Owner);
if (doSecondTry)
GrantAttachableActions(ent, user, false);
return;
}
var exists = ent.Comp.Action != null;
_actionContainerSystem.EnsureAction(ent, ref ent.Comp.Action, ent.Comp.ActionId, actionsContainerComponent);
if (ent.Comp.Action is not { } actionId)
return;
_actionsSystem.GrantContainedAction(user, ent.Owner, actionId);
if (exists)
return;
_metaDataSystem.SetEntityName(actionId, ent.Comp.ActionName);
_metaDataSystem.SetEntityDescription(actionId, ent.Comp.ActionDesc);
if (_actionsSystem.GetAction(actionId) is { } action)
{
var actionEnt = action.AsNullable();
_actionsSystem.SetIcon(actionEnt, ent.Comp.Icon);
_actionsSystem.SetIconOn(actionEnt, ent.Comp.IconActive);
_actionsSystem.SetEnabled(actionEnt, ent.Comp.Attached);
_actionsSystem.SetUseDelay(actionEnt, ent.Comp.UseDelay);
}
Dirty(ent);
}
private void RelayAttachableActions(Entity<AttachableToggleableComponent> attachable, EntityUid user)
{
if (attachable.Comp.ActionsToRelayWhitelist == null || !TryComp(attachable.Owner, out ActionsContainerComponent? actionsContainerComponent))
return;
foreach (var actionUid in actionsContainerComponent.Container.ContainedEntities)
{
if (!_entityWhitelistSystem.IsWhitelistPass(attachable.Comp.ActionsToRelayWhitelist, actionUid))
continue;
_actionsSystem.GrantContainedAction(user, (attachable.Owner, actionsContainerComponent), actionUid);
}
}
private void OnRemoveAttachableActions(Entity<AttachableToggleableComponent> ent, ref RemoveAttachableActionsEvent args)
{
RemoveAttachableActions(ent, args.User);
RemoveRelayedActions(ent, args.User);
}
private void RemoveAttachableActions(Entity<AttachableToggleableComponent> ent, EntityUid user)
{
if (ent.Comp.Action is not { } action)
return;
if (!HasComp<InstantActionComponent>(action) ||
!TryComp(action, out ActionComponent? actionComponent) ||
actionComponent.AttachedEntity != user)
{
return;
}
_actionsSystem.RemoveProvidedAction(user, ent, action);
}
private void RemoveRelayedActions(Entity<AttachableToggleableComponent> attachable, EntityUid user)
{
if (attachable.Comp.ActionsToRelayWhitelist == null || !TryComp(attachable.Owner, out ActionsContainerComponent? actionsContainerComponent))
return;
foreach (var actionUid in actionsContainerComponent.Container.ContainedEntities)
{
if (!_entityWhitelistSystem.IsWhitelistPass(attachable.Comp.ActionsToRelayWhitelist, actionUid))
continue;
_actionsSystem.RemoveProvidedAction(user, attachable.Owner, actionUid);
}
}
private void OnAttachableToggleAction(Entity<AttachableToggleableComponent> attachable,
ref AttachableToggleActionEvent args)
{
args.Handled = true;
if (!attachable.Comp.Attached)
return;
if (!_attachableHolderSystem.TryGetHolder(attachable.Owner, out var holderUid) ||
!TryComp(holderUid, out AttachableHolderComponent? holderComponent) ||
!_attachableHolderSystem.TryGetSlotId(holderUid.Value, attachable.Owner, out var slotId))
{
return;
}
var ev = new AttachableToggleStartedEvent(holderUid.Value,
args.Performer,
slotId);
RaiseLocalEvent(attachable.Owner, ref ev);
}
private void OnToggleAction(Entity<AttachableToggleableComponent> attachable, ref ToggleActionEvent args)
{
if (attachable.Comp.AttachedOnly && !attachable.Comp.Attached)
args.Handled = true;
}
#endregion
}