diff --git a/Content.Client/Disposal/Systems/DisposalUnitSystem.cs b/Content.Client/Disposal/Systems/DisposalUnitSystem.cs index 8c40c78421..b9e4a38660 100644 --- a/Content.Client/Disposal/Systems/DisposalUnitSystem.cs +++ b/Content.Client/Disposal/Systems/DisposalUnitSystem.cs @@ -96,24 +96,22 @@ public sealed class DisposalUnitSystem : SharedDisposalUnitSystem private void UpdateState(EntityUid uid, SharedDisposalUnitComponent unit, SpriteComponent sprite, AppearanceComponent appearance) { if (!_appearanceSystem.TryGetData(uid, Visuals.VisualState, out var state, appearance)) - { return; - } sprite.LayerSetVisible(DisposalUnitVisualLayers.Unanchored, state == VisualState.UnAnchored); sprite.LayerSetVisible(DisposalUnitVisualLayers.Base, state == VisualState.Anchored); - sprite.LayerSetVisible(DisposalUnitVisualLayers.BaseFlush, state is VisualState.Flushing or VisualState.Charging); + sprite.LayerSetVisible(DisposalUnitVisualLayers.OverlayFlush, state is VisualState.OverlayFlushing or VisualState.OverlayCharging); var chargingState = sprite.LayerMapTryGet(DisposalUnitVisualLayers.BaseCharging, out var chargingLayer) ? sprite.LayerGetState(chargingLayer) : new RSI.StateId(DefaultChargeState); // This is a transient state so not too worried about replaying in range. - if (state == VisualState.Flushing) + if (state == VisualState.OverlayFlushing) { if (!_animationSystem.HasRunningAnimation(uid, AnimationKey)) { - var flushState = sprite.LayerMapTryGet(DisposalUnitVisualLayers.BaseFlush, out var flushLayer) + var flushState = sprite.LayerMapTryGet(DisposalUnitVisualLayers.OverlayFlush, out var flushLayer) ? sprite.LayerGetState(flushLayer) : new RSI.StateId(DefaultFlushState); @@ -125,7 +123,7 @@ public sealed class DisposalUnitSystem : SharedDisposalUnitSystem { new AnimationTrackSpriteFlick { - LayerKey = DisposalUnitVisualLayers.BaseFlush, + LayerKey = DisposalUnitVisualLayers.OverlayFlush, KeyFrames = { // Play the flush animation @@ -154,26 +152,18 @@ public sealed class DisposalUnitSystem : SharedDisposalUnitSystem _animationSystem.Play(uid, anim, AnimationKey); } } - else if (state == VisualState.Charging) - { - sprite.LayerSetState(DisposalUnitVisualLayers.BaseFlush, chargingState); - } + else if (state == VisualState.OverlayCharging) + sprite.LayerSetState(DisposalUnitVisualLayers.OverlayFlush, new RSI.StateId("disposal-charging")); else - { _animationSystem.Stop(uid, AnimationKey); - } if (!_appearanceSystem.TryGetData(uid, Visuals.Handle, out var handleState, appearance)) - { handleState = HandleState.Normal; - } sprite.LayerSetVisible(DisposalUnitVisualLayers.OverlayEngaged, handleState != HandleState.Normal); if (!_appearanceSystem.TryGetData(uid, Visuals.Light, out var lightState, appearance)) - { lightState = LightStates.Off; - } sprite.LayerSetVisible(DisposalUnitVisualLayers.OverlayCharging, (lightState & LightStates.Charging) != 0); @@ -189,7 +179,7 @@ public enum DisposalUnitVisualLayers : byte Unanchored, Base, BaseCharging, - BaseFlush, + OverlayFlush, OverlayCharging, OverlayReady, OverlayFull, diff --git a/Content.Client/Toilet/ToiletVisualsSystem.cs b/Content.Client/Toilet/ToiletVisualsSystem.cs deleted file mode 100644 index 5367772ab0..0000000000 --- a/Content.Client/Toilet/ToiletVisualsSystem.cs +++ /dev/null @@ -1,25 +0,0 @@ -using Content.Shared.Toilet; -using Robust.Client.GameObjects; - -namespace Content.Client.Toilet; - -public sealed class ToiletVisualsSystem : VisualizerSystem -{ - protected override void OnAppearanceChange(EntityUid uid, ToiletComponent component, ref AppearanceChangeEvent args) - { - if (args.Sprite == null) return; - - AppearanceSystem.TryGetData(uid, ToiletVisuals.LidOpen, out var lidOpen, args.Component); - AppearanceSystem.TryGetData(uid, ToiletVisuals.SeatUp, out var seatUp, args.Component); - - var state = (lidOpen, seatUp) switch - { - (false, false) => "closed_toilet_seat_down", - (false, true) => "closed_toilet_seat_up", - (true, false) => "open_toilet_seat_down", - (true, true) => "open_toilet_seat_up" - }; - - args.Sprite.LayerSetState(0, state); - } -} diff --git a/Content.Server/Construction/Conditions/ToiletLidClosed.cs b/Content.Server/Construction/Conditions/ToiletLidClosed.cs deleted file mode 100644 index 40a3be006b..0000000000 --- a/Content.Server/Construction/Conditions/ToiletLidClosed.cs +++ /dev/null @@ -1,39 +0,0 @@ -using Content.Shared.Construction; -using Content.Shared.Examine; -using Content.Shared.Toilet; -using JetBrains.Annotations; - -namespace Content.Server.Construction.Conditions -{ - [UsedImplicitly] - [DataDefinition] - public sealed partial class ToiletLidClosed : IGraphCondition - { - public bool Condition(EntityUid uid, IEntityManager entityManager) - { - if (!entityManager.TryGetComponent(uid, out ToiletComponent? toilet)) - return false; - - return !toilet.LidOpen; - } - - public bool DoExamine(ExaminedEvent args) - { - var entity = args.Examined; - - if (!IoCManager.Resolve().TryGetComponent(entity, out ToiletComponent? toilet)) return false; - if (!toilet.LidOpen) return false; - - args.PushMarkup(Loc.GetString("construction-examine-condition-toilet-lid-closed") + "\n"); - return true; - } - - public IEnumerable GenerateGuideEntry() - { - yield return new ConstructionGuideEntry() - { - Localization = "construction-step-condition-toilet-lid-closed" - }; - } - } -} diff --git a/Content.Server/Disposal/Unit/EntitySystems/DisposalUnitSystem.cs b/Content.Server/Disposal/Unit/EntitySystems/DisposalUnitSystem.cs index a03ba5d231..8a7ea438be 100644 --- a/Content.Server/Disposal/Unit/EntitySystems/DisposalUnitSystem.cs +++ b/Content.Server/Disposal/Unit/EntitySystems/DisposalUnitSystem.cs @@ -135,8 +135,7 @@ public sealed class DisposalUnitSystem : SharedDisposalUnitSystem { // This is not an interaction, activation, or alternative verb type because unfortunately most users are // unwilling to accept that this is where they belong and don't want to accidentally climb inside. - if (!component.MobsCanEnter || - !args.CanAccess || + if (!args.CanAccess || !args.CanInteract || component.Container.ContainedEntities.Contains(args.User) || !_actionBlockerSystem.CanMove(args.User)) @@ -630,10 +629,10 @@ public sealed class DisposalUnitSystem : SharedDisposalUnitSystem switch (state) { case DisposalsPressureState.Flushed: - _appearance.SetData(uid, SharedDisposalUnitComponent.Visuals.VisualState, SharedDisposalUnitComponent.VisualState.Flushing, appearance); + _appearance.SetData(uid, SharedDisposalUnitComponent.Visuals.VisualState, SharedDisposalUnitComponent.VisualState.OverlayFlushing, appearance); break; case DisposalsPressureState.Pressurizing: - _appearance.SetData(uid, SharedDisposalUnitComponent.Visuals.VisualState, SharedDisposalUnitComponent.VisualState.Charging, appearance); + _appearance.SetData(uid, SharedDisposalUnitComponent.Visuals.VisualState, SharedDisposalUnitComponent.VisualState.OverlayCharging, appearance); break; case DisposalsPressureState.Ready: _appearance.SetData(uid, SharedDisposalUnitComponent.Visuals.VisualState, SharedDisposalUnitComponent.VisualState.Anchored, appearance); diff --git a/Content.Server/Resist/EscapeInventorySystem.cs b/Content.Server/Resist/EscapeInventorySystem.cs index 6bce38fbac..35c7d0bc65 100644 --- a/Content.Server/Resist/EscapeInventorySystem.cs +++ b/Content.Server/Resist/EscapeInventorySystem.cs @@ -1,5 +1,5 @@ using Content.Server.Popups; -using Content.Server.Storage.Components; +using Content.Shared.Storage.Components; using Content.Shared.ActionBlocker; using Content.Shared.DoAfter; using Content.Shared.Hands.EntitySystems; diff --git a/Content.Server/Storage/Components/SecretStashComponent.cs b/Content.Server/Storage/Components/SecretStashComponent.cs deleted file mode 100644 index a63cb074ad..0000000000 --- a/Content.Server/Storage/Components/SecretStashComponent.cs +++ /dev/null @@ -1,39 +0,0 @@ -using Content.Server.Storage.EntitySystems; -using Content.Shared.Containers.ItemSlots; -using Content.Shared.Item; -using Content.Shared.Toilet; -using Robust.Shared.Containers; -using Robust.Shared.Prototypes; - -namespace Content.Server.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. - /// - [RegisterComponent] - [Access(typeof(SecretStashSystem))] - public sealed partial class SecretStashComponent : Component - { - /// - /// Max item size that can be fitted into secret stash. - /// - [DataField("maxItemSize")] - public ProtoId MaxItemSize = "Small"; - - /// - /// IC secret stash name. For example "the toilet cistern". - /// If empty string, will replace it with entity name in init. - /// - [DataField("secretPartName", readOnly: true)] - public string SecretPartName { get; set; } = ""; - - /// - /// Container used to keep secret stash item. - /// - [ViewVariables] - public ContainerSlot ItemContainer = default!; - - } -} diff --git a/Content.Server/Toilet/ToiletSystem.cs b/Content.Server/Toilet/ToiletSystem.cs index 8bf8457e07..e184ddf0d5 100644 --- a/Content.Server/Toilet/ToiletSystem.cs +++ b/Content.Server/Toilet/ToiletSystem.cs @@ -1,197 +1,8 @@ -using Content.Server.Body.Systems; -using Content.Shared.Buckle; -using Content.Server.Popups; -using Content.Server.Storage.Components; -using Content.Server.Storage.EntitySystems; -using Content.Shared.Audio; -using Content.Shared.Body.Components; -using Content.Shared.Body.Part; -using Content.Shared.Buckle.Components; -using Content.Shared.Examine; -using Content.Shared.IdentityManagement; -using Content.Shared.Interaction; -using Content.Shared.Interaction.Events; -using Content.Shared.Popups; -using Content.Shared.Toilet; -using Content.Shared.Tools; -using Content.Shared.Tools.Components; -using Content.Shared.Verbs; -using Robust.Shared.Audio; -using Robust.Shared.Audio.Systems; -using Robust.Shared.Player; -using Robust.Shared.Random; -using SharedToolSystem = Content.Shared.Tools.Systems.SharedToolSystem; +using Content.Shared.Toilet.Systems; -namespace Content.Server.Toilet +namespace Content.Server.Toilet; + +public sealed class ToiletSystem : SharedToiletSystem { - public sealed class ToiletSystem : EntitySystem - { - [Dependency] private readonly IRobustRandom _random = default!; - [Dependency] private readonly SharedAppearanceSystem _appearance = default!; - [Dependency] private readonly BodySystem _body = default!; - [Dependency] private readonly SharedAudioSystem _audio = default!; - [Dependency] private readonly SecretStashSystem _secretStash = default!; - [Dependency] private readonly PopupSystem _popup = default!; - [Dependency] private readonly SharedToolSystem _tool = default!; - public override void Initialize() - { - base.Initialize(); - SubscribeLocalEvent(OnInit); - SubscribeLocalEvent(OnMapInit); - SubscribeLocalEvent(OnInteractUsing); - SubscribeLocalEvent(OnInteractHand); - SubscribeLocalEvent(OnExamine); - SubscribeLocalEvent(OnSuicide); - SubscribeLocalEvent(OnToiletPried); - SubscribeLocalEvent>(OnToggleSeatVerb); - } - - private void OnSuicide(EntityUid uid, ToiletComponent component, SuicideEvent args) - { - if (args.Handled) - return; - - // Check that victim has a head - // FIXME: since suiciding turns you into a ghost immediately, both messages are seen, not sure how this can be fixed - if (TryComp(args.Victim, out var body) && - _body.BodyHasPartType(args.Victim, BodyPartType.Head, body)) - { - var othersMessage = Loc.GetString("toilet-component-suicide-head-message-others", - ("victim", Identity.Entity(args.Victim, EntityManager)), ("owner", uid)); - _popup.PopupEntity(othersMessage, uid, Filter.PvsExcept(args.Victim), true, PopupType.MediumCaution); - - var selfMessage = Loc.GetString("toilet-component-suicide-head-message", - ("owner", uid)); - _popup.PopupEntity(selfMessage, uid, args.Victim, PopupType.LargeCaution); - - args.SetHandled(SuicideKind.Asphyxiation); - } - else - { - var othersMessage = Loc.GetString("toilet-component-suicide-message-others", - ("victim", Identity.Entity(args.Victim, EntityManager)), ("owner", uid)); - _popup.PopupEntity(othersMessage, uid, Filter.PvsExcept(uid), true, PopupType.MediumCaution); - - var selfMessage = Loc.GetString("toilet-component-suicide-message", - ("owner", uid)); - _popup.PopupEntity(selfMessage, uid, args.Victim, PopupType.LargeCaution); - - args.SetHandled(SuicideKind.Blunt); - } - } - - private void OnInit(EntityUid uid, ToiletComponent component, ComponentInit args) - { - EnsureComp(uid); - } - - private void OnMapInit(EntityUid uid, ToiletComponent component, MapInitEvent args) - { - // roll is toilet seat will be up or down - component.IsSeatUp = _random.Prob(0.5f); - UpdateSprite(uid, component); - } - - private void OnInteractUsing(EntityUid uid, ToiletComponent component, InteractUsingEvent args) - { - if (args.Handled) - return; - - // are player trying place or lift of cistern lid? - if (_tool.UseTool(args.Used, args.User, uid, component.PryLidTime, component.PryingQuality, new ToiletPryDoAfterEvent())) - { - args.Handled = true; - } - // maybe player trying to hide something inside cistern? - else if (component.LidOpen) - { - args.Handled = true; - _secretStash.TryHideItem(uid, args.User, args.Used); - } - } - - private void OnInteractHand(EntityUid uid, ToiletComponent component, InteractHandEvent args) - { - if (args.Handled) - return; - - // trying get something from stash? - if (component.LidOpen) - { - var gotItem = _secretStash.TryGetItem(uid, args.User); - if (gotItem) - { - args.Handled = true; - return; - } - } - - args.Handled = true; - } - - private void OnToggleSeatVerb(EntityUid uid, ToiletComponent component, GetVerbsEvent args) - { - if (!args.CanInteract || !args.CanAccess || !CanToggle(uid)) - return; - - var alterToiletSeatText = component.IsSeatUp ? Loc.GetString("toilet-seat-close") : Loc.GetString("toilet-seat-open"); - - var verb = new AlternativeVerb() - { - Act = () => { - if (CanToggle(uid)) - ToggleToiletSeat(uid, component); - }, - Text = alterToiletSeatText - }; - - args.Verbs.Add(verb); - } - - private void OnExamine(EntityUid uid, ToiletComponent component, ExaminedEvent args) - { - if (args.IsInDetailsRange && component.LidOpen) - { - if (_secretStash.HasItemInside(uid)) - { - var msg = Loc.GetString("toilet-component-on-examine-found-hidden-item"); - args.PushMarkup(msg); - } - } - } - - private void OnToiletPried(EntityUid uid, ToiletComponent toilet, ToiletPryDoAfterEvent args) - { - if (args.Cancelled) - return; - - toilet.LidOpen = !toilet.LidOpen; - UpdateSprite(uid, toilet); - } - - public bool CanToggle(EntityUid uid) - { - return TryComp(uid, out var strap) && strap.BuckledEntities.Count == 0; - } - - public void ToggleToiletSeat(EntityUid uid, ToiletComponent? component = null) - { - if (!Resolve(uid, ref component)) - return; - - component.IsSeatUp = !component.IsSeatUp; - _audio.PlayPvs(component.ToggleSound, uid, AudioParams.Default.WithVariation(SharedContentAudioSystem.DefaultVariation)); - UpdateSprite(uid, component); - } - - private void UpdateSprite(EntityUid uid, ToiletComponent component) - { - if (!TryComp(uid, out var appearance)) - return; - - _appearance.SetData(uid, ToiletVisuals.LidOpen, component.LidOpen, appearance); - _appearance.SetData(uid, ToiletVisuals.SeatUp, component.IsSeatUp, appearance); - } - } } diff --git a/Content.Shared/Buckle/Components/StrapComponent.cs b/Content.Shared/Buckle/Components/StrapComponent.cs index f25e1b0374..72c92ebf84 100644 --- a/Content.Shared/Buckle/Components/StrapComponent.cs +++ b/Content.Shared/Buckle/Components/StrapComponent.cs @@ -22,9 +22,14 @@ public sealed partial class StrapComponent : Component /// Entities that this strap accepts and can buckle /// If null it accepts any entity /// - [DataField] - [ViewVariables] - public EntityWhitelist? AllowedEntities; + [DataField, ViewVariables(VVAccess.ReadWrite)] + public EntityWhitelist? Whitelist; + + /// + /// Entities that this strap does not accept and cannot buckle. + /// + [DataField, ViewVariables(VVAccess.ReadWrite)] + public EntityWhitelist? Blacklist; /// /// The change in position to the strapped mob diff --git a/Content.Shared/Buckle/SharedBuckleSystem.Buckle.cs b/Content.Shared/Buckle/SharedBuckleSystem.Buckle.cs index 3d1fbf2b69..0d67473ffe 100644 --- a/Content.Shared/Buckle/SharedBuckleSystem.Buckle.cs +++ b/Content.Shared/Buckle/SharedBuckleSystem.Buckle.cs @@ -221,8 +221,8 @@ public abstract partial class SharedBuckleSystem } // Does it pass the Whitelist - if (strapComp.AllowedEntities != null && - !strapComp.AllowedEntities.IsValid(userUid, EntityManager)) + if (strapComp.Whitelist != null && + !strapComp.Whitelist.IsValid(buckleUid, EntityManager) || strapComp.Blacklist?.IsValid(buckleUid, EntityManager) == true) { if (_netManager.IsServer) _popup.PopupEntity(Loc.GetString("buckle-component-cannot-fit-message"), userUid, buckleUid, PopupType.Medium); diff --git a/Content.Shared/Disposal/Components/SharedDisposalUnitComponent.cs b/Content.Shared/Disposal/Components/SharedDisposalUnitComponent.cs index 72586be1ec..4948cb6640 100644 --- a/Content.Shared/Disposal/Components/SharedDisposalUnitComponent.cs +++ b/Content.Shared/Disposal/Components/SharedDisposalUnitComponent.cs @@ -1,4 +1,5 @@ using Robust.Shared.Audio; +using Content.Shared.Whitelist; using Robust.Shared.Containers; using Robust.Shared.GameStates; using Robust.Shared.Serialization; @@ -17,6 +18,18 @@ public abstract partial class SharedDisposalUnitComponent : Component [ViewVariables(VVAccess.ReadWrite), DataField("soundFlush")] public SoundSpecifier? FlushSound = new SoundPathSpecifier("/Audio/Machines/disposalflush.ogg"); + /// + /// Blacklists (prevents) entities listed from being placed inside. + /// + [DataField] + public EntityWhitelist? Blacklist; + + /// + /// Whitelists (allows) entities listed from being placed inside. + /// + [DataField] + public EntityWhitelist? Whitelist; + /// /// Sound played when an object is inserted into the disposal unit. /// @@ -33,20 +46,20 @@ public abstract partial class SharedDisposalUnitComponent : Component /// /// State for this disposals unit. /// - [DataField("state")] + [DataField] public DisposalsPressureState State; // TODO: Just make this use vaulting. /// /// We'll track whatever just left disposals so we know what collision we need to ignore until they stop intersecting our BB. /// - [ViewVariables, DataField("recentlyEjected")] + [ViewVariables, DataField] public List RecentlyEjected = new(); /// /// Next time the disposal unit will be pressurized. /// - [DataField("nextPressurized", customTypeSerializer:typeof(TimeOffsetSerializer))] + [DataField(customTypeSerializer:typeof(TimeOffsetSerializer))] public TimeSpan NextPressurized = TimeSpan.Zero; /// @@ -58,63 +71,60 @@ public abstract partial class SharedDisposalUnitComponent : Component /// /// How long it takes from the start of a flush animation to return the sprite to normal. /// - [DataField("flushDelay")] + [DataField] public TimeSpan FlushDelay = TimeSpan.FromSeconds(3); - [DataField("mobsCanEnter")] - public bool MobsCanEnter = true; - /// /// Removes the pressure requirement for flushing. /// - [DataField("disablePressure"), ViewVariables(VVAccess.ReadWrite)] + [DataField, ViewVariables(VVAccess.ReadWrite)] public bool DisablePressure; /// - /// Last time that an entity tried to exit this disposal unit. + /// Last time that an entity tried to exit this disposal unit. /// [ViewVariables] public TimeSpan LastExitAttempt; - [DataField("autoEngageEnabled")] + [DataField] public bool AutomaticEngage = true; [ViewVariables(VVAccess.ReadWrite)] - [DataField("autoEngageTime")] + [DataField] public TimeSpan AutomaticEngageTime = TimeSpan.FromSeconds(30); /// - /// Delay from trying to enter disposals ourselves. + /// Delay from trying to enter disposals ourselves. /// [ViewVariables(VVAccess.ReadWrite)] - [DataField("entryDelay")] + [DataField] public float EntryDelay = 0.5f; /// - /// Delay from trying to shove someone else into disposals. + /// Delay from trying to shove someone else into disposals. /// [ViewVariables(VVAccess.ReadWrite)] public float DraggedEntryDelay = 2.0f; /// - /// Container of entities inside this disposal unit. + /// Container of entities inside this disposal unit. /// [ViewVariables] public Container Container = default!; // TODO: Network power shit instead fam. - [ViewVariables, DataField("powered")] + [ViewVariables, DataField] public bool Powered; /// /// Was the disposals unit engaged for a manual flush. /// - [ViewVariables(VVAccess.ReadWrite), DataField("engaged")] + [ViewVariables(VVAccess.ReadWrite), DataField] public bool Engaged; /// /// Next time this unit will flush. Is the lesser of and /// - [ViewVariables, DataField("nextFlush", customTypeSerializer:typeof(TimeOffsetSerializer))] + [ViewVariables, DataField(customTypeSerializer:typeof(TimeOffsetSerializer))] public TimeSpan? NextFlush; [Serializable, NetSerializable] @@ -130,8 +140,8 @@ public abstract partial class SharedDisposalUnitComponent : Component { UnAnchored, Anchored, - Flushing, - Charging + OverlayFlushing, + OverlayCharging } [Serializable, NetSerializable] diff --git a/Content.Shared/Disposal/SharedDisposalUnitSystem.cs b/Content.Shared/Disposal/SharedDisposalUnitSystem.cs index 9afd683cbd..c39139f9a5 100644 --- a/Content.Shared/Disposal/SharedDisposalUnitSystem.cs +++ b/Content.Shared/Disposal/SharedDisposalUnitSystem.cs @@ -1,12 +1,10 @@ -using System.Diagnostics.CodeAnalysis; +using System.Diagnostics.CodeAnalysis; using Content.Shared.Body.Components; using Content.Shared.Disposal.Components; using Content.Shared.DoAfter; using Content.Shared.DragDrop; using Content.Shared.Emag.Systems; using Content.Shared.Item; -using Content.Shared.Mobs.Components; -using Content.Shared.Mobs.Systems; using Content.Shared.Throwing; using Robust.Shared.Audio; using Robust.Shared.Physics.Components; @@ -26,7 +24,6 @@ public abstract class SharedDisposalUnitSystem : EntitySystem { [Dependency] protected readonly IGameTiming GameTiming = default!; [Dependency] protected readonly MetaDataSystem Metadata = default!; - [Dependency] private readonly MobStateSystem _mobState = default!; [Dependency] protected readonly SharedJointSystem Joints = default!; protected static TimeSpan ExitAttemptDelay = TimeSpan.FromSeconds(0.5); @@ -112,19 +109,21 @@ public abstract class SharedDisposalUnitSystem : EntitySystem if (!Transform(uid).Anchored) return false; - // TODO: Probably just need a disposable tag. var storable = HasComp(entity); if (!storable && !HasComp(entity)) return false; - //Check if the entity is a mob and if mobs can be inserted - if (TryComp(entity, out var damageState) && !component.MobsCanEnter) + if (component.Blacklist?.IsValid(entity, EntityManager) == true) return false; - if (TryComp(entity, out var physics) && (physics.CanCollide || storable)) - return true; + if (component.Whitelist != null && component.Whitelist?.IsValid(entity, EntityManager) != true) + return false; + + if (TryComp(entity, out var physics) && (physics.CanCollide) || storable) + return true; + else + return false; - return damageState != null && (!component.MobsCanEnter || _mobState.IsDead(entity, damageState)); } public abstract void DoInsertDisposalUnit(EntityUid uid, EntityUid toInsert, EntityUid user, SharedDisposalUnitComponent? disposal = null); diff --git a/Content.Server/Plants/Components/PottedPlantHideComponent.cs b/Content.Shared/Plants/PottedPlantHideComponent.cs similarity index 80% rename from Content.Server/Plants/Components/PottedPlantHideComponent.cs rename to Content.Shared/Plants/PottedPlantHideComponent.cs index bc35bbe44f..2e02272494 100644 --- a/Content.Server/Plants/Components/PottedPlantHideComponent.cs +++ b/Content.Shared/Plants/PottedPlantHideComponent.cs @@ -1,8 +1,7 @@ -using Content.Server.Plants.Systems; -using Content.Server.Storage.Components; +using Content.Shared.Storage.Components; using Robust.Shared.Audio; -namespace Content.Server.Plants.Components +namespace Content.Shared.Plants { /// /// Interaction wrapper for . diff --git a/Content.Server/Plants/Systems/PottedPlantHideSystem.cs b/Content.Shared/Plants/PottedPlantHideSystem.cs similarity index 86% rename from Content.Server/Plants/Systems/PottedPlantHideSystem.cs rename to Content.Shared/Plants/PottedPlantHideSystem.cs index 09571c60a1..fd256fd926 100644 --- a/Content.Server/Plants/Systems/PottedPlantHideSystem.cs +++ b/Content.Shared/Plants/PottedPlantHideSystem.cs @@ -1,18 +1,15 @@ -using Content.Server.Plants.Components; -using Content.Server.Popups; -using Content.Server.Storage.Components; -using Content.Server.Storage.EntitySystems; -using Content.Shared.Audio; +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; -using Robust.Shared.Player; -namespace Content.Server.Plants.Systems +namespace Content.Shared.Plants { public sealed class PottedPlantHideSystem : EntitySystem { - [Dependency] private readonly PopupSystem _popupSystem = default!; + [Dependency] private readonly SharedPopupSystem _popupSystem = default!; [Dependency] private readonly SecretStashSystem _stashSystem = default!; [Dependency] private readonly SharedAudioSystem _audio = default!; diff --git a/Content.Shared/Plunger/Components/PlungerComponent.cs b/Content.Shared/Plunger/Components/PlungerComponent.cs new file mode 100644 index 0000000000..101121fe4c --- /dev/null +++ b/Content.Shared/Plunger/Components/PlungerComponent.cs @@ -0,0 +1,18 @@ +using Robust.Shared.GameStates; + +namespace Content.Shared.Plunger.Components +{ + /// + /// Allows entity to unblock target entity with PlungerUseComponent. + /// + [RegisterComponent, NetworkedComponent,AutoGenerateComponentState] + public sealed partial class PlungerComponent : Component + { + /// + /// Duration of plunger doafter event. + /// + [DataField] + [AutoNetworkedField] + public float PlungeDuration = 2f; + } +} diff --git a/Content.Shared/Plunger/Components/PlungerUseComponent.cs b/Content.Shared/Plunger/Components/PlungerUseComponent.cs new file mode 100644 index 0000000000..e886a4ef7a --- /dev/null +++ b/Content.Shared/Plunger/Components/PlungerUseComponent.cs @@ -0,0 +1,42 @@ +using Robust.Shared.Audio; +using Robust.Shared.GameStates; +using Content.Shared.Random; +using Robust.Shared.Prototypes; + +namespace Content.Shared.Plunger.Components +{ + /// + /// Entity can interact with plungers. + /// + [RegisterComponent, NetworkedComponent, AutoGenerateComponentState] + public sealed partial class PlungerUseComponent : Component + { + /// + /// If true entity has been plungered. + /// + [DataField] + [AutoNetworkedField] + public bool Plunged; + + /// + /// If true entity can interact with plunger. + /// + [DataField] + [AutoNetworkedField] + public bool NeedsPlunger = false; + + /// + /// A weighted random entity prototype containing the different loot that rummaging can provide. + /// + [DataField] + [AutoNetworkedField] + public ProtoId PlungerLoot = "PlungerLoot"; + + + /// + /// Sound played on rummage completion. + /// + [DataField] + public SoundSpecifier Sound = new SoundPathSpecifier("/Audio/Effects/Fluids/glug.ogg"); + } +} diff --git a/Content.Shared/Plunger/PlungerDoAfterEvent.cs b/Content.Shared/Plunger/PlungerDoAfterEvent.cs new file mode 100644 index 0000000000..b803f5136f --- /dev/null +++ b/Content.Shared/Plunger/PlungerDoAfterEvent.cs @@ -0,0 +1,10 @@ + +using Content.Shared.DoAfter; +using Robust.Shared.Serialization; + +namespace Content.Shared.Plunger; + +[Serializable, NetSerializable] +public sealed partial class PlungerDoAfterEvent : SimpleDoAfterEvent +{ +} diff --git a/Content.Shared/Plunger/Systems/PlungerSystem.cs b/Content.Shared/Plunger/Systems/PlungerSystem.cs new file mode 100644 index 0000000000..57bd77a7d9 --- /dev/null +++ b/Content.Shared/Plunger/Systems/PlungerSystem.cs @@ -0,0 +1,79 @@ +using Content.Shared.DoAfter; +using Content.Shared.Interaction; +using Content.Shared.Popups; +using Content.Shared.Plunger.Components; +using Robust.Shared.Audio.Systems; +using Robust.Shared.Timing; +using Content.Shared.Random.Helpers; +using Robust.Shared.Network; +using Robust.Shared.Prototypes; +using Robust.Shared.Random; +using Content.Shared.Random; + +namespace Content.Shared.Plunger.Systems; + +/// +/// Plungers can be used to unblock entities with PlungerUseComponent. +/// +public sealed class PlungerSystem : EntitySystem +{ + [Dependency] protected readonly IPrototypeManager _proto = default!; + [Dependency] protected readonly IRobustRandom _random = default!; + [Dependency] private readonly SharedAudioSystem _audio = default!; + [Dependency] private readonly SharedDoAfterSystem _doAfter = default!; + [Dependency] private readonly SharedPopupSystem _popup = default!; + + public override void Initialize() + { + SubscribeLocalEvent(OnInteract); + SubscribeLocalEvent(OnDoAfter); + } + + private void OnInteract(EntityUid uid, PlungerComponent component, AfterInteractEvent args) + { + if (args.Handled) + return; + + if (!args.CanReach || args.Target is not { Valid: true } target) + return; + + if (!TryComp(args.Target, out var plunger)) + return; + + if (plunger.NeedsPlunger) + return; + + _doAfter.TryStartDoAfter(new DoAfterArgs(EntityManager, args.User, component.PlungeDuration, new PlungerDoAfterEvent(), uid, target, uid) + { + BreakOnMove = true, + BreakOnDamage = true, + MovementThreshold = 1.0f, + }); + args.Handled = true; + } + + private void OnDoAfter(EntityUid uid, PlungerComponent 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 PlungerUseComponent? plunge)) + return; + + _popup.PopupClient(Loc.GetString("plunger-unblock", ("target", target)), args.User, args.User, PopupType.Medium); + plunge.Plunged = true; + + var spawn = _proto.Index(plunge.PlungerLoot).Pick(_random); + + _audio.PlayPredicted(plunge.Sound, uid, uid); + Spawn(spawn, Transform(target).Coordinates); + RemComp(target); + Dirty(target, plunge); + + args.Handled = true; + } +} + diff --git a/Content.Shared/Storage/Components/SecretStashComponent.cs b/Content.Shared/Storage/Components/SecretStashComponent.cs new file mode 100644 index 0000000000..8595f79ca5 --- /dev/null +++ b/Content.Shared/Storage/Components/SecretStashComponent.cs @@ -0,0 +1,90 @@ +using Content.Shared.Storage.EntitySystems; +using Content.Shared.Containers.ItemSlots; +using Content.Shared.Item; +using Robust.Shared.Containers; +using Robust.Shared.Prototypes; +using Content.Shared.Tools; +using Robust.Shared.GameStates; +using Content.Shared.DoAfter; +using Robust.Shared.Serialization; + +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. + /// + [RegisterComponent, NetworkedComponent, AutoGenerateComponentState] + [Access(typeof(SecretStashSystem))] + public sealed partial class SecretStashComponent : Component + { + /// + /// Max item size that can be fitted into secret stash. + /// + [DataField("maxItemSize")] + public ProtoId MaxItemSize = "Small"; + + /// + /// If stash has way to open then this will switch between open and closed. + /// + [DataField, AutoNetworkedField] + public bool ToggleOpen; + + /// + /// Prying the door. + /// + [DataField] + public float PryDoorTime = 1f; + + [DataField] + public ProtoId PryingQuality = "Prying"; + + /// + /// Is stash openable?. + /// + [DataField, AutoNetworkedField] + public bool OpenableStash = false; + + /// + /// IC secret stash name. For example "the toilet cistern". + /// If empty string, will replace it with entity name in init. + /// + [DataField] + public string SecretPartName { get; set; } = ""; + + [DataField, AutoNetworkedField] + public string ExamineStash = "comp-secret-stash-on-examine-found-hidden-item"; + + /// + /// Container used to keep secret stash item. + /// + [ViewVariables] + public ContainerSlot ItemContainer = default!; + + } + + /// + /// Simple pry event for prying open a stash door. + /// + [Serializable, NetSerializable] + public sealed partial class StashPryDoAfterEvent : SimpleDoAfterEvent + { + } + + /// + /// Visualizers for handling stash open closed state if stash has door. + /// + [Serializable, NetSerializable] + public enum StashVisuals : byte + { + DoorVisualState, + } + + [Serializable, NetSerializable] + public enum DoorVisualState : byte + { + DoorOpen, + DoorClosed + } +} diff --git a/Content.Server/Storage/EntitySystems/SecretStashSystem.cs b/Content.Shared/Storage/EntitySystems/SecretStashSystem.cs similarity index 55% rename from Content.Server/Storage/EntitySystems/SecretStashSystem.cs rename to Content.Shared/Storage/EntitySystems/SecretStashSystem.cs index 49be0a2f88..9aee1b982e 100644 --- a/Content.Server/Storage/EntitySystems/SecretStashSystem.cs +++ b/Content.Shared/Storage/EntitySystems/SecretStashSystem.cs @@ -1,25 +1,37 @@ -using Content.Server.Popups; -using Content.Server.Storage.Components; +using Content.Shared.Popups; +using Content.Shared.Storage.Components; using Content.Shared.Destructible; using Content.Shared.Hands.Components; using Content.Shared.Hands.EntitySystems; using Content.Shared.Item; using Robust.Shared.Containers; +using Content.Shared.Interaction; +using Content.Shared.Tools.Systems; +using Content.Shared.Examine; -namespace Content.Server.Storage.EntitySystems +namespace Content.Shared.Storage.EntitySystems { + /// + /// Secret Stash allows an item to be hidden within. + /// public sealed class SecretStashSystem : EntitySystem { - [Dependency] private readonly PopupSystem _popupSystem = default!; + [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(OnSecretStashPried); + SubscribeLocalEvent(OnInteractUsing); + SubscribeLocalEvent(OnInteractHand); + SubscribeLocalEvent(OnExamine); } private void OnInit(EntityUid uid, SecretStashComponent component, ComponentInit args) @@ -42,6 +54,73 @@ namespace Content.Server.Storage.EntitySystems return component.ItemContainer.ContainedEntity != null; } + private void OnInteractUsing(EntityUid uid, SecretStashComponent component, InteractUsingEvent args) + { + if (args.Handled) + return; + + if (!component.OpenableStash) + return; + + // 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; + } + } + + private void OnInteractHand(EntityUid uid, SecretStashComponent component, InteractHandEvent args) + { + if (args.Handled) + return; + + if (!component.OpenableStash) + return; + + // trying to get something from stash? + if (component.ToggleOpen) + { + var gotItem = TryGetItem(uid, args.User); + if (gotItem) + { + args.Handled = true; + return; + } + } + args.Handled = true; + } + + private void OnSecretStashPried(EntityUid uid, SecretStashComponent component, StashPryDoAfterEvent args) + { + if (args.Cancelled) + return; + + ToggleOpen(uid, component); + } + + public void ToggleOpen(EntityUid uid, SecretStashComponent? component = null, MetaDataComponent? meta = null) + { + if (!Resolve(uid, ref component)) + return; + + component.ToggleOpen = !component.ToggleOpen; + + UpdateAppearance(uid, component); + Dirty(uid, component, meta); + } + + private void UpdateAppearance(EntityUid uid, SecretStashComponent? component = null) + { + if (!Resolve(uid, ref component)) + return; + + _appearance.SetData(uid, StashVisuals.DoorVisualState, component.ToggleOpen ? DoorVisualState.DoorOpen : DoorVisualState.DoorClosed); + } + /// /// Tries to hide item inside secret stash from hands of user. /// @@ -62,7 +141,7 @@ namespace Content.Server.Storage.EntitySystems if (container.ContainedEntity != null) { var msg = Loc.GetString("comp-secret-stash-action-hide-container-not-empty"); - _popupSystem.PopupEntity(msg, uid, userUid); + _popupSystem.PopupClient(msg, uid, userUid); return false; } @@ -71,7 +150,7 @@ namespace Content.Server.Storage.EntitySystems { var msg = Loc.GetString("comp-secret-stash-action-hide-item-too-big", ("item", itemToHideUid), ("stash", GetSecretPartName(uid, component))); - _popupSystem.PopupEntity(msg, uid, userUid); + _popupSystem.PopupClient(msg, uid, userUid); return false; } @@ -84,7 +163,7 @@ namespace Content.Server.Storage.EntitySystems // all done, show success message var successMsg = Loc.GetString("comp-secret-stash-action-hide-success", ("item", itemToHideUid), ("this", GetSecretPartName(uid, component))); - _popupSystem.PopupEntity(successMsg, uid, userUid); + _popupSystem.PopupClient(successMsg, uid, userUid); return true; } @@ -113,11 +192,23 @@ namespace Content.Server.Storage.EntitySystems // show success message var successMsg = Loc.GetString("comp-secret-stash-action-get-item-found-something", ("stash", GetSecretPartName(uid, component))); - _popupSystem.PopupEntity(successMsg, uid, userUid); + _popupSystem.PopupClient(successMsg, uid, userUid); return true; } + private void OnExamine(EntityUid uid, SecretStashComponent component, ExaminedEvent args) + { + if (args.IsInDetailsRange && component.ToggleOpen) + { + if (HasItemInside(uid)) + { + var msg = Loc.GetString(component.ExamineStash); + args.PushMarkup(msg); + } + } + } + private string GetSecretPartName(EntityUid uid, SecretStashComponent stash) { if (stash.SecretPartName != "") diff --git a/Content.Shared/Toilet/Components/ToiletComponent.cs b/Content.Shared/Toilet/Components/ToiletComponent.cs new file mode 100644 index 0000000000..5de74e08f6 --- /dev/null +++ b/Content.Shared/Toilet/Components/ToiletComponent.cs @@ -0,0 +1,40 @@ +using Robust.Shared.Audio; +using Robust.Shared.GameStates; +using Robust.Shared.Serialization; + +namespace Content.Shared.Toilet.Components +{ + /// + /// Toilets that can be flushed, seats toggled up and down, items hidden in cistern. + /// + [RegisterComponent, NetworkedComponent, AutoGenerateComponentState] + public sealed partial class ToiletComponent : Component + { + /// + /// Toggles seat state. + /// + [DataField, AutoNetworkedField] + public bool ToggleSeat; + + + /// + /// Sound to play when toggling toilet seat. + /// + [DataField] + public SoundSpecifier SeatSound = new SoundPathSpecifier("/Audio/Effects/toilet_seat_down.ogg"); + } + + [Serializable, NetSerializable] + public enum ToiletVisuals : byte + { + SeatVisualState, + } + + [Serializable, NetSerializable] + public enum SeatVisualState : byte + { + SeatUp, + SeatDown + } +} + diff --git a/Content.Shared/Toilet/Systems/SharedToiletSystem.cs b/Content.Shared/Toilet/Systems/SharedToiletSystem.cs new file mode 100644 index 0000000000..87df69e88d --- /dev/null +++ b/Content.Shared/Toilet/Systems/SharedToiletSystem.cs @@ -0,0 +1,109 @@ +using Content.Shared.Buckle.Components; +using Content.Shared.Interaction; +using Content.Shared.Verbs; +using Content.Shared.Plunger.Components; +using Robust.Shared.Audio.Systems; +using Robust.Shared.Random; +using Robust.Shared.Utility; +using Content.Shared.Toilet.Components; + +namespace Content.Shared.Toilet.Systems +{ + /// + /// Handles sprite changes for both toilet seat up and down as well as for lid open and closed. Handles interactions with hidden stash + /// + + public abstract class SharedToiletSystem : EntitySystem + { + [Dependency] private readonly IRobustRandom _random = default!; + [Dependency] private readonly SharedAudioSystem _audio = default!; + [Dependency] private readonly SharedAppearanceSystem _appearance = default!; + + public override void Initialize() + { + base.Initialize(); + + SubscribeLocalEvent(OnMapInit); + SubscribeLocalEvent>(OnToggleSeatVerb); + SubscribeLocalEvent(OnActivateInWorld); + } + + private void OnMapInit(EntityUid uid, ToiletComponent component, MapInitEvent args) + { + if (_random.Prob(0.5f)) + component.ToggleSeat = true; + + if (_random.Prob(0.3f)) + { + TryComp(uid, out var plunger); + + if (plunger == null) + return; + + plunger.NeedsPlunger = true; + } + + UpdateAppearance(uid); + Dirty(uid, component); + } + + public bool CanToggle(EntityUid uid) + { + return TryComp(uid, out var strap) && strap.BuckledEntities.Count == 0; + } + + private void OnToggleSeatVerb(EntityUid uid, ToiletComponent component, GetVerbsEvent args) + { + if (!args.CanInteract || !args.CanAccess || !CanToggle(uid) || args.Hands == null) + return; + + AlternativeVerb toggleVerb = new() + { + Act = () => ToggleToiletSeat(uid, args.User, component) + }; + + if (component.ToggleSeat) + { + toggleVerb.Text = Loc.GetString("toilet-seat-close"); + toggleVerb.Icon = + new SpriteSpecifier.Texture(new ResPath("/Textures/Interface/VerbIcons/close.svg.192dpi.png")); + } + else + { + toggleVerb.Text = Loc.GetString("toilet-seat-open"); + toggleVerb.Icon = + new SpriteSpecifier.Texture(new ResPath("/Textures/Interface/VerbIcons/open.svg.192dpi.png")); + } + args.Verbs.Add(toggleVerb); + } + + private void OnActivateInWorld(EntityUid uid, ToiletComponent comp, ActivateInWorldEvent args) + { + if (args.Handled) + return; + + args.Handled = true; + ToggleToiletSeat(uid, args.User, comp); + } + + public void ToggleToiletSeat(EntityUid uid, EntityUid? user = null, ToiletComponent? component = null, MetaDataComponent? meta = null) + { + if (!Resolve(uid, ref component)) + return; + + component.ToggleSeat = !component.ToggleSeat; + + _audio.PlayPredicted(component.SeatSound, uid, uid); + UpdateAppearance(uid, component); + Dirty(uid, component, meta); + } + + private void UpdateAppearance(EntityUid uid, ToiletComponent? component = null) + { + if (!Resolve(uid, ref component)) + return; + + _appearance.SetData(uid, ToiletVisuals.SeatVisualState, component.ToggleSeat ? SeatVisualState.SeatUp : SeatVisualState.SeatDown); + } + } +} diff --git a/Content.Shared/Toilet/ToiletComponent.cs b/Content.Shared/Toilet/ToiletComponent.cs deleted file mode 100644 index 161bf81c99..0000000000 --- a/Content.Shared/Toilet/ToiletComponent.cs +++ /dev/null @@ -1,32 +0,0 @@ -using Content.Shared.DoAfter; -using Content.Shared.Tools; -using Robust.Shared.Audio; -using Robust.Shared.Serialization; -using Robust.Shared.Serialization.TypeSerializers.Implementations.Custom.Prototype; - -namespace Content.Shared.Toilet -{ - [RegisterComponent] - public sealed partial class ToiletComponent : Component - { - [DataField("pryLidTime")] - public float PryLidTime = 1f; - - [DataField("pryingQuality", customTypeSerializer:typeof(PrototypeIdSerializer))] - public string PryingQuality = "Prying"; - - [DataField("toggleSound")] - public SoundSpecifier ToggleSound = new SoundPathSpecifier("/Audio/Effects/toilet_seat_down.ogg"); - - [DataField("lidOpen")] - public bool LidOpen = false; - - [DataField("isSeatUp")] - public bool IsSeatUp = false; - } - - [Serializable, NetSerializable] - public sealed partial class ToiletPryDoAfterEvent : SimpleDoAfterEvent - { - } -} diff --git a/Content.Shared/Toilet/ToiletVisuals.cs b/Content.Shared/Toilet/ToiletVisuals.cs deleted file mode 100644 index c5992bc0be..0000000000 --- a/Content.Shared/Toilet/ToiletVisuals.cs +++ /dev/null @@ -1,10 +0,0 @@ -using Robust.Shared.Serialization; - -namespace Content.Shared.Toilet; - -[Serializable, NetSerializable] -public enum ToiletVisuals -{ - LidOpen, - SeatUp -} diff --git a/Resources/Audio/Effects/Fluids/attributions.yml b/Resources/Audio/Effects/Fluids/attributions.yml index 4e28c99252..aebe3e3c3f 100644 --- a/Resources/Audio/Effects/Fluids/attributions.yml +++ b/Resources/Audio/Effects/Fluids/attributions.yml @@ -12,3 +12,13 @@ license: "CC0-1.0" copyright: "Created by brittmosel" source: "https://freesound.org/people/brittmosel/sounds/529300/" + +- files: ["splash.ogg"] + license: "CC0-1.0" + copyright: "Created by deadrobotmusic" + source: "https://freesound.org/people/deadrobotmusic/sounds/609953/" + +- files: ["flush.ogg"] + license: "CC-BY-SA-3.0" + copyright: "Created by the_toilet_guy" + source: "https://freesound.org/people/the_toilet_guy/sounds/98770/" diff --git a/Resources/Audio/Effects/Fluids/flush.ogg b/Resources/Audio/Effects/Fluids/flush.ogg new file mode 100644 index 0000000000..f4ef31c2ab Binary files /dev/null and b/Resources/Audio/Effects/Fluids/flush.ogg differ diff --git a/Resources/Audio/Effects/Fluids/splash.ogg b/Resources/Audio/Effects/Fluids/splash.ogg new file mode 100644 index 0000000000..4233eff6db Binary files /dev/null and b/Resources/Audio/Effects/Fluids/splash.ogg differ diff --git a/Resources/Audio/Weapons/glug.ogg b/Resources/Audio/Weapons/glug.ogg new file mode 100644 index 0000000000..424bedd351 Binary files /dev/null and b/Resources/Audio/Weapons/glug.ogg differ 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 d41933b374..d0dfed2b5d 100644 --- a/Resources/Locale/en-US/storage/components/secret-stash-component.ftl +++ b/Resources/Locale/en-US/storage/components/secret-stash-component.ftl @@ -5,6 +5,7 @@ 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 diff --git a/Resources/Locale/en-US/toilet/toilet-component.ftl b/Resources/Locale/en-US/toilet/toilet-component.ftl index 7807759e42..1129f61b4d 100644 --- a/Resources/Locale/en-US/toilet/toilet-component.ftl +++ b/Resources/Locale/en-US/toilet/toilet-component.ftl @@ -7,3 +7,5 @@ toilet-component-suicide-message-others = {CAPITALIZE(THE($victim))} bashes them toilet-component-suicide-message = You bash yourself with {THE($owner)}! toilet-seat-close = Close Seat toilet-seat-open = Open Seat + +plunger-unblock = You unblock the {THE($target)}! diff --git a/Resources/Prototypes/Entities/Objects/Specific/Janitorial/janitor.yml b/Resources/Prototypes/Entities/Objects/Specific/Janitorial/janitor.yml index db08481dc5..cad2d9a924 100644 --- a/Resources/Prototypes/Entities/Objects/Specific/Janitorial/janitor.yml +++ b/Resources/Prototypes/Entities/Objects/Specific/Janitorial/janitor.yml @@ -622,6 +622,15 @@ damage: types: Blunt: 3 + - type: Plunger + +- type: weightedRandomEntity + id: PlungerLoot + weights: + RandomSpawner100: 56 + SpacemenFigureSpawner: 28 + SpawnMobCockroach: 5 + MaintenanceToolSpawner: 5 - type: entity parent: BaseItem diff --git a/Resources/Prototypes/Entities/Structures/Furniture/toilet.yml b/Resources/Prototypes/Entities/Structures/Furniture/toilet.yml index a6f14b383c..f02b126248 100644 --- a/Resources/Prototypes/Entities/Structures/Furniture/toilet.yml +++ b/Resources/Prototypes/Entities/Structures/Furniture/toilet.yml @@ -2,48 +2,100 @@ name: toilet id: ToiletEmpty suffix: Empty - parent: SeatBase + parent: [ DisposalUnitBase, SeatBase ] description: The HT-451, a torque rotation-based, waste disposal unit for small matter. This one seems remarkably clean. components: - - type: MeleeSound - soundGroups: - Brute: - path: - "/Audio/Weapons/slash.ogg" - - type: Anchorable - type: Sprite sprite: Structures/Furniture/toilet.rsi - state: closed_toilet_seat_up + layers: + - state: condisposal + map: [ "enum.DisposalUnitVisualLayers.Unanchored" ] + - state: disposal + map: [ "enum.DisposalUnitVisualLayers.Base" ] + - state: disposal-flush + map: [ "enum.DisposalUnitVisualLayers.OverlayFlush" ] + - state: dispover-charge + map: [ "enum.DisposalUnitVisualLayers.OverlayCharging" ] + - state: dispover-ready + map: [ "enum.DisposalUnitVisualLayers.OverlayReady" ] + - state: dispover-full + map: [ "enum.DisposalUnitVisualLayers.OverlayFull" ] + - state: dispover-handle + map: [ "enum.DisposalUnitVisualLayers.OverlayEngaged" ] + - map: [ "DoorVisualState.DoorOpen" ] + - map: [ "SeatVisualState.SeatUp" ] + - type: Rotatable + - type: Transform + noRot: false + - type: Strap + whitelist: + components: + - HumanoidAppearance + - type: DisposalUnit + autoEngageEnabled: false + noUI: true + blacklist: + components: + - HumanoidAppearance + - Plunger + - SolutionTransfer + whitelist: + components: + - Item + soundFlush: /Audio/Effects/Fluids/flush.ogg + soundInsert: /Audio/Effects/Fluids/splash.ogg - type: Toilet - - type: SecretStash - secretPartName: secret-stash-part-toilet - type: ContainerContainer containers: stash: !type:ContainerSlot {} - - type: SolutionContainerManager - solutions: - drainBuffer: - maxVol: 500 - toilet: - maxVol: 250 - - type: Transform - anchored: true + disposals: !type:Container - type: Physics bodyType: Static - type: Construction graph: Toilet node: toilet + - type: PlungerUse - type: Appearance + - type: SecretStash + secretPartName: secret-stash-part-toilet + examineStash: toilet-component-on-examine-found-hidden-item + openableStash: true - type: Drain autoDrain: false - - type: DumpableSolution - solution: drainBuffer - - type: SolutionContainerVisuals - maxFillLevels: 1 - fillBaseName: fill- - solutionName: drainBuffer - type: StaticPrice price: 25 + - type: UserInterface + interfaces: + - key: enum.DisposalUnitUiKey.Key + type: DisposalUnitBoundUserInterface + - type: RatKingRummageable + - type: SolutionContainerManager + solutions: + drainBuffer: + maxVol: 100 + tank: + maxVol: 500 + - type: SolutionRegeneration + solution: tank + generated: + reagents: + - ReagentId: Water + Quantity: 1 + - type: DrainableSolution + solution: tank + - type: ReagentTank + - type: DumpableSolution + solution: drainBuffer + - type: GenericVisualizer + visuals: + enum.ToiletVisuals.SeatVisualState: + SeatVisualState.SeatUp: + SeatUp: { state: disposal-up } + SeatDown: { state: disposal-down } + enum.StashVisuals.DoorVisualState: + DoorVisualState.DoorOpen: + DoorOpen: { state: disposal-open } + DoorClosed: { state: disposal-closed } - type: entity id: ToiletDirtyWater diff --git a/Resources/Prototypes/Entities/Structures/Piping/Disposal/units.yml b/Resources/Prototypes/Entities/Structures/Piping/Disposal/units.yml index 1193182d09..25047bb575 100644 --- a/Resources/Prototypes/Entities/Structures/Piping/Disposal/units.yml +++ b/Resources/Prototypes/Entities/Structures/Piping/Disposal/units.yml @@ -10,7 +10,6 @@ components: - type: Sprite sprite: Structures/Piping/disposal.rsi - snapCardinals: true layers: - state: condisposal map: [ "enum.DisposalUnitVisualLayers.Unanchored" ] @@ -19,7 +18,7 @@ - state: disposal-charging map: [ "enum.DisposalUnitVisualLayers.BaseCharging" ] - state: disposal-flush - map: [ "enum.DisposalUnitVisualLayers.BaseFlush" ] + map: [ "enum.DisposalUnitVisualLayers.OverlayFlush" ] - state: dispover-charge map: [ "enum.DisposalUnitVisualLayers.OverlayCharging" ] - state: dispover-ready @@ -30,17 +29,6 @@ map: [ "enum.DisposalUnitVisualLayers.OverlayEngaged" ] - type: Physics bodyType: Static - - type: Fixtures - fixtures: - fix1: - shape: - !type:PhysShapeAabb - bounds: "-0.25,-0.4,0.25,0.4" - density: 75 - mask: - - MachineMask - layer: - - MachineLayer - type: Destructible thresholds: - trigger: @@ -82,6 +70,9 @@ parent: DisposalUnitBase name: disposal unit components: + - type: Sprite + sprite: Structures/Piping/disposal.rsi + snapCardinals: true - type: Construction graph: DisposalMachine node: disposal_unit @@ -100,6 +91,7 @@ components: - type: Sprite sprite: Structures/Piping/disposal.rsi + snapCardinals: true layers: - state: conmailing map: [ "enum.DisposalUnitVisualLayers.Unanchored" ] @@ -108,7 +100,7 @@ - state: mailing-charging map: [ "enum.DisposalUnitVisualLayers.BaseCharging" ] - state: mailing-flush - map: [ "enum.DisposalUnitVisualLayers.BaseFlush" ] + map: [ "enum.DisposalUnitVisualLayers.OverlayFlush" ] - state: dispover-charge map: [ "enum.DisposalUnitVisualLayers.OverlayCharging" ] - state: dispover-ready diff --git a/Resources/Prototypes/Recipes/Construction/Graphs/furniture/toilet.yml b/Resources/Prototypes/Recipes/Construction/Graphs/furniture/toilet.yml index 7daf9ca22d..d9a4cd02fb 100644 --- a/Resources/Prototypes/Recipes/Construction/Graphs/furniture/toilet.yml +++ b/Resources/Prototypes/Recipes/Construction/Graphs/furniture/toilet.yml @@ -24,7 +24,6 @@ conditions: - !type:EntityAnchored anchored: false - - !type:ToiletLidClosed {} steps: - tool: Welding doAfter: 2 diff --git a/Resources/Prototypes/Recipes/Construction/furniture.yml b/Resources/Prototypes/Recipes/Construction/furniture.yml index 2597877187..a5cf53107d 100644 --- a/Resources/Prototypes/Recipes/Construction/furniture.yml +++ b/Resources/Prototypes/Recipes/Construction/furniture.yml @@ -606,7 +606,7 @@ description: A human excrement flushing apparatus. icon: sprite: Structures/Furniture/toilet.rsi - state: closed_toilet_seat_up + state: disposal objectType: Structure placementMode: SnapgridCenter canBuildInImpassable: false diff --git a/Resources/Textures/Structures/Furniture/toilet.rsi/closed_toilet_seat_down.png b/Resources/Textures/Structures/Furniture/toilet.rsi/closed_toilet_seat_down.png deleted file mode 100644 index 738c92a6d3..0000000000 Binary files a/Resources/Textures/Structures/Furniture/toilet.rsi/closed_toilet_seat_down.png and /dev/null differ diff --git a/Resources/Textures/Structures/Furniture/toilet.rsi/closed_toilet_seat_up.png b/Resources/Textures/Structures/Furniture/toilet.rsi/closed_toilet_seat_up.png deleted file mode 100644 index 204c5ff89d..0000000000 Binary files a/Resources/Textures/Structures/Furniture/toilet.rsi/closed_toilet_seat_up.png and /dev/null differ diff --git a/Resources/Textures/Structures/Furniture/toilet.rsi/condisposal.png b/Resources/Textures/Structures/Furniture/toilet.rsi/condisposal.png new file mode 100644 index 0000000000..c60cabe80b Binary files /dev/null and b/Resources/Textures/Structures/Furniture/toilet.rsi/condisposal.png differ diff --git a/Resources/Textures/Structures/Furniture/toilet.rsi/disposal-charging.png b/Resources/Textures/Structures/Furniture/toilet.rsi/disposal-charging.png new file mode 100644 index 0000000000..cb9ad3a9c7 Binary files /dev/null and b/Resources/Textures/Structures/Furniture/toilet.rsi/disposal-charging.png differ diff --git a/Resources/Textures/Structures/Furniture/toilet.rsi/disposal-closed.png b/Resources/Textures/Structures/Furniture/toilet.rsi/disposal-closed.png new file mode 100644 index 0000000000..98c4088aba Binary files /dev/null and b/Resources/Textures/Structures/Furniture/toilet.rsi/disposal-closed.png differ diff --git a/Resources/Textures/Structures/Furniture/toilet.rsi/disposal-down.png b/Resources/Textures/Structures/Furniture/toilet.rsi/disposal-down.png new file mode 100644 index 0000000000..97712ddc65 Binary files /dev/null and b/Resources/Textures/Structures/Furniture/toilet.rsi/disposal-down.png differ diff --git a/Resources/Textures/Structures/Furniture/toilet.rsi/disposal-flush.png b/Resources/Textures/Structures/Furniture/toilet.rsi/disposal-flush.png new file mode 100644 index 0000000000..811284448c Binary files /dev/null and b/Resources/Textures/Structures/Furniture/toilet.rsi/disposal-flush.png differ diff --git a/Resources/Textures/Structures/Furniture/toilet.rsi/disposal-open.png b/Resources/Textures/Structures/Furniture/toilet.rsi/disposal-open.png new file mode 100644 index 0000000000..f48f67bf9a Binary files /dev/null and b/Resources/Textures/Structures/Furniture/toilet.rsi/disposal-open.png differ diff --git a/Resources/Textures/Structures/Furniture/toilet.rsi/disposal-up.png b/Resources/Textures/Structures/Furniture/toilet.rsi/disposal-up.png new file mode 100644 index 0000000000..3b37aba472 Binary files /dev/null and b/Resources/Textures/Structures/Furniture/toilet.rsi/disposal-up.png differ diff --git a/Resources/Textures/Structures/Furniture/toilet.rsi/disposal.png b/Resources/Textures/Structures/Furniture/toilet.rsi/disposal.png new file mode 100644 index 0000000000..cb2db2f9d8 Binary files /dev/null and b/Resources/Textures/Structures/Furniture/toilet.rsi/disposal.png differ diff --git a/Resources/Textures/Structures/Furniture/toilet.rsi/dispover-charge.png b/Resources/Textures/Structures/Furniture/toilet.rsi/dispover-charge.png new file mode 100644 index 0000000000..23ecafa9d8 Binary files /dev/null and b/Resources/Textures/Structures/Furniture/toilet.rsi/dispover-charge.png differ diff --git a/Resources/Textures/Structures/Furniture/toilet.rsi/dispover-full.png b/Resources/Textures/Structures/Furniture/toilet.rsi/dispover-full.png new file mode 100644 index 0000000000..ceb04dfb26 Binary files /dev/null and b/Resources/Textures/Structures/Furniture/toilet.rsi/dispover-full.png differ diff --git a/Resources/Textures/Structures/Furniture/toilet.rsi/dispover-handle.png b/Resources/Textures/Structures/Furniture/toilet.rsi/dispover-handle.png new file mode 100644 index 0000000000..4f40192d5b Binary files /dev/null and b/Resources/Textures/Structures/Furniture/toilet.rsi/dispover-handle.png differ diff --git a/Resources/Textures/Structures/Furniture/toilet.rsi/dispover-ready.png b/Resources/Textures/Structures/Furniture/toilet.rsi/dispover-ready.png new file mode 100644 index 0000000000..bac1c76ec9 Binary files /dev/null and b/Resources/Textures/Structures/Furniture/toilet.rsi/dispover-ready.png differ diff --git a/Resources/Textures/Structures/Furniture/toilet.rsi/meta.json b/Resources/Textures/Structures/Furniture/toilet.rsi/meta.json index 0761994277..047ddbb24b 100644 --- a/Resources/Textures/Structures/Furniture/toilet.rsi/meta.json +++ b/Resources/Textures/Structures/Furniture/toilet.rsi/meta.json @@ -4,24 +4,240 @@ "x": 32, "y": 32 }, - "license": "CC-BY-SA-3.0", - "copyright": "Taken from cev-eris at commit https://github.com/discordia-space/CEV-Eris/commit/2cb66bae0e253e13d37f8939e0983bb94fee243e", + "license": "CC-BY-NC-SA-3.0", + "copyright": "Made by brainfood1183 (github) for ss14", "states": [ { - "name": "closed_toilet_seat_down", - "directions": 4 + "name": "condisposal", + "directions": 4, + "delays": [ + [ + 1.0 + ], + [ + 1.0 + ], + [ + 1.0 + ], + [ + 1.0 + ] + ] }, { - "name": "closed_toilet_seat_up", - "directions": 4 + "name": "disposal-open", + "directions": 4, + "delays": [ + [ + 1.0 + ], + [ + 1.0 + ], + [ + 1.0 + ], + [ + 1.0 + ] + ] }, { - "name": "open_toilet_seat_down", - "directions": 4 + "name": "disposal-closed", + "directions": 4, + "delays": [ + [ + 1.0 + ], + [ + 1.0 + ], + [ + 1.0 + ], + [ + 1.0 + ] + ] }, { - "name": "open_toilet_seat_up", - "directions": 4 + "name": "disposal", + "directions": 4, + "delays": [ + [ + 1.0 + ], + [ + 1.0 + ], + [ + 1.0 + ], + [ + 1.0 + ] + ] + }, + { + "name": "disposal-up", + "directions": 4, + "delays": [ + [ + 1.0 + ], + [ + 1.0 + ], + [ + 1.0 + ], + [ + 1.0 + ] + ] + }, + { + "name": "disposal-down", + "directions": 4, + "delays": [ + [ + 1.0 + ], + [ + 1.0 + ], + [ + 1.0 + ], + [ + 1.0 + ] + ] + }, + { + "name": "disposal-charging", + "directions": 4, + "delays": [ + [ + 1.0 + ], + [ + 1.0 + ], + [ + 1.0 + ], + [ + 1.0 + ] + ] + }, + { + "name": "disposal-flush", + "directions": 4, + "delays": [ + [ + 1.0, + 1.0, + 1.0, + 1.0, + 1.0 + ], + [ + 1.0, + 1.0, + 1.0, + 1.0, + 1.0 + ], + [ + 1.0, + 1.0, + 1.0, + 1.0, + 1.0 + ], + [ + 1.0, + 1.0, + 1.0, + 1.0, + 1.0 + ] + ] + }, + { + "name": "dispover-charge", + "directions": 4, + "delays": [ + [ + 1.0 + ], + [ + 1.0 + ], + [ + 1.0 + ], + [ + 1.0 + ] + ] + }, + { + "name": "dispover-full", + "directions": 4, + "delays": [ + [ + 1.0 + ], + [ + 1.0 + ], + [ + 1.0 + ], + [ + 1.0 + ] + ] + }, + { + "name": "dispover-handle", + "directions": 4, + "delays": [ + [ + 1.0 + ], + [ + 1.0 + ], + [ + 1.0 + ], + [ + 1.0 + ] + ] + }, + { + "name": "dispover-ready", + "directions": 4, + "delays": [ + [ + 1.0 + ], + [ + 1.0 + ], + [ + 1.0 + ], + [ + 1.0 + ] + ] } ] -} \ No newline at end of file +} diff --git a/Resources/Textures/Structures/Furniture/toilet.rsi/open_toilet_seat_down.png b/Resources/Textures/Structures/Furniture/toilet.rsi/open_toilet_seat_down.png deleted file mode 100644 index a487afc0b8..0000000000 Binary files a/Resources/Textures/Structures/Furniture/toilet.rsi/open_toilet_seat_down.png and /dev/null differ diff --git a/Resources/Textures/Structures/Furniture/toilet.rsi/open_toilet_seat_up.png b/Resources/Textures/Structures/Furniture/toilet.rsi/open_toilet_seat_up.png deleted file mode 100644 index 77995657cf..0000000000 Binary files a/Resources/Textures/Structures/Furniture/toilet.rsi/open_toilet_seat_up.png and /dev/null differ