Add bluespace lockers (#12954)

* add bluespace lockers

* add command linkbluespacelocker

* add command clearbluespacelockerlinks

* fix unwelding method

* move bluespace locker functionality to own component

* add options to disable transporting certain things

* remove unused imports

* unlock target lockers when opening + minor optimization to unwelding
This commit is contained in:
Chief-Engineer
2022-12-19 21:47:37 -06:00
committed by GitHub
parent 9a7654791a
commit 067932712a
7 changed files with 247 additions and 3 deletions

View File

@@ -0,0 +1,33 @@
using Content.Server.Storage.Components;
using Content.Shared.Administration;
using Robust.Shared.Console;
namespace Content.Server.Administration.Commands;
[AdminCommand(AdminFlags.Admin)]
public sealed class ClearBluespaceLockerLinks : IConsoleCommand
{
public string Command => "clearbluespacelockerlinks";
public string Description => "Removes the bluespace links of the given uid. Does not remove links this uid is the target of.";
public string Help => "Usage: clearbluespacelockerlinks <storage uid>";
public void Execute(IConsoleShell shell, string argStr, string[] args)
{
if (args.Length != 1)
{
shell.WriteError(Loc.GetString("shell-wrong-arguments-number"));
return;
}
if (!EntityUid.TryParse(args[0], out var entityUid))
{
shell.WriteError(Loc.GetString("shell-entity-uid-must-be-number"));
return;
}
var entityManager = IoCManager.Resolve<IEntityManager>();
if (entityManager.TryGetComponent<EntityStorageComponent>(entityUid, out var originComponent))
entityManager.RemoveComponent(entityUid, originComponent);
}
}

View File

@@ -0,0 +1,62 @@
using Content.Server.Storage.Components;
using Content.Shared.Administration;
using Robust.Shared.Console;
namespace Content.Server.Administration.Commands;
[AdminCommand(AdminFlags.Admin)]
public sealed class LinkBluespaceLocker : IConsoleCommand
{
public string Command => "linkbluespacelocker";
public string Description => "Links an entity, the target, to another as a bluespace locker target.";
public string Help => "Usage: linkbluespacelocker <two-way link> <origin storage uid> <target storage uid>";
public void Execute(IConsoleShell shell, string argStr, string[] args)
{
if (args.Length != 3)
{
shell.WriteError(Loc.GetString("shell-wrong-arguments-number"));
return;
}
if (!Boolean.TryParse(args[0], out var bidirectional))
{
shell.WriteError(Loc.GetString("shell-invalid-bool"));
return;
}
if (!EntityUid.TryParse(args[1], out var originUid))
{
shell.WriteError(Loc.GetString("shell-entity-uid-must-be-number"));
return;
}
if (!EntityUid.TryParse(args[2], out var targetUid))
{
shell.WriteError(Loc.GetString("shell-entity-uid-must-be-number"));
return;
}
var entityManager = IoCManager.Resolve<IEntityManager>();
if (!entityManager.TryGetComponent<EntityStorageComponent>(originUid, out var originComponent))
{
shell.WriteError(Loc.GetString("shell-entity-with-uid-lacks-component", ("uid", originUid), ("componentName", nameof(EntityStorageComponent))));
return;
}
if (!entityManager.TryGetComponent<EntityStorageComponent>(targetUid, out var targetComponent))
{
shell.WriteError(Loc.GetString("shell-entity-with-uid-lacks-component", ("uid", targetUid), ("componentName", nameof(EntityStorageComponent))));
return;
}
entityManager.EnsureComponent<BluespaceLockerComponent>(originUid, out var originBluespaceComponent);
originBluespaceComponent.BluespaceLinks.Add(targetComponent);
if (bidirectional)
{
entityManager.EnsureComponent<BluespaceLockerComponent>(targetUid, out var targetBluespaceComponent);
targetBluespaceComponent.BluespaceLinks.Add(originComponent);
}
}
}

View File

@@ -109,12 +109,13 @@ namespace Content.Server.Lock
return true;
}
public void Unlock(EntityUid uid, EntityUid user, LockComponent? lockComp = null)
public void Unlock(EntityUid uid, EntityUid? user, LockComponent? lockComp = null)
{
if (!Resolve(uid, ref lockComp))
return;
lockComp.Owner.PopupMessage(user, Loc.GetString("lock-comp-do-unlock-success", ("entityName", EntityManager.GetComponent<MetaDataComponent>(lockComp.Owner).EntityName)));
if (user is {Valid: true})
lockComp.Owner.PopupMessage(user.Value, Loc.GetString("lock-comp-do-unlock-success", ("entityName", EntityManager.GetComponent<MetaDataComponent>(lockComp.Owner).EntityName)));
lockComp.Locked = false;
if (lockComp.UnlockSound != null)

View File

@@ -0,0 +1,30 @@
namespace Content.Server.Storage.Components;
[RegisterComponent]
public sealed class BluespaceLockerComponent : Component
{
/// <summary>
/// Determines if gas will be transported.
/// </summary>
[DataField("transportGas"), ViewVariables(VVAccess.ReadWrite)]
public bool TransportGas = true;
/// <summary>
/// Determines if entities will be transported.
/// </summary>
[DataField("transportEntities"), ViewVariables(VVAccess.ReadWrite)]
public bool TransportEntities = true;
/// <summary>
/// Determines if entities with a Mind component will be transported.
/// </summary>
[DataField("allowSentient"), ViewVariables(VVAccess.ReadWrite)]
public bool AllowSentient = true;
/// <summary>
/// If length > 0, when something is added to the storage, it will instead be teleported to a random storage
/// from the list and the other storage will be opened.
/// </summary>
[DataField("bluespaceLinks"), ViewVariables(VVAccess.ReadOnly)]
public HashSet<EntityStorageComponent> BluespaceLinks = new();
}

View File

@@ -114,6 +114,7 @@ public sealed class StorageOpenAttemptEvent : CancellableEntityEventArgs
Silent = silent;
}
}
public sealed class StorageBeforeOpenEvent : EventArgs { }
public sealed class StorageAfterOpenEvent : EventArgs { }
public sealed class StorageCloseAttemptEvent : CancellableEntityEventArgs { }
public sealed class StorageBeforeCloseEvent : EventArgs

