Decal Placer + add new decals for mapping (#6548)

* abomination

* okay its less unabashedly garbage now

* other UI changes

* its britney bitch

* proper greyscale full/half/quarter tiles

* misc cleanup

* rsi

* Add a palette system. It's Kara's problem now.

* oops

* a

* Departmental palette alpha tweaks

* oopy

* so true

* Update Content.Shared/Decals/ColorPalettePrototype.cs

Co-authored-by: metalgearsloth <31366439+metalgearsloth@users.noreply.github.com>

* fixes for that

* neutral light color and new warning lines

* dirt

* checkerboards

* oop

Co-authored-by: moonheart08 <moonheart08@users.noreply.github.com>
Co-authored-by: metalgearsloth <31366439+metalgearsloth@users.noreply.github.com>
This commit is contained in:
mirrorcult
2022-02-08 13:54:41 -07:00
committed by GitHub
parent 2d5c082eba
commit 31769edf5f
80 changed files with 1223 additions and 12 deletions

View File

@@ -8,6 +8,7 @@
<GridContainer Columns="3">
<Button Name="SpawnEntitiesButton" Text="{Loc 'sandbox-window-spawn-entities-button'}" />
<Button Name="SpawnTilesButton" Text="{Loc 'sandbox-window-spawn-tiles-button'} " />
<Button Name="SpawnDecalsButton" Text="{Loc 'sandbox-window-spawn-decals-button'}" />
<Button Name="LoadGamePrototypeButton" Text="{Loc 'load-game-prototype'}"/>
<cc:CommandButton Command="deleteewc Singularity" Name="DeleteSingulos" Text="{Loc 'delete-singularities'}"/>
<cc:UICommandButton Command="events" Text="{Loc 'open-station-events'}" WindowType="{x:Type abt:StationEventsWindow}" />

View File

@@ -1,5 +1,6 @@
using System.IO;
using Content.Client.Administration.Managers;
using Content.Client.Decals.UI;
using Content.Shared.Administration;
using Robust.Client.AutoGenerated;
using Robust.Client.Placement;
@@ -16,10 +17,11 @@ using Robust.Shared.Prototypes;
namespace Content.Client.Administration.UI.Tabs.AdminbusTab
{
[GenerateTypedNameReferences]
public partial class AdminbusTab : Control
public sealed partial class AdminbusTab : Control
{
private EntitySpawnWindow? _entitySpawnWindow;
private TileSpawnWindow? _tileSpawnWindow;
private DecalPlacerWindow? _decalPlacerWindow;
public AdminbusTab()
{
@@ -29,6 +31,7 @@ namespace Content.Client.Administration.UI.Tabs.AdminbusTab
// TODO: This will probably need some command check at some point
SpawnEntitiesButton.OnPressed += SpawnEntitiesButtonOnOnPressed;
SpawnTilesButton.OnPressed += SpawnTilesButtonOnOnPressed;
SpawnDecalsButton.OnPressed += SpawnDecalsButtonOnPressed;
LoadGamePrototypeButton.OnPressed += LoadGamePrototypeButtonOnOnPressed;
LoadGamePrototypeButton.Visible = IoCManager.Resolve<IClientAdminManager>().HasFlag(AdminFlags.Query);
}
@@ -73,5 +76,15 @@ namespace Content.Client.Administration.UI.Tabs.AdminbusTab
EntitySystem.Get<AdminSystem>().OpenCommand(_tileSpawnWindow);
}
private void SpawnDecalsButtonOnPressed(BaseButton.ButtonEventArgs obj)
{
if (_decalPlacerWindow == null || _decalPlacerWindow.Disposed)
{
_decalPlacerWindow = new DecalPlacerWindow(IoCManager.Resolve<IPrototypeManager>());
}
EntitySystem.Get<AdminSystem>().OpenCommand(_decalPlacerWindow);
}
}
}

View File

@@ -0,0 +1,112 @@
using Content.Shared.Decals;
using Robust.Client.GameObjects;
using Robust.Client.Input;
using Robust.Shared.Input;
using Robust.Shared.Input.Binding;
namespace Content.Client.Decals;
// This is shit and basically a half-rewrite of PlacementManager
// TODO refactor placementmanager so this isnt shit anymore
public sealed class DecalPlacementSystem : EntitySystem
{
[Dependency] private readonly IInputManager _inputManager = default!;
[Dependency] private readonly InputSystem _inputSystem = default!;
private string? _decalId;
private Color _decalColor = Color.White;
private Angle _decalAngle = Angle.Zero;
private bool _snap;
private int _zIndex;
private bool _cleanable;
private bool _active;
private bool _placing;
private bool _erasing;
public override void Initialize()
{
base.Initialize();
CommandBinds.Builder.Bind(EngineKeyFunctions.EditorPlaceObject, new PointerStateInputCmdHandler(
(session, coords, uid) =>
{
if (!_active || _placing || _decalId == null)
return false;
_placing = true;
if (_snap)
{
var newPos = new Vector2(
(float) (MathF.Round(coords.X - 0.5f, MidpointRounding.AwayFromZero) + 0.5),
(float) (MathF.Round(coords.Y - 0.5f, MidpointRounding.AwayFromZero) + 0.5)
);
coords = coords.WithPosition(newPos);
}
coords = coords.Offset(new Vector2(-0.5f, -0.5f));
if (!coords.IsValid(EntityManager))
return false;
var decal = new Decal(coords.Position, _decalId, _decalColor, _decalAngle, _zIndex, _cleanable);
RaiseNetworkEvent(new RequestDecalPlacementEvent(decal, coords));
return true;
},
(session, coords, uid) =>
{
if (!_active)
return false;
_placing = false;
return true;
}, true))
.Bind(EngineKeyFunctions.EditorCancelPlace, new PointerStateInputCmdHandler(
(session, coords, uid) =>
{
if (!_active || _erasing)
return false;
_erasing = true;
RaiseNetworkEvent(new RequestDecalRemovalEvent(coords));
return true;
}, (session, coords, uid) =>
{
if (!_active)
return false;
_erasing = false;
return true;
}, true)).Register<DecalPlacementSystem>();
}
public override void Shutdown()
{
base.Shutdown();
CommandBinds.Unregister<DecalPlacementSystem>();
}
public void UpdateDecalInfo(string id, Color color, float rotation, bool snap, int zIndex, bool cleanable)
{
_decalId = id;
_decalColor = color;
_decalAngle = Angle.FromDegrees(rotation);
_snap = snap;
_zIndex = zIndex;
_cleanable = cleanable;
}
public void SetActive(bool active)
{
_active = active;
if (_active)
_inputManager.Contexts.SetActiveContext("editor");
else
_inputSystem.SetEntityContextActive();
}
}

