From 1f320eccd7909c8f1a1a4cc5a053e3529ecc3615 Mon Sep 17 00:00:00 2001 From: metalgearsloth <31366439+metalgearsloth@users.noreply.github.com> Date: Fri, 19 Jul 2019 18:09:33 +1000 Subject: [PATCH] Add basic teleportation and portals (#269) * Add basic teleportation and portals * Address PJB's feedback and minor cleanup --- Content.Client/EntryPoint.cs | 2 + .../Movement/HandTeleporterVisualizer2D.cs | 35 +++ .../Components/Movement/PortalVisualizer2D.cs | 55 ++++ Content.Server/EntryPoint.cs | 4 + .../Movement/ServerPortalComponent.cs | 212 ++++++++++++++ .../Movement/ServerTeleporterComponent.cs | 261 ++++++++++++++++++ .../Movement/TeleportableComponent.cs | 10 + .../GameObjects/EntitySystems/PortalSystem.cs | 25 ++ .../Components/Movement/SharedPortal.cs | 25 ++ .../Movement/SharedTeleporterComponent.cs | 48 ++++ Content.Shared/GameObjects/ContentNetIDs.cs | 1 + Resources/Audio/effects/teleport_arrival.ogg | Bin 0 -> 12904 bytes .../Audio/effects/teleport_departure.ogg | Bin 0 -> 12904 bytes Resources/Prototypes/Entities/Mobs.yml | 1 + Resources/Prototypes/Entities/Teleporters.yml | 62 +++++ .../Textures/Effects/portal.rsi/meta.json | 51 ++++ .../Effects/portal.rsi/portal-pending.png | Bin 0 -> 1689 bytes .../Effects/portal.rsi/portal-unconnected.png | Bin 0 -> 1700 bytes Resources/Textures/Objects/hand_tele.png | Bin 0 -> 242 bytes .../Objects/hand_tele.rsi/charging.png | Bin 0 -> 6348 bytes .../Textures/Objects/hand_tele.rsi/meta.json | 33 +++ .../Textures/Objects/hand_tele.rsi/ready.png | Bin 0 -> 4910 bytes 22 files changed, 825 insertions(+) create mode 100644 Content.Client/GameObjects/Components/Movement/HandTeleporterVisualizer2D.cs create mode 100644 Content.Client/GameObjects/Components/Movement/PortalVisualizer2D.cs create mode 100644 Content.Server/GameObjects/Components/Movement/ServerPortalComponent.cs create mode 100644 Content.Server/GameObjects/Components/Movement/ServerTeleporterComponent.cs create mode 100644 Content.Server/GameObjects/Components/Movement/TeleportableComponent.cs create mode 100644 Content.Server/GameObjects/EntitySystems/PortalSystem.cs create mode 100644 Content.Shared/GameObjects/Components/Movement/SharedPortal.cs create mode 100644 Content.Shared/GameObjects/Components/Movement/SharedTeleporterComponent.cs create mode 100644 Resources/Audio/effects/teleport_arrival.ogg create mode 100644 Resources/Audio/effects/teleport_departure.ogg create mode 100644 Resources/Prototypes/Entities/Teleporters.yml create mode 100644 Resources/Textures/Effects/portal.rsi/meta.json create mode 100644 Resources/Textures/Effects/portal.rsi/portal-pending.png create mode 100644 Resources/Textures/Effects/portal.rsi/portal-unconnected.png create mode 100644 Resources/Textures/Objects/hand_tele.png create mode 100644 Resources/Textures/Objects/hand_tele.rsi/charging.png create mode 100644 Resources/Textures/Objects/hand_tele.rsi/meta.json create mode 100644 Resources/Textures/Objects/hand_tele.rsi/ready.png diff --git a/Content.Client/EntryPoint.cs b/Content.Client/EntryPoint.cs index a81c2b3f34..1b9894011e 100644 --- a/Content.Client/EntryPoint.cs +++ b/Content.Client/EntryPoint.cs @@ -26,6 +26,7 @@ using System; using Content.Client.Chat; using Content.Client.GameObjects.Components; using Content.Client.GameObjects.Components.Mobs; +using Content.Client.GameObjects.Components.Movement; using Content.Client.GameObjects.Components.Research; using Content.Client.GameObjects.Components.Sound; using Content.Client.Interfaces.Chat; @@ -33,6 +34,7 @@ using Content.Client.UserInterface; using Content.Shared.GameObjects.Components.Markers; using Content.Shared.GameObjects.Components.Materials; using Content.Shared.GameObjects.Components.Mobs; +using Content.Shared.GameObjects.Components.Movement; using Content.Shared.GameObjects.Components.Research; using Robust.Client.Interfaces.State; using Robust.Client.Interfaces.UserInterface; diff --git a/Content.Client/GameObjects/Components/Movement/HandTeleporterVisualizer2D.cs b/Content.Client/GameObjects/Components/Movement/HandTeleporterVisualizer2D.cs new file mode 100644 index 0000000000..06b67654ce --- /dev/null +++ b/Content.Client/GameObjects/Components/Movement/HandTeleporterVisualizer2D.cs @@ -0,0 +1,35 @@ +using System; +using Content.Shared.GameObjects.Components.Movement; +using JetBrains.Annotations; +using Robust.Client.GameObjects; +using Robust.Client.Interfaces.GameObjects.Components; + +namespace Content.Client.GameObjects.Components.Movement +{ + [UsedImplicitly] + public class HandTeleporterVisualizer2D : AppearanceVisualizer + { + + public override void OnChangeData(AppearanceComponent component) + { + var sprite = component.Owner.GetComponent(); + if (!component.TryGetData(TeleporterVisuals.VisualState, out TeleporterVisualState state)) + { + state = TeleporterVisualState.Ready; + } + + switch (state) + { + case TeleporterVisualState.Charging: + sprite.LayerSetState(0, "charging"); + break; + case TeleporterVisualState.Ready: + sprite.LayerSetState(0, "ready"); + break; + default: + throw new ArgumentOutOfRangeException(); + } + } + } + +} diff --git a/Content.Client/GameObjects/Components/Movement/PortalVisualizer2D.cs b/Content.Client/GameObjects/Components/Movement/PortalVisualizer2D.cs new file mode 100644 index 0000000000..a2eed9a340 --- /dev/null +++ b/Content.Client/GameObjects/Components/Movement/PortalVisualizer2D.cs @@ -0,0 +1,55 @@ +using Content.Shared.GameObjects.Components.Movement; +using JetBrains.Annotations; +using Robust.Client.GameObjects; +using Robust.Client.Interfaces.GameObjects.Components; +using Robust.Shared.Interfaces.GameObjects; + +namespace Content.Client.GameObjects.Components.Movement +{ + [UsedImplicitly] + public class PortalVisualizer2D : AppearanceVisualizer + { + public override void InitializeEntity(IEntity entity) + { + base.InitializeEntity(entity); + + var sprite = entity.GetComponent(); + + sprite.LayerMapSet(Layers.Portal, sprite.AddLayerState("portal-pending")); + sprite.LayerSetShader(Layers.Portal, "unshaded"); + + } + + public override void OnChangeData(AppearanceComponent component) + { + base.OnChangeData(component); + + var sprite = component.Owner.GetComponent(); + if (component.TryGetData(PortalVisuals.State, out var state)) + { + switch (state) + { + case PortalState.Pending: + sprite.LayerSetState(Layers.Portal, "portal-pending"); + break; + // TODO: Spritework here? + case PortalState.UnableToTeleport: + sprite.LayerSetState(Layers.Portal, "portal-unconnected"); + break; + case PortalState.RecentlyTeleported: + sprite.LayerSetState(Layers.Portal, "portal-unconnected"); + break; + } + } + else + { + sprite.LayerSetState(Layers.Portal, "portal-pending"); + } + } + + enum Layers + { + Portal + } + } +} diff --git a/Content.Server/EntryPoint.cs b/Content.Server/EntryPoint.cs index 12c99c3ebd..1fa2ed4e6f 100644 --- a/Content.Server/EntryPoint.cs +++ b/Content.Server/EntryPoint.cs @@ -58,6 +58,7 @@ using Robust.Shared.Interfaces.Log; using Content.Server.GameObjects.Components.Explosive; using Content.Server.GameObjects.Components.Items; using Content.Server.GameObjects.Components.Triggers; +using Content.Shared.GameObjects.Components.Movement; namespace Content.Server { @@ -178,6 +179,9 @@ namespace Content.Server factory.RegisterReference(); factory.Register(); + factory.Register(); + factory.Register(); + factory.Register(); factory.Register(); diff --git a/Content.Server/GameObjects/Components/Movement/ServerPortalComponent.cs b/Content.Server/GameObjects/Components/Movement/ServerPortalComponent.cs new file mode 100644 index 0000000000..70eb876a8e --- /dev/null +++ b/Content.Server/GameObjects/Components/Movement/ServerPortalComponent.cs @@ -0,0 +1,212 @@ +using System; +using System.Collections.Generic; +using Content.Shared.GameObjects.Components.Movement; +using Robust.Server.GameObjects; +using Robust.Server.GameObjects.EntitySystems; +using Robust.Server.Interfaces.GameObjects; +using Robust.Shared.Interfaces.GameObjects; +using Robust.Shared.IoC; +using Robust.Shared.Serialization; +using Robust.Shared.Timers; +using Robust.Shared.ViewVariables; + +namespace Content.Server.GameObjects.Components.Movement +{ + public class ServerPortalComponent : SharedPortalComponent + { +#pragma warning disable 649 + [Dependency] private readonly IServerEntityManager _serverEntityManager; +#pragma warning restore 649 + + // Potential improvements: Different sounds, + // Add Gateways + // More efficient form of GetEntitiesIntersecting, + // Put portal above most other things layer-wise + // Add telefragging (get entities on connecting portal and force brute damage) + + private AppearanceComponent _appearanceComponent; + private IEntity _connectingTeleporter; + private PortalState _state = PortalState.Pending; + [ViewVariables(VVAccess.ReadWrite)] private float _individualPortalCooldown; + [ViewVariables] private float _overallPortalCooldown; + [ViewVariables] private bool _onCooldown; + [ViewVariables] private string _departureSound; + [ViewVariables] private string _arrivalSound; + public List immuneEntities = new List(); // K + [ViewVariables(VVAccess.ReadWrite)] private float _aliveTime; + + public override void ExposeData(ObjectSerializer serializer) + { + base.ExposeData(serializer); + // How long will the portal stay up: 0 is infinite + serializer.DataField(ref _aliveTime, "alive_time", 10.0f); + // How long before a specific person can go back into it + serializer.DataField(ref _individualPortalCooldown, "individual_cooldown", 2.1f); + // How long before anyone can go in it + serializer.DataField(ref _overallPortalCooldown, "overall_cooldown", 2.0f); + serializer.DataField(ref _departureSound, "departure_sound", "/Audio/effects/teleport_departure.ogg"); + serializer.DataField(ref _arrivalSound, "arrival_sound", "/Audio/effects/teleport_arrival.ogg"); + } + + public override void Initialize() + { + base.Initialize(); + _appearanceComponent = Owner.GetComponent(); + } + + public override void OnAdd() + { + // This will blow up an entity it's attached to + base.OnAdd(); + if (Owner.TryGetComponent(out var collide)) + { + collide.IsHardCollidable = false; + } + + _state = PortalState.Pending; + if (_aliveTime > 0) + { + Timer.Spawn(TimeSpan.FromSeconds(_aliveTime), () => Owner.Delete()); + } + } + + public override void OnRemove() + { + _appearanceComponent = null; + + base.OnRemove(); + } + + public bool CanBeConnected() + { + if (_connectingTeleporter == null) + { + return true; + } + return false; + } + + public void TryConnectPortal(IEntity otherPortal) + { + if (otherPortal.TryGetComponent(out var connectedPortal) && connectedPortal.CanBeConnected()) + { + _connectingTeleporter = otherPortal; + connectedPortal._connectingTeleporter = Owner; + TryChangeState(PortalState.Pending); + } + } + + public void TryChangeState(PortalState targetState) + { + if (Owner == null) + { + return; + } + + _state = targetState; + if (_appearanceComponent != null) + { + _appearanceComponent.SetData(PortalVisuals.State, _state); + } + } + + private void releaseCooldown(IEntity entity) + { + if (immuneEntities.Contains(entity)) + { + immuneEntities.Remove(entity); + } + + if (_connectingTeleporter != null && + _connectingTeleporter.TryGetComponent(out var otherPortal)) + { + otherPortal.immuneEntities.Remove(entity); + } + } + + public IEnumerable GetPortableEntities() + { + foreach (var entity in _serverEntityManager.GetEntitiesIntersecting(Owner)) + { + if (IsEntityPortable(entity)) + { + yield return entity; + } + } + } + + private bool IsEntityPortable(IEntity entity) + { + // TODO: Check if it's slotted etc. Otherwise the slot item itself gets ported. + if (!immuneEntities.Contains(entity) && entity.HasComponent()) + { + return true; + } + return false; + } + + // TODO: Fix portal updates for performance + public void OnUpdate() + { + if (_onCooldown == false) + { + foreach (var entity in GetPortableEntities()) + { + TryPortalEntity(entity); + break; + } + } + } + + public void StartCooldown() + { + if (_overallPortalCooldown > 0 && _onCooldown == false) + { + _onCooldown = true; + TryChangeState(PortalState.RecentlyTeleported); + if (_connectingTeleporter != null) + { + _connectingTeleporter.TryGetComponent(out var otherPortal); + if (otherPortal != null) + { + otherPortal.TryChangeState(PortalState.RecentlyTeleported); + Timer.Spawn(TimeSpan.FromSeconds(_overallPortalCooldown), () => + { + _onCooldown = false; + TryChangeState(PortalState.Pending); + otherPortal.TryChangeState(PortalState.Pending); + }); + } + } + } + else + { + // Just in case? + _onCooldown = false; + } + } + + public void TryPortalEntity(IEntity entity) + { + + if (!immuneEntities.Contains(entity)) + { + var position = _connectingTeleporter.Transform.GridPosition; + var soundPlayer = IoCManager.Resolve().GetEntitySystem(); + + // Departure + // Do we need to rate-limit sounds to stop ear BLAST? + soundPlayer.Play(_departureSound, entity.Transform.GridPosition); + entity.Transform.DetachParent(); + entity.Transform.GridPosition = position; + soundPlayer.Play(_arrivalSound, entity.Transform.GridPosition); + TryChangeState(PortalState.RecentlyTeleported); + // To stop spam teleporting. Could potentially look at adding a timer to flush this from the portal + immuneEntities.Add(entity); + _connectingTeleporter.GetComponent().immuneEntities.Add(entity); + Timer.Spawn(TimeSpan.FromSeconds(_individualPortalCooldown), () => releaseCooldown(entity)); + StartCooldown(); + } + } + } +} diff --git a/Content.Server/GameObjects/Components/Movement/ServerTeleporterComponent.cs b/Content.Server/GameObjects/Components/Movement/ServerTeleporterComponent.cs new file mode 100644 index 0000000000..78d97a2125 --- /dev/null +++ b/Content.Server/GameObjects/Components/Movement/ServerTeleporterComponent.cs @@ -0,0 +1,261 @@ +using System; +using System.Linq; +using Content.Server.GameObjects.EntitySystems; +using Content.Shared.GameObjects.Components.Movement; +using Robust.Server.GameObjects; +using Robust.Server.GameObjects.EntitySystems; +using Robust.Server.Interfaces.GameObjects; +using Robust.Shared.GameObjects; +using Robust.Shared.Interfaces.GameObjects; +using Robust.Shared.Interfaces.Map; +using Robust.Shared.IoC; +using Robust.Shared.Log; +using Robust.Shared.Map; +using Robust.Shared.Maths; +using Robust.Shared.Serialization; +using Robust.Shared.Timers; +using Robust.Shared.ViewVariables; + +namespace Content.Server.GameObjects.Components.Movement +{ + + public class ServerTeleporterComponent : Component, IAfterAttack + { +#pragma warning disable 649 + [Dependency] private readonly IMapManager _mapManager; + [Dependency] private readonly IServerEntityManager _serverEntityManager; +#pragma warning restore 649 + // TODO: Look at MapManager.Map for Beacons to get all entities on grid + public ItemTeleporterState State => _state; + + public override string Name => "ItemTeleporter"; + + [ViewVariables] private float _chargeTime; + [ViewVariables] private float _cooldown; + [ViewVariables] private int _range; + [ViewVariables] private ItemTeleporterState _state; + [ViewVariables] private TeleporterType _teleporterType; + [ViewVariables] private string _departureSound; + [ViewVariables] private string _arrivalSound; + [ViewVariables] private string _cooldownSound; + // If the direct OR random teleport will try to avoid hitting collidables + [ViewVariables] private bool _avoidCollidable; + [ViewVariables] private float _portalAliveTime; + + private AppearanceComponent _appearanceComponent; + + private Random _spreadRandom; + + public override void ExposeData(ObjectSerializer serializer) + { + base.ExposeData(serializer); + + serializer.DataField(ref _teleporterType, "teleporter_type", TeleporterType.Random); + serializer.DataField(ref _range, "range", 15); + serializer.DataField(ref _chargeTime, "charge_time", 0.2f); + serializer.DataField(ref _cooldown, "cooldown", 2.0f); + serializer.DataField(ref _avoidCollidable, "avoid_walls", true); + serializer.DataField(ref _departureSound, "departure_sound", "/Audio/effects/teleport_departure.ogg"); + serializer.DataField(ref _arrivalSound, "arrival_sound", "/Audio/effects/teleport_arrival.ogg"); + serializer.DataField(ref _cooldownSound, "cooldown_sound", null); + serializer.DataField(ref _portalAliveTime, "portal_alive_time", 5.0f); // TODO: Change this to 0 before PR? + } + + public override void OnRemove() + { + _appearanceComponent = null; + + base.OnRemove(); + } + + private void SetState(ItemTeleporterState newState) + { + if (newState == ItemTeleporterState.Cooldown) + { + _appearanceComponent.SetData(TeleporterVisuals.VisualState, TeleporterVisualState.Charging); + } + else + { + _appearanceComponent.SetData(TeleporterVisuals.VisualState, TeleporterVisualState.Ready); + } + _state = newState; + } + + void IAfterAttack.AfterAttack(AfterAttackEventArgs eventArgs) + { + if (_teleporterType == TeleporterType.Directed) + { + var userTarget = eventArgs.ClickLocation.ToWorld(_mapManager); + TryDirectedTeleport(eventArgs.User, userTarget); + } + + if (_teleporterType == TeleporterType.Random) + { + TryRandomTeleport(eventArgs.User); + } + } + + public void TryDirectedTeleport(IEntity user, GridCoordinates grid) + { + // Checks + if (user.Transform.GridPosition.Distance(_mapManager, grid) > _range) + { + return; + } + + if (_state == ItemTeleporterState.On) + { + return; + } + if (_avoidCollidable) + { + foreach (var entity in _serverEntityManager.GetEntitiesIntersecting(grid)) + { + // Added this component to avoid stacking portals and causing shenanigans + // TODO: Doesn't do a great job of stopping stacking portals for directed + if (entity.HasComponent() || entity.HasComponent()) + { + return; + } + } + } + // Start / Continue + if (_state == ItemTeleporterState.Off) + { + SetState(ItemTeleporterState.Charging); + // Play charging sound here if you want + } + + if (_state != ItemTeleporterState.Charging) + { + return; + } + + Timer.Spawn(TimeSpan.FromSeconds(_chargeTime), () => Teleport(user, new Vector2(grid.X, grid.Y))); + StartCooldown(); + } + + public void StartCooldown() + { + SetState(ItemTeleporterState.Cooldown); + Timer.Spawn(TimeSpan.FromSeconds(_chargeTime + _cooldown), () => SetState(ItemTeleporterState.Off)); + if (_cooldownSound != null) + { + var soundPlayer = IoCManager.Resolve().GetEntitySystem(); + soundPlayer.Play(_cooldownSound, Owner); + } + } + + public override void Initialize() + { + _appearanceComponent = Owner.GetComponent(); + _spreadRandom = new Random(Owner.Uid.GetHashCode() ^ DateTime.Now.GetHashCode()); + _state = ItemTeleporterState.Off; + base.Initialize(); + } + + private bool emptySpace(IEntity user, Vector2 target) + { + // TODO: Check the user's spot? Upside is no stacking TPs but downside is they can't unstuck themselves from walls. + foreach (var entity in _serverEntityManager.GetEntitiesIntersecting(user.Transform.MapID, target)) + { + if (entity.HasComponent() || entity.HasComponent()) + { + return false; + } + } + return true; + } + + private Vector2 randomEmptySpot(IEntity user, int range) + { + Vector2 targetVector = user.Transform.GridPosition.Position; + // Definitely a better way to do this + foreach (var i in Enumerable.Range(0, 5)) + { + var randomRange = _spreadRandom.Next(0, range); + var angle = Angle.FromDegrees(_spreadRandom.Next(0, 359)); + targetVector = user.Transform.GridPosition.Position + angle.ToVec() * randomRange; + if (emptySpace(user, targetVector)) + { + return targetVector; + } + if (i == 19) + { + return targetVector; + } + } + + return targetVector; + } + + public void TryRandomTeleport(IEntity user) + { + // Checks + if (_state == ItemTeleporterState.On) + { + return; + } + + Vector2 targetVector; + if (_avoidCollidable) + { + targetVector = randomEmptySpot(user, _range); + } + else + { + var randomRange = _spreadRandom.Next(0, _range); + var angle = Angle.FromDegrees(_spreadRandom.Next(0, 359)); + targetVector = user.Transform.GridPosition.Position + angle.ToVec() * randomRange; + } + // Start / Continue + if (_state == ItemTeleporterState.Off) + { + SetState(ItemTeleporterState.Charging); + } + + if (_state != ItemTeleporterState.Charging) + { + return; + } + + // Seemed easier to just start the cd timer at the same time + Timer.Spawn(TimeSpan.FromSeconds(_chargeTime), () => Teleport(user, targetVector)); + StartCooldown(); + } + + public void Teleport(IEntity user, Vector2 vector) + { + // Messy maybe? + GridCoordinates targetGrid = new GridCoordinates(vector, user.Transform.GridID); + var soundPlayer = IoCManager.Resolve().GetEntitySystem(); + + // If portals use those, otherwise just move em over + if (_portalAliveTime > 0.0f) + { + // Call Delete here as the teleporter should have control over portal longevity + // Departure portal + var departurePortal = _serverEntityManager.SpawnEntityAt("Portal", user.Transform.GridPosition); + departurePortal.TryGetComponent(out var departureComponent); + + // Arrival portal + var arrivalPortal = _serverEntityManager.SpawnEntityAt("Portal", targetGrid); + arrivalPortal.TryGetComponent(out var arrivalComponent); + + // Connect. TODO: If the OnUpdate in ServerPortalComponent is changed this may need to change as well. + arrivalComponent.TryConnectPortal(departurePortal); + } + else + { + // Departure + soundPlayer.Play(_departureSound, user.Transform.GridPosition); + + // Arrival + user.Transform.DetachParent(); + user.Transform.WorldPosition = vector; + soundPlayer.Play(_arrivalSound, user.Transform.GridPosition); + } + + } + } +} diff --git a/Content.Server/GameObjects/Components/Movement/TeleportableComponent.cs b/Content.Server/GameObjects/Components/Movement/TeleportableComponent.cs new file mode 100644 index 0000000000..f5f0701ca4 --- /dev/null +++ b/Content.Server/GameObjects/Components/Movement/TeleportableComponent.cs @@ -0,0 +1,10 @@ +using Robust.Shared.GameObjects; +using Robust.Shared.Log; + +namespace Content.Server.GameObjects.Components.Movement +{ + public class TeleportableComponent : Component + { + public override string Name => "Teleportable"; + } +} diff --git a/Content.Server/GameObjects/EntitySystems/PortalSystem.cs b/Content.Server/GameObjects/EntitySystems/PortalSystem.cs new file mode 100644 index 0000000000..77eaf9bdcd --- /dev/null +++ b/Content.Server/GameObjects/EntitySystems/PortalSystem.cs @@ -0,0 +1,25 @@ +using Content.Server.GameObjects.Components.Movement; +using JetBrains.Annotations; +using Robust.Shared.GameObjects; +using Robust.Shared.GameObjects.Systems; + +namespace Content.Server.GameObjects.EntitySystems +{ + [UsedImplicitly] + public class PortalSystem : EntitySystem + { + public override void Initialize() + { + EntityQuery = new TypeEntityQuery(typeof(ServerPortalComponent)); + } + + public override void Update(float frameTime) + { + foreach (var entity in RelevantEntities) + { + var comp = entity.GetComponent(); + comp.OnUpdate(); + } + } + } +} diff --git a/Content.Shared/GameObjects/Components/Movement/SharedPortal.cs b/Content.Shared/GameObjects/Components/Movement/SharedPortal.cs new file mode 100644 index 0000000000..8f09903953 --- /dev/null +++ b/Content.Shared/GameObjects/Components/Movement/SharedPortal.cs @@ -0,0 +1,25 @@ +using System; +using Robust.Shared.GameObjects; +using Robust.Shared.Serialization; + +namespace Content.Shared.GameObjects.Components.Movement +{ + public abstract class SharedPortalComponent : Component { + public override string Name => "Portal"; + } + + [Serializable, NetSerializable] + public enum PortalVisuals + { + State + } + + [Serializable, NetSerializable] + public enum PortalState + { + RecentlyTeleported, + Pending, + UnableToTeleport, + } + +} diff --git a/Content.Shared/GameObjects/Components/Movement/SharedTeleporterComponent.cs b/Content.Shared/GameObjects/Components/Movement/SharedTeleporterComponent.cs new file mode 100644 index 0000000000..de607bb84f --- /dev/null +++ b/Content.Shared/GameObjects/Components/Movement/SharedTeleporterComponent.cs @@ -0,0 +1,48 @@ +using System; +using Robust.Shared.GameObjects; +using Robust.Shared.Map; +using Robust.Shared.Serialization; + +namespace Content.Shared.GameObjects.Components.Movement +{ + public enum ItemTeleporterState + { + Off, + Charging, + On, + Cooldown, + } + + public enum TeleporterType + { + Directed, + Random, + Beacon, + } + + [NetSerializable] + [Serializable] + public enum TeleporterVisuals + { + VisualState, + } + + [NetSerializable] + [Serializable] + public enum TeleporterVisualState + { + Ready, + Charging, + } + + [Serializable, NetSerializable] + public class TeleportMessage : ComponentMessage + { + public readonly GridCoordinates Target; + + public TeleportMessage(GridCoordinates target) + { + Target = target; + } + } +} diff --git a/Content.Shared/GameObjects/ContentNetIDs.cs b/Content.Shared/GameObjects/ContentNetIDs.cs index fe8a7cae1e..a2b54f0dff 100644 --- a/Content.Shared/GameObjects/ContentNetIDs.cs +++ b/Content.Shared/GameObjects/ContentNetIDs.cs @@ -21,5 +21,6 @@ public const uint LATHE = 1016; public const uint LATHE_DATABASE = 1017; public const uint MATERIAL_STORAGE = 1018; + public const uint HAND_TELEPORTER = 1019; } } diff --git a/Resources/Audio/effects/teleport_arrival.ogg b/Resources/Audio/effects/teleport_arrival.ogg new file mode 100644 index 0000000000000000000000000000000000000000..650867782b7455435ea183596678c7ba27a81774 GIT binary patch literal 12904 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⩔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⩔8(;QOT=T(_23|q_WO6XDl=IjE1tDr-y7TEGG?s0b*Oq#z&ofRjgVA zDhd^hfEp15R3LE>!O(=QEpz?_Z#KEZ~U{r;HX4R7xFGu^?48 zs|Gfy*EebNj1~2eHm^^rY0`KG>eGWJz3+O~kFMG8LGDPXUSeKps0rlAWLjfC500&F z%298}0RZ_?6ZudmE%DXaBOEwE4O#rQ8E~7OrMJ@z3@9oanP^h ztg;N&MDaRSnvZE*NH^yMvSp&CE>r2Tblt zu=8xDWp7)~XSkI@xP%{Xl^?1*uTAar_E!2Umj%+cm z@`KIbo#r>K(MoMYx=O^=w22$6QD<8JCv5SGYoB(@uFlFFIhVPaWtw@Xn6pRulHcr< zP{wl2wO7`D8F=TCs7#1H$1tYWjauzHT$e^g@Q!%_>?A4;`;+DC#s`YMBRI~bc+{;~rtJB=E6I-5HUkBMr?vt@m!m*MB zMSuxYds)(3=dmsNRtEsJN6inM4Ic_I)+m=mc%fx1nF_?HMI}UVdP8piW5=shucv%B zY(%qY{7rmr^Squ{@bvQ5T03+m^VLwoWLPzj;ZFJ*D0deT;vDtsKRn!L^|EteO;Z8hZg4#vWnBwTPn;9vv%EzUA?c-kkUegQK=De5kxExx* z)EoVH#W*~3s)zX6g4w8+z9NYUHrEX*?`^h~NY|G=lOo*|3!ewhe58uZ(3a7=$3k?O zbHj{^W!NK;b3)vQ$KEK<5$Pi|es~OVQet4LAN4-CDoznr$l`FPFA_=js3j~am7Xp5 zV?W$!Gt6YCy7|T_d+)2jR5Aq6(L~4~0z9vLu`@=9dYWz?CCiK4)dMpnVhmH4|LqSK z-PIb!;VuH{+rY{-H=e$$fl?0KDCf`PsX&%Iv6k9Dd#zB%IQ`pwVaCBtk-FdS5j)6Q zS2}d!%*DEvV(W#h?I!9M^0?5utM=x*8(@NzZ#7TiLv3Qqffh77DGdlyAC zD7iFVfu>zvXklBB0jFl|HKU%S*%=sY0i^ znVd(mlgMF+?b7U=02RI~>r9rAUv@q8^89hPS;%T%$t7`DkBgM$n&+rY zHn0zm_X)m^_FR$&)lK2Z`p2q@Yi(oJ?7Q=qH}I;BrlrX0Cai=pKTA73p?cK4EhsBv zJYm@z87^fj))gxz(1XUAz#>;d=9QGH!1Z)89d1fvc(Ie3WVoWhi?Z{_A@u;PUWo*& zz7&{!2$Ki}CeXGk6XAC?EbL?lsPw6JNaO76){oVHN6kMOWYF8TRw=lom-We;QOH3v zfG~}8EuscD@_^>{U%+b>m>rgzU{8wKiYn7{L`jAc^RE3nI|js_wSK6ucdiU)?}swM mh+U>(euK?rxHzI9?0G2X*1ot~mknPI*tj9c=f=9ItUm$9OhrNf literal 0 HcmV?d00001 diff --git a/Resources/Textures/Effects/portal.rsi/portal-unconnected.png b/Resources/Textures/Effects/portal.rsi/portal-unconnected.png new file mode 100644 index 0000000000000000000000000000000000000000..8023e01bc423c3900386d740d90a8ca77a434e46 GIT binary patch literal 1700 zcmZvdYgAKL7RPT$3feIpXL0WK0OCnJrM(<4_;-hLJqfijV0v0O@BSQr(H#Y=S z(jtKgQ$<0LqI9q_+A_SvBx)HR3dkb{i#FvI0ATZ)WMQWw%p2x4N8d%TBhh zc^5}6TyxYWSm5in=Hsne2f}xbKGedPe@?aVS&hrgHrWwz_wJvR`E8>_;g+#JJ)Fn+ zC7#hdo@Pi%N;RlQ&EPHBWO@eNVM(1UXrO0B?sL1Q?a4HETTN9p-D0O=yIGwY7O&u+ z;czA&9fZsKO@UN`ai|v!bW0lU?>~>0lgV9eqZ!vAf_g$BDgfS}CLc_If>Rz@e2%G6 zLmHBUF6QzCykcBDv%JINlc_XMk>F2mYPO*1jgsD1{oZ=1kbFTpHf3cE2wz;YvAk!}l68fr4W#%!ncduD^o&OSesLeVAXK;BnDdHjIen?L=m6T} z-P~yZGX2Eida~O0P0rol;F=xr3{~rUB)rFclq)^%-tKDk)Q3G_h0gnj65IppmtLxM zh{+4$ud}~piiq3Gm409!l7x~iTvu(WrFHg#gHH6-jb`2EB=phk{iMh1bFWQ%ZVGQF zTw&l_XP**I{&4-Up;8))F$9G^o%$_J*}ML=eG|JlQ}>u!EgIvF8{_ef)Lf6Fr|V84 z&xmVm!}w{)fGxMSoafo-+wftI-1#vF;d^z8z$&O$)ld}Kx8tc1DADy_r4IN9mA#z$ zLUN@$4wQ9ql+2*>s~NDD(FUn()*;3K%C^}p%d?YTc#)qgFOEM(Cz*QyJ&xszfghQ| zuRDYK>1Zx)))JcstiAI|_Kx^H!*CVVo#^tWAAjB`N)#yT4nLJe<1EI`3lD_~DnDM- zp=|Nc>Cujx(TZy8NW$Hjk%VYu6@Az@5?#+AU=;(fiZK&}@XfBBE3Mz?Pld8oT9bT= zavX9^(=Qn=|Mp?Nw<)ILlf#_lEN?s%jV-7!fW^K3=Y-$d~6p$ixc~qrL@V zsm$qW2fgqpWw1-PpLkewE{#o_vCnn=s zuthtU)60>P#@dQX#ZQ7SDE=c!JsSb;Oi?8HkqYOHz@yfEA@Fm`lJ8Y*^k)n8k_17e zk$rtMTA3giF|ygx$m^$A2;j2Bv7)rm2;_VIA9seH?u7AHKIz%y$UcKAzvqJFKdi*> zEjXdaU3^-%8AdiBF=gxFD^1d+8m8OMuhf*1&Z=6;fu_1Th}@f+u=rb^L_N;kbk$|g zoK{)G*$cbITk?nD9p&HhfMSMut0ov5ML?Mr_e?p!S~O$FKK}Q`sLc-4h+{yywQWVp}^TybAOqT{CfJ0K&QTFWlb!o z#}#X;mBeYg(V39X6?*4QmXQ7O<(}T|f=ErnJ2qeRDc5sB7NeJV#I&O?292zw)Zdie zsDSuseHuKQmTS)ZwMuS#M9HHq%=~mO3m9+L;0IeI^sX@K!VGLZ7UP4E-l|B!cM!iz z!>qKV?h2q`3>QIO5a|IcC=cj-D=5Fy`%P(z1U~aKkxcd)rMw%G;08_ zBbk>E#5ueT6-R>Jf~#X~8SM^(OR-(R{0hW$Z>%lZ|1QKLgtdzlPS{xaZ=|a+X^&dS z*xw;i)>~^=b&~2fX6G4uUVDL+5{N%HB%_x8b#T0^urUSueSqy-cC%|k4+;JUWJ4{8 literal 0 HcmV?d00001 diff --git a/Resources/Textures/Objects/hand_tele.png b/Resources/Textures/Objects/hand_tele.png new file mode 100644 index 0000000000000000000000000000000000000000..68e8459c5a267002b10629e21dde62ab3d9cc5d1 GIT binary patch literal 242 zcmeAS@N?(olHy`uVBq!ia0vp^3LwnE3?yBabR7dymH|E?t_KbrV3pP6wu=mQq_8El=kuhzlaPug7lC^NIQwe<`pCN(uRi{qU&KtmWy zg8YIR9G=}s19H+mT^vI!dY@i6$km|0(~ubbY*t#<`~MrxYJAx6RB_i8_NtrbS$co2 zdRWM?H=(Aud2IsIzNb5_8RVyMbz988(8(zHGoUi4c|(XJ|Dz~Z+Z`($y=tD$ULZ0r of9L0xT?b8%tlz?$`7ho2u@cLVH)rhffL1biy85}Sb4q9e05NJ?761SM literal 0 HcmV?d00001 diff --git a/Resources/Textures/Objects/hand_tele.rsi/charging.png b/Resources/Textures/Objects/hand_tele.rsi/charging.png new file mode 100644 index 0000000000000000000000000000000000000000..64fefef232c13597e94298adaa04bf947a46c2df GIT binary patch literal 6348 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 literal 0 HcmV?d00001 diff --git a/Resources/Textures/Objects/hand_tele.rsi/meta.json b/Resources/Textures/Objects/hand_tele.rsi/meta.json new file mode 100644 index 0000000000..64e209f934 --- /dev/null +++ b/Resources/Textures/Objects/hand_tele.rsi/meta.json @@ -0,0 +1,33 @@ +{ + "version": 1, + "size": { + "x": 32, + "y": 32 + }, + "license": "CC-BY-SA-3.0", + "copyright": "https://github.com/discordia-space/CEV-Eris/raw/237d8f7894617007d75c71d5d9feb4354c78debd/icons/obj/device.dmi", + "states": [ + { + "name": "charging", + "directions": 1, + "delays": [ + [ + 1.0, + 1.0, + 1.0, + 1.0 + ] + ] + }, + { + "name": "ready", + "directions": 1, + "delays": [ + [ + 5.0, + 5.0 + ] + ] + } + ] +} \ No newline at end of file diff --git a/Resources/Textures/Objects/hand_tele.rsi/ready.png b/Resources/Textures/Objects/hand_tele.rsi/ready.png new file mode 100644 index 0000000000000000000000000000000000000000..b810596d4144db6712e06e9c40345bc32f0827aa GIT binary patch literal 4910 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 literal 0 HcmV?d00001