using Content.Shared.DoAfter;
+using Robust.Shared.Audio;
using Robust.Shared.GameStates;
using Robust.Shared.Prototypes;
using Robust.Shared.Serialization;
[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]
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;
[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);
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;
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,
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;
}
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)"
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}
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
# clothing
construction-graph-tag-backpack = backpack
+
+# chemistry
+construction-graph-tag-centrifuge-compatible = centrifugable container
Steel: 50
- type: Shakeable
- type: ReactionMixer
- mixOnInteract: false
+ mixerType: None
reactionTypes:
- Shake
--- /dev/null
+- 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
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
--- /dev/null
+# 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
--- /dev/null
+{
+ "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
+ }
+ ]
+}