View File

@@ -0,0 +1,98 @@
using System.Linq;
using Content.Client.CharacterAppearance;
using Content.Client.Stylesheets;
using Robust.Client.UserInterface;
using Robust.Client.UserInterface.Controls;
namespace Content.Client.Decals.UI;
/// <summary>
/// This is just copied from EyeColorPicker btw. I might make it more generic
/// </summary>
public sealed class AlphaColorPicker : Control
{
public event Action<Color>? OnColorPicked;
private readonly ColorSlider _colorSliderR;
private readonly ColorSlider _colorSliderG;
private readonly ColorSlider _colorSliderB;
private readonly ColorSlider _colorSliderA;
private PaletteColorPicker? _picker;
private Color _lastColor;
public void SetData(Color color)
{
_lastColor = color;
_colorSliderR.ColorValue = color.RByte;
_colorSliderG.ColorValue = color.GByte;
_colorSliderB.ColorValue = color.BByte;
_colorSliderA.ColorValue = color.AByte;
}
public AlphaColorPicker()
{
Button pickerOpen;
var vBox = new BoxContainer
{
Orientation = BoxContainer.LayoutOrientation.Vertical
};
AddChild(vBox);
vBox.AddChild(_colorSliderR = new ColorSlider(StyleNano.StyleClassSliderRed));
vBox.AddChild(_colorSliderG = new ColorSlider(StyleNano.StyleClassSliderGreen));
vBox.AddChild(_colorSliderB = new ColorSlider(StyleNano.StyleClassSliderBlue));
vBox.AddChild(_colorSliderA = new ColorSlider(StyleNano.StyleClassSliderWhite));
vBox.AddChild(pickerOpen = new Button
{
Text = "Palette"
});
pickerOpen.OnPressed += _ =>
{
if (_picker is null)
{
_picker = new PaletteColorPicker();
_picker.OpenToLeft();
_picker.PaletteList.OnItemSelected += args =>
{
SetData((args.ItemList.GetSelected().First().Metadata as Color?)!.Value);
ColorValueChanged();
};
}
else
{
if (_picker.IsOpen)
{
_picker.Close();
}
else
{
_picker.Open();
}
}
};
var colorValueChanged = ColorValueChanged;
_colorSliderR.OnValueChanged += colorValueChanged;
_colorSliderG.OnValueChanged += colorValueChanged;
_colorSliderB.OnValueChanged += colorValueChanged;
_colorSliderA.OnValueChanged += colorValueChanged;
}
private void ColorValueChanged()
{
var newColor = new Color(
_colorSliderR.ColorValue,
_colorSliderG.ColorValue,
_colorSliderB.ColorValue,
_colorSliderA.ColorValue
);
OnColorPicked?.Invoke(newColor);
_lastColor = newColor;
}
}

View File

@@ -0,0 +1,26 @@
<DefaultWindow xmlns="https://spacestation14.io"
xmlns:ui="clr-namespace:Content.Client.Decals.UI"
Title="{Loc 'decal-placer-window-title'}"
MinSize="250 500"
SetSize="250 500">
<BoxContainer Orientation="Vertical">
<LineEdit Name="Search" />
<ScrollContainer VerticalExpand="True">
<GridContainer Name="Grid" Columns="6">
<!-- Decals get added here by code -->
</GridContainer>
</ScrollContainer>
<ui:AlphaColorPicker Name="ColorPicker"/>
<CheckBox Name="EnableColor" Text="{Loc 'decal-placer-window-use-color'}" />
<CheckBox Name="EnableSnap" Text="{Loc 'decal-placer-window-enable-snap'}" />
<CheckBox Name="EnableCleanable" Text="{Loc 'decal-placer-window-enable-cleanable'}" />
<BoxContainer Name="SpinBoxContainer" Orientation="Horizontal">
<Label Text="{Loc 'decal-placer-window-rotation'}" Margin="0 0 0 1" />
</BoxContainer>
<BoxContainer Orientation="Horizontal">
<Label Text="{Loc 'decal-placer-window-zindex'}" Margin="0 0 0 1" />
<SpinBox Name="ZIndexSpinBox" HorizontalExpand ="True" />
</BoxContainer>
</BoxContainer>
</DefaultWindow>

View File

