Chemistry revamp and bartending features
-Ability to mix drinks to create cocktails with shiny icons -New Chemistry System which can relay chemistry events to corresponding components -moved some solution logic from Shared to Server -fixed some weird stuff with DrinkComponent
This commit is contained in:
@@ -1,7 +1,9 @@
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.ComponentModel.Design;
|
||||
using System.Linq;
|
||||
using Content.Server.Chemistry;
|
||||
using Content.Shared.GameObjects.Components.Chemistry;
|
||||
using Content.Server.GameObjects.Components.Nutrition;
|
||||
using Content.Server.GameObjects.EntitySystems;
|
||||
using Content.Server.Interfaces;
|
||||
@@ -12,16 +14,19 @@ using Robust.Shared.GameObjects;
|
||||
using Robust.Shared.Interfaces.GameObjects;
|
||||
using Robust.Shared.IoC;
|
||||
using Robust.Shared.Localization;
|
||||
using Robust.Shared.Maths;
|
||||
using Robust.Shared.Prototypes;
|
||||
using Robust.Shared.Serialization;
|
||||
using Robust.Shared.Utility;
|
||||
using Robust.Shared.ViewVariables;
|
||||
|
||||
namespace Content.Server.GameObjects.Components.Chemistry
|
||||
{
|
||||
/// <summary>
|
||||
/// Shared ECS component that manages a liquid solution of reagents.
|
||||
/// ECS component that manages a liquid solution of reagents.
|
||||
/// </summary>
|
||||
[RegisterComponent]
|
||||
internal class SolutionComponent : Shared.GameObjects.Components.Chemistry.SolutionComponent, IExamine
|
||||
internal class SolutionComponent : SharedSolutionComponent, IExamine
|
||||
{
|
||||
#pragma warning disable 649
|
||||
[Dependency] private readonly IPrototypeManager _prototypeManager;
|
||||
@@ -31,17 +36,93 @@ namespace Content.Server.GameObjects.Components.Chemistry
|
||||
|
||||
private IEnumerable<ReactionPrototype> _reactions;
|
||||
private AudioSystem _audioSystem;
|
||||
private ChemistrySystem _chemistrySystem;
|
||||
|
||||
[ViewVariables]
|
||||
protected Solution _containedSolution = new Solution();
|
||||
protected int _maxVolume;
|
||||
private SolutionCaps _capabilities;
|
||||
|
||||
/// <summary>
|
||||
/// The maximum volume of the container.
|
||||
/// </summary>
|
||||
[ViewVariables(VVAccess.ReadWrite)]
|
||||
public int MaxVolume
|
||||
{
|
||||
get => _maxVolume;
|
||||
set => _maxVolume = value; // Note that the contents won't spill out if the capacity is reduced.
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// The total volume of all the of the reagents in the container.
|
||||
/// </summary>
|
||||
[ViewVariables]
|
||||
public int CurrentVolume => _containedSolution.TotalVolume;
|
||||
|
||||
/// <summary>
|
||||
/// The volume without reagents remaining in the container.
|
||||
/// </summary>
|
||||
[ViewVariables]
|
||||
public int EmptyVolume => MaxVolume - CurrentVolume;
|
||||
|
||||
/// <summary>
|
||||
/// The current blended color of all the reagents in the container.
|
||||
/// </summary>
|
||||
[ViewVariables(VVAccess.ReadWrite)]
|
||||
public Color SubstanceColor { get; private set; }
|
||||
|
||||
/// <summary>
|
||||
/// The current capabilities of this container (is the top open to pour? can I inject it into another object?).
|
||||
/// </summary>
|
||||
[ViewVariables(VVAccess.ReadWrite)]
|
||||
public SolutionCaps Capabilities
|
||||
{
|
||||
get => _capabilities;
|
||||
set => _capabilities = value;
|
||||
}
|
||||
|
||||
public IReadOnlyList<Solution.ReagentQuantity> ReagentList => _containedSolution.Contents;
|
||||
|
||||
/// <summary>
|
||||
/// Shortcut for Capabilities PourIn flag to avoid binary operators.
|
||||
/// </summary>
|
||||
public bool CanPourIn => (Capabilities & SolutionCaps.PourIn) != 0;
|
||||
/// <summary>
|
||||
/// Shortcut for Capabilities PourOut flag to avoid binary operators.
|
||||
/// </summary>
|
||||
public bool CanPourOut => (Capabilities & SolutionCaps.PourOut) != 0;
|
||||
/// <summary>
|
||||
/// Shortcut for Capabilities Injectable flag
|
||||
/// </summary>
|
||||
public bool Injectable => (Capabilities & SolutionCaps.Injectable) != 0;
|
||||
/// <summary>
|
||||
/// Shortcut for Capabilities Injector flag
|
||||
/// </summary>
|
||||
public bool Injector => (Capabilities & SolutionCaps.Injector) != 0;
|
||||
|
||||
/// <inheritdoc />
|
||||
public override void ExposeData(ObjectSerializer serializer)
|
||||
{
|
||||
base.ExposeData(serializer);
|
||||
|
||||
serializer.DataField(ref _maxVolume, "maxVol", 0);
|
||||
serializer.DataField(ref _containedSolution, "contents", _containedSolution);
|
||||
serializer.DataField(ref _capabilities, "caps", SolutionCaps.None);
|
||||
}
|
||||
|
||||
public override void Initialize()
|
||||
{
|
||||
base.Initialize();
|
||||
_audioSystem = _entitySystemManager.GetEntitySystem<AudioSystem>();
|
||||
_chemistrySystem = _entitySystemManager.GetEntitySystem<ChemistrySystem>();
|
||||
_reactions = _prototypeManager.EnumeratePrototypes<ReactionPrototype>();
|
||||
|
||||
}
|
||||
|
||||
protected override void Startup()
|
||||
{
|
||||
base.Startup();
|
||||
Init();
|
||||
}
|
||||
|
||||
public void Init()
|
||||
{
|
||||
_reactions = _prototypeManager.EnumeratePrototypes<ReactionPrototype>();
|
||||
_audioSystem = _entitySystemManager.GetEntitySystem<AudioSystem>();
|
||||
RecalculateColor();
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
@@ -54,6 +135,85 @@ namespace Content.Server.GameObjects.Components.Chemistry
|
||||
_reactions = _prototypeManager.EnumeratePrototypes<ReactionPrototype>();
|
||||
}
|
||||
|
||||
/// <inheritdoc />
|
||||
protected override void Shutdown()
|
||||
{
|
||||
base.Shutdown();
|
||||
|
||||
_containedSolution.RemoveAllSolution();
|
||||
_containedSolution = new Solution();
|
||||
}
|
||||
|
||||
public void RemoveAllSolution()
|
||||
{
|
||||
_containedSolution.RemoveAllSolution();
|
||||
OnSolutionChanged();
|
||||
}
|
||||
|
||||
public bool TryRemoveReagent(string reagentId, int quantity)
|
||||
{
|
||||
if (!ContainsReagent(reagentId, out var currentQuantity)) return false;
|
||||
|
||||
_containedSolution.RemoveReagent(reagentId, quantity);
|
||||
OnSolutionChanged();
|
||||
return true;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Attempt to remove the specified quantity from this solution
|
||||
/// </summary>
|
||||
/// <param name="quantity">Quantity of this solution to remove</param>
|
||||
/// <returns>Whether or not the solution was successfully removed</returns>
|
||||
public bool TryRemoveSolution(int quantity)
|
||||
{
|
||||
if (CurrentVolume == 0)
|
||||
return false;
|
||||
|
||||
_containedSolution.RemoveSolution(quantity);
|
||||
OnSolutionChanged();
|
||||
return true;
|
||||
}
|
||||
|
||||
public Solution SplitSolution(int quantity)
|
||||
{
|
||||
var solutionSplit = _containedSolution.SplitSolution(quantity);
|
||||
OnSolutionChanged();
|
||||
return solutionSplit;
|
||||
}
|
||||
|
||||
protected void RecalculateColor()
|
||||
{
|
||||
if(_containedSolution.TotalVolume == 0)
|
||||
SubstanceColor = Color.White;
|
||||
|
||||
Color mixColor = default;
|
||||
float runningTotalQuantity = 0;
|
||||
|
||||
foreach (var reagent in _containedSolution)
|
||||
{
|
||||
runningTotalQuantity += reagent.Quantity;
|
||||
|
||||
if(!_prototypeManager.TryIndex(reagent.ReagentId, out ReagentPrototype proto))
|
||||
continue;
|
||||
|
||||
if (mixColor == default)
|
||||
mixColor = proto.SubstanceColor;
|
||||
|
||||
mixColor = BlendRGB(mixColor, proto.SubstanceColor, reagent.Quantity / runningTotalQuantity);
|
||||
}
|
||||
}
|
||||
|
||||
private Color BlendRGB(Color rgb1, Color rgb2, float amount)
|
||||
{
|
||||
var r = (float)Math.Round(rgb1.R + (rgb2.R - rgb1.R) * amount, 1);
|
||||
var g = (float)Math.Round(rgb1.G + (rgb2.G - rgb1.G) * amount, 1);
|
||||
var b = (float)Math.Round(rgb1.B + (rgb2.B - rgb1.B) * amount, 1);
|
||||
var alpha = (float)Math.Round(rgb1.A + (rgb2.A - rgb1.A) * amount, 1);
|
||||
|
||||
return new Color(r, g, b, alpha);
|
||||
}
|
||||
|
||||
|
||||
/// <summary>
|
||||
/// Transfers solution from the held container to the target container.
|
||||
/// </summary>
|
||||
@@ -121,6 +281,10 @@ namespace Content.Server.GameObjects.Components.Chemistry
|
||||
void IExamine.Examine(FormattedMessage message)
|
||||
{
|
||||
message.AddText(_loc.GetString("Contains:\n"));
|
||||
if (ReagentList.Count == 0)
|
||||
{
|
||||
message.AddText("Nothing.\n");
|
||||
}
|
||||
foreach (var reagent in ReagentList)
|
||||
{
|
||||
if (_prototypeManager.TryIndex(reagent.ReagentId, out ReagentPrototype proto))
|
||||
@@ -256,7 +420,7 @@ namespace Content.Server.GameObjects.Components.Chemistry
|
||||
}
|
||||
if(!skipReactionCheck)
|
||||
CheckForReaction();
|
||||
OnSolutionChanged();
|
||||
_chemistrySystem.HandleSolutionChange(Owner);
|
||||
return true;
|
||||
}
|
||||
|
||||
@@ -324,5 +488,37 @@ namespace Content.Server.GameObjects.Components.Chemistry
|
||||
//Play reaction sound client-side
|
||||
_audioSystem.Play("/Audio/effects/chemistry/bubbles.ogg", Owner.Transform.GridPosition);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Check if the solution contains the specified reagent.
|
||||
/// </summary>
|
||||
/// <param name="reagentId">The reagent to check for.</param>
|
||||
/// <param name="quantity">Output the quantity of the reagent if it is contained, 0 if it isn't.</param>
|
||||
/// <returns>Return true if the solution contains the reagent.</returns>
|
||||
public bool ContainsReagent(string reagentId, out int quantity)
|
||||
{
|
||||
foreach (var reagent in _containedSolution.Contents)
|
||||
{
|
||||
if (reagent.ReagentId == reagentId)
|
||||
{
|
||||
quantity = reagent.Quantity;
|
||||
return true;
|
||||
}
|
||||
}
|
||||
quantity = 0;
|
||||
return false;
|
||||
}
|
||||
|
||||
public string GetMajorReagentId()
|
||||
{
|
||||
if (_containedSolution.Contents.Count == 0)
|
||||
{
|
||||
return "";
|
||||
}
|
||||
var majorReagent = _containedSolution.Contents.OrderByDescending(reagent => reagent.Quantity).First();;
|
||||
return majorReagent.ReagentId;
|
||||
}
|
||||
|
||||
protected virtual void OnSolutionChanged() => _chemistrySystem.HandleSolutionChange(Owner);
|
||||
}
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user