Files
tbd-station-14/Content.Shared/Delivery/SharedDeliverySystem.cs
ScarKy0 3281f408eb Cargo Mail System (#35429)
* shitcode init

* biocoding, SpawnTableOnUse, Moving shit to shared

* server :(

* fixes

* ok works

* Discard changes to Content.Shared/Interaction/Events/GettingUsedAttemptEvent.cs

* Discard changes to Content.Shared/Forensics/Components/FingerprintMaskComponent.cs

* Discard changes to Content.Shared/Forensics/Components/FingerprintComponent.cs

* Discard changes to Content.Server/Forensics/Systems/ForensicsSystem.cs

* Discard changes to Content.Server/StationRecords/Systems/StationRecordsSystem.cs

* Discard changes to Content.Server/Storage/EntitySystems/SpawnItemsOnUseSystem.cs

* Discard changes to Content.Shared/Interaction/Events/GettingUsedAttemptEvent.cs

* big stuff

* preperation

* temperory spawning thing for testing

* Update CargoDeliveryDataComponent.cs

* kinda proper spawning idk god save me

* cleanup (kinda)

* preparation 2.0

* stuff i think

* entity table work

* renames

* spawn ratio based on players

* comment

* letter tables

* more spam

* package tables

* comment

* biocodedn't

* builds correctly

* cleaning

* Update deliveries_tables.yml

* labels

* package sprites

* mail teleporter

* revert testing value

* fix test

* fix other test

* i love tests

* mail teleporter enabled by default

* random cooldowns

* fixtures

* Discard changes to Content.Shared/FingerprintReader/FingerprintReaderComponent.cs

* Discard changes to Content.Shared/FingerprintReader/FingerprintReaderSystem.cs

* Discard changes to Content.Shared/Interaction/Events/GettingUsedAttemptEvent.cs

* Discard changes to Resources/Locale/en-US/fingerprint-reader/fingerprint-reader.ftl

* clean

* fuck paper scrap

* oops

* fuck SpawnTableOnUse

* mail teleporter board in QM locker + addressed review

* oops

* clean

* sound on delivery spawn

* address review

* partial review address

* partial review addressing

* addressing partial review

* pratarial revivew address

* misprediction hell

* stuff

* more stuff

* unrelated

* TODO

* link

* partial review

* DirtyField

---------

Co-authored-by: Milon <milonpl.git@proton.me>
2025-03-07 14:51:08 +01:00

176 lines
6.0 KiB
C#

using System.Linq;
using Content.Shared.Shuttles.Components;
using Content.Shared.Examine;
using Content.Shared.FingerprintReader;
using Content.Shared.Hands.EntitySystems;
using Content.Shared.Interaction.Events;
using Content.Shared.NameModifier.EntitySystems;
using Content.Shared.Popups;
using Content.Shared.Tag;
using Content.Shared.Verbs;
using Robust.Shared.Audio.Systems;
using Robust.Shared.Containers;
namespace Content.Shared.Delivery;
/// <summary>
/// Shared side of the DeliverySystem.
/// This covers for letters/packages, as well as spawning a reward for the player upon opening.
/// </summary>
public abstract class SharedDeliverySystem : EntitySystem
{
[Dependency] private readonly SharedAppearanceSystem _appearance = default!;
[Dependency] private readonly SharedAudioSystem _audio = default!;
[Dependency] private readonly SharedPopupSystem _popup = default!;
[Dependency] private readonly FingerprintReaderSystem _fingerprintReader = default!;
[Dependency] private readonly TagSystem _tag = default!;
[Dependency] private readonly SharedContainerSystem _container = default!;
[Dependency] private readonly SharedHandsSystem _hands = default!;
[Dependency] private readonly NameModifierSystem _nameModifier = default!;
public override void Initialize()
{
base.Initialize();
SubscribeLocalEvent<DeliveryComponent, ExaminedEvent>(OnExamine);
SubscribeLocalEvent<DeliveryComponent, UseInHandEvent>(OnUseInHand);
SubscribeLocalEvent<DeliveryComponent, GetVerbsEvent<AlternativeVerb>>(OnGetVerbs);
}
private void OnExamine(Entity<DeliveryComponent> ent, ref ExaminedEvent args)
{
var jobTitle = ent.Comp.RecipientJobTitle ?? Loc.GetString("delivery-recipient-no-job");
var recipientName = ent.Comp.RecipientName ?? Loc.GetString("delivery-recipient-no-name");
if (ent.Comp.IsOpened)
{
args.PushText(Loc.GetString("delivery-already-opened-examine"));
}
args.PushText(Loc.GetString("delivery-recipient-examine", ("recipient", recipientName), ("job", jobTitle)));
}
private void OnUseInHand(Entity<DeliveryComponent> ent, ref UseInHandEvent args)
{
args.Handled = true;
if (ent.Comp.IsOpened)
return;
if (ent.Comp.IsLocked)
TryUnlockDelivery(ent, args.User);
else
OpenDelivery(ent, args.User);
}
private void OnGetVerbs(Entity<DeliveryComponent> ent, ref GetVerbsEvent<AlternativeVerb> args)
{
if (!args.CanAccess || !args.CanInteract || args.Hands == null || ent.Comp.IsOpened)
return;
if (_hands.IsHolding(args.User, ent))
return;
var user = args.User;
args.Verbs.Add(new AlternativeVerb()
{
Act = () =>
{
if (ent.Comp.IsLocked)
TryUnlockDelivery(ent, user);
else
OpenDelivery(ent, user, false);
},
Text = ent.Comp.IsLocked ? Loc.GetString("delivery-unlock-verb") : Loc.GetString("delivery-open-verb"),
});
}
private bool TryUnlockDelivery(Entity<DeliveryComponent> ent, EntityUid user, bool rewardMoney = true)
{
// Check fingerprint access if there is a reader on the mail
if (TryComp<FingerprintReaderComponent>(ent, out var reader) && !_fingerprintReader.IsAllowed((ent, reader), user))
return false;
var deliveryName = _nameModifier.GetBaseName(ent.Owner);
_audio.PlayPredicted(ent.Comp.UnlockSound, user, user);
ent.Comp.IsLocked = false;
UpdateAntiTamperVisuals(ent, ent.Comp.IsLocked);
DirtyField(ent, ent.Comp, nameof(DeliveryComponent.IsLocked));
var ev = new DeliveryUnlockedEvent(user);
RaiseLocalEvent(ent, ref ev);
if (rewardMoney)
GrantSpesoReward(ent.AsNullable());
_popup.PopupPredicted(Loc.GetString("delivery-unlocked", ("delivery", deliveryName)), user, user);
return true;
}
private void OpenDelivery(Entity<DeliveryComponent> ent, EntityUid user, bool attemptPickup = true)
{
var deliveryName = _nameModifier.GetBaseName(ent.Owner);
_audio.PlayPredicted(ent.Comp.OpenSound, user, user);
var ev = new DeliveryOpenedEvent(user);
RaiseLocalEvent(ent, ref ev);
if (attemptPickup)
_hands.TryDrop(user, ent);
ent.Comp.IsOpened = true;
_appearance.SetData(ent, DeliveryVisuals.IsTrash, ent.Comp.IsOpened);
_tag.AddTags(ent, "Trash", "Recyclable");
EnsureComp<SpaceGarbageComponent>(ent);
DirtyField(ent.Owner, ent.Comp, nameof(DeliveryComponent.IsOpened));
_popup.PopupPredicted(Loc.GetString("delivery-opened", ("delivery", deliveryName)), user, user);
if (!_container.TryGetContainer(ent, ent.Comp.Container, out var container))
return;
if (attemptPickup)
{
foreach (var entity in container.ContainedEntities.ToArray())
{
_hands.PickupOrDrop(user, entity);
}
}
else
{
_container.EmptyContainer(container, true, Transform(ent.Owner).Coordinates);
}
}
// TODO: generic updateVisuals from component data
private void UpdateAntiTamperVisuals(EntityUid uid, bool isLocked)
{
_appearance.SetData(uid, DeliveryVisuals.IsLocked, isLocked);
// If we're trying to unlock, always remove the priority tape
if (!isLocked)
_appearance.SetData(uid, DeliveryVisuals.IsPriority, false);
}
protected virtual void GrantSpesoReward(Entity<DeliveryComponent?> ent) { }
}
/// <summary>
/// Event raised on the delivery when it is unlocked.
/// </summary>
[ByRefEvent]
public readonly record struct DeliveryUnlockedEvent(EntityUid User);
/// <summary>
/// Event raised on the delivery when it is opened.
/// </summary>
[ByRefEvent]
public readonly record struct DeliveryOpenedEvent(EntityUid User);