@@ -0,0 +1,167 @@
using Content.Client.Stylesheets;
using Content.Shared.Decals;
using Robust.Client.AutoGenerated;
using Robust.Client.Graphics;
using Robust.Client.UserInterface.Controls;
using Robust.Client.UserInterface.CustomControls;
using Robust.Client.UserInterface.XAML;
using Robust.Client.Utility;
using Robust.Shared.Prototypes;
using static Robust.Client.UserInterface.Controls.BaseButton;
namespace Content.Client.Decals.UI;
[GenerateTypedNameReferences]
public sealed partial class DecalPlacerWindow : DefaultWindow
{
private readonly IPrototypeManager _prototypeManager;
private readonly DecalPlacementSystem _decalPlacementSystem;
public FloatSpinBox RotationSpinBox;
private Dictionary<string, Texture>? _decals;
private string? _selected;
private Color _color = Color.White;
private bool _useColor;
private bool _snap;
private float _rotation;
private bool _cleanable;
private int _zIndex;
public DecalPlacerWindow(IPrototypeManager prototypeManager)
{
RobustXamlLoader.Load(this);
_prototypeManager = prototypeManager;
_decalPlacementSystem = EntitySystem.Get<DecalPlacementSystem>();
// This needs to be done in C# so we can have custom stuff passed in the constructor
// and thus have a proper step size
RotationSpinBox = new FloatSpinBox(90.0f, 0)
{
HorizontalExpand = true
};
SpinBoxContainer.AddChild(RotationSpinBox);
Search.OnTextChanged += _ => RefreshList();
ColorPicker.OnColorPicked += color =>
{
_color = color;
UpdateDecalPlacementInfo();
RefreshList();
};
RotationSpinBox.OnValueChanged += args =>
{
_rotation = args.Value;
UpdateDecalPlacementInfo();
};
EnableColor.OnToggled += args =>
{
_useColor = args.Pressed;
UpdateDecalPlacementInfo();
RefreshList();
};
EnableSnap.OnToggled += args =>
{
_snap = args.Pressed;
UpdateDecalPlacementInfo();
};
EnableCleanable.OnToggled += args =>
{
_cleanable = args.Pressed;
UpdateDecalPlacementInfo();
};
// i have to make this a member method for some reason and i have no idea why its only for spinboxes
ZIndexSpinBox.ValueChanged += ZIndexSpinboxChanged;
Populate();
}
private void UpdateDecalPlacementInfo()
{
if (_selected is null)
return;
var color = _useColor ? _color : Color.White;
_decalPlacementSystem.UpdateDecalInfo(_selected, color, _rotation, _snap, _zIndex, _cleanable);
}
private void RefreshList()
{
// Clear
Grid.RemoveAllChildren();
if (_decals == null) return;
var filter = Search.Text;
foreach (var (decal, tex) in _decals)
{
if (!decal.ToLowerInvariant().Contains(filter.ToLowerInvariant()))
continue;
var button = new TextureButton
{
TextureNormal = tex,
Name = decal,
ToolTip = decal,
Modulate = _useColor ? _color : Color.White
};
button.OnPressed += ButtonOnPressed;
if (_selected == decal)
{
var panelContainer = new PanelContainer
{
PanelOverride = new StyleBoxFlat
{
BackgroundColor = StyleNano.ButtonColorDefault
},
Children =
{
button
}
};
Grid.AddChild(panelContainer);
}
else
Grid.AddChild(button);
}
}
private void ZIndexSpinboxChanged(object? sender, ValueChangedEventArgs e)
{
_zIndex = e.Value;
UpdateDecalPlacementInfo();
}
private void ButtonOnPressed(ButtonEventArgs obj)
{
if (obj.Button.Name == null) return;
_selected = obj.Button.Name;
UpdateDecalPlacementInfo();
RefreshList();
}
public void Populate()
{
var prototypes = _prototypeManager.EnumeratePrototypes<DecalPrototype>();
_decals = new Dictionary<string, Texture>();
foreach (var decalPrototype in prototypes)
{
_decals.Add(decalPrototype.ID, decalPrototype.Sprite.Frame0());
}
RefreshList();
}
protected override void Opened()
{
base.Opened();
_decalPlacementSystem.SetActive(true);
}
public override void Close()
{
base.Close();
_decalPlacementSystem.SetActive(false);
}
}

View File

@@ -0,0 +1,12 @@
<DefaultWindow xmlns="https://spacestation14.io"
Title="{Loc 'palette-color-picker-window-title'}"
MinSize="250 500"
SetSize="250 500">
<BoxContainer Orientation="Vertical">
<ItemList Name="PaletteList" VerticalExpand="True" Access="Public"/>
<BoxContainer Orientation="Horizontal">
<Label Text="Palette"/>
<OptionButton Name="Palettes"/>
</BoxContainer>
</BoxContainer>
</DefaultWindow>

View File

@@ -0,0 +1,54 @@
using Content.Shared.Decals;
using Robust.Client.AutoGenerated;
using Robust.Client.Graphics;
using Robust.Client.ResourceManagement;
using Robust.Client.UserInterface.CustomControls;
using Robust.Client.UserInterface.XAML;
using Robust.Shared.Prototypes;
namespace Content.Client.Decals.UI;
[GenerateTypedNameReferences]
public sealed partial class PaletteColorPicker : DefaultWindow
{
[Dependency] private readonly IPrototypeManager _prototypeManager = default!;
[Dependency] private readonly IResourceCache _resourceCache = default!;
private readonly TextureResource _tex;
public PaletteColorPicker()
{
RobustXamlLoader.Load(this);
IoCManager.InjectDependencies(this);
_tex = _resourceCache.GetResource<TextureResource>("/Textures/Interface/Nano/button.svg.96dpi.png");
var i = 0;
foreach (var palette in _prototypeManager.EnumeratePrototypes<ColorPalettePrototype>())
{
Palettes.AddItem(palette.Name);
Palettes.SetItemMetadata(i, palette); // ew
i += 1;
}
Palettes.OnItemSelected += args =>
{
Palettes.SelectId(args.Id);
SetupList();
};
Palettes.Select(0);
SetupList();
}
private void SetupList()
{
PaletteList.Clear();
foreach (var (color, value) in (Palettes.SelectedMetadata as ColorPalettePrototype)!.Colors)
{
var item = PaletteList.AddItem(color, _tex.Texture);
item.Metadata = value;
item.IconModulate = value;
}
}
}

