Skip to content
Draft
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
91 changes: 91 additions & 0 deletions Content.Client/_DEN/Recolor/RecolorVisualizerSystem.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,91 @@
using Content.Shared._DEN.Recolor;
using Content.Shared.Sound;
using Robust.Client.GameObjects;
using Robust.Client.Graphics;
using Robust.Shared.Prototypes;

#pragma warning disable IDE1006 // Naming Styles
namespace Content.Client._DEN.Recolor;
#pragma warning restore IDE1006 // Naming Styles

public sealed partial class RecolorVisualizerSystem : VisualizerSystem<RecoloredComponent>
{
[Dependency] private readonly IPrototypeManager _prototype = default!;

public override void Initialize()
{
base.Initialize();

SubscribeLocalEvent<RecoloredComponent, ComponentShutdown>(OnComponentShutdown);
}

protected override void OnAppearanceChange(EntityUid uid,
RecoloredComponent component,
ref AppearanceChangeEvent args)
{
base.OnAppearanceChange(uid, component, ref args);

if (args.Sprite == null
|| !AppearanceSystem.TryGetData(uid, RecolorVisuals.RecolorDirty, out var dirty)
|| dirty is not true)
return;

ApplyRecolor((uid, component), args.Sprite);
AppearanceSystem.SetData(uid, RecolorVisuals.RecolorDirty, false);
}

private void OnComponentShutdown(Entity<RecoloredComponent> ent, ref ComponentShutdown args)
{
if (TerminatingOrDeleted(ent.Owner))
return;

RemoveRecolor(ent);
}

private void ApplyRecolor(Entity<RecoloredComponent> ent, SpriteComponent sprite)
{
ShaderPrototype? shader = null;
if (ent.Comp.Shader != null
&& _prototype.TryIndex<ShaderPrototype>(ent.Comp.Shader, out var proto))
shader = proto;

foreach (var spriteLayer in sprite.AllLayers)
{
if (spriteLayer is not SpriteComponent.Layer layer
|| !ent.Comp.AffectLayersWithShaders && layer.Shader != null)
continue;

SpriteSystem.LayerSetColor(layer, sprite.Color);

if (shader != null)
{
var instance = shader.Instance();
sprite.LayerSetShader(layer, instance);
}
}
}

private void RemoveRecolor(Entity<RecoloredComponent> ent)
{
if (!TryComp<SpriteComponent>(ent.Owner, out var sprite)
|| !AppearanceSystem.TryGetData(ent.Owner, RecolorVisuals.RecolorDirty, out _))
return;

ShaderPrototype? shader = null;
if (ent.Comp.Shader != null
&& _prototype.TryIndex<ShaderPrototype>(ent.Comp.Shader, out var proto))
shader = proto;

foreach (var spriteLayer in sprite.AllLayers)
{
if (spriteLayer is not SpriteComponent.Layer layer
|| shader != null && layer.Shader != shader.Instance())
continue;

sprite.LayerSetShader(layer, "");

if (layer.Color == ent.Comp.Color && ent.Comp.PreviousColor != null)
SpriteSystem.LayerSetColor(layer, ent.Comp.PreviousColor.Value);
}
}
}
53 changes: 53 additions & 0 deletions Content.Server/_DEN/Recolor/RecolorSystem.Applier.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,53 @@
using Content.Shared._DEN.Recolor;
using Content.Shared.DoAfter;
using Content.Shared.Interaction;

#pragma warning disable IDE1006 // Naming Styles
namespace Content.Server._DEN.Recolor;
#pragma warning restore IDE1006 // Naming Styles

public sealed partial class RecolorSystem : SharedRecolorSystem
{
private void OnRecolorApplierAfterInteract(Entity<RecolorApplierComponent> ent, ref AfterInteractEvent args)
{
if (!args.CanReach || args.Target is not { Valid: true } target || HasComp<RecoloredComponent>(target))
return;

TryStartApplyRecolorDoAfter(args.User, target, ent);
}

private void TryStartApplyRecolorDoAfter(EntityUid user,
EntityUid target,
Entity<RecolorApplierComponent> applier)
{
var doAfterEvent = new ApplyRecolorDoAfterEvent
{
Color = applier.Comp.Color,
Shader = applier.Comp.Shader,
AffectLayersWithShaders = applier.Comp.AffectLayersWithShaders,
Removeable = applier.Comp.Removeable
};

var doAfterArgs = new DoAfterArgs(EntityManager,
user: user,
seconds: (float)applier.Comp.DoAfterDuration.TotalSeconds,
@event: doAfterEvent,
eventTarget: applier,
target: target,
used: applier);

_doAfterSystem.TryStartDoAfter(doAfterArgs);
}

private void OnApplyRecolorDoAfterEvent(Entity<RecolorApplierComponent> ent, ref ApplyRecolorDoAfterEvent args)
{
if (args.Target is null)
return;

Recolor(uid: args.Target.Value,
color: args.Color,
shader: args.Shader,
affectLayersWithShaders: args.AffectLayersWithShaders,
removeable: args.Removeable);
}
}
67 changes: 67 additions & 0 deletions Content.Server/_DEN/Recolor/RecolorSystem.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,67 @@
using Content.Server.DoAfter;
using Content.Shared._DEN.Recolor;
using Content.Shared.Interaction;
using JetBrains.Annotations;
using Robust.Server.GameObjects;

