diff --git a/Content.Server/Cargo/Systems/PricingSystem.cs b/Content.Server/Cargo/Systems/PricingSystem.cs index 7b777d233f5..e301ea83206 100644 --- a/Content.Server/Cargo/Systems/PricingSystem.cs +++ b/Content.Server/Cargo/Systems/PricingSystem.cs @@ -283,6 +283,35 @@ public double GetPrice(EntityUid uid, bool includeContents = true) return price; } + // Begin Frontier - GetPrice variant that uses predicate + /// + /// Appraises an entity, returning its price. Respects predicate - an entity that is excluded will be removed from the + /// + /// The entity to appraise. + /// Whether to examine its contents. + /// An optional predicate that controls whether or not the entity or its children are counted toward the total. + /// The price of the entity. + public double GetPriceConditional(EntityUid uid, bool includeContents = true, Func? predicate = null) + { + if (predicate is not null && !predicate(uid)) + return 0.0; + + var price = GetPrice(uid, false); + + if (includeContents && TryComp(uid, out var containers)) + { + foreach (var container in containers.Containers.Values) + { + foreach (var ent in container.ContainedEntities) + { + price += GetPriceConditional(ent, true, predicate); + } + } + } + return price; + } + // End Frontier - GetPrice variant that uses predicate + private double GetMaterialsPrice(EntityUid uid) { double price = 0; @@ -441,7 +470,7 @@ public double AppraiseGrid(EntityUid grid, Func? predicate = nu { if (predicate is null || predicate(child)) { - var subPrice = GetPrice(child); + var subPrice = GetPriceConditional(child, true, predicate); // Frontier: GetPrice(targetId, out var deed)) - sellValue = (int)_pricing.AppraiseGrid((EntityUid)(deed?.ShuttleUid!)); + sellValue = (int)_pricing.AppraiseGrid((EntityUid)(deed?.ShuttleUid!), LacksPreserveOnSaleComp); + // Adjust for taxes sellValue = CalculateShipResaleValue((shipyardConsoleUid, component), sellValue); } @@ -376,7 +378,7 @@ 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 disableSaleQuery = GetEntityQuery(); var xformQuery = GetEntityQuery(); var disableSaleMsg = FindDisableShipyardSaleObjects(shuttleUid, (ShipyardConsoleUiKey)args.UiKey, disableSaleQuery, xformQuery); if (disableSaleMsg != null) @@ -386,7 +388,7 @@ public void OnSellMessage(EntityUid uid, ShipyardConsoleComponent component, Shi return; } - var saleResult = TrySellShuttle(stationUid, shuttleUid, out var bill); + var saleResult = TrySellShuttle(stationUid, shuttleUid, uid, out var bill); if (saleResult.Error != ShipyardSaleError.Success) { switch (saleResult.Error) @@ -486,7 +488,7 @@ private void OnConsoleUIOpened(EntityUid uid, ShipyardConsoleComponent component int sellValue = 0; if (deed?.ShuttleUid != null) { - sellValue = (int)_pricing.AppraiseGrid((EntityUid)(deed?.ShuttleUid!)); + sellValue = (int)_pricing.AppraiseGrid((EntityUid)(deed?.ShuttleUid!), LacksPreserveOnSaleComp); sellValue = CalculateShipResaleValue((uid, component), sellValue); } @@ -579,7 +581,7 @@ private void OnItemSlotChanged(EntityUid uid, ShipyardConsoleComponent component int sellValue = 0; if (deed?.ShuttleUid != null) { - sellValue = (int)_pricing.AppraiseGrid(deed.ShuttleUid.Value); + sellValue = (int)_pricing.AppraiseGrid(deed.ShuttleUid.Value, LacksPreserveOnSaleComp); sellValue = CalculateShipResaleValue((uid, component), sellValue); } @@ -627,13 +629,14 @@ private void OnItemSlotChanged(EntityUid uid, ShipyardConsoleComponent component } /// - /// Looks for a living, sapient being aboard a particular entity. + /// Looks for any entities marked as preventing sale on a shuttle /// - /// The entity to search (e.g. a shuttle, a station) - /// A query to get the MobState from an entity + /// The entity to search (e.g. a shuttle, a station) + /// The UI key of the current shipyard console. Used to see if the shipyard should ignore this check + /// A query to get any marked objects 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) + /// The reason that a shuttle should be blocked from sale, null otherwise. + public string? FindDisableShipyardSaleObjects(EntityUid shuttle, ShipyardConsoleUiKey key, EntityQuery disableSaleQuery, EntityQuery xformQuery) { var xform = xformQuery.GetComponent(shuttle); var childEnumerator = xform.ChildEnumerator; @@ -641,8 +644,11 @@ private void OnItemSlotChanged(EntityUid uid, ShipyardConsoleComponent component while (childEnumerator.MoveNext(out var child)) { if (disableSaleQuery.TryGetComponent(child, out var disableSale) + && disableSale.BlockSale is true && !disableSale.AllowedShipyardTypes.Contains(key)) - return disableSale.Reason; + { + return disableSale.Reason ?? "shipyard-console-fallback-prevent-sale"; + } } return null; @@ -784,7 +790,7 @@ private void OnInitDeedSpawner(EntityUid uid, StationDeedSpawnerComponent compon if (xform.GridUid == null) return; - if (!TryComp(xform.GridUid.Value, out var shuttleDeed) || !TryComp(xform.GridUid.Value, out var shuttle) || !HasComp(xform.GridUid.Value) || shuttle == null || ShipyardMap == null) + if (!TryComp(xform.GridUid.Value, out var shuttleDeed) || !TryComp(xform.GridUid.Value, out var shuttle) || !HasComp(xform.GridUid.Value) || shuttle == null || ShipyardMap == null) return; var output = DeedRegex.Replace($"{shuttleDeed.ShuttleOwner}", ""); // Removes content inside parentheses along with parentheses and a preceding space diff --git a/Content.Server/_NF/Shipyard/Systems/ShipyardSystem.cs b/Content.Server/_NF/Shipyard/Systems/ShipyardSystem.cs index 2a42bc97ec8..5133ced2719 100644 --- a/Content.Server/_NF/Shipyard/Systems/ShipyardSystem.cs +++ b/Content.Server/_NF/Shipyard/Systems/ShipyardSystem.cs @@ -4,6 +4,7 @@ using Content.Server.Cargo.Systems; using Content.Server.Station.Systems; using Content.Shared._NF.Shipyard.Components; +using Content.Server._NF.Shipyard.Components; using Content.Shared._NF.Shipyard; using Content.Shared.GameTicking; using Robust.Server.GameObjects; @@ -33,6 +34,7 @@ public sealed partial class ShipyardSystem : SharedShipyardSystem [Dependency] private readonly MapLoaderSystem _mapLoader = default!; [Dependency] private readonly MetaDataSystem _metaData = default!; [Dependency] private readonly MapSystem _map = default!; + [Dependency] private readonly SharedTransformSystem _transform = default!; public MapId? ShipyardMap { get; private set; } private float _shuttleIndex; @@ -198,11 +200,12 @@ private bool TryAddShuttle(string shuttlePath, [NotNullWhen(true)] out EntityUid } /// - /// Checks a shuttle to make sure that it is docked to the given station, and that there are no lifeforms aboard. Then it appraises the grid, outputs to the server log, and deletes the grid + /// Checks a shuttle to make sure that it is docked to the given station, and that there are no lifeforms aboard. Then it teleports tagged items on top of the console, appraises the grid, outputs to the server log, and deletes the grid /// /// The ID of the station that the shuttle is docked to /// The grid ID of the shuttle to be appraised and sold - public ShipyardSaleResult TrySellShuttle(EntityUid stationUid, EntityUid shuttleUid, out int bill) + /// The ID of the console being used to sell the ship + public ShipyardSaleResult TrySellShuttle(EntityUid stationUid, EntityUid shuttleUid, EntityUid consoleUid, out int bill) { ShipyardSaleResult result = new ShipyardSaleResult(); bill = 0; @@ -268,7 +271,12 @@ public ShipyardSaleResult TrySellShuttle(EntityUid stationUid, EntityUid shuttle _station.DeleteStation(shuttleStationUid); } - bill = (int)_pricing.AppraiseGrid(shuttleUid); + if (TryComp(consoleUid, out var comp)) + { + CleanGrid(shuttleUid, consoleUid); + } + + bill = (int)_pricing.AppraiseGrid(shuttleUid, LacksPreserveOnSaleComp); QueueDel(shuttleUid); _sawmill.Info($"Sold shuttle {shuttleUid} for {bill}"); @@ -279,6 +287,49 @@ public ShipyardSaleResult TrySellShuttle(EntityUid stationUid, EntityUid shuttle return result; } + private void CleanGrid(EntityUid grid, EntityUid destination) + { + var xform = Transform(grid); + var enumerator = xform.ChildEnumerator; + var entitiesToPreserve = new List(); + + while (enumerator.MoveNext(out var child)) + { + FindEntitiesToPreserve(child, ref entitiesToPreserve); + } + foreach (var ent in entitiesToPreserve) + { + // Teleport this item and all its children to the floor (or space). + _transform.SetCoordinates(ent, new EntityCoordinates(destination, 0, 0)); + _transform.AttachToGridOrMap(ent); + } + } + + // checks if something has the ShipyardPreserveOnSaleComponent and if it does, adds it to the list + private void FindEntitiesToPreserve(EntityUid entity, ref List output) + { + if (TryComp(entity, out var comp) && comp.PreserveOnSale == true) + { + output.Add(entity); + return; + } + else if (TryComp(entity, out var containers)) + { + foreach (var container in containers.Containers.Values) + { + foreach (var ent in container.ContainedEntities) + { + FindEntitiesToPreserve(ent, ref output); + } + } + } + } + + // returns false if it has ShipyardPreserveOnSaleComponent, true otherwise + private bool LacksPreserveOnSaleComp(EntityUid uid) + { + return !TryComp(uid, out var comp) || comp.PreserveOnSale == false; + } private void CleanupShipyard() { if (ShipyardMap == null || !_mapManager.MapExists(ShipyardMap.Value)) diff --git a/Content.Shared/_NF/Shipyard/Components/DisableShipyardSaleComponent.cs b/Content.Shared/_NF/Shipyard/Components/DisableShipyardSaleComponent.cs deleted file mode 100644 index e2d151f1b6b..00000000000 --- a/Content.Shared/_NF/Shipyard/Components/DisableShipyardSaleComponent.cs +++ /dev/null @@ -1,22 +0,0 @@ -using Content.Shared._NF.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/Shipyard/Components/ShipyardSellConditionComponent.cs b/Content.Shared/_NF/Shipyard/Components/ShipyardSellConditionComponent.cs new file mode 100644 index 00000000000..c9712ab5cc2 --- /dev/null +++ b/Content.Shared/_NF/Shipyard/Components/ShipyardSellConditionComponent.cs @@ -0,0 +1,34 @@ +namespace Content.Shared._NF.Shipyard.Components; + +/// +/// A component that disables the sale of the ship it's on from a shipyard console. +/// +[RegisterComponent] +public sealed partial class ShipyardSellConditionComponent : Component +{ + /// + /// Whether this item is preserved on shipyard sale. + /// + [DataField] + public bool PreserveOnSale = false; + + /// + /// Whether this item prevents shipyard sale. + /// + [DataField] + public bool BlockSale = false; + + /// + /// The message to print off when a shipyard sale is disabled. + /// + [DataField] + public LocId? Reason; + + /// + /// The console types that should allow selling this object if BlockSale is true. + /// + [DataField] + public List AllowedShipyardTypes = new(); + + +} 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 52ef66dcc61..ed538bcb3de 100644 --- a/Resources/Locale/en-US/_NF/shipyard/shipyard-console-component.ftl +++ b/Resources/Locale/en-US/_NF/shipyard/shipyard-console-component.ftl @@ -26,6 +26,7 @@ shipyard-console-invalid-voucher-type = This voucher cannot be used at this cons 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-fallback-prevent-sale = YML-class bugs detected onboard. Please file a bug report when possible. shipyard-console-menu-size-label = Size:{" "} shipyard-console-menu-class-label = Class:{" "} @@ -77,4 +78,4 @@ shipyard-console-engine-APU = APU shipyard-console-engine-Welding = Welding Fuel shipyard-console-engine-Plasma = Plasma shipyard-console-engine-Uranium = Uranium -shipyard-console-engine-Bananium = Bananium \ No newline at end of file +shipyard-console-engine-Bananium = Bananium diff --git a/Resources/Prototypes/Entities/Objects/Devices/pda.yml b/Resources/Prototypes/Entities/Objects/Devices/pda.yml index 03d1d843101..982c9f9bbbb 100644 --- a/Resources/Prototypes/Entities/Objects/Devices/pda.yml +++ b/Resources/Prototypes/Entities/Objects/Devices/pda.yml @@ -141,6 +141,8 @@ wipeVerbText: pda-wipe-device-verb-text # Frontier wipeVerbPopup: pda-wiped-device # Frontier mindRoles: [] # Frontier: no default value here + - type: ShipyardSellCondition # Frontier: Teleport to shipyard console on sale + preserveOnSale: true - type: entity parent: BasePDA diff --git a/Resources/Prototypes/Entities/Objects/Misc/dat_fukken_disk.yml b/Resources/Prototypes/Entities/Objects/Misc/dat_fukken_disk.yml index 56e0e46d8a2..3c5e0a8c4d6 100644 --- a/Resources/Prototypes/Entities/Objects/Misc/dat_fukken_disk.yml +++ b/Resources/Prototypes/Entities/Objects/Misc/dat_fukken_disk.yml @@ -22,7 +22,8 @@ - HighRiskItem - type: StealTarget stealGroup: NukeDisk - - type: DisableShipyardSale # Frontier + - type: ShipyardSellCondition # Frontier + blockSale: true # Frontier reason: shipyard-console-station-resources # Frontier allowedShipyardTypes: # Frontier: mail profits removed, sector in shambles - Syndicate # Frontier diff --git a/Resources/Prototypes/Entities/Objects/Misc/identification_cards.yml b/Resources/Prototypes/Entities/Objects/Misc/identification_cards.yml index 7fc28617a17..4991df178e6 100644 --- a/Resources/Prototypes/Entities/Objects/Misc/identification_cards.yml +++ b/Resources/Prototypes/Entities/Objects/Misc/identification_cards.yml @@ -28,6 +28,8 @@ - type: StaticPrice # Frontier price: 5 # Frontier The card need to cost less then the PDA that comes with a card and a pen in it. - type: CargoSellBlacklist # Frontier + - type: ShipyardSellCondition # Frontier: Teleport to shipyard console on sale + preserveOnSale: true #IDs with layers diff --git a/Resources/Prototypes/Entities/Objects/Misc/space_cash.yml b/Resources/Prototypes/Entities/Objects/Misc/space_cash.yml index c26ca15099f..0af2e744c3e 100644 --- a/Resources/Prototypes/Entities/Objects/Misc/space_cash.yml +++ b/Resources/Prototypes/Entities/Objects/Misc/space_cash.yml @@ -62,6 +62,8 @@ - ItemMask - type: Appearance - type: CargoSellBlacklist + - type: ShipyardSellCondition # Frontier: Teleport to shipyard console on sale + preserveOnSale: true - type: material id: Credit diff --git a/Resources/Prototypes/Entities/Structures/Machines/nuke.yml b/Resources/Prototypes/Entities/Structures/Machines/nuke.yml index d7179ed6f91..a28db72e888 100644 --- a/Resources/Prototypes/Entities/Structures/Machines/nuke.yml +++ b/Resources/Prototypes/Entities/Structures/Machines/nuke.yml @@ -111,7 +111,8 @@ follow: true location: nuclear bomb - type: FTLSmashImmune - - type: DisableShipyardSale # Frontier + - type: ShipyardSellCondition # Frontier + blockSale: true # Frontier reason: shipyard-console-dangerous-materials # Frontier allowedShipyardTypes: # Frontier: - Syndicate # Frontier diff --git a/Resources/Prototypes/Nyanotrasen/Entities/Structures/Machines/mailTeleporter.yml b/Resources/Prototypes/Nyanotrasen/Entities/Structures/Machines/mailTeleporter.yml index d3151c7a1eb..41c079280d5 100644 --- a/Resources/Prototypes/Nyanotrasen/Entities/Structures/Machines/mailTeleporter.yml +++ b/Resources/Prototypes/Nyanotrasen/Entities/Structures/Machines/mailTeleporter.yml @@ -4,7 +4,7 @@ name: mail teleporter description: Teleports mail addressed to the crew of this station. components: - - type: MailTeleporter + - type: MailTeleporter - type: InteractionOutline - type: Physics bodyType: Static @@ -39,7 +39,8 @@ True: {visible: true} False: {visible: false} - type: PowerSwitch - - type: DisableShipyardSale # Frontier + - type: ShipyardSellCondition # Frontier + blockSale: true # Frontier reason: shipyard-console-station-resources # Frontier allowedShipyardTypes: # Frontier: mail profits removed, sector in shambles - Syndicate # Frontier diff --git a/Resources/Prototypes/_NF/Entities/Structures/Storage/Crates/crates.yml b/Resources/Prototypes/_NF/Entities/Structures/Storage/Crates/crates.yml index eb4e03daabc..62758c80856 100644 --- a/Resources/Prototypes/_NF/Entities/Structures/Storage/Crates/crates.yml +++ b/Resources/Prototypes/_NF/Entities/Structures/Storage/Crates/crates.yml @@ -138,7 +138,8 @@ turnInValues: FrontierUplinkCoin: 3 Doubloon: 2 - - type: DisableShipyardSale + - type: ShipyardSellCondition + blockSale: true reason: shipyard-console-contraband-onboard allowedShipyardTypes: - Syndicate