View File

@@ -145,6 +145,7 @@ namespace Content.Client.EscapeMenu.UI.Tabs
AddButton(ContentKeyFunctions.OpenEntitySpawnWindow);
AddButton(ContentKeyFunctions.OpenSandboxWindow);
AddButton(ContentKeyFunctions.OpenTileSpawnWindow);
AddButton(ContentKeyFunctions.OpenDecalSpawnWindow);
AddButton(ContentKeyFunctions.OpenAdminMenu);
AddHeader("ui-options-header-misc");

View File

@@ -97,6 +97,7 @@ namespace Content.Client.Input
common.AddFunction(ContentKeyFunctions.OpenEntitySpawnWindow);
common.AddFunction(ContentKeyFunctions.OpenSandboxWindow);
common.AddFunction(ContentKeyFunctions.OpenTileSpawnWindow);
common.AddFunction(ContentKeyFunctions.OpenDecalSpawnWindow);
common.AddFunction(ContentKeyFunctions.OpenAdminMenu);
}
}

View File

@@ -1,4 +1,5 @@
using System;
using Content.Client.Decals.UI;
using Content.Client.HUD;
using Content.Client.Markers;
using Content.Shared.Input;
@@ -29,6 +30,7 @@ namespace Content.Client.Sandbox
public readonly Button RespawnButton;
public readonly Button SpawnEntitiesButton;
public readonly Button SpawnTilesButton;
public readonly Button SpawnDecalsButton;
public readonly Button GiveFullAccessButton; //A button that just puts a captain's ID in your hands.
public readonly Button GiveAghostButton;
public readonly Button ToggleLightButton;
@@ -64,6 +66,9 @@ namespace Content.Client.Sandbox
SpawnTilesButton = new Button { Text = Loc.GetString("sandbox-window-spawn-tiles-button") };
vBox.AddChild(SpawnTilesButton);
SpawnDecalsButton = new Button { Text = Loc.GetString("sandbox-window-spawn-decals-button") };
vBox.AddChild(SpawnDecalsButton);
GiveFullAccessButton = new Button { Text = Loc.GetString("sandbox-window-grant-full-access-button") };
vBox.AddChild(GiveFullAccessButton);
@@ -128,6 +133,7 @@ namespace Content.Client.Sandbox
private SandboxWindow? _window;
private EntitySpawnWindow? _spawnWindow;
private TileSpawnWindow? _tilesSpawnWindow;
private DecalPlacerWindow? _decalSpawnWindow;
private bool _sandboxWindowToggled;
public void Initialize()
@@ -150,6 +156,8 @@ namespace Content.Client.Sandbox
InputCmdHandler.FromDelegate(session => ToggleSandboxWindow()));
_inputManager.SetInputCommand(ContentKeyFunctions.OpenTileSpawnWindow,
InputCmdHandler.FromDelegate(session => ToggleTilesWindow()));
_inputManager.SetInputCommand(ContentKeyFunctions.OpenDecalSpawnWindow,
InputCmdHandler.FromDelegate(session => ToggleDecalsWindow()));
}
private void SandboxButtonPressed(bool newValue)
@@ -205,6 +213,7 @@ namespace Content.Client.Sandbox
_window.RespawnButton.OnPressed += OnRespawnButtonOnOnPressed;
_window.SpawnTilesButton.OnPressed += OnSpawnTilesButtonClicked;
_window.SpawnEntitiesButton.OnPressed += OnSpawnEntitiesButtonClicked;
_window.SpawnDecalsButton.OnPressed += OnSpawnDecalsButtonClicked;
_window.GiveFullAccessButton.OnPressed += OnGiveAdminAccessButtonClicked;
_window.GiveAghostButton.OnPressed += OnGiveAghostButtonClicked;
_window.ToggleLightButton.OnToggled += OnToggleLightButtonClicked;
@@ -240,6 +249,11 @@ namespace Content.Client.Sandbox
ToggleTilesWindow();
}
private void OnSpawnDecalsButtonClicked(BaseButton.ButtonEventArgs obj)
{
ToggleDecalsWindow();
}
private void OnToggleLightButtonClicked(BaseButton.ButtonEventArgs args)
{
ToggleLight();
@@ -327,6 +341,25 @@ namespace Content.Client.Sandbox
}
}
private void ToggleDecalsWindow()
{
if (_decalSpawnWindow == null)
{
_decalSpawnWindow = new DecalPlacerWindow(_prototypeManager);
_decalSpawnWindow.OpenToLeft();
return;
}
if (_decalSpawnWindow.IsOpen)
{
_decalSpawnWindow.Close();
}
else
{
_decalSpawnWindow.Open();
}
}
private void ToggleLight()
{
_consoleHost.ExecuteCommand("togglelight");

View File

@@ -66,6 +66,7 @@ namespace Content.Client.Stylesheets
public const string StyleClassSliderRed = "Red";
public const string StyleClassSliderGreen = "Green";
public const string StyleClassSliderBlue = "Blue";
public const string StyleClassSliderWhite = "White";
public const string StyleClassLabelHeadingBigger = "LabelHeadingBigger";
public const string StyleClassLabelKeyText = "LabelKeyText";
@@ -436,6 +437,7 @@ namespace Content.Client.Stylesheets
var sliderFillGreen = new StyleBoxTexture(sliderFillBox) {Modulate = Color.LimeGreen};
var sliderFillRed = new StyleBoxTexture(sliderFillBox) {Modulate = Color.Red};
var sliderFillBlue = new StyleBoxTexture(sliderFillBox) {Modulate = Color.Blue};
var sliderFillWhite = new StyleBoxTexture(sliderFillBox) { Modulate = Color.White };
var boxFont13 = resCache.GetFont("/Fonts/Boxfont-round/Boxfont Round.ttf", 13);
@@ -1155,6 +1157,11 @@ namespace Content.Client.Stylesheets
new StyleProperty(Slider.StylePropertyFill, sliderFillBlue),
}),
new StyleRule(new SelectorElement(typeof(Slider), new []{StyleClassSliderWhite}, null, null), new []
{
new StyleProperty(Slider.StylePropertyFill, sliderFillWhite),
}),
// chat channel option selector
new StyleRule(new SelectorElement(typeof(Button), new[] {StyleClassChatChannelSelectorButton}, null, null), new[]
{

View File

@@ -1,21 +1,19 @@
using System;
using System.Collections.Generic;
using System.Diagnostics.CodeAnalysis;
using System.Diagnostics.CodeAnalysis;
using Content.Server.Administration.Managers;
using Content.Shared.Administration;
using Content.Shared.Decals;
using Content.Shared.Maps;
using Robust.Server.Player;
using Robust.Shared.Enums;
using Robust.Shared.GameObjects;
using Robust.Shared.IoC;
using Robust.Shared.Map;
using Robust.Shared.Maths;
using Robust.Shared.Player;
namespace Content.Server.Decals
{
public class DecalSystem : SharedDecalSystem
public sealed class DecalSystem : SharedDecalSystem
{
[Dependency] private readonly IPlayerManager _playerManager = default!;
[Dependency] private readonly IAdminManager _adminManager = default!;
private readonly Dictionary<GridId, HashSet<Vector2i>> _dirtyChunks = new();
private readonly Dictionary<IPlayerSession, Dictionary<GridId, HashSet<Vector2i>>> _previousSentChunks = new();
@@ -26,6 +24,9 @@ namespace Content.Server.Decals
_playerManager.PlayerStatusChanged += OnPlayerStatusChanged;
MapManager.TileChanged += OnTileChanged;
SubscribeNetworkEvent<RequestDecalPlacementEvent>(OnDecalPlacementRequest);
SubscribeNetworkEvent<RequestDecalRemovalEvent>(OnDecalRemovalRequest);
}
public override void Shutdown()
@@ -79,6 +80,42 @@ namespace Content.Server.Decals
}
}
private void OnDecalPlacementRequest(RequestDecalPlacementEvent ev, EntitySessionEventArgs eventArgs)
{
if (eventArgs.SenderSession is not IPlayerSession session)
return;
// bad
if (!_adminManager.HasAdminFlag(session, AdminFlags.Mapping))
return;
if (!ev.Coordinates.IsValid(EntityManager))
return;
TryAddDecal(ev.Decal, ev.Coordinates, out _);
}
private void OnDecalRemovalRequest(RequestDecalRemovalEvent ev, EntitySessionEventArgs eventArgs)
{
if (eventArgs.SenderSession is not IPlayerSession session)
return;
// bad
if (!_adminManager.HasAdminFlag(session, AdminFlags.Mapping))
return;
if (!ev.Coordinates.IsValid(EntityManager))
return;
var gridId = ev.Coordinates.GetGridId(EntityManager);
// remove all decals on the same tile
foreach (var decal in GetDecalsInRange(gridId, ev.Coordinates.Position))
{
RemoveDecal(gridId, decal);
}
}
protected override void DirtyChunk(GridId id, Vector2i chunkIndices)
{
if(!_dirtyChunks.ContainsKey(id))
@@ -89,15 +126,24 @@ namespace Content.Server.Decals
public bool TryAddDecal(string id, EntityCoordinates coordinates, [NotNullWhen(true)] out uint? uid, Color? color = null, Angle? rotation = null, int zIndex = 0, bool cleanable = false)
{
uid = 0;
if (!PrototypeManager.HasIndex<DecalPrototype>(id))
rotation ??= Angle.Zero;
var decal = new Decal(coordinates.Position, id, color, rotation.Value, zIndex, cleanable);
return TryAddDecal(decal, coordinates, out uid);
}
public bool TryAddDecal(Decal decal, EntityCoordinates coordinates, [NotNull] out uint? uid)
{
uid = 0;
if (!PrototypeManager.HasIndex<DecalPrototype>(decal.Id))
return false;
var gridId = coordinates.GetGridId(EntityManager);
if (MapManager.GetGrid(gridId).GetTileRef(coordinates).IsSpace())
return false;
rotation ??= Angle.Zero;
var decal = new Decal(coordinates.Position, id, color, rotation.Value, zIndex, cleanable);
var chunkCollection = DecalGridChunkCollection(gridId);
uid = chunkCollection.NextUid++;
var chunkIndices = GetChunkIndices(decal.Coordinates);
@@ -106,6 +152,7 @@ namespace Content.Server.Decals
chunkCollection.ChunkCollection[chunkIndices][uid.Value] = decal;
ChunkIndex[gridId][uid.Value] = chunkIndices;
DirtyChunk(gridId, chunkIndices);
return true;
}

View File

@@ -0,0 +1,11 @@
using Robust.Shared.Prototypes;
namespace Content.Shared.Decals;
[Prototype("palette")]
public sealed class ColorPalettePrototype : IPrototype
{
[DataField("id")] public string ID { get; } = null!;
[DataField("name")] public string Name { get; } = null!;
[DataField("colors")] public Dictionary<string, Color> Colors { get; } = null!;
}

View File

@@ -8,6 +8,7 @@ using Robust.Shared.IoC;
using Robust.Shared.Map;
using Robust.Shared.Maths;
using Robust.Shared.Prototypes;
using Robust.Shared.Serialization;
namespace Content.Shared.Decals
{
@@ -150,4 +151,31 @@ namespace Content.Shared.Decals
return _xIndex <= _chunkRT.X;
}
}
/// <summary>
/// Sent by clients to request that a decal is placed on the server.
/// </summary>
[Serializable, NetSerializable]
public sealed class RequestDecalPlacementEvent : EntityEventArgs
{
public Decal Decal;
public EntityCoordinates Coordinates;
public RequestDecalPlacementEvent(Decal decal, EntityCoordinates coordinates)
{
Decal = decal;
Coordinates = coordinates;
}
}
[Serializable, NetSerializable]
public sealed class RequestDecalRemovalEvent : EntityEventArgs
{
public EntityCoordinates Coordinates;
public RequestDecalRemovalEvent(EntityCoordinates coordinates)
{
Coordinates = coordinates;
}
}
}