#pragma warning disable IDE1006 // Naming Styles
namespace Content.Server._DEN.Recolor;
#pragma warning restore IDE1006 // Naming Styles

public sealed partial class RecolorSystem : SharedRecolorSystem
{
[Dependency] private readonly AppearanceSystem _appearance = default!;
[Dependency] private readonly DoAfterSystem _doAfterSystem = default!;

public override void Initialize()
{
base.Initialize();

SubscribeLocalEvent<RecolorApplierComponent, AfterInteractEvent>(OnRecolorApplierAfterInteract);
SubscribeLocalEvent<RecolorApplierComponent, ApplyRecolorDoAfterEvent>(OnApplyRecolorDoAfterEvent);

SubscribeLocalEvent<RecoloredComponent, ComponentStartup>(OnRecoloredStartup);
SubscribeLocalEvent<RecoloredComponent, ComponentShutdown>(OnRecoloredShutdown);
}

private void OnRecoloredStartup(Entity<RecoloredComponent> ent, ref ComponentStartup args)
{
_appearance.SetData(ent, RecolorVisuals.RecolorDirty, true);
}

private void OnRecoloredShutdown(Entity<RecoloredComponent> ent, ref ComponentShutdown args)
{
_appearance.SetData(ent, RecolorVisuals.RecolorDirty, true);
}

[PublicAPI]
public void Recolor(EntityUid uid,
Color color,
string? shader = null,
bool affectLayersWithShaders = false,
bool removeable = true)
{
if (HasComp<RecoloredComponent>(uid))
return;

EnsureComp<AppearanceComponent>(uid);
var recoloredComponent = new RecoloredComponent
{
Color = color,
Shader = shader,
AffectLayersWithShaders = affectLayersWithShaders,
Removeable = removeable
};

AddComp(uid, recoloredComponent);
}

[PublicAPI]
public void RemoveRecolor(Entity<RecoloredComponent?> ent)
{
if (!Resolve(ent.Owner, ref ent.Comp, logMissing: false))
return;

RemComp<RecoloredComponent>(ent);
}
}
38 changes: 38 additions & 0 deletions Content.Shared/_DEN/Recolor/RecolorApplierComponent.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,38 @@
using Robust.Shared.Prototypes;

namespace Content.Shared._DEN.Recolor;

[RegisterComponent]
public sealed partial class RecolorApplierComponent : Component
{
/// <summary>
/// The color to change the sprite to.
/// </summary>
[DataField]
public Color Color = Color.White;

/// <summary>
/// Whether or not this component can be removed by an entity with RecolorRemoverComponent.
/// </summary>
[DataField]
public bool Removeable = true;

/// <summary>
/// Whether or not the recolor should apply to layers that already have shaders.
/// </summary>
[DataField]
public bool AffectLayersWithShaders = false;

/// <summary>
/// The shader to apply to the recolored entity.
/// Sorry, we don't have ShaderPrototype in Shared, because ShaderPrototype is clientside.
/// </summary>
[DataField]
public string? Shader = "Greyscale";

/// <summary>
/// How long it takes for this object to apply the recolor to the target.
/// </summary>
[DataField]
public TimeSpan DoAfterDuration = TimeSpan.FromSeconds(3.0f);
}
37 changes: 37 additions & 0 deletions Content.Shared/_DEN/Recolor/RecoloredComponent.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,37 @@
using Robust.Shared.Prototypes;

namespace Content.Shared._DEN.Recolor;

[RegisterComponent]
public sealed partial class RecoloredComponent : Component
{
/// <summary>
/// The color to change the sprite to.
/// </summary>
[DataField]
public Color Color = Color.White;

/// <summary>
/// Whether or not this component can be removed by an entity with RecolorRemoverComponent.
/// </summary>
[DataField]
public bool Removeable = true;

/// <summary>
/// Whether or not the recolor should apply to layers that already have shaders.
/// </summary>
[DataField]
public bool AffectLayersWithShaders = false;

/// <summary>
/// The shader to apply to the recolored entity.
/// Sorry, we don't have ShaderPrototype in Shared, because ShaderPrototype is clientside.
/// </summary>
[DataField]
public string? Shader = null;

/// <summary>
/// The previous color of the sprite, before recoloring.
/// </summary>
public Color? PreviousColor;
}
31 changes: 31 additions & 0 deletions Content.Shared/_DEN/Recolor/SharedRecolorSystem.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,31 @@
using Content.Shared.DoAfter;
using Robust.Shared.Serialization;

#pragma warning disable IDE1006 // Naming Styles
namespace Content.Shared._DEN.Recolor;
#pragma warning restore IDE1006 // Naming Styles

public abstract partial class SharedRecolorSystem : EntitySystem
{
public override void Initialize()
{
base.Initialize();
}
}

[Serializable, NetSerializable]
public enum RecolorVisuals : byte
{
RecolorDirty
}

[Serializable, NetSerializable]
public sealed partial class ApplyRecolorDoAfterEvent : DoAfterEvent
{
public Color Color;
public string? Shader = null;
public bool AffectLayersWithShaders = false;
public bool Removeable = true;

public override DoAfterEvent Clone() => this;
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,15 @@
- type: entity
parent: BaseItem
id: SprayPaintCan
name: yellow spray paint
description: it's spray paint
components:
- type: Sprite
sprite: Objects/Tools/crowbar.rsi
state: icon
- type: Item
storedSprite:
sprite: Objects/Tools/crowbar.rsi
state: storage
- type: RecolorApplier
color: "#ffff00"