From 43af0349dd9218ca84345129c1c35376e58588b5 Mon Sep 17 00:00:00 2001 From: Shroomerian Date: Sun, 22 Sep 2024 14:42:27 +0200 Subject: [PATCH] Smuggling posters are now random and NFSD gets rewarded for getting prints of posters and dead drops (#1667) * Changed the time between dead drops and distance * Removed deprecated GetMapEntityId for TryGetMap * Removed tags that match autogenereated ones and simplified new Color * Made the distance even smaller * Revert "Made the distance even smaller" This reverts commit a008ba64a3236db3bb8c7603673d7d23cbfa5644. * Added check to see how many dead drop components are nearby * Changed behaviour of dead drop posters and added all of the previous posters to the dead drop system * Posters are finally random, only one can be active per point of interest * Added a message of the drop pod location to the NSFD after 15 minutes and made the wait time faster for posters * Scanning activated dead drop posters gives 30k cash to NFSD and dead drop location is now said by NFSD Outpost * NFSD scanning objects on the dead drop will reward them with 60k spessos and functioned the code for giving rewards * Changed spessos rewards to FUC * The timer now resets for the current poster if it detects any other posters that are active nearby * Lowered the chance of posters appearing and thus being Dead Drops, made pirates have a 1/3 of knowing the smuggler's ship ID after 15 minutes and added a guidebook entry * Increased the MinimumCooldown to 15 minutes and NFSD now gets progressive hints at dead drop posters' locations * Syndicate Supply Ships are now limited to 5 and if more are spawned, the oldest one gets deleted. Also only detectives get rewards for scanning dead drops and their posters * Moved all of the radio strings to locale and additional comments * Forensic scanner system now tracks based on access level rather than role name, removed accidental file and added a 1/10 chance to spawn a syndicate mob in the dead drop. Also fixed some stuff from last commit * Removed the syndicate mob guy because he would step on mines and lose his gun after FTL * Changed the dead drop poster cooldown to 5-10 minutes but now only 2 can be active at a time * Random contraband generation, part one * Station dead drop config, generation * Forensics cartridge, nfsd uplink entry * forensic scanner, staton definitions * Missing/inconsistent localization strings * HardSuit->Hardsuit * A few more uplink fixes * Revise dead drop tracking, always payout, add $ * Prototype-based smuggling message passing * POI map fixes, revert health, remove MapInit * Logging, anchored items, no drops @ courthouse * Dead drop value fiddling, uplink rewrites * cleanup * CVARS! * Move prototype to Shared * Fix config, cleanup, random error in drop pod msgs * fix forensic printouts, dead drop spawn menu * separate speso/fuc reward prints * Min/Max delay to floats * DeadDropSystem: type fix * Fix pirate delay, NFSD delay: 14-16 mins * Random fax generator & event * Roundstart hint generation * Remove test dead drop hints * Bugfixes for dead drop hint gen * more paper! * Smuggling guidebook entry, remove unneeded sec pgs * More smoog crate value, c3 trade crates, debug * Better drop pod scanner rewards * DD writable VV field, fix crate parent * WindowedEvent fixes * event naming * Add time to dead drop notes * Freelancer->Freelance channel * PodLocation messages: fix precision for vectors * Disable shipyard sale component, tweak smoog param * Multiple faxes per event * fax and logic * security_uplink_catalog: forensic module 2->4 FUC --------- Co-authored-by: Dvir <39403717+dvir001@users.noreply.github.com> Co-authored-by: Whatstone Co-authored-by: Checkraze <71046427+Cheackraze@users.noreply.github.com> --- .../Systems/ForensicScannerSystem.cs | 150 ++++- .../Systems/ShipyardSystem.Consoles.cs | 34 ++ .../Shipyard/Systems/ShipyardSystem.cs | 4 + .../Forensics/ForensicsCartridgeComponent.cs | 4 + .../_NF/GameRule/NfAdventureRuleSystem.cs | 7 + .../DeadDropHintVariationPassComponent.cs | 23 + .../DeadDropHintVariationPassSystem.cs | 30 + .../_NF/GameTicking/SectorGeneratedEvent.cs | 11 + .../Components/ContrabandPodGridComponent.cs | 14 + .../Smuggling/Components/DeadDropComponent.cs | 40 +- .../Components/DeadDropHintComponent.cs | 10 + .../Components/PotentialDeadDropComponent.cs | 16 + .../Components/SectorDeadDropComponent.cs | 30 + .../Components/StationDeadDropComponent.cs | 19 + .../StationDeadDropHintExemptComponent.cs | 7 + .../StationDeadDropReportingComponent.cs | 17 + .../_NF/Smuggling/DeadDropSystem.cs | 527 +++++++++++++++++- .../_NF/Smuggling/WindowedCounter.cs | 54 ++ .../Components/BluespaceCargoRuleComponent.cs | 0 .../Components/BluespaceErrorRuleComponent.cs | 0 .../Components/RandomFaxRuleComponent.cs | 104 ++++ .../Events/Actions/GetRandomDeadDropAction.cs | 80 +++ .../Events/BluespaceCargoRule.cs | 0 .../Events/BluespaceErrorRule.cs | 0 .../_NF/StationEvents/Events/RandomFaxRule.cs | 124 +++++ .../DisableShipyardSaleComponent.cs | 22 + Content.Shared/_NF/CCVar/NFCCVars.cs | 22 + .../SmugglingReportMessageSetPrototype.cs | 60 ++ .../forensic-scanner-slot-component.ftl | 1 + .../Locale/en-US/_NF/forensics/forensics.ftl | 6 + .../Locale/en-US/_NF/guidebook/guides.ftl | 3 + .../shipyard/shipyard-console-component.ftl | 4 + .../en-US/_NF/smuggling/deaddrop-note.ftl | 23 + .../Locale/en-US/_NF/smuggling/deaddrop.ftl | 15 +- .../_NF/smuggling/fake-deaddrop-hints.ftl | 56 ++ .../Locale/en-US/_NF/store/uplink-catalog.ftl | 84 +-- Resources/Maps/_NF/POI/arena.yml | 6 +- Resources/Maps/_NF/POI/caseyscasino.yml | 132 ++--- Resources/Maps/_NF/POI/grifty.yml | 43 +- Resources/Maps/_NF/POI/lodge.yml | 2 - Resources/Maps/_NF/POI/tinnia.yml | 4 +- Resources/Maps/deaddrop.yml | 1 + .../Objects/Devices/forensic_scanner.yml | 26 +- .../Entities/Objects/Misc/dat_fukken_disk.yml | 5 + .../Entities/Structures/Furniture/chairs.yml | 8 + .../Entities/Structures/Machines/nuke.yml | 5 + .../Structures/Machines/vending_machines.yml | 2 + .../Structures/Piping/Disposal/units.yml | 2 + Resources/Prototypes/Guidebook/security.yml | 5 +- .../Structures/Machines/mailTeleporter.yml | 5 + .../_NF/Catalog/Fills/Crates/trade.yml | 26 + .../_NF/Catalog/security_uplink_catalog.yml | 21 +- .../_NF/Entities/Markers/Spawners/crates.yml | 8 +- .../_NF/Entities/Markers/Spawners/posters.yml | 20 +- .../Entities/Objects/Devices/cartridges.yml | 18 + .../_NF/Entities/Objects/Misc/paper.yml | 17 + .../Prototypes/_NF/Entities/Stations/base.yml | 9 +- .../_NF/Entities/Stations/nanotrasen.yml | 15 + .../Structures/Storage/Crates/crates.yml | 21 +- .../Wallmounts/Signs/contraband.yml | 271 +++------ Resources/Prototypes/_NF/Events/events.yml | 1 + Resources/Prototypes/_NF/Events/nf_events.yml | 19 + .../Prototypes/_NF/GameRules/roundstart.yml | 12 + .../Prototypes/_NF/GameRules/variation.yml | 6 + Resources/Prototypes/_NF/Guidebook/nfsd.yml | 4 + .../_NF/PointsOfInterest/anomalousgeode.yml | 1 + .../_NF/PointsOfInterest/anomalouslab.yml | 1 + .../_NF/PointsOfInterest/caseys.yml | 14 +- .../_NF/PointsOfInterest/courthouse.yml | 13 + .../Prototypes/_NF/PointsOfInterest/cove.yml | 3 + .../_NF/PointsOfInterest/grifty.yml | 2 +- .../Prototypes/_NF/PointsOfInterest/lodge.yml | 2 + .../_NF/PointsOfInterest/lpbravo.yml | 1 + .../_NF/PointsOfInterest/mchobo.yml | 2 +- .../Prototypes/_NF/PointsOfInterest/nfsd.yml | 3 + .../_NF/PointsOfInterest/thepit.yml | 12 + .../_NF/PointsOfInterest/tinniasrest.yml | 12 + .../Prototypes/_NF/PointsOfInterest/trade.yml | 2 + .../_NF/Roles/Jobs/Nfsd/detectivenf.yml | 3 +- .../_NF/SectorServices/deaddrops.yml | 5 + .../_NF/Smuggling/fake_dead_drop_hints.yml | 5 + .../_NF/Smuggling/report_message_sets.yml | 33 ++ Resources/Prototypes/_NF/game_presets.yml | 2 +- .../_NF/Guidebook/NFSD/Smuggling.xml | 46 ++ .../cartridge.rsi/contraband_forensics.png | Bin 0 -> 202 bytes .../Objects/Devices/cartridge.rsi/meta.json | 14 + .../forensiccartridge.png | Bin 0 -> 385 bytes .../forensic_scanner.rsi/forensicnew.png | Bin 0 -> 736 bytes .../Devices/forensic_scanner.rsi/meta.json | 31 ++ Resources/migration.yml | 6 +- 90 files changed, 2104 insertions(+), 424 deletions(-) create mode 100644 Content.Server/_NF/Forensics/ForensicsCartridgeComponent.cs create mode 100644 Content.Server/_NF/GameTicking/Rules/VariationPass/Components/DeadDropHintVariationPassComponent.cs create mode 100644 Content.Server/_NF/GameTicking/Rules/VariationPass/Systems/DeadDropHintVariationPassSystem.cs create mode 100644 Content.Server/_NF/GameTicking/SectorGeneratedEvent.cs create mode 100644 Content.Server/_NF/Smuggling/Components/ContrabandPodGridComponent.cs create mode 100644 Content.Server/_NF/Smuggling/Components/DeadDropHintComponent.cs create mode 100644 Content.Server/_NF/Smuggling/Components/PotentialDeadDropComponent.cs create mode 100644 Content.Server/_NF/Smuggling/Components/SectorDeadDropComponent.cs create mode 100644 Content.Server/_NF/Smuggling/Components/StationDeadDropComponent.cs create mode 100644 Content.Server/_NF/Smuggling/Components/StationDeadDropHintExemptComponent.cs create mode 100644 Content.Server/_NF/Smuggling/Components/StationDeadDropReportingComponent.cs create mode 100644 Content.Server/_NF/Smuggling/WindowedCounter.cs rename Content.Server/{ => _NF}/StationEvents/Components/BluespaceCargoRuleComponent.cs (100%) rename Content.Server/{ => _NF}/StationEvents/Components/BluespaceErrorRuleComponent.cs (100%) create mode 100644 Content.Server/_NF/StationEvents/Components/RandomFaxRuleComponent.cs create mode 100644 Content.Server/_NF/StationEvents/Events/Actions/GetRandomDeadDropAction.cs rename Content.Server/{ => _NF}/StationEvents/Events/BluespaceCargoRule.cs (100%) rename Content.Server/{ => _NF}/StationEvents/Events/BluespaceErrorRule.cs (100%) create mode 100644 Content.Server/_NF/StationEvents/Events/RandomFaxRule.cs create mode 100644 Content.Shared/Shipyard/Components/DisableShipyardSaleComponent.cs create mode 100644 Content.Shared/_NF/Smuggling/Prototypes/SmugglingReportMessageSetPrototype.cs create mode 100644 Resources/Locale/en-US/_NF/forensics/forensic-scanner-slot-component.ftl create mode 100644 Resources/Locale/en-US/_NF/forensics/forensics.ftl create mode 100644 Resources/Locale/en-US/_NF/smuggling/deaddrop-note.ftl create mode 100644 Resources/Locale/en-US/_NF/smuggling/fake-deaddrop-hints.ftl create mode 100644 Resources/Prototypes/_NF/GameRules/variation.yml create mode 100644 Resources/Prototypes/_NF/Guidebook/nfsd.yml create mode 100644 Resources/Prototypes/_NF/SectorServices/deaddrops.yml create mode 100644 Resources/Prototypes/_NF/Smuggling/fake_dead_drop_hints.yml create mode 100644 Resources/Prototypes/_NF/Smuggling/report_message_sets.yml create mode 100644 Resources/ServerInfo/_NF/Guidebook/NFSD/Smuggling.xml create mode 100644 Resources/Textures/_NF/Objects/Devices/cartridge.rsi/contraband_forensics.png create mode 100644 Resources/Textures/_NF/Objects/Devices/cartridge.rsi/meta.json create mode 100644 Resources/Textures/_NF/Objects/Devices/forensic_scanner.rsi/forensiccartridge.png create mode 100644 Resources/Textures/_NF/Objects/Devices/forensic_scanner.rsi/forensicnew.png create mode 100644 Resources/Textures/_NF/Objects/Devices/forensic_scanner.rsi/meta.json diff --git a/Content.Server/Forensics/Systems/ForensicScannerSystem.cs b/Content.Server/Forensics/Systems/ForensicScannerSystem.cs index b6dc068f789..3586d60b4ac 100644 --- a/Content.Server/Forensics/Systems/ForensicScannerSystem.cs +++ b/Content.Server/Forensics/Systems/ForensicScannerSystem.cs @@ -1,6 +1,11 @@ using System.Linq; using System.Text; using Content.Server.Popups; +using Content.Server.Stack; // Frontier +using Content.Server._NF.Smuggling; // Frontier +using Content.Server._NF.Smuggling.Components; // Frontier +using Content.Server.Cargo.Systems; // Frontier +using Content.Server.Radio.EntitySystems; // Frontier using Content.Shared.UserInterface; using Content.Shared.DoAfter; using Content.Shared.Fluids.Components; @@ -9,13 +14,24 @@ using Content.Shared.Interaction; using Content.Shared.Paper; using Content.Shared.Verbs; +using Content.Shared.Stacks; // Frontier +using Content.Shared.Radio; // Frontier +using Content.Shared.Access.Systems; // Frontier +using Robust.Shared.Prototypes; // Frontier using Content.Shared.Tag; using Robust.Shared.Audio.Systems; using Robust.Server.GameObjects; using Robust.Shared.Audio; -using Robust.Shared.Player; using Robust.Shared.Timing; using Content.Server.Chemistry.Containers.EntitySystems; +using Content.Shared.Containers.ItemSlots; // Frontier +using Content.Server.Cargo.Components; // Frontier +using Content.Server._NF.SectorServices; // Frontier +using Content.Shared.FixedPoint; // Frontier +using Robust.Shared.Configuration; // Frontier +using Content.Shared._NF.CCVar; +using Content.Shared._NF.Bank; // Frontier + // todo: remove this stinky LINQy namespace Content.Server.Forensics @@ -33,6 +49,30 @@ public sealed class ForensicScannerSystem : EntitySystem [Dependency] private readonly ForensicsSystem _forensicsSystem = default!; [Dependency] private readonly SolutionContainerSystem _solutionContainerSystem = default!; [Dependency] private readonly TagSystem _tag = default!; + [Dependency] private readonly StackSystem _stackSystem = default!; // Frontier + [Dependency] private readonly SharedAudioSystem _audio = default!; // Frontier + [Dependency] private readonly IPrototypeManager _prototypeManager = default!; // Frontier + [Dependency] private readonly RadioSystem _radio = default!; // Frontier + [Dependency] private readonly AccessReaderSystem _accessReader = default!; // Frontier + [Dependency] private readonly DeadDropSystem _deadDrop = default!; // Frontier + [Dependency] private readonly ItemSlotsSystem _itemSlots = default!; // Frontier + [Dependency] private readonly CargoSystem _cargo = default!; // Frontier + [Dependency] private readonly SectorServiceSystem _service = default!; // Frontier + [Dependency] private readonly IConfigurationManager _cfg = default!; // Frontier + + // Frontier: payout constants + // Temporary values, sane defaults, will be overwritten by CVARs. + private int _minFUCPayout = 2; + + private const int ActiveUnusedDeadDropSpesoReward = 20000; + private const float ActiveUnusedDeadDropFUCReward = 2.0f; + private const int ActiveUsedDeadDropSpesoReward = 10000; + private const float ActiveUsedDeadDropFUCReward = 1.0f; + private const int InactiveUsedDeadDropSpesoReward = 5000; + private const float InactiveUsedDeadDropFUCReward = 0.5f; + private const int DropPodSpesoReward = 10000; + private const float DropPodFUCReward = 1.0f; + // End Frontier: payout constants public override void Initialize() { @@ -45,7 +85,73 @@ public override void Initialize() SubscribeLocalEvent(OnPrint); SubscribeLocalEvent(OnClear); SubscribeLocalEvent(OnDoAfter); + + Subs.CVar(_cfg, NFCCVars.SmugglingMinFucPayout, OnMinFucPayoutChanged, true); // Frontier + } + + private void OnMinFucPayoutChanged(int newMin) + { + _minFUCPayout = newMin; + } + + // Frontier: add dead drop rewards + /// + /// Rewards the NFSD department for scanning a dead drop. + /// Gives some amount of spesos and FUC to the + /// + private void GiveReward(EntityUid uidOrigin, EntityUid target, int spesoAmount, FixedPoint2 fucAmount, string msg) + { + SoundSpecifier confirmSound = new SoundPathSpecifier("/Audio/Effects/Cargo/ping.ogg"); + _audio.PlayPvs(_audio.GetSound(confirmSound), uidOrigin); + + // Credit the NFSD's bank account (todo: split these) + if (spesoAmount > 0) + { + var queryBank = EntityQuery(); + foreach (var account in queryBank) + { + _cargo.DeductFunds(account, -spesoAmount); + } + } + else + spesoAmount = 0; + + if (fucAmount > 0) + { + // Accumulate sector-wide FUCs, pay out if min threshold met + if (TryComp(_service.GetServiceEntity(), out var sectorDD)) + { + sectorDD.FUCAccumulator += fucAmount; + if (sectorDD.FUCAccumulator >= _minFUCPayout) + { + // inherent floor + int payout = sectorDD.FUCAccumulator.Int(); + sectorDD.FUCAccumulator -= payout; + + var stackPrototype = _prototypeManager.Index("FrontierUplinkCoin"); + _stackSystem.Spawn(payout, stackPrototype, Transform(target).Coordinates); + } + } + } + else + fucAmount = 0; + + var channel = _prototypeManager.Index("Nfsd"); + string msgString = Loc.GetString(msg); + if (fucAmount >= 1) + { + msgString = msgString + " " + Loc.GetString("forensic-reward-amount", + ("spesos", BankSystemExtensions.ToSpesoString(spesoAmount)), + ("fuc", BankSystemExtensions.ToFUCString(fucAmount.Int()))); + } + else + { + msgString = msgString + " " + Loc.GetString("forensic-reward-amount-speso-only", + ("spesos", BankSystemExtensions.ToSpesoString(spesoAmount))); + } + _radio.SendRadioMessage(uidOrigin, msgString, channel, uidOrigin); } + // End Frontier: add dead drop rewards private void UpdateUserInterface(EntityUid uid, ForensicScannerComponent component) { @@ -87,6 +193,48 @@ private void OnDoAfter(EntityUid uid, ForensicScannerComponent component, DoAfte scanner.Residues = forensics.Residues.ToList(); } + // Frontier: contraband poster/pod scanning + if (_itemSlots.TryGetSlot(uid, "forensics_cartridge", out var itemSlot) && itemSlot.HasItem) + { + EntityUid target = args.Args.Target.Value; + if (TryComp(target, out var deadDrop)) + { + // If there's a dead drop note present, pay out regardless and compromise the dead drop. + if (_gameTiming.CurTime >= deadDrop.NextDrop) + { + int spesoReward; + FixedPoint2 fucReward; + string msg; + if (deadDrop.DeadDropCalled) + { + spesoReward = ActiveUsedDeadDropSpesoReward; + fucReward = ActiveUsedDeadDropFUCReward; + msg = "forensic-reward-dead-drop-used-present"; + } + else + { + spesoReward = ActiveUnusedDeadDropSpesoReward; + fucReward = ActiveUnusedDeadDropFUCReward; + msg = "forensic-reward-dead-drop-unused"; + } + GiveReward(uid, target, spesoReward, fucReward, msg); + _deadDrop.CompromiseDeadDrop(target, deadDrop); + } + // Otherwise, if it's been used, pay out at a reduced rate and compromise it. + else if (deadDrop.DeadDropCalled) + { + GiveReward(uid, target, InactiveUsedDeadDropSpesoReward, InactiveUsedDeadDropFUCReward, "forensic-reward-dead-drop-used-gone"); + _deadDrop.CompromiseDeadDrop(target, deadDrop); + } + } + else if (TryComp(Transform(target).GridUid, out var pod) && !pod.Scanned) + { + GiveReward(uid, target, DropPodSpesoReward, DropPodFUCReward, "forensic-reward-pod"); + pod.Scanned = true; + } + } + // End Frontier: contraband poster/pod scanning + if (_tag.HasTag(args.Args.Target.Value, "DNASolutionScannable")) { scanner.SolutionDNAs = _forensicsSystem.GetSolutionsDNA(args.Args.Target.Value); diff --git a/Content.Server/Shipyard/Systems/ShipyardSystem.Consoles.cs b/Content.Server/Shipyard/Systems/ShipyardSystem.Consoles.cs index e6df3b63be3..80b1aada631 100644 --- a/Content.Server/Shipyard/Systems/ShipyardSystem.Consoles.cs +++ b/Content.Server/Shipyard/Systems/ShipyardSystem.Consoles.cs @@ -41,6 +41,7 @@ using Robust.Shared.Audio.Systems; using Content.Shared.Access; using Content.Shared.Tiles; +using Content.Server._NF.Smuggling.Components; namespace Content.Server.Shipyard.Systems; @@ -367,6 +368,17 @@ public void OnSellMessage(EntityUid uid, ShipyardConsoleComponent component, Shi var shuttleName = ToPrettyString(shuttleUid); // Grab the name before it gets 1984'd + // Check for shipyard blacklisting components + var disableSaleQuery = GetEntityQuery(); + var xformQuery = GetEntityQuery(); + var disableSaleMsg = FindDisableShipyardSaleObjects(shuttleUid, (ShipyardConsoleUiKey)args.UiKey, disableSaleQuery, xformQuery); + if (disableSaleMsg != null) + { + ConsolePopup(args.Actor, Loc.GetString(disableSaleMsg)); + PlayDenySound(args.Actor, uid, component); + return; + } + var saleResult = TrySellShuttle(stationUid, shuttleUid, out var bill); if (saleResult.Error != ShipyardSaleError.Success) { @@ -606,6 +618,28 @@ private void OnItemSlotChanged(EntityUid uid, ShipyardConsoleComponent component return null; } + /// + /// Looks for a living, sapient being aboard a particular entity. + /// + /// The entity to search (e.g. a shuttle, a station) + /// A query to get the MobState from an entity + /// A query to get the transform component of an entity + /// The name of the sapient being if one was found, null otherwise. + public string? FindDisableShipyardSaleObjects(EntityUid shuttle, ShipyardConsoleUiKey key, EntityQuery disableSaleQuery, EntityQuery xformQuery) + { + var xform = xformQuery.GetComponent(shuttle); + var childEnumerator = xform.ChildEnumerator; + + while (childEnumerator.MoveNext(out var child)) + { + if (disableSaleQuery.TryGetComponent(child, out var disableSale) + && !disableSale.AllowedShipyardTypes.Contains(key)) + return disableSale.Reason; + } + + return null; + } + private struct IDShipAccesses { public IReadOnlyCollection> Tags; diff --git a/Content.Server/Shipyard/Systems/ShipyardSystem.cs b/Content.Server/Shipyard/Systems/ShipyardSystem.cs index 875defcb4b7..ee10289800b 100644 --- a/Content.Server/Shipyard/Systems/ShipyardSystem.cs +++ b/Content.Server/Shipyard/Systems/ShipyardSystem.cs @@ -18,6 +18,7 @@ using Content.Shared.Shipyard.Events; using Content.Shared.Mobs.Components; using Robust.Shared.Containers; +using Content.Server._NF.Smuggling.Components; namespace Content.Server.Shipyard.Systems; @@ -46,12 +47,15 @@ public enum ShipyardSaleError Undocked, // Ship is not docked with the station. OrganicsAboard, // Sapient intelligence is aboard, cannot sell, would delete the organics InvalidShip, // Ship is invalid + MessageOverwritten, // Overwritten message. } + // TODO: swap to strictly being a formatted message. public struct ShipyardSaleResult { public ShipyardSaleError Error; // Whether or not the ship can be sold. public string? OrganicName; // In case an organic is aboard, this will be set to the first that's aboard. + public string? OverwrittenMessage; // The message to write if Error is MessageOverwritten. } public override void Initialize() diff --git a/Content.Server/_NF/Forensics/ForensicsCartridgeComponent.cs b/Content.Server/_NF/Forensics/ForensicsCartridgeComponent.cs new file mode 100644 index 00000000000..fab8e5c321b --- /dev/null +++ b/Content.Server/_NF/Forensics/ForensicsCartridgeComponent.cs @@ -0,0 +1,4 @@ +namespace Content.Server._NF.Forensics; + +[RegisterComponent] +public sealed partial class ForensicsCartridgeComponent : Component; diff --git a/Content.Server/_NF/GameRule/NfAdventureRuleSystem.cs b/Content.Server/_NF/GameRule/NfAdventureRuleSystem.cs index 6f789e7b256..9cb90e57e24 100644 --- a/Content.Server/_NF/GameRule/NfAdventureRuleSystem.cs +++ b/Content.Server/_NF/GameRule/NfAdventureRuleSystem.cs @@ -13,6 +13,9 @@ using Content.Shared._NF.GameRule; using Content.Server.Procedural; using Content.Shared.Bank.Components; +using Content.Server._NF.GameTicking.Events; +using Content.Server.GameTicking.Events; +using Content.Server.GameTicking.Rules.Components; using Content.Shared.Procedural; using Robust.Server.GameObjects; using Robust.Server.Maps; @@ -23,6 +26,7 @@ using Robust.Shared.Random; using Robust.Shared.Map.Components; using Content.Shared.Shuttles.Components; +using Content.Server._NF.GameTicking.Events; using Content.Server.Shuttles.Systems; using Content.Server.Cargo.Components; using Content.Server.GameTicking; @@ -161,6 +165,9 @@ protected override void Started(EntityUid uid, AdventureRuleComponent component, base.Started(uid, component, gameRule, args); + // Using invalid entity, we don't have a relevant entity to reference here. + RaiseLocalEvent(EntityUid.Invalid, new StationsGeneratedEvent(), broadcast: true); // TODO: attach this to a meaningful entity. + var dungenTypes = _prototypeManager.EnumeratePrototypes(); foreach (var dunGen in dungenTypes) diff --git a/Content.Server/_NF/GameTicking/Rules/VariationPass/Components/DeadDropHintVariationPassComponent.cs b/Content.Server/_NF/GameTicking/Rules/VariationPass/Components/DeadDropHintVariationPassComponent.cs new file mode 100644 index 00000000000..bf92b96e0f9 --- /dev/null +++ b/Content.Server/_NF/GameTicking/Rules/VariationPass/Components/DeadDropHintVariationPassComponent.cs @@ -0,0 +1,23 @@ +using Robust.Shared.Prototypes; + +namespace Content.Server.GameTicking.Rules.VariationPass.Components; + +/// +/// This handles generating round-start dead drop hints. +/// +[RegisterComponent] +public sealed partial class DeadDropHintVariationPassComponent : Component +{ + /// + /// Chance that a potential hint will be generated on a table. + /// Remember, the average number + /// + [DataField] + public float HintSpawnChance = 0.02f; + + /// + /// The entity to spawn for a hint. + /// + [DataField] + public EntProtoId HintSpawnPrototype = "PaperDeadDropHint"; +} diff --git a/Content.Server/_NF/GameTicking/Rules/VariationPass/Systems/DeadDropHintVariationPassSystem.cs b/Content.Server/_NF/GameTicking/Rules/VariationPass/Systems/DeadDropHintVariationPassSystem.cs new file mode 100644 index 00000000000..9ea4e00c858 --- /dev/null +++ b/Content.Server/_NF/GameTicking/Rules/VariationPass/Systems/DeadDropHintVariationPassSystem.cs @@ -0,0 +1,30 @@ +using Content.Server._NF.Smuggling.Components; +using Content.Server.GameTicking.Rules.VariationPass.Components; +using Content.Shared.Climbing.Components; +using Content.Shared.Placeable; + +namespace Content.Server.GameTicking.Rules.VariationPass; + +/// +public sealed class DeadDropHintVariationPass : VariationPassSystem +{ + protected override void ApplyVariation(Entity ent, ref StationVariationPassEvent args) + { + if (HasComp(args.Station)) + return; + + // Best query for table-like objects: bonkable filters out grills. + var query = AllEntityQuery(); + while (query.MoveNext(out var uid, out var _, out var _, out var xform)) + { + if (!IsMemberOfStation((uid, xform), ref args)) + continue; + + var prob = Random.NextFloat(); + if (prob < ent.Comp.HintSpawnChance) + { + SpawnAttachedTo(ent.Comp.HintSpawnPrototype, xform.Coordinates); + } + } + } +} diff --git a/Content.Server/_NF/GameTicking/SectorGeneratedEvent.cs b/Content.Server/_NF/GameTicking/SectorGeneratedEvent.cs new file mode 100644 index 00000000000..f86d66da55b --- /dev/null +++ b/Content.Server/_NF/GameTicking/SectorGeneratedEvent.cs @@ -0,0 +1,11 @@ +namespace Content.Server._NF.GameTicking.Events; + +/// +/// Raised once all of the stations have been generated. +/// +public sealed class StationsGeneratedEvent : EntityEventArgs +{ + public StationsGeneratedEvent() + { + } +} diff --git a/Content.Server/_NF/Smuggling/Components/ContrabandPodGridComponent.cs b/Content.Server/_NF/Smuggling/Components/ContrabandPodGridComponent.cs new file mode 100644 index 00000000000..af891980ac1 --- /dev/null +++ b/Content.Server/_NF/Smuggling/Components/ContrabandPodGridComponent.cs @@ -0,0 +1,14 @@ +namespace Content.Server._NF.Smuggling.Components; + +/// +/// Denotes a grid that is brought in via a dead drop. +/// +[RegisterComponent] +public sealed partial class ContrabandPodGridComponent : Component +{ + /// + /// Maximum number of dead drops to spawn on the station. + /// + [DataField] + public bool Scanned = false; +} diff --git a/Content.Server/_NF/Smuggling/Components/DeadDropComponent.cs b/Content.Server/_NF/Smuggling/Components/DeadDropComponent.cs index a0296bc03b7..a0c20ce4fa7 100644 --- a/Content.Server/_NF/Smuggling/Components/DeadDropComponent.cs +++ b/Content.Server/_NF/Smuggling/Components/DeadDropComponent.cs @@ -13,48 +13,66 @@ public sealed partial class DeadDropComponent : Component /// /// When the next drop will occur. Used internally. /// - [DataField("nextDrop")] + [DataField, ViewVariables(VVAccess.ReadOnly)] public TimeSpan? NextDrop; + /// + /// A non-nullable proxy to overwrite NextDrop + /// + [ViewVariables(VVAccess.ReadWrite)] + public TimeSpan NextDropVV + { + get { return NextDrop ?? TimeSpan.Zero; } + set { NextDrop = value; } + } + /// /// Minimum wait time in seconds to wait for the next dead drop. /// - [DataField("minimumCoolDown")] + [DataField] + //Use 10 seconds for testing public int MinimumCoolDown = 900; // 900 / 60 = 15 minutes /// /// Max wait time in seconds to wait for the next dead drop. /// - [DataField("maximumCoolDown")] + [DataField] + //Use 15 seconds for testing public int MaximumCoolDown = 5400; // 5400 / 60 = 90 minutes /// /// Minimum distance to spawn the drop. /// - [DataField("minimumDistance")] - public int MinimumDistance = 6500; + [DataField] + public int MinimumDistance = 4500; /// /// Max distance to spawn the drop. /// - [DataField("maximumDistance")] - public int MaximumDistance = 9900; + [DataField] + public int MaximumDistance = 6500; /// /// The paper prototype to spawn. /// - [DataField("hintPaper", customTypeSerializer: typeof(PrototypeIdSerializer))] + [DataField(customTypeSerializer: typeof(PrototypeIdSerializer))] public string HintPaper = "PaperCargoInvoice"; + /// + /// Whether or not a drop pod has been called for this dead drop. + /// + [DataField] + public bool DeadDropCalled = false; + /// /// Location of the grid to spawn in as the dead drop. /// - [DataField("dropGrid")] + [DataField] public string DropGrid = "/Maps/deaddrop.yml"; /// /// The color of your grid. the name should be set by the mapper when mapping. /// - [DataField("color")] - public Color Color = new Color(225, 15, 155); + [DataField] + public Color Color = new(225, 15, 155); } diff --git a/Content.Server/_NF/Smuggling/Components/DeadDropHintComponent.cs b/Content.Server/_NF/Smuggling/Components/DeadDropHintComponent.cs new file mode 100644 index 00000000000..9e10b07c74b --- /dev/null +++ b/Content.Server/_NF/Smuggling/Components/DeadDropHintComponent.cs @@ -0,0 +1,10 @@ +namespace Content.Server._NF.Smuggling.Components; + +/// +/// Denotes an item as having hints to existing dead drops. +/// These should be generated dynamically from the current set of dead drops on spawn. +/// +[RegisterComponent] +public sealed partial class DeadDropHintComponent : Component +{ +} diff --git a/Content.Server/_NF/Smuggling/Components/PotentialDeadDropComponent.cs b/Content.Server/_NF/Smuggling/Components/PotentialDeadDropComponent.cs new file mode 100644 index 00000000000..fa48c548cbf --- /dev/null +++ b/Content.Server/_NF/Smuggling/Components/PotentialDeadDropComponent.cs @@ -0,0 +1,16 @@ +namespace Content.Server._NF.Smuggling.Components; + +/// +/// Denotes an item as being a potential dead drop candidate. +/// If/when dead drops are generated or moved, this entity may +/// receive a DeadDropComponent. +/// +/// +/// Should be improved upon as a way to generate random dead drops. +/// +[RegisterComponent] +public sealed partial class PotentialDeadDropComponent : Component +{ + [DataField] + public string HintText = "dead-drop-hint-generic"; +} diff --git a/Content.Server/_NF/Smuggling/Components/SectorDeadDropComponent.cs b/Content.Server/_NF/Smuggling/Components/SectorDeadDropComponent.cs new file mode 100644 index 00000000000..25ecb5b5632 --- /dev/null +++ b/Content.Server/_NF/Smuggling/Components/SectorDeadDropComponent.cs @@ -0,0 +1,30 @@ +using Content.Shared.Dataset; +using Content.Shared.FixedPoint; +using Robust.Shared.Prototypes; + +namespace Content.Server._NF.Smuggling.Components; + +/// +/// Stores dead drop information for the entire sector. +/// Frequency of dead drops, and other dead drop mechanics should be driven by this state. +/// +[RegisterComponent] +public sealed partial class SectorDeadDropComponent : Component +{ + /// + /// Accumulator for FUC values. Pays out at a given amount. + /// + [ViewVariables(VVAccess.ReadWrite)] + public FixedPoint2 FUCAccumulator = FixedPoint2.Zero; + + // Utility field for windowing reported events. Having more in an hour results in more precise information. + [ViewVariables(VVAccess.ReadWrite)] + public WindowedCounter? ReportedEventsThisHour = null; + + // In the case of providing a fake location for alternative notifications, which names can we draw from? + [ViewVariables(VVAccess.ReadWrite)] + public Dictionary DeadDropStationNames = new(); + + [DataField] + public ProtoId FakeDeadDropHints = default!; +} diff --git a/Content.Server/_NF/Smuggling/Components/StationDeadDropComponent.cs b/Content.Server/_NF/Smuggling/Components/StationDeadDropComponent.cs new file mode 100644 index 00000000000..562687e5481 --- /dev/null +++ b/Content.Server/_NF/Smuggling/Components/StationDeadDropComponent.cs @@ -0,0 +1,19 @@ +namespace Content.Server._NF.Smuggling.Components; + +/// +/// Denotes a station as one that has dead drops spawned on it. +/// When the map is initialized, anything with PotentialComponent +/// may become a DeadDrop. +/// +/// When a dead drop on the station is compromised, another +/// potential dead drop is selected instead. +/// +[RegisterComponent] +public sealed partial class StationDeadDropComponent : Component +{ + /// + /// Maximum number of dead drops to spawn on the station. + /// + [DataField] + public int MaxDeadDrops = 3; +} diff --git a/Content.Server/_NF/Smuggling/Components/StationDeadDropHintExemptComponent.cs b/Content.Server/_NF/Smuggling/Components/StationDeadDropHintExemptComponent.cs new file mode 100644 index 00000000000..6e9ebeddaf8 --- /dev/null +++ b/Content.Server/_NF/Smuggling/Components/StationDeadDropHintExemptComponent.cs @@ -0,0 +1,7 @@ +namespace Content.Server._NF.Smuggling.Components; + +/// +/// A station that has this will not have hint spawn on it. +/// +[RegisterComponent] +public sealed partial class StationDeadDropHintExemptComponent : Component; diff --git a/Content.Server/_NF/Smuggling/Components/StationDeadDropReportingComponent.cs b/Content.Server/_NF/Smuggling/Components/StationDeadDropReportingComponent.cs new file mode 100644 index 00000000000..cbf27cbc0b7 --- /dev/null +++ b/Content.Server/_NF/Smuggling/Components/StationDeadDropReportingComponent.cs @@ -0,0 +1,17 @@ +using Robust.Shared.Prototypes; +using Content.Shared._NF.Smuggling.Prototypes; + +namespace Content.Server._NF.Smuggling.Components; + +/// +/// Denotes a station as one that will report, to a given radio channel, +/// +/// When a dead drop on the station is compromised, another +/// potential dead drop is selected instead. +/// +[RegisterComponent] +public sealed partial class StationDeadDropReportingComponent : Component +{ + [DataField(required: true)] + public ProtoId MessageSet; +} diff --git a/Content.Server/_NF/Smuggling/DeadDropSystem.cs b/Content.Server/_NF/Smuggling/DeadDropSystem.cs index 0e3a8463942..14ed6affccc 100644 --- a/Content.Server/_NF/Smuggling/DeadDropSystem.cs +++ b/Content.Server/_NF/Smuggling/DeadDropSystem.cs @@ -1,20 +1,26 @@ -using System.Text; +using System.Linq; +using System.Text; +using Content.Server._NF.GameTicking.Events; +using Content.Server._NF.SectorServices; using Content.Server._NF.Smuggling.Components; using Content.Server.Administration.Logs; using Content.Server.Radio.EntitySystems; using Content.Server.Shipyard.Systems; using Content.Server.Shuttles.Components; using Content.Server.Shuttles.Systems; -using Content.Shared.Coordinates; +using Content.Server.Station.Components; +using Content.Server.Station.Systems; +using Content.Shared._NF.CCVar; +using Content.Shared._NF.Smuggling.Prototypes; using Content.Shared.Database; using Content.Shared.Hands.Components; using Content.Shared.Hands.EntitySystems; using Content.Shared.Paper; -using Content.Shared.Radio; using Content.Shared.Shuttles.Components; using Content.Shared.Verbs; using Robust.Server.GameObjects; using Robust.Server.Maps; +using Robust.Shared.Configuration; using Robust.Shared.Map; using Robust.Shared.Prototypes; using Robust.Shared.Random; @@ -35,19 +41,393 @@ public sealed class DeadDropSystem : EntitySystem [Dependency] private readonly ShipyardSystem _shipyard = default!; [Dependency] private readonly ShuttleSystem _shuttle = default!; [Dependency] private readonly IGameTiming _timing = default!; - [Dependency] private readonly IMapManager _mapManager = default!; + [Dependency] private readonly SharedMapSystem _mapManager = default!; + [Dependency] private readonly StationSystem _station = default!; + [Dependency] private readonly SectorServiceSystem _sectorService = default!; + [Dependency] private readonly IConfigurationManager _cfg = default!; + private ISawmill _sawmill = default!; + private readonly Queue _drops = []; + + private const int MaxHintTimeErrorSeconds = 300; // +/- 5 minutes + private const int MinCluesPerHint = 1; + private const int MaxCluesPerHint = 2; + + // Temporary values, sane defaults, will be overwritten by CVARs. + private int _maxDeadDrops = 8; + private int _maxSimultaneousPods = 5; + private int _minDeadDropTimeout = 900; + private int _maxDeadDropTimeout = 5400; + private int _minDeadDropDistance = 6500; + private int _maxDeadDropDistance = 8000; + private int _minDeadDropHints = 3; + private int _maxDeadDropHints = 5; public override void Initialize() { base.Initialize(); - SubscribeLocalEvent(OnStartup); + + SubscribeLocalEvent(OnStartup); //TODO: compromise on shutdown if the stat SubscribeLocalEvent>(AddSearchVerb); + SubscribeLocalEvent(OnDeadDropUnanchored); + SubscribeLocalEvent(OnStationStartup); + SubscribeLocalEvent(OnStationShutdown); + SubscribeLocalEvent(OnStationsGenerated); + SubscribeLocalEvent(OnSectorDeadDropInit); + + Subs.CVar(_cfg, NFCCVars.SmugglingMaxSimultaneousPods, OnMaxSimultaneousPodsChanged, true); + Subs.CVar(_cfg, NFCCVars.SmugglingMaxDeadDrops, OnMaxDeadDropsChanged, true); // TODO: handle this better - will not be reflected until next round. + Subs.CVar(_cfg, NFCCVars.DeadDropMinTimeout, OnMinDeadDropTimeoutChanged, true); + Subs.CVar(_cfg, NFCCVars.DeadDropMaxTimeout, OnMaxDeadDropTimeoutChanged, true); + Subs.CVar(_cfg, NFCCVars.DeadDropMinDistance, OnMinDeadDropDistanceChanged, true); + Subs.CVar(_cfg, NFCCVars.DeadDropMaxDistance, OnMaxDeadDropDistanceChanged, true); + Subs.CVar(_cfg, NFCCVars.DeadDropMinHints, OnMinDeadDropHintsChanged, true); + Subs.CVar(_cfg, NFCCVars.DeadDropMaxHints, OnMaxDeadDropHintsChanged, true); + + _sawmill = Logger.GetSawmill("deaddrop"); + } + + private void OnSectorDeadDropInit(EntityUid _, SectorDeadDropComponent component, ComponentInit args) + { + component.ReportedEventsThisHour = new(TimeSpan.FromMinutes(60)); + } + + // CVAR setters + private void OnMaxSimultaneousPodsChanged(int newMax) + { + _maxSimultaneousPods = newMax; + } + + private void OnMinDeadDropTimeoutChanged(int newMax) + { + _minDeadDropTimeout = newMax; + // Change all existing dead drop timeouts + var minTime = _timing.CurTime + TimeSpan.FromSeconds(_minDeadDropTimeout); + var query = EntityManager.AllEntityQueryEnumerator(); + while (query.MoveNext(out var _, out var comp)) + { + comp.MinimumCoolDown = _minDeadDropTimeout; + if (comp.NextDrop < minTime) + comp.NextDrop = minTime; + } + } + + private void OnMaxDeadDropTimeoutChanged(int newMax) + { + _maxDeadDropTimeout = newMax; + // Change all existing dead drop timeouts + var maxTime = _timing.CurTime + TimeSpan.FromSeconds(_maxDeadDropTimeout); + var query = EntityManager.AllEntityQueryEnumerator(); + while (query.MoveNext(out var _, out var comp)) + { + comp.MaximumCoolDown = _maxDeadDropTimeout; + if (comp.NextDrop > maxTime) + comp.NextDrop = maxTime; + } + } + + private void OnMinDeadDropDistanceChanged(int newMax) + { + _minDeadDropDistance = newMax; + // Change all existing dead drop timeouts + var query = EntityManager.AllEntityQueryEnumerator(); + while (query.MoveNext(out var _, out var comp)) + { + comp.MinimumDistance = _minDeadDropDistance; + } + } + + private void OnMaxDeadDropDistanceChanged(int newMax) + { + _maxDeadDropDistance = newMax; + // Change all existing dead drop timeouts + var query = EntityManager.AllEntityQueryEnumerator(); + while (query.MoveNext(out var _, out var comp)) + { + comp.MaximumDistance = _maxDeadDropDistance; + } + } + + private void OnMinDeadDropHintsChanged(int newMin) + { + _minDeadDropHints = newMin; + } + + private void OnMaxDeadDropHintsChanged(int newMax) + { + _maxDeadDropHints = newMax; + } + + private void OnMaxDeadDropsChanged(int newMax) + { + _maxDeadDrops = newMax; + } + + // When a dead drop is unanchored, consider it compromised (we don't want people stealing the dead drop generators, these need to exist in public places) + private void OnDeadDropUnanchored(EntityUid uid, DeadDropComponent comp, AnchorStateChangedEvent args) + { + if (args.Anchored) + return; + + CompromiseDeadDrop(uid, comp); + } + + // There is some redundancy here - this should ideally run once over all the stations once worldgen is complete + // Then once on any new stations if/when they're created. + private void OnStationStartup(EntityUid stationUid, StationDeadDropComponent component, ComponentStartup _) + { + if (TryComp(_sectorService.GetServiceEntity(), out var deadDrop)) + { + deadDrop.DeadDropStationNames[stationUid] = MetaData(stationUid).EntityName; + } + } + + // There is some redundancy here - this should ideally run once over all the stations once worldgen is complete + // Then once on any new stations if/when they're created. + private void OnStationShutdown(EntityUid stationUid, StationDeadDropComponent component, ComponentShutdown _) + { + if (TryComp(_sectorService.GetServiceEntity(), out var deadDrop)) + { + deadDrop.DeadDropStationNames.Remove(stationUid); + } + } + + public void CompromiseDeadDrop(EntityUid uid, DeadDropComponent _) + { + //Get our station. + var station = _station.GetOwningStation(uid); + //Remove the dead drop. + RemComp(uid); + //Find a new potential dead drop to spawn. + var deadDropQuery = EntityManager.EntityQueryEnumerator(); + List<(EntityUid ent, PotentialDeadDropComponent comp)> potentialDeadDrops = new(); + while (deadDropQuery.MoveNext(out var ent, out var potentialDeadDrop)) + { + // This potential dead drop is not on our station + if (_station.GetOwningStation(ent) != station) + continue; + + // This item already has an active dead drop, skip it + if (HasComp(ent)) + continue; + + potentialDeadDrops.Add((ent, potentialDeadDrop)); + } + + // We have a potential dead drop, + if (potentialDeadDrops.Count > 0) + { + var item = _random.Pick(potentialDeadDrops); + AddDeadDrop(item.ent); + _sawmill.Debug($"Dead drop at {uid} compromised, new drop at {item.ent}!"); + } + else + { + _sawmill.Warning($"Dead drop at {uid} compromised, no new drop assigned!"); + } + } + + // Ensures that a given entity is a valid dead drop with the current global settings. + public void AddDeadDrop(EntityUid entity) + { + var deadDrop = EnsureComp(entity); + deadDrop.MinimumCoolDown = _minDeadDropTimeout; + deadDrop.MaximumCoolDown = _maxDeadDropTimeout; + deadDrop.MinimumDistance = _minDeadDropDistance; + deadDrop.MaximumDistance = _maxDeadDropDistance; + } + + private void OnStationsGenerated(StationsGeneratedEvent args) + { + _sawmill.Debug("Generating dead drops!"); + // Distribute total number of dead drops to assign between each station. + var remainingDeadDrops = _maxDeadDrops; + + Dictionary assignedDeadDrops = new(); + var stationDropQuery = AllEntityQuery(); + while (stationDropQuery.MoveNext(out var station, out var stationDeadDrop)) + { + var deadDropCount = int.Min(remainingDeadDrops, _random.Next(0, stationDeadDrop.MaxDeadDrops + 1)); + assignedDeadDrops[station] = (deadDropCount, stationDeadDrop.MaxDeadDrops); + remainingDeadDrops -= deadDropCount; + } + + // We have remaining dead drops, assign them to whichever stations have remaining space (in a random order) + if (remainingDeadDrops > 0) + { + var stationList = assignedDeadDrops.Keys.ToList(); + _random.Shuffle(stationList); + foreach (var station in stationList) + { + var dropTuple = assignedDeadDrops[station]; + + // Insert as many dead drops here as we can. + var remainingSpace = dropTuple.max - dropTuple.assigned; + remainingSpace = int.Min(remainingSpace, remainingDeadDrops); + dropTuple.assigned += remainingSpace; + assignedDeadDrops[station] = dropTuple; + + // Adjust global counts. + remainingDeadDrops -= remainingSpace; + + if (remainingDeadDrops <= 0) + break; + } + } + + _sawmill.Debug("Drop assignments:"); + foreach (var (station, dropSet) in assignedDeadDrops) + { + _sawmill.Debug($" {MetaData(station).EntityName} will place {dropSet.assigned} dead drops."); + } + + // For each station, distribute its assigned dead drops to potential dead drop components available on their grids. + Dictionary> potentialDropEntitiesPerStation = new(); + var potentialDropQuery = AllEntityQuery(); + while (potentialDropQuery.MoveNext(out var ent, out var _)) + { + var station = _station.GetOwningStation(ent); + if (station is null) + { + continue; + } + + // All dead drops must be anchored. + if (!TryComp(ent, out TransformComponent? xform) || !xform.Anchored) + continue; + + var stationUid = station.Value; + if (assignedDeadDrops.ContainsKey(stationUid)) + { + if (!potentialDropEntitiesPerStation.ContainsKey(stationUid)) + potentialDropEntitiesPerStation[stationUid] = new List(); + + potentialDropEntitiesPerStation[stationUid].Add(ent); + } + } + + List<(EntityUid, EntityUid)> deadDropStationTuples = new(); + StringBuilder dropList = new(); + foreach (var (station, potentialDropList) in potentialDropEntitiesPerStation) + { + if (!assignedDeadDrops.TryGetValue(station, out var stationDrops)) + { + continue; + } + + List drops = new(); + _random.Shuffle(potentialDropList); + for (int i = 0; i < potentialDropList.Count && i < stationDrops.assigned; i++) + { + var dropUid = potentialDropList[i]; + AddDeadDrop(dropUid); + deadDropStationTuples.Add((station, dropUid)); + drops.Add(dropUid); + + if (dropList.Length <= 0) + dropList.Append(dropUid); + else + dropList.Append($", {dropUid}"); + } + if (dropList.Length > 0) + { + _sawmill.Debug($"{MetaData(station).EntityName} dead drops assigned: {dropList}"); + dropList.Clear(); + } + } + + // From all existing hints, select a set few to be actual hints, replace the text in the remainder with random hints from a set. + var hintQuery = AllEntityQuery(); + + List allHints = new(); + + while (hintQuery.MoveNext(out var ent, out var _)) + { + allHints.Add(ent); + } + + _random.Shuffle(allHints); + + // Generate a random number of hints. + var numHints = _random.Next(_minDeadDropHints, _maxDeadDropHints + 1); + + for (int i = 0; i < allHints.Count && i < numHints; i++) + { + var ent = allHints[i]; + + var hintCount = _random.Next(MinCluesPerHint, MaxCluesPerHint + 1); + _random.Shuffle(deadDropStationTuples); + + var hintLines = new StringBuilder(); + var hints = 0; + // Format the hint lines we need from a random set of dead drops. + for (var j = 0; j < deadDropStationTuples.Count && hints < hintCount; j++) + { + var hintTuple = deadDropStationTuples[j]; + string objectHintString; + if (TryComp(hintTuple.Item2, out var potentialDeadDrop)) + objectHintString = Loc.GetString(potentialDeadDrop.HintText); + else + objectHintString = Loc.GetString("dead-drop-hint-generic"); + + string stationHintString; + if (TryComp(hintTuple.Item1, out MetaDataComponent? stationMetadata)) + stationHintString = stationMetadata.EntityName; + else + stationHintString = Loc.GetString("dead-drop-station-hint-generic"); + + string timeString; + if (TryComp(hintTuple.Item2, out var deadDrop) && deadDrop.NextDrop != null) + { + var dropTimeWithError = deadDrop.NextDrop.Value + TimeSpan.FromSeconds(_random.Next(-MaxHintTimeErrorSeconds, MaxHintTimeErrorSeconds)); + timeString = Loc.GetString("dead-drop-time-known", ("time", dropTimeWithError.ToString("hh\\:mm") + ":00")); + } + else + { + timeString = Loc.GetString("dead-drop-time-unknown"); + } + + hintLines.AppendLine(Loc.GetString("dead-drop-hint-line", ("object", objectHintString), ("poi", stationHintString), ("time", timeString))); + hints++; + } + var hintText = new StringBuilder(); + hintText.AppendLine(Loc.GetString("dead-drop-hint-note", ("drops", hintLines))); + + // Select some number of dead drops to hint + if (TryComp(ent, out var paper)) + _paper.SetContent((ent, paper), hintText.ToString()); + + // Hint generated, destroy component + RemComp(ent); + _sawmill.Debug($"Dead drop hint generated at {ent}."); + } + + if (TryComp(_sectorService.GetServiceEntity(), out var sectorDeadDrop) && + _prototypeManager.TryIndex(sectorDeadDrop.FakeDeadDropHints, out var deadDropHints)) + { + var hintCount = deadDropHints.Values.Count; + for (int i = numHints; i < allHints.Count; i++) + { + var ent = allHints[i]; + + // Randomly assign a string from our list of fake hint strings. + var index = _random.Next(0, hintCount); + var msg = Loc.GetString(deadDropHints.Values[index]); + + // Select some number of dead drops to hint + if (TryComp(ent, out var paper)) + _paper.SetContent((ent, paper), msg); + + // Hint generated, destroy component + RemComp(ent); + } + } } private void OnStartup(EntityUid paintingUid, DeadDropComponent component, ComponentStartup _) { //set up the timing of the first activation - component.NextDrop = _timing.CurTime + TimeSpan.FromSeconds(_random.Next(component.MinimumCoolDown, component.MaximumCoolDown)); + if (component.NextDrop == null) + component.NextDrop = _timing.CurTime + TimeSpan.FromSeconds(_random.Next(component.MinimumCoolDown, component.MaximumCoolDown)); } private void AddSearchVerb(EntityUid uid, DeadDropComponent component, GetVerbsEvent args) @@ -55,6 +435,9 @@ private void AddSearchVerb(EntityUid uid, DeadDropComponent component, GetVerbsE if (!args.CanInteract || !args.CanAccess || args.Hands == null || _timing.CurTime < component.NextDrop) return; + var xform = Transform(uid); + var targetCoordinates = xform.Coordinates; + //here we build our dynamic verb. Using the object's sprite for now to make it more dynamic for the moment. InteractionVerb searchVerb = new() { @@ -91,24 +474,55 @@ private void SendDeadDrop(EntityUid uid, DeadDropComponent component, EntityUid _shuttle.SetIFFColor(gridUids[0], component.Color); _shuttle.AddIFFFlag(gridUids[0], IFFFlags.HideLabel); - //this is where we set up all the information that FTL is going to need, including a new null entitiy as a destination target because FTL needs it for reasons? + //this is where we set up all the information that FTL is going to need, including a new null entity as a destination target because FTL needs it for reasons? //dont ask me im just fulfilling FTL requirements. var dropLocation = _random.NextVector2(component.MinimumDistance, component.MaximumDistance); var mapId = Transform(user).MapID; - var mapUid = _mapManager.GetMapEntityId(mapId); + //tries to get the map uid, if it fails, it will return which I would assume will make the component try again. + if (!_mapManager.TryGetMap(mapId, out var mapUid)) + { + return; + } + + // Get sector info (with sane defaults if it doesn't exist) + int maxSimultaneousPods = 5; + int deadDropsThisHour = 0; + if (TryComp(_sectorService.GetServiceEntity(), out var sectorDeadDrop)) + { + maxSimultaneousPods = _maxSimultaneousPods; + if (sectorDeadDrop.ReportedEventsThisHour != null) + { + deadDropsThisHour = sectorDeadDrop.ReportedEventsThisHour.Count(); + sectorDeadDrop.ReportedEventsThisHour.AddEvent(); + } + } + + //this will spawn in the latest ship, and delete the oldest one available if the amount of ships exceeds 5. if (TryComp(gridUids[0], out var shuttle)) { - _shuttle.FTLToCoordinates(gridUids[0], shuttle, new EntityCoordinates(mapUid, dropLocation), 0f, 0f, 35f); + _shuttle.FTLToCoordinates(gridUids[0], shuttle, new EntityCoordinates(mapUid.Value, dropLocation), 0f, 0f, 35f); + _drops.Enqueue(gridUids[0]); + + if (_drops.Count > maxSimultaneousPods) + { + //removes the first element of the queue + var entityToRemove = _drops.Dequeue(); + _adminLogger.Add(LogType.Action, LogImpact.Medium, $"{entityToRemove} queued for deletion"); + EntityManager.QueueDeleteEntity(entityToRemove); + } } //tattle on the smuggler here, but obfuscate it a bit if possible to just the grid it was summoned from. - var channel = _prototypeManager.Index("Nfsd"); var sender = Transform(user).GridUid ?? uid; - _radio.SendRadioMessage(sender, Loc.GetString("deaddrop-security-report"), channel, uid); _adminLogger.Add(LogType.Action, LogImpact.Medium, $"{ToPrettyString(user)} sent a dead drop to {dropLocation.ToString()} from {ToPrettyString(uid)} at {Transform(uid).Coordinates.ToString()}"); + //reset the timer (needed for the text) + component.NextDrop = _timing.CurTime + TimeSpan.FromSeconds(_random.Next(component.MinimumCoolDown, component.MaximumCoolDown)); + + var hintNextDrop = component.NextDrop.Value + TimeSpan.FromSeconds(_random.Next(-MaxHintTimeErrorSeconds, MaxHintTimeErrorSeconds + 1)); + // here we are just building a string for the hint paper so that it looks pretty and RP-like on the paper itself. var dropHint = new StringBuilder(); dropHint.AppendLine(Loc.GetString("deaddrop-hint-pretext")); @@ -116,6 +530,8 @@ private void SendDeadDrop(EntityUid uid, DeadDropComponent component, EntityUid dropHint.AppendLine(dropLocation.ToString()); dropHint.AppendLine(); dropHint.AppendLine(Loc.GetString("deaddrop-hint-posttext")); + dropHint.AppendLine(); + dropHint.AppendLine(Loc.GetString("deaddrop-hint-next-drop", ("time", hintNextDrop.ToString("hh\\:mm") + ":00"))); var paper = EntityManager.SpawnEntity(component.HintPaper, Transform(uid).Coordinates); @@ -127,7 +543,90 @@ private void SendDeadDrop(EntityUid uid, DeadDropComponent component, EntityUid _meta.SetEntityDescription(paper, Loc.GetString("deaddrop-hint-desc")); _hands.PickupOrDrop(user, paper, handsComp: hands); - //reset the timer - component.NextDrop = _timing.CurTime + TimeSpan.FromSeconds(_random.Next(component.MinimumCoolDown, component.MaximumCoolDown)); + component.DeadDropCalled = true; + //logic of posters ends here and logic of radio signals begins here + + var deadDropQuery = EntityManager.EntityQueryEnumerator(); + while (deadDropQuery.MoveNext(out var reportStation, out var reportComp)) + { + if (!TryComp(reportStation, out var stationData)) + continue; // Not a station? + + var gridUid = _station.GetLargestGrid(stationData); + if (gridUid == null) + continue; // Nobody to send our message. + + if (!_prototypeManager.TryIndex(reportComp.MessageSet, out var messageSets)) + continue; + + foreach (var messageSet in messageSets.MessageSets) + { + float delayMinutes; + if (messageSet.MinDelay >= messageSet.MaxDelay) + delayMinutes = messageSet.MinDelay; + else + delayMinutes = _random.NextFloat(messageSet.MinDelay, messageSet.MaxDelay); + + if (!_random.Prob(messageSet.Probability)) + continue; + + string messageLoc = ""; + SmugglingReportMessageType messageType = SmugglingReportMessageType.General; + float messageError = 0.0f; + foreach (var message in messageSet.Messages) + { + if (deadDropsThisHour < message.HourlyThreshold) + { + messageLoc = message.Message; + messageType = message.Type; + messageError = message.MaxPodLocationError; + break; + } + } + + if (string.IsNullOrEmpty(messageLoc)) + continue; + + string output; + switch (messageType) + { + case SmugglingReportMessageType.General: + default: + output = Loc.GetString(messageLoc); + break; + case SmugglingReportMessageType.DeadDropStation: + output = Loc.GetString(messageLoc, ("location", MetaData(sender).EntityName)); + break; + case SmugglingReportMessageType.DeadDropStationWithRandomAlt: + if (sectorDeadDrop is not null) + { + string[] names = [MetaData(sender).EntityName, _random.Pick(sectorDeadDrop.DeadDropStationNames.Values)]; + _random.Shuffle(names); + output = Loc.GetString(messageLoc, ("location1", names[0]), ("location2", names[1])); + } + else + { + output = Loc.GetString(messageLoc, ("location1", MetaData(sender).EntityName)); // Looks strange, but still has a proper value. + } + break; + case SmugglingReportMessageType.PodLocation: + var error = _random.NextVector2(messageError); + output = Loc.GetString(messageLoc, ("x", $"{dropLocation.X + error.X:F0}"), ("y", $"{dropLocation.Y + error.Y:F0}")); + break; + } + + if (delayMinutes > 0) + { + Timer.Spawn(TimeSpan.FromMinutes(delayMinutes), () => + { + _radio.SendRadioMessage(gridUid.Value, output, messageSets.Channel, uid); + }); + } + else + { + _radio.SendRadioMessage(gridUid.Value, output, messageSets.Channel, uid); + } + } + } } -} +} \ No newline at end of file diff --git a/Content.Server/_NF/Smuggling/WindowedCounter.cs b/Content.Server/_NF/Smuggling/WindowedCounter.cs new file mode 100644 index 00000000000..61df4922f1f --- /dev/null +++ b/Content.Server/_NF/Smuggling/WindowedCounter.cs @@ -0,0 +1,54 @@ +using Robust.Shared.Timing; + +namespace Content.Server._NF.Smuggling; + +// +// A counter to keep track of the number of events that happened over a shifting window of fixed length (e.g. "an hour ago"). +// +public sealed class WindowedCounter +{ + private readonly IGameTiming _timing; + private List _times; + private TimeSpan _window; + + public WindowedCounter(TimeSpan window) + { + _timing = IoCManager.Resolve(); + _times = new(); + _window = window; + } + + public void Clear() + { + _times.Clear(); + } + + public void SetWindow(TimeSpan newWindow) + { + _window = newWindow; + RemoveStaleEvents(); + } + + public void AddEvent() + { + _times.Add(_timing.CurTime); + RemoveStaleEvents(); + } + + public int Count() + { + RemoveStaleEvents(); + return _times.Count; + } + + void RemoveStaleEvents() + { + while (_times.Count > 0) + { + if (_times[0] < _timing.CurTime - _window) + _times.RemoveAt(0); + else + break; + } + } +} \ No newline at end of file diff --git a/Content.Server/StationEvents/Components/BluespaceCargoRuleComponent.cs b/Content.Server/_NF/StationEvents/Components/BluespaceCargoRuleComponent.cs similarity index 100% rename from Content.Server/StationEvents/Components/BluespaceCargoRuleComponent.cs rename to Content.Server/_NF/StationEvents/Components/BluespaceCargoRuleComponent.cs diff --git a/Content.Server/StationEvents/Components/BluespaceErrorRuleComponent.cs b/Content.Server/_NF/StationEvents/Components/BluespaceErrorRuleComponent.cs similarity index 100% rename from Content.Server/StationEvents/Components/BluespaceErrorRuleComponent.cs rename to Content.Server/_NF/StationEvents/Components/BluespaceErrorRuleComponent.cs diff --git a/Content.Server/_NF/StationEvents/Components/RandomFaxRuleComponent.cs b/Content.Server/_NF/StationEvents/Components/RandomFaxRuleComponent.cs new file mode 100644 index 00000000000..2cb084e6413 --- /dev/null +++ b/Content.Server/_NF/StationEvents/Components/RandomFaxRuleComponent.cs @@ -0,0 +1,104 @@ +using Content.Server.StationEvents.Events; +using Content.Shared.Fax.Components; +using Content.Shared.Paper; +using Robust.Shared.Prototypes; + +namespace Content.Server.StationEvents.Components; + +[RegisterComponent, Access(typeof(RandomFaxRule))] +public sealed partial class RandomFaxRuleComponent : Component +{ + /// + /// FaxPrintout fields. All strings apart from PrototypeId will be localized + /// + [DataField(required: true)] + public string Name { get; private set; } = default!; + + [DataField] + public string? Label { get; private set; } + + [DataField(required: true)] + public string Content { get; private set; } = default!; + + [DataField(required: true)] + public EntProtoId PrototypeId { get; private set; } = default!; + + [DataField] + public string? StampState { get; private set; } + + [DataField] + public List? StampedBy { get; private set; } = new(); + + [DataField] + public bool Locked { get; private set; } + + /// + /// The localized string + /// + [DataField] + public string? FromAddress; + + // TODO: run arbitrary functions + + /// + /// All the valid IWireActions currently in this layout. + /// + [DataField] + public List? PreFaxActions { get; private set; } + + /// + /// All the valid IWireActions currently in this layout. + /// + [DataField] + public List? PerRecipientActions { get; private set; } + + /// + /// Minimum faxes to send + /// + [DataField] + public int MinFaxes { get; private set; } = 1; + + /// + /// Maximum faxes to send + /// + [DataField] + public int MaxFaxes { get; private set; } = 1; +} + +// TODO: relocate these definitions. +public interface IPreFaxAction +{ + /// + /// Initializes the action. Intended to setup resources, but the action should not be stateful. + /// + public void Initialize(); + + /// + /// Formats a fax printout with general information (target station) + /// + public void Format(EntityUid station, ref EditableFaxPrintout printout, ref string? fromAddress); +} + +public interface IRecipientFaxAction +{ + /// + /// Initializes the action. Intended to setup resources, but the action should not be stateful. + /// + public void Initialize(); + + /// + /// Formats a fax printout with recipient-specific information (target station, fax machine entity) + /// + public void Format(EntityUid station, EntityUid fax, FaxMachineComponent faxComponent, ref EditableFaxPrintout printout, ref string? fromAddress); +} + +public sealed partial class EditableFaxPrintout +{ + public string Name = default!; + public string? Label; + public string Content = default!; + public string PrototypeId = default!; + public string? StampState; + public List StampedBy = new(); + public bool Locked; +} \ No newline at end of file diff --git a/Content.Server/_NF/StationEvents/Events/Actions/GetRandomDeadDropAction.cs b/Content.Server/_NF/StationEvents/Events/Actions/GetRandomDeadDropAction.cs new file mode 100644 index 00000000000..f905523271a --- /dev/null +++ b/Content.Server/_NF/StationEvents/Events/Actions/GetRandomDeadDropAction.cs @@ -0,0 +1,80 @@ +using System.Text; +using Content.Server._NF.Smuggling.Components; +using Content.Server.Station.Systems; +using Content.Server.StationEvents.Components; +using Robust.Shared.Random; + +namespace Content.Server.StationEvents.Events; + +/// +/// An action that gets a set number of dead drops from a +/// +[DataDefinition] +public sealed partial class GetRandomDeadDropAction : IPreFaxAction +{ + private IEntityManager _entityManager = default!; + private IRobustRandom _random = default!; + private StationSystem _station = default!; + + const int MaxHintTimeErrorSeconds = 300; + + public void Initialize() + { + _entityManager = IoCManager.Resolve(); + _random = IoCManager.Resolve(); + + _station = _entityManager.EntitySysManager.GetEntitySystem(); + } + + public void Format(EntityUid station, ref EditableFaxPrintout printout, ref string? fromAddress) + { + List<(EntityUid station, EntityUid ent)> entityList = new(); + var hintQuery = _entityManager.AllEntityQueryEnumerator(); + while (hintQuery.MoveNext(out var ent, out var _)) + { + var stationUid = _station.GetOwningStation(ent); + if (stationUid != null) + entityList.Add((stationUid.Value, ent)); + } + + _random.Shuffle(entityList); + + int hintCount = _random.Next(2, 4); + + var hintLines = new StringBuilder(); + var hints = 0; + for (var i = 0; i < entityList.Count && hints < hintCount; i++) + { + var hintTuple = entityList[i]; + string objectHintString; + if (_entityManager.TryGetComponent(hintTuple.Item2, out var potentialDeadDrop)) + objectHintString = Loc.GetString(potentialDeadDrop.HintText); + else + objectHintString = Loc.GetString("dead-drop-hint-generic"); + + string stationHintString; + if (_entityManager.TryGetComponent(hintTuple.Item1, out MetaDataComponent? stationMetadata)) + stationHintString = stationMetadata.EntityName; + else + stationHintString = Loc.GetString("dead-drop-station-hint-generic"); + + string timeString; + if (_entityManager.TryGetComponent(hintTuple.Item2, out var deadDrop) && deadDrop.NextDrop != null) + { + var dropTimeWithError = deadDrop.NextDrop.Value + TimeSpan.FromSeconds(_random.Next(-MaxHintTimeErrorSeconds, MaxHintTimeErrorSeconds)); + timeString = Loc.GetString("dead-drop-time-known", ("time", dropTimeWithError.ToString("hh\\:mm") + ":00")); + } + else + { + timeString = Loc.GetString("dead-drop-time-unknown"); + } + + hintLines.AppendLine(Loc.GetString("dead-drop-hint-line", ("object", objectHintString), ("poi", stationHintString), ("time", timeString))); + hints++; + } + var hintText = new StringBuilder(); + hintText.AppendLine(Loc.GetString("dead-drop-hint-note", ("drops", hintLines))); + + printout.Content = hintText.ToString(); + } +} diff --git a/Content.Server/StationEvents/Events/BluespaceCargoRule.cs b/Content.Server/_NF/StationEvents/Events/BluespaceCargoRule.cs similarity index 100% rename from Content.Server/StationEvents/Events/BluespaceCargoRule.cs rename to Content.Server/_NF/StationEvents/Events/BluespaceCargoRule.cs diff --git a/Content.Server/StationEvents/Events/BluespaceErrorRule.cs b/Content.Server/_NF/StationEvents/Events/BluespaceErrorRule.cs similarity index 100% rename from Content.Server/StationEvents/Events/BluespaceErrorRule.cs rename to Content.Server/_NF/StationEvents/Events/BluespaceErrorRule.cs diff --git a/Content.Server/_NF/StationEvents/Events/RandomFaxRule.cs b/Content.Server/_NF/StationEvents/Events/RandomFaxRule.cs new file mode 100644 index 00000000000..c3ebff3aa6a --- /dev/null +++ b/Content.Server/_NF/StationEvents/Events/RandomFaxRule.cs @@ -0,0 +1,124 @@ +using Content.Server.Station.Components; +using Content.Server.StationEvents.Components; +using Content.Shared.GameTicking.Components; +using Content.Shared.Fax.Components; +using Content.Server.Fax; +using Content.Server.Station.Systems; +using Robust.Shared.Random; + +namespace Content.Server.StationEvents.Events; + +public sealed class RandomFaxRule : StationEventSystem +{ + [Dependency] private readonly IEntityManager _entMan = default!; + [Dependency] private readonly FaxSystem _faxSystem = default!; + [Dependency] private readonly StationSystem _stationSystem = default!; + [Dependency] private readonly IRobustRandom _random = default!; + + private const int MaxRetries = 10; + protected override void Added(EntityUid uid, RandomFaxRuleComponent component, GameRuleComponent gameRule, GameRuleAddedEvent args) + { + base.Added(uid, component, gameRule, args); + + if (component.PreFaxActions != null) + { + foreach (var action in component.PreFaxActions) + { + action.Initialize(); + } + } + + if (component.PerRecipientActions != null) + { + foreach (var action in component.PerRecipientActions) + { + action.Initialize(); + } + } + } + + protected override void Started(EntityUid uid, RandomFaxRuleComponent component, GameRuleComponent gameRule, GameRuleStartedEvent args) + { + base.Started(uid, component, gameRule, args); + + var numFaxes = _random.Next(component.MinFaxes, component.MaxFaxes); + + List stations = new(); + int retries = 0; + int faxesSent = 0; + while (faxesSent < numFaxes && retries < MaxRetries) + { + if (!TryGetRandomStation(out var chosenStation, HasComp)) + return; + + if (stations.Contains(chosenStation.Value)) + { + retries++; + continue; + } + + if (!TryComp(chosenStation, out var stationData)) + { + retries++; + continue; + } + + var grid = StationSystem.GetLargestGrid(stationData); + + if (grid is null) + { + retries++; + continue; + } + + EditableFaxPrintout localPrintout = new() + { + Content = Loc.GetString(component.Content), + Name = Loc.GetString(component.Name), + Label = component.Label != null ? Loc.GetString(component.Label) : null, + PrototypeId = component.PrototypeId, + StampState = component.StampState, + StampedBy = component.StampedBy ?? new(), + Locked = component.Locked + }; + string? localAddress = component.FromAddress; + if (component.PreFaxActions != null) + { + foreach (var action in component.PreFaxActions) + { + action.Format(uid, ref localPrintout, ref localAddress); + } + } + + var faxQuery = _entMan.EntityQueryEnumerator(); + while (faxQuery.MoveNext(out var faxUid, out var faxComp)) + { + if (_stationSystem.GetOwningStation(faxUid) != chosenStation) + continue; + + EditableFaxPrintout recipientPrintout = localPrintout; + string? recipientAddress = localAddress; + if (component.PerRecipientActions != null) + { + foreach (var action in component.PerRecipientActions) + { + action.Format(uid, faxUid, faxComp, ref recipientPrintout, ref recipientAddress); + } + } + + FaxPrintout printout = new( + content: recipientPrintout.Content, + name: recipientPrintout.Name, + label: recipientPrintout.Label, + prototypeId: recipientPrintout.PrototypeId, + stampState: recipientPrintout.StampState, + stampedBy: recipientPrintout.StampedBy, + locked: recipientPrintout.Locked + ); + _faxSystem.Receive(faxUid, printout, recipientAddress, faxComp); + break; + } + faxesSent++; + } + } +} diff --git a/Content.Shared/Shipyard/Components/DisableShipyardSaleComponent.cs b/Content.Shared/Shipyard/Components/DisableShipyardSaleComponent.cs new file mode 100644 index 00000000000..ff6f89d6dbf --- /dev/null +++ b/Content.Shared/Shipyard/Components/DisableShipyardSaleComponent.cs @@ -0,0 +1,22 @@ +using Content.Shared.Shipyard; + +namespace Content.Server._NF.Smuggling.Components; + +/// +/// A component that disables the sale of the ship it's on from a shipyard console. +/// +[RegisterComponent] +public sealed partial class DisableShipyardSaleComponent : Component +{ + /// + /// The message to print off when a shipyard sale is disabled. + /// + [DataField(required: true)] + public string Reason; + + /// + /// The console types that should allow selling this object. + /// + [DataField] + public List AllowedShipyardTypes = new(); +} diff --git a/Content.Shared/_NF/CCVar/NFCCVars.cs b/Content.Shared/_NF/CCVar/NFCCVars.cs index 5201d26d8f4..f8e4a397e67 100644 --- a/Content.Shared/_NF/CCVar/NFCCVars.cs +++ b/Content.Shared/_NF/CCVar/NFCCVars.cs @@ -109,4 +109,26 @@ public sealed class NFCCVars /// public static readonly CVarDef SalvageExpeditionMaxActive = CVarDef.Create("nf14.salvage.expedition_max_active", 15, CVar.REPLICATED); + + /* + * Smuggling + */ + public static readonly CVarDef SmugglingMaxSimultaneousPods = + CVarDef.Create("nf14.smuggling.max_simultaneous_pods", 5, CVar.REPLICATED); + public static readonly CVarDef SmugglingMaxDeadDrops = + CVarDef.Create("nf14.smuggling.max_sector_dead_drops", 10, CVar.REPLICATED); + public static readonly CVarDef SmugglingMinFucPayout = + CVarDef.Create("nf14.smuggling.min_fuc_payout", 2, CVar.REPLICATED); + public static readonly CVarDef DeadDropMinTimeout = + CVarDef.Create("nf14.smuggling.min_timeout", 900, CVar.REPLICATED); + public static readonly CVarDef DeadDropMaxTimeout = + CVarDef.Create("nf14.smuggling.max_timeout", 5400, CVar.REPLICATED); + public static readonly CVarDef DeadDropMinDistance = + CVarDef.Create("nf14.smuggling.min_distance", 6500, CVar.REPLICATED); + public static readonly CVarDef DeadDropMaxDistance = + CVarDef.Create("nf14.smuggling.max_distance", 8000, CVar.REPLICATED); + public static readonly CVarDef DeadDropMinHints = + CVarDef.Create("nf14.smuggling.min_hints", 3, CVar.REPLICATED); + public static readonly CVarDef DeadDropMaxHints = + CVarDef.Create("nf14.smuggling.max_hints", 4, CVar.REPLICATED); } diff --git a/Content.Shared/_NF/Smuggling/Prototypes/SmugglingReportMessageSetPrototype.cs b/Content.Shared/_NF/Smuggling/Prototypes/SmugglingReportMessageSetPrototype.cs new file mode 100644 index 00000000000..78a97fa727a --- /dev/null +++ b/Content.Shared/_NF/Smuggling/Prototypes/SmugglingReportMessageSetPrototype.cs @@ -0,0 +1,60 @@ +using Content.Shared.Radio; +using Robust.Shared.Prototypes; + +namespace Content.Shared._NF.Smuggling.Prototypes; + +// Data types for the sending of smuggling messages over radio. +[Prototype("smugglingReportMessageSet")] +public sealed class SmugglingReportMessageSetPrototype : IPrototype +{ + [IdDataField] + public string ID { get; private set; } = default!; + // The radio channel to send this message off to. + [DataField(required: true)] + public ProtoId Channel; + // The sets of messages to be sent off on this radio channel when a smuggling dead drop is taken. + [DataField(required: true)] + public List MessageSets { get; private set; } = new(); +} + +[DataDefinition] +public sealed partial class SmugglingReportMessageSet +{ + // The minimum delay, in minutes, that this message will be sent at. + [DataField] + public float MinDelay { get; private set; } = 0.0f; + // The maximum delay, in minutes, that this message will be sent at. + [DataField] + public float MaxDelay { get; private set; } = 0.0f; + // The list of messages to be sent. The first message in the list whose threshold is met will be sent off. + [DataField(required: true)] + public List Messages { get; private set; } = new(); + // The probability of sending a message from this set. + [DataField("prob")] + public float Probability { get; private set; } = 1.0f; +} + +[DataDefinition] +public sealed partial class SmugglingReportMessage +{ + // The localization string of the message to be printed. + [DataField(required: true)] + public string Message { get; private set; } = default!; + // If the number of smuggling events this hour is lower than this value, this message will be printed off. + [DataField] + public int HourlyThreshold { get; private set; } = int.MaxValue; + // The type of message to be printed off. Arguments should correspond to + [DataField] + public SmugglingReportMessageType Type { get; private set; } = SmugglingReportMessageType.General; + // The maximum error for the pod location in meters. Printed locations will be a random location within X meters of the drop pod. + [DataField("maxError")] + public float MaxPodLocationError { get; private set; } = 0.0f; +} + +public enum SmugglingReportMessageType : byte +{ + General, // No location. + DeadDropStation, // Gives one station, the location of the dead drop. One arg: $location + DeadDropStationWithRandomAlt, // Gives two stations, a random one of which had the dead drop. Two args: $location1, $location2 + PodLocation, // Gives the location of the drop pod. Two args: $x, $y +} diff --git a/Resources/Locale/en-US/_NF/forensics/forensic-scanner-slot-component.ftl b/Resources/Locale/en-US/_NF/forensics/forensic-scanner-slot-component.ftl new file mode 100644 index 00000000000..1a153efea0f --- /dev/null +++ b/Resources/Locale/en-US/_NF/forensics/forensic-scanner-slot-component.ftl @@ -0,0 +1 @@ +forensic-scanner-slot-component-slot-name-cartridge = Cartridge \ No newline at end of file diff --git a/Resources/Locale/en-US/_NF/forensics/forensics.ftl b/Resources/Locale/en-US/_NF/forensics/forensics.ftl new file mode 100644 index 00000000000..5144015b1bb --- /dev/null +++ b/Resources/Locale/en-US/_NF/forensics/forensics.ftl @@ -0,0 +1,6 @@ +forensic-reward-dead-drop-unused = Unused syndicate dead drop found! +forensic-reward-dead-drop-used-gone = Evidence of syndicate dead drop use found! +forensic-reward-dead-drop-used-present = Used syndicate dead drop found! +forensic-reward-pod = Syndicate drop pod found! +forensic-reward-amount-speso-only = Crediting {$spesos} for the investigative work! +forensic-reward-amount = Crediting {$spesos} and {$fuc} for the investigative work! \ No newline at end of file diff --git a/Resources/Locale/en-US/_NF/guidebook/guides.ftl b/Resources/Locale/en-US/_NF/guidebook/guides.ftl index f318f97e004..a6c7b0958dd 100644 --- a/Resources/Locale/en-US/_NF/guidebook/guides.ftl +++ b/Resources/Locale/en-US/_NF/guidebook/guides.ftl @@ -7,6 +7,9 @@ guide-entry-expeditions = Expeditions guide-entry-shipyard = Shipyard guide-entry-frontier-rules = Server Rules +# Security entries +guide-entry-nfsd-smuggling = Smuggling + # Expedition faction entries guide-entry-expedition-aberrant-flesh = Aberrant Flesh guide-entry-expedition-argocytes = Argocytes diff --git a/Resources/Locale/en-US/_NF/shipyard/shipyard-console-component.ftl b/Resources/Locale/en-US/_NF/shipyard/shipyard-console-component.ftl index 2c307fd533f..f2d0a8bd96d 100644 --- a/Resources/Locale/en-US/_NF/shipyard/shipyard-console-component.ftl +++ b/Resources/Locale/en-US/_NF/shipyard/shipyard-console-component.ftl @@ -23,6 +23,10 @@ shipyard-console-appraisal-label = Shuttle Estimated Value:{" "} shipyard-console-no-voucher-redemptions = All voucher redemptions have been used. shipyard-console-invalid-voucher-type = This voucher cannot be used at this console. +shipyard-console-contraband-onboard = Smuggled contraband detected onboard. +shipyard-console-station-resources = Vital station resources detected onboard. +shipyard-console-dangerous-materials = Dangerous materials detected onboard. + shipyard-console-menu-size-label = Size:{" "} shipyard-console-menu-class-label = Class:{" "} diff --git a/Resources/Locale/en-US/_NF/smuggling/deaddrop-note.ftl b/Resources/Locale/en-US/_NF/smuggling/deaddrop-note.ftl new file mode 100644 index 00000000000..93e86d2cc0b --- /dev/null +++ b/Resources/Locale/en-US/_NF/smuggling/deaddrop-note.ftl @@ -0,0 +1,23 @@ +dead-drop-hint-note = Not much time. + Spinward Syndicate needs help. + Known drops: + + {$drops} + + Check these to lend a hand. + For a better tomorrow. + +dead-drop-time-known = next drop around {$time} +dead-drop-time-unknown = next drop placed when we can +dead-drop-hint-line = - {CAPITALIZE(INDEFINITE($object))} {$object} at {$poi} ({$time}) + +dead-drop-hint-generic = object +dead-drop-hint-poster = poster +dead-drop-hint-table = table +dead-drop-hint-vendor = vending machine +dead-drop-hint-bench = bench +dead-drop-hint-chair = chair +dead-drop-hint-stool = stool +dead-drop-hint-disposals = disposals bin + +dead-drop-station-hint-generic = a random location diff --git a/Resources/Locale/en-US/_NF/smuggling/deaddrop.ftl b/Resources/Locale/en-US/_NF/smuggling/deaddrop.ftl index 462895c0217..684cb75b822 100644 --- a/Resources/Locale/en-US/_NF/smuggling/deaddrop.ftl +++ b/Resources/Locale/en-US/_NF/smuggling/deaddrop.ftl @@ -1,6 +1,17 @@ deaddrop-search-text = Search closer -deaddrop-security-report = Syndicate smuggling activities detected in your sector deaddrop-hint-pretext = A Syndicate drop pod will be dispatched to the following coordinates: deaddrop-hint-posttext = Our agents on the inside will pay anyone willing to smuggle these goods into NT territory. +deaddrop-hint-next-drop = Expect the next drop here around {$time}. deaddrop-hint-name = neatly folded paper -deaddrop-hint-desc = A piece of paper, cleanly folded to fit into a small hiding space \ No newline at end of file +deaddrop-hint-desc = A piece of paper, cleanly folded to fit into a small hiding space + +smuggling-report-nfsd-general = Syndicate dead drop activity detected. +smuggling-report-nfsd-alternative = Syndicate dead drop activity detected, possible locations: {$location1} or {$location2}. +smuggling-report-nfsd-specific = Syndicate dead drop activity detected at {$location}. +smuggling-report-nfsd-pod = Syndicate drop pod detected. Estimated location: ({$x}, {$y}) +smuggling-report-pirate = Plunder ahoy! Syndicate drop pod detected around ({$x}, {$y}) + +deaddrop-faxed-hint-name = suspicious fax +deaddrop-faxed-hint-content = + Not much time. + Spinward Syndicate needs help. diff --git a/Resources/Locale/en-US/_NF/smuggling/fake-deaddrop-hints.ftl b/Resources/Locale/en-US/_NF/smuggling/fake-deaddrop-hints.ftl new file mode 100644 index 00000000000..8847a181e35 --- /dev/null +++ b/Resources/Locale/en-US/_NF/smuggling/fake-deaddrop-hints.ftl @@ -0,0 +1,56 @@ +# Thank you to Dusty for the fake hints. +dead-drop-fake-hint-1 = Next time remember to bring a screwdriver! A screwdriver! These boxes never have a screwdriver! +dead-drop-fake-hint-2 = Boss said to bring a tie to the meeting. But also to wear the company cloak! Which one!? +dead-drop-fake-hint-3 = Remember! Remember! You need a multitool! +dead-drop-fake-hint-4 = Replacing the light never stops the flickering. What IS it. +dead-drop-fake-hint-5 = I am not racist but the SCREAM. THE SCREAM. +dead-drop-fake-hint-6 = Hey mom, got your fax, sorry about the horse. But who let it drive? +dead-drop-fake-hint-7 = Maint, the hallways are making that groaning again. +dead-drop-fake-hint-8 = CREW: Always check the pot before you flush. We lost the doc again. +dead-drop-fake-hint-9 = Mental note: Double check what that was based on. +dead-drop-fake-hint-10 = He said he needed jiggah. What does it mean!? +dead-drop-fake-hint-11 = Ok so now they say the only uniform choice we have are skirts? Gotta call HR. +dead-drop-fake-hint-12 = Third shift. Saw a bear outside of the window. I want to go home. +dead-drop-fake-hint-13 = It's all just pizza. Everyone just makes pizza. It's always pizza. +dead-drop-fake-hint-14 = Next time you get pizza remember to ask if they have eggs. +dead-drop-fake-hint-15 = Third bench. End of shift. Every shift. +dead-drop-fake-hint-16 = Open the hatch. Weld the plate. Then the rods. Close it up. +dead-drop-fake-hint-17 = I'm not racist but so many EYES. +dead-drop-fake-hint-18 = Me back be burkin me. Call da sawbone aftah work. +dead-drop-fake-hint-19 = Jiggah ya natural. Jiggah. It be not 'ard. +dead-drop-fake-hint-20 = Aftah work lets get bubs. +dead-drop-fake-hint-21 = Always check their stampahs. +dead-drop-fake-hint-22 = Tout yah ass or da STC will fine ya. +dead-drop-fake-hint-23 = Left it in the third plant pot. +dead-drop-fake-hint-24 = Lawyas be fudge. +dead-drop-fake-hint-25 = You ever feel like they're just... Looking at you? +dead-drop-fake-hint-26 = Weird how the STC never signs their parking ticket as POS. +dead-drop-fake-hint-27 = If you are reading this learn to park. +dead-drop-fake-hint-28 = Butter slice. +dead-drop-fake-hint-29 = Hsssssksksssksksssskssss. +dead-drop-fake-hint-30 = Remember birthday cake on Tuesday. +dead-drop-fake-hint-31 = Carrot juice. +dead-drop-fake-hint-32 = Everyone needs power tools. +dead-drop-fake-hint-33 = It's OK if your potatoes are dirty. +dead-drop-fake-hint-34 = Raw potatoes don't like it hot. +dead-drop-fake-hint-35 = You are the Lord of the Rings. +dead-drop-fake-hint-36 = He ain't afraid to step on Grandma's dainty toes. +dead-drop-fake-hint-37 = Give it the ollllllll tappa tappa. +dead-drop-fake-hint-38 = My miners all died, do I write letters of apology or just wait until they come back? +dead-drop-fake-hint-39 = One cake and three oranges. +dead-drop-fake-hint-40 = And as always, enjoy! +dead-drop-fake-hint-41 = NEVER DISCARD ANY ACCUMULATED JUICES! +dead-drop-fake-hint-42 = Ya savey I like me nooks, an' I like me crannies. +dead-drop-fake-hint-43 = Ten tips for cooking at home number one. Stab whoever stole my microwave. +dead-drop-fake-hint-44 = It smells like fish EVERYWHERE. +dead-drop-fake-hint-45 = He just... Microwaved onion. +dead-drop-fake-hint-46 = I think that monkey knows it's in the box. +dead-drop-fake-hint-47 = Thanks for the food! +dead-drop-fake-hint-48 = The hell is a pine? +dead-drop-fake-hint-49 = If I 'ave to wait one more minute to jiggah. +dead-drop-fake-hint-50 = Magnet, radiation, magnet, blood +dead-drop-fake-hint-51 = ships filling up fast with black cant brea +dead-drop-fake-hint-52 = They're knocking on all of the lockers taping this inside they might take us red shirt brown shoes +dead-drop-fake-hint-53 = Tomorrow goal: Write that book. Smoog?? +dead-drop-fake-hint-54 = First thing tomorrow we start jogging. +dead-drop-fake-hint-55 = In the toilet are the keys. diff --git a/Resources/Locale/en-US/_NF/store/uplink-catalog.ftl b/Resources/Locale/en-US/_NF/store/uplink-catalog.ftl index 84823e32b91..4c05e768b97 100644 --- a/Resources/Locale/en-US/_NF/store/uplink-catalog.ftl +++ b/Resources/Locale/en-US/_NF/store/uplink-catalog.ftl @@ -9,28 +9,28 @@ store-category-secexplosives = Explosives store-category-secbundles = Bundles -uplink-security-hardsuit-name = Security HardSuit +uplink-security-hardsuit-name = Security Hardsuit uplink-security-hardsuit-desc = Standard issue armored EVA suit. Bulky armor plating slightly limits movement speed. -uplink-security-hardsuit-patrol-name = Security Patrol HardSuit +uplink-security-hardsuit-patrol-name = Security Patrol Hardsuit uplink-security-hardsuit-patrol-desc = A light-weight variant of the Security EVA suit. Greater movement speed comes at the cost of slightly lower protection. -uplink-security-hardsuit-brigmedic-name = BrigMedic HardSuit +uplink-security-hardsuit-brigmedic-name = Brigmedic Hardsuit uplink-security-hardsuit-brigmedic-desc = A lightly armored EVA suit. Designed for rescue operations, it sacrifices most of its armor in favor of movement speed. -uplink-security-hardsuit-warden-name = Bailiff HardSuit +uplink-security-hardsuit-warden-name = Bailiff Hardsuit uplink-security-hardsuit-warden-desc = A moderately reinforced variant of the Security EVA suit. Modern plating increases resistances without sacrificing range of motion. uplink-security-hardsuit-experimental-name = Experimental Combat Suit uplink-security-hardsuit-experimental-desc = An advanced combat suit researched by the greatest Nanotrasen minds. Heavily protected and extremely mobile. -uplink-security-hardsuit-sheriff-name = Sheriff's HardSuit +uplink-security-hardsuit-sheriff-name = Sheriff's Hardsuit uplink-security-hardsuit-sheriff-desc = A heavily reinforced Security EVA suit. Provides maximum resistance while maintaining the range of motion expected of security forces. uplink-security-hardsuit-combat-name = Patrol Combat Hardsuit -uplink-security-hardsuit-combat-desc = An armored patrol suit for combat meant to be utilized for multi-environmental hostile engagements. +uplink-security-hardsuit-combat-desc = An armored patrol suit for combat. Suitable for multi-environment hostile engagements. uplink-security-hardsuit-commmand-name = Command Combat Hardsuit -uplink-security-hardsuit-commmand-desc = An advanced combat suit designed for seasoned nfsd supervisors meant to be utilized for multi-environmental hostile engagements. -uplink-security-lethalarmory-name = Lethal armory stock crate +uplink-security-hardsuit-commmand-desc = An advanced combat suit designed for seasoned NFSD supervisors. Suitable for multi-environment hostile engagements. +uplink-security-lethalarmory-name = Lethal Armory Stock Crate uplink-security-lethalarmory-desc = A crate containing weapons necessary to fill an armory. -uplink-security-nonlethalarmory-name = Non-lethal armory stock crate +uplink-security-nonlethalarmory-name = Non-lethal Armory Stock Crate uplink-security-nonlethalarmory-desc = A crate containing non-lethal weapons necessary to fill an armory. -uplink-security-mk58-name = MK 58 -uplink-security-mk58-desc = Cheap, standard issue side-arm. Uses .35 Auto. +uplink-security-mk58-name = MK58 +uplink-security-mk58-desc = Cheap, standard issue sidearm. Uses .35 Auto. uplink-security-kammerer-name = Kammerer uplink-security-kammerer-desc = Pump action shotgun. Uses .50 shotgun shells. Holds 4. uplink-security-disabler-name = Disabler @@ -38,11 +38,11 @@ uplink-security-disabler-desc = Standard issue non-lethal stun gun. Has an inter uplink-security-stunbaton-name = Stun Baton uplink-security-stunbaton-desc = Standard issue non-lethal stun baton. Has an internal battery, but requires using a security charging dock. uplink-security-deckard-name = Deckard -uplink-security-deckard-desc = A very capable revolver, imported from the Tannhouser Gate. Uses .45 magnum. +uplink-security-deckard-desc = A very capable revolver, imported from the Tannhauser Gate. Uses .45 magnum. uplink-security-emitter-name = EMP Emitter uplink-security-emitter-desc = A high-energy pulse emitter tuned to disrupt electronics and power systems. Harmless to living things. Projectiles pass through glass. Has an internal battery, but requires using a security charging dock. uplink-security-n1984-name = N1984 -uplink-security-n1984-desc = Standard issue Officer's pistol. Uses .45 magnum magazines. +uplink-security-n1984-desc = Standard issue officer's pistol. Uses .45 magnum magazines. uplink-security-enforcer-name = Enforcer uplink-security-enforcer-desc = An updated model of the Kammerer, boasts a 7 shell magazine. Uses .50 shotgun shells. uplink-security-lecter-name = Lecter @@ -52,17 +52,17 @@ uplink-security-lasercarbine-desc = Standard issue laser carbine. Has an interna uplink-security-disablersmg-name = Disabler SMG uplink-security-disablersmg-desc = Fully automatic, rapid fire disabler. Tuned to the same frequency as standard disablers, the ultimate less-lethal instrument. Has an internal battery, but requires using a security charging dock. uplink-security-energysword-name = Energy Sword -uplink-security-energysword-desc = Legally Distinct Energy Sword. Chance to reflect projectiles. -uplink-security-wt550-name = WT 550 -uplink-security-wt550-desc = A fully automatic sub machine gun. This design uses special top-mounted magazines, allowing for simpler and faster operation in the field. Uses .35 auto. +uplink-security-energysword-desc = Reverse-engineered, NT manufactured energy sword. Chance to reflect projectiles. +uplink-security-wt550-name = WT550 +uplink-security-wt550-desc = A fully automatic submachine gun. This design uses special top-mounted magazines, allowing for simpler and faster operation in the field. Uses .35 auto. uplink-security-energygun-name = Energy Gun -uplink-security-energygun-desc = A semi automatic energy gun capable of firing both non-lethal stun bolts, as well as overcharged lethal energy bolts. Has an internal battery, but requires using a security charging dock. +uplink-security-energygun-desc = A semi-automatic energy gun capable of firing both non-lethal stun bolts, as well as overcharged lethal energy bolts. Has an internal battery, but requires using a security charging dock. uplink-security-emprpg-name = RPG-7 uplink-security-emprpg-desc = A rocket propelled grenade launcher. Comes with 1 EMP round. uplink-security-empgrenade-name = EMP Grenade uplink-security-empgrenade-desc = A handheld grenade that emits a high energy pulse that disrupts electronics and power systems in a moderately large radius. -uplink-security-holo-name = Holo Barrier -uplink-security-holo-desc = A battery powered holo projecter that places temporary barriers to bar movement. +uplink-security-holo-name = Holobarrier +uplink-security-holo-desc = A battery powered holoprojecter that places temporary barriers to bar movement. uplink-security-jetpack-name = Jetpack uplink-security-jetpack-desc = A pre-filled jetpack for EVA. Comes in a fashionable green. uplink-security-magboots-name = NFSD Magboots @@ -70,18 +70,18 @@ uplink-security-magboots-desc = Light weight magboots designed to keep the weare uplink-security-techfab-name = NFSD Techfab uplink-security-techfab-desc = A circuit board for a NFSD Techfab. Allows the production of ammunition, magazines, weapons, and numerous other utilities. Uses raw resources. Can be upgraded. uplink-security-key-name = NFSD Encryption Keys -uplink-security-key-desc = A box of 4 encryption keys that give access to the NFSD radio channel +uplink-security-key-desc = A box of 4 encryption keys that give access to the NFSD radio channel. uplink-security-emprocket-name = EMP Rocket -uplink-security-emprocket-desc = An EMP Rocket for the RPG-7 +uplink-security-emprocket-desc = An EMP rocket for the RPG-7. uplink-security-thrusterkit-name = Thruster Upgrade Kit -uplink-security-thrusterkit-desc = Contains 12 Super Capaciters. Perfect for upgrading a ship's thrusters. -uplink-security-magazinepistol-name = .35 auto pistol magazines -uplink-security-magazinepistol-desc = A box containing 4 magazines for .35 auto. -uplink-security-20riflemagazine-name = .20 rifle magazines -uplink-security-20riflemagazine-desc = A box containing 4 magazines for .20 rifle. -uplink-security-wt550magazine-name = .35 auto top-mounted magazines -uplink-security-wt550magazine-desc = A box containing 3 magazines for .35 auto top-mounted. -uplink-security-hypo-name = Hypo Spray +uplink-security-thrusterkit-desc = Contains 12 super capaciters. Perfect for upgrading three thrusters. +uplink-security-magazinepistol-name = .35 Auto Pistol Magazines +uplink-security-magazinepistol-desc = A box containing 4 filled .35 auto magazines. +uplink-security-20riflemagazine-name = .20 Rifle Magazines +uplink-security-20riflemagazine-desc = A box containing 4 filled .20 rifle magazines. +uplink-security-wt550magazine-name = .35 Auto Top-mounted Magazines +uplink-security-wt550magazine-desc = A box containing 3 filled .35 auto top-mounted magazines. +uplink-security-hypo-name = Hypospray uplink-security-hypo-desc = A sterile medical injector for instant delivery of medications. uplink-security-ambuzol-name = Ambuzol Syringe uplink-security-ambuzol-desc = 15u of anti-viral medication to halt the progress of the highly contagious zombie virus. @@ -96,25 +96,25 @@ uplink-security-truncheon-desc = Standard issue blunt object. Excellent for crac uplink-security-armingsword-name = Plasteel Arming Sword uplink-security-armingsword-desc = Ancient design meets modern materials. uplink-security-captainsword-name = Captain's Sabre -uplink-security-captainsword-desc = A sword normally reserved for Captains, Admirality, and other high-command. Has a small chance to reflect incoming projectiles. +uplink-security-captainsword-desc = A sword normally reserved for captains, admiralty, and other high command. Has a small chance to reflect incoming projectiles. uplink-security-pulsepistol-name = Pulse Pistol uplink-security-pulsepistol-desc = A high powered laser pistol normally reserved for elite ERT units. Has an internal battery, but requires using a security charging dock. uplink-security-pulsecarbine-name = Pulse Carbine -uplink-security-pulsecarbine-desc = A high powered laser carbine normally reserved for elite ERT and Combat units. Has an internal battery, but requires using a security charging dock. +uplink-security-pulsecarbine-desc = A high powered laser carbine normally reserved for elite ERT and combat units. Has an internal battery, but requires using a security charging dock. uplink-security-hammer-name = Breaching Hammer -uplink-security-hammer-desc = A large two handed hammer that is perfect for breaking down doors, or breaching through hull plating. +uplink-security-hammer-desc = A large, two-handed hammer that is perfect for breaking down doors, or breaching through hull plating. uplink-security-teleshield-name = Telescopic Shield -uplink-security-teleshield-desc = An expandable hand-held shield offering excellent protection. +uplink-security-teleshield-desc = An expandable handheld shield offering excellent protection. uplink-security-energyshield-name = Energy Shield uplink-security-energyshield-desc = An exotic energy shield that blocks most incoming damage. uplink-security-swat-name = Swat Gas Mask uplink-security-swat-desc = A full-face covering version of the standard issue NFSD Gas Mask. uplink-security-speedloader-name = .45 Magnum Speed Loader -uplink-security-speedloader-desc = A revolver speed loader that comes pre-loaded with .45 magnum. +uplink-security-speedloader-desc = A revolver speed loader that comes pre-loaded with .45 magnum cartridges. uplink-security-speedloaderrubber-name = .45 Magnum Rubber Speed Loader -uplink-security-speedloaderrubber-desc = A revolver speed loader that comes pre-loaded with .45 magnum rubber. +uplink-security-speedloaderrubber-desc = A revolver speed loader that comes pre-loaded with .45 rubber cartridges. uplink-security-shotlethal-name = Lethal Shotgun Shells -uplink-security-shotlethal-desc = A box of lethal .50 calibre pellet shotgun shells. +uplink-security-shotlethal-desc = A box of lethal .50 calibre buckshot shotgun shells. uplink-security-shotbeanbag-name = Beanbag Shotgun Shells uplink-security-shotbeanbag-desc = A box of non-lethal .50 calibre beanbag shotgun shells. uplink-security-shotincend-name = Incendiary Shotgun Shells @@ -133,7 +133,7 @@ uplink-security-shrapnelgrenade-box-name = Shrapnel Grenade Box uplink-security-shrapnelgrenade-box-desc = A box containing 4 shrapnel grenades. uplink-security-smokegrenade-box-name = Smoke Grenade Box uplink-security-smokegrenade-box-desc = A box containing 4 smoke grenades. -uplink-security-teargasgrenade-box-name = Tear gas Grenade Box +uplink-security-teargasgrenade-box-name = Tear Gas Grenade Box uplink-security-teargasgrenade-box-desc = A box containing 4 tear gas grenades. uplink-security-flashbanggrenade-box-name = Flashbang Grenade Box uplink-security-flashbanggrenade-box-desc = A box containing 4 flasbang grenades. @@ -143,16 +143,18 @@ uplink-security-breachingcharge-box-name = Breaching Charge Box uplink-security-breachingcharge-box-desc = A box containing 4 breaching charges. uplink-security-hoverbike-name = NFSD Hoverbike Flatpack uplink-security-hoverbike-desc = Flatpack containing NFSD issued turbine with bike handles. Keys already slotted in the ignition. Very safe. -uplink-security-trackingdart-bundle-name = Tracking Darts Bundle -uplink-security-trackingdart-bundle-desc = A bundle containing a Lake type launcher 5 tracking darts and 5 pinpointers. +uplink-security-trackingdart-bundle-name = Tracking Dart Bundle +uplink-security-trackingdart-bundle-desc = A bundle containing a Lake type launcher, 5 tracking darts and 5 pinpointers. uplink-security-emp-bundle-name = EMP Bundle uplink-security-emp-bundle-desc = A bundle containing a Lake type launcher and 12 EMP grenades. uplink-security-emp-ammo-name = EMP Projectile Ammo -uplink-security-emp-ammo-desc = A a box containing 4 EMP projectile grenades. +uplink-security-emp-ammo-desc = A box containing 4 EMP projectile grenades. uplink-security-trackingdart-ammo-name = Tracking Darts Ammo uplink-security-trackingdart-ammo-desc = A a box containing 4 tracking darts. uplink-security-spaceblade-sec-name = Security SpaceBlade -uplink-security-spaceblade-sec-desc = Latest stun tech +uplink-security-spaceblade-sec-desc = The latest in stun tech. +uplink-security-contraband-forensics-module-name = Contraband Forenics Module +uplink-security-contraband-forensics-module-desc = A program for scanning and reporting contraband dead drops and pods for bounties. Slots into a forensic scanner. store-category-piratehardsuits = EVA Suits store-category-pirateweapons = Weapons diff --git a/Resources/Maps/_NF/POI/arena.yml b/Resources/Maps/_NF/POI/arena.yml index 34e6e1bfe91..2275d5b79cb 100644 --- a/Resources/Maps/_NF/POI/arena.yml +++ b/Resources/Maps/_NF/POI/arena.yml @@ -139,6 +139,8 @@ entities: angularDamping: 999999 linearDamping: 999999 - type: GridPathfinding + - type: BecomesStation + id: Arena - type: Gravity gravityShakeSound: !type:SoundPathSpecifier path: /Audio/Effects/alert.ogg @@ -17327,7 +17329,7 @@ entities: - type: Transform pos: 11.5,7.5 parent: 2 -- proto: RandomPosterContraband +- proto: RandomPosterContrabandDeadDrop entities: - uid: 2984 components: @@ -17459,8 +17461,6 @@ entities: - type: Transform pos: -9.5,-14.5 parent: 2 -- proto: RandomPosterContrabandDeadDrop - entities: - uid: 3128 components: - type: Transform diff --git a/Resources/Maps/_NF/POI/caseyscasino.yml b/Resources/Maps/_NF/POI/caseyscasino.yml index 16e2cc2d1e3..607cab12525 100644 --- a/Resources/Maps/_NF/POI/caseyscasino.yml +++ b/Resources/Maps/_NF/POI/caseyscasino.yml @@ -5695,82 +5695,6 @@ entities: rot: 3.141592653589793 rad pos: -10.473477,8.532509 parent: 2 -- proto: PosterContrabandBeachStarYamamoto - entities: - - uid: 443 - components: - - type: Transform - pos: 3.5,13.5 - parent: 2 -- proto: PosterContrabandHaveaPuff - entities: - - uid: 944 - components: - - type: Transform - pos: 15.5,2.5 - parent: 2 - - uid: 1142 - components: - - type: Transform - rot: 1.5707963267948966 rad - pos: 4.5,7.5 - parent: 2 -- proto: PosterContrabandKudzu - entities: - - uid: 526 - components: - - type: Transform - pos: -4.5,7.5 - parent: 2 -- proto: PosterContrabandMaskedMen - entities: - - uid: 423 - components: - - type: Transform - pos: -0.5,7.5 - parent: 2 -- proto: PosterContrabandPunchShit - entities: - - uid: 257 - components: - - type: Transform - pos: -6.5,-7.5 - parent: 2 -- proto: PosterContrabandRobustSoftdrinks - entities: - - uid: 505 - components: - - type: Transform - pos: 5.5,-8.5 - parent: 2 -- proto: PosterContrabandSmoke - entities: - - uid: 1150 - components: - - type: Transform - pos: 5.5,2.5 - parent: 2 -- proto: PosterContrabandSpaceCola - entities: - - uid: 524 - components: - - type: Transform - pos: 14.5,-3.5 - parent: 2 -- proto: PosterContrabandTheGriffin - entities: - - uid: 747 - components: - - type: Transform - pos: 4.5,2.5 - parent: 2 -- proto: PosterContrabandTools - entities: - - uid: 446 - components: - - type: Transform - pos: 6.5,-8.5 - parent: 2 - proto: PosterLegitBarDrinks entities: - uid: 511 @@ -6174,6 +6098,62 @@ entities: parent: 2 - proto: RandomPosterContrabandDeadDrop entities: + - uid: 443 + components: + - type: Transform + pos: 3.5,13.5 + parent: 2 + - uid: 944 + components: + - type: Transform + pos: 15.5,2.5 + parent: 2 + - uid: 1142 + components: + - type: Transform + rot: 1.5707963267948966 rad + pos: 4.5,7.5 + parent: 2 + - uid: 526 + components: + - type: Transform + pos: -4.5,7.5 + parent: 2 + - uid: 423 + components: + - type: Transform + pos: -0.5,7.5 + parent: 2 + - uid: 257 + components: + - type: Transform + pos: -6.5,-7.5 + parent: 2 + - uid: 505 + components: + - type: Transform + pos: 5.5,-8.5 + parent: 2 + - uid: 1150 + components: + - type: Transform + pos: 5.5,2.5 + parent: 2 + - uid: 524 + components: + - type: Transform + pos: 14.5,-3.5 + parent: 2 + - uid: 747 + components: + - type: Transform + pos: 4.5,2.5 + parent: 2 + - uid: 446 + components: + - type: Transform + pos: 6.5,-8.5 + parent: 2 - uid: 1143 components: - type: Transform diff --git a/Resources/Maps/_NF/POI/grifty.yml b/Resources/Maps/_NF/POI/grifty.yml index 5df6328ee95..9d877432d31 100644 --- a/Resources/Maps/_NF/POI/grifty.yml +++ b/Resources/Maps/_NF/POI/grifty.yml @@ -441,8 +441,6 @@ entities: chunkSize: 4 - type: GasTileOverlay - type: RadiationGridResistance - - type: BecomesStation - id: Grifty - proto: AirAlarm entities: - uid: 396 @@ -4550,108 +4548,85 @@ entities: - type: Transform pos: -0.62397563,5.3686275 parent: 1 -- proto: PosterContrabandAmbrosiaVulgaris +- proto: RandomPosterContrabandDeadDrop entities: + - uid: 579 + components: + - type: Transform + pos: -4.5,4.5 + parent: 1 - uid: 44 components: - type: Transform rot: -1.5707963267948966 rad pos: -8.5,-1.5 parent: 1 -- proto: PosterContrabandBorgFancy - entities: - uid: 570 components: - type: Transform pos: 4.5,-2.5 parent: 1 -- proto: PosterContrabandBreadLies - entities: - uid: 809 components: - type: Transform pos: -4.5,-2.5 parent: 1 -- proto: PosterContrabandClown - entities: - uid: 811 components: - type: Transform rot: 1.5707963267948966 rad pos: -10.5,1.5 parent: 1 -- proto: PosterContrabandDonk - entities: - uid: 551 components: - type: Transform pos: 3.5,4.5 parent: 1 -- proto: PosterContrabandDonutCorp - entities: - uid: 583 components: - type: Transform pos: 4.5,-0.5 parent: 1 -- proto: PosterContrabandHaveaPuff - entities: - uid: 582 components: - type: Transform pos: 4.5,4.5 parent: 1 -- proto: PosterContrabandMissingGloves - entities: - uid: 630 components: - type: Transform pos: -5.5,9.5 parent: 1 -- proto: PosterContrabandPwrGame - entities: - uid: 584 components: - type: Transform pos: 5.5,9.5 parent: 1 -- proto: PosterContrabandRedRum - entities: - uid: 585 components: - type: Transform pos: 8.5,9.5 parent: 1 -- proto: PosterContrabandShamblersJuice - entities: - uid: 611 components: - type: Transform pos: 10.5,6.5 parent: 1 -- proto: PosterContrabandSmoke - entities: - uid: 573 components: - type: Transform pos: 10.5,1.5 parent: 1 -- proto: PosterContrabandSpaceCola - entities: - uid: 715 components: - type: Transform pos: 4.5,5.5 parent: 1 -- proto: PosterContrabandSpaceUp - entities: - uid: 615 components: - type: Transform pos: 9.5,8.5 parent: 1 -- proto: PosterContrabandVoteWeh - entities: - uid: 609 components: - type: Transform @@ -4887,12 +4862,12 @@ entities: - type: Transform pos: -1.5,5.5 parent: 1 -- proto: RandomPosterContrabandDeadDrop +- proto: RandomSoap entities: - - uid: 579 + - uid: 765 components: - type: Transform - pos: -4.5,4.5 + pos: -8.5,5.5 parent: 1 - proto: ReinforcedPlasmaWindow entities: diff --git a/Resources/Maps/_NF/POI/lodge.yml b/Resources/Maps/_NF/POI/lodge.yml index ae0fe891118..35d2679d57c 100644 --- a/Resources/Maps/_NF/POI/lodge.yml +++ b/Resources/Maps/_NF/POI/lodge.yml @@ -2881,8 +2881,6 @@ entities: chunkSize: 4 - type: GasTileOverlay - type: RadiationGridResistance - - type: BecomesStation - id: Lodge - type: StationTransit - proto: AirCanister entities: diff --git a/Resources/Maps/_NF/POI/tinnia.yml b/Resources/Maps/_NF/POI/tinnia.yml index afd13801f9b..9cbc47900d6 100644 --- a/Resources/Maps/_NF/POI/tinnia.yml +++ b/Resources/Maps/_NF/POI/tinnia.yml @@ -11502,7 +11502,7 @@ entities: - type: Transform pos: 3.5,-12.5 parent: 1 -- proto: RandomPosterContraband +- proto: RandomPosterContrabandDeadDrop entities: - uid: 1984 components: @@ -11544,8 +11544,6 @@ entities: - type: Transform pos: 7.5,11.5 parent: 1 -- proto: RandomPosterContrabandDeadDrop - entities: - uid: 2162 components: - type: Transform diff --git a/Resources/Maps/deaddrop.yml b/Resources/Maps/deaddrop.yml index b826bec3364..8e9c4545c14 100644 --- a/Resources/Maps/deaddrop.yml +++ b/Resources/Maps/deaddrop.yml @@ -42,6 +42,7 @@ entities: - type: SpreaderGrid - type: Shuttle - type: GridPathfinding + - type: ContrabandPodGrid - gravityShakeSound: !type:SoundPathSpecifier path: /Audio/Effects/alert.ogg type: Gravity diff --git a/Resources/Prototypes/Entities/Objects/Devices/forensic_scanner.yml b/Resources/Prototypes/Entities/Objects/Devices/forensic_scanner.yml index fbb477de64b..2fcfc5217e7 100644 --- a/Resources/Prototypes/Entities/Objects/Devices/forensic_scanner.yml +++ b/Resources/Prototypes/Entities/Objects/Devices/forensic_scanner.yml @@ -5,13 +5,13 @@ description: A handheld device that can scan objects for fingerprints and fibers. components: - type: Sprite - sprite: Objects/Devices/forensic_scanner.rsi + sprite: _NF/Objects/Devices/forensic_scanner.rsi # Frontier: add _NF state: forensicnew - type: Item size: Small storedRotation: 90 - type: Clothing - sprite: Objects/Devices/forensic_scanner.rsi + sprite: _NF/Objects/Devices/forensic_scanner.rsi # Frontier: add _NF quickEquip: false slots: - Belt @@ -29,6 +29,28 @@ - Forensics - type: StealTarget stealGroup: ForensicScanner + - type: Appearance # Frontier + - type: ItemSlots # Frontier + slots: # Frontier + forensics_cartridge: # Frontier + name: forensic-scanner-slot-component-slot-name-cartridge # Frontier + insertSound: /Audio/Machines/id_insert.ogg # Frontier + ejectSound: /Audio/Machines/id_swipe.ogg # Frontier + whitelist: # Frontier + components: # Frontier + - ForensicsCartridge # Frontier + - type: ItemMapper # Frontier + sprite: _NF/Objects/Devices/forensic_scanner.rsi # Frontier + mapLayers: # Frontier + forensiccartridge: # Frontier + whitelist: # Frontier + components: # Frontier + - ForensicsCartridge # Frontier + - type: ContainerContainer # Frontier + containers: # Frontier + storagebase: !type:Container # Frontier + ents: [] # Frontier + forensics_cartridge: !type:ContainerSlot {} # Frontier - type: entity name: forensic scanner report diff --git a/Resources/Prototypes/Entities/Objects/Misc/dat_fukken_disk.yml b/Resources/Prototypes/Entities/Objects/Misc/dat_fukken_disk.yml index 04424a50294..9ed19b3c088 100644 --- a/Resources/Prototypes/Entities/Objects/Misc/dat_fukken_disk.yml +++ b/Resources/Prototypes/Entities/Objects/Misc/dat_fukken_disk.yml @@ -22,6 +22,11 @@ - HighRiskItem - type: StealTarget stealGroup: NukeDisk + - type: DisableShipyardSale # Frontier + reason: shipyard-console-station-resources # Frontier + allowedShipyardTypes: # Frontier: mail profits removed, sector in shambles + - Syndicate # Frontier + - BlackMarket # Frontier - type: entity name: nuclear authentication disk diff --git a/Resources/Prototypes/Entities/Structures/Furniture/chairs.yml b/Resources/Prototypes/Entities/Structures/Furniture/chairs.yml index c1d767905c0..a0bdd3dfb29 100644 --- a/Resources/Prototypes/Entities/Structures/Furniture/chairs.yml +++ b/Resources/Prototypes/Entities/Structures/Furniture/chairs.yml @@ -72,6 +72,8 @@ bodyType: Static - type: Transform anchored: true + - type: PotentialDeadDrop # Frontier + hintText: dead-drop-hint-chair # Frontier #Starts unanchored, can be rotated while anchored - type: entity @@ -96,6 +98,8 @@ bodyType: Static - type: Transform anchored: true + - type: PotentialDeadDrop # Frontier + hintText: dead-drop-hint-stool # Frontier - type: entity name: chair @@ -371,6 +375,8 @@ - type: Construction graph: Seat node: chairSteelBench + - type: PotentialDeadDrop # Frontier + hintText: dead-drop-hint-bench # Frontier - type: entity name: wooden bench @@ -401,3 +407,5 @@ max: 4 - type: StaticPrice price: 20 + - type: PotentialDeadDrop # Frontier + hintText: dead-drop-hint-bench # Frontier diff --git a/Resources/Prototypes/Entities/Structures/Machines/nuke.yml b/Resources/Prototypes/Entities/Structures/Machines/nuke.yml index dd9069e7312..01f64504196 100644 --- a/Resources/Prototypes/Entities/Structures/Machines/nuke.yml +++ b/Resources/Prototypes/Entities/Structures/Machines/nuke.yml @@ -110,6 +110,11 @@ - type: WarpPoint follow: true location: nuclear bomb + - type: DisableShipyardSale # Frontier + reason: shipyard-console-dangerous-materials # Frontier + allowedShipyardTypes: # Frontier: + - Syndicate # Frontier + - BlackMarket # Frontier - type: entity parent: NuclearBomb diff --git a/Resources/Prototypes/Entities/Structures/Machines/vending_machines.yml b/Resources/Prototypes/Entities/Structures/Machines/vending_machines.yml index 859938d13bc..013923dbbdd 100644 --- a/Resources/Prototypes/Entities/Structures/Machines/vending_machines.yml +++ b/Resources/Prototypes/Entities/Structures/Machines/vending_machines.yml @@ -103,6 +103,8 @@ - type: WiresVisuals - type: MarketModifier # Frontier mod: 10 # Frontier + - type: PotentialDeadDrop # Frontier + hintText: dead-drop-hint-vendor # Frontier - type: entity parent: VendingMachine diff --git a/Resources/Prototypes/Entities/Structures/Piping/Disposal/units.yml b/Resources/Prototypes/Entities/Structures/Piping/Disposal/units.yml index f7cb0c9dde6..02d4f715b81 100644 --- a/Resources/Prototypes/Entities/Structures/Piping/Disposal/units.yml +++ b/Resources/Prototypes/Entities/Structures/Piping/Disposal/units.yml @@ -87,6 +87,8 @@ type: DisposalUnitBoundUserInterface - type: RatKingRummageable - type: RequireProjectileTarget + - type: PotentialDeadDrop # Frontier + hintText: dead-drop-hint-disposals # Frontier - type: entity id: MailingUnit diff --git a/Resources/Prototypes/Guidebook/security.yml b/Resources/Prototypes/Guidebook/security.yml index 17edb030065..73fc073f061 100644 --- a/Resources/Prototypes/Guidebook/security.yml +++ b/Resources/Prototypes/Guidebook/security.yml @@ -4,9 +4,10 @@ text: "/ServerInfo/Guidebook/Security/Security.xml" children: - Forensics - - Defusal +# - Defusal # Frontier - largely irrelevant - CriminalRecords - - SpaceLaw +# - SpaceLaw # Frontier - irrelevant + - Smuggling # Frontier - type: guideEntry id: Forensics diff --git a/Resources/Prototypes/Nyanotrasen/Entities/Structures/Machines/mailTeleporter.yml b/Resources/Prototypes/Nyanotrasen/Entities/Structures/Machines/mailTeleporter.yml index 0103b5cdb5b..d3151c7a1eb 100644 --- a/Resources/Prototypes/Nyanotrasen/Entities/Structures/Machines/mailTeleporter.yml +++ b/Resources/Prototypes/Nyanotrasen/Entities/Structures/Machines/mailTeleporter.yml @@ -39,3 +39,8 @@ True: {visible: true} False: {visible: false} - type: PowerSwitch + - type: DisableShipyardSale # Frontier + reason: shipyard-console-station-resources # Frontier + allowedShipyardTypes: # Frontier: mail profits removed, sector in shambles + - Syndicate # Frontier + - BlackMarket # Frontier diff --git a/Resources/Prototypes/_NF/Catalog/Fills/Crates/trade.yml b/Resources/Prototypes/_NF/Catalog/Fills/Crates/trade.yml index 674c1ca9392..e020b1c9b1b 100644 --- a/Resources/Prototypes/_NF/Catalog/Fills/Crates/trade.yml +++ b/Resources/Prototypes/_NF/Catalog/Fills/Crates/trade.yml @@ -21,3 +21,29 @@ - type: entity id: CrateTradeContrabandSecureCyberSunFilled parent: CrateTradeContrabandSecureCyberSun + +- type: entity + id: CrateTradeSecureNormalFilledContraband + parent: [CrateTradeSecureNormalFilled, BaseC3SyndicateContrabandNoValue] + description: Contains goods made in the Spinward sector, ready to be smuggled to a cargo depot for profit. MAKE SURE THE CRATE IS INTACT. + components: + - type: StaticPrice + price: 3000 + - type: DisableShipyardSale + reason: shipyard-console-contraband-onboard + allowedShipyardTypes: + - Syndicate + - BlackMarket + +- type: entity + id: CrateTradeSecureHighFilledContraband + parent: [CrateTradeSecureHighFilled, BaseC3SyndicateContrabandNoValue] + description: Contains exotic goods made in the Spinward sector, ready to be smuggled to a cargo depot for profit. MAKE SURE THE CRATE IS INTACT. + components: + - type: StaticPrice + price: 5000 + - type: DisableShipyardSale + reason: shipyard-console-contraband-onboard + allowedShipyardTypes: + - Syndicate + - BlackMarket diff --git a/Resources/Prototypes/_NF/Catalog/security_uplink_catalog.yml b/Resources/Prototypes/_NF/Catalog/security_uplink_catalog.yml index 4658402ad2d..c4f9ef37201 100644 --- a/Resources/Prototypes/_NF/Catalog/security_uplink_catalog.yml +++ b/Resources/Prototypes/_NF/Catalog/security_uplink_catalog.yml @@ -1290,4 +1290,23 @@ - !type:StoreWhitelistCondition whitelist: tags: - - SecurityUplink \ No newline at end of file + - SecurityUplink + +- type: listing + id: UplinkSecurityContrabandForensicsModule + name: uplink-security-contraband-forensics-module-name + description: uplink-security-contraband-forensics-module-desc + productEntity: ContrabandForensicsModule + icon: { sprite: _NF/Objects/Devices/cartridge.rsi, state: contraband_forensics } + cost: + FrontierUplinkCoin: 4 + categories: + - UplinkSecurityUtility + conditions: + - !type:StoreWhitelistCondition + whitelist: + tags: + - SecurityUplink + - !type:BuyerJobCondition + whitelist: + - Sheriff diff --git a/Resources/Prototypes/_NF/Entities/Markers/Spawners/crates.yml b/Resources/Prototypes/_NF/Entities/Markers/Spawners/crates.yml index a055c122788..cc5c4942005 100644 --- a/Resources/Prototypes/_NF/Entities/Markers/Spawners/crates.yml +++ b/Resources/Prototypes/_NF/Entities/Markers/Spawners/crates.yml @@ -14,13 +14,11 @@ - CrateTradeContrabandSecureCyberSunFilled - CrateTradeContrabandSecureDonkFilled - CrateTradeContrabandSecureNormalFilled - - CrateTradeSecureHighFilled - - CrateTradeSecureNormalFilled #its in here twice for balance. TODO: find a better crate to put here instead like an animal one or something. - - CrateTradeSecureNormalFilled - - CrateTradeSecureNormalFilled + - CrateTradeSecureHighFilledContraband + - CrateTradeSecureNormalFilledContraband #its in here twice for balance. TODO: find a better crate to put here instead like an animal one or something. + - CrateTradeSecureNormalFilledContraband - LandMineExplosive - BannerSyndicate - - BannerSyndicate rarePrototypes: - CrateSyndicateSurplusBundle - CrateSyndicateLightSurplusBundle diff --git a/Resources/Prototypes/_NF/Entities/Markers/Spawners/posters.yml b/Resources/Prototypes/_NF/Entities/Markers/Spawners/posters.yml index b8a5cba5d0c..2d8eda8242f 100644 --- a/Resources/Prototypes/_NF/Entities/Markers/Spawners/posters.yml +++ b/Resources/Prototypes/_NF/Entities/Markers/Spawners/posters.yml @@ -2,25 +2,7 @@ parent: MarkerBase id: RandomPosterContrabandDeadDrop name: random contraband poster spawner - suffix: DeadDrop, 50 - components: - - type: Sprite - layers: - - state: red - - sprite: Structures/Wallmounts/posters.rsi - state: random_contraband - - type: RandomSpawner - offset: 0 - prototypes: - - RandomPosterContrabandDeadDrop100 - - RandomPosterContraband #this is the entire non-deaddrop contraband poster list, making this essentially a 50/50 chance - chance: 0.9 - -- type: entity - parent: MarkerBase - id: RandomPosterContrabandDeadDrop100 - name: random contraband poster spawner - suffix: DeadDrop, 100 + suffix: DeadDrop components: - type: Sprite layers: diff --git a/Resources/Prototypes/_NF/Entities/Objects/Devices/cartridges.yml b/Resources/Prototypes/_NF/Entities/Objects/Devices/cartridges.yml index 3a7f5a2c197..643d986aa27 100644 --- a/Resources/Prototypes/_NF/Entities/Objects/Devices/cartridges.yml +++ b/Resources/Prototypes/_NF/Entities/Objects/Devices/cartridges.yml @@ -18,3 +18,21 @@ - type: BountyContractsCartridge - type: AccessReader access: [["HeadOfSecurity"], ["HeadOfPersonnel"]] + +# Not a PDA cartridge (then why is this here) +- type: entity + parent: BaseItem + id: ContrabandForensicsModule + name: contraband forensics module + description: A program for scanning and reporting contraband dead drops and pods for bounties. + components: + - type: Sprite + sprite: _NF/Objects/Devices/cartridge.rsi + state: contraband_forensics + - type: Icon + sprite: _NF/Objects/Devices/cartridge.rsi + state: contraband_forensics + - type: ForensicsCartridge + - type: GuideHelp + guides: + - Forensics diff --git a/Resources/Prototypes/_NF/Entities/Objects/Misc/paper.yml b/Resources/Prototypes/_NF/Entities/Objects/Misc/paper.yml index b766dce4211..e33e34f4a9f 100644 --- a/Resources/Prototypes/_NF/Entities/Objects/Misc/paper.yml +++ b/Resources/Prototypes/_NF/Entities/Objects/Misc/paper.yml @@ -90,3 +90,20 @@ prob: 0.2 - id: Paper prob: 0.2 + +- type: entity + id: PaperDeadDropHint + parent: Paper + name: suspicious note + description: A crinkled sheet with scribbles on it. Seems to have been written in a hurry. + categories: [ HideSpawnMenu ] + components: + - type: DeadDropHint + - type: Paper + content: deaddrop-faxed-hint-content + +- type: entity + id: PaperDeadDropFax + parent: PaperDeadDropHint + name: suspicious fax + description: A sheet with blurry, crooked text on it. Seems to have been copied in a hurry. diff --git a/Resources/Prototypes/_NF/Entities/Stations/base.yml b/Resources/Prototypes/_NF/Entities/Stations/base.yml index aa7b22022c9..c9855c073ea 100644 --- a/Resources/Prototypes/_NF/Entities/Stations/base.yml +++ b/Resources/Prototypes/_NF/Entities/Stations/base.yml @@ -100,4 +100,11 @@ overrideList: matchParents: true id: - - SheetUranium # Counts as food \ No newline at end of file + - SheetUranium # Counts as food + +- type: entity + id: BaseStationDeadDrops + abstract: true + components: + - type: StationDeadDrop + maxDeadDrops: 2 # A few to be stumbled upon. diff --git a/Resources/Prototypes/_NF/Entities/Stations/nanotrasen.yml b/Resources/Prototypes/_NF/Entities/Stations/nanotrasen.yml index ce236e0b889..62c08677f48 100644 --- a/Resources/Prototypes/_NF/Entities/Stations/nanotrasen.yml +++ b/Resources/Prototypes/_NF/Entities/Stations/nanotrasen.yml @@ -15,6 +15,7 @@ categories: [ HideSpawnMenu ] components: - type: Transform + - type: StationDeadDropHintExempt - type: entity id: StandardFrontierOutpost @@ -28,6 +29,13 @@ components: - type: Transform +- type: entity + id: DeadDropFrontierOutpost + parent: + - StandardFrontierOutpost + - BaseStationDeadDrops + categories: [ HideSpawnMenu ] + - type: entity id: MarketFrontierOutpost parent: @@ -49,6 +57,13 @@ - BaseStationShuttles categories: [ HideSpawnMenu ] +- type: entity + id: DeadDropShipyardFrontierOutpost + parent: + - ShipyardFrontierOutpost + - BaseStationDeadDrops + categories: [ HideSpawnMenu ] + - type: entity id: SecurityFrontierOutpost parent: diff --git a/Resources/Prototypes/_NF/Entities/Structures/Storage/Crates/crates.yml b/Resources/Prototypes/_NF/Entities/Structures/Storage/Crates/crates.yml index eac8cbfaae1..4aba170ffe7 100644 --- a/Resources/Prototypes/_NF/Entities/Structures/Storage/Crates/crates.yml +++ b/Resources/Prototypes/_NF/Entities/Structures/Storage/Crates/crates.yml @@ -107,7 +107,12 @@ price: 7500 - type: Contraband turnInValues: - FrontierUplinkCoin: 5 + FrontierUplinkCoin: 3 + - type: DisableShipyardSale + reason: shipyard-console-contraband-onboard + allowedShipyardTypes: + - Syndicate + - BlackMarket - type: entity parent: @@ -138,7 +143,12 @@ price: 15000 - type: Contraband turnInValues: - FrontierUplinkCoin: 5 + FrontierUplinkCoin: 3 + - type: DisableShipyardSale + reason: shipyard-console-contraband-onboard + allowedShipyardTypes: + - Syndicate + - BlackMarket - type: entity parent: @@ -169,7 +179,12 @@ price: 15000 - type: Contraband turnInValues: - FrontierUplinkCoin: 5 + FrontierUplinkCoin: 3 + - type: DisableShipyardSale + reason: shipyard-console-contraband-onboard + allowedShipyardTypes: + - Syndicate + - BlackMarket - type: entity parent: CrateSecgear diff --git a/Resources/Prototypes/_NF/Entities/Structures/Wallmounts/Signs/contraband.yml b/Resources/Prototypes/_NF/Entities/Structures/Wallmounts/Signs/contraband.yml index b825df60355..a33a4a51d2f 100644 --- a/Resources/Prototypes/_NF/Entities/Structures/Wallmounts/Signs/contraband.yml +++ b/Resources/Prototypes/_NF/Entities/Structures/Wallmounts/Signs/contraband.yml @@ -1,462 +1,337 @@ - type: entity - parent: PosterContrabandFreeTonto + id: DeadDropBase + categories: [ HideSpawnMenu ] + components: + - type: PotentialDeadDrop + hintText: dead-drop-hint-poster + +- type: entity + parent: [DeadDropBase, PosterContrabandFreeTonto] id: PosterContrabandFreeTontoDD suffix: DeadDrop - components: - - type: DeadDrop - type: entity - parent: PosterContrabandAtmosiaDeclarationIndependence + parent: [DeadDropBase, PosterContrabandAtmosiaDeclarationIndependence] id: PosterContrabandAtmosiaDeclarationIndependenceDD suffix: DeadDrop - components: - - type: DeadDrop - type: entity - parent: PosterContrabandFunPolice + parent: [DeadDropBase, PosterContrabandFunPolice] id: PosterContrabandFunPoliceDD suffix: DeadDrop - components: - - type: DeadDrop - type: entity - parent: PosterContrabandLustyExomorph + parent: [DeadDropBase, PosterContrabandLustyExomorph] id: PosterContrabandLustyExomorphDD suffix: DeadDrop - components: - - type: DeadDrop - type: entity - parent: PosterContrabandSyndicateRecruitment + parent: [DeadDropBase, PosterContrabandSyndicateRecruitment] id: PosterContrabandSyndicateRecruitmentDD suffix: DeadDrop - components: - - type: DeadDrop - type: entity - parent: PosterContrabandClown + parent: [DeadDropBase, PosterContrabandClown] id: PosterContrabandClownDD suffix: DeadDrop - components: - - type: DeadDrop - type: entity - parent: PosterContrabandSmoke + parent: [DeadDropBase, PosterContrabandSmoke] id: PosterContrabandSmokeDD suffix: DeadDrop - components: - - type: DeadDrop - type: entity - parent: PosterContrabandGreyTide + parent: [DeadDropBase, PosterContrabandGreyTide] id: PosterContrabandGreyTideDD suffix: DeadDrop - components: - - type: DeadDrop - type: entity - parent: PosterContrabandMissingGloves + parent: [DeadDropBase, PosterContrabandMissingGloves] id: PosterContrabandMissingGlovesDD suffix: DeadDrop - components: - - type: DeadDrop - type: entity - parent: PosterContrabandHackingGuide + parent: [DeadDropBase, PosterContrabandHackingGuide] id: PosterContrabandHackingGuideDD suffix: DeadDrop - components: - - type: DeadDrop - type: entity - parent: PosterContrabandRIPBadger + parent: [DeadDropBase, PosterContrabandRIPBadger] id: PosterContrabandRIPBadgerDD suffix: DeadDrop - components: - - type: DeadDrop - type: entity - parent: PosterContrabandAmbrosiaVulgaris + parent: [DeadDropBase, PosterContrabandAmbrosiaVulgaris] id: PosterContrabandAmbrosiaVulgarisDD suffix: DeadDrop - components: - - type: DeadDrop - type: entity - parent: PosterContrabandDonutCorp + parent: [DeadDropBase, PosterContrabandDonutCorp] id: PosterContrabandDonutCorpDD suffix: DeadDrop - components: - - type: DeadDrop - type: entity - parent: PosterContrabandEAT + parent: [DeadDropBase, PosterContrabandEAT] id: PosterContrabandEATDD suffix: DeadDrop - components: - - type: DeadDrop - type: entity - parent: PosterContrabandTools + parent: [DeadDropBase, PosterContrabandTools] id: PosterContrabandToolsDD suffix: DeadDrop - components: - - type: DeadDrop - type: entity - parent: PosterContrabandPower + parent: [DeadDropBase, PosterContrabandPower] id: PosterContrabandPowerDD suffix: DeadDrop - components: - - type: DeadDrop - type: entity - parent: PosterContrabandSpaceCube + parent: [DeadDropBase, PosterContrabandSpaceCube] id: PosterContrabandSpaceCubeDD suffix: DeadDrop - components: - - type: DeadDrop - type: entity - parent: PosterContrabandCommunistState + parent: [DeadDropBase, PosterContrabandCommunistState] id: PosterContrabandCommunistStateDD suffix: DeadDrop - components: - - type: DeadDrop - type: entity - parent: PosterContrabandLamarr + parent: [DeadDropBase, PosterContrabandLamarr] id: PosterContrabandLamarrDD suffix: DeadDrop - components: - - type: DeadDrop - type: entity - parent: PosterContrabandBorgFancy + parent: [DeadDropBase, PosterContrabandBorgFancy] id: PosterContrabandBorgFancyDD suffix: DeadDrop - components: - - type: DeadDrop - type: entity - parent: PosterContrabandBorgFancyv2 + parent: [DeadDropBase, PosterContrabandBorgFancyv2] id: PosterContrabandBorgFancyv2DD suffix: DeadDrop - components: - - type: DeadDrop - type: entity - parent: PosterContrabandKosmicheskayaStantsiya + parent: [DeadDropBase, PosterContrabandKosmicheskayaStantsiya] id: PosterContrabandKosmicheskayaStantsiyaDD suffix: DeadDrop - components: - - type: DeadDrop - type: entity - parent: PosterContrabandRebelsUnite + parent: [DeadDropBase, PosterContrabandRebelsUnite] id: PosterContrabandRebelsUniteDD suffix: DeadDrop - components: - - type: DeadDrop - type: entity - parent: PosterContrabandC20r + parent: [DeadDropBase, PosterContrabandC20r] id: PosterContrabandC20rDD suffix: DeadDrop - components: - - type: DeadDrop - type: entity - parent: PosterContrabandHaveaPuff + parent: [DeadDropBase, PosterContrabandHaveaPuff] id: PosterContrabandHaveaPuffDD suffix: DeadDrop - components: - - type: DeadDrop - type: entity - parent: PosterContrabandRevolver + parent: [DeadDropBase, PosterContrabandRevolver] id: PosterContrabandRevolverDD suffix: DeadDrop - components: - - type: DeadDrop - type: entity - parent: PosterContrabandDDayPromo + parent: [DeadDropBase, PosterContrabandDDayPromo] id: PosterContrabandDDayPromoDD suffix: DeadDrop - components: - - type: DeadDrop - type: entity - parent: PosterContrabandSyndicatePistol + parent: [DeadDropBase, PosterContrabandSyndicatePistol] id: PosterContrabandSyndicatePistolDD suffix: DeadDrop - components: - - type: DeadDrop - type: entity - parent: PosterContrabandEnergySwords + parent: [DeadDropBase, PosterContrabandEnergySwords] id: PosterContrabandEnergySwordsDD suffix: DeadDrop - components: - - type: DeadDrop - type: entity - parent: PosterContrabandRedRum + parent: [DeadDropBase, PosterContrabandRedRum] id: PosterContrabandRedRumDD suffix: DeadDrop - components: - - type: DeadDrop - type: entity - parent: PosterContrabandCC64KAd + parent: [DeadDropBase, PosterContrabandCC64KAd] id: PosterContrabandCC64KAdDD suffix: DeadDrop - components: - - type: DeadDrop - type: entity - parent: PosterContrabandPunchShit + parent: [DeadDropBase, PosterContrabandPunchShit] id: PosterContrabandPunchShitDD suffix: DeadDrop - components: - - type: DeadDrop - type: entity - parent: PosterContrabandTheGriffin + parent: [DeadDropBase, PosterContrabandTheGriffin] id: PosterContrabandTheGriffinDD suffix: DeadDrop - components: - - type: DeadDrop - type: entity - parent: PosterContrabandFreeDrone + parent: [DeadDropBase, PosterContrabandFreeDrone] id: PosterContrabandFreeDroneDD suffix: DeadDrop - components: - - type: DeadDrop - type: entity - parent: PosterContrabandBustyBackdoorExoBabes6 + parent: [DeadDropBase, PosterContrabandBustyBackdoorExoBabes6] id: PosterContrabandBustyBackdoorExoBabes6DD suffix: DeadDrop - components: - - type: DeadDrop - type: entity - parent: PosterContrabandRobustSoftdrinks + parent: [DeadDropBase, PosterContrabandRobustSoftdrinks] id: PosterContrabandRobustSoftdrinksDD suffix: DeadDrop - components: - - type: DeadDrop - type: entity - parent: PosterContrabandShamblersJuice + parent: [DeadDropBase, PosterContrabandShamblersJuice] id: PosterContrabandShamblersJuiceDD suffix: DeadDrop - components: - - type: DeadDrop - type: entity - parent: PosterContrabandPwrGame + parent: [DeadDropBase, PosterContrabandPwrGame] id: PosterContrabandPwrGameDD suffix: DeadDrop - components: - - type: DeadDrop - type: entity - parent: PosterContrabandSunkist + parent: [DeadDropBase, PosterContrabandSunkist] id: PosterContrabandSunkistDD suffix: DeadDrop - components: - - type: DeadDrop - type: entity - parent: PosterContrabandSpaceCola + parent: [DeadDropBase, PosterContrabandSpaceCola] id: PosterContrabandSpaceColaDD suffix: DeadDrop - components: - - type: DeadDrop - type: entity - parent: PosterContrabandSpaceUp + parent: [DeadDropBase, PosterContrabandSpaceUp] id: PosterContrabandSpaceUpDD suffix: DeadDrop - components: - - type: DeadDrop - type: entity - parent: PosterContrabandKudzu + parent: [DeadDropBase, PosterContrabandKudzu] id: PosterContrabandKudzuDD suffix: DeadDrop - components: - - type: DeadDrop - type: entity - parent: PosterContrabandMaskedMen + parent: [DeadDropBase, PosterContrabandMaskedMen] id: PosterContrabandMaskedMenDD suffix: DeadDrop - components: - - type: DeadDrop - type: entity - parent: PosterContrabandUnreadableAnnouncement + parent: [DeadDropBase, PosterContrabandUnreadableAnnouncement] id: PosterContrabandUnreadableAnnouncementDD suffix: DeadDrop - components: - - type: DeadDrop - type: entity - parent: PosterContrabandFreeSyndicateEncryptionKey + parent: [DeadDropBase, PosterContrabandFreeSyndicateEncryptionKey] id: PosterContrabandFreeSyndicateEncryptionKeyDD suffix: DeadDrop - components: - - type: DeadDrop - type: entity - parent: PosterContrabandBountyHunters + parent: [DeadDropBase, PosterContrabandBountyHunters] id: PosterContrabandBountyHuntersDD suffix: DeadDrop - components: - - type: DeadDrop - type: entity - parent: PosterContrabandTheBigGasTruth + parent: [DeadDropBase, PosterContrabandTheBigGasTruth] id: PosterContrabandTheBigGasTruthDD suffix: DeadDrop - components: - - type: DeadDrop - type: entity - parent: PosterContrabandWehWatches + parent: [DeadDropBase, PosterContrabandWehWatches] id: PosterContrabandWehWatchesDD suffix: DeadDrop - components: - - type: DeadDrop - type: entity - parent: PosterContrabandVoteWeh + parent: [DeadDropBase, PosterContrabandVoteWeh] id: PosterContrabandVoteWehDD suffix: DeadDrop - components: - - type: DeadDrop # These 3 originally from VEEGEE - type: entity - parent: PosterContrabandBeachStarYamamoto + parent: [DeadDropBase, PosterContrabandBeachStarYamamoto] id: PosterContrabandBeachStarYamamotoDD suffix: DeadDrop - components: - - type: DeadDrop - type: entity - parent: PosterContrabandHighEffectEngineering + parent: [DeadDropBase, PosterContrabandHighEffectEngineering] id: PosterContrabandHighEffectEngineeringDD suffix: DeadDrop - components: - - type: DeadDrop - type: entity - parent: PosterContrabandNuclearDeviceInformational + parent: [DeadDropBase, PosterContrabandNuclearDeviceInformational] id: PosterContrabandNuclearDeviceInformationalDD suffix: DeadDrop - components: - - type: DeadDrop - type: entity - parent: PosterContrabandRise + parent: [DeadDropBase, PosterContrabandRise] id: PosterContrabandRiseDD suffix: DeadDrop - components: - - type: DeadDrop - type: entity - parent: PosterContrabandRevolt + parent: [DeadDropBase, PosterContrabandRevolt] id: PosterContrabandRevoltDD suffix: DeadDrop - components: - - type: DeadDrop - type: entity - parent: PosterContrabandMoth + parent: [DeadDropBase, PosterContrabandMoth] id: PosterContrabandMothDD suffix: DeadDrop - components: - - type: DeadDrop - type: entity - parent: PosterContrabandCybersun600 + parent: [DeadDropBase, PosterContrabandCybersun600] id: PosterContrabandCybersun600DD suffix: DeadDrop - components: - - type: DeadDrop - type: entity - parent: PosterContrabandDonk + parent: [DeadDropBase, PosterContrabandDonk] id: PosterContrabandDonkDD suffix: DeadDrop - components: - - type: DeadDrop - type: entity - parent: PosterContrabandEnlistGorlex + parent: [DeadDropBase, PosterContrabandEnlistGorlex] id: PosterContrabandEnlistGorlexDD suffix: DeadDrop - components: - - type: DeadDrop - type: entity - parent: PosterContrabandInterdyne + parent: [DeadDropBase, PosterContrabandInterdyne] id: PosterContrabandInterdyneDD suffix: DeadDrop - components: - - type: DeadDrop - type: entity - parent: PosterContrabandWaffleCorp + parent: [DeadDropBase, PosterContrabandWaffleCorp] id: PosterContrabandWaffleCorpDD suffix: DeadDrop - components: - - type: DeadDrop - type: entity - parent: NFPosterContrabandLvhiStation + parent: [DeadDropBase, NFPosterContrabandLvhiStation] id: NFPosterContrabandLvhiStationDD suffix: DeadDrop - components: - - type: DeadDrop - type: entity - parent: NFPosterContrabandLvhiShuttle + parent: [DeadDropBase, NFPosterContrabandLvhiShuttle] id: NFPosterContrabandLvhiShuttleDD suffix: DeadDrop - components: - - type: DeadDrop - type: entity - parent: NFPosterContrabandLvhiSpace + parent: [DeadDropBase, NFPosterContrabandLvhiSpace] id: NFPosterContrabandLvhiSpaceDD suffix: DeadDrop - components: - - type: DeadDrop - type: entity - parent: NFPosterContrabandFsbLogo + parent: [DeadDropBase, NFPosterContrabandFsbLogo] id: NFPosterContrabandFsbLogoDD suffix: DeadDrop - components: - - type: DeadDrop - type: entity - parent: NFPosterContrabandFsbStasis + parent: [DeadDropBase, NFPosterContrabandFsbStasis] id: NFPosterContrabandFsbStasisDD suffix: DeadDrop - components: - - type: DeadDrop - type: entity - parent: NFPosterContrabandFsbApothecary + parent: [DeadDropBase, NFPosterContrabandFsbApothecary] id: NFPosterContrabandFsbApothecaryDD suffix: DeadDrop - components: - - type: DeadDrop diff --git a/Resources/Prototypes/_NF/Events/events.yml b/Resources/Prototypes/_NF/Events/events.yml index e29b3384d9a..b8c625e4b6e 100644 --- a/Resources/Prototypes/_NF/Events/events.yml +++ b/Resources/Prototypes/_NF/Events/events.yml @@ -24,6 +24,7 @@ - id: BluespaceMcCargoCrate # Frontier - id: BluespaceSyndicateCrate # Frontier - id: BluespaceBrokenMcDelivery # Frontier + - id: SmugglingFax # Frontier - type: entityTable id: BasicAntagEventsTable diff --git a/Resources/Prototypes/_NF/Events/nf_events.yml b/Resources/Prototypes/_NF/Events/nf_events.yml index 745438428c4..55f556b8a56 100644 --- a/Resources/Prototypes/_NF/Events/nf_events.yml +++ b/Resources/Prototypes/_NF/Events/nf_events.yml @@ -55,3 +55,22 @@ - type: BluespaceCargoRule spawnerPrototype: CrateSyndicateLightSurplusBundle maximumSpawns: 2 + +- type: entity + id: SmugglingFax + parent: BaseStationEventShortDelay + components: + - type: StationEvent + weight: 8 # Fairly common. + duration: 35 + earliestStart: 30 + minimumPlayers: 20 + reoccurrenceDelay: 50 + - type: RandomFaxRule + minFaxes: 1 + maxFaxes: 2 + name: deaddrop-faxed-hint-name + prototypeId: PaperDeadDropFax + content: deaddrop-faxed-hint-content # Default contents, will be edited. + preFaxActions: + - !type:GetRandomDeadDropAction diff --git a/Resources/Prototypes/_NF/GameRules/roundstart.yml b/Resources/Prototypes/_NF/GameRules/roundstart.yml index db906c73af0..c04dc6b73f6 100644 --- a/Resources/Prototypes/_NF/GameRules/roundstart.yml +++ b/Resources/Prototypes/_NF/GameRules/roundstart.yml @@ -31,3 +31,15 @@ - id: BluespaceSyndicateFTLInterception - id: BluespaceWizardFederationScout - id: BluespaceBloodMoon + +# variation passes +- type: entity + id: FrontierRoundstartVariation + parent: BaseGameRule + components: + - type: RoundstartStationVariationRule + rules: + - id: BasicPoweredLightVariationPass + - id: BasicTrashVariationPass + - id: BasicPuddleMessVariationPass + - id: BasicDeadDropHintVariationPass diff --git a/Resources/Prototypes/_NF/GameRules/variation.yml b/Resources/Prototypes/_NF/GameRules/variation.yml new file mode 100644 index 00000000000..89466249f67 --- /dev/null +++ b/Resources/Prototypes/_NF/GameRules/variation.yml @@ -0,0 +1,6 @@ +- type: entity + id: BasicDeadDropHintVariationPass + parent: BaseVariationPass + components: + - type: DeadDropHintVariationPass + hintSpawnChance: 0.03 diff --git a/Resources/Prototypes/_NF/Guidebook/nfsd.yml b/Resources/Prototypes/_NF/Guidebook/nfsd.yml new file mode 100644 index 00000000000..2ccb23b5440 --- /dev/null +++ b/Resources/Prototypes/_NF/Guidebook/nfsd.yml @@ -0,0 +1,4 @@ +- type: guideEntry + id: Smuggling + name: guide-entry-nfsd-smuggling + text: "/ServerInfo/_NF/Guidebook/NFSD/Smuggling.xml" diff --git a/Resources/Prototypes/_NF/PointsOfInterest/anomalousgeode.yml b/Resources/Prototypes/_NF/PointsOfInterest/anomalousgeode.yml index 86ca596e6c4..9e4b351b43b 100644 --- a/Resources/Prototypes/_NF/PointsOfInterest/anomalousgeode.yml +++ b/Resources/Prototypes/_NF/PointsOfInterest/anomalousgeode.yml @@ -31,3 +31,4 @@ mapNameTemplate: 'Anomalous Geode' - type: StationJobs availableJobs: {} + - type: StationDeadDropHintExempt diff --git a/Resources/Prototypes/_NF/PointsOfInterest/anomalouslab.yml b/Resources/Prototypes/_NF/PointsOfInterest/anomalouslab.yml index fecbc5119c4..c59c246fcc6 100644 --- a/Resources/Prototypes/_NF/PointsOfInterest/anomalouslab.yml +++ b/Resources/Prototypes/_NF/PointsOfInterest/anomalouslab.yml @@ -31,3 +31,4 @@ mapNameTemplate: 'Anomalous Lab' - type: StationJobs availableJobs: {} + - type: StationDeadDropHintExempt diff --git a/Resources/Prototypes/_NF/PointsOfInterest/caseys.yml b/Resources/Prototypes/_NF/PointsOfInterest/caseys.yml index a25041b8d93..0298e0bd5de 100644 --- a/Resources/Prototypes/_NF/PointsOfInterest/caseys.yml +++ b/Resources/Prototypes/_NF/PointsOfInterest/caseys.yml @@ -9,9 +9,21 @@ # Notes: # Down at your local saloon - type: pointOfInterest - id: Caseys + id: CaseysCasino name: "Crazy Casey's Casino" rangeMin: 3250 rangeMax: 5600 iffColor: "#ffa600" #yellorange for faction style gridPath: /Maps/_NF/POI/caseyscasino.yml + +- type: gameMap + id: CaseysCasino + mapName: "Crazy Casey's Casino" + mapPath: /Maps/_NF/POI/caseyscasino.yml + minPlayers: 0 + stations: + CaseysCasino: + stationProto: DeadDropFrontierOutpost + components: + - type: StationNameSetup + mapNameTemplate: "Crazy Casey's Casino" diff --git a/Resources/Prototypes/_NF/PointsOfInterest/courthouse.yml b/Resources/Prototypes/_NF/PointsOfInterest/courthouse.yml index 3799dc4e751..549b312fbbd 100644 --- a/Resources/Prototypes/_NF/PointsOfInterest/courthouse.yml +++ b/Resources/Prototypes/_NF/PointsOfInterest/courthouse.yml @@ -16,3 +16,16 @@ iffColor: "#8e6444" #brown alwaysSpawn: true gridPath: /Maps/_NF/POI/courthouse.yml + +- type: gameMap + id: Courthouse + mapName: "Courthouse" + mapPath: /Maps/_NF/POI/courthouse.yml + minPlayers: 0 + stations: + Courthouse: + stationProto: StandardFrontierOutpost + components: + - type: StationNameSetup + mapNameTemplate: "Courthouse" + - type: StationDeadDropHintExempt diff --git a/Resources/Prototypes/_NF/PointsOfInterest/cove.yml b/Resources/Prototypes/_NF/PointsOfInterest/cove.yml index b02b9fdfcb2..1acaa85b9f9 100644 --- a/Resources/Prototypes/_NF/PointsOfInterest/cove.yml +++ b/Resources/Prototypes/_NF/PointsOfInterest/cove.yml @@ -35,3 +35,6 @@ PirateCaptain: [ 1, 1 ] PirateFirstMate: [ 1, 1 ] Pirate: [ 0, 0 ] + - type: StationDeadDropReporting + messageSet: Pirate + - type: StationDeadDropHintExempt diff --git a/Resources/Prototypes/_NF/PointsOfInterest/grifty.yml b/Resources/Prototypes/_NF/PointsOfInterest/grifty.yml index cc73e2e225e..30edd8a07ed 100644 --- a/Resources/Prototypes/_NF/PointsOfInterest/grifty.yml +++ b/Resources/Prototypes/_NF/PointsOfInterest/grifty.yml @@ -25,7 +25,7 @@ minPlayers: 0 stations: Grifty: - stationProto: ShipyardFrontierOutpost + stationProto: DeadDropShipyardFrontierOutpost components: - type: StationNameSetup mapNameTemplate: "Grifty's Gas n Grub" diff --git a/Resources/Prototypes/_NF/PointsOfInterest/lodge.yml b/Resources/Prototypes/_NF/PointsOfInterest/lodge.yml index 0daefa30ece..228142e53a3 100644 --- a/Resources/Prototypes/_NF/PointsOfInterest/lodge.yml +++ b/Resources/Prototypes/_NF/PointsOfInterest/lodge.yml @@ -32,3 +32,5 @@ availableJobs: Pilot: [ -1, -1 ] Mercenary: [ -1, -1 ] + - type: StationDeadDrop + maxDeadDrops: 1 # Fewer here, players start here. diff --git a/Resources/Prototypes/_NF/PointsOfInterest/lpbravo.yml b/Resources/Prototypes/_NF/PointsOfInterest/lpbravo.yml index f0fa910c477..0e7406abbff 100644 --- a/Resources/Prototypes/_NF/PointsOfInterest/lpbravo.yml +++ b/Resources/Prototypes/_NF/PointsOfInterest/lpbravo.yml @@ -33,3 +33,4 @@ mapNameTemplate: 'Listening Point Bravo' - type: StationJobs availableJobs: {} + - type: StationDeadDropHintExempt diff --git a/Resources/Prototypes/_NF/PointsOfInterest/mchobo.yml b/Resources/Prototypes/_NF/PointsOfInterest/mchobo.yml index 06854fff268..dc583abbd6f 100644 --- a/Resources/Prototypes/_NF/PointsOfInterest/mchobo.yml +++ b/Resources/Prototypes/_NF/PointsOfInterest/mchobo.yml @@ -26,7 +26,7 @@ minPlayers: 0 stations: McHobo: - stationProto: ShipyardFrontierOutpost + stationProto: DeadDropShipyardFrontierOutpost components: - type: StationNameSetup mapNameTemplate: "Derelict McCargo" diff --git a/Resources/Prototypes/_NF/PointsOfInterest/nfsd.yml b/Resources/Prototypes/_NF/PointsOfInterest/nfsd.yml index dc532ae6448..ee61a171144 100644 --- a/Resources/Prototypes/_NF/PointsOfInterest/nfsd.yml +++ b/Resources/Prototypes/_NF/PointsOfInterest/nfsd.yml @@ -41,3 +41,6 @@ Cadet: [ 0, 0 ] # Others: PublicAffairsLiaison: [ 1, 1 ] + - type: StationDeadDropReporting + messageSet: Nfsd + - type: StationDeadDropHintExempt diff --git a/Resources/Prototypes/_NF/PointsOfInterest/thepit.yml b/Resources/Prototypes/_NF/PointsOfInterest/thepit.yml index 767b33a4293..6f4ea3741d7 100644 --- a/Resources/Prototypes/_NF/PointsOfInterest/thepit.yml +++ b/Resources/Prototypes/_NF/PointsOfInterest/thepit.yml @@ -17,3 +17,15 @@ spawnChance: 1 iffColor: "#ffa600" #yellorange for faction style gridPath: /Maps/_NF/POI/arena.yml + +- type: gameMap + id: ThePit + mapName: "The Pit" + mapPath: /Maps/_NF/POI/arena.yml + minPlayers: 0 + stations: + ThePit: + stationProto: DeadDropFrontierOutpost + components: + - type: StationNameSetup + mapNameTemplate: "The Pit" diff --git a/Resources/Prototypes/_NF/PointsOfInterest/tinniasrest.yml b/Resources/Prototypes/_NF/PointsOfInterest/tinniasrest.yml index 80bd318dcb7..766e505e452 100644 --- a/Resources/Prototypes/_NF/PointsOfInterest/tinniasrest.yml +++ b/Resources/Prototypes/_NF/PointsOfInterest/tinniasrest.yml @@ -15,3 +15,15 @@ rangeMax: 2900 iffColor: "#ffa600" #yellorange for faction style gridPath: /Maps/_NF/POI/tinnia.yml + +- type: gameMap + id: Tinnia + mapName: "Tinnia's Rest" + mapPath: /Maps/_NF/POI/tinnia.yml + minPlayers: 0 + stations: + Tinnia: + stationProto: DeadDropFrontierOutpost + components: + - type: StationNameSetup + mapNameTemplate: "Tinnia's Rest" diff --git a/Resources/Prototypes/_NF/PointsOfInterest/trade.yml b/Resources/Prototypes/_NF/PointsOfInterest/trade.yml index 9f6294e60bf..98d5c4d6486 100644 --- a/Resources/Prototypes/_NF/PointsOfInterest/trade.yml +++ b/Resources/Prototypes/_NF/PointsOfInterest/trade.yml @@ -30,4 +30,6 @@ components: - type: StationNameSetup mapNameTemplate: 'Trade Outpost' + - type: StationDeadDrop + maxDeadDrops: 3 # Many here, it's huge. \ No newline at end of file diff --git a/Resources/Prototypes/_NF/Roles/Jobs/Nfsd/detectivenf.yml b/Resources/Prototypes/_NF/Roles/Jobs/Nfsd/detectivenf.yml index 5cad7beace9..a8a5c676b3f 100644 --- a/Resources/Prototypes/_NF/Roles/Jobs/Nfsd/detectivenf.yml +++ b/Resources/Prototypes/_NF/Roles/Jobs/Nfsd/detectivenf.yml @@ -44,4 +44,5 @@ - ForensicScanner - FrontierUplinkCoin10 - LogProbeCartridge - - RubberStampDetective \ No newline at end of file + - RubberStampDetective + - ContrabandForensicsModule diff --git a/Resources/Prototypes/_NF/SectorServices/deaddrops.yml b/Resources/Prototypes/_NF/SectorServices/deaddrops.yml new file mode 100644 index 00000000000..8d143806a44 --- /dev/null +++ b/Resources/Prototypes/_NF/SectorServices/deaddrops.yml @@ -0,0 +1,5 @@ +- type: sectorService + id: DeadDrops + components: + - type: SectorDeadDrop + fakeDeadDropHints: FakeDeadDropHints \ No newline at end of file diff --git a/Resources/Prototypes/_NF/Smuggling/fake_dead_drop_hints.yml b/Resources/Prototypes/_NF/Smuggling/fake_dead_drop_hints.yml new file mode 100644 index 00000000000..0e2e11f08c9 --- /dev/null +++ b/Resources/Prototypes/_NF/Smuggling/fake_dead_drop_hints.yml @@ -0,0 +1,5 @@ +- type: localizedDataset + id: FakeDeadDropHints + values: + prefix: dead-drop-fake-hint- + count: 55 \ No newline at end of file diff --git a/Resources/Prototypes/_NF/Smuggling/report_message_sets.yml b/Resources/Prototypes/_NF/Smuggling/report_message_sets.yml new file mode 100644 index 00000000000..8bce5f58e82 --- /dev/null +++ b/Resources/Prototypes/_NF/Smuggling/report_message_sets.yml @@ -0,0 +1,33 @@ +- type: smugglingReportMessageSet + id: Nfsd + channel: Nfsd + messageSets: + - prob: 0.8 + messages: + - hourlyThreshold: 1 + type: General + message: smuggling-report-nfsd-general + - hourlyThreshold: 2 + type: DeadDropStationWithRandomAlt + message: smuggling-report-nfsd-alternative + - type: DeadDropStation + message: smuggling-report-nfsd-specific + - prob: 0.8 + minDelay: 14 + maxDelay: 16 + messages: + - type: PodLocation + message: smuggling-report-nfsd-pod + maxError: 900 # Should be slightly larger than the giant radar range + +- type: smugglingReportMessageSet + id: Pirate + channel: Freelance + messageSets: + - prob: 0.333 + minDelay: 14 + maxDelay: 16 + messages: + - type: PodLocation + message: smuggling-report-pirate + maxError: 600 # Pirates don't have giant radars, let's cut them a break. diff --git a/Resources/Prototypes/_NF/game_presets.yml b/Resources/Prototypes/_NF/game_presets.yml index 6fc0ae34d0a..fecaca5380c 100644 --- a/Resources/Prototypes/_NF/game_presets.yml +++ b/Resources/Prototypes/_NF/game_presets.yml @@ -9,4 +9,4 @@ - Adventure - BasicStationEventScheduler - BluespaceEventScheduler - - BasicRoundstartVariation + - FrontierRoundstartVariation diff --git a/Resources/ServerInfo/_NF/Guidebook/NFSD/Smuggling.xml b/Resources/ServerInfo/_NF/Guidebook/NFSD/Smuggling.xml new file mode 100644 index 00000000000..f1f1f88ef05 --- /dev/null +++ b/Resources/ServerInfo/_NF/Guidebook/NFSD/Smuggling.xml @@ -0,0 +1,46 @@ + + + # Smuggling Prevention + + As an officer in the NFSD, part of your job is to prevent smuggling. + + Smugglers use [color=#9ef5ff]dead drops[/color], objects where the Syndicate leaves notes with clues to the location of [color=#c4261d]drop pods[/color] filled with contraband. + + Remember: + - These are spread across the seedier points of interest across the sector. + - Use any clue you might find lying around. + - Ask around to see if the public's seen suspicious activity. + - Be on the lookout for strange traffic patterns. + + As NFSD officers, part of your job is to prevent smuggling, and to catch smugglers in the act. + + Keep Nanotrasen's monopoly on commerce in the sector strong, for a secure future. + + + + + + ## Notifications + + When a note is taken from a [color=#9ef5ff]dead drop[/color], a [color=#c4261d]drop pod[/color] is teleported into the sector. This [color=#c4261d]drop pod[/color] contains cargo, some of it contraband, and these illicit goods tend to be sold for profit. + + With cutting-edge Nanotrasen bluespace technology, [bold]we can get signatures when these notes are taken[/bold], and, with some delay, get [bold]the approximate location of a [color=#c4261d]drop pod[/color][/bold]. + + Due to the nature of bluespace, [bold]these notifications are not completely reliable[/bold], but that is no excuse for laziness. Remain vigilant. + + ## Forensics + + As an incentive to prevent smuggling and catch active smugglers, a [color=#5d8c45]contraband forensics module[/color] comes standard in a Detective's gear. [bold]Using these rewards your department for good field work[/bold]. + + [color=#5d8c45]Scanning[/color] active or previously used [color=#9ef5ff]dead drops[/color], and Syndicate [color=#c4261d]drop pods[/color] credits the NFSD's bank with spesos and FUCs. FUCs will be paid out to the scanning officer when enough have been collected. + + [bold]You get bigger rewards by being the first to a [color=#9ef5ff]dead drop[/color], so get moving![/bold] + + + + + + + [bold]Remember to use any DNA you find to track down and prosecute the smugglers.[/bold] It is one of your duties as an NFSD officer to keep sector commerce legal. + + \ No newline at end of file diff --git a/Resources/Textures/_NF/Objects/Devices/cartridge.rsi/contraband_forensics.png b/Resources/Textures/_NF/Objects/Devices/cartridge.rsi/contraband_forensics.png new file mode 100644 index 0000000000000000000000000000000000000000..6d3adb75fe605f8f815b2c7b230dbccf9a6d25b2 GIT binary patch literal 202 zcmeAS@N?(olHy`uVBq!ia0vp^3LwnE3?yBabR7dyiUB?$u0YyAT_)L6>;M1%YmAJ7 z3tWGnHQUj~XcuBq8fGLbDY7l_hbd4IcS(?6FauB#6x5d0UIhyKdb&7*5plXJ#I2VD@8-a^`*Xi>+c0gYtm{LB^fw4(9i- p%)6TKE&FT@f6ATgSAVBhGq4*n+H-9aH31sT;OXk;vd$@?2>{84ORxX{ literal 0 HcmV?d00001 diff --git a/Resources/Textures/_NF/Objects/Devices/cartridge.rsi/meta.json b/Resources/Textures/_NF/Objects/Devices/cartridge.rsi/meta.json new file mode 100644 index 00000000000..68d2b20dd41 --- /dev/null +++ b/Resources/Textures/_NF/Objects/Devices/cartridge.rsi/meta.json @@ -0,0 +1,14 @@ +{ + "version": 1, + "license": "CC-BY-SA-3.0", + "copyright": "contraband_forensics made by Whatstone (Discord)", + "size": { + "x": 32, + "y": 32 + }, + "states": [ + { + "name": "contraband_forensics" + } + ] +} diff --git a/Resources/Textures/_NF/Objects/Devices/forensic_scanner.rsi/forensiccartridge.png b/Resources/Textures/_NF/Objects/Devices/forensic_scanner.rsi/forensiccartridge.png new file mode 100644 index 0000000000000000000000000000000000000000..7a4ce9583056cc71e8752bf007ee727338b96a46 GIT binary patch literal 385 zcmeAS@N?(olHy`uVBq!ia0vp^4nVBH!3-p)I`?e@QceLrA+8`n69)zk@%;M!1snE? z3o|%aDY%EH^>^j(-LW7o(Ou0!K{rvpdPi2`oWPKNX9IPaHAY6so>~sC$^~XwvWhc& z*pxW|Xo^@#kY6yve{3M6v3D_0#SKpv#}JRsx07!3H7oG6<#GpoY_Q(@fAzXl0oB6h zySg*yo438^Z=JTz+VZC?1K+fS=(Uw9%-i%G(t8RoJhLugu}OK--^jyx;TfaA&3?)A zI;68>ykYO z)8*A4bvM+1J&`y&UO(J=3Zu$|x0mPe|IPh;ch|84@A`x;_*Z?ucHewG=V{mJd;K)O ztvktmZF7A}$vK7E%lAxw&A1Ua>-}2R=VkeOU#SKzI4r#HgL?frVZ$dP7kP^!jsyM3 N;OXk;vd$@?2>=jGr1t;- literal 0 HcmV?d00001 diff --git a/Resources/Textures/_NF/Objects/Devices/forensic_scanner.rsi/forensicnew.png b/Resources/Textures/_NF/Objects/Devices/forensic_scanner.rsi/forensicnew.png new file mode 100644 index 0000000000000000000000000000000000000000..3aa0cb605f727d278c87db0c1670c8b32acd6a56 GIT binary patch literal 736 zcmV<60w4W}P)HI(6&ns6z|VW-nPfbZV#2c4!_X zgHF;VAXb}4;O+^MHz?xJA<1Cc<#kBzd{3XxG^ASE`@zGz^X~rNeee73?g0{sL?V$$ zB$EFVpIc-2e$oN}vs!lj|99YX*%>$QB=v6$jhORWYDz^cwt~VYtlm)>BpSYwc)8X? zG_s6LCN0{ww?6=YHF9wi)*p5N0LG{&=W|)Cti;BO>3UuC)Ac&GJ{};OJ6}k>u8V$( zLSQr-o^wr7-{AlrE=5g4QPc4HKdWK7T@~(gYz+qZH-5fiVhg zPebAPi_s34lKzan!Weag3n!jfMW@{oqWl4XPP>J8V%2?77Zf$kv8Yi@Lw{zlfg52S z_B|B{hJ@#jPfo?d>w#d%(|~nVg?UJ!rc}><)6nmyz**O4G#eHG7|jNrW$ukN)(uJ- z%%5S$2BatiibB8`J%1Z#-Qg2pO8SlnFso&sSuOi|M`fhbnJXCpK)gL3ay$o(W&`0n zWc0Xe)_?b)G*;jB7#yV2Zi$@ZznsN|d6A#4hNM&!H4WKZ-nPMc+ykyr|J%+N+{K2c z{?H!4(e=70*DCuW&jxca5DcMK*&iCu7)3UhchucBxId}?w7oG_e`Ewu%0P&MwZtsm zZtvpF*IiG8dCn4|AQ?BrWrH6@+z0@U4Q|@{Bg4?Y1WSFm4nmYm-^3_oXcz{lVFCbd z-cBNVaT&4SzAh8~lhXGz#J_~04~fgr{cf9g&hMPV?F9Hs^(7LCL?V&=+x!A>F&t^M S3pjHC0000