View File

@@ -38,6 +38,7 @@ namespace Content.Shared.Input
public static readonly BoundKeyFunction OpenEntitySpawnWindow = "OpenEntitySpawnWindow";
public static readonly BoundKeyFunction OpenSandboxWindow = "OpenSandboxWindow";
public static readonly BoundKeyFunction OpenTileSpawnWindow = "OpenTileSpawnWindow";
public static readonly BoundKeyFunction OpenDecalSpawnWindow = "OpenDecalSpawnWindow";
public static readonly BoundKeyFunction OpenAdminMenu = "OpenAdminMenu";
public static readonly BoundKeyFunction TakeScreenshot = "TakeScreenshot";
public static readonly BoundKeyFunction TakeScreenshotNoUI = "TakeScreenshotNoUI";

View File

@@ -0,0 +1,7 @@
decal-placer-window-title = Decal Placer
decal-placer-window-use-color = Custom Color
decal-placer-window-rotation = Rotation
decal-placer-window-zindex = Draw Depth
decal-placer-window-enable-snap = Snap To Tile
decal-placer-window-enable-cleanable = Cleanable
palette-color-picker-window-title = Palettes

View File

@@ -112,6 +112,7 @@ ui-options-function-open-abilities-menu = Open action menu
ui-options-function-open-entity-spawn-window = Open entity spawn menu
ui-options-function-open-sandbox-window = Open sandbox menu
ui-options-function-open-tile-spawn-window = Open tile spawn menu
ui-options-function-open-decal-spawn-window = Open decal spawn menu
ui-options-function-open-admin-menu = Open admin menu
ui-options-function-take-screenshot = Take screenshot

