-
Notifications
You must be signed in to change notification settings - Fork 147
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
- Loading branch information
1 parent
8385fb1
commit d237304
Showing
41 changed files
with
1,000 additions
and
6 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,116 @@ | ||
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 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) | ||
{ | ||
var shader = _protoMan.Index<ShaderPrototype>(component.ShaderName).Instance(); | ||
|
||
if (args.Sprite == null) | ||
return; | ||
|
||
// What is this even doing? It's not even checking what the value is. | ||
if (!_appearance.TryGetData(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)) | ||
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)) | ||
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; | ||
var 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; | ||
} | ||
} | ||
} | ||
} | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,178 @@ | ||
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) | ||
{ | ||
BreakOnMove = true, | ||
NeedHand = true, | ||
BreakOnHandChange = true | ||
}; | ||
|
||
_doAfterSystem.TryStartDoAfter(doAfterEventArgs); | ||
} | ||
|
||
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 (HasComp<PaintedComponent>(slotEnt.Value) || !entity.Comp.Blacklist?.IsValid(slotEnt.Value, | ||
EntityManager) != true | ||
|| HasComp<RandomSpriteComponent>(slotEnt.Value) || | ||
HasComp<HumanoidAppearanceComponent>( | ||
slotEnt.Value)) | ||
{ | ||
continue; | ||
} | ||
|
||
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; | ||
} | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -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); | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,9 @@ | ||
using Content.Shared.DoAfter; | ||
using Robust.Shared.Serialization; | ||
|
||
namespace Content.Shared.Paint; | ||
|
||
[Serializable, NetSerializable] | ||
public sealed partial class PaintDoAfterEvent : SimpleDoAfterEvent | ||
{ | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -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; | ||
} |
Oops, something went wrong.