Alchemy tools: pestle and mortar (#144)

* add mortar & pestle mechanic

* setup prototypes

* Update mixing_types.yml

* add bubble sound collection, fix errors
This commit is contained in:
Ed
2024-05-08 17:15:31 +03:00
committed by GitHub
parent 54d86b8d8f
commit 7b2328ba22
25 changed files with 321 additions and 78 deletions

View File

@@ -0,0 +1,88 @@
using Content.Shared.Chemistry.Components;
using Content.Shared.Chemistry.Components.SolutionManager;
using Content.Shared.Chemistry.EntitySystems;
using Content.Shared.FixedPoint;
using Content.Shared.Interaction;
using Content.Shared.Kitchen.Components;
using Content.Shared.Stacks;
using Robust.Shared.Audio.Systems;
using Robust.Shared.Containers;
using Robust.Shared.Random;
namespace Content.Server._CP14.Alchemy;
public sealed partial class CP14AlchemyExtractionSystem : EntitySystem
{
[Dependency] private readonly SharedAudioSystem _audio = default!;
[Dependency] private readonly SharedContainerSystem _container = default!;
[Dependency] private readonly SharedSolutionContainerSystem _solutionContainer = default!;
[Dependency] private readonly IRobustRandom _random = default!;
[Dependency] private readonly SharedStackSystem _stackSystem = default!;
public override void Initialize()
{
base.Initialize();
SubscribeLocalEvent<CP14MortarComponent, InteractUsingEvent>(OnInteractUsing);
}
private void OnInteractUsing(Entity<CP14MortarComponent> mortar, ref InteractUsingEvent args)
{
if (!TryComp<CP14PestleComponent>(args.Used, out var pestle))
return;
_audio.PlayPvs(pestle.HitSound, mortar);
if (!_random.Prob(pestle.Probability))
return;
if (!TryComp<SolutionContainerManagerComponent>(mortar, out var solutionManagerComp))
return;
var solutionManager = new Entity<SolutionContainerManagerComponent?>(mortar, solutionManagerComp);
if (!_solutionContainer.TryGetSolution(solutionManager, mortar.Comp.Solution, out var solutionEnt, out var solution))
return;
if (!_container.TryGetContainer(mortar, mortar.Comp.ContainerId, out var container))
return;
if (container.ContainedEntities.Count == 0)
return;
var ent = _random.Pick(container.ContainedEntities);
var juiceSolution = CompOrNull<ExtractableComponent>(ent)?.JuiceSolution;
if (juiceSolution is null)
return;
if (TryComp<StackComponent>(ent, out var stack))
{
var totalVolume = juiceSolution.Volume * stack.Count;
if (totalVolume <= 0)
return;
// Maximum number of items we can process in the stack without going over AvailableVolume
// We add a small tolerance, because floats are inaccurate.
var fitsCount = (int) (stack.Count * FixedPoint2.Min(solution.AvailableVolume / totalVolume + 0.01, 1));
if (fitsCount <= 0)
return;
// Make a copy of the solution to scale
// Otherwise we'll actually change the volume of the remaining stack too
var scaledSolution = new Solution(juiceSolution);
scaledSolution.ScaleSolution(fitsCount);
juiceSolution = scaledSolution;
_stackSystem.SetCount(ent, stack.Count - fitsCount); // Setting to 0 will QueueDel
}
else
{
if (juiceSolution.Volume > solution.AvailableVolume)
return;
QueueDel(ent);
}
_solutionContainer.TryAddSolution(solutionEnt.Value, juiceSolution);
}
}

View File

@@ -0,0 +1,13 @@
namespace Content.Server._CP14.Alchemy;
[RegisterComponent, Access(typeof(CP14AlchemyExtractionSystem))]
public sealed partial class CP14MortarComponent : Component
{
[DataField(required: true)]
public string Solution = string.Empty;
[DataField(required: true)]
public string ContainerId = string.Empty;
}

View File

@@ -0,0 +1,13 @@
using Robust.Shared.Audio;
namespace Content.Server._CP14.Alchemy;
[RegisterComponent, Access(typeof(CP14AlchemyExtractionSystem))]
public sealed partial class CP14PestleComponent : Component
{
[DataField]
public float Probability = 0.1f;
[DataField]
public SoundSpecifier HitSound = new SoundCollectionSpecifier("CP14Pestle", AudioParams.Default.WithVariation(0.2f));
}

View File

@@ -68,7 +68,7 @@ namespace Content.Shared.Chemistry.Reaction
[DataField("impact", serverOnly: true)] public LogImpact Impact = LogImpact.Low;
// TODO SERV3: Empty on the client, (de)serialize on the server with module manager is server module
[DataField("sound", serverOnly: true)] public SoundSpecifier Sound { get; private set; } = new SoundPathSpecifier("/Audio/Effects/Chemistry/bubbles.ogg");
[DataField("sound", serverOnly: true)] public SoundSpecifier Sound { get; private set; } = new SoundCollectionSpecifier("CP14Bubbles", AudioParams.Default.WithVariation(0.2f));
/// <summary>
/// If true, this reaction will only consume only integer multiples of the reactant amounts. If there are not

View File

@@ -0,0 +1,4 @@
- files: ["bubbles.ogg"]
license: "CC0-1.0"
copyright: 'magnuswaker of Freesound.org. Mixed from stereo to mono.'
source: "https://freesound.org/people/magnuswaker/sounds/540074/"

Binary file not shown.

View File

@@ -0,0 +1,4 @@
- files: ["pestle1.ogg", "pestle2.ogg", "pestle3.ogg", "pestle4.ogg", "pestle5.ogg"]
license: "CC-BY-4.0"
copyright: 'Australopithecusman of Freesound.org. Cropped and Mixed from stereo to mono.'
source: "https://freesound.org/people/Australopithecusman/sounds/388900/"

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

View File

@@ -19,15 +19,15 @@
id: DummyJuice
verbText: mixing-verb-default-juice
icon:
sprite: Structures/Machines/juicer.rsi
state: juicer0
sprite: _CP14/Objects/Specific/Alchemy/mortar_pestle.rsi
state: full
- type: mixingCategory
id: DummyCondense
verbText: mixing-verb-default-condense
icon:
sprite: Structures/Piping/Atmospherics/condenser.rsi
state: display
sprite: _CP14/Objects/Specific/Alchemy/mortar_pestle.rsi
state: full
# Alternative Mixing Methods

View File

@@ -1,70 +0,0 @@
- type: entity
parent: BaseItem
id: CP14Cauldron
name: cauldron
description: A heavy cauldron. It is not as bulky as a vat, but can be carried in your hands.
components:
- type: Item
size: Ginormous
- type: MultiHandedItem
- type: ClothingSpeedModifier
walkModifier: 0.6
sprintModifier: 0.6
- type: HeldSpeedModifier
- type: Sprite
sprite: _CP14/Objects/Specific/Alchemy/cauldron.rsi
layers:
- state: icon
- state: liq-1
map: ["enum.SolutionContainerLayers.Fill"]
visible: false
- type: SolutionContainerManager
solutions:
vat:
maxVol: 100
- type: Spillable
solution: vat
- type: DrainableSolution
solution: vat
- type: ExaminableSolution
solution: vat
- type: MixableSolution
solution: vat
- type: RefillableSolution
solution: vat
- type: DrawableSolution
solution: vat
- type: DumpableSolution
solution: vat
- type: SolutionItemStatus
solution: vat
- type: Drink
solution: vat
- type: Injector
solutionName: vat
injectOnly: false
ignoreMobs: true
minTransferAmount: 10
maxTransferAmount: 100
transferAmount: 50
toggleState: 1 # draw
- type: UserInterface
interfaces:
enum.TransferAmountUiKey.Key:
type: TransferAmountBoundUserInterface
- type: Appearance
- type: SolutionContainerVisuals
maxFillLevels: 5
fillBaseName: liq-
inHandsMaxFillLevels: 5
inHandsFillBaseName: -fill
- type: DamageOtherOnHit
damage:
types:
Blunt: 5
- type: DamageOnHighSpeedImpact
minimumSpeed: 2
damage:
types:
Blunt: 5

View File

@@ -0,0 +1,146 @@
- type: entity
parent: BaseItem
id: CP14Cauldron
name: cauldron
description: A heavy cauldron. It is not as bulky as a vat, but can be carried in your hands.
components:
- type: Item
size: Ginormous
- type: MultiHandedItem
- type: ClothingSpeedModifier
walkModifier: 0.6
sprintModifier: 0.6
- type: HeldSpeedModifier
- type: Sprite
sprite: _CP14/Objects/Specific/Alchemy/cauldron.rsi
layers:
- state: icon
- state: liq-1
map: ["enum.SolutionContainerLayers.Fill"]
visible: false
- type: SolutionContainerManager
solutions:
vat:
maxVol: 100
- type: Spillable
solution: vat
- type: DrainableSolution
solution: vat
- type: ExaminableSolution
solution: vat
- type: MixableSolution
solution: vat
- type: RefillableSolution
solution: vat
- type: DrawableSolution
solution: vat
- type: DumpableSolution
solution: vat
- type: SolutionItemStatus
solution: vat
- type: Drink
solution: vat
- type: Injector
solutionName: vat
injectOnly: false
ignoreMobs: true
minTransferAmount: 10
maxTransferAmount: 100
transferAmount: 50
toggleState: 1 # draw
- type: UserInterface
interfaces:
enum.TransferAmountUiKey.Key:
type: TransferAmountBoundUserInterface
- type: Appearance
- type: SolutionContainerVisuals
maxFillLevels: 5
fillBaseName: liq-
inHandsMaxFillLevels: 5
inHandsFillBaseName: -fill
- type: DamageOtherOnHit
damage:
types:
Blunt: 5
- type: DamageOnHighSpeedImpact
minimumSpeed: 2
damage:
types:
Blunt: 5
- type: entity
id: CP14Pestle
name: pestle
description: A device for conveniently grinding various materials in liquids
parent: BaseItem
components:
- type: Item
size: Tiny
- type: Sprite
sprite: _CP14/Objects/Specific/Alchemy/mortar_pestle.rsi
state: pestle
- type: CP14Pestle
- type: entity
id: CP14Mortar
name: mortar
description: Alchemical sturdy plate for grinding reagents
parent: BaseStorageItem
components:
- type: Item
size: Normal
- type: Sprite
sprite: _CP14/Objects/Specific/Alchemy/mortar_pestle.rsi
layers:
- state: mortar_base
- state: liq-1
map: ["enum.SolutionContainerLayers.Fill"]
visible: false
- type: SolutionContainerManager
solutions:
mortar:
maxVol: 30
- type: Spillable
solution: mortar
- type: DrainableSolution
solution: mortar
- type: ExaminableSolution
solution: mortar
- type: MixableSolution
solution: mortar
- type: RefillableSolution
solution: mortar
- type: DrawableSolution
solution: mortar
- type: DumpableSolution
solution: mortar
- type: SolutionItemStatus
solution: mortar
- type: Drink
solution: mortar
- type: Injector
solutionName: mortar
injectOnly: false
ignoreMobs: true
minTransferAmount: 1
maxTransferAmount: 10
transferAmount: 5
toggleState: 1 # draw
- type: UserInterface
interfaces:
enum.TransferAmountUiKey.Key:
type: TransferAmountBoundUserInterface
enum.StorageUiKey.Key:
type: StorageBoundUserInterface
- type: Appearance
- type: SolutionContainerVisuals
maxFillLevels: 4
fillBaseName: liq-
- type: Storage
maxItemSize: Normal
cP14CanStorageSolutionManagers: false
grid:
- 0,0,1,1
- type: CP14Mortar
solution: mortar
containerId: storagebase

View File

@@ -80,8 +80,7 @@
containerId: storagebase
- type: Storage
cP14CanStorageSolutionManagers: false
maxItemSize: Normal
grid:
- 0,0,4,3
blacklist:
components:
- SolutionContainerManager
- type: Anchorable

View File

@@ -0,0 +1,14 @@
- type: soundCollection
id: CP14Pestle
files:
- /Audio/_CP14/Items/Pestle/pestle1.ogg
- /Audio/_CP14/Items/Pestle/pestle2.ogg
- /Audio/_CP14/Items/Pestle/pestle3.ogg
- /Audio/_CP14/Items/Pestle/pestle4.ogg
- /Audio/_CP14/Items/Pestle/pestle5.ogg
- type: soundCollection
id: CP14Bubbles
files:
- /Audio/Effects/Chemistry/bubbles.ogg
- /Audio/_CP14/Effects/bubbles.ogg

Binary file not shown.

After

Width:  |  Height:  |  Size: 412 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 114 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 137 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 158 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 159 B

View File

@@ -0,0 +1,32 @@
{
"version": 1,
"size": {
"x": 32,
"y": 32
},
"license": "CC-BY-SA-3.0",
"copyright": "Created by TheShuEd (Github) for CrystallPunk14",
"states": [
{
"name": "full"
},
{
"name": "liq-1"
},
{
"name": "liq-2"
},
{
"name": "liq-3"
},
{
"name": "liq-4"
},
{
"name": "mortar_base"
},
{
"name": "pestle"
}
]
}

Binary file not shown.

After

Width:  |  Height:  |  Size: 363 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 251 B