View File

@@ -2,6 +2,7 @@ sandbox-window-title = Sandbox Panel
sandbox-window-respawn-button = Respawn
sandbox-window-spawn-entities-button = Spawn Entities
sandbox-window-spawn-tiles-button = Spawn Tiles
sandbox-window-spawn-decals-button = Spawn Decals
sandbox-window-grant-full-access-button = Grant Full Access
sandbox-window-ghost-button = Ghost
sandbox-window-toggle-lights-button = Toggle Lights
@@ -11,4 +12,4 @@ sandbox-window-toggle-subfloor-button = Toggle Subfloor
sandbox-window-toggle-suicide-button = Suicide
sandbox-window-show-spawns-button = Shows Spawns
sandbox-window-show-bb-button = Show BB
sandbox-window-link-machines-button = Link machines
sandbox-window-link-machines-button = Link machines

View File

@@ -0,0 +1,76 @@
- type: decal
id: FullTileOverlayGreyscale
tags: ["station", "overlay"]
sprite:
sprite: Decals/Overlays/greyscale.rsi
state: fulltile_overlay
- type: decal
id: HalfTileOverlayGreyscale
tags: ["station", "overlay"]
sprite:
sprite: Decals/Overlays/greyscale.rsi
state: halftile_overlay
- type: decal
id: HalfTileOverlayGreyscale90
tags: ["station", "overlay"]
sprite:
sprite: Decals/Overlays/greyscale.rsi
state: halftile_overlay_90
- type: decal
id: HalfTileOverlayGreyscale180
tags: ["station", "overlay"]
sprite:
sprite: Decals/Overlays/greyscale.rsi
state: halftile_overlay_180
- type: decal
id: HalfTileOverlayGreyscale270
tags: ["station", "overlay"]
sprite:
sprite: Decals/Overlays/greyscale.rsi
state: halftile_overlay_270
- type: decal
id: QuarterTileOverlayGreyscale
tags: ["station", "overlay"]
sprite:
sprite: Decals/Overlays/greyscale.rsi
state: quartertile_overlay
- type: decal
id: QuarterTileOverlayGreyscale90
tags: ["station", "overlay"]
sprite:
sprite: Decals/Overlays/greyscale.rsi
state: quartertile_overlay_90
- type: decal
id: QuarterTileOverlayGreyscale180
tags: ["station", "overlay"]
sprite:
sprite: Decals/Overlays/greyscale.rsi
state: quartertile_overlay_180
- type: decal
id: QuarterTileOverlayGreyscale270
tags: ["station", "overlay"]
sprite:
sprite: Decals/Overlays/greyscale.rsi
state: quartertile_overlay_270
- type: decal
id: CheckerNESW
tags: ["station", "overlay"]
sprite:
sprite: Decals/Overlays/greyscale.rsi
state: checkerNESW
- type: decal
id: CheckerNWSE
tags: ["station", "overlay"]
sprite:
sprite: Decals/Overlays/greyscale.rsi
state: checkerNWSE

