diff --git a/Content.Shared/_DV/Chemistry/Components/CartridgeFabricatorComponent.cs b/Content.Shared/_DV/Chemistry/Components/CartridgeFabricatorComponent.cs new file mode 100644 index 00000000000..5df21a5f693 --- /dev/null +++ b/Content.Shared/_DV/Chemistry/Components/CartridgeFabricatorComponent.cs @@ -0,0 +1,24 @@ +using Robust.Shared.Audio; + +namespace Content.Shared._DV.Chemistry.Components; + +[RegisterComponent] +public sealed partial class CartridgeFabricatorComponent : Component +{ + [DataField] + public List Whitelist = []; + + [DataField] + public string InputSolution = "drink"; + + [DataField] + public string OutputSolution = "cartridge"; + + public bool Emagged = false; + + [DataField] + public SoundSpecifier SuccessSound = new SoundPathSpecifier("/Audio/Machines/tray_eject.ogg"); + + [DataField] + public SoundSpecifier FailureSound = new SoundPathSpecifier("/Audio/Machines/custom_deny.ogg"); +} diff --git a/Content.Shared/_DV/Chemistry/Systems/CartridgeFabricatorSystem.cs b/Content.Shared/_DV/Chemistry/Systems/CartridgeFabricatorSystem.cs new file mode 100644 index 00000000000..731d07c8efd --- /dev/null +++ b/Content.Shared/_DV/Chemistry/Systems/CartridgeFabricatorSystem.cs @@ -0,0 +1,137 @@ +using System.Linq; +using Content.Shared._DV.Chemistry.Components; +using Content.Shared.Power.EntitySystems; +using Content.Shared.Chemistry.Components; +using Content.Shared.Chemistry.Components.SolutionManager; +using Content.Shared.Chemistry.EntitySystems; +using Content.Shared.Chemistry.Reagent; +using Content.Shared.Emag.Systems; +using Content.Shared.Hands.EntitySystems; +using Content.Shared.Interaction; +using Content.Shared.Labels.Components; +using Content.Shared.Popups; +using Content.Shared.Tag; +using Robust.Shared.Audio.Systems; +using Robust.Shared.Prototypes; +using Robust.Shared.Network; +using Content.Shared.Labels.EntitySystems; + +namespace Content.Shared._DV.Chemistry.Systems; + +public sealed class CartridgeFabricatorSystem : EntitySystem +{ + [Dependency] private readonly INetManager _net = default!; + [Dependency] private readonly SharedSolutionContainerSystem _container = default!; + [Dependency] private readonly SharedPopupSystem _popup = default!; + [Dependency] private readonly TagSystem _tags = default!; + [Dependency] private readonly SharedHandsSystem _hands = default!; + [Dependency] private readonly SharedAudioSystem _audio = default!; + [Dependency] private readonly IPrototypeManager _prototype = default!; + [Dependency] private readonly SharedPowerReceiverSystem _power = default!; + [Dependency] private readonly SharedLabelSystem _labels = default!; + private static readonly ProtoId[] BottleTags = ["Bottle"]; + + public override void Initialize() + { + base.Initialize(); + + SubscribeLocalEvent(OnInteractUsing); + + SubscribeLocalEvent(OnAttemptEmag); + SubscribeLocalEvent(OnEmagged); + } + + private bool ContainsDisallowedReagents(CartridgeFabricatorComponent fab, Solution solution) + { + foreach (ReagentQuantity reagent in solution.Contents) + { + _prototype.TryIndex(reagent.Reagent.Prototype, out var prototype); + if (fab.Whitelist.Any(entry => entry != prototype?.Group)) + { + return true; + } + } + return false; + } + + private void OnInteractUsing(Entity entity, ref InteractUsingEvent args) + { + if (args.Handled || !_power.IsPowered(entity.Owner)) + return; + + if (!TryComp(args.Used, out SolutionContainerManagerComponent? manager)) + return; + + if (!_tags.HasAnyTag(args.Used, BottleTags)) + return; + + if (!_container.TryGetSolution((args.Used, manager), + entity.Comp.InputSolution, + out var _, + out var fromSolution)) + return; + + // This looks to be something we can handle + args.Handled = true; + + if (fromSolution.Volume == 0) + { + _popup.PopupClient(Loc.GetString("cartridge-fabricator-empty-input"), args.User, PopupType.Medium); + _audio.PlayPredicted(entity.Comp.FailureSound, entity.Owner, args.User); + return; + } + + // Emagging allows users to make cartridges out of anything + if (!entity.Comp.Emagged && + ContainsDisallowedReagents(entity.Comp, fromSolution)) + { + _popup.PopupClient(Loc.GetString("cartridge-fabricator-denied"), args.User, PopupType.Medium); + _audio.PlayPredicted(entity.Comp.FailureSound, entity.Owner, args.User); + return; + } + + if (_net.IsServer) + { + var coords = Transform(entity.Owner).Coordinates; + var cartridge = Spawn("BaseEmptyHypoCartridge", coords); + + var cartridgeManager = EnsureComp(cartridge); + if (!_container.TryGetSolution((cartridge, cartridgeManager), + entity.Comp.OutputSolution, + out var cartridgeSolutionEnt, + out var _)) + return; // Something very wrong here? + + if (!_container.TryAddSolution(cartridgeSolutionEnt.Value, fromSolution)) + return; + + if (TryComp(args.Used, out var bottleLabel)) + { + // Propagate the label from the bottle onto the cartridge + _labels.Label(cartridge, bottleLabel.CurrentLabel); + } + + QueueDel(args.Used); // Ensure the bottle is gone + _hands.TryPickupAnyHand(args.User, cartridge); + } + + _popup.PopupClient(Loc.GetString("cartridge-fabricator-success", ("amount", fromSolution.Volume)), args.User, PopupType.Medium); + _audio.PlayPredicted(entity.Comp.SuccessSound, entity.Owner, args.User); + } + + private void OnAttemptEmag(Entity entity, ref OnAttemptEmagEvent args) + { + if (entity.Comp.Emagged) + { + // No point in raising more local events when we're already emagged + args.Handled = true; + return; + } + } + + private void OnEmagged(Entity entity, ref GotEmaggedEvent args) + { + entity.Comp.Emagged = true; + args.Handled = true; + } +} diff --git a/Resources/Locale/en-US/_DV/chemistry/cartridge-fabricator-component.ftl b/Resources/Locale/en-US/_DV/chemistry/cartridge-fabricator-component.ftl new file mode 100644 index 00000000000..9af8aef915e --- /dev/null +++ b/Resources/Locale/en-US/_DV/chemistry/cartridge-fabricator-component.ftl @@ -0,0 +1,3 @@ +cartridge-fabricator-success = Created a cartridge with {$amount}u +cartridge-fabricator-denied = Input contains non-medical reagents +cartridge-fabricator-empty-input = Input contains no reagents diff --git a/Resources/Prototypes/_DV/Entities/Objects/Devices/CircuitBoards/Machine/production.yml b/Resources/Prototypes/_DV/Entities/Objects/Devices/CircuitBoards/Machine/production.yml new file mode 100644 index 00000000000..36000ab2a91 --- /dev/null +++ b/Resources/Prototypes/_DV/Entities/Objects/Devices/CircuitBoards/Machine/production.yml @@ -0,0 +1,17 @@ + +- type: entity + id: CartridgeFabricatorMachineCircuitboard + parent: BaseMachineCircuitboard + name: cartridge fabricator machine board + description: A machine printed circuit board for a cartridge fabricator + components: + - type: Sprite + state: service + - type: MachineBoard + prototype: CartridgeFabricator + stackRequirements: + Manipulator: 2 + Capacitor: 1 + # replacing the console screen + Glass: 1 + Cable: 2 diff --git a/Resources/Prototypes/_DV/Entities/Objects/Specific/Medical/hypospray.yml b/Resources/Prototypes/_DV/Entities/Objects/Specific/Medical/hypospray.yml index 8b18c6a5333..3f3f3357b23 100644 --- a/Resources/Prototypes/_DV/Entities/Objects/Specific/Medical/hypospray.yml +++ b/Resources/Prototypes/_DV/Entities/Objects/Specific/Medical/hypospray.yml @@ -35,9 +35,47 @@ - type: entity id: BaseEmptyHypoCartridge - name: hypo cartridge + name: cartridge parent: [BaseItem, BaseRestrictedContraband] + # TODO: Make these destructible components: + - type: Damageable + damageContainer: Inorganic + damageModifierSet: Glass + - type: Destructible + thresholds: + - trigger: + !type:DamageTrigger + damage: 5 + behaviors: + - !type:PlaySoundBehavior + sound: + collection: GlassBreak + params: + volume: -4 + - !type:SpillBehavior + solution: beaker + - !type:SpawnEntitiesBehavior + spawn: + ShardGlass: + min: 1 + max: 1 + transferForensics: true + - !type:DoActsBehavior + acts: [ "Destruction" ] + - type: DamageOnLand + damage: + types: + Blunt: 10 # glass resistance set reduces damage. Need to land twice (w/o hitting wall) + - type: DamageOtherOnHit + damage: + types: + Blunt: 5 + - type: DamageOnHighSpeedImpact + minimumSpeed: 2 + damage: + types: + Blunt: 5 - type: SolutionContainerManager solutions: cartridge: diff --git a/Resources/Prototypes/_DV/Structures/Machines/cartridge_fabricator.yml b/Resources/Prototypes/_DV/Structures/Machines/cartridge_fabricator.yml new file mode 100644 index 00000000000..0c98f2b65af --- /dev/null +++ b/Resources/Prototypes/_DV/Structures/Machines/cartridge_fabricator.yml @@ -0,0 +1,40 @@ +- type: entity + id: CartridgeFabricator + name: cartridge fabricator + parent: [ BaseMachinePowered, ConstructibleMachine ] + description: Converts bottles of chemicals into hypospray cartridges + components: + # TODO: Make this not the seed extractor + - type: Sprite + sprite: Structures/Machines/seed_extractor.rsi + snapCardinals: true + layers: + - state: seedextractor-off + - state: seedextractor-unlit + shader: unshaded + map: ["enum.PowerDeviceVisualLayers.Powered"] + - type: Physics + bodyType: Static + - type: Fixtures + fixtures: + fix1: + shape: + !type:PhysShapeAabb + bounds: "-0.25,-0.4,0.25,0.4" + density: 190 + mask: + - MachineMask + layer: + - MachineLayer + - type: Appearance + - type: GenericVisualizer + visuals: + enum.PowerDeviceVisuals.Powered: + enum.PowerDeviceVisualLayers.Powered: + True: { visible: true } + False: { visible: false } + - type: CartridgeFabricator + whitelist: + - Medicine + - type: Machine + board: CartridgeFabricatorMachineCircuitboard