using Content.Shared.Hands.Components;
using Content.Shared.Interaction.Events;
using Content.Shared.Teleportation.Systems;
using Content.Shared.Teleportation.Components;
using Content.Shared.Verbs;
using Robust.Server.Audio;
using Robust.Server.GameObjects;
using Robust.Shared.GameObjects;
using Robust.Shared.Map;
using Robust.Shared.Map.Components;
using Robust.Shared.EntitySerialization;
using Robust.Shared.EntitySerialization.Systems;
namespace Content.Server.Teleportation;
///
/// This handles pocket dimensions and their portals.
///
public sealed class PocketDimensionSystem : EntitySystem
{
[Dependency] private readonly AudioSystem _audio = default!;
[Dependency] private readonly LinkedEntitySystem _link = default!;
[Dependency] private readonly MapLoaderSystem _mapLoader = default!;
[Dependency] private readonly IMapManager _mapMan = default!;
[Dependency] private readonly SharedTransformSystem _transform = default!;
private ISawmill _sawmill = default!;
///
public override void Initialize()
{
base.Initialize();
SubscribeLocalEvent(OnRemoved);
SubscribeLocalEvent>(OnGetVerbs);
_sawmill = Logger.GetSawmill("pocket_dimension");
}
private void OnRemoved(EntityUid uid, PocketDimensionComponent comp, ComponentRemove args)
{
if (!Deleted(comp.PocketDimensionMap))
QueueDel(comp.PocketDimensionMap.Value);
}
private void OnGetVerbs(EntityUid uid, PocketDimensionComponent comp, GetVerbsEvent args)
{
if (!args.CanAccess || !args.CanInteract || !HasComp(args.User))
return;
AlternativeVerb verb = new()
{
Text = Loc.GetString("pocket-dimension-verb-text"),
Act = () => HandleActivation(uid, comp, args.User)
};
args.Verbs.Add(verb);
}
///
/// Handles toggling the portal to the pocket dimension.
///
private void HandleActivation(EntityUid uid, PocketDimensionComponent comp, EntityUid user)
{
if (Deleted(comp.PocketDimensionMap))
{
if (!_mapLoader.TryLoadMap(comp.PocketDimensionPath, out var map, out var roots))
{
_sawmill.Error($"Failed to load pocket dimension map {comp.PocketDimensionPath}");
return;
}
comp.PocketDimensionMap = _mapMan.GetMapEntityId(map.Value.Comp.MapId);
// find the pocket dimension's first grid and put the portal there
bool foundGrid = false;
foreach (var root in roots)
{
if (!HasComp(root))
continue;
// spawn the permanent portal into the pocket dimension, now ready to be used
var pos = new EntityCoordinates(root, 0, 0);
comp.ExitPortal = Spawn(comp.ExitPortalPrototype, pos);
EnsureComp(comp.ExitPortal!.Value, out var portal);
// the TryUnlink cleanup when first trying to create portal will fail without this
EnsureComp(uid);
portal.CanTeleportToOtherMaps = true;
_sawmill.Info($"Created pocket dimension on grid {root} of map {map}");
// if someone closes your portal you can use the one inside to escape
_link.OneWayLink(comp.ExitPortal.Value, uid);
foundGrid = true;
break;
}
if (!foundGrid)
{
_sawmill.Error($"Pocket dimension {comp.PocketDimensionPath} had no grids!");
QueueDel(comp.PocketDimensionMap);
return;
}
}
var dimension = comp.ExitPortal!.Value;
if (comp.PortalEnabled)
{
// unlink us
_link.TryUnlink(dimension, uid);
comp.PortalEnabled = false;
_audio.PlayPvs(comp.ClosePortalSound, uid);
// if you are stuck inside the pocket dimension you can use the internal portal to escape
_link.OneWayLink(dimension, uid);
}
else
{
// cleanup
_link.TryUnlink(dimension, uid);
// link us to the pocket dimension
_link.TryLink(dimension, uid);
comp.PortalEnabled = true;
_audio.PlayPvs(comp.OpenPortalSound, uid);
}
}
}