Portal sprite & behavior tweaks (#19179)
* Portal repalette * Portal traversal verb + anchoring * map restriction * optional max distance checks if we decide to have them later * lower pointlight energy slightly hopefully makes it less garish
This commit is contained in:
@@ -22,7 +22,6 @@ public sealed class HandTeleporterSystem : EntitySystem
|
|||||||
public override void Initialize()
|
public override void Initialize()
|
||||||
{
|
{
|
||||||
SubscribeLocalEvent<HandTeleporterComponent, UseInHandEvent>(OnUseInHand);
|
SubscribeLocalEvent<HandTeleporterComponent, UseInHandEvent>(OnUseInHand);
|
||||||
|
|
||||||
SubscribeLocalEvent<HandTeleporterComponent, TeleporterDoAfterEvent>(OnDoAfter);
|
SubscribeLocalEvent<HandTeleporterComponent, TeleporterDoAfterEvent>(OnDoAfter);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -1,4 +1,5 @@
|
|||||||
using Content.Server.Mind.Components;
|
using Content.Server.Ghost.Components;
|
||||||
|
using Content.Server.Mind.Components;
|
||||||
using Content.Shared.Administration.Logs;
|
using Content.Shared.Administration.Logs;
|
||||||
using Content.Shared.Database;
|
using Content.Shared.Database;
|
||||||
using Content.Shared.Teleportation.Systems;
|
using Content.Shared.Teleportation.Systems;
|
||||||
@@ -14,7 +15,7 @@ public sealed class PortalSystem : SharedPortalSystem
|
|||||||
protected override void LogTeleport(EntityUid portal, EntityUid subject, EntityCoordinates source,
|
protected override void LogTeleport(EntityUid portal, EntityUid subject, EntityCoordinates source,
|
||||||
EntityCoordinates target)
|
EntityCoordinates target)
|
||||||
{
|
{
|
||||||
if (HasComp<MindContainerComponent>(subject))
|
if (HasComp<MindContainerComponent>(subject) && !HasComp<GhostComponent>(subject))
|
||||||
_adminLogger.Add(LogType.Teleport, LogImpact.Low, $"{ToPrettyString(subject):player} teleported via {ToPrettyString(portal)} from {source} to {target}");
|
_adminLogger.Add(LogType.Teleport, LogImpact.Low, $"{ToPrettyString(subject):player} teleported via {ToPrettyString(portal)} from {source} to {target}");
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -38,7 +38,8 @@ public sealed class HandTeleporterComponent : Component
|
|||||||
/// <summary>
|
/// <summary>
|
||||||
/// Delay for creating the portals in seconds.
|
/// Delay for creating the portals in seconds.
|
||||||
/// </summary>
|
/// </summary>
|
||||||
[DataField("portalCreationDelay")] public float PortalCreationDelay = 2.5f;
|
[DataField("portalCreationDelay")]
|
||||||
|
public float PortalCreationDelay = 1.0f;
|
||||||
}
|
}
|
||||||
|
|
||||||
[Serializable, NetSerializable]
|
[Serializable, NetSerializable]
|
||||||
|
|||||||
@@ -28,4 +28,23 @@ public sealed class PortalComponent : Component
|
|||||||
/// </summary>
|
/// </summary>
|
||||||
[DataField("maxRandomRadius")]
|
[DataField("maxRandomRadius")]
|
||||||
public float MaxRandomRadius = 7.0f;
|
public float MaxRandomRadius = 7.0f;
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// If false, this portal will fail to teleport and fizzle out if attempting to send an entity to a different map
|
||||||
|
/// </summary>
|
||||||
|
/// <remarks>
|
||||||
|
/// Shouldn't be able to teleport people to centcomm or the eshuttle from the station
|
||||||
|
/// </remarks>
|
||||||
|
[DataField("canTeleportToOtherMaps")]
|
||||||
|
public bool CanTeleportToOtherMaps = false;
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Maximum distance that portals can teleport to, in all cases. Mostly this matters for linked portals.
|
||||||
|
/// Null means no restriction on distance.
|
||||||
|
/// </summary>
|
||||||
|
/// <remarks>
|
||||||
|
/// Obviously this should strictly be larger than <see cref="MaxRandomRadius"/> (or null)
|
||||||
|
/// </remarks>
|
||||||
|
[DataField("maxTeleportRadius")]
|
||||||
|
public float? MaxTeleportRadius;
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,15 +1,20 @@
|
|||||||
using System.Linq;
|
using System.Linq;
|
||||||
using Content.Shared.Directions;
|
using Content.Shared.Ghost;
|
||||||
|
using Content.Shared.Pinpointer;
|
||||||
|
using Content.Shared.Popups;
|
||||||
using Content.Shared.Projectiles;
|
using Content.Shared.Projectiles;
|
||||||
using Content.Shared.Pulling;
|
using Content.Shared.Pulling;
|
||||||
using Content.Shared.Pulling.Components;
|
using Content.Shared.Pulling.Components;
|
||||||
using Content.Shared.Teleportation.Components;
|
using Content.Shared.Teleportation.Components;
|
||||||
|
using Content.Shared.Verbs;
|
||||||
using Robust.Shared.GameStates;
|
using Robust.Shared.GameStates;
|
||||||
using Robust.Shared.Map;
|
using Robust.Shared.Map;
|
||||||
using Robust.Shared.Network;
|
using Robust.Shared.Network;
|
||||||
using Robust.Shared.Physics.Dynamics;
|
using Robust.Shared.Physics.Dynamics;
|
||||||
using Robust.Shared.Physics.Events;
|
using Robust.Shared.Physics.Events;
|
||||||
|
using Robust.Shared.Player;
|
||||||
using Robust.Shared.Random;
|
using Robust.Shared.Random;
|
||||||
|
using Robust.Shared.Utility;
|
||||||
|
|
||||||
namespace Content.Shared.Teleportation.Systems;
|
namespace Content.Shared.Teleportation.Systems;
|
||||||
|
|
||||||
@@ -24,6 +29,7 @@ public abstract class SharedPortalSystem : EntitySystem
|
|||||||
[Dependency] private readonly SharedAudioSystem _audio = default!;
|
[Dependency] private readonly SharedAudioSystem _audio = default!;
|
||||||
[Dependency] private readonly SharedTransformSystem _transform = default!;
|
[Dependency] private readonly SharedTransformSystem _transform = default!;
|
||||||
[Dependency] private readonly SharedPullingSystem _pulling = default!;
|
[Dependency] private readonly SharedPullingSystem _pulling = default!;
|
||||||
|
[Dependency] private readonly SharedPopupSystem _popup = default!;
|
||||||
|
|
||||||
private const string PortalFixture = "portalFixture";
|
private const string PortalFixture = "portalFixture";
|
||||||
private const string ProjectileFixture = "projectile";
|
private const string ProjectileFixture = "projectile";
|
||||||
@@ -35,11 +41,42 @@ public abstract class SharedPortalSystem : EntitySystem
|
|||||||
{
|
{
|
||||||
SubscribeLocalEvent<PortalComponent, StartCollideEvent>(OnCollide);
|
SubscribeLocalEvent<PortalComponent, StartCollideEvent>(OnCollide);
|
||||||
SubscribeLocalEvent<PortalComponent, EndCollideEvent>(OnEndCollide);
|
SubscribeLocalEvent<PortalComponent, EndCollideEvent>(OnEndCollide);
|
||||||
|
SubscribeLocalEvent<PortalComponent, GetVerbsEvent<AlternativeVerb>>(OnGetVerbs);
|
||||||
|
|
||||||
SubscribeLocalEvent<PortalTimeoutComponent, ComponentGetState>(OnGetState);
|
SubscribeLocalEvent<PortalTimeoutComponent, ComponentGetState>(OnGetState);
|
||||||
SubscribeLocalEvent<PortalTimeoutComponent, ComponentHandleState>(OnHandleState);
|
SubscribeLocalEvent<PortalTimeoutComponent, ComponentHandleState>(OnHandleState);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private void OnGetVerbs(EntityUid uid, PortalComponent component, GetVerbsEvent<AlternativeVerb> args)
|
||||||
|
{
|
||||||
|
// Traversal altverb for ghosts to use that bypasses normal functionality
|
||||||
|
if (!args.CanAccess || !HasComp<SharedGhostComponent>(args.User))
|
||||||
|
return;
|
||||||
|
|
||||||
|
// Don't use the verb with unlinked or with multi-output portals
|
||||||
|
// (this is only intended to be useful for ghosts to see where a linked portal leads)
|
||||||
|
var disabled = !TryComp<LinkedEntityComponent>(uid, out var link) || link.LinkedEntities.Count != 1;
|
||||||
|
|
||||||
|
args.Verbs.Add(new AlternativeVerb
|
||||||
|
{
|
||||||
|
Priority = 11,
|
||||||
|
Act = () =>
|
||||||
|
{
|
||||||
|
if (link == null || disabled)
|
||||||
|
return;
|
||||||
|
|
||||||
|
var ent = link.LinkedEntities.First();
|
||||||
|
TeleportEntity(uid, args.User, Transform(ent).Coordinates, ent, false);
|
||||||
|
},
|
||||||
|
Disabled = disabled,
|
||||||
|
Text = Loc.GetString("portal-component-ghost-traverse"),
|
||||||
|
Message = disabled
|
||||||
|
? Loc.GetString("portal-component-no-linked-entities")
|
||||||
|
: Loc.GetString("portal-component-can-ghost-traverse"),
|
||||||
|
Icon = new SpriteSpecifier.Texture(new("/Textures/Interface/VerbIcons/open.svg.192dpi.png"))
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
private void OnGetState(EntityUid uid, PortalTimeoutComponent component, ref ComponentGetState args)
|
private void OnGetState(EntityUid uid, PortalTimeoutComponent component, ref ComponentGetState args)
|
||||||
{
|
{
|
||||||
args.State = new PortalTimeoutComponentState(component.EnteredPortal);
|
args.State = new PortalTimeoutComponentState(component.EnteredPortal);
|
||||||
@@ -138,12 +175,38 @@ public abstract class SharedPortalSystem : EntitySystem
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
private void TeleportEntity(EntityUid portal, EntityUid subject, EntityCoordinates target, EntityUid? targetEntity=null,
|
private void TeleportEntity(EntityUid portal, EntityUid subject, EntityCoordinates target, EntityUid? targetEntity=null, bool playSound=true,
|
||||||
PortalComponent? portalComponent = null)
|
PortalComponent? portalComponent = null)
|
||||||
{
|
{
|
||||||
if (!Resolve(portal, ref portalComponent))
|
if (!Resolve(portal, ref portalComponent))
|
||||||
return;
|
return;
|
||||||
|
|
||||||
|
var ourCoords = Transform(portal).Coordinates;
|
||||||
|
var onSameMap = ourCoords.GetMapId(EntityManager) == target.GetMapId(EntityManager);
|
||||||
|
var distanceInvalid = portalComponent.MaxTeleportRadius != null
|
||||||
|
&& ourCoords.TryDistance(EntityManager, target, out var distance)
|
||||||
|
&& distance > portalComponent.MaxTeleportRadius;
|
||||||
|
|
||||||
|
if (!onSameMap && !portalComponent.CanTeleportToOtherMaps || distanceInvalid)
|
||||||
|
{
|
||||||
|
if (!_netMan.IsServer)
|
||||||
|
return;
|
||||||
|
|
||||||
|
// Early out if this is an invalid configuration
|
||||||
|
_popup.PopupCoordinates(Loc.GetString("portal-component-invalid-configuration-fizzle"),
|
||||||
|
ourCoords, Filter.Pvs(ourCoords, entityMan: EntityManager), true);
|
||||||
|
|
||||||
|
_popup.PopupCoordinates(Loc.GetString("portal-component-invalid-configuration-fizzle"),
|
||||||
|
target, Filter.Pvs(target, entityMan: EntityManager), true);
|
||||||
|
|
||||||
|
QueueDel(portal);
|
||||||
|
|
||||||
|
if (targetEntity != null)
|
||||||
|
QueueDel(targetEntity.Value);
|
||||||
|
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
var arrivalSound = CompOrNull<PortalComponent>(targetEntity)?.ArrivalSound ?? portalComponent.ArrivalSound;
|
var arrivalSound = CompOrNull<PortalComponent>(targetEntity)?.ArrivalSound ?? portalComponent.ArrivalSound;
|
||||||
var departureSound = portalComponent.DepartureSound;
|
var departureSound = portalComponent.DepartureSound;
|
||||||
|
|
||||||
@@ -159,6 +222,9 @@ public abstract class SharedPortalSystem : EntitySystem
|
|||||||
|
|
||||||
_transform.SetCoordinates(subject, target);
|
_transform.SetCoordinates(subject, target);
|
||||||
|
|
||||||
|
if (!playSound)
|
||||||
|
return;
|
||||||
|
|
||||||
_audio.PlayPredicted(departureSound, portal, subject);
|
_audio.PlayPredicted(departureSound, portal, subject);
|
||||||
_audio.PlayPredicted(arrivalSound, subject, subject);
|
_audio.PlayPredicted(arrivalSound, subject, subject);
|
||||||
}
|
}
|
||||||
|
|||||||
8
Resources/Locale/en-US/portal/portal.ftl
Normal file
8
Resources/Locale/en-US/portal/portal.ftl
Normal file
@@ -0,0 +1,8 @@
|
|||||||
|
### Portal verb text
|
||||||
|
|
||||||
|
portal-component-ghost-traverse = Traverse
|
||||||
|
|
||||||
|
portal-component-no-linked-entities = Can't ghost traverse a portal that doesn't have exactly 1 linked portal
|
||||||
|
portal-component-can-ghost-traverse = Teleport to the linked portal
|
||||||
|
|
||||||
|
portal-component-invalid-configuration-fizzle = The portal fizzles out!
|
||||||
@@ -4,6 +4,8 @@
|
|||||||
name: bluespace portal
|
name: bluespace portal
|
||||||
description: Transports you to a linked destination!
|
description: Transports you to a linked destination!
|
||||||
components:
|
components:
|
||||||
|
- type: Transform
|
||||||
|
anchored: True
|
||||||
- type: InteractionOutline
|
- type: InteractionOutline
|
||||||
- type: Clickable
|
- type: Clickable
|
||||||
- type: Physics
|
- type: Physics
|
||||||
@@ -34,8 +36,9 @@
|
|||||||
- type: PointLight
|
- type: PointLight
|
||||||
color: OrangeRed
|
color: OrangeRed
|
||||||
radius: 3
|
radius: 3
|
||||||
energy: 3
|
energy: 1
|
||||||
netsync: false
|
netsync: false
|
||||||
|
|
||||||
- type: entity
|
- type: entity
|
||||||
id: PortalBlue
|
id: PortalBlue
|
||||||
parent: BasePortal
|
parent: BasePortal
|
||||||
@@ -46,5 +49,5 @@
|
|||||||
- type: PointLight
|
- type: PointLight
|
||||||
color: SkyBlue
|
color: SkyBlue
|
||||||
radius: 3
|
radius: 3
|
||||||
energy: 3
|
energy: 1
|
||||||
netsync: false
|
netsync: false
|
||||||
|
|||||||
@@ -5,7 +5,7 @@
|
|||||||
"y": 32
|
"y": 32
|
||||||
},
|
},
|
||||||
"license": "CC-BY-SA-3.0",
|
"license": "CC-BY-SA-3.0",
|
||||||
"copyright": "Taken from tgStation at commit https://github.com/tgstation/tgstation/blob/4a367160a204db4c5b51c1f811a3b899f0bde3ea/icons/obj/stationobjs.dmi",
|
"copyright": "Taken from tgStation at commit https://github.com/tgstation/tgstation/blob/4a367160a204db4c5b51c1f811a3b899f0bde3ea/icons/obj/stationobjs.dmi and repaletted using old tg sprites by mirrorcult",
|
||||||
"states": [
|
"states": [
|
||||||
{
|
{
|
||||||
"name": "portal-blue",
|
"name": "portal-blue",
|
||||||
@@ -24,15 +24,6 @@
|
|||||||
0.1, 0.1
|
0.1, 0.1
|
||||||
]
|
]
|
||||||
]
|
]
|
||||||
},
|
|
||||||
{
|
|
||||||
"name": "portal",
|
|
||||||
"delays": [
|
|
||||||
[
|
|
||||||
0.1, 0.1, 0.1, 0.1, 0.1, 0.1, 0.1, 0.1, 0.1, 0.1, 0.1, 0.1,
|
|
||||||
0.1, 0.1
|
|
||||||
]
|
|
||||||
]
|
|
||||||
}
|
}
|
||||||
]
|
]
|
||||||
}
|
}
|
||||||
|
|||||||
Binary file not shown.
|
Before Width: | Height: | Size: 2.9 KiB After Width: | Height: | Size: 1.7 KiB |
Binary file not shown.
|
Before Width: | Height: | Size: 1.6 KiB After Width: | Height: | Size: 1.7 KiB |
Binary file not shown.
|
Before Width: | Height: | Size: 2.8 KiB |
Reference in New Issue
Block a user