]> git.smokeofanarchy.ru Git - space-station-14.git/commitdiff
Quantum Spin Inverter (#23756)
authorNemanja <98561806+EmoGarbage404@users.noreply.github.com>
Tue, 9 Jan 2024 07:37:55 +0000 (02:37 -0500)
committerGitHub <noreply@github.com>
Tue, 9 Jan 2024 07:37:55 +0000 (00:37 -0700)
14 files changed:
Content.Shared/Teleportation/Components/SwapTeleporterComponent.cs [new file with mode: 0644]
Content.Shared/Teleportation/Systems/SwapTeleporterSystem.cs [new file with mode: 0644]
Resources/Locale/en-US/portal/swap-teleporter.ftl [new file with mode: 0644]
Resources/Locale/en-US/research/technologies.ftl
Resources/Prototypes/Entities/Objects/Devices/swapper.yml [new file with mode: 0644]
Resources/Prototypes/Entities/Structures/Machines/lathe.yml
Resources/Prototypes/Recipes/Lathes/devices.yml
Resources/Prototypes/Research/experimental.yml
Resources/Prototypes/tags.yml
Resources/Textures/Objects/Devices/swapper.rsi/icon.png [new file with mode: 0644]
Resources/Textures/Objects/Devices/swapper.rsi/inhand-left.png [new file with mode: 0644]
Resources/Textures/Objects/Devices/swapper.rsi/inhand-right.png [new file with mode: 0644]
Resources/Textures/Objects/Devices/swapper.rsi/linked.png [new file with mode: 0644]
Resources/Textures/Objects/Devices/swapper.rsi/meta.json [new file with mode: 0644]

diff --git a/Content.Shared/Teleportation/Components/SwapTeleporterComponent.cs b/Content.Shared/Teleportation/Components/SwapTeleporterComponent.cs
new file mode 100644 (file)
index 0000000..7a7bac8
--- /dev/null
@@ -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
+}
diff --git a/Content.Shared/Teleportation/Systems/SwapTeleporterSystem.cs b/Content.Shared/Teleportation/Systems/SwapTeleporterSystem.cs
new file mode 100644 (file)
index 0000000..e900700
--- /dev/null
@@ -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));
+        }
+    }
+}
diff --git a/Resources/Locale/en-US/portal/swap-teleporter.ftl b/Resources/Locale/en-US/portal/swap-teleporter.ftl
new file mode 100644 (file)
index 0000000..f13fa9b
--- /dev/null
@@ -0,0 +1,17 @@
+swap-teleporter-popup-link-create = Quantum link established!
+swap-teleporter-popup-link-fail-already = Quantum link failed! Link already present on device.
+swap-teleporter-popup-link-fail-already-other = Quantum link failed! Link already present on secondary device.
+swap-teleporter-popup-link-destroyed = Quantum link destroyed!
+swap-teleporter-popup-teleport-cancel-time = It's still recharging!
+swap-teleporter-popup-teleport-cancel-link = It's not linked with another device!
+swap-teleporter-popup-teleport-other = {CAPITALIZE(THE($entity))} activates, and you find yourself somewhere else.
+
+swap-teleporter-verb-destroy-link = Destroy Quantum Link
+
+swap-teleporter-examine-link-present = [color=forestgreen]It is linked to another device.[/color] Alt-Click to break the quantum link.
+swap-teleporter-examine-link-absent = [color=yellow]It is not currently linked.[/color] Use on another device to establish a quantum link.
+swap-teleporter-examine-time-remaining = Time left to recharge: [color=purple]{$second} second{$second ->
+    [one].
+    *[other]s.
+}[/color]
+
index 99d0c20c65dca9b841083f5702754aaf9e16ba8a..973ef360a9de9aabf6ff82f1c97848752288036b 100644 (file)
@@ -56,6 +56,7 @@ research-technology-anomaly-harnessing = Anomaly Core Harnessing
 research-technology-grappling = Grappling
 research-technology-abnormal-artifact-manipulation = Abnormal Artifact Manipulation
 research-technology-gravity-manipulation = Gravity Manipulation
+research-technology-quantum-leaping = Quantum Leaping
 research-technology-advanced-anomaly-research = Advanced Anomaly Research
 research-technology-rped = Rapid Part Exchange
 research-technology-super-parts = Super Parts
