* Added special crayon with infinite charges for borg usage.
* Use battery system to manage charges.
* Reverted extra changes
* Set charge on init
* removed init assignment
* Added comments to crayoncomponent
* tweaked comments
* Working with the new charges component, but at what cost?
* Remvoed extra field
* Apply suggestion from @slarticodefast
Co-authored-by: slarticodefast <161409025+slarticodefast@users.noreply.github.com>
* Apply suggestion from @slarticodefast
Co-authored-by: slarticodefast <161409025+slarticodefast@users.noreply.github.com>
* Apply suggestion from @slarticodefast
Co-authored-by: slarticodefast <161409025+slarticodefast@users.noreply.github.com>
* Apply suggestion from @slarticodefast
Co-authored-by: slarticodefast <161409025+slarticodefast@users.noreply.github.com>
* Fix renamed variables and descriptions in comments
* Variable naming, comment cleanup and autonetworking.
* Fix for test case, modified on init
* Cleaned up/merged charges logic
* review
---------
Co-authored-by: slarticodefast <161409025+slarticodefast@users.noreply.github.com>
+++ /dev/null
-using Content.Shared.Crayon;
-using Robust.Shared.GameObjects;
-using Robust.Shared.ViewVariables;
-
-namespace Content.Client.Crayon
-{
- [RegisterComponent]
- public sealed partial class CrayonComponent : SharedCrayonComponent
- {
- [ViewVariables(VVAccess.ReadWrite)] public bool UIUpdateNeeded;
- [ViewVariables] public int Charges { get; set; }
- [ViewVariables] public int Capacity { get; set; }
- }
-}
using Content.Client.Items;
using Content.Client.Message;
using Content.Client.Stylesheets;
+using Content.Shared.Charges.Components;
+using Content.Shared.Charges.Systems;
using Content.Shared.Crayon;
using Robust.Client.UserInterface;
using Robust.Client.UserInterface.Controls;
-using Robust.Shared.GameObjects;
-using Robust.Shared.GameStates;
-using Robust.Shared.Localization;
using Robust.Shared.Timing;
namespace Content.Client.Crayon;
public sealed class CrayonSystem : SharedCrayonSystem
{
- // Didn't do in shared because I don't think most of the server stuff can be predicted.
+ [Dependency] private readonly SharedChargesSystem _charges = default!;
+ [Dependency] private readonly EntityManager _entityManager = default!;
+
public override void Initialize()
{
base.Initialize();
- SubscribeLocalEvent<CrayonComponent, ComponentHandleState>(OnCrayonHandleState);
- Subs.ItemStatus<CrayonComponent>(ent => new StatusControl(ent));
- }
-
- private static void OnCrayonHandleState(EntityUid uid, CrayonComponent component, ref ComponentHandleState args)
- {
- if (args.Current is not CrayonComponentState state) return;
- component.Color = state.Color;
- component.SelectedState = state.State;
- component.Charges = state.Charges;
- component.Capacity = state.Capacity;
-
- component.UIUpdateNeeded = true;
+ Subs.ItemStatus<CrayonComponent>(ent => new StatusControl(ent, _charges, _entityManager));
}
private sealed class StatusControl : Control
{
- private readonly CrayonComponent _parent;
+ private readonly Entity<CrayonComponent> _crayon;
+ private readonly SharedChargesSystem _charges;
private readonly RichTextLabel _label;
+ private readonly int _capacity;
- public StatusControl(CrayonComponent parent)
+ public StatusControl(Entity<CrayonComponent> crayon, SharedChargesSystem charges, EntityManager entityManage)
{
- _parent = parent;
+ _crayon = crayon;
+ _charges = charges;
+ _capacity = entityManage.GetComponent<LimitedChargesComponent>(_crayon.Owner).MaxCharges;
_label = new RichTextLabel { StyleClasses = { StyleNano.StyleClassItemStatus } };
AddChild(_label);
-
- parent.UIUpdateNeeded = true;
}
protected override void FrameUpdate(FrameEventArgs args)
{
base.FrameUpdate(args);
- if (!_parent.UIUpdateNeeded)
- {
- return;
- }
-
- _parent.UIUpdateNeeded = false;
_label.SetMarkup(Robust.Shared.Localization.Loc.GetString("crayon-drawing-label",
- ("color",_parent.Color),
- ("state",_parent.SelectedState),
- ("charges", _parent.Charges),
- ("capacity",_parent.Capacity)));
+ ("color",_crayon.Comp.Color),
+ ("state",_crayon.Comp.SelectedState),
+ ("charges", _charges.GetCurrentCharges(_crayon.Owner)),
+ ("capacity", _capacity)));
}
}
}
+++ /dev/null
-using Content.Server.UserInterface;
-using Content.Shared.Crayon;
-using Robust.Server.GameObjects;
-using Robust.Shared.Audio;
-
-namespace Content.Server.Crayon
-{
- [RegisterComponent]
- public sealed partial class CrayonComponent : SharedCrayonComponent
- {
- [DataField("useSound")] public SoundSpecifier? UseSound;
-
- [ViewVariables(VVAccess.ReadWrite)]
- [DataField("selectableColor")]
- public bool SelectableColor { get; set; }
-
- [ViewVariables(VVAccess.ReadWrite)]
- public int Charges { get; set; }
-
- [ViewVariables(VVAccess.ReadWrite)]
- [DataField("capacity")]
- public int Capacity { get; set; } = 30;
-
- [ViewVariables(VVAccess.ReadWrite)]
- [DataField("deleteEmpty")]
- public bool DeleteEmpty = true;
- }
-}
using Content.Server.Administration.Logs;
using Content.Server.Decals;
using Content.Server.Popups;
+using Content.Shared.Charges.Systems;
using Content.Shared.Crayon;
using Content.Shared.Database;
using Content.Shared.Decals;
using Robust.Server.GameObjects;
using Robust.Shared.Audio;
using Robust.Shared.Audio.Systems;
-using Robust.Shared.GameStates;
using Robust.Shared.Prototypes;
namespace Content.Server.Crayon;
[Dependency] private readonly DecalSystem _decals = default!;
[Dependency] private readonly PopupSystem _popup = default!;
[Dependency] private readonly SharedAudioSystem _audio = default!;
+ [Dependency] private readonly SharedChargesSystem _charges = default!;
[Dependency] private readonly UserInterfaceSystem _uiSystem = default!;
public override void Initialize()
{
base.Initialize();
- SubscribeLocalEvent<CrayonComponent, ComponentInit>(OnCrayonInit);
+
+ SubscribeLocalEvent<CrayonComponent, MapInitEvent>(OnMapInit);
SubscribeLocalEvent<CrayonComponent, CrayonSelectMessage>(OnCrayonBoundUI);
SubscribeLocalEvent<CrayonComponent, CrayonColorMessage>(OnCrayonBoundUIColor);
SubscribeLocalEvent<CrayonComponent, UseInHandEvent>(OnCrayonUse, before: new[] { typeof(FoodSystem) });
SubscribeLocalEvent<CrayonComponent, AfterInteractEvent>(OnCrayonAfterInteract, after: new[] { typeof(FoodSystem) });
SubscribeLocalEvent<CrayonComponent, DroppedEvent>(OnCrayonDropped);
- SubscribeLocalEvent<CrayonComponent, ComponentGetState>(OnCrayonGetState);
}
- private static void OnCrayonGetState(EntityUid uid, CrayonComponent component, ref ComponentGetState args)
+ private void OnMapInit(Entity<CrayonComponent> ent, ref MapInitEvent args)
{
- args.State = new CrayonComponentState(component.Color, component.SelectedState, component.Charges, component.Capacity);
+ // Get the first one from the catalog and set it as default
+ var decal = _prototypeManager.EnumeratePrototypes<DecalPrototype>().FirstOrDefault(x => x.Tags.Contains("crayon"));
+ ent.Comp.SelectedState = decal?.ID ?? string.Empty;
+ Dirty(ent);
}
private void OnCrayonAfterInteract(EntityUid uid, CrayonComponent component, AfterInteractEvent args)
if (args.Handled || !args.CanReach)
return;
- if (component.Charges <= 0)
+ if (_charges.IsEmpty(uid))
{
if (component.DeleteEmpty)
UseUpCrayon(uid, args.User);
if (component.UseSound != null)
_audio.PlayPvs(component.UseSound, uid, AudioParams.Default.WithVariation(0.125f));
- // Decrease "Ammo"
- component.Charges--;
- Dirty(uid, component);
+ _charges.TryUseCharge(uid);
_adminLogger.Add(LogType.CrayonDraw, LogImpact.Low, $"{ToPrettyString(args.User):user} drew a {component.Color:color} {component.SelectedState}");
args.Handled = true;
- if (component.DeleteEmpty && component.Charges <= 0)
+ if (component.DeleteEmpty && _charges.IsEmpty(uid))
UseUpCrayon(uid, args.User);
else
- _uiSystem.ServerSendUiMessage(uid, SharedCrayonComponent.CrayonUiKey.Key, new CrayonUsedMessage(component.SelectedState));
+ _uiSystem.ServerSendUiMessage(uid, CrayonUiKey.Key, new CrayonUsedMessage(component.SelectedState));
}
private void OnCrayonUse(EntityUid uid, CrayonComponent component, UseInHandEvent args)
if (args.Handled)
return;
- if (!_uiSystem.HasUi(uid, SharedCrayonComponent.CrayonUiKey.Key))
- {
+ if (!_uiSystem.HasUi(uid, CrayonUiKey.Key))
return;
- }
- _uiSystem.TryToggleUi(uid, SharedCrayonComponent.CrayonUiKey.Key, args.User);
+ _uiSystem.TryToggleUi(uid, CrayonUiKey.Key, args.User);
- _uiSystem.SetUiState(uid, SharedCrayonComponent.CrayonUiKey.Key, new CrayonBoundUserInterfaceState(component.SelectedState, component.SelectableColor, component.Color));
+ _uiSystem.SetUiState(uid, CrayonUiKey.Key, new CrayonBoundUserInterfaceState(component.SelectedState, component.SelectableColor, component.Color));
args.Handled = true;
}
return;
component.SelectedState = args.State;
-
Dirty(uid, component);
}
private void OnCrayonBoundUIColor(EntityUid uid, CrayonComponent component, CrayonColorMessage args)
{
- // you still need to ensure that the given color is a valid color
+ // Ensure that the given color can be changed or already matches
if (!component.SelectableColor || args.Color == component.Color)
return;
component.Color = args.Color;
Dirty(uid, component);
-
- }
-
- private void OnCrayonInit(EntityUid uid, CrayonComponent component, ComponentInit args)
- {
- component.Charges = component.Capacity;
-
- // Get the first one from the catalog and set it as default
- var decal = _prototypeManager.EnumeratePrototypes<DecalPrototype>().FirstOrDefault(x => x.Tags.Contains("crayon"));
- component.SelectedState = decal?.ID ?? string.Empty;
- Dirty(uid, component);
}
private void OnCrayonDropped(EntityUid uid, CrayonComponent component, DroppedEvent args)
{
// TODO: Use the existing event.
- _uiSystem.CloseUi(uid, SharedCrayonComponent.CrayonUiKey.Key, args.User);
+ _uiSystem.CloseUi(uid, CrayonUiKey.Key, args.User);
}
private void UseUpCrayon(EntityUid uid, EntityUid user)
--- /dev/null
+using Robust.Shared.Audio;
+using Robust.Shared.GameStates;
+using Robust.Shared.Serialization;
+
+namespace Content.Shared.Crayon;
+
+/// <summary>
+/// Component holding the state of a crayon-like component
+/// </summary>
+[RegisterComponent, NetworkedComponent, AutoGenerateComponentState]
+[Access(typeof(SharedCrayonSystem))]
+public sealed partial class CrayonComponent : Component
+{
+ /// <summary>
+ /// The ID of currently selected decal prototype that will be placed when the crayon is used.
+ /// </summary>
+ [DataField, AutoNetworkedField]
+ public string SelectedState;
+
+ /// <summary>
+ /// Color with which the crayon will draw.
+ /// </summary>
+ [DataField, AutoNetworkedField]
+ public Color Color;
+
+ /// <summary>
+ /// Play a sound when drawing if specified.
+ /// </summary>
+ [DataField]
+ public SoundSpecifier? UseSound;
+
+ /// <summary>
+ /// Can the color can be changed?
+ /// </summary>
+ [DataField, AutoNetworkedField]
+ public bool SelectableColor;
+
+ /// <summary>
+ /// Should the crayon be deleted when all charges are consumed?
+ /// </summary>
+ [DataField, AutoNetworkedField]
+ public bool DeleteEmpty = true;
+}
+
+/// <summary>
+/// Opens the crayon window for decal and color selection.
+/// </summary>
+[Serializable, NetSerializable]
+public enum CrayonUiKey : byte
+{
+ Key,
+}
+
+/// <summary>
+/// Used by the client to notify the server about the selected decal ID
+/// </summary>
+[Serializable, NetSerializable]
+public sealed class CrayonSelectMessage : BoundUserInterfaceMessage
+{
+ public readonly string State;
+ public CrayonSelectMessage(string selected)
+ {
+ State = selected;
+ }
+}
+
+/// <summary>
+/// Sets the color of the crayon, used by Rainbow Crayon
+/// </summary>
+[Serializable, NetSerializable]
+public sealed class CrayonColorMessage : BoundUserInterfaceMessage
+{
+ public readonly Color Color;
+ public CrayonColorMessage(Color color)
+ {
+ Color = color;
+ }
+}
+
+/// <summary>
+/// Server to CLIENT. Notifies the BUI that a decal with given ID has been drawn.
+/// Allows the client UI to advance forward in the client-only ephemeral queue,
+/// preventing the crayon from becoming a magic text storage device.
+/// </summary>
+[Serializable, NetSerializable]
+public sealed class CrayonUsedMessage : BoundUserInterfaceMessage
+{
+ public readonly string DrawnDecal;
+
+ public CrayonUsedMessage(string drawn)
+ {
+ DrawnDecal = drawn;
+ }
+}
+
+/// <summary>
+/// The state of the crayon UI as sent by the server
+/// </summary>
+/// <summary>
+/// TODO: Delete this and use component states and predict the UI.
+/// This info is already networked on its own.
+/// </summary>
+[Serializable, NetSerializable]
+public sealed class CrayonBoundUserInterfaceState : BoundUserInterfaceState
+{
+ public string Selected;
+ /// <summary>
+ /// Can the color can be changed
+ /// </summary>
+ public bool SelectableColor;
+ public Color Color;
+
+ public CrayonBoundUserInterfaceState(string selected, bool selectableColor, Color color)
+ {
+ Selected = selected;
+ SelectableColor = selectableColor;
+ Color = color;
+ }
+}
+++ /dev/null
-using Robust.Shared.GameStates;
-using Robust.Shared.Serialization;
-
-namespace Content.Shared.Crayon
-{
-
- /// <summary>
- /// Component holding the state of a crayon-like component
- /// </summary>
- [NetworkedComponent, ComponentProtoName("Crayon"), Access(typeof(SharedCrayonSystem))]
- public abstract partial class SharedCrayonComponent : Component
- {
- /// <summary>
- /// The ID of currently selected decal prototype that will be placed when the crayon is used
- /// </summary>
- public string SelectedState { get; set; } = string.Empty;
-
- /// <summary>
- /// Color with which the crayon will draw
- /// </summary>
- [DataField("color")]
- public Color Color;
-
- [Serializable, NetSerializable]
- public enum CrayonUiKey : byte
- {
- Key,
- }
- }
-
- /// <summary>
- /// Used by the client to notify the server about the selected decal ID
- /// </summary>
- [Serializable, NetSerializable]
- public sealed class CrayonSelectMessage : BoundUserInterfaceMessage
- {
- public readonly string State;
- public CrayonSelectMessage(string selected)
- {
- State = selected;
- }
- }
-
- /// <summary>
- /// Sets the color of the crayon, used by Rainbow Crayon
- /// </summary>
- [Serializable, NetSerializable]
- public sealed class CrayonColorMessage : BoundUserInterfaceMessage
- {
- public readonly Color Color;
- public CrayonColorMessage(Color color)
- {
- Color = color;
- }
- }
-
- /// <summary>
- /// Server to CLIENT. Notifies the BUI that a decal with given ID has been drawn.
- /// Allows the client UI to advance forward in the client-only ephemeral queue,
- /// preventing the crayon from becoming a magic text storage device.
- /// </summary>
- [Serializable, NetSerializable]
- public sealed class CrayonUsedMessage : BoundUserInterfaceMessage
- {
- public readonly string DrawnDecal;
-
- public CrayonUsedMessage(string drawn)
- {
- DrawnDecal = drawn;
- }
- }
-
- /// <summary>
- /// Component state, describes how many charges are left in the crayon in the near-hand UI
- /// </summary>
- [Serializable, NetSerializable]
- public sealed class CrayonComponentState : ComponentState
- {
- public readonly Color Color;
- public readonly string State;
- public readonly int Charges;
- public readonly int Capacity;
-
- public CrayonComponentState(Color color, string state, int charges, int capacity)
- {
- Color = color;
- State = state;
- Charges = charges;
- Capacity = capacity;
- }
- }
-
- /// <summary>
- /// The state of the crayon UI as sent by the server
- /// </summary>
- [Serializable, NetSerializable]
- public sealed class CrayonBoundUserInterfaceState : BoundUserInterfaceState
- {
- public string Selected;
- /// <summary>
- /// Whether or not the color can be selected
- /// </summary>
- public bool SelectableColor;
- public Color Color;
-
- public CrayonBoundUserInterfaceState(string selected, bool selectableColor, Color color)
- {
- Selected = selected;
- SelectableColor = selectableColor;
- Color = color;
- }
- }
-}
enum.CrayonUiKey.Key:
type: CrayonBoundUserInterface
- type: Crayon
- capacity: 25
+ selectedState: like
+ - type: LimitedCharges
+ maxCharges: 25
- type: Food
- type: FlavorProfile
flavors:
- type: Crayon
color: Red
selectableColor: true
- capacity: 30
+ - type: LimitedCharges
+ maxCharges: 30
- type: Tag
tags:
- Write
- Recyclable
- Trash
+- type: entity
+ parent: CrayonRainbow
+ id: CrayonInfinite # should not be player available to prevent decal spam
+ name: infinite crayon
+ components:
+ - type: Crayon
+ deleteEmpty: false
+ - type: AutoRecharge
+ rechargeDuration: 5
+
- type: entity
parent: Crayon
id: CrayonBlack