From 85e36266fa5d24dd742b050e2a64c0a68cdc66db Mon Sep 17 00:00:00 2001 From: themias <89101928+themias@users.noreply.github.com> Date: Mon, 29 Jul 2024 21:49:05 -0400 Subject: [PATCH] Add envelopes (#30298) * Add envelopes * oops * Remove unused loc string * comments and fixes --- Content.Client/Paper/EnvelopeSystem.cs | 35 ++++++ Content.Shared/Paper/EnvelopeComponent.cs | 63 ++++++++++ Content.Shared/Paper/EnvelopeSystem.cs | 108 ++++++++++++++++++ Resources/Locale/en-US/paper/envelope.ftl | 11 ++ .../Catalog/Fills/Boxes/general.yml | 14 +++ .../Catalog/Fills/Crates/service.yml | 1 + .../VendingMachines/Inventories/cart.yml | 1 + .../Entities/Objects/Misc/paper.yml | 78 +++++++++++++ .../paper_heading_postage_stamp.svg.96dpi.png | Bin 0 -> 2387 bytes ...er_heading_postage_stamp.svg.96dpi.png.yml | 2 + .../Objects/Storage/boxes.rsi/envelope.png | Bin 0 -> 158 bytes .../Objects/Storage/boxes.rsi/meta.json | 3 + 12 files changed, 316 insertions(+) create mode 100644 Content.Client/Paper/EnvelopeSystem.cs create mode 100644 Content.Shared/Paper/EnvelopeComponent.cs create mode 100644 Content.Shared/Paper/EnvelopeSystem.cs create mode 100644 Resources/Locale/en-US/paper/envelope.ftl create mode 100644 Resources/Textures/Interface/Paper/paper_heading_postage_stamp.svg.96dpi.png create mode 100644 Resources/Textures/Interface/Paper/paper_heading_postage_stamp.svg.96dpi.png.yml create mode 100644 Resources/Textures/Objects/Storage/boxes.rsi/envelope.png diff --git a/Content.Client/Paper/EnvelopeSystem.cs b/Content.Client/Paper/EnvelopeSystem.cs new file mode 100644 index 0000000000..f405ed1518 --- /dev/null +++ b/Content.Client/Paper/EnvelopeSystem.cs @@ -0,0 +1,35 @@ +using Content.Shared.Paper; +using Robust.Client.GameObjects; + +namespace Content.Client.Paper; + +public sealed class EnvelopeSystem : VisualizerSystem +{ + public override void Initialize() + { + base.Initialize(); + SubscribeLocalEvent(OnAfterAutoHandleState); + } + + private void OnAfterAutoHandleState(Entity ent, ref AfterAutoHandleStateEvent args) + { + UpdateAppearance(ent); + } + + private void UpdateAppearance(Entity ent, SpriteComponent? sprite = null) + { + if (!Resolve(ent.Owner, ref sprite)) + return; + + sprite.LayerSetVisible(EnvelopeVisualLayers.Open, ent.Comp.State == EnvelopeComponent.EnvelopeState.Open); + sprite.LayerSetVisible(EnvelopeVisualLayers.Sealed, ent.Comp.State == EnvelopeComponent.EnvelopeState.Sealed); + sprite.LayerSetVisible(EnvelopeVisualLayers.Torn, ent.Comp.State == EnvelopeComponent.EnvelopeState.Torn); + } + + public enum EnvelopeVisualLayers : byte + { + Open, + Sealed, + Torn + } +} diff --git a/Content.Shared/Paper/EnvelopeComponent.cs b/Content.Shared/Paper/EnvelopeComponent.cs new file mode 100644 index 0000000000..e56fbd85af --- /dev/null +++ b/Content.Shared/Paper/EnvelopeComponent.cs @@ -0,0 +1,63 @@ +using Content.Shared.DoAfter; +using Robust.Shared.Audio; +using Robust.Shared.GameStates; +using Robust.Shared.Serialization; + +namespace Content.Shared.Paper; + +[RegisterComponent, NetworkedComponent, AutoGenerateComponentState(true)] +public sealed partial class EnvelopeComponent : Component +{ + /// + /// The current open/sealed/torn state of the envelope + /// + [ViewVariables, DataField, AutoNetworkedField] + public EnvelopeState State = EnvelopeState.Open; + + [DataField, ViewVariables] + public string SlotId = "letter_slot"; + + /// + /// Stores the current sealing/tearing doafter of the envelope + /// to prevent doafter spam/prediction issues + /// + [DataField, ViewVariables] + public DoAfterId? EnvelopeDoAfter; + + /// + /// How long it takes to seal the envelope closed + /// + [DataField, ViewVariables] + public TimeSpan SealDelay = TimeSpan.FromSeconds(1); + + /// + /// How long it takes to tear open the envelope + /// + [DataField, ViewVariables] + public TimeSpan TearDelay = TimeSpan.FromSeconds(1); + + /// + /// The sound to play when the envelope is sealed closed + /// + [DataField, ViewVariables] + public SoundPathSpecifier? SealSound = new SoundPathSpecifier("/Audio/Effects/packetrip.ogg"); + + /// + /// The sound to play when the envelope is torn open + /// + [DataField, ViewVariables] + public SoundPathSpecifier? TearSound = new SoundPathSpecifier("/Audio/Effects/poster_broken.ogg"); + + [Serializable, NetSerializable] + public enum EnvelopeState : byte + { + Open, + Sealed, + Torn + } +} + +[Serializable, NetSerializable] +public sealed partial class EnvelopeDoAfterEvent : SimpleDoAfterEvent +{ +} diff --git a/Content.Shared/Paper/EnvelopeSystem.cs b/Content.Shared/Paper/EnvelopeSystem.cs new file mode 100644 index 0000000000..560c2c82f9 --- /dev/null +++ b/Content.Shared/Paper/EnvelopeSystem.cs @@ -0,0 +1,108 @@ +using Content.Shared.DoAfter; +using Content.Shared.Containers.ItemSlots; +using Content.Shared.Verbs; +using Robust.Shared.Audio.Systems; +using Content.Shared.Examine; + +namespace Content.Shared.Paper; + +public sealed class EnvelopeSystem : EntitySystem +{ + [Dependency] private readonly SharedDoAfterSystem _doAfterSystem = default!; + [Dependency] private readonly SharedAudioSystem _audioSystem = default!; + [Dependency] private readonly ItemSlotsSystem _itemSlotsSystem = default!; + + public override void Initialize() + { + base.Initialize(); + + SubscribeLocalEvent(OnInsertAttempt); + SubscribeLocalEvent(OnEjectAttempt); + SubscribeLocalEvent>(OnGetAltVerbs); + SubscribeLocalEvent(OnDoAfter); + SubscribeLocalEvent(OnExamine); + } + + private void OnExamine(Entity ent, ref ExaminedEvent args) + { + if (ent.Comp.State == EnvelopeComponent.EnvelopeState.Sealed) + { + args.PushMarkup(Loc.GetString("envelope-sealed-examine", ("envelope", ent.Owner))); + } + else if (ent.Comp.State == EnvelopeComponent.EnvelopeState.Torn) + { + args.PushMarkup(Loc.GetString("envelope-torn-examine", ("envelope", ent.Owner))); + } + } + + private void OnGetAltVerbs(Entity ent, ref GetVerbsEvent args) + { + if (!args.CanAccess || !args.CanInteract || args.Hands == null) + return; + + if (ent.Comp.State == EnvelopeComponent.EnvelopeState.Torn) + return; + + var user = args.User; + args.Verbs.Add(new AlternativeVerb() + { + Text = Loc.GetString(ent.Comp.State == EnvelopeComponent.EnvelopeState.Open ? "envelope-verb-seal" : "envelope-verb-tear"), + IconEntity = GetNetEntity(ent.Owner), + Act = () => + { + TryStartDoAfter(ent, user, ent.Comp.State == EnvelopeComponent.EnvelopeState.Open ? ent.Comp.SealDelay : ent.Comp.TearDelay); + }, + }); + } + + private void OnInsertAttempt(Entity ent, ref ItemSlotInsertAttemptEvent args) + { + args.Cancelled |= ent.Comp.State != EnvelopeComponent.EnvelopeState.Open; + } + + private void OnEjectAttempt(Entity ent, ref ItemSlotEjectAttemptEvent args) + { + args.Cancelled |= ent.Comp.State == EnvelopeComponent.EnvelopeState.Sealed; + } + + private void TryStartDoAfter(Entity ent, EntityUid user, TimeSpan delay) + { + if (ent.Comp.EnvelopeDoAfter.HasValue) + return; + + var doAfterEventArgs = new DoAfterArgs(EntityManager, user, delay, new EnvelopeDoAfterEvent(), ent.Owner, ent.Owner) + { + BreakOnDamage = true, + NeedHand = true, + BreakOnHandChange = true, + MovementThreshold = 0.01f, + DistanceThreshold = 1.0f, + }; + + if (_doAfterSystem.TryStartDoAfter(doAfterEventArgs, out var doAfterId)) + ent.Comp.EnvelopeDoAfter = doAfterId; + } + private void OnDoAfter(Entity ent, ref EnvelopeDoAfterEvent args) + { + ent.Comp.EnvelopeDoAfter = null; + + if (args.Cancelled) + return; + + if (ent.Comp.State == EnvelopeComponent.EnvelopeState.Open) + { + _audioSystem.PlayPredicted(ent.Comp.SealSound, ent.Owner, args.User); + ent.Comp.State = EnvelopeComponent.EnvelopeState.Sealed; + Dirty(ent.Owner, ent.Comp); + } + else if (ent.Comp.State == EnvelopeComponent.EnvelopeState.Sealed) + { + _audioSystem.PlayPredicted(ent.Comp.TearSound, ent.Owner, args.User); + ent.Comp.State = EnvelopeComponent.EnvelopeState.Torn; + Dirty(ent.Owner, ent.Comp); + + if (_itemSlotsSystem.TryGetSlot(ent.Owner, ent.Comp.SlotId, out var slotComp)) + _itemSlotsSystem.TryEjectToHands(ent.Owner, slotComp, args.User); + } + } +} diff --git a/Resources/Locale/en-US/paper/envelope.ftl b/Resources/Locale/en-US/paper/envelope.ftl new file mode 100644 index 0000000000..bb7993d284 --- /dev/null +++ b/Resources/Locale/en-US/paper/envelope.ftl @@ -0,0 +1,11 @@ +envelope-verb-seal = Seal +envelope-verb-tear = Tear + +envelope-letter-slot = Letter + +envelope-sealed-examine = [color=gray]{CAPITALIZE(THE($envelope))} is sealed.[/color] +envelope-torn-examine = [color=yellow]{CAPITALIZE(THE($envelope))} is torn and unusable![/color] + +envelope-default-message = TO: + + FROM: \ No newline at end of file diff --git a/Resources/Prototypes/Catalog/Fills/Boxes/general.yml b/Resources/Prototypes/Catalog/Fills/Boxes/general.yml index 85ee9f9ab8..cadf413f47 100644 --- a/Resources/Prototypes/Catalog/Fills/Boxes/general.yml +++ b/Resources/Prototypes/Catalog/Fills/Boxes/general.yml @@ -432,3 +432,17 @@ - id: DartYellow amount: 2 +- type: entity + name: envelope box + parent: BoxCardboard + id: BoxEnvelope + description: A box filled with envelopes. + components: + - type: Sprite + layers: + - state: box + - state: envelope + - type: StorageFill + contents: + - id: Envelope + amount: 9 \ No newline at end of file diff --git a/Resources/Prototypes/Catalog/Fills/Crates/service.yml b/Resources/Prototypes/Catalog/Fills/Crates/service.yml index 693ddab14e..d922056a8b 100644 --- a/Resources/Prototypes/Catalog/Fills/Crates/service.yml +++ b/Resources/Prototypes/Catalog/Fills/Crates/service.yml @@ -128,6 +128,7 @@ - id: BoxFolderRed - id: BoxFolderYellow - id: NewtonCradle + - id: BoxEnvelope - type: entity id: CrateServiceFaxMachine diff --git a/Resources/Prototypes/Catalog/VendingMachines/Inventories/cart.yml b/Resources/Prototypes/Catalog/VendingMachines/Inventories/cart.yml index cb0ef3246d..303b209053 100644 --- a/Resources/Prototypes/Catalog/VendingMachines/Inventories/cart.yml +++ b/Resources/Prototypes/Catalog/VendingMachines/Inventories/cart.yml @@ -8,6 +8,7 @@ RubberStampApproved: 1 RubberStampDenied: 1 Paper: 10 + Envelope: 10 EncryptionKeyCargo: 2 EncryptionKeyEngineering: 2 EncryptionKeyMedical: 2 diff --git a/Resources/Prototypes/Entities/Objects/Misc/paper.yml b/Resources/Prototypes/Entities/Objects/Misc/paper.yml index 42b850f2b2..12e1a09a0c 100644 --- a/Resources/Prototypes/Entities/Objects/Misc/paper.yml +++ b/Resources/Prototypes/Entities/Objects/Misc/paper.yml @@ -564,3 +564,81 @@ Blunt: 10 - type: StealTarget stealGroup: BoxFolderQmClipboard + +- type: entity + name: envelope + parent: BaseItem + id: Envelope + description: 'A small envelope for keeping prying eyes off of your sensitive documents.' + components: + - type: Sprite + sprite: Objects/Misc/bureaucracy.rsi + layers: + - state: envelope_open + map: ["enum.EnvelopeVisualLayers.Open"] + - state: envelope_closed + map: ["enum.EnvelopeVisualLayers.Sealed"] + visible: false + - state: envelope_torn + map: ["enum.EnvelopeVisualLayers.Torn"] + visible: false + - state: paper_stamp-generic + map: ["enum.PaperVisualLayers.Stamp"] + visible: false + - type: Paper + escapeFormatting: false + content: envelope-default-message + - type: PaperVisuals + headerImagePath: "/Textures/Interface/Paper/paper_heading_postage_stamp.svg.96dpi.png" + headerMargin: 216.0, 0.0, 0.0, 0.0 + contentMargin: 0.0, 0.0, 0.0, 0.0 + maxWritableArea: 368.0, 256.0 + - type: Envelope + - type: ContainerContainer + containers: + letter_slot: !type:ContainerSlot + - type: ItemSlots + slots: + letter_slot: + name: envelope-letter-slot + insertSound: /Audio/Effects/packetrip.ogg + ejectSound: /Audio/Effects/packetrip.ogg + whitelist: + tags: + - Paper + - type: ActivatableUI + key: enum.PaperUiKey.Key + requireHands: false + - type: UserInterface + interfaces: + enum.PaperUiKey.Key: + type: PaperBoundUserInterface + - type: Item + size: Tiny + - type: Tag + tags: + - Trash + - Document + #- type: Appearance, hide stamp marks until we have some kind of displacement + - type: Flammable + fireSpread: true + canResistFire: false + alwaysCombustible: true + canExtinguish: true + damage: + types: + Heat: 1 + - type: FireVisuals + sprite: Effects/fire.rsi + normalState: fire + - type: Damageable + damageModifierSet: Wood + - type: Destructible + thresholds: + - trigger: + !type:DamageTrigger + damage: 15 + behaviors: + - !type:EmptyAllContainersBehaviour + - !type:DoActsBehavior + acts: [ "Destruction" ] \ No newline at end of file diff --git a/Resources/Textures/Interface/Paper/paper_heading_postage_stamp.svg.96dpi.png b/Resources/Textures/Interface/Paper/paper_heading_postage_stamp.svg.96dpi.png new file mode 100644 index 0000000000000000000000000000000000000000..ec3566f63ea73dc31d7ead72e5d4e6a57e0167b4 GIT binary patch literal 2387 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 FV1g7~g);yE literal 0 HcmV?d00001 diff --git a/Resources/Textures/Interface/Paper/paper_heading_postage_stamp.svg.96dpi.png.yml b/Resources/Textures/Interface/Paper/paper_heading_postage_stamp.svg.96dpi.png.yml new file mode 100644 index 0000000000..5c43e23305 --- /dev/null +++ b/Resources/Textures/Interface/Paper/paper_heading_postage_stamp.svg.96dpi.png.yml @@ -0,0 +1,2 @@ +sample: + filter: true diff --git a/Resources/Textures/Objects/Storage/boxes.rsi/envelope.png b/Resources/Textures/Objects/Storage/boxes.rsi/envelope.png new file mode 100644 index 0000000000000000000000000000000000000000..93d52099fc2feb4c11840f0a4caf5dcbc41e18ca GIT binary patch literal 158 zcmeAS@N?(olHy`uVBq!ia0vp^3LwnE1|*BCs=fdz#^NA%Cx&(BWL^R}5uPrNArY-_ zFFJBH81T4U)QYnzZg_HImzGA?jFgx4yMty)UNU50IB;M0CVSep{de76vYt)THQ4$4 zsm@B1^tEmCnN{y{)n2`IKqrtlU-SjrcCL{B(hMJ-FesFWEL&Fa6yy>HPgg&ebxsLQ E04{1c0ssI2 literal 0 HcmV?d00001 diff --git a/Resources/Textures/Objects/Storage/boxes.rsi/meta.json b/Resources/Textures/Objects/Storage/boxes.rsi/meta.json index 6963a9cdad..cdae38099d 100644 --- a/Resources/Textures/Objects/Storage/boxes.rsi/meta.json +++ b/Resources/Textures/Objects/Storage/boxes.rsi/meta.json @@ -220,6 +220,9 @@ }, { "name": "vials" + }, + { + "name": "envelope" } ] } -- 2.52.0