Electrolysis and Centrifuge (#22517)
* electrolysis and centrifuge * sprote * final * bomp! * COUGH COUGH SPROTE * boarsd
@@ -0,0 +1,9 @@
|
||||
using Content.Shared.Chemistry.EntitySystems;
|
||||
|
||||
namespace Content.Client.Chemistry.EntitySystems;
|
||||
|
||||
/// <inheritdoc/>
|
||||
public sealed class SolutionContainerMixerSystem : SharedSolutionContainerMixerSystem
|
||||
{
|
||||
|
||||
}
|
||||
@@ -0,0 +1,29 @@
|
||||
using Content.Server.Power.Components;
|
||||
using Content.Server.Power.EntitySystems;
|
||||
using Content.Shared.Chemistry.Components;
|
||||
using Content.Shared.Chemistry.EntitySystems;
|
||||
|
||||
namespace Content.Server.Chemistry.EntitySystems;
|
||||
|
||||
/// <inheritdoc/>
|
||||
public sealed class SolutionContainerMixerSystem : SharedSolutionContainerMixerSystem
|
||||
{
|
||||
/// <inheritdoc/>
|
||||
public override void Initialize()
|
||||
{
|
||||
base.Initialize();
|
||||
|
||||
SubscribeLocalEvent<SolutionContainerMixerComponent, PowerChangedEvent>(OnPowerChanged);
|
||||
}
|
||||
|
||||
private void OnPowerChanged(Entity<SolutionContainerMixerComponent> ent, ref PowerChangedEvent args)
|
||||
{
|
||||
if (!args.Powered)
|
||||
StopMix(ent);
|
||||
}
|
||||
|
||||
protected override bool HasPower(Entity<SolutionContainerMixerComponent> entity)
|
||||
{
|
||||
return this.IsPowered(entity, EntityManager);
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,44 @@
|
||||
using Content.Shared.Chemistry.EntitySystems;
|
||||
using Content.Shared.Chemistry.Reaction;
|
||||
using Robust.Shared.Audio;
|
||||
using Robust.Shared.Audio.Components;
|
||||
using Robust.Shared.GameStates;
|
||||
using Robust.Shared.Serialization;
|
||||
using Robust.Shared.Serialization.TypeSerializers.Implementations.Custom;
|
||||
|
||||
namespace Content.Shared.Chemistry.Components;
|
||||
|
||||
/// <summary>
|
||||
/// This is used for an entity that uses <see cref="ReactionMixerComponent"/> to mix any container with a solution after a period of time.
|
||||
/// </summary>
|
||||
[RegisterComponent, NetworkedComponent, AutoGenerateComponentState]
|
||||
[Access(typeof(SharedSolutionContainerMixerSystem))]
|
||||
public sealed partial class SolutionContainerMixerComponent : Component
|
||||
{
|
||||
[DataField, ViewVariables(VVAccess.ReadWrite)]
|
||||
public string ContainerId = "mixer";
|
||||
|
||||
[DataField, AutoNetworkedField]
|
||||
public bool Mixing;
|
||||
|
||||
/// <summary>
|
||||
/// How long it takes for mixing to occurs.
|
||||
/// </summary>
|
||||
[DataField, ViewVariables(VVAccess.ReadWrite), AutoNetworkedField]
|
||||
public TimeSpan MixDuration;
|
||||
|
||||
[DataField(customTypeSerializer: typeof(TimeOffsetSerializer)), ViewVariables(VVAccess.ReadWrite), AutoNetworkedField]
|
||||
public TimeSpan MixTimeEnd;
|
||||
|
||||
[DataField, AutoNetworkedField]
|
||||
public SoundSpecifier? MixingSound;
|
||||
|
||||
[DataField]
|
||||
public Entity<AudioComponent>? MixingSoundEntity;
|
||||
}
|
||||
|
||||
[Serializable, NetSerializable]
|
||||
public enum SolutionContainerMixerVisuals : byte
|
||||
{
|
||||
Mixing
|
||||
}
|
||||
@@ -0,0 +1,125 @@
|
||||
using Content.Shared.Chemistry.Components;
|
||||
using Content.Shared.Chemistry.Reaction;
|
||||
using Content.Shared.Interaction;
|
||||
using Content.Shared.Popups;
|
||||
using Robust.Shared.Audio.Systems;
|
||||
using Robust.Shared.Containers;
|
||||
using Robust.Shared.Network;
|
||||
using Robust.Shared.Timing;
|
||||
|
||||
namespace Content.Shared.Chemistry.EntitySystems;
|
||||
|
||||
/// <summary>
|
||||
/// This handles <see cref="SolutionContainerMixerComponent"/>
|
||||
/// </summary>
|
||||
public abstract class SharedSolutionContainerMixerSystem : EntitySystem
|
||||
{
|
||||
[Dependency] private readonly IGameTiming _timing = default!;
|
||||
[Dependency] private readonly INetManager _net = default!;
|
||||
[Dependency] private readonly SharedAppearanceSystem _appearance = default!;
|
||||
[Dependency] private readonly SharedAudioSystem _audio = default!;
|
||||
[Dependency] private readonly ChemicalReactionSystem _chemicalReaction = default!;
|
||||
[Dependency] private readonly SharedContainerSystem _container = default!;
|
||||
[Dependency] private readonly SharedPopupSystem _popup = default!;
|
||||
[Dependency] private readonly SolutionContainerSystem _solution = default!;
|
||||
|
||||
/// <inheritdoc/>
|
||||
public override void Initialize()
|
||||
{
|
||||
SubscribeLocalEvent<SolutionContainerMixerComponent, ActivateInWorldEvent>(OnActivateInWorld);
|
||||
SubscribeLocalEvent<SolutionContainerMixerComponent, ContainerIsRemovingAttemptEvent>(OnRemoveAttempt);
|
||||
}
|
||||
|
||||
private void OnActivateInWorld(Entity<SolutionContainerMixerComponent> entity, ref ActivateInWorldEvent args)
|
||||
{
|
||||
TryStartMix(entity, args.User);
|
||||
}
|
||||
|
||||
private void OnRemoveAttempt(Entity<SolutionContainerMixerComponent> ent, ref ContainerIsRemovingAttemptEvent args)
|
||||
{
|
||||
if (args.Container.ID == ent.Comp.ContainerId && ent.Comp.Mixing)
|
||||
args.Cancel();
|
||||
}
|
||||
|
||||
protected virtual bool HasPower(Entity<SolutionContainerMixerComponent> entity)
|
||||
{
|
||||
return true;
|
||||
}
|
||||
|
||||
public void TryStartMix(Entity<SolutionContainerMixerComponent> entity, EntityUid? user)
|
||||
{
|
||||
var (uid, comp) = entity;
|
||||
if (comp.Mixing)
|
||||
return;
|
||||
|
||||
if (!HasPower(entity))
|
||||
{
|
||||
if (user != null)
|
||||
_popup.PopupClient(Loc.GetString("solution-container-mixer-no-power"), entity, user.Value);
|
||||
return;
|
||||
}
|
||||
|
||||
if (!_container.TryGetContainer(uid, comp.ContainerId, out var container) || container.Count == 0)
|
||||
{
|
||||
if (user != null)
|
||||
_popup.PopupClient(Loc.GetString("solution-container-mixer-popup-nothing-to-mix"), entity, user.Value);
|
||||
return;
|
||||
}
|
||||
|
||||
comp.Mixing = true;
|
||||
if (_net.IsServer)
|
||||
comp.MixingSoundEntity = _audio.PlayPvs(comp.MixingSound, entity, comp.MixingSound?.Params.WithLoop(true));
|
||||
comp.MixTimeEnd = _timing.CurTime + comp.MixDuration;
|
||||
_appearance.SetData(entity, SolutionContainerMixerVisuals.Mixing, true);
|
||||
Dirty(uid, comp);
|
||||
}
|
||||
|
||||
public void StopMix(Entity<SolutionContainerMixerComponent> entity)
|
||||
{
|
||||
var (uid, comp) = entity;
|
||||
if (!comp.Mixing)
|
||||
return;
|
||||
_audio.Stop(comp.MixingSoundEntity);
|
||||
_appearance.SetData(entity, SolutionContainerMixerVisuals.Mixing, false);
|
||||
comp.Mixing = false;
|
||||
comp.MixingSoundEntity = null;
|
||||
Dirty(uid, comp);
|
||||
}
|
||||
|
||||
public void FinishMix(Entity<SolutionContainerMixerComponent> entity)
|
||||
{
|
||||
var (uid, comp) = entity;
|
||||
if (!comp.Mixing)
|
||||
return;
|
||||
StopMix(entity);
|
||||
|
||||
if (!TryComp<ReactionMixerComponent>(entity, out var reactionMixer)
|
||||
|| !_container.TryGetContainer(uid, comp.ContainerId, out var container))
|
||||
return;
|
||||
|
||||
foreach (var ent in container.ContainedEntities)
|
||||
{
|
||||
if (!_solution.TryGetFitsInDispenser(ent, out var solution))
|
||||
continue;
|
||||
|
||||
_chemicalReaction.FullyReactSolution(solution, ent, solution.MaxVolume, reactionMixer);
|
||||
}
|
||||
}
|
||||
|
||||
public override void Update(float frameTime)
|
||||
{
|
||||
base.Update(frameTime);
|
||||
|
||||
var query = EntityQueryEnumerator<SolutionContainerMixerComponent>();
|
||||
while (query.MoveNext(out var uid, out var comp))
|
||||
{
|
||||
if (!comp.Mixing)
|
||||
continue;
|
||||
|
||||
if (_timing.CurTime < comp.MixTimeEnd)
|
||||
continue;
|
||||
|
||||
FinishMix((uid, comp));
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -112,7 +112,8 @@ namespace Content.Shared.Chemistry.Reaction
|
||||
{
|
||||
lowestUnitReactions = FixedPoint2.Zero;
|
||||
return false;
|
||||
} else if(solution.Temperature > reaction.MaximumTemperature)
|
||||
}
|
||||
if (solution.Temperature > reaction.MaximumTemperature)
|
||||
{
|
||||
lowestUnitReactions = FixedPoint2.Zero;
|
||||
return false;
|
||||
@@ -126,7 +127,7 @@ namespace Content.Shared.Chemistry.Reaction
|
||||
}
|
||||
|
||||
var attempt = new ReactionAttemptEvent(reaction, solution);
|
||||
RaiseLocalEvent(owner, attempt, false);
|
||||
RaiseLocalEvent(owner, attempt);
|
||||
if (attempt.Cancelled)
|
||||
{
|
||||
lowestUnitReactions = FixedPoint2.Zero;
|
||||
|
||||
14
Content.Shared/Chemistry/Reaction/MixingCategoryPrototype.cs
Normal file
@@ -0,0 +1,14 @@
|
||||
using Robust.Shared.Prototypes;
|
||||
|
||||
namespace Content.Shared.Chemistry.Reaction;
|
||||
|
||||
/// <summary>
|
||||
/// This is a prototype for a method of chemical mixing, to be used by <see cref="ReactionMixerComponent"/>
|
||||
/// </summary>
|
||||
[Prototype("mixingCategory")]
|
||||
public sealed partial class MixingCategoryPrototype : IPrototype
|
||||
{
|
||||
/// <inheritdoc/>
|
||||
[IdDataField]
|
||||
public string ID { get; } = default!;
|
||||
}
|
||||
@@ -1,4 +1,5 @@
|
||||
using Content.Shared.Chemistry.Components;
|
||||
using Robust.Shared.Prototypes;
|
||||
|
||||
namespace Content.Shared.Chemistry.Reaction;
|
||||
|
||||
@@ -10,7 +11,7 @@ public sealed partial class ReactionMixerComponent : Component
|
||||
/// </summary>
|
||||
[ViewVariables]
|
||||
[DataField]
|
||||
public List<string> ReactionTypes = default!;
|
||||
public List<ProtoId<MixingCategoryPrototype>> ReactionTypes = default!;
|
||||
|
||||
/// <summary>
|
||||
/// A string which identifies the string to be sent when successfully mixing a solution
|
||||
|
||||
@@ -48,7 +48,7 @@ namespace Content.Shared.Chemistry.Reaction
|
||||
/// The required mixing categories for an entity to mix the solution with for the reaction to occur
|
||||
/// </summary>
|
||||
[DataField("requiredMixerCategories")]
|
||||
public List<string>? MixingCategories = null;
|
||||
public List<ProtoId<MixingCategoryPrototype>>? MixingCategories;
|
||||
|
||||
/// <summary>
|
||||
/// Reagents created when the reaction occurs.
|
||||
|
||||
@@ -8,6 +8,16 @@
|
||||
copyright: "Created by dotY21."
|
||||
source: "https://freesound.org/people/dotY21/sounds/330726/"
|
||||
|
||||
- files: ["buzz_loop.ogg"]
|
||||
license: "CC0-1.0"
|
||||
copyright: "Created by Duasun, converted to OGG by EmoGarbage404 (github)."
|
||||
source: "https://freesound.org/people/Duasun/sounds/712127/"
|
||||
|
||||
- files: ["spinning.ogg"]
|
||||
license: "CC0-1.0"
|
||||
copyright: "Created by MrLindstrom, modified and converted to OGG by EmoGarbage404 (github)."
|
||||
source: "https://freesound.org/people/MrLindstrom/sounds/543964/"
|
||||
|
||||
- files: ["vending_restock_start.ogg"]
|
||||
license: "CC0-1.0"
|
||||
copyright: "https://freesound.org/people/Defaultv/"
|
||||
|
||||
BIN
Resources/Audio/Machines/buzz_loop.ogg
Normal file
BIN
Resources/Audio/Machines/spinning.ogg
Normal file
@@ -0,0 +1,3 @@
|
||||
solution-container-mixer-activate = Activate
|
||||
solution-container-mixer-no-power = No power!
|
||||
solution-container-mixer-popup-nothing-to-mix = Nothing inside!
|
||||
8
Resources/Prototypes/Chemistry/mixing_types.yml
Normal file
@@ -0,0 +1,8 @@
|
||||
- type: mixingCategory
|
||||
id: Centrifuge
|
||||
|
||||
- type: mixingCategory
|
||||
id: Electrolysis
|
||||
|
||||
- type: mixingCategory
|
||||
id: Holy
|
||||
@@ -808,6 +808,36 @@
|
||||
Cable: 3
|
||||
Steel: 2
|
||||
|
||||
- type: entity
|
||||
id: ElectrolysisUnitMachineCircuitboard
|
||||
parent: BaseMachineCircuitboard
|
||||
name: electrolysis unit machine board
|
||||
description: A machine printed circuit board for an electrolysis unit.
|
||||
components:
|
||||
- type: Sprite
|
||||
state: medical
|
||||
- type: MachineBoard
|
||||
prototype: MachineElectrolysisUnit
|
||||
requirements:
|
||||
Capacitor: 2
|
||||
materialRequirements:
|
||||
Cable: 1
|
||||
|
||||
- type: entity
|
||||
id: CentrifugeMachineCircuitboard
|
||||
parent: BaseMachineCircuitboard
|
||||
name: centrifuge machine board
|
||||
description: A machine printed circuit board for a centrifuge.
|
||||
components:
|
||||
- type: Sprite
|
||||
state: medical
|
||||
- type: MachineBoard
|
||||
prototype: MachineCentrifuge
|
||||
requirements:
|
||||
Manipulator: 1
|
||||
materialRequirements:
|
||||
Steel: 1
|
||||
|
||||
- type: entity
|
||||
id: MaterialReclaimerMachineCircuitboard
|
||||
parent: BaseMachineCircuitboard
|
||||
|
||||
@@ -0,0 +1,119 @@
|
||||
- type: entity
|
||||
id: BaseTabletopChemicalMachine
|
||||
parent: [ BaseMachinePowered, ConstructibleMachine ]
|
||||
abstract: true
|
||||
components:
|
||||
- type: Transform
|
||||
anchored: true
|
||||
- type: SolutionContainerMixer
|
||||
- type: ReactionMixer
|
||||
- type: Sprite
|
||||
drawdepth: SmallObjects
|
||||
snapCardinals: true
|
||||
- type: Appearance
|
||||
- type: Physics
|
||||
- type: Fixtures
|
||||
fixtures:
|
||||
fix1:
|
||||
shape:
|
||||
!type:PhysShapeAabb
|
||||
bounds: "-0.17,0,0.20,0.4"
|
||||
mask:
|
||||
- TabletopMachineMask
|
||||
layer:
|
||||
- TabletopMachineLayer
|
||||
- type: ItemSlots
|
||||
slots:
|
||||
mixer:
|
||||
whitelist:
|
||||
components:
|
||||
- FitsInDispenser
|
||||
- type: Machine
|
||||
- type: Wires
|
||||
layoutId: chem
|
||||
- type: WiresPanel
|
||||
- type: WiresVisuals
|
||||
- type: ContainerContainer
|
||||
containers:
|
||||
mixer: !type:ContainerSlot
|
||||
machine_board: !type:Container
|
||||
machine_parts: !type:Container
|
||||
|
||||
- type: entity
|
||||
id: MachineElectrolysisUnit
|
||||
parent: BaseTabletopChemicalMachine
|
||||
name: electrolysis unit
|
||||
description: The latest in medicinal electrocution technology.
|
||||
components:
|
||||
- type: SolutionContainerMixer
|
||||
mixDuration: 5
|
||||
mixingSound:
|
||||
path: /Audio/Machines/buzz_loop.ogg
|
||||
params:
|
||||
volume: -5
|
||||
- type: ReactionMixer
|
||||
reactionTypes:
|
||||
- Electrolysis
|
||||
- type: Sprite
|
||||
sprite: Structures/Machines/Medical/electrolysis.rsi
|
||||
offset: "0.0,0.4"
|
||||
layers:
|
||||
- state: base
|
||||
- state: panel
|
||||
map: ["enum.WiresVisualLayers.MaintenancePanel"]
|
||||
visible: false
|
||||
- state: unshaded
|
||||
map: ["enum.PowerDeviceVisualLayers.Powered"]
|
||||
shader: unshaded
|
||||
- type: GenericVisualizer
|
||||
visuals:
|
||||
enum.SolutionContainerMixerVisuals.Mixing:
|
||||
enum.PowerDeviceVisualLayers.Powered:
|
||||
True: {state: "spinning"}
|
||||
False: {state: "unshaded"}
|
||||
enum.PowerDeviceVisuals.Powered:
|
||||
enum.PowerDeviceVisualLayers.Powered:
|
||||
True: { visible: True }
|
||||
False: { visible: False }
|
||||
- type: Machine
|
||||
board: ElectrolysisUnitMachineCircuitboard
|
||||
|
||||
- type: entity
|
||||
id: MachineCentrifuge
|
||||
parent: BaseTabletopChemicalMachine
|
||||
name: tabletop centrifuge
|
||||
description: Around and around it goes...
|
||||
components:
|
||||
- type: SolutionContainerMixer
|
||||
mixDuration: 10
|
||||
mixingSound:
|
||||
path: /Audio/Machines/spinning.ogg
|
||||
params:
|
||||
volume: -4
|
||||
- type: ReactionMixer
|
||||
reactionTypes:
|
||||
- Centrifuge
|
||||
- type: Sprite
|
||||
sprite: Structures/Machines/Medical/centrifuge.rsi
|
||||
offset: "0.0,0.4"
|
||||
layers:
|
||||
- state: base
|
||||
map: ["beyblade"]
|
||||
- state: panel
|
||||
map: ["enum.WiresVisualLayers.MaintenancePanel"]
|
||||
visible: false
|
||||
- state: unshaded
|
||||
map: ["enum.PowerDeviceVisualLayers.Powered"]
|
||||
shader: unshaded
|
||||
- type: GenericVisualizer
|
||||
visuals:
|
||||
enum.SolutionContainerMixerVisuals.Mixing:
|
||||
beyblade:
|
||||
True: {state: "base-spinning"}
|
||||
False: {state: "base"}
|
||||
enum.PowerDeviceVisuals.Powered:
|
||||
enum.PowerDeviceVisualLayers.Powered:
|
||||
True: { visible: True }
|
||||
False: { visible: False }
|
||||
- type: Machine
|
||||
board: CentrifugeMachineCircuitboard
|
||||
@@ -308,6 +308,8 @@
|
||||
idleState: icon
|
||||
runningState: building
|
||||
staticRecipes:
|
||||
- ElectrolysisUnitMachineCircuitboard
|
||||
- CentrifugeMachineCircuitboard
|
||||
- CondenserMachineCircuitBoard
|
||||
dynamicRecipes:
|
||||
- ThermomachineFreezerMachineCircuitBoard
|
||||
|
||||
@@ -360,6 +360,22 @@
|
||||
Glass: 900
|
||||
Gold: 100
|
||||
|
||||
- type: latheRecipe
|
||||
id: ElectrolysisUnitMachineCircuitboard
|
||||
result: ElectrolysisUnitMachineCircuitboard
|
||||
completetime: 4
|
||||
materials:
|
||||
Steel: 100
|
||||
Glass: 900
|
||||
|
||||
- type: latheRecipe
|
||||
id: CentrifugeMachineCircuitboard
|
||||
result: CentrifugeMachineCircuitboard
|
||||
completetime: 4
|
||||
materials:
|
||||
Steel: 100
|
||||
Glass: 900
|
||||
|
||||
- type: latheRecipe
|
||||
id: MaterialReclaimerMachineCircuitboard
|
||||
result: MaterialReclaimerMachineCircuitboard
|
||||
|
||||
|
After Width: | Height: | Size: 810 B |
|
After Width: | Height: | Size: 626 B |
@@ -0,0 +1,31 @@
|
||||
{
|
||||
"version": 1,
|
||||
"license": "CC0-1.0",
|
||||
"copyright": "Adapted from CEV-Eris by EmoGarbage404 (github) for Space Station 14.",
|
||||
"size": {
|
||||
"x": 32,
|
||||
"y": 32
|
||||
},
|
||||
"states": [
|
||||
{
|
||||
"name": "base"
|
||||
},
|
||||
{
|
||||
"name": "base-spinning",
|
||||
"delays":
|
||||
[
|
||||
[
|
||||
0.1,
|
||||
0.1,
|
||||
0.1
|
||||
]
|
||||
]
|
||||
},
|
||||
{
|
||||
"name": "panel"
|
||||
},
|
||||
{
|
||||
"name": "unshaded"
|
||||
}
|
||||
]
|
||||
}
|
||||
|
After Width: | Height: | Size: 163 B |
|
After Width: | Height: | Size: 144 B |
|
After Width: | Height: | Size: 670 B |
@@ -0,0 +1,32 @@
|
||||
{
|
||||
"version": 1,
|
||||
"license": "CC0-1.0",
|
||||
"copyright": "Adapted from CEV-Eris by EmoGarbage404 (github) for Space Station 14.",
|
||||
"size": {
|
||||
"x": 32,
|
||||
"y": 32
|
||||
},
|
||||
"states": [
|
||||
{
|
||||
"name": "base"
|
||||
},
|
||||
{
|
||||
"name": "spinning",
|
||||
"delays":
|
||||
[
|
||||
[
|
||||
0.25,
|
||||
0.25,
|
||||
0.25,
|
||||
0.25
|
||||
]
|
||||
]
|
||||
},
|
||||
{
|
||||
"name": "panel"
|
||||
},
|
||||
{
|
||||
"name": "unshaded"
|
||||
}
|
||||
]
|
||||
}
|
||||
|
After Width: | Height: | Size: 194 B |
|
After Width: | Height: | Size: 407 B |
|
After Width: | Height: | Size: 206 B |