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));
|
||||
}
|
||||
}
|
||||
}
|
||||
17
Resources/Locale/en-US/portal/swap-teleporter.ftl
Normal file
17
Resources/Locale/en-US/portal/swap-teleporter.ftl
Normal file
@@ -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]
|
||||
|
||||
@@ -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
|
||||
|
||||
27
Resources/Prototypes/Entities/Objects/Devices/swapper.yml
Normal file
27
Resources/Prototypes/Entities/Objects/Devices/swapper.yml
Normal file
@@ -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
|
||||
@@ -300,6 +300,7 @@
|
||||
- FauxTileAstroGrass
|
||||
- FauxTileAstroIce
|
||||
- OreBagOfHolding
|
||||
- DeviceQuantumSpinInverter
|
||||
- type: EmagLatheRecipes
|
||||
emagDynamicRecipes:
|
||||
- ExplosivePayload
|
||||
|
||||
@@ -188,6 +188,15 @@
|
||||
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
|
||||
|
||||
@@ -152,3 +152,15 @@
|
||||
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
|
||||
|
||||
@@ -931,6 +931,9 @@
|
||||
- type: Tag
|
||||
id: ProximitySensor
|
||||
|
||||
- type: Tag
|
||||
id: QuantumSpinInverter
|
||||
|
||||
- type: Tag
|
||||
id: Radio
|
||||
|
||||
|
||||
BIN
Resources/Textures/Objects/Devices/swapper.rsi/icon.png
Normal file
BIN
Resources/Textures/Objects/Devices/swapper.rsi/icon.png
Normal file
Binary file not shown.
|
After Width: | Height: | Size: 325 B |
BIN
Resources/Textures/Objects/Devices/swapper.rsi/inhand-left.png
Normal file
BIN
Resources/Textures/Objects/Devices/swapper.rsi/inhand-left.png
Normal file
Binary file not shown.
|
After Width: | Height: | Size: 363 B |
BIN
Resources/Textures/Objects/Devices/swapper.rsi/inhand-right.png
Normal file
BIN
Resources/Textures/Objects/Devices/swapper.rsi/inhand-right.png
Normal file
Binary file not shown.
|
After Width: | Height: | Size: 359 B |
BIN
Resources/Textures/Objects/Devices/swapper.rsi/linked.png
Normal file
BIN
Resources/Textures/Objects/Devices/swapper.rsi/linked.png
Normal file
Binary file not shown.
|
After Width: | Height: | Size: 555 B |
36
Resources/Textures/Objects/Devices/swapper.rsi/meta.json
Normal file
36
Resources/Textures/Objects/Devices/swapper.rsi/meta.json
Normal file
@@ -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
|
||||
}
|
||||
]
|
||||
}
|
||||
Reference in New Issue
Block a user