diff --git a/Content.Server/_DV/Mail/EntitySystems/MailSystem.cs b/Content.Server/_DV/Mail/EntitySystems/MailSystem.cs index 534b9c4ad29..63ac33d8d51 100644 --- a/Content.Server/_DV/Mail/EntitySystems/MailSystem.cs +++ b/Content.Server/_DV/Mail/EntitySystems/MailSystem.cs @@ -7,7 +7,6 @@ using Content.Server.Destructible.Thresholds.Triggers; using Content.Server.Destructible.Thresholds; using Content.Server.Destructible; -using Content.Server.Mind; using Content.Server.Popups; using Content.Server.Spawners.EntitySystems; using Content.Server.Station.Systems; @@ -64,7 +63,6 @@ public sealed class MailSystem : EntitySystem [Dependency] private readonly IRobustRandom _random = default!; [Dependency] private readonly IdCardSystem _idCardSystem = default!; [Dependency] private readonly MetaDataSystem _metaDataSystem = default!; - // [Dependency] private readonly MindSystem _mindSystem = default!; // Frontier: warning suppression [Dependency] private readonly OpenableSystem _openable = default!; [Dependency] private readonly PopupSystem _popupSystem = default!; [Dependency] private readonly SharedAppearanceSystem _appearanceSystem = default!; @@ -85,6 +83,8 @@ public sealed class MailSystem : EntitySystem private static readonly ProtoId MailTag = "Mail"; // Frontier private static readonly ProtoId TrashTag = "Trash"; // Frontier private static readonly ProtoId RecyclableTag = "Recyclable"; // Frontier + private bool _loggedNoValidTeleporters; // HardLight + private bool _loggedNoCandidates; // HardLight public override void Initialize() { @@ -461,6 +461,34 @@ private bool TryMatchJobTitleToPrototype(string jobTitle, [NotNullWhen(true)] ou return jobPrototype != null; } + // HardLight start + private static void MergeParcelWeights(Dictionary target, IReadOnlyDictionary source) + { + foreach (var (key, value) in source) + { + target[key] = value; + } + } + + private void LogNoValidTeleportersOnce() + { + if (_loggedNoValidTeleporters) + return; + + _loggedNoValidTeleporters = true; + _sawmill.Info("List of valid mail teleporters was empty!"); + } + + private void LogNoCandidatesOnce() + { + if (_loggedNoCandidates) + return; + + _loggedNoCandidates = true; + _sawmill.Info("List of mail candidates was empty!"); + } + // HardLight end + /// /// Handle all the gritty details particular to a new mail entity. /// @@ -554,29 +582,27 @@ public void SetupMail(EntityUid uid, SectorMailComponent component, MailRecipien } /// - /// Return the parcels waiting for delivery. + /// Return how many parcels are waiting for delivery on this teleporter's tile. // HardLight: Rephrased /// - /// The mail teleporter to check. - private List GetUndeliveredParcels(EntityUid uid) + private uint GetUndeliveredParcelCount(EntityUid uid) // HardLight { // An alternative solution would be to keep a list of the unopened // parcels spawned by the teleporter and see if they're not carried // by someone, but this is simple, and simple is good. var coordinates = Transform(uid).Coordinates; const LookupFlags lookupFlags = LookupFlags.Dynamic | LookupFlags.Sundries; - var entitiesInTile = _lookup.GetEntitiesIntersecting(coordinates, lookupFlags); - return entitiesInTile.Where(HasComp).ToList(); - } + // HardLight start + uint count = 0; + foreach (var entity in entitiesInTile) + { + if (HasComp(entity)) + count++; + } - /// - /// Return how many parcels are waiting for delivery. - /// - /// The mail teleporter to check. - private uint GetUndeliveredParcelCount(EntityUid uid) - { - return (uint)GetUndeliveredParcels(uid).Count; + return count; + // HardLight end } /// @@ -642,7 +668,6 @@ public bool TryGetMailRecipientForReceiver(EntityUid receiverUid, [NotNullWhen(t // End Frontier var accessTags = access.Tags; - //var mayReceivePriorityMail = !(_mindSystem.GetMind(receiverUid) == null); recipient = new MailRecipient( idCard.Comp.FullName, @@ -665,25 +690,12 @@ private List GetMailRecipientCandidates() // Frontier: remove Ent { var candidateList = new List(); var query = EntityQueryEnumerator(); - //var teleporterStation = _stationSystem.GetOwningStation(uid); // Frontier: unnecessary while (query.MoveNext(out var receiverUid, out _)) { - var location = Transform(receiverUid); - - // Frontier: sector-wide mail - // var receiverStation = _stationSystem.GetOwningStation(receiverUid); - // if (receiverStation != teleporterStation) - // continue; - - // Are you on expedition or in FTL? No mail for you. - if (location.MapID != Transform(receiverUid).MapID) - continue; - // Is this player displaying as SSD? If so, skip 'em. if (TryComp(receiverUid, out SSDIndicatorComponent? ssd) && ssd.IsSSD) continue; - // End Frontier // Skip ghosts and adminghosts if (HasComp(receiverUid) || HasComp(receiverUid)) @@ -723,17 +735,19 @@ private void SpawnMail(SectorMailComponent component) // If list of teleporters is empty, return. if (validTeleporters.Count <= 0) { - _sawmill.Info("List of valid mail teleporters was empty!"); + LogNoValidTeleportersOnce(); // HardLight return; } + _loggedNoValidTeleporters = false; // HardLight var candidateList = GetMailRecipientCandidates(); if (candidateList.Count <= 0) { - _sawmill.Info("List of mail candidates was empty!"); + LogNoCandidatesOnce(); // HardLight return; } + _loggedNoCandidates = false; // HardLight if (!_prototypeManager.TryIndex(component.MailPool, out var pool)) { @@ -742,28 +756,33 @@ private void SpawnMail(SectorMailComponent component) } var deliveryCount = component.MinimumDeliveriesPerTeleport + candidateList.Count / component.CandidatesPerDelivery; + var remainingCandidates = new List(candidateList); // HardLight + _random.Shuffle(remainingCandidates); // HardLight for (var i = 0; i < deliveryCount; i++) { - var candidate = _random.Pick(candidateList); + // HardLight start + if (remainingCandidates.Count == 0) + { + remainingCandidates = new List(candidateList); + _random.Shuffle(remainingCandidates); + } + + var candidate = remainingCandidates[^1]; + remainingCandidates.RemoveAt(remainingCandidates.Count - 1); + // HardLight end var possibleParcels = new Dictionary(pool.Everyone); if (TryMatchJobTitleToPrototype(candidate.Job, out var jobPrototype) && pool.Jobs.TryGetValue(jobPrototype.ID, out var jobParcels)) { - possibleParcels = possibleParcels - .Concat(jobParcels) - .GroupBy(g => g.Key) - .ToDictionary(pair => pair.Key, pair => pair.First().Value); + MergeParcelWeights(possibleParcels, jobParcels); // HardLight } if (TryMatchJobTitleToDepartment(candidate.Job, out var department) && pool.Departments.TryGetValue(department, out var departmentParcels)) { - possibleParcels = possibleParcels - .Concat(departmentParcels) - .GroupBy(g => g.Key) - .ToDictionary(pair => pair.Key, pair => pair.First().Value); + MergeParcelWeights(possibleParcels, departmentParcels); // HardLight } var accumulated = 0f; diff --git a/Content.Server/_DV/Mail/MailCommands.cs b/Content.Server/_DV/Mail/MailCommands.cs index 29900f9001e..169222aa9a5 100644 --- a/Content.Server/_DV/Mail/MailCommands.cs +++ b/Content.Server/_DV/Mail/MailCommands.cs @@ -111,7 +111,7 @@ public override void Execute(IConsoleShell shell, string argStr, string[] args) return; } - if (!mailSystem.TryGetMailTeleporterForReceiver(recipientUid, out var teleporterComponent, out var teleporterUid)) + if (!mailSystem.TryGetMailTeleporterForReceiver(recipientUid, out _, out var teleporterUid)) // HardLight: var teleporterComponent<_ { shell.WriteLine(Loc.GetString("command-mailto-no-teleporter-found")); return; @@ -183,7 +183,7 @@ public sealed class MailNowCommand : IConsoleCommand [Dependency] private readonly IEntityManager _entityManager = default!; [Dependency] private readonly IEntitySystemManager _entitySystemManager = default!; // Frontier - public async void Execute(IConsoleShell shell, string argStr, string[] args) + public void Execute(IConsoleShell shell, string argStr, string[] args) // HardLight: Removed async { var sectorService = _entitySystemManager.GetEntitySystem(); // Frontier: sector-wide mail diff --git a/Resources/Prototypes/_NF/SectorServices/services.yml b/Resources/Prototypes/_NF/SectorServices/services.yml index 12314b05047..973fc3a672e 100644 --- a/Resources/Prototypes/_NF/SectorServices/services.yml +++ b/Resources/Prototypes/_NF/SectorServices/services.yml @@ -7,6 +7,7 @@ id: Mail components: - type: SectorLogisticStats + - type: SectorMail # Contains sector-wide mail parameters - type: sectorService id: ShuttleRecords