Bluespace locker event prep (#13397)
This commit is contained in:
@@ -52,17 +52,17 @@ public sealed class LinkBluespaceLocker : IConsoleCommand
|
|||||||
}
|
}
|
||||||
|
|
||||||
entityManager.EnsureComponent<BluespaceLockerComponent>(originUid, out var originBluespaceComponent);
|
entityManager.EnsureComponent<BluespaceLockerComponent>(originUid, out var originBluespaceComponent);
|
||||||
originBluespaceComponent.BluespaceLinks.Add(targetComponent);
|
originBluespaceComponent.BluespaceLinks.Add(targetUid);
|
||||||
entityManager.EnsureComponent<BluespaceLockerComponent>(targetUid, out var targetBluespaceComponent);
|
entityManager.EnsureComponent<BluespaceLockerComponent>(targetUid, out var targetBluespaceComponent);
|
||||||
if (bidirectional)
|
if (bidirectional)
|
||||||
{
|
{
|
||||||
targetBluespaceComponent.BluespaceLinks.Add(originComponent);
|
targetBluespaceComponent.BluespaceLinks.Add(originUid);
|
||||||
}
|
}
|
||||||
else if (targetBluespaceComponent.BluespaceLinks.Count == 0)
|
else if (targetBluespaceComponent.BluespaceLinks.Count == 0)
|
||||||
{
|
{
|
||||||
targetBluespaceComponent.AllowSentient = false;
|
targetBluespaceComponent.BehaviorProperties.TransportSentient = false;
|
||||||
targetBluespaceComponent.TransportEntities = false;
|
targetBluespaceComponent.BehaviorProperties.TransportEntities = false;
|
||||||
targetBluespaceComponent.TransportGas = false;
|
targetBluespaceComponent.BehaviorProperties.TransportGas = false;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
51
Content.Server/StationEvents/Events/BluespaceLocker.cs
Normal file
51
Content.Server/StationEvents/Events/BluespaceLocker.cs
Normal file
@@ -0,0 +1,51 @@
|
|||||||
|
using System.Linq;
|
||||||
|
using Content.Server.Resist;
|
||||||
|
using Content.Server.Station.Components;
|
||||||
|
using Content.Server.Storage.Components;
|
||||||
|
using Content.Shared.Access.Components;
|
||||||
|
using Content.Shared.Coordinates;
|
||||||
|
using Robust.Shared.Random;
|
||||||
|
|
||||||
|
namespace Content.Server.StationEvents.Events;
|
||||||
|
|
||||||
|
public sealed class BluespaceLockerLink : StationEventSystem
|
||||||
|
{
|
||||||
|
[Dependency] private readonly IRobustRandom _robustRandom = default!;
|
||||||
|
|
||||||
|
public override string Prototype => "BluespaceLockerLink";
|
||||||
|
|
||||||
|
public override void Started()
|
||||||
|
{
|
||||||
|
base.Started();
|
||||||
|
|
||||||
|
var targets = EntityQuery<EntityStorageComponent, ResistLockerComponent>().ToList();
|
||||||
|
_robustRandom.Shuffle(targets);
|
||||||
|
|
||||||
|
foreach (var target in targets)
|
||||||
|
{
|
||||||
|
var potentialLink = target.Item1.Owner;
|
||||||
|
|
||||||
|
if (HasComp<AccessReaderComponent>(potentialLink) ||
|
||||||
|
HasComp<BluespaceLockerComponent>(potentialLink) ||
|
||||||
|
!HasComp<StationMemberComponent>(potentialLink.ToCoordinates().GetGridUid(EntityManager)))
|
||||||
|
continue;
|
||||||
|
|
||||||
|
using var compInitializeHandle = EntityManager.AddComponentUninitialized<BluespaceLockerComponent>(potentialLink);
|
||||||
|
var comp = compInitializeHandle.Comp;
|
||||||
|
|
||||||
|
comp.PickLinksFromSameMap = true;
|
||||||
|
comp.MinBluespaceLinks = 1;
|
||||||
|
comp.BluespaceEffectOnInit = true;
|
||||||
|
comp.BehaviorProperties.BluespaceEffectOnTeleportSource = true;
|
||||||
|
comp.AutoLinksBidirectional = true;
|
||||||
|
comp.AutoLinksUseProperties = true;
|
||||||
|
comp.AutoLinkProperties.BluespaceEffectOnTeleportSource = true;
|
||||||
|
|
||||||
|
compInitializeHandle.Dispose();
|
||||||
|
|
||||||
|
Sawmill.Info($"Converted {ToPrettyString(potentialLink)} to bluespace locker");
|
||||||
|
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -1,32 +1,16 @@
|
|||||||
namespace Content.Server.Storage.Components;
|
using System.Threading;
|
||||||
|
|
||||||
|
namespace Content.Server.Storage.Components;
|
||||||
|
|
||||||
[RegisterComponent]
|
[RegisterComponent]
|
||||||
public sealed class BluespaceLockerComponent : Component
|
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>
|
/// <summary>
|
||||||
/// If length > 0, when something is added to the storage, it will instead be teleported to a random storage
|
/// 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.
|
/// from the list and the other storage will be opened.
|
||||||
/// </summary>
|
/// </summary>
|
||||||
[DataField("bluespaceLinks"), ViewVariables(VVAccess.ReadOnly)]
|
[DataField("bluespaceLinks"), ViewVariables(VVAccess.ReadOnly)]
|
||||||
public HashSet<EntityStorageComponent> BluespaceLinks = new();
|
public HashSet<EntityUid> BluespaceLinks = new();
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// Each time the system attempts to get a link, it will link additional lockers to ensure the minimum amount
|
/// Each time the system attempts to get a link, it will link additional lockers to ensure the minimum amount
|
||||||
@@ -54,8 +38,126 @@ public sealed class BluespaceLockerComponent : Component
|
|||||||
public bool PickLinksFromStationGrids = true;
|
public bool PickLinksFromStationGrids = true;
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// Determines if links automatically added are bidirectional
|
/// Determines if links automatically added are restricted to having the same access
|
||||||
|
/// </summary>
|
||||||
|
[DataField("pickLinksFromSameAccess"), ViewVariables(VVAccess.ReadWrite)]
|
||||||
|
public bool PickLinksFromSameAccess = true;
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Determines if links automatically added are restricted to existing bluespace lockers
|
||||||
|
/// </summary>
|
||||||
|
[DataField("pickLinksFromBluespaceLockers"), ViewVariables(VVAccess.ReadWrite)]
|
||||||
|
public bool PickLinksFromBluespaceLockers;
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Determines if links automatically added are restricted to non-bluespace lockers
|
||||||
|
/// </summary>
|
||||||
|
[DataField("pickLinksFromNonBluespaceLockers"), ViewVariables(VVAccess.ReadWrite)]
|
||||||
|
public bool PickLinksFromNonBluespaceLockers = true;
|
||||||
|
|
||||||
|
public CancellationTokenSource? CancelToken;
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Determines if bluespace effect is show on component init
|
||||||
|
/// </summary>
|
||||||
|
[DataField("bluespaceEffectOnInit")]
|
||||||
|
public bool BluespaceEffectOnInit;
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Determines if links automatically added get the source locker set as a target
|
||||||
/// </summary>
|
/// </summary>
|
||||||
[DataField("autoLinksBidirectional"), ViewVariables(VVAccess.ReadWrite)]
|
[DataField("autoLinksBidirectional"), ViewVariables(VVAccess.ReadWrite)]
|
||||||
public bool AutoLinksBidirectional;
|
public bool AutoLinksBidirectional;
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Determines if links automatically use <see cref="AutoLinkProperties"/>
|
||||||
|
/// </summary>
|
||||||
|
[DataField("autoLinksUseProperties"), ViewVariables(VVAccess.ReadWrite)]
|
||||||
|
public bool AutoLinksUseProperties;
|
||||||
|
|
||||||
|
public int UsesSinceLinkClear;
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Determines properties of automatically created links
|
||||||
|
/// </summary>
|
||||||
|
[DataField("autoLinkProperties"), ViewVariables(VVAccess.ReadOnly)]
|
||||||
|
public BluespaceLockerBehaviorProperties AutoLinkProperties = new();
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Determines properties of this locker
|
||||||
|
/// </summary>
|
||||||
|
[DataField("behaviorProperties"), ViewVariables(VVAccess.ReadOnly)]
|
||||||
|
public BluespaceLockerBehaviorProperties BehaviorProperties = new();
|
||||||
|
}
|
||||||
|
|
||||||
|
[DataDefinition]
|
||||||
|
public record BluespaceLockerBehaviorProperties
|
||||||
|
{
|
||||||
|
/// <summary>
|
||||||
|
/// Determines if gas will be transported.
|
||||||
|
/// </summary>
|
||||||
|
[DataField("transportGas"), ViewVariables(VVAccess.ReadWrite)]
|
||||||
|
public bool TransportGas { get; set; } = true;
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Determines if entities will be transported.
|
||||||
|
/// </summary>
|
||||||
|
[DataField("transportEntities"), ViewVariables(VVAccess.ReadWrite)]
|
||||||
|
public bool TransportEntities { get; set; } = true;
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Determines if entities with a Mind component will be transported.
|
||||||
|
/// </summary>
|
||||||
|
[DataField("transportSentient"), ViewVariables(VVAccess.ReadWrite)]
|
||||||
|
public bool TransportSentient { get; set; } = true;
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Delay to wait after closing before transporting
|
||||||
|
/// </summary>
|
||||||
|
[DataField("delay"), ViewVariables(VVAccess.ReadWrite)]
|
||||||
|
public float Delay { get; set; }
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Defines prototype to spawn for bluespace effect
|
||||||
|
/// </summary>
|
||||||
|
[DataField("bluespaceEffectPrototype"), ViewVariables(VVAccess.ReadWrite)]
|
||||||
|
public string BluespaceEffectPrototype { get; set; } = "EffectFlashBluespace";
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Determines if bluespace effect is show on teleport at the source
|
||||||
|
/// </summary>
|
||||||
|
[DataField("bluespaceEffectOnTeleportSource"), ViewVariables(VVAccess.ReadWrite)]
|
||||||
|
public bool BluespaceEffectOnTeleportSource { get; set; }
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Determines if bluespace effect is show on teleport at the target
|
||||||
|
/// </summary>
|
||||||
|
[DataField("bluespaceEffectOnTeleportTarget"), ViewVariables(VVAccess.ReadWrite)]
|
||||||
|
public bool BluespaceEffectOnTeleportTarget { get; set; }
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Uses left before the locker is destroyed. -1 indicates infinite
|
||||||
|
/// </summary>
|
||||||
|
[DataField("destroyAfterUses"), ViewVariables(VVAccess.ReadWrite)]
|
||||||
|
public int DestroyAfterUses { get; set; } = -1;
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// How to destroy the locker after it runs out of uses
|
||||||
|
/// </summary>
|
||||||
|
[DataField("destroyType"), ViewVariables(VVAccess.ReadWrite)]
|
||||||
|
public BluespaceLockerDestroyType DestroyType { get; set; } = BluespaceLockerDestroyType.Delete;
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Uses left before the lockers links are cleared. -1 indicates infinite
|
||||||
|
/// </summary>
|
||||||
|
[DataField("clearLinksEvery"), ViewVariables(VVAccess.ReadWrite)]
|
||||||
|
public int ClearLinksEvery { get; set; } = -1;
|
||||||
|
}
|
||||||
|
|
||||||
|
[Flags]
|
||||||
|
public enum BluespaceLockerDestroyType
|
||||||
|
{
|
||||||
|
Delete,
|
||||||
|
DeleteComponent,
|
||||||
|
Explode,
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,10 +1,14 @@
|
|||||||
using System.Linq;
|
using System.Linq;
|
||||||
|
using System.Threading;
|
||||||
|
using Content.Server.DoAfter;
|
||||||
|
using Content.Server.Explosion.EntitySystems;
|
||||||
using Content.Server.Lock;
|
using Content.Server.Lock;
|
||||||
using Content.Server.Mind.Components;
|
using Content.Server.Mind.Components;
|
||||||
using Content.Server.Resist;
|
using Content.Server.Resist;
|
||||||
using Content.Server.Station.Components;
|
using Content.Server.Station.Components;
|
||||||
using Content.Server.Storage.Components;
|
using Content.Server.Storage.Components;
|
||||||
using Content.Server.Tools.Systems;
|
using Content.Server.Tools.Systems;
|
||||||
|
using Content.Shared.Access.Components;
|
||||||
using Content.Shared.Coordinates;
|
using Content.Shared.Coordinates;
|
||||||
using Robust.Shared.Random;
|
using Robust.Shared.Random;
|
||||||
|
|
||||||
@@ -17,6 +21,8 @@ public sealed class BluespaceLockerSystem : EntitySystem
|
|||||||
[Dependency] private readonly EntityStorageSystem _entityStorage = default!;
|
[Dependency] private readonly EntityStorageSystem _entityStorage = default!;
|
||||||
[Dependency] private readonly WeldableSystem _weldableSystem = default!;
|
[Dependency] private readonly WeldableSystem _weldableSystem = default!;
|
||||||
[Dependency] private readonly LockSystem _lockSystem = default!;
|
[Dependency] private readonly LockSystem _lockSystem = default!;
|
||||||
|
[Dependency] private readonly DoAfterSystem _doAfterSystem = default!;
|
||||||
|
[Dependency] private readonly ExplosionSystem _explosionSystem = default!;
|
||||||
|
|
||||||
public override void Initialize()
|
public override void Initialize()
|
||||||
{
|
{
|
||||||
@@ -25,11 +31,20 @@ public sealed class BluespaceLockerSystem : EntitySystem
|
|||||||
SubscribeLocalEvent<BluespaceLockerComponent, ComponentStartup>(OnStartup);
|
SubscribeLocalEvent<BluespaceLockerComponent, ComponentStartup>(OnStartup);
|
||||||
SubscribeLocalEvent<BluespaceLockerComponent, StorageBeforeOpenEvent>(PreOpen);
|
SubscribeLocalEvent<BluespaceLockerComponent, StorageBeforeOpenEvent>(PreOpen);
|
||||||
SubscribeLocalEvent<BluespaceLockerComponent, StorageAfterCloseEvent>(PostClose);
|
SubscribeLocalEvent<BluespaceLockerComponent, StorageAfterCloseEvent>(PostClose);
|
||||||
|
SubscribeLocalEvent<BluespaceLockerComponent, BluespaceLockerTeleportDelayComplete>(OnBluespaceLockerTeleportDelayComplete);
|
||||||
}
|
}
|
||||||
|
|
||||||
private void OnStartup(EntityUid uid, BluespaceLockerComponent component, ComponentStartup args)
|
private void OnStartup(EntityUid uid, BluespaceLockerComponent component, ComponentStartup args)
|
||||||
{
|
{
|
||||||
GetTargetStorage(component);
|
GetTarget(uid, component);
|
||||||
|
|
||||||
|
if (component.BluespaceEffectOnInit)
|
||||||
|
BluespaceEffect(uid, component);
|
||||||
|
}
|
||||||
|
|
||||||
|
private void BluespaceEffect(EntityUid uid, BluespaceLockerComponent component)
|
||||||
|
{
|
||||||
|
Spawn(component.BehaviorProperties.BluespaceEffectPrototype, uid.ToCoordinates());
|
||||||
}
|
}
|
||||||
|
|
||||||
private void PreOpen(EntityUid uid, BluespaceLockerComponent component, StorageBeforeOpenEvent args)
|
private void PreOpen(EntityUid uid, BluespaceLockerComponent component, StorageBeforeOpenEvent args)
|
||||||
@@ -39,63 +54,110 @@ public sealed class BluespaceLockerSystem : EntitySystem
|
|||||||
if (!Resolve(uid, ref entityStorageComponent))
|
if (!Resolve(uid, ref entityStorageComponent))
|
||||||
return;
|
return;
|
||||||
|
|
||||||
|
component.CancelToken?.Cancel();
|
||||||
|
|
||||||
// Select target
|
// Select target
|
||||||
var targetContainerStorageComponent = GetTargetStorage(component);
|
var target = GetTarget(uid, component);
|
||||||
if (targetContainerStorageComponent == null)
|
if (target == null)
|
||||||
return;
|
return;
|
||||||
BluespaceLockerComponent? targetContainerBluespaceComponent = null;
|
|
||||||
|
|
||||||
// Close target if it is open
|
// Close target if it is open
|
||||||
if (targetContainerStorageComponent.Open)
|
if (target.Value.storageComponent.Open)
|
||||||
_entityStorage.CloseStorage(targetContainerStorageComponent.Owner, targetContainerStorageComponent);
|
_entityStorage.CloseStorage(target.Value.uid, target.Value.storageComponent);
|
||||||
|
|
||||||
// Apply bluespace effects if target is not a bluespace locker, otherwise let it handle it
|
// Apply bluespace effects if target is not a bluespace locker, otherwise let it handle it
|
||||||
if (!Resolve(targetContainerStorageComponent.Owner, ref targetContainerBluespaceComponent, false))
|
if (target.Value.bluespaceLockerComponent == null)
|
||||||
{
|
{
|
||||||
// Move contained items
|
// Move contained items
|
||||||
if (component.TransportEntities)
|
if (component.BehaviorProperties.TransportEntities || component.BehaviorProperties.TransportSentient)
|
||||||
foreach (var entity in targetContainerStorageComponent.Contents.ContainedEntities.ToArray())
|
foreach (var entity in target.Value.storageComponent.Contents.ContainedEntities.ToArray())
|
||||||
{
|
{
|
||||||
if (!component.AllowSentient && EntityManager.HasComponent<MindComponent>(entity))
|
if (EntityManager.HasComponent<MindComponent>(entity))
|
||||||
continue;
|
{
|
||||||
entityStorageComponent.Contents.Insert(entity, EntityManager);
|
if (component.BehaviorProperties.TransportSentient)
|
||||||
|
entityStorageComponent.Contents.Insert(entity, EntityManager);
|
||||||
|
}
|
||||||
|
else if (component.BehaviorProperties.TransportEntities)
|
||||||
|
entityStorageComponent.Contents.Insert(entity, EntityManager);
|
||||||
}
|
}
|
||||||
|
|
||||||
// Move contained air
|
// Move contained air
|
||||||
if (component.TransportGas)
|
if (component.BehaviorProperties.TransportGas)
|
||||||
{
|
{
|
||||||
entityStorageComponent.Air.CopyFromMutable(targetContainerStorageComponent.Air);
|
entityStorageComponent.Air.CopyFromMutable(target.Value.storageComponent.Air);
|
||||||
targetContainerStorageComponent.Air.Clear();
|
target.Value.storageComponent.Air.Clear();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Bluespace effects
|
||||||
|
if (component.BehaviorProperties.BluespaceEffectOnTeleportSource)
|
||||||
|
BluespaceEffect(target.Value.uid, component);
|
||||||
|
if (component.BehaviorProperties.BluespaceEffectOnTeleportTarget)
|
||||||
|
BluespaceEffect(uid, component);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
DestroyAfterLimit(uid, component);
|
||||||
}
|
}
|
||||||
|
|
||||||
private bool ValidLink(BluespaceLockerComponent component, EntityStorageComponent link)
|
private bool ValidLink(EntityUid locker, EntityUid link, BluespaceLockerComponent lockerComponent)
|
||||||
{
|
{
|
||||||
return link.Owner.Valid && link.LifeStage != ComponentLifeStage.Deleted;
|
return link.Valid && TryComp<EntityStorageComponent>(link, out var linkStorage) && linkStorage.LifeStage != ComponentLifeStage.Deleted && link != locker;
|
||||||
}
|
}
|
||||||
|
|
||||||
private bool ValidAutolink(BluespaceLockerComponent component, EntityStorageComponent link)
|
/// <returns>True if any HashSet in <paramref name="a"/> would grant access to <paramref name="b"/></returns>
|
||||||
|
private bool AccessMatch(IReadOnlyCollection<HashSet<string>>? a, IReadOnlyCollection<HashSet<string>>? b)
|
||||||
{
|
{
|
||||||
if (!ValidLink(component, link))
|
if ((a == null || a.Count == 0) && (b == null || b.Count == 0))
|
||||||
|
return true;
|
||||||
|
if (a != null && a.Any(aSet => aSet.Count == 0))
|
||||||
|
return true;
|
||||||
|
if (b != null && b.Any(bSet => bSet.Count == 0))
|
||||||
|
return true;
|
||||||
|
|
||||||
|
if (a != null && b != null)
|
||||||
|
return a.Any(aSet => b.Any(aSet.SetEquals));
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
private bool ValidAutolink(EntityUid locker, EntityUid link, BluespaceLockerComponent lockerComponent)
|
||||||
|
{
|
||||||
|
if (!ValidLink(locker, link, lockerComponent))
|
||||||
return false;
|
return false;
|
||||||
|
|
||||||
if (component.PickLinksFromSameMap &&
|
if (lockerComponent.PickLinksFromSameMap &&
|
||||||
link.Owner.ToCoordinates().GetMapId(_entityManager) == component.Owner.ToCoordinates().GetMapId(_entityManager))
|
link.ToCoordinates().GetMapId(_entityManager) != locker.ToCoordinates().GetMapId(_entityManager))
|
||||||
return false;
|
return false;
|
||||||
|
|
||||||
if (component.PickLinksFromStationGrids &&
|
if (lockerComponent.PickLinksFromStationGrids &&
|
||||||
!_entityManager.HasComponent<StationMemberComponent>(link.Owner.ToCoordinates().GetGridUid(_entityManager)))
|
!HasComp<StationMemberComponent>(link.ToCoordinates().GetGridUid(_entityManager)))
|
||||||
return false;
|
return false;
|
||||||
|
|
||||||
if (component.PickLinksFromResistLockers &&
|
if (lockerComponent.PickLinksFromResistLockers &&
|
||||||
!_entityManager.HasComponent<ResistLockerComponent>(link.Owner))
|
!HasComp<ResistLockerComponent>(link))
|
||||||
return false;
|
return false;
|
||||||
|
|
||||||
|
if (lockerComponent.PickLinksFromSameAccess)
|
||||||
|
{
|
||||||
|
TryComp<AccessReaderComponent>(locker, out var sourceAccess);
|
||||||
|
TryComp<AccessReaderComponent>(link, out var targetAccess);
|
||||||
|
if (!AccessMatch(sourceAccess?.AccessLists, targetAccess?.AccessLists))
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (HasComp<BluespaceLockerComponent>(link))
|
||||||
|
{
|
||||||
|
if (lockerComponent.PickLinksFromNonBluespaceLockers)
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
if (lockerComponent.PickLinksFromBluespaceLockers)
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
private EntityStorageComponent? GetTargetStorage(BluespaceLockerComponent component)
|
private (EntityUid uid, EntityStorageComponent storageComponent, BluespaceLockerComponent? bluespaceLockerComponent)? GetTarget(EntityUid lockerUid, BluespaceLockerComponent component)
|
||||||
{
|
{
|
||||||
while (true)
|
while (true)
|
||||||
{
|
{
|
||||||
@@ -103,20 +165,27 @@ public sealed class BluespaceLockerSystem : EntitySystem
|
|||||||
if (component.BluespaceLinks.Count < component.MinBluespaceLinks)
|
if (component.BluespaceLinks.Count < component.MinBluespaceLinks)
|
||||||
{
|
{
|
||||||
// Get an shuffle the list of all EntityStorages
|
// Get an shuffle the list of all EntityStorages
|
||||||
var storages = _entityManager.EntityQuery<EntityStorageComponent>().ToArray();
|
var storages = EntityQuery<EntityStorageComponent>().ToArray();
|
||||||
_robustRandom.Shuffle(storages);
|
_robustRandom.Shuffle(storages);
|
||||||
|
|
||||||
// Add valid candidates till MinBluespaceLinks is met
|
// Add valid candidates till MinBluespaceLinks is met
|
||||||
foreach (var storage in storages)
|
foreach (var storage in storages)
|
||||||
{
|
{
|
||||||
if (!ValidAutolink(component, storage))
|
var potentialLink = storage.Owner;
|
||||||
|
|
||||||
|
if (!ValidAutolink(lockerUid, potentialLink, component))
|
||||||
continue;
|
continue;
|
||||||
|
|
||||||
component.BluespaceLinks.Add(storage);
|
component.BluespaceLinks.Add(potentialLink);
|
||||||
if (component.AutoLinksBidirectional)
|
if (component.AutoLinksBidirectional || component.AutoLinksUseProperties)
|
||||||
{
|
{
|
||||||
_entityManager.EnsureComponent<BluespaceLockerComponent>(storage.Owner, out var targetBluespaceComponent);
|
var targetBluespaceComponent = EnsureComp<BluespaceLockerComponent>(potentialLink);
|
||||||
targetBluespaceComponent.BluespaceLinks.Add(_entityManager.GetComponent<EntityStorageComponent>(component.Owner));
|
|
||||||
|
if (component.AutoLinksBidirectional)
|
||||||
|
targetBluespaceComponent.BluespaceLinks.Add(lockerUid);
|
||||||
|
|
||||||
|
if (component.AutoLinksUseProperties)
|
||||||
|
targetBluespaceComponent.BehaviorProperties = component.AutoLinkProperties with {};
|
||||||
}
|
}
|
||||||
if (component.BluespaceLinks.Count >= component.MinBluespaceLinks)
|
if (component.BluespaceLinks.Count >= component.MinBluespaceLinks)
|
||||||
break;
|
break;
|
||||||
@@ -130,61 +199,136 @@ public sealed class BluespaceLockerSystem : EntitySystem
|
|||||||
// Attempt to select, validate, and return a link
|
// Attempt to select, validate, and return a link
|
||||||
var links = component.BluespaceLinks.ToArray();
|
var links = component.BluespaceLinks.ToArray();
|
||||||
var link = links[_robustRandom.Next(0, component.BluespaceLinks.Count)];
|
var link = links[_robustRandom.Next(0, component.BluespaceLinks.Count)];
|
||||||
if (ValidLink(component, link))
|
if (ValidLink(lockerUid, link, component))
|
||||||
return link;
|
return (link, Comp<EntityStorageComponent>(link), CompOrNull<BluespaceLockerComponent>(link));
|
||||||
component.BluespaceLinks.Remove(link);
|
component.BluespaceLinks.Remove(link);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
private void PostClose(EntityUid uid, BluespaceLockerComponent component, StorageAfterCloseEvent args)
|
private void PostClose(EntityUid uid, BluespaceLockerComponent component, StorageAfterCloseEvent args)
|
||||||
|
{
|
||||||
|
PostClose(uid, component);
|
||||||
|
}
|
||||||
|
|
||||||
|
private void OnBluespaceLockerTeleportDelayComplete(EntityUid uid, BluespaceLockerComponent component, BluespaceLockerTeleportDelayComplete args)
|
||||||
|
{
|
||||||
|
PostClose(uid, component, false);
|
||||||
|
}
|
||||||
|
|
||||||
|
private void PostClose(EntityUid uid, BluespaceLockerComponent component, bool doDelay = true)
|
||||||
{
|
{
|
||||||
EntityStorageComponent? entityStorageComponent = null;
|
EntityStorageComponent? entityStorageComponent = null;
|
||||||
|
|
||||||
if (!Resolve(uid, ref entityStorageComponent))
|
if (!Resolve(uid, ref entityStorageComponent))
|
||||||
return;
|
return;
|
||||||
|
|
||||||
|
component.CancelToken?.Cancel();
|
||||||
|
|
||||||
|
// Do delay
|
||||||
|
if (doDelay && component.BehaviorProperties.Delay > 0)
|
||||||
|
{
|
||||||
|
EnsureComp<DoAfterComponent>(uid);
|
||||||
|
component.CancelToken = new CancellationTokenSource();
|
||||||
|
|
||||||
|
_doAfterSystem.DoAfter(
|
||||||
|
new DoAfterEventArgs(uid, component.BehaviorProperties.Delay, component.CancelToken.Token)
|
||||||
|
{
|
||||||
|
UserFinishedEvent = new BluespaceLockerTeleportDelayComplete()
|
||||||
|
});
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
// Select target
|
// Select target
|
||||||
var targetContainerStorageComponent = GetTargetStorage(component);
|
var target = GetTarget(uid, component);
|
||||||
if (targetContainerStorageComponent == null)
|
if (target == null)
|
||||||
return;
|
return;
|
||||||
|
|
||||||
// Move contained items
|
// Move contained items
|
||||||
if (component.TransportEntities)
|
if (component.BehaviorProperties.TransportEntities || component.BehaviorProperties.TransportSentient)
|
||||||
foreach (var entity in entityStorageComponent.Contents.ContainedEntities.ToArray())
|
foreach (var entity in entityStorageComponent.Contents.ContainedEntities.ToArray())
|
||||||
{
|
{
|
||||||
if (!component.AllowSentient && EntityManager.HasComponent<MindComponent>(entity))
|
if (EntityManager.HasComponent<MindComponent>(entity))
|
||||||
continue;
|
{
|
||||||
targetContainerStorageComponent.Contents.Insert(entity, EntityManager);
|
if (component.BehaviorProperties.TransportSentient)
|
||||||
|
target.Value.storageComponent.Contents.Insert(entity, EntityManager);
|
||||||
|
}
|
||||||
|
else if (component.BehaviorProperties.TransportEntities)
|
||||||
|
target.Value.storageComponent.Contents.Insert(entity, EntityManager);
|
||||||
}
|
}
|
||||||
|
|
||||||
// Move contained air
|
// Move contained air
|
||||||
if (component.TransportGas)
|
if (component.BehaviorProperties.TransportGas)
|
||||||
{
|
{
|
||||||
targetContainerStorageComponent.Air.CopyFromMutable(entityStorageComponent.Air);
|
target.Value.storageComponent.Air.CopyFromMutable(entityStorageComponent.Air);
|
||||||
entityStorageComponent.Air.Clear();
|
entityStorageComponent.Air.Clear();
|
||||||
}
|
}
|
||||||
|
|
||||||
// Open and empty target
|
// Open and empty target
|
||||||
if (targetContainerStorageComponent.Open)
|
if (target.Value.storageComponent.Open)
|
||||||
{
|
{
|
||||||
_entityStorage.EmptyContents(targetContainerStorageComponent.Owner, targetContainerStorageComponent);
|
_entityStorage.EmptyContents(target.Value.uid, target.Value.storageComponent);
|
||||||
_entityStorage.ReleaseGas(targetContainerStorageComponent.Owner, targetContainerStorageComponent);
|
_entityStorage.ReleaseGas(target.Value.uid, target.Value.storageComponent);
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
if (targetContainerStorageComponent.IsWeldedShut)
|
if (target.Value.storageComponent.IsWeldedShut)
|
||||||
{
|
{
|
||||||
// It gets bluespaced open...
|
// It gets bluespaced open...
|
||||||
_weldableSystem.ForceWeldedState(targetContainerStorageComponent.Owner, false);
|
_weldableSystem.ForceWeldedState(target.Value.uid, false);
|
||||||
if (targetContainerStorageComponent.IsWeldedShut)
|
if (target.Value.storageComponent.IsWeldedShut)
|
||||||
targetContainerStorageComponent.IsWeldedShut = false;
|
target.Value.storageComponent.IsWeldedShut = false;
|
||||||
}
|
}
|
||||||
LockComponent? lockComponent = null;
|
LockComponent? lockComponent = null;
|
||||||
if (Resolve(targetContainerStorageComponent.Owner, ref lockComponent, false) && lockComponent.Locked)
|
if (Resolve(target.Value.uid, ref lockComponent, false) && lockComponent.Locked)
|
||||||
_lockSystem.Unlock(lockComponent.Owner, lockComponent.Owner, lockComponent);
|
_lockSystem.Unlock(target.Value.uid, target.Value.uid, lockComponent);
|
||||||
|
|
||||||
_entityStorage.OpenStorage(targetContainerStorageComponent.Owner, targetContainerStorageComponent);
|
_entityStorage.OpenStorage(target.Value.uid, target.Value.storageComponent);
|
||||||
|
}
|
||||||
|
|
||||||
|
// Bluespace effects
|
||||||
|
if (component.BehaviorProperties.BluespaceEffectOnTeleportSource)
|
||||||
|
BluespaceEffect(uid, component);
|
||||||
|
if (component.BehaviorProperties.BluespaceEffectOnTeleportTarget)
|
||||||
|
BluespaceEffect(target.Value.uid, component);
|
||||||
|
|
||||||
|
DestroyAfterLimit(uid, component);
|
||||||
|
}
|
||||||
|
|
||||||
|
private void DestroyAfterLimit(EntityUid uid, BluespaceLockerComponent component)
|
||||||
|
{
|
||||||
|
if (component.BehaviorProperties.ClearLinksEvery != -1)
|
||||||
|
{
|
||||||
|
component.UsesSinceLinkClear++;
|
||||||
|
if (component.BehaviorProperties.ClearLinksEvery >= component.UsesSinceLinkClear)
|
||||||
|
{
|
||||||
|
component.BluespaceLinks.Clear();
|
||||||
|
component.UsesSinceLinkClear = 0;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if (component.BehaviorProperties.DestroyAfterUses == -1)
|
||||||
|
return;
|
||||||
|
|
||||||
|
component.BehaviorProperties.DestroyAfterUses--;
|
||||||
|
if (component.BehaviorProperties.DestroyAfterUses > 0)
|
||||||
|
return;
|
||||||
|
|
||||||
|
switch (component.BehaviorProperties.DestroyType)
|
||||||
|
{
|
||||||
|
case BluespaceLockerDestroyType.Explode:
|
||||||
|
_explosionSystem.QueueExplosion(uid.ToCoordinates().ToMap(_entityManager),
|
||||||
|
ExplosionSystem.DefaultExplosionPrototypeId, 4, 1, 2, maxTileBreak: 0);
|
||||||
|
goto case BluespaceLockerDestroyType.Delete;
|
||||||
|
case BluespaceLockerDestroyType.Delete:
|
||||||
|
QueueDel(uid);
|
||||||
|
break;
|
||||||
|
case BluespaceLockerDestroyType.DeleteComponent:
|
||||||
|
RemComp<BluespaceLockerComponent>(uid);
|
||||||
|
break;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private sealed class BluespaceLockerTeleportDelayComplete : EntityEventArgs
|
||||||
|
{
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -353,3 +353,25 @@
|
|||||||
state: syndicate
|
state: syndicate
|
||||||
state_open: syndicate_open
|
state_open: syndicate_open
|
||||||
state_closed: syndicate_door
|
state_closed: syndicate_door
|
||||||
|
|
||||||
|
# Bluespace
|
||||||
|
- type: entity
|
||||||
|
id: LockerBluespaceStation
|
||||||
|
name: bluespace locker
|
||||||
|
suffix: once to station
|
||||||
|
parent: LockerSyndicatePersonal
|
||||||
|
description: Advanced locker technology.
|
||||||
|
components:
|
||||||
|
- type: BluespaceLocker
|
||||||
|
minBluespaceLinks: 1
|
||||||
|
bluespaceEffectOnInit: true
|
||||||
|
behaviorProperties:
|
||||||
|
delay: 0.5
|
||||||
|
bluespaceEffectOnTeleportSource: true
|
||||||
|
bluespaceEffectOnTeleportTarget: true
|
||||||
|
destroyAfterUses: 2
|
||||||
|
destroyType: Delete
|
||||||
|
autoLinksUseProperties: true
|
||||||
|
autoLinkProperties:
|
||||||
|
destroyAfterUses: 2
|
||||||
|
destroyType: DeleteComponent
|
||||||
|
|||||||
@@ -127,8 +127,49 @@
|
|||||||
parent: ClosetBase
|
parent: ClosetBase
|
||||||
description: It's a storage unit.
|
description: It's a storage unit.
|
||||||
components:
|
components:
|
||||||
- type: Appearance
|
- type: Appearance
|
||||||
visuals:
|
visuals:
|
||||||
- type: StorageVisualizer
|
- type: StorageVisualizer
|
||||||
state_open: generic_open
|
state_open: generic_open
|
||||||
state_closed: generic_door
|
state_closed: generic_door
|
||||||
|
|
||||||
|
# Bluespace closet
|
||||||
|
- type: entity
|
||||||
|
id: ClosetBluespace
|
||||||
|
name: suspicious closet
|
||||||
|
suffix: Bluespace
|
||||||
|
parent: ClosetMaintenance
|
||||||
|
description: It's a storage unit... right?
|
||||||
|
components:
|
||||||
|
- type: BluespaceLocker
|
||||||
|
pickLinksFromSameMap: true
|
||||||
|
minBluespaceLinks: 1
|
||||||
|
behaviorProperties:
|
||||||
|
bluespaceEffectOnTeleportSource: true
|
||||||
|
autoLinksBidirectional: true
|
||||||
|
autoLinksUseProperties: true
|
||||||
|
autoLinkProperties:
|
||||||
|
bluespaceEffectOnTeleportSource: true
|
||||||
|
|
||||||
|
# Unstable bluespace closet
|
||||||
|
- type: entity
|
||||||
|
id: ClosetBluespaceUnstable
|
||||||
|
name: suspicious closet
|
||||||
|
suffix: Bluespace unstable
|
||||||
|
parent: ClosetMaintenance
|
||||||
|
description: It's a storage unit... right?
|
||||||
|
components:
|
||||||
|
- type: BluespaceLocker
|
||||||
|
pickLinksFromSameMap: true
|
||||||
|
minBluespaceLinks: 1
|
||||||
|
behaviorProperties:
|
||||||
|
transportEntities: false
|
||||||
|
bluespaceEffectOnTeleportSource: true
|
||||||
|
clearLinksEvery: 2
|
||||||
|
autoLinksBidirectional: true
|
||||||
|
autoLinksUseProperties: true
|
||||||
|
autoLinkProperties:
|
||||||
|
transportEntities: false
|
||||||
|
bluespaceEffectOnTeleportSource: true
|
||||||
|
destroyAfterUses: 2
|
||||||
|
destroyType: DeleteComponent
|
||||||
|
|||||||
@@ -7,6 +7,16 @@
|
|||||||
startAfter: 30
|
startAfter: 30
|
||||||
endAfter: 35
|
endAfter: 35
|
||||||
|
|
||||||
|
- type: gameRule
|
||||||
|
id: BluespaceLockerLink
|
||||||
|
config:
|
||||||
|
!type:StationEventRuleConfiguration
|
||||||
|
id: BluespaceLockerLink
|
||||||
|
weight: 0
|
||||||
|
reoccurrenceDelay: 5
|
||||||
|
earliestStart: 1
|
||||||
|
endAfter: 1
|
||||||
|
|
||||||
- type: gameRule
|
- type: gameRule
|
||||||
id: BreakerFlip
|
id: BreakerFlip
|
||||||
config:
|
config:
|
||||||
|
|||||||
Reference in New Issue
Block a user