Skip to content

Commit

Permalink
Mail! (#16)
Browse files Browse the repository at this point in the history
* Add Mail!!111!!

* Update MailSystem.cs

* Make mailbag mail only

* Remove invalid mail types

* Update MailSystem.cs

* Enable wizard cosplay mail
  • Loading branch information
DebugOk authored Oct 1, 2023
1 parent b854689 commit 3d83d66
Show file tree
Hide file tree
Showing 38 changed files with 2,975 additions and 3 deletions.
8 changes: 8 additions & 0 deletions Content.Client/Nyanotrasen/Mail/MailComponent.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,8 @@
using Content.Shared.Mail;

namespace Content.Client.Mail
{
[RegisterComponent]
public sealed partial class MailComponent : SharedMailComponent
{}
}
59 changes: 59 additions & 0 deletions Content.Client/Nyanotrasen/Mail/MailSystem.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,59 @@
using Robust.Client.GameObjects;
using Content.Shared.Mail;
using Content.Shared.StatusIcon;
using Content.Shared.StatusIcon.Components;
using Robust.Client.State;
using Robust.Shared.Prototypes;
using Robust.Shared.Utility;

namespace Content.Client.Mail
{
/// <summary>
/// Display a cool stamp on the parcel based on the job of the recipient.
/// </summary>
/// <remarks>
/// GenericVisualizer is not powerful enough to handle setting a string on
/// visual data then directly relaying that string to a layer's state.
/// I.e. there is nothing like a regex capture group for visual data.
///
/// Hence why this system exists.
///
/// To do this with GenericVisualizer would require a separate condition
/// for every job value, which would be extra mess to maintain.
///
/// It would look something like this, multipled a couple dozen times.
///
/// enum.MailVisuals.JobIcon:
/// enum.MailVisualLayers.JobStamp:
/// StationEngineer:
/// state: StationEngineer
/// SecurityOfficer:
/// state: SecurityOfficer
/// </remarks>
public sealed class MailJobVisualizerSystem : VisualizerSystem<MailComponent>
{
protected override void OnAppearanceChange(EntityUid uid, MailComponent component, ref AppearanceChangeEvent args)
{
if (args.Sprite == null)
return;

if (args.Component.TryGetData(MailVisuals.JobIcon, out string job))
{
job = job.Substring(7); // :clueless:

args.Sprite.LayerSetState(MailVisualLayers.JobStamp, job);
}

}
}

public enum MailVisualLayers : byte
{
Icon,
Lock,
FragileStamp,
JobStamp,
PriorityTape,
Breakage,
}
}
108 changes: 108 additions & 0 deletions Content.Server/Nyanotrasen/Mail/Components/MailComponent.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,108 @@
using System.Threading;
using Robust.Shared.Audio;
using Content.Shared.Storage;
using Content.Shared.Mail;

namespace Content.Server.Mail.Components
{
[RegisterComponent]
public sealed partial class MailComponent : SharedMailComponent
{
[ViewVariables(VVAccess.ReadWrite)]
[DataField("recipient")]
public string Recipient = "None";

[ViewVariables(VVAccess.ReadWrite)]
[DataField("recipientJob")]
public string RecipientJob = "None";

// Why do we not use LockComponent?
// Because this can't be locked again,
// and we have special conditions for unlocking,
// and we don't want to add a verb.
[ViewVariables(VVAccess.ReadWrite)]
[DataField("isLocked")]
public bool IsLocked = true;

/// <summary>
/// Is this parcel profitable to deliver for the station?
/// </summary>
/// <remarks>
/// The station won't receive any award on delivery if this is false.
/// This is useful for broken fragile packages and packages that were
/// not delivered in time.
/// </remarks>
[DataField("isProfitable")]
public bool IsProfitable = true;

/// <summary>
/// Is this package considered fragile?
/// </summary>
/// <remarks>
/// This can be set to true in the YAML files for a mail delivery to
/// always be Fragile, despite its contents.
/// </remarks>
[DataField("isFragile")]
public bool IsFragile = false;

/// <summary>
/// Is this package considered priority mail?
/// </summary>
/// <remarks>
/// There will be a timer set for its successful delivery. The
/// station's bank account will be penalized if it is not delivered on
/// time.
///
/// This is set to false on successful delivery.
///
/// This can be set to true in the YAML files for a mail delivery to
/// always be Priority.
/// </remarks>
[DataField("isPriority")]
public bool IsPriority = false;

/// <summary>
/// What will be packaged when the mail is spawned.
/// </summary>
[DataField("contents")]
public List<EntitySpawnEntry> Contents = new();

/// <summary>
/// The amount that cargo will be awarded for delivering this mail.
/// </summary>
[DataField("bounty")]
public int Bounty = 750;

/// <summary>
/// Penalty if the mail is destroyed.
/// </summary>
[DataField("penalty")]
public int Penalty = -250;

/// <summary>
/// The sound that's played when the mail's lock is broken.
/// </summary>
[DataField("penaltySound")]
public SoundSpecifier PenaltySound = new SoundPathSpecifier("/Audio/Machines/Nuke/angry_beep.ogg");

/// <summary>
/// The sound that's played when the mail's opened.
/// </summary>
[DataField("openSound")]
public SoundSpecifier OpenSound = new SoundPathSpecifier("/Audio/Effects/packetrip.ogg");

/// <summary>
/// The sound that's played when the mail's lock has been emagged.
/// </summary>
[DataField("emagSound")]
public SoundSpecifier EmagSound = new SoundCollectionSpecifier("sparks");

/// <summary>
/// Whether this component is enabled.
/// Removed when it becomes trash.
/// </summary>
public bool IsEnabled = true;

public CancellationTokenSource? priorityCancelToken;
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,6 @@
namespace Content.Server.Mail.Components
{
[RegisterComponent]
public sealed partial class MailReceiverComponent : Component
{}
}
108 changes: 108 additions & 0 deletions Content.Server/Nyanotrasen/Mail/Components/MailTeleporterComponent.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,108 @@
using Robust.Shared.Audio;

namespace Content.Server.Mail.Components
{
/// <summary>
/// This is for the mail teleporter.
/// Random mail will be teleported to this every few minutes.
/// </summary>
[RegisterComponent]
public sealed partial class MailTeleporterComponent : Component
{

// Not starting accumulator at 0 so mail carriers have some deliveries to make shortly after roundstart.
[DataField("accumulator")]
public float Accumulator = 285f;

[DataField("teleportInterval")]
public TimeSpan TeleportInterval = TimeSpan.FromMinutes(5);

/// <summary>
/// The sound that's played when new mail arrives.
/// </summary>
[DataField("teleportSound")]
public SoundSpecifier TeleportSound = new SoundPathSpecifier("/Audio/Effects/teleport_arrival.ogg");

/// <summary>
/// The MailDeliveryPoolPrototype that's used to select what mail this
/// teleporter can deliver.
/// </summary>
[DataField("mailPool")]
public string MailPool = "RandomMailDeliveryPool";

/// <summary>
/// How many mail candidates do we need per actual delivery sent when
/// the mail goes out? The number of candidates is divided by this number
/// to determine how many deliveries will be teleported in.
/// It does not determine unique recipients. That is random.
/// </summary>
[DataField("candidatesPerDelivery")]
public int CandidatesPerDelivery = 8;

[DataField("minimumDeliveriesPerTeleport")]
public int MinimumDeliveriesPerTeleport = 1;

/// <summary>
/// Do not teleport any more mail in, if there are at least this many
/// undelivered parcels.
/// </summary>
/// <remarks>
/// Currently this works by checking how many MailComponent entities
/// are sitting on the teleporter's tile.
///
/// It should be noted that if the number of actual deliveries to be
/// made based on the number of candidates divided by candidates per
/// delivery exceeds this number, the teleporter will spawn more mail
/// than this number.
///
/// This is just a simple check to see if anyone's been picking up the
/// mail lately to prevent entity bloat for the sake of performance.
/// </remarks>
[DataField("maximumUndeliveredParcels")]
public int MaximumUndeliveredParcels = 5;

/// <summary>
/// Any item that breaks or is destroyed in less than this amount of
/// damage is one of the types of items considered fragile.
/// </summary>
[DataField("fragileDamageThreshold")]
public int FragileDamageThreshold = 10;

/// <summary>
/// What's the bonus for delivering a fragile package intact?
/// </summary>
[DataField("fragileBonus")]
public int FragileBonus = 100;

/// <summary>
/// What's the malus for failing to deliver a fragile package?
/// </summary>
[DataField("fragileMalus")]
public int FragileMalus = -100;

/// <summary>
/// What's the chance for any one delivery to be marked as priority mail?
/// </summary>
[DataField("priorityChance")]
public float PriorityChance = 0.1f;

/// <summary>
/// How long until a priority delivery is considered as having failed
/// if not delivered?
/// </summary>
[DataField("priorityDuration")]
public TimeSpan priorityDuration = TimeSpan.FromMinutes(5);

/// <summary>
/// What's the bonus for delivering a priority package on time?
/// </summary>
[DataField("priorityBonus")]
public int PriorityBonus = 250;

/// <summary>
/// What's the malus for failing to deliver a priority package?
/// </summary>
[DataField("priorityMalus")]
public int PriorityMalus = -250;
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,9 @@
namespace Content.Server.Mail;

/// <summary>
/// Designates a station as a place for sending and receiving mail.
/// </summary>
[RegisterComponent]
public sealed partial class StationMailRouterComponent : Component
{
}
Loading

0 comments on commit 3d83d66

Please sign in to comment.