135 lines
4.1 KiB
C#
135 lines
4.1 KiB
C#
using System.Linq;
|
|
using Content.Server.Store.Systems;
|
|
using Content.Shared.PDA;
|
|
using Content.Shared.PDA.Ringer;
|
|
using Content.Shared.Store.Components;
|
|
using Robust.Shared.Random;
|
|
|
|
namespace Content.Server.PDA.Ringer;
|
|
|
|
/// <summary>
|
|
/// Handles the server-side logic for <see cref="SharedRingerSystem"/>.
|
|
/// </summary>
|
|
public sealed class RingerSystem : SharedRingerSystem
|
|
{
|
|
[Dependency] private readonly IRobustRandom _random = default!;
|
|
|
|
/// <inheritdoc/>
|
|
public override void Initialize()
|
|
{
|
|
base.Initialize();
|
|
|
|
SubscribeLocalEvent<RingerComponent, MapInitEvent>(OnMapInit);
|
|
SubscribeLocalEvent<RingerComponent, CurrencyInsertAttemptEvent>(OnCurrencyInsert);
|
|
|
|
SubscribeLocalEvent<RingerUplinkComponent, GenerateUplinkCodeEvent>(OnGenerateUplinkCode);
|
|
}
|
|
|
|
/// <summary>
|
|
/// Randomizes a ringtone for <see cref="RingerComponent"/> on <see cref="MapInitEvent"/>.
|
|
/// </summary>
|
|
private void OnMapInit(Entity<RingerComponent> ent, ref MapInitEvent args)
|
|
{
|
|
UpdateRingerRingtone(ent, GenerateRingtone());
|
|
}
|
|
|
|
/// <summary>
|
|
/// Handles the <see cref="CurrencyInsertAttemptEvent"/> for <see cref="RingerUplinkComponent"/>.
|
|
/// </summary>
|
|
private void OnCurrencyInsert(Entity<RingerComponent> ent, ref CurrencyInsertAttemptEvent args)
|
|
{
|
|
// TODO: Store isn't predicted, can't move it to shared
|
|
if (!TryComp<RingerUplinkComponent>(ent, out var uplink))
|
|
{
|
|
args.Cancel();
|
|
return;
|
|
}
|
|
|
|
// if the store can be locked, it must be unlocked first before inserting currency. Stops traitor checking.
|
|
if (!uplink.Unlocked)
|
|
args.Cancel();
|
|
}
|
|
|
|
/// <summary>
|
|
/// Handles the <see cref="GenerateUplinkCodeEvent"/> for generating an uplink code.
|
|
/// </summary>
|
|
private void OnGenerateUplinkCode(Entity<RingerUplinkComponent> ent, ref GenerateUplinkCodeEvent ev)
|
|
{
|
|
var code = GenerateRingtone();
|
|
|
|
// Set the code on the component
|
|
ent.Comp.Code = code;
|
|
|
|
// Return the code via the event
|
|
ev.Code = code;
|
|
}
|
|
|
|
/// <inheritdoc/>
|
|
public override bool TryToggleUplink(EntityUid uid, Note[] ringtone, EntityUid? user = null)
|
|
{
|
|
if (!TryComp<RingerUplinkComponent>(uid, out var uplink))
|
|
return false;
|
|
|
|
if (!HasComp<StoreComponent>(uid))
|
|
return false;
|
|
|
|
// Wasn't generated yet
|
|
if (uplink.Code is null)
|
|
return false;
|
|
|
|
// On the server, we always check if the code matches
|
|
if (!uplink.Code.SequenceEqual(ringtone))
|
|
return false;
|
|
|
|
return ToggleUplinkInternal((uid, uplink));
|
|
}
|
|
|
|
/// <summary>
|
|
/// Generates a random ringtone using the C pentatonic scale.
|
|
/// </summary>
|
|
/// <returns>An array of Notes representing the ringtone.</returns>
|
|
/// <remarks>The logic for this is on the Server so that we don't get a different result on the Client every time.</remarks>
|
|
private Note[] GenerateRingtone()
|
|
{
|
|
// Default to using C pentatonic so it at least sounds not terrible.
|
|
return GenerateRingtone(new[]
|
|
{
|
|
Note.C,
|
|
Note.D,
|
|
Note.E,
|
|
Note.G,
|
|
Note.A
|
|
});
|
|
}
|
|
|
|
/// <summary>
|
|
/// Generates a random ringtone using the specified notes.
|
|
/// </summary>
|
|
/// <param name="notes">The notes to choose from when generating the ringtone.</param>
|
|
/// <returns>An array of Notes representing the ringtone.</returns>
|
|
/// <remarks>The logic for this is on the Server so that we don't get a different result on the Client every time.</remarks>
|
|
private Note[] GenerateRingtone(Note[] notes)
|
|
{
|
|
var ringtone = new Note[RingtoneLength];
|
|
|
|
for (var i = 0; i < RingtoneLength; i++)
|
|
{
|
|
ringtone[i] = _random.Pick(notes);
|
|
}
|
|
|
|
return ringtone;
|
|
}
|
|
}
|
|
|
|
/// <summary>
|
|
/// Event raised to generate a new uplink code for a PDA.
|
|
/// </summary>
|
|
[ByRefEvent]
|
|
public record struct GenerateUplinkCodeEvent
|
|
{
|
|
/// <summary>
|
|
/// The generated uplink code (filled in by the event handler).
|
|
/// </summary>
|
|
public Note[]? Code;
|
|
}
|