Blast doors & shutters (#4822)

This commit is contained in:
mirrorcult
2021-10-10 03:43:50 -07:00
committed by GitHub
parent 72d30724c4
commit 9b4e495c95
32 changed files with 325 additions and 37 deletions

View File

@@ -30,6 +30,9 @@ namespace Content.Client.Doors
[DataField("animatedPanel")] [DataField("animatedPanel")]
private bool _animatedPanel = true; private bool _animatedPanel = true;
[DataField("simpleVisuals")]
private bool _simpleVisuals = false;
/// <summary> /// <summary>
/// Whether the BaseUnlit layer should still be visible when the airlock /// Whether the BaseUnlit layer should still be visible when the airlock
/// is opened. /// is opened.
@@ -50,17 +53,20 @@ namespace Content.Client.Doors
flick.LayerKey = DoorVisualLayers.Base; flick.LayerKey = DoorVisualLayers.Base;
flick.KeyFrames.Add(new AnimationTrackSpriteFlick.KeyFrame("closing", 0f)); flick.KeyFrames.Add(new AnimationTrackSpriteFlick.KeyFrame("closing", 0f));
var flickUnlit = new AnimationTrackSpriteFlick(); if (!_simpleVisuals)
CloseAnimation.AnimationTracks.Add(flickUnlit);
flickUnlit.LayerKey = DoorVisualLayers.BaseUnlit;
flickUnlit.KeyFrames.Add(new AnimationTrackSpriteFlick.KeyFrame("closing_unlit", 0f));
if (_animatedPanel)
{ {
var flickMaintenancePanel = new AnimationTrackSpriteFlick(); var flickUnlit = new AnimationTrackSpriteFlick();
CloseAnimation.AnimationTracks.Add(flickMaintenancePanel); CloseAnimation.AnimationTracks.Add(flickUnlit);
flickMaintenancePanel.LayerKey = WiresVisualizer.WiresVisualLayers.MaintenancePanel; flickUnlit.LayerKey = DoorVisualLayers.BaseUnlit;
flickMaintenancePanel.KeyFrames.Add(new AnimationTrackSpriteFlick.KeyFrame("panel_closing", 0f)); flickUnlit.KeyFrames.Add(new AnimationTrackSpriteFlick.KeyFrame("closing_unlit", 0f));
if (_animatedPanel)
{
var flickMaintenancePanel = new AnimationTrackSpriteFlick();
CloseAnimation.AnimationTracks.Add(flickMaintenancePanel);
flickMaintenancePanel.LayerKey = WiresVisualizer.WiresVisualLayers.MaintenancePanel;
flickMaintenancePanel.KeyFrames.Add(new AnimationTrackSpriteFlick.KeyFrame("panel_closing", 0f));
}
} }
} }
@@ -71,26 +77,32 @@ namespace Content.Client.Doors
flick.LayerKey = DoorVisualLayers.Base; flick.LayerKey = DoorVisualLayers.Base;
flick.KeyFrames.Add(new AnimationTrackSpriteFlick.KeyFrame("opening", 0f)); flick.KeyFrames.Add(new AnimationTrackSpriteFlick.KeyFrame("opening", 0f));
var flickUnlit = new AnimationTrackSpriteFlick(); if (!_simpleVisuals)
OpenAnimation.AnimationTracks.Add(flickUnlit);
flickUnlit.LayerKey = DoorVisualLayers.BaseUnlit;
flickUnlit.KeyFrames.Add(new AnimationTrackSpriteFlick.KeyFrame("opening_unlit", 0f));
if (_animatedPanel)
{ {
var flickMaintenancePanel = new AnimationTrackSpriteFlick(); var flickUnlit = new AnimationTrackSpriteFlick();
OpenAnimation.AnimationTracks.Add(flickMaintenancePanel); OpenAnimation.AnimationTracks.Add(flickUnlit);
flickMaintenancePanel.LayerKey = WiresVisualizer.WiresVisualLayers.MaintenancePanel; flickUnlit.LayerKey = DoorVisualLayers.BaseUnlit;
flickMaintenancePanel.KeyFrames.Add(new AnimationTrackSpriteFlick.KeyFrame("panel_opening", 0f)); flickUnlit.KeyFrames.Add(new AnimationTrackSpriteFlick.KeyFrame("opening_unlit", 0f));
if (_animatedPanel)
{
var flickMaintenancePanel = new AnimationTrackSpriteFlick();
OpenAnimation.AnimationTracks.Add(flickMaintenancePanel);
flickMaintenancePanel.LayerKey = WiresVisualizer.WiresVisualLayers.MaintenancePanel;
flickMaintenancePanel.KeyFrames.Add(new AnimationTrackSpriteFlick.KeyFrame("panel_opening", 0f));
}
} }
} }
DenyAnimation = new Animation {Length = TimeSpan.FromSeconds(_denyDelay)}; if (!_simpleVisuals)
{ {
var flick = new AnimationTrackSpriteFlick(); DenyAnimation = new Animation {Length = TimeSpan.FromSeconds(_denyDelay)};
DenyAnimation.AnimationTracks.Add(flick); {
flick.LayerKey = DoorVisualLayers.BaseUnlit; var flick = new AnimationTrackSpriteFlick();
flick.KeyFrames.Add(new AnimationTrackSpriteFlick.KeyFrame("deny_unlit", 0f)); DenyAnimation.AnimationTracks.Add(flick);
flick.LayerKey = DoorVisualLayers.BaseUnlit;
flick.KeyFrames.Add(new AnimationTrackSpriteFlick.KeyFrame("deny_unlit", 0f));
}
} }
} }
@@ -126,15 +138,18 @@ namespace Content.Client.Doors
case DoorVisualState.Open: case DoorVisualState.Open:
sprite.LayerSetState(DoorVisualLayers.Base, "open"); sprite.LayerSetState(DoorVisualLayers.Base, "open");
unlitVisible = _openUnlitVisible; unlitVisible = _openUnlitVisible;
if (_openUnlitVisible) if (_openUnlitVisible && !_simpleVisuals)
{ {
sprite.LayerSetState(DoorVisualLayers.BaseUnlit, "open_unlit"); sprite.LayerSetState(DoorVisualLayers.BaseUnlit, "open_unlit");
} }
break; break;
case DoorVisualState.Closed: case DoorVisualState.Closed:
sprite.LayerSetState(DoorVisualLayers.Base, "closed"); sprite.LayerSetState(DoorVisualLayers.Base, "closed");
sprite.LayerSetState(DoorVisualLayers.BaseUnlit, "closed_unlit"); if (!_simpleVisuals)
sprite.LayerSetState(DoorVisualLayers.BaseBolted, "bolted_unlit"); {
sprite.LayerSetState(DoorVisualLayers.BaseUnlit, "closed_unlit");
sprite.LayerSetState(DoorVisualLayers.BaseBolted, "bolted_unlit");
}
break; break;
case DoorVisualState.Opening: case DoorVisualState.Opening:
animPlayer.Play(OpenAnimation, AnimationKey); animPlayer.Play(OpenAnimation, AnimationKey);
@@ -161,9 +176,12 @@ namespace Content.Client.Doors
boltedVisible = true; boltedVisible = true;
} }
sprite.LayerSetVisible(DoorVisualLayers.BaseUnlit, unlitVisible); if (!_simpleVisuals)
sprite.LayerSetVisible(DoorVisualLayers.BaseWelded, weldedVisible); {
sprite.LayerSetVisible(DoorVisualLayers.BaseBolted, unlitVisible && boltedVisible); sprite.LayerSetVisible(DoorVisualLayers.BaseUnlit, unlitVisible);
sprite.LayerSetVisible(DoorVisualLayers.BaseWelded, weldedVisible);
sprite.LayerSetVisible(DoorVisualLayers.BaseBolted, unlitVisible && boltedVisible);
}
} }
} }

