Add space heaters (#25250)

This commit is contained in:
Menshin
2024-02-28 19:27:29 +01:00
committed by GitHub
parent 221719c27d
commit 9884351101
21 changed files with 770 additions and 15 deletions

View File

@@ -0,0 +1,90 @@
using Content.Shared.Atmos.Piping.Portable.Components;
using JetBrains.Annotations;
using Robust.Client.UserInterface.Controls;
namespace Content.Client.Atmos.UI;
/// <summary>
/// Initializes a <see cref="SpaceHeaterWindow"/> and updates it when new server messages are received.
/// </summary>
[UsedImplicitly]
public sealed class SpaceHeaterBoundUserInterface : BoundUserInterface
{
[ViewVariables]
private SpaceHeaterWindow? _window;
public SpaceHeaterBoundUserInterface(EntityUid owner, Enum uiKey) : base(owner, uiKey)
{
}
protected override void Open()
{
base.Open();
_window = new SpaceHeaterWindow();
if (State != null)
UpdateState(State);
_window.OpenCentered();
_window.OnClose += Close;
_window.ToggleStatusButton.OnPressed += _ => OnToggleStatusButtonPressed();
_window.IncreaseTempRange.OnPressed += _ => OnTemperatureRangeChanged(_window.TemperatureChangeDelta);
_window.DecreaseTempRange.OnPressed += _ => OnTemperatureRangeChanged(-_window.TemperatureChangeDelta);
_window.ModeSelector.OnItemSelected += OnModeChanged;
_window.PowerLevelSelector.OnItemSelected += OnPowerLevelChange;
}
private void OnToggleStatusButtonPressed()
{
_window?.SetActive(!_window.Active);
SendMessage(new SpaceHeaterToggleMessage());
}
private void OnTemperatureRangeChanged(float changeAmount)
{
SendMessage(new SpaceHeaterChangeTemperatureMessage(changeAmount));
}
private void OnModeChanged(OptionButton.ItemSelectedEventArgs args)
{
_window?.ModeSelector.SelectId(args.Id);
SendMessage(new SpaceHeaterChangeModeMessage((SpaceHeaterMode)args.Id));
}
private void OnPowerLevelChange(RadioOptionItemSelectedEventArgs<int> args)
{
_window?.PowerLevelSelector.Select(args.Id);
SendMessage(new SpaceHeaterChangePowerLevelMessage((SpaceHeaterPowerLevel)args.Id));
}
/// <summary>
/// Update the UI state based on server-sent info
/// </summary>
/// <param name="state"></param>
protected override void UpdateState(BoundUserInterfaceState state)
{
base.UpdateState(state);
if (_window == null || state is not SpaceHeaterBoundUserInterfaceState cast)
return;
_window.SetActive(cast.Enabled);
_window.ModeSelector.SelectId((int)cast.Mode);
_window.PowerLevelSelector.Select((int)cast.PowerLevel);
_window.MinTemp = cast.MinTemperature;
_window.MaxTemp = cast.MaxTemperature;
_window.SetTemperature(cast.TargetTemperature);
}
protected override void Dispose(bool disposing)
{
base.Dispose(disposing);
if (!disposing)
return;
_window?.Dispose();
}
}

View File

@@ -0,0 +1,34 @@
<DefaultWindow xmlns="https://spacestation14.io"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
MinSize="280 160" Title="Temperature Control Unit">
<BoxContainer Name="VboxContainer" Orientation="Vertical" Margin="5 5 5 5" SeparationOverride="10">
<BoxContainer Orientation="Horizontal" HorizontalExpand="True">
<BoxContainer Orientation="Horizontal" HorizontalExpand="True">
<Button Text="{Loc comp-space-heater-ui-status-disabled}" Access="Public" Name="ToggleStatusButton"/>
</BoxContainer>
<BoxContainer Orientation="Horizontal" SeparationOverride="5">
<Label Text="{Loc comp-space-heater-ui-mode}"/>
<OptionButton Access="Public" Name="ModeSelector"/>
</BoxContainer>
</BoxContainer>
<BoxContainer Orientation="Horizontal" HorizontalExpand="True" SeparationOverride="5">
<BoxContainer Orientation="Horizontal" HorizontalExpand="True">
<Label Text="{Loc comp-space-heater-ui-thermostat}"/>
</BoxContainer>
<BoxContainer Orientation="Horizontal" HorizontalExpand="True" HorizontalAlignment="Right">
<Button Text="{Loc comp-space-heater-ui-decrease-temperature-range}" Access="Public" Name="DecreaseTempRange" StyleClasses="OpenRight"/>
<LineEdit Name ="Thermostat" MinSize="55 0"></LineEdit>
<Button Text="{Loc comp-space-heater-ui-increase-temperature-range}" Access="Public" Name="IncreaseTempRange" StyleClasses="OpenLeft"/>
</BoxContainer>
</BoxContainer>
<BoxContainer Orientation="Horizontal" HorizontalExpand="True" SeparationOverride="5">
<Label Text="{Loc comp-space-heater-ui-power-consumption}"/>
<BoxContainer Name="PowerLevelSelectorHBox" Access="Public" SeparationOverride="2"/>
</BoxContainer>
</BoxContainer>
</DefaultWindow>

View File

@@ -0,0 +1,73 @@
using Robust.Client.AutoGenerated;
using Robust.Client.UserInterface.Controls;
using Robust.Client.UserInterface.CustomControls;
using Robust.Client.UserInterface.XAML;
using Content.Shared.Atmos;
using Content.Shared.Atmos.Piping.Portable.Components;
namespace Content.Client.Atmos.UI;
/// <summary>
/// Client-side UI used to control a space heater.
/// </summary>
[GenerateTypedNameReferences]
public sealed partial class SpaceHeaterWindow : DefaultWindow
{
// To account for a minimum delta temperature for atmos equalization to trigger we use a fixed step for target temperature increment/decrement
public int TemperatureChangeDelta = 5;
public bool Active;
// Temperatures range bounds in Kelvin (K)
public float MinTemp;
public float MaxTemp;
public RadioOptions<int> PowerLevelSelector;
public SpaceHeaterWindow()
{
RobustXamlLoader.Load(this);
// Add the Mode selector list
foreach (var value in Enum.GetValues<SpaceHeaterMode>())
{
ModeSelector.AddItem(Loc.GetString($"comp-space-heater-mode-{value}"), (int)value);
}
// Add the Power level radio buttons
PowerLevelSelectorHBox.AddChild(PowerLevelSelector = new RadioOptions<int>(RadioOptionsLayout.Horizontal));
PowerLevelSelector.FirstButtonStyle = "OpenRight";
PowerLevelSelector.LastButtonStyle = "OpenLeft";
PowerLevelSelector.ButtonStyle = "OpenBoth";
foreach (var value in Enum.GetValues<SpaceHeaterPowerLevel>())
{
PowerLevelSelector.AddItem(Loc.GetString($"comp-space-heater-ui-{value}-power-consumption"), (int)value);
}
// Only allow temperature increment/decrement of TemperatureChangeDelta
Thermostat.Editable = false;
}
public void SetActive(bool active)
{
Active = active;
ToggleStatusButton.Pressed = active;
if (active)
{
ToggleStatusButton.Text = Loc.GetString("comp-space-heater-ui-status-enabled");
}
else
{
ToggleStatusButton.Text = Loc.GetString("comp-space-heater-ui-status-disabled");
}
}
public void SetTemperature(float targetTemperature)
{
Thermostat.SetText($"{targetTemperature - Atmospherics.T0C} °C");
IncreaseTempRange.Disabled = targetTemperature + TemperatureChangeDelta > MaxTemp;
DecreaseTempRange.Disabled = targetTemperature - TemperatureChangeDelta < MinTemp;
}
}

View File

@@ -55,6 +55,7 @@ namespace Content.Server.Atmos.Piping.Unary.EntitySystems
private void OnThermoMachineUpdated(EntityUid uid, GasThermoMachineComponent thermoMachine, ref AtmosDeviceUpdateEvent args)
{
thermoMachine.LastEnergyDelta = 0f;
if (!(_power.IsPowered(uid) && TryComp<ApcPowerReceiverComponent>(uid, out var receiver)))
return;
@@ -100,12 +101,14 @@ namespace Content.Server.Atmos.Piping.Unary.EntitySystems
if (thermoMachine.Atmospheric)
{
_atmosphereSystem.AddHeat(heatExchangeGasMixture, dQActual);
thermoMachine.LastEnergyDelta = dQActual;
}
else
{
float dQLeak = dQActual * thermoMachine.EnergyLeakPercentage;
float dQPipe = dQActual - dQLeak;
_atmosphereSystem.AddHeat(heatExchangeGasMixture, dQPipe);
thermoMachine.LastEnergyDelta = dQPipe;
if (dQLeak != 0f && _atmosphereSystem.GetContainingMixture(uid) is { } containingMixture)
_atmosphereSystem.AddHeat(containingMixture, dQLeak);

View File

@@ -0,0 +1,58 @@
using Content.Shared.Atmos;
using Content.Shared.Atmos.Piping.Portable.Components;
using Content.Shared.Atmos.Visuals;
namespace Content.Server.Atmos.Portable;
[RegisterComponent]
public sealed partial class SpaceHeaterComponent : Component
{
/// <summary>
/// Current mode the space heater is in. Possible values : Auto, Heat and Cool
/// </summary>
[DataField, ViewVariables(VVAccess.ReadWrite)]
public SpaceHeaterMode Mode = SpaceHeaterMode.Auto;
/// <summary>
/// The power level the space heater is currently set to. Possible values : Low, Medium, High
/// </summary>
[DataField, ViewVariables(VVAccess.ReadWrite)]
public SpaceHeaterPowerLevel PowerLevel = SpaceHeaterPowerLevel.Medium;
/// <summary>
/// Maximum target temperature the device can be set to
/// </summary>
[DataField, ViewVariables(VVAccess.ReadWrite)]
public float MaxTemperature = Atmospherics.T20C + 20;
/// <summary>
/// Minimal target temperature the device can be set to
/// </summary>
[DataField, ViewVariables(VVAccess.ReadWrite)]
public float MinTemperature = Atmospherics.T0C - 10;
/// <summary>
/// Coefficient of performance. Output power / input power.
/// Positive for heaters, negative for freezers.
/// </summary>
[DataField("heatingCoefficientOfPerformance")]
[ViewVariables(VVAccess.ReadWrite)]
public float HeatingCp = 1f;
[DataField("coolingCoefficientOfPerformance")]
[ViewVariables(VVAccess.ReadWrite)]
public float CoolingCp = -0.9f;
/// <summary>
/// The delta from the target temperature after which the space heater switch mode while in Auto. Value should account for the thermomachine temperature tolerance.
/// </summary>
[DataField]
[ViewVariables(VVAccess.ReadWrite)]
public float AutoModeSwitchThreshold = 0.8f;
/// <summary>
/// Current electrical power consumption, in watts, of the space heater at medium power level. Passed to the thermomachine component.
/// </summary>
[DataField, ViewVariables(VVAccess.ReadWrite)]
public float PowerConsumption = 3500f;
}

View File

@@ -0,0 +1,191 @@
using Content.Server.Atmos.EntitySystems;
using Content.Server.Atmos.Piping.Components;
using Content.Server.Atmos.Piping.Unary.Components;
using Content.Server.Popups;
using Content.Server.Power.Components;
using Content.Server.Power.EntitySystems;
using Content.Shared.Atmos.Piping.Portable.Components;
using Content.Shared.Atmos.Visuals;
using Content.Shared.UserInterface;
using Robust.Server.GameObjects;
namespace Content.Server.Atmos.Portable;
public sealed class SpaceHeaterSystem : EntitySystem
{
[Dependency] private readonly AtmosphereSystem _atmosphereSystem = default!;
[Dependency] private readonly PopupSystem _popup = default!;
[Dependency] private readonly PowerReceiverSystem _power = default!;
[Dependency] private readonly SharedAppearanceSystem _appearance = default!;
[Dependency] private readonly UserInterfaceSystem _userInterfaceSystem = default!;
public override void Initialize()
{
base.Initialize();
SubscribeLocalEvent<SpaceHeaterComponent, ActivatableUIOpenAttemptEvent>(OnUIActivationAttempt);
SubscribeLocalEvent<SpaceHeaterComponent, BeforeActivatableUIOpenEvent>(OnBeforeOpened);
SubscribeLocalEvent<SpaceHeaterComponent, AtmosDeviceUpdateEvent>(OnDeviceUpdated);
SubscribeLocalEvent<SpaceHeaterComponent, MapInitEvent>(OnInit);
SubscribeLocalEvent<SpaceHeaterComponent, PowerChangedEvent>(OnPowerChanged);
SubscribeLocalEvent<SpaceHeaterComponent, SpaceHeaterChangeModeMessage>(OnModeChanged);
SubscribeLocalEvent<SpaceHeaterComponent, SpaceHeaterChangePowerLevelMessage>(OnPowerLevelChanged);
SubscribeLocalEvent<SpaceHeaterComponent, SpaceHeaterChangeTemperatureMessage>(OnTemperatureChanged);
SubscribeLocalEvent<SpaceHeaterComponent, SpaceHeaterToggleMessage>(OnToggle);
}
private void OnInit(EntityUid uid, SpaceHeaterComponent spaceHeater, MapInitEvent args)
{
if (!TryComp<GasThermoMachineComponent>(uid, out var thermoMachine))
return;
thermoMachine.Cp = spaceHeater.HeatingCp;
thermoMachine.HeatCapacity = spaceHeater.PowerConsumption;
}
private void OnBeforeOpened(EntityUid uid, SpaceHeaterComponent spaceHeater, BeforeActivatableUIOpenEvent args)
{
DirtyUI(uid, spaceHeater);
}
private void OnUIActivationAttempt(EntityUid uid, SpaceHeaterComponent spaceHeater, ActivatableUIOpenAttemptEvent args)
{
if (!Comp<TransformComponent>(uid).Anchored)
{
_popup.PopupEntity(Loc.GetString("comp-space-heater-unanchored", ("device", Loc.GetString("comp-space-heater-device-name"))), uid, args.User);
args.Cancel();
}
}
private void OnDeviceUpdated(EntityUid uid, SpaceHeaterComponent spaceHeater, ref AtmosDeviceUpdateEvent args)
{
if (!_power.IsPowered(uid)
|| !TryComp<GasThermoMachineComponent>(uid, out var thermoMachine))
{
return;
}
UpdateAppearance(uid);
// If in automatic temperature mode, check if we need to adjust the heat exchange direction
if (spaceHeater.Mode == SpaceHeaterMode.Auto)
{
var environment = _atmosphereSystem.GetContainingMixture(uid);
if (environment == null)
return;
if (environment.Temperature <= thermoMachine.TargetTemperature - (thermoMachine.TemperatureTolerance + spaceHeater.AutoModeSwitchThreshold))
{
thermoMachine.Cp = spaceHeater.HeatingCp;
}
else if (environment.Temperature >= thermoMachine.TargetTemperature + (thermoMachine.TemperatureTolerance + spaceHeater.AutoModeSwitchThreshold))
{
thermoMachine.Cp = spaceHeater.CoolingCp;
}
}
}
private void OnPowerChanged(EntityUid uid, SpaceHeaterComponent spaceHeater, ref PowerChangedEvent args)
{
UpdateAppearance(uid);
DirtyUI(uid, spaceHeater);
}
private void OnToggle(EntityUid uid, SpaceHeaterComponent spaceHeater, SpaceHeaterToggleMessage args)
{
ApcPowerReceiverComponent? powerReceiver = null;
if (!Resolve(uid, ref powerReceiver))
return;
_power.TogglePower(uid);
UpdateAppearance(uid);
DirtyUI(uid, spaceHeater);
}
private void OnTemperatureChanged(EntityUid uid, SpaceHeaterComponent spaceHeater, SpaceHeaterChangeTemperatureMessage args)
{
if (!TryComp<GasThermoMachineComponent>(uid, out var thermoMachine))
return;
thermoMachine.TargetTemperature += args.Temperature;
UpdateAppearance(uid);
DirtyUI(uid, spaceHeater);
}
private void OnModeChanged(EntityUid uid, SpaceHeaterComponent spaceHeater, SpaceHeaterChangeModeMessage args)
{
if (!TryComp<GasThermoMachineComponent>(uid, out var thermoMachine))
return;
spaceHeater.Mode = args.Mode;
if (spaceHeater.Mode == SpaceHeaterMode.Heat)
thermoMachine.Cp = spaceHeater.HeatingCp;
else if (spaceHeater.Mode == SpaceHeaterMode.Cool)
thermoMachine.Cp = spaceHeater.CoolingCp;
DirtyUI(uid, spaceHeater);
}
private void OnPowerLevelChanged(EntityUid uid, SpaceHeaterComponent spaceHeater, SpaceHeaterChangePowerLevelMessage args)
{
if (!TryComp<GasThermoMachineComponent>(uid, out var thermoMachine))
return;
spaceHeater.PowerLevel = args.PowerLevel;
switch (spaceHeater.PowerLevel)
{
case SpaceHeaterPowerLevel.Low:
thermoMachine.HeatCapacity = spaceHeater.PowerConsumption / 2;
break;
case SpaceHeaterPowerLevel.Medium:
thermoMachine.HeatCapacity = spaceHeater.PowerConsumption;
break;
case SpaceHeaterPowerLevel.High:
thermoMachine.HeatCapacity = spaceHeater.PowerConsumption * 2;
break;
}
DirtyUI(uid, spaceHeater);
}
private void DirtyUI(EntityUid uid, SpaceHeaterComponent? spaceHeater)
{
if (!Resolve(uid, ref spaceHeater)
|| !TryComp<GasThermoMachineComponent>(uid, out var thermoMachine)
|| !TryComp<ApcPowerReceiverComponent>(uid, out var powerReceiver))
{
return;
}
_userInterfaceSystem.TrySetUiState(uid, SpaceHeaterUiKey.Key,
new SpaceHeaterBoundUserInterfaceState(spaceHeater.MinTemperature, spaceHeater.MaxTemperature, thermoMachine.TargetTemperature, !powerReceiver.PowerDisabled, spaceHeater.Mode, spaceHeater.PowerLevel));
}
private void UpdateAppearance(EntityUid uid)
{
if (!_power.IsPowered(uid) || !TryComp<GasThermoMachineComponent>(uid, out var thermoMachine))
{
_appearance.SetData(uid, SpaceHeaterVisuals.State, SpaceHeaterState.Off);
return;
}
if (thermoMachine.LastEnergyDelta > 0)
{
_appearance.SetData(uid, SpaceHeaterVisuals.State, SpaceHeaterState.Heating);
}
else if (thermoMachine.LastEnergyDelta < 0)
{
_appearance.SetData(uid, SpaceHeaterVisuals.State, SpaceHeaterState.Cooling);
}
else
{
_appearance.SetData(uid, SpaceHeaterVisuals.State, SpaceHeaterState.StandBy);
}
}
}

View File

@@ -0,0 +1,90 @@
using Robust.Shared.Serialization;
namespace Content.Shared.Atmos.Piping.Portable.Components;
[Serializable]
[NetSerializable]
public enum SpaceHeaterUiKey
{
Key
}
[Serializable]
[NetSerializable]
public sealed class SpaceHeaterToggleMessage : BoundUserInterfaceMessage
{
}
[Serializable]
[NetSerializable]
public sealed class SpaceHeaterChangeTemperatureMessage : BoundUserInterfaceMessage
{
public float Temperature { get; }
public SpaceHeaterChangeTemperatureMessage(float temperature)
{
Temperature = temperature;
}
}
[Serializable]
[NetSerializable]
public sealed class SpaceHeaterChangePowerLevelMessage : BoundUserInterfaceMessage
{
public SpaceHeaterPowerLevel PowerLevel { get; }
public SpaceHeaterChangePowerLevelMessage(SpaceHeaterPowerLevel powerLevel)
{
PowerLevel = powerLevel;
}
}
[Serializable]
[NetSerializable]
public sealed class SpaceHeaterChangeModeMessage : BoundUserInterfaceMessage
{
public SpaceHeaterMode Mode { get; }
public SpaceHeaterChangeModeMessage(SpaceHeaterMode mode)
{
Mode = mode;
}
}
[Serializable]
[NetSerializable]
public sealed class SpaceHeaterBoundUserInterfaceState : BoundUserInterfaceState
{
public float MinTemperature { get; }
public float MaxTemperature { get; }
public float TargetTemperature { get; }
public bool Enabled { get; }
public SpaceHeaterMode Mode { get; }
public SpaceHeaterPowerLevel PowerLevel { get; }
public SpaceHeaterBoundUserInterfaceState(float minTemperature, float maxTemperature, float temperature, bool enabled, SpaceHeaterMode mode, SpaceHeaterPowerLevel powerLevel)
{
MinTemperature = minTemperature;
MaxTemperature = maxTemperature;
TargetTemperature = temperature;
Enabled = enabled;
Mode = mode;
PowerLevel = powerLevel;
}
}
[Serializable, NetSerializable]
public enum SpaceHeaterMode : byte
{
Auto,
Heat,
Cool
}
[Serializable, NetSerializable]
public enum SpaceHeaterPowerLevel : byte
{
Low,
Medium,
High
}

View File

@@ -0,0 +1,27 @@
using Robust.Shared.Serialization;
namespace Content.Shared.Atmos.Visuals;
/// <summary>
/// Used for the visualizer
/// </summary>
[Serializable, NetSerializable]
public enum SpaceHeaterVisualLayers : byte
{
Main
}
[Serializable, NetSerializable]
public enum SpaceHeaterVisuals : byte
{
State,
}
[Serializable, NetSerializable]
public enum SpaceHeaterState : byte
{
Off,
StandBy,
Heating,
Cooling,
}

View File

@@ -0,0 +1,18 @@
comp-space-heater-ui-thermostat = Thermostat:
comp-space-heater-ui-mode = Mode
comp-space-heater-ui-status-disabled = Off
comp-space-heater-ui-status-enabled = On
comp-space-heater-ui-increase-temperature-range = +
comp-space-heater-ui-decrease-temperature-range = -
comp-space-heater-mode-Auto = Auto
comp-space-heater-mode-Heat = Heat
comp-space-heater-mode-Cool = Cool
comp-space-heater-ui-power-consumption = Power level:
comp-space-heater-ui-Low-power-consumption = Low
comp-space-heater-ui-Medium-power-consumption = Medium
comp-space-heater-ui-High-power-consumption = High
comp-space-heater-device-name = space heater
comp-space-heater-unanchored = The {$device} is not anchored.

View File

@@ -38,6 +38,7 @@ wires-board-name-windoor = Windoor Control
wires-board-name-mech = Mech
wires-board-name-fatextractor = FatExtractor
wires-board-name-flatpacker = Flatpacker
wires-board-name-spaceheater = Space Heater
# names that get displayed in the wire hacking hud & admin logs.

View File

@@ -451,7 +451,7 @@
id: PortableScrubberMachineCircuitBoard
parent: BaseMachineCircuitboard
name: portable scrubber machine board
description: A PCB for a portable scrubber.
description: A machine printed circuit board for a portable scrubber.
components:
- type: Sprite
state: engineering
@@ -464,6 +464,22 @@
Cable: 5
Glass: 2
- type: entity
id: SpaceHeaterMachineCircuitBoard
parent: BaseMachineCircuitboard
name: space heater machine board
description: A machine printed circuit board for a space heater.
components:
- type: Sprite
state: engineering
- type: MachineBoard
prototype: SpaceHeater
requirements:
MatterBin: 1
Capacitor: 2
materialRequirements:
Cable: 5
- type: entity
id: CloningPodMachineCircuitboard
parent: BaseMachineCircuitboard

View File

@@ -390,6 +390,7 @@
- ThermomachineFreezerMachineCircuitBoard
- HellfireFreezerMachineCircuitBoard
- PortableScrubberMachineCircuitBoard
- SpaceHeaterMachineCircuitBoard
- CloningPodMachineCircuitboard
- MedicalScannerMachineCircuitboard
- CryoPodMachineCircuitboard

View File

@@ -1,15 +1,15 @@
- type: entity
id: PortableScrubber
parent: BaseStructureDynamic
parent: [BaseMachinePowered, ConstructibleMachine]
name: portable scrubber
description: It scrubs, portably!
components:
- type: Transform
noRot: true
- type: InteractionOutline
anchored: false
- type: Physics
bodyType: Dynamic
canCollide: false
- type: AtmosDevice
joinSystem: true
- type: Fixtures
fixtures:
fix1:
@@ -34,9 +34,6 @@
shader: unshaded
visible: false
map: ["enum.PortableScrubberVisualLayers.IsDraining"]
- type: Pullable
- type: AtmosDevice
joinSystem: true
- type: PortableScrubber
gasMixture:
volume: 1250
@@ -49,7 +46,6 @@
volume: 1
- type: ApcPowerReceiver
powerLoad: 2000
- type: ExtensionCableReceiver
- type: Appearance
- type: GenericVisualizer
visuals:
@@ -94,13 +90,109 @@
min: 1
max: 3
SheetGlass1:
min: 1
max: 2
- !type:DoActsBehavior
acts: [ "Destruction" ]
- type: entity
id: SpaceHeater
parent: [BaseMachinePowered, ConstructibleMachine]
name: space heater
description: A bluespace technology device that alters local temperature. Commonly referred to as a "Space Heater".
suffix: Unanchored
components:
- type: Transform
anchored: false
- type: Physics
bodyType: Dynamic
- type: AtmosDevice
joinSystem: true
- type: Fixtures
fixtures:
fix1:
shape:
!type:PhysShapeAabb
bounds: "-0.15,-0.35,0.15,0.5"
density: 100
mask:
- MachineMask
layer:
- MachineLayer
- type: ApcPowerReceiver
powerDisabled: true #starts off
- type: Sprite
sprite: Structures/Piping/Atmospherics/Portable/portable_sheater.rsi
noRot: true
layers:
- state: sheaterOff
map: ["enum.SpaceHeaterVisualLayers.Main"]
- state: sheaterPanelOpen
map: [ "enum.WiresVisualLayers.MaintenancePanel" ]
- type: Appearance
- type: GenericVisualizer
visuals:
enum.SpaceHeaterVisuals.State:
SpaceHeaterVisualLayers.Main:
Off: { state: sheaterOff }
StandBy: { state: sheaterStandby }
Heating: { state: sheaterHeat }
Cooling: { state: sheaterCool }
- type: Machine
board: SpaceHeaterMachineCircuitBoard
- type: WiresPanel
- type: WiresVisuals
- type: UserInterface
interfaces:
- key: enum.SpaceHeaterUiKey.Key
type: SpaceHeaterBoundUserInterface
- type: ActivatableUI
inHandsOnly: false
key: enum.SpaceHeaterUiKey.Key
- type: SpaceHeater
- type: GasThermoMachine
temperatureTolerance: 0.2
atmospheric: true
- type: Damageable
damageContainer: Inorganic
damageModifierSet: Metallic
- type: Destructible
thresholds:
- trigger:
!type:DamageTrigger
damage: 600
behaviors:
- !type:DoActsBehavior
acts: [ "Destruction" ]
- trigger:
!type:DamageTrigger
damage: 300
behaviors:
- !type:PlaySoundBehavior
sound:
collection: MetalBreak
- !type:SpawnEntitiesBehavior
spawn:
SheetSteel1:
min: 1
max: 3
- !type:DoActsBehavior
acts: [ "Destruction" ]
- type: CollideOnAnchor
enable: true
- type: ContainerContainer
containers:
machine_board: !type:Container
machine_parts: !type:Container
- type: entity
parent: SpaceHeater
id: SpaceHeaterAnchored
suffix: Anchored
components:
- type: Transform
anchored: true
- type: Physics
bodyType: Static
- type: entity
parent: SpaceHeaterAnchored
id: SpaceHeaterEnabled
suffix: Anchored, Enabled
components:
- type: ApcPowerReceiver
powerDisabled: false

View File

@@ -156,6 +156,16 @@
Glass: 900
Gold: 50
- type: latheRecipe
id: SpaceHeaterMachineCircuitBoard
result: SpaceHeaterMachineCircuitBoard
category: Circuitry
completetime: 4
materials:
Steel: 150
Glass: 900
Gold: 50
- type: latheRecipe
id: MedicalScannerMachineCircuitboard
result: MedicalScannerMachineCircuitboard

View File

@@ -160,6 +160,7 @@
recipeUnlocks:
- HellfireFreezerMachineCircuitBoard
- PortableScrubberMachineCircuitBoard
- SpaceHeaterMachineCircuitBoard
- HolofanProjector
- type: technology

View File

@@ -0,0 +1,50 @@
{
"version": 1,
"license": "CC-BY-SA-3.0",
"copyright": "Taken from tgstation at https://github.com/tgstation/tgstation/commit/ead7e05f990de05b7f5f93d39f9671498cb0aa01",
"size": {
"x": 32,
"y": 32
},
"states": [
{
"name": "sheaterOff"
},
{
"name": "sheaterCool",
"delays": [
[
0.2,
0.2,
0.2,
0.2,
0.2,
0.2,
0.2,
0.2
]
]
},
{
"name": "sheaterHeat",
"delays": [
[
0.2,
0.2,
0.2,
0.2,
0.2,
0.2,
0.2,
0.2
]
]
},
{
"name": "sheaterPanelOpen"
},
{
"name": "sheaterStandby"
}
]
}

Binary file not shown.

After

Width:  |  Height:  |  Size: 994 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 911 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 5.9 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 187 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 338 B