This commit is contained in:
Tobias Berger 2026-08-14 01:46:09 +00:00 committed by GitHub
commit 0ea54ca377
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194
29 changed files with 329 additions and 163 deletions

View File

@ -21,8 +21,11 @@ public sealed class SignalTimerBoundUserInterface : BoundUserInterface
_window = this.CreateWindow<SignalTimerWindow>();
_window.OnStartTimer += StartTimer;
_window.OnCurrentTextChanged += OnTextChanged;
_window.OnCurrentDelayMinutesChanged += OnDelayChanged;
_window.OnCurrentDelaySecondsChanged += OnDelayChanged;
// Begin Monolith
// _window.OnCurrentDelayMinutesChanged += OnDelayChanged;
// _window.OnCurrentDelaySecondsChanged += OnDelayChanged;
_window.OnCurrentDelayChanged += OnDelayChanged;
// End Monolith
}
public void StartTimer()
@ -35,11 +38,11 @@ public sealed class SignalTimerBoundUserInterface : BoundUserInterface
SendMessage(new SignalTimerTextChangedMessage(newText));
}
private void OnDelayChanged(string newDelay)
private void OnDelayChanged(TimeSpan newDelay) // Monolith - Change from string to TimeSpan
{
if (_window == null)
return;
SendMessage(new SignalTimerDelayChangedMessage(_window.GetDelay()));
SendMessage(new SignalTimerDelayChangedMessage(newDelay)); // Mono
}
/// <summary>
@ -54,8 +57,11 @@ public sealed class SignalTimerBoundUserInterface : BoundUserInterface
return;
_window.SetCurrentText(cast.CurrentText);
_window.SetCurrentDelayMinutes(cast.CurrentDelayMinutes);
_window.SetCurrentDelaySeconds(cast.CurrentDelaySeconds);
// Begin Monolith
// _window.SetCurrentDelayMinutes(cast.CurrentDelayMinutes);
// _window.SetCurrentDelaySeconds(cast.CurrentDelaySeconds);
_window.SetCurrentDelay(cast.CurrentDelay);
// End Monolith
_window.SetShowText(cast.ShowText);
_window.SetTriggerTime(cast.TriggerTime);
_window.SetTimerStarted(cast.TimerStarted);

View File

@ -7,10 +7,14 @@
</BoxContainer>
<BoxContainer Name="DelayEdit" Orientation="Horizontal">
<Label Name="CurrentDelay" Text="{Loc signal-timer-menu-delay}" />
<!-- Mono: Changed from integer MM:SS to float seconds
<LineEdit Name="CurrentDelayEditMinutes" MinWidth="32" />
<Label Name="Colon" Text=":" />
<LineEdit Name="CurrentDelayEditSeconds" MinWidth="32" />
<Label Name="DelayInfo" Text=" (mm:ss)" />
-->
<FloatSpinBox Name="CurrentDelayEdit" MinWidth="32" />
<Label Text="s" />
</BoxContainer>
<Button Name="StartTimer" Text="{Loc signal-timer-menu-start}" />
</BoxContainer>

View File

