diff --git a/Content.Client/_DEN/Recolor/RecolorVisualizerSystem.cs b/Content.Client/_DEN/Recolor/RecolorVisualizerSystem.cs new file mode 100644 index 0000000000..99d1e01b13 --- /dev/null +++ b/Content.Client/_DEN/Recolor/RecolorVisualizerSystem.cs @@ -0,0 +1,148 @@ +using System.Linq; +using Content.Client.Clothing; +using Content.Client.Items.Systems; +using Content.Shared._DEN.Recolor; +using Content.Shared._DEN.Recolor.Components; +using Content.Shared.Clothing; +using Content.Shared.Hands; +using Robust.Client.GameObjects; + +namespace Content.Client._DEN.Recolor; + +public sealed class RecolorVisualizerSystem : VisualizerSystem +{ + [Dependency] private readonly ItemSystem _item = default!; + + public override void Initialize() + { + base.Initialize(); + + SubscribeLocalEvent(OnComponentShutdown); + + SubscribeLocalEvent(ApplyRecolorInHands, + after: [typeof(ItemSystem)]); + + SubscribeLocalEvent(ApplyRecolorEquipment, + after: [typeof(ClientClothingSystem)]); + } + + + protected override void OnAppearanceChange(EntityUid uid, RecoloredComponent component, ref AppearanceChangeEvent args) + { + base.OnAppearanceChange(uid, component, ref args); + + if (args.Sprite == null) + return; + + ApplyRecolorSprite((uid, component), args.Sprite); + _item.VisualsChanged(uid); + } + + private void OnComponentShutdown(Entity ent, ref ComponentShutdown args) + { + if (TerminatingOrDeleted(ent.Owner)) + return; + + if (!TryComp(ent, out SpriteComponent? sprite)) + return; + + RemoveRecolor(ent,sprite); + _item.VisualsChanged(ent); + } + + private void ApplyRecolorInHands(Entity ent, ref GetInhandVisualsEvent args) + { + ApplyRecolorLayers(ent,args.Layers); + } + + private void ApplyRecolorEquipment(Entity ent, ref GetEquipmentVisualsEvent args) + { + ApplyRecolorLayers(ent,args.Layers); + } + + private void ApplyRecolorLayers(Entity ent, List<(string, PrototypeLayerData)> layers) + { + if(!TryComp(ent, out var appearance)) + return; + + if (!AppearanceSystem.TryGetData(ent, RecolorVisuals.RecolorData, out RecolorData recolorData,appearance)) + return; + + foreach (var (_, layerData) in layers) + { + // Apply Color + layerData.Color = recolorData.Color; + + //Test shader whitelists and blacklists + if (!AllowedShader(layerData.Shader, recolorData)) + continue; + + // Apply shaders + layerData.Shader = recolorData.Shader; + } + } + + private void ApplyRecolorSprite(Entity ent, SpriteComponent sprite) + { + if(!TryComp(ent, out var appearance)) + return; + + if (!AppearanceSystem.TryGetData(ent, RecolorVisuals.RecolorData, out RecolorData recolorData, appearance)) + return; + + for (var i = 0; i < sprite.AllLayers.Count(); i++) + { + if (!SpriteSystem.TryGetLayer((ent, sprite), i, out var layer, false)) + continue; + + // Apply color + SpriteSystem.LayerSetColor(layer, recolorData.Color); + + var layerShader = layer.ShaderPrototype; + + if (!AllowedShader(layerShader?.Id, recolorData)) + continue; + + // Apply shaders + if (recolorData.Shader != null) + sprite.LayerSetShader(i, recolorData.Shader); + } + } + + private void RemoveRecolor(Entity ent, SpriteComponent sprite) + { + if(!TryComp(ent, out var appearance)) + return; + + if (!AppearanceSystem.TryGetData(ent, RecolorVisuals.RecolorData, out RecolorData recolorData, appearance)) + return; + + for (var i = 0; i < sprite.AllLayers.Count(); i++) + { + // TODO: Make it possible to get the previous color and shaders, currently impossible due to sprite system being fully clientside + + if (!SpriteSystem.TryGetLayer((ent, sprite), i, out var layer, false)) + continue; + + // Remove colors + SpriteSystem.LayerSetColor(layer, Color.White); + + // Remove shaders + var layerShader = layer.ShaderPrototype; + + if (!AllowedShader(layerShader?.Id, recolorData)) + continue; + + sprite.LayerSetShader(i, null, null); + } + } + + private static bool AllowedShader(string? shader, RecolorData appearanceData) + { + if (shader == null) + return true; + + return (appearanceData.ShaderBlacklist == null || !appearanceData.ShaderBlacklist.Contains(shader)) + && (appearanceData.ShaderWhitelist == null || appearanceData.ShaderWhitelist.Contains(shader)); + } +} diff --git a/Content.Client/_DEN/Recolor/UI/RecolorApplierColorSelectorBui.cs b/Content.Client/_DEN/Recolor/UI/RecolorApplierColorSelectorBui.cs new file mode 100644 index 0000000000..9814dfc3c9 --- /dev/null +++ b/Content.Client/_DEN/Recolor/UI/RecolorApplierColorSelectorBui.cs @@ -0,0 +1,33 @@ +using Content.Shared._DEN.Recolor; +using Robust.Client.UserInterface; + +namespace Content.Client._DEN.Recolor.UI; + +public sealed class RecolorApplierColorSelectorBui(EntityUid owner, Enum uiKey) : BoundUserInterface(owner, uiKey) +{ + [ViewVariables] + private RecolorApplierColorSelectorMenu? _menu; + + protected override void Open() + { + base.Open(); + + _menu = this.CreateWindow(); + _menu.OnColorChanged += SelectColor; + } + + protected override void UpdateState(BoundUserInterfaceState state) + { + base.UpdateState(state); + // Set the color of the color selector to the same color as the spray paint currently is + if (state is RecolorSystem.RecolorApplierColorState recolorApplierColorState) + { + _menu?.SelectColor(recolorApplierColorState.Color); + } + } + // Sent out when a new color is chosen + private void SelectColor(Color color) + { + SendMessage(new RecolorSystem.RecolorApplierColorMessage(color)); + } +} diff --git a/Content.Client/_DEN/Recolor/UI/RecolorApplierColorSelectorMenu.xaml b/Content.Client/_DEN/Recolor/UI/RecolorApplierColorSelectorMenu.xaml new file mode 100644 index 0000000000..f6ebeb438f --- /dev/null +++ b/Content.Client/_DEN/Recolor/UI/RecolorApplierColorSelectorMenu.xaml @@ -0,0 +1,9 @@ + + + + + diff --git a/Content.Client/_DEN/Recolor/UI/RecolorApplierColorSelectorMenu.xaml.cs b/Content.Client/_DEN/Recolor/UI/RecolorApplierColorSelectorMenu.xaml.cs new file mode 100644 index 0000000000..c9048ae970 --- /dev/null +++ b/Content.Client/_DEN/Recolor/UI/RecolorApplierColorSelectorMenu.xaml.cs @@ -0,0 +1,29 @@ +using Content.Shared._DEN.Recolor; +using Robust.Client.AutoGenerated; +using Robust.Client.UserInterface.Controls; +using Robust.Client.UserInterface.CustomControls; +using Robust.Client.UserInterface.XAML; + +namespace Content.Client._DEN.Recolor.UI; + +[GenerateTypedNameReferences] +public sealed partial class RecolorApplierColorSelectorMenu: DefaultWindow +{ + public Action? OnColorChanged; + + public RecolorApplierColorSelectorMenu() + { + RobustXamlLoader.Load(this); + + ColorSelector.OnColorChanged += color => + { + OnColorChanged?.Invoke(color); + }; + } + + // Set the color selector's color. + public void SelectColor(Color color) + { + ColorSelector.Color = color; + } +} diff --git a/Content.Shared/_DEN/Recolor/Components/RecolorApplierColorSelectorComponent.cs b/Content.Shared/_DEN/Recolor/Components/RecolorApplierColorSelectorComponent.cs new file mode 100644 index 0000000000..431e905d80 --- /dev/null +++ b/Content.Shared/_DEN/Recolor/Components/RecolorApplierColorSelectorComponent.cs @@ -0,0 +1,4 @@ +namespace Content.Shared._DEN.Recolor.Components; + +[RegisterComponent] +public sealed partial class RecolorApplierColorSelectorComponent : Component; diff --git a/Content.Shared/_DEN/Recolor/Components/RecolorApplierComponent.cs b/Content.Shared/_DEN/Recolor/Components/RecolorApplierComponent.cs new file mode 100644 index 0000000000..9b4a96e0a4 --- /dev/null +++ b/Content.Shared/_DEN/Recolor/Components/RecolorApplierComponent.cs @@ -0,0 +1,89 @@ +using Content.Shared.Whitelist; +using Robust.Shared.Audio; +using Robust.Shared.GameStates; +using Robust.Shared.Utility; + +namespace Content.Shared._DEN.Recolor.Components; + +[RegisterComponent, NetworkedComponent, AutoGenerateComponentState] +public sealed partial class RecolorApplierComponent : Component +{ + + /// + /// RecolorData to recolor items with. + /// + [DataField, AutoNetworkedField] + public RecolorData RecolorData; + + /// + /// How long it takes for this object to apply the recolor to the target. + /// + [DataField] + public TimeSpan DoAfterDuration = TimeSpan.FromSeconds(2.0f); + + /// + /// Sound to play when the doafter is over. + /// + [DataField] + public SoundSpecifier? DoafterSound = new SoundPathSpecifier("/Audio/Effects/spray2.ogg"); + + /// + /// Maximum amount of uses the applier can spray, if left null the applier can apply infinitely. + /// + [DataField] + public int? MaxUses; + + /// + /// Current amount of uses the applier can spray. + /// + [ViewVariables(VVAccess.ReadWrite)] + public int UsesLeft; + + /// + /// LocId used for the "you're outta paint!" popup. + /// + [DataField] + public LocId NoMoreUsesPopup = "spray-paint-empty"; + + /// + /// LocId used for the "you can't paint that!" popup. + /// + [DataField] + public LocId CantRecolorPopup = "spray-paint-fail"; + + /// + /// LocId used for the "you can't paint that!" popup. + /// + [DataField] + public LocId ColorShowcaseExamine = "spray-paint-examine-color"; + + /// + /// LocId used for the "you can't paint that!" popup. + /// + [DataField] + public LocId UsesExamine = "spray-paint-examine-uses"; + + /// + /// Entity Whitelist to determine what items can be repainted. + /// + [DataField] + public EntityWhitelist? EntityWhitelist; + + /// + /// Entity Blacklist to determine what items can't be repainted. + /// + [DataField] + public EntityWhitelist? EntityBlacklist; + + /// + /// LocId used for the apply recolor verb. + /// + [DataField] + public LocId VerbText = "verb-spray-paint"; + + /// + /// Icon used for the apply recolor verb. + /// + [DataField] + public SpriteSpecifier VerbIcon = new SpriteSpecifier.Texture(new ResPath("/Textures/_DEN/Interface/VerbIcons/paint-spray-can.svg.192dpi.png")); +} diff --git a/Content.Shared/_DEN/Recolor/Components/RecolorRemoverComponent.cs b/Content.Shared/_DEN/Recolor/Components/RecolorRemoverComponent.cs new file mode 100644 index 0000000000..061aa2b548 --- /dev/null +++ b/Content.Shared/_DEN/Recolor/Components/RecolorRemoverComponent.cs @@ -0,0 +1,21 @@ +using Robust.Shared.Audio; +using Robust.Shared.GameStates; + +namespace Content.Shared._DEN.Recolor.Components; + +[RegisterComponent, NetworkedComponent] +public sealed partial class RecolorRemoverComponent : Component +{ + /// + /// How long it takes for this object to remove the recolor on the target. + /// + [DataField] + public TimeSpan DoAfterDuration = TimeSpan.FromSeconds(2.0f); + + /// + /// Sound to play when the doafter is over. + /// + [DataField] + public SoundSpecifier? DoafterSound; + +} diff --git a/Content.Shared/_DEN/Recolor/Components/RecoloredComponent.cs b/Content.Shared/_DEN/Recolor/Components/RecoloredComponent.cs new file mode 100644 index 0000000000..277ac10967 --- /dev/null +++ b/Content.Shared/_DEN/Recolor/Components/RecoloredComponent.cs @@ -0,0 +1,19 @@ +using Robust.Shared.GameStates; + +namespace Content.Shared._DEN.Recolor.Components; + +[RegisterComponent, NetworkedComponent, AutoGenerateComponentState] +public sealed partial class RecoloredComponent : Component +{ + /// + /// RecolorData this component is storing. + /// + [DataField, AutoNetworkedField] + public RecolorData RecolorData; + + /// + /// Examine text as a locid. + /// + [DataField, AutoNetworkedField] + public LocId ExamineText = "recolored-examine"; +} diff --git a/Content.Shared/_DEN/Recolor/RecolorSystem.Applier.ColorSelector.cs b/Content.Shared/_DEN/Recolor/RecolorSystem.Applier.ColorSelector.cs new file mode 100644 index 0000000000..fabb3df3b1 --- /dev/null +++ b/Content.Shared/_DEN/Recolor/RecolorSystem.Applier.ColorSelector.cs @@ -0,0 +1,43 @@ +using Content.Shared._DEN.Recolor.Components; +using Robust.Shared.Serialization; + +namespace Content.Shared._DEN.Recolor; + +public sealed partial class RecolorSystem +{ + private void OnBoundUIOpened(Entity ent, ref BoundUIOpenedEvent args) + { + if (args.UiKey is not RecolorApplierColorSelectorKey key + || !TryComp(ent.Owner, out var recolorApplier)) + return; + + var state = new RecolorApplierColorState(recolorApplier.RecolorData.Color); + _ui.SetUiState(ent.Owner, key, state); + } + + private void OnRecolorApplierColorChanged(Entity ent, ref RecolorApplierColorMessage args) + { + if (!TryComp(ent, out var recolorApplier)) + return; + + ChangeColor((ent.Owner,recolorApplier),args.Color); + } + + [Serializable, NetSerializable] + public enum RecolorApplierColorSelectorKey : byte + { + Key, + } + + [Serializable, NetSerializable] + public sealed class RecolorApplierColorMessage(Color color) : BoundUserInterfaceMessage + { + public readonly Color Color = color; + } + + [Serializable, NetSerializable] + public sealed class RecolorApplierColorState(Color color) : BoundUserInterfaceState + { + public readonly Color Color = color; + } +} diff --git a/Content.Shared/_DEN/Recolor/RecolorSystem.Applier.cs b/Content.Shared/_DEN/Recolor/RecolorSystem.Applier.cs new file mode 100644 index 0000000000..32a46840d4 --- /dev/null +++ b/Content.Shared/_DEN/Recolor/RecolorSystem.Applier.cs @@ -0,0 +1,160 @@ +using Content.Shared._DEN.Recolor.Components; +using Content.Shared.DoAfter; +using Content.Shared.Examine; +using Content.Shared.Interaction; +using Content.Shared.Verbs; +using JetBrains.Annotations; + +namespace Content.Shared._DEN.Recolor; + +public sealed partial class RecolorSystem +{ + /// + /// Change the color any given recolor applier applies. + /// + /// Recolor applier to change the color of. + /// Color to change to. + /// Name of the color you're changing to, purely for flavor. + [PublicAPI] + public void ChangeColor(Entity ent, Color color, string? colorName = null) + { + var recolorData = ent.Comp.RecolorData; + + if (color == recolorData.Color) + return; + + recolorData.Color = color; + recolorData.ColorName = colorName ?? null; + + Dirty(ent); + } + + private static void OnComponentStartup(Entity ent, ref ComponentStartup args) + { + if (ent.Comp.MaxUses != null) + ent.Comp.UsesLeft = ent.Comp.MaxUses.Value; + } + + private void OnRecolorApplierAfterInteract(Entity ent, ref AfterInteractEvent args) + { + if (!args.CanReach + || args.Target == null + || args.Handled + || !CanRecolor(ent, args.User, args.Target.Value)) + { + return; + } + + args.Handled = TryStartApplyRecolorDoAfter(args.User, args.Target.Value, ent); + } + + private bool TryStartApplyRecolorDoAfter( + EntityUid user, + EntityUid target, + Entity applier) + { + var doAfterEvent = new ApplyRecolorDoAfterEvent + { + RecolorData = applier.Comp.RecolorData, + }; + + var doAfterArgs = new DoAfterArgs(EntityManager, + user: user, + seconds: (float)applier.Comp.DoAfterDuration.TotalSeconds, + @event: doAfterEvent, + eventTarget: applier, + target: target, + used: applier) + { + BreakOnDropItem = true, + BreakOnMove = true, + BreakOnHandChange = true, + }; + + return _doAfter.TryStartDoAfter(doAfterArgs); + } + + private void OnApplyRecolorDoAfterEvent(Entity ent, ref ApplyRecolorDoAfterEvent args) + { + if (args.Target is null || args.Handled || args.Cancelled) + return; + + Recolor( + uid: args.Target.Value, + recolorData: args.RecolorData + ); + + _audio.PlayPredicted(ent.Comp.DoafterSound, ent, args.User); + + ent.Comp.UsesLeft -= 1; + + Dirty(ent); + + args.Handled = true; + } + + private bool CanRecolor(Entity applier, EntityUid user, EntityUid target, bool? verb = false) + { + // Check if the applier is opened + + // All this code is to make sure not to send a popup if this is done with a verb. sigh + EntityUid? closedUser = user; + + if (verb != null && verb.Value) + closedUser = null; + + if (_openable.IsClosed(applier, closedUser, predicted: true)) + return false; + + // Check whitelist and blacklist + if (!_whitelist.CheckBoth(target, applier.Comp.EntityBlacklist, applier.Comp.EntityWhitelist)) + { + _popup.PopupClient(Loc.GetString(applier.Comp.CantRecolorPopup, ("target", target)),applier, user); + return false; + } + + // Check if there's enough uses left + if (applier.Comp is { UsesLeft: <= 0, MaxUses: not null }) + { + _popup.PopupClient(Loc.GetString(applier.Comp.NoMoreUsesPopup, ("name", applier)),applier, user); + return false; + } + + return true; + } + + private void OnGetApplierVerbs(Entity ent, ref GetVerbsEvent args) + { + if (!args.CanAccess + || !args.CanInteract + || !CanRecolor(ent, args.User, args.Target, true)) + return; + + var user = args.User; + var target = args.Target; + + var verb = new UtilityVerb + { + Act = () => TryStartApplyRecolorDoAfter(user, target, ent), + Text = Loc.GetString(ent.Comp.VerbText), + Icon = ent.Comp.VerbIcon, + }; + + args.Verbs.Add(verb); + } + + private void OnExamined(Entity ent, ref ExaminedEvent args) + { + if (!args.IsInDetailsRange) + return; + var recolorData = ent.Comp.RecolorData; + + var colorName = GetColorName(recolorData); + + args.PushMarkup(Loc.GetString(ent.Comp.ColorShowcaseExamine, ("color", recolorData.Color), ("colorName", colorName))); + + // If max uses isn't null (signifying this item has infinite uses), show uses count + if (ent.Comp.MaxUses != null) + args.PushMarkup(Loc.GetString(ent.Comp.UsesExamine, ("uses", ent.Comp.UsesLeft))); + } +} diff --git a/Content.Shared/_DEN/Recolor/RecolorSystem.Remover.cs b/Content.Shared/_DEN/Recolor/RecolorSystem.Remover.cs new file mode 100644 index 0000000000..548c86a2fd --- /dev/null +++ b/Content.Shared/_DEN/Recolor/RecolorSystem.Remover.cs @@ -0,0 +1,86 @@ +using Content.Shared._DEN.Recolor.Components; +using Content.Shared.DoAfter; +using Content.Shared.Interaction; +using Content.Shared.Verbs; +using Robust.Shared.Utility; + +namespace Content.Shared._DEN.Recolor; + +public sealed partial class RecolorSystem +{ + private void OnRecolorRemoverAfterInteract(Entity ent, ref AfterInteractEvent args) + { + if (args.Target == null || args.Handled || !args.CanReach) + return; + if (!TryComp(args.Target, out var recolored) || !recolored.RecolorData.Removable) + return; + + args.Handled = TryStartRemoveRecolorDoAfter(args.User, (args.Target.Value, recolored), ent); + } + + private bool TryStartRemoveRecolorDoAfter( + EntityUid user, + Entity target, + Entity remover) + { + var doAfterEvent = new RemoveRecolorDoAfterEvent(); + + var doAfterArgs = new DoAfterArgs(EntityManager, + user: user, + seconds: (float)remover.Comp.DoAfterDuration.TotalSeconds, + @event: doAfterEvent, + eventTarget: remover, + target: target, + used: remover) + { + BreakOnDropItem = true, + BreakOnMove = true, + BreakOnHandChange = true, + }; + + var recolorData = target.Comp.RecolorData; + + if (recolorData.PaintType != null) + _popup.PopupClient(Loc.GetString("recolor-remover-start-popup", ("name", target), ("paintType", recolorData.PaintType)),remover,user); + + return _doAfter.TryStartDoAfter(doAfterArgs); + } + + private void OnRemoveRecolorDoAfterEvent(Entity ent, ref RemoveRecolorDoAfterEvent args) + { + if (args.Handled || args.Cancelled || !TryComp(args.Target, out var recolored)) + return; + + var recolorData = recolored.RecolorData; + + RemoveRecolor((args.Target.Value, recolored)); + + _audio.PlayPredicted(ent.Comp.DoafterSound, ent, args.User); + + if (recolorData.PaintType != null) + _popup.PopupClient(Loc.GetString("recolor-remover-finish-popup", ("name", args.Target), ("paintType", recolorData.PaintType)), ent, args.User); + + args.Handled = true; + } + + private void OnGetRemoverVerbs(Entity ent, ref GetVerbsEvent args) + { + if (!args.CanInteract || !args.CanAccess) + return; + + if (!TryComp(args.Target, out var recolored) || !recolored.RecolorData.Removable) + return; + + var user = args.User; + var target = args.Target; + + var verb = new UtilityVerb + { + Act = () => TryStartRemoveRecolorDoAfter(user, (target, recolored), ent), + Text = Loc.GetString("verb-remove-recolor"), + Icon = new SpriteSpecifier.Texture(new ResPath("/Textures/Interface/VerbIcons/bubbles.svg.192dpi.png")), + }; + + args.Verbs.Add(verb); + } +} diff --git a/Content.Shared/_DEN/Recolor/RecolorSystem.cs b/Content.Shared/_DEN/Recolor/RecolorSystem.cs new file mode 100644 index 0000000000..d483455312 --- /dev/null +++ b/Content.Shared/_DEN/Recolor/RecolorSystem.cs @@ -0,0 +1,249 @@ +using Content.Shared._DEN.Recolor.Components; +using Content.Shared.DoAfter; +using Content.Shared.Examine; +using Content.Shared.Interaction; +using Content.Shared.Nutrition.EntitySystems; +using Content.Shared.Popups; +using Content.Shared.Verbs; +using Content.Shared.Whitelist; +using JetBrains.Annotations; +using Robust.Shared.Audio.Systems; +using Robust.Shared.ColorNaming; +using Robust.Shared.Serialization; + +namespace Content.Shared._DEN.Recolor; + +public sealed partial class RecolorSystem : EntitySystem +{ + [Dependency] private readonly ILocalizationManager _localizationManager = default!; + + [Dependency] private readonly EntityWhitelistSystem _whitelist = default!; + [Dependency] private readonly OpenableSystem _openable = default!; + [Dependency] private readonly SharedAppearanceSystem _appearance = default!; + [Dependency] private readonly SharedAudioSystem _audio = default!; + [Dependency] private readonly SharedDoAfterSystem _doAfter = default!; + [Dependency] private readonly SharedPopupSystem _popup = default!; + [Dependency] private readonly SharedUserInterfaceSystem _ui = default!; + + public override void Initialize() + { + base.Initialize(); + // Recolored events + SubscribeLocalEvent(OnComponentStartup); + SubscribeLocalEvent(OnComponentRemove); + SubscribeLocalEvent(OnExamined); + + // Recolor Applier Events + SubscribeLocalEvent(OnRecolorApplierAfterInteract); + SubscribeLocalEvent(OnApplyRecolorDoAfterEvent); + SubscribeLocalEvent(OnComponentStartup); + SubscribeLocalEvent>(OnGetApplierVerbs); + SubscribeLocalEvent(OnExamined); + + // Recolor Applier Color Selector Events + SubscribeLocalEvent(OnBoundUIOpened); + SubscribeLocalEvent(OnRecolorApplierColorChanged); + + // Recolor Remover Events + SubscribeLocalEvent(OnRecolorRemoverAfterInteract); + SubscribeLocalEvent>(OnGetRemoverVerbs); + SubscribeLocalEvent(OnRemoveRecolorDoAfterEvent); + } + + /// + /// Recolor an entity using provided recolorData. + /// + /// Entity to recolor. + /// Recolor data to use when recoloring. + [PublicAPI] + public void Recolor( + EntityUid uid, + RecolorData recolorData) + { + if (HasComp(uid)) + { + //Replace old recolored component. you can spray things with paint twice.. right? + RemComp(uid); + } + + EnsureComp(uid); + + var comp = new RecoloredComponent + { + RecolorData = recolorData, + }; + + AddComp(uid, comp); + Dirty((uid, comp)); + } + + /// + /// Recolor an entity with simple parameters. + /// + /// Entity to recolor. + /// Color to recolor to. + /// If the recoloring can be removed by regular means. + /// Shader to replace default shaders with. + /// Paint type to use, purely for flavor. + /// Examine text LocId to use. + [PublicAPI] + public void Recolor( + EntityUid uid, + Color color, + bool removable, + string? shader, + string? paintType, + string examineText = "recolored-examine") + { + if (HasComp(uid)) + { + //Replace old recolored component. you can spray things with paint twice.. right? + RemComp(uid); + } + + EnsureComp(uid); + + var comp = new RecoloredComponent + { + RecolorData = new RecolorData + { + Color = color, + Removable = removable, + Shader = shader, + PaintType = paintType, + }, + ExamineText = examineText, + }; + + AddComp(uid, comp); + Dirty((uid, comp)); + } + + /// Entity to remove the recolor of. + [PublicAPI] + public void RemoveRecolor(Entity ent) + { + if (!Resolve(ent.Owner, ref ent.Comp, logMissing: false)) + return; + + RemComp(ent, ent.Comp); + } + + private void OnComponentStartup(Entity ent, ref ComponentStartup args) + { + RefreshVisuals(ent); + } + + private void OnComponentRemove(Entity ent, ref ComponentRemove args) + { + RemoveVisuals(ent); + } + + private void OnExamined(Entity ent, ref ExaminedEvent args) + { + if (!args.IsInDetailsRange) + return; + + var recolorData = ent.Comp.RecolorData; + + if (recolorData is { PaintType: not null}) + { + var colorName = GetColorName(recolorData); + + args.PushMarkup(Loc.GetString( + ent.Comp.ExamineText, + ("color", recolorData.Color), + ("colorName", colorName), + ("paintType", recolorData.PaintType))); + } + } + + + private string GetColorName(RecolorData recolorData) + { + // Get the color's name. If the color itself has a color defined, use it + return recolorData.ColorName ?? + // Otherwise, use what colornaming THINKS it is. + ColorNaming.Describe(recolorData.Color, _localizationManager); + } + + private void RefreshVisuals(Entity ent) + { + if (!TryComp(ent, out AppearanceComponent? appearance)) + return; + + var recolorData = ent.Comp.RecolorData; + + _appearance.SetData(ent, RecolorVisuals.RecolorData, recolorData, appearance); + } + + private void RemoveVisuals(Entity ent) + { + _appearance.RemoveData(ent, RecolorVisuals.RecolorData); + } +} + +/// +/// Stores information regarding recolored objects. +/// +[Serializable, NetSerializable] +[DataDefinition] +public sealed partial class RecolorData +{ + /// + /// Color to recolor with. + /// + [DataField] + public Color Color { get; set; } = Color.White; + /// + /// Whether the Recolor can be removed by the RecolorRemoverSystem. + /// + [DataField] + public bool Removable { get; set; } = true; + /// + /// Name of the color, purely for flavor. + /// + [DataField] + public string? ColorName { get; set; } + /// + /// Purely for flavor, used for locale information. + /// + [DataField] + public string? PaintType { get; set; } + /// + /// Replaces layers shader with this shader. + /// + [DataField] + public string? Shader { get; set; } = "Desaturated"; + /// + /// If used, these will be the only shaders replaced. + /// + [DataField] + public List? ShaderBlacklist { get; set; } + /// + /// If used, these shaders will never be replaced. + /// + [DataField] + public List? ShaderWhitelist { get; set; } +} + +[Serializable, NetSerializable] +public sealed partial class ApplyRecolorDoAfterEvent : DoAfterEvent +{ + public RecolorData RecolorData; + + public override DoAfterEvent Clone() + { + return this; + } +} + +[Serializable, NetSerializable] +public sealed partial class RemoveRecolorDoAfterEvent : SimpleDoAfterEvent; + + +[Serializable, NetSerializable] +public enum RecolorVisuals +{ + RecolorData +} diff --git a/Resources/Locale/en-US/_DEN/recolor/examine.ftl b/Resources/Locale/en-US/_DEN/recolor/examine.ftl new file mode 100644 index 0000000000..1d318f9a39 --- /dev/null +++ b/Resources/Locale/en-US/_DEN/recolor/examine.ftl @@ -0,0 +1,8 @@ +spray-paint-examine-color = It contains [color={$color}]{$colorName}[/color] spray paint. + +spray-paint-examine-uses = There { $uses -> +[one] is [color=yellow]{$uses}[/color] spray +*[other] are [color=yellow]{$uses}[/color] sprays +} left. + +recolored-examine = It is covered in [color={$color}]{$colorName}[/color] {$paintType}. diff --git a/Resources/Locale/en-US/_DEN/recolor/popups.ftl b/Resources/Locale/en-US/_DEN/recolor/popups.ftl new file mode 100644 index 0000000000..9b9c797e19 --- /dev/null +++ b/Resources/Locale/en-US/_DEN/recolor/popups.ftl @@ -0,0 +1,5 @@ +spray-paint-empty = {$name} is out of paint! +spray-paint-fail = {$target} can't be spray painted! + +recolor-remover-start-popup = You start cleaning the {$paintType} off of the {$name}... +recolor-remover-finish-popup = You clean the {$paintType} off of the {$name}! diff --git a/Resources/Locale/en-US/_DEN/recolor/ui.ftl b/Resources/Locale/en-US/_DEN/recolor/ui.ftl new file mode 100644 index 0000000000..6704e264b1 --- /dev/null +++ b/Resources/Locale/en-US/_DEN/recolor/ui.ftl @@ -0,0 +1 @@ +recolor-applier-color-selector-window-title = Magic Spray Paint diff --git a/Resources/Locale/en-US/_DEN/recolor/verbs.ftl b/Resources/Locale/en-US/_DEN/recolor/verbs.ftl new file mode 100644 index 0000000000..d7ea9ac214 --- /dev/null +++ b/Resources/Locale/en-US/_DEN/recolor/verbs.ftl @@ -0,0 +1,4 @@ +verb-spray-paint = Spray Paint +verb-remove-recolor = Remove recolor + +verb-recolor-applier-color-selector-openui = Change color diff --git a/Resources/Prototypes/Entities/Objects/Specific/Janitorial/soap.yml b/Resources/Prototypes/Entities/Objects/Specific/Janitorial/soap.yml index 69eff77aeb..461c621735 100644 --- a/Resources/Prototypes/Entities/Objects/Specific/Janitorial/soap.yml +++ b/Resources/Prototypes/Entities/Objects/Specific/Janitorial/soap.yml @@ -68,6 +68,7 @@ - type: CleansForensics - type: Residue residueAdjective: residue-slippery + - type: RecolorRemover # DEN - type: entity parent: BaseSoap diff --git a/Resources/Prototypes/_DEN/Catalog/Cargo/cargo_fun.yml b/Resources/Prototypes/_DEN/Catalog/Cargo/cargo_fun.yml new file mode 100644 index 0000000000..2fea64e2b2 --- /dev/null +++ b/Resources/Prototypes/_DEN/Catalog/Cargo/cargo_fun.yml @@ -0,0 +1,19 @@ +- type: cargoProduct + id: FunSprayPaintBundle + icon: + sprite: _DEN/Objects/Tools/spray-paint.rsi + state: icon + product: CrateFunSprayPaintVariety + cost: 2000 + category: cargoproduct-category-name-fun + group: market + +- type: cargoProduct + id: FunSprayPaintMagic + icon: + sprite: _DEN/Objects/Tools/spray-paint.rsi + state: icon-rainbow + product: CrateFunSprayPaintMagic + cost: 2000 + category: cargoproduct-category-name-fun + group: market diff --git a/Resources/Prototypes/_DEN/Catalog/Fills/Boxes/general.yml b/Resources/Prototypes/_DEN/Catalog/Fills/Boxes/general.yml new file mode 100644 index 0000000000..277204d83c --- /dev/null +++ b/Resources/Prototypes/_DEN/Catalog/Fills/Boxes/general.yml @@ -0,0 +1,71 @@ +- type: entity + parent: BoxCardboard + id: BoxSprayPaintBase + abstract: true + components: + - type: Sprite + layers: + - state: box + - state: clown # placeholder + - type: Storage + grid: + - 0,0,5,3 + whitelist: + components: + - RecolorApplier + +- type: entity + parent: BoxSprayPaintBase + id: BoxSprayPaintColors + name: colored spray paint + description: Box filled with a rainbow assortment of spray paints! + components: + - type: EntityTableContainerFill + containers: + storagebase: !type:AllSelector + children: + - id: SprayPaintCanRed + - id: SprayPaintCanOrange + - id: SprayPaintCanYellow + - id: SprayPaintCanLimeGreen + - id: SprayPaintCanGreen + - id: SprayPaintCanCyan + - id: SprayPaintCanBlue + - id: SprayPaintCanPurple + - id: SprayPaintCanPink + - id: SprayPaintCanBrown + +- type: entity + parent: BoxSprayPaintBase + id: BoxSprayPaintShades + name: greyscale spray paint + description: Box filled with greyscale spray paints! + components: + - type: EntityTableContainerFill + containers: + storagebase: !type:AllSelector + children: + - id: SprayPaintCanWhite + amount: 4 + - id: SprayPaintCanGray + amount: 4 + - id: SprayPaintCanBlack + amount: 4 + +- type: entity + parent: BoxSprayPaintBase + id: BoxSprayPaintMetals + name: metallic spray paint + description: Box filled with metallic spray paints! + components: + - type: EntityTableContainerFill + containers: + storagebase: !type:AllSelector + children: + - id: SprayPaintCanGold + amount: 4 + - id: SprayPaintCanSilver + amount: 4 + - id: SprayPaintCanCopper + amount: 4 + diff --git a/Resources/Prototypes/_DEN/Catalog/Fills/Crates/fun.yml b/Resources/Prototypes/_DEN/Catalog/Fills/Crates/fun.yml new file mode 100644 index 0000000000..b94e91f392 --- /dev/null +++ b/Resources/Prototypes/_DEN/Catalog/Fills/Crates/fun.yml @@ -0,0 +1,25 @@ +- type: entity + parent: CratePlastic + id: CrateFunSprayPaintVariety + name: bulk spray paint crate + description: A crate filled with a variety of spray paints! + components: + - type: EntityTableContainerFill + containers: + entity_storage: !type:AllSelector + children: + - id: BoxSprayPaintColors + - id: BoxSprayPaintShades + - id: BoxSprayPaintMetals + +- type: entity + parent: CratePlastic + id: CrateFunSprayPaintMagic + name: magic spray paint crate + description: A crate filled with.. A singular can of magic spray paint! + components: + - type: EntityTableContainerFill + containers: + entity_storage: !type:AllSelector + children: + - id: SprayPaintCanMagic diff --git a/Resources/Prototypes/_DEN/Entities/Objects/Tools/paint_cans.yml b/Resources/Prototypes/_DEN/Entities/Objects/Tools/paint_cans.yml new file mode 100644 index 0000000000..499614d511 --- /dev/null +++ b/Resources/Prototypes/_DEN/Entities/Objects/Tools/paint_cans.yml @@ -0,0 +1,512 @@ + + +- type: entity + parent: BaseItem + id: SprayPaintCan + abstract: true + description: A can of high quality spray paint with patented anti-skin technology. + components: + - type: Appearance + - type: Sprite + sprite: _DEN/Objects/Tools/spray-paint.rsi + state: spray-can-base + - type: Openable + closeable: true + sound: + path: /Audio/Effects/pop_high.ogg + closeSound: + path: /Audio/Effects/pop_high.ogg + - type: PhysicalComposition + materialComposition: + Steel: 100 + - type: RecolorApplier # The important one\ + recolorData: + color: white + paintType: &paintType spray paint + shaderBlacklist: &blacklist + - unshaded + - DisplacedDraw + entityBlacklist: + components: + - HumanoidProfile # Prevent spraying on humans + - Puddle # It's a liquid lol + - Cable # For accessibility reasons. + - CableVisualizer # See above + - AtmosPipeColor # It doesn't work currently, unsure why? Possibly because gaspipecolor visualizer runs first. + - PipeColorVisuals # See above + maxUses: 64 # Might be too much? Unsure. + +# Rainbow colors + +- type: entity + parent: SprayPaintCan + id: SprayPaintCanRed + name: red spray paint + components: + - type: RecolorApplier + recolorData: + color: red + colorName: red + paintType: *paintType + shaderBlacklist: *blacklist + - type: Sprite + layers: + - state: spray-can-base + map: ["Base"] + - state: spray-can-color-closed + map: ["enum.OpenableVisuals.Layer"] + color: red + - type: GenericVisualizer + visuals: + enum.OpenableVisuals.Opened: + enum.OpenableVisuals.Layer: + True: {state: "spray-can-color-open" , color: red} + False: {state: "spray-can-color-closed" , color: red} + +- type: entity + parent: SprayPaintCan + id: SprayPaintCanOrange + name: orange spray paint + components: + - type: RecolorApplier + recolorData: + color: orange + colorName: orange + paintType: *paintType + shaderBlacklist: *blacklist + - type: Sprite + layers: + - state: spray-can-base + map: ["Base"] + - state: spray-can-color-closed + map: ["enum.OpenableVisuals.Layer"] + color: orange + - type: GenericVisualizer + visuals: + enum.OpenableVisuals.Opened: + enum.OpenableVisuals.Layer: + True: {state: "spray-can-color-open" , color: orange} + False: {state: "spray-can-color-closed" , color: orange} + +- type: entity + parent: SprayPaintCan + id: SprayPaintCanYellow + name: yellow spray paint + components: + - type: RecolorApplier + recolorData: + color: yellow + colorName: yellow + paintType: *paintType + shaderBlacklist: *blacklist + - type: Sprite + layers: + - state: spray-can-base + map: ["Base"] + - state: spray-can-color-closed + map: ["enum.OpenableVisuals.Layer"] + color: yellow + - type: GenericVisualizer + visuals: + enum.OpenableVisuals.Opened: + enum.OpenableVisuals.Layer: + True: {state: "spray-can-color-open" , color: yellow} + False: {state: "spray-can-color-closed" , color: yellow} + +- type: entity + parent: SprayPaintCan + id: SprayPaintCanLimeGreen + name: lime green spray paint + components: + - type: RecolorApplier + recolorData: + color: greenyellow + colorName: lime green + paintType: *paintType + shaderBlacklist: *blacklist + - type: Sprite + layers: + - state: spray-can-base + map: ["Base"] + - state: spray-can-color-closed + map: ["enum.OpenableVisuals.Layer"] + color: greenyellow + - type: GenericVisualizer + visuals: + enum.OpenableVisuals.Opened: + enum.OpenableVisuals.Layer: + True: {state: "spray-can-color-open" , color: greenyellow} + False: {state: "spray-can-color-closed" , color: greenyellow} + +- type: entity + parent: SprayPaintCan + id: SprayPaintCanGreen + name: green spray paint + components: + - type: RecolorApplier + recolorData: + color: green + colorName: green + paintType: *paintType + shaderBlacklist: *blacklist + - type: Sprite + layers: + - state: spray-can-base + map: ["Base"] + - state: spray-can-color-closed + map: ["enum.OpenableVisuals.Layer"] + color: green + - type: GenericVisualizer + visuals: + enum.OpenableVisuals.Opened: + enum.OpenableVisuals.Layer: + True: {state: "spray-can-color-open" , color: green} + False: {state: "spray-can-color-closed" , color: green} + +- type: entity + parent: SprayPaintCan + id: SprayPaintCanCyan + name: cyan spray paint + components: + - type: RecolorApplier + recolorData: + color: cyan + colorName: cyan + paintType: *paintType + shaderBlacklist: *blacklist + - type: Sprite + layers: + - state: spray-can-base + map: ["Base"] + - state: spray-can-color-closed + map: ["enum.OpenableVisuals.Layer"] + color: cyan + - type: GenericVisualizer + visuals: + enum.OpenableVisuals.Opened: + enum.OpenableVisuals.Layer: + True: {state: "spray-can-color-open" , color: cyan} + False: {state: "spray-can-color-closed" , color: cyan} + +- type: entity + parent: SprayPaintCan + id: SprayPaintCanBlue + name: blue spray paint + components: + - type: RecolorApplier + recolorData: + color: blue + colorName: blue + paintType: *paintType + shaderBlacklist: *blacklist + - type: Sprite + layers: + - state: spray-can-base + map: ["Base"] + - state: spray-can-color-closed + map: ["enum.OpenableVisuals.Layer"] + color: blue + - type: GenericVisualizer + visuals: + enum.OpenableVisuals.Opened: + enum.OpenableVisuals.Layer: + True: {state: "spray-can-color-open" , color: blue} + False: {state: "spray-can-color-closed" , color: blue} + +- type: entity + parent: SprayPaintCan + id: SprayPaintCanPurple + name: purple spray paint + components: + - type: RecolorApplier + recolorData: + color: purple + colorName: purple + paintType: *paintType + shaderBlacklist: *blacklist + - type: Sprite + layers: + - state: spray-can-base + map: ["Base"] + - state: spray-can-color-closed + map: ["enum.OpenableVisuals.Layer"] + color: purple + - type: GenericVisualizer + visuals: + enum.OpenableVisuals.Opened: + enum.OpenableVisuals.Layer: + True: {state: "spray-can-color-open" , color: purple} + False: {state: "spray-can-color-closed" , color: purple} + +- type: entity + parent: SprayPaintCan + id: SprayPaintCanPink + name: pink spray paint + components: + - type: RecolorApplier + recolorData: + color: hotpink + colorName: pink + paintType: *paintType + shaderBlacklist: *blacklist + - type: Sprite + layers: + - state: spray-can-base + map: ["Base"] + - state: spray-can-color-closed + map: ["enum.OpenableVisuals.Layer"] + color: hotpink + - type: GenericVisualizer + visuals: + enum.OpenableVisuals.Opened: + enum.OpenableVisuals.Layer: + True: {state: "spray-can-color-open" , color: hotpink} + False: {state: "spray-can-color-closed" , color: hotpink} + +# Metallics + +- type: entity + parent: SprayPaintCan + id: SprayPaintCanGold + name: gold spray paint + components: + - type: RecolorApplier + recolorData: + color: gold + colorName: gold + paintType: *paintType + shaderBlacklist: *blacklist + - type: Sprite + layers: + - state: spray-can-base + map: ["Base"] + - state: spray-can-color-closed + map: ["enum.OpenableVisuals.Layer"] + color: gold + - type: GenericVisualizer + visuals: + enum.OpenableVisuals.Opened: + enum.OpenableVisuals.Layer: + True: {state: "spray-can-color-open" , color: gold} + False: {state: "spray-can-color-closed" , color: gold} + +- type: entity + parent: SprayPaintCan + id: SprayPaintCanSilver + name: silver spray paint + components: + - type: RecolorApplier + recolorData: + color: silver + colorName: silver + paintType: *paintType + shaderBlacklist: *blacklist + - type: Sprite + layers: + - state: spray-can-base + map: ["Base"] + - state: spray-can-color-closed + map: ["enum.OpenableVisuals.Layer"] + color: silver + - type: GenericVisualizer + visuals: + enum.OpenableVisuals.Opened: + enum.OpenableVisuals.Layer: + True: {state: "spray-can-color-open" , color: silver} + False: {state: "spray-can-color-closed" , color: silver} + +- type: entity + parent: SprayPaintCan + id: SprayPaintCanCopper + name: copper spray paint + components: + - type: RecolorApplier + recolorData: + color: chocolate + colorName: copper + paintType: *paintType + shaderBlacklist: *blacklist + - type: Sprite + layers: + - state: spray-can-base + map: ["Base"] + - state: spray-can-color-closed + map: ["enum.OpenableVisuals.Layer"] + color: chocolate + - type: GenericVisualizer + visuals: + enum.OpenableVisuals.Opened: + enum.OpenableVisuals.Layer: + True: {state: "spray-can-color-open" , color: chocolate} + False: {state: "spray-can-color-closed" , color: chocolate} + +# Shades and Neutrals + +- type: entity + parent: SprayPaintCan + id: SprayPaintCanWhite + name: white spray paint + components: + - type: RecolorApplier + recolorData: + color: white + paintType: *paintType + shaderBlacklist: *blacklist + - type: Sprite + layers: + - state: spray-can-base + map: ["Base"] + - state: spray-can-color-closed + map: ["enum.OpenableVisuals.Layer"] + color: white + - type: GenericVisualizer + visuals: + enum.OpenableVisuals.Opened: + enum.OpenableVisuals.Layer: + True: {state: "spray-can-color-open" , color: white} + False: {state: "spray-can-color-closed" , color: white} + +- type: entity + parent: SprayPaintCan + id: SprayPaintCanGray + name: gray spray paint + components: + - type: RecolorApplier + recolorData: + color: gray + paintType: *paintType + shaderBlacklist: *blacklist + - type: Sprite + layers: + - state: spray-can-base + map: ["Base"] + - state: spray-can-color-closed + map: ["enum.OpenableVisuals.Layer"] + color: gray + - type: GenericVisualizer + visuals: + enum.OpenableVisuals.Opened: + enum.OpenableVisuals.Layer: + True: {state: "spray-can-color-open" , color: gray} + False: {state: "spray-can-color-closed" , color: gray} + +- type: entity + parent: SprayPaintCan + id: SprayPaintCanBlack + name: black spray paint + components: + - type: RecolorApplier + recolorData: + color: &black "#333333" + paintType: *paintType + shaderBlacklist: *blacklist + colorName: black + - type: Sprite + layers: + - state: spray-can-base + map: ["Base"] + - state: spray-can-color-closed + map: ["enum.OpenableVisuals.Layer"] + color: *black + - type: GenericVisualizer + visuals: + enum.OpenableVisuals.Opened: + enum.OpenableVisuals.Layer: + True: {state: "spray-can-color-open" , color: *black} + False: {state: "spray-can-color-closed" , color: *black} + +- type: entity + parent: SprayPaintCan + id: SprayPaintCanBrown + name: brown spray paint + components: + - type: RecolorApplier + recolorData: + color: saddlebrown + colorName: brown + paintType: *paintType + shaderBlacklist: *blacklist + - type: Sprite + layers: + - state: spray-can-base + map: ["Base"] + - state: spray-can-color-closed + map: ["enum.OpenableVisuals.Layer"] + color: saddlebrown + - type: GenericVisualizer + visuals: + enum.OpenableVisuals.Opened: + enum.OpenableVisuals.Layer: + True: {state: "spray-can-color-open" , color: saddlebrown} + False: {state: "spray-can-color-closed" , color: saddlebrown} + +# Magic spray paints + +- type: entity + parent: SprayPaintCan + id: SprayPaintCanMagic + name: magic spray paint + components: + - type: RecolorApplier + recolorData: + color: white + paintType: *paintType + shaderBlacklist: *blacklist + - type: RecolorApplierColorSelector + - type: ActivatableUI + blockSpectators: true + inHandsOnly: true + singleUser: true + verbText: verb-recolor-applier-color-selector-openui #fuck you i can make it as long as i want + key: enum.RecolorApplierColorSelectorKey.Key + - type: UserInterface + interfaces: + enum.RecolorApplierColorSelectorKey.Key: + type: RecolorApplierColorSelectorBui + - type: Sprite + layers: + - state: spray-can-rainbow + map: ["Base"] + - state: spray-can-rainbow-cap + map: ["enum.OpenableVisuals.Layer"] + - type: GenericVisualizer + visuals: + enum.OpenableVisuals.Opened: + enum.OpenableVisuals.Layer: + True: {visible: false} + False: {visible: true} + +- type: entity + parent: SprayPaintCanMagic + id: SprayPaintCanMagicMapping + suffix: do not map + name: infinite magic spray paint + components: + - type: RecolorApplier + maxUses: + +- type: entity + parent: SprayPaintCan + id: SprayPaintCanInvisible + suffix: do not map + name: invisible spray paint + components: + - type: RecolorApplier + recolorData: + color: "#FFFFFF00" + colorName: invisible + paintType: *paintType + shaderBlacklist: *blacklist + - type: Sprite + layers: + - state: spray-can-base + map: ["Base"] + - state: spray-can-color-closed + map: ["enum.OpenableVisuals.Layer"] + color: white + - type: GenericVisualizer + visuals: + enum.OpenableVisuals.Opened: + enum.OpenableVisuals.Layer: + True: {state: "spray-can-color-open" , color: white} + False: {state: "spray-can-color-closed" , color: white} diff --git a/Resources/Prototypes/_DEN/Shaders/desaturated.yml b/Resources/Prototypes/_DEN/Shaders/desaturated.yml new file mode 100644 index 0000000000..064bab5f14 --- /dev/null +++ b/Resources/Prototypes/_DEN/Shaders/desaturated.yml @@ -0,0 +1,4 @@ +- type: shader + id: Desaturated + kind: source + path: "/Textures/_DEN/Shaders/desaturated.swsl" diff --git a/Resources/Textures/_DEN/Interface/VerbIcons/attributions.txt b/Resources/Textures/_DEN/Interface/VerbIcons/attributions.txt new file mode 100644 index 0000000000..9109b52bcd --- /dev/null +++ b/Resources/Textures/_DEN/Interface/VerbIcons/attributions.txt @@ -0,0 +1 @@ +Paint Spray Can Outline SVG Vector from https://www.svgrepo.com/svg/5941/paint-spray-can-outline, license cc0 diff --git a/Resources/Textures/_DEN/Interface/VerbIcons/paint-spray-can.svg b/Resources/Textures/_DEN/Interface/VerbIcons/paint-spray-can.svg new file mode 100644 index 0000000000..1b40bd3134 --- /dev/null +++ b/Resources/Textures/_DEN/Interface/VerbIcons/paint-spray-can.svg @@ -0,0 +1 @@ + \ No newline at end of file diff --git a/Resources/Textures/_DEN/Interface/VerbIcons/paint-spray-can.svg.192dpi.png b/Resources/Textures/_DEN/Interface/VerbIcons/paint-spray-can.svg.192dpi.png new file mode 100644 index 0000000000..545a6730bd Binary files /dev/null and b/Resources/Textures/_DEN/Interface/VerbIcons/paint-spray-can.svg.192dpi.png differ diff --git a/Resources/Textures/_DEN/Interface/VerbIcons/paint-spray-can.svg.192dpi.png.yml b/Resources/Textures/_DEN/Interface/VerbIcons/paint-spray-can.svg.192dpi.png.yml new file mode 100644 index 0000000000..5c43e23305 --- /dev/null +++ b/Resources/Textures/_DEN/Interface/VerbIcons/paint-spray-can.svg.192dpi.png.yml @@ -0,0 +1,2 @@ +sample: + filter: true diff --git a/Resources/Textures/_DEN/Objects/Tools/spray-paint.rsi/icon-rainbow.png b/Resources/Textures/_DEN/Objects/Tools/spray-paint.rsi/icon-rainbow.png new file mode 100644 index 0000000000..672bc36d66 Binary files /dev/null and b/Resources/Textures/_DEN/Objects/Tools/spray-paint.rsi/icon-rainbow.png differ diff --git a/Resources/Textures/_DEN/Objects/Tools/spray-paint.rsi/icon.png b/Resources/Textures/_DEN/Objects/Tools/spray-paint.rsi/icon.png new file mode 100644 index 0000000000..c3646aa756 Binary files /dev/null and b/Resources/Textures/_DEN/Objects/Tools/spray-paint.rsi/icon.png differ diff --git a/Resources/Textures/_DEN/Objects/Tools/spray-paint.rsi/meta.json b/Resources/Textures/_DEN/Objects/Tools/spray-paint.rsi/meta.json new file mode 100644 index 0000000000..c77d9c56bd --- /dev/null +++ b/Resources/Textures/_DEN/Objects/Tools/spray-paint.rsi/meta.json @@ -0,0 +1,32 @@ +{ + "version": 1, + "license": "CC-BY-SA-3.0", + "copyright": "Sprites by honeyed-lemons (github)", + "size": { + "x": 32, + "y": 32 + }, + "states": [ + { + "name": "icon" + }, + { + "name": "icon-rainbow" + }, + { + "name": "spray-can-base" + }, + { + "name": "spray-can-color-closed" + }, + { + "name": "spray-can-color-open" + }, + { + "name": "spray-can-rainbow" + }, + { + "name": "spray-can-rainbow-cap" + } + ] +} diff --git a/Resources/Textures/_DEN/Objects/Tools/spray-paint.rsi/spray-can-base.png b/Resources/Textures/_DEN/Objects/Tools/spray-paint.rsi/spray-can-base.png new file mode 100644 index 0000000000..1fc007a468 Binary files /dev/null and b/Resources/Textures/_DEN/Objects/Tools/spray-paint.rsi/spray-can-base.png differ diff --git a/Resources/Textures/_DEN/Objects/Tools/spray-paint.rsi/spray-can-color-closed.png b/Resources/Textures/_DEN/Objects/Tools/spray-paint.rsi/spray-can-color-closed.png new file mode 100644 index 0000000000..0e5f537a48 Binary files /dev/null and b/Resources/Textures/_DEN/Objects/Tools/spray-paint.rsi/spray-can-color-closed.png differ diff --git a/Resources/Textures/_DEN/Objects/Tools/spray-paint.rsi/spray-can-color-open.png b/Resources/Textures/_DEN/Objects/Tools/spray-paint.rsi/spray-can-color-open.png new file mode 100644 index 0000000000..3e1a68f62d Binary files /dev/null and b/Resources/Textures/_DEN/Objects/Tools/spray-paint.rsi/spray-can-color-open.png differ diff --git a/Resources/Textures/_DEN/Objects/Tools/spray-paint.rsi/spray-can-rainbow-cap.png b/Resources/Textures/_DEN/Objects/Tools/spray-paint.rsi/spray-can-rainbow-cap.png new file mode 100644 index 0000000000..98dbe1a26d Binary files /dev/null and b/Resources/Textures/_DEN/Objects/Tools/spray-paint.rsi/spray-can-rainbow-cap.png differ diff --git a/Resources/Textures/_DEN/Objects/Tools/spray-paint.rsi/spray-can-rainbow.png b/Resources/Textures/_DEN/Objects/Tools/spray-paint.rsi/spray-can-rainbow.png new file mode 100644 index 0000000000..c2754d127b Binary files /dev/null and b/Resources/Textures/_DEN/Objects/Tools/spray-paint.rsi/spray-can-rainbow.png differ diff --git a/Resources/Textures/_DEN/Shaders/desaturated.swsl b/Resources/Textures/_DEN/Shaders/desaturated.swsl new file mode 100644 index 0000000000..cffe13fad1 --- /dev/null +++ b/Resources/Textures/_DEN/Shaders/desaturated.swsl @@ -0,0 +1,5 @@ +void fragment() { + highp vec4 color = zTexture(UV); + COLOR.rgb = mix(vec3(dot(color.rgb, vec3(0.399, 0.687, 0.214))), color.rgb, 0); + COLOR.a = color.a; +}