diff --git a/Content.Server/_CP14/Alchemy/CP14AlchemyExtractionSystem.cs b/Content.Server/_CP14/Alchemy/CP14AlchemyExtractionSystem.cs new file mode 100644 index 0000000000..16a3768949 --- /dev/null +++ b/Content.Server/_CP14/Alchemy/CP14AlchemyExtractionSystem.cs @@ -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(OnInteractUsing); + } + + private void OnInteractUsing(Entity mortar, ref InteractUsingEvent args) + { + if (!TryComp(args.Used, out var pestle)) + return; + + _audio.PlayPvs(pestle.HitSound, mortar); + + if (!_random.Prob(pestle.Probability)) + return; + + if (!TryComp(mortar, out var solutionManagerComp)) + return; + var solutionManager = new Entity(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(ent)?.JuiceSolution; + if (juiceSolution is null) + return; + + if (TryComp(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); + } +} diff --git a/Content.Server/_CP14/Alchemy/CP14MortarComponent.cs b/Content.Server/_CP14/Alchemy/CP14MortarComponent.cs new file mode 100644 index 0000000000..c5c14eab52 --- /dev/null +++ b/Content.Server/_CP14/Alchemy/CP14MortarComponent.cs @@ -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; +} + diff --git a/Content.Server/_CP14/Alchemy/CP14PestleComponent.cs b/Content.Server/_CP14/Alchemy/CP14PestleComponent.cs new file mode 100644 index 0000000000..65c878feb5 --- /dev/null +++ b/Content.Server/_CP14/Alchemy/CP14PestleComponent.cs @@ -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)); +} diff --git a/Content.Shared/Chemistry/Reaction/ReactionPrototype.cs b/Content.Shared/Chemistry/Reaction/ReactionPrototype.cs index f4e9619b17..584da74539 100644 --- a/Content.Shared/Chemistry/Reaction/ReactionPrototype.cs +++ b/Content.Shared/Chemistry/Reaction/ReactionPrototype.cs @@ -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)); /// /// If true, this reaction will only consume only integer multiples of the reactant amounts. If there are not diff --git a/Resources/Audio/_CP14/Effects/attributions.yml b/Resources/Audio/_CP14/Effects/attributions.yml new file mode 100644 index 0000000000..c4513e0a13 --- /dev/null +++ b/Resources/Audio/_CP14/Effects/attributions.yml @@ -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/" \ No newline at end of file diff --git a/Resources/Audio/_CP14/Effects/bubbles.ogg b/Resources/Audio/_CP14/Effects/bubbles.ogg new file mode 100644 index 0000000000..9f4dde02bf Binary files /dev/null and b/Resources/Audio/_CP14/Effects/bubbles.ogg differ diff --git a/Resources/Audio/_CP14/Items/Pestle/attributions.yml b/Resources/Audio/_CP14/Items/Pestle/attributions.yml new file mode 100644 index 0000000000..936f54bb20 --- /dev/null +++ b/Resources/Audio/_CP14/Items/Pestle/attributions.yml @@ -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/" \ No newline at end of file diff --git a/Resources/Audio/_CP14/Items/Pestle/pestle1.ogg b/Resources/Audio/_CP14/Items/Pestle/pestle1.ogg new file mode 100644 index 0000000000..6fb8eda366 Binary files /dev/null and b/Resources/Audio/_CP14/Items/Pestle/pestle1.ogg differ diff --git a/Resources/Audio/_CP14/Items/Pestle/pestle2.ogg b/Resources/Audio/_CP14/Items/Pestle/pestle2.ogg new file mode 100644 index 0000000000..9191d3d011 Binary files /dev/null and b/Resources/Audio/_CP14/Items/Pestle/pestle2.ogg differ diff --git a/Resources/Audio/_CP14/Items/Pestle/pestle3.ogg b/Resources/Audio/_CP14/Items/Pestle/pestle3.ogg new file mode 100644 index 0000000000..ab5d215b00 Binary files /dev/null and b/Resources/Audio/_CP14/Items/Pestle/pestle3.ogg differ diff --git a/Resources/Audio/_CP14/Items/Pestle/pestle4.ogg b/Resources/Audio/_CP14/Items/Pestle/pestle4.ogg new file mode 100644 index 0000000000..dc9b0ffad5 Binary files /dev/null and b/Resources/Audio/_CP14/Items/Pestle/pestle4.ogg differ diff --git a/Resources/Audio/_CP14/Items/Pestle/pestle5.ogg b/Resources/Audio/_CP14/Items/Pestle/pestle5.ogg new file mode 100644 index 0000000000..f635a04e78 Binary files /dev/null and b/Resources/Audio/_CP14/Items/Pestle/pestle5.ogg differ diff --git a/Resources/Prototypes/Chemistry/mixing_types.yml b/Resources/Prototypes/Chemistry/mixing_types.yml index bf3adb5795..f98818d84a 100644 --- a/Resources/Prototypes/Chemistry/mixing_types.yml +++ b/Resources/Prototypes/Chemistry/mixing_types.yml @@ -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 diff --git a/Resources/Prototypes/_CP14/Entities/Objects/Specific/Alchemy/cauldron.yml b/Resources/Prototypes/_CP14/Entities/Objects/Specific/Alchemy/cauldron.yml deleted file mode 100644 index 0bbf8330ec..0000000000 --- a/Resources/Prototypes/_CP14/Entities/Objects/Specific/Alchemy/cauldron.yml +++ /dev/null @@ -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 - \ No newline at end of file diff --git a/Resources/Prototypes/_CP14/Entities/Objects/Specific/Alchemy/tools.yml b/Resources/Prototypes/_CP14/Entities/Objects/Specific/Alchemy/tools.yml new file mode 100644 index 0000000000..90fb2cbcab --- /dev/null +++ b/Resources/Prototypes/_CP14/Entities/Objects/Specific/Alchemy/tools.yml @@ -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 \ No newline at end of file diff --git a/Resources/Prototypes/_CP14/Entities/Structures/Specific/Alchemy/vat.yml b/Resources/Prototypes/_CP14/Entities/Structures/Specific/Alchemy/vat.yml index 06f0ed6726..36606b2c0d 100644 --- a/Resources/Prototypes/_CP14/Entities/Structures/Specific/Alchemy/vat.yml +++ b/Resources/Prototypes/_CP14/Entities/Structures/Specific/Alchemy/vat.yml @@ -80,8 +80,7 @@ containerId: storagebase - type: Storage cP14CanStorageSolutionManagers: false + maxItemSize: Normal grid: - 0,0,4,3 - blacklist: - components: - - SolutionContainerManager \ No newline at end of file + - type: Anchorable \ No newline at end of file diff --git a/Resources/Prototypes/_CP14/SoundCollections/alchemy.yml b/Resources/Prototypes/_CP14/SoundCollections/alchemy.yml new file mode 100644 index 0000000000..26c19f03b8 --- /dev/null +++ b/Resources/Prototypes/_CP14/SoundCollections/alchemy.yml @@ -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 \ No newline at end of file diff --git a/Resources/Textures/_CP14/Objects/Specific/Alchemy/mortar_pestle.rsi/full.png b/Resources/Textures/_CP14/Objects/Specific/Alchemy/mortar_pestle.rsi/full.png new file mode 100644 index 0000000000..8e941b8a76 Binary files /dev/null and b/Resources/Textures/_CP14/Objects/Specific/Alchemy/mortar_pestle.rsi/full.png differ diff --git a/Resources/Textures/_CP14/Objects/Specific/Alchemy/mortar_pestle.rsi/liq-1.png b/Resources/Textures/_CP14/Objects/Specific/Alchemy/mortar_pestle.rsi/liq-1.png new file mode 100644 index 0000000000..edec1a176c Binary files /dev/null and b/Resources/Textures/_CP14/Objects/Specific/Alchemy/mortar_pestle.rsi/liq-1.png differ diff --git a/Resources/Textures/_CP14/Objects/Specific/Alchemy/mortar_pestle.rsi/liq-2.png b/Resources/Textures/_CP14/Objects/Specific/Alchemy/mortar_pestle.rsi/liq-2.png new file mode 100644 index 0000000000..9b96e0e940 Binary files /dev/null and b/Resources/Textures/_CP14/Objects/Specific/Alchemy/mortar_pestle.rsi/liq-2.png differ diff --git a/Resources/Textures/_CP14/Objects/Specific/Alchemy/mortar_pestle.rsi/liq-3.png b/Resources/Textures/_CP14/Objects/Specific/Alchemy/mortar_pestle.rsi/liq-3.png new file mode 100644 index 0000000000..f8d5b6cf4f Binary files /dev/null and b/Resources/Textures/_CP14/Objects/Specific/Alchemy/mortar_pestle.rsi/liq-3.png differ diff --git a/Resources/Textures/_CP14/Objects/Specific/Alchemy/mortar_pestle.rsi/liq-4.png b/Resources/Textures/_CP14/Objects/Specific/Alchemy/mortar_pestle.rsi/liq-4.png new file mode 100644 index 0000000000..e6e528e05e Binary files /dev/null and b/Resources/Textures/_CP14/Objects/Specific/Alchemy/mortar_pestle.rsi/liq-4.png differ diff --git a/Resources/Textures/_CP14/Objects/Specific/Alchemy/mortar_pestle.rsi/meta.json b/Resources/Textures/_CP14/Objects/Specific/Alchemy/mortar_pestle.rsi/meta.json new file mode 100644 index 0000000000..26006bfbd6 --- /dev/null +++ b/Resources/Textures/_CP14/Objects/Specific/Alchemy/mortar_pestle.rsi/meta.json @@ -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" + } + ] +} \ No newline at end of file diff --git a/Resources/Textures/_CP14/Objects/Specific/Alchemy/mortar_pestle.rsi/mortar_base.png b/Resources/Textures/_CP14/Objects/Specific/Alchemy/mortar_pestle.rsi/mortar_base.png new file mode 100644 index 0000000000..5c128df368 Binary files /dev/null and b/Resources/Textures/_CP14/Objects/Specific/Alchemy/mortar_pestle.rsi/mortar_base.png differ diff --git a/Resources/Textures/_CP14/Objects/Specific/Alchemy/mortar_pestle.rsi/pestle.png b/Resources/Textures/_CP14/Objects/Specific/Alchemy/mortar_pestle.rsi/pestle.png new file mode 100644 index 0000000000..96a7f164fc Binary files /dev/null and b/Resources/Textures/_CP14/Objects/Specific/Alchemy/mortar_pestle.rsi/pestle.png differ