View File

@@ -279,7 +279,9 @@ namespace Content.Client.Entry
"IncreaseDamageOnWield", "IncreaseDamageOnWield",
"AmbientOnPowered", "AmbientOnPowered",
"TabletopGame", "TabletopGame",
"LitOnPowered" "LitOnPowered",
"TriggerOnSignalReceived",
"ToggleDoorOnTrigger"
}; };
} }
} }

View File

@@ -107,6 +107,12 @@ namespace Content.Server.Doors.Components
[ViewVariables(VVAccess.ReadWrite)] [DataField("bumpOpen")] [ViewVariables(VVAccess.ReadWrite)] [DataField("bumpOpen")]
public bool BumpOpen = true; public bool BumpOpen = true;
/// <summary>
/// Whether the door will open when it is activated or clicked.
/// </summary>
[ViewVariables(VVAccess.ReadWrite)] [DataField("clickOpen")]
public bool ClickOpen = true;
/// <summary> /// <summary>
/// Whether the door starts open when it's first loaded from prototype. A door won't start open if its prototype is also welded shut. /// Whether the door starts open when it's first loaded from prototype. A door won't start open if its prototype is also welded shut.
/// Handled in Startup(). /// Handled in Startup().
@@ -164,6 +170,12 @@ namespace Content.Server.Doors.Components
[DataField("denySound")] [DataField("denySound")]
public SoundSpecifier? DenySound; public SoundSpecifier? DenySound;
/// <summary>
/// Should this door automatically close if its been open for too long?
/// </summary>
[DataField("autoClose")]
public bool AutoClose;
/// <summary> /// <summary>
/// Default time that the door should take to pry open. /// Default time that the door should take to pry open.
/// </summary> /// </summary>
@@ -238,6 +250,9 @@ namespace Content.Server.Doors.Components
void IActivate.Activate(ActivateEventArgs eventArgs) void IActivate.Activate(ActivateEventArgs eventArgs)
{ {
if (!ClickOpen)
return;
DoorClickShouldActivateEvent ev = new DoorClickShouldActivateEvent(eventArgs); DoorClickShouldActivateEvent ev = new DoorClickShouldActivateEvent(eventArgs);
Owner.EntityManager.EventBus.RaiseLocalEvent(Owner.Uid, ev, false); Owner.EntityManager.EventBus.RaiseLocalEvent(Owner.Uid, ev, false);
if (ev.Handled) if (ev.Handled)
@@ -255,9 +270,15 @@ namespace Content.Server.Doors.Components
#region Opening #region Opening
public void TryOpen(IEntity user) public void TryOpen(IEntity? user=null)
{ {
if (CanOpenByEntity(user)) if (user == null)
{
// a machine opened it or something, idk
Open();
return;
}
else if (CanOpenByEntity(user))
{ {
Open(); Open();
@@ -384,9 +405,9 @@ namespace Content.Server.Doors.Components
#region Closing #region Closing
public void TryClose(IEntity user) public void TryClose(IEntity? user=null)
{ {
if (!CanCloseByEntity(user)) if (user != null && !CanCloseByEntity(user))
{ {
Deny(); Deny();
return; return;
@@ -610,6 +631,9 @@ namespace Content.Server.Doors.Components
if (State != DoorState.Open) if (State != DoorState.Open)
return; return;
if (!AutoClose)
return;
var autoev = new BeforeDoorAutoCloseEvent(); var autoev = new BeforeDoorAutoCloseEvent();
Owner.EntityManager.EventBus.RaiseLocalEvent(Owner.Uid, autoev, false); Owner.EntityManager.EventBus.RaiseLocalEvent(Owner.Uid, autoev, false);
if (autoev.Cancelled) if (autoev.Cancelled)

View File

@@ -0,0 +1,10 @@
using Robust.Shared.GameObjects;
namespace Content.Server.Doors.Components
{
[RegisterComponent]
public class ToggleDoorOnTriggerComponent : Component
{
public override string Name => "ToggleDoorOnTrigger";
}
}

View File

@@ -1,9 +1,11 @@
using System; using System;
using Content.Server.Doors.Components;
using Content.Server.Explosion.Components; using Content.Server.Explosion.Components;
using Content.Server.Flash; using Content.Server.Flash;
using Content.Server.Flash.Components; using Content.Server.Flash.Components;
using Content.Shared.Acts; using Content.Shared.Acts;
using Content.Shared.Audio; using Content.Shared.Audio;
using Content.Shared.Doors;
using JetBrains.Annotations; using JetBrains.Annotations;
using Robust.Shared.Audio; using Robust.Shared.Audio;
using Robust.Shared.GameObjects; using Robust.Shared.GameObjects;
@@ -43,6 +45,7 @@ namespace Content.Server.Explosion
SubscribeLocalEvent<SoundOnTriggerComponent, TriggerEvent>(HandleSoundTrigger); SubscribeLocalEvent<SoundOnTriggerComponent, TriggerEvent>(HandleSoundTrigger);
SubscribeLocalEvent<ExplodeOnTriggerComponent, TriggerEvent>(HandleExplodeTrigger); SubscribeLocalEvent<ExplodeOnTriggerComponent, TriggerEvent>(HandleExplodeTrigger);
SubscribeLocalEvent<FlashOnTriggerComponent, TriggerEvent>(HandleFlashTrigger); SubscribeLocalEvent<FlashOnTriggerComponent, TriggerEvent>(HandleFlashTrigger);
SubscribeLocalEvent<ToggleDoorOnTriggerComponent, TriggerEvent>(HandleDoorTrigger);
} }
#region Explosions #region Explosions
@@ -89,6 +92,25 @@ namespace Content.Server.Explosion
EntityManager.QueueDeleteEntity(uid); EntityManager.QueueDeleteEntity(uid);
} }
private void HandleDoorTrigger(EntityUid uid, ToggleDoorOnTriggerComponent component, TriggerEvent args)
{
if (EntityManager.TryGetComponent<ServerDoorComponent>(uid, out var door))
{
switch (door.State)
{
case SharedDoorComponent.DoorState.Open:
door.Close();
break;
case SharedDoorComponent.DoorState.Closed:
door.Open();
break;
case SharedDoorComponent.DoorState.Closing:
case SharedDoorComponent.DoorState.Opening:
break;
}
}
}
private void HandleCollide(EntityUid uid, TriggerOnCollideComponent component, StartCollideEvent args) private void HandleCollide(EntityUid uid, TriggerOnCollideComponent component, StartCollideEvent args)
{ {
Trigger(component.Owner); Trigger(component.Owner);

View File

@@ -0,0 +1,10 @@
using Robust.Shared.GameObjects;
namespace Content.Server.MachineLinking.Components
{
[RegisterComponent]
public class TriggerOnSignalReceivedComponent : Component
{
public override string Name => "TriggerOnSignalReceived";
}
}

View File

@@ -1,10 +1,12 @@
using Content.Server.MachineLinking.Components; using Content.Server.MachineLinking.Components;
using Content.Server.MachineLinking.Events; using Content.Server.MachineLinking.Events;
using Content.Shared.Interaction; using Content.Shared.Interaction;
using JetBrains.Annotations;
using Robust.Shared.GameObjects; using Robust.Shared.GameObjects;
namespace Content.Server.MachineLinking.System namespace Content.Server.MachineLinking.System
{ {
[UsedImplicitly]
public class SignalButtonSystem : EntitySystem public class SignalButtonSystem : EntitySystem
{ {
public override void Initialize() public override void Initialize()

View File

@@ -72,7 +72,7 @@ namespace Content.Server.MachineLinking.System
if (!IsInRange(component, link.ReceiverComponent)) continue; if (!IsInRange(component, link.ReceiverComponent)) continue;
RaiseLocalEvent(link.ReceiverComponent.Owner.Uid, RaiseLocalEvent(link.ReceiverComponent.Owner.Uid,
new SignalReceivedEvent(link.Receiverport.Name, args.Value)); new SignalReceivedEvent(link.Receiverport.Name, args.Value), false);
} }
} }

View File

@@ -0,0 +1,25 @@
using Content.Server.Explosion;
using Content.Server.MachineLinking.Components;
using Content.Server.MachineLinking.Events;
using Robust.Shared.GameObjects;
using Robust.Shared.IoC;
namespace Content.Server.MachineLinking.System
{
public class TriggerOnSignalReceivedSystem : EntitySystem
{
[Dependency] private readonly TriggerSystem _trigger = default!;
public override void Initialize()
{
base.Initialize();
SubscribeLocalEvent<TriggerOnSignalReceivedComponent, SignalReceivedEvent>(OnSignalReceived);
}
private void OnSignalReceived(EntityUid uid, TriggerOnSignalReceivedComponent component, SignalReceivedEvent args)
{
_trigger.Trigger(EntityManager.GetEntity(uid));
}
}
}

Binary file not shown.

View File

@@ -0,0 +1,35 @@
- type: entity
id: BlastDoor
parent: BaseShutter
name: blast door
description: This one says 'BLAST DONGER'.
components:
- type: Sprite
netsync: false
drawdepth: Mobs # They're on the same layer as mobs, perspective.
sprite: Structures/Doors/Shutters/blastdoor.rsi
layers:
- state: closed
map: ["enum.DoorVisualLayers.Base"]
- type: Door
closeTimeOne: 0.4
closeTimeTwo: 0.4
openTimeOne: 0.4
openTimeTwo: 0.4
crushDamage:
types:
Blunt: 25 # yowch
- type: Occluder
- type: Appearance
visuals:
- type: AirlockVisualizer
simpleVisuals: true
animationTime: 1.0
- type: entity
id: BlastDoorOpen
parent: BlastDoor
suffix: Open
components:
- type: Door
startOpen: true

View File

@@ -0,0 +1,136 @@
- type: entity
id: BaseShutter
parent: BaseStructure
name: shutter
abstract: true
description: One shudders to think about what might be behind this shutter.
components:
- type: Sprite
netsync: false
drawdepth: Mobs # They're on the same layer as mobs, perspective.
sprite: Structures/Doors/Shutters/shutters.rsi
layers:
- state: closed
map: ["enum.DoorVisualLayers.Base"]
- type: Physics
fixtures:
- shape:
!type:PhysShapeAabb
bounds: "-0.49,-0.49,0.49,0.49" # don't want this colliding with walls or they won't close
mass: 100
mask:
- MobImpassable
layer:
- Opaque
- Impassable
- MobImpassable
- VaultImpassable
- SmallImpassable
- type: Door
board: DoorElectronics
bumpOpen: false
clickOpen: false
autoClose: false
closeTimeOne: 0.2
closeTimeTwo: 1.2
openTimeOne: 1.2
openTimeTwo: 0.2
inhibitCrush: false
crushDamage:
types:
Blunt: 5 # getting shutters closed on you probably doesn't hurt that much
openSound:
path: /Audio/Machines/blastdoor.ogg
closeSound:
path: /Audio/Machines/blastdoor.ogg
- type: Appearance
visuals:
- type: AirlockVisualizer
simpleVisuals: true
animationTime: 1.4
- type: UserInterface
interfaces:
- key: enum.WiresUiKey.Key
type: WiresBoundUserInterface
- type: Airtight
fixVacuum: true
- type: Damageable
damageContainer: Inorganic
damageModifierSet: Metallic
- type: Destructible
thresholds:
- trigger:
!type:DamageTrigger
damage: 500
behaviors:
- !type:DoActsBehavior
acts: ["Destruction"]
- type: IconSmooth
key: walls
mode: NoSprite
- type: SignalReceiver
inputs:
- name: pressed
- type: ToggleDoorOnTrigger
- type: TriggerOnSignalReceived
placement:
mode: SnapgridCenter
- type: entity
id: ShuttersNormal
parent: BaseShutter
components:
- type: Occluder
- type: entity
id: ShuttersNormalOpen
parent: ShuttersNormal
suffix: Open
components:
- type: Door
startOpen: true
- type: entity
id: ShuttersRadiation
parent: BaseShutter
name: radiation shutters
description: Why did they make these shutters radioactive?
components:
- type: Sprite
netsync: false
drawdepth: Mobs # They're on the same layer as mobs, perspective.
sprite: Structures/Doors/Shutters/shutters_radiation.rsi
layers:
- state: closed
map: ["enum.DoorVisualLayers.Base"]
- type: Occluder
- type: entity
id: ShuttersRadiationOpen
parent: ShuttersRadiation
suffix: Open
components:
- type: Door
startOpen: true
- type: entity
id: ShuttersWindow
parent: BaseShutter
name: window shutters
description: The Best (TM) place to see your friends explode!
components:
- type: Sprite
netsync: false
drawdepth: Mobs # They're on the same layer as mobs, perspective.
sprite: Structures/Doors/Shutters/shutters_window.rsi
layers:
- state: closed
map: ["enum.DoorVisualLayers.Base"]
- type: entity
id: ShuttersWindowOpen
parent: ShuttersWindow
suffix: Open
components:
- type: Door
startOpen: true

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.3 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 2.1 KiB

View File

@@ -0,0 +1 @@
{"name":1,"size":{"x":32,"y":32},"states":[{"name":"closing","directions":1,"delays":[[0.1,0.1,0.1,0.1,0.1,0.1,0.1,0.4]]},{"name":"opening","directions":1,"delays":[[0.1,0.1,0.1,0.1,0.1,0.1,0.1,0.4]]},{"name":"open","directions":1},{"name":"closed","directions":1}],"license":"CC-BY-SA-3.0","copyright":"Tgstation at 97b4295aca9f31a750456e40730d05b5837e39fc","version":1}

Binary file not shown.

After

Width:  |  Height:  |  Size: 268 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 2.0 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 552 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 2.1 KiB

View File

@@ -0,0 +1 @@
{"name":1,"size":{"x":32,"y":32},"states":[{"name":"closing","directions":1,"delays":[[0.05,0.05,0.05,0.05,0.05,0.05,0.05,0.05,0.05,0.05,0.05,0.05,0.05,0.05,0.05,0.05,0.05,0.05,0.05,0.05,0.05,0.05,0.05,0.05,0.05,0.05,0.05]]},{"name":"opening","directions":1,"delays":[[0.05,0.05,0.05,0.05,0.05,0.05,0.05,0.05,0.05,0.05,0.05,0.05,0.05,0.05,0.05,0.05,0.05,0.05,0.05,0.05,0.05,0.05,0.05,0.05,0.05,0.05,0.05]]},{"name":"open","directions":1},{"name":"closed","directions":1}],"license":"CC-BY-SA-3.0","copyright":"Tgstation at 97b0.05295aca9f31a7500.0556e0.050730d05b5837e39fc","version":1}

Binary file not shown.

After

Width:  |  Height:  |  Size: 278 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 2.2 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 642 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 2.3 KiB

View File

@@ -0,0 +1 @@
{"name":1,"size":{"x":32,"y":32},"states":[{"name":"closed","directions":1},{"name":"open","directions":1},{"name":"opening","directions":1,"delays":[[0.05,0.05,0.05,0.05,0.05,0.05,0.05,0.05,0.05,0.05,0.05,0.05,0.05,0.05,0.05,0.05,0.05,0.05,0.05,0.05,0.05,0.05,0.05,0.05,0.05,0.05,0.05]]},{"name":"closing","directions":1,"delays":[[0.05,0.05,0.05,0.05,0.05,0.05,0.05,0.05,0.05,0.05,0.05,0.05,0.05,0.05,0.05,0.05,0.05,0.05,0.05,0.05,0.05,0.05,0.05,0.05,0.05,0.05,0.05]]}],"license":"CC-BY-SA-3.0","copyright":"Tgstation at 97b0.05295aca9f31a7500.0556e0.050730d05b5837e39fc","version":1}

Binary file not shown.

After

Width:  |  Height:  |  Size: 283 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 2.4 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 645 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 2.3 KiB

View File

@@ -0,0 +1 @@
{"name":1,"size":{"x":32,"y":32},"states":[{"name":"closed","directions":1},{"name":"opening","directions":1,"delays":[[0.05,0.05,0.05,0.05,0.05,0.05,0.05,0.05,0.05,0.05,0.05,0.05,0.05,0.05,0.05,0.05,0.05,0.05,0.05,0.05,0.05,0.05,0.05,0.05,0.05,0.05,0.05]]},{"name":"closing","directions":1,"delays":[[0.05,0.05,0.05,0.05,0.05,0.05,0.05,0.05,0.05,0.05,0.05,0.05,0.05,0.05,0.05,0.05,0.05,0.05,0.05,0.05,0.05,0.05,0.05,0.05,0.05,0.05,0.05]]},{"name":"open","directions":1}],"license":"CC-BY-SA-3.0","copyright":"Tgstation at 97b0.05295aca9f31a7500.0556e0.050730d05b5837e39fc","version":1}

Binary file not shown.

After

Width:  |  Height:  |  Size: 278 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 2.4 KiB