Skip to content

Commit

Permalink
Add Envelopes (#553)
Browse files Browse the repository at this point in the history
* Fix some abnormally high-capacity boxes (#28314)

* Add envelopes (#30298)

* Add envelopes

* oops

* Remove unused loc string

* comments and fixes

---------

Co-authored-by: Lamrr <[email protected]>
Co-authored-by: themias <[email protected]>
Co-authored-by: Mnemotechnican <[email protected]>
  • Loading branch information
4 people authored Feb 22, 2025
1 parent 476e23b commit ee3ca30
Show file tree
Hide file tree
Showing 15 changed files with 380 additions and 82 deletions.
35 changes: 35 additions & 0 deletions Content.Client/Paper/EnvelopeSystem.cs
Original file line number Diff line number Diff line change
@@ -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
}
}
63 changes: 63 additions & 0 deletions Content.Shared/Paper/EnvelopeComponent.cs
Original file line number Diff line number Diff line change
@@ -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
{
}
108 changes: 108 additions & 0 deletions Content.Shared/Paper/EnvelopeSystem.cs
Original file line number Diff line number Diff line change
@@ -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);
}
}
}
11 changes: 11 additions & 0 deletions Resources/Locale/en-US/paper/envelope.ftl
Original file line number Diff line number Diff line change
@@ -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:
Loading

0 comments on commit ee3ca30

Please sign in to comment.