diff --git a/Resources/Prototypes/Entities/Objects/Devices/swapper.yml b/Resources/Prototypes/Entities/Objects/Devices/swapper.yml
new file mode 100644 (file)
index 0000000..8a743f4
--- /dev/null
@@ -0,0 +1,27 @@
+- type: entity
+  parent: BaseItem
+  id: DeviceQuantumSpinInverter
+  name: quantum spin inverter
+  description: An experimental device that is able to swap the locations of two entities by switching their particles' spin values. Must be linked to another device to function.
+  components:
+  - type: Sprite
+    sprite: Objects/Devices/swapper.rsi
+    layers:
+    - state: icon
+      map: ["base"]
+  - type: Item
+    size: Small
+  - type: Appearance
+  - type: SwapTeleporter
+    teleporterWhitelist:
+      tags:
+      - QuantumSpinInverter
+  - type: GenericVisualizer
+    visuals:
+      enum.SwapTeleporterVisuals.Linked:
+        base:
+          True: { state: linked }
+          False: { state: icon }
+  - type: Tag
+    tags:
+    - QuantumSpinInverter
index fe9dcd1d8777eb742dc2a132d617564e3c113bcf..0d9e3ad767cc868ed38532a77190cc9e6bcac04c 100644 (file)
       - FauxTileAstroGrass
       - FauxTileAstroIce
       - OreBagOfHolding
+      - DeviceQuantumSpinInverter
   - type: EmagLatheRecipes
     emagDynamicRecipes:
       - ExplosivePayload
index d541c3635362e57b40e5aa4f9d29c7a1fcbc4678..1a1f5326e39c621890df6a3c9fef9a892c2ba6c3 100644 (file)
     Glass: 400
     Silver: 200
 
+- type: latheRecipe
+  id: DeviceQuantumSpinInverter
+  result: DeviceQuantumSpinInverter
+  completetime: 5
+  materials:
+    Steel: 700
+    Glass: 100
+    Uranium: 100
+
 - type: latheRecipe
   id: WeaponProtoKineticAccelerator
   result: WeaponProtoKineticAccelerator
index c6289fa9c73ae890388087104775c9db52119cbb..bbb6ce568c2e0bcd54446f2c55cead59e94eef41 100644 (file)
   recipeUnlocks:
     - WeaponForceGun
     - WeaponTetherGun
+
+- type: technology
+  id: QuantumLeaping
+  name: research-technology-quantum-leaping
+  icon:
+    sprite: Objects/Devices/swapper.rsi
+    state: icon
+  discipline: Experimental
+  tier: 3
+  cost: 10000
+  recipeUnlocks:
+  - DeviceQuantumSpinInverter
index abc5cfa4836a0ce8f3bd70901fe9ac5fc221c6f3..d982cfbd5d493075250427e2ea17782a28a9acfc 100644 (file)
 - type: Tag
   id: ProximitySensor
 
+- type: Tag
+  id: QuantumSpinInverter
+
 - type: Tag
   id: Radio
 
diff --git a/Resources/Textures/Objects/Devices/swapper.rsi/icon.png b/Resources/Textures/Objects/Devices/swapper.rsi/icon.png
new file mode 100644 (file)
index 0000000..8aef40e
Binary files /dev/null and b/Resources/Textures/Objects/Devices/swapper.rsi/icon.png differ
diff --git a/Resources/Textures/Objects/Devices/swapper.rsi/inhand-left.png b/Resources/Textures/Objects/Devices/swapper.rsi/inhand-left.png
new file mode 100644 (file)
index 0000000..08b70fe
Binary files /dev/null and b/Resources/Textures/Objects/Devices/swapper.rsi/inhand-left.png differ
diff --git a/Resources/Textures/Objects/Devices/swapper.rsi/inhand-right.png b/Resources/Textures/Objects/Devices/swapper.rsi/inhand-right.png
new file mode 100644 (file)
index 0000000..39b94bf
Binary files /dev/null and b/Resources/Textures/Objects/Devices/swapper.rsi/inhand-right.png differ
diff --git a/Resources/Textures/Objects/Devices/swapper.rsi/linked.png b/Resources/Textures/Objects/Devices/swapper.rsi/linked.png
new file mode 100644 (file)
index 0000000..6c57487
Binary files /dev/null and b/Resources/Textures/Objects/Devices/swapper.rsi/linked.png differ
diff --git a/Resources/Textures/Objects/Devices/swapper.rsi/meta.json b/Resources/Textures/Objects/Devices/swapper.rsi/meta.json
new file mode 100644 (file)
index 0000000..40b7f3a
--- /dev/null
@@ -0,0 +1,36 @@
+{
+    "version": 1,
+    "license": "CC-BY-SA-3.0",
+    "copyright": "tgstation at https://github.com/tgstation/tgstation/commit/71a1fee2f13730adee5302d34bfa0f0262314d63",
+    "size": {
+        "x": 32,
+        "y": 32
+    },
+    "states": [
+        {
+            "name": "icon"
+        },
+        {
+            "name": "linked",
+            "delays": [
+                [
+                    0.1,
+                    0.1,
+                    0.1,
+                    0.1,
+                    0.1,
+                    0.1,
+                    0.1
+                ]
+            ]
+        },
+        {
+            "name": "inhand-left",
+            "directions": 4
+        },
+        {
+            "name": "inhand-right",
+            "directions": 4
+        }
+    ]
+}