From: ScarKy0 <106310278+ScarKy0@users.noreply.github.com> Date: Thu, 15 Jan 2026 19:45:20 +0000 (+0100) Subject: Add Paper Centrifuge (#42040) X-Git-Url: https://git.smokeofanarchy.ru/gitweb.cgi?a=commitdiff_plain;h=c7e8bbbf873deb9e341facf73c443b0b39936f6f;p=space-station-14.git Add Paper Centrifuge (#42040) * init * sound * sprite, half functional construction * proper recipe * oops * loop sound * inhands * review * review squared --- diff --git a/Content.Shared/Chemistry/Reaction/ReactionMixerComponent.cs b/Content.Shared/Chemistry/Reaction/ReactionMixerComponent.cs index 25fda25607..247b203e21 100644 --- a/Content.Shared/Chemistry/Reaction/ReactionMixerComponent.cs +++ b/Content.Shared/Chemistry/Reaction/ReactionMixerComponent.cs @@ -1,4 +1,5 @@ using Content.Shared.DoAfter; +using Robust.Shared.Audio; using Robust.Shared.GameStates; using Robust.Shared.Prototypes; using Robust.Shared.Serialization; @@ -20,17 +21,34 @@ public sealed partial class ReactionMixerComponent : Component [DataField, AutoNetworkedField] public LocId MixMessage = "default-mixing-success"; + /// + /// The sound to play when mixing. + /// + [DataField] + public SoundSpecifier? MixingSound; + /// /// Defines if interacting is enough to mix with this component. /// [DataField, AutoNetworkedField] - public bool MixOnInteract = true; + public ReactionMixerType MixerType = ReactionMixerType.Machine; /// /// How long it takes to mix with this. /// [DataField, AutoNetworkedField] public TimeSpan TimeToMix = TimeSpan.Zero; + + // Used to cancel the played sound. + public EntityUid? AudioStream; +} + +[Serializable, NetSerializable] +public enum ReactionMixerType +{ + None, // Mixing is handled by its own system. + Machine, // Mixing is handled via interaction. + Handheld // Mixing is handled via using in hand } [ByRefEvent] diff --git a/Content.Shared/Chemistry/Reaction/ReactionMixerSystem.cs b/Content.Shared/Chemistry/Reaction/ReactionMixerSystem.cs index 4bc878a42f..030c8b890c 100644 --- a/Content.Shared/Chemistry/Reaction/ReactionMixerSystem.cs +++ b/Content.Shared/Chemistry/Reaction/ReactionMixerSystem.cs @@ -4,7 +4,10 @@ using Content.Shared.IdentityManagement; using Content.Shared.Interaction; using Content.Shared.Nutrition.EntitySystems; using Content.Shared.Chemistry.EntitySystems; +using Content.Shared.Interaction.Events; using Content.Shared.Popups; +using Robust.Shared.Audio.Systems; +using Robust.Shared.Network; namespace Content.Shared.Chemistry.Reaction; @@ -13,24 +16,64 @@ public sealed partial class ReactionMixerSystem : EntitySystem [Dependency] private readonly SharedPopupSystem _popup = default!; [Dependency] private readonly SharedSolutionContainerSystem _solutionContainer = default!; [Dependency] private readonly SharedDoAfterSystem _doAfter = default!; + [Dependency] private readonly SharedAudioSystem _audio = default!; + [Dependency] private readonly INetManager _net = default!; public override void Initialize() { base.Initialize(); + SubscribeLocalEvent(OnUseInHand); SubscribeLocalEvent(OnAfterInteract, before: [typeof(IngestionSystem)]); SubscribeLocalEvent(OnShake); SubscribeLocalEvent(OnDoAfter); } + private void OnUseInHand(Entity ent, ref UseInHandEvent args) + { + if (args.Handled) + return; + + if (ent.Comp.MixerType != ReactionMixerType.Handheld) + return; + + args.Handled = true; + + if (!CanMix(ent.AsNullable(), ent)) + return; + + if (_net.IsServer) // Cannot cancel predicted audio. + ent.Comp.AudioStream = _audio.PlayPvs(ent.Comp.MixingSound, ent)?.Entity; + + var doAfterArgs = new DoAfterArgs(EntityManager, + args.User, + ent.Comp.TimeToMix, + new ReactionMixDoAfterEvent(), + ent, + ent, + ent) + { + NeedHand = true, + BreakOnDamage = true, + BreakOnDropItem = true, + BreakOnHandChange = true, + BreakOnMove = true + }; + + _doAfter.TryStartDoAfter(doAfterArgs); + } + private void OnAfterInteract(Entity ent, ref AfterInteractEvent args) { - if (!args.Target.HasValue || !args.CanReach || !ent.Comp.MixOnInteract) + if (!args.Target.HasValue || !args.CanReach || ent.Comp.MixerType != ReactionMixerType.Machine) return; if (!CanMix(ent.AsNullable(), args.Target.Value)) return; + if (_net.IsServer) // Cannot cancel predicted audio. + ent.Comp.AudioStream = _audio.PlayPvs(ent.Comp.MixingSound, ent)?.Entity; + var doAfterArgs = new DoAfterArgs(EntityManager, args.User, ent.Comp.TimeToMix, new ReactionMixDoAfterEvent(), ent, args.Target.Value, ent); _doAfter.TryStartDoAfter(doAfterArgs); @@ -39,6 +82,11 @@ public sealed partial class ReactionMixerSystem : EntitySystem private void OnDoAfter(Entity ent, ref ReactionMixDoAfterEvent args) { + ent.Comp.AudioStream = _audio.Stop(ent.Comp.AudioStream); + + if (args.Cancelled) + return; + if (args.Target == null) return; @@ -46,8 +94,7 @@ public sealed partial class ReactionMixerSystem : EntitySystem return; _popup.PopupClient( - Loc.GetString( - ent.Comp.MixMessage, + Loc.GetString(ent.Comp.MixMessage, ("mixed", Identity.Entity(args.Target.Value, EntityManager)), ("mixer", Identity.Entity(ent.Owner, EntityManager))), args.User, @@ -69,14 +116,18 @@ public sealed partial class ReactionMixerSystem : EntitySystem if (!Resolve(ent, ref ent.Comp, false)) // The used entity needs the component to be able to mix a solution return false; + if (!_solutionContainer.TryGetMixableSolution(target, out _, out var mixableSolution)) + return false; + + // Can't mix nothing. + if (mixableSolution.Volume <= 0) + return false; + var mixAttemptEvent = new MixingAttemptEvent(ent); RaiseLocalEvent(ent, ref mixAttemptEvent); if (mixAttemptEvent.Cancelled) return false; - if (!_solutionContainer.TryGetMixableSolution(target, out _, out _)) - return false; - return true; } diff --git a/Resources/Audio/Items/Medical/attributions.yml b/Resources/Audio/Items/Medical/attributions.yml index 09c4dcd0f5..23023781c7 100644 --- a/Resources/Audio/Items/Medical/attributions.yml +++ b/Resources/Audio/Items/Medical/attributions.yml @@ -3,6 +3,11 @@ copyright: "Taken from FM Synthesis on freesound.org" source: "https://freesound.org/people/FreqMan/sounds/32683/" +- files: ["paper_centrifuge.ogg"] + license: "CC-BY-4.0" + copyright: "Taken from temawas on freesound. Cut, converted to .ogg" + source: "https://freesound.org/people/temawas/sounds/179248/" + - files: ["jet_injector.ogg"] license: "CC-BY-3.0" copyright: "Orginal audio by EminYILDIRIM -- https://freesound.org/s/548009/ -- License: Attribution 4.0, 2imitk -- https://freesound.org/s/279044/ -- License: Attribution 3.0 and brunoboselli -- https://freesound.org/s/457294/ -- License: Creative Commons 0 -- https://freesound.org/s/460586/ -- License: Attribution NonCommercial 4.0, modified by Princess-Cheeseballs (GitHub)" diff --git a/Resources/Audio/Items/Medical/paper_centrifuge.ogg b/Resources/Audio/Items/Medical/paper_centrifuge.ogg new file mode 100644 index 0000000000..829512fbfd Binary files /dev/null and b/Resources/Audio/Items/Medical/paper_centrifuge.ogg differ diff --git a/Resources/Locale/en-US/chemistry/components/mixing-component.ftl b/Resources/Locale/en-US/chemistry/components/mixing-component.ftl index c434246fab..b6ca5226da 100644 --- a/Resources/Locale/en-US/chemistry/components/mixing-component.ftl +++ b/Resources/Locale/en-US/chemistry/components/mixing-component.ftl @@ -14,4 +14,5 @@ mixing-verb-shake = shake default-mixing-success = You mix the {$mixed} with the {$mixer} bible-mixing-success = You bless the {$mixed} with the {$mixer} spoon-mixing-success = You stir the {$mixed} with the {$mixer} +handheld-centrifuge-success = You seperate chemicals in the {$mixed} diff --git a/Resources/Locale/en-US/recipes/tags.ftl b/Resources/Locale/en-US/recipes/tags.ftl index 8d739eef6f..db636df773 100644 --- a/Resources/Locale/en-US/recipes/tags.ftl +++ b/Resources/Locale/en-US/recipes/tags.ftl @@ -73,6 +73,7 @@ construction-graph-tag-apron = an apron construction-graph-tag-utility-belt = a utility belt soil-construction-graph-any-mushroom = any mushroom construction-graph-tag-mop-basic = mop +construction-graph-tag-paper = office paper construction-graph-tag-core-pinpointer-piece = piece of core pinpointer # toys @@ -153,3 +154,6 @@ construction-graph-tag-spationaut-hardsuit = spationaut hardsuit # clothing construction-graph-tag-backpack = backpack + +# chemistry +construction-graph-tag-centrifuge-compatible = centrifugable container diff --git a/Resources/Prototypes/Entities/Objects/Consumable/Drinks/drinks_special.yml b/Resources/Prototypes/Entities/Objects/Consumable/Drinks/drinks_special.yml index 0d3907c50f..2a44c451ab 100644 --- a/Resources/Prototypes/Entities/Objects/Consumable/Drinks/drinks_special.yml +++ b/Resources/Prototypes/Entities/Objects/Consumable/Drinks/drinks_special.yml @@ -131,7 +131,7 @@ Steel: 50 - type: Shakeable - type: ReactionMixer - mixOnInteract: false + mixerType: None reactionTypes: - Shake diff --git a/Resources/Prototypes/Entities/Objects/Specific/Chemistry/paper_centrifuge.yml b/Resources/Prototypes/Entities/Objects/Specific/Chemistry/paper_centrifuge.yml new file mode 100644 index 0000000000..aaeeb17bf8 --- /dev/null +++ b/Resources/Prototypes/Entities/Objects/Specific/Chemistry/paper_centrifuge.yml @@ -0,0 +1,60 @@ +- type: entity + abstract: true + parent: BaseItem + id: BaseHandheldMixer + components: + - type: SolutionContainerManager + solutions: + mixer: + maxVol: 10 + - type: MixableSolution + solution: mixer + - type: RefillableSolution + solution: mixer + - type: DrainableSolution + solution: mixer + - type: ExaminableSolution + solution: mixer + exactVolume: true + - type: DrawableSolution + solution: mixer + - type: InjectableSolution + solution: mixer + - type: Spillable + solution: mixer + - type: SolutionTransfer + - type: SolutionItemStatus + solution: mixer + - type: Appearance + - type: DnaSubstanceTrace + - type: ReactionMixer + +- type: entity + parent: BaseHandheldMixer + id: HandheldMixerPaperCentrifuge + name: paper centrifuge + description: A small portable makeshift centrifuge. Works by rotating the paper sheets when its cords are pulled. + components: + - type: Sprite + sprite: Objects/Specific/Chemistry/paper_centrifuge.rsi + layers: + - state: icon + - state: fill-1 + map: [ "enum.SolutionContainerLayers.Fill" ] + visible: false + - type: SolutionContainerVisuals + maxFillLevels: 4 + fillBaseName: fill- + - type: Construction + graph: MakeshiftCentrifuge + node: makeshiftCentrifuge + - type: ReactionMixer + reactionTypes: + - Centrifuge + mixerType: Handheld + mixMessage: handheld-centrifuge-success + timeToMix: 10 + mixingSound: !type:SoundPathSpecifier + path: /Audio/Items/Medical/paper_centrifuge.ogg + params: + loop: true diff --git a/Resources/Prototypes/Recipes/Construction/tools.yml b/Resources/Prototypes/Recipes/Construction/tools.yml index ed83abfc1a..6b89f3687a 100644 --- a/Resources/Prototypes/Recipes/Construction/tools.yml +++ b/Resources/Prototypes/Recipes/Construction/tools.yml @@ -45,3 +45,11 @@ targetNode: random_gate category: construction-category-tools objectType: Item + +- type: construction + id: MakeshiftCentrifuge + graph: MakeshiftCentrifuge + startNode: start + targetNode: makeshiftCentrifuge + category: construction-category-tools + objectType: Item diff --git a/Resources/Prototypes/Recipes/Crafting/Graphs/improvised/paper_centrifuge.yml b/Resources/Prototypes/Recipes/Crafting/Graphs/improvised/paper_centrifuge.yml new file mode 100644 index 0000000000..46c040f881 --- /dev/null +++ b/Resources/Prototypes/Recipes/Crafting/Graphs/improvised/paper_centrifuge.yml @@ -0,0 +1,39 @@ +# Mortar +- type: constructionGraph + id: MakeshiftCentrifuge + start: start + graph: + - node: start + edges: + - to: makeshiftCentrifuge + steps: + - material: MetalRod + amount: 2 + - material: Cable + amount: 15 + doAfter: 2 + - tag: CentrifugeCompatible + icon: + sprite: Objects/Specific/Chemistry/vial.rsi + state: vial + name: construction-graph-tag-centrifuge-compatible + doAfter: 2 + - tag: CentrifugeCompatible + icon: + sprite: Objects/Specific/Chemistry/vial.rsi + state: vial + name: construction-graph-tag-centrifuge-compatible + doAfter: 2 + - tag: Paper + icon: + sprite: Objects/Misc/bureaucracy.rsi + state: paper + name: construction-graph-tag-paper + - tag: Paper + icon: + sprite: Objects/Misc/bureaucracy.rsi + state: paper + name: construction-graph-tag-paper + + - node: makeshiftCentrifuge + entity: HandheldMixerPaperCentrifuge diff --git a/Resources/Textures/Objects/Specific/Chemistry/paper_centrifuge.rsi/fill-1.png b/Resources/Textures/Objects/Specific/Chemistry/paper_centrifuge.rsi/fill-1.png new file mode 100644 index 0000000000..d32accae69 Binary files /dev/null and b/Resources/Textures/Objects/Specific/Chemistry/paper_centrifuge.rsi/fill-1.png differ diff --git a/Resources/Textures/Objects/Specific/Chemistry/paper_centrifuge.rsi/fill-2.png b/Resources/Textures/Objects/Specific/Chemistry/paper_centrifuge.rsi/fill-2.png new file mode 100644 index 0000000000..108a3e79b3 Binary files /dev/null and b/Resources/Textures/Objects/Specific/Chemistry/paper_centrifuge.rsi/fill-2.png differ diff --git a/Resources/Textures/Objects/Specific/Chemistry/paper_centrifuge.rsi/fill-3.png b/Resources/Textures/Objects/Specific/Chemistry/paper_centrifuge.rsi/fill-3.png new file mode 100644 index 0000000000..ee4a1f0e4e Binary files /dev/null and b/Resources/Textures/Objects/Specific/Chemistry/paper_centrifuge.rsi/fill-3.png differ diff --git a/Resources/Textures/Objects/Specific/Chemistry/paper_centrifuge.rsi/fill-4.png b/Resources/Textures/Objects/Specific/Chemistry/paper_centrifuge.rsi/fill-4.png new file mode 100644 index 0000000000..df4f4b6cbc Binary files /dev/null and b/Resources/Textures/Objects/Specific/Chemistry/paper_centrifuge.rsi/fill-4.png differ diff --git a/Resources/Textures/Objects/Specific/Chemistry/paper_centrifuge.rsi/icon.png b/Resources/Textures/Objects/Specific/Chemistry/paper_centrifuge.rsi/icon.png new file mode 100644 index 0000000000..ff66cd1048 Binary files /dev/null and b/Resources/Textures/Objects/Specific/Chemistry/paper_centrifuge.rsi/icon.png differ diff --git a/Resources/Textures/Objects/Specific/Chemistry/paper_centrifuge.rsi/inhand-left.png b/Resources/Textures/Objects/Specific/Chemistry/paper_centrifuge.rsi/inhand-left.png new file mode 100644 index 0000000000..5ae3bcfebf Binary files /dev/null and b/Resources/Textures/Objects/Specific/Chemistry/paper_centrifuge.rsi/inhand-left.png differ diff --git a/Resources/Textures/Objects/Specific/Chemistry/paper_centrifuge.rsi/inhand-right.png b/Resources/Textures/Objects/Specific/Chemistry/paper_centrifuge.rsi/inhand-right.png new file mode 100644 index 0000000000..5ae3bcfebf Binary files /dev/null and b/Resources/Textures/Objects/Specific/Chemistry/paper_centrifuge.rsi/inhand-right.png differ diff --git a/Resources/Textures/Objects/Specific/Chemistry/paper_centrifuge.rsi/meta.json b/Resources/Textures/Objects/Specific/Chemistry/paper_centrifuge.rsi/meta.json new file mode 100644 index 0000000000..a839622821 --- /dev/null +++ b/Resources/Textures/Objects/Specific/Chemistry/paper_centrifuge.rsi/meta.json @@ -0,0 +1,34 @@ +{ + "version": 1, + "license": "CC-BY-SA-3.0", + "copyright": "Sprites created by DispenserG0inUp(Discord)", + "size": { + "x": 32, + "y": 32 + }, + "states": [ + { + "name": "icon" + }, + { + "name": "fill-1" + }, + { + "name": "fill-2" + }, + { + "name": "fill-3" + }, + { + "name": "fill-4" + }, + { + "name": "inhand-left", + "directions": 4 + }, + { + "name": "inhand-right", + "directions": 4 + } + ] +}