Spray Paint (Review Ready) (#23003)

* Spray Paint (Draft)

* paint colors, paints in maints loot, cargo crate of paints.

* fix

* remove paint (sort of)

* moved paintcleaner into own system

* Moved paint to server (had to unfortunately)

* doafter now breaks when moving away.

* cant paint mobstatecomp

* loads of fixes

* fixes

* fixes

* nopaintshadercomp

* fixes

* fix

* use locale for paint remove string

* remove nopaintshadercomponent and use blacklist

* remove enabled.true from visualizer

* paint doafter event.

* add verbs for paint and remove paint and icon for paint verb.

* fixes

* no longer replaces shader when shader exists.

* replace forloop with foreach, check shader before adding and removing.

* paint doafter now separate so no copy paste code

* Entities in sprayed targets item slots are also now correctly sprayed.

* fix

* fix

* fix airlock psray painter now removes painted before painting door.

* spray paints now use openablecomponent.

* fix

* fix damn accesstypes.

* fix

* fix
This commit is contained in:
brainfood1183
2024-03-18 21:29:48 +00:00
committed by GitHub
parent 3b8320dbbe
commit e4d5e7f1ae
41 changed files with 1008 additions and 11 deletions

View File

@@ -0,0 +1,60 @@
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);
}

View File

@@ -0,0 +1,9 @@
using Content.Shared.DoAfter;
using Robust.Shared.Serialization;
namespace Content.Shared.Paint;
[Serializable, NetSerializable]
public sealed partial class PaintDoAfterEvent : SimpleDoAfterEvent
{
}

View File

@@ -0,0 +1,24 @@
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;
}

View File

@@ -0,0 +1,9 @@
using Content.Shared.DoAfter;
using Robust.Shared.Serialization;
namespace Content.Shared.Paint;
[Serializable, NetSerializable]
public sealed partial class PaintRemoverDoAfterEvent : SimpleDoAfterEvent
{
}

View File

@@ -0,0 +1,96 @@
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);
}
}

View File

@@ -0,0 +1,41 @@
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,
}

View File

@@ -0,0 +1,11 @@
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)
{
}
}

View File

@@ -4,6 +4,7 @@ using Content.Shared.DoAfter;
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;
@@ -129,6 +130,8 @@ public abstract class SharedSprayPainterSystem : EntitySystem
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,