* refactor and add Department to PaintableAirlock, move it to server dir since its in namespace
* add departments to doors, cleanup
* add style -> departments mapping
* AirlockDepartmentsPrototype
* update shared spray stuff to have department
* name file the same as the class name
* department optional
* refactor spray painter system + send department
* fixy
* client
* no need to rewrite ActivateableUi
* pro ops
* the reckoning
* hiss
* .
* :trollface:
* add standard atmos colors to palette
* Update Content.Shared/SprayPainter/SharedSprayPainterSystem.cs
---------
Co-authored-by: deltanedas <@deltanedas:kde.org>
Co-authored-by: metalgearsloth <31366439+metalgearsloth@users.noreply.github.com>
public List<SprayPainterEntry> Entries { get; private set; } = new();
- public override void Initialize()
+ protected override void CacheStyles()
{
- base.Initialize();
+ base.CacheStyles();
- foreach (string style in Styles)
+ Entries.Clear();
+ foreach (var style in Styles)
{
+ var name = style.Name;
string? iconPath = Groups
- .FindAll(x => x.StylePaths.ContainsKey(style))?
- .MaxBy(x => x.IconPriority)?.StylePaths[style];
+ .FindAll(x => x.StylePaths.ContainsKey(name))?
+ .MaxBy(x => x.IconPriority)?.StylePaths[name];
if (iconPath == null)
{
- Entries.Add(new SprayPainterEntry(style, null));
+ Entries.Add(new SprayPainterEntry(name, null));
continue;
}
RSIResource doorRsi = _resourceCache.GetResource<RSIResource>(SpriteSpecifierSerializer.TextureRoot / new ResPath(iconPath));
if (!doorRsi.RSI.TryGetState("closed", out var icon))
{
- Entries.Add(new SprayPainterEntry(style, null));
+ Entries.Add(new SprayPainterEntry(name, null));
continue;
}
- Entries.Add(new SprayPainterEntry(style, icon.Frame0));
+ Entries.Add(new SprayPainterEntry(name, icon.Frame0));
}
}
}
using Content.Shared.SprayPainter;
+using Content.Shared.SprayPainter.Components;
using Robust.Client.GameObjects;
using Robust.Client.UserInterface.Controls;
{
base.Open();
+ if (!EntMan.TryGetComponent<SprayPainterComponent>(Owner, out var comp))
+ return;
+
_window = new SprayPainterWindow();
_painter = EntMan.System<SprayPainterSystem>();
- _window.OpenCentered();
_window.OnClose += Close;
_window.OnSpritePicked = OnSpritePicked;
_window.OnColorPicked = OnColorPicked;
+
+ _window.Populate(_painter.Entries, comp.Index, comp.PickedColor, comp.ColorPalette);
+
+ _window.OpenCentered();
}
protected override void Dispose(bool disposing)
_window?.Dispose();
}
- protected override void UpdateState(BoundUserInterfaceState state)
- {
- base.UpdateState(state);
-
- if (_window == null)
- return;
-
- if (_painter == null)
- return;
-
- if (state is not SprayPainterBoundUserInterfaceState stateCast)
- return;
-
- _window.Populate(_painter.Entries,
- stateCast.SelectedStyle,
- stateCast.SelectedColorKey,
- stateCast.Palette);
- }
-
private void OnSpritePicked(ItemList.ItemListSelectedEventArgs args)
{
SendMessage(new SprayPainterSpritePickedMessage(args.ItemIndex));
-using System.Linq;
-using Content.Server.Administration.Logs;
using Content.Server.Atmos.Piping.Components;
using Content.Server.Atmos.Piping.EntitySystems;
-using Content.Server.Popups;
-using Content.Shared.Database;
using Content.Shared.DoAfter;
-using Content.Shared.Doors.Components;
+using Content.Shared.Interaction;
using Content.Shared.SprayPainter;
using Content.Shared.SprayPainter.Components;
-using Content.Shared.SprayPainter.Prototypes;
-using Content.Shared.Interaction;
-using JetBrains.Annotations;
-using Robust.Server.GameObjects;
-using Robust.Shared.Audio;
-using Robust.Shared.Audio.Systems;
-using Robust.Shared.Player;
namespace Content.Server.SprayPainter;
/// <summary>
-/// A system for painting airlocks and pipes using enginner painter
+/// Handles spraying pipes using a spray painter.
+/// Airlocks are handled in shared.
/// </summary>
-[UsedImplicitly]
public sealed class SprayPainterSystem : SharedSprayPainterSystem
{
- [Dependency] private readonly IAdminLogManager _adminLogger = default!;
- [Dependency] private readonly UserInterfaceSystem _userInterfaceSystem = default!;
- [Dependency] private readonly SharedDoAfterSystem _doAfterSystem = default!;
- [Dependency] private readonly PopupSystem _popupSystem = default!;
- [Dependency] private readonly SharedAudioSystem _audio = default!;
- [Dependency] private readonly SharedAppearanceSystem _appearance = default!;
- [Dependency] private readonly AtmosPipeColorSystem _pipeColorSystem = default!;
+ [Dependency] private readonly AtmosPipeColorSystem _pipeColor = default!;
public override void Initialize()
{
base.Initialize();
- SubscribeLocalEvent<SprayPainterComponent, ComponentInit>(OnInit);
- SubscribeLocalEvent<SprayPainterComponent, AfterInteractEvent>(AfterInteractOn);
- SubscribeLocalEvent<SprayPainterComponent, ActivateInWorldEvent>(OnActivate);
- SubscribeLocalEvent<SprayPainterComponent, SprayPainterSpritePickedMessage>(OnSpritePicked);
- SubscribeLocalEvent<SprayPainterComponent, SprayPainterColorPickedMessage>(OnColorPicked);
- SubscribeLocalEvent<SprayPainterComponent, SprayPainterDoAfterEvent>(OnDoAfter);
- }
-
- private void OnInit(EntityUid uid, SprayPainterComponent component, ComponentInit args)
- {
- if (component.ColorPalette.Count == 0)
- return;
+ SubscribeLocalEvent<SprayPainterComponent, SprayPainterPipeDoAfterEvent>(OnPipeDoAfter);
- SetColor(uid, component, component.ColorPalette.First().Key);
+ SubscribeLocalEvent<AtmosPipeColorComponent, InteractUsingEvent>(OnPipeInteract);
}
- private void OnDoAfter(EntityUid uid, SprayPainterComponent component, SprayPainterDoAfterEvent args)
+ private void OnPipeDoAfter(Entity<SprayPainterComponent> ent, ref SprayPainterPipeDoAfterEvent args)
{
- component.IsSpraying = false;
-
if (args.Handled || args.Cancelled)
return;
- if (args.Args.Target == null)
+ if (args.Args.Target is not {} target)
return;
- EntityUid target = (EntityUid) args.Args.Target;
-
- _audio.PlayPvs(component.SpraySound, uid);
-
- if (TryComp<AtmosPipeColorComponent>(target, out var atmosPipeColorComp))
- {
- _pipeColorSystem.SetColor(target, atmosPipeColorComp, args.Color ?? Color.White);
- } else { // Target is an airlock
- if (args.Sprite != null)
- {
- _appearance.SetData(target, DoorVisuals.BaseRSI, args.Sprite);
- _adminLogger.Add(LogType.Action, LogImpact.Low, $"{ToPrettyString(args.Args.User):user} painted {ToPrettyString(args.Args.Target.Value):target}");
- }
- }
-
- args.Handled = true;
- }
-
- private void OnActivate(EntityUid uid, SprayPainterComponent component, ActivateInWorldEvent args)
- {
- if (!EntityManager.TryGetComponent(args.User, out ActorComponent? actor))
+ if (!TryComp<AtmosPipeColorComponent>(target, out var color))
return;
- DirtyUI(uid, component);
- _userInterfaceSystem.TryOpen(uid, SprayPainterUiKey.Key, actor.PlayerSession);
- args.Handled = true;
- }
-
- private void AfterInteractOn(EntityUid uid, SprayPainterComponent component, AfterInteractEvent args)
- {
- if (component.IsSpraying || args.Target is not { Valid: true } target || !args.CanReach)
- return;
+ Audio.PlayPvs(ent.Comp.SpraySound, ent);
- if (EntityManager.TryGetComponent<PaintableAirlockComponent>(target, out var airlock))
- {
- if (!_prototypeManager.TryIndex<AirlockGroupPrototype>(airlock.Group, out var grp))
- {
- Log.Error("Group not defined: %s", airlock.Group);
- return;
- }
-
- string style = Styles[component.Index];
- if (!grp.StylePaths.TryGetValue(style, out var sprite))
- {
- string msg = Loc.GetString("spray-painter-style-not-available");
- _popupSystem.PopupEntity(msg, args.User, args.User);
- return;
- }
- component.IsSpraying = true;
-
- var doAfterEventArgs = new DoAfterArgs(EntityManager, args.User, component.AirlockSprayTime, new SprayPainterDoAfterEvent(sprite, null), uid, target: target, used: uid)
- {
- BreakOnTargetMove = true,
- BreakOnUserMove = true,
- BreakOnDamage = true,
- NeedHand = true,
- };
- _doAfterSystem.TryStartDoAfter(doAfterEventArgs);
-
- // Log attempt
- _adminLogger.Add(LogType.Action, LogImpact.Low, $"{ToPrettyString(args.User):user} is painting {ToPrettyString(uid):target} to '{style}' at {Transform(uid).Coordinates:targetlocation}");
- } else { // Painting pipes
- if(component.PickedColor is null)
- return;
-
- if (!EntityManager.HasComponent<AtmosPipeColorComponent>(target))
- return;
-
- if(!component.ColorPalette.TryGetValue(component.PickedColor, out var color))
- return;
-
- var doAfterEventArgs = new DoAfterArgs(EntityManager, args.User, component.PipeSprayTime, new SprayPainterDoAfterEvent(null, color), uid, target, uid)
- {
- BreakOnTargetMove = true,
- BreakOnUserMove = true,
- BreakOnDamage = true,
- CancelDuplicate = true,
- DuplicateCondition = DuplicateConditions.SameTarget,
- NeedHand = true,
- };
-
- _doAfterSystem.TryStartDoAfter(doAfterEventArgs);
- }
- }
+ _pipeColor.SetColor(target, color, args.Color);
- private void OnColorPicked(EntityUid uid, SprayPainterComponent component, SprayPainterColorPickedMessage args)
- {
- SetColor(uid, component, args.Key);
- }
-
- private void OnSpritePicked(EntityUid uid, SprayPainterComponent component, SprayPainterSpritePickedMessage args)
- {
- component.Index = args.Index;
- DirtyUI(uid, component);
+ args.Handled = true;
}
- private void SetColor(EntityUid uid, SprayPainterComponent component, string? paletteKey)
+ private void OnPipeInteract(Entity<AtmosPipeColorComponent> ent, ref InteractUsingEvent args)
{
- if (paletteKey == null)
+ if (args.Handled)
return;
- if (!component.ColorPalette.ContainsKey(paletteKey) || paletteKey == component.PickedColor)
+ if (!TryComp<SprayPainterComponent>(args.Used, out var painter) || painter.PickedColor is not {} colorName)
return;
- component.PickedColor = paletteKey;
- DirtyUI(uid, component);
- }
-
- private void DirtyUI(EntityUid uid, SprayPainterComponent? component = null)
- {
- if (!Resolve(uid, ref component))
+ if (!painter.ColorPalette.TryGetValue(colorName, out var color))
return;
- _userInterfaceSystem.TrySetUiState(
- uid,
- SprayPainterUiKey.Key,
- new SprayPainterBoundUserInterfaceState(
- component.Index,
- component.PickedColor,
- component.ColorPalette));
+ var doAfterEventArgs = new DoAfterArgs(EntityManager, args.User, painter.PipeSprayTime, new SprayPainterPipeDoAfterEvent(color), args.Used, target: ent, used: args.Used)
+ {
+ BreakOnTargetMove = true,
+ BreakOnUserMove = true,
+ BreakOnDamage = true,
+ CancelDuplicate = true,
+ // multiple pipes can be sprayed at once just not the same one
+ DuplicateCondition = DuplicateConditions.SameTarget,
+ NeedHand = true
+ };
+
+ args.Handled = DoAfter.TryStartDoAfter(doAfterEventArgs);
}
}
+using Content.Shared.Roles;
using Content.Shared.SprayPainter.Prototypes;
-using Robust.Shared.Serialization.TypeSerializers.Implementations.Custom.Prototype;
+using Robust.Shared.GameStates;
+using Robust.Shared.Prototypes;
-namespace Content.Server.SprayPainter;
+namespace Content.Shared.SprayPainter.Components;
-[RegisterComponent]
+[RegisterComponent, NetworkedComponent, AutoGenerateComponentState]
public sealed partial class PaintableAirlockComponent : Component
{
- [DataField("group", customTypeSerializer:typeof(PrototypeIdSerializer<AirlockGroupPrototype>))]
- public string Group = default!;
+ /// <summary>
+ /// Group of styles this airlock can be painted with, e.g. glass, standard or external.
+ /// </summary>
+ [DataField(required: true), AutoNetworkedField]
+ public ProtoId<AirlockGroupPrototype> Group = string.Empty;
+
+ /// <summary>
+ /// Department this airlock is painted as, or none.
+ /// Must be specified in prototypes for turf war to work.
+ /// To better catch any mistakes, you need to explicitly state a non-styled airlock has a null department.
+ /// </summary>
+ [DataField(required: true), AutoNetworkedField]
+ public ProtoId<DepartmentPrototype>? Department;
}
+using Content.Shared.DoAfter;
using Robust.Shared.Audio;
using Robust.Shared.GameStates;
namespace Content.Shared.SprayPainter.Components;
-[RegisterComponent, NetworkedComponent]
+[RegisterComponent, NetworkedComponent, AutoGenerateComponentState]
public sealed partial class SprayPainterComponent : Component
{
- [DataField("spraySound")]
+ [DataField]
public SoundSpecifier SpraySound = new SoundPathSpecifier("/Audio/Effects/spray2.ogg");
- [DataField("airlockSprayTime")]
- public float AirlockSprayTime = 3.0f;
+ [DataField]
+ public TimeSpan AirlockSprayTime = TimeSpan.FromSeconds(3);
- [DataField("pipeSprayTime")]
- public float PipeSprayTime = 1.0f;
+ [DataField]
+ public TimeSpan PipeSprayTime = TimeSpan.FromSeconds(1);
- [DataField("isSpraying")]
- public bool IsSpraying = false;
+ /// <summary>
+ /// DoAfterId for airlock spraying.
+ /// Pipes do not track doafters so you can spray multiple at once.
+ /// </summary>
+ [DataField]
+ public DoAfterId? AirlockDoAfter;
- [ViewVariables(VVAccess.ReadWrite)]
+ /// <summary>
+ /// Pipe color chosen to spray with.
+ /// </summary>
+ [DataField, AutoNetworkedField]
public string? PickedColor;
- [ViewVariables(VVAccess.ReadWrite)]
- [DataField("colorPalette")]
+ /// <summary>
+ /// Pipe colors that can be selected.
+ /// </summary>
+ [DataField]
public Dictionary<string, Color> ColorPalette = new();
- public int Index = default!;
+ /// <summary>
+ /// Airlock style index selected.
+ /// After prototype reload this might not be the same style but it will never be out of bounds.
+ /// </summary>
+ [DataField, AutoNetworkedField]
+ public int Index;
}
--- /dev/null
+using Content.Shared.Roles;
+using Robust.Shared.Prototypes;
+
+namespace Content.Shared.SprayPainter.Prototypes;
+
+/// <summary>
+/// Maps airlock style names to department ids.
+/// </summary>
+[Prototype("airlockDepartments")]
+public sealed class AirlockDepartmentsPrototype : IPrototype
+{
+ [IdDataField]
+ public string ID { get; private set; } = default!;
+
+ /// <summary>
+ /// Dictionary of style names to department ids.
+ /// If a style does not have a department (e.g. external) it is set to null.
+ /// </summary>
+ [DataField(required: true)]
+ public Dictionary<string, ProtoId<DepartmentPrototype>> Departments = new();
+}
+++ /dev/null
-using System.Linq;
-using Content.Shared.SprayPainter.Prototypes;
-using Robust.Shared.Prototypes;
-
-namespace Content.Shared.SprayPainter;
-
-public abstract class SharedSprayPainterSystem : EntitySystem
-{
- [Dependency] protected readonly IPrototypeManager _prototypeManager = default!;
-
- public List<string> Styles { get; private set; } = new();
- public List<AirlockGroupPrototype> Groups { get; private set; } = new();
-
- public override void Initialize()
- {
- base.Initialize();
-
- SortedSet<string> styles = new();
- foreach (AirlockGroupPrototype grp in _prototypeManager.EnumeratePrototypes<AirlockGroupPrototype>())
- {
- Groups.Add(grp);
- foreach (string style in grp.StylePaths.Keys)
- {
- styles.Add(style);
- }
- }
- Styles = styles.ToList();
- }
-}
--- /dev/null
+using Content.Shared.Administration.Logs;
+using Content.Shared.Database;
+using Content.Shared.DoAfter;
+using Content.Shared.Doors.Components;
+using Content.Shared.Interaction;
+using Content.Shared.Popups;
+using Content.Shared.SprayPainter.Components;
+using Content.Shared.SprayPainter.Prototypes;
+using Robust.Shared.Audio.Systems;
+using Robust.Shared.Prototypes;
+using System.Linq;
+
+namespace Content.Shared.SprayPainter;
+
+/// <summary>
+/// System for painting airlocks using a spray painter.
+/// Pipes are handled serverside since AtmosPipeColorSystem is server only.
+/// </summary>
+public abstract class SharedSprayPainterSystem : EntitySystem
+{
+ [Dependency] protected readonly IPrototypeManager Proto = default!;
+ [Dependency] private readonly ISharedAdminLogManager _adminLogger = default!;
+ [Dependency] protected readonly SharedAppearanceSystem Appearance = default!;
+ [Dependency] protected readonly SharedAudioSystem Audio = default!;
+ [Dependency] protected readonly SharedDoAfterSystem DoAfter = default!;
+ [Dependency] private readonly SharedPopupSystem _popup = default!;
+ [Dependency] private readonly SharedUserInterfaceSystem _ui = default!;
+
+ public List<AirlockStyle> Styles { get; private set; } = new();
+ public List<AirlockGroupPrototype> Groups { get; private set; } = new();
+
+ [ValidatePrototypeId<AirlockDepartmentsPrototype>]
+ private const string Departments = "Departments";
+
+ public override void Initialize()
+ {
+ base.Initialize();
+
+ CacheStyles();
+
+ SubscribeLocalEvent<SprayPainterComponent, MapInitEvent>(OnMapInit);
+ SubscribeLocalEvent<SprayPainterComponent, SprayPainterDoorDoAfterEvent>(OnDoorDoAfter);
+ Subs.BuiEvents<SprayPainterComponent>(SprayPainterUiKey.Key, subs =>
+ {
+ subs.Event<SprayPainterSpritePickedMessage>(OnSpritePicked);
+ subs.Event<SprayPainterColorPickedMessage>(OnColorPicked);
+ });
+
+ SubscribeLocalEvent<PaintableAirlockComponent, InteractUsingEvent>(OnAirlockInteract);
+
+ SubscribeLocalEvent<PrototypesReloadedEventArgs>(OnPrototypesReloaded);
+ }
+
+ private void OnMapInit(Entity<SprayPainterComponent> ent, ref MapInitEvent args)
+ {
+ if (ent.Comp.ColorPalette.Count == 0)
+ return;
+
+ SetColor(ent, ent.Comp.ColorPalette.First().Key);
+ }
+
+ private void OnDoorDoAfter(Entity<SprayPainterComponent> ent, ref SprayPainterDoorDoAfterEvent args)
+ {
+ ent.Comp.AirlockDoAfter = null;
+
+ if (args.Handled || args.Cancelled)
+ return;
+
+ if (args.Args.Target is not {} target)
+ return;
+
+ if (!TryComp<PaintableAirlockComponent>(target, out var airlock))
+ return;
+
+ airlock.Department = args.Department;
+ Dirty(target, airlock);
+
+ Audio.PlayPredicted(ent.Comp.SpraySound, ent, args.Args.User);
+ Appearance.SetData(target, DoorVisuals.BaseRSI, args.Sprite);
+ _adminLogger.Add(LogType.Action, LogImpact.Low, $"{ToPrettyString(args.Args.User):user} painted {ToPrettyString(args.Args.Target.Value):target}");
+
+ args.Handled = true;
+ }
+
+ #region UI messages
+
+ private void OnColorPicked(Entity<SprayPainterComponent> ent, ref SprayPainterColorPickedMessage args)
+ {
+ SetColor(ent, args.Key);
+ }
+
+ private void OnSpritePicked(Entity<SprayPainterComponent> ent, ref SprayPainterSpritePickedMessage args)
+ {
+ if (args.Index >= Styles.Count)
+ return;
+
+ ent.Comp.Index = args.Index;
+ Dirty(ent, ent.Comp);
+ }
+
+ private void SetColor(Entity<SprayPainterComponent> ent, string? paletteKey)
+ {
+ if (paletteKey == null || paletteKey == ent.Comp.PickedColor)
+ return;
+
+ if (!ent.Comp.ColorPalette.ContainsKey(paletteKey))
+ return;
+
+ ent.Comp.PickedColor = paletteKey;
+ Dirty(ent, ent.Comp);
+ }
+
+ #endregion
+
+ private void OnAirlockInteract(Entity<PaintableAirlockComponent> ent, ref InteractUsingEvent args)
+ {
+ if (args.Handled)
+ return;
+
+ if (!TryComp<SprayPainterComponent>(args.Used, out var painter) || painter.AirlockDoAfter != null)
+ return;
+
+ var group = Proto.Index<AirlockGroupPrototype>(ent.Comp.Group);
+
+ var style = Styles[painter.Index];
+ if (!group.StylePaths.TryGetValue(style.Name, out var sprite))
+ {
+ string msg = Loc.GetString("spray-painter-style-not-available");
+ _popup.PopupEntity(msg, args.User, args.User);
+ return;
+ }
+
+ var doAfterEventArgs = new DoAfterArgs(EntityManager, args.User, painter.AirlockSprayTime, new SprayPainterDoorDoAfterEvent(sprite, style.Department), args.Used, target: ent, used: args.Used)
+ {
+ BreakOnTargetMove = true,
+ BreakOnUserMove = true,
+ BreakOnDamage = true,
+ NeedHand = true
+ };
+ if (!DoAfter.TryStartDoAfter(doAfterEventArgs, out var id))
+ return;
+
+ // since we are now spraying an airlock prevent spraying more at the same time
+ // pipes ignore this
+ painter.AirlockDoAfter = id;
+ args.Handled = true;
+
+ // Log the attempt
+ _adminLogger.Add(LogType.Action, LogImpact.Low, $"{ToPrettyString(args.User):user} is painting {ToPrettyString(ent):target} to '{style.Name}' at {Transform(ent).Coordinates:targetlocation}");
+ }
+
+ #region Style caching
+
+ private void OnPrototypesReloaded(PrototypesReloadedEventArgs args)
+ {
+ if (!args.WasModified<AirlockGroupPrototype>() && !args.WasModified<AirlockDepartmentsPrototype>())
+ return;
+
+ Styles.Clear();
+ Groups.Clear();
+ CacheStyles();
+
+ // style index might be invalid now so check them all
+ var max = Styles.Count - 1;
+ var query = AllEntityQuery<SprayPainterComponent>();
+ while (query.MoveNext(out var uid, out var comp))
+ {
+ if (comp.Index > max)
+ {
+ comp.Index = max;
+ Dirty(uid, comp);
+ }
+ }
+ }
+
+ protected virtual void CacheStyles()
+ {
+ // collect every style's name
+ var names = new SortedSet<string>();
+ foreach (var group in Proto.EnumeratePrototypes<AirlockGroupPrototype>())
+ {
+ Groups.Add(group);
+ foreach (var style in group.StylePaths.Keys)
+ {
+ names.Add(style);
+ }
+ }
+
+ // get their department ids too for the final style list
+ var departments = Proto.Index<AirlockDepartmentsPrototype>(Departments);
+ Styles.Capacity = names.Count;
+ foreach (var name in names)
+ {
+ departments.Departments.TryGetValue(name, out var department);
+ Styles.Add(new AirlockStyle(name, department));
+ }
+ }
+
+ #endregion
+}
+
+public record struct AirlockStyle(string Name, string? Department);
[Serializable, NetSerializable]
public sealed class SprayPainterSpritePickedMessage : BoundUserInterfaceMessage
{
- public int Index { get; }
+ public readonly int Index;
public SprayPainterSpritePickedMessage(int index)
{
[Serializable, NetSerializable]
public sealed class SprayPainterColorPickedMessage : BoundUserInterfaceMessage
{
- public string? Key { get; }
+ public readonly string? Key;
public SprayPainterColorPickedMessage(string? key)
{
}
[Serializable, NetSerializable]
-public sealed class SprayPainterBoundUserInterfaceState : BoundUserInterfaceState
+public sealed partial class SprayPainterDoorDoAfterEvent : DoAfterEvent
{
- public int SelectedStyle { get; }
- public string? SelectedColorKey { get; }
- public Dictionary<string, Color> Palette { get; }
+ /// <summary>
+ /// Base RSI path to set for the door sprite.
+ /// </summary>
+ [DataField]
+ public string Sprite;
- public SprayPainterBoundUserInterfaceState(int selectedStyle, string? selectedColorKey, Dictionary<string, Color> palette)
+ /// <summary>
+ /// Department id to set for the door, if the style has one.
+ /// </summary>
+ [DataField]
+ public string? Department;
+
+ public SprayPainterDoorDoAfterEvent(string sprite, string? department)
{
- SelectedStyle = selectedStyle;
- SelectedColorKey = selectedColorKey;
- Palette = palette;
+ Sprite = sprite;
+ Department = department;
}
+
+ public override DoAfterEvent Clone() => this;
}
[Serializable, NetSerializable]
-public sealed partial class SprayPainterDoAfterEvent : DoAfterEvent
+public sealed partial class SprayPainterPipeDoAfterEvent : DoAfterEvent
{
- [DataField("sprite")]
- public string? Sprite = null;
-
- [DataField("color")]
- public Color? Color = null;
+ /// <summary>
+ /// Color of the pipe to set.
+ /// </summary>
+ [DataField]
+ public Color Color;
- private SprayPainterDoAfterEvent()
+ public SprayPainterPipeDoAfterEvent(Color color)
{
- }
-
- public SprayPainterDoAfterEvent(string? sprite, Color? color)
- {
- Sprite = sprite;
Color = color;
}
name: spray painter
description: A spray painter for painting airlocks and pipes.
components:
- - type: Sprite
- sprite: Objects/Tools/spray_painter.rsi
- state: spray_painter
- - type: Item
- sprite: Objects/Tools/spray_painter.rsi
- - type: UserInterface
- interfaces:
- - key: enum.SprayPainterUiKey.Key
- type: SprayPainterBoundUserInterface
- - type: SprayPainter
- colorPalette:
- red: '#FF1212FF'
- yellow: '#B3A234FF'
- brown: '#947507FF'
- green: '#3AB334FF'
- cyan: '#03FCD3FF'
- blue: '#0335FCFF'
- white: '#FFFFFFFF'
- black: '#333333FF'
- - type: StaticPrice
- price: 40
+ - type: Sprite
+ sprite: Objects/Tools/spray_painter.rsi
+ state: spray_painter
+ - type: Item
+ sprite: Objects/Tools/spray_painter.rsi
+ - type: ActivatableUI
+ key: enum.SprayPainterUiKey.Key
+ - type: UserInterface
+ interfaces:
+ - key: enum.SprayPainterUiKey.Key
+ type: SprayPainterBoundUserInterface
+ - type: SprayPainter
+ colorPalette:
+ red: '#FF1212FF'
+ yellow: '#B3A234FF'
+ brown: '#947507FF'
+ green: '#3AB334FF'
+ cyan: '#03FCD3FF'
+ blue: '#0335FCFF'
+ white: '#FFFFFFFF'
+ black: '#333333FF'
+ # standard atmos pipes
+ waste: '#990000'
+ distro: '#0055cc'
+ air: '#03fcd3'
+ mix: '#947507'
+ - type: StaticPrice
+ price: 40
components:
- type: Sprite
sprite: Structures/Doors/Airlocks/Standard/engineering.rsi
+ - type: PaintableAirlock
+ department: Engineering
- type: entity
- parent: Airlock
+ parent: AirlockEngineering
id: AirlockAtmospherics
suffix: Atmospherics
components:
components:
- type: Sprite
sprite: Structures/Doors/Airlocks/Standard/cargo.rsi
+ - type: PaintableAirlock
+ department: Cargo
- type: entity
parent: Airlock
components:
- type: Sprite
sprite: Structures/Doors/Airlocks/Standard/medical.rsi
+ - type: PaintableAirlock
+ department: Medical
- type: entity
- parent: Airlock
+ parent: AirlockMedical
id: AirlockVirology
suffix: Virology
components:
sprite: Structures/Doors/Airlocks/Standard/virology.rsi
- type: entity
- parent: Airlock
+ parent: AirlockMedical
id: AirlockChemistry
suffix: Chemistry
- components:
- - type: Sprite
- sprite: Structures/Doors/Airlocks/Standard/medical.rsi
- type: entity
parent: Airlock
components:
- type: Sprite
sprite: Structures/Doors/Airlocks/Standard/science.rsi
+ - type: PaintableAirlock
+ department: Science
- type: entity
parent: Airlock
sprite: Structures/Doors/Airlocks/Standard/command.rsi
- type: WiresPanelSecurity
securityLevel: medSecurity
+ - type: PaintableAirlock
+ department: Command
- type: entity
parent: Airlock
components:
- type: Sprite
sprite: Structures/Doors/Airlocks/Standard/security.rsi
+ - type: PaintableAirlock
+ department: Security
- type: entity
parent: Airlock
sprite: Structures/Doors/Airlocks/Standard/maint.rsi
- type: entity
- parent: Airlock
+ parent: AirlockSecurity # if you get syndie door somehow it counts as sec
id: AirlockSyndicate
suffix: Syndicate
components:
sprite: Structures/Doors/Airlocks/Standard/syndicate.rsi
- type: entity
- parent: Airlock
+ parent: AirlockCargo
id: AirlockMining
suffix: Mining(Salvage)
components:
sprite: Structures/Doors/Airlocks/Standard/mining.rsi
- type: entity
- parent: Airlock
+ parent: AirlockCommand # if you get centcom door somehow it counts as command, also inherit panel
id: AirlockCentralCommand
suffix: Central Command
components:
- type: Sprite
sprite: Structures/Doors/Airlocks/Standard/centcomm.rsi
- - type: WiresPanelSecurity
- securityLevel: medSecurity
- type: entity
parent: Airlock
- type: Sprite
sprite: Structures/Doors/Airlocks/Glass/engineering.rsi
- type: PaintableAirlock
- group: Glass
+ department: Engineering
- type: entity
parent: AirlockGlass
components:
- type: Sprite
sprite: Structures/Doors/Airlocks/Glass/maint.rsi
- - type: PaintableAirlock
- group: Glass
- type: entity
- parent: AirlockGlass
+ parent: AirlockEngineeringGlass
id: AirlockAtmosphericsGlass
suffix: Atmospherics
components:
- type: Sprite
sprite: Structures/Doors/Airlocks/Glass/atmospherics.rsi
- - type: PaintableAirlock
- group: Glass
- type: entity
parent: AirlockGlass
- type: Sprite
sprite: Structures/Doors/Airlocks/Glass/cargo.rsi
- type: PaintableAirlock
- group: Glass
-
-- type: entity
- parent: AirlockGlass
- id: AirlockChemistryGlass
- suffix: Chemistry
- components:
- - type: Sprite
- sprite: Structures/Doors/Airlocks/Glass/medical.rsi
- - type: PaintableAirlock
- group: Glass
+ department: Cargo
- type: entity
parent: AirlockGlass
- type: Sprite
sprite: Structures/Doors/Airlocks/Glass/medical.rsi
- type: PaintableAirlock
- group: Glass
+ department: Medical
- type: entity
- parent: AirlockGlass
+ parent: AirlockMedicalGlass
+ id: AirlockChemistryGlass
+ suffix: Chemistry
+
+- type: entity
+ parent: AirlockMedicalGlass
id: AirlockVirologyGlass
suffix: Virology
components:
- type: Sprite
sprite: Structures/Doors/Airlocks/Glass/virology.rsi
- - type: PaintableAirlock
- group: Glass
- type: entity
parent: AirlockGlass
- type: Sprite
sprite: Structures/Doors/Airlocks/Glass/science.rsi
- type: PaintableAirlock
- group: Glass
+ department: Science
- type: entity
parent: AirlockGlass
- type: Sprite
sprite: Structures/Doors/Airlocks/Glass/command.rsi
- type: PaintableAirlock
- group: Glass
+ department: Command
- type: WiresPanelSecurity
securityLevel: medSecurity
- type: Sprite
sprite: Structures/Doors/Airlocks/Glass/security.rsi
- type: PaintableAirlock
- group: Glass
+ department: Security
- type: entity
- parent: AirlockGlass
+ parent: AirlockSecurityGlass # see standard
id: AirlockSyndicateGlass
suffix: Syndicate
components:
- type: Sprite
sprite: Structures/Doors/Airlocks/Glass/syndicate.rsi
- - type: PaintableAirlock
- group: Glass
- type: entity
- parent: AirlockGlass
+ parent: AirlockCargoGlass
id: AirlockMiningGlass
suffix: Mining(Salvage)
components:
sprite: Structures/Doors/Airlocks/Glass/mining.rsi
- type: entity
- parent: AirlockGlass
+ parent: AirlockCommandGlass # see standard
id: AirlockCentralCommandGlass
suffix: Central Command
components:
sprite: Structures/Doors/Airlocks/Glass/centcomm.rsi
- type: WiresPanelSecurity
securityLevel: medSecurity
-
mode: NoSprite
- type: PaintableAirlock
group: Standard
+ department: Civilian
- type: AccessReader
- type: StaticPrice
price: 150
sprite: Structures/Doors/Airlocks/Standard/external.rsi
- type: PaintableAirlock
group: External
+ department: null
- type: entity
parent: AirlockExternal
- ForceNoFixRotations
- type: PaintableAirlock
group: Shuttle
+ department: null
- type: Construction
graph: AirlockShuttle
node: airlock
iconPriority: 40
stylePaths:
shuttle: Structures/Doors/Airlocks/Glass/shuttle.rsi
+
+# fun
+- type: airlockDepartments
+ id: Departments
+ departments:
+ atmospherics: Engineering
+ basic: Civilian
+ cargo: Cargo
+ command: Command
+ engineering: Engineering
+ freezer: Civilian
+ maintenance: Civilian
+ medical: Medical
+ science: Science
+ security: Security
+ virology: Medical