--- /dev/null
+using System.Linq;
+using Robust.Client.GameObjects;
+using static Robust.Client.GameObjects.SpriteComponent;
+using Content.Shared.Clothing;
+using Content.Shared.Hands;
+using Content.Shared.Paint;
+using Robust.Client.Graphics;
+using Robust.Shared.Prototypes;
+
+namespace Content.Client.Paint
+{
+ public sealed class PaintedVisualizerSystem : VisualizerSystem<PaintedComponent>
+ {
+ /// <summary>
+ /// Visualizer for Paint which applies a shader and colors the entity.
+ /// </summary>
+
+ [Dependency] private readonly SharedAppearanceSystem _appearance = default!;
+ [Dependency] private readonly IPrototypeManager _protoMan = default!;
+
+ public ShaderInstance? Shader; // in Robust.Client.Graphics so cannot move to shared component.
+
+ public override void Initialize()
+ {
+ base.Initialize();
+
+ SubscribeLocalEvent<PaintedComponent, HeldVisualsUpdatedEvent>(OnHeldVisualsUpdated);
+ SubscribeLocalEvent<PaintedComponent, ComponentShutdown>(OnShutdown);
+ SubscribeLocalEvent<PaintedComponent, EquipmentVisualsUpdatedEvent>(OnEquipmentVisualsUpdated);
+ }
+
+ protected override void OnAppearanceChange(EntityUid uid, PaintedComponent component, ref AppearanceChangeEvent args)
+ {
+ // ShaderPrototype sadly in Robust.Client, cannot move to shared component.
+ Shader = _protoMan.Index<ShaderPrototype>(component.ShaderName).Instance();
+
+ if (args.Sprite == null)
+ return;
+
+ if (!_appearance.TryGetData<bool>(uid, PaintVisuals.Painted, out bool isPainted))
+ return;
+
+ var sprite = args.Sprite;
+
+
+ foreach (var spriteLayer in sprite.AllLayers)
+ {
+ if (spriteLayer is not Layer layer)
+ continue;
+
+ if (layer.Shader == null) // If shader isn't null we dont want to replace the original shader.
+ {
+ layer.Shader = Shader;
+ layer.Color = component.Color;
+ }
+ }
+ }
+
+ private void OnHeldVisualsUpdated(EntityUid uid, PaintedComponent component, HeldVisualsUpdatedEvent args)
+ {
+ if (args.RevealedLayers.Count == 0)
+ return;
+
+ if (!TryComp(args.User, out SpriteComponent? sprite))
+ return;
+
+ foreach (var revealed in args.RevealedLayers)
+ {
+ if (!sprite.LayerMapTryGet(revealed, out var layer) || sprite[layer] is not Layer notlayer)
+ continue;
+
+ sprite.LayerSetShader(layer, component.ShaderName);
+ sprite.LayerSetColor(layer, component.Color);
+ }
+ }
+
+ private void OnEquipmentVisualsUpdated(EntityUid uid, PaintedComponent component, EquipmentVisualsUpdatedEvent args)
+ {
+ if (args.RevealedLayers.Count == 0)
+ return;
+
+ if (!TryComp(args.Equipee, out SpriteComponent? sprite))
+ return;
+
+ foreach (var revealed in args.RevealedLayers)
+ {
+ if (!sprite.LayerMapTryGet(revealed, out var layer) || sprite[layer] is not Layer notlayer)
+ continue;
+
+ sprite.LayerSetShader(layer, component.ShaderName);
+ sprite.LayerSetColor(layer, component.Color);
+ }
+ }
+
+ private void OnShutdown(EntityUid uid, PaintedComponent component, ref ComponentShutdown args)
+ {
+ if (!TryComp(uid, out SpriteComponent? sprite))
+ return;
+
+ component.BeforeColor = sprite.Color;
+ Shader = _protoMan.Index<ShaderPrototype>(component.ShaderName).Instance();
+
+ if (!Terminating(uid))
+ {
+ foreach (var spriteLayer in sprite.AllLayers)
+ {
+ if (spriteLayer is not Layer layer)
+ continue;
+
+ if (layer.Shader == Shader) // If shader isn't same as one in component we need to ignore it.
+ {
+ layer.Shader = null;
+ if (layer.Color == component.Color) // If color isn't the same as one in component we don't want to change it.
+ layer.Color = component.BeforeColor;
+ }
+ }
+ }
+ }
+ }
+}
--- /dev/null
+using Content.Shared.Popups;
+using Content.Shared.Paint;
+using Content.Shared.Sprite;
+using Content.Shared.DoAfter;
+using Content.Shared.Interaction;
+using Content.Server.Chemistry.Containers.EntitySystems;
+using Robust.Shared.Audio.Systems;
+using Content.Shared.Humanoid;
+using Robust.Shared.Utility;
+using Content.Shared.Verbs;
+using Content.Shared.SubFloor;
+using Content.Server.Nutrition.Components;
+using Content.Shared.Inventory;
+using Content.Server.Nutrition.EntitySystems;
+
+namespace Content.Server.Paint;
+
+/// <summary>
+/// Colors target and consumes reagent on each color success.
+/// </summary>
+public sealed class PaintSystem : SharedPaintSystem
+{
+ [Dependency] private readonly SharedAudioSystem _audio = default!;
+ [Dependency] private readonly SharedPopupSystem _popup = default!;
+ [Dependency] private readonly SolutionContainerSystem _solutionContainer = default!;
+ [Dependency] private readonly SharedAppearanceSystem _appearanceSystem = default!;
+ [Dependency] private readonly SharedDoAfterSystem _doAfterSystem = default!;
+ [Dependency] private readonly InventorySystem _inventory = default!;
+ [Dependency] private readonly OpenableSystem _openable = default!;
+
+ public override void Initialize()
+ {
+ base.Initialize();
+
+ SubscribeLocalEvent<PaintComponent, AfterInteractEvent>(OnInteract);
+ SubscribeLocalEvent<PaintComponent, PaintDoAfterEvent>(OnPaint);
+ SubscribeLocalEvent<PaintComponent, GetVerbsEvent<UtilityVerb>>(OnPaintVerb);
+ }
+
+ private void OnInteract(EntityUid uid, PaintComponent component, AfterInteractEvent args)
+ {
+ if (!args.CanReach)
+ return;
+
+ if (args.Target is not { Valid: true } target)
+ return;
+
+ PrepPaint(uid, component, target, args.User);
+ }
+
+ private void OnPaintVerb(EntityUid uid, PaintComponent component, GetVerbsEvent<UtilityVerb> args)
+ {
+ if (!args.CanInteract || !args.CanAccess)
+ return;
+
+ var paintText = Loc.GetString("paint-verb");
+
+ var verb = new UtilityVerb()
+ {
+ Act = () =>
+ {
+ PrepPaint(uid, component, args.Target, args.User);
+ },
+
+ Text = paintText,
+ Icon = new SpriteSpecifier.Texture(new("/Textures/Interface/VerbIcons/paint.svg.192dpi.png"))
+ };
+ args.Verbs.Add(verb);
+ }
+ private void PrepPaint(EntityUid uid, PaintComponent component, EntityUid target, EntityUid user)
+ {
+
+ var doAfterEventArgs = new DoAfterArgs(EntityManager, user, component.Delay, new PaintDoAfterEvent(), uid, target: target, used: uid)
+ {
+ BreakOnTargetMove = true,
+ BreakOnUserMove = true,
+ BreakOnDamage = true,
+ NeedHand = true,
+ BreakOnHandChange = true
+ };
+
+ if (!_doAfterSystem.TryStartDoAfter(doAfterEventArgs))
+ return;
+ }
+
+ private void OnPaint(Entity<PaintComponent> entity, ref PaintDoAfterEvent args)
+ {
+ if (args.Target == null || args.Used == null)
+ return;
+
+ if (args.Handled || args.Cancelled)
+ return;
+
+ if (args.Target is not { Valid: true } target)
+ return;
+
+ if (!_openable.IsOpen(entity))
+ {
+ _popup.PopupEntity(Loc.GetString("paint-closed", ("used", args.Used)), args.User, args.User, PopupType.Medium);
+ return;
+ }
+
+ if (HasComp<PaintedComponent>(target) || HasComp<RandomSpriteComponent>(target))
+ {
+ _popup.PopupEntity(Loc.GetString("paint-failure-painted", ("target", args.Target)), args.User, args.User, PopupType.Medium);
+ return;
+ }
+
+ if (!entity.Comp.Blacklist?.IsValid(target, EntityManager) != true || HasComp<HumanoidAppearanceComponent>(target) || HasComp<SubFloorHideComponent>(target))
+ {
+ _popup.PopupEntity(Loc.GetString("paint-failure", ("target", args.Target)), args.User, args.User, PopupType.Medium);
+ return;
+ }
+
+
+ if (TryPaint(entity, target))
+ {
+ EnsureComp<PaintedComponent>(target, out PaintedComponent? paint);
+ EnsureComp<AppearanceComponent>(target);
+
+ paint.Color = entity.Comp.Color; // set the target color to the color specified in the spray paint yml.
+ _audio.PlayPvs(entity.Comp.Spray, entity);
+ paint.Enabled = true;
+
+ if (HasComp<InventoryComponent>(target)) // Paint any clothing the target is wearing.
+ {
+ if (_inventory.TryGetSlots(target, out var slotDefinitions))
+ {
+ foreach (var slot in slotDefinitions)
+ {
+ if (!_inventory.TryGetSlotEntity(target, slot.Name, out var slotEnt))
+ continue;
+
+ if (slotEnt == null)
+ return;
+
+ if (HasComp<PaintedComponent>(slotEnt.Value) || !entity.Comp.Blacklist?.IsValid(slotEnt.Value, EntityManager) != true
+ || HasComp<RandomSpriteComponent>(slotEnt.Value) || HasComp<HumanoidAppearanceComponent>(slotEnt.Value))
+ return;
+
+ EnsureComp<PaintedComponent>(slotEnt.Value, out PaintedComponent? slotpaint);
+ EnsureComp<AppearanceComponent>(slotEnt.Value);
+ slotpaint.Color = entity.Comp.Color;
+ _appearanceSystem.SetData(slotEnt.Value, PaintVisuals.Painted, true);
+ Dirty(slotEnt.Value, slotpaint);
+ }
+ }
+ }
+
+ _popup.PopupEntity(Loc.GetString("paint-success", ("target", args.Target)), args.User, args.User, PopupType.Medium);
+ _appearanceSystem.SetData(target, PaintVisuals.Painted, true);
+ Dirty(target, paint);
+ args.Handled = true;
+ return;
+ }
+
+ if (!TryPaint(entity, target))
+ {
+ _popup.PopupEntity(Loc.GetString("paint-empty", ("used", args.Used)), args.User, args.User, PopupType.Medium);
+ return;
+ }
+ }
+
+ private bool TryPaint(Entity<PaintComponent> reagent, EntityUid target)
+ {
+ if (HasComp<HumanoidAppearanceComponent>(target) || HasComp<SubFloorHideComponent>(target))
+ return false;
+
+ if (_solutionContainer.TryGetSolution(reagent.Owner, reagent.Comp.Solution, out _, out var solution))
+ {
+ var quantity = solution.RemoveReagent(reagent.Comp.Reagent, reagent.Comp.ConsumptionUnit);
+ if (quantity > 0)// checks quantity of solution is more than 0.
+ return true;
+
+ if (quantity < 1)
+ return false;
+ }
+ return false;
+ }
+}
_adminLogger.Add(LogType.EntitySpawn, LogImpact.Low, $"{ToPrettyString(args.User)} used {ToPrettyString(uid)} which spawned {ToPrettyString(entityToPlaceInHands.Value)}");
}
- if (component.Sound != null)
- {
- _audio.PlayPvs(component.Sound, uid);
- }
-
component.Uses--;
// Delete entity only if component was successfully used
if (entityToPlaceInHands != null)
{
_hands.PickupOrDrop(args.User, entityToPlaceInHands.Value);
+ _audio.PlayPvs(component.Sound, entityToPlaceInHands.Value);
}
}
}
--- /dev/null
+using Content.Shared.Chemistry.Reagent;
+using Content.Shared.FixedPoint;
+using Robust.Shared.Audio;
+using Content.Shared.Whitelist;
+using Robust.Shared.Prototypes;
+using Robust.Shared.GameStates;
+
+namespace Content.Shared.Paint;
+
+/// <summary>
+/// Entity when used on another entity will paint target entity.
+/// </summary>
+[RegisterComponent, NetworkedComponent, AutoGenerateComponentState]
+[Access(typeof(SharedPaintSystem))]
+public sealed partial class PaintComponent : Component
+{
+ /// <summary>
+ /// Noise made when paint applied.
+ /// </summary>
+ [DataField]
+ public SoundSpecifier Spray = new SoundPathSpecifier("/Audio/Effects/spray2.ogg");
+
+ /// <summary>
+ /// Solution on the entity that contains the paint.
+ /// </summary>
+ [DataField]
+ public string Solution = "drink";
+
+ /// <summary>
+ /// How long the doafter will take.
+ /// </summary>
+ [DataField]
+ public int Delay = 2;
+
+ /// <summary>
+ /// Reagent that will be used as paint.
+ /// </summary>
+ [DataField, AutoNetworkedField]
+ public ProtoId<ReagentPrototype> Reagent = "SpaceGlue";
+
+ /// <summary>
+ /// Color that the painting entity will instruct the painted entity to be.
+ /// </summary>
+ [DataField, AutoNetworkedField]
+ public Color Color = Color.FromHex("#c62121");
+
+ [DataField, ViewVariables(VVAccess.ReadWrite)]
+ public EntityWhitelist? Blacklist;
+ /// <summary>
+ /// Reagent consumption per use.
+ /// </summary>
+ [DataField]
+ public FixedPoint2 ConsumptionUnit = FixedPoint2.New(5);
+
+ /// <summary>
+ /// Duration per unit
+ /// </summary>
+ [DataField]
+ public TimeSpan DurationPerUnit = TimeSpan.FromSeconds(6);
+}
--- /dev/null
+using Content.Shared.DoAfter;
+using Robust.Shared.Serialization;
+
+namespace Content.Shared.Paint;
+
+[Serializable, NetSerializable]
+public sealed partial class PaintDoAfterEvent : SimpleDoAfterEvent
+{
+}
--- /dev/null
+using Robust.Shared.GameStates;
+using Robust.Shared.Audio;
+
+namespace Content.Shared.Paint;
+
+/// <summary>
+/// Removes paint from an entity that was painted with spray paint.
+/// </summary>
+[RegisterComponent, NetworkedComponent]
+[Access(typeof(PaintRemoverSystem))]
+public sealed partial class PaintRemoverComponent : Component
+{
+ /// <summary>
+ /// Sound when target is cleaned.
+ /// </summary>
+ [DataField]
+ public SoundSpecifier Sound = new SoundPathSpecifier("/Audio/Effects/Fluids/watersplash.ogg");
+
+ /// <summary>
+ /// DoAfter wait time.
+ /// </summary>
+ [DataField]
+ public float CleanDelay = 2f;
+}
--- /dev/null
+using Content.Shared.DoAfter;
+using Robust.Shared.Serialization;
+
+namespace Content.Shared.Paint;
+
+[Serializable, NetSerializable]
+public sealed partial class PaintRemoverDoAfterEvent : SimpleDoAfterEvent
+{
+}
--- /dev/null
+using Content.Shared.Popups;
+using Content.Shared.Interaction;
+using Content.Shared.DoAfter;
+using Content.Shared.Verbs;
+using Content.Shared.Sprite;
+using Robust.Shared.Audio.Systems;
+using Robust.Shared.Timing;
+
+namespace Content.Shared.Paint;
+
+/// <summary>
+/// Removes paint from an entity.
+/// </summary>
+public sealed class PaintRemoverSystem : SharedPaintSystem
+{
+ [Dependency] private readonly SharedPopupSystem _popup = default!;
+ [Dependency] private readonly SharedDoAfterSystem _doAfter = default!;
+ [Dependency] private readonly SharedAudioSystem _audio = default!;
+ [Dependency] private readonly IGameTiming _timing = default!;
+ [Dependency] private readonly SharedAppearanceSystem _appearanceSystem = default!;
+
+ public override void Initialize()
+ {
+ base.Initialize();
+
+ SubscribeLocalEvent<PaintRemoverComponent, AfterInteractEvent>(OnInteract);
+ SubscribeLocalEvent<PaintRemoverComponent, PaintRemoverDoAfterEvent>(OnDoAfter);
+ SubscribeLocalEvent<PaintRemoverComponent, GetVerbsEvent<UtilityVerb>>(OnPaintRemoveVerb);
+ }
+
+ // When entity is painted, remove paint from that entity.
+ private void OnInteract(EntityUid uid, PaintRemoverComponent component, AfterInteractEvent args)
+ {
+ if (args.Handled)
+ return;
+
+ if (!args.CanReach || args.Target is not { Valid: true } target || !HasComp<PaintedComponent>(target))
+ return;
+
+ _doAfter.TryStartDoAfter(new DoAfterArgs(EntityManager, args.User, component.CleanDelay, new PaintRemoverDoAfterEvent(), uid, args.Target, uid)
+ {
+ BreakOnUserMove = true,
+ BreakOnDamage = true,
+ BreakOnTargetMove = true,
+ MovementThreshold = 1.0f,
+ });
+ args.Handled = true;
+ }
+
+ private void OnDoAfter(EntityUid uid, PaintRemoverComponent component, DoAfterEvent args)
+ {
+ if (args.Cancelled || args.Handled || args.Args.Target == null)
+ return;
+
+ if (args.Target is not { Valid: true } target)
+ return;
+
+ if (!TryComp(target, out PaintedComponent? paint))
+ return;
+
+ paint.Enabled = false;
+ _audio.PlayPredicted(component.Sound, target, args.User);
+ _popup.PopupClient(Loc.GetString("paint-removed", ("target", target)), args.User, args.User, PopupType.Medium);
+ _appearanceSystem.SetData(target, PaintVisuals.Painted, false);
+ RemComp<PaintedComponent>(target);
+ Dirty(target, paint);
+
+ args.Handled = true;
+ }
+
+ private void OnPaintRemoveVerb(EntityUid uid, PaintRemoverComponent component, GetVerbsEvent<UtilityVerb> args)
+ {
+ if (!args.CanInteract || !args.CanAccess)
+ return;
+
+ var paintremovalText = Loc.GetString("paint-remove-verb");
+
+ var verb = new UtilityVerb()
+ {
+ Act = () => {
+
+ _doAfter.TryStartDoAfter(new DoAfterArgs(EntityManager, args.User, component.CleanDelay, new PaintRemoverDoAfterEvent(), uid, args.Target, uid)
+ {
+ BreakOnUserMove = true,
+ BreakOnDamage = true,
+ BreakOnTargetMove = true,
+ MovementThreshold = 1.0f,
+ });
+ },
+
+ Text = paintremovalText
+ };
+
+ args.Verbs.Add(verb);
+ }
+}
--- /dev/null
+using Robust.Shared.GameStates;
+using Robust.Shared.Serialization;
+
+namespace Content.Shared.Paint;
+
+/// <summary>
+/// Component applied to target entity when painted.
+/// </summary>
+[RegisterComponent, NetworkedComponent, AutoGenerateComponentState]
+public sealed partial class PaintedComponent : Component
+{
+ /// <summary>
+ /// Color of the paint.
+ /// </summary>
+ [DataField, AutoNetworkedField]
+ public Color Color = Color.FromHex("#2cdbd5");
+
+ /// <summary>
+ /// Used to remove the color when component removed.
+ /// </summary>
+ [DataField, AutoNetworkedField]
+ public Color BeforeColor;
+
+ /// <summary>
+ /// If paint is enabled.
+ /// </summary>
+ [DataField, AutoNetworkedField]
+ public bool Enabled;
+
+ /// <summary>
+ /// Name of the shader.
+ /// </summary>
+ [DataField, AutoNetworkedField]
+ public string ShaderName = "Greyscale";
+}
+
+[Serializable, NetSerializable]
+public enum PaintVisuals : byte
+{
+ Painted,
+}
--- /dev/null
+namespace Content.Shared.Paint;
+
+/// <summary>
+/// Colors target and consumes reagent on each color success.
+/// </summary>
+public abstract class SharedPaintSystem : EntitySystem
+{
+ public virtual void UpdateAppearance(EntityUid uid, PaintedComponent? component = null)
+ {
+ }
+}
using Content.Shared.Doors.Components;
using Content.Shared.Interaction;
using Content.Shared.Popups;
+using Content.Shared.Paint;
using Content.Shared.SprayPainter.Components;
using Content.Shared.SprayPainter.Prototypes;
using Robust.Shared.Audio.Systems;
return;
}
+ RemComp<PaintedComponent>(ent);
+
var doAfterEventArgs = new DoAfterArgs(EntityManager, args.User, painter.AirlockSprayTime, new SprayPainterDoorDoAfterEvent(sprite, style.Department), args.Used, target: ent, used: args.Used)
{
BreakOnTargetMove = true,
--- /dev/null
+paint-success = {THE($target)} has been covered in paint!
+paint-failure = Can't cover {THE($target)} in paint!
+paint-failure-painted = {THE($target)} is already covered in paint!
+paint-empty = {THE($used)} is empty!
+paint-removed = You clean off the paint!
+paint-closed = You must open {THE($used)} first!
+paint-verb = Paint
+paint-remove-verb = Remove Paint
category: cargoproduct-category-name-fun
group: market
+- type: cargoProduct
+ id: FunSprayPaints
+ icon:
+ sprite: Objects/Fun/spraycans.rsi
+ state: death2_cap
+ product: CrateFunSprayPaints
+ cost: 2000
+ category: Fun
+ group: market
+
- type: cargoProduct
id: FunParty
icon:
contents:
- id: SnapPopBox
- id: CrazyGlue
- amount: 2
- id: PlasticBanana
+ - id: FunnyPaint
+ orGroup: Paint
+ prob: 0.5
+ - id: FunnyPaintYellow
+ orGroup: Paint
+ prob: 0.5
- id: WhoopieCushion
- id: ToyHammer
- id: MrChips
- orGroup: GiftPool
+ prob: 0.5
+ orGroup: Dummy
- id: MrDips
- orGroup: Giftpool
+ prob: 0.5
+ orGroup: Dummy
- id: RevolverCapGun
- id: BalloonNT
- id: ClothingShoesClownLarge
amount: 15
prob: 0.05
+- type: entity
+ id: CrateFunSprayPaints
+ name: spray paint crate
+ description: a crate filled with spray paint.
+ parent: CratePlastic
+ suffix: Spray Paint
+ components:
+ - type: StorageFill
+ contents:
+ - id: SprayPaintBlue
+ amount: 2
+ prob: 0.33
+ - id: SprayPaintRed
+ amount: 2
+ prob: 0.33
+ - id: SprayPaintOrange
+ amount: 2
+ prob: 0.33
+ - id: SprayPaintBlack
+ amount: 2
+ prob: 0.33
+ - id: SprayPaintGreen
+ amount: 2
+ prob: 0.33
+ - id: SprayPaintPurple
+ amount: 2
+ prob: 0.33
+ - id: SprayPaintWhite
+ amount: 2
+ prob: 0.33
+ - id: DeathPaint
+ amount: 2
+ - id: DeathPaintTwo
+ amount: 2
+
- type: entity
name: dartboard box set
description: A box with everything you need for a fun game of darts.
prob: 0.25
- id: StrangePill
prob: 0.20
+ - id: DeathPaint
+ prob: 0.05
+ - id: DeathPaintTwo
+ prob: 0.05
- id: DrinkMopwataBottleRandom
prob: 0.20
- id: ModularReceiver
- CrateMaterialPlastic
- CrateMaterialWood
- CrateMaterialPlasteel
+ - CrateFunSprayPaints
- CrateFunArtSupplies
- CrateEngineeringCableLV
- CrateEngineeringCableMV
- MaterialCloth10
- MaterialWoodPlank10
- ResearchDisk
+ - DeathPaint
- Plunger
+ - SprayPaintBlue
+ - SprayPaintRed
+ - SprayPaintGreen
+ - SprayPaintOrange
- TechnologyDisk
- PowerCellMedium
- PowerCellSmall
tags:
- Carp
- DoorBumpOpener
+ - NoPaint
- type: ReplacementAccent
accent: genericAggressive
- type: Speech
- RevenantTheme
- type: Speech
speechVerb: Ghost
+ - type: Tag
+ tags:
+ - NoPaint
- type: Tag
tags:
- CannotSuicide
+ - NoPaint
# From the uplink injector
- type: entity
tags:
- CannotSuicide
- FootstepSound
+ - NoPaint
- type: Inventory
templateId: holoclown
- type: Hands
--- /dev/null
+# Base Paints
+- type: entity
+ parent: BaseItem
+ id: PaintBase
+ name: spray paint
+ description: A tin of spray paint.
+ noSpawn: true
+ components:
+ - type: Appearance
+ - type: Sprite
+ sprite: Objects/Fun/spraycans.rsi
+ state: clown_cap
+ layers:
+ - state: clown_cap
+ map: ["enum.OpenableVisuals.Layer"]
+ - type: Paint
+ consumptionUnit: 10
+ blacklist:
+ tags:
+ - NoPaint
+ - type: Item
+ sprite: Objects/Fun/spraycans.rsi
+ heldPrefix: spray
+ - type: SolutionContainerManager
+ solutions:
+ drink:
+ maxVol: 50
+ reagents:
+ - ReagentId: SpaceGlue
+ Quantity: 50
+ - type: TrashOnSolutionEmpty
+ solution: drink
+ - type: Sealable
+ - type: Openable
+ sound:
+ path: /Audio/Effects/pop_high.ogg
+ closeable: true
+ closeSound:
+ path: /Audio/Effects/pop_high.ogg
+
+# Paints
+
+# funnypaint
+- type: entity
+ parent: PaintBase
+ id: FunnyPaint
+ name: funny paint
+ description: A tin of funny paint, manufactured by Honk! Co.
+ components:
+ - type: Paint
+ color: "#fa74df"
+ - type: Item
+ sprite: Objects/Fun/spraycans.rsi
+ heldPrefix: clown
+ - type: GenericVisualizer
+ visuals:
+ enum.OpenableVisuals.Opened:
+ enum.OpenableVisuals.Layer:
+ True: {state: "clown"}
+ False: {state: "clown_cap"}
+
+- type: entity
+ parent: PaintBase
+ id: FunnyPaintYellow
+ name: funny paint
+ description: A tin of funny paint, manufactured by Honk! Co.
+ components:
+ - type: Paint
+ color: "#d5e028"
+ - type: Item
+ sprite: Objects/Fun/spraycans.rsi
+ heldPrefix: clown
+ - type: Sprite
+ sprite: Objects/Fun/spraycans.rsi
+ state: clown2_cap
+ layers:
+ - state: clown2_cap
+ map: ["enum.OpenableVisuals.Layer"]
+ - type: GenericVisualizer
+ visuals:
+ enum.OpenableVisuals.Opened:
+ enum.OpenableVisuals.Layer:
+ True: {state: "clown2"}
+ False: {state: "clown2_cap"}
+
+#death paint
+- type: entity
+ parent: PaintBase
+ id: DeathPaint
+ components:
+ - type: Paint
+ color: "#ff20c8"
+ - type: Item
+ sprite: Objects/Fun/spraycans.rsi
+ heldPrefix: spray
+ - type: Sprite
+ sprite: Objects/Fun/spraycans.rsi
+ state: death_cap
+ layers:
+ - state: death_cap
+ map: ["enum.OpenableVisuals.Layer"]
+ - type: GenericVisualizer
+ visuals:
+ enum.OpenableVisuals.Opened:
+ enum.OpenableVisuals.Layer:
+ True: {state: "death"}
+ False: {state: "death_cap"}
+
+- type: entity
+ parent: PaintBase
+ id: DeathPaintTwo
+ components:
+ - type: Paint
+ color: "#ff2020"
+ - type: Item
+ sprite: Objects/Fun/spraycans.rsi
+ heldPrefix: spray
+ - type: Sprite
+ sprite: Objects/Fun/spraycans.rsi
+ state: death2_cap
+ layers:
+ - state: death2_cap
+ map: ["enum.OpenableVisuals.Layer"]
+ - type: GenericVisualizer
+ visuals:
+ enum.OpenableVisuals.Opened:
+ enum.OpenableVisuals.Layer:
+ True: {state: "death2"}
+ False: {state: "death2_cap"}
+
+#Sprays
+
+#Blue
+- type: entity
+ parent: PaintBase
+ id: SprayPaintBlue
+ suffix: Blue
+ components:
+ - type: Sprite
+ sprite: Objects/Fun/spraycans.rsi
+ layers:
+ - state: spray
+ map: ["Base"]
+ - state: spray_cap_colors
+ map: ["enum.OpenableVisuals.Layer"]
+ color: "#5890f7"
+ - type: Paint
+ color: "#5890f7"
+ - type: GenericVisualizer
+ visuals:
+ enum.OpenableVisuals.Opened:
+ enum.OpenableVisuals.Layer:
+ True: {state: "spray_colors" , color: "#5890f7"}
+ False: {state: "spray_cap_colors" , color: "#5890f7"}
+
+#Red
+- type: entity
+ parent: PaintBase
+ id: SprayPaintRed
+ suffix: Red
+ components:
+ - type: Sprite
+ sprite: Objects/Fun/spraycans.rsi
+ layers:
+ - state: spray
+ map: ["Base"]
+ - state: spray_cap_colors
+ map: ["enum.OpenableVisuals.Layer"]
+ color: "#ff3b3b"
+ - type: Paint
+ color: "#ff3b3b"
+ - type: GenericVisualizer
+ visuals:
+ enum.OpenableVisuals.Opened:
+ enum.OpenableVisuals.Layer:
+ True: {state: "spray_colors" , color: "#ff3b3b"}
+ False: {state: "spray_cap_colors" , color: "#ff3b3b"}
+
+#Green
+- type: entity
+ parent: PaintBase
+ id: SprayPaintGreen
+ suffix: Green
+ components:
+ - type: Sprite
+ sprite: Objects/Fun/spraycans.rsi
+ layers:
+ - state: spray
+ map: ["Base"]
+ - state: spray_cap_colors
+ map: ["enum.OpenableVisuals.Layer"]
+ color: "#73f170"
+ - type: Paint
+ color: "#73f170"
+ - type: GenericVisualizer
+ visuals:
+ enum.OpenableVisuals.Opened:
+ enum.OpenableVisuals.Layer:
+ True: {state: "spray_colors" , color: "#73f170"}
+ False: {state: "spray_cap_colors" , color: "#73f170"}
+
+#Black
+- type: entity
+ parent: PaintBase
+ id: SprayPaintBlack
+ suffix: Black
+ components:
+ - type: Sprite
+ sprite: Objects/Fun/spraycans.rsi
+ layers:
+ - state: spray
+ map: ["Base"]
+ - state: spray_cap_colors
+ map: ["enum.OpenableVisuals.Layer"]
+ color: "#3a3a3a"
+ - type: Paint
+ color: "#3a3a3a"
+ - type: GenericVisualizer
+ visuals:
+ enum.OpenableVisuals.Opened:
+ enum.OpenableVisuals.Layer:
+ True: {state: "spray_colors" , color: "#3a3a3a"}
+ False: {state: "spray_cap_colors" , color: "#3a3a3a"}
+
+#Orange
+- type: entity
+ parent: PaintBase
+ id: SprayPaintOrange
+ suffix: Orange
+ components:
+ - type: Sprite
+ sprite: Objects/Fun/spraycans.rsi
+ layers:
+ - state: spray
+ map: ["Base"]
+ - state: spray_cap_colors
+ map: ["enum.OpenableVisuals.Layer"]
+ color: "#f6a44b"
+ - type: Paint
+ color: "#f6a44b"
+ - type: GenericVisualizer
+ visuals:
+ enum.OpenableVisuals.Opened:
+ enum.OpenableVisuals.Layer:
+ True: {state: "spray_colors" , color: "#f6a44b"}
+ False: {state: "spray_cap_colors" , color: "#f6a44b"}
+
+#Purple
+- type: entity
+ parent: PaintBase
+ id: SprayPaintPurple
+ suffix: Purple
+ components:
+ - type: Sprite
+ sprite: Objects/Fun/spraycans.rsi
+ layers:
+ - state: spray
+ map: ["Base"]
+ - state: spray_cap_colors
+ map: ["enum.OpenableVisuals.Layer"]
+ color: "#c063f5"
+ - type: Paint
+ color: "#c063f5"
+ - type: GenericVisualizer
+ visuals:
+ enum.OpenableVisuals.Opened:
+ enum.OpenableVisuals.Layer:
+ True: {state: "spray_colors" , color: "#c063f5"}
+ False: {state: "spray_cap_colors" , color: "#c063f5"}
+
+#White
+- type: entity
+ parent: PaintBase
+ id: SprayPaintWhite
+ suffix: White
+ components:
+ - type: Sprite
+ sprite: Objects/Fun/spraycans.rsi
+ layers:
+ - state: spray
+ map: ["Base"]
+ - state: spray_cap_colors
+ map: ["enum.OpenableVisuals.Layer"]
+ color: "#f2f2f2"
+ - type: Paint
+ color: "#f2f2f2"
+ - type: GenericVisualizer
+ visuals:
+ enum.OpenableVisuals.Opened:
+ enum.OpenableVisuals.Layer:
+ True: {state: "spray_colors" , color: "#f2f2f2"}
+ False: {state: "spray_cap_colors" , color: "#f2f2f2"}
- type: Tag
tags:
- Sheet
+ - NoPaint
- type: Material
- type: Damageable
damageContainer: Inorganic
tags:
- Sheet
- Metal
+ - NoPaint
- type: Damageable
damageContainer: Inorganic
damageModifierSet: Metallic
- type: Tag
tags:
- Sheet
+ - NoPaint
- type: Damageable
damageContainer: Inorganic
- type: Destructible
- type: Tag
tags:
- Sheet
+ - NoPaint
- type: entity
parent: SheetPlasma
tags:
- Plastic
- Sheet
+ - NoPaint
- type: Material
- type: PhysicalComposition
materialComposition:
- type: Tag
tags:
- RawMaterial
+ - NoPaint
- type: Damageable
damageContainer: Inorganic
- type: Destructible
Blunt: 5
- type: Stack
count: 1
+ - type: Tag
+ tags:
+ - NoPaint
- type: Damageable
damageContainer: Inorganic
- type: Destructible
solution: soap
- type: DeleteOnSolutionEmpty
solution: soap
+ - type: PaintRemover
- type: FlavorProfile
flavors:
- clean
malus: 0
- type: Reflect
enabled: false
+ - type: Tag
+ tags:
+ - NoPaint
- type: IgnitionSource
temperature: 700
- type: Tag
tags:
- Write
+ - NoPaint
- type: DisarmMalus
malus: 0
sound:
path: /Audio/Ambience/Objects/fireplace.ogg
- type: AlwaysHot
+ - type: Tag
+ tags:
+ - NoPaint
- type: entity
id: LegionnaireBonfire
behaviors:\r
- !type:DoActsBehavior\r
acts: [ "Destruction" ]\r
+ - type: Tag\r
+ tags:\r
+ - NoPaint\r
\r
- type: entity\r
id: HoloFan\r
- type: GuideHelp
guides:
- Botany
+ - type: Tag
+ tags:
+ - NoPaint
- type: entity
parent: hydroponicsTray
- type: Tag
id: NoBlockAnchoring
+- type: Tag
+ id: NoPaint
+
- type: Tag
id: NozzleBackTank
--- /dev/null
+<?xml version="1.0" encoding="UTF-8" standalone="no"?>
+<svg
+ width="24"
+ height="24"
+ viewBox="0 0 24 24"
+ version="1.1"
+ id="svg834"
+ sodipodi:docname="plus.svg"
+ inkscape:version="1.1.2 (b8e25be833, 2022-02-05)"
+ xmlns:inkscape="http://www.inkscape.org/namespaces/inkscape"
+ xmlns:sodipodi="http://sodipodi.sourceforge.net/DTD/sodipodi-0.dtd"
+ xmlns="http://www.w3.org/2000/svg"
+ xmlns:svg="http://www.w3.org/2000/svg">
+ <defs
+ id="defs838" />
+ <sodipodi:namedview
+ id="namedview836"
+ pagecolor="#505050"
+ bordercolor="#eeeeee"
+ borderopacity="1"
+ inkscape:pageshadow="0"
+ inkscape:pageopacity="0"
+ inkscape:pagecheckerboard="0"
+ showgrid="false"
+ inkscape:zoom="33.666667"
+ inkscape:cx="11.985149"
+ inkscape:cy="7.2475248"
+ inkscape:window-width="1920"
+ inkscape:window-height="1017"
+ inkscape:window-x="-8"
+ inkscape:window-y="-8"
+ inkscape:window-maximized="1"
+ inkscape:current-layer="svg834" />
+ <path
+ d="M 22.69307,10.252475 H 13.806931 V 1.3663366 H 10.252475 V 10.252475 H 1.3663366 v 3.554455 h 8.8861384 v 8.886139 h 3.554456 V 13.80693 h 8.886139 z"
+ id="path832"
+ inkscape:label="path832"
+ style="opacity:1;fill:#ffffff;fill-opacity:1;stroke-width:0.888614" />
+</svg>
--- /dev/null
+sample:
+ filter: true
{
"name": "spray"
},
- {
- "name": "spray_cap"
- },
{
"name": "spray_cap_colors"
},
{
"name": "equipped-BELT",
"directions": 4
+ },
+ {
+ "name": "clown-inhand-right",
+ "directions": 4
+ },
+ {
+ "name": "clown-inhand-left",
+ "directions": 4
+ },
+ {
+ "name": "spray-inhand-right",
+ "directions": 4
+ },
+ {
+ "name": "spray-inhand-left",
+ "directions": 4
}
]
}