From d647aee19b1c13706a36faae27a8d87dd7f5faf6 Mon Sep 17 00:00:00 2001 From: Beck Date: Sun, 23 Jun 2024 16:03:15 -0700 Subject: [PATCH 1/4] First commit --- .../Nutrition/EntitySystems/FoodSystem.cs | 5 + .../Nutrition/EntitySystems/UtensilSystem.cs | 2 +- .../Plants/PottedPlantHideComponent.cs | 17 - .../Plants/PottedPlantHideSystem.cs | 63 --- .../Components/SecretStashComponent.cs | 79 +++- .../EntitySystems/SecretStashSystem.cs | 411 +++++++++++------- .../components/secret-stash-component.ftl | 31 +- .../Structures/Furniture/potted_plants.yml | 8 +- .../Entities/Structures/Furniture/toilet.yml | 14 +- 9 files changed, 349 insertions(+), 281 deletions(-) delete mode 100644 Content.Shared/Plants/PottedPlantHideComponent.cs delete mode 100644 Content.Shared/Plants/PottedPlantHideSystem.cs diff --git a/Content.Server/Nutrition/EntitySystems/FoodSystem.cs b/Content.Server/Nutrition/EntitySystems/FoodSystem.cs index 1862b4e19f14..a359e0ffae4b 100644 --- a/Content.Server/Nutrition/EntitySystems/FoodSystem.cs +++ b/Content.Server/Nutrition/EntitySystems/FoodSystem.cs @@ -33,6 +33,7 @@ using System.Linq; using Robust.Server.GameObjects; using Content.Shared.Whitelist; +using Content.Shared.Destructible; namespace Content.Server.Nutrition.EntitySystems; @@ -322,6 +323,10 @@ public void DeleteAndSpawnTrash(FoodComponent component, EntityUid food, EntityU if (ev.Cancelled) return; + var dev = new DestructionEventArgs(); + + RaiseLocalEvent(food, dev); + if (string.IsNullOrEmpty(component.Trash)) { QueueDel(food); diff --git a/Content.Server/Nutrition/EntitySystems/UtensilSystem.cs b/Content.Server/Nutrition/EntitySystems/UtensilSystem.cs index 43087214a453..67f66d3ee76d 100644 --- a/Content.Server/Nutrition/EntitySystems/UtensilSystem.cs +++ b/Content.Server/Nutrition/EntitySystems/UtensilSystem.cs @@ -49,7 +49,7 @@ private void OnAfterInteract(EntityUid uid, UtensilComponent component, AfterInt if ((food.Utensil & component.Types) == 0) { _popupSystem.PopupEntity(Loc.GetString("food-system-wrong-utensil", ("food", target), ("utensil", component.Owner)), user, user); - return (false, true); + return (false, false); } if (!_interactionSystem.InRangeUnobstructed(user, target, popup: true)) diff --git a/Content.Shared/Plants/PottedPlantHideComponent.cs b/Content.Shared/Plants/PottedPlantHideComponent.cs deleted file mode 100644 index 2e02272494fe..000000000000 --- a/Content.Shared/Plants/PottedPlantHideComponent.cs +++ /dev/null @@ -1,17 +0,0 @@ -using Content.Shared.Storage.Components; -using Robust.Shared.Audio; - -namespace Content.Shared.Plants -{ - /// - /// Interaction wrapper for . - /// Gently rustle after each interaction with plant. - /// - [RegisterComponent] - [Access(typeof(PottedPlantHideSystem))] - public sealed partial class PottedPlantHideComponent : Component - { - [DataField("rustleSound")] - public SoundSpecifier RustleSound = new SoundPathSpecifier("/Audio/Effects/plant_rustle.ogg"); - } -} diff --git a/Content.Shared/Plants/PottedPlantHideSystem.cs b/Content.Shared/Plants/PottedPlantHideSystem.cs deleted file mode 100644 index cbe052f8d5c6..000000000000 --- a/Content.Shared/Plants/PottedPlantHideSystem.cs +++ /dev/null @@ -1,63 +0,0 @@ -using Content.Shared.Popups; -using Content.Shared.Storage.Components; -using Content.Shared.Storage.EntitySystems; -using Content.Shared.Interaction; -using Robust.Shared.Audio; -using Robust.Shared.Audio.Systems; - -namespace Content.Shared.Plants -{ - public sealed class PottedPlantHideSystem : EntitySystem - { - [Dependency] private readonly SharedPopupSystem _popupSystem = default!; - [Dependency] private readonly SecretStashSystem _stashSystem = default!; - [Dependency] private readonly SharedAudioSystem _audio = default!; - - public override void Initialize() - { - base.Initialize(); - SubscribeLocalEvent(OnInit); - SubscribeLocalEvent(OnInteractUsing); - SubscribeLocalEvent(OnInteractHand); - } - - private void OnInit(EntityUid uid, PottedPlantHideComponent component, ComponentInit args) - { - EntityManager.EnsureComponent(uid); - } - - private void OnInteractUsing(EntityUid uid, PottedPlantHideComponent component, InteractUsingEvent args) - { - if (args.Handled) - return; - - Rustle(uid, component, args.User); - args.Handled = _stashSystem.TryHideItem(uid, args.User, args.Used); - } - - private void OnInteractHand(EntityUid uid, PottedPlantHideComponent component, InteractHandEvent args) - { - if (args.Handled) - return; - - Rustle(uid, component, args.User); - - var gotItem = _stashSystem.TryGetItem(uid, args.User); - if (!gotItem) - { - var msg = Loc.GetString("potted-plant-hide-component-interact-hand-got-no-item-message"); - _popupSystem.PopupClient(msg, uid, args.User); - } - - args.Handled = gotItem; - } - - private void Rustle(EntityUid uid, PottedPlantHideComponent? component = null, EntityUid? user = null) - { - if (!Resolve(uid, ref component)) - return; - - _audio.PlayPredicted(component.RustleSound, uid, user, AudioParams.Default.WithVariation(0.25f)); - } - } -} diff --git a/Content.Shared/Storage/Components/SecretStashComponent.cs b/Content.Shared/Storage/Components/SecretStashComponent.cs index 8595f79ca57b..b9cf06396a71 100644 --- a/Content.Shared/Storage/Components/SecretStashComponent.cs +++ b/Content.Shared/Storage/Components/SecretStashComponent.cs @@ -7,54 +7,89 @@ using Robust.Shared.GameStates; using Content.Shared.DoAfter; using Robust.Shared.Serialization; +using Robust.Shared.Audio; namespace Content.Shared.Storage.Components { /// /// Logic for a secret slot stash, like plant pot or toilet cistern. - /// Unlike it doesn't have interaction logic or verbs. - /// Other classes like should implement it. + /// Unlike it has logic for opening and closing + /// the stash. /// [RegisterComponent, NetworkedComponent, AutoGenerateComponentState] [Access(typeof(SecretStashSystem))] public sealed partial class SecretStashComponent : Component { /// - /// Max item size that can be fitted into secret stash. + /// Max item size that can be inserted into secret stash. /// [DataField("maxItemSize")] public ProtoId MaxItemSize = "Small"; /// - /// If stash has way to open then this will switch between open and closed. + /// If stash has way to open then this will switch between open and closed. /// [DataField, AutoNetworkedField] - public bool ToggleOpen; + public bool IsStashOpen = false; /// - /// Prying the door. + /// If a tool is needed to open the stash, this time will be used. /// [DataField] - public float PryDoorTime = 1f; + public float OpenStashTime = 1f; + /// + /// If a tool is needed to close the stash, this time will be used. + /// [DataField] - public ProtoId PryingQuality = "Prying"; + public float CloseStashTime = 1f; /// - /// Is stash openable?. + /// What type of tool quality is needed to open the stash. + /// If null, the stash will only be openable by a verb. /// - [DataField, AutoNetworkedField] - public bool OpenableStash = false; + [DataField] + public ProtoId? StashOpenToolQualityNeeded; + + /// + /// What type of tool quality is needed to close the stash. + /// If null, the stash will only be closable by a verb. + /// + [DataField] + public ProtoId? StashCloseToolQualityNeeded; + + /// + /// This sound will be played when you try to insert an item in the stash. + /// The sound will be played whether or not the item is actually inserted. + /// + [DataField] + public SoundSpecifier? TryInsertItemSound; /// - /// IC secret stash name. For example "the toilet cistern". - /// If empty string, will replace it with entity name in init. + /// This sound will be played when you try to remove an item in the stash. + /// The sound will be played whether or not the item is actually removed. /// [DataField] - public string SecretPartName { get; set; } = ""; + public SoundSpecifier? TryRemoveItemSound; + /// + /// If true the stash can be opened and closed, if false the stash cannot be opened or closed. + /// [DataField, AutoNetworkedField] - public string ExamineStash = "comp-secret-stash-on-examine-found-hidden-item"; + public bool CanBeOpenedAndClosed = true; + + /// + /// If true, verbs will appear to help interact with the stash. + /// + [DataField, AutoNetworkedField] + public bool HasVerbs = true; + + /// + /// The name of the secret stash. For example "the toilet cistern". + /// If null, the name of the entity will be used instead. + /// + [DataField] + public string? SecretStashName; /// /// Container used to keep secret stash item. @@ -65,26 +100,26 @@ public sealed partial class SecretStashComponent : Component } /// - /// Simple pry event for prying open a stash door. + /// Simple pry event for prying open a stash door. /// [Serializable, NetSerializable] - public sealed partial class StashPryDoAfterEvent : SimpleDoAfterEvent + public sealed partial class SecretStashPryDoAfterEventToggleIsOpen : SimpleDoAfterEvent { } /// - /// Visualizers for handling stash open closed state if stash has door. + /// Visualizers for handling stash open closed state if stash has door. /// [Serializable, NetSerializable] public enum StashVisuals : byte { - DoorVisualState, + StashVisualState, } [Serializable, NetSerializable] - public enum DoorVisualState : byte + public enum StashVisualState : byte { - DoorOpen, - DoorClosed + StashOpen, + StashClosed } } diff --git a/Content.Shared/Storage/EntitySystems/SecretStashSystem.cs b/Content.Shared/Storage/EntitySystems/SecretStashSystem.cs index 9aee1b982e0a..0470a344aac8 100644 --- a/Content.Shared/Storage/EntitySystems/SecretStashSystem.cs +++ b/Content.Shared/Storage/EntitySystems/SecretStashSystem.cs @@ -8,215 +8,306 @@ using Content.Shared.Interaction; using Content.Shared.Tools.Systems; using Content.Shared.Examine; +using Robust.Shared.Audio; +using Robust.Shared.Audio.Systems; +using Content.Shared.Verbs; +using Content.Shared.IdentityManagement; -namespace Content.Shared.Storage.EntitySystems +namespace Content.Shared.Storage.EntitySystems; + +/// +/// Secret Stash allows an item to be hidden within. +/// +public sealed class SecretStashSystem : EntitySystem { - /// - /// Secret Stash allows an item to be hidden within. - /// - public sealed class SecretStashSystem : EntitySystem + [Dependency] private readonly SharedPopupSystem _popupSystem = default!; + [Dependency] private readonly SharedHandsSystem _handsSystem = default!; + [Dependency] private readonly SharedContainerSystem _containerSystem = default!; + [Dependency] private readonly SharedItemSystem _item = default!; + [Dependency] private readonly SharedToolSystem _tool = default!; + [Dependency] private readonly SharedAppearanceSystem _appearance = default!; + [Dependency] private readonly SharedAudioSystem _audio = default!; + + public override void Initialize() { - [Dependency] private readonly SharedPopupSystem _popupSystem = default!; - [Dependency] private readonly SharedHandsSystem _handsSystem = default!; - [Dependency] private readonly SharedContainerSystem _containerSystem = default!; - [Dependency] private readonly SharedItemSystem _item = default!; - [Dependency] private readonly SharedToolSystem _tool = default!; - [Dependency] private readonly SharedAppearanceSystem _appearance = default!; - - public override void Initialize() + base.Initialize(); + SubscribeLocalEvent(OnInit); + SubscribeLocalEvent(OnDestroyed); + SubscribeLocalEvent(OnSecretStashOpenStateToggled); + SubscribeLocalEvent(OnInteractUsing); + SubscribeLocalEvent(OnInteractHand); + SubscribeLocalEvent(OnExamine); + SubscribeLocalEvent>(OnGetVerb); + } + + private void OnInit(Entity entity, ref ComponentInit args) + { + UpdateAppearance(entity); + entity.Comp.ItemContainer = _containerSystem.EnsureContainer(entity, "stash", out _); + Dirty(entity); + } + + private void OnDestroyed(Entity entity, ref DestructionEventArgs args) + { + var storedInside = _containerSystem.EmptyContainer(entity.Comp.ItemContainer); + if (storedInside != null && storedInside.Count >= 1) { - base.Initialize(); - SubscribeLocalEvent(OnInit); - SubscribeLocalEvent(OnDestroyed); - SubscribeLocalEvent(OnSecretStashPried); - SubscribeLocalEvent(OnInteractUsing); - SubscribeLocalEvent(OnInteractHand); - SubscribeLocalEvent(OnExamine); + var popup = Loc.GetString("comp-secret-stash-on-destroyed-popup", ("stashname", GetStashName(entity))); + _popupSystem.PopupEntity(popup, storedInside[0], PopupType.MediumCaution); } + } - private void OnInit(EntityUid uid, SecretStashComponent component, ComponentInit args) + private void OnInteractUsing(Entity entity, ref InteractUsingEvent args) + { + if (args.Handled) + return; + + if (entity.Comp.IsStashOpen) { - component.ItemContainer = _containerSystem.EnsureContainer(uid, "stash", out _); + if (!entity.Comp.CanBeOpenedAndClosed || !TryOpenOrCloseStash(entity, args.Used, args.User)) + TryStashItem(entity, args.User, args.Used); + + args.Handled = true; } + else if (entity.Comp.CanBeOpenedAndClosed) + args.Handled = TryOpenOrCloseStash(entity, args.Used, args.User); + } + + private void OnInteractHand(Entity entity, ref InteractHandEvent args) + { + if (args.Handled) + return; + + if (entity.Comp.IsStashOpen) + args.Handled = TryGetItem(entity, args.User); + } + + /// + /// Try to open or close the given stash. + /// + /// Returns false if you can't interact with the stash with the given item. + private bool TryOpenOrCloseStash(Entity entity, EntityUid? toolToToggle, EntityUid user) + { + var neededToolQuantity = entity.Comp.IsStashOpen ? entity.Comp.StashCloseToolQualityNeeded : entity.Comp.StashOpenToolQualityNeeded; + var time = entity.Comp.IsStashOpen ? entity.Comp.CloseStashTime : entity.Comp.OpenStashTime; + var evt = new SecretStashPryDoAfterEventToggleIsOpen(); + + // If neededToolQuantity is null it can only be open be opened with the verbs. + if (toolToToggle == null || neededToolQuantity == null) + return false; + + return _tool.UseTool(toolToToggle.Value, user, entity, time, neededToolQuantity, evt); + } + + private void OnSecretStashOpenStateToggled(Entity entity, ref SecretStashPryDoAfterEventToggleIsOpen args) + { + if (args.Cancelled) + return; + + ToggleSecretStashState(entity); + } + /// + /// Toggle the state of the stash and update appearance. + /// + private void ToggleSecretStashState(Entity entity) + { + entity.Comp.IsStashOpen = !entity.Comp.IsStashOpen; + UpdateAppearance(entity); + Dirty(entity); + } + + /// + /// Tries to hide the given item into the stash. + /// + /// True if item was hidden inside stash and false otherwise. + private bool TryStashItem(Entity entity, EntityUid userUid, EntityUid itemToHideUid) + { + if (!TryComp(itemToHideUid, out var itemComp)) + return false; - private void OnDestroyed(EntityUid uid, SecretStashComponent component, DestructionEventArgs args) + _audio.PlayPredicted(entity.Comp.TryInsertItemSound, entity, userUid, AudioParams.Default.WithVariation(0.25f)); + + // check if secret stash is already occupied + var container = entity.Comp.ItemContainer; + if (HasItemInside(entity)) { - _containerSystem.EmptyContainer(component.ItemContainer); + var popup = Loc.GetString("comp-secret-stash-action-hide-container-not-empty"); + _popupSystem.PopupClient(popup, entity, userUid); + return false; } - /// - /// Is there something inside secret stash item container? - /// - public bool HasItemInside(EntityUid uid, SecretStashComponent? component = null) + // check if item is too big to fit into secret stash + if (_item.GetSizePrototype(itemComp.Size) > _item.GetSizePrototype(entity.Comp.MaxItemSize)) { - if (!Resolve(uid, ref component)) - return false; - return component.ItemContainer.ContainedEntity != null; + var msg = Loc.GetString("comp-secret-stash-action-hide-item-too-big", + ("item", itemToHideUid), ("stashname", GetStashName(entity))); + _popupSystem.PopupClient(msg, entity, userUid); + return false; } - private void OnInteractUsing(EntityUid uid, SecretStashComponent component, InteractUsingEvent args) - { - if (args.Handled) - return; + // try to move item from hands to stash container + if (!_handsSystem.TryDropIntoContainer(userUid, itemToHideUid, container)) + return false; - if (!component.OpenableStash) - return; + // all done, show success message + var successMsg = Loc.GetString("comp-secret-stash-action-hide-success", + ("item", itemToHideUid), ("stashname", GetStashName(entity))); + _popupSystem.PopupClient(successMsg, entity, userUid); + return true; + } - // is player trying place or lift off cistern lid? - if (_tool.UseTool(args.Used, args.User, uid, component.PryDoorTime, component.PryingQuality, new StashPryDoAfterEvent())) - args.Handled = true; - // maybe player is trying to hide something inside cistern? - else if (component.ToggleOpen) - { - TryHideItem(uid, args.User, args.Used); - args.Handled = true; - } - } + /// + /// Try the given item in the stash and place it in users hand. + /// If user can't take hold the item in their hands, the item will be dropped onto the ground. + /// + /// True if user received item. + private bool TryGetItem(Entity entity, EntityUid userUid) + { + if (!TryComp(userUid, out var handsComp)) + return false; - private void OnInteractHand(EntityUid uid, SecretStashComponent component, InteractHandEvent args) - { - if (args.Handled) - return; + _audio.PlayPredicted(entity.Comp.TryRemoveItemSound, entity, userUid, AudioParams.Default.WithVariation(0.25f)); - if (!component.OpenableStash) - return; + // check if secret stash has something inside + var itemInStash = entity.Comp.ItemContainer.ContainedEntity; + if (itemInStash == null) + return false; - // trying to get something from stash? - if (component.ToggleOpen) - { - var gotItem = TryGetItem(uid, args.User); - if (gotItem) - { - args.Handled = true; - return; - } - } - args.Handled = true; - } + _handsSystem.PickupOrDrop(userUid, itemInStash.Value, handsComp: handsComp); - private void OnSecretStashPried(EntityUid uid, SecretStashComponent component, StashPryDoAfterEvent args) - { - if (args.Cancelled) - return; + // show success message + var successMsg = Loc.GetString("comp-secret-stash-action-get-item-found-something", + ("stashname", GetStashName(entity))); + _popupSystem.PopupClient(successMsg, entity, userUid); - ToggleOpen(uid, component); - } + return true; + } - public void ToggleOpen(EntityUid uid, SecretStashComponent? component = null, MetaDataComponent? meta = null) - { - if (!Resolve(uid, ref component)) - return; + #region Helper functions - component.ToggleOpen = !component.ToggleOpen; + private string GetStashName(Entity entity) + { + if (entity.Comp.SecretStashName == null) + return Identity.Name(entity, EntityManager); + return Loc.GetString(entity.Comp.SecretStashName); + } - UpdateAppearance(uid, component); - Dirty(uid, component, meta); - } + private void UpdateAppearance(Entity entity) + { + _appearance.SetData(entity, StashVisuals.StashVisualState, entity.Comp.IsStashOpen ? StashVisualState.StashOpen : StashVisualState.StashClosed); + } - private void UpdateAppearance(EntityUid uid, SecretStashComponent? component = null) - { - if (!Resolve(uid, ref component)) - return; + private bool HasItemInside(Entity entity) + { + return entity.Comp.ItemContainer.ContainedEntity != null; + } - _appearance.SetData(uid, StashVisuals.DoorVisualState, component.ToggleOpen ? DoorVisualState.DoorOpen : DoorVisualState.DoorClosed); - } + #endregion + + #region User interface functions - /// - /// Tries to hide item inside secret stash from hands of user. - /// - /// True if item was hidden inside stash - public bool TryHideItem(EntityUid uid, EntityUid userUid, EntityUid itemToHideUid, - SecretStashComponent? component = null, ItemComponent? item = null, - HandsComponent? hands = null) + private void OnExamine(Entity entity, ref ExaminedEvent args) + { + if (args.IsInDetailsRange && entity.Comp.IsStashOpen) { - if (!Resolve(uid, ref component)) - return false; - if (!Resolve(itemToHideUid, ref item)) - return false; - if (!Resolve(userUid, ref hands)) - return false; - - // check if secret stash is already occupied - var container = component.ItemContainer; - if (container.ContainedEntity != null) + if (HasItemInside(entity)) { - var msg = Loc.GetString("comp-secret-stash-action-hide-container-not-empty"); - _popupSystem.PopupClient(msg, uid, userUid); - return false; + var msg = Loc.GetString("comp-secret-stash-on-examine-found-hidden-item", ("stashname", GetStashName(entity))); + args.PushMarkup(msg); } + } + } - // check if item is too big to fit into secret stash - if (_item.GetSizePrototype(item.Size) > _item.GetSizePrototype(component.MaxItemSize)) - { - var msg = Loc.GetString("comp-secret-stash-action-hide-item-too-big", - ("item", itemToHideUid), ("stash", GetSecretPartName(uid, component))); - _popupSystem.PopupClient(msg, uid, userUid); - return false; - } + private void OnGetVerb(Entity entity, ref GetVerbsEvent args) + { + if (!args.CanInteract || !args.CanAccess || !entity.Comp.HasVerbs) + return; - // try to move item from hands to stash container - if (!_handsSystem.TryDropIntoContainer(userUid, itemToHideUid, container)) - { - return false; - } + var user = args.User; + var item = args.Using; + var stashName = GetStashName(entity); - // all done, show success message - var successMsg = Loc.GetString("comp-secret-stash-action-hide-success", - ("item", itemToHideUid), ("this", GetSecretPartName(uid, component))); - _popupSystem.PopupClient(successMsg, uid, userUid); - return true; - } + var itemVerb = new InteractionVerb(); + var toggleVerb = new InteractionVerb(); - /// - /// Try get item and place it in users hand. - /// If user can't take it by hands, will drop item from container. - /// - /// True if user received item - public bool TryGetItem(EntityUid uid, EntityUid userUid, SecretStashComponent? component = null, - HandsComponent? hands = null) + // This will add the verb relating to inserting / grabbing items. + if (entity.Comp.IsStashOpen) { - if (!Resolve(uid, ref component)) - return false; - if (!Resolve(userUid, ref hands)) - return false; - - // check if secret stash has something inside - var container = component.ItemContainer; - if (container.ContainedEntity == null) + if (item != null) { - return false; - } + itemVerb.Text = Loc.GetString("comp-secret-stash-verb-insert-into-stash"); + if (HasItemInside(entity)) + { + itemVerb.Disabled = true; + itemVerb.Message = Loc.GetString("comp-secret-stash-verb-insert-message-item-already-inside", ("stashname", stashName)); + } + else + { + itemVerb.Message = Loc.GetString("comp-secret-stash-verb-insert-message-no-item", ("item", item), ("stashname", stashName)); + } - _handsSystem.PickupOrDrop(userUid, container.ContainedEntity.Value, handsComp: hands); + itemVerb.Act = () => TryStashItem(entity, user, item.Value); + } + else + { + itemVerb.Text = Loc.GetString("comp-secret-stash-verb-take-out-item"); + itemVerb.Message = Loc.GetString("comp-secret-stash-verb-take-out-message-something", ("stashname", stashName)); + if (!HasItemInside(entity)) + { + itemVerb.Disabled = true; + itemVerb.Message = Loc.GetString("comp-secret-stash-verb-take-out-message-nothing", ("stashname", stashName)); + } - // show success message - var successMsg = Loc.GetString("comp-secret-stash-action-get-item-found-something", - ("stash", GetSecretPartName(uid, component))); - _popupSystem.PopupClient(successMsg, uid, userUid); + itemVerb.Act = () => TryGetItem(entity, user); + } - return true; + args.Verbs.Add(itemVerb); } - private void OnExamine(EntityUid uid, SecretStashComponent component, ExaminedEvent args) + // You can't open or close so skip this verb. + if (!entity.Comp.CanBeOpenedAndClosed) + return; + + // This verb is for opening / closing the stash. + if (entity.Comp.IsStashOpen) { - if (args.IsInDetailsRange && component.ToggleOpen) + toggleVerb.Text = toggleVerb.Message = Loc.GetString("comp-secret-stash-verb-close"); + var neededQual = entity.Comp.StashCloseToolQualityNeeded; + + // If neededQual is null you don't need a tool to open / close. + if (neededQual != null && + (item == null || !_tool.HasQuality(item.Value, neededQual))) { - if (HasItemInside(uid)) - { - var msg = Loc.GetString(component.ExamineStash); - args.PushMarkup(msg); - } + toggleVerb.Disabled = true; + toggleVerb.Message = Loc.GetString("comp-secret-stash-verb-cant-close", ("stashname", stashName)); } - } - private string GetSecretPartName(EntityUid uid, SecretStashComponent stash) + if (neededQual == null) + toggleVerb.Act = () => ToggleSecretStashState(entity); + else + toggleVerb.Act = () => TryOpenOrCloseStash(entity, item, user); + + args.Verbs.Add(toggleVerb); + } + else { - if (stash.SecretPartName != "") - return Loc.GetString(stash.SecretPartName); + // The open verb should only appear when holding the correct tool or if no tool is needed. - var entityName = Loc.GetString("comp-secret-stash-secret-part-name", ("this", uid)); + toggleVerb.Text = toggleVerb.Message = Loc.GetString("comp-secret-stash-verb-open"); + var neededQual = entity.Comp.StashOpenToolQualityNeeded; - return entityName; + if (neededQual == null) + { + toggleVerb.Act = () => ToggleSecretStashState(entity); + args.Verbs.Add(toggleVerb); + } + else if (item != null && _tool.HasQuality(item.Value, neededQual)) + { + toggleVerb.Act = () => TryOpenOrCloseStash(entity, item, user); + args.Verbs.Add(toggleVerb); + } } } + + #endregion } diff --git a/Resources/Locale/en-US/storage/components/secret-stash-component.ftl b/Resources/Locale/en-US/storage/components/secret-stash-component.ftl index d0dfed2b5dda..2a8f505e2b1c 100644 --- a/Resources/Locale/en-US/storage/components/secret-stash-component.ftl +++ b/Resources/Locale/en-US/storage/components/secret-stash-component.ftl @@ -1,11 +1,24 @@ ### Secret stash component. Stuff like potted plants, comfy chair cushions, etc... -comp-secret-stash-secret-part-name = { THE($item) } -comp-secret-stash-action-hide-success = You hide { THE($item) } in { $this } -comp-secret-stash-action-hide-container-not-empty = There's already something in here?! -comp-secret-stash-action-hide-item-too-big = { THE($item) } is too big to fit in {$stash}! -comp-secret-stash-action-get-item-found-something = There was something inside {$stash}! -comp-secret-stash-on-examine-found-hidden-item = There is something hidden inside. - -secret-stash-part-plant = the plant -secret-stash-part-toilet = the toilet cistern +comp-secret-stash-action-hide-success = You hide { THE($item) } in the {$stashname}. +comp-secret-stash-action-hide-container-not-empty = There's already something in here!? +comp-secret-stash-action-hide-item-too-big = { THE($item) } is too big to fit in the {$stashname}. +comp-secret-stash-action-get-item-found-something = There was something inside the {$stashname}! +comp-secret-stash-on-examine-found-hidden-item = There is something hidden inside the {$stashname}! +comp-secret-stash-on-destroyed-popup = Something falls out of the the {$stashname}! + +### Verbs +comp-secret-stash-verb-insert-into-stash = Stash item +comp-secret-stash-verb-insert-message-item-already-inside = There is already an item inside the {$stashname}. +comp-secret-stash-verb-insert-message-no-item = Hide { THE($item) } in the {$stashname}. +comp-secret-stash-verb-take-out-item = Grab item +comp-secret-stash-verb-take-out-message-something = Take the contents of the {$stashname} out. +comp-secret-stash-verb-take-out-message-nothing = There is nothing inside the {$stashname}. + +comp-secret-stash-verb-close = Close +comp-secret-stash-verb-cant-close = You can't close the {$stashname} with that. +comp-secret-stash-verb-open = Open + +### Stash names +secret-stash-plant = plant +secret-stash-toilet = toilet cistern diff --git a/Resources/Prototypes/Entities/Structures/Furniture/potted_plants.yml b/Resources/Prototypes/Entities/Structures/Furniture/potted_plants.yml index 53d268facf4d..f30b816ae9e7 100644 --- a/Resources/Prototypes/Entities/Structures/Furniture/potted_plants.yml +++ b/Resources/Prototypes/Entities/Structures/Furniture/potted_plants.yml @@ -24,9 +24,13 @@ offset: "0.0,0.3" sprite: Structures/Furniture/potted_plants.rsi noRot: true - - type: PottedPlantHide - type: SecretStash - secretPartName: secret-stash-part-plant + isStashOpen: true + tryInsertItemSound: /Audio/Effects/plant_rustle.ogg + tryRemoveItemSound: /Audio/Effects/plant_rustle.ogg + canBeOpenedAndClosed: false + hasVerbs: false + secretStashName: secret-stash-plant - type: ContainerContainer containers: stash: !type:ContainerSlot {} diff --git a/Resources/Prototypes/Entities/Structures/Furniture/toilet.yml b/Resources/Prototypes/Entities/Structures/Furniture/toilet.yml index 1eb5176e9e03..26e8c686e5ed 100644 --- a/Resources/Prototypes/Entities/Structures/Furniture/toilet.yml +++ b/Resources/Prototypes/Entities/Structures/Furniture/toilet.yml @@ -68,9 +68,9 @@ - type: PlungerUse - type: Appearance - type: SecretStash - secretPartName: secret-stash-part-toilet - examineStash: toilet-component-on-examine-found-hidden-item - openableStash: true + stashOpenToolQualityNeeded: Prying + stashCloseToolQualityNeeded: Prying + secretStashName: secret-stash-toilet - type: Drain autoDrain: false - type: StaticPrice @@ -103,10 +103,10 @@ SeatVisualState.SeatUp: SeatUp: { state: disposal-up } SeatDown: { state: disposal-down } - enum.StashVisuals.DoorVisualState: - DoorVisualState.DoorOpen: - DoorOpen: { state: disposal-open } - DoorClosed: { state: disposal-closed } + enum.StashVisuals.StashVisualState: + StashVisualState.StashOpen: + StashOpen: { state: disposal-open } + StashClosed: { state: disposal-closed } - type: entity id: ToiletDirtyWater From 8d530021c1df50acc108123483485f314394e655 Mon Sep 17 00:00:00 2001 From: Beck Date: Sun, 23 Jun 2024 18:30:12 -0700 Subject: [PATCH 2/4] Will do this in another PR! --- Content.Server/Nutrition/EntitySystems/FoodSystem.cs | 5 ----- 1 file changed, 5 deletions(-) diff --git a/Content.Server/Nutrition/EntitySystems/FoodSystem.cs b/Content.Server/Nutrition/EntitySystems/FoodSystem.cs index a359e0ffae4b..1862b4e19f14 100644 --- a/Content.Server/Nutrition/EntitySystems/FoodSystem.cs +++ b/Content.Server/Nutrition/EntitySystems/FoodSystem.cs @@ -33,7 +33,6 @@ using System.Linq; using Robust.Server.GameObjects; using Content.Shared.Whitelist; -using Content.Shared.Destructible; namespace Content.Server.Nutrition.EntitySystems; @@ -323,10 +322,6 @@ public void DeleteAndSpawnTrash(FoodComponent component, EntityUid food, EntityU if (ev.Cancelled) return; - var dev = new DestructionEventArgs(); - - RaiseLocalEvent(food, dev); - if (string.IsNullOrEmpty(component.Trash)) { QueueDel(food); From 066bc9dacffbf6d8f8c08a55eae14fe1ea9f947d Mon Sep 17 00:00:00 2001 From: Beck Date: Mon, 24 Jun 2024 22:13:57 -0700 Subject: [PATCH 3/4] maybe? --- Content.Shared/Storage/EntitySystems/SecretStashSystem.cs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Content.Shared/Storage/EntitySystems/SecretStashSystem.cs b/Content.Shared/Storage/EntitySystems/SecretStashSystem.cs index 0470a344aac8..6eef309fd6f7 100644 --- a/Content.Shared/Storage/EntitySystems/SecretStashSystem.cs +++ b/Content.Shared/Storage/EntitySystems/SecretStashSystem.cs @@ -267,7 +267,7 @@ private void OnGetVerb(Entity entity, ref GetVerbsEvent Date: Sun, 30 Jun 2024 22:47:28 -0700 Subject: [PATCH 4/4] Moved stuff to ToolOpenableSystem because its smarter and cooler --- .../Nutrition/EntitySystems/UtensilSystem.cs | 3 +- .../Components/SecretStashComponent.cs | 63 ------- .../EntitySystems/SecretStashSystem.cs | 162 ++++------------- .../Tools/Components/ToolOpenableComponent.cs | 84 +++++++++ .../Tools/Systems/ToolOpenableSystem.cs | 171 ++++++++++++++++++ .../components/tool-openable-component.ftl | 6 + .../Structures/Furniture/potted_plants.yml | 2 - .../Entities/Structures/Furniture/toilet.yml | 14 +- 8 files changed, 302 insertions(+), 203 deletions(-) create mode 100644 Content.Shared/Tools/Components/ToolOpenableComponent.cs create mode 100644 Content.Shared/Tools/Systems/ToolOpenableSystem.cs create mode 100644 Resources/Locale/en-US/tools/components/tool-openable-component.ftl diff --git a/Content.Server/Nutrition/EntitySystems/UtensilSystem.cs b/Content.Server/Nutrition/EntitySystems/UtensilSystem.cs index 67f66d3ee76d..80f6bb8cc86c 100644 --- a/Content.Server/Nutrition/EntitySystems/UtensilSystem.cs +++ b/Content.Server/Nutrition/EntitySystems/UtensilSystem.cs @@ -4,6 +4,7 @@ using Content.Shared.Interaction; using Content.Shared.Nutrition.Components; using Content.Shared.Nutrition.EntitySystems; +using Content.Shared.Tools.EntitySystems; using Robust.Shared.Audio; using Robust.Shared.Audio.Systems; using Robust.Shared.Random; @@ -25,7 +26,7 @@ public override void Initialize() { base.Initialize(); - SubscribeLocalEvent(OnAfterInteract, after: new[] { typeof(ItemSlotsSystem) }); + SubscribeLocalEvent(OnAfterInteract, after: new[] { typeof(ItemSlotsSystem), typeof(ToolOpenableSystem) }); } /// diff --git a/Content.Shared/Storage/Components/SecretStashComponent.cs b/Content.Shared/Storage/Components/SecretStashComponent.cs index b9cf06396a71..3bf8e2e871ba 100644 --- a/Content.Shared/Storage/Components/SecretStashComponent.cs +++ b/Content.Shared/Storage/Components/SecretStashComponent.cs @@ -26,38 +26,6 @@ public sealed partial class SecretStashComponent : Component [DataField("maxItemSize")] public ProtoId MaxItemSize = "Small"; - /// - /// If stash has way to open then this will switch between open and closed. - /// - [DataField, AutoNetworkedField] - public bool IsStashOpen = false; - - /// - /// If a tool is needed to open the stash, this time will be used. - /// - [DataField] - public float OpenStashTime = 1f; - - /// - /// If a tool is needed to close the stash, this time will be used. - /// - [DataField] - public float CloseStashTime = 1f; - - /// - /// What type of tool quality is needed to open the stash. - /// If null, the stash will only be openable by a verb. - /// - [DataField] - public ProtoId? StashOpenToolQualityNeeded; - - /// - /// What type of tool quality is needed to close the stash. - /// If null, the stash will only be closable by a verb. - /// - [DataField] - public ProtoId? StashCloseToolQualityNeeded; - /// /// This sound will be played when you try to insert an item in the stash. /// The sound will be played whether or not the item is actually inserted. @@ -72,12 +40,6 @@ public sealed partial class SecretStashComponent : Component [DataField] public SoundSpecifier? TryRemoveItemSound; - /// - /// If true the stash can be opened and closed, if false the stash cannot be opened or closed. - /// - [DataField, AutoNetworkedField] - public bool CanBeOpenedAndClosed = true; - /// /// If true, verbs will appear to help interact with the stash. /// @@ -96,30 +58,5 @@ public sealed partial class SecretStashComponent : Component /// [ViewVariables] public ContainerSlot ItemContainer = default!; - - } - - /// - /// Simple pry event for prying open a stash door. - /// - [Serializable, NetSerializable] - public sealed partial class SecretStashPryDoAfterEventToggleIsOpen : SimpleDoAfterEvent - { - } - - /// - /// Visualizers for handling stash open closed state if stash has door. - /// - [Serializable, NetSerializable] - public enum StashVisuals : byte - { - StashVisualState, - } - - [Serializable, NetSerializable] - public enum StashVisualState : byte - { - StashOpen, - StashClosed } } diff --git a/Content.Shared/Storage/EntitySystems/SecretStashSystem.cs b/Content.Shared/Storage/EntitySystems/SecretStashSystem.cs index 6eef309fd6f7..901d744df5f3 100644 --- a/Content.Shared/Storage/EntitySystems/SecretStashSystem.cs +++ b/Content.Shared/Storage/EntitySystems/SecretStashSystem.cs @@ -12,6 +12,7 @@ using Robust.Shared.Audio.Systems; using Content.Shared.Verbs; using Content.Shared.IdentityManagement; +using Content.Shared.Tools.EntitySystems; namespace Content.Shared.Storage.EntitySystems; @@ -24,27 +25,23 @@ public sealed class SecretStashSystem : EntitySystem [Dependency] private readonly SharedHandsSystem _handsSystem = default!; [Dependency] private readonly SharedContainerSystem _containerSystem = default!; [Dependency] private readonly SharedItemSystem _item = default!; - [Dependency] private readonly SharedToolSystem _tool = default!; - [Dependency] private readonly SharedAppearanceSystem _appearance = default!; [Dependency] private readonly SharedAudioSystem _audio = default!; + [Dependency] private readonly ToolOpenableSystem _toolOpenableSystem = default!; + public override void Initialize() { base.Initialize(); SubscribeLocalEvent(OnInit); SubscribeLocalEvent(OnDestroyed); - SubscribeLocalEvent(OnSecretStashOpenStateToggled); - SubscribeLocalEvent(OnInteractUsing); + SubscribeLocalEvent(OnInteractUsing, after: new[] { typeof(ToolOpenableSystem) }); SubscribeLocalEvent(OnInteractHand); - SubscribeLocalEvent(OnExamine); SubscribeLocalEvent>(OnGetVerb); } private void OnInit(Entity entity, ref ComponentInit args) { - UpdateAppearance(entity); entity.Comp.ItemContainer = _containerSystem.EnsureContainer(entity, "stash", out _); - Dirty(entity); } private void OnDestroyed(Entity entity, ref DestructionEventArgs args) @@ -59,61 +56,18 @@ private void OnDestroyed(Entity entity, ref DestructionEve private void OnInteractUsing(Entity entity, ref InteractUsingEvent args) { - if (args.Handled) + if (args.Handled || !IsStashOpen(entity)) return; - if (entity.Comp.IsStashOpen) - { - if (!entity.Comp.CanBeOpenedAndClosed || !TryOpenOrCloseStash(entity, args.Used, args.User)) - TryStashItem(entity, args.User, args.Used); - - args.Handled = true; - } - else if (entity.Comp.CanBeOpenedAndClosed) - args.Handled = TryOpenOrCloseStash(entity, args.Used, args.User); + args.Handled = TryStashItem(entity, args.User, args.Used); } private void OnInteractHand(Entity entity, ref InteractHandEvent args) { - if (args.Handled) + if (args.Handled || !IsStashOpen(entity)) return; - if (entity.Comp.IsStashOpen) - args.Handled = TryGetItem(entity, args.User); - } - - /// - /// Try to open or close the given stash. - /// - /// Returns false if you can't interact with the stash with the given item. - private bool TryOpenOrCloseStash(Entity entity, EntityUid? toolToToggle, EntityUid user) - { - var neededToolQuantity = entity.Comp.IsStashOpen ? entity.Comp.StashCloseToolQualityNeeded : entity.Comp.StashOpenToolQualityNeeded; - var time = entity.Comp.IsStashOpen ? entity.Comp.CloseStashTime : entity.Comp.OpenStashTime; - var evt = new SecretStashPryDoAfterEventToggleIsOpen(); - - // If neededToolQuantity is null it can only be open be opened with the verbs. - if (toolToToggle == null || neededToolQuantity == null) - return false; - - return _tool.UseTool(toolToToggle.Value, user, entity, time, neededToolQuantity, evt); - } - - private void OnSecretStashOpenStateToggled(Entity entity, ref SecretStashPryDoAfterEventToggleIsOpen args) - { - if (args.Cancelled) - return; - - ToggleSecretStashState(entity); - } - /// - /// Toggle the state of the stash and update appearance. - /// - private void ToggleSecretStashState(Entity entity) - { - entity.Comp.IsStashOpen = !entity.Comp.IsStashOpen; - UpdateAppearance(entity); - Dirty(entity); + args.Handled = TryGetItem(entity, args.User); } /// @@ -183,41 +137,6 @@ private bool TryGetItem(Entity entity, EntityUid userUid) return true; } - #region Helper functions - - private string GetStashName(Entity entity) - { - if (entity.Comp.SecretStashName == null) - return Identity.Name(entity, EntityManager); - return Loc.GetString(entity.Comp.SecretStashName); - } - - private void UpdateAppearance(Entity entity) - { - _appearance.SetData(entity, StashVisuals.StashVisualState, entity.Comp.IsStashOpen ? StashVisualState.StashOpen : StashVisualState.StashClosed); - } - - private bool HasItemInside(Entity entity) - { - return entity.Comp.ItemContainer.ContainedEntity != null; - } - - #endregion - - #region User interface functions - - private void OnExamine(Entity entity, ref ExaminedEvent args) - { - if (args.IsInDetailsRange && entity.Comp.IsStashOpen) - { - if (HasItemInside(entity)) - { - var msg = Loc.GetString("comp-secret-stash-on-examine-found-hidden-item", ("stashname", GetStashName(entity))); - args.PushMarkup(msg); - } - } - } - private void OnGetVerb(Entity entity, ref GetVerbsEvent args) { if (!args.CanInteract || !args.CanAccess || !entity.Comp.HasVerbs) @@ -228,10 +147,9 @@ private void OnGetVerb(Entity entity, ref GetVerbsEvent entity, ref GetVerbsEvent ToggleSecretStashState(entity); - else - toggleVerb.Act = () => TryOpenOrCloseStash(entity, item, user); + #region Helper functions - args.Verbs.Add(toggleVerb); - } - else - { - // The open verb should only appear when holding the correct tool or if no tool is needed. + /// + /// The stash name if it exists, or the entity name if it doesn't. + /// + private string GetStashName(Entity entity) + { + if (entity.Comp.SecretStashName == null) + return Identity.Name(entity, EntityManager); + return Loc.GetString(entity.Comp.SecretStashName); + } - toggleVerb.Text = toggleVerb.Message = Loc.GetString("comp-secret-stash-verb-open"); - var neededQual = entity.Comp.StashOpenToolQualityNeeded; + /// + /// True if the stash is open OR the there is no toolOpenableComponent attacheded to the entity + /// and false otherwise. + /// + private bool IsStashOpen(Entity stash) + { + return _toolOpenableSystem.IsOpen(stash); + } - if (neededQual == null) - { - toggleVerb.Act = () => ToggleSecretStashState(entity); - args.Verbs.Add(toggleVerb); - } - else if (item != null && _tool.HasQuality(item.Value, neededQual)) - { - toggleVerb.Act = () => TryOpenOrCloseStash(entity, item, user); - args.Verbs.Add(toggleVerb); - } - } + private bool HasItemInside(Entity entity) + { + return entity.Comp.ItemContainer.ContainedEntity != null; } #endregion diff --git a/Content.Shared/Tools/Components/ToolOpenableComponent.cs b/Content.Shared/Tools/Components/ToolOpenableComponent.cs new file mode 100644 index 000000000000..82cdf611da9a --- /dev/null +++ b/Content.Shared/Tools/Components/ToolOpenableComponent.cs @@ -0,0 +1,84 @@ +using Robust.Shared.Prototypes; +using Robust.Shared.GameStates; +using Content.Shared.DoAfter; +using Robust.Shared.Serialization; + +namespace Content.Shared.Tools.Components +{ + /// + /// Logic for using tools (Or verbs) to open / close something on an entity. + /// + [RegisterComponent, NetworkedComponent, AutoGenerateComponentState] + public sealed partial class ToolOpenableComponent : Component + { + /// + /// Is the openable part open or closed? + /// + [DataField, AutoNetworkedField] + public bool IsOpen = false; + + /// + /// If a tool is needed to open the entity, this time will be used. + /// + [DataField] + public float OpenTime = 1f; + + /// + /// If a tool is needed to close the entity, this time will be used. + /// + [DataField] + public float CloseTime = 1f; + + /// + /// What type of tool quality is needed to open this? + /// If null, the it will only be openable by a verb. + /// + [DataField] + public ProtoId? OpenToolQualityNeeded; + + /// + /// What type of tool quality is needed to close this. + /// If null, this will only be closable by a verb. + /// + [DataField] + public ProtoId? CloseToolQualityNeeded; + + /// + /// If true, verbs will appear to help interact with opening / closing. + /// + [DataField, AutoNetworkedField] + public bool HasVerbs = true; + + /// + /// The name of what is being open and closed. + /// E.g toilet lid, pannel, compartment. + /// + [DataField, AutoNetworkedField] + public string? Name; + + } + + /// + /// Simple do after event for opening or closing. + /// + [Serializable, NetSerializable] + public sealed partial class ToolOpenableDoAfterEventToggleOpen : SimpleDoAfterEvent + { + } + + /// + /// Visualizers for handling stash open closed state if stash has door. + /// + [Serializable, NetSerializable] + public enum ToolOpenableVisuals : byte + { + ToolOpenableVisualState, + } + + [Serializable, NetSerializable] + public enum ToolOpenableVisualState : byte + { + Open, + Closed + } +} diff --git a/Content.Shared/Tools/Systems/ToolOpenableSystem.cs b/Content.Shared/Tools/Systems/ToolOpenableSystem.cs new file mode 100644 index 000000000000..4951040350af --- /dev/null +++ b/Content.Shared/Tools/Systems/ToolOpenableSystem.cs @@ -0,0 +1,171 @@ +using Content.Shared.IdentityManagement; +using Content.Shared.Tools.Components; +using Content.Shared.Tools.Systems; +using Content.Shared.Interaction; +using Content.Shared.Examine; +using Content.Shared.Verbs; + +namespace Content.Shared.Tools.EntitySystems; + +public sealed class ToolOpenableSystem : EntitySystem +{ + [Dependency] private readonly SharedToolSystem _tool = default!; + [Dependency] private readonly SharedAppearanceSystem _appearance = default!; + + public override void Initialize() + { + base.Initialize(); + SubscribeLocalEvent(OnInit); + SubscribeLocalEvent(OnOpenableStateToggled); + SubscribeLocalEvent(OnInteractUsing); + SubscribeLocalEvent(OnExamine); + SubscribeLocalEvent>(OnGetVerb); + } + + private void OnInit(Entity entity, ref ComponentInit args) + { + UpdateAppearance(entity); + Dirty(entity); + } + + private void OnInteractUsing(Entity entity, ref InteractUsingEvent args) + { + if (args.Handled) + return; + + if (TryOpenClose(entity, args.Used, args.User)) + args.Handled = true; + } + + /// + /// Try to open or close what is openable. + /// + /// Returns false if you can't interact with the openable thing with the given item. + private bool TryOpenClose(Entity entity, EntityUid? toolToToggle, EntityUid user) + { + var neededToolQuantity = entity.Comp.IsOpen ? entity.Comp.CloseToolQualityNeeded : entity.Comp.OpenToolQualityNeeded; + var time = entity.Comp.IsOpen ? entity.Comp.CloseTime : entity.Comp.OpenTime; + var evt = new ToolOpenableDoAfterEventToggleOpen(); + + // If neededToolQuantity is null it can only be open be opened with the verbs. + if (toolToToggle == null || neededToolQuantity == null) + return false; + + return _tool.UseTool(toolToToggle.Value, user, entity, time, neededToolQuantity, evt); + } + + private void OnOpenableStateToggled(Entity entity, ref ToolOpenableDoAfterEventToggleOpen args) + { + if (args.Cancelled) + return; + + ToggleState(entity); + } + + /// + /// Toggle the state and update appearance. + /// + private void ToggleState(Entity entity) + { + entity.Comp.IsOpen = !entity.Comp.IsOpen; + UpdateAppearance(entity); + Dirty(entity); + } + + #region Helper functions + + private string GetName(Entity entity) + { + if (entity.Comp.Name == null) + return Identity.Name(entity, EntityManager); + return Loc.GetString(entity.Comp.Name); + } + + public bool IsOpen(EntityUid uid, ToolOpenableComponent? component = null) + { + if (!Resolve(uid, ref component, false)) + return true; + + return component.IsOpen; + } + + private void UpdateAppearance(Entity entity) + { + _appearance.SetData(entity, ToolOpenableVisuals.ToolOpenableVisualState, entity.Comp.IsOpen ? ToolOpenableVisualState.Open : ToolOpenableVisualState.Closed); + } + + #endregion + + #region User interface functions + + private void OnExamine(Entity entity, ref ExaminedEvent args) + { + if (!args.IsInDetailsRange) + return; + + string msg; + var name = GetName(entity); + if (entity.Comp.IsOpen) + msg = Loc.GetString("tool-openable-component-examine-opened", ("name", name)); + else + msg = Loc.GetString("tool-openable-component-examine-closed", ("name", name)); + + args.PushMarkup(msg); + } + + private void OnGetVerb(Entity entity, ref GetVerbsEvent args) + { + if (!args.CanInteract || !args.CanAccess || !entity.Comp.HasVerbs) + return; + + var user = args.User; + var item = args.Using; + var name = GetName(entity); + + var toggleVerb = new InteractionVerb + { + IconEntity = GetNetEntity(item) + }; + + if (entity.Comp.IsOpen) + { + toggleVerb.Text = toggleVerb.Message = Loc.GetString("tool-openable-component-verb-close"); + var neededQual = entity.Comp.CloseToolQualityNeeded; + + // If neededQual is null you don't need a tool to open / close. + if (neededQual != null && + (item == null || !_tool.HasQuality(item.Value, neededQual))) + { + toggleVerb.Disabled = true; + toggleVerb.Message = Loc.GetString("tool-openable-component-verb-cant-close", ("name", name)); + } + + if (neededQual == null) + toggleVerb.Act = () => ToggleState(entity); + else + toggleVerb.Act = () => TryOpenClose(entity, item, user); + + args.Verbs.Add(toggleVerb); + } + else + { + // The open verb should only appear when holding the correct tool or if no tool is needed. + + toggleVerb.Text = toggleVerb.Message = Loc.GetString("tool-openable-component-verb-open"); + var neededQual = entity.Comp.OpenToolQualityNeeded; + + if (neededQual == null) + { + toggleVerb.Act = () => ToggleState(entity); + args.Verbs.Add(toggleVerb); + } + else if (item != null && _tool.HasQuality(item.Value, neededQual)) + { + toggleVerb.Act = () => TryOpenClose(entity, item, user); + args.Verbs.Add(toggleVerb); + } + } + } + + #endregion +} diff --git a/Resources/Locale/en-US/tools/components/tool-openable-component.ftl b/Resources/Locale/en-US/tools/components/tool-openable-component.ftl new file mode 100644 index 000000000000..c45b6c69dafb --- /dev/null +++ b/Resources/Locale/en-US/tools/components/tool-openable-component.ftl @@ -0,0 +1,6 @@ +tool-openable-component-examine-closed = The {$name} is closed. +tool-openable-component-examine-opened = The {$name} is open. + +tool-openable-component-verb-close = Close +tool-openable-component-verb-open = Open +tool-openable-component-verb-cant-close = You can't close the {$name} with that. diff --git a/Resources/Prototypes/Entities/Structures/Furniture/potted_plants.yml b/Resources/Prototypes/Entities/Structures/Furniture/potted_plants.yml index f30b816ae9e7..2d35c94bf10c 100644 --- a/Resources/Prototypes/Entities/Structures/Furniture/potted_plants.yml +++ b/Resources/Prototypes/Entities/Structures/Furniture/potted_plants.yml @@ -25,10 +25,8 @@ sprite: Structures/Furniture/potted_plants.rsi noRot: true - type: SecretStash - isStashOpen: true tryInsertItemSound: /Audio/Effects/plant_rustle.ogg tryRemoveItemSound: /Audio/Effects/plant_rustle.ogg - canBeOpenedAndClosed: false hasVerbs: false secretStashName: secret-stash-plant - type: ContainerContainer diff --git a/Resources/Prototypes/Entities/Structures/Furniture/toilet.yml b/Resources/Prototypes/Entities/Structures/Furniture/toilet.yml index 26e8c686e5ed..6a603b7deb25 100644 --- a/Resources/Prototypes/Entities/Structures/Furniture/toilet.yml +++ b/Resources/Prototypes/Entities/Structures/Furniture/toilet.yml @@ -68,9 +68,11 @@ - type: PlungerUse - type: Appearance - type: SecretStash - stashOpenToolQualityNeeded: Prying - stashCloseToolQualityNeeded: Prying secretStashName: secret-stash-toilet + - type: ToolOpenable + openToolQualityNeeded: Prying + closeToolQualityNeeded: Prying + name: secret-stash-toilet - type: Drain autoDrain: false - type: StaticPrice @@ -103,10 +105,10 @@ SeatVisualState.SeatUp: SeatUp: { state: disposal-up } SeatDown: { state: disposal-down } - enum.StashVisuals.StashVisualState: - StashVisualState.StashOpen: - StashOpen: { state: disposal-open } - StashClosed: { state: disposal-closed } + enum.ToolOpenableVisuals.ToolOpenableVisualState: + ToolOpenableVisualState.StashOpen: + Open: { state: disposal-open } + Closed: { state: disposal-closed } - type: entity id: ToiletDirtyWater