View File

@@ -0,0 +1,52 @@
# If you spawn any of these, they should probably be made cleanable as well.
- type: decal
id: Dirt
tags: ["station", "dirty"]
sprite:
sprite: Decals/dirty.rsi
state: dirt
- type: decal
id: DirtLight
tags: ["station", "dirty"]
sprite:
sprite: Decals/dirty.rsi
state: dirtlight
- type: decal
id: DirtMedium
tags: ["station", "dirty"]
sprite:
sprite: Decals/dirty.rsi
state: dirtmedium
- type: decal
id: DirtHeavy
tags: ["station", "dirty"]
sprite:
sprite: Decals/dirty.rsi
state: dirtheavy
- type: decal
id: Damaged
tags: ["station", "dirty"]
sprite:
sprite: Decals/dirty.rsi
state: damaged
- type: decal
id: Remains
tags: ["station", "dirty"]
sprite:
sprite: Decals/dirty.rsi
state: remains
- type: decal
id: Rust
tags: ["station", "dirty"]
sprite:
sprite: Decals/dirty.rsi
state: rust

View File

@@ -0,0 +1,237 @@
- type: decal
id: Arrows
tags: ["station", "markings"]
sprite:
sprite: Decals/markings.rsi
state: arrows
- type: decal
id: ArrowsGreyscale
tags: ["station", "markings"]
sprite:
sprite: Decals/markings.rsi
state: arrows_greyscale
- type: decal
id: Bot
tags: ["station", "markings"]
sprite:
sprite: Decals/markings.rsi
state: bot
- type: decal
id: BotGreyscale
tags: ["station", "markings"]
sprite:
sprite: Decals/markings.rsi
state: bot_greyscale
- type: decal
id: BotLeft
tags: ["station", "markings"]
sprite:
sprite: Decals/markings.rsi
state: bot_left
- type: decal
id: BotLeftGreyscale
tags: ["station", "markings"]
sprite:
sprite: Decals/markings.rsi
state: bot_left_greyscale
- type: decal
id: BotRight
tags: ["station", "markings"]
sprite:
sprite: Decals/markings.rsi
state: bot_right
- type: decal
id: BotRightGreyscale
tags: ["station", "markings"]
sprite:
sprite: Decals/markings.rsi
state: bot_right_greyscale
- type: decal
id: Box
tags: ["station", "markings"]
sprite:
sprite: Decals/markings.rsi
state: box
- type: decal
id: BoxGreyscale
tags: ["station", "markings"]
sprite:
sprite: Decals/markings.rsi
state: box_greyscale
- type: decal
id: Caution
tags: ["station", "markings"]
sprite:
sprite: Decals/markings.rsi
state: caution
- type: decal
id: CautionGreyscale
tags: ["station", "markings"]
sprite:
sprite: Decals/markings.rsi
state: caution_greyscale
- type: decal
id: Delivery
tags: ["station", "markings"]
sprite:
sprite: Decals/markings.rsi
state: delivery
- type: decal
id: DeliveryGreyscale
tags: ["station", "markings"]
sprite:
sprite: Decals/markings.rsi
state: delivery_greyscale
- type: decal
id: LoadingArea
tags: ["station", "markings"]
sprite:
sprite: Decals/markings.rsi
state: loading_area
- type: decal
id: LoadingAreaGreyscale
tags: ["station", "markings"]
sprite:
sprite: Decals/markings.rsi
state: loading_area_greyscale
- type: decal
id: StandClear
tags: ["station", "markings"]
sprite:
sprite: Decals/markings.rsi
state: standclear
- type: decal
id: StandClearGreyscale
tags: ["station", "markings"]
sprite:
sprite: Decals/markings.rsi
state: standclear_greyscale
- type: decal
id: WarnBox
tags: ["station", "markings"]
sprite:
sprite: Decals/markings.rsi
state: warn_box
- type: decal
id: WarnBoxGreyscale
tags: ["station", "markings"]
sprite:
sprite: Decals/markings.rsi
state: warn_box_greyscale
- type: decal
id: WarnEnd
tags: ["station", "markings"]
sprite:
sprite: Decals/markings.rsi
state: warn_end
- type: decal
id: WarnEndGreyscale
tags: ["station", "markings"]
sprite:
sprite: Decals/markings.rsi
state: warn_end_greyscale
- type: decal
id: WarnFull
tags: ["station", "markings"]
sprite:
sprite: Decals/markings.rsi
state: warn_full
- type: decal
id: WarnFullGreyscale
tags: ["station", "markings"]
sprite:
sprite: Decals/markings.rsi
state: warn_full_greyscale
- type: decal
id: WarningLine
tags: ["station", "markings"]
sprite:
sprite: Decals/markings.rsi
state: warningline
- type: decal
id: WarningLineGreyscale
tags: ["station", "markings"]
sprite:
sprite: Decals/markings.rsi
state: warningline_greyscale
- type: decal
id: WarningLineCorner
tags: ["station", "markings"]
sprite:
sprite: Decals/markings.rsi
state: warninglinecorner
- type: decal
id: WarningLineCornerGreyscale
tags: ["station", "markings"]
sprite:
sprite: Decals/markings.rsi
state: warninglinecorner_greyscale
- type: decal
id: WarningLineCornerFlipped
tags: ["station", "markings"]
sprite:
sprite: Decals/markings.rsi
state: warninglinecorner_flipped
- type: decal
id: WarningLineCornerFlippedGreyscale
tags: ["station", "markings"]
sprite:
sprite: Decals/markings.rsi
state: warninglinecorner_flipped_greyscale
- type: decal
id: WarnCorner
tags: ["station", "markings"]
sprite:
sprite: Decals/markings.rsi
state: warn_corner
- type: decal
id: WarnCornerGreyscale
tags: ["station", "markings"]
sprite:
sprite: Decals/markings.rsi
state: warn_corner_greyscale
- type: decal
id: WarnCornerFlipped
tags: ["station", "markings"]
sprite:
sprite: Decals/markings.rsi
state: warn_corner_flipped
- type: decal
id: WarnCornerFlippedGreyscale
tags: ["station", "markings"]
sprite:
sprite: Decals/markings.rsi
state: warn_corner_flipped_greyscale