@ -15,8 +15,11 @@ public sealed partial class SignalTimerWindow : DefaultWindow
private const int MaxTextLength = 5;
public event Action<string>? OnCurrentTextChanged;
public event Action<string>? OnCurrentDelayMinutesChanged;
public event Action<string>? OnCurrentDelaySecondsChanged;
// Begin Monolith
// public event Action<string>? OnCurrentDelayMinutesChanged;
// public event Action<string>? OnCurrentDelaySecondsChanged;
public event Action<TimeSpan>? OnCurrentDelayChanged;
// End Monolith
private TimeSpan? _triggerTime;
@ -30,8 +33,11 @@ public sealed partial class SignalTimerWindow : DefaultWindow
IoCManager.InjectDependencies(this);
CurrentTextEdit.OnTextChanged += e => OnCurrentTextChange(e.Text);
CurrentDelayEditMinutes.OnTextChanged += e => OnCurrentDelayMinutesChange(e.Text);
CurrentDelayEditSeconds.OnTextChanged += e => OnCurrentDelaySecondsChange(e.Text);
// Begin Monolith
// CurrentDelayEditMinutes.OnTextChanged += e => OnCurrentDelayMinutesChange(e.Text);
// CurrentDelayEditSeconds.OnTextChanged += e => OnCurrentDelaySecondsChange(e.Text);
CurrentDelayEdit.OnValueChanged += e => CurrentDelayChange(TimeSpan.FromSeconds(e.Value));
// End Monolith
StartTimer.OnPressed += _ => StartTimerWeh();
}
@ -77,81 +83,93 @@ public sealed partial class SignalTimerWindow : DefaultWindow
OnCurrentTextChanged?.Invoke(text);
}
public void OnCurrentDelayMinutesChange(string text)
// Begin Monolith
// public void OnCurrentDelayMinutesChange(string text)
// {
// List<char> toRemove = new();
// foreach (var a in text)
// {
// if (!char.IsDigit(a))
// toRemove.Add(a);
// }
// foreach (var a in toRemove)
// {
// CurrentDelayEditMinutes.Text = text.Replace(a.ToString(),"");
// }
// if (CurrentDelayEditMinutes.Text == "")
// return;
// while (CurrentDelayEditMinutes.Text[0] == '0' && CurrentDelayEditMinutes.Text.Length > 2)
// {
// CurrentDelayEditMinutes.Text = CurrentDelayEditMinutes.Text.Remove(0, 1);
// }
// if (CurrentDelayEditMinutes.Text.Length > 2)
// {
// CurrentDelayEditMinutes.Text = CurrentDelayEditMinutes.Text.Remove(2);
// }
// OnCurrentDelayMinutesChanged?.Invoke(CurrentDelayEditMinutes.Text);
// }
// public void OnCurrentDelaySecondsChange(string text)
// {
// List<char> toRemove = new();
// foreach (var a in text)
// {
// if (!char.IsDigit(a))
// toRemove.Add(a);
// }
// foreach (var a in toRemove)
// {
// CurrentDelayEditSeconds.Text = text.Replace(a.ToString(), "");
// }
// if (CurrentDelayEditSeconds.Text == "")
// return;
// while (CurrentDelayEditSeconds.Text[0] == '0' && CurrentDelayEditSeconds.Text.Length > 2)
// {
// CurrentDelayEditSeconds.Text = CurrentDelayEditSeconds.Text.Remove(0, 1);
// }
// if (CurrentDelayEditSeconds.Text.Length > 2)
// {
// CurrentDelayEditSeconds.Text = CurrentDelayEditSeconds.Text.Remove(2);
// }
// OnCurrentDelaySecondsChanged?.Invoke(CurrentDelayEditSeconds.Text);
// }
public void CurrentDelayChange(TimeSpan newValue)
{
List<char> toRemove = new();
foreach (var a in text)
{
if (!char.IsDigit(a))
toRemove.Add(a);
}
foreach (var a in toRemove)
{
CurrentDelayEditMinutes.Text = text.Replace(a.ToString(),"");
}
if (CurrentDelayEditMinutes.Text == "")
return;
while (CurrentDelayEditMinutes.Text[0] == '0' && CurrentDelayEditMinutes.Text.Length > 2)
{
CurrentDelayEditMinutes.Text = CurrentDelayEditMinutes.Text.Remove(0, 1);
}
if (CurrentDelayEditMinutes.Text.Length > 2)
{
CurrentDelayEditMinutes.Text = CurrentDelayEditMinutes.Text.Remove(2);
}
OnCurrentDelayMinutesChanged?.Invoke(CurrentDelayEditMinutes.Text);
}
public void OnCurrentDelaySecondsChange(string text)
{
List<char> toRemove = new();
foreach (var a in text)
{
if (!char.IsDigit(a))
toRemove.Add(a);
}
foreach (var a in toRemove)
{
CurrentDelayEditSeconds.Text = text.Replace(a.ToString(), "");
}
if (CurrentDelayEditSeconds.Text == "")
return;
while (CurrentDelayEditSeconds.Text[0] == '0' && CurrentDelayEditSeconds.Text.Length > 2)
{
CurrentDelayEditSeconds.Text = CurrentDelayEditSeconds.Text.Remove(0, 1);
}
if (CurrentDelayEditSeconds.Text.Length > 2)
{
CurrentDelayEditSeconds.Text = CurrentDelayEditSeconds.Text.Remove(2);
}
OnCurrentDelaySecondsChanged?.Invoke(CurrentDelayEditSeconds.Text);
OnCurrentDelayChanged?.Invoke(newValue);
}
// End Monolith
public void SetCurrentText(string text)
{
CurrentTextEdit.Text = text;
}
// Begin Monolith
// public void SetCurrentDelayMinutes(string delay)
// {
// CurrentDelayEditMinutes.Text = delay;
// }
public void SetCurrentDelayMinutes(string delay)
// public void SetCurrentDelaySeconds(string delay)
// {
// CurrentDelayEditSeconds.Text = delay;
// }
public void SetCurrentDelay(TimeSpan delay)
{
CurrentDelayEditMinutes.Text = delay;
CurrentDelayEdit.Value = (float)delay.TotalSeconds;
}
public void SetCurrentDelaySeconds(string delay)
{
CurrentDelayEditSeconds.Text = delay;
}
// End Monolith
public void SetShowText(bool showTime)
{
TextEdit.Visible = showTime;
@ -176,8 +194,10 @@ public sealed partial class SignalTimerWindow : DefaultWindow
public void SetHasAccess(bool hasAccess)
{
CurrentTextEdit.Editable = hasAccess;
CurrentDelayEditMinutes.Editable = hasAccess;
CurrentDelayEditSeconds.Editable = hasAccess;
// Begin Monolith
// CurrentDelayEditMinutes.Editable = hasAccess;
// CurrentDelayEditSeconds.Editable = hasAccess;
// End Monolith
StartTimer.Disabled = !hasAccess;
}
@ -186,10 +206,13 @@ public sealed partial class SignalTimerWindow : DefaultWindow
/// </summary>
public TimeSpan GetDelay()
{
if (!double.TryParse(CurrentDelayEditMinutes.Text, out var minutes))
minutes = 0;
if (!double.TryParse(CurrentDelayEditSeconds.Text, out var seconds))
seconds = 0;
return TimeSpan.FromMinutes(minutes) + TimeSpan.FromSeconds(seconds);
// Begin Monolith
// if (!double.TryParse(CurrentDelayEditMinutes.Text, out var minutes))
// minutes = 0;
// if (!double.TryParse(CurrentDelayEditSeconds.Text, out var seconds))
// seconds = 0;
// return TimeSpan.FromMinutes(minutes) + TimeSpan.FromSeconds(seconds);
return TimeSpan.FromSeconds(CurrentDelayEdit.Value);
// End Monolith
}
}

