Files
wwdpublic/Content.Client/Paint/PaintVisualizerSystem.cs
SimpleStation14 763da782d4 Mirror: Spray Paint (Review Ready) (#258)
## Mirror of PR #23003: [Spray Paint (Review
Ready)](https://github.com/space-wizards/space-station-14/pull/23003)
from <img src="https://avatars.githubusercontent.com/u/10567778?v=4"
alt="space-wizards" width="22"/>
[space-wizards](https://github.com/space-wizards)/[space-station-14](https://github.com/space-wizards/space-station-14)

###### `e4d5e7f1aebfc37b1bc3453fdb39578f3897b6a1`

PR opened by <img
src="https://avatars.githubusercontent.com/u/113240905?v=4"
width="16"/><a href="https://github.com/brainfood1183">
brainfood1183</a> at 2023-12-26 12:15:46 UTC

---

PR changed 41 files with 1008 additions and 11 deletions.

The PR had the following labels:
- Changes: Sprites
- Status: Needs Review


---

<details open="true"><summary><h1>Original Body</h1></summary>

> Added - new spray paint sprites and entities.
> Added - paint system which lets you paint over entities.
> 
>
https://github.com/space-wizards/space-station-14/assets/113240905/b1b30678-b9c5-45e2-83a7-d146d02c3751
> 
> - [x] Spray paint can paint objects.
> - [x] Spray paint can come in different colors.
> - [x] Spray paint can be removed.
> - [x] Add a doafter for removing paint.
> - [x] Stealth objects will keep their post shader.
> - [x] Sprite outlines work after object is sprayed.
> - [x] Add doafter for applying paint.
> - [x] Add a blacklist for items that should not be painted.
> - [x] entity sprites in icon slots appear colored correctly.
> - [x] Add verbs for paint and remove paint for painting containers.
> - [x] Spraying an entity with inventory slots. Now any items in
inventory slots are also painted.
> - [x] Spray paints now use fewer entities (no cap, open and closed
entities) instead they use OpenableComponent and visualizers.
> - [x] Spray paints must be opened to use them and can be resealed.
> 
> What Can Be Painted
> 
> Everything with the following exceptions, Mobs with
humanoidAppearanceComponent, Items with RandomSpriteComponent,
BlackListed Entities such as Holograms, Entities which go under the
floor, Entities that are already painted.
> 
> How it works
> 
> Painting an entity will attempt to add both a greyscale shader and
color to each layer of the entity. This will fail if the target has
humanoidappearance, is already painted, has randomspritecomponent, is an
under floor entity or is blacklisted from being painted.
> 
> when paint is applied each layer is shaded and colored only if there
is no existing shader.
> The icon, inhands and clothing sprites are recolored and shaded.
> 
> When an entity is painted it cannot be painted over without first
removing the current paint.
> 
> To remove the paint you use soap on the painted entity. When soap is
used on the entity the color is removed (if the layer had a color prior
to removal it will revert to that color, seems like it is color + color
then color - color to end up back at original color) from the layer
provided the current color of the layer matches the color in
paintedcomponent. The shader is also removed provided the current shader
matches the one in paintedcomponent.
> 
> bugs & work arounds
> 
> Currently entities with RandomSpriteComponent cannot be sprayed, these
include screwdrivers and wirecutters (essentially sprites that have a
random color on spawn. Entities that have a specific color on spawn work
fine.) The paint system checks for RandomSpriteComponent and provides
the all ready painted response.
> 
> Entities and clothing in hand or equipped if sprayed or paint removed
will not update their visual status until interacted with.
> 
> - [x] I have added screenshots/videos to this PR showcasing its
changes ingame, **or** this PR does not require an ingame showcase
> 
> 
> **Changelog**
> 🆑
> - add: Added Spray Paints.
> 
> 


</details>

Co-authored-by: SimpleStation14 <Unknown>
2024-05-12 01:36:24 -04:00

121 lines
4.3 KiB
C#

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;
}
}
}
}
}
}