diff --git a/Content.Client/Atmos/EntitySystems/GasCanisterAppearanceSystem.cs b/Content.Client/Atmos/EntitySystems/GasCanisterAppearanceSystem.cs new file mode 100644 index 00000000000..e351b672bd0 --- /dev/null +++ b/Content.Client/Atmos/EntitySystems/GasCanisterAppearanceSystem.cs @@ -0,0 +1,31 @@ +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; + +/// +/// Used to change the appearance of gas canisters. +/// +public sealed class GasCanisterAppearanceSystem : VisualizerSystem +{ + [Dependency] private readonly IPrototypeManager _prototypeManager = default!; + + protected override void OnAppearanceChange(EntityUid uid, GasCanisterComponent component, ref AppearanceChangeEvent args) + { + if (!AppearanceSystem.TryGetData(uid, + PaintableVisuals.Prototype, + out var protoName, + args.Component) || args.Sprite is null) + 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); + } +} diff --git a/Content.Client/Atmos/UI/GasPressurePumpBoundUserInterface.cs b/Content.Client/Atmos/UI/GasPressurePumpBoundUserInterface.cs index 0c07eec4025..b2bd92f6f1b 100644 --- a/Content.Client/Atmos/UI/GasPressurePumpBoundUserInterface.cs +++ b/Content.Client/Atmos/UI/GasPressurePumpBoundUserInterface.cs @@ -1,8 +1,6 @@ -using Content.Shared.Atmos; -using Content.Shared.Atmos.Components; +using Content.Shared.Atmos.Components; using Content.Shared.Atmos.Piping.Binary.Components; using Content.Shared.IdentityManagement; -using Content.Shared.Localizations; using JetBrains.Annotations; using Robust.Client.UserInterface; @@ -14,9 +12,6 @@ namespace Content.Client.Atmos.UI; [UsedImplicitly] public sealed class GasPressurePumpBoundUserInterface : BoundUserInterface { - [ViewVariables] - private const float MaxPressure = Atmospherics.MaxOutputPressure; - [ViewVariables] private GasPressurePumpWindow? _window; diff --git a/Content.Client/Charges/ChargesSystem.cs b/Content.Client/Charges/ChargesSystem.cs new file mode 100644 index 00000000000..d99a5763330 --- /dev/null +++ b/Content.Client/Charges/ChargesSystem.cs @@ -0,0 +1,52 @@ +using Content.Client.Actions; +using Content.Shared.Actions; +using Content.Shared.Charges.Components; +using Content.Shared.Charges.Systems; + +namespace Content.Client.Charges; + +public sealed class ChargesSystem : SharedChargesSystem +{ + [Dependency] private readonly ActionsSystem _actions = default!; + + private readonly Dictionary _lastCharges = new(); + private readonly Dictionary _tempLastCharges = new(); + + public override void Update(float frameTime) + { + // Technically this should probably be in frameupdate but no one will ever notice a tick of delay on this. + base.Update(frameTime); + + if (!_timing.IsFirstTimePredicted) + return; + + // Update recharging actions. Server doesn't actually care about this and it's a waste of performance, actions are immediate. + var query = AllEntityQuery(); + + while (query.MoveNext(out var uid, out var recharge, out var charges)) + { + BaseActionComponent? actionComp = null; + + if (!_actions.ResolveActionData(uid, ref actionComp, logError: false)) + continue; + + var current = GetCurrentCharges((uid, charges, recharge)); + + if (!_lastCharges.TryGetValue(uid, out var last) || current != last) + { + _actions.UpdateAction(uid, actionComp); + } + + _tempLastCharges[uid] = current; + } + + _lastCharges.Clear(); + + foreach (var (uid, value) in _tempLastCharges) + { + _lastCharges[uid] = value; + } + + _tempLastCharges.Clear(); + } +} diff --git a/Content.Client/Charges/Systems/ChargesSystem.cs b/Content.Client/Charges/Systems/ChargesSystem.cs deleted file mode 100644 index 9170ac5e948..00000000000 --- a/Content.Client/Charges/Systems/ChargesSystem.cs +++ /dev/null @@ -1,5 +0,0 @@ -using Content.Shared.Charges.Systems; - -namespace Content.Client.Charges.Systems; - -public sealed class ChargesSystem : SharedChargesSystem { } diff --git a/Content.Client/Doors/DoorSystem.cs b/Content.Client/Doors/DoorSystem.cs index 5e3de813d65..3d9a3e2a9aa 100644 --- a/Content.Client/Doors/DoorSystem.cs +++ b/Content.Client/Doors/DoorSystem.cs @@ -1,16 +1,18 @@ using Content.Shared.Doors.Components; using Content.Shared.Doors.Systems; +using Content.Shared.SprayPainter.Prototypes; using Robust.Client.Animations; using Robust.Client.GameObjects; -using Robust.Client.ResourceManagement; -using Robust.Shared.Serialization.TypeSerializers.Implementations; +using Robust.Shared.Prototypes; namespace Content.Client.Doors; public sealed class DoorSystem : SharedDoorSystem { [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!; public override void Initialize() { @@ -84,8 +86,8 @@ private void OnAppearanceChange(Entity entity, ref AppearanceChan if (!AppearanceSystem.TryGetData(entity, DoorVisuals.State, out var state, args.Component)) state = DoorState.Closed; - if (AppearanceSystem.TryGetData(entity, DoorVisuals.BaseRSI, out var baseRsi, args.Component)) - UpdateSpriteLayers(args.Sprite, baseRsi); + if (AppearanceSystem.TryGetData(entity, PaintableVisuals.Prototype, out var prototype, args.Component)) + UpdateSpriteLayers((entity.Owner, args.Sprite), prototype); if (_animationSystem.HasRunningAnimation(entity, DoorComponent.AnimationKey)) _animationSystem.Stop(entity.Owner, DoorComponent.AnimationKey); @@ -95,21 +97,21 @@ private void OnAppearanceChange(Entity entity, ref AppearanceChan private void UpdateAppearanceForDoorState(Entity entity, SpriteComponent sprite, DoorState state) { - sprite.DrawDepth = state is DoorState.Open ? entity.Comp.OpenDrawDepth : entity.Comp.ClosedDrawDepth; + _sprite.SetDrawDepth((entity.Owner, sprite), state is DoorState.Open ? entity.Comp.OpenDrawDepth : entity.Comp.ClosedDrawDepth); switch (state) { case DoorState.Open: foreach (var (layer, layerState) in entity.Comp.OpenSpriteStates) { - sprite.LayerSetState(layer, layerState); + _sprite.LayerSetRsiState((entity.Owner, sprite), layer, layerState); } return; case DoorState.Closed: foreach (var (layer, layerState) in entity.Comp.ClosedSpriteStates) { - sprite.LayerSetState(layer, layerState); + _sprite.LayerSetRsiState((entity.Owner, sprite), layer, layerState); } return; @@ -138,14 +140,14 @@ private void UpdateAppearanceForDoorState(Entity entity, SpriteCo } } - private void UpdateSpriteLayers(SpriteComponent sprite, string baseRsi) + private void UpdateSpriteLayers(Entity sprite, string targetProto) { - if (!_resourceCache.TryGetResource(SpriteSpecifierSerializer.TextureRoot / baseRsi, out var res)) - { - Log.Error("Unable to load RSI '{0}'. Trace:\n{1}", baseRsi, Environment.StackTrace); + if (!_prototypeManager.TryIndex(targetProto, out var target)) + return; + + if (!target.TryGetComponent(out SpriteComponent? targetSprite, _componentFactory)) return; - } - sprite.BaseRSI = res.RSI; + _sprite.SetBaseRsi(sprite.AsNullable(), targetSprite.BaseRSI); } } diff --git a/Content.Client/IconSmoothing/IconSmoothComponent.cs b/Content.Client/IconSmoothing/IconSmoothComponent.cs index 4667a81d4a4..7da4ff29dce 100644 --- a/Content.Client/IconSmoothing/IconSmoothComponent.cs +++ b/Content.Client/IconSmoothing/IconSmoothComponent.cs @@ -2,6 +2,8 @@ using Robust.Client.Graphics; using Robust.Shared.Serialization.TypeSerializers.Implementations.Custom.Prototype; +// ReSharper disable InconsistentNaming + namespace Content.Client.IconSmoothing { /// diff --git a/Content.Client/SprayPainter/SprayPainterSystem.cs b/Content.Client/SprayPainter/SprayPainterSystem.cs index 6a1d27e98b7..8f7d7f03622 100644 --- a/Content.Client/SprayPainter/SprayPainterSystem.cs +++ b/Content.Client/SprayPainter/SprayPainterSystem.cs @@ -1,56 +1,129 @@ +using System.Linq; +using Content.Client.Items; +using Content.Client.Message; +using Content.Client.Stylesheets; +using Content.Shared.Decals; using Content.Shared.SprayPainter; -using Robust.Client.Graphics; -using Robust.Client.ResourceManagement; -using Robust.Shared.Serialization.TypeSerializers.Implementations; +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; -using System.Linq; -using Robust.Shared.Graphics; namespace Content.Client.SprayPainter; +/// +/// Client-side spray painter functions. Caches information for spray painter windows and updates the UI to reflect component state. +/// public sealed class SprayPainterSystem : SharedSprayPainterSystem { - [Dependency] private readonly IResourceCache _resourceCache = default!; + [Dependency] private readonly UserInterfaceSystem _ui = default!; + + public List Decals = []; + public Dictionary> PaintableGroupsByCategory = new(); + public Dictionary> PaintableStylesByGroup = new(); + + public override void Initialize() + { + base.Initialize(); - public List Entries { get; private set; } = new(); + Subs.ItemStatus(ent => new StatusControl(ent)); + SubscribeLocalEvent(OnStateUpdate); + SubscribeLocalEvent(OnPrototypesReloaded); - protected override void CacheStyles() + CachePrototypes(); + } + + private void OnStateUpdate(Entity ent, ref AfterAutoHandleStateEvent args) { - base.CacheStyles(); + UpdateUi(ent); + } - Entries.Clear(); - foreach (var style in Styles) + protected override void UpdateUi(Entity ent) + { + if (_ui.TryGetOpenUi(ent.Owner, SprayPainterUiKey.Key, out var bui)) + bui.Update(); + } + + private void OnPrototypesReloaded(PrototypesReloadedEventArgs args) + { + if (!args.WasModified() || !args.WasModified() || !args.WasModified()) + return; + + CachePrototypes(); + } + + private void CachePrototypes() + { + PaintableGroupsByCategory.Clear(); + PaintableStylesByGroup.Clear(); + foreach (var category in Proto.EnumeratePrototypes().OrderBy(x => x.ID)) { - var name = style.Name; - string? iconPath = Groups - .FindAll(x => x.StylePaths.ContainsKey(name))? - .MaxBy(x => x.IconPriority)?.StylePaths[name]; - if (iconPath == null) + var groupList = new List(); + foreach (var groupId in category.Groups) { - Entries.Add(new SprayPainterEntry(name, null)); - continue; + if (!Proto.TryIndex(groupId, out var group)) + continue; + + groupList.Add(groupId); + PaintableStylesByGroup[groupId] = group.Styles; } - RSIResource doorRsi = _resourceCache.GetResource(SpriteSpecifierSerializer.TextureRoot / new ResPath(iconPath)); - if (!doorRsi.RSI.TryGetState("closed", out var icon)) - { - Entries.Add(new SprayPainterEntry(name, null)); + if (groupList.Count > 0) + PaintableGroupsByCategory[category.ID] = groupList; + } + + Decals.Clear(); + foreach (var decalPrototype in Proto.EnumeratePrototypes().OrderBy(x => x.ID)) + { + if (!decalPrototype.Tags.Contains("station") + && !decalPrototype.Tags.Contains("markings") + || decalPrototype.Tags.Contains("dirty")) continue; - } - Entries.Add(new SprayPainterEntry(name, icon.Frame0)); + Decals.Add(new SprayPainterDecalEntry(decalPrototype.ID, decalPrototype.Sprite)); } } -} -public sealed class SprayPainterEntry -{ - public string Name; - public Texture? Icon; - - public SprayPainterEntry(string name, Texture? icon) + private sealed class StatusControl : Control { - Name = name; - Icon = icon; + private readonly RichTextLabel _label; + private readonly Entity _entity; + private DecalPaintMode? _lastPaintingDecals = null; + + public StatusControl(Entity 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)))); + } } } + +/// +/// A spray paintable decal, mapped by ID. +/// +public sealed record SprayPainterDecalEntry(string Name, SpriteSpecifier Sprite); diff --git a/Content.Client/SprayPainter/UI/SprayPainterBoundUserInterface.cs b/Content.Client/SprayPainter/UI/SprayPainterBoundUserInterface.cs index 7d6a6cf2a5a..701ec80bac8 100644 --- a/Content.Client/SprayPainter/UI/SprayPainterBoundUserInterface.cs +++ b/Content.Client/SprayPainter/UI/SprayPainterBoundUserInterface.cs @@ -1,42 +1,96 @@ +using Content.Shared.Decals; using Content.Shared.SprayPainter; using Content.Shared.SprayPainter.Components; using Robust.Client.UserInterface; using Robust.Client.UserInterface.Controls; +using Robust.Shared.Prototypes; namespace Content.Client.SprayPainter.UI; -public sealed class SprayPainterBoundUserInterface : BoundUserInterface +/// +/// A BUI for a spray painter. Allows selecting pipe colours, decals, and paintable object types sorted by category. +/// +public sealed class SprayPainterBoundUserInterface(EntityUid owner, Enum uiKey) : BoundUserInterface(owner, uiKey) { [ViewVariables] private SprayPainterWindow? _window; - public SprayPainterBoundUserInterface(EntityUid owner, Enum uiKey) : base(owner, uiKey) + protected override void Open() { + base.Open(); + + if (_window == null) + { + _window = this.CreateWindow(); + + _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(); + _window.PopulateCategories(sprayPainter.PaintableStylesByGroup, sprayPainter.PaintableGroupsByCategory, sprayPainter.Decals); + Update(); + + if (EntMan.TryGetComponent(Owner, out SprayPainterComponent? sprayPainterComp)) + _window.SetSelectedTab(sprayPainterComp.SelectedTab); } - protected override void Open() + public override void Update() { - base.Open(); + if (_window == null) + return; - _window = this.CreateWindow(); + if (!EntMan.TryGetComponent(Owner, out SprayPainterComponent? sprayPainter)) + return; - _window.OnSpritePicked = OnSpritePicked; - _window.OnColorPicked = OnColorPicked; + _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); + } - if (EntMan.TryGetComponent(Owner, out SprayPainterComponent? comp)) - { - _window.Populate(EntMan.System().Entries, comp.Index, comp.PickedColor, comp.ColorPalette); - } + 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 protoId) + { + SendPredictedMessage(new SprayPainterSetDecalMessage(protoId)); + } + + private void OnTabChanged(int index, bool isSelectedTabWithDecals) + { + SendPredictedMessage(new SprayPainterTabChangedMessage(index, isSelectedTabWithDecals)); } - private void OnSpritePicked(ItemList.ItemListSelectedEventArgs args) + private void OnSpritePicked(string group, string style) { - SendMessage(new SprayPainterSpritePickedMessage(args.ItemIndex)); + SendPredictedMessage(new SprayPainterSetPaintableStyleMessage(group, style)); } - private void OnColorPicked(ItemList.ItemListSelectedEventArgs args) + private void OnSetPipeColor(ItemList.ItemListSelectedEventArgs args) { var key = _window?.IndexToColorKey(args.ItemIndex); - SendMessage(new SprayPainterColorPickedMessage(key)); + SendPredictedMessage(new SprayPainterSetPipeColorMessage(key)); } } diff --git a/Content.Client/SprayPainter/UI/SprayPainterDecals.xaml b/Content.Client/SprayPainter/UI/SprayPainterDecals.xaml new file mode 100644 index 00000000000..0d5c8e4f167 --- /dev/null +++ b/Content.Client/SprayPainter/UI/SprayPainterDecals.xaml @@ -0,0 +1,26 @@ + + + - [DataField("charges"), ViewVariables(VVAccess.ReadWrite), AutoNetworkedField] + [DataField, AutoNetworkedField] public int Charges = 30; /// diff --git a/Content.Shared/RCD/Systems/RCDAmmoSystem.cs b/Content.Shared/RCD/Systems/RCDAmmoSystem.cs index b8b483f1ee7..9104591ad11 100644 --- a/Content.Shared/RCD/Systems/RCDAmmoSystem.cs +++ b/Content.Shared/RCD/Systems/RCDAmmoSystem.cs @@ -10,7 +10,7 @@ namespace Content.Shared.RCD.Systems; public sealed class RCDAmmoSystem : EntitySystem { - [Dependency] private readonly SharedChargesSystem _charges = default!; + [Dependency] private readonly SharedChargesSystem _sharedCharges = default!; [Dependency] private readonly SharedPopupSystem _popup = default!; [Dependency] private readonly IGameTiming _timing = default!; @@ -41,6 +41,7 @@ private void OnAfterInteract(EntityUid uid, RCDAmmoComponent comp, AfterInteract !TryComp(target, out var charges)) return; + var current = _sharedCharges.GetCurrentCharges((target, charges)); var user = args.User; // ## Frontier - Shipyard RCD ammo only fits in shipyard RCD. @@ -53,7 +54,7 @@ private void OnAfterInteract(EntityUid uid, RCDAmmoComponent comp, AfterInteract } args.Handled = true; - var count = Math.Min(charges.MaxCharges - charges.Charges, comp.Charges); + var count = Math.Min(charges.MaxCharges - current, comp.Charges); if (count <= 0) { _popup.PopupClient(Loc.GetString("rcd-ammo-component-after-interact-full"), target, user); @@ -61,7 +62,7 @@ private void OnAfterInteract(EntityUid uid, RCDAmmoComponent comp, AfterInteract } _popup.PopupClient(Loc.GetString("rcd-ammo-component-after-interact-refilled"), target, user); - _charges.AddCharges(target, count, charges); + _sharedCharges.AddCharges(target, count); comp.Charges -= count; Dirty(uid, comp); diff --git a/Content.Shared/RCD/Systems/RCDSystem.cs b/Content.Shared/RCD/Systems/RCDSystem.cs index 8875cb43972..114c4f539af 100644 --- a/Content.Shared/RCD/Systems/RCDSystem.cs +++ b/Content.Shared/RCD/Systems/RCDSystem.cs @@ -24,7 +24,6 @@ using Content.Shared._NF.Shipyard.Components; using Content.Shared.Access.Components; using Content.Shared.Administration.Logs; -using Content.Shared.Charges.Components; using Content.Shared.Charges.Systems; using Content.Shared.Construction; using Content.Shared.Database; @@ -49,6 +48,7 @@ using Robust.Shared.Prototypes; using Robust.Shared.Serialization; using Robust.Shared.Timing; + // Frontier namespace Content.Shared.RCD.Systems; @@ -62,7 +62,7 @@ public class RCDSystem : EntitySystem [Dependency] private readonly ITileDefinitionManager _tileDefMan = default!; [Dependency] private readonly FloorTileSystem _floors = default!; [Dependency] private readonly SharedAudioSystem _audio = default!; - [Dependency] private readonly SharedChargesSystem _charges = default!; + [Dependency] private readonly SharedChargesSystem _sharedCharges = default!; [Dependency] private readonly SharedDoAfterSystem _doAfter = default!; [Dependency] private readonly SharedInteractionSystem _interaction = default!; [Dependency] private readonly SharedPopupSystem _popup = default!; @@ -387,7 +387,7 @@ private void OnDoAfter(EntityUid uid, RCDComponent component, RCDDoAfterEvent ar // Play audio and consume charges _audio.PlayPredicted(component.SuccessSound, uid, args.User); - _charges.UseCharges(uid, args.Cost); + _sharedCharges.AddCharges(uid, -args.Cost); } private void OnRCDconstructionGhostRotationEvent(RCDConstructionGhostRotationEvent ev, EntitySessionEventArgs session) @@ -419,11 +419,13 @@ public bool IsRCDOperationStillValid(EntityUid uid, RCDComponent component, MapG // Update cached prototype if required UpdateCachedPrototype(uid, component); + var prototype = _protoManager.Index(component.ProtoId); + // Check that the RCD has enough ammo to get the job done - TryComp(uid, out var charges); + var charges = _sharedCharges.GetCurrentCharges(uid); // Both of these were messages were suppose to be predicted, but HasInsufficientCharges wasn't being checked on the client for some reason? - if (_charges.IsEmpty(uid, charges)) + if (charges == 0) { if (popMsgs) _popup.PopupClient(Loc.GetString("rcd-component-no-ammo-message"), uid, user); @@ -431,7 +433,7 @@ public bool IsRCDOperationStillValid(EntityUid uid, RCDComponent component, MapG return false; } - if (_charges.HasInsufficientCharges(uid, component.CachedPrototype.Cost, charges)) + if (prototype.Cost > charges) { if (popMsgs) _popup.PopupClient(Loc.GetString("rcd-component-insufficient-ammo-message"), uid, user); diff --git a/Content.Shared/SprayPainter/Components/PaintableAirlockComponent.cs b/Content.Shared/SprayPainter/Components/PaintableAirlockComponent.cs deleted file mode 100644 index fdd0aeeb7f9..00000000000 --- a/Content.Shared/SprayPainter/Components/PaintableAirlockComponent.cs +++ /dev/null @@ -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 -{ - /// - /// Group of styles this airlock can be painted with, e.g. glass, standard or external. - /// - [DataField(required: true), AutoNetworkedField] - public ProtoId Group = string.Empty; - - /// - /// Department this airlock is painted as, or none. - /// Must be specified in prototypes for turf war to work. - /// To better catch any mistakes, you need to explicitly state a non-styled airlock has a null department. - /// - [DataField(required: true), AutoNetworkedField] - public ProtoId? Department; -} diff --git a/Content.Shared/SprayPainter/Components/PaintableComponent.cs b/Content.Shared/SprayPainter/Components/PaintableComponent.cs new file mode 100644 index 00000000000..cfcb6a6c63c --- /dev/null +++ b/Content.Shared/SprayPainter/Components/PaintableComponent.cs @@ -0,0 +1,19 @@ +using Content.Shared.SprayPainter.Prototypes; +using Robust.Shared.GameStates; +using Robust.Shared.Prototypes; + +namespace Content.Shared.SprayPainter.Components; + +/// +/// Marks objects that can be painted with the spray painter. +/// +[RegisterComponent, NetworkedComponent] +public sealed partial class PaintableComponent : Component +{ + /// + /// Group of styles this airlock can be painted with, e.g. glass, standard or external. + /// Set to null to make an entity unpaintable. + /// + [DataField(required: true)] + public ProtoId? Group; +} diff --git a/Content.Shared/SprayPainter/Components/PaintedComponent.cs b/Content.Shared/SprayPainter/Components/PaintedComponent.cs new file mode 100644 index 00000000000..83f0e6e692c --- /dev/null +++ b/Content.Shared/SprayPainter/Components/PaintedComponent.cs @@ -0,0 +1,18 @@ +using Robust.Shared.GameStates; +using Robust.Shared.Serialization.TypeSerializers.Implementations.Custom; + +namespace Content.Shared.SprayPainter.Components; + +/// +/// Used to mark an entity that has been repainted. +/// +[RegisterComponent, NetworkedComponent] +[AutoGenerateComponentState, AutoGenerateComponentPause] +public sealed partial class PaintedComponent : Component +{ + /// + /// The time after which the entity is dried and does not appear as "freshly painted". + /// + [DataField(customTypeSerializer: typeof(TimeOffsetSerializer)), AutoNetworkedField, AutoPausedField] + public TimeSpan DryTime; +} diff --git a/Content.Shared/SprayPainter/Components/SprayPainterAmmo.cs b/Content.Shared/SprayPainter/Components/SprayPainterAmmo.cs new file mode 100644 index 00000000000..d869c96d31c --- /dev/null +++ b/Content.Shared/SprayPainter/Components/SprayPainterAmmo.cs @@ -0,0 +1,17 @@ +using Robust.Shared.GameStates; + +namespace Content.Shared.SprayPainter.Components; + +/// +/// Items with this component can be used to recharge a spray painter. +/// +[RegisterComponent, NetworkedComponent, AutoGenerateComponentState] +[Access(typeof(SprayPainterAmmoSystem))] +public sealed partial class SprayPainterAmmoComponent : Component +{ + /// + /// The value by which the charge in the spray painter will be recharged. + /// + [DataField, AutoNetworkedField] + public int Charges = 15; +} diff --git a/Content.Shared/SprayPainter/Components/SprayPainterComponent.cs b/Content.Shared/SprayPainter/Components/SprayPainterComponent.cs index 0591cb2dcbd..5485870766d 100644 --- a/Content.Shared/SprayPainter/Components/SprayPainterComponent.cs +++ b/Content.Shared/SprayPainter/Components/SprayPainterComponent.cs @@ -1,26 +1,42 @@ -using Content.Shared.DoAfter; +using Content.Shared.Decals; using Robust.Shared.Audio; using Robust.Shared.GameStates; +using Robust.Shared.Prototypes; namespace Content.Shared.SprayPainter.Components; -[RegisterComponent, NetworkedComponent, AutoGenerateComponentState] +/// +/// Denotes an object that can be used to alter the appearance of paintable objects (e.g. doors, gas canisters). +/// +[RegisterComponent, NetworkedComponent, AutoGenerateComponentState(true)] public sealed partial class SprayPainterComponent : Component { + public const string DefaultPickedColor = "red"; + public static readonly ProtoId DefaultDecal = "Arrows"; + + /// + /// The sound to be played after painting the entities. + /// [DataField] public SoundSpecifier SpraySound = new SoundPathSpecifier("/Audio/Effects/spray2.ogg"); + /// + /// The amount of time it takes to paint a pipe. + /// [DataField] - public TimeSpan AirlockSprayTime = TimeSpan.FromSeconds(3); + public TimeSpan PipeSprayTime = TimeSpan.FromSeconds(1); + /// + /// The cost of spray painting a pipe, in charges. + /// [DataField] - public TimeSpan PipeSprayTime = TimeSpan.FromSeconds(1); + public int PipeChargeCost = 1; /// /// Pipe color chosen to spray with. /// [DataField, AutoNetworkedField] - public string? PickedColor; + public string PickedColor = DefaultPickedColor; /// /// Pipe colors that can be selected. @@ -29,9 +45,82 @@ public sealed partial class SprayPainterComponent : Component public Dictionary ColorPalette = new(); /// - /// Airlock style index selected. - /// After prototype reload this might not be the same style but it will never be out of bounds. + /// Spray paintable object styles selected per object. + /// + [DataField, AutoNetworkedField] + public Dictionary StylesByGroup = new(); + + /// + /// The currently open tab of the painter + /// (Are you selecting canister color?) + /// + [DataField, AutoNetworkedField] + public int SelectedTab; + + /// + /// Whether or not the painter should be painting or removing decals when clicked. + /// + [DataField, AutoNetworkedField] + public DecalPaintMode DecalMode = DecalPaintMode.Off; + + /// + /// The currently selected decal prototype. + /// + [DataField, AutoNetworkedField] + public ProtoId SelectedDecal = DefaultDecal; + + /// + /// The color in which to paint the decal. + /// + [DataField, AutoNetworkedField] + public Color? SelectedDecalColor; + + /// + /// The angle at which to paint the decal. /// [DataField, AutoNetworkedField] - public int Index; + public int SelectedDecalAngle; + + /// + /// The angle at which to paint the decal. + /// + [DataField, AutoNetworkedField] + public bool SnapDecals = true; + + /// + /// The cost of spray painting a decal, in charges. + /// + [DataField] + public int DecalChargeCost = 1; + + /// + /// How long does the painter leave items as freshly painted? + /// + [DataField] + public TimeSpan FreshPaintDuration = TimeSpan.FromMinutes(15); + + /// + /// The sound to play when swapping between decal modes. + /// + [DataField] + public SoundSpecifier SoundSwitchDecalMode = new SoundPathSpecifier("/Audio/Machines/quickbeep.ogg", AudioParams.Default.WithVolume(1.5f)); +} + +/// +/// A set of operating modes for decal painting. +/// +public enum DecalPaintMode : byte +{ + /// + /// Clicking on the floor does nothing. + /// + Off = 0, + /// + /// Clicking on the floor adds a decal at the requested spot (or snapped to the grid) + /// + Add = 1, + /// + /// Clicking on the floor removes all decals at the requested spot (or snapped to the grid) + /// + Remove = 2, } diff --git a/Content.Shared/SprayPainter/Prototypes/AirlockDepartmentsPrototype.cs b/Content.Shared/SprayPainter/Prototypes/AirlockDepartmentsPrototype.cs deleted file mode 100644 index b61aa037cc9..00000000000 --- a/Content.Shared/SprayPainter/Prototypes/AirlockDepartmentsPrototype.cs +++ /dev/null @@ -1,21 +0,0 @@ -using Content.Shared.Roles; -using Robust.Shared.Prototypes; - -namespace Content.Shared.SprayPainter.Prototypes; - -/// -/// Maps airlock style names to department ids. -/// -[Prototype("airlockDepartments")] -public sealed partial class AirlockDepartmentsPrototype : IPrototype -{ - [IdDataField] - public string ID { get; private set; } = default!; - - /// - /// Dictionary of style names to department ids. - /// If a style does not have a department (e.g. external) it is set to null. - /// - [DataField(required: true)] - public Dictionary> Departments = new(); -} diff --git a/Content.Shared/SprayPainter/Prototypes/AirlockGroupPrototype.cs b/Content.Shared/SprayPainter/Prototypes/AirlockGroupPrototype.cs deleted file mode 100644 index 24c28b8b7a7..00000000000 --- a/Content.Shared/SprayPainter/Prototypes/AirlockGroupPrototype.cs +++ /dev/null @@ -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 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; -} diff --git a/Content.Shared/SprayPainter/Prototypes/PaintableGroupCategoryPrototype.cs b/Content.Shared/SprayPainter/Prototypes/PaintableGroupCategoryPrototype.cs new file mode 100644 index 00000000000..ba6423d94a5 --- /dev/null +++ b/Content.Shared/SprayPainter/Prototypes/PaintableGroupCategoryPrototype.cs @@ -0,0 +1,19 @@ +using Robust.Shared.Prototypes; + +namespace Content.Shared.SprayPainter.Prototypes; + +/// +/// A category of spray paintable items (e.g. airlocks, crates) +/// +[Prototype] +public sealed partial class PaintableGroupCategoryPrototype : IPrototype +{ + [IdDataField] + public string ID { get; private set; } = default!; + + /// + /// Each group that makes up this category. + /// + [DataField(required: true)] + public List> Groups = new(); +} diff --git a/Content.Shared/SprayPainter/Prototypes/PaintableGroupPrototype.cs b/Content.Shared/SprayPainter/Prototypes/PaintableGroupPrototype.cs new file mode 100644 index 00000000000..73944c4e6e3 --- /dev/null +++ b/Content.Shared/SprayPainter/Prototypes/PaintableGroupPrototype.cs @@ -0,0 +1,53 @@ +using Robust.Shared.Prototypes; +using Robust.Shared.Serialization; + +namespace Content.Shared.SprayPainter.Prototypes; + +/// +/// Contains a map of the objects from which the spray painter will take texture to paint another from the same group. +/// +[Prototype] +public sealed partial class PaintableGroupPrototype : IPrototype +{ + [IdDataField] + public string ID { get; private set; } = default!; + + /// + /// The time required to paint an object from a given group, in seconds. + /// + [DataField] + public float Time = 2.0f; + + /// + /// To number of charges needed to paint an object of this group. + /// + [DataField] + public int Cost = 1; + + /// + /// The default style to start painting. + /// + [DataField(required: true)] + public string DefaultStyle = default!; + + /// + /// Map from localization keys and entity identifiers displayed in the spray painter menu. + /// + [DataField(required: true)] + public Dictionary Styles = new(); + + /// + /// If multiple groups have the same key, the group with the highest IconPriority has its icon displayed. + /// + [DataField] + public int IconPriority; +} + +[Serializable, NetSerializable] +public enum PaintableVisuals +{ + /// + /// The prototype to base the object's visuals off. + /// + Prototype +} diff --git a/Content.Shared/SprayPainter/SharedSprayPainterSystem.cs b/Content.Shared/SprayPainter/SharedSprayPainterSystem.cs index 48a941d598f..266e3f74d6f 100644 --- a/Content.Shared/SprayPainter/SharedSprayPainterSystem.cs +++ b/Content.Shared/SprayPainter/SharedSprayPainterSystem.cs @@ -1,115 +1,177 @@ +using System.Linq; using Content.Shared.Administration.Logs; +using Content.Shared.Charges.Components; +using Content.Shared.Charges.Systems; using Content.Shared.Database; using Content.Shared.DoAfter; -using Content.Shared.Doors.Components; +using Content.Shared.Examine; using Content.Shared.Interaction; using Content.Shared.Popups; using Content.Shared.SprayPainter.Components; using Content.Shared.SprayPainter.Prototypes; +using Content.Shared.Verbs; using Robust.Shared.Audio.Systems; using Robust.Shared.Prototypes; -using System.Linq; +using Robust.Shared.Timing; +using Robust.Shared.Utility; namespace Content.Shared.SprayPainter; /// -/// 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. /// public abstract class SharedSprayPainterSystem : EntitySystem { + [Dependency] private readonly IGameTiming _timing = 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 SharedAudioSystem Audio = default!; + [Dependency] protected readonly SharedChargesSystem Charges = default!; [Dependency] protected readonly SharedDoAfterSystem DoAfter = default!; - [Dependency] private readonly SharedPopupSystem _popup = default!; - - public List Styles { get; private set; } = new(); - public List Groups { get; private set; } = new(); - - [ValidatePrototypeId] - private const string Departments = "Departments"; + [Dependency] private readonly SharedPopupSystem _popup = default!; public override void Initialize() { base.Initialize(); - CacheStyles(); - SubscribeLocalEvent(OnMapInit); - SubscribeLocalEvent(OnDoorDoAfter); - Subs.BuiEvents(SprayPainterUiKey.Key, subs => - { - subs.Event(OnSpritePicked); - subs.Event(OnColorPicked); - }); - SubscribeLocalEvent(OnAirlockInteract); + SubscribeLocalEvent(OnPainterDoAfter); + SubscribeLocalEvent>(OnPainterGetAltVerbs); + SubscribeLocalEvent(OnPaintableInteract); + SubscribeLocalEvent(OnPainedExamined); - SubscribeLocalEvent(OnPrototypesReloaded); + Subs.BuiEvents(SprayPainterUiKey.Key, + subs => + { + subs.Event(OnSetPaintable); + subs.Event(OnSetPipeColor); + subs.Event(OnTabChanged); + subs.Event(OnSetDecal); + subs.Event(OnSetDecalColor); + subs.Event(OnSetDecalAngle); + subs.Event(OnSetDecalSnap); + }); } private void OnMapInit(Entity ent, ref MapInitEvent args) { - if (ent.Comp.ColorPalette.Count == 0) + bool stylesByGroupPopulated = false; + foreach (var groupProto in Proto.EnumeratePrototypes()) + { + ent.Comp.StylesByGroup[groupProto.ID] = groupProto.DefaultStyle; + stylesByGroupPopulated = true; + } + if (stylesByGroupPopulated) + Dirty(ent); + + if (ent.Comp.ColorPalette.Count > 0) + SetPipeColor(ent, ent.Comp.ColorPalette.First().Key); + } + + private void SetPipeColor(Entity ent, string? paletteKey) + { + if (paletteKey == null || paletteKey == ent.Comp.PickedColor) + return; + + if (!ent.Comp.ColorPalette.ContainsKey(paletteKey)) return; - SetColor(ent, ent.Comp.ColorPalette.First().Key); + ent.Comp.PickedColor = paletteKey; + Dirty(ent); + UpdateUi(ent); } - private void OnDoorDoAfter(Entity ent, ref SprayPainterDoorDoAfterEvent args) + #region Interaction + + private void OnPainterDoAfter(Entity ent, ref SprayPainterDoAfterEvent args) { if (args.Handled || args.Cancelled) return; - if (args.Args.Target is not {} target) + if (args.Args.Target is not { } target) return; - if (!TryComp(target, out var airlock)) + if (!HasComp(target)) return; - airlock.Department = args.Department; - Dirty(target, airlock); - + Appearance.SetData(target, PaintableVisuals.Prototype, args.Prototype); 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}"); + Charges.TryUseCharges(new Entity(ent, EnsureComp(ent)), args.Cost); - args.Handled = true; - } + var paintedComponent = EnsureComp(target); + paintedComponent.DryTime = _timing.CurTime + ent.Comp.FreshPaintDuration; + Dirty(target, paintedComponent); - #region UI messages + var ev = new EntityPaintedEvent( + User: args.User, + Tool: ent, + Prototype: args.Prototype, + Group: args.Group); + RaiseLocalEvent(target, ref ev); - private void OnColorPicked(Entity ent, ref SprayPainterColorPickedMessage args) - { - SetColor(ent, args.Key); + AdminLogger.Add(LogType.Action, + LogImpact.Low, + $"{ToPrettyString(args.Args.User):user} painted {ToPrettyString(args.Args.Target.Value):target}"); + + args.Handled = true; } - private void OnSpritePicked(Entity ent, ref SprayPainterSpritePickedMessage args) + private void OnPainterGetAltVerbs(Entity ent, ref GetVerbsEvent args) { - if (args.Index >= Styles.Count) + if (!args.CanAccess || !args.CanInteract || !args.Using.HasValue) return; - ent.Comp.Index = args.Index; - Dirty(ent, ent.Comp); + 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); } - private void SetColor(Entity ent, string? paletteKey) + /// + /// Toggles whether clicking on the floor paints a decal or not. + /// + private void TogglePaintDecals(Entity ent, EntityUid user) { - if (paletteKey == null || paletteKey == ent.Comp.PickedColor) + if (!_timing.IsFirstTimePredicted) return; - if (!ent.Comp.ColorPalette.ContainsKey(paletteKey)) - 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); - ent.Comp.PickedColor = paletteKey; - Dirty(ent, ent.Comp); + // Make the machine beep. + Audio.PlayPredicted(ent.Comp.SoundSwitchDecalMode, ent, user, ent.Comp.SoundSwitchDecalMode.Params.WithPitchScale(pitch)); } - #endregion - - private void OnAirlockInteract(Entity ent, ref InteractUsingEvent args) + /// + /// 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. + /// + private void OnPaintableInteract(Entity ent, ref InteractUsingEvent args) { if (args.Handled) return; @@ -117,79 +179,140 @@ private void OnAirlockInteract(Entity ent, ref Intera if (!TryComp(args.Used, out var painter)) return; - var group = Proto.Index(ent.Comp.Group); + if (ent.Comp.Group is not { } group + || !painter.StylesByGroup.TryGetValue(group, out var selectedStyle) + || !Proto.TryIndex(group, out PaintableGroupPrototype? targetGroup)) + return; + + // Valid paint target. + args.Handled = true; + + if (TryComp(args.Used, out var charges) + && charges.LastCharges < targetGroup.Cost) + { + var msg = Loc.GetString("spray-painter-interact-no-charges"); + _popup.PopupClient(msg, args.User, args.User); + return; + } - var style = Styles[painter.Index]; - if (!group.StylePaths.TryGetValue(style.Name, out var sprite)) + if (!targetGroup.Styles.TryGetValue(selectedStyle, out var proto)) { - string msg = Loc.GetString("spray-painter-style-not-available"); + var msg = Loc.GetString("spray-painter-style-not-available"); _popup.PopupClient(msg, args.User, args.User); return; } - var doAfterEventArgs = new DoAfterArgs(EntityManager, args.User, painter.AirlockSprayTime, new SprayPainterDoorDoAfterEvent(sprite, style.Department), args.Used, target: ent, used: args.Used) + var doAfterEventArgs = new DoAfterArgs(EntityManager, + args.User, + targetGroup.Time, + new SprayPainterDoAfterEvent(proto, group, targetGroup.Cost), + args.Used, + target: ent, + used: args.Used) { BreakOnMove = true, BreakOnDamage = true, NeedHand = true, }; - if (!DoAfter.TryStartDoAfter(doAfterEventArgs, out var id)) - return; - args.Handled = true; + if (!DoAfter.TryStartDoAfter(doAfterEventArgs, out _)) + return; // 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}"); + } + + /// + /// Prints out if an object has been painted recently. + /// + private void OnPainedExamined(Entity ent, ref ExaminedEvent args) + { + // If the paint's dried, it isn't detectable. + if (_timing.CurTime > ent.Comp.DryTime) + return; + + args.PushText(Loc.GetString("spray-painter-on-examined-painted-message")); } - #region Style caching + #endregion Interaction - private void OnPrototypesReloaded(PrototypesReloadedEventArgs args) + #region UI + + /// + /// Sets the style that a particular type of paintable object (e.g. lockers) should be painted in. + /// + private void OnSetPaintable(Entity ent, ref SprayPainterSetPaintableStyleMessage args) { - if (!args.WasModified() && !args.WasModified()) + if (!ent.Comp.StylesByGroup.ContainsKey(args.Group)) return; - Styles.Clear(); - Groups.Clear(); - CacheStyles(); + ent.Comp.StylesByGroup[args.Group] = args.Style; + Dirty(ent); + UpdateUi(ent); + } - // style index might be invalid now so check them all - var max = Styles.Count - 1; - var query = AllEntityQuery(); - while (query.MoveNext(out var uid, out var comp)) - { - if (comp.Index > max) - { - comp.Index = max; - Dirty(uid, comp); - } - } + /// + /// Changes the color to paint pipes in. + /// + private void OnSetPipeColor(Entity ent, ref SprayPainterSetPipeColorMessage args) + { + SetPipeColor(ent, args.Key); } - protected virtual void CacheStyles() + /// + /// Tracks the tab the spray painter was on. + /// + private void OnTabChanged(Entity ent, ref SprayPainterTabChangedMessage args) { - // collect every style's name - var names = new SortedSet(); - foreach (var group in Proto.EnumeratePrototypes()) - { - Groups.Add(group); - foreach (var style in group.StylePaths.Keys) - { - names.Add(style); - } - } + ent.Comp.SelectedTab = args.Index; + Dirty(ent); + } - // get their department ids too for the final style list - var departments = Proto.Index(Departments); - Styles.Capacity = names.Count; - foreach (var name in names) - { - departments.Departments.TryGetValue(name, out var department); - Styles.Add(new AirlockStyle(name, department)); - } + /// + /// Sets the decal prototype to paint. + /// + private void OnSetDecal(Entity ent, ref SprayPainterSetDecalMessage args) + { + ent.Comp.SelectedDecal = args.DecalPrototype; + Dirty(ent); + UpdateUi(ent); + } + + /// + /// Sets the angle to paint decals at. + /// + private void OnSetDecalAngle(Entity ent, ref SprayPainterSetDecalAngleMessage args) + { + ent.Comp.SelectedDecalAngle = args.Angle; + Dirty(ent); + UpdateUi(ent); + } + + /// + /// Enables or disables snap-to-grid when painting decals. + /// + private void OnSetDecalSnap(Entity ent, ref SprayPainterSetDecalSnapMessage args) + { + ent.Comp.SnapDecals = args.Snap; + Dirty(ent); + UpdateUi(ent); + } + + /// + /// Sets the decal to paint on the ground. + /// + private void OnSetDecalColor(Entity ent, ref SprayPainterSetDecalColorMessage args) + { + ent.Comp.SelectedDecalColor = args.Color; + Dirty(ent); + UpdateUi(ent); + } + + protected virtual void UpdateUi(Entity ent) + { } #endregion } - -public record struct AirlockStyle(string Name, string? Department); diff --git a/Content.Shared/SprayPainter/SprayPainterAmmoSystem.cs b/Content.Shared/SprayPainter/SprayPainterAmmoSystem.cs new file mode 100644 index 00000000000..d43420efc52 --- /dev/null +++ b/Content.Shared/SprayPainter/SprayPainterAmmoSystem.cs @@ -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; + +/// +/// The system handles interactions with spray painter ammo. +/// +public sealed class SprayPainterAmmoSystem : EntitySystem +{ + [Dependency] private readonly SharedChargesSystem _charges = default!; + [Dependency] private readonly SharedPopupSystem _popup = default!; + + public override void Initialize() + { + base.Initialize(); + + SubscribeLocalEvent(OnExamine); + SubscribeLocalEvent(OnAfterInteract); + } + + private void OnAfterInteract(Entity ent, ref AfterInteractEvent args) + { + if (args.Handled || !args.CanReach) + return; + + if (args.Target is not { Valid: true } target || + !HasComp(target) || + !TryComp(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 ent, ref ExaminedEvent args) + { + if (!args.IsInDetailsRange) + return; + + var examineMessage = Loc.GetString("rcd-ammo-component-on-examine", ("charges", ent.Comp.Charges)); + args.PushText(examineMessage); + } +} diff --git a/Content.Shared/SprayPainter/SprayPainterEvents.cs b/Content.Shared/SprayPainter/SprayPainterEvents.cs index b88b054ad14..db9de9c2787 100644 --- a/Content.Shared/SprayPainter/SprayPainterEvents.cs +++ b/Content.Shared/SprayPainter/SprayPainterEvents.cs @@ -1,4 +1,7 @@ +using Content.Shared.Decals; using Content.Shared.DoAfter; +using Content.Shared.SprayPainter.Prototypes; +using Robust.Shared.Prototypes; using Robust.Shared.Serialization; namespace Content.Shared.SprayPainter; @@ -10,46 +13,75 @@ public enum SprayPainterUiKey } [Serializable, NetSerializable] -public sealed class SprayPainterSpritePickedMessage : BoundUserInterfaceMessage +public sealed class SprayPainterSetDecalMessage(ProtoId protoId) : BoundUserInterfaceMessage { - public readonly int Index; + public ProtoId DecalPrototype = protoId; +} - public SprayPainterSpritePickedMessage(int index) - { - Index = index; - } +[Serializable, NetSerializable] +public sealed class SprayPainterSetDecalColorMessage(Color? color) : BoundUserInterfaceMessage +{ + public Color? Color = color; } [Serializable, NetSerializable] -public sealed class SprayPainterColorPickedMessage : BoundUserInterfaceMessage +public sealed class SprayPainterSetDecalSnapMessage(bool snap) : BoundUserInterfaceMessage { - public readonly string? Key; + public bool Snap = snap; +} - public SprayPainterColorPickedMessage(string? key) - { - Key = key; - } +[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 partial class SprayPainterDoorDoAfterEvent : DoAfterEvent +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 +{ + /// + /// The prototype to use to repaint this object. + /// + [DataField] + public string Prototype; + /// - /// Base RSI path to set for the door sprite. + /// The group ID of the object being painted. /// [DataField] - public string Sprite; + public string Group; /// - /// Department id to set for the door, if the style has one. + /// The cost, in charges, to paint this object. /// [DataField] - public string? Department; + public int Cost; - public SprayPainterDoorDoAfterEvent(string sprite, string? department) + public SprayPainterDoAfterEvent(string prototype, string group, int cost) { - Sprite = sprite; - Department = department; + Prototype = prototype; + Group = group; + Cost = cost; } public override DoAfterEvent Clone() => this; @@ -71,3 +103,17 @@ public SprayPainterPipeDoAfterEvent(Color color) public override DoAfterEvent Clone() => this; } + +/// +/// An action raised on an entity when it is spray painted. +/// +/// The entity painting this item. +/// The entity used to paint this item. +/// The prototype used to generate the new painted appearance. +/// The group of the entity being painted (e.g. airlocks with glass, canisters). +[ByRefEvent] +public partial record struct EntityPaintedEvent( + EntityUid? User, + EntityUid Tool, + EntProtoId Prototype, + ProtoId Group); diff --git a/Content.Shared/_DV/Abilities/CoughingUpItemComponent.cs b/Content.Shared/_DV/Abilities/CoughingUpItemComponent.cs new file mode 100644 index 00000000000..07a4fa995ff --- /dev/null +++ b/Content.Shared/_DV/Abilities/CoughingUpItemComponent.cs @@ -0,0 +1,19 @@ +using Robust.Shared.GameStates; +using Robust.Shared.Serialization.TypeSerializers.Implementations.Custom; + +namespace Content.Shared._DV.Abilities; + +/// +/// Spawns the item from after the coughing sound is finished. +/// +/// +/// Client doesn't care about spawning so the field isn't networked. +/// +[RegisterComponent, NetworkedComponent, Access(typeof(ItemCougherSystem))] +[AutoGenerateComponentPause] +public sealed partial class CoughingUpItemComponent : Component +{ + [DataField(customTypeSerializer: typeof(TimeOffsetSerializer))] + [AutoPausedField] + public TimeSpan NextCough; +} diff --git a/Content.Shared/_DV/Abilities/ItemCougherComponent.cs b/Content.Shared/_DV/Abilities/ItemCougherComponent.cs new file mode 100644 index 00000000000..74936f5232b --- /dev/null +++ b/Content.Shared/_DV/Abilities/ItemCougherComponent.cs @@ -0,0 +1,48 @@ +using Robust.Shared.Audio; +using Robust.Shared.GameStates; +using Robust.Shared.Prototypes; + +namespace Content.Shared._DV.Abilities; + +/// +/// Adds an action to cough up an item. +/// Other systems can enable this action when their conditions are met. +/// +[RegisterComponent, NetworkedComponent, Access(typeof(ItemCougherSystem))] +public sealed partial class ItemCougherComponent : Component +{ + /// + /// The item to spawn after the coughing sound plays. + /// + [DataField(required: true)] + public EntProtoId Item; + + /// + /// The action to give the player. + /// + [DataField(required: true)] + public EntProtoId Action; + + [DataField] + public EntityUid? ActionEntity; + + /// + /// Popup to show to everyone when coughing up an item. + /// Gets "name" passed as the identity of the mob. + /// + [DataField(required: true)] + public LocId CoughPopup; + + /// + /// Sound played + /// The sound length controls how long it takes for the item to spawn. + /// + [DataField] + public SoundSpecifier Sound = new SoundPathSpecifier("/Audio/Animals/cat_hiss.ogg") + { + Params = new AudioParams + { + Variation = 0.15f + } + }; +} diff --git a/Content.Shared/_DV/Abilities/ItemCougherSystem.cs b/Content.Shared/_DV/Abilities/ItemCougherSystem.cs new file mode 100644 index 00000000000..2cae7d26275 --- /dev/null +++ b/Content.Shared/_DV/Abilities/ItemCougherSystem.cs @@ -0,0 +1,112 @@ +using Content.Shared.Actions; +using Content.Shared.Clothing.Components; +using Content.Shared.IdentityManagement; +using Content.Shared.Inventory; +using Content.Shared.Popups; +using Robust.Shared.Audio.Systems; +using Robust.Shared.Network; +using Robust.Shared.Timing; + +namespace Content.Shared._DV.Abilities; + +public sealed class ItemCougherSystem : EntitySystem +{ + [Dependency] private readonly IGameTiming _timing = default!; + [Dependency] private readonly INetManager _net = default!; + [Dependency] private readonly InventorySystem _inventory = default!; + [Dependency] private readonly SharedActionsSystem _actions = default!; + [Dependency] private readonly SharedAudioSystem _audio = default!; + [Dependency] private readonly SharedPopupSystem _popup = default!; + + private EntityQuery _query; + + public override void Initialize() + { + base.Initialize(); + + _query = GetEntityQuery(); + + SubscribeLocalEvent(OnMapInit); + SubscribeLocalEvent(OnCoughItemAction); + } + + public override void Update(float frameTime) + { + base.Update(frameTime); + + if (_net.IsClient) + return; + + var query = EntityQueryEnumerator(); + while (query.MoveNext(out var uid, out var coughing, out var comp, out var xform)) + { + if (_timing.CurTime < coughing.NextCough) + continue; + + var spawned = Spawn(comp.Item, xform.Coordinates); + RemCompDeferred(uid, coughing); + + var ev = new ItemCoughedUpEvent(spawned); + RaiseLocalEvent(uid, ref ev); + } + } + + private void OnMapInit(Entity ent, ref MapInitEvent args) + { + if (ent.Comp.ActionEntity != null) + return; + + _actions.AddAction(ent, ref ent.Comp.ActionEntity, ent.Comp.Action); + } + + private void OnCoughItemAction(Entity ent, ref CoughItemActionEvent args) + { + if (_inventory.TryGetSlotEntity(ent, "mask", out var maskUid) && + TryComp(maskUid, out var mask) && + !mask.IsToggled) + { + _popup.PopupClient(Loc.GetString("item-cougher-mask", ("mask", maskUid)), ent, ent); + return; + } + + var msg = Loc.GetString(ent.Comp.CoughPopup, ("name", Identity.Entity(ent, EntityManager))); + _popup.PopupPredicted(msg, ent, ent); + _audio.PlayPredicted(ent.Comp.Sound, ent, ent); + + var path = _audio.ResolveSound(ent.Comp.Sound); // Frontier: resolve sound + var coughing = EnsureComp(ent); + coughing.NextCough = _timing.CurTime + _audio.GetAudioLength(path); + args.Handled = true; + + // disable it until another system calls EnableAction + SetActionEnabled((ent, ent.Comp), false); + } + + /// + /// Enables the coughing action. + /// Other systems have to call this, this is not used internally. + /// + public void EnableAction(Entity ent) + { + SetActionEnabled(ent, true); + } + + public void SetActionEnabled(Entity ent, bool enabled) + { + if (!_query.Resolve(ent, ref ent.Comp) || ent.Comp.ActionEntity is not {} action) + return; + + _actions.SetEnabled(action, enabled); + } +} + +/// +/// Raised on the mob after it coughs up an item. +/// +[ByRefEvent] +public record struct ItemCoughedUpEvent(EntityUid Item); + +/// +/// Action event that must use. +/// +public sealed partial class CoughItemActionEvent : InstantActionEvent; diff --git a/Content.Shared/_DV/Felinid/FelinidComponent.cs b/Content.Shared/_DV/Felinid/FelinidComponent.cs new file mode 100644 index 00000000000..2aa6f8994a5 --- /dev/null +++ b/Content.Shared/_DV/Felinid/FelinidComponent.cs @@ -0,0 +1,20 @@ +using Content.Shared.FixedPoint; +using Robust.Shared.GameStates; + +namespace Content.Shared._DV.Abilities.Felinid; + +/// +/// Felenid god component controls 3 things: +/// 1. When you use to cough up a hairball, it purges chemicals from your bloodstream. +/// 2. Enables the cough hairball action after eating a mouse with FelinidFoodComponent. +/// 3. Full immunity to hairball vomiting chance. +/// +[RegisterComponent, NetworkedComponent, Access(typeof(SharedFelinidSystem))] +public sealed partial class FelinidComponent : Component +{ + /// + /// Quantity of reagents to purge from the bloodstream. + /// + [DataField] + public FixedPoint2 PurgedQuantity = 20; +} diff --git a/Content.Shared/_DV/Felinid/FelinidFoodComponent.cs b/Content.Shared/_DV/Felinid/FelinidFoodComponent.cs new file mode 100644 index 00000000000..b81573dd1ce --- /dev/null +++ b/Content.Shared/_DV/Felinid/FelinidFoodComponent.cs @@ -0,0 +1,16 @@ +using Robust.Shared.GameStates; + +namespace Content.Shared._DV.Abilities.Felinid; + +/// +/// Makes this food let felinids cough up a hairball when eaten. +/// +[RegisterComponent, NetworkedComponent, Access(typeof(SharedFelinidSystem))] +public sealed partial class FelinidFoodComponent : Component +{ + /// + /// Extra hunger to satiate for felinids. + /// + [DataField] + public float BonusHunger = 50f; +} diff --git a/Content.Shared/_DV/Felinid/HairballComponent.cs b/Content.Shared/_DV/Felinid/HairballComponent.cs new file mode 100644 index 00000000000..fa8b4c0726f --- /dev/null +++ b/Content.Shared/_DV/Felinid/HairballComponent.cs @@ -0,0 +1,22 @@ +using Robust.Shared.GameStates; + +namespace Content.Shared._DV.Abilities.Felinid; + +/// +/// Causes players to randomly vomit when trying to pick this up, or when it gets thrown at them. +/// +[RegisterComponent, NetworkedComponent, Access(typeof(SharedFelinidSystem))] +public sealed partial class HairballComponent : Component +{ + /// + /// The solution to put purged chemicals into. + /// + [DataField] + public string SolutionName = "hairball"; + + /// + /// Probability of someone vomiting when picking it up or getting it thrown at them. + /// + [DataField] + public float VomitProb = 0.2f; +} diff --git a/Content.Shared/_DV/Felinid/SharedFelinidSystem.cs b/Content.Shared/_DV/Felinid/SharedFelinidSystem.cs new file mode 100644 index 00000000000..9652925f3c4 --- /dev/null +++ b/Content.Shared/_DV/Felinid/SharedFelinidSystem.cs @@ -0,0 +1,32 @@ +using Content.Shared.Nutrition; +using Content.Shared.Nutrition.Components; +using Content.Shared.Nutrition.EntitySystems; + +namespace Content.Shared._DV.Abilities.Felinid; + +/// +/// Makes eating enable a felinids hairball action. +/// Other interactions are in the server system. +/// +public abstract class SharedFelinidSystem : EntitySystem +{ + [Dependency] private readonly HungerSystem _hunger = default!; + [Dependency] private readonly ItemCougherSystem _cougher = default!; + + public override void Initialize() + { + base.Initialize(); + + SubscribeLocalEvent(OnMouseEaten); + } + + private void OnMouseEaten(Entity ent, ref BeforeFullyEatenEvent args) + { + var user = args.User; + if (!HasComp(user) || !TryComp(user, out var hunger)) + return; + + _hunger.ModifyHunger(user, ent.Comp.BonusHunger, hunger); + _cougher.EnableAction(user); + } +} diff --git a/Content.Shared/_Null/Systems/SharedMiningSystem.cs b/Content.Shared/_Null/Systems/SharedMiningSystem.cs new file mode 100644 index 00000000000..6f1c2a86bbd --- /dev/null +++ b/Content.Shared/_Null/Systems/SharedMiningSystem.cs @@ -0,0 +1,3 @@ +namespace Content.Shared._Null.Systems; + +public abstract class SharedMiningSystem : EntitySystem; diff --git a/Resources/Audio/Effects/spray2.ogg b/Resources/Audio/Effects/spray2.ogg index d184d76bd00..f1a04ddac83 100644 Binary files a/Resources/Audio/Effects/spray2.ogg and b/Resources/Audio/Effects/spray2.ogg differ diff --git a/Resources/Audio/MidiCustom/space-station-14.sf2 b/Resources/Audio/MidiCustom/space-station-14.sf2 index 54c6eb61963..758567bdbcc 100644 Binary files a/Resources/Audio/MidiCustom/space-station-14.sf2 and b/Resources/Audio/MidiCustom/space-station-14.sf2 differ diff --git a/Resources/Locale/en-US/_DV/abilities/item-cougher.ftl b/Resources/Locale/en-US/_DV/abilities/item-cougher.ftl new file mode 100644 index 00000000000..56d80b02f2f --- /dev/null +++ b/Resources/Locale/en-US/_DV/abilities/item-cougher.ftl @@ -0,0 +1 @@ +item-cougher-mask = Take off your {$mask} first. diff --git a/Resources/Locale/en-US/_Null/warper.ftl b/Resources/Locale/en-US/_Null/warper.ftl new file mode 100644 index 00000000000..3676106908d --- /dev/null +++ b/Resources/Locale/en-US/_Null/warper.ftl @@ -0,0 +1,7 @@ +warper-cancelled-sealed = This warper appears sealed, for now. +warper-cancelled-no-destination = This warper has no destination. +warper-cancelled-no-source = This warper has no ID that marks it a valid destination. +warper-cancelled-invalid-destination = This warper's destination is invalid. +warper-map-invalid = This warper leads to a harrowing void. Perhaps it's best I should not enter... +warper-on-examine-source = This is labelled ID "{$location}". +warper-on-examine-destination = This warper leads to location ID "{$location}". \ No newline at end of file diff --git a/Resources/Locale/en-US/engineer-painter/engineer-painter.ftl b/Resources/Locale/en-US/engineer-painter/engineer-painter.ftl deleted file mode 100644 index d3d3ccc4448..00000000000 --- a/Resources/Locale/en-US/engineer-painter/engineer-painter.ftl +++ /dev/null @@ -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 diff --git a/Resources/Locale/en-US/spray-painter/spray-painter.ftl b/Resources/Locale/en-US/spray-painter/spray-painter.ftl new file mode 100644 index 00000000000..dc54c5c8b8c --- /dev/null +++ b/Resources/Locale/en-US/spray-painter/spray-painter.ftl @@ -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 diff --git a/Resources/Prototypes/Actions/types.yml b/Resources/Prototypes/Actions/types.yml index 48a200c398c..acf0fbdf9bf 100644 --- a/Resources/Prototypes/Actions/types.yml +++ b/Resources/Prototypes/Actions/types.yml @@ -93,8 +93,9 @@ name: Break Free description: Activating your freedom implant will free you from any hand restraints components: + - type: LimitedCharges + maxCharges: 3 - type: InstantAction - charges: 3 checkCanInteract: false itemIconStyle: BigAction priority: -20 @@ -121,9 +122,10 @@ name: Activate EMP description: Triggers a small EMP pulse around you components: + - type: LimitedCharges + maxCharges: 3 - type: InstantAction checkCanInteract: false - charges: 3 useDelay: 5 itemIconStyle: BigAction priority: -20 @@ -137,9 +139,10 @@ name: SCRAM! description: Randomly teleports you within a large distance. components: + - type: LimitedCharges + maxCharges: 2 - type: InstantAction checkCanInteract: false - charges: 2 useDelay: 5 itemIconStyle: BigAction priority: -20 @@ -155,8 +158,9 @@ components: - type: ConfirmableAction popup: dna-scrambler-action-popup + - type: LimitedCharges + maxCharges: 1 - type: InstantAction - charges: 1 itemIconStyle: BigAction priority: -20 icon: diff --git a/Resources/Prototypes/Catalog/VendingMachines/Inventories/youtool.yml b/Resources/Prototypes/Catalog/VendingMachines/Inventories/youtool.yml index 0f16e366c09..ad041303dff 100644 --- a/Resources/Prototypes/Catalog/VendingMachines/Inventories/youtool.yml +++ b/Resources/Prototypes/Catalog/VendingMachines/Inventories/youtool.yml @@ -17,6 +17,7 @@ trayScanner: 15 GasAnalyzer: 15 SprayPainter: 15 + SprayPainterAmmo: 5 # Some engineer forgot to take the multitool out the youtool when working on it, happens. contrabandInventory: Multitool: 5 diff --git a/Resources/Prototypes/Entities/Objects/Consumable/Food/frozen.yml b/Resources/Prototypes/Entities/Objects/Consumable/Food/frozen.yml index 1551bde65cf..8b3f08ca695 100644 --- a/Resources/Prototypes/Entities/Objects/Consumable/Food/frozen.yml +++ b/Resources/Prototypes/Entities/Objects/Consumable/Food/frozen.yml @@ -293,3 +293,5 @@ - Trash - type: SpaceGarbage - type: GoblinPreciousTrash # Frontier + - type: StaticPrice + price: 1 \ No newline at end of file diff --git a/Resources/Prototypes/Entities/Objects/Misc/fluff_lights.yml b/Resources/Prototypes/Entities/Objects/Misc/fluff_lights.yml index 27888fea726..0f263772de2 100644 --- a/Resources/Prototypes/Entities/Objects/Misc/fluff_lights.yml +++ b/Resources/Prototypes/Entities/Objects/Misc/fluff_lights.yml @@ -159,7 +159,6 @@ - type: Flash - type: LimitedCharges maxCharges: 3 - charges: 3 - type: AutoRecharge rechargeDuration: 30 - type: MeleeWeapon diff --git a/Resources/Prototypes/Entities/Objects/Tools/lantern.yml b/Resources/Prototypes/Entities/Objects/Tools/lantern.yml index 09f8607ef6d..18f7a5d134c 100644 --- a/Resources/Prototypes/Entities/Objects/Tools/lantern.yml +++ b/Resources/Prototypes/Entities/Objects/Tools/lantern.yml @@ -88,7 +88,6 @@ - type: Flash - type: LimitedCharges maxCharges: 15 - charges: 15 - type: MeleeWeapon damage: types: diff --git a/Resources/Prototypes/Entities/Objects/Tools/spray_painter.yml b/Resources/Prototypes/Entities/Objects/Tools/spray_painter.yml index d2154326783..23d7b68d37d 100644 --- a/Resources/Prototypes/Entities/Objects/Tools/spray_painter.yml +++ b/Resources/Prototypes/Entities/Objects/Tools/spray_painter.yml @@ -1,8 +1,8 @@ - type: entity - parent: [BaseItem, RecyclableItemDeviceSmall] # Frontier: added RecyclableItemDeviceSmall + parent: BaseItem id: SprayPainter name: spray painter - description: A spray painter for painting airlocks and pipes. + description: A spray painter for painting airlocks, pipes, and other items. components: - type: Sprite sprite: Objects/Tools/spray_painter.rsi @@ -31,4 +31,46 @@ air: '#03fcd3' mix: '#947507' - type: StaticPrice - price: 20 # Frontier 40<20 + price: 40 + - type: LimitedCharges + maxCharges: 15 + lastCharges: 15 + - type: PhysicalComposition + materialComposition: + 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 diff --git a/Resources/Prototypes/Entities/Objects/Tools/tools.yml b/Resources/Prototypes/Entities/Objects/Tools/tools.yml index 304a8bb728d..16236e34c52 100644 --- a/Resources/Prototypes/Entities/Objects/Tools/tools.yml +++ b/Resources/Prototypes/Entities/Objects/Tools/tools.yml @@ -378,7 +378,6 @@ - Deconstruct - type: LimitedCharges maxCharges: 30 - charges: 30 - type: Sprite sprite: _NF/Objects/Tools/rcd.rsi # Frontier state: icon @@ -410,7 +409,7 @@ suffix: Empty components: - type: LimitedCharges - charges: 0 + lastCharges: -1 - type: entity id: RCDRecharging @@ -421,7 +420,6 @@ components: - type: LimitedCharges maxCharges: 20 - charges: 20 - type: AutoRecharge rechargeDuration: 10 diff --git a/Resources/Prototypes/Entities/Objects/Weapons/Guns/Projectiles/projectiles.yml b/Resources/Prototypes/Entities/Objects/Weapons/Guns/Projectiles/projectiles.yml index 13916137bc2..3b16f0c87e1 100644 --- a/Resources/Prototypes/Entities/Objects/Weapons/Guns/Projectiles/projectiles.yml +++ b/Resources/Prototypes/Entities/Objects/Weapons/Guns/Projectiles/projectiles.yml @@ -1089,6 +1089,7 @@ bounds: "-0.15,-0.3,0.15,0.3" hard: false mask: + - Opaque - Impassable - BulletImpassable fly-by: *flybyfixture diff --git a/Resources/Prototypes/Entities/Objects/Weapons/Melee/sword.yml b/Resources/Prototypes/Entities/Objects/Weapons/Melee/sword.yml index 5b5d6ec020b..c9761c0250f 100644 --- a/Resources/Prototypes/Entities/Objects/Weapons/Melee/sword.yml +++ b/Resources/Prototypes/Entities/Objects/Weapons/Melee/sword.yml @@ -104,7 +104,6 @@ - type: DashAbility - type: LimitedCharges maxCharges: 3 - charges: 3 - type: AutoRecharge rechargeDuration: 20 - type: Clothing diff --git a/Resources/Prototypes/Entities/Objects/Weapons/security.yml b/Resources/Prototypes/Entities/Objects/Weapons/security.yml index 0b090563ac0..feb5d449648 100644 --- a/Resources/Prototypes/Entities/Objects/Weapons/security.yml +++ b/Resources/Prototypes/Entities/Objects/Weapons/security.yml @@ -245,7 +245,6 @@ - type: Flash - type: LimitedCharges maxCharges: 5 - charges: 5 - type: MeleeWeapon wideAnimationRotation: 180 damage: @@ -281,7 +280,6 @@ components: - type: LimitedCharges maxCharges: 2 - charges: 2 - type: entity name: portable flasher diff --git a/Resources/Prototypes/Entities/Structures/Doors/Airlocks/airlocks.yml b/Resources/Prototypes/Entities/Structures/Doors/Airlocks/airlocks.yml index cf6d5a89dff..093b12c1fad 100644 --- a/Resources/Prototypes/Entities/Structures/Doors/Airlocks/airlocks.yml +++ b/Resources/Prototypes/Entities/Structures/Doors/Airlocks/airlocks.yml @@ -15,8 +15,7 @@ components: - type: Sprite sprite: Structures/Doors/Airlocks/Standard/engineering.rsi - - type: PaintableAirlock - department: Engineering + - type: Wires layoutId: AirlockEngineering @@ -35,8 +34,16 @@ components: - type: Sprite sprite: Structures/Doors/Airlocks/Standard/cargo.rsi - - type: PaintableAirlock - department: Cargo + - type: Wires + layoutId: AirlockCargo + +- type: entity + parent: Airlock + id: AirlockSalvage + suffix: Salvage + components: + - type: Sprite + sprite: Structures/Doors/Airlocks/Standard/cargo.rsi # Null Sector set to Cargo (we aint got these!) - type: Wires layoutId: AirlockCargo @@ -57,8 +64,6 @@ components: - type: Sprite sprite: Structures/Doors/Airlocks/Standard/medical.rsi - - type: PaintableAirlock - department: Medical - type: Wires layoutId: AirlockMedical @@ -85,8 +90,6 @@ components: - type: Sprite sprite: Structures/Doors/Airlocks/Standard/science.rsi - - type: PaintableAirlock - department: Science - type: Wires layoutId: AirlockScience @@ -99,8 +102,6 @@ sprite: Structures/Doors/Airlocks/Standard/command.rsi - type: WiresPanelSecurity securityLevel: medSecurity - - type: PaintableAirlock - department: Command - type: Wires layoutId: AirlockCommand @@ -111,8 +112,6 @@ components: - type: Sprite sprite: Structures/Doors/Airlocks/Standard/security.rsi - - type: PaintableAirlock - department: Security - type: Wires layoutId: AirlockSecurity @@ -141,6 +140,8 @@ sprite: Structures/Doors/Airlocks/Standard/mining.rsi - type: Wires layoutId: AirlockCargo + - type: Paintable + group: null - type: entity parent: AirlockCommand # if you get centcom door somehow it counts as command, also inherit panel @@ -157,6 +158,8 @@ components: - type: Sprite sprite: Structures/Doors/Airlocks/Standard/hatch.rsi + - type: Paintable + group: null - type: entity parent: Airlock @@ -165,6 +168,8 @@ components: - type: Sprite sprite: Structures/Doors/Airlocks/Standard/hatch_maint.rsi + - type: Paintable + group: null # Glass - type: entity @@ -174,8 +179,6 @@ components: - type: Sprite sprite: Structures/Doors/Airlocks/Glass/engineering.rsi - - type: PaintableAirlock - department: Engineering - type: Wires layoutId: AirlockEngineering @@ -202,8 +205,16 @@ components: - type: Sprite sprite: Structures/Doors/Airlocks/Glass/cargo.rsi - - type: PaintableAirlock - department: Cargo + - type: Wires + layoutId: AirlockCargo + +- type: entity + parent: AirlockGlass + id: AirlockSalvageGlass + suffix: Salvage + components: + - type: Sprite + sprite: Structures/Doors/Airlocks/Glass/cargo.rsi # Null Sector set to Cargo (we aint got these!) - type: Wires layoutId: AirlockCargo @@ -224,8 +235,6 @@ components: - type: Sprite sprite: Structures/Doors/Airlocks/Glass/medical.rsi - - type: PaintableAirlock - department: Medical - type: Wires layoutId: AirlockMedical @@ -252,8 +261,6 @@ components: - type: Sprite sprite: Structures/Doors/Airlocks/Glass/science.rsi - - type: PaintableAirlock - department: Science - type: Wires layoutId: AirlockScience @@ -264,8 +271,6 @@ components: - type: Sprite sprite: Structures/Doors/Airlocks/Glass/command.rsi - - type: PaintableAirlock - department: Command - type: WiresPanelSecurity securityLevel: medSecurity - type: Wires @@ -278,8 +283,6 @@ components: - type: Sprite sprite: Structures/Doors/Airlocks/Glass/security.rsi - - type: PaintableAirlock - department: Security - type: Wires layoutId: AirlockSecurity @@ -298,6 +301,8 @@ components: - type: Sprite sprite: Structures/Doors/Airlocks/Glass/mining.rsi + - type: Paintable + group: null - type: entity parent: AirlockCommandGlass # see standard @@ -306,3 +311,31 @@ components: - type: Sprite sprite: Structures/Doors/Airlocks/Glass/centcomm.rsi + +- type: entity + parent: AirlockGlass + id: AirlockStandardGlass + suffix: Service + components: + - type: Sprite + sprite: Structures/Doors/Airlocks/Glass/basic.rsi + +- type: entity + parent: Airlock + id: AirlockXeno + suffix: Xeno + components: + - type: Sprite + sprite: Structures/Doors/Airlocks/Standard/centcomm.rsi # Null Sector : No Xeno Airlocks Implemented + - type: Paintable + group: null + +- type: entity + parent: AirlockGlass + id: AirlockGlassXeno + suffix: Xeno + components: + - type: Sprite + sprite: Structures/Doors/Airlocks/Glass/centcomm.rsi # Null Sector : No Xeno Airlocks Implemented + - type: Paintable + group: null diff --git a/Resources/Prototypes/Entities/Structures/Doors/Airlocks/base_structureairlocks.yml b/Resources/Prototypes/Entities/Structures/Doors/Airlocks/base_structureairlocks.yml index 42e15a4f677..799e1c41381 100644 --- a/Resources/Prototypes/Entities/Structures/Doors/Airlocks/base_structureairlocks.yml +++ b/Resources/Prototypes/Entities/Structures/Doors/Airlocks/base_structureairlocks.yml @@ -162,9 +162,8 @@ - type: IconSmooth key: walls mode: NoSprite - - type: PaintableAirlock - group: Standard - department: Civilian + - type: Paintable + group: AirlockStandard - type: StaticPrice price: 150 - type: LightningTarget @@ -225,8 +224,8 @@ - type: Construction graph: Airlock node: glassAirlock - - type: PaintableAirlock - group: Glass + - type: Paintable + group: AirlockGlass - type: RadiationBlocker resistance: 2 - type: Tag diff --git a/Resources/Prototypes/Entities/Structures/Doors/Airlocks/clockwork.yml b/Resources/Prototypes/Entities/Structures/Doors/Airlocks/clockwork.yml index 87b76fb16c2..123f1ef2c7f 100644 --- a/Resources/Prototypes/Entities/Structures/Doors/Airlocks/clockwork.yml +++ b/Resources/Prototypes/Entities/Structures/Doors/Airlocks/clockwork.yml @@ -10,6 +10,8 @@ node: airlock containers: - board + - type: Paintable + group: null - type: entity parent: AirlockGlass @@ -23,5 +25,7 @@ node: glassAirlock containers: - board - # - type: StaticPrice # Frontier - TODO: material value rework - # price: 165 # Frontier + - type: StaticPrice + price: 165 + - type: Paintable + group: null diff --git a/Resources/Prototypes/Entities/Structures/Doors/Airlocks/external.yml b/Resources/Prototypes/Entities/Structures/Doors/Airlocks/external.yml index 7f5c0190250..773065bea04 100644 --- a/Resources/Prototypes/Entities/Structures/Doors/Airlocks/external.yml +++ b/Resources/Prototypes/Entities/Structures/Doors/Airlocks/external.yml @@ -16,11 +16,10 @@ path: /Audio/Machines/airlock_deny.ogg - type: Sprite sprite: Structures/Doors/Airlocks/Standard/external.rsi - - type: PaintableAirlock - group: External - department: null - type: Wires layoutId: AirlockExternal + - type: Paintable + group: null - type: entity parent: AirlockExternal @@ -33,8 +32,6 @@ enabled: false - type: Sprite sprite: Structures/Doors/Airlocks/Glass/external.rsi - - type: PaintableAirlock - group: ExternalGlass - type: Fixtures fixtures: fix1: diff --git a/Resources/Prototypes/Entities/Structures/Doors/Airlocks/shuttle.yml b/Resources/Prototypes/Entities/Structures/Doors/Airlocks/shuttle.yml index 6c0e4bbc032..8b99f4d24d3 100644 --- a/Resources/Prototypes/Entities/Structures/Doors/Airlocks/shuttle.yml +++ b/Resources/Prototypes/Entities/Structures/Doors/Airlocks/shuttle.yml @@ -53,14 +53,13 @@ - type: Tag tags: - ForceNoFixRotations - - type: PaintableAirlock - group: Shuttle - department: null - type: Construction graph: AirlockShuttle node: airlock - type: StaticPrice price: 350 + - type: Paintable + group: null - type: entity id: AirlockGlassShuttle @@ -73,8 +72,6 @@ sprite: Structures/Doors/Airlocks/Glass/shuttle.rsi - type: Occluder enabled: false - - type: PaintableAirlock - group: ShuttleGlass - type: Door occludes: false - type: Fixtures diff --git a/Resources/Prototypes/Entities/Structures/Doors/airlock_groups.yml b/Resources/Prototypes/Entities/Structures/Doors/airlock_groups.yml deleted file mode 100644 index 23d17f008d4..00000000000 --- a/Resources/Prototypes/Entities/Structures/Doors/airlock_groups.yml +++ /dev/null @@ -1,86 +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 - science: Structures/Doors/Airlocks/Standard/science.rsi - security: Structures/Doors/Airlocks/Standard/security.rsi - virology: Structures/Doors/Airlocks/Standard/virology.rsi - mercenary: _NF/Structures/Doors/Airlocks/Standard/mercenary.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 - security: Structures/Doors/Airlocks/Glass/security.rsi - virology: Structures/Doors/Airlocks/Glass/virology.rsi - mercenary: _NF/Structures/Doors/Airlocks/Glass/mercenary.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 - science: Science - security: Security - virology: Medical diff --git a/Resources/Prototypes/Entities/Structures/Storage/Canisters/gas_canisters.yml b/Resources/Prototypes/Entities/Structures/Storage/Canisters/gas_canisters.yml index 713d5c1fa75..fb1df2cedde 100644 --- a/Resources/Prototypes/Entities/Structures/Storage/Canisters/gas_canisters.yml +++ b/Resources/Prototypes/Entities/Structures/Storage/Canisters/gas_canisters.yml @@ -10,10 +10,10 @@ - type: Transform noRot: true - type: Sprite - sprite: _NF/Structures/Storage/canister.rsi # Frontier + sprite: _NF/Structures/Storage/canister.rsi noRot: true layers: - - state: air # Frontier + - state: air - type: Appearance - type: GenericVisualizer visuals: @@ -35,6 +35,9 @@ 1: { state: can-o1, shader: "unshaded" } 2: { state: can-o2, shader: "unshaded" } 3: { state: can-o3, shader: "unshaded" } + - type: ActivatableUI + key: enum.GasCanisterUiKey.Key + - type: ActivatableUIRequiresLock - type: UserInterface interfaces: enum.GasCanisterUiKey.Key: @@ -113,10 +116,8 @@ - type: GuideHelp guides: - GasCanisters - - type: Climbable # mono - - type: FootstepModifier # mono - footstepSoundCollection: - collection: FootstepHull + - type: Paintable + group: Canisters - type: entity parent: GasCanister diff --git a/Resources/Prototypes/Entities/Structures/Storage/Closets/Lockers/base_structurelockers.yml b/Resources/Prototypes/Entities/Structures/Storage/Closets/Lockers/base_structurelockers.yml index 8e2d1a6e54c..a30ae00e961 100644 --- a/Resources/Prototypes/Entities/Structures/Storage/Closets/Lockers/base_structurelockers.yml +++ b/Resources/Prototypes/Entities/Structures/Storage/Closets/Lockers/base_structurelockers.yml @@ -54,6 +54,8 @@ node: done containers: - entity_storage + - type: Paintable + group: Locker - type: entity id: LockerBaseSecure diff --git a/Resources/Prototypes/Entities/Structures/Storage/Closets/Lockers/lockers.yml b/Resources/Prototypes/Entities/Structures/Storage/Closets/Lockers/lockers.yml index b625801c0f8..cb6b1963428 100644 --- a/Resources/Prototypes/Entities/Structures/Storage/Closets/Lockers/lockers.yml +++ b/Resources/Prototypes/Entities/Structures/Storage/Closets/Lockers/lockers.yml @@ -16,6 +16,8 @@ path: /Audio/Effects/woodenclosetclose.ogg openSound: path: /Audio/Effects/woodenclosetopen.ogg + - type: Paintable + group: null # not shaped like other lockers # Basic - type: entity @@ -174,6 +176,8 @@ node: done containers: - entity_storage + - type: Paintable + group: null - type: entity id: LockerFreezer diff --git a/Resources/Prototypes/Entities/Structures/Storage/Closets/base_structureclosets.yml b/Resources/Prototypes/Entities/Structures/Storage/Closets/base_structureclosets.yml index bdff9216b4e..297c7f8aead 100644 --- a/Resources/Prototypes/Entities/Structures/Storage/Closets/base_structureclosets.yml +++ b/Resources/Prototypes/Entities/Structures/Storage/Closets/base_structureclosets.yml @@ -124,6 +124,8 @@ node: done containers: - entity_storage + - type: Paintable + group: Closet #Wall Closet - type: entity @@ -200,6 +202,8 @@ node: done containers: - entity_storage + - type: Paintable + group: WallCloset #Wall locker - type: entity @@ -223,6 +227,8 @@ - state: welded visible: false map: ["enum.WeldableLayers.BaseWelded"] + - type: Paintable + group: WallLocker #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. diff --git a/Resources/Prototypes/Entities/Structures/Storage/Crates/base_structurecrates.yml b/Resources/Prototypes/Entities/Structures/Storage/Crates/base_structurecrates.yml index abb5e2d605e..125a2d0fadb 100644 --- a/Resources/Prototypes/Entities/Structures/Storage/Crates/base_structurecrates.yml +++ b/Resources/Prototypes/Entities/Structures/Storage/Crates/base_structurecrates.yml @@ -154,3 +154,5 @@ - Energy reflectProb: 0.2 spread: 90 + - type: Paintable + group: CrateSecure diff --git a/Resources/Prototypes/Entities/Structures/Storage/Crates/crates.yml b/Resources/Prototypes/Entities/Structures/Storage/Crates/crates.yml index 486e23300c3..95b54720c28 100644 --- a/Resources/Prototypes/Entities/Structures/Storage/Crates/crates.yml +++ b/Resources/Prototypes/Entities/Structures/Storage/Crates/crates.yml @@ -12,6 +12,8 @@ - Energy reflectProb: 0.2 spread: 90 + - type: Paintable + group: CrateSteel - type: RadiationBlockingContainer resistance: 2.5 @@ -31,6 +33,8 @@ - entity_storage - type: StaticPrice price: 80 # Frontier: 100<80 - TODO: material value rework + - type: Paintable + group: CratePlastic - type: entity parent: CrateBaseWeldable @@ -47,7 +51,7 @@ containers: - entity_storage - type: StaticPrice - price: 40 # Monolith + price: 40 - type: entity parent: CratePlastic @@ -66,6 +70,8 @@ node: done containers: - entity_storage + - type: Paintable + group: null - type: entity parent: CratePlastic @@ -172,6 +178,15 @@ damageModifierSet: Web - type: Destructible thresholds: + - trigger: # Excess damage, don't spawn entities + !type:DamageTrigger + damage: 100 + behaviors: + - !type:PlaySoundBehavior + sound: + collection: WoodDestroy + - !type:DoActsBehavior + acts: ["Destruction"] - trigger: !type:DamageTrigger damage: 50 @@ -513,6 +528,15 @@ state: base - type: Destructible thresholds: + - trigger: # Excess damage, don't spawn entities + !type:DamageTrigger + damage: 75 + behaviors: + - !type:PlaySoundBehavior + sound: + collection: WoodDestroy + - !type:DoActsBehavior + acts: ["Destruction"] - trigger: !type:DamageTrigger damage: 15 @@ -555,6 +579,15 @@ state: base - type: Destructible thresholds: + - trigger: # Excess damage, don't spawn entities + !type:DamageTrigger + damage: 400 + behaviors: + - !type:PlaySoundBehavior + sound: + collection: WoodDestroy + - !type:DoActsBehavior + acts: ["Destruction"] - trigger: !type:DamageTrigger damage: 200 # discourage just beating the grave to break it open @@ -633,6 +666,8 @@ sprite: Structures/Storage/Crates/labels.rsi offset: "0.0,0.03125" map: ["enum.PaperLabelVisuals.Layer"] + - type: Paintable + group: null - type: entity parent: CrateBaseSecure @@ -659,6 +694,9 @@ map: ["enum.PaperLabelVisuals.Layer"] - type: AccessReader # access: [["Janitor"]] + access: [["Janitor"]] + - type: Paintable + group: null - type: entity parent: CrateBaseWeldable diff --git a/Resources/Prototypes/Entities/Structures/Walls/Rock/andesite.yml b/Resources/Prototypes/Entities/Structures/Walls/Rock/andesite.yml new file mode 100644 index 00000000000..fdb10595d51 --- /dev/null +++ b/Resources/Prototypes/Entities/Structures/Walls/Rock/andesite.yml @@ -0,0 +1,188 @@ +# Andesite variants +- type: entity + id: WallRockAndesite + name: andesite + parent: NFWallRockOre # Frontier: WallRock # Null-Rogue Combined-Sectors Program -You are yet another crewman inhabiting the Null Sector! You're one of the lucky survivors hoping with the development of the furthest reaches of space, fighting back to survive any Armada interventions that may happen along the way! + You are an inhabitant of the Null Sector, emerging from your asteroid dwelling or from a local resting safe-spot. + You're one of the luckier ones, as most have been taken by the Armada. Your goal is to explore these reaches of space, gather the wherewithal to survive, and to fight any invaders for your right to live. Not everything is explained: adventure is all about finding what's out there, for better or worse. -[color=#33bbff]If you'd like a hand, ask on the radio, or talk to somebody.[/color] -The [color=#dd0000]Valet[/color] or [color=#009933]Sector Coordinator[/color] should be able to help, they're to the lower-right of medbay. + [color=#009933]If you'd like a hand, ask on the radio, or talk to somebody.[/color] +If a [color=#dd0000]Valet[/color] or [color=#33bbff]Sector Coordinator[/color] (SC) is available, they may help. The SC's office is to the southeast of medbay. + + Nearby the SC's office are the Automated Trade Machines (ATMs), and Shipyard terminals. ## Economy @@ -12,21 +15,34 @@ The [color=#dd0000]Valet[/color] or [color=#009933]Sector Coordinator[/color] sh - Any money that you make is [bold]yours[/bold], and will be available in future shifts if deposited at a [color=#44cc00]Bank ATM[/color] on Lark Station, the Expeditionary Lodge, or Tinnia's Rest. + Any money that you make is [bold]yours[/bold], and will be available in future shifts if deposited at a [color=#44cc00]Bank ATM[/color] on Lark Station, the Expeditionary Lodge, or any other Point of Interest (PoI) that has an ATM. + + To make money, why not: + - Join a ship's crew? Ask on the radio if anyone needs extra crew, and work to help your captain. + - Get a job on station? Ask the [color=#009933]Sector Coordinator[/color] (follow the SC signs) if anything needs doing. + - Use a [color=#cccc00]shipyard console[/color] to purchase your own ship? + + Money allows one to spend it on various amenities, weapons, new ships and more. For most, it is their primary objective. For others, it is just another resource to consider. + + ## Amenities + + + + + Some provisions (e.g. air tanks, salvage equipment) are available from vendors on Lark Station, but most amenities (medical services, food) should be offered by players on shuttles or at the Trade Depot. + + Be sure to stock up on spare fuel before any extended voyages, and take advantage of the [color=#33bbff]medical implanter[/color] in your loadout, or from a NanoMed vendor. + + Shifts in the Null Sector typically last for [color=#33bbff]Seven Hours[/color]. Though you are not obligated to stay. If you need to end your ventures early or take a break, use the [color=#33bbff]cryo sleep chambers[/color] in the medbay on Lark or Kemak Stations. -To make money, why not: -- Join a ship's crew? Ask on the radio if anyone needs extra crew, and work to help your captain. -- Get a job on station? Ask the [color=#009933]Sector Coordinator[/color] (follow the SC signs) if anything needs doing. -- Use a [color=#cccc00]shipyard console[/color] to purchase your own ship? + When a shift ends, it is typically due to an oncoming automated Armadan scout vessel on its routine patrol. Anyone who wants to make sure they live to tell the tale should sell their vessel as soon as the warning is sounded from the Lark Relay. -## Amenities + ### Food - - + + - Some provisions (e.g. air tanks, salvage equipment) are available from vendors on Lark Station, but most amenities (medical services, food) should be offered by players on shuttles or at the Trade Depot. -Be sure to stock up at the Trade Depot if going out on an extended voyage, and take advantage of the [color=#33bbff]medical insurance tracker[/color] in your loadout, or from the NanoMed vendor in medbay. + Expedition-goers will have it easy, as food and food-prep materials are quick to find out there. Everyone else has to scavenge for food, create it using biomass at kemak, or eat crap organs and die. It is scarce for those who don't know where to look, so treasure what you have. -Shifts in the Null Sector typically last for [color=#33bbff]Seven Hours[/color]. Though you are not obligated to stay. If you need to end your ventures early or take a break, use the [color=#33bbff]cryo sleep chambers[/color] in the medbay on Lark Station. diff --git a/Resources/Textures/Objects/Tools/spray_painter.rsi/ammo-inhand-left.png b/Resources/Textures/Objects/Tools/spray_painter.rsi/ammo-inhand-left.png new file mode 100644 index 00000000000..d81e805bb25 Binary files /dev/null and b/Resources/Textures/Objects/Tools/spray_painter.rsi/ammo-inhand-left.png differ diff --git a/Resources/Textures/Objects/Tools/spray_painter.rsi/ammo-inhand-right.png b/Resources/Textures/Objects/Tools/spray_painter.rsi/ammo-inhand-right.png new file mode 100644 index 00000000000..b4083c87e1e Binary files /dev/null and b/Resources/Textures/Objects/Tools/spray_painter.rsi/ammo-inhand-right.png differ diff --git a/Resources/Textures/Objects/Tools/spray_painter.rsi/ammo.png b/Resources/Textures/Objects/Tools/spray_painter.rsi/ammo.png new file mode 100644 index 00000000000..ca64c2a7580 Binary files /dev/null and b/Resources/Textures/Objects/Tools/spray_painter.rsi/ammo.png differ diff --git a/Resources/Textures/Objects/Tools/spray_painter.rsi/inhand-left.png b/Resources/Textures/Objects/Tools/spray_painter.rsi/inhand-left.png new file mode 100644 index 00000000000..34787a11f5f Binary files /dev/null and b/Resources/Textures/Objects/Tools/spray_painter.rsi/inhand-left.png differ diff --git a/Resources/Textures/Objects/Tools/spray_painter.rsi/inhand-right.png b/Resources/Textures/Objects/Tools/spray_painter.rsi/inhand-right.png new file mode 100644 index 00000000000..ce4df846069 Binary files /dev/null and b/Resources/Textures/Objects/Tools/spray_painter.rsi/inhand-right.png differ diff --git a/Resources/Textures/Objects/Tools/spray_painter.rsi/meta.json b/Resources/Textures/Objects/Tools/spray_painter.rsi/meta.json index 056ba0a8563..14af8406380 100644 --- a/Resources/Textures/Objects/Tools/spray_painter.rsi/meta.json +++ b/Resources/Textures/Objects/Tools/spray_painter.rsi/meta.json @@ -1,14 +1,33 @@ { - "copyright" : "Taken from https://github.com/tgstation/tgstation at commit a21274e56ae84b2c96e8b6beeca805df3d5402e8.", - "license" : "CC-BY-SA-3.0", - "size" : { - "x" : 32, - "y" : 32 - }, - "states" : [ - { - "name" : "spray_painter" - } - ], - "version" : 1 + "version": 1, + "license": "CC-BY-SA-3.0", + "copyright": "Taken from https://github.com/tgstation/tgstation at commit a21274e56ae84b2c96e8b6beeca805df3d5402e8, Inhand sprites by onesch, ammo by Paradoxmi (Discord).", + "size": { + "x": 32, + "y": 32 + }, + "states": [ + { + "name": "spray_painter" + }, + { + "name": "ammo" + }, + { + "name": "inhand-left", + "directions": 4 + }, + { + "name": "inhand-right", + "directions": 4 + }, + { + "name": "ammo-inhand-left", + "directions": 4 + }, + { + "name": "ammo-inhand-right", + "directions": 4 + } + ] } diff --git a/Resources/Textures/Structures/Storage/Crates/wood.rsi/closed.png b/Resources/Textures/Structures/Storage/Crates/wood.rsi/closed.png index 472214c5209..58895d592e8 100644 Binary files a/Resources/Textures/Structures/Storage/Crates/wood.rsi/closed.png and b/Resources/Textures/Structures/Storage/Crates/wood.rsi/closed.png differ diff --git a/Resources/Textures/Structures/Storage/Crates/wood.rsi/icon.png b/Resources/Textures/Structures/Storage/Crates/wood.rsi/icon.png index e93350a1b2b..fb3fe5b27e5 100644 Binary files a/Resources/Textures/Structures/Storage/Crates/wood.rsi/icon.png and b/Resources/Textures/Structures/Storage/Crates/wood.rsi/icon.png differ diff --git a/Resources/Textures/Structures/Walls/rock.rsi/meta.json b/Resources/Textures/Structures/Walls/rock.rsi/meta.json index a29513356c5..9ec12987a68 100644 --- a/Resources/Textures/Structures/Walls/rock.rsi/meta.json +++ b/Resources/Textures/Structures/Walls/rock.rsi/meta.json @@ -155,6 +155,12 @@ { "name": "rock_tin" }, + { + "name": "rock_scrap" + }, + { + "name": "rock_bluespace" + }, { "name": "rock_uranium" }, diff --git a/Resources/Textures/Structures/Walls/rock.rsi/rock_bluespace.png b/Resources/Textures/Structures/Walls/rock.rsi/rock_bluespace.png new file mode 100644 index 00000000000..e3368c34d71 Binary files /dev/null and b/Resources/Textures/Structures/Walls/rock.rsi/rock_bluespace.png differ diff --git a/Resources/Textures/Structures/Walls/rock.rsi/rock_scrap.png b/Resources/Textures/Structures/Walls/rock.rsi/rock_scrap.png new file mode 100644 index 00000000000..56274c584af Binary files /dev/null and b/Resources/Textures/Structures/Walls/rock.rsi/rock_scrap.png differ diff --git a/Resources/Textures/Tiles/lattice.png b/Resources/Textures/Tiles/lattice.png index 9a33ae0f9a0..53657f0538e 100644 Binary files a/Resources/Textures/Tiles/lattice.png and b/Resources/Textures/Tiles/lattice.png differ diff --git a/Resources/Textures/_NF/Decals/stencil.rsi/meta.json b/Resources/Textures/_NF/Decals/stencil.rsi/meta.json new file mode 100644 index 00000000000..69cebf29d0f --- /dev/null +++ b/Resources/Textures/_NF/Decals/stencil.rsi/meta.json @@ -0,0 +1,149 @@ +{ + "version": 1, + "license": "CC-BY-SA-4.0", + "copyright": "made by Alkheemist (GitHub/Discord)", + "size": { + "x": 32, + "y": 32 + }, + "states": [ + { + "name": "stencil0" + }, + { + "name": "stencil1" + }, + { + "name": "stencil2" + }, + { + "name": "stencil3" + }, + { + "name": "stencil4" + }, + { + "name": "stencil5" + }, + { + "name": "stencil6" + }, + { + "name": "stencil7" + }, + { + "name": "stencil8" + }, + { + "name": "stencil9" + }, + { + "name": "stencilA" + }, + { + "name": "stencilB" + }, + { + "name": "stencilC" + }, + { + "name": "stencilD" + }, + { + "name": "stencilE" + }, + { + "name": "stencilF" + }, + { + "name": "stencilG" + }, + { + "name": "stencilH" + }, + { + "name": "stencilI" + }, + { + "name": "stencilJ" + }, + { + "name": "stencilK" + }, + { + "name": "stencilL" + }, + { + "name": "stencilM" + }, + { + "name": "stencilN" + }, + { + "name": "stencilO" + }, + { + "name": "stencilP" + }, + { + "name": "stencilQ" + }, + { + "name": "stencilR" + }, + { + "name": "stencilS" + }, + { + "name": "stencilT" + }, + { + "name": "stencilU" + }, + { + "name": "stencilV" + }, + { + "name": "stencilW" + }, + { + "name": "stencilX" + }, + { + "name": "stencilY" + }, + { + "name": "stencilZ" + }, + { + "name": "stencil_Ampersand" + }, + { + "name": "stencil_Asterix" + }, + { + "name": "stencil_Dash" + }, + { + "name": "stencil_Equals" + }, + { + "name": "stencil_Exclaim" + }, + { + "name": "stencil_Hash" + }, + { + "name": "stencil_Speso" + }, + { + "name": "stencil_Multiocular" + }, + { + "name": "stencil_Plus" + }, + { + "name": "stencil_Question" + } + ] +} diff --git a/Resources/Textures/_NF/Decals/stencil.rsi/stencil0.png b/Resources/Textures/_NF/Decals/stencil.rsi/stencil0.png new file mode 100644 index 00000000000..5c313927469 Binary files /dev/null and b/Resources/Textures/_NF/Decals/stencil.rsi/stencil0.png differ diff --git a/Resources/Textures/_NF/Decals/stencil.rsi/stencil1.png b/Resources/Textures/_NF/Decals/stencil.rsi/stencil1.png new file mode 100644 index 00000000000..aaf94006b44 Binary files /dev/null and b/Resources/Textures/_NF/Decals/stencil.rsi/stencil1.png differ diff --git a/Resources/Textures/_NF/Decals/stencil.rsi/stencil2.png b/Resources/Textures/_NF/Decals/stencil.rsi/stencil2.png new file mode 100644 index 00000000000..4c2b20879b4 Binary files /dev/null and b/Resources/Textures/_NF/Decals/stencil.rsi/stencil2.png differ diff --git a/Resources/Textures/_NF/Decals/stencil.rsi/stencil3.png b/Resources/Textures/_NF/Decals/stencil.rsi/stencil3.png new file mode 100644 index 00000000000..2e32055f76d Binary files /dev/null and b/Resources/Textures/_NF/Decals/stencil.rsi/stencil3.png differ diff --git a/Resources/Textures/_NF/Decals/stencil.rsi/stencil4.png b/Resources/Textures/_NF/Decals/stencil.rsi/stencil4.png new file mode 100644 index 00000000000..041dbb45c20 Binary files /dev/null and b/Resources/Textures/_NF/Decals/stencil.rsi/stencil4.png differ diff --git a/Resources/Textures/_NF/Decals/stencil.rsi/stencil5.png b/Resources/Textures/_NF/Decals/stencil.rsi/stencil5.png new file mode 100644 index 00000000000..0fac5c690bf Binary files /dev/null and b/Resources/Textures/_NF/Decals/stencil.rsi/stencil5.png differ diff --git a/Resources/Textures/_NF/Decals/stencil.rsi/stencil6.png b/Resources/Textures/_NF/Decals/stencil.rsi/stencil6.png new file mode 100644 index 00000000000..46ceaf0d565 Binary files /dev/null and b/Resources/Textures/_NF/Decals/stencil.rsi/stencil6.png differ diff --git a/Resources/Textures/_NF/Decals/stencil.rsi/stencil7.png b/Resources/Textures/_NF/Decals/stencil.rsi/stencil7.png new file mode 100644 index 00000000000..54826779ced Binary files /dev/null and b/Resources/Textures/_NF/Decals/stencil.rsi/stencil7.png differ diff --git a/Resources/Textures/_NF/Decals/stencil.rsi/stencil8.png b/Resources/Textures/_NF/Decals/stencil.rsi/stencil8.png new file mode 100644 index 00000000000..7c97b60c89b Binary files /dev/null and b/Resources/Textures/_NF/Decals/stencil.rsi/stencil8.png differ diff --git a/Resources/Textures/_NF/Decals/stencil.rsi/stencil9.png b/Resources/Textures/_NF/Decals/stencil.rsi/stencil9.png new file mode 100644 index 00000000000..d0c4a680d2a Binary files /dev/null and b/Resources/Textures/_NF/Decals/stencil.rsi/stencil9.png differ diff --git a/Resources/Textures/_NF/Decals/stencil.rsi/stencilA.png b/Resources/Textures/_NF/Decals/stencil.rsi/stencilA.png new file mode 100644 index 00000000000..c3a92548260 Binary files /dev/null and b/Resources/Textures/_NF/Decals/stencil.rsi/stencilA.png differ diff --git a/Resources/Textures/_NF/Decals/stencil.rsi/stencilB.png b/Resources/Textures/_NF/Decals/stencil.rsi/stencilB.png new file mode 100644 index 00000000000..fc62e40c950 Binary files /dev/null and b/Resources/Textures/_NF/Decals/stencil.rsi/stencilB.png differ diff --git a/Resources/Textures/_NF/Decals/stencil.rsi/stencilC.png b/Resources/Textures/_NF/Decals/stencil.rsi/stencilC.png new file mode 100644 index 00000000000..e79cf206cc8 Binary files /dev/null and b/Resources/Textures/_NF/Decals/stencil.rsi/stencilC.png differ diff --git a/Resources/Textures/_NF/Decals/stencil.rsi/stencilD.png b/Resources/Textures/_NF/Decals/stencil.rsi/stencilD.png new file mode 100644 index 00000000000..649f4043a55 Binary files /dev/null and b/Resources/Textures/_NF/Decals/stencil.rsi/stencilD.png differ diff --git a/Resources/Textures/_NF/Decals/stencil.rsi/stencilE.png b/Resources/Textures/_NF/Decals/stencil.rsi/stencilE.png new file mode 100644 index 00000000000..a387756bbc9 Binary files /dev/null and b/Resources/Textures/_NF/Decals/stencil.rsi/stencilE.png differ diff --git a/Resources/Textures/_NF/Decals/stencil.rsi/stencilF.png b/Resources/Textures/_NF/Decals/stencil.rsi/stencilF.png new file mode 100644 index 00000000000..e50436bd474 Binary files /dev/null and b/Resources/Textures/_NF/Decals/stencil.rsi/stencilF.png differ diff --git a/Resources/Textures/_NF/Decals/stencil.rsi/stencilG.png b/Resources/Textures/_NF/Decals/stencil.rsi/stencilG.png new file mode 100644 index 00000000000..3e1fd2acabd Binary files /dev/null and b/Resources/Textures/_NF/Decals/stencil.rsi/stencilG.png differ diff --git a/Resources/Textures/_NF/Decals/stencil.rsi/stencilH.png b/Resources/Textures/_NF/Decals/stencil.rsi/stencilH.png new file mode 100644 index 00000000000..26e8f89348c Binary files /dev/null and b/Resources/Textures/_NF/Decals/stencil.rsi/stencilH.png differ diff --git a/Resources/Textures/_NF/Decals/stencil.rsi/stencilI.png b/Resources/Textures/_NF/Decals/stencil.rsi/stencilI.png new file mode 100644 index 00000000000..3c8bc08bc73 Binary files /dev/null and b/Resources/Textures/_NF/Decals/stencil.rsi/stencilI.png differ diff --git a/Resources/Textures/_NF/Decals/stencil.rsi/stencilJ.png b/Resources/Textures/_NF/Decals/stencil.rsi/stencilJ.png new file mode 100644 index 00000000000..360ffb4fab2 Binary files /dev/null and b/Resources/Textures/_NF/Decals/stencil.rsi/stencilJ.png differ diff --git a/Resources/Textures/_NF/Decals/stencil.rsi/stencilK.png b/Resources/Textures/_NF/Decals/stencil.rsi/stencilK.png new file mode 100644 index 00000000000..7c5dcd10a3c Binary files /dev/null and b/Resources/Textures/_NF/Decals/stencil.rsi/stencilK.png differ diff --git a/Resources/Textures/_NF/Decals/stencil.rsi/stencilL.png b/Resources/Textures/_NF/Decals/stencil.rsi/stencilL.png new file mode 100644 index 00000000000..cc578c18b5c Binary files /dev/null and b/Resources/Textures/_NF/Decals/stencil.rsi/stencilL.png differ diff --git a/Resources/Textures/_NF/Decals/stencil.rsi/stencilM.png b/Resources/Textures/_NF/Decals/stencil.rsi/stencilM.png new file mode 100644 index 00000000000..f69505f58be Binary files /dev/null and b/Resources/Textures/_NF/Decals/stencil.rsi/stencilM.png differ diff --git a/Resources/Textures/_NF/Decals/stencil.rsi/stencilN.png b/Resources/Textures/_NF/Decals/stencil.rsi/stencilN.png new file mode 100644 index 00000000000..0052eeba7cd Binary files /dev/null and b/Resources/Textures/_NF/Decals/stencil.rsi/stencilN.png differ diff --git a/Resources/Textures/_NF/Decals/stencil.rsi/stencilO.png b/Resources/Textures/_NF/Decals/stencil.rsi/stencilO.png new file mode 100644 index 00000000000..d8b11a7d9c3 Binary files /dev/null and b/Resources/Textures/_NF/Decals/stencil.rsi/stencilO.png differ diff --git a/Resources/Textures/_NF/Decals/stencil.rsi/stencilP.png b/Resources/Textures/_NF/Decals/stencil.rsi/stencilP.png new file mode 100644 index 00000000000..dd7ef904272 Binary files /dev/null and b/Resources/Textures/_NF/Decals/stencil.rsi/stencilP.png differ diff --git a/Resources/Textures/_NF/Decals/stencil.rsi/stencilQ.png b/Resources/Textures/_NF/Decals/stencil.rsi/stencilQ.png new file mode 100644 index 00000000000..b4af8484176 Binary files /dev/null and b/Resources/Textures/_NF/Decals/stencil.rsi/stencilQ.png differ diff --git a/Resources/Textures/_NF/Decals/stencil.rsi/stencilR.png b/Resources/Textures/_NF/Decals/stencil.rsi/stencilR.png new file mode 100644 index 00000000000..bb4f0a2d040 Binary files /dev/null and b/Resources/Textures/_NF/Decals/stencil.rsi/stencilR.png differ diff --git a/Resources/Textures/_NF/Decals/stencil.rsi/stencilS.png b/Resources/Textures/_NF/Decals/stencil.rsi/stencilS.png new file mode 100644 index 00000000000..4a1a69e1b02 Binary files /dev/null and b/Resources/Textures/_NF/Decals/stencil.rsi/stencilS.png differ diff --git a/Resources/Textures/_NF/Decals/stencil.rsi/stencilT.png b/Resources/Textures/_NF/Decals/stencil.rsi/stencilT.png new file mode 100644 index 00000000000..f895a92edda Binary files /dev/null and b/Resources/Textures/_NF/Decals/stencil.rsi/stencilT.png differ diff --git a/Resources/Textures/_NF/Decals/stencil.rsi/stencilU.png b/Resources/Textures/_NF/Decals/stencil.rsi/stencilU.png new file mode 100644 index 00000000000..2e09e218b96 Binary files /dev/null and b/Resources/Textures/_NF/Decals/stencil.rsi/stencilU.png differ diff --git a/Resources/Textures/_NF/Decals/stencil.rsi/stencilV.png b/Resources/Textures/_NF/Decals/stencil.rsi/stencilV.png new file mode 100644 index 00000000000..572e40bf95b Binary files /dev/null and b/Resources/Textures/_NF/Decals/stencil.rsi/stencilV.png differ diff --git a/Resources/Textures/_NF/Decals/stencil.rsi/stencilW.png b/Resources/Textures/_NF/Decals/stencil.rsi/stencilW.png new file mode 100644 index 00000000000..29cb98300e1 Binary files /dev/null and b/Resources/Textures/_NF/Decals/stencil.rsi/stencilW.png differ diff --git a/Resources/Textures/_NF/Decals/stencil.rsi/stencilX.png b/Resources/Textures/_NF/Decals/stencil.rsi/stencilX.png new file mode 100644 index 00000000000..d76ab6de061 Binary files /dev/null and b/Resources/Textures/_NF/Decals/stencil.rsi/stencilX.png differ diff --git a/Resources/Textures/_NF/Decals/stencil.rsi/stencilY.png b/Resources/Textures/_NF/Decals/stencil.rsi/stencilY.png new file mode 100644 index 00000000000..9f5f0ed52f8 Binary files /dev/null and b/Resources/Textures/_NF/Decals/stencil.rsi/stencilY.png differ diff --git a/Resources/Textures/_NF/Decals/stencil.rsi/stencilZ.png b/Resources/Textures/_NF/Decals/stencil.rsi/stencilZ.png new file mode 100644 index 00000000000..7c40d838a30 Binary files /dev/null and b/Resources/Textures/_NF/Decals/stencil.rsi/stencilZ.png differ diff --git a/Resources/Textures/_NF/Decals/stencil.rsi/stencil_Ampersand.png b/Resources/Textures/_NF/Decals/stencil.rsi/stencil_Ampersand.png new file mode 100644 index 00000000000..ac7583bf5f8 Binary files /dev/null and b/Resources/Textures/_NF/Decals/stencil.rsi/stencil_Ampersand.png differ diff --git a/Resources/Textures/_NF/Decals/stencil.rsi/stencil_Asterix.png b/Resources/Textures/_NF/Decals/stencil.rsi/stencil_Asterix.png new file mode 100644 index 00000000000..37ca93425fa Binary files /dev/null and b/Resources/Textures/_NF/Decals/stencil.rsi/stencil_Asterix.png differ diff --git a/Resources/Textures/_NF/Decals/stencil.rsi/stencil_Dash.png b/Resources/Textures/_NF/Decals/stencil.rsi/stencil_Dash.png new file mode 100644 index 00000000000..c77f630a77c Binary files /dev/null and b/Resources/Textures/_NF/Decals/stencil.rsi/stencil_Dash.png differ diff --git a/Resources/Textures/_NF/Decals/stencil.rsi/stencil_Equals.png b/Resources/Textures/_NF/Decals/stencil.rsi/stencil_Equals.png new file mode 100644 index 00000000000..caefdf09947 Binary files /dev/null and b/Resources/Textures/_NF/Decals/stencil.rsi/stencil_Equals.png differ diff --git a/Resources/Textures/_NF/Decals/stencil.rsi/stencil_Exclaim.png b/Resources/Textures/_NF/Decals/stencil.rsi/stencil_Exclaim.png new file mode 100644 index 00000000000..9b7024bc539 Binary files /dev/null and b/Resources/Textures/_NF/Decals/stencil.rsi/stencil_Exclaim.png differ diff --git a/Resources/Textures/_NF/Decals/stencil.rsi/stencil_Hash.png b/Resources/Textures/_NF/Decals/stencil.rsi/stencil_Hash.png new file mode 100644 index 00000000000..1e22e4b779e Binary files /dev/null and b/Resources/Textures/_NF/Decals/stencil.rsi/stencil_Hash.png differ diff --git a/Resources/Textures/_NF/Decals/stencil.rsi/stencil_Multiocular.png b/Resources/Textures/_NF/Decals/stencil.rsi/stencil_Multiocular.png new file mode 100644 index 00000000000..284401a1880 Binary files /dev/null and b/Resources/Textures/_NF/Decals/stencil.rsi/stencil_Multiocular.png differ diff --git a/Resources/Textures/_NF/Decals/stencil.rsi/stencil_Plus.png b/Resources/Textures/_NF/Decals/stencil.rsi/stencil_Plus.png new file mode 100644 index 00000000000..e50358d2840 Binary files /dev/null and b/Resources/Textures/_NF/Decals/stencil.rsi/stencil_Plus.png differ diff --git a/Resources/Textures/_NF/Decals/stencil.rsi/stencil_Question.png b/Resources/Textures/_NF/Decals/stencil.rsi/stencil_Question.png new file mode 100644 index 00000000000..63fd0ad8f2f Binary files /dev/null and b/Resources/Textures/_NF/Decals/stencil.rsi/stencil_Question.png differ diff --git a/Resources/Textures/_NF/Decals/stencil.rsi/stencil_Speso.png b/Resources/Textures/_NF/Decals/stencil.rsi/stencil_Speso.png new file mode 100644 index 00000000000..a6c97f78596 Binary files /dev/null and b/Resources/Textures/_NF/Decals/stencil.rsi/stencil_Speso.png differ diff --git a/Resources/Textures/_NF/Objects/Fun/magic_spraypainter.rsi/icon-inhand-left.png b/Resources/Textures/_NF/Objects/Fun/magic_spraypainter.rsi/icon-inhand-left.png new file mode 100644 index 00000000000..d3f64053db2 Binary files /dev/null and b/Resources/Textures/_NF/Objects/Fun/magic_spraypainter.rsi/icon-inhand-left.png differ diff --git a/Resources/Textures/_NF/Objects/Fun/magic_spraypainter.rsi/icon-inhand-right.png b/Resources/Textures/_NF/Objects/Fun/magic_spraypainter.rsi/icon-inhand-right.png new file mode 100644 index 00000000000..c5ce26d90dd Binary files /dev/null and b/Resources/Textures/_NF/Objects/Fun/magic_spraypainter.rsi/icon-inhand-right.png differ diff --git a/Resources/Textures/_NF/Objects/Fun/magic_spraypainter.rsi/icon.png b/Resources/Textures/_NF/Objects/Fun/magic_spraypainter.rsi/icon.png new file mode 100644 index 00000000000..9c4ebda950b Binary files /dev/null and b/Resources/Textures/_NF/Objects/Fun/magic_spraypainter.rsi/icon.png differ diff --git a/Resources/Textures/_NF/Objects/Fun/magic_spraypainter.rsi/meta.json b/Resources/Textures/_NF/Objects/Fun/magic_spraypainter.rsi/meta.json new file mode 100644 index 00000000000..79c0a4915a9 --- /dev/null +++ b/Resources/Textures/_NF/Objects/Fun/magic_spraypainter.rsi/meta.json @@ -0,0 +1,45 @@ +{ + "version": 1, + "license": "CC-BY-SA-3.0", + "copyright": "Taken from https://github.com/tgstation/tgstation at commit a21274e56ae84b2c96e8b6beeca805df3d5402e8, Inhand sprites by onesch, ammo by Paradoxmi (Discord), edited and animated by Worldwaker (github).", + "size": { + "x": 32, + "y": 32 + }, + "states": [ + { + "name": "icon", + "delays": [ + [ + 0.2, + 0.2, + 0.2, + 0.2, + 0.2, + 0.2, + 0.2 + ] + ] + }, + { + "name": "icon-inhand-left", + "directions": 4, + "delays": [ + [ 0.5, 0.5, 0.5 ], + [ 0.5, 0.5, 0.5 ], + [ 0.5, 0.5, 0.5 ], + [ 0.5, 0.5, 0.5 ] + ] + }, + { + "name": "icon-inhand-right", + "directions": 4, + "delays": [ + [ 0.5, 0.5, 0.5 ], + [ 0.5, 0.5, 0.5 ], + [ 0.5, 0.5, 0.5 ], + [ 0.5, 0.5, 0.5 ] + ] + } + ] +} diff --git a/Resources/Textures/_Null/Objects/Fun/eeble.rsi/eeble.png b/Resources/Textures/_Null/Objects/Fun/eeble.rsi/eeble.png new file mode 100644 index 00000000000..974d6a83da1 Binary files /dev/null and b/Resources/Textures/_Null/Objects/Fun/eeble.rsi/eeble.png differ diff --git a/Resources/Textures/_Null/Objects/Fun/eeble.rsi/meta.json b/Resources/Textures/_Null/Objects/Fun/eeble.rsi/meta.json new file mode 100644 index 00000000000..0e70b283ed0 --- /dev/null +++ b/Resources/Textures/_Null/Objects/Fun/eeble.rsi/meta.json @@ -0,0 +1,14 @@ +{ + "version": 1, + "license": "CC-BY-SA-3.0", + "copyright": "LukeZurg22 for Vault Station & Null Sector", + "size": { + "x": 8, + "y": 8 + }, + "states": [ + { + "name": "eeble" + } + ] +} diff --git a/Resources/Textures/_Null/Structures/Walls/scrap.rsi/full.png b/Resources/Textures/_Null/Structures/Walls/scrap.rsi/full.png new file mode 100644 index 00000000000..1432c676f71 Binary files /dev/null and b/Resources/Textures/_Null/Structures/Walls/scrap.rsi/full.png differ diff --git a/Resources/Textures/_Null/Structures/Walls/scrap.rsi/meta.json b/Resources/Textures/_Null/Structures/Walls/scrap.rsi/meta.json new file mode 100644 index 00000000000..1fd4e38556a --- /dev/null +++ b/Resources/Textures/_Null/Structures/Walls/scrap.rsi/meta.json @@ -0,0 +1 @@ +{"version": 1, "size": {"x": 32, "y": 32}, "license": "CC-BY-SA-3.0", "copyright": "https://github.com/vgstation-coders/vgstation13/raw/99cc2ab62d65a3a7b554dc7b21ff5f57c835f973/icons/turf/walls.dmi temporary placeholder for new scrap walls", "states": [{"name": "full"}, {"name": "scrap0", "directions": 4}, {"name": "scrap1", "directions": 4}, {"name": "scrap2", "directions": 4}, {"name": "scrap3", "directions": 4}, {"name": "scrap4", "directions": 4}, {"name": "scrap5", "directions": 4}, {"name": "scrap6", "directions": 4}, {"name": "scrap7", "directions": 4}]} diff --git a/Resources/Textures/_Null/Structures/Walls/scrap.rsi/scrap0.png b/Resources/Textures/_Null/Structures/Walls/scrap.rsi/scrap0.png new file mode 100644 index 00000000000..c17ebc0c9e2 Binary files /dev/null and b/Resources/Textures/_Null/Structures/Walls/scrap.rsi/scrap0.png differ diff --git a/Resources/Textures/_Null/Structures/Walls/scrap.rsi/scrap1.png b/Resources/Textures/_Null/Structures/Walls/scrap.rsi/scrap1.png new file mode 100644 index 00000000000..8a7f7e774fb Binary files /dev/null and b/Resources/Textures/_Null/Structures/Walls/scrap.rsi/scrap1.png differ diff --git a/Resources/Textures/_Null/Structures/Walls/scrap.rsi/scrap2.png b/Resources/Textures/_Null/Structures/Walls/scrap.rsi/scrap2.png new file mode 100644 index 00000000000..c17ebc0c9e2 Binary files /dev/null and b/Resources/Textures/_Null/Structures/Walls/scrap.rsi/scrap2.png differ diff --git a/Resources/Textures/_Null/Structures/Walls/scrap.rsi/scrap3.png b/Resources/Textures/_Null/Structures/Walls/scrap.rsi/scrap3.png new file mode 100644 index 00000000000..8a7f7e774fb Binary files /dev/null and b/Resources/Textures/_Null/Structures/Walls/scrap.rsi/scrap3.png differ diff --git a/Resources/Textures/_Null/Structures/Walls/scrap.rsi/scrap4.png b/Resources/Textures/_Null/Structures/Walls/scrap.rsi/scrap4.png new file mode 100644 index 00000000000..ff6c5e064a1 Binary files /dev/null and b/Resources/Textures/_Null/Structures/Walls/scrap.rsi/scrap4.png differ diff --git a/Resources/Textures/_Null/Structures/Walls/scrap.rsi/scrap5.png b/Resources/Textures/_Null/Structures/Walls/scrap.rsi/scrap5.png new file mode 100644 index 00000000000..f77fd38c4f3 Binary files /dev/null and b/Resources/Textures/_Null/Structures/Walls/scrap.rsi/scrap5.png differ diff --git a/Resources/Textures/_Null/Structures/Walls/scrap.rsi/scrap6.png b/Resources/Textures/_Null/Structures/Walls/scrap.rsi/scrap6.png new file mode 100644 index 00000000000..ff6c5e064a1 Binary files /dev/null and b/Resources/Textures/_Null/Structures/Walls/scrap.rsi/scrap6.png differ diff --git a/Resources/Textures/_Null/Structures/Walls/scrap.rsi/scrap7.png b/Resources/Textures/_Null/Structures/Walls/scrap.rsi/scrap7.png new file mode 100644 index 00000000000..a213f292bed Binary files /dev/null and b/Resources/Textures/_Null/Structures/Walls/scrap.rsi/scrap7.png differ diff --git a/Resources/Textures/_Null/Structures/pressure_plate.rsi/icon.png b/Resources/Textures/_Null/Structures/pressure_plate.rsi/icon.png index c6c622a7c76..2fe3b9de860 100644 Binary files a/Resources/Textures/_Null/Structures/pressure_plate.rsi/icon.png and b/Resources/Textures/_Null/Structures/pressure_plate.rsi/icon.png differ diff --git a/Resources/Textures/_Null/Structures/pressure_plate.rsi/meta.json b/Resources/Textures/_Null/Structures/pressure_plate.rsi/meta.json index 667f11b5c3f..78fa280adf4 100644 --- a/Resources/Textures/_Null/Structures/pressure_plate.rsi/meta.json +++ b/Resources/Textures/_Null/Structures/pressure_plate.rsi/meta.json @@ -1,7 +1,7 @@ { "version": 1, "license": "CC0-1.0", - "copyright": "By LukeZurg22", + "copyright": "By Sketch for Null Sector & TC14", "size": { "x": 32, "y": 32 diff --git a/Resources/Textures/_Null/Structures/pressure_plate.rsi/pressed.png b/Resources/Textures/_Null/Structures/pressure_plate.rsi/pressed.png index b386a7ad6ff..a1c0e29be99 100644 Binary files a/Resources/Textures/_Null/Structures/pressure_plate.rsi/pressed.png and b/Resources/Textures/_Null/Structures/pressure_plate.rsi/pressed.png differ diff --git a/Resources/Textures/_Null/Structures/warper.rsi/meta.json b/Resources/Textures/_Null/Structures/warper.rsi/meta.json new file mode 100644 index 00000000000..66fefecfc0f --- /dev/null +++ b/Resources/Textures/_Null/Structures/warper.rsi/meta.json @@ -0,0 +1,32 @@ +{ + "version": 1, + "license": "CC-BY-SA-3.0", + "copyright": "Copyright 2023 Jonas Ljunsgtröm", + "size": { + "x": 32, + "y": 64 + }, + "states": [ + { + "name": "warningtape_down", + "delays": [ + [ + 0.2, + 0.2, + 0.2, + 0.2, + 0.2 + ] + ] + }, + { + "name": "stair_down" + }, + { + "name": "stair_up" + }, + { + "name": "warningtape_up" + } + ] +} diff --git a/Resources/Textures/_Null/Structures/warper.rsi/stair_down.png b/Resources/Textures/_Null/Structures/warper.rsi/stair_down.png new file mode 100644 index 00000000000..d85b34ae25a Binary files /dev/null and b/Resources/Textures/_Null/Structures/warper.rsi/stair_down.png differ diff --git a/Resources/Textures/_Null/Structures/warper.rsi/stair_up.png b/Resources/Textures/_Null/Structures/warper.rsi/stair_up.png new file mode 100644 index 00000000000..1507e0f0554 Binary files /dev/null and b/Resources/Textures/_Null/Structures/warper.rsi/stair_up.png differ diff --git a/Resources/Textures/_Null/Structures/warper.rsi/warningtape_down.png b/Resources/Textures/_Null/Structures/warper.rsi/warningtape_down.png new file mode 100644 index 00000000000..4cdbd7229e1 Binary files /dev/null and b/Resources/Textures/_Null/Structures/warper.rsi/warningtape_down.png differ diff --git a/Resources/Textures/_Null/Structures/warper.rsi/warningtape_up.png b/Resources/Textures/_Null/Structures/warper.rsi/warningtape_up.png new file mode 100644 index 00000000000..fedbfddd27b Binary files /dev/null and b/Resources/Textures/_Null/Structures/warper.rsi/warningtape_up.png differ diff --git a/Resources/Textures/_TC14/Structures/Walls/stone.rsi/full.png b/Resources/Textures/_TC14/Structures/Walls/stone.rsi/full.png new file mode 100644 index 00000000000..1432c676f71 Binary files /dev/null and b/Resources/Textures/_TC14/Structures/Walls/stone.rsi/full.png differ diff --git a/Resources/Textures/_TC14/Structures/Walls/stone.rsi/meta.json b/Resources/Textures/_TC14/Structures/Walls/stone.rsi/meta.json new file mode 100644 index 00000000000..69097998ac9 --- /dev/null +++ b/Resources/Textures/_TC14/Structures/Walls/stone.rsi/meta.json @@ -0,0 +1 @@ +{"version": 1, "size": {"x": 32, "y": 32}, "license": "CC-BY-SA-3.0", "copyright": "https://github.com/vgstation-coders/vgstation13/raw/99cc2ab62d65a3a7b554dc7b21ff5f57c835f973/icons/turf/walls.dmi", "states": [{"name": "full"}, {"name": "stone0", "directions": 4}, {"name": "stone1", "directions": 4}, {"name": "stone2", "directions": 4}, {"name": "stone3", "directions": 4}, {"name": "stone4", "directions": 4}, {"name": "stone5", "directions": 4}, {"name": "stone6", "directions": 4}, {"name": "stone7", "directions": 4}]} diff --git a/Resources/Textures/_TC14/Structures/Walls/stone.rsi/stone0.png b/Resources/Textures/_TC14/Structures/Walls/stone.rsi/stone0.png new file mode 100644 index 00000000000..c17ebc0c9e2 Binary files /dev/null and b/Resources/Textures/_TC14/Structures/Walls/stone.rsi/stone0.png differ diff --git a/Resources/Textures/_TC14/Structures/Walls/stone.rsi/stone1.png b/Resources/Textures/_TC14/Structures/Walls/stone.rsi/stone1.png new file mode 100644 index 00000000000..8a7f7e774fb Binary files /dev/null and b/Resources/Textures/_TC14/Structures/Walls/stone.rsi/stone1.png differ diff --git a/Resources/Textures/_TC14/Structures/Walls/stone.rsi/stone2.png b/Resources/Textures/_TC14/Structures/Walls/stone.rsi/stone2.png new file mode 100644 index 00000000000..c17ebc0c9e2 Binary files /dev/null and b/Resources/Textures/_TC14/Structures/Walls/stone.rsi/stone2.png differ diff --git a/Resources/Textures/_TC14/Structures/Walls/stone.rsi/stone3.png b/Resources/Textures/_TC14/Structures/Walls/stone.rsi/stone3.png new file mode 100644 index 00000000000..8a7f7e774fb Binary files /dev/null and b/Resources/Textures/_TC14/Structures/Walls/stone.rsi/stone3.png differ diff --git a/Resources/Textures/_TC14/Structures/Walls/stone.rsi/stone4.png b/Resources/Textures/_TC14/Structures/Walls/stone.rsi/stone4.png new file mode 100644 index 00000000000..ff6c5e064a1 Binary files /dev/null and b/Resources/Textures/_TC14/Structures/Walls/stone.rsi/stone4.png differ diff --git a/Resources/Textures/_TC14/Structures/Walls/stone.rsi/stone5.png b/Resources/Textures/_TC14/Structures/Walls/stone.rsi/stone5.png new file mode 100644 index 00000000000..f77fd38c4f3 Binary files /dev/null and b/Resources/Textures/_TC14/Structures/Walls/stone.rsi/stone5.png differ diff --git a/Resources/Textures/_TC14/Structures/Walls/stone.rsi/stone6.png b/Resources/Textures/_TC14/Structures/Walls/stone.rsi/stone6.png new file mode 100644 index 00000000000..ff6c5e064a1 Binary files /dev/null and b/Resources/Textures/_TC14/Structures/Walls/stone.rsi/stone6.png differ diff --git a/Resources/Textures/_TC14/Structures/Walls/stone.rsi/stone7.png b/Resources/Textures/_TC14/Structures/Walls/stone.rsi/stone7.png new file mode 100644 index 00000000000..a213f292bed Binary files /dev/null and b/Resources/Textures/_TC14/Structures/Walls/stone.rsi/stone7.png differ