View File

@ -51,8 +51,11 @@ public sealed class SignalTimerSystem : EntitySystem
if (_ui.HasUi(uid, SignalTimerUiKey.Key))
{
_ui.SetUiState(uid, SignalTimerUiKey.Key, new SignalTimerBoundUserInterfaceState(component.Label,
TimeSpan.FromSeconds(component.Delay).Minutes.ToString("D2"),
TimeSpan.FromSeconds(component.Delay).Seconds.ToString("D2"),
// Begin Monolith
// TimeSpan.FromSeconds(component.Delay).Minutes.ToString("D2"),
// TimeSpan.FromSeconds(component.Delay).Seconds.ToString("D2"),
TimeSpan.FromSeconds(component.Delay),
// End Monolith
component.CanEditLabel,
time,
active != null,
@ -73,8 +76,11 @@ public sealed class SignalTimerSystem : EntitySystem
if (_ui.HasUi(uid, SignalTimerUiKey.Key))
{
_ui.SetUiState(uid, SignalTimerUiKey.Key, new SignalTimerBoundUserInterfaceState(signalTimer.Label,
TimeSpan.FromSeconds(signalTimer.Delay).Minutes.ToString("D2"),
TimeSpan.FromSeconds(signalTimer.Delay).Seconds.ToString("D2"),
// Begin Monolith
// TimeSpan.FromSeconds(signalTimer.Delay).Minutes.ToString("D2"),
// TimeSpan.FromSeconds(signalTimer.Delay).Seconds.ToString("D2"),
TimeSpan.FromSeconds(signalTimer.Delay),
// End Monolith
signalTimer.CanEditLabel,
TimeSpan.Zero,
false,

View File

@ -33,33 +33,37 @@ public sealed class InteractorSystem : SharedInteractorSystem
return;
}
var i = count - 1;
var netEnt = ent.Comp.TargetEntities[i].Item1;
var target = GetEntity(netEnt);
_constructionQuery.TryComp(target, out var construction);
var originalCount = construction?.InteractionQueue?.Count ?? 0;
if (!InteractWith(ent, target))
// Mono
for (var i = count - 1; i >= 0; i--)
{
// have to remove it since user's filter was bad due to unhandled interaction
RemoveTarget(ent, target);
Machine.Failed(ent.Owner);
return;
}
var netEnt = ent.Comp.TargetEntities[i].Item1;
var target = GetEntity(netEnt);
_constructionQuery.TryComp(target, out var construction);
var originalCount = construction?.InteractionQueue?.Count ?? 0;
if (!InteractWith(ent, target))
{
// have to remove it since user's filter was bad due to unhandled interaction
// RemoveTarget(ent, target); // Mono
Machine.Failed(ent.Owner);
continue; // Mono
}
// construction supercode queues it instead of starting a doafter now, assume that queuing means it has started
var newCount = construction?.InteractionQueue?.Count ?? 0;
if (newCount > originalCount
|| HasDoAfter(ent))
{
Machine.Started(ent.Owner);
UpdateAppearance(ent, InteractorState.Active);
}
else
{
// no doafter, complete it immediately
TryRemoveTarget(ent, target);
Machine.Completed(ent.Owner);
UpdateAppearance(ent);
// construction supercode queues it instead of starting a doafter now, assume that queuing means it has started
var newCount = construction?.InteractionQueue?.Count ?? 0;
if (newCount > originalCount
|| HasDoAfter(ent))
{
Machine.Started(ent.Owner);
UpdateAppearance(ent, InteractorState.Active);
}
else
{
// no doafter, complete it immediately
TryRemoveTarget(ent, target);
Machine.Completed(ent.Owner);
UpdateAppearance(ent);
}
break; // Mono
}
}
}

View File

@ -0,0 +1,19 @@
using Content.Shared.DeviceLinking;
using Content.Shared.Research.Prototypes;
using Robust.Shared.Prototypes;
namespace Content.Server._Goobstation.Lathe;
/// <summary>
/// Lets a lathe produce the last made recipe, controlled by signal port.
/// The port must be added by something else e.g. AutomationSlots
/// </summary>
[RegisterComponent, Access(typeof(LatheAutomationSystem))]
public sealed partial class LatheAutomationComponent : Component
{
[ViewVariables]
public LatheRecipePrototype? LastRecipe;
[DataField]
public ProtoId<SinkPortPrototype> PrintPort = "LathePrint";
}

View File

@ -0,0 +1,40 @@
using Content.Shared._Goobstation.Factory;
using Content.Server.Lathe;
using Content.Shared.DeviceLinking.Events;
using Content.Shared.Lathe;
namespace Content.Server._Goobstation.Lathe;
public sealed class LatheAutomationSystem : EntitySystem
{
[Dependency] private readonly AutomationSystem _automation = default!;
[Dependency] private readonly LatheSystem _lathe = default!;
public override void Initialize()
{
base.Initialize();
SubscribeLocalEvent<LatheAutomationComponent, LatheStartPrintingEvent>(OnStartPrinting);
SubscribeLocalEvent<LatheAutomationComponent, SignalReceivedEvent>(OnSignalReceived);
}
private void OnStartPrinting(Entity<LatheAutomationComponent> ent, ref LatheStartPrintingEvent args)
{
ent.Comp.LastRecipe = args.Recipe;
}
private void OnSignalReceived(Entity<LatheAutomationComponent> ent, ref SignalReceivedEvent args)
{
if (!_automation.IsAutomated(ent))
return;
if (args.Port != ent.Comp.PrintPort)
return;
if (ent.Comp.LastRecipe is not {} recipe)
return;
_lathe.TryAddToQueue(ent.Owner, recipe, 1);
_lathe.TryStartProducing(ent.Owner); // Won't do anything otherwise
}
}

View File

@ -15,24 +15,33 @@ public enum SignalTimerUiKey : byte
public sealed class SignalTimerBoundUserInterfaceState : BoundUserInterfaceState
{
public string CurrentText;
public string CurrentDelayMinutes;
public string CurrentDelaySeconds;
// Begin Monolith
// public string CurrentDelayMinutes;
// public string CurrentDelaySeconds;
public TimeSpan CurrentDelay;
// End Monolith
public bool ShowText;
public TimeSpan TriggerTime;
public bool TimerStarted;
public bool HasAccess;
public SignalTimerBoundUserInterfaceState(string currentText,
string currentDelayMinutes,
string currentDelaySeconds,
// Begin Monolith
// string currentDelayMinutes,
// string currentDelaySeconds,
TimeSpan currentDelay,
// End Monolith
bool showText,
TimeSpan triggerTime,
bool timerStarted,
bool hasAccess)
{
CurrentText = currentText;
CurrentDelayMinutes = currentDelayMinutes;
CurrentDelaySeconds = currentDelaySeconds;
// Begin Monolith
// CurrentDelayMinutes = currentDelayMinutes;
// CurrentDelaySeconds = currentDelaySeconds;
CurrentDelay = currentDelay;
// End Monolith
ShowText = showText;
TriggerTime = triggerTime;
TimerStarted = timerStarted;

View File

@ -126,10 +126,7 @@ public sealed class AutomationSystem : EntitySystem
return null;
}
public bool IsAutomated(EntityUid uid)
{
return _automatedQuery.HasComp(uid);
}
public bool IsAutomated(EntityUid uid) => _automatedQuery.HasComp(uid);
public bool HasSlot(Entity<AutomationSlotsComponent?> ent, string port, bool input)
{

View File

@ -95,3 +95,7 @@ signal-port-description-rad-low = Signal port set to HIGH if the tank is below 6
signal-port-name-rad-full = Full
signal-port-description-rad-full = Signal port set to HIGH if the tank is above 66% pressure, LOW otherwise.
# Lathe
signal-port-name-lathe-print = Print last recipe
signal-port-description-lathe-print = Signal port that prints the last set recipe when pulsed.

View File

@ -1,5 +1,5 @@
- type: entity
parent: BaseMachinePowered
parent: [ BaseMachinePowered, BaseAutomatedMachine ] # Goob: Automation
id: FaxMachineBase
name: long range fax machine
description: Bluespace technologies on the application of bureaucracy.

View File

@ -1,6 +1,6 @@
- type: entity
id: MachineFlatpacker
parent: [ BaseMachinePowered, ConstructibleMachine ]
parent: [ BaseMachinePowered, ConstructibleMachine, BaseAutomatedMachine ] # Goob: Automation + silo
name: Flatpacker 1001
description: An industrial machine used for expediting machine construction across the station.
components:

View File

@ -1,6 +1,6 @@
- type: entity
id: BaseLathe
parent: [ BaseMachinePowered, ConstructibleMachine ]
parent: [ BaseMachinePowered, ConstructibleMachine, BaseAutomatedMachine ] # Goob: Automation
abstract: true
name: lathe
components:
@ -55,6 +55,10 @@
- !type:AutomatedMaterialStorage
input: AutomationSlotMaterials
output: null
- !type:AutomatedPorts
sinks:
- LathePrint
- type: LatheAutomation # Goobstation
- type: TechnologyDatabase
supportedDisciplines: # DeltaV - don't add it to every map
- Industrial

View File

@ -1,6 +1,6 @@
- type: entity
id: KitchenMicrowave
parent: [ BaseMachinePowered, SmallConstructibleMachine ]
parent: [ BaseMachinePowered, SmallConstructibleMachine, BaseAutomatedMachine ] # Goob: Automation
name: microwave
description: It's magic.
components:

View File

@ -1,6 +1,6 @@
- type: entity
id: KitchenReagentGrinder
parent: [ BaseMachinePowered, SmallConstructibleMachine ]
parent: [ BaseMachinePowered, SmallConstructibleMachine, BaseAutomatedMachine ] # Goob: Automation
name: reagent grinder
description: From BlenderTech. Will It Blend? Let's find out!
components:

View File

@ -1,7 +1,7 @@
- type: entity
abstract: true
id: DisposalUnitBase
parent: BaseMachinePowered
parent: [ BaseMachinePowered, BaseAutomatedMachine ] # Goob: Automation
description: A pneumatic waste disposal unit.
placement:
mode: SnapgridCenter

View File

@ -1,5 +1,6 @@
- type: entity
id: RadiationCollector
parent: BaseAutomatedMachine # Goob: Automation
name: radiation collector
suffix: Empty tank
description: A machine that collects radiation and turns it into power. Requires plasma gas to function.

View File

@ -1,6 +1,6 @@
- type: entity
abstract: true
parent: [ BaseStructureDynamic, BasePaperLabelableVisualized ]
parent: [ BaseStructureDynamic, BasePaperLabelableVisualized, BaseAutomatedMachine ]
id: GasCanister
name: gas canister
description: A canister that can contain any type of gas. It can be attached to connector ports using a wrench.

View File

@ -59,6 +59,14 @@
- material: Cable
amount: 1
doAfter: 1
# Mono
- to: timer
steps:
- material: Steel
amount: 1
- material: Cable
amount: 2
doAfter: 1
- node: logic_gate
entity: LogicGateOr
edges:
@ -124,3 +132,17 @@
- !type:SpawnPrototype
prototype: PartRodMetal1
amount: 1
# Mono
- node: timer
entity: SignalTimerItem
edges:
- to: empty
steps:
- tool: Prying
doAfter: 2
completed:
- !type:SpawnPrototype
prototype: SheetSteel1
- !type:SpawnPrototype
prototype: CableApcStack1
amount: 2

View File

@ -1133,6 +1133,9 @@
objectType: Structure
placementMode: SnapgridCenter
canBuildInImpassable: false
icon:
sprite: _NF/Structures/conveyor.rsi # Mono
state: conveyor_stopped_cw
conditions:
- !type:TileNotBlocked

View File

@ -77,7 +77,6 @@
- LatheUpgradeKitCryo
# End DeltaV Additions
# Begin Goobstation Additions
- UpgradeKitAutomation
- RoboticArmCircuitboard
- StorageBinCircuitboard
- InteractorCircuitboard

View File

@ -99,3 +99,10 @@
id: DisposalEject
name: signal-port-name-eject
description: signal-port-description-eject
# Lathe
- type: sinkPort
id: LathePrint
name: signal-port-name-lathe-print
description: signal-port-description-lathe-print

View File

@ -4,31 +4,6 @@
#
# SPDX-License-Identifier: AGPL-3.0-or-later
- type: entity
parent: BaseItem
id: UpgradeKitAutomation
name: automation upgrade kit
description: An upgrade kit with all the parts needed to upgrade a machine. This one allows extra automation options by linking robotic arms.
components:
- type: Sprite
sprite: _DV/Objects/Tools/lathe_upgrade_kit.rsi
state: icon
- type: UpgradeKit
whitelist:
components:
- AutomationSlots # automation needs code to support it, can't work on literally any machine
blacklist:
components:
- Automated
- UpgradedMachine
components:
- type: UpgradedMachine
upgrade: upgrade-kit-automation
- type: Automated
- type: DeviceNetwork
deviceNetId: Wireless
receiveFrequencyId: BasicDevice
# for entities that come pre-automated
- type: entity
abstract: true

View File

@ -13,4 +13,3 @@
recipes:
- LatheUpgradeKitHyper
- LatheUpgradeKitCryo
- UpgradeKitAutomation

View File

@ -1,8 +0,0 @@
- type: latheRecipe
parent: BaseUpgradeKit
id: UpgradeKitAutomation
result: UpgradeKitAutomation
materials:
Glass: 500
Steel: 200
Silver: 300

View File

@ -0,0 +1,28 @@
- type: entity
parent: BaseLogicItem
id: SignalTimerItem
name: timer
description: Sends a signal after a specified time after receiving a signal.
components:
- type: Sprite
layers:
- state: base
- sprite: _Mono/Objects/Devices/gates.rsi
state: timer
- type: Construction
node: timer
- type: SignalTimer
canEditLabel: false
- type: DeviceLinkSource
ports:
- Start
- Timer
- type: DeviceLinkSink
ports:
- Trigger
- type: ActivatableUI
key: enum.SignalTimerUiKey.Key
- type: UserInterface
interfaces:
enum.SignalTimerUiKey.Key:
type: SignalTimerBoundUserInterface

View File

@ -0,0 +1,10 @@
- type: construction
name: timer
id: Timer
graph: LogicGate
startNode: start
targetNode: timer
category: construction-category-tools
description: Sends a signal after a specified time after receiving a signal.
icon: { sprite: _Mono/Objects/Devices/gates.rsi, state: timer }
objectType: Item

View File

@ -0,0 +1,14 @@
{
"version": 1,
"license": "CC-BY-SA-3.0",
"copyright": "Sprite timer made by Ilya246 for SS14, modified from Textures/Objects/Devices/gates.rsi.",
"size": {
"x": 32,
"y": 32
},
"states": [
{
"name": "timer"
}
]
}

Binary file not shown.

After

Width:  |  Height:  |  Size: 6.9 KiB