View File

@@ -0,0 +1,116 @@
using System.Linq;
using Content.Server.Lock;
using Content.Server.Mind.Components;
using Content.Server.Storage.Components;
using Content.Server.Tools.Systems;
using Microsoft.Extensions.DependencyModel;
namespace Content.Server.Storage.EntitySystems;
public sealed class BluespaceLockerSystem : EntitySystem
{
[Dependency] private readonly EntityStorageSystem _entityStorage = default!;
[Dependency] private readonly WeldableSystem _weldableSystem = default!;
[Dependency] private readonly LockSystem _lockSystem = default!;
public override void Initialize()
{
base.Initialize();
SubscribeLocalEvent<BluespaceLockerComponent, StorageBeforeOpenEvent>(PreOpen);
SubscribeLocalEvent<BluespaceLockerComponent, StorageAfterCloseEvent>(PostClose);
}
private void PreOpen(EntityUid uid, BluespaceLockerComponent component, StorageBeforeOpenEvent args)
{
EntityStorageComponent? entityStorageComponent = null;
if (component.BluespaceLinks is not { Count: > 0 })
return;
if (!Resolve(uid, ref entityStorageComponent))
return;
// Select target
var targetContainerStorageComponent = component.BluespaceLinks.ToArray()[new Random().Next(0, component.BluespaceLinks.Count)];
BluespaceLockerComponent? targetContainerBluespaceComponent = null;
// Close target if it is open
if (targetContainerStorageComponent.Open)
_entityStorage.CloseStorage(targetContainerStorageComponent.Owner, targetContainerStorageComponent);
// Apply bluespace effects if target is not a bluespace locker, otherwise let it handle it
if (!Resolve(targetContainerStorageComponent.Owner, ref targetContainerBluespaceComponent, false) ||
targetContainerBluespaceComponent.BluespaceLinks is not { Count: > 0 })
{
// Move contained items
if (component.TransportEntities)
foreach (var entity in targetContainerStorageComponent.Contents.ContainedEntities.ToArray())
{
if (!component.AllowSentient && EntityManager.HasComponent<MindComponent>(entity))
continue;
entityStorageComponent.Contents.Insert(entity, EntityManager);
}
// Move contained air
if (component.TransportGas)
{
entityStorageComponent.Air.CopyFromMutable(targetContainerStorageComponent.Air);
targetContainerStorageComponent.Air.Clear();
}
}
}
private void PostClose(EntityUid uid, BluespaceLockerComponent component, StorageAfterCloseEvent args)
{
EntityStorageComponent? entityStorageComponent = null;
if (component.BluespaceLinks is not { Count: > 0 })
return;
if (!Resolve(uid, ref entityStorageComponent))
return;
// Select target
var targetContainerStorageComponent = component.BluespaceLinks.ToArray()[new Random().Next(0, component.BluespaceLinks.Count)];
// Move contained items
if (component.TransportEntities)
foreach (var entity in entityStorageComponent.Contents.ContainedEntities.ToArray())
{
if (!component.AllowSentient && EntityManager.HasComponent<MindComponent>(entity))
continue;
targetContainerStorageComponent.Contents.Insert(entity, EntityManager);
}
// Move contained air
if (component.TransportGas)
{
targetContainerStorageComponent.Air.CopyFromMutable(entityStorageComponent.Air);
entityStorageComponent.Air.Clear();
}
// Open and empty target
if (targetContainerStorageComponent.Open)
{
_entityStorage.EmptyContents(targetContainerStorageComponent.Owner, targetContainerStorageComponent);
_entityStorage.ReleaseGas(targetContainerStorageComponent.Owner, targetContainerStorageComponent);
}
else
{
if (targetContainerStorageComponent.IsWeldedShut)
{
// It gets bluespaced open...
_weldableSystem.ForceWeldedState(targetContainerStorageComponent.Owner, false);
if (targetContainerStorageComponent.IsWeldedShut)
targetContainerStorageComponent.IsWeldedShut = false;
}
LockComponent? lockComponent = null;
if (Resolve(targetContainerStorageComponent.Owner, ref lockComponent, false) && lockComponent.Locked)
_lockSystem.Unlock(lockComponent.Owner, lockComponent.Owner, lockComponent);
_entityStorage.OpenStorage(targetContainerStorageComponent.Owner, targetContainerStorageComponent);
}
}
}

View File

@@ -164,6 +164,7 @@ public sealed class EntityStorageSystem : EntitySystem
if (!Resolve(uid, ref component))
return;
RaiseLocalEvent(uid, new StorageBeforeOpenEvent());
component.Open = true;
EmptyContents(uid, component);
ModifyComponents(uid, component);
@@ -414,7 +415,7 @@ public sealed class EntityStorageSystem : EntitySystem
}
}
private void ReleaseGas(EntityUid uid, EntityStorageComponent component)
public void ReleaseGas(EntityUid uid, EntityStorageComponent component)
{
if (!component.Airtight)
return;