using System; using System.Collections.Generic; using Content.Shared.Chemistry; using Robust.Shared.GameObjects; using Robust.Shared.IoC; using Robust.Shared.Maths; using Robust.Shared.Prototypes; using Robust.Shared.Serialization; using Robust.Shared.ViewVariables; namespace Content.Shared.GameObjects.Components.Chemistry { public class SolutionComponent : Component { #pragma warning disable 649 [Dependency] private readonly IPrototypeManager _prototypeManager; #pragma warning restore 649 [ViewVariables] protected Solution _containedSolution = new Solution(); protected decimal _maxVolume; private SolutionCaps _capabilities; /// /// Triggered when the solution contents change. /// public event Action SolutionChanged; /// /// The maximum volume of the container. /// [ViewVariables(VVAccess.ReadWrite)] public decimal MaxVolume { get => _maxVolume; set => _maxVolume = value; // Note that the contents won't spill out if the capacity is reduced. } /// /// The total volume of all the of the reagents in the container. /// [ViewVariables] public decimal CurrentVolume => _containedSolution.TotalVolume; /// /// The volume without reagents remaining in the container. /// [ViewVariables] public decimal EmptyVolume => MaxVolume - CurrentVolume; /// /// The current blended color of all the reagents in the container. /// [ViewVariables(VVAccess.ReadWrite)] public Color SubstanceColor { get; private set; } /// /// The current capabilities of this container (is the top open to pour? can I inject it into another object?). /// [ViewVariables(VVAccess.ReadWrite)] public SolutionCaps Capabilities { get => _capabilities; set => _capabilities = value; } public IReadOnlyList ReagentList => _containedSolution.Contents; /// /// Shortcut for Capabilities PourIn flag to avoid binary operators. /// public bool CanPourIn => (Capabilities & SolutionCaps.PourIn) != 0; /// /// Shortcut for Capabilities PourOut flag to avoid binary operators. /// public bool CanPourOut => (Capabilities & SolutionCaps.PourOut) != 0; /// /// Shortcut for Capabilities Injectable flag /// public bool Injectable => (Capabilities & SolutionCaps.Injectable) != 0; /// /// Shortcut for Capabilities Injector flag /// public bool Injector => (Capabilities & SolutionCaps.Injector) != 0; /// public override string Name => "Solution"; /// public sealed override uint? NetID => ContentNetIDs.SOLUTION; /// 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); } /// protected override void Startup() { base.Startup(); RecalculateColor(); } /// protected override void Shutdown() { base.Shutdown(); _containedSolution.RemoveAllSolution(); _containedSolution = new Solution(); } public void RemoveAllSolution() { _containedSolution.RemoveAllSolution(); OnSolutionChanged(); } public bool TryRemoveReagent(string reagentId, decimal quantity) { if (!ContainsReagent(reagentId, out var currentQuantity)) return false; _containedSolution.RemoveReagent(reagentId, quantity); OnSolutionChanged(); return true; } /// /// Attempt to remove the specified quantity from this solution /// /// Quantity of this solution to remove /// Whether or not the solution was successfully removed public bool TryRemoveSolution(int quantity) { if (CurrentVolume == 0) return false; _containedSolution.RemoveSolution(quantity); OnSolutionChanged(); return true; } public Solution SplitSolution(decimal quantity) { var solutionSplit = _containedSolution.SplitSolution(quantity); OnSolutionChanged(); return solutionSplit; } protected void RecalculateColor() { if(_containedSolution.TotalVolume == 0) SubstanceColor = Color.White; Color mixColor = default; var runningTotalQuantity = 0M; 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, (float) (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); } /// public override ComponentState GetComponentState() { return new SolutionComponentState(); } /// public override void HandleComponentState(ComponentState curState, ComponentState nextState) { base.HandleComponentState(curState, nextState); if(curState == null) return; var compState = (SolutionComponentState)curState; //TODO: Make me work! } [Serializable, NetSerializable] public class SolutionComponentState : ComponentState { public SolutionComponentState() : base(ContentNetIDs.SOLUTION) { } } /// /// Check if the solution contains the specified reagent. /// /// The reagent to check for. /// Output the quantity of the reagent if it is contained, 0 if it isn't. /// Return true if the solution contains the reagent. public bool ContainsReagent(string reagentId, out decimal quantity) { foreach (var reagent in _containedSolution.Contents) { if (reagent.ReagentId == reagentId) { quantity = reagent.Quantity; return true; } } quantity = 0; return false; } protected virtual void OnSolutionChanged() { SolutionChanged?.Invoke(); } } }