diff --git a/Content.Client/SprayPainter/SprayPainterSystem.cs b/Content.Client/SprayPainter/SprayPainterSystem.cs index 4476e2a90a..6a1d27e98b 100644 --- a/Content.Client/SprayPainter/SprayPainterSystem.cs +++ b/Content.Client/SprayPainter/SprayPainterSystem.cs @@ -14,29 +14,31 @@ public sealed class SprayPainterSystem : SharedSprayPainterSystem public List 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(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)); } } } diff --git a/Content.Client/SprayPainter/UI/SprayPainterBoundUserInterface.cs b/Content.Client/SprayPainter/UI/SprayPainterBoundUserInterface.cs index d5c57a601f..e8442d2390 100644 --- a/Content.Client/SprayPainter/UI/SprayPainterBoundUserInterface.cs +++ b/Content.Client/SprayPainter/UI/SprayPainterBoundUserInterface.cs @@ -1,4 +1,5 @@ using Content.Shared.SprayPainter; +using Content.Shared.SprayPainter.Components; using Robust.Client.GameObjects; using Robust.Client.UserInterface.Controls; @@ -20,14 +21,20 @@ public sealed class SprayPainterBoundUserInterface : BoundUserInterface { base.Open(); + if (!EntMan.TryGetComponent(Owner, out var comp)) + return; + _window = new SprayPainterWindow(); _painter = EntMan.System(); - _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) @@ -37,25 +44,6 @@ public sealed class SprayPainterBoundUserInterface : BoundUserInterface _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)); diff --git a/Content.Server/SprayPainter/SprayPainterSystem.cs b/Content.Server/SprayPainter/SprayPainterSystem.cs index 4f8f1cda2e..e49c49c1da 100644 --- a/Content.Server/SprayPainter/SprayPainterSystem.cs +++ b/Content.Server/SprayPainter/SprayPainterSystem.cs @@ -1,186 +1,69 @@ -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; /// -/// A system for painting airlocks and pipes using enginner painter +/// Handles spraying pipes using a spray painter. +/// Airlocks are handled in shared. /// -[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(OnInit); - SubscribeLocalEvent(AfterInteractOn); - SubscribeLocalEvent(OnActivate); - SubscribeLocalEvent(OnSpritePicked); - SubscribeLocalEvent(OnColorPicked); - SubscribeLocalEvent(OnDoAfter); + SubscribeLocalEvent(OnPipeDoAfter); + + SubscribeLocalEvent(OnPipeInteract); } - private void OnInit(EntityUid uid, SprayPainterComponent component, ComponentInit args) + private void OnPipeDoAfter(Entity ent, ref SprayPainterPipeDoAfterEvent args) { - if (component.ColorPalette.Count == 0) - return; - - SetColor(uid, component, component.ColorPalette.First().Key); - } - - private void OnDoAfter(EntityUid uid, SprayPainterComponent component, SprayPainterDoAfterEvent 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; + if (!TryComp(target, out var color)) + return; - _audio.PlayPvs(component.SpraySound, uid); + Audio.PlayPvs(ent.Comp.SpraySound, ent); - if (TryComp(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}"); - } - } + _pipeColor.SetColor(target, color, args.Color); args.Handled = true; } - private void OnActivate(EntityUid uid, SprayPainterComponent component, ActivateInWorldEvent args) + private void OnPipeInteract(Entity ent, ref InteractUsingEvent args) { - if (!EntityManager.TryGetComponent(args.User, out ActorComponent? actor)) - 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) + if (args.Handled) return; - if (EntityManager.TryGetComponent(target, out var airlock)) + if (!TryComp(args.Used, out var painter) || painter.PickedColor is not {} colorName) + return; + + if (!painter.ColorPalette.TryGetValue(colorName, out var color)) + return; + + var doAfterEventArgs = new DoAfterArgs(EntityManager, args.User, painter.PipeSprayTime, new SprayPainterPipeDoAfterEvent(color), args.Used, target: ent, used: args.Used) { - if (!_prototypeManager.TryIndex(airlock.Group, out var grp)) - { - Log.Error("Group not defined: %s", airlock.Group); - return; - } + 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 + }; - 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(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); - } - } - - 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); - } - - private void SetColor(EntityUid uid, SprayPainterComponent component, string? paletteKey) - { - if (paletteKey == null) - return; - - if (!component.ColorPalette.ContainsKey(paletteKey) || paletteKey == component.PickedColor) - return; - - component.PickedColor = paletteKey; - DirtyUI(uid, component); - } - - private void DirtyUI(EntityUid uid, SprayPainterComponent? component = null) - { - if (!Resolve(uid, ref component)) - return; - - _userInterfaceSystem.TrySetUiState( - uid, - SprayPainterUiKey.Key, - new SprayPainterBoundUserInterfaceState( - component.Index, - component.PickedColor, - component.ColorPalette)); + args.Handled = DoAfter.TryStartDoAfter(doAfterEventArgs); } } diff --git a/Content.Shared/SprayPainter/Components/PaintableAirlockComponent.cs b/Content.Shared/SprayPainter/Components/PaintableAirlockComponent.cs index 10fd72434e..fdd0aeeb7f 100644 --- a/Content.Shared/SprayPainter/Components/PaintableAirlockComponent.cs +++ b/Content.Shared/SprayPainter/Components/PaintableAirlockComponent.cs @@ -1,11 +1,24 @@ +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))] - public string Group = default!; + /// + /// Group of styles this airlock can be painted with, e.g. glass, standard or external. + /// + [DataField(required: true), AutoNetworkedField] + public ProtoId Group = string.Empty; + + /// + /// 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. + /// + [DataField(required: true), AutoNetworkedField] + public ProtoId? Department; } diff --git a/Content.Shared/SprayPainter/Components/SprayPainterComponent.cs b/Content.Shared/SprayPainter/Components/SprayPainterComponent.cs index e4581527b7..1742b13f8d 100644 --- a/Content.Shared/SprayPainter/Components/SprayPainterComponent.cs +++ b/Content.Shared/SprayPainter/Components/SprayPainterComponent.cs @@ -1,29 +1,44 @@ +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; + /// + /// DoAfterId for airlock spraying. + /// Pipes do not track doafters so you can spray multiple at once. + /// + [DataField] + public DoAfterId? AirlockDoAfter; - [ViewVariables(VVAccess.ReadWrite)] + /// + /// Pipe color chosen to spray with. + /// + [DataField, AutoNetworkedField] public string? PickedColor; - [ViewVariables(VVAccess.ReadWrite)] - [DataField("colorPalette")] + /// + /// Pipe colors that can be selected. + /// + [DataField] public Dictionary ColorPalette = new(); - public int Index = default!; + /// + /// Airlock style index selected. + /// After prototype reload this might not be the same style but it will never be out of bounds. + /// + [DataField, AutoNetworkedField] + public int Index; } diff --git a/Content.Shared/SprayPainter/Prototypes/AirlockDepartmentsPrototype.cs b/Content.Shared/SprayPainter/Prototypes/AirlockDepartmentsPrototype.cs new file mode 100644 index 0000000000..3553597c52 --- /dev/null +++ b/Content.Shared/SprayPainter/Prototypes/AirlockDepartmentsPrototype.cs @@ -0,0 +1,21 @@ +using Content.Shared.Roles; +using Robust.Shared.Prototypes; + +namespace Content.Shared.SprayPainter.Prototypes; + +/// +/// Maps airlock style names to department ids. +/// +[Prototype("airlockDepartments")] +public sealed class AirlockDepartmentsPrototype : IPrototype +{ + [IdDataField] + public string ID { get; private set; } = default!; + + /// + /// Dictionary of style names to department ids. + /// If a style does not have a department (e.g. external) it is set to null. + /// + [DataField(required: true)] + public Dictionary> Departments = new(); +} diff --git a/Content.Shared/SprayPainter/SharedDevicePainterSystem.cs b/Content.Shared/SprayPainter/SharedDevicePainterSystem.cs deleted file mode 100644 index ff43b119f6..0000000000 --- a/Content.Shared/SprayPainter/SharedDevicePainterSystem.cs +++ /dev/null @@ -1,29 +0,0 @@ -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 Styles { get; private set; } = new(); - public List Groups { get; private set; } = new(); - - public override void Initialize() - { - base.Initialize(); - - SortedSet styles = new(); - foreach (AirlockGroupPrototype grp in _prototypeManager.EnumeratePrototypes()) - { - Groups.Add(grp); - foreach (string style in grp.StylePaths.Keys) - { - styles.Add(style); - } - } - Styles = styles.ToList(); - } -} diff --git a/Content.Shared/SprayPainter/SharedSprayPainterSystem.cs b/Content.Shared/SprayPainter/SharedSprayPainterSystem.cs new file mode 100644 index 0000000000..c0f0f6d115 --- /dev/null +++ b/Content.Shared/SprayPainter/SharedSprayPainterSystem.cs @@ -0,0 +1,202 @@ +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; + +/// +/// System for painting airlocks using a spray painter. +/// Pipes are handled serverside since AtmosPipeColorSystem is server only. +/// +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 Styles { get; private set; } = new(); + public List Groups { get; private set; } = new(); + + [ValidatePrototypeId] + private const string Departments = "Departments"; + + public override void Initialize() + { + base.Initialize(); + + CacheStyles(); + + SubscribeLocalEvent(OnMapInit); + SubscribeLocalEvent(OnDoorDoAfter); + Subs.BuiEvents(SprayPainterUiKey.Key, subs => + { + subs.Event(OnSpritePicked); + subs.Event(OnColorPicked); + }); + + SubscribeLocalEvent(OnAirlockInteract); + + SubscribeLocalEvent(OnPrototypesReloaded); + } + + private void OnMapInit(Entity ent, ref MapInitEvent args) + { + if (ent.Comp.ColorPalette.Count == 0) + return; + + SetColor(ent, ent.Comp.ColorPalette.First().Key); + } + + private void OnDoorDoAfter(Entity ent, ref SprayPainterDoorDoAfterEvent args) + { + ent.Comp.AirlockDoAfter = null; + + if (args.Handled || args.Cancelled) + return; + + if (args.Args.Target is not {} target) + return; + + if (!TryComp(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 ent, ref SprayPainterColorPickedMessage args) + { + SetColor(ent, args.Key); + } + + private void OnSpritePicked(Entity ent, ref SprayPainterSpritePickedMessage args) + { + if (args.Index >= Styles.Count) + return; + + ent.Comp.Index = args.Index; + Dirty(ent, ent.Comp); + } + + private void SetColor(Entity 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 ent, ref InteractUsingEvent args) + { + if (args.Handled) + return; + + if (!TryComp(args.Used, out var painter) || painter.AirlockDoAfter != null) + return; + + var group = Proto.Index(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() && !args.WasModified()) + return; + + Styles.Clear(); + Groups.Clear(); + CacheStyles(); + + // style index might be invalid now so check them all + var max = Styles.Count - 1; + var query = AllEntityQuery(); + 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(); + foreach (var group in Proto.EnumeratePrototypes()) + { + 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(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); diff --git a/Content.Shared/SprayPainter/SprayPainterEvents.cs b/Content.Shared/SprayPainter/SprayPainterEvents.cs index f0e0208610..b88b054ad1 100644 --- a/Content.Shared/SprayPainter/SprayPainterEvents.cs +++ b/Content.Shared/SprayPainter/SprayPainterEvents.cs @@ -12,7 +12,7 @@ public enum SprayPainterUiKey [Serializable, NetSerializable] public sealed class SprayPainterSpritePickedMessage : BoundUserInterfaceMessage { - public int Index { get; } + public readonly int Index; public SprayPainterSpritePickedMessage(int index) { @@ -23,7 +23,7 @@ public sealed class SprayPainterSpritePickedMessage : BoundUserInterfaceMessage [Serializable, NetSerializable] public sealed class SprayPainterColorPickedMessage : BoundUserInterfaceMessage { - public string? Key { get; } + public readonly string? Key; public SprayPainterColorPickedMessage(string? key) { @@ -32,36 +32,40 @@ public sealed class SprayPainterColorPickedMessage : BoundUserInterfaceMessage } [Serializable, NetSerializable] -public sealed class SprayPainterBoundUserInterfaceState : BoundUserInterfaceState +public sealed partial class SprayPainterDoorDoAfterEvent : DoAfterEvent { - public int SelectedStyle { get; } - public string? SelectedColorKey { get; } - public Dictionary Palette { get; } + /// + /// Base RSI path to set for the door sprite. + /// + [DataField] + public string Sprite; - public SprayPainterBoundUserInterfaceState(int selectedStyle, string? selectedColorKey, Dictionary palette) + /// + /// Department id to set for the door, if the style has one. + /// + [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; + /// + /// Color of the pipe to set. + /// + [DataField] + public Color Color; - [DataField("color")] - public Color? Color = null; - - private SprayPainterDoAfterEvent() + public SprayPainterPipeDoAfterEvent(Color color) { - } - - public SprayPainterDoAfterEvent(string? sprite, Color? color) - { - Sprite = sprite; Color = color; } diff --git a/Resources/Prototypes/Entities/Objects/Tools/spray_painter.yml b/Resources/Prototypes/Entities/Objects/Tools/spray_painter.yml index 8a8c569510..903b8d3f90 100644 --- a/Resources/Prototypes/Entities/Objects/Tools/spray_painter.yml +++ b/Resources/Prototypes/Entities/Objects/Tools/spray_painter.yml @@ -4,24 +4,31 @@ 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 diff --git a/Resources/Prototypes/Entities/Structures/Doors/Airlocks/airlocks.yml b/Resources/Prototypes/Entities/Structures/Doors/Airlocks/airlocks.yml index 549a42c264..ce2b326129 100644 --- a/Resources/Prototypes/Entities/Structures/Doors/Airlocks/airlocks.yml +++ b/Resources/Prototypes/Entities/Structures/Doors/Airlocks/airlocks.yml @@ -13,9 +13,11 @@ 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: @@ -29,6 +31,8 @@ components: - type: Sprite sprite: Structures/Doors/Airlocks/Standard/cargo.rsi + - type: PaintableAirlock + department: Cargo - type: entity parent: Airlock @@ -37,9 +41,11 @@ 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: @@ -47,12 +53,9 @@ 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 @@ -61,6 +64,8 @@ components: - type: Sprite sprite: Structures/Doors/Airlocks/Standard/science.rsi + - type: PaintableAirlock + department: Science - type: entity parent: Airlock @@ -71,6 +76,8 @@ sprite: Structures/Doors/Airlocks/Standard/command.rsi - type: WiresPanelSecurity securityLevel: medSecurity + - type: PaintableAirlock + department: Command - type: entity parent: Airlock @@ -79,6 +86,8 @@ components: - type: Sprite sprite: Structures/Doors/Airlocks/Standard/security.rsi + - type: PaintableAirlock + department: Security - type: entity parent: Airlock @@ -89,7 +98,7 @@ 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: @@ -97,7 +106,7 @@ sprite: Structures/Doors/Airlocks/Standard/syndicate.rsi - type: entity - parent: Airlock + parent: AirlockCargo id: AirlockMining suffix: Mining(Salvage) components: @@ -105,14 +114,12 @@ 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 @@ -181,7 +188,7 @@ - type: Sprite sprite: Structures/Doors/Airlocks/Glass/engineering.rsi - type: PaintableAirlock - group: Glass + department: Engineering - type: entity parent: AirlockGlass @@ -190,18 +197,14 @@ 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 @@ -211,17 +214,7 @@ - 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 @@ -231,17 +224,20 @@ - 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 @@ -251,7 +247,7 @@ - type: Sprite sprite: Structures/Doors/Airlocks/Glass/science.rsi - type: PaintableAirlock - group: Glass + department: Science - type: entity parent: AirlockGlass @@ -261,7 +257,7 @@ - type: Sprite sprite: Structures/Doors/Airlocks/Glass/command.rsi - type: PaintableAirlock - group: Glass + department: Command - type: WiresPanelSecurity securityLevel: medSecurity @@ -273,20 +269,18 @@ - 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: @@ -294,7 +288,7 @@ sprite: Structures/Doors/Airlocks/Glass/mining.rsi - type: entity - parent: AirlockGlass + parent: AirlockCommandGlass # see standard id: AirlockCentralCommandGlass suffix: Central Command components: @@ -302,4 +296,3 @@ sprite: Structures/Doors/Airlocks/Glass/centcomm.rsi - type: WiresPanelSecurity securityLevel: medSecurity - diff --git a/Resources/Prototypes/Entities/Structures/Doors/Airlocks/base_structureairlocks.yml b/Resources/Prototypes/Entities/Structures/Doors/Airlocks/base_structureairlocks.yml index d610fb25fb..9930e6631d 100644 --- a/Resources/Prototypes/Entities/Structures/Doors/Airlocks/base_structureairlocks.yml +++ b/Resources/Prototypes/Entities/Structures/Doors/Airlocks/base_structureairlocks.yml @@ -134,6 +134,7 @@ mode: NoSprite - type: PaintableAirlock group: Standard + department: Civilian - type: AccessReader - type: StaticPrice price: 150 diff --git a/Resources/Prototypes/Entities/Structures/Doors/Airlocks/external.yml b/Resources/Prototypes/Entities/Structures/Doors/Airlocks/external.yml index 5e2eb5689f..75b23f7071 100644 --- a/Resources/Prototypes/Entities/Structures/Doors/Airlocks/external.yml +++ b/Resources/Prototypes/Entities/Structures/Doors/Airlocks/external.yml @@ -18,6 +18,7 @@ sprite: Structures/Doors/Airlocks/Standard/external.rsi - type: PaintableAirlock group: External + department: null - type: entity parent: AirlockExternal diff --git a/Resources/Prototypes/Entities/Structures/Doors/Airlocks/shuttle.yml b/Resources/Prototypes/Entities/Structures/Doors/Airlocks/shuttle.yml index 8c5aa49b8e..21d485be0c 100644 --- a/Resources/Prototypes/Entities/Structures/Doors/Airlocks/shuttle.yml +++ b/Resources/Prototypes/Entities/Structures/Doors/Airlocks/shuttle.yml @@ -64,6 +64,7 @@ - ForceNoFixRotations - type: PaintableAirlock group: Shuttle + department: null - type: Construction graph: AirlockShuttle node: airlock diff --git a/Resources/Prototypes/Entities/Structures/Doors/airlock_groups.yml b/Resources/Prototypes/Entities/Structures/Doors/airlock_groups.yml index b22d11826c..09ce1a05d9 100644 --- a/Resources/Prototypes/Entities/Structures/Doors/airlock_groups.yml +++ b/Resources/Prototypes/Entities/Structures/Doors/airlock_groups.yml @@ -58,3 +58,19 @@ 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