]> git.smokeofanarchy.ru Git - space-station-14.git/commitdiff
Add Paper Centrifuge (#42040)
authorScarKy0 <106310278+ScarKy0@users.noreply.github.com>
Thu, 15 Jan 2026 19:45:20 +0000 (20:45 +0100)
committerGitHub <noreply@github.com>
Thu, 15 Jan 2026 19:45:20 +0000 (19:45 +0000)
* init

* sound

* sprite, half functional construction

* proper recipe

* oops

* loop sound

* inhands

* review

* review squared

18 files changed:
Content.Shared/Chemistry/Reaction/ReactionMixerComponent.cs
Content.Shared/Chemistry/Reaction/ReactionMixerSystem.cs
Resources/Audio/Items/Medical/attributions.yml
Resources/Audio/Items/Medical/paper_centrifuge.ogg [new file with mode: 0644]
Resources/Locale/en-US/chemistry/components/mixing-component.ftl
Resources/Locale/en-US/recipes/tags.ftl
Resources/Prototypes/Entities/Objects/Consumable/Drinks/drinks_special.yml
Resources/Prototypes/Entities/Objects/Specific/Chemistry/paper_centrifuge.yml [new file with mode: 0644]
Resources/Prototypes/Recipes/Construction/tools.yml
Resources/Prototypes/Recipes/Crafting/Graphs/improvised/paper_centrifuge.yml [new file with mode: 0644]
Resources/Textures/Objects/Specific/Chemistry/paper_centrifuge.rsi/fill-1.png [new file with mode: 0644]
Resources/Textures/Objects/Specific/Chemistry/paper_centrifuge.rsi/fill-2.png [new file with mode: 0644]
Resources/Textures/Objects/Specific/Chemistry/paper_centrifuge.rsi/fill-3.png [new file with mode: 0644]
Resources/Textures/Objects/Specific/Chemistry/paper_centrifuge.rsi/fill-4.png [new file with mode: 0644]
Resources/Textures/Objects/Specific/Chemistry/paper_centrifuge.rsi/icon.png [new file with mode: 0644]
Resources/Textures/Objects/Specific/Chemistry/paper_centrifuge.rsi/inhand-left.png [new file with mode: 0644]
Resources/Textures/Objects/Specific/Chemistry/paper_centrifuge.rsi/inhand-right.png [new file with mode: 0644]
Resources/Textures/Objects/Specific/Chemistry/paper_centrifuge.rsi/meta.json [new file with mode: 0644]

index 25fda2560777fe031f19296e390edbd7287e1662..247b203e218008240886a7550cd5c109abbe1521 100644 (file)
@@ -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";
 
+    /// <summary>
+    /// The sound to play when mixing.
+    /// </summary>
+    [DataField]
+    public SoundSpecifier? MixingSound;
+
     /// <summary>
     /// Defines if interacting is enough to mix with this component.
     /// </summary>
     [DataField, AutoNetworkedField]
-    public bool MixOnInteract = true;
+    public ReactionMixerType MixerType = ReactionMixerType.Machine;
 
     /// <summary>
     /// How long it takes to mix with this.
     /// </summary>
     [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]
index 4bc878a42f89215f0ef0a340ace186c1fd0554a1..030c8b890cc3ed5846f760f2a8d880f6ae0f9373 100644 (file)
@@ -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<ReactionMixerComponent, UseInHandEvent>(OnUseInHand);
         SubscribeLocalEvent<ReactionMixerComponent, AfterInteractEvent>(OnAfterInteract, before: [typeof(IngestionSystem)]);
         SubscribeLocalEvent<ReactionMixerComponent, ShakeEvent>(OnShake);
         SubscribeLocalEvent<ReactionMixerComponent, ReactionMixDoAfterEvent>(OnDoAfter);
     }
 
+    private void OnUseInHand(Entity<ReactionMixerComponent> 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<ReactionMixerComponent> 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<ReactionMixerComponent> 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;
     }
 
index 09c4dcd0f59ed461d4275c69ee6238e472bd6eee..23023781c7355f5ebd9f82b5640e6f94292feda7 100644 (file)
@@ -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 (file)
index 0000000..829512f
Binary files /dev/null and b/Resources/Audio/Items/Medical/paper_centrifuge.ogg differ
index c434246fab78605a110eec6fb8d5c65cc81838a7..b6ca5226da084c0185f46d045e80b621ad55da17 100644 (file)
@@ -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}
 
index 8d739eef6fc34e3a3a060f2abc32e247f3f4fe28..db636df773edecb9e26e95e1f28f9e1675682d3f 100644 (file)
@@ -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
index 0d3907c50f0ed0405403d1745e29cf787e7b37c3..2a44c451ab96f718af65415c87b80e4316df6c9a 100644 (file)
       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 (file)
index 0000000..aaeeb17
--- /dev/null
@@ -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
index ed83abfc1a32b4d5309358a6eb4603b7361f99bc..6b89f3687ab2a67763238c2a1f93d1d895567bbc 100644 (file)
   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 (file)
index 0000000..46c040f
--- /dev/null
@@ -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 (file)
index 0000000..d32acca
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 (file)
index 0000000..108a3e7
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 (file)
index 0000000..ee4a1f0
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 (file)
index 0000000..df4f4b6
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 (file)
index 0000000..ff66cd1
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 (file)
index 0000000..5ae3bcf
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 (file)
index 0000000..5ae3bcf
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 (file)
index 0000000..a839622
--- /dev/null
@@ -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
+        }
+    ]
+}