Add envelopes (#30298)

* Add envelopes

* oops

* Remove unused loc string

* comments and fixes
This commit is contained in:
themias
2024-07-29 21:49:05 -04:00
committed by GitHub
parent b16de9bb35
commit 85e36266fa
12 changed files with 316 additions and 0 deletions

View File

@@ -0,0 +1,35 @@
using Content.Shared.Paper;
using Robust.Client.GameObjects;
namespace Content.Client.Paper;
public sealed class EnvelopeSystem : VisualizerSystem<EnvelopeComponent>
{
public override void Initialize()
{
base.Initialize();
SubscribeLocalEvent<EnvelopeComponent, AfterAutoHandleStateEvent>(OnAfterAutoHandleState);
}
private void OnAfterAutoHandleState(Entity<EnvelopeComponent> ent, ref AfterAutoHandleStateEvent args)
{
UpdateAppearance(ent);
}
private void UpdateAppearance(Entity<EnvelopeComponent> 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
}
}

View File

@@ -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
{
/// <summary>
/// The current open/sealed/torn state of the envelope
/// </summary>
[ViewVariables, DataField, AutoNetworkedField]
public EnvelopeState State = EnvelopeState.Open;
[DataField, ViewVariables]
public string SlotId = "letter_slot";
/// <summary>
/// Stores the current sealing/tearing doafter of the envelope
/// to prevent doafter spam/prediction issues
/// </summary>
[DataField, ViewVariables]
public DoAfterId? EnvelopeDoAfter;
/// <summary>
/// How long it takes to seal the envelope closed
/// </summary>
[DataField, ViewVariables]
public TimeSpan SealDelay = TimeSpan.FromSeconds(1);
/// <summary>
/// How long it takes to tear open the envelope
/// </summary>
[DataField, ViewVariables]
public TimeSpan TearDelay = TimeSpan.FromSeconds(1);
/// <summary>
/// The sound to play when the envelope is sealed closed
/// </summary>
[DataField, ViewVariables]
public SoundPathSpecifier? SealSound = new SoundPathSpecifier("/Audio/Effects/packetrip.ogg");
/// <summary>
/// The sound to play when the envelope is torn open
/// </summary>
[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
{
}

View File

@@ -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<EnvelopeComponent, ItemSlotInsertAttemptEvent>(OnInsertAttempt);
SubscribeLocalEvent<EnvelopeComponent, ItemSlotEjectAttemptEvent>(OnEjectAttempt);
SubscribeLocalEvent<EnvelopeComponent, GetVerbsEvent<AlternativeVerb>>(OnGetAltVerbs);
SubscribeLocalEvent<EnvelopeComponent, EnvelopeDoAfterEvent>(OnDoAfter);
SubscribeLocalEvent<EnvelopeComponent, ExaminedEvent>(OnExamine);
}
private void OnExamine(Entity<EnvelopeComponent> 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<EnvelopeComponent> ent, ref GetVerbsEvent<AlternativeVerb> 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<EnvelopeComponent> ent, ref ItemSlotInsertAttemptEvent args)
{
args.Cancelled |= ent.Comp.State != EnvelopeComponent.EnvelopeState.Open;
}
private void OnEjectAttempt(Entity<EnvelopeComponent> ent, ref ItemSlotEjectAttemptEvent args)
{
args.Cancelled |= ent.Comp.State == EnvelopeComponent.EnvelopeState.Sealed;
}
private void TryStartDoAfter(Entity<EnvelopeComponent> 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<EnvelopeComponent> 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);
}
}
}

View File

@@ -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:

View File

@@ -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

View File

@@ -128,6 +128,7 @@
- id: BoxFolderRed
- id: BoxFolderYellow
- id: NewtonCradle
- id: BoxEnvelope
- type: entity
id: CrateServiceFaxMachine

View File

@@ -8,6 +8,7 @@
RubberStampApproved: 1
RubberStampDenied: 1
Paper: 10
Envelope: 10
EncryptionKeyCargo: 2
EncryptionKeyEngineering: 2
EncryptionKeyMedical: 2

View File

@@ -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" ]

Binary file not shown.

After

Width:  |  Height:  |  Size: 2.3 KiB

View File

@@ -0,0 +1,2 @@
sample:
filter: true

Binary file not shown.

After

Width:  |  Height:  |  Size: 158 B

View File

@@ -220,6 +220,9 @@
},
{
"name": "vials"
},
{
"name": "envelope"
}
]
}