View File

@@ -0,0 +1,13 @@
- type: palette
id: Departmental
name: Departmental
colors:
command: "#52B4E996"
service: "#9FED5896"
engineering: "#EFB34196"
security: "#DE3A3A96"
bar: "#79150096"
science: "#D381C996"
cargo: "#A4610696"
neutral: "#D4D4D496"
neutralLight: "#D4D4D428"

View File

@@ -0,0 +1,20 @@
- type: palette
id: Sixteen
name: Sixteen
colors:
black: "#1D1D21"
red: "#B02E26"
green: "#5E7C16"
brown: "#835432"
blue: "#3C44AA"
purple: "#8932B8"
cyan: "#169C9C"
"light gray": "#9D9D97"
gray: "#474F52"
pink: "#F38BAA"
lime: "#80C71F"
yellow: "#FED83D"
"light blue": "#3AB3DA"
magenta: "#C74EBD"
orange: "#F9801D"
white: "#F9FFFE"

Binary file not shown.

After

Width:  |  Height:  |  Size: 185 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 185 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 191 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 167 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 168 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 184 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 185 B

View File

@@ -0,0 +1,44 @@
{
"version": 1,
"license": "CC-BY-NC-SA-3.0",
"copyright": "mirrorcult",
"size": {
"x": 32,
"y": 32
},
"states": [
{
"name": "fulltile_overlay"
},
{
"name": "halftile_overlay"
},
{
"name": "halftile_overlay_90"
},
{
"name": "halftile_overlay_180"
},
{
"name": "halftile_overlay_270"
},
{
"name": "quartertile_overlay"
},
{
"name": "quartertile_overlay_90"
},
{
"name": "quartertile_overlay_180"
},
{
"name": "quartertile_overlay_270"
},
{
"name": "checkerNESW"
},
{
"name": "checkerNWSE"
}
]
}

Binary file not shown.

After

Width:  |  Height:  |  Size: 163 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 167 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 166 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 163 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.4 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.1 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.2 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.2 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.3 KiB

View File

@@ -0,0 +1,32 @@
{
"version": 1,
"license": "CC-BY-SA-3.0",
"copyright": "tgstation at 606005645d3a14c4439e5ce14785650121b22678",
"size": {
"x": 32,
"y": 32
},
"states": [
{
"name": "dirt"
},
{
"name": "dirtlight",
},
{
"name": "dirtmedium",
},
{
"name": "dirtheavy",
},
{
"name": "damaged",
},
{
"name": "remains"
},
{
"name": "rust"
}
]
}

Binary file not shown.

After

Width:  |  Height:  |  Size: 663 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 3.1 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 213 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 196 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 200 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 243 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 310 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 264 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 310 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 261 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 339 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 265 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 246 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 213 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 215 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 270 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 255 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 233 B

View File

@@ -0,0 +1,113 @@
{
"version": 1,
"license": "CC-BY-SA-3.0",
"copyright": "tgstation at 606005645d3a14c4439e5ce14785650121b22678",
"size": {
"x": 32,
"y": 32
},
"states": [
{
"name": "arrows"
},
{
"name": "arrows_greyscale"
},
{
"name": "bot",
},
{
"name": "bot_greyscale",
},
{
"name": "bot_left",
},
{
"name": "bot_left_greyscale",
},
{
"name": "bot_right"
},
{
"name": "bot_right_greyscale"
},
{
"name": "box"
},
{
"name": "box_greyscale"
},
{
"name": "caution"
},
{
"name": "caution_greyscale",
},
{
"name": "delivery",
},
{
"name": "delivery_greyscale",
},
{
"name": "loading_area",
},
{
"name": "loading_area_greyscale"
},
{
"name": "standclear"
},
{
"name": "standclear_greyscale"
},
{
"name": "warn_box"
},
{
"name": "warn_box_greyscale"
},
{
"name": "warn_end"
},
{
"name": "warn_end_greyscale",
},
{
"name": "warn_full",
},
{
"name": "warn_full_greyscale",
},
{
"name": "warningline",
},
{
"name": "warningline_greyscale"
},
{
"name": "warninglinecorner"
},
{
"name": "warninglinecorner_greyscale"
},
{
"name": "warninglinecorner_flipped"
},
{
"name": "warninglinecorner_flipped_greyscale"
},
{
"name": "warn_corner"
},
{
"name": "warn_corner_greyscale"
},
{
"name": "warn_corner_flipped"
},
{
"name": "warn_corner_flipped_greyscale"
}
]
}

Binary file not shown.

After

Width:  |  Height:  |  Size: 341 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 254 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 343 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 286 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 259 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 260 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 230 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 232 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 269 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 247 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 248 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 255 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 217 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 202 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 216 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 220 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 196 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 191 B

View File

@@ -314,6 +314,9 @@ binds:
- function: OpenAdminMenu
type: State
key: F7
- function: OpenDecalSpawnWindow
type: State
key: F8
- function: OpenSandboxWindow
type: State
key: B