Quantum Spin Inverter (#23756)
This commit is contained in:
@@ -0,0 +1,65 @@
|
||||
using Content.Shared.Teleportation.Systems;
|
||||
using Content.Shared.Whitelist;
|
||||
using Robust.Shared.Audio;
|
||||
using Robust.Shared.GameStates;
|
||||
using Robust.Shared.Serialization;
|
||||
using Robust.Shared.Serialization.TypeSerializers.Implementations.Custom;
|
||||
|
||||
namespace Content.Shared.Teleportation.Components;
|
||||
|
||||
/// <summary>
|
||||
/// This is used for an entity that, when linked to another valid entity, allows the two to swap positions,
|
||||
/// additionally swapping the positions of the parents.
|
||||
/// </summary>
|
||||
[RegisterComponent, NetworkedComponent, AutoGenerateComponentState]
|
||||
[Access(typeof(SwapTeleporterSystem))]
|
||||
public sealed partial class SwapTeleporterComponent : Component
|
||||
{
|
||||
/// <summary>
|
||||
/// The other SwapTeleporterComponent that this one is linked to
|
||||
/// </summary>
|
||||
[DataField, AutoNetworkedField]
|
||||
public EntityUid? LinkedEnt;
|
||||
|
||||
/// <summary>
|
||||
/// the time at which <see cref="TeleportDelay"/> ends and the teleportation occurs
|
||||
/// </summary>
|
||||
[DataField(customTypeSerializer: typeof(TimeOffsetSerializer)), ViewVariables(VVAccess.ReadWrite), AutoNetworkedField]
|
||||
public TimeSpan? TeleportTime;
|
||||
|
||||
/// <summary>
|
||||
/// Delay after starting the teleport and it occuring.
|
||||
/// </summary>
|
||||
[DataField, ViewVariables(VVAccess.ReadWrite)]
|
||||
public TimeSpan TeleportDelay = TimeSpan.FromSeconds(2.5f);
|
||||
|
||||
/// <summary>
|
||||
/// The time at which <see cref="Cooldown"/> ends and teleportation can occur again.
|
||||
/// </summary>
|
||||
[DataField(customTypeSerializer: typeof(TimeOffsetSerializer)), ViewVariables(VVAccess.ReadWrite), AutoNetworkedField]
|
||||
public TimeSpan NextTeleportUse;
|
||||
|
||||
/// <summary>
|
||||
/// A minimum waiting period inbetween teleports.
|
||||
/// </summary>
|
||||
[DataField, ViewVariables(VVAccess.ReadWrite)]
|
||||
public TimeSpan Cooldown = TimeSpan.FromMinutes(5);
|
||||
|
||||
/// <summary>
|
||||
/// Sound played when teleportation begins
|
||||
/// </summary>
|
||||
[DataField]
|
||||
public SoundSpecifier? TeleportSound = new SoundPathSpecifier("/Audio/Weapons/flash.ogg");
|
||||
|
||||
/// <summary>
|
||||
/// A whitelist for what entities are valid for <see cref="LinkedEnt"/>.
|
||||
/// </summary>
|
||||
[DataField]
|
||||
public EntityWhitelist TeleporterWhitelist = new();
|
||||
}
|
||||
|
||||
[Serializable, NetSerializable]
|
||||
public enum SwapTeleporterVisuals : byte
|
||||
{
|
||||
Linked
|
||||
}
|
||||
246
Content.Shared/Teleportation/Systems/SwapTeleporterSystem.cs
Normal file
246
Content.Shared/Teleportation/Systems/SwapTeleporterSystem.cs
Normal file
@@ -0,0 +1,246 @@
|
||||
using Content.Shared.Examine;
|
||||
using Content.Shared.IdentityManagement;
|
||||
using Content.Shared.Interaction;
|
||||
using Content.Shared.Popups;
|
||||
using Content.Shared.Teleportation.Components;
|
||||
using Content.Shared.Verbs;
|
||||
using Robust.Shared.Audio.Systems;
|
||||
using Robust.Shared.Map.Components;
|
||||
using Robust.Shared.Physics;
|
||||
using Robust.Shared.Physics.Components;
|
||||
using Robust.Shared.Timing;
|
||||
|
||||
namespace Content.Shared.Teleportation.Systems;
|
||||
|
||||
/// <summary>
|
||||
/// This handles <see cref="SwapTeleporterComponent"/>
|
||||
/// </summary>
|
||||
public sealed class SwapTeleporterSystem : EntitySystem
|
||||
{
|
||||
[Dependency] private readonly IGameTiming _timing = default!;
|
||||
[Dependency] private readonly SharedAudioSystem _audio = default!;
|
||||
[Dependency] private readonly SharedAppearanceSystem _appearance = default!;
|
||||
[Dependency] private readonly SharedPopupSystem _popup = default!;
|
||||
[Dependency] private readonly SharedTransformSystem _transform = default!;
|
||||
|
||||
private EntityQuery<TransformComponent> _xformQuery;
|
||||
|
||||
/// <inheritdoc/>
|
||||
public override void Initialize()
|
||||
{
|
||||
SubscribeLocalEvent<SwapTeleporterComponent, AfterInteractEvent>(OnInteract);
|
||||
SubscribeLocalEvent<SwapTeleporterComponent, GetVerbsEvent<AlternativeVerb>>(OnGetAltVerb);
|
||||
SubscribeLocalEvent<SwapTeleporterComponent, ActivateInWorldEvent>(OnActivateInWorld);
|
||||
SubscribeLocalEvent<SwapTeleporterComponent, ExaminedEvent>(OnExamined);
|
||||
|
||||
SubscribeLocalEvent<SwapTeleporterComponent, EntityUnpausedEvent>(OnUnpaused);
|
||||
SubscribeLocalEvent<SwapTeleporterComponent, ComponentShutdown>(OnShutdown);
|
||||
|
||||
_xformQuery = GetEntityQuery<TransformComponent>();
|
||||
}
|
||||
|
||||
private void OnInteract(Entity<SwapTeleporterComponent> ent, ref AfterInteractEvent args)
|
||||
{
|
||||
var (uid, comp) = ent;
|
||||
if (args.Target == null)
|
||||
return;
|
||||
|
||||
var target = args.Target.Value;
|
||||
|
||||
if (!TryComp<SwapTeleporterComponent>(target, out var targetComp))
|
||||
return;
|
||||
|
||||
if (!comp.TeleporterWhitelist.IsValid(target, EntityManager) ||
|
||||
!targetComp.TeleporterWhitelist.IsValid(uid, EntityManager))
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
if (comp.LinkedEnt != null)
|
||||
{
|
||||
_popup.PopupClient(Loc.GetString("swap-teleporter-popup-link-fail-already"), uid, args.User);
|
||||
return;
|
||||
}
|
||||
|
||||
if (targetComp.LinkedEnt != null)
|
||||
{
|
||||
_popup.PopupClient(Loc.GetString("swap-teleporter-popup-link-fail-already-other"), uid, args.User);
|
||||
return;
|
||||
}
|
||||
|
||||
comp.LinkedEnt = target;
|
||||
targetComp.LinkedEnt = uid;
|
||||
Dirty(uid, comp);
|
||||
Dirty(target, targetComp);
|
||||
_appearance.SetData(uid, SwapTeleporterVisuals.Linked, true);
|
||||
_appearance.SetData(target, SwapTeleporterVisuals.Linked, true);
|
||||
_popup.PopupClient(Loc.GetString("swap-teleporter-popup-link-create"), uid, args.User);
|
||||
}
|
||||
|
||||
private void OnGetAltVerb(Entity<SwapTeleporterComponent> ent, ref GetVerbsEvent<AlternativeVerb> args)
|
||||
{
|
||||
var (uid, comp) = ent;
|
||||
if (!args.CanAccess || !args.CanInteract || args.Hands == null || comp.TeleportTime != null)
|
||||
return;
|
||||
|
||||
if (!TryComp<SwapTeleporterComponent>(comp.LinkedEnt, out var otherComp) || otherComp.TeleportTime != null)
|
||||
return;
|
||||
|
||||
var user = args.User;
|
||||
args.Verbs.Add(new AlternativeVerb
|
||||
{
|
||||
Text = Loc.GetString("swap-teleporter-verb-destroy-link"),
|
||||
Priority = 1,
|
||||
Act = () =>
|
||||
{
|
||||
DestroyLink((uid, comp), user);
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
private void OnActivateInWorld(Entity<SwapTeleporterComponent> ent, ref ActivateInWorldEvent args)
|
||||
{
|
||||
var (uid, comp) = ent;
|
||||
var user = args.User;
|
||||
if (comp.TeleportTime != null)
|
||||
return;
|
||||
|
||||
if (comp.LinkedEnt == null)
|
||||
{
|
||||
_popup.PopupClient(Loc.GetString("swap-teleporter-popup-teleport-cancel-link"), ent, user);
|
||||
return;
|
||||
}
|
||||
|
||||
// don't allow teleporting to happen if the linked one is already teleporting
|
||||
if (!TryComp<SwapTeleporterComponent>(comp.LinkedEnt, out var otherComp)
|
||||
|| otherComp.TeleportTime != null)
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
if (_timing.CurTime < comp.NextTeleportUse)
|
||||
{
|
||||
_popup.PopupClient(Loc.GetString("swap-teleporter-popup-teleport-cancel-time"), ent, user);
|
||||
return;
|
||||
}
|
||||
|
||||
_audio.PlayPredicted(comp.TeleportSound, uid, user);
|
||||
_audio.PlayPredicted(otherComp.TeleportSound, comp.LinkedEnt.Value, user);
|
||||
comp.NextTeleportUse = _timing.CurTime + comp.Cooldown;
|
||||
comp.TeleportTime = _timing.CurTime + comp.TeleportDelay;
|
||||
Dirty(uid, comp);
|
||||
}
|
||||
|
||||
public void DoTeleport(Entity<SwapTeleporterComponent, TransformComponent> ent)
|
||||
{
|
||||
var (uid, comp, xform) = ent;
|
||||
|
||||
comp.TeleportTime = null;
|
||||
|
||||
Dirty(uid, comp);
|
||||
if (comp.LinkedEnt is not { } linkedEnt)
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
var teleEnt = GetTeleportingEntity((uid, xform));
|
||||
var teleEntXform = Transform(teleEnt);
|
||||
var otherTeleEnt = GetTeleportingEntity((linkedEnt, Transform(linkedEnt)));
|
||||
var otherTeleEntXform = Transform(otherTeleEnt);
|
||||
|
||||
_popup.PopupEntity(Loc.GetString("swap-teleporter-popup-teleport-other",
|
||||
("entity", Identity.Entity(linkedEnt, EntityManager))),
|
||||
otherTeleEnt,
|
||||
otherTeleEnt,
|
||||
PopupType.MediumCaution);
|
||||
var pos = teleEntXform.Coordinates;
|
||||
var otherPos = otherTeleEntXform.Coordinates;
|
||||
|
||||
_transform.SetCoordinates(teleEnt, otherPos);
|
||||
_transform.SetCoordinates(otherTeleEnt, pos);
|
||||
}
|
||||
|
||||
/// <remarks>
|
||||
/// HYAH -link
|
||||
/// </remarks>
|
||||
public void DestroyLink(Entity<SwapTeleporterComponent?> ent, EntityUid? user)
|
||||
{
|
||||
if (!Resolve(ent, ref ent.Comp, false))
|
||||
return;
|
||||
var linkedNullable = ent.Comp.LinkedEnt;
|
||||
|
||||
ent.Comp.LinkedEnt = null;
|
||||
ent.Comp.TeleportTime = null;
|
||||
_appearance.SetData(ent, SwapTeleporterVisuals.Linked, false);
|
||||
Dirty(ent, ent.Comp);
|
||||
|
||||
if (user != null)
|
||||
_popup.PopupClient(Loc.GetString("swap-teleporter-popup-link-destroyed"), ent, user.Value);
|
||||
else
|
||||
_popup.PopupEntity(Loc.GetString("swap-teleporter-popup-link-destroyed"), ent);
|
||||
|
||||
if (linkedNullable is {} linked)
|
||||
DestroyLink(linked, user); // the linked one is shown globally
|
||||
}
|
||||
|
||||
private EntityUid GetTeleportingEntity(Entity<TransformComponent> ent)
|
||||
{
|
||||
var parent = ent.Comp.ParentUid;
|
||||
|
||||
if (HasComp<MapGridComponent>(parent) || HasComp<MapComponent>(parent))
|
||||
return ent;
|
||||
|
||||
if (!_xformQuery.TryGetComponent(parent, out var parentXform) || parentXform.Anchored)
|
||||
return ent;
|
||||
|
||||
if (!TryComp<PhysicsComponent>(parent, out var body) || body.BodyType == BodyType.Static)
|
||||
return ent;
|
||||
|
||||
return GetTeleportingEntity((parent, parentXform));
|
||||
}
|
||||
|
||||
private void OnExamined(Entity<SwapTeleporterComponent> ent, ref ExaminedEvent args)
|
||||
{
|
||||
var (_, comp) = ent;
|
||||
using (args.PushGroup(nameof(SwapTeleporterComponent)))
|
||||
{
|
||||
var locale = comp.LinkedEnt == null
|
||||
? "swap-teleporter-examine-link-absent"
|
||||
: "swap-teleporter-examine-link-present";
|
||||
args.PushMarkup(Loc.GetString(locale));
|
||||
|
||||
if (_timing.CurTime < comp.NextTeleportUse)
|
||||
{
|
||||
args.PushMarkup(Loc.GetString("swap-teleporter-examine-time-remaining",
|
||||
("second", (int) ((comp.NextTeleportUse - _timing.CurTime).TotalSeconds + 0.5f))));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private void OnUnpaused(Entity<SwapTeleporterComponent> ent, ref EntityUnpausedEvent args)
|
||||
{
|
||||
ent.Comp.NextTeleportUse += args.PausedTime;
|
||||
}
|
||||
|
||||
private void OnShutdown(Entity<SwapTeleporterComponent> ent, ref ComponentShutdown args)
|
||||
{
|
||||
DestroyLink((ent, ent), null);
|
||||
}
|
||||
|
||||
public override void Update(float frameTime)
|
||||
{
|
||||
base.Update(frameTime);
|
||||
|
||||
var query = EntityQueryEnumerator<SwapTeleporterComponent, TransformComponent>();
|
||||
while (query.MoveNext(out var uid, out var comp, out var xform))
|
||||
{
|
||||
if (comp.TeleportTime == null)
|
||||
continue;
|
||||
|
||||
if (_timing.CurTime < comp.TeleportTime)
|
||||
continue;
|
||||
|
||||
DoTeleport((uid, comp, xform));
|
||||
}
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user