* PaintableAirlockComponent and AirlockGroupPrototype have been replaced * Slightly redesigned SprayPainterSystem for greater versatility * Added handling of changes to the appearance of doors and storages * PaintableGroup prototypes have been created * Generating tabs with styles in the UI * Fix error with undiscovered layer * Slight improvement * Removed unnecessary property * The category for `PaintableGroup` was allocated to a separate prototype so that the engine itself would check if the category existed * Added canisters, but repainting doesn't work * Added localization to styles * Fix sprite changing * Added the ability to paint canisters * slight ui improvement * Fix yamllinter errors * Fix test * The UI now remembers which tab was open * Fix build (?) * Rename * Charges have been added to the spray painter * Added a charge texture for the spray painter * Now spray painter can paint decals * Increased number of charges * Spawning dummy objects has been replaced by PrototypeManager * added a signature about the painting of the object * fix * Code commenting * Fix upstream * Update Content.Shared/SprayPainter/Components/SprayPainterAmmo.cs Co-authored-by: pathetic meowmeow <uhhadd@gmail.com> * review * Now decals can only be painted if the corresponding tab in the menu is open. * Fixed a bug with pipe and decal tabs not being remembered * Update EntityStorageVisualizerSystem.cs * record * loc * Cleanup * Revert electrified visuals * more cleanup, fix charges, del ammo4 * no empty file, remove meta component * closet exceptions, storage visualizer fixes * enable/disable decal through alt-verb * Fix missed merge conflicts * fix snap offset, button event handlers * simpler order, fix snap loc string * Remove PaintableViz.BaseRSI, no decal item, A-Z * State-respecting UI, BUI updates, FTL fixes * revert DecalPlacerWindow changes * revert unwanted changes, cleanup function order * Limit SprayPainterAmmo write access to AmmoSystem * Remove PaintedSystem * spray paint ammo lathe recipe, youtool listing * category as a list, groups as subtabs * Restore inhand copyright in meta.json * empty spray painter, recipe produces an empty one * allow alpha on spray painter decals * add comments * paintable wall lockers * Restrict painting more objects * Suggested event changes, event cleanup * component comments, fix ammo inhands * uncleanable decals, dirty styles on mapinit * organize paintables, separate emergency/closet grp * fix categories newline at EOF * airlock group whitespace cleanup * realphabetize * Clean up EntityStorageViz merge conflict markers * Apply requested changes * Apply suggestions from sowelipililimute's review Co-authored-by: pathetic meowmeow <uhhadd@gmail.com> * betrayal most foul * Remove members from EntityPaintedEvent * No emerg. group, steelsec to secure, locker/closet * Enable repainting the medical wall locker * comments, no flags on PaintableVisuals * Remove locked variants from closets/wall closets * removable decals * off value consistency * can't paint away those bones * fix precedence * Remove AirlockDepartment, AirlockGroup protos Both unused. * whitelist consistency re: ammo component * add standing emergency closet styles * alphabetize the spray painter listings --------- Co-authored-by: Ertanic <black.ikra.14@gmail.com> Co-authored-by: Эдуард <36124833+Ertanic@users.noreply.github.com> Co-authored-by: pathetic meowmeow <uhhadd@gmail.com>
This commit is contained in:
@@ -0,0 +1,28 @@
|
|||||||
|
using Content.Shared.Atmos.Piping.Unary.Components;
|
||||||
|
using Content.Shared.SprayPainter.Prototypes;
|
||||||
|
using Robust.Client.GameObjects;
|
||||||
|
using Robust.Shared.Prototypes;
|
||||||
|
|
||||||
|
namespace Content.Client.Atmos.EntitySystems;
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Used to change the appearance of gas canisters.
|
||||||
|
/// </summary>
|
||||||
|
public sealed class GasCanisterAppearanceSystem : VisualizerSystem<GasCanisterComponent>
|
||||||
|
{
|
||||||
|
[Dependency] private readonly IPrototypeManager _prototypeManager = default!;
|
||||||
|
|
||||||
|
protected override void OnAppearanceChange(EntityUid uid, GasCanisterComponent component, ref AppearanceChangeEvent args)
|
||||||
|
{
|
||||||
|
if (!AppearanceSystem.TryGetData<string>(uid, PaintableVisuals.Prototype, out var protoName, args.Component) || args.Sprite is not { } old)
|
||||||
|
return;
|
||||||
|
|
||||||
|
if (!_prototypeManager.HasIndex(protoName))
|
||||||
|
return;
|
||||||
|
|
||||||
|
// Create the given prototype and get its first layer.
|
||||||
|
var tempUid = Spawn(protoName);
|
||||||
|
SpriteSystem.LayerSetRsiState(uid, 0, SpriteSystem.LayerGetRsiState(tempUid, 0));
|
||||||
|
QueueDel(tempUid);
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -1,16 +1,17 @@
|
|||||||
using Content.Shared.Doors.Components;
|
using Content.Shared.Doors.Components;
|
||||||
using Content.Shared.Doors.Systems;
|
using Content.Shared.Doors.Systems;
|
||||||
|
using Content.Shared.SprayPainter.Prototypes;
|
||||||
using Robust.Client.Animations;
|
using Robust.Client.Animations;
|
||||||
using Robust.Client.GameObjects;
|
using Robust.Client.GameObjects;
|
||||||
using Robust.Client.ResourceManagement;
|
using Robust.Shared.Prototypes;
|
||||||
using Robust.Shared.Serialization.TypeSerializers.Implementations;
|
|
||||||
|
|
||||||
namespace Content.Client.Doors;
|
namespace Content.Client.Doors;
|
||||||
|
|
||||||
public sealed class DoorSystem : SharedDoorSystem
|
public sealed class DoorSystem : SharedDoorSystem
|
||||||
{
|
{
|
||||||
[Dependency] private readonly AnimationPlayerSystem _animationSystem = default!;
|
[Dependency] private readonly AnimationPlayerSystem _animationSystem = default!;
|
||||||
[Dependency] private readonly IResourceCache _resourceCache = default!;
|
[Dependency] private readonly IComponentFactory _componentFactory = default!;
|
||||||
|
[Dependency] private readonly IPrototypeManager _prototypeManager = default!;
|
||||||
[Dependency] private readonly SpriteSystem _sprite = default!;
|
[Dependency] private readonly SpriteSystem _sprite = default!;
|
||||||
|
|
||||||
public override void Initialize()
|
public override void Initialize()
|
||||||
@@ -85,8 +86,8 @@ public sealed class DoorSystem : SharedDoorSystem
|
|||||||
if (!AppearanceSystem.TryGetData<DoorState>(entity, DoorVisuals.State, out var state, args.Component))
|
if (!AppearanceSystem.TryGetData<DoorState>(entity, DoorVisuals.State, out var state, args.Component))
|
||||||
state = DoorState.Closed;
|
state = DoorState.Closed;
|
||||||
|
|
||||||
if (AppearanceSystem.TryGetData<string>(entity, DoorVisuals.BaseRSI, out var baseRsi, args.Component))
|
if (AppearanceSystem.TryGetData<string>(entity, PaintableVisuals.Prototype, out var prototype, args.Component))
|
||||||
UpdateSpriteLayers((entity.Owner, args.Sprite), baseRsi);
|
UpdateSpriteLayers((entity.Owner, args.Sprite), prototype);
|
||||||
|
|
||||||
if (_animationSystem.HasRunningAnimation(entity, DoorComponent.AnimationKey))
|
if (_animationSystem.HasRunningAnimation(entity, DoorComponent.AnimationKey))
|
||||||
_animationSystem.Stop(entity.Owner, DoorComponent.AnimationKey);
|
_animationSystem.Stop(entity.Owner, DoorComponent.AnimationKey);
|
||||||
@@ -139,14 +140,14 @@ public sealed class DoorSystem : SharedDoorSystem
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
private void UpdateSpriteLayers(Entity<SpriteComponent> sprite, string baseRsi)
|
private void UpdateSpriteLayers(Entity<SpriteComponent> sprite, string targetProto)
|
||||||
{
|
{
|
||||||
if (!_resourceCache.TryGetResource<RSIResource>(SpriteSpecifierSerializer.TextureRoot / baseRsi, out var res))
|
if (!_prototypeManager.TryIndex(targetProto, out var target))
|
||||||
{
|
|
||||||
Log.Error("Unable to load RSI '{0}'. Trace:\n{1}", baseRsi, Environment.StackTrace);
|
|
||||||
return;
|
return;
|
||||||
}
|
|
||||||
|
|
||||||
_sprite.SetBaseRsi(sprite.AsNullable(), res.RSI);
|
if (!target.TryGetComponent(out SpriteComponent? targetSprite, _componentFactory))
|
||||||
|
return;
|
||||||
|
|
||||||
|
_sprite.SetBaseRsi(sprite.AsNullable(), targetSprite.BaseRSI);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,56 +1,129 @@
|
|||||||
using Content.Shared.SprayPainter;
|
|
||||||
using Robust.Client.Graphics;
|
|
||||||
using Robust.Client.ResourceManagement;
|
|
||||||
using Robust.Shared.Serialization.TypeSerializers.Implementations;
|
|
||||||
using Robust.Shared.Utility;
|
|
||||||
using System.Linq;
|
using System.Linq;
|
||||||
using Robust.Shared.Graphics;
|
using Content.Client.Items;
|
||||||
|
using Content.Client.Message;
|
||||||
|
using Content.Client.Stylesheets;
|
||||||
|
using Content.Shared.Decals;
|
||||||
|
using Content.Shared.SprayPainter;
|
||||||
|
using Content.Shared.SprayPainter.Components;
|
||||||
|
using Content.Shared.SprayPainter.Prototypes;
|
||||||
|
using Robust.Client.GameObjects;
|
||||||
|
using Robust.Client.UserInterface;
|
||||||
|
using Robust.Client.UserInterface.Controls;
|
||||||
|
using Robust.Shared.Prototypes;
|
||||||
|
using Robust.Shared.Timing;
|
||||||
|
using Robust.Shared.Utility;
|
||||||
|
|
||||||
namespace Content.Client.SprayPainter;
|
namespace Content.Client.SprayPainter;
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Client-side spray painter functions. Caches information for spray painter windows and updates the UI to reflect component state.
|
||||||
|
/// </summary>
|
||||||
public sealed class SprayPainterSystem : SharedSprayPainterSystem
|
public sealed class SprayPainterSystem : SharedSprayPainterSystem
|
||||||
{
|
{
|
||||||
[Dependency] private readonly IResourceCache _resourceCache = default!;
|
[Dependency] private readonly UserInterfaceSystem _ui = default!;
|
||||||
|
|
||||||
public List<SprayPainterEntry> Entries { get; private set; } = new();
|
public List<SprayPainterDecalEntry> Decals = [];
|
||||||
|
public Dictionary<string, List<string>> PaintableGroupsByCategory = new();
|
||||||
|
public Dictionary<string, Dictionary<string, EntProtoId>> PaintableStylesByGroup = new();
|
||||||
|
|
||||||
protected override void CacheStyles()
|
public override void Initialize()
|
||||||
{
|
{
|
||||||
base.CacheStyles();
|
base.Initialize();
|
||||||
|
|
||||||
Entries.Clear();
|
Subs.ItemStatus<SprayPainterComponent>(ent => new StatusControl(ent));
|
||||||
foreach (var style in Styles)
|
SubscribeLocalEvent<SprayPainterComponent, AfterAutoHandleStateEvent>(OnStateUpdate);
|
||||||
|
SubscribeLocalEvent<PrototypesReloadedEventArgs>(OnPrototypesReloaded);
|
||||||
|
|
||||||
|
CachePrototypes();
|
||||||
|
}
|
||||||
|
|
||||||
|
private void OnStateUpdate(Entity<SprayPainterComponent> ent, ref AfterAutoHandleStateEvent args)
|
||||||
|
{
|
||||||
|
UpdateUi(ent);
|
||||||
|
}
|
||||||
|
|
||||||
|
protected override void UpdateUi(Entity<SprayPainterComponent> ent)
|
||||||
|
{
|
||||||
|
if (_ui.TryGetOpenUi(ent.Owner, SprayPainterUiKey.Key, out var bui))
|
||||||
|
bui.Update();
|
||||||
|
}
|
||||||
|
|
||||||
|
private void OnPrototypesReloaded(PrototypesReloadedEventArgs args)
|
||||||
|
{
|
||||||
|
if (!args.WasModified<PaintableGroupCategoryPrototype>() || !args.WasModified<PaintableGroupPrototype>() || !args.WasModified<DecalPrototype>())
|
||||||
|
return;
|
||||||
|
|
||||||
|
CachePrototypes();
|
||||||
|
}
|
||||||
|
|
||||||
|
private void CachePrototypes()
|
||||||
|
{
|
||||||
|
PaintableGroupsByCategory.Clear();
|
||||||
|
PaintableStylesByGroup.Clear();
|
||||||
|
foreach (var category in Proto.EnumeratePrototypes<PaintableGroupCategoryPrototype>().OrderBy(x => x.ID))
|
||||||
{
|
{
|
||||||
var name = style.Name;
|
var groupList = new List<string>();
|
||||||
string? iconPath = Groups
|
foreach (var groupId in category.Groups)
|
||||||
.FindAll(x => x.StylePaths.ContainsKey(name))?
|
|
||||||
.MaxBy(x => x.IconPriority)?.StylePaths[name];
|
|
||||||
if (iconPath == null)
|
|
||||||
{
|
{
|
||||||
Entries.Add(new SprayPainterEntry(name, null));
|
if (!Proto.TryIndex(groupId, out var group))
|
||||||
continue;
|
continue;
|
||||||
|
|
||||||
|
groupList.Add(groupId);
|
||||||
|
PaintableStylesByGroup[groupId] = group.Styles;
|
||||||
}
|
}
|
||||||
|
|
||||||
RSIResource doorRsi = _resourceCache.GetResource<RSIResource>(SpriteSpecifierSerializer.TextureRoot / new ResPath(iconPath));
|
if (groupList.Count > 0)
|
||||||
if (!doorRsi.RSI.TryGetState("closed", out var icon))
|
PaintableGroupsByCategory[category.ID] = groupList;
|
||||||
{
|
}
|
||||||
Entries.Add(new SprayPainterEntry(name, null));
|
|
||||||
continue;
|
|
||||||
}
|
|
||||||
|
|
||||||
Entries.Add(new SprayPainterEntry(name, icon.Frame0));
|
Decals.Clear();
|
||||||
|
foreach (var decalPrototype in Proto.EnumeratePrototypes<DecalPrototype>().OrderBy(x => x.ID))
|
||||||
|
{
|
||||||
|
if (!decalPrototype.Tags.Contains("station")
|
||||||
|
&& !decalPrototype.Tags.Contains("markings")
|
||||||
|
|| decalPrototype.Tags.Contains("dirty"))
|
||||||
|
continue;
|
||||||
|
|
||||||
|
Decals.Add(new SprayPainterDecalEntry(decalPrototype.ID, decalPrototype.Sprite));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private sealed class StatusControl : Control
|
||||||
|
{
|
||||||
|
private readonly RichTextLabel _label;
|
||||||
|
private readonly Entity<SprayPainterComponent> _entity;
|
||||||
|
private DecalPaintMode? _lastPaintingDecals = null;
|
||||||
|
|
||||||
|
public StatusControl(Entity<SprayPainterComponent> ent)
|
||||||
|
{
|
||||||
|
_entity = ent;
|
||||||
|
_label = new RichTextLabel { StyleClasses = { StyleNano.StyleClassItemStatus } };
|
||||||
|
AddChild(_label);
|
||||||
|
}
|
||||||
|
|
||||||
|
protected override void FrameUpdate(FrameEventArgs args)
|
||||||
|
{
|
||||||
|
base.FrameUpdate(args);
|
||||||
|
|
||||||
|
if (_entity.Comp.DecalMode == _lastPaintingDecals)
|
||||||
|
return;
|
||||||
|
|
||||||
|
_lastPaintingDecals = _entity.Comp.DecalMode;
|
||||||
|
|
||||||
|
string modeLocString = _entity.Comp.DecalMode switch
|
||||||
|
{
|
||||||
|
DecalPaintMode.Add => "spray-painter-item-status-add",
|
||||||
|
DecalPaintMode.Remove => "spray-painter-item-status-remove",
|
||||||
|
_ => "spray-painter-item-status-off"
|
||||||
|
};
|
||||||
|
|
||||||
|
_label.SetMarkupPermissive(Robust.Shared.Localization.Loc.GetString("spray-painter-item-status-label",
|
||||||
|
("mode", Robust.Shared.Localization.Loc.GetString(modeLocString))));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
public sealed class SprayPainterEntry
|
/// <summary>
|
||||||
{
|
/// A spray paintable decal, mapped by ID.
|
||||||
public string Name;
|
/// </summary>
|
||||||
public Texture? Icon;
|
public sealed record SprayPainterDecalEntry(string Name, SpriteSpecifier Sprite);
|
||||||
|
|
||||||
public SprayPainterEntry(string name, Texture? icon)
|
|
||||||
{
|
|
||||||
Name = name;
|
|
||||||
Icon = icon;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|||||||
@@ -1,42 +1,96 @@
|
|||||||
|
using Content.Shared.Decals;
|
||||||
using Content.Shared.SprayPainter;
|
using Content.Shared.SprayPainter;
|
||||||
using Content.Shared.SprayPainter.Components;
|
using Content.Shared.SprayPainter.Components;
|
||||||
using Robust.Client.UserInterface;
|
using Robust.Client.UserInterface;
|
||||||
using Robust.Client.UserInterface.Controls;
|
using Robust.Client.UserInterface.Controls;
|
||||||
|
using Robust.Shared.Prototypes;
|
||||||
|
|
||||||
namespace Content.Client.SprayPainter.UI;
|
namespace Content.Client.SprayPainter.UI;
|
||||||
|
|
||||||
public sealed class SprayPainterBoundUserInterface : BoundUserInterface
|
/// <summary>
|
||||||
|
/// A BUI for a spray painter. Allows selecting pipe colours, decals, and paintable object types sorted by category.
|
||||||
|
/// </summary>
|
||||||
|
public sealed class SprayPainterBoundUserInterface(EntityUid owner, Enum uiKey) : BoundUserInterface(owner, uiKey)
|
||||||
{
|
{
|
||||||
[ViewVariables]
|
[ViewVariables]
|
||||||
private SprayPainterWindow? _window;
|
private SprayPainterWindow? _window;
|
||||||
|
|
||||||
public SprayPainterBoundUserInterface(EntityUid owner, Enum uiKey) : base(owner, uiKey)
|
|
||||||
{
|
|
||||||
}
|
|
||||||
|
|
||||||
protected override void Open()
|
protected override void Open()
|
||||||
{
|
{
|
||||||
base.Open();
|
base.Open();
|
||||||
|
|
||||||
_window = this.CreateWindow<SprayPainterWindow>();
|
if (_window == null)
|
||||||
|
|
||||||
_window.OnSpritePicked = OnSpritePicked;
|
|
||||||
_window.OnColorPicked = OnColorPicked;
|
|
||||||
|
|
||||||
if (EntMan.TryGetComponent(Owner, out SprayPainterComponent? comp))
|
|
||||||
{
|
{
|
||||||
_window.Populate(EntMan.System<SprayPainterSystem>().Entries, comp.Index, comp.PickedColor, comp.ColorPalette);
|
_window = this.CreateWindow<SprayPainterWindow>();
|
||||||
|
|
||||||
|
_window.OnSpritePicked += OnSpritePicked;
|
||||||
|
_window.OnSetPipeColor += OnSetPipeColor;
|
||||||
|
_window.OnTabChanged += OnTabChanged;
|
||||||
|
_window.OnDecalChanged += OnDecalChanged;
|
||||||
|
_window.OnDecalColorChanged += OnDecalColorChanged;
|
||||||
|
_window.OnDecalAngleChanged += OnDecalAngleChanged;
|
||||||
|
_window.OnDecalSnapChanged += OnDecalSnapChanged;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
var sprayPainter = EntMan.System<SprayPainterSystem>();
|
||||||
|
_window.PopulateCategories(sprayPainter.PaintableStylesByGroup, sprayPainter.PaintableGroupsByCategory, sprayPainter.Decals);
|
||||||
|
Update();
|
||||||
|
|
||||||
|
if (EntMan.TryGetComponent(Owner, out SprayPainterComponent? sprayPainterComp))
|
||||||
|
_window.SetSelectedTab(sprayPainterComp.SelectedTab);
|
||||||
}
|
}
|
||||||
|
|
||||||
private void OnSpritePicked(ItemList.ItemListSelectedEventArgs args)
|
public override void Update()
|
||||||
{
|
{
|
||||||
SendMessage(new SprayPainterSpritePickedMessage(args.ItemIndex));
|
if (_window == null)
|
||||||
|
return;
|
||||||
|
|
||||||
|
if (!EntMan.TryGetComponent(Owner, out SprayPainterComponent? sprayPainter))
|
||||||
|
return;
|
||||||
|
|
||||||
|
_window.PopulateColors(sprayPainter.ColorPalette);
|
||||||
|
if (sprayPainter.PickedColor != null)
|
||||||
|
_window.SelectColor(sprayPainter.PickedColor);
|
||||||
|
_window.SetSelectedStyles(sprayPainter.StylesByGroup);
|
||||||
|
_window.SetSelectedDecal(sprayPainter.SelectedDecal);
|
||||||
|
_window.SetDecalAngle(sprayPainter.SelectedDecalAngle);
|
||||||
|
_window.SetDecalColor(sprayPainter.SelectedDecalColor);
|
||||||
|
_window.SetDecalSnap(sprayPainter.SnapDecals);
|
||||||
}
|
}
|
||||||
|
|
||||||
private void OnColorPicked(ItemList.ItemListSelectedEventArgs args)
|
private void OnDecalSnapChanged(bool snap)
|
||||||
|
{
|
||||||
|
SendPredictedMessage(new SprayPainterSetDecalSnapMessage(snap));
|
||||||
|
}
|
||||||
|
|
||||||
|
private void OnDecalAngleChanged(int angle)
|
||||||
|
{
|
||||||
|
SendPredictedMessage(new SprayPainterSetDecalAngleMessage(angle));
|
||||||
|
}
|
||||||
|
|
||||||
|
private void OnDecalColorChanged(Color? color)
|
||||||
|
{
|
||||||
|
SendPredictedMessage(new SprayPainterSetDecalColorMessage(color));
|
||||||
|
}
|
||||||
|
|
||||||
|
private void OnDecalChanged(ProtoId<DecalPrototype> protoId)
|
||||||
|
{
|
||||||
|
SendPredictedMessage(new SprayPainterSetDecalMessage(protoId));
|
||||||
|
}
|
||||||
|
|
||||||
|
private void OnTabChanged(int index, bool isSelectedTabWithDecals)
|
||||||
|
{
|
||||||
|
SendPredictedMessage(new SprayPainterTabChangedMessage(index, isSelectedTabWithDecals));
|
||||||
|
}
|
||||||
|
|
||||||
|
private void OnSpritePicked(string group, string style)
|
||||||
|
{
|
||||||
|
SendPredictedMessage(new SprayPainterSetPaintableStyleMessage(group, style));
|
||||||
|
}
|
||||||
|
|
||||||
|
private void OnSetPipeColor(ItemList.ItemListSelectedEventArgs args)
|
||||||
{
|
{
|
||||||
var key = _window?.IndexToColorKey(args.ItemIndex);
|
var key = _window?.IndexToColorKey(args.ItemIndex);
|
||||||
SendMessage(new SprayPainterColorPickedMessage(key));
|
SendPredictedMessage(new SprayPainterSetPipeColorMessage(key));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
26
Content.Client/SprayPainter/UI/SprayPainterDecals.xaml
Normal file
26
Content.Client/SprayPainter/UI/SprayPainterDecals.xaml
Normal file
@@ -0,0 +1,26 @@
|
|||||||
|
<controls:SprayPainterDecals
|
||||||
|
xmlns="https://spacestation14.io"
|
||||||
|
xmlns:controls="clr-namespace:Content.Client.SprayPainter.UI">
|
||||||
|
<BoxContainer Orientation="Vertical">
|
||||||
|
<Label Text="{Loc 'spray-painter-selected-decals'}" />
|
||||||
|
<ScrollContainer VerticalExpand="True">
|
||||||
|
<GridContainer Columns="7" Name="DecalsGrid">
|
||||||
|
<!-- populated by code -->
|
||||||
|
</GridContainer>
|
||||||
|
</ScrollContainer>
|
||||||
|
|
||||||
|
<BoxContainer Orientation="Vertical">
|
||||||
|
<ColorSelectorSliders Name="ColorSelector" IsAlphaVisible="True" />
|
||||||
|
<CheckBox Name="UseCustomColorCheckBox" Text="{Loc 'spray-painter-use-custom-color'}" />
|
||||||
|
<CheckBox Name="SnapToTileCheckBox" Text="{Loc 'spray-painter-use-snap-to-tile'}" />
|
||||||
|
</BoxContainer>
|
||||||
|
|
||||||
|
<BoxContainer Orientation="Horizontal">
|
||||||
|
<Label Text="{Loc 'spray-painter-angle-rotation'}" />
|
||||||
|
<SpinBox Name="AngleSpinBox" HorizontalExpand="True" />
|
||||||
|
<Button Text="{Loc 'spray-painter-angle-rotation-90-sub'}" Name="SubAngleButton" />
|
||||||
|
<Button Text="{Loc 'spray-painter-angle-rotation-reset'}" Name="SetZeroAngleButton" />
|
||||||
|
<Button Text="{Loc 'spray-painter-angle-rotation-90-add'}" Name="AddAngleButton" />
|
||||||
|
</BoxContainer>
|
||||||
|
</BoxContainer>
|
||||||
|
</controls:SprayPainterDecals>
|
||||||
174
Content.Client/SprayPainter/UI/SprayPainterDecals.xaml.cs
Normal file
174
Content.Client/SprayPainter/UI/SprayPainterDecals.xaml.cs
Normal file
@@ -0,0 +1,174 @@
|
|||||||
|
using System.Numerics;
|
||||||
|
using Content.Client.Stylesheets;
|
||||||
|
using Content.Shared.Decals;
|
||||||
|
using Robust.Client.AutoGenerated;
|
||||||
|
using Robust.Client.GameObjects;
|
||||||
|
using Robust.Client.Graphics;
|
||||||
|
using Robust.Client.UserInterface;
|
||||||
|
using Robust.Client.UserInterface.Controls;
|
||||||
|
using Robust.Client.UserInterface.XAML;
|
||||||
|
using Robust.Shared.Prototypes;
|
||||||
|
|
||||||
|
namespace Content.Client.SprayPainter.UI;
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Used to control decal painting parameters for the spray painter.
|
||||||
|
/// </summary>
|
||||||
|
[GenerateTypedNameReferences]
|
||||||
|
public sealed partial class SprayPainterDecals : Control
|
||||||
|
{
|
||||||
|
public Action<ProtoId<DecalPrototype>>? OnDecalSelected;
|
||||||
|
public Action<Color?>? OnColorChanged;
|
||||||
|
public Action<int>? OnAngleChanged;
|
||||||
|
public Action<bool>? OnSnapChanged;
|
||||||
|
|
||||||
|
private SpriteSystem? _sprite;
|
||||||
|
private string _selectedDecal = string.Empty;
|
||||||
|
private List<SprayPainterDecalEntry> _decals = [];
|
||||||
|
|
||||||
|
public SprayPainterDecals()
|
||||||
|
{
|
||||||
|
RobustXamlLoader.Load(this);
|
||||||
|
|
||||||
|
AddAngleButton.OnButtonUp += _ => AngleSpinBox.Value += 90;
|
||||||
|
SubAngleButton.OnButtonUp += _ => AngleSpinBox.Value -= 90;
|
||||||
|
SetZeroAngleButton.OnButtonUp += _ => AngleSpinBox.Value = 0;
|
||||||
|
AngleSpinBox.ValueChanged += args => OnAngleChanged?.Invoke(args.Value);
|
||||||
|
|
||||||
|
UseCustomColorCheckBox.OnPressed += UseCustomColorCheckBoxOnOnPressed;
|
||||||
|
SnapToTileCheckBox.OnPressed += SnapToTileCheckBoxOnOnPressed;
|
||||||
|
ColorSelector.OnColorChanged += OnColorSelected;
|
||||||
|
}
|
||||||
|
|
||||||
|
private void UseCustomColorCheckBoxOnOnPressed(BaseButton.ButtonEventArgs _)
|
||||||
|
{
|
||||||
|
OnColorChanged?.Invoke(UseCustomColorCheckBox.Pressed ? ColorSelector.Color : null);
|
||||||
|
UpdateColorButtons(UseCustomColorCheckBox.Pressed);
|
||||||
|
}
|
||||||
|
|
||||||
|
private void SnapToTileCheckBoxOnOnPressed(BaseButton.ButtonEventArgs _)
|
||||||
|
{
|
||||||
|
OnSnapChanged?.Invoke(SnapToTileCheckBox.Pressed);
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Updates the decal list.
|
||||||
|
/// </summary>
|
||||||
|
public void PopulateDecals(List<SprayPainterDecalEntry> decals, SpriteSystem sprite)
|
||||||
|
{
|
||||||
|
_sprite ??= sprite;
|
||||||
|
|
||||||
|
_decals = decals;
|
||||||
|
DecalsGrid.Children.Clear();
|
||||||
|
|
||||||
|
foreach (var decal in decals)
|
||||||
|
{
|
||||||
|
var button = new TextureButton()
|
||||||
|
{
|
||||||
|
TextureNormal = sprite.Frame0(decal.Sprite),
|
||||||
|
Name = decal.Name,
|
||||||
|
ToolTip = decal.Name,
|
||||||
|
Scale = new Vector2(2, 2),
|
||||||
|
};
|
||||||
|
button.OnPressed += DecalButtonOnPressed;
|
||||||
|
|
||||||
|
if (UseCustomColorCheckBox.Pressed)
|
||||||
|
{
|
||||||
|
button.Modulate = ColorSelector.Color;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (_selectedDecal == decal.Name)
|
||||||
|
{
|
||||||
|
var panelContainer = new PanelContainer()
|
||||||
|
{
|
||||||
|
PanelOverride = new StyleBoxFlat()
|
||||||
|
{
|
||||||
|
BackgroundColor = StyleNano.ButtonColorDefault,
|
||||||
|
},
|
||||||
|
Children =
|
||||||
|
{
|
||||||
|
button,
|
||||||
|
},
|
||||||
|
};
|
||||||
|
DecalsGrid.AddChild(panelContainer);
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
DecalsGrid.AddChild(button);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private void OnColorSelected(Color color)
|
||||||
|
{
|
||||||
|
if (!UseCustomColorCheckBox.Pressed)
|
||||||
|
return;
|
||||||
|
|
||||||
|
OnColorChanged?.Invoke(color);
|
||||||
|
|
||||||
|
UpdateColorButtons(UseCustomColorCheckBox.Pressed);
|
||||||
|
}
|
||||||
|
|
||||||
|
private void UpdateColorButtons(bool apply)
|
||||||
|
{
|
||||||
|
Color modulateColor = apply ? ColorSelector.Color : Color.White;
|
||||||
|
foreach (var button in DecalsGrid.Children)
|
||||||
|
{
|
||||||
|
switch (button)
|
||||||
|
{
|
||||||
|
case TextureButton:
|
||||||
|
button.Modulate = modulateColor;
|
||||||
|
break;
|
||||||
|
case PanelContainer panelContainer:
|
||||||
|
{
|
||||||
|
foreach (TextureButton textureButton in panelContainer.Children)
|
||||||
|
textureButton.Modulate = modulateColor;
|
||||||
|
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private void DecalButtonOnPressed(BaseButton.ButtonEventArgs obj)
|
||||||
|
{
|
||||||
|
if (obj.Button.Name is not { } name)
|
||||||
|
return;
|
||||||
|
|
||||||
|
_selectedDecal = name;
|
||||||
|
OnDecalSelected?.Invoke(_selectedDecal);
|
||||||
|
|
||||||
|
if (_sprite is null)
|
||||||
|
return;
|
||||||
|
|
||||||
|
PopulateDecals(_decals, _sprite);
|
||||||
|
}
|
||||||
|
|
||||||
|
public void SetSelectedDecal(string name)
|
||||||
|
{
|
||||||
|
_selectedDecal = name;
|
||||||
|
|
||||||
|
if (_sprite is null)
|
||||||
|
return;
|
||||||
|
|
||||||
|
PopulateDecals(_decals, _sprite);
|
||||||
|
}
|
||||||
|
|
||||||
|
public void SetAngle(int degrees)
|
||||||
|
{
|
||||||
|
AngleSpinBox.OverrideValue(degrees);
|
||||||
|
}
|
||||||
|
|
||||||
|
public void SetColor(Color? color)
|
||||||
|
{
|
||||||
|
UseCustomColorCheckBox.Pressed = color != null;
|
||||||
|
if (color != null)
|
||||||
|
ColorSelector.Color = color.Value;
|
||||||
|
UpdateColorButtons(UseCustomColorCheckBox.Pressed);
|
||||||
|
}
|
||||||
|
|
||||||
|
public void SetSnap(bool snap)
|
||||||
|
{
|
||||||
|
SnapToTileCheckBox.Pressed = snap;
|
||||||
|
}
|
||||||
|
}
|
||||||
12
Content.Client/SprayPainter/UI/SprayPainterGroup.xaml
Normal file
12
Content.Client/SprayPainter/UI/SprayPainterGroup.xaml
Normal file
@@ -0,0 +1,12 @@
|
|||||||
|
<BoxContainer
|
||||||
|
xmlns="https://spacestation14.io"
|
||||||
|
xmlns:controls="clr-namespace:Content.Client.UserInterface.Controls"
|
||||||
|
Orientation="Vertical">
|
||||||
|
<Label Text="{Loc 'spray-painter-selected-style'}" />
|
||||||
|
<controls:ListContainer
|
||||||
|
Name="StyleList"
|
||||||
|
Toggle="True"
|
||||||
|
Group="True">
|
||||||
|
<!-- populated by code -->
|
||||||
|
</controls:ListContainer>
|
||||||
|
</BoxContainer>
|
||||||
66
Content.Client/SprayPainter/UI/SprayPainterGroup.xaml.cs
Normal file
66
Content.Client/SprayPainter/UI/SprayPainterGroup.xaml.cs
Normal file
@@ -0,0 +1,66 @@
|
|||||||
|
using Content.Client.UserInterface.Controls;
|
||||||
|
using Robust.Client.AutoGenerated;
|
||||||
|
using Robust.Client.UserInterface.Controls;
|
||||||
|
using Robust.Client.UserInterface.XAML;
|
||||||
|
|
||||||
|
namespace Content.Client.SprayPainter.UI;
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Used to display a group of paintable styles in the spray painter menu.
|
||||||
|
/// (e.g. each type of paintable locker or plastic crate)
|
||||||
|
/// </summary>
|
||||||
|
[GenerateTypedNameReferences]
|
||||||
|
public sealed partial class SprayPainterGroup : BoxContainer
|
||||||
|
{
|
||||||
|
public event Action<SpriteListData>? OnButtonPressed;
|
||||||
|
|
||||||
|
public SprayPainterGroup()
|
||||||
|
{
|
||||||
|
RobustXamlLoader.Load(this);
|
||||||
|
|
||||||
|
StyleList.GenerateItem = GenerateItems;
|
||||||
|
}
|
||||||
|
|
||||||
|
public void PopulateList(List<SpriteListData> spriteList)
|
||||||
|
{
|
||||||
|
StyleList.PopulateList(spriteList);
|
||||||
|
}
|
||||||
|
|
||||||
|
public void SelectItemByStyle(string key)
|
||||||
|
{
|
||||||
|
foreach (var elem in StyleList.Data)
|
||||||
|
{
|
||||||
|
if (elem is not SpriteListData spriteElem)
|
||||||
|
continue;
|
||||||
|
|
||||||
|
if (spriteElem.Style == key)
|
||||||
|
{
|
||||||
|
StyleList.Select(spriteElem);
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private void GenerateItems(ListData data, ListContainerButton button)
|
||||||
|
{
|
||||||
|
if (data is not SpriteListData spriteListData)
|
||||||
|
return;
|
||||||
|
|
||||||
|
var box = new BoxContainer() { Orientation = LayoutOrientation.Horizontal };
|
||||||
|
var protoView = new EntityPrototypeView();
|
||||||
|
protoView.SetPrototype(spriteListData.Prototype);
|
||||||
|
var label = new Label()
|
||||||
|
{
|
||||||
|
Text = Loc.GetString($"spray-painter-style-{spriteListData.Group.ToLower()}-{spriteListData.Style.ToLower()}")
|
||||||
|
};
|
||||||
|
|
||||||
|
box.AddChild(protoView);
|
||||||
|
box.AddChild(label);
|
||||||
|
button.AddChild(box);
|
||||||
|
button.AddStyleClass(ListContainer.StyleClassListContainerButton);
|
||||||
|
button.OnPressed += _ => OnButtonPressed?.Invoke(spriteListData);
|
||||||
|
|
||||||
|
if (spriteListData.SelectedIndex == button.Index)
|
||||||
|
button.Pressed = true;
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -1,34 +1,6 @@
|
|||||||
<DefaultWindow xmlns="https://spacestation14.io"
|
<DefaultWindow xmlns="https://spacestation14.io"
|
||||||
MinSize="500 300"
|
MinSize="520 300"
|
||||||
SetSize="500 500"
|
SetSize="520 700"
|
||||||
Title="{Loc 'spray-painter-window-title'}">
|
Title="{Loc 'spray-painter-window-title'}">
|
||||||
<BoxContainer Orientation="Horizontal"
|
<TabContainer Name="Tabs"/>
|
||||||
HorizontalExpand="True"
|
|
||||||
VerticalExpand="True"
|
|
||||||
SeparationOverride="4"
|
|
||||||
MinWidth="450">
|
|
||||||
<BoxContainer Orientation="Vertical"
|
|
||||||
HorizontalExpand="True"
|
|
||||||
VerticalExpand="True"
|
|
||||||
SeparationOverride="4"
|
|
||||||
MinWidth="200">
|
|
||||||
<Label Name="SelectedSpriteLabel"
|
|
||||||
Text="{Loc 'spray-painter-selected-style'}">
|
|
||||||
</Label>
|
|
||||||
<ItemList Name="SpriteList"
|
|
||||||
SizeFlagsStretchRatio="8"
|
|
||||||
VerticalExpand="True"/>
|
|
||||||
</BoxContainer>
|
|
||||||
<BoxContainer Orientation="Vertical"
|
|
||||||
HorizontalExpand="True"
|
|
||||||
VerticalExpand="True"
|
|
||||||
SeparationOverride="4"
|
|
||||||
MinWidth="200">
|
|
||||||
<Label Name="SelectedColorLabel"
|
|
||||||
Text="{Loc 'spray-painter-selected-color'}"/>
|
|
||||||
<ItemList Name="ColorList"
|
|
||||||
SizeFlagsStretchRatio="8"
|
|
||||||
VerticalExpand="True"/>
|
|
||||||
</BoxContainer>
|
|
||||||
</BoxContainer>
|
|
||||||
</DefaultWindow>
|
</DefaultWindow>
|
||||||
|
|||||||
@@ -1,12 +1,19 @@
|
|||||||
|
using System.Linq;
|
||||||
|
using Content.Client.UserInterface.Controls;
|
||||||
|
using Content.Shared.Decals;
|
||||||
using Robust.Client.AutoGenerated;
|
using Robust.Client.AutoGenerated;
|
||||||
using Robust.Client.GameObjects;
|
using Robust.Client.GameObjects;
|
||||||
using Robust.Client.UserInterface.Controls;
|
using Robust.Client.UserInterface.Controls;
|
||||||
using Robust.Client.UserInterface.CustomControls;
|
using Robust.Client.UserInterface.CustomControls;
|
||||||
using Robust.Client.UserInterface.XAML;
|
using Robust.Client.UserInterface.XAML;
|
||||||
|
using Robust.Shared.Prototypes;
|
||||||
using Robust.Shared.Utility;
|
using Robust.Shared.Utility;
|
||||||
|
|
||||||
namespace Content.Client.SprayPainter.UI;
|
namespace Content.Client.SprayPainter.UI;
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// A window to select spray painter settings by object type, as well as pipe colours and decals.
|
||||||
|
/// </summary>
|
||||||
[GenerateTypedNameReferences]
|
[GenerateTypedNameReferences]
|
||||||
public sealed partial class SprayPainterWindow : DefaultWindow
|
public sealed partial class SprayPainterWindow : DefaultWindow
|
||||||
{
|
{
|
||||||
@@ -15,13 +22,33 @@ public sealed partial class SprayPainterWindow : DefaultWindow
|
|||||||
|
|
||||||
private readonly SpriteSystem _spriteSystem;
|
private readonly SpriteSystem _spriteSystem;
|
||||||
|
|
||||||
public Action<ItemList.ItemListSelectedEventArgs>? OnSpritePicked;
|
// Events
|
||||||
public Action<ItemList.ItemListSelectedEventArgs>? OnColorPicked;
|
public event Action<string, string>? OnSpritePicked;
|
||||||
|
public event Action<int, bool>? OnTabChanged;
|
||||||
|
public event Action<ProtoId<DecalPrototype>>? OnDecalChanged;
|
||||||
|
public event Action<ItemList.ItemListSelectedEventArgs>? OnSetPipeColor;
|
||||||
|
public event Action<Color?>? OnDecalColorChanged;
|
||||||
|
public event Action<int>? OnDecalAngleChanged;
|
||||||
|
public event Action<bool>? OnDecalSnapChanged;
|
||||||
|
|
||||||
|
// Pipe color data
|
||||||
|
private ItemList _colorList = default!;
|
||||||
public Dictionary<string, int> ItemColorIndex = new();
|
public Dictionary<string, int> ItemColorIndex = new();
|
||||||
|
|
||||||
private Dictionary<string, Color> currentPalette = new();
|
private Dictionary<string, Color> _currentPalette = new();
|
||||||
private const string colorLocKeyPrefix = "pipe-painter-color-";
|
private const string ColorLocKeyPrefix = "pipe-painter-color-";
|
||||||
private List<SprayPainterEntry> CurrentEntries = new List<SprayPainterEntry>();
|
|
||||||
|
// Paintable objects
|
||||||
|
private Dictionary<string, Dictionary<string, EntProtoId>> _currentStylesByGroup = new();
|
||||||
|
private Dictionary<string, List<string>> _currentGroupsByCategory = new();
|
||||||
|
|
||||||
|
// Tab controls
|
||||||
|
private Dictionary<string, SprayPainterGroup> _paintableControls = new();
|
||||||
|
private BoxContainer? _pipeControl;
|
||||||
|
|
||||||
|
// Decals
|
||||||
|
private List<SprayPainterDecalEntry> _currentDecals = [];
|
||||||
|
private SprayPainterDecals? _sprayPainterDecals;
|
||||||
|
|
||||||
private readonly SpriteSpecifier _colorEntryIconTexture = new SpriteSpecifier.Rsi(
|
private readonly SpriteSpecifier _colorEntryIconTexture = new SpriteSpecifier.Rsi(
|
||||||
new ResPath("Structures/Piping/Atmospherics/pipe.rsi"),
|
new ResPath("Structures/Piping/Atmospherics/pipe.rsi"),
|
||||||
@@ -32,13 +59,14 @@ public sealed partial class SprayPainterWindow : DefaultWindow
|
|||||||
RobustXamlLoader.Load(this);
|
RobustXamlLoader.Load(this);
|
||||||
IoCManager.InjectDependencies(this);
|
IoCManager.InjectDependencies(this);
|
||||||
_spriteSystem = _sysMan.GetEntitySystem<SpriteSystem>();
|
_spriteSystem = _sysMan.GetEntitySystem<SpriteSystem>();
|
||||||
|
Tabs.OnTabChanged += (index) => OnTabChanged?.Invoke(index, _sprayPainterDecals?.GetPositionInParent() == index);
|
||||||
}
|
}
|
||||||
|
|
||||||
private string GetColorLocString(string? colorKey)
|
private string GetColorLocString(string? colorKey)
|
||||||
{
|
{
|
||||||
if (string.IsNullOrEmpty(colorKey))
|
if (string.IsNullOrEmpty(colorKey))
|
||||||
return Loc.GetString("pipe-painter-no-color-selected");
|
return Loc.GetString("pipe-painter-no-color-selected");
|
||||||
var locKey = colorLocKeyPrefix + colorKey;
|
var locKey = ColorLocKeyPrefix + colorKey;
|
||||||
|
|
||||||
if (!_loc.TryGetString(locKey, out var locString))
|
if (!_loc.TryGetString(locKey, out var locString))
|
||||||
locString = colorKey;
|
locString = colorKey;
|
||||||
@@ -48,51 +76,229 @@ public sealed partial class SprayPainterWindow : DefaultWindow
|
|||||||
|
|
||||||
public string? IndexToColorKey(int index)
|
public string? IndexToColorKey(int index)
|
||||||
{
|
{
|
||||||
return (string?) ColorList[index].Metadata;
|
return _colorList[index].Text;
|
||||||
}
|
}
|
||||||
|
|
||||||
public void Populate(List<SprayPainterEntry> entries, int selectedStyle, string? selectedColorKey, Dictionary<string, Color> palette)
|
private void OnStyleSelected(ListData data)
|
||||||
{
|
{
|
||||||
// Only clear if the entries change. Otherwise the list would "jump" after selecting an item
|
if (data is SpriteListData listData)
|
||||||
if (!CurrentEntries.Equals(entries))
|
OnSpritePicked?.Invoke(listData.Group, listData.Style);
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Wrapper to allow for selecting/deselecting the event to avoid loops
|
||||||
|
/// </summary>
|
||||||
|
private void OnColorPicked(ItemList.ItemListSelectedEventArgs args)
|
||||||
|
{
|
||||||
|
OnSetPipeColor?.Invoke(args);
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Setup function for the window.
|
||||||
|
/// </summary>
|
||||||
|
/// <param name="stylesByGroup">Each group, mapped by name to the set of named styles by their associated entity prototype.</param>
|
||||||
|
/// <param name="groupsByCategory">The set of categories and the groups associated with them.</param>
|
||||||
|
/// <param name="decals">A list of each decal.</param>
|
||||||
|
public void PopulateCategories(Dictionary<string, Dictionary<string, EntProtoId>> stylesByGroup, Dictionary<string, List<string>> groupsByCategory, List<SprayPainterDecalEntry> decals)
|
||||||
|
{
|
||||||
|
bool tabsCleared = false;
|
||||||
|
var lastTab = Tabs.CurrentTab;
|
||||||
|
|
||||||
|
if (!_currentGroupsByCategory.Equals(groupsByCategory))
|
||||||
{
|
{
|
||||||
CurrentEntries = entries;
|
// Destroy all existing tabs
|
||||||
SpriteList.Clear();
|
tabsCleared = true;
|
||||||
foreach (var entry in entries)
|
_paintableControls.Clear();
|
||||||
|
_pipeControl = null;
|
||||||
|
_sprayPainterDecals = null;
|
||||||
|
Tabs.RemoveAllChildren();
|
||||||
|
}
|
||||||
|
|
||||||
|
// Only clear if the entries change. Otherwise the list would "jump" after selecting an item
|
||||||
|
if (tabsCleared || !_currentStylesByGroup.Equals(stylesByGroup))
|
||||||
|
{
|
||||||
|
_currentStylesByGroup = stylesByGroup;
|
||||||
|
|
||||||
|
var tabIndex = 0;
|
||||||
|
foreach (var (categoryName, categoryGroups) in groupsByCategory.OrderBy(c => c.Key))
|
||||||
{
|
{
|
||||||
SpriteList.AddItem(entry.Name, entry.Icon);
|
if (categoryGroups.Count <= 0)
|
||||||
|
continue;
|
||||||
|
|
||||||
|
// Repopulating controls:
|
||||||
|
// ensure that categories with multiple groups have separate subtabs
|
||||||
|
// but single-group categories do not.
|
||||||
|
if (tabsCleared)
|
||||||
|
{
|
||||||
|
TabContainer? subTabs = null;
|
||||||
|
if (categoryGroups.Count > 1)
|
||||||
|
subTabs = new();
|
||||||
|
|
||||||
|
foreach (var group in categoryGroups)
|
||||||
|
{
|
||||||
|
if (!stylesByGroup.TryGetValue(group, out var styles))
|
||||||
|
continue;
|
||||||
|
|
||||||
|
var groupControl = new SprayPainterGroup();
|
||||||
|
groupControl.OnButtonPressed += OnStyleSelected;
|
||||||
|
_paintableControls[group] = groupControl;
|
||||||
|
if (categoryGroups.Count > 1)
|
||||||
|
{
|
||||||
|
if (subTabs != null)
|
||||||
|
{
|
||||||
|
subTabs?.AddChild(groupControl);
|
||||||
|
var subTabLocalization = Loc.GetString("spray-painter-tab-group-" + group.ToLower());
|
||||||
|
TabContainer.SetTabTitle(groupControl, subTabLocalization);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
Tabs.AddChild(groupControl);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if (subTabs != null)
|
||||||
|
Tabs.AddChild(subTabs);
|
||||||
|
|
||||||
|
var tabLocalization = Loc.GetString("spray-painter-tab-category-" + categoryName.ToLower());
|
||||||
|
Tabs.SetTabTitle(tabIndex, tabLocalization);
|
||||||
|
tabIndex++;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Finally, populate all groups with new data.
|
||||||
|
foreach (var group in categoryGroups)
|
||||||
|
{
|
||||||
|
if (!stylesByGroup.TryGetValue(group, out var styles) ||
|
||||||
|
!_paintableControls.TryGetValue(group, out var control))
|
||||||
|
continue;
|
||||||
|
|
||||||
|
var dataList = styles
|
||||||
|
.Select(e => new SpriteListData(group, e.Key, e.Value, 0))
|
||||||
|
.OrderBy(d => Loc.GetString($"spray-painter-style-{group.ToLower()}-{d.Style.ToLower()}"))
|
||||||
|
.ToList();
|
||||||
|
control.PopulateList(dataList);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
if (!currentPalette.Equals(palette))
|
PopulateColors(_currentPalette);
|
||||||
{
|
|
||||||
currentPalette = palette;
|
|
||||||
ItemColorIndex.Clear();
|
|
||||||
ColorList.Clear();
|
|
||||||
|
|
||||||
|
if (!_currentDecals.Equals(decals))
|
||||||
|
{
|
||||||
|
_currentDecals = decals;
|
||||||
|
|
||||||
|
if (_sprayPainterDecals is null)
|
||||||
|
{
|
||||||
|
_sprayPainterDecals = new SprayPainterDecals();
|
||||||
|
|
||||||
|
_sprayPainterDecals.OnDecalSelected += id => OnDecalChanged?.Invoke(id);
|
||||||
|
_sprayPainterDecals.OnColorChanged += color => OnDecalColorChanged?.Invoke(color);
|
||||||
|
_sprayPainterDecals.OnAngleChanged += angle => OnDecalAngleChanged?.Invoke(angle);
|
||||||
|
_sprayPainterDecals.OnSnapChanged += snap => OnDecalSnapChanged?.Invoke(snap);
|
||||||
|
|
||||||
|
Tabs.AddChild(_sprayPainterDecals);
|
||||||
|
TabContainer.SetTabTitle(_sprayPainterDecals, Loc.GetString("spray-painter-tab-category-decals"));
|
||||||
|
}
|
||||||
|
|
||||||
|
_sprayPainterDecals.PopulateDecals(decals, _spriteSystem);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (tabsCleared)
|
||||||
|
SetSelectedTab(lastTab);
|
||||||
|
}
|
||||||
|
|
||||||
|
public void PopulateColors(Dictionary<string, Color> palette)
|
||||||
|
{
|
||||||
|
// Create pipe tab controls if they don't exist
|
||||||
|
bool tabCreated = false;
|
||||||
|
if (_pipeControl == null)
|
||||||
|
{
|
||||||
|
_pipeControl = new BoxContainer() { Orientation = BoxContainer.LayoutOrientation.Vertical };
|
||||||
|
|
||||||
|
var label = new Label() { Text = Loc.GetString("spray-painter-selected-color") };
|
||||||
|
|
||||||
|
_colorList = new ItemList() { VerticalExpand = true };
|
||||||
|
_colorList.OnItemSelected += OnColorPicked;
|
||||||
|
|
||||||
|
_pipeControl.AddChild(label);
|
||||||
|
_pipeControl.AddChild(_colorList);
|
||||||
|
|
||||||
|
Tabs.AddChild(_pipeControl);
|
||||||
|
TabContainer.SetTabTitle(_pipeControl, Loc.GetString("spray-painter-tab-category-pipes"));
|
||||||
|
tabCreated = true;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Populate the tab if needed (new tab/new data)
|
||||||
|
if (tabCreated || !_currentPalette.Equals(palette))
|
||||||
|
{
|
||||||
|
_currentPalette = palette;
|
||||||
|
ItemColorIndex.Clear();
|
||||||
|
_colorList.Clear();
|
||||||
|
|
||||||
|
int index = 0;
|
||||||
foreach (var color in palette)
|
foreach (var color in palette)
|
||||||
{
|
{
|
||||||
var locString = GetColorLocString(color.Key);
|
var locString = GetColorLocString(color.Key);
|
||||||
var item = ColorList.AddItem(locString, _spriteSystem.Frame0(_colorEntryIconTexture));
|
var item = _colorList.AddItem(locString, _spriteSystem.Frame0(_colorEntryIconTexture), metadata: color.Key);
|
||||||
item.IconModulate = color.Value;
|
item.IconModulate = color.Value;
|
||||||
item.Metadata = color.Key;
|
|
||||||
|
|
||||||
ItemColorIndex.Add(color.Key, ColorList.IndexOf(item));
|
ItemColorIndex.Add(color.Key, index);
|
||||||
|
index++;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// Disable event so we don't send a new event for pre-selectedStyle entry and end up in a loop
|
|
||||||
|
|
||||||
if (selectedColorKey != null)
|
|
||||||
{
|
|
||||||
var index = ItemColorIndex[selectedColorKey];
|
|
||||||
ColorList.OnItemSelected -= OnColorPicked;
|
|
||||||
ColorList[index].Selected = true;
|
|
||||||
ColorList.OnItemSelected += OnColorPicked;
|
|
||||||
}
|
|
||||||
|
|
||||||
SpriteList.OnItemSelected -= OnSpritePicked;
|
|
||||||
SpriteList[selectedStyle].Selected = true;
|
|
||||||
SpriteList.OnItemSelected += OnSpritePicked;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
# region Setters
|
||||||
|
public void SetSelectedStyles(Dictionary<string, string> selectedStyles)
|
||||||
|
{
|
||||||
|
foreach (var (group, style) in selectedStyles)
|
||||||
|
{
|
||||||
|
if (!_paintableControls.TryGetValue(group, out var control))
|
||||||
|
continue;
|
||||||
|
|
||||||
|
control.SelectItemByStyle(style);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public void SelectColor(string color)
|
||||||
|
{
|
||||||
|
if (_colorList != null && ItemColorIndex.TryGetValue(color, out var colorIdx))
|
||||||
|
{
|
||||||
|
_colorList.OnItemSelected -= OnColorPicked;
|
||||||
|
_colorList[colorIdx].Selected = true;
|
||||||
|
_colorList.OnItemSelected += OnColorPicked;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public void SetSelectedTab(int tab)
|
||||||
|
{
|
||||||
|
Tabs.CurrentTab = int.Min(tab, Tabs.ChildCount - 1);
|
||||||
|
}
|
||||||
|
|
||||||
|
public void SetSelectedDecal(string decal)
|
||||||
|
{
|
||||||
|
if (_sprayPainterDecals != null)
|
||||||
|
_sprayPainterDecals.SetSelectedDecal(decal);
|
||||||
|
}
|
||||||
|
|
||||||
|
public void SetDecalAngle(int angle)
|
||||||
|
{
|
||||||
|
if (_sprayPainterDecals != null)
|
||||||
|
_sprayPainterDecals.SetAngle(angle);
|
||||||
|
}
|
||||||
|
|
||||||
|
public void SetDecalColor(Color? color)
|
||||||
|
{
|
||||||
|
if (_sprayPainterDecals != null)
|
||||||
|
_sprayPainterDecals.SetColor(color);
|
||||||
|
}
|
||||||
|
|
||||||
|
public void SetDecalSnap(bool snap)
|
||||||
|
{
|
||||||
|
if (_sprayPainterDecals != null)
|
||||||
|
_sprayPainterDecals.SetSnap(snap);
|
||||||
|
}
|
||||||
|
# endregion
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public record SpriteListData(string Group, string Style, EntProtoId Prototype, int SelectedIndex) : ListData;
|
||||||
|
|||||||
@@ -1,10 +1,15 @@
|
|||||||
|
using Content.Shared.SprayPainter.Prototypes;
|
||||||
using Content.Shared.Storage;
|
using Content.Shared.Storage;
|
||||||
using Robust.Client.GameObjects;
|
using Robust.Client.GameObjects;
|
||||||
|
using Robust.Shared.Prototypes;
|
||||||
|
|
||||||
namespace Content.Client.Storage.Visualizers;
|
namespace Content.Client.Storage.Visualizers;
|
||||||
|
|
||||||
public sealed class EntityStorageVisualizerSystem : VisualizerSystem<EntityStorageVisualsComponent>
|
public sealed class EntityStorageVisualizerSystem : VisualizerSystem<EntityStorageVisualsComponent>
|
||||||
{
|
{
|
||||||
|
[Dependency] private readonly IPrototypeManager _prototypeManager = default!;
|
||||||
|
[Dependency] private readonly IComponentFactory _componentFactory = default!;
|
||||||
|
|
||||||
public override void Initialize()
|
public override void Initialize()
|
||||||
{
|
{
|
||||||
base.Initialize();
|
base.Initialize();
|
||||||
@@ -26,12 +31,34 @@ public sealed class EntityStorageVisualizerSystem : VisualizerSystem<EntityStora
|
|||||||
SpriteSystem.LayerSetRsiState((uid, sprite), StorageVisualLayers.Base, comp.StateBaseClosed);
|
SpriteSystem.LayerSetRsiState((uid, sprite), StorageVisualLayers.Base, comp.StateBaseClosed);
|
||||||
}
|
}
|
||||||
|
|
||||||
protected override void OnAppearanceChange(EntityUid uid, EntityStorageVisualsComponent comp, ref AppearanceChangeEvent args)
|
protected override void OnAppearanceChange(EntityUid uid,
|
||||||
|
EntityStorageVisualsComponent comp,
|
||||||
|
ref AppearanceChangeEvent args)
|
||||||
{
|
{
|
||||||
if (args.Sprite == null
|
if (args.Sprite == null
|
||||||
|| !AppearanceSystem.TryGetData<bool>(uid, StorageVisuals.Open, out var open, args.Component))
|
|| !AppearanceSystem.TryGetData<bool>(uid, StorageVisuals.Open, out var open, args.Component))
|
||||||
return;
|
return;
|
||||||
|
|
||||||
|
var forceRedrawBase = false;
|
||||||
|
if (AppearanceSystem.TryGetData<string>(uid, PaintableVisuals.Prototype, out var prototype, args.Component))
|
||||||
|
{
|
||||||
|
if (_prototypeManager.TryIndex(prototype, out var proto))
|
||||||
|
{
|
||||||
|
if (proto.TryGetComponent(out SpriteComponent? sprite, _componentFactory))
|
||||||
|
{
|
||||||
|
SpriteSystem.SetBaseRsi((uid, args.Sprite), sprite.BaseRSI);
|
||||||
|
}
|
||||||
|
if (proto.TryGetComponent(out EntityStorageVisualsComponent? visuals, _componentFactory))
|
||||||
|
{
|
||||||
|
comp.StateBaseOpen = visuals.StateBaseOpen;
|
||||||
|
comp.StateBaseClosed = visuals.StateBaseClosed;
|
||||||
|
comp.StateDoorOpen = visuals.StateDoorOpen;
|
||||||
|
comp.StateDoorClosed = visuals.StateDoorClosed;
|
||||||
|
forceRedrawBase = true;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
// Open/Closed state for the storage entity.
|
// Open/Closed state for the storage entity.
|
||||||
if (SpriteSystem.LayerMapTryGet((uid, args.Sprite), StorageVisualLayers.Door, out _, false))
|
if (SpriteSystem.LayerMapTryGet((uid, args.Sprite), StorageVisualLayers.Door, out _, false))
|
||||||
{
|
{
|
||||||
@@ -52,6 +79,8 @@ public sealed class EntityStorageVisualizerSystem : VisualizerSystem<EntityStora
|
|||||||
|
|
||||||
if (comp.StateBaseOpen != null)
|
if (comp.StateBaseOpen != null)
|
||||||
SpriteSystem.LayerSetRsiState((uid, args.Sprite), StorageVisualLayers.Base, comp.StateBaseOpen);
|
SpriteSystem.LayerSetRsiState((uid, args.Sprite), StorageVisualLayers.Base, comp.StateBaseOpen);
|
||||||
|
else if (forceRedrawBase && comp.StateBaseClosed != null)
|
||||||
|
SpriteSystem.LayerSetRsiState((uid, args.Sprite), StorageVisualLayers.Base, comp.StateBaseClosed);
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
@@ -68,6 +97,8 @@ public sealed class EntityStorageVisualizerSystem : VisualizerSystem<EntityStora
|
|||||||
|
|
||||||
if (comp.StateBaseClosed != null)
|
if (comp.StateBaseClosed != null)
|
||||||
SpriteSystem.LayerSetRsiState((uid, args.Sprite), StorageVisualLayers.Base, comp.StateBaseClosed);
|
SpriteSystem.LayerSetRsiState((uid, args.Sprite), StorageVisualLayers.Base, comp.StateBaseClosed);
|
||||||
|
else if (forceRedrawBase && comp.StateBaseOpen != null)
|
||||||
|
SpriteSystem.LayerSetRsiState((uid, args.Sprite), StorageVisualLayers.Base, comp.StateBaseOpen);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,27 +1,134 @@
|
|||||||
using Content.Server.Atmos.Piping.Components;
|
using Content.Server.Atmos.Piping.Components;
|
||||||
using Content.Server.Atmos.Piping.EntitySystems;
|
using Content.Server.Atmos.Piping.EntitySystems;
|
||||||
|
using Content.Server.Charges;
|
||||||
|
using Content.Server.Decals;
|
||||||
|
using Content.Server.Destructible;
|
||||||
|
using Content.Server.Popups;
|
||||||
|
using Content.Shared.Atmos.Piping.Unary.Components;
|
||||||
|
using Content.Shared.Charges.Components;
|
||||||
|
using Content.Shared.Coordinates.Helpers;
|
||||||
|
using Content.Shared.Database;
|
||||||
|
using Content.Shared.Decals;
|
||||||
using Content.Shared.DoAfter;
|
using Content.Shared.DoAfter;
|
||||||
using Content.Shared.Interaction;
|
using Content.Shared.Interaction;
|
||||||
using Content.Shared.SprayPainter;
|
using Content.Shared.SprayPainter;
|
||||||
using Content.Shared.SprayPainter.Components;
|
using Content.Shared.SprayPainter.Components;
|
||||||
|
using Robust.Server.Audio;
|
||||||
|
using Robust.Server.GameObjects;
|
||||||
|
using Robust.Shared.Prototypes;
|
||||||
|
|
||||||
namespace Content.Server.SprayPainter;
|
namespace Content.Server.SprayPainter;
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// Handles spraying pipes using a spray painter.
|
/// Handles spraying pipes and decals using a spray painter.
|
||||||
/// Airlocks are handled in shared.
|
/// Other paintable objects are handled in shared.
|
||||||
/// </summary>
|
/// </summary>
|
||||||
public sealed class SprayPainterSystem : SharedSprayPainterSystem
|
public sealed class SprayPainterSystem : SharedSprayPainterSystem
|
||||||
{
|
{
|
||||||
[Dependency] private readonly AtmosPipeColorSystem _pipeColor = default!;
|
[Dependency] private readonly AtmosPipeColorSystem _pipeColor = default!;
|
||||||
|
[Dependency] private readonly PopupSystem _popup = default!;
|
||||||
|
[Dependency] private readonly DecalSystem _decals = default!;
|
||||||
|
[Dependency] private readonly AudioSystem _audio = default!;
|
||||||
|
[Dependency] private readonly ChargesSystem _charges = default!;
|
||||||
|
[Dependency] private readonly TransformSystem _transform = default!;
|
||||||
|
|
||||||
public override void Initialize()
|
public override void Initialize()
|
||||||
{
|
{
|
||||||
base.Initialize();
|
base.Initialize();
|
||||||
|
|
||||||
SubscribeLocalEvent<SprayPainterComponent, SprayPainterPipeDoAfterEvent>(OnPipeDoAfter);
|
SubscribeLocalEvent<SprayPainterComponent, SprayPainterPipeDoAfterEvent>(OnPipeDoAfter);
|
||||||
|
SubscribeLocalEvent<SprayPainterComponent, AfterInteractEvent>(OnFloorAfterInteract);
|
||||||
SubscribeLocalEvent<AtmosPipeColorComponent, InteractUsingEvent>(OnPipeInteract);
|
SubscribeLocalEvent<AtmosPipeColorComponent, InteractUsingEvent>(OnPipeInteract);
|
||||||
|
SubscribeLocalEvent<GasCanisterComponent, EntityPaintedEvent>(OnCanisterPainted);
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Handles drawing decals when a spray painter is used to interact with the floor.
|
||||||
|
/// Spray painter must have decal painting enabled and enough charges of paint to paint on the floor.
|
||||||
|
/// </summary>
|
||||||
|
private void OnFloorAfterInteract(Entity<SprayPainterComponent> ent, ref AfterInteractEvent args)
|
||||||
|
{
|
||||||
|
if (args.Handled || !args.CanReach || args.Target != null)
|
||||||
|
return;
|
||||||
|
|
||||||
|
// Includes both off and all other don't cares
|
||||||
|
if (ent.Comp.DecalMode != DecalPaintMode.Add && ent.Comp.DecalMode != DecalPaintMode.Remove)
|
||||||
|
return;
|
||||||
|
|
||||||
|
args.Handled = true;
|
||||||
|
if (TryComp(ent, out LimitedChargesComponent? charges) && charges.LastCharges < ent.Comp.DecalChargeCost)
|
||||||
|
{
|
||||||
|
_popup.PopupEntity(Loc.GetString("spray-painter-interact-no-charges"), args.User, args.User);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
var position = args.ClickLocation;
|
||||||
|
if (ent.Comp.SnapDecals)
|
||||||
|
position = position.SnapToGrid(EntityManager);
|
||||||
|
|
||||||
|
if (ent.Comp.DecalMode == DecalPaintMode.Add)
|
||||||
|
{
|
||||||
|
// Offset painting for adding decals
|
||||||
|
position = position.Offset(new(-0.5f));
|
||||||
|
|
||||||
|
if (!_decals.TryAddDecal(ent.Comp.SelectedDecal, position, out _, ent.Comp.SelectedDecalColor, Angle.FromDegrees(ent.Comp.SelectedDecalAngle), 0, false))
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
var gridUid = _transform.GetGrid(args.ClickLocation);
|
||||||
|
if (gridUid is not { } grid || !TryComp<DecalGridComponent>(grid, out var decalGridComp))
|
||||||
|
{
|
||||||
|
_popup.PopupEntity(Loc.GetString("spray-painter-interact-nothing-to-remove"), args.User, args.User);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
var decals = _decals.GetDecalsInRange(grid, position.Position, validDelegate: IsDecalRemovable);
|
||||||
|
if (decals.Count <= 0)
|
||||||
|
{
|
||||||
|
_popup.PopupEntity(Loc.GetString("spray-painter-interact-nothing-to-remove"), args.User, args.User);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
foreach (var decal in decals)
|
||||||
|
{
|
||||||
|
_decals.RemoveDecal(grid, decal.Index, decalGridComp);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
_audio.PlayPvs(ent.Comp.SpraySound, ent);
|
||||||
|
|
||||||
|
_charges.TryUseCharges((ent, charges), ent.Comp.DecalChargeCost);
|
||||||
|
|
||||||
|
AdminLogger.Add(LogType.CrayonDraw, LogImpact.Low, $"{EntityManager.ToPrettyString(args.User):user} painted a {ent.Comp.SelectedDecal}");
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Handles drawing decals when a spray painter is used to interact with the floor.
|
||||||
|
/// Spray painter must have decal painting enabled and enough charges of paint to paint on the floor.
|
||||||
|
/// </summary>
|
||||||
|
private bool IsDecalRemovable(Decal decal)
|
||||||
|
{
|
||||||
|
if (!Proto.TryIndex<DecalPrototype>(decal.Id, out var decalProto))
|
||||||
|
return false;
|
||||||
|
|
||||||
|
return (decalProto.Tags.Contains("station")
|
||||||
|
|| decalProto.Tags.Contains("markings"))
|
||||||
|
&& !decalProto.Tags.Contains("dirty");
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Event handler when gas canisters are painted.
|
||||||
|
/// The canister's color should not change when it's destroyed.
|
||||||
|
/// </summary>
|
||||||
|
private void OnCanisterPainted(Entity<GasCanisterComponent> ent, ref EntityPaintedEvent args)
|
||||||
|
{
|
||||||
|
var dummy = Spawn(args.Prototype);
|
||||||
|
|
||||||
|
var destructibleComp = EnsureComp<DestructibleComponent>(dummy);
|
||||||
|
CopyComp(dummy, ent, destructibleComp);
|
||||||
|
|
||||||
|
Del(dummy);
|
||||||
}
|
}
|
||||||
|
|
||||||
private void OnPipeDoAfter(Entity<SprayPainterComponent> ent, ref SprayPainterPipeDoAfterEvent args)
|
private void OnPipeDoAfter(Entity<SprayPainterComponent> ent, ref SprayPainterPipeDoAfterEvent args)
|
||||||
@@ -29,14 +136,17 @@ public sealed class SprayPainterSystem : SharedSprayPainterSystem
|
|||||||
if (args.Handled || args.Cancelled)
|
if (args.Handled || args.Cancelled)
|
||||||
return;
|
return;
|
||||||
|
|
||||||
if (args.Args.Target is not {} target)
|
if (args.Args.Target is not { } target)
|
||||||
return;
|
return;
|
||||||
|
|
||||||
if (!TryComp<AtmosPipeColorComponent>(target, out var color))
|
if (!TryComp<AtmosPipeColorComponent>(target, out var color))
|
||||||
return;
|
return;
|
||||||
|
|
||||||
Audio.PlayPvs(ent.Comp.SpraySound, ent);
|
if (TryComp<LimitedChargesComponent>(ent, out var charges) &&
|
||||||
|
!_charges.TryUseCharges((ent, charges), ent.Comp.PipeChargeCost))
|
||||||
|
return;
|
||||||
|
|
||||||
|
Audio.PlayPvs(ent.Comp.SpraySound, ent);
|
||||||
_pipeColor.SetColor(target, color, args.Color);
|
_pipeColor.SetColor(target, color, args.Color);
|
||||||
|
|
||||||
args.Handled = true;
|
args.Handled = true;
|
||||||
@@ -47,13 +157,28 @@ public sealed class SprayPainterSystem : SharedSprayPainterSystem
|
|||||||
if (args.Handled)
|
if (args.Handled)
|
||||||
return;
|
return;
|
||||||
|
|
||||||
if (!TryComp<SprayPainterComponent>(args.Used, out var painter) || painter.PickedColor is not {} colorName)
|
if (!TryComp<SprayPainterComponent>(args.Used, out var painter) ||
|
||||||
|
painter.PickedColor is not { } colorName)
|
||||||
return;
|
return;
|
||||||
|
|
||||||
if (!painter.ColorPalette.TryGetValue(colorName, out var color))
|
if (!painter.ColorPalette.TryGetValue(colorName, out var color))
|
||||||
return;
|
return;
|
||||||
|
|
||||||
var doAfterEventArgs = new DoAfterArgs(EntityManager, args.User, painter.PipeSprayTime, new SprayPainterPipeDoAfterEvent(color), args.Used, target: ent, used: args.Used)
|
if (TryComp<LimitedChargesComponent>(args.Used, out var charges)
|
||||||
|
&& charges.LastCharges < painter.PipeChargeCost)
|
||||||
|
{
|
||||||
|
var msg = Loc.GetString("spray-painter-interact-no-charges");
|
||||||
|
_popup.PopupEntity(msg, args.User, args.User);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
var doAfterEventArgs = new DoAfterArgs(EntityManager,
|
||||||
|
args.User,
|
||||||
|
painter.PipeSprayTime,
|
||||||
|
new SprayPainterPipeDoAfterEvent(color),
|
||||||
|
args.Used,
|
||||||
|
target: ent,
|
||||||
|
used: args.Used)
|
||||||
{
|
{
|
||||||
BreakOnMove = true,
|
BreakOnMove = true,
|
||||||
BreakOnDamage = true,
|
BreakOnDamage = true,
|
||||||
|
|||||||
@@ -317,7 +317,6 @@ public enum DoorVisuals : byte
|
|||||||
BoltLights,
|
BoltLights,
|
||||||
EmergencyLights,
|
EmergencyLights,
|
||||||
ClosedLights,
|
ClosedLights,
|
||||||
BaseRSI,
|
|
||||||
}
|
}
|
||||||
|
|
||||||
public enum DoorVisualLayers : byte
|
public enum DoorVisualLayers : byte
|
||||||
|
|||||||
@@ -1,24 +0,0 @@
|
|||||||
using Content.Shared.Roles;
|
|
||||||
using Content.Shared.SprayPainter.Prototypes;
|
|
||||||
using Robust.Shared.GameStates;
|
|
||||||
using Robust.Shared.Prototypes;
|
|
||||||
|
|
||||||
namespace Content.Shared.SprayPainter.Components;
|
|
||||||
|
|
||||||
[RegisterComponent, NetworkedComponent, AutoGenerateComponentState]
|
|
||||||
public sealed partial class PaintableAirlockComponent : Component
|
|
||||||
{
|
|
||||||
/// <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;
|
|
||||||
}
|
|
||||||
19
Content.Shared/SprayPainter/Components/PaintableComponent.cs
Normal file
19
Content.Shared/SprayPainter/Components/PaintableComponent.cs
Normal file
@@ -0,0 +1,19 @@
|
|||||||
|
using Content.Shared.SprayPainter.Prototypes;
|
||||||
|
using Robust.Shared.GameStates;
|
||||||
|
using Robust.Shared.Prototypes;
|
||||||
|
|
||||||
|
namespace Content.Shared.SprayPainter.Components;
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Marks objects that can be painted with the spray painter.
|
||||||
|
/// </summary>
|
||||||
|
[RegisterComponent, NetworkedComponent]
|
||||||
|
public sealed partial class PaintableComponent : Component
|
||||||
|
{
|
||||||
|
/// <summary>
|
||||||
|
/// Group of styles this airlock can be painted with, e.g. glass, standard or external.
|
||||||
|
/// Set to null to make an entity unpaintable.
|
||||||
|
/// </summary>
|
||||||
|
[DataField(required: true)]
|
||||||
|
public ProtoId<PaintableGroupPrototype>? Group;
|
||||||
|
}
|
||||||
18
Content.Shared/SprayPainter/Components/PaintedComponent.cs
Normal file
18
Content.Shared/SprayPainter/Components/PaintedComponent.cs
Normal file
@@ -0,0 +1,18 @@
|
|||||||
|
using Robust.Shared.GameStates;
|
||||||
|
using Robust.Shared.Serialization.TypeSerializers.Implementations.Custom;
|
||||||
|
|
||||||
|
namespace Content.Shared.SprayPainter.Components;
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Used to mark an entity that has been repainted.
|
||||||
|
/// </summary>
|
||||||
|
[RegisterComponent, NetworkedComponent]
|
||||||
|
[AutoGenerateComponentState, AutoGenerateComponentPause]
|
||||||
|
public sealed partial class PaintedComponent : Component
|
||||||
|
{
|
||||||
|
/// <summary>
|
||||||
|
/// The time after which the entity is dried and does not appear as "freshly painted".
|
||||||
|
/// </summary>
|
||||||
|
[DataField(customTypeSerializer: typeof(TimeOffsetSerializer)), AutoNetworkedField, AutoPausedField]
|
||||||
|
public TimeSpan DryTime;
|
||||||
|
}
|
||||||
17
Content.Shared/SprayPainter/Components/SprayPainterAmmo.cs
Normal file
17
Content.Shared/SprayPainter/Components/SprayPainterAmmo.cs
Normal file
@@ -0,0 +1,17 @@
|
|||||||
|
using Robust.Shared.GameStates;
|
||||||
|
|
||||||
|
namespace Content.Shared.SprayPainter.Components;
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Items with this component can be used to recharge a spray painter.
|
||||||
|
/// </summary>
|
||||||
|
[RegisterComponent, NetworkedComponent, AutoGenerateComponentState]
|
||||||
|
[Access(typeof(SprayPainterAmmoSystem))]
|
||||||
|
public sealed partial class SprayPainterAmmoComponent : Component
|
||||||
|
{
|
||||||
|
/// <summary>
|
||||||
|
/// The value by which the charge in the spray painter will be recharged.
|
||||||
|
/// </summary>
|
||||||
|
[DataField, AutoNetworkedField]
|
||||||
|
public int Charges = 15;
|
||||||
|
}
|
||||||
@@ -1,26 +1,42 @@
|
|||||||
using Content.Shared.DoAfter;
|
using Content.Shared.Decals;
|
||||||
using Robust.Shared.Audio;
|
using Robust.Shared.Audio;
|
||||||
using Robust.Shared.GameStates;
|
using Robust.Shared.GameStates;
|
||||||
|
using Robust.Shared.Prototypes;
|
||||||
|
|
||||||
namespace Content.Shared.SprayPainter.Components;
|
namespace Content.Shared.SprayPainter.Components;
|
||||||
|
|
||||||
[RegisterComponent, NetworkedComponent, AutoGenerateComponentState]
|
/// <summary>
|
||||||
|
/// Denotes an object that can be used to alter the appearance of paintable objects (e.g. doors, gas canisters).
|
||||||
|
/// </summary>
|
||||||
|
[RegisterComponent, NetworkedComponent, AutoGenerateComponentState(true)]
|
||||||
public sealed partial class SprayPainterComponent : Component
|
public sealed partial class SprayPainterComponent : Component
|
||||||
{
|
{
|
||||||
|
public const string DefaultPickedColor = "red";
|
||||||
|
public static readonly ProtoId<DecalPrototype> DefaultDecal = "Arrows";
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// The sound to be played after painting the entities.
|
||||||
|
/// </summary>
|
||||||
[DataField]
|
[DataField]
|
||||||
public SoundSpecifier SpraySound = new SoundPathSpecifier("/Audio/Effects/spray2.ogg");
|
public SoundSpecifier SpraySound = new SoundPathSpecifier("/Audio/Effects/spray2.ogg");
|
||||||
|
|
||||||
[DataField]
|
/// <summary>
|
||||||
public TimeSpan AirlockSprayTime = TimeSpan.FromSeconds(3);
|
/// The amount of time it takes to paint a pipe.
|
||||||
|
/// </summary>
|
||||||
[DataField]
|
[DataField]
|
||||||
public TimeSpan PipeSprayTime = TimeSpan.FromSeconds(1);
|
public TimeSpan PipeSprayTime = TimeSpan.FromSeconds(1);
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// The cost of spray painting a pipe, in charges.
|
||||||
|
/// </summary>
|
||||||
|
[DataField]
|
||||||
|
public int PipeChargeCost = 1;
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// Pipe color chosen to spray with.
|
/// Pipe color chosen to spray with.
|
||||||
/// </summary>
|
/// </summary>
|
||||||
[DataField, AutoNetworkedField]
|
[DataField, AutoNetworkedField]
|
||||||
public string? PickedColor;
|
public string PickedColor = DefaultPickedColor;
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// Pipe colors that can be selected.
|
/// Pipe colors that can be selected.
|
||||||
@@ -29,9 +45,82 @@ public sealed partial class SprayPainterComponent : Component
|
|||||||
public Dictionary<string, Color> ColorPalette = new();
|
public Dictionary<string, Color> ColorPalette = new();
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// Airlock style index selected.
|
/// Spray paintable object styles selected per object.
|
||||||
/// After prototype reload this might not be the same style but it will never be out of bounds.
|
|
||||||
/// </summary>
|
/// </summary>
|
||||||
[DataField, AutoNetworkedField]
|
[DataField, AutoNetworkedField]
|
||||||
public int Index;
|
public Dictionary<string, string> StylesByGroup = new();
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// The currently open tab of the painter
|
||||||
|
/// (Are you selecting canister color?)
|
||||||
|
/// </summary>
|
||||||
|
[DataField, AutoNetworkedField]
|
||||||
|
public int SelectedTab;
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Whether or not the painter should be painting or removing decals when clicked.
|
||||||
|
/// </summary>
|
||||||
|
[DataField, AutoNetworkedField]
|
||||||
|
public DecalPaintMode DecalMode = DecalPaintMode.Off;
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// The currently selected decal prototype.
|
||||||
|
/// </summary>
|
||||||
|
[DataField, AutoNetworkedField]
|
||||||
|
public ProtoId<DecalPrototype> SelectedDecal = DefaultDecal;
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// The color in which to paint the decal.
|
||||||
|
/// </summary>
|
||||||
|
[DataField, AutoNetworkedField]
|
||||||
|
public Color? SelectedDecalColor;
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// The angle at which to paint the decal.
|
||||||
|
/// </summary>
|
||||||
|
[DataField, AutoNetworkedField]
|
||||||
|
public int SelectedDecalAngle;
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// The angle at which to paint the decal.
|
||||||
|
/// </summary>
|
||||||
|
[DataField, AutoNetworkedField]
|
||||||
|
public bool SnapDecals = true;
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// The cost of spray painting a decal, in charges.
|
||||||
|
/// </summary>
|
||||||
|
[DataField]
|
||||||
|
public int DecalChargeCost = 1;
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// How long does the painter leave items as freshly painted?
|
||||||
|
/// </summary>
|
||||||
|
[DataField]
|
||||||
|
public TimeSpan FreshPaintDuration = TimeSpan.FromMinutes(15);
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// The sound to play when swapping between decal modes.
|
||||||
|
/// </summary>
|
||||||
|
[DataField]
|
||||||
|
public SoundSpecifier SoundSwitchDecalMode = new SoundPathSpecifier("/Audio/Machines/quickbeep.ogg", AudioParams.Default.WithVolume(1.5f));
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// A set of operating modes for decal painting.
|
||||||
|
/// </summary>
|
||||||
|
public enum DecalPaintMode : byte
|
||||||
|
{
|
||||||
|
/// <summary>
|
||||||
|
/// Clicking on the floor does nothing.
|
||||||
|
/// </summary>
|
||||||
|
Off = 0,
|
||||||
|
/// <summary>
|
||||||
|
/// Clicking on the floor adds a decal at the requested spot (or snapped to the grid)
|
||||||
|
/// </summary>
|
||||||
|
Add = 1,
|
||||||
|
/// <summary>
|
||||||
|
/// Clicking on the floor removes all decals at the requested spot (or snapped to the grid)
|
||||||
|
/// </summary>
|
||||||
|
Remove = 2,
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,21 +0,0 @@
|
|||||||
using Content.Shared.Roles;
|
|
||||||
using Robust.Shared.Prototypes;
|
|
||||||
|
|
||||||
namespace Content.Shared.SprayPainter.Prototypes;
|
|
||||||
|
|
||||||
/// <summary>
|
|
||||||
/// Maps airlock style names to department ids.
|
|
||||||
/// </summary>
|
|
||||||
[Prototype]
|
|
||||||
public sealed partial 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();
|
|
||||||
}
|
|
||||||
@@ -1,19 +0,0 @@
|
|||||||
using Robust.Shared.Prototypes;
|
|
||||||
|
|
||||||
namespace Content.Shared.SprayPainter.Prototypes;
|
|
||||||
|
|
||||||
[Prototype("AirlockGroup")]
|
|
||||||
public sealed partial class AirlockGroupPrototype : IPrototype
|
|
||||||
{
|
|
||||||
[IdDataField]
|
|
||||||
public string ID { get; private set; } = default!;
|
|
||||||
|
|
||||||
[DataField("stylePaths")]
|
|
||||||
public Dictionary<string, string> StylePaths = default!;
|
|
||||||
|
|
||||||
// The priority determines, which sprite is used when showing
|
|
||||||
// the icon for a style in the SprayPainter UI. The highest priority
|
|
||||||
// gets shown.
|
|
||||||
[DataField("iconPriority")]
|
|
||||||
public int IconPriority = 0;
|
|
||||||
}
|
|
||||||
@@ -0,0 +1,19 @@
|
|||||||
|
using Robust.Shared.Prototypes;
|
||||||
|
|
||||||
|
namespace Content.Shared.SprayPainter.Prototypes;
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// A category of spray paintable items (e.g. airlocks, crates)
|
||||||
|
/// </summary>
|
||||||
|
[Prototype]
|
||||||
|
public sealed partial class PaintableGroupCategoryPrototype : IPrototype
|
||||||
|
{
|
||||||
|
[IdDataField]
|
||||||
|
public string ID { get; private set; } = default!;
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Each group that makes up this category.
|
||||||
|
/// </summary>
|
||||||
|
[DataField(required: true)]
|
||||||
|
public List<ProtoId<PaintableGroupPrototype>> Groups = new();
|
||||||
|
}
|
||||||
@@ -0,0 +1,53 @@
|
|||||||
|
using Robust.Shared.Prototypes;
|
||||||
|
using Robust.Shared.Serialization;
|
||||||
|
|
||||||
|
namespace Content.Shared.SprayPainter.Prototypes;
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Contains a map of the objects from which the spray painter will take texture to paint another from the same group.
|
||||||
|
/// </summary>
|
||||||
|
[Prototype]
|
||||||
|
public sealed partial class PaintableGroupPrototype : IPrototype
|
||||||
|
{
|
||||||
|
[IdDataField]
|
||||||
|
public string ID { get; private set; } = default!;
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// The time required to paint an object from a given group, in seconds.
|
||||||
|
/// </summary>
|
||||||
|
[DataField]
|
||||||
|
public float Time = 2.0f;
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// To number of charges needed to paint an object of this group.
|
||||||
|
/// </summary>
|
||||||
|
[DataField]
|
||||||
|
public int Cost = 1;
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// The default style to start painting.
|
||||||
|
/// </summary>
|
||||||
|
[DataField(required: true)]
|
||||||
|
public string DefaultStyle = default!;
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Map from localization keys and entity identifiers displayed in the spray painter menu.
|
||||||
|
/// </summary>
|
||||||
|
[DataField(required: true)]
|
||||||
|
public Dictionary<string, EntProtoId> Styles = new();
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// If multiple groups have the same key, the group with the highest IconPriority has its icon displayed.
|
||||||
|
/// </summary>
|
||||||
|
[DataField]
|
||||||
|
public int IconPriority;
|
||||||
|
}
|
||||||
|
|
||||||
|
[Serializable, NetSerializable]
|
||||||
|
public enum PaintableVisuals
|
||||||
|
{
|
||||||
|
/// <summary>
|
||||||
|
/// The prototype to base the object's visuals off.
|
||||||
|
/// </summary>
|
||||||
|
Prototype
|
||||||
|
}
|
||||||
@@ -1,100 +1,77 @@
|
|||||||
using Content.Shared.Administration.Logs;
|
using Content.Shared.Administration.Logs;
|
||||||
|
using Content.Shared.Charges.Components;
|
||||||
|
using Content.Shared.Charges.Systems;
|
||||||
using Content.Shared.Database;
|
using Content.Shared.Database;
|
||||||
using Content.Shared.DoAfter;
|
using Content.Shared.DoAfter;
|
||||||
using Content.Shared.Doors.Components;
|
using Content.Shared.Examine;
|
||||||
using Content.Shared.Interaction;
|
using Content.Shared.Interaction;
|
||||||
using Content.Shared.Popups;
|
using Content.Shared.Popups;
|
||||||
using Content.Shared.SprayPainter.Components;
|
using Content.Shared.SprayPainter.Components;
|
||||||
using Content.Shared.SprayPainter.Prototypes;
|
using Content.Shared.SprayPainter.Prototypes;
|
||||||
|
using Content.Shared.Verbs;
|
||||||
using Robust.Shared.Audio.Systems;
|
using Robust.Shared.Audio.Systems;
|
||||||
using Robust.Shared.Prototypes;
|
using Robust.Shared.Prototypes;
|
||||||
|
using Robust.Shared.Timing;
|
||||||
|
using Robust.Shared.Utility;
|
||||||
using System.Linq;
|
using System.Linq;
|
||||||
|
|
||||||
namespace Content.Shared.SprayPainter;
|
namespace Content.Shared.SprayPainter;
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// System for painting airlocks using a spray painter.
|
/// System for painting paintable objects using a spray painter.
|
||||||
/// Pipes are handled serverside since AtmosPipeColorSystem is server only.
|
/// Pipes are handled serverside since AtmosPipeColorSystem is server only.
|
||||||
/// </summary>
|
/// </summary>
|
||||||
public abstract class SharedSprayPainterSystem : EntitySystem
|
public abstract class SharedSprayPainterSystem : EntitySystem
|
||||||
{
|
{
|
||||||
|
[Dependency] private readonly IGameTiming _timing = default!;
|
||||||
[Dependency] protected readonly IPrototypeManager Proto = default!;
|
[Dependency] protected readonly IPrototypeManager Proto = default!;
|
||||||
[Dependency] private readonly ISharedAdminLogManager _adminLogger = default!;
|
[Dependency] protected readonly ISharedAdminLogManager AdminLogger = default!;
|
||||||
[Dependency] protected readonly SharedAppearanceSystem Appearance = default!;
|
[Dependency] protected readonly SharedAppearanceSystem Appearance = default!;
|
||||||
[Dependency] protected readonly SharedAudioSystem Audio = default!;
|
[Dependency] protected readonly SharedAudioSystem Audio = default!;
|
||||||
|
[Dependency] protected readonly SharedChargesSystem Charges = default!;
|
||||||
[Dependency] protected readonly SharedDoAfterSystem DoAfter = default!;
|
[Dependency] protected readonly SharedDoAfterSystem DoAfter = default!;
|
||||||
[Dependency] private readonly SharedPopupSystem _popup = default!;
|
[Dependency] private readonly SharedPopupSystem _popup = default!;
|
||||||
|
|
||||||
public List<AirlockStyle> Styles { get; private set; } = new();
|
|
||||||
public List<AirlockGroupPrototype> Groups { get; private set; } = new();
|
|
||||||
|
|
||||||
private static readonly ProtoId<AirlockDepartmentsPrototype> Departments = "Departments";
|
|
||||||
|
|
||||||
public override void Initialize()
|
public override void Initialize()
|
||||||
{
|
{
|
||||||
base.Initialize();
|
base.Initialize();
|
||||||
|
|
||||||
CacheStyles();
|
|
||||||
|
|
||||||
SubscribeLocalEvent<SprayPainterComponent, MapInitEvent>(OnMapInit);
|
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<SprayPainterComponent, SprayPainterDoAfterEvent>(OnPainterDoAfter);
|
||||||
|
SubscribeLocalEvent<SprayPainterComponent, GetVerbsEvent<AlternativeVerb>>(OnPainterGetAltVerbs);
|
||||||
|
SubscribeLocalEvent<PaintableComponent, InteractUsingEvent>(OnPaintableInteract);
|
||||||
|
SubscribeLocalEvent<PaintedComponent, ExaminedEvent>(OnPainedExamined);
|
||||||
|
|
||||||
SubscribeLocalEvent<PrototypesReloadedEventArgs>(OnPrototypesReloaded);
|
Subs.BuiEvents<SprayPainterComponent>(SprayPainterUiKey.Key,
|
||||||
|
subs =>
|
||||||
|
{
|
||||||
|
subs.Event<SprayPainterSetPaintableStyleMessage>(OnSetPaintable);
|
||||||
|
subs.Event<SprayPainterSetPipeColorMessage>(OnSetPipeColor);
|
||||||
|
subs.Event<SprayPainterTabChangedMessage>(OnTabChanged);
|
||||||
|
subs.Event<SprayPainterSetDecalMessage>(OnSetDecal);
|
||||||
|
subs.Event<SprayPainterSetDecalColorMessage>(OnSetDecalColor);
|
||||||
|
subs.Event<SprayPainterSetDecalAngleMessage>(OnSetDecalAngle);
|
||||||
|
subs.Event<SprayPainterSetDecalSnapMessage>(OnSetDecalSnap);
|
||||||
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
private void OnMapInit(Entity<SprayPainterComponent> ent, ref MapInitEvent args)
|
private void OnMapInit(Entity<SprayPainterComponent> ent, ref MapInitEvent args)
|
||||||
{
|
{
|
||||||
if (ent.Comp.ColorPalette.Count == 0)
|
bool stylesByGroupPopulated = false;
|
||||||
return;
|
foreach (var groupProto in Proto.EnumeratePrototypes<PaintableGroupPrototype>())
|
||||||
|
{
|
||||||
|
ent.Comp.StylesByGroup[groupProto.ID] = groupProto.DefaultStyle;
|
||||||
|
stylesByGroupPopulated = true;
|
||||||
|
}
|
||||||
|
if (stylesByGroupPopulated)
|
||||||
|
Dirty(ent);
|
||||||
|
|
||||||
SetColor(ent, ent.Comp.ColorPalette.First().Key);
|
if (ent.Comp.ColorPalette.Count > 0)
|
||||||
|
SetPipeColor(ent, ent.Comp.ColorPalette.First().Key);
|
||||||
}
|
}
|
||||||
|
|
||||||
private void OnDoorDoAfter(Entity<SprayPainterComponent> ent, ref SprayPainterDoorDoAfterEvent args)
|
private void SetPipeColor(Entity<SprayPainterComponent> ent, string? paletteKey)
|
||||||
{
|
|
||||||
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)
|
if (paletteKey == null || paletteKey == ent.Comp.PickedColor)
|
||||||
return;
|
return;
|
||||||
@@ -103,12 +80,98 @@ public abstract class SharedSprayPainterSystem : EntitySystem
|
|||||||
return;
|
return;
|
||||||
|
|
||||||
ent.Comp.PickedColor = paletteKey;
|
ent.Comp.PickedColor = paletteKey;
|
||||||
Dirty(ent, ent.Comp);
|
Dirty(ent);
|
||||||
|
UpdateUi(ent);
|
||||||
}
|
}
|
||||||
|
|
||||||
#endregion
|
#region Interaction
|
||||||
|
|
||||||
private void OnAirlockInteract(Entity<PaintableAirlockComponent> ent, ref InteractUsingEvent args)
|
private void OnPainterDoAfter(Entity<SprayPainterComponent> ent, ref SprayPainterDoAfterEvent args)
|
||||||
|
{
|
||||||
|
if (args.Handled || args.Cancelled)
|
||||||
|
return;
|
||||||
|
|
||||||
|
if (args.Args.Target is not { } target)
|
||||||
|
return;
|
||||||
|
|
||||||
|
if (!HasComp<PaintableComponent>(target))
|
||||||
|
return;
|
||||||
|
|
||||||
|
Appearance.SetData(target, PaintableVisuals.Prototype, args.Prototype);
|
||||||
|
Audio.PlayPredicted(ent.Comp.SpraySound, ent, args.Args.User);
|
||||||
|
Charges.TryUseCharges(new Entity<LimitedChargesComponent?>(ent, EnsureComp<LimitedChargesComponent>(ent)), args.Cost);
|
||||||
|
|
||||||
|
var paintedComponent = EnsureComp<PaintedComponent>(target);
|
||||||
|
paintedComponent.DryTime = _timing.CurTime + ent.Comp.FreshPaintDuration;
|
||||||
|
Dirty(target, paintedComponent);
|
||||||
|
|
||||||
|
var ev = new EntityPaintedEvent(
|
||||||
|
User: args.User,
|
||||||
|
Tool: ent,
|
||||||
|
Prototype: args.Prototype,
|
||||||
|
Group: args.Group);
|
||||||
|
RaiseLocalEvent(target, ref ev);
|
||||||
|
|
||||||
|
AdminLogger.Add(LogType.Action,
|
||||||
|
LogImpact.Low,
|
||||||
|
$"{ToPrettyString(args.Args.User):user} painted {ToPrettyString(args.Args.Target.Value):target}");
|
||||||
|
|
||||||
|
args.Handled = true;
|
||||||
|
}
|
||||||
|
|
||||||
|
private void OnPainterGetAltVerbs(Entity<SprayPainterComponent> ent, ref GetVerbsEvent<AlternativeVerb> args)
|
||||||
|
{
|
||||||
|
if (!args.CanAccess || !args.CanInteract || !args.Using.HasValue)
|
||||||
|
return;
|
||||||
|
|
||||||
|
var user = args.User;
|
||||||
|
|
||||||
|
AlternativeVerb verb = new()
|
||||||
|
{
|
||||||
|
Text = Loc.GetString("spray-painter-verb-toggle-decals"),
|
||||||
|
Icon = new SpriteSpecifier.Texture(new ResPath("/Textures/Interface/VerbIcons/settings.svg.192dpi.png")),
|
||||||
|
Act = () => TogglePaintDecals(ent, user),
|
||||||
|
Impact = LogImpact.Low
|
||||||
|
};
|
||||||
|
args.Verbs.Add(verb);
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Toggles whether clicking on the floor paints a decal or not.
|
||||||
|
/// </summary>
|
||||||
|
private void TogglePaintDecals(Entity<SprayPainterComponent> ent, EntityUid user)
|
||||||
|
{
|
||||||
|
if (!_timing.IsFirstTimePredicted)
|
||||||
|
return;
|
||||||
|
|
||||||
|
var pitch = 1.0f;
|
||||||
|
switch (ent.Comp.DecalMode)
|
||||||
|
{
|
||||||
|
case DecalPaintMode.Off:
|
||||||
|
default:
|
||||||
|
ent.Comp.DecalMode = DecalPaintMode.Add;
|
||||||
|
pitch = 1.0f;
|
||||||
|
break;
|
||||||
|
case DecalPaintMode.Add:
|
||||||
|
ent.Comp.DecalMode = DecalPaintMode.Remove;
|
||||||
|
pitch = 1.2f;
|
||||||
|
break;
|
||||||
|
case DecalPaintMode.Remove:
|
||||||
|
ent.Comp.DecalMode = DecalPaintMode.Off;
|
||||||
|
pitch = 0.8f;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
Dirty(ent);
|
||||||
|
|
||||||
|
// Make the machine beep.
|
||||||
|
Audio.PlayPredicted(ent.Comp.SoundSwitchDecalMode, ent, user, ent.Comp.SoundSwitchDecalMode.Params.WithPitchScale(pitch));
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Handles spray paint interactions with an object.
|
||||||
|
/// An object must belong to a spray paintable group to be painted, and the painter must have sufficient ammo to paint it.
|
||||||
|
/// </summary>
|
||||||
|
private void OnPaintableInteract(Entity<PaintableComponent> ent, ref InteractUsingEvent args)
|
||||||
{
|
{
|
||||||
if (args.Handled)
|
if (args.Handled)
|
||||||
return;
|
return;
|
||||||
@@ -116,79 +179,140 @@ public abstract class SharedSprayPainterSystem : EntitySystem
|
|||||||
if (!TryComp<SprayPainterComponent>(args.Used, out var painter))
|
if (!TryComp<SprayPainterComponent>(args.Used, out var painter))
|
||||||
return;
|
return;
|
||||||
|
|
||||||
var group = Proto.Index<AirlockGroupPrototype>(ent.Comp.Group);
|
if (ent.Comp.Group is not { } group
|
||||||
|
|| !painter.StylesByGroup.TryGetValue(group, out var selectedStyle)
|
||||||
|
|| !Proto.TryIndex(group, out PaintableGroupPrototype? targetGroup))
|
||||||
|
return;
|
||||||
|
|
||||||
var style = Styles[painter.Index];
|
// Valid paint target.
|
||||||
if (!group.StylePaths.TryGetValue(style.Name, out var sprite))
|
args.Handled = true;
|
||||||
|
|
||||||
|
if (TryComp<LimitedChargesComponent>(args.Used, out var charges)
|
||||||
|
&& charges.LastCharges < targetGroup.Cost)
|
||||||
{
|
{
|
||||||
string msg = Loc.GetString("spray-painter-style-not-available");
|
var msg = Loc.GetString("spray-painter-interact-no-charges");
|
||||||
_popup.PopupClient(msg, args.User, args.User);
|
_popup.PopupClient(msg, args.User, args.User);
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
var doAfterEventArgs = new DoAfterArgs(EntityManager, args.User, painter.AirlockSprayTime, new SprayPainterDoorDoAfterEvent(sprite, style.Department), args.Used, target: ent, used: args.Used)
|
if (!targetGroup.Styles.TryGetValue(selectedStyle, out var proto))
|
||||||
|
{
|
||||||
|
var msg = Loc.GetString("spray-painter-style-not-available");
|
||||||
|
_popup.PopupClient(msg, args.User, args.User);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
var doAfterEventArgs = new DoAfterArgs(EntityManager,
|
||||||
|
args.User,
|
||||||
|
targetGroup.Time,
|
||||||
|
new SprayPainterDoAfterEvent(proto, group, targetGroup.Cost),
|
||||||
|
args.Used,
|
||||||
|
target: ent,
|
||||||
|
used: args.Used)
|
||||||
{
|
{
|
||||||
BreakOnMove = true,
|
BreakOnMove = true,
|
||||||
BreakOnDamage = true,
|
BreakOnDamage = true,
|
||||||
NeedHand = true,
|
NeedHand = true,
|
||||||
};
|
};
|
||||||
if (!DoAfter.TryStartDoAfter(doAfterEventArgs, out var id))
|
|
||||||
return;
|
|
||||||
|
|
||||||
args.Handled = true;
|
if (!DoAfter.TryStartDoAfter(doAfterEventArgs, out _))
|
||||||
|
return;
|
||||||
|
|
||||||
// Log the attempt
|
// 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}");
|
AdminLogger.Add(LogType.Action,
|
||||||
|
LogImpact.Low,
|
||||||
|
$"{ToPrettyString(args.User):user} is painting {ToPrettyString(ent):target} to '{selectedStyle}' at {Transform(ent).Coordinates:targetlocation}");
|
||||||
}
|
}
|
||||||
|
|
||||||
#region Style caching
|
/// <summary>
|
||||||
|
/// Prints out if an object has been painted recently.
|
||||||
private void OnPrototypesReloaded(PrototypesReloadedEventArgs args)
|
/// </summary>
|
||||||
|
private void OnPainedExamined(Entity<PaintedComponent> ent, ref ExaminedEvent args)
|
||||||
{
|
{
|
||||||
if (!args.WasModified<AirlockGroupPrototype>() && !args.WasModified<AirlockDepartmentsPrototype>())
|
// If the paint's dried, it isn't detectable.
|
||||||
|
if (_timing.CurTime > ent.Comp.DryTime)
|
||||||
return;
|
return;
|
||||||
|
|
||||||
Styles.Clear();
|
args.PushText(Loc.GetString("spray-painter-on-examined-painted-message"));
|
||||||
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()
|
#endregion Interaction
|
||||||
{
|
|
||||||
// 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
|
#region UI
|
||||||
var departments = Proto.Index(Departments);
|
|
||||||
Styles.Capacity = names.Count;
|
/// <summary>
|
||||||
foreach (var name in names)
|
/// Sets the style that a particular type of paintable object (e.g. lockers) should be painted in.
|
||||||
{
|
/// </summary>
|
||||||
departments.Departments.TryGetValue(name, out var department);
|
private void OnSetPaintable(Entity<SprayPainterComponent> ent, ref SprayPainterSetPaintableStyleMessage args)
|
||||||
Styles.Add(new AirlockStyle(name, department));
|
{
|
||||||
}
|
if (!ent.Comp.StylesByGroup.ContainsKey(args.Group))
|
||||||
|
return;
|
||||||
|
|
||||||
|
ent.Comp.StylesByGroup[args.Group] = args.Style;
|
||||||
|
Dirty(ent);
|
||||||
|
UpdateUi(ent);
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Changes the color to paint pipes in.
|
||||||
|
/// </summary>
|
||||||
|
private void OnSetPipeColor(Entity<SprayPainterComponent> ent, ref SprayPainterSetPipeColorMessage args)
|
||||||
|
{
|
||||||
|
SetPipeColor(ent, args.Key);
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Tracks the tab the spray painter was on.
|
||||||
|
/// </summary>
|
||||||
|
private void OnTabChanged(Entity<SprayPainterComponent> ent, ref SprayPainterTabChangedMessage args)
|
||||||
|
{
|
||||||
|
ent.Comp.SelectedTab = args.Index;
|
||||||
|
Dirty(ent);
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Sets the decal prototype to paint.
|
||||||
|
/// </summary>
|
||||||
|
private void OnSetDecal(Entity<SprayPainterComponent> ent, ref SprayPainterSetDecalMessage args)
|
||||||
|
{
|
||||||
|
ent.Comp.SelectedDecal = args.DecalPrototype;
|
||||||
|
Dirty(ent);
|
||||||
|
UpdateUi(ent);
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Sets the angle to paint decals at.
|
||||||
|
/// </summary>
|
||||||
|
private void OnSetDecalAngle(Entity<SprayPainterComponent> ent, ref SprayPainterSetDecalAngleMessage args)
|
||||||
|
{
|
||||||
|
ent.Comp.SelectedDecalAngle = args.Angle;
|
||||||
|
Dirty(ent);
|
||||||
|
UpdateUi(ent);
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Enables or disables snap-to-grid when painting decals.
|
||||||
|
/// </summary>
|
||||||
|
private void OnSetDecalSnap(Entity<SprayPainterComponent> ent, ref SprayPainterSetDecalSnapMessage args)
|
||||||
|
{
|
||||||
|
ent.Comp.SnapDecals = args.Snap;
|
||||||
|
Dirty(ent);
|
||||||
|
UpdateUi(ent);
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Sets the decal to paint on the ground.
|
||||||
|
/// </summary>
|
||||||
|
private void OnSetDecalColor(Entity<SprayPainterComponent> ent, ref SprayPainterSetDecalColorMessage args)
|
||||||
|
{
|
||||||
|
ent.Comp.SelectedDecalColor = args.Color;
|
||||||
|
Dirty(ent);
|
||||||
|
UpdateUi(ent);
|
||||||
|
}
|
||||||
|
|
||||||
|
protected virtual void UpdateUi(Entity<SprayPainterComponent> ent)
|
||||||
|
{
|
||||||
}
|
}
|
||||||
|
|
||||||
#endregion
|
#endregion
|
||||||
}
|
}
|
||||||
|
|
||||||
public record struct AirlockStyle(string Name, string? Department);
|
|
||||||
|
|||||||
62
Content.Shared/SprayPainter/SprayPainterAmmoSystem.cs
Normal file
62
Content.Shared/SprayPainter/SprayPainterAmmoSystem.cs
Normal file
@@ -0,0 +1,62 @@
|
|||||||
|
using Content.Shared.Charges.Components;
|
||||||
|
using Content.Shared.Charges.Systems;
|
||||||
|
using Content.Shared.Examine;
|
||||||
|
using Content.Shared.Interaction;
|
||||||
|
using Content.Shared.Popups;
|
||||||
|
using Content.Shared.SprayPainter.Components;
|
||||||
|
|
||||||
|
namespace Content.Shared.SprayPainter;
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// The system handles interactions with spray painter ammo.
|
||||||
|
/// </summary>
|
||||||
|
public sealed class SprayPainterAmmoSystem : EntitySystem
|
||||||
|
{
|
||||||
|
[Dependency] private readonly SharedChargesSystem _charges = default!;
|
||||||
|
[Dependency] private readonly SharedPopupSystem _popup = default!;
|
||||||
|
|
||||||
|
public override void Initialize()
|
||||||
|
{
|
||||||
|
base.Initialize();
|
||||||
|
|
||||||
|
SubscribeLocalEvent<SprayPainterAmmoComponent, ExaminedEvent>(OnExamine);
|
||||||
|
SubscribeLocalEvent<SprayPainterAmmoComponent, AfterInteractEvent>(OnAfterInteract);
|
||||||
|
}
|
||||||
|
|
||||||
|
private void OnAfterInteract(Entity<SprayPainterAmmoComponent> ent, ref AfterInteractEvent args)
|
||||||
|
{
|
||||||
|
if (args.Handled || !args.CanReach)
|
||||||
|
return;
|
||||||
|
|
||||||
|
if (args.Target is not { Valid: true } target ||
|
||||||
|
!HasComp<SprayPainterComponent>(target) ||
|
||||||
|
!TryComp<LimitedChargesComponent>(target, out var charges))
|
||||||
|
return;
|
||||||
|
|
||||||
|
var user = args.User;
|
||||||
|
args.Handled = true;
|
||||||
|
var count = Math.Min(charges.MaxCharges - charges.LastCharges, ent.Comp.Charges);
|
||||||
|
if (count <= 0)
|
||||||
|
{
|
||||||
|
_popup.PopupClient(Loc.GetString("spray-painter-ammo-after-interact-full"), target, user);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
_popup.PopupClient(Loc.GetString("spray-painter-ammo-after-interact-refilled"), target, user);
|
||||||
|
_charges.AddCharges(target, count);
|
||||||
|
ent.Comp.Charges -= count;
|
||||||
|
Dirty(ent, ent.Comp);
|
||||||
|
|
||||||
|
if (ent.Comp.Charges <= 0)
|
||||||
|
PredictedQueueDel(ent.Owner);
|
||||||
|
}
|
||||||
|
|
||||||
|
private void OnExamine(Entity<SprayPainterAmmoComponent> ent, ref ExaminedEvent args)
|
||||||
|
{
|
||||||
|
if (!args.IsInDetailsRange)
|
||||||
|
return;
|
||||||
|
|
||||||
|
var examineMessage = Loc.GetString("rcd-ammo-component-on-examine", ("charges", ent.Comp.Charges));
|
||||||
|
args.PushText(examineMessage);
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -1,4 +1,7 @@
|
|||||||
|
using Content.Shared.Decals;
|
||||||
using Content.Shared.DoAfter;
|
using Content.Shared.DoAfter;
|
||||||
|
using Content.Shared.SprayPainter.Prototypes;
|
||||||
|
using Robust.Shared.Prototypes;
|
||||||
using Robust.Shared.Serialization;
|
using Robust.Shared.Serialization;
|
||||||
|
|
||||||
namespace Content.Shared.SprayPainter;
|
namespace Content.Shared.SprayPainter;
|
||||||
@@ -10,46 +13,75 @@ public enum SprayPainterUiKey
|
|||||||
}
|
}
|
||||||
|
|
||||||
[Serializable, NetSerializable]
|
[Serializable, NetSerializable]
|
||||||
public sealed class SprayPainterSpritePickedMessage : BoundUserInterfaceMessage
|
public sealed class SprayPainterSetDecalMessage(ProtoId<DecalPrototype> protoId) : BoundUserInterfaceMessage
|
||||||
{
|
{
|
||||||
public readonly int Index;
|
public ProtoId<DecalPrototype> DecalPrototype = protoId;
|
||||||
|
|
||||||
public SprayPainterSpritePickedMessage(int index)
|
|
||||||
{
|
|
||||||
Index = index;
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
[Serializable, NetSerializable]
|
[Serializable, NetSerializable]
|
||||||
public sealed class SprayPainterColorPickedMessage : BoundUserInterfaceMessage
|
public sealed class SprayPainterSetDecalColorMessage(Color? color) : BoundUserInterfaceMessage
|
||||||
{
|
{
|
||||||
public readonly string? Key;
|
public Color? Color = color;
|
||||||
|
|
||||||
public SprayPainterColorPickedMessage(string? key)
|
|
||||||
{
|
|
||||||
Key = key;
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
[Serializable, NetSerializable]
|
[Serializable, NetSerializable]
|
||||||
public sealed partial class SprayPainterDoorDoAfterEvent : DoAfterEvent
|
public sealed class SprayPainterSetDecalSnapMessage(bool snap) : BoundUserInterfaceMessage
|
||||||
|
{
|
||||||
|
public bool Snap = snap;
|
||||||
|
}
|
||||||
|
|
||||||
|
[Serializable, NetSerializable]
|
||||||
|
public sealed class SprayPainterSetDecalAngleMessage(int angle) : BoundUserInterfaceMessage
|
||||||
|
{
|
||||||
|
public int Angle = angle;
|
||||||
|
}
|
||||||
|
|
||||||
|
[Serializable, NetSerializable]
|
||||||
|
public sealed class SprayPainterTabChangedMessage(int index, bool isSelectedTabWithDecals) : BoundUserInterfaceMessage
|
||||||
|
{
|
||||||
|
public readonly int Index = index;
|
||||||
|
public readonly bool IsSelectedTabWithDecals = isSelectedTabWithDecals;
|
||||||
|
}
|
||||||
|
|
||||||
|
[Serializable, NetSerializable]
|
||||||
|
public sealed class SprayPainterSetPaintableStyleMessage(string group, string style) : BoundUserInterfaceMessage
|
||||||
|
{
|
||||||
|
public readonly string Group = group;
|
||||||
|
public readonly string Style = style;
|
||||||
|
}
|
||||||
|
|
||||||
|
[Serializable, NetSerializable]
|
||||||
|
public sealed class SprayPainterSetPipeColorMessage(string? key) : BoundUserInterfaceMessage
|
||||||
|
{
|
||||||
|
public readonly string? Key = key;
|
||||||
|
}
|
||||||
|
|
||||||
|
[Serializable, NetSerializable]
|
||||||
|
public sealed partial class SprayPainterDoAfterEvent : DoAfterEvent
|
||||||
{
|
{
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// Base RSI path to set for the door sprite.
|
/// The prototype to use to repaint this object.
|
||||||
/// </summary>
|
/// </summary>
|
||||||
[DataField]
|
[DataField]
|
||||||
public string Sprite;
|
public string Prototype;
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// Department id to set for the door, if the style has one.
|
/// The group ID of the object being painted.
|
||||||
/// </summary>
|
/// </summary>
|
||||||
[DataField]
|
[DataField]
|
||||||
public string? Department;
|
public string Group;
|
||||||
|
|
||||||
public SprayPainterDoorDoAfterEvent(string sprite, string? department)
|
/// <summary>
|
||||||
|
/// The cost, in charges, to paint this object.
|
||||||
|
/// </summary>
|
||||||
|
[DataField]
|
||||||
|
public int Cost;
|
||||||
|
|
||||||
|
public SprayPainterDoAfterEvent(string prototype, string group, int cost)
|
||||||
{
|
{
|
||||||
Sprite = sprite;
|
Prototype = prototype;
|
||||||
Department = department;
|
Group = group;
|
||||||
|
Cost = cost;
|
||||||
}
|
}
|
||||||
|
|
||||||
public override DoAfterEvent Clone() => this;
|
public override DoAfterEvent Clone() => this;
|
||||||
@@ -71,3 +103,17 @@ public sealed partial class SprayPainterPipeDoAfterEvent : DoAfterEvent
|
|||||||
|
|
||||||
public override DoAfterEvent Clone() => this;
|
public override DoAfterEvent Clone() => this;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// An action raised on an entity when it is spray painted.
|
||||||
|
/// </summary>
|
||||||
|
/// <param name="User">The entity painting this item.</param>
|
||||||
|
/// <param name="Tool">The entity used to paint this item.</param>
|
||||||
|
/// <param name="Prototype">The prototype used to generate the new painted appearance.</param>
|
||||||
|
/// <param name="Group">The group of the entity being painted (e.g. airlocks with glass, canisters).</param>
|
||||||
|
[ByRefEvent]
|
||||||
|
public partial record struct EntityPaintedEvent(
|
||||||
|
EntityUid? User,
|
||||||
|
EntityUid Tool,
|
||||||
|
EntProtoId Prototype,
|
||||||
|
ProtoId<PaintableGroupPrototype> Group);
|
||||||
|
|||||||
@@ -1,14 +0,0 @@
|
|||||||
spray-painter-window-title = Spray painter
|
|
||||||
|
|
||||||
spray-painter-style-not-available = Cannot apply the selected style to this type of airlock
|
|
||||||
spray-painter-selected-style = Selected style:
|
|
||||||
|
|
||||||
spray-painter-selected-color = Selected color:
|
|
||||||
spray-painter-color-red = red
|
|
||||||
spray-painter-color-yellow = yellow
|
|
||||||
spray-painter-color-brown = brown
|
|
||||||
spray-painter-color-green = green
|
|
||||||
spray-painter-color-cyan = cyan
|
|
||||||
spray-painter-color-blue = blue
|
|
||||||
spray-painter-color-white = white
|
|
||||||
spray-painter-color-black = black
|
|
||||||
194
Resources/Locale/en-US/spray-painter/spray-painter.ftl
Normal file
194
Resources/Locale/en-US/spray-painter/spray-painter.ftl
Normal file
@@ -0,0 +1,194 @@
|
|||||||
|
# Components
|
||||||
|
spray-painter-ammo-on-examine = It holds {$charges} charges.
|
||||||
|
spray-painter-ammo-after-interact-full = The spray painter is full!
|
||||||
|
spray-painter-ammo-after-interact-refilled = You refill the spray painter.
|
||||||
|
|
||||||
|
spray-painter-interact-no-charges = Not enough paint left.
|
||||||
|
spray-painter-interact-nothing-to-remove = Nothing to remove!
|
||||||
|
|
||||||
|
spray-painter-on-examined-painted-message = It seems to have been freshly painted.
|
||||||
|
spray-painter-style-not-available = Cannot apply the selected style to this object.
|
||||||
|
|
||||||
|
spray-painter-verb-toggle-decals = Toggle decal painting
|
||||||
|
|
||||||
|
spray-painter-item-status-label = Decals: {$mode}
|
||||||
|
spray-painter-item-status-add = [color=green]Add[/color]
|
||||||
|
spray-painter-item-status-remove = [color=red]Remove[/color]
|
||||||
|
spray-painter-item-status-off = [color=gray]Off[/color]
|
||||||
|
|
||||||
|
# UI
|
||||||
|
spray-painter-window-title = Spray Painter
|
||||||
|
|
||||||
|
spray-painter-selected-style = Selected style:
|
||||||
|
|
||||||
|
spray-painter-selected-decals = Selected decal:
|
||||||
|
spray-painter-use-custom-color = Use custom color
|
||||||
|
spray-painter-use-snap-to-tile = Snap to tile
|
||||||
|
|
||||||
|
spray-painter-angle-rotation = Rotation:
|
||||||
|
spray-painter-angle-rotation-90-sub = -90°
|
||||||
|
spray-painter-angle-rotation-reset = 0°
|
||||||
|
spray-painter-angle-rotation-90-add = +90°
|
||||||
|
|
||||||
|
spray-painter-selected-color = Selected color:
|
||||||
|
spray-painter-color-red = red
|
||||||
|
spray-painter-color-yellow = yellow
|
||||||
|
spray-painter-color-brown = brown
|
||||||
|
spray-painter-color-green = green
|
||||||
|
spray-painter-color-cyan = cyan
|
||||||
|
spray-painter-color-blue = blue
|
||||||
|
spray-painter-color-white = white
|
||||||
|
spray-painter-color-black = black
|
||||||
|
|
||||||
|
# Categories (tabs)
|
||||||
|
spray-painter-tab-category-airlocks = Airlocks
|
||||||
|
spray-painter-tab-category-canisters = Canisters
|
||||||
|
spray-painter-tab-category-crates = Crates
|
||||||
|
spray-painter-tab-category-lockers = Lockers
|
||||||
|
spray-painter-tab-category-pipes = Pipes
|
||||||
|
spray-painter-tab-category-decals = Decals
|
||||||
|
|
||||||
|
# Groups (subtabs)
|
||||||
|
spray-painter-tab-group-airlockstandard = Standard
|
||||||
|
spray-painter-tab-group-airlockglass = Glass
|
||||||
|
|
||||||
|
spray-painter-tab-group-cratesteel = Steel
|
||||||
|
spray-painter-tab-group-crateplastic = Plastic
|
||||||
|
spray-painter-tab-group-cratesecure = Secure
|
||||||
|
|
||||||
|
spray-painter-tab-group-closet = Unlocked
|
||||||
|
spray-painter-tab-group-locker = Secure
|
||||||
|
spray-painter-tab-group-wallcloset = Unlocked (Wall)
|
||||||
|
spray-painter-tab-group-walllocker = Secure (Wall)
|
||||||
|
|
||||||
|
# Airlocks
|
||||||
|
spray-painter-style-airlockstandard-atmospherics = Atmospheric
|
||||||
|
spray-painter-style-airlockstandard-basic = Basic
|
||||||
|
spray-painter-style-airlockstandard-cargo = Cargo
|
||||||
|
spray-painter-style-airlockstandard-chemistry = Chemistry
|
||||||
|
spray-painter-style-airlockstandard-command = Command
|
||||||
|
spray-painter-style-airlockstandard-engineering = Engineering
|
||||||
|
spray-painter-style-airlockstandard-freezer = Freezer
|
||||||
|
spray-painter-style-airlockstandard-hydroponics = Hydroponics
|
||||||
|
spray-painter-style-airlockstandard-maintenance = Maintenance
|
||||||
|
spray-painter-style-airlockstandard-medical = Medical
|
||||||
|
spray-painter-style-airlockstandard-salvage = Salvage
|
||||||
|
spray-painter-style-airlockstandard-science = Science
|
||||||
|
spray-painter-style-airlockstandard-security = Security
|
||||||
|
spray-painter-style-airlockstandard-virology = Virology
|
||||||
|
|
||||||
|
spray-painter-style-airlockglass-atmospherics = Atmospherics
|
||||||
|
spray-painter-style-airlockglass-basic = Basic
|
||||||
|
spray-painter-style-airlockglass-cargo = Cargo
|
||||||
|
spray-painter-style-airlockglass-chemistry = Chemistry
|
||||||
|
spray-painter-style-airlockglass-command = Command
|
||||||
|
spray-painter-style-airlockglass-engineering = Engineering
|
||||||
|
spray-painter-style-airlockglass-hydroponics = Hydroponics
|
||||||
|
spray-painter-style-airlockglass-maintenance = Maintenance
|
||||||
|
spray-painter-style-airlockglass-medical = Medical
|
||||||
|
spray-painter-style-airlockglass-salvage = Salvage
|
||||||
|
spray-painter-style-airlockglass-science = Science
|
||||||
|
spray-painter-style-airlockglass-security = Security
|
||||||
|
spray-painter-style-airlockglass-virology = Virology
|
||||||
|
|
||||||
|
# Lockers
|
||||||
|
spray-painter-style-locker-atmospherics = Atmospherics
|
||||||
|
spray-painter-style-locker-basic = Basic
|
||||||
|
spray-painter-style-locker-botanist = Botanist
|
||||||
|
spray-painter-style-locker-brigmedic = Brigmedic
|
||||||
|
spray-painter-style-locker-captain = Captain
|
||||||
|
spray-painter-style-locker-ce = CE
|
||||||
|
spray-painter-style-locker-chemical = Chemical
|
||||||
|
spray-painter-style-locker-clown = Clown
|
||||||
|
spray-painter-style-locker-cmo = CMO
|
||||||
|
spray-painter-style-locker-doctor = Doctor
|
||||||
|
spray-painter-style-locker-electrical = Electrical
|
||||||
|
spray-painter-style-locker-engineer = Engineer
|
||||||
|
spray-painter-style-locker-evac = Evac repair
|
||||||
|
spray-painter-style-locker-hop = HOP
|
||||||
|
spray-painter-style-locker-hos = HOS
|
||||||
|
spray-painter-style-locker-medicine = Medicine
|
||||||
|
spray-painter-style-locker-mime = Mime
|
||||||
|
spray-painter-style-locker-paramedic = Paramedic
|
||||||
|
spray-painter-style-locker-quartermaster = Quartermaster
|
||||||
|
spray-painter-style-locker-rd = RD
|
||||||
|
spray-painter-style-locker-representative = Representative
|
||||||
|
spray-painter-style-locker-salvage = Salvage
|
||||||
|
spray-painter-style-locker-scientist = Scientist
|
||||||
|
spray-painter-style-locker-security = Security
|
||||||
|
spray-painter-style-locker-welding = Welding
|
||||||
|
|
||||||
|
spray-painter-style-closet-basic = Basic
|
||||||
|
spray-painter-style-closet-biohazard = Biohazard
|
||||||
|
spray-painter-style-closet-biohazard-science = Biohazard (science)
|
||||||
|
spray-painter-style-closet-biohazard-virology = Biohazard (virology)
|
||||||
|
spray-painter-style-closet-biohazard-security = Biohazard (security)
|
||||||
|
spray-painter-style-closet-biohazard-janitor = Biohazard (janitor)
|
||||||
|
spray-painter-style-closet-bomb = Bomb suit
|
||||||
|
spray-painter-style-closet-bomb-janitor = Bomb suit (janitor)
|
||||||
|
spray-painter-style-closet-chef = Chef
|
||||||
|
spray-painter-style-closet-fire = Fire-safety
|
||||||
|
spray-painter-style-closet-janitor = Janitor
|
||||||
|
spray-painter-style-closet-legal = Lawyer
|
||||||
|
spray-painter-style-closet-nitrogen = Internals (nitrogen)
|
||||||
|
spray-painter-style-closet-oxygen = Internals (oxygen)
|
||||||
|
spray-painter-style-closet-radiation = Radiation suit
|
||||||
|
spray-painter-style-closet-tool = Tools
|
||||||
|
|
||||||
|
spray-painter-style-wallcloset-atmospherics = Atmospherics
|
||||||
|
spray-painter-style-wallcloset-basic = Basic
|
||||||
|
spray-painter-style-wallcloset-black = Black
|
||||||
|
spray-painter-style-wallcloset-blue = Blue
|
||||||
|
spray-painter-style-wallcloset-fire = Fire-safety
|
||||||
|
spray-painter-style-wallcloset-green = Green
|
||||||
|
spray-painter-style-wallcloset-grey = Grey
|
||||||
|
spray-painter-style-wallcloset-mixed = Mixed
|
||||||
|
spray-painter-style-wallcloset-nitrogen = Internals (nitrogen)
|
||||||
|
spray-painter-style-wallcloset-orange = Orange
|
||||||
|
spray-painter-style-wallcloset-oxygen = Internals (oxygen)
|
||||||
|
spray-painter-style-wallcloset-pink = Pink
|
||||||
|
spray-painter-style-wallcloset-white = White
|
||||||
|
spray-painter-style-wallcloset-yellow = Yellow
|
||||||
|
|
||||||
|
spray-painter-style-walllocker-evac = Evac repair
|
||||||
|
spray-painter-style-walllocker-medical = Medical
|
||||||
|
|
||||||
|
# Crates
|
||||||
|
spray-painter-style-cratesteel-basic = Basic
|
||||||
|
spray-painter-style-cratesteel-electrical = Electrical
|
||||||
|
spray-painter-style-cratesteel-engineering = Engineering
|
||||||
|
spray-painter-style-cratesteel-radiation = Radiation
|
||||||
|
spray-painter-style-cratesteel-science = Science
|
||||||
|
spray-painter-style-cratesteel-surgery = Surgery
|
||||||
|
|
||||||
|
spray-painter-style-crateplastic-basic = Basic
|
||||||
|
spray-painter-style-crateplastic-chemistry = Chemistry
|
||||||
|
spray-painter-style-crateplastic-command = Command
|
||||||
|
spray-painter-style-crateplastic-hydroponics = Hydroponics
|
||||||
|
spray-painter-style-crateplastic-medical = Medical
|
||||||
|
spray-painter-style-crateplastic-oxygen = Oxygen
|
||||||
|
|
||||||
|
spray-painter-style-cratesecure-basic = Basic
|
||||||
|
spray-painter-style-cratesecure-chemistry = Chemistry
|
||||||
|
spray-painter-style-cratesecure-command = Command
|
||||||
|
spray-painter-style-cratesecure-engineering = Engineering
|
||||||
|
spray-painter-style-cratesecure-hydroponics = Hydroponics
|
||||||
|
spray-painter-style-cratesecure-medical = Medical
|
||||||
|
spray-painter-style-cratesecure-plasma = Plasma
|
||||||
|
spray-painter-style-cratesecure-private = Private
|
||||||
|
spray-painter-style-cratesecure-science = Science
|
||||||
|
spray-painter-style-cratesecure-secgear = Secgear
|
||||||
|
spray-painter-style-cratesecure-weapon = Weapon
|
||||||
|
|
||||||
|
# Canisters
|
||||||
|
spray-painter-style-canisters-air = Air
|
||||||
|
spray-painter-style-canisters-ammonia = Ammonia
|
||||||
|
spray-painter-style-canisters-carbon-dioxide = Carbon dioxide
|
||||||
|
spray-painter-style-canisters-frezon = Frezon
|
||||||
|
spray-painter-style-canisters-nitrogen = Nitrogen
|
||||||
|
spray-painter-style-canisters-nitrous-oxide = Nitrous oxide
|
||||||
|
spray-painter-style-canisters-oxygen = Oxygen
|
||||||
|
spray-painter-style-canisters-plasma = Plasma
|
||||||
|
spray-painter-style-canisters-storage = Storage
|
||||||
|
spray-painter-style-canisters-tritium = Tritium
|
||||||
|
spray-painter-style-canisters-water-vapor = Water vapor
|
||||||
@@ -13,6 +13,7 @@
|
|||||||
FlashlightLantern: 5
|
FlashlightLantern: 5
|
||||||
ClothingHandsGlovesColorYellowBudget: 3
|
ClothingHandsGlovesColorYellowBudget: 3
|
||||||
SprayPainter: 3
|
SprayPainter: 3
|
||||||
|
SprayPainterAmmo: 5
|
||||||
# Some engineer forgot to take the multitool out the youtool when working on it, happens.
|
# Some engineer forgot to take the multitool out the youtool when working on it, happens.
|
||||||
contrabandInventory:
|
contrabandInventory:
|
||||||
Multitool: 1
|
Multitool: 1
|
||||||
|
|||||||
@@ -35,6 +35,7 @@
|
|||||||
components:
|
components:
|
||||||
- StationMap
|
- StationMap
|
||||||
- SprayPainter
|
- SprayPainter
|
||||||
|
- SprayPainterAmmo
|
||||||
- NetworkConfigurator
|
- NetworkConfigurator
|
||||||
- RCD
|
- RCD
|
||||||
- RCDAmmo
|
- RCDAmmo
|
||||||
@@ -119,6 +120,7 @@
|
|||||||
components:
|
components:
|
||||||
- StationMap
|
- StationMap
|
||||||
- SprayPainter
|
- SprayPainter
|
||||||
|
- SprayPainterAmmo
|
||||||
- NetworkConfigurator
|
- NetworkConfigurator
|
||||||
- RCD
|
- RCD
|
||||||
- RCDAmmo
|
- RCDAmmo
|
||||||
|
|||||||
@@ -2,7 +2,7 @@
|
|||||||
parent: BaseItem
|
parent: BaseItem
|
||||||
id: SprayPainter
|
id: SprayPainter
|
||||||
name: spray painter
|
name: spray painter
|
||||||
description: A spray painter for painting airlocks and pipes.
|
description: A spray painter for painting airlocks, pipes, and other items.
|
||||||
components:
|
components:
|
||||||
- type: Sprite
|
- type: Sprite
|
||||||
sprite: Objects/Tools/spray_painter.rsi
|
sprite: Objects/Tools/spray_painter.rsi
|
||||||
@@ -32,6 +32,45 @@
|
|||||||
mix: '#947507'
|
mix: '#947507'
|
||||||
- type: StaticPrice
|
- type: StaticPrice
|
||||||
price: 40
|
price: 40
|
||||||
|
- type: LimitedCharges
|
||||||
|
maxCharges: 15
|
||||||
|
lastCharges: 15
|
||||||
- type: PhysicalComposition
|
- type: PhysicalComposition
|
||||||
materialComposition:
|
materialComposition:
|
||||||
Steel: 100
|
Steel: 100
|
||||||
|
|
||||||
|
- type: entity
|
||||||
|
parent: SprayPainter
|
||||||
|
id: SprayPainterRecharging
|
||||||
|
suffix: Admeme
|
||||||
|
components:
|
||||||
|
- type: AutoRecharge
|
||||||
|
rechargeDuration: 1
|
||||||
|
|
||||||
|
- type: entity
|
||||||
|
parent: SprayPainter
|
||||||
|
id: SprayPainterEmpty
|
||||||
|
suffix: Empty
|
||||||
|
components:
|
||||||
|
- type: LimitedCharges
|
||||||
|
lastCharges: -1
|
||||||
|
|
||||||
|
- type: entity
|
||||||
|
parent: BaseItem
|
||||||
|
id: SprayPainterAmmo
|
||||||
|
name: compressed paint
|
||||||
|
description: A cartridge of highly compressed paint, commonly used in spray painters.
|
||||||
|
components:
|
||||||
|
- type: SprayPainterAmmo
|
||||||
|
- type: Sprite
|
||||||
|
sprite: Objects/Tools/spray_painter.rsi
|
||||||
|
state: ammo
|
||||||
|
- type: Item
|
||||||
|
sprite: Objects/Tools/spray_painter.rsi
|
||||||
|
heldPrefix: ammo
|
||||||
|
- type: PhysicalComposition
|
||||||
|
materialComposition:
|
||||||
|
Steel: 10
|
||||||
|
Plastic: 10
|
||||||
|
- type: StaticPrice
|
||||||
|
price: 30
|
||||||
|
|||||||
@@ -15,8 +15,6 @@
|
|||||||
components:
|
components:
|
||||||
- type: Sprite
|
- type: Sprite
|
||||||
sprite: Structures/Doors/Airlocks/Standard/engineering.rsi
|
sprite: Structures/Doors/Airlocks/Standard/engineering.rsi
|
||||||
- type: PaintableAirlock
|
|
||||||
department: Engineering
|
|
||||||
- type: Wires
|
- type: Wires
|
||||||
layoutId: AirlockEngineering
|
layoutId: AirlockEngineering
|
||||||
|
|
||||||
@@ -35,8 +33,6 @@
|
|||||||
components:
|
components:
|
||||||
- type: Sprite
|
- type: Sprite
|
||||||
sprite: Structures/Doors/Airlocks/Standard/cargo.rsi
|
sprite: Structures/Doors/Airlocks/Standard/cargo.rsi
|
||||||
- type: PaintableAirlock
|
|
||||||
department: Cargo
|
|
||||||
- type: Wires
|
- type: Wires
|
||||||
layoutId: AirlockCargo
|
layoutId: AirlockCargo
|
||||||
|
|
||||||
@@ -67,8 +63,6 @@
|
|||||||
components:
|
components:
|
||||||
- type: Sprite
|
- type: Sprite
|
||||||
sprite: Structures/Doors/Airlocks/Standard/medical.rsi
|
sprite: Structures/Doors/Airlocks/Standard/medical.rsi
|
||||||
- type: PaintableAirlock
|
|
||||||
department: Medical
|
|
||||||
- type: Wires
|
- type: Wires
|
||||||
layoutId: AirlockMedical
|
layoutId: AirlockMedical
|
||||||
|
|
||||||
@@ -95,8 +89,6 @@
|
|||||||
components:
|
components:
|
||||||
- type: Sprite
|
- type: Sprite
|
||||||
sprite: Structures/Doors/Airlocks/Standard/science.rsi
|
sprite: Structures/Doors/Airlocks/Standard/science.rsi
|
||||||
- type: PaintableAirlock
|
|
||||||
department: Science
|
|
||||||
- type: Wires
|
- type: Wires
|
||||||
layoutId: AirlockScience
|
layoutId: AirlockScience
|
||||||
|
|
||||||
@@ -109,8 +101,6 @@
|
|||||||
sprite: Structures/Doors/Airlocks/Standard/command.rsi
|
sprite: Structures/Doors/Airlocks/Standard/command.rsi
|
||||||
- type: WiresPanelSecurity
|
- type: WiresPanelSecurity
|
||||||
securityLevel: medSecurity
|
securityLevel: medSecurity
|
||||||
- type: PaintableAirlock
|
|
||||||
department: Command
|
|
||||||
- type: Wires
|
- type: Wires
|
||||||
layoutId: AirlockCommand
|
layoutId: AirlockCommand
|
||||||
|
|
||||||
@@ -121,8 +111,6 @@
|
|||||||
components:
|
components:
|
||||||
- type: Sprite
|
- type: Sprite
|
||||||
sprite: Structures/Doors/Airlocks/Standard/security.rsi
|
sprite: Structures/Doors/Airlocks/Standard/security.rsi
|
||||||
- type: PaintableAirlock
|
|
||||||
department: Security
|
|
||||||
- type: Wires
|
- type: Wires
|
||||||
layoutId: AirlockSecurity
|
layoutId: AirlockSecurity
|
||||||
|
|
||||||
@@ -151,6 +139,8 @@
|
|||||||
sprite: Structures/Doors/Airlocks/Standard/mining.rsi
|
sprite: Structures/Doors/Airlocks/Standard/mining.rsi
|
||||||
- type: Wires
|
- type: Wires
|
||||||
layoutId: AirlockCargo
|
layoutId: AirlockCargo
|
||||||
|
- type: Paintable
|
||||||
|
group: null
|
||||||
|
|
||||||
- type: entity
|
- type: entity
|
||||||
parent: AirlockCommand # if you get centcom door somehow it counts as command, also inherit panel
|
parent: AirlockCommand # if you get centcom door somehow it counts as command, also inherit panel
|
||||||
@@ -167,6 +157,8 @@
|
|||||||
components:
|
components:
|
||||||
- type: Sprite
|
- type: Sprite
|
||||||
sprite: Structures/Doors/Airlocks/Standard/hatch.rsi
|
sprite: Structures/Doors/Airlocks/Standard/hatch.rsi
|
||||||
|
- type: Paintable
|
||||||
|
group: null
|
||||||
|
|
||||||
- type: entity
|
- type: entity
|
||||||
parent: Airlock
|
parent: Airlock
|
||||||
@@ -175,6 +167,8 @@
|
|||||||
components:
|
components:
|
||||||
- type: Sprite
|
- type: Sprite
|
||||||
sprite: Structures/Doors/Airlocks/Standard/hatch_maint.rsi
|
sprite: Structures/Doors/Airlocks/Standard/hatch_maint.rsi
|
||||||
|
- type: Paintable
|
||||||
|
group: null
|
||||||
|
|
||||||
# Glass
|
# Glass
|
||||||
- type: entity
|
- type: entity
|
||||||
@@ -184,8 +178,6 @@
|
|||||||
components:
|
components:
|
||||||
- type: Sprite
|
- type: Sprite
|
||||||
sprite: Structures/Doors/Airlocks/Glass/engineering.rsi
|
sprite: Structures/Doors/Airlocks/Glass/engineering.rsi
|
||||||
- type: PaintableAirlock
|
|
||||||
department: Engineering
|
|
||||||
- type: Wires
|
- type: Wires
|
||||||
layoutId: AirlockEngineering
|
layoutId: AirlockEngineering
|
||||||
|
|
||||||
@@ -212,8 +204,6 @@
|
|||||||
components:
|
components:
|
||||||
- type: Sprite
|
- type: Sprite
|
||||||
sprite: Structures/Doors/Airlocks/Glass/cargo.rsi
|
sprite: Structures/Doors/Airlocks/Glass/cargo.rsi
|
||||||
- type: PaintableAirlock
|
|
||||||
department: Cargo
|
|
||||||
- type: Wires
|
- type: Wires
|
||||||
layoutId: AirlockCargo
|
layoutId: AirlockCargo
|
||||||
|
|
||||||
@@ -244,8 +234,6 @@
|
|||||||
components:
|
components:
|
||||||
- type: Sprite
|
- type: Sprite
|
||||||
sprite: Structures/Doors/Airlocks/Glass/medical.rsi
|
sprite: Structures/Doors/Airlocks/Glass/medical.rsi
|
||||||
- type: PaintableAirlock
|
|
||||||
department: Medical
|
|
||||||
- type: Wires
|
- type: Wires
|
||||||
layoutId: AirlockMedical
|
layoutId: AirlockMedical
|
||||||
|
|
||||||
@@ -272,8 +260,6 @@
|
|||||||
components:
|
components:
|
||||||
- type: Sprite
|
- type: Sprite
|
||||||
sprite: Structures/Doors/Airlocks/Glass/science.rsi
|
sprite: Structures/Doors/Airlocks/Glass/science.rsi
|
||||||
- type: PaintableAirlock
|
|
||||||
department: Science
|
|
||||||
- type: Wires
|
- type: Wires
|
||||||
layoutId: AirlockScience
|
layoutId: AirlockScience
|
||||||
|
|
||||||
@@ -284,8 +270,6 @@
|
|||||||
components:
|
components:
|
||||||
- type: Sprite
|
- type: Sprite
|
||||||
sprite: Structures/Doors/Airlocks/Glass/command.rsi
|
sprite: Structures/Doors/Airlocks/Glass/command.rsi
|
||||||
- type: PaintableAirlock
|
|
||||||
department: Command
|
|
||||||
- type: WiresPanelSecurity
|
- type: WiresPanelSecurity
|
||||||
securityLevel: medSecurity
|
securityLevel: medSecurity
|
||||||
- type: Wires
|
- type: Wires
|
||||||
@@ -298,8 +282,6 @@
|
|||||||
components:
|
components:
|
||||||
- type: Sprite
|
- type: Sprite
|
||||||
sprite: Structures/Doors/Airlocks/Glass/security.rsi
|
sprite: Structures/Doors/Airlocks/Glass/security.rsi
|
||||||
- type: PaintableAirlock
|
|
||||||
department: Security
|
|
||||||
- type: Wires
|
- type: Wires
|
||||||
layoutId: AirlockSecurity
|
layoutId: AirlockSecurity
|
||||||
|
|
||||||
@@ -318,6 +300,8 @@
|
|||||||
components:
|
components:
|
||||||
- type: Sprite
|
- type: Sprite
|
||||||
sprite: Structures/Doors/Airlocks/Glass/mining.rsi
|
sprite: Structures/Doors/Airlocks/Glass/mining.rsi
|
||||||
|
- type: Paintable
|
||||||
|
group: null
|
||||||
|
|
||||||
- type: entity
|
- type: entity
|
||||||
parent: AirlockCommandGlass # see standard
|
parent: AirlockCommandGlass # see standard
|
||||||
@@ -342,6 +326,8 @@
|
|||||||
components:
|
components:
|
||||||
- type: Sprite
|
- type: Sprite
|
||||||
sprite: Structures/Doors/Airlocks/Standard/xeno.rsi
|
sprite: Structures/Doors/Airlocks/Standard/xeno.rsi
|
||||||
|
- type: Paintable
|
||||||
|
group: null
|
||||||
|
|
||||||
- type: entity
|
- type: entity
|
||||||
parent: AirlockGlass
|
parent: AirlockGlass
|
||||||
@@ -350,3 +336,5 @@
|
|||||||
components:
|
components:
|
||||||
- type: Sprite
|
- type: Sprite
|
||||||
sprite: Structures/Doors/Airlocks/Glass/xeno.rsi
|
sprite: Structures/Doors/Airlocks/Glass/xeno.rsi
|
||||||
|
- type: Paintable
|
||||||
|
group: null
|
||||||
|
|||||||
@@ -158,9 +158,8 @@
|
|||||||
- board
|
- board
|
||||||
- type: PlacementReplacement
|
- type: PlacementReplacement
|
||||||
key: walls
|
key: walls
|
||||||
- type: PaintableAirlock
|
- type: Paintable
|
||||||
group: Standard
|
group: AirlockStandard
|
||||||
department: Civilian
|
|
||||||
- type: StaticPrice
|
- type: StaticPrice
|
||||||
price: 150
|
price: 150
|
||||||
- type: LightningTarget
|
- type: LightningTarget
|
||||||
@@ -220,8 +219,8 @@
|
|||||||
- type: Construction
|
- type: Construction
|
||||||
graph: Airlock
|
graph: Airlock
|
||||||
node: glassAirlock
|
node: glassAirlock
|
||||||
- type: PaintableAirlock
|
- type: Paintable
|
||||||
group: Glass
|
group: AirlockGlass
|
||||||
- type: RadiationBlocker
|
- type: RadiationBlocker
|
||||||
resistance: 2
|
resistance: 2
|
||||||
- type: Tag
|
- type: Tag
|
||||||
|
|||||||
@@ -10,6 +10,8 @@
|
|||||||
node: airlock
|
node: airlock
|
||||||
containers:
|
containers:
|
||||||
- board
|
- board
|
||||||
|
- type: Paintable
|
||||||
|
group: null
|
||||||
|
|
||||||
- type: entity
|
- type: entity
|
||||||
parent: AirlockGlass
|
parent: AirlockGlass
|
||||||
@@ -25,3 +27,5 @@
|
|||||||
- board
|
- board
|
||||||
- type: StaticPrice
|
- type: StaticPrice
|
||||||
price: 165
|
price: 165
|
||||||
|
- type: Paintable
|
||||||
|
group: null
|
||||||
|
|||||||
@@ -16,11 +16,10 @@
|
|||||||
path: /Audio/Machines/airlock_deny.ogg
|
path: /Audio/Machines/airlock_deny.ogg
|
||||||
- type: Sprite
|
- type: Sprite
|
||||||
sprite: Structures/Doors/Airlocks/Standard/external.rsi
|
sprite: Structures/Doors/Airlocks/Standard/external.rsi
|
||||||
- type: PaintableAirlock
|
|
||||||
group: External
|
|
||||||
department: null
|
|
||||||
- type: Wires
|
- type: Wires
|
||||||
layoutId: AirlockExternal
|
layoutId: AirlockExternal
|
||||||
|
- type: Paintable
|
||||||
|
group: null
|
||||||
|
|
||||||
- type: entity
|
- type: entity
|
||||||
parent: AirlockExternal
|
parent: AirlockExternal
|
||||||
@@ -33,8 +32,6 @@
|
|||||||
enabled: false
|
enabled: false
|
||||||
- type: Sprite
|
- type: Sprite
|
||||||
sprite: Structures/Doors/Airlocks/Glass/external.rsi
|
sprite: Structures/Doors/Airlocks/Glass/external.rsi
|
||||||
- type: PaintableAirlock
|
|
||||||
group: ExternalGlass
|
|
||||||
- type: Fixtures
|
- type: Fixtures
|
||||||
fixtures:
|
fixtures:
|
||||||
fix1:
|
fix1:
|
||||||
|
|||||||
@@ -52,14 +52,13 @@
|
|||||||
- type: Tag
|
- type: Tag
|
||||||
tags:
|
tags:
|
||||||
- ForceNoFixRotations
|
- ForceNoFixRotations
|
||||||
- type: PaintableAirlock
|
|
||||||
group: Shuttle
|
|
||||||
department: null
|
|
||||||
- type: Construction
|
- type: Construction
|
||||||
graph: AirlockShuttle
|
graph: AirlockShuttle
|
||||||
node: airlock
|
node: airlock
|
||||||
- type: StaticPrice
|
- type: StaticPrice
|
||||||
price: 350
|
price: 350
|
||||||
|
- type: Paintable
|
||||||
|
group: null
|
||||||
|
|
||||||
- type: entity
|
- type: entity
|
||||||
id: AirlockGlassShuttle
|
id: AirlockGlassShuttle
|
||||||
@@ -72,8 +71,6 @@
|
|||||||
sprite: Structures/Doors/Airlocks/Glass/shuttle.rsi
|
sprite: Structures/Doors/Airlocks/Glass/shuttle.rsi
|
||||||
- type: Occluder
|
- type: Occluder
|
||||||
enabled: false
|
enabled: false
|
||||||
- type: PaintableAirlock
|
|
||||||
group: ShuttleGlass
|
|
||||||
- type: Door
|
- type: Door
|
||||||
occludes: false
|
occludes: false
|
||||||
- type: Fixtures
|
- type: Fixtures
|
||||||
|
|||||||
@@ -1,87 +0,0 @@
|
|||||||
- type: AirlockGroup
|
|
||||||
id: Standard
|
|
||||||
iconPriority: 100
|
|
||||||
stylePaths:
|
|
||||||
atmospherics: Structures/Doors/Airlocks/Standard/atmospherics.rsi
|
|
||||||
basic: Structures/Doors/Airlocks/Standard/basic.rsi
|
|
||||||
cargo: Structures/Doors/Airlocks/Standard/cargo.rsi
|
|
||||||
chemistry: Structures/Doors/Airlocks/Standard/chemistry.rsi
|
|
||||||
command: Structures/Doors/Airlocks/Standard/command.rsi
|
|
||||||
engineering: Structures/Doors/Airlocks/Standard/engineering.rsi
|
|
||||||
freezer: Structures/Doors/Airlocks/Standard/freezer.rsi
|
|
||||||
hydroponics: Structures/Doors/Airlocks/Standard/hydroponics.rsi
|
|
||||||
maintenance: Structures/Doors/Airlocks/Standard/maint.rsi
|
|
||||||
medical: Structures/Doors/Airlocks/Standard/medical.rsi
|
|
||||||
salvage: Structures/Doors/Airlocks/Standard/salvage.rsi
|
|
||||||
science: Structures/Doors/Airlocks/Standard/science.rsi
|
|
||||||
security: Structures/Doors/Airlocks/Standard/security.rsi
|
|
||||||
virology: Structures/Doors/Airlocks/Standard/virology.rsi
|
|
||||||
|
|
||||||
- type: AirlockGroup
|
|
||||||
id: Glass
|
|
||||||
iconPriority: 90
|
|
||||||
stylePaths:
|
|
||||||
atmospherics: Structures/Doors/Airlocks/Glass/atmospherics.rsi
|
|
||||||
basic: Structures/Doors/Airlocks/Glass/basic.rsi
|
|
||||||
cargo: Structures/Doors/Airlocks/Glass/cargo.rsi
|
|
||||||
command: Structures/Doors/Airlocks/Glass/command.rsi
|
|
||||||
chemistry: Structures/Doors/Airlocks/Glass/chemistry.rsi
|
|
||||||
science: Structures/Doors/Airlocks/Glass/science.rsi
|
|
||||||
engineering: Structures/Doors/Airlocks/Glass/engineering.rsi
|
|
||||||
glass: Structures/Doors/Airlocks/Glass/glass.rsi
|
|
||||||
hydroponics: Structures/Doors/Airlocks/Glass/hydroponics.rsi
|
|
||||||
maintenance: Structures/Doors/Airlocks/Glass/maint.rsi
|
|
||||||
medical: Structures/Doors/Airlocks/Glass/medical.rsi
|
|
||||||
salvage: Structures/Doors/Airlocks/Glass/salvage.rsi
|
|
||||||
security: Structures/Doors/Airlocks/Glass/security.rsi
|
|
||||||
virology: Structures/Doors/Airlocks/Glass/virology.rsi
|
|
||||||
|
|
||||||
- type: AirlockGroup
|
|
||||||
id: Windoor
|
|
||||||
iconPriority: 80
|
|
||||||
stylePaths:
|
|
||||||
basic: Structures/Doors/Airlocks/Glass/glass.rsi
|
|
||||||
|
|
||||||
- type: AirlockGroup
|
|
||||||
id: External
|
|
||||||
iconPriority: 70
|
|
||||||
stylePaths:
|
|
||||||
external: Structures/Doors/Airlocks/Standard/external.rsi
|
|
||||||
|
|
||||||
- type: AirlockGroup
|
|
||||||
id: ExternalGlass
|
|
||||||
iconPriority: 60
|
|
||||||
stylePaths:
|
|
||||||
external: Structures/Doors/Airlocks/Glass/external.rsi
|
|
||||||
|
|
||||||
- type: AirlockGroup
|
|
||||||
id: Shuttle
|
|
||||||
iconPriority: 50
|
|
||||||
stylePaths:
|
|
||||||
shuttle: Structures/Doors/Airlocks/Standard/shuttle.rsi
|
|
||||||
|
|
||||||
- type: AirlockGroup
|
|
||||||
id: ShuttleGlass
|
|
||||||
iconPriority: 40
|
|
||||||
stylePaths:
|
|
||||||
shuttle: Structures/Doors/Airlocks/Glass/shuttle.rsi
|
|
||||||
|
|
||||||
# fun
|
|
||||||
- type: airlockDepartments
|
|
||||||
id: Departments
|
|
||||||
departments:
|
|
||||||
atmospherics: Engineering
|
|
||||||
basic: Civilian
|
|
||||||
cargo: Cargo
|
|
||||||
chemistry: Medical
|
|
||||||
command: Command
|
|
||||||
engineering: Engineering
|
|
||||||
freezer: Civilian
|
|
||||||
glass: Civilian
|
|
||||||
hydroponics: Civilian
|
|
||||||
maintenance: Civilian
|
|
||||||
medical: Medical
|
|
||||||
salvage: Cargo
|
|
||||||
science: Science
|
|
||||||
security: Security
|
|
||||||
virology: Medical
|
|
||||||
@@ -112,6 +112,8 @@
|
|||||||
- type: GuideHelp
|
- type: GuideHelp
|
||||||
guides:
|
guides:
|
||||||
- GasCanisters
|
- GasCanisters
|
||||||
|
- type: Paintable
|
||||||
|
group: Canisters
|
||||||
|
|
||||||
- type: entity
|
- type: entity
|
||||||
parent: GasCanister
|
parent: GasCanister
|
||||||
|
|||||||
@@ -54,6 +54,8 @@
|
|||||||
node: done
|
node: done
|
||||||
containers:
|
containers:
|
||||||
- entity_storage
|
- entity_storage
|
||||||
|
- type: Paintable
|
||||||
|
group: Locker
|
||||||
|
|
||||||
- type: entity
|
- type: entity
|
||||||
id: LockerBaseSecure
|
id: LockerBaseSecure
|
||||||
|
|||||||
@@ -17,6 +17,8 @@
|
|||||||
path: /Audio/Effects/woodenclosetclose.ogg
|
path: /Audio/Effects/woodenclosetclose.ogg
|
||||||
openSound:
|
openSound:
|
||||||
path: /Audio/Effects/woodenclosetopen.ogg
|
path: /Audio/Effects/woodenclosetopen.ogg
|
||||||
|
- type: Paintable
|
||||||
|
group: null # not shaped like other lockers
|
||||||
|
|
||||||
# Basic
|
# Basic
|
||||||
- type: entity
|
- type: entity
|
||||||
@@ -190,6 +192,8 @@
|
|||||||
node: done
|
node: done
|
||||||
containers:
|
containers:
|
||||||
- entity_storage
|
- entity_storage
|
||||||
|
- type: Paintable
|
||||||
|
group: null
|
||||||
|
|
||||||
- type: entity
|
- type: entity
|
||||||
id: LockerFreezer
|
id: LockerFreezer
|
||||||
|
|||||||
@@ -124,6 +124,8 @@
|
|||||||
node: done
|
node: done
|
||||||
containers:
|
containers:
|
||||||
- entity_storage
|
- entity_storage
|
||||||
|
- type: Paintable
|
||||||
|
group: Closet
|
||||||
|
|
||||||
#Wall Closet
|
#Wall Closet
|
||||||
- type: entity
|
- type: entity
|
||||||
@@ -205,6 +207,8 @@
|
|||||||
node: done
|
node: done
|
||||||
containers:
|
containers:
|
||||||
- entity_storage
|
- entity_storage
|
||||||
|
- type: Paintable
|
||||||
|
group: WallCloset
|
||||||
|
|
||||||
#Wall locker
|
#Wall locker
|
||||||
- type: entity
|
- type: entity
|
||||||
@@ -228,6 +232,8 @@
|
|||||||
- state: welded
|
- state: welded
|
||||||
visible: false
|
visible: false
|
||||||
map: ["enum.WeldableLayers.BaseWelded"]
|
map: ["enum.WeldableLayers.BaseWelded"]
|
||||||
|
- type: Paintable
|
||||||
|
group: WallLocker
|
||||||
|
|
||||||
#Base suit storage unit
|
#Base suit storage unit
|
||||||
#I am terribly sorry for duplicating the closet almost-wholesale, but the game malds at me if I don't so here we are.
|
#I am terribly sorry for duplicating the closet almost-wholesale, but the game malds at me if I don't so here we are.
|
||||||
|
|||||||
@@ -154,3 +154,5 @@
|
|||||||
- Energy
|
- Energy
|
||||||
reflectProb: 0.2
|
reflectProb: 0.2
|
||||||
spread: 90
|
spread: 90
|
||||||
|
- type: Paintable
|
||||||
|
group: CrateSecure
|
||||||
|
|||||||
@@ -12,6 +12,8 @@
|
|||||||
- Energy
|
- Energy
|
||||||
reflectProb: 0.2
|
reflectProb: 0.2
|
||||||
spread: 90
|
spread: 90
|
||||||
|
- type: Paintable
|
||||||
|
group: CrateSteel
|
||||||
- type: RadiationBlockingContainer
|
- type: RadiationBlockingContainer
|
||||||
resistance: 2.5
|
resistance: 2.5
|
||||||
|
|
||||||
@@ -31,6 +33,8 @@
|
|||||||
- entity_storage
|
- entity_storage
|
||||||
- type: StaticPrice
|
- type: StaticPrice
|
||||||
price: 100
|
price: 100
|
||||||
|
- type: Paintable
|
||||||
|
group: CratePlastic
|
||||||
|
|
||||||
- type: entity
|
- type: entity
|
||||||
parent: CratePlastic
|
parent: CratePlastic
|
||||||
@@ -49,6 +53,8 @@
|
|||||||
node: done
|
node: done
|
||||||
containers:
|
containers:
|
||||||
- entity_storage
|
- entity_storage
|
||||||
|
- type: Paintable
|
||||||
|
group: null
|
||||||
|
|
||||||
- type: entity
|
- type: entity
|
||||||
parent: CratePlastic
|
parent: CratePlastic
|
||||||
@@ -840,6 +846,8 @@
|
|||||||
sprite: Structures/Storage/Crates/labels.rsi
|
sprite: Structures/Storage/Crates/labels.rsi
|
||||||
offset: "0.0,0.03125"
|
offset: "0.0,0.03125"
|
||||||
map: ["enum.PaperLabelVisuals.Layer"]
|
map: ["enum.PaperLabelVisuals.Layer"]
|
||||||
|
- type: Paintable
|
||||||
|
group: null
|
||||||
|
|
||||||
- type: entity
|
- type: entity
|
||||||
parent: CrateBaseSecure
|
parent: CrateBaseSecure
|
||||||
@@ -866,6 +874,8 @@
|
|||||||
map: ["enum.PaperLabelVisuals.Layer"]
|
map: ["enum.PaperLabelVisuals.Layer"]
|
||||||
- type: AccessReader
|
- type: AccessReader
|
||||||
access: [["Janitor"]]
|
access: [["Janitor"]]
|
||||||
|
- type: Paintable
|
||||||
|
group: null
|
||||||
|
|
||||||
- type: entity
|
- type: entity
|
||||||
parent: CrateBaseWeldable
|
parent: CrateBaseWeldable
|
||||||
|
|||||||
40
Resources/Prototypes/Paintables/airlock_groups.yml
Normal file
40
Resources/Prototypes/Paintables/airlock_groups.yml
Normal file
@@ -0,0 +1,40 @@
|
|||||||
|
- type: paintableGroup
|
||||||
|
id: AirlockStandard
|
||||||
|
time: 3
|
||||||
|
cost: 3
|
||||||
|
defaultStyle: basic
|
||||||
|
styles:
|
||||||
|
atmospherics: AirlockAtmospherics
|
||||||
|
basic: Airlock
|
||||||
|
cargo: AirlockCargo
|
||||||
|
chemistry: AirlockChemistry
|
||||||
|
command: AirlockCommand
|
||||||
|
engineering: AirlockEngineering
|
||||||
|
freezer: AirlockFreezer
|
||||||
|
hydroponics: AirlockHydroponics
|
||||||
|
maintenance: AirlockMaint
|
||||||
|
medical: AirlockMedical
|
||||||
|
salvage: AirlockSalvage
|
||||||
|
science: AirlockScience
|
||||||
|
security: AirlockSecurity
|
||||||
|
virology: AirlockVirology
|
||||||
|
|
||||||
|
- type: paintableGroup
|
||||||
|
id: AirlockGlass
|
||||||
|
time: 3
|
||||||
|
cost: 3
|
||||||
|
defaultStyle: basic
|
||||||
|
styles:
|
||||||
|
atmospherics: AirlockAtmosphericsGlass
|
||||||
|
basic: AirlockGlass
|
||||||
|
cargo: AirlockCargoGlass
|
||||||
|
chemistry: AirlockChemistryGlass
|
||||||
|
command: AirlockCommandGlass
|
||||||
|
engineering: AirlockEngineeringGlass
|
||||||
|
hydroponics: AirlockHydroponicsGlass
|
||||||
|
maintenance: AirlockMaintGlass
|
||||||
|
medical: AirlockMedicalGlass
|
||||||
|
salvage: AirlockSalvageGlass
|
||||||
|
science: AirlockScienceGlass
|
||||||
|
security: AirlockSecurityGlass
|
||||||
|
virology: AirlockVirologyGlass
|
||||||
16
Resources/Prototypes/Paintables/canister_groups.yml
Normal file
16
Resources/Prototypes/Paintables/canister_groups.yml
Normal file
@@ -0,0 +1,16 @@
|
|||||||
|
- type: paintableGroup
|
||||||
|
cost: 2
|
||||||
|
id: Canisters
|
||||||
|
defaultStyle: storage
|
||||||
|
styles:
|
||||||
|
air: AirCanister
|
||||||
|
ammonia: AmmoniaCanister
|
||||||
|
carbon-dioxide: CarbonDioxideCanister
|
||||||
|
frezon: FrezonCanister
|
||||||
|
nitrogen: NitrogenCanister
|
||||||
|
nitrous-oxide: NitrousOxideCanister
|
||||||
|
oxygen: OxygenCanister
|
||||||
|
plasma: PlasmaCanister
|
||||||
|
storage: StorageCanister
|
||||||
|
tritium: TritiumCanister
|
||||||
|
water-vapor: WaterVaporCanister
|
||||||
25
Resources/Prototypes/Paintables/categories.yml
Normal file
25
Resources/Prototypes/Paintables/categories.yml
Normal file
@@ -0,0 +1,25 @@
|
|||||||
|
- type: paintableGroupCategory
|
||||||
|
id: Airlocks
|
||||||
|
groups:
|
||||||
|
- AirlockStandard
|
||||||
|
- AirlockGlass
|
||||||
|
|
||||||
|
- type: paintableGroupCategory
|
||||||
|
id: Canisters
|
||||||
|
groups:
|
||||||
|
- Canisters
|
||||||
|
|
||||||
|
- type: paintableGroupCategory
|
||||||
|
id: Crates
|
||||||
|
groups:
|
||||||
|
- CrateSteel
|
||||||
|
- CratePlastic
|
||||||
|
- CrateSecure
|
||||||
|
|
||||||
|
- type: paintableGroupCategory
|
||||||
|
id: Lockers
|
||||||
|
groups:
|
||||||
|
- Locker
|
||||||
|
- Closet
|
||||||
|
- WallLocker
|
||||||
|
- WallCloset
|
||||||
38
Resources/Prototypes/Paintables/crate_groups.yml
Normal file
38
Resources/Prototypes/Paintables/crate_groups.yml
Normal file
@@ -0,0 +1,38 @@
|
|||||||
|
- type: paintableGroup
|
||||||
|
id: CrateSteel
|
||||||
|
cost: 2
|
||||||
|
defaultStyle: basic
|
||||||
|
styles:
|
||||||
|
basic: CrateGenericSteel
|
||||||
|
electrical: CrateElectrical
|
||||||
|
engineering: CrateEngineering
|
||||||
|
radiation: CrateRadiation
|
||||||
|
science: CrateScience
|
||||||
|
surgery: CrateSurgery
|
||||||
|
|
||||||
|
- type: paintableGroup
|
||||||
|
id: CratePlastic
|
||||||
|
cost: 2
|
||||||
|
defaultStyle: basic
|
||||||
|
styles:
|
||||||
|
basic: CratePlastic
|
||||||
|
hydroponics: CrateHydroponics
|
||||||
|
medical: CrateMedical
|
||||||
|
oxygen: CrateInternals
|
||||||
|
|
||||||
|
- type: paintableGroup
|
||||||
|
id: CrateSecure
|
||||||
|
cost: 2
|
||||||
|
defaultStyle: basic
|
||||||
|
styles:
|
||||||
|
basic: CrateSecure
|
||||||
|
chemistry: CrateChemistrySecure
|
||||||
|
command: CrateCommandSecure
|
||||||
|
engineering: CrateEngineeringSecure
|
||||||
|
hydroponics: CrateHydroSecure
|
||||||
|
medical: CrateMedicalSecure
|
||||||
|
plasma: CratePlasma
|
||||||
|
private: CratePrivateSecure
|
||||||
|
science: CrateScienceSecure
|
||||||
|
secgear: CrateSecgear
|
||||||
|
weapon: CrateWeaponSecure
|
||||||
80
Resources/Prototypes/Paintables/locker_groups.yml
Normal file
80
Resources/Prototypes/Paintables/locker_groups.yml
Normal file
@@ -0,0 +1,80 @@
|
|||||||
|
- type: paintableGroup
|
||||||
|
id: Locker
|
||||||
|
cost: 2
|
||||||
|
defaultStyle: basic
|
||||||
|
styles:
|
||||||
|
atmospherics: LockerAtmospherics
|
||||||
|
basic: ClosetSteelBase
|
||||||
|
botanist: LockerBotanist
|
||||||
|
brigmedic: LockerBrigmedic
|
||||||
|
captain: LockerCaptain
|
||||||
|
ce: LockerChiefEngineer
|
||||||
|
chemical: LockerChemistry
|
||||||
|
clown: LockerClown
|
||||||
|
cmo: LockerChiefMedicalOfficer
|
||||||
|
doctor: LockerMedical
|
||||||
|
electrical: LockerElectricalSupplies
|
||||||
|
engineer: LockerEngineer
|
||||||
|
evac: LockerEvacRepair
|
||||||
|
hop: LockerHeadOfPersonnel
|
||||||
|
hos: LockerHeadOfSecurity
|
||||||
|
mime: LockerMime
|
||||||
|
medicine: LockerMedicine
|
||||||
|
paramedic: LockerParamedic
|
||||||
|
quartermaster: LockerQuarterMaster
|
||||||
|
rd: LockerResearchDirector
|
||||||
|
representative: LockerRepresentative
|
||||||
|
salvage: LockerSalvageSpecialist
|
||||||
|
scientist: LockerScientist
|
||||||
|
security: LockerSecurity
|
||||||
|
welding: LockerWeldingSupplies
|
||||||
|
|
||||||
|
- type: paintableGroup
|
||||||
|
id: Closet
|
||||||
|
cost: 2
|
||||||
|
defaultStyle: basic
|
||||||
|
styles:
|
||||||
|
basic: ClosetSteelBase
|
||||||
|
biohazard: ClosetL3
|
||||||
|
biohazard-janitor: ClosetL3Janitor
|
||||||
|
biohazard-science: ClosetL3Science
|
||||||
|
biohazard-security: ClosetL3Security
|
||||||
|
biohazard-virology: ClosetL3Virology
|
||||||
|
bomb: ClosetBomb
|
||||||
|
bomb-janitor: ClosetJanitorBomb
|
||||||
|
chef: ClosetChef
|
||||||
|
fire: ClosetFire
|
||||||
|
janitor: ClosetJanitor
|
||||||
|
legal: ClosetLegal
|
||||||
|
nitrogen: ClosetEmergencyN2
|
||||||
|
oxygen: ClosetEmergency
|
||||||
|
radiation: ClosetRadiationSuit
|
||||||
|
tool: ClosetTool
|
||||||
|
|
||||||
|
- type: paintableGroup
|
||||||
|
id: WallCloset
|
||||||
|
cost: 2
|
||||||
|
defaultStyle: basic
|
||||||
|
styles:
|
||||||
|
atmospherics: ClosetWallAtmospherics
|
||||||
|
basic: ClosetWall
|
||||||
|
black: ClosetWallBlack
|
||||||
|
blue: ClosetWallBlue
|
||||||
|
fire: ClosetWallFire
|
||||||
|
green: ClosetWallGreen
|
||||||
|
grey: ClosetWallGrey
|
||||||
|
mixed: ClosetWallMixed
|
||||||
|
nitrogen: ClosetWallEmergencyN2
|
||||||
|
orange: ClosetWallOrange
|
||||||
|
oxygen: ClosetWallEmergency
|
||||||
|
pink: ClosetWallPink
|
||||||
|
white: ClosetWallWhite
|
||||||
|
yellow: ClosetWallYellow
|
||||||
|
|
||||||
|
- type: paintableGroup
|
||||||
|
id: WallLocker
|
||||||
|
cost: 2
|
||||||
|
defaultStyle: medical
|
||||||
|
styles:
|
||||||
|
evac: LockerWallEvacRepair
|
||||||
|
medical: LockerWallMedical
|
||||||
@@ -12,6 +12,7 @@
|
|||||||
- NetworkConfigurator
|
- NetworkConfigurator
|
||||||
- Signaller
|
- Signaller
|
||||||
- SprayPainter
|
- SprayPainter
|
||||||
|
- SprayPainterAmmo
|
||||||
- FlashlightLantern
|
- FlashlightLantern
|
||||||
- HandheldGPSBasic
|
- HandheldGPSBasic
|
||||||
- TRayScanner
|
- TRayScanner
|
||||||
|
|||||||
@@ -154,11 +154,19 @@
|
|||||||
- type: latheRecipe
|
- type: latheRecipe
|
||||||
parent: BaseToolRecipe
|
parent: BaseToolRecipe
|
||||||
id: SprayPainter
|
id: SprayPainter
|
||||||
result: SprayPainter
|
result: SprayPainterEmpty
|
||||||
materials:
|
materials:
|
||||||
Steel: 300
|
Steel: 300
|
||||||
Plastic: 100
|
Plastic: 100
|
||||||
|
|
||||||
|
- type: latheRecipe
|
||||||
|
parent: BaseToolRecipe
|
||||||
|
id: SprayPainterAmmo
|
||||||
|
result: SprayPainterAmmo
|
||||||
|
materials:
|
||||||
|
Steel: 150
|
||||||
|
Plastic: 50
|
||||||
|
|
||||||
- type: latheRecipe
|
- type: latheRecipe
|
||||||
parent: BaseToolRecipe
|
parent: BaseToolRecipe
|
||||||
id: UtilityBelt
|
id: UtilityBelt
|
||||||
|
|||||||
Binary file not shown.
|
After Width: | Height: | Size: 274 B |
Binary file not shown.
|
After Width: | Height: | Size: 305 B |
BIN
Resources/Textures/Objects/Tools/spray_painter.rsi/ammo.png
Normal file
BIN
Resources/Textures/Objects/Tools/spray_painter.rsi/ammo.png
Normal file
Binary file not shown.
|
After Width: | Height: | Size: 947 B |
@@ -1,22 +1,33 @@
|
|||||||
{
|
{
|
||||||
"copyright" : "Taken from https://github.com/tgstation/tgstation at commit a21274e56ae84b2c96e8b6beeca805df3d5402e8, Inhand sprites by onesch",
|
"version": 1,
|
||||||
"license" : "CC-BY-SA-3.0",
|
"license": "CC-BY-SA-3.0",
|
||||||
"size" : {
|
"copyright": "Taken from https://github.com/tgstation/tgstation at commit a21274e56ae84b2c96e8b6beeca805df3d5402e8, Inhand sprites by onesch, ammo by Paradoxmi (Discord).",
|
||||||
"x" : 32,
|
"size": {
|
||||||
"y" : 32
|
"x": 32,
|
||||||
},
|
"y": 32
|
||||||
"states" : [
|
},
|
||||||
{
|
"states": [
|
||||||
"name" : "spray_painter"
|
{
|
||||||
},
|
"name": "spray_painter"
|
||||||
{
|
},
|
||||||
"name": "inhand-left",
|
{
|
||||||
"directions": 4
|
"name": "ammo"
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
"name": "inhand-right",
|
"name": "inhand-left",
|
||||||
"directions": 4
|
"directions": 4
|
||||||
}
|
},
|
||||||
],
|
{
|
||||||
"version" : 1
|
"name": "inhand-right",
|
||||||
|
"directions": 4
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"name": "ammo-inhand-left",
|
||||||
|
"directions": 4
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"name": "ammo-inhand-right",
|
||||||
|
"directions": 4
|
||||||
|
}
|
||||||
|
]
|
||||||
}
|
}
|
||||||
|
|||||||
Reference in New Issue
Block a user