Solution refactor (#4407)
* Rename SolutionContainerCaps -> Capability * Move IExamine event to Chemistry System. * ECS the ISolutionChange into SolutionChangeEvent * Unify SolutionContainer into a single shared component * Replace ISolutionInteraction with SolutionContainerComponent * Move all methods from SolutionContainer to ChemistrySystem * Refactor EntitySystem calls to Dependencies * Refactor SolutionContainer to SolutionManager * Fix yamls * Fix test fails * Fix post merge issues * Fix various issues with SolutionManager * More fixes * Fix more components * Fix events not being directed * Fixes for Hypospray * Separate removal and iteration on Metabolism * Fix creampie problems * Address some of sloth's issues * Refactors for Systems * Refactored solution location * Fix tests * Address more sloth issues * Fix dependency * Fix merge conflicts * Add xmldocs for Capabilities components * Remove HasSolution/TryGetDefaultSolution and Add/Remove Drainable/Refillable * Replace Grindable/Juiceable with Extractable * Refactor field names * Fix Drainable * Fix some issues with spillable and injector * Fix issues with Grinder * Fix Beaker having duplicate solutions * Fix foaming * Address some MGS issues * Fix Uid issues * Fix errors in solution Tranfer * Fixed some extra values constant values * Cola is drinkable now
This commit is contained in:
@@ -1,12 +0,0 @@
|
||||
using Content.Shared.Chemistry.Solution.Components;
|
||||
using Robust.Shared.GameObjects;
|
||||
|
||||
namespace Content.Client.Chemistry.Components
|
||||
{
|
||||
[RegisterComponent]
|
||||
[ComponentReference(typeof(SharedSolutionContainerComponent))]
|
||||
public class SolutionContainerComponent : SharedSolutionContainerComponent
|
||||
{
|
||||
|
||||
}
|
||||
}
|
||||
@@ -1,5 +1,5 @@
|
||||
using System;
|
||||
using Content.Shared.Chemistry.Solution.Components;
|
||||
using Content.Shared.Chemistry.EntitySystems;
|
||||
using JetBrains.Annotations;
|
||||
using Robust.Client.GameObjects;
|
||||
using Robust.Shared.Maths;
|
||||
|
||||
@@ -224,8 +224,7 @@ namespace Content.Client.Entry
|
||||
"DebugEquip",
|
||||
"InnateActions",
|
||||
"ReagentGrinder",
|
||||
"Grindable",
|
||||
"Juiceable",
|
||||
"Extractable",
|
||||
"WelderRefinable",
|
||||
"ConveyorAssembly",
|
||||
"TwoWayLever",
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
using System.Collections.Generic;
|
||||
using Content.Shared.Chemistry.Components;
|
||||
using Content.Shared.Chemistry.Reagent;
|
||||
using Content.Shared.Chemistry.Solution;
|
||||
using Content.Shared.Kitchen.Components;
|
||||
using Robust.Client.AutoGenerated;
|
||||
using Robust.Client.GameObjects;
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using Content.Shared.Chemistry.Components;
|
||||
using Content.Shared.Chemistry.Reagent;
|
||||
using Content.Shared.Chemistry.Solution;
|
||||
using Content.Shared.Kitchen.Components;
|
||||
using JetBrains.Annotations;
|
||||
using Robust.Client.GameObjects;
|
||||
|
||||
@@ -4,7 +4,7 @@ using Robust.Client.UserInterface.Controls;
|
||||
using Robust.Shared.GameObjects;
|
||||
using Robust.Shared.IoC;
|
||||
using Robust.Shared.Prototypes;
|
||||
using static Content.Shared.Chemistry.Solution.Solution;
|
||||
using static Content.Shared.Chemistry.Components.Solution;
|
||||
|
||||
namespace Content.Client.Kitchen.UI
|
||||
{
|
||||
|
||||
@@ -28,6 +28,7 @@ namespace Content.IntegrationTests.Tests.Body
|
||||
components:
|
||||
- type: Bloodstream
|
||||
max_volume: 100
|
||||
- type: SolutionContainerManager
|
||||
- type: Body
|
||||
template: HumanoidTemplate
|
||||
preset: HumanPreset
|
||||
|
||||
@@ -1,17 +1,13 @@
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
using System.Threading.Tasks;
|
||||
using Content.Server.Chemistry.Components;
|
||||
using Content.Server.Fluids.Components;
|
||||
using Content.Shared.Chemistry.Components;
|
||||
using Content.Shared.Chemistry.EntitySystems;
|
||||
using Content.Shared.Chemistry.Reaction;
|
||||
using Content.Shared.Chemistry.Reagent;
|
||||
using Content.Shared.Coordinates;
|
||||
using NUnit.Framework;
|
||||
using Robust.Shared.GameObjects;
|
||||
using Robust.Shared.Map;
|
||||
using Robust.Shared.Prototypes;
|
||||
using Robust.Shared.Timing;
|
||||
using Robust.Shared.Utility;
|
||||
|
||||
namespace Content.IntegrationTests.Tests.Chemistry
|
||||
@@ -37,17 +33,19 @@ namespace Content.IntegrationTests.Tests.Chemistry
|
||||
Console.WriteLine($"Testing {reactionPrototype.ID}");
|
||||
|
||||
IEntity beaker;
|
||||
SolutionContainerComponent component = null;
|
||||
Solution component = null;
|
||||
|
||||
server.Assert(() =>
|
||||
{
|
||||
mapManager.CreateNewMapEntity(MapId.Nullspace);
|
||||
|
||||
beaker = entityManager.SpawnEntity("BluespaceBeaker", MapCoordinates.Nullspace);
|
||||
Assert.That(beaker.TryGetComponent(out component));
|
||||
Assert.That(EntitySystem.Get<SolutionContainerSystem>()
|
||||
.TryGetSolution(beaker, "beaker", out component));
|
||||
foreach (var (id, reactant) in reactionPrototype.Reactants)
|
||||
{
|
||||
Assert.That(component.TryAddReagent(id, reactant.Amount, out var quantity));
|
||||
Assert.That(EntitySystem.Get<SolutionContainerSystem>()
|
||||
.TryAddReagent(beaker.Uid, component, id, reactant.Amount, out var quantity));
|
||||
Assert.That(reactant.Amount, Is.EqualTo(quantity));
|
||||
}
|
||||
});
|
||||
@@ -61,7 +59,7 @@ namespace Content.IntegrationTests.Tests.Chemistry
|
||||
var foundProductsMap = reactionPrototype.Products
|
||||
.Concat(reactionPrototype.Reactants.Where(x => x.Value.Catalyst).ToDictionary(x => x.Key, x => x.Value.Amount))
|
||||
.ToDictionary(x => x, x => false);
|
||||
foreach (var reagent in component.Solution.Contents)
|
||||
foreach (var reagent in component.Contents)
|
||||
{
|
||||
Assert.That(foundProductsMap.TryFirstOrNull(x => x.Key.Key == reagent.ReagentId && x.Key.Value == reagent.Quantity, out var foundProduct));
|
||||
foundProductsMap[foundProduct.Value.Key] = true;
|
||||
|
||||
@@ -1,9 +1,8 @@
|
||||
using System;
|
||||
using System.Threading.Tasks;
|
||||
using Content.Server.Fluids.Components;
|
||||
using Content.Shared.Chemistry;
|
||||
using Content.Shared.Chemistry.Components;
|
||||
using Content.Shared.Chemistry.Reagent;
|
||||
using Content.Shared.Chemistry.Solution;
|
||||
using Content.Shared.Coordinates;
|
||||
using NUnit.Framework;
|
||||
using Robust.Shared.GameObjects;
|
||||
|
||||
@@ -1,6 +1,8 @@
|
||||
using Content.Server.AI.WorldState;
|
||||
using Content.Server.AI.WorldState.States;
|
||||
using Content.Server.Chemistry.Components;
|
||||
using Content.Server.Nutrition.Components;
|
||||
using Content.Shared.Chemistry.EntitySystems;
|
||||
using Robust.Shared.GameObjects;
|
||||
|
||||
namespace Content.Server.AI.Utility.Considerations.Nutrition.Drink
|
||||
{
|
||||
@@ -10,14 +12,16 @@ namespace Content.Server.AI.Utility.Considerations.Nutrition.Drink
|
||||
{
|
||||
var target = context.GetState<TargetEntityState>().GetValue();
|
||||
|
||||
if (target == null || target.Deleted || !target.TryGetComponent(out SolutionContainerComponent? drink))
|
||||
if (target == null
|
||||
|| target.Deleted
|
||||
|| !EntitySystem.Get<SolutionContainerSystem>().TryGetSolution(target, DrinkComponent.DefaultSolutionName, out var drink))
|
||||
{
|
||||
return 0.0f;
|
||||
}
|
||||
|
||||
var nutritionValue = 0;
|
||||
|
||||
foreach (var reagent in drink.ReagentList)
|
||||
foreach (var reagent in drink.Contents)
|
||||
{
|
||||
// TODO
|
||||
nutritionValue += (reagent.Quantity * 30).Int();
|
||||
|
||||
@@ -1,6 +1,8 @@
|
||||
using Content.Server.AI.WorldState;
|
||||
using Content.Server.AI.WorldState.States;
|
||||
using Content.Server.Chemistry.Components;
|
||||
using Content.Server.Nutrition.Components;
|
||||
using Content.Shared.Chemistry.EntitySystems;
|
||||
using Robust.Shared.GameObjects;
|
||||
|
||||
namespace Content.Server.AI.Utility.Considerations.Nutrition.Food
|
||||
{
|
||||
@@ -10,14 +12,15 @@ namespace Content.Server.AI.Utility.Considerations.Nutrition.Food
|
||||
{
|
||||
var target = context.GetState<TargetEntityState>().GetValue();
|
||||
|
||||
if (target == null || target.Deleted || !target.TryGetComponent(out SolutionContainerComponent? food))
|
||||
if (target == null || target.Deleted ||
|
||||
!EntitySystem.Get<SolutionContainerSystem>().TryGetSolution(target, FoodComponent.SolutionName, out var food))
|
||||
{
|
||||
return 0.0f;
|
||||
}
|
||||
|
||||
var nutritionValue = 0;
|
||||
|
||||
foreach (var reagent in food.ReagentList)
|
||||
foreach (var reagent in food.Contents)
|
||||
{
|
||||
// TODO
|
||||
nutritionValue += (reagent.Quantity * 30).Int();
|
||||
|
||||
@@ -1,10 +1,10 @@
|
||||
using Content.Server.Administration.Managers;
|
||||
using Content.Server.Chemistry.Components;
|
||||
using Content.Server.EUI;
|
||||
using Content.Shared.Administration;
|
||||
using Content.Shared.Chemistry.Components;
|
||||
using Content.Shared.Chemistry.Components.SolutionManager;
|
||||
using Content.Shared.Chemistry.EntitySystems;
|
||||
using Content.Shared.Chemistry.Reagent;
|
||||
using Content.Shared.Chemistry.Solution;
|
||||
using Content.Shared.Chemistry.Solution.Components;
|
||||
using Content.Shared.Eui;
|
||||
using Content.Shared.Verbs;
|
||||
using Robust.Server.GameObjects;
|
||||
@@ -13,7 +13,6 @@ using Robust.Shared.GameObjects;
|
||||
using Robust.Shared.IoC;
|
||||
using Robust.Shared.Localization;
|
||||
|
||||
|
||||
namespace Content.Server.Administration.Verbs
|
||||
{
|
||||
[GlobalVerb]
|
||||
@@ -34,9 +33,8 @@ namespace Content.Server.Administration.Verbs
|
||||
{
|
||||
// ISolutionInteractionsComponent doesn't exactly have an interface for "admin tries to refill this", so...
|
||||
// Still have a path for SolutionContainerComponent in case it doesn't allow direct refilling.
|
||||
if (!target.HasComponent<SolutionContainerComponent>()
|
||||
&& !(target.TryGetComponent(out ISolutionInteractionsComponent? interactions)
|
||||
&& interactions.CanInject))
|
||||
if (!(target.HasComponent<SolutionContainerManagerComponent>()
|
||||
&& target.HasComponent<InjectableSolutionComponent>()))
|
||||
{
|
||||
data.Visibility = VerbVisibility.Invisible;
|
||||
return;
|
||||
@@ -87,7 +85,8 @@ namespace Content.Server.Administration.Verbs
|
||||
|
||||
public override EuiStateBase GetNewState()
|
||||
{
|
||||
if (_target.TryGetComponent(out SolutionContainerComponent? container))
|
||||
if (EntitySystem.Get<SolutionContainerSystem>()
|
||||
.TryGetSolution(_target, "default", out var container))
|
||||
{
|
||||
return new AdminAddReagentEuiState
|
||||
{
|
||||
@@ -96,16 +95,6 @@ namespace Content.Server.Administration.Verbs
|
||||
};
|
||||
}
|
||||
|
||||
if (_target.TryGetComponent(out ISolutionInteractionsComponent? interactions))
|
||||
{
|
||||
return new AdminAddReagentEuiState
|
||||
{
|
||||
// We don't exactly have an absolute total volume so good enough.
|
||||
CurVolume = ReagentUnit.Zero,
|
||||
MaxVolume = interactions.InjectSpaceAvailable
|
||||
};
|
||||
}
|
||||
|
||||
return new AdminAddReagentEuiState
|
||||
{
|
||||
CurVolume = ReagentUnit.Zero,
|
||||
@@ -131,15 +120,21 @@ namespace Content.Server.Administration.Verbs
|
||||
|
||||
var id = doAdd.ReagentId;
|
||||
var amount = doAdd.Amount;
|
||||
var solutionsSys = EntitySystem.Get<SolutionContainerSystem>();
|
||||
|
||||
if (_target.TryGetComponent(out SolutionContainerComponent? container))
|
||||
{
|
||||
container.TryAddReagent(id, amount, out _);
|
||||
}
|
||||
else if (_target.TryGetComponent(out ISolutionInteractionsComponent? interactions))
|
||||
if (_target.TryGetComponent(out InjectableSolutionComponent? injectable)
|
||||
&& solutionsSys.TryGetSolution(_target, injectable.Name, out var targetSolution))
|
||||
{
|
||||
var solution = new Solution(id, amount);
|
||||
interactions.Inject(solution);
|
||||
solutionsSys.Inject(_target.Uid, targetSolution, solution);
|
||||
}
|
||||
else
|
||||
{
|
||||
//TODO decide how to find the solution
|
||||
if (solutionsSys.TryGetSolution(_target, "default", out var solution))
|
||||
{
|
||||
solutionsSys.TryAddReagent(_target.Uid,solution, id, amount, out _);
|
||||
}
|
||||
}
|
||||
|
||||
StateDirty();
|
||||
|
||||
@@ -1,10 +1,9 @@
|
||||
using Content.Server.Atmos.EntitySystems;
|
||||
using Content.Server.Fluids.Components;
|
||||
using Content.Shared.Chemistry.Components;
|
||||
using Content.Shared.Chemistry.Reagent;
|
||||
using Content.Shared.Chemistry.Solution;
|
||||
using Content.Shared.Maps;
|
||||
using JetBrains.Annotations;
|
||||
using Robust.Server.GameObjects;
|
||||
using Robust.Shared.Serialization.Manager.Attributes;
|
||||
|
||||
namespace Content.Server.Atmos.Reactions
|
||||
|
||||
@@ -1,11 +1,10 @@
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
using Content.Server.Body.Circulatory;
|
||||
using Content.Server.Chemistry.Components;
|
||||
using Content.Shared.Body.Networks;
|
||||
using Content.Shared.Chemistry.Components;
|
||||
using Content.Shared.Chemistry.EntitySystems;
|
||||
using Content.Shared.Chemistry.Reagent;
|
||||
using Content.Shared.Chemistry.Solution;
|
||||
using Content.Shared.Chemistry.Solution.Components;
|
||||
using Robust.Shared.GameObjects;
|
||||
using Robust.Shared.Serialization.Manager.Attributes;
|
||||
using Robust.Shared.ViewVariables;
|
||||
@@ -18,6 +17,7 @@ namespace Content.Server.Body.Behavior
|
||||
/// </summary>
|
||||
public class StomachBehavior : MechanismBehavior
|
||||
{
|
||||
private const string DefaultSolutionName = "stomach";
|
||||
private float _accumulatedFrameTime;
|
||||
|
||||
/// <summary>
|
||||
@@ -30,7 +30,6 @@ namespace Content.Server.Body.Behavior
|
||||
/// </param>
|
||||
public override void Update(float frameTime)
|
||||
{
|
||||
|
||||
// Do not metabolise if the organ does not have a body.
|
||||
if (Body == null)
|
||||
{
|
||||
@@ -49,8 +48,8 @@ namespace Content.Server.Body.Behavior
|
||||
|
||||
// Note that "Owner" should be the organ that has this behaviour/mechanism, and it should have a dedicated
|
||||
// solution container. "Body.Owner" is something else, and may have more than one solution container.
|
||||
if (!Owner.TryGetComponent(out SolutionContainerComponent? solution) ||
|
||||
!Body.Owner.TryGetComponent(out BloodstreamComponent? bloodstream))
|
||||
if (!Body.Owner.TryGetComponent(out BloodstreamComponent? bloodstream)
|
||||
|| !EntitySystem.Get<SolutionContainerSystem>().TryGetSolution(Owner, SharedBloodstreamComponent.DefaultSolutionName, out var solution))
|
||||
{
|
||||
return;
|
||||
}
|
||||
@@ -68,13 +67,15 @@ namespace Content.Server.Body.Behavior
|
||||
// This reagent has been in the somach long enough, TRY to transfer it.
|
||||
// But first, check if the reagent still exists, and how much is left.
|
||||
// Some poor spessman may have washed down a potassium snack with some water.
|
||||
if (solution.Solution.ContainsReagent(delta.ReagentId, out ReagentUnit quantity)){
|
||||
|
||||
if (quantity > delta.Quantity) {
|
||||
if (solution.ContainsReagent(delta.ReagentId, out ReagentUnit quantity))
|
||||
{
|
||||
if (quantity > delta.Quantity)
|
||||
{
|
||||
quantity = delta.Quantity;
|
||||
}
|
||||
|
||||
solution.TryRemoveReagent(delta.ReagentId, quantity);
|
||||
EntitySystem.Get<SolutionContainerSystem>()
|
||||
.TryRemoveReagent(Owner.Uid, solution, delta.ReagentId, quantity);
|
||||
transferSolution.AddReagent(delta.ReagentId, quantity);
|
||||
}
|
||||
|
||||
@@ -86,17 +87,28 @@ namespace Content.Server.Body.Behavior
|
||||
bloodstream.TryTransferSolution(transferSolution);
|
||||
}
|
||||
|
||||
public Solution? StomachSolution
|
||||
{
|
||||
get
|
||||
{
|
||||
EntitySystem.Get<SolutionContainerSystem>().TryGetSolution(Owner, DefaultSolutionName, out var solution);
|
||||
return solution;
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Max volume of internal solution storage
|
||||
/// </summary>
|
||||
public ReagentUnit MaxVolume
|
||||
{
|
||||
get => Owner.TryGetComponent(out SharedSolutionContainerComponent? solution) ? solution.MaxVolume : ReagentUnit.Zero;
|
||||
get =>
|
||||
StomachSolution?.MaxVolume ?? ReagentUnit.Zero;
|
||||
|
||||
set
|
||||
{
|
||||
if (Owner.TryGetComponent(out SharedSolutionContainerComponent? solution))
|
||||
if (StomachSolution != null)
|
||||
{
|
||||
solution.MaxVolume = value;
|
||||
StomachSolution.MaxVolume = value;
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -118,27 +130,25 @@ namespace Content.Server.Body.Behavior
|
||||
/// <summary>
|
||||
/// Used to track how long each reagent has been in the stomach
|
||||
/// </summary>
|
||||
[ViewVariables]
|
||||
private readonly List<ReagentDelta> _reagentDeltas = new();
|
||||
[ViewVariables] private readonly List<ReagentDelta> _reagentDeltas = new();
|
||||
|
||||
public override void Startup()
|
||||
{
|
||||
base.Startup();
|
||||
|
||||
Owner.EnsureComponentWarn(out SolutionContainerComponent solution);
|
||||
|
||||
var solution = EntitySystem.Get<SolutionContainerSystem>().EnsureSolution(Owner, DefaultSolutionName);
|
||||
solution.MaxVolume = InitialMaxVolume;
|
||||
}
|
||||
|
||||
public bool CanTransferSolution(Solution solution)
|
||||
{
|
||||
if (!Owner.TryGetComponent(out SharedSolutionContainerComponent? solutionComponent))
|
||||
if (StomachSolution == null)
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
// TODO: For now no partial transfers. Potentially change by design
|
||||
if (!solutionComponent.CanAddSolution(solution))
|
||||
if (!StomachSolution.CanAddSolution(solution))
|
||||
{
|
||||
return false;
|
||||
}
|
||||
@@ -151,13 +161,13 @@ namespace Content.Server.Body.Behavior
|
||||
if (Owner == null || !CanTransferSolution(solution))
|
||||
return false;
|
||||
|
||||
if (!Owner.TryGetComponent(out SolutionContainerComponent? solutionComponent))
|
||||
if (StomachSolution == null)
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
// Add solution to _stomachContents
|
||||
solutionComponent.TryAddSolution(solution);
|
||||
EntitySystem.Get<SolutionContainerSystem>().TryAddSolution(Owner.Uid, StomachSolution, solution);
|
||||
// Add each reagent to _reagentDeltas. Used to track how long each reagent has been in the stomach
|
||||
foreach (var reagent in solution.Contents)
|
||||
{
|
||||
|
||||
@@ -1,13 +1,12 @@
|
||||
using System;
|
||||
using System.Linq;
|
||||
using Content.Server.Atmos;
|
||||
using Content.Server.Atmos.EntitySystems;
|
||||
using Content.Server.Body.Respiratory;
|
||||
using Content.Server.Chemistry.Components;
|
||||
using Content.Shared.Atmos;
|
||||
using Content.Shared.Body.Networks;
|
||||
using Content.Shared.Chemistry.Components;
|
||||
using Content.Shared.Chemistry.EntitySystems;
|
||||
using Content.Shared.Chemistry.Reagent;
|
||||
using Content.Shared.Chemistry.Solution;
|
||||
using Robust.Shared.GameObjects;
|
||||
using Robust.Shared.Serialization.Manager.Attributes;
|
||||
using Robust.Shared.ViewVariables;
|
||||
@@ -23,32 +22,34 @@ namespace Content.Server.Body.Circulatory
|
||||
/// <summary>
|
||||
/// Max volume of internal solution storage
|
||||
/// </summary>
|
||||
[DataField("maxVolume")]
|
||||
[ViewVariables] private ReagentUnit _initialMaxVolume = ReagentUnit.New(250);
|
||||
[DataField("maxVolume")] [ViewVariables]
|
||||
private ReagentUnit _initialMaxVolume = ReagentUnit.New(250);
|
||||
|
||||
/// <summary>
|
||||
/// Internal solution for reagent storage
|
||||
/// </summary>
|
||||
[ViewVariables] private SolutionContainerComponent _internalSolution = default!;
|
||||
[ViewVariables] private Solution? _internalSolution;
|
||||
|
||||
/// <summary>
|
||||
/// Empty volume of internal solution
|
||||
/// </summary>
|
||||
[ViewVariables] public ReagentUnit EmptyVolume => _internalSolution.EmptyVolume;
|
||||
[ViewVariables]
|
||||
public ReagentUnit EmptyVolume => _internalSolution?.AvailableVolume ?? ReagentUnit.Zero;
|
||||
|
||||
[ViewVariables]
|
||||
public GasMixture Air { get; set; } = new(6)
|
||||
{ Temperature = Atmospherics.NormalBodyTemperature };
|
||||
|
||||
[ViewVariables] public SolutionContainerComponent Solution => _internalSolution;
|
||||
|
||||
protected override void Initialize()
|
||||
{
|
||||
base.Initialize();
|
||||
|
||||
_internalSolution = Owner.EnsureComponent<SolutionContainerComponent>();
|
||||
_internalSolution = EntitySystem.Get<SolutionContainerSystem>().EnsureSolution(Owner, DefaultSolutionName);
|
||||
if (_internalSolution != null)
|
||||
{
|
||||
_internalSolution.MaxVolume = _initialMaxVolume;
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Attempt to transfer provided solution to internal solution.
|
||||
@@ -59,12 +60,14 @@ namespace Content.Server.Body.Circulatory
|
||||
public override bool TryTransferSolution(Solution solution)
|
||||
{
|
||||
// For now doesn't support partial transfers
|
||||
if (solution.TotalVolume + _internalSolution.CurrentVolume > _internalSolution.MaxVolume)
|
||||
var current = _internalSolution?.CurrentVolume ?? ReagentUnit.Zero;
|
||||
var max = _internalSolution?.MaxVolume ?? ReagentUnit.Zero;
|
||||
if (solution.TotalVolume + current > max)
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
_internalSolution.TryAddSolution(solution);
|
||||
EntitySystem.Get<SolutionContainerSystem>().TryAddSolution(Owner.Uid, _internalSolution, solution);
|
||||
return true;
|
||||
}
|
||||
|
||||
|
||||
@@ -1,5 +1,6 @@
|
||||
using System.Collections.Generic;
|
||||
using System.ComponentModel.DataAnnotations;
|
||||
using Content.Shared.Body.Networks;
|
||||
using Content.Shared.Chemistry.Reagent;
|
||||
using Robust.Shared.GameObjects;
|
||||
using Robust.Shared.Serialization.Manager.Attributes;
|
||||
@@ -25,11 +26,11 @@ namespace Content.Server.Body.Metabolism
|
||||
public float UpdateFrequency = 1.0f;
|
||||
|
||||
/// <summary>
|
||||
/// Whether this metabolizer should attempt to metabolize chemicals in its parent bodies' bloodstream,
|
||||
/// From which solution will this metabolizer attempt to metabolize chemicals in its parent bodies' bloodstream,
|
||||
/// as opposed to a solution container on the metabolizing entity itself.
|
||||
/// </summary>
|
||||
[DataField("takeFromBloodstream")]
|
||||
public bool TakeFromBloodstream = true;
|
||||
[DataField("solution")]
|
||||
public string SolutionName { get; set; } = SharedBloodstreamComponent.DefaultSolutionName;
|
||||
|
||||
/// <summary>
|
||||
/// A dictionary mapping reagent string IDs to a list of effects & associated metabolism rate.
|
||||
|
||||
@@ -1,20 +1,36 @@
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
using Content.Server.Body.Circulatory;
|
||||
using Content.Server.Chemistry.Components;
|
||||
using Content.Shared.Body.Components;
|
||||
using Content.Shared.Body.Mechanism;
|
||||
using Content.Shared.Chemistry.Components;
|
||||
using Content.Shared.Chemistry.EntitySystems;
|
||||
using Content.Shared.Chemistry.Reagent;
|
||||
using Content.Shared.Chemistry.Solution;
|
||||
using JetBrains.Annotations;
|
||||
using Robust.Shared.GameObjects;
|
||||
using Robust.Shared.IoC;
|
||||
using Robust.Shared.Prototypes;
|
||||
|
||||
namespace Content.Server.Body.Metabolism
|
||||
{
|
||||
// TODO mirror in the future working on mechanisms move updating here to BodySystem so it can be ordered?
|
||||
[UsedImplicitly]
|
||||
public class MetabolizerSystem : EntitySystem
|
||||
{
|
||||
[Dependency]
|
||||
private readonly SolutionContainerSystem _solutionContainerSystem = default!;
|
||||
|
||||
|
||||
public override void Initialize()
|
||||
{
|
||||
base.Initialize();
|
||||
|
||||
SubscribeLocalEvent<MetabolizerComponent, ComponentInit>(OnMetabolizerInit);
|
||||
}
|
||||
|
||||
private void OnMetabolizerInit(EntityUid uid, MetabolizerComponent component, ComponentInit args)
|
||||
{
|
||||
_solutionContainerSystem.EnsureSolution(EntityManager.GetEntity(uid), component.SolutionName);
|
||||
}
|
||||
|
||||
public override void Update(float frameTime)
|
||||
{
|
||||
base.Update(frameTime);
|
||||
@@ -35,37 +51,34 @@ namespace Content.Server.Body.Metabolism
|
||||
private void TryMetabolize(MetabolizerComponent comp)
|
||||
{
|
||||
var owner = comp.Owner;
|
||||
var reagentList = new List<Solution.ReagentQuantity>();
|
||||
SolutionContainerComponent? solution = null;
|
||||
IReadOnlyList<Solution.ReagentQuantity> reagentList = new List<Solution.ReagentQuantity>();
|
||||
Solution? solution = null;
|
||||
SharedBodyComponent? body = null;
|
||||
var solutionsSys = Get<SolutionContainerSystem>();
|
||||
|
||||
// if this field is passed we should try and take from the bloodstream over anything else
|
||||
if (comp.TakeFromBloodstream && owner.TryGetComponent<SharedMechanismComponent>(out var mech))
|
||||
if (owner.TryGetComponent<SharedMechanismComponent>(out var mech))
|
||||
{
|
||||
body = mech.Body;
|
||||
if (body != null)
|
||||
{
|
||||
if (body.Owner.TryGetComponent<BloodstreamComponent>(out var bloodstream)
|
||||
&& bloodstream.Solution.CurrentVolume >= ReagentUnit.Zero)
|
||||
if (body.Owner.HasComponent<BloodstreamComponent>()
|
||||
&& solutionsSys.TryGetSolution(body.Owner, comp.SolutionName, out solution)
|
||||
&& solution.CurrentVolume >= ReagentUnit.Zero)
|
||||
{
|
||||
solution = bloodstream.Solution;
|
||||
reagentList = bloodstream.Solution.ReagentList.ToList();
|
||||
reagentList = solution.Contents;
|
||||
}
|
||||
}
|
||||
}
|
||||
else if (owner.TryGetComponent<SolutionContainerComponent>(out var sol))
|
||||
{
|
||||
// if we have no mechanism/body but a solution container instead,
|
||||
// we'll just use that to metabolize from
|
||||
solution = sol;
|
||||
reagentList = sol.ReagentList.ToList();
|
||||
}
|
||||
|
||||
if (solution == null || reagentList.Count == 0)
|
||||
{
|
||||
// We're all outta ideas on where to metabolize from
|
||||
return;
|
||||
}
|
||||
|
||||
List<Solution.ReagentQuantity> removeReagents = new(5);
|
||||
|
||||
// Run metabolism for each reagent, remove metabolized reagents
|
||||
foreach (var reagent in reagentList)
|
||||
{
|
||||
@@ -100,8 +113,10 @@ namespace Content.Server.Body.Metabolism
|
||||
effect.Metabolize(ent, reagent);
|
||||
}
|
||||
|
||||
solution.TryRemoveReagent(reagent.ReagentId, metabolism.MetabolismRate);
|
||||
}
|
||||
removeReagents.Add(new Solution.ReagentQuantity(reagent.ReagentId, metabolism.MetabolismRate));
|
||||
}
|
||||
|
||||
solutionsSys.TryRemoveAllReagents(solution, removeReagents);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,9 +1,7 @@
|
||||
using System;
|
||||
using System.Linq;
|
||||
using System.Threading.Tasks;
|
||||
using Content.Server.Atmos;
|
||||
using Content.Server.Atmos.EntitySystems;
|
||||
using Content.Server.Chemistry.Components;
|
||||
using Content.Server.Fluids.Components;
|
||||
using Content.Server.Hands.Components;
|
||||
using Content.Server.Notification;
|
||||
@@ -11,11 +9,10 @@ using Content.Server.Plants;
|
||||
using Content.Shared.ActionBlocker;
|
||||
using Content.Shared.Audio;
|
||||
using Content.Shared.Botany;
|
||||
using Content.Shared.Chemistry.EntitySystems;
|
||||
using Content.Shared.Chemistry.Reagent;
|
||||
using Content.Shared.Chemistry.Solution.Components;
|
||||
using Content.Shared.Examine;
|
||||
using Content.Shared.Interaction;
|
||||
using Content.Shared.Interaction.Events;
|
||||
using Content.Shared.Notification.Managers;
|
||||
using Content.Shared.Random.Helpers;
|
||||
using Content.Shared.Tag;
|
||||
@@ -40,17 +37,23 @@ namespace Content.Server.Botany.Components
|
||||
{
|
||||
public const float HydroponicsSpeedMultiplier = 1f;
|
||||
public const float HydroponicsConsumptionMultiplier = 4f;
|
||||
private const string SoilSolutionName = "soil";
|
||||
|
||||
[Dependency] private readonly IRobustRandom _random = default!;
|
||||
[Dependency] private readonly IPrototypeManager _prototypeManager = default!;
|
||||
[Dependency] private readonly IGameTiming _gameTiming = default!;
|
||||
[ComponentDependency] private readonly AppearanceComponent? _appearanceComponent = default!;
|
||||
|
||||
public override string Name => "PlantHolder";
|
||||
|
||||
[ViewVariables] private int _lastProduce;
|
||||
|
||||
[ViewVariables(VVAccess.ReadWrite)] private int _missingGas;
|
||||
|
||||
private readonly TimeSpan _cycleDelay = TimeSpan.FromSeconds(15f);
|
||||
|
||||
[ViewVariables] private TimeSpan _lastCycle = TimeSpan.Zero;
|
||||
|
||||
[ViewVariables(VVAccess.ReadWrite)] private bool _updateSpriteAfterUpdate;
|
||||
|
||||
[ViewVariables(VVAccess.ReadWrite)]
|
||||
@@ -117,16 +120,6 @@ namespace Content.Server.Botany.Components
|
||||
[ViewVariables(VVAccess.ReadWrite)]
|
||||
public bool ForceUpdate { get; set; }
|
||||
|
||||
[ComponentDependency] private readonly SolutionContainerComponent? _solutionContainer = default!;
|
||||
[ComponentDependency] private readonly AppearanceComponent? _appearanceComponent = default!;
|
||||
|
||||
protected override void Initialize()
|
||||
{
|
||||
base.Initialize();
|
||||
|
||||
Owner.EnsureComponentWarn<SolutionContainerComponent>();
|
||||
}
|
||||
|
||||
public void WeedInvasion()
|
||||
{
|
||||
// TODO
|
||||
@@ -207,7 +200,8 @@ namespace Content.Server.Botany.Components
|
||||
// Water consumption.
|
||||
if (Seed.WaterConsumption > 0 && WaterLevel > 0 && _random.Prob(0.75f))
|
||||
{
|
||||
WaterLevel -= MathF.Max(0f, Seed.NutrientConsumption * HydroponicsConsumptionMultiplier * HydroponicsSpeedMultiplier);
|
||||
WaterLevel -= MathF.Max(0f,
|
||||
Seed.NutrientConsumption * HydroponicsConsumptionMultiplier * HydroponicsSpeedMultiplier);
|
||||
if (DrawWarnings)
|
||||
_updateSpriteAfterUpdate = true;
|
||||
}
|
||||
@@ -248,7 +242,8 @@ namespace Content.Server.Botany.Components
|
||||
_updateSpriteAfterUpdate = true;
|
||||
}
|
||||
|
||||
var environment = EntitySystem.Get<AtmosphereSystem>().GetTileMixture(Owner.Transform.Coordinates, true)?? GasMixture.SpaceGas;
|
||||
var environment = EntitySystem.Get<AtmosphereSystem>().GetTileMixture(Owner.Transform.Coordinates, true) ??
|
||||
GasMixture.SpaceGas;
|
||||
|
||||
if (Seed.ConsumeGasses.Count > 0)
|
||||
{
|
||||
@@ -306,7 +301,8 @@ namespace Content.Server.Botany.Components
|
||||
{
|
||||
foreach (var (gas, amount) in Seed.ExudeGasses)
|
||||
{
|
||||
environment.AdjustMoles(gas, MathF.Max(1f, MathF.Round((amount * MathF.Round(Seed.Potency)) / exudeCount)));
|
||||
environment.AdjustMoles(gas,
|
||||
MathF.Max(1f, MathF.Round((amount * MathF.Round(Seed.Potency)) / exudeCount)));
|
||||
}
|
||||
}
|
||||
|
||||
@@ -428,8 +424,8 @@ namespace Content.Server.Botany.Components
|
||||
{
|
||||
if (!Seed.CheckHarvest(user, hands.GetActiveHand?.Owner))
|
||||
return false;
|
||||
|
||||
} else if (!Seed.CheckHarvest(user))
|
||||
}
|
||||
else if (!Seed.CheckHarvest(user))
|
||||
{
|
||||
return false;
|
||||
}
|
||||
@@ -546,10 +542,11 @@ namespace Content.Server.Botany.Components
|
||||
|
||||
public void UpdateReagents()
|
||||
{
|
||||
if (_solutionContainer == null)
|
||||
var solutionSystem = EntitySystem.Get<SolutionContainerSystem>();
|
||||
if (!solutionSystem.TryGetSolution(Owner, SoilSolutionName, out var solution))
|
||||
return;
|
||||
|
||||
if (_solutionContainer.Solution.TotalVolume <= 0 || MutationLevel >= 25)
|
||||
if (solution.TotalVolume <= 0 || MutationLevel >= 25)
|
||||
{
|
||||
if (MutationLevel >= 0)
|
||||
{
|
||||
@@ -560,12 +557,10 @@ namespace Content.Server.Botany.Components
|
||||
else
|
||||
{
|
||||
var one = ReagentUnit.New(1);
|
||||
|
||||
foreach (var (reagent, amount) in _solutionContainer.ReagentList.ToArray())
|
||||
foreach (var reagent in solutionSystem.RemoveEachReagent(solution, one))
|
||||
{
|
||||
var reagentProto = _prototypeManager.Index<ReagentPrototype>(reagent);
|
||||
reagentProto.ReactionPlant(Owner);
|
||||
_solutionContainer.Solution.RemoveReagent(reagent, amount < one ? amount : one);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -591,21 +586,25 @@ namespace Content.Server.Botany.Components
|
||||
|
||||
if (Dead)
|
||||
{
|
||||
_appearanceComponent.SetData(PlantHolderVisuals.Plant, new SpriteSpecifier.Rsi(Seed.PlantRsi, "dead"));
|
||||
_appearanceComponent.SetData(PlantHolderVisuals.Plant,
|
||||
new SpriteSpecifier.Rsi(Seed.PlantRsi, "dead"));
|
||||
}
|
||||
else if (Harvest)
|
||||
{
|
||||
_appearanceComponent.SetData(PlantHolderVisuals.Plant, new SpriteSpecifier.Rsi(Seed.PlantRsi, "harvest"));
|
||||
_appearanceComponent.SetData(PlantHolderVisuals.Plant,
|
||||
new SpriteSpecifier.Rsi(Seed.PlantRsi, "harvest"));
|
||||
}
|
||||
else if (Age < Seed.Maturation)
|
||||
{
|
||||
var growthStage = Math.Max(1, (int) ((Age * Seed.GrowthStages) / Seed.Maturation));
|
||||
_appearanceComponent.SetData(PlantHolderVisuals.Plant, new SpriteSpecifier.Rsi(Seed.PlantRsi,$"stage-{growthStage}"));
|
||||
_appearanceComponent.SetData(PlantHolderVisuals.Plant,
|
||||
new SpriteSpecifier.Rsi(Seed.PlantRsi, $"stage-{growthStage}"));
|
||||
_lastProduce = Age;
|
||||
}
|
||||
else
|
||||
{
|
||||
_appearanceComponent.SetData(PlantHolderVisuals.Plant, new SpriteSpecifier.Rsi(Seed.PlantRsi,$"stage-{Seed.GrowthStages}"));
|
||||
_appearanceComponent.SetData(PlantHolderVisuals.Plant,
|
||||
new SpriteSpecifier.Rsi(Seed.PlantRsi, $"stage-{Seed.GrowthStages}"));
|
||||
}
|
||||
}
|
||||
else
|
||||
@@ -617,7 +616,9 @@ namespace Content.Server.Botany.Components
|
||||
if (!DrawWarnings) return;
|
||||
_appearanceComponent.SetData(PlantHolderVisuals.WaterLight, WaterLevel <= 10);
|
||||
_appearanceComponent.SetData(PlantHolderVisuals.NutritionLight, NutritionLevel <= 2);
|
||||
_appearanceComponent.SetData(PlantHolderVisuals.AlertLight, WeedLevel >= 5 || PestLevel >= 5 || Toxins >= 40 || ImproperHeat || ImproperLight || ImproperPressure || _missingGas > 0);
|
||||
_appearanceComponent.SetData(PlantHolderVisuals.AlertLight,
|
||||
WeedLevel >= 5 || PestLevel >= 5 || Toxins >= 40 || ImproperHeat || ImproperLight || ImproperPressure ||
|
||||
_missingGas > 0);
|
||||
_appearanceComponent.SetData(PlantHolderVisuals.HarvestLight, Harvest);
|
||||
}
|
||||
|
||||
@@ -644,7 +645,7 @@ namespace Content.Server.Botany.Components
|
||||
var user = eventArgs.User;
|
||||
var usingItem = eventArgs.Using;
|
||||
|
||||
if (usingItem == null || usingItem.Deleted || !EntitySystem.Get<ActionBlockerSystem>().CanInteract(user))
|
||||
if (usingItem.Deleted || !EntitySystem.Get<ActionBlockerSystem>().CanInteract(user))
|
||||
return false;
|
||||
|
||||
if (usingItem.TryGetComponent(out SeedComponent? seeds))
|
||||
@@ -676,7 +677,8 @@ namespace Content.Server.Botany.Components
|
||||
return true;
|
||||
}
|
||||
|
||||
user.PopupMessageCursor(Loc.GetString("plant-holder-component-already-seeded-message", ("name", Owner.Name)));
|
||||
user.PopupMessageCursor(Loc.GetString("plant-holder-component-already-seeded-message",
|
||||
("name", Owner.Name)));
|
||||
return false;
|
||||
}
|
||||
|
||||
@@ -684,8 +686,10 @@ namespace Content.Server.Botany.Components
|
||||
{
|
||||
if (WeedLevel > 0)
|
||||
{
|
||||
user.PopupMessageCursor(Loc.GetString("plant-holder-component-remove-weeds-message",("name", Owner.Name)));
|
||||
user.PopupMessageOtherClients(Loc.GetString("plant-holder-component-remove-weeds-others-message",("otherName", user.Name)));
|
||||
user.PopupMessageCursor(Loc.GetString("plant-holder-component-remove-weeds-message",
|
||||
("name", Owner.Name)));
|
||||
user.PopupMessageOtherClients(Loc.GetString("plant-holder-component-remove-weeds-others-message",
|
||||
("otherName", user.Name)));
|
||||
WeedLevel = 0;
|
||||
UpdateSprite();
|
||||
}
|
||||
@@ -701,8 +705,10 @@ namespace Content.Server.Botany.Components
|
||||
{
|
||||
if (Seed != null)
|
||||
{
|
||||
user.PopupMessageCursor(Loc.GetString("plant-holder-component-remove-plant-message",("name", Owner.Name)));
|
||||
user.PopupMessageOtherClients(Loc.GetString("plant-holder-component-remove-plant-others-message",("name", user.Name)));
|
||||
user.PopupMessageCursor(Loc.GetString("plant-holder-component-remove-plant-message",
|
||||
("name", Owner.Name)));
|
||||
user.PopupMessageOtherClients(Loc.GetString("plant-holder-component-remove-plant-others-message",
|
||||
("name", user.Name)));
|
||||
RemovePlant();
|
||||
}
|
||||
else
|
||||
@@ -713,31 +719,38 @@ namespace Content.Server.Botany.Components
|
||||
return true;
|
||||
}
|
||||
|
||||
if (usingItem.TryGetComponent(out ISolutionInteractionsComponent? solution) && solution.CanDrain)
|
||||
var solutionSystem = EntitySystem.Get<SolutionContainerSystem>();
|
||||
if (solutionSystem.TryGetDrainableSolution(usingItem.Uid, out var solution)
|
||||
&& solutionSystem.TryGetSolution(Owner, SoilSolutionName, out var targetSolution))
|
||||
{
|
||||
var amount = ReagentUnit.New(5);
|
||||
var sprayed = false;
|
||||
var targetEntity = Owner.Uid;
|
||||
var solutionEntity = usingItem.Uid;
|
||||
|
||||
if (usingItem.TryGetComponent(out SprayComponent? spray))
|
||||
{
|
||||
sprayed = true;
|
||||
amount = ReagentUnit.New(1);
|
||||
|
||||
SoundSystem.Play(Filter.Pvs(usingItem), spray.SpraySound.GetSound(), usingItem, AudioHelpers.WithVariation(0.125f));
|
||||
SoundSystem.Play(Filter.Pvs(usingItem), spray.SpraySound.GetSound(), usingItem,
|
||||
AudioHelpers.WithVariation(0.125f));
|
||||
}
|
||||
|
||||
var split = solution.Drain(amount);
|
||||
var split = solutionSystem.Drain(solutionEntity, solution, amount);
|
||||
if (split.TotalVolume == 0)
|
||||
{
|
||||
user.PopupMessageCursor(Loc.GetString("plant-holder-component-empty-message",("owner", usingItem)));
|
||||
user.PopupMessageCursor(Loc.GetString("plant-holder-component-empty-message",
|
||||
("owner", usingItem)));
|
||||
return true;
|
||||
}
|
||||
|
||||
user.PopupMessageCursor(Loc.GetString(sprayed ? "plant-holder-component-spray-message" : "plant-holder-component-transfer-message",
|
||||
user.PopupMessageCursor(Loc.GetString(
|
||||
sprayed ? "plant-holder-component-spray-message" : "plant-holder-component-transfer-message",
|
||||
("owner", Owner),
|
||||
("amount", split.TotalVolume)));
|
||||
|
||||
_solutionContainer?.TryAddSolution(split);
|
||||
solutionSystem.TryAddSolution(targetEntity, targetSolution, split);
|
||||
|
||||
ForceUpdateByExternalCause();
|
||||
|
||||
@@ -766,7 +779,8 @@ namespace Content.Server.Botany.Components
|
||||
|
||||
var seed = Seed.SpawnSeedPacket(user.Transform.Coordinates);
|
||||
seed.RandomOffset(0.25f);
|
||||
user.PopupMessageCursor(Loc.GetString("plant-holder-component-take-sample-message", ("seedName", Seed.DisplayName)));
|
||||
user.PopupMessageCursor(Loc.GetString("plant-holder-component-take-sample-message",
|
||||
("seedName", Seed.DisplayName)));
|
||||
Health -= (_random.Next(3, 5) * 10);
|
||||
|
||||
if (_random.Prob(0.3f))
|
||||
@@ -794,10 +808,11 @@ namespace Content.Server.Botany.Components
|
||||
("usingItem", usingItem),
|
||||
("owner", Owner)));
|
||||
|
||||
if (usingItem.TryGetComponent(out SolutionContainerComponent? solution2))
|
||||
if (solutionSystem.TryGetSolution(usingItem, ProduceComponent.SolutionName, out var solution2))
|
||||
{
|
||||
// This deliberately discards overfill.
|
||||
_solutionContainer?.TryAddSolution(solution2.SplitSolution(solution2.Solution.TotalVolume));
|
||||
solutionSystem.TryAddSolution(usingItem.Uid, solution2,
|
||||
solutionSystem.SplitSolution(usingItem.Uid, solution2, solution2.TotalVolume));
|
||||
|
||||
ForceUpdateByExternalCause();
|
||||
}
|
||||
@@ -839,9 +854,12 @@ namespace Content.Server.Botany.Components
|
||||
+ "\n");
|
||||
|
||||
if (Health <= Seed.Endurance / 2)
|
||||
message.AddMarkup(Loc.GetString("plant-holder-component-something-already-growing-low-health-message",
|
||||
("healthState", Loc.GetString(Age > Seed.Lifespan ? "plant-holder-component-plant-old-adjective" :
|
||||
"plant-holder-component-plant-unhealthy-adjective")))
|
||||
message.AddMarkup(Loc.GetString(
|
||||
"plant-holder-component-something-already-growing-low-health-message",
|
||||
("healthState",
|
||||
Loc.GetString(Age > Seed.Lifespan
|
||||
? "plant-holder-component-plant-old-adjective"
|
||||
: "plant-holder-component-plant-unhealthy-adjective")))
|
||||
+ "\n");
|
||||
}
|
||||
else
|
||||
@@ -855,8 +873,10 @@ namespace Content.Server.Botany.Components
|
||||
if (PestLevel >= 5)
|
||||
message.AddMarkup(Loc.GetString("plant-holder-component-pest-high-level-message") + "\n");
|
||||
|
||||
message.AddMarkup(Loc.GetString($"plant-holder-component-water-level-message",("waterLevel", (int)WaterLevel)) + "\n");
|
||||
message.AddMarkup(Loc.GetString($"plant-holder-component-nutrient-level-message", ("nutritionLevel", (int)NutritionLevel)) + "\n");
|
||||
message.AddMarkup(Loc.GetString($"plant-holder-component-water-level-message",
|
||||
("waterLevel", (int) WaterLevel)) + "\n");
|
||||
message.AddMarkup(Loc.GetString($"plant-holder-component-nutrient-level-message",
|
||||
("nutritionLevel", (int) NutritionLevel)) + "\n");
|
||||
|
||||
if (DrawWarnings)
|
||||
{
|
||||
|
||||
@@ -1,8 +1,9 @@
|
||||
using Content.Server.Chemistry.Components;
|
||||
using Content.Shared.Chemistry.EntitySystems;
|
||||
using Content.Shared.Chemistry.Reagent;
|
||||
using Robust.Server.GameObjects;
|
||||
using Robust.Shared.GameObjects;
|
||||
using Robust.Shared.IoC;
|
||||
using Robust.Shared.Log;
|
||||
using Robust.Shared.Maths;
|
||||
using Robust.Shared.Prototypes;
|
||||
using Robust.Shared.Serialization;
|
||||
@@ -15,9 +16,9 @@ namespace Content.Server.Botany.Components
|
||||
public class ProduceComponent : Component, ISerializationHooks
|
||||
{
|
||||
public override string Name => "Produce";
|
||||
public const string SolutionName = "produce";
|
||||
|
||||
[DataField("seed")]
|
||||
private string? _seedName;
|
||||
[DataField("seed")] private string? _seedName;
|
||||
|
||||
[ViewVariables]
|
||||
public Seed? Seed
|
||||
@@ -39,9 +40,13 @@ namespace Content.Server.Botany.Components
|
||||
sprite.LayerSetState(0, Seed.PlantIconState);
|
||||
}
|
||||
|
||||
var solutionContainer = Owner.EnsureComponent<SolutionContainerComponent>();
|
||||
|
||||
solutionContainer.RemoveAllSolution();
|
||||
EntitySystem.Get<SolutionContainerSystem>().RemoveAllSolution(Owner.Uid);
|
||||
var solutionContainer = EntitySystem.Get<SolutionContainerSystem>().EnsureSolution(Owner, SolutionName);
|
||||
if (solutionContainer == null)
|
||||
{
|
||||
Logger.Warning($"No solution container found in {nameof(ProduceComponent)}.");
|
||||
return;
|
||||
}
|
||||
|
||||
foreach (var (chem, quantity) in Seed.Chemicals)
|
||||
{
|
||||
@@ -50,7 +55,7 @@ namespace Content.Server.Botany.Components
|
||||
amount += ReagentUnit.New(Potency / quantity.PotencyDivisor);
|
||||
amount = ReagentUnit.New((int) MathHelper.Clamp(amount.Float(), quantity.Min, quantity.Max));
|
||||
solutionContainer.MaxVolume += amount;
|
||||
solutionContainer.Solution.AddReagent(chem, amount);
|
||||
solutionContainer.AddReagent(chem, amount);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -6,10 +6,10 @@ using Content.Server.Items;
|
||||
using Content.Server.Power.Components;
|
||||
using Content.Server.UserInterface;
|
||||
using Content.Shared.ActionBlocker;
|
||||
using Content.Shared.Chemistry;
|
||||
using Content.Shared.Chemistry.Components;
|
||||
using Content.Shared.Chemistry.Components.SolutionManager;
|
||||
using Content.Shared.Chemistry.EntitySystems;
|
||||
using Content.Shared.Chemistry.Reagent;
|
||||
using Content.Shared.Chemistry.Solution;
|
||||
using Content.Shared.Interaction;
|
||||
using Content.Shared.Notification.Managers;
|
||||
using Content.Shared.Random.Helpers;
|
||||
@@ -35,19 +35,31 @@ namespace Content.Server.Chemistry.Components
|
||||
[RegisterComponent]
|
||||
[ComponentReference(typeof(IActivate))]
|
||||
[ComponentReference(typeof(IInteractUsing))]
|
||||
public class ChemMasterComponent : SharedChemMasterComponent, IActivate, IInteractUsing, ISolutionChange
|
||||
public class ChemMasterComponent : SharedChemMasterComponent, IActivate, IInteractUsing
|
||||
{
|
||||
[ViewVariables] private ContainerSlot _beakerContainer = default!;
|
||||
[ViewVariables] private bool HasBeaker => _beakerContainer.ContainedEntity != null;
|
||||
[ViewVariables] private bool _bufferModeTransfer = true;
|
||||
[ViewVariables]
|
||||
private ContainerSlot _beakerContainer = default!;
|
||||
|
||||
[ViewVariables] private bool Powered => !Owner.TryGetComponent(out ApcPowerReceiverComponent? receiver) || receiver.Powered;
|
||||
[ViewVariables]
|
||||
private bool HasBeaker => _beakerContainer.ContainedEntity != null;
|
||||
|
||||
[ViewVariables] private readonly Solution BufferSolution = new();
|
||||
[ViewVariables]
|
||||
private bool _bufferModeTransfer = true;
|
||||
|
||||
[ViewVariables] private BoundUserInterface? UserInterface => Owner.GetUIOrNull(ChemMasterUiKey.Key);
|
||||
[ViewVariables]
|
||||
private bool Powered => !Owner.TryGetComponent(out ApcPowerReceiverComponent? receiver) || receiver.Powered;
|
||||
|
||||
[ViewVariables]
|
||||
private Solution BufferSolution => _bufferSolution ??= EntitySystem.Get<SolutionContainerSystem>().EnsureSolution(Owner, SolutionName);
|
||||
|
||||
private Solution? _bufferSolution;
|
||||
|
||||
[ViewVariables]
|
||||
private BoundUserInterface? UserInterface => Owner.GetUIOrNull(ChemMasterUiKey.Key);
|
||||
|
||||
[DataField("clickSound")]
|
||||
private SoundSpecifier _clickSound = new SoundPathSpecifier("/Audio/Machines/machine_switch.ogg");
|
||||
|
||||
[DataField("clickSound")] private SoundSpecifier _clickSound = new SoundPathSpecifier("/Audio/Machines/machine_switch.ogg");
|
||||
|
||||
/// <summary>
|
||||
/// Called once per instance of this component. Gets references to any other components needed
|
||||
@@ -65,8 +77,7 @@ namespace Content.Server.Chemistry.Components
|
||||
_beakerContainer =
|
||||
ContainerHelpers.EnsureContainer<ContainerSlot>(Owner, $"{Name}-reagentContainerContainer");
|
||||
|
||||
//BufferSolution = Owner.BufferSolution
|
||||
BufferSolution.RemoveAllSolution();
|
||||
_bufferSolution = EntitySystem.Get<SolutionContainerSystem>().EnsureSolution(Owner, SolutionName);
|
||||
|
||||
UpdateUserInterface();
|
||||
}
|
||||
@@ -76,13 +87,13 @@ namespace Content.Server.Chemistry.Components
|
||||
base.HandleMessage(message, component);
|
||||
switch (message)
|
||||
{
|
||||
case PowerChangedMessage powerChanged:
|
||||
OnPowerChanged(powerChanged);
|
||||
case PowerChangedMessage:
|
||||
OnPowerChanged();
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
private void OnPowerChanged(PowerChangedMessage e)
|
||||
private void OnPowerChanged()
|
||||
{
|
||||
UpdateUserInterface();
|
||||
}
|
||||
@@ -140,6 +151,7 @@ namespace Content.Server.Chemistry.Components
|
||||
/// Checks whether the player entity is able to use the chem master.
|
||||
/// </summary>
|
||||
/// <param name="playerEntity">The player entity.</param>
|
||||
/// <param name="needsPower">whether the device requires power</param>
|
||||
/// <returns>Returns true if the entity can use the chem master, and false if it cannot.</returns>
|
||||
private bool PlayerCanUseChemMaster(IEntity? playerEntity, bool needsPower = true)
|
||||
{
|
||||
@@ -166,25 +178,30 @@ namespace Content.Server.Chemistry.Components
|
||||
private ChemMasterBoundUserInterfaceState GetUserInterfaceState()
|
||||
{
|
||||
var beaker = _beakerContainer.ContainedEntity;
|
||||
if (beaker == null)
|
||||
EntitySystem.Get<SolutionContainerSystem>().TryGetSolution(beaker, SolutionName, out var beakerSolution);
|
||||
// TODO this is just a guess
|
||||
if (beaker == null || beakerSolution == null)
|
||||
{
|
||||
return new ChemMasterBoundUserInterfaceState(Powered, false, ReagentUnit.New(0), ReagentUnit.New(0),
|
||||
"", Owner.Name, new List<Solution.ReagentQuantity>(), BufferSolution.Contents, _bufferModeTransfer, BufferSolution.TotalVolume);
|
||||
"", Owner.Name, new List<Solution.ReagentQuantity>(), BufferSolution.Contents, _bufferModeTransfer,
|
||||
BufferSolution.TotalVolume);
|
||||
}
|
||||
|
||||
var solution = beaker.GetComponent<SolutionContainerComponent>();
|
||||
return new ChemMasterBoundUserInterfaceState(Powered, true, solution.CurrentVolume, solution.MaxVolume,
|
||||
beaker.Name, Owner.Name, solution.ReagentList, BufferSolution.Contents, _bufferModeTransfer, BufferSolution.TotalVolume);
|
||||
|
||||
return new ChemMasterBoundUserInterfaceState(Powered, true, beakerSolution.CurrentVolume,
|
||||
beakerSolution.MaxVolume,
|
||||
beaker.Name, Owner.Name, beakerSolution.Contents, BufferSolution.Contents, _bufferModeTransfer,
|
||||
BufferSolution.TotalVolume);
|
||||
}
|
||||
|
||||
private void UpdateUserInterface()
|
||||
public void UpdateUserInterface()
|
||||
{
|
||||
var state = GetUserInterfaceState();
|
||||
UserInterface?.SetState(state);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// If this component contains an entity with a <see cref="SolutionContainerComponent"/>, eject it.
|
||||
/// If this component contains an entity with a <see cref="Solution"/>, eject it.
|
||||
/// Tries to eject into user's hands first, then ejects onto chem master if both hands are full.
|
||||
/// </summary>
|
||||
private void TryEject(IEntity user)
|
||||
@@ -200,7 +217,8 @@ namespace Content.Server.Chemistry.Components
|
||||
_beakerContainer.Remove(beaker);
|
||||
UpdateUserInterface();
|
||||
|
||||
if(!user.TryGetComponent<HandsComponent>(out var hands) || !beaker.TryGetComponent<ItemComponent>(out var item))
|
||||
if (!user.TryGetComponent<HandsComponent>(out var hands) ||
|
||||
!beaker.TryGetComponent<ItemComponent>(out var item))
|
||||
return;
|
||||
if (hands.CanPutInHand(item))
|
||||
hands.PutInHand(item);
|
||||
@@ -214,7 +232,9 @@ namespace Content.Server.Chemistry.Components
|
||||
if (beaker is null)
|
||||
return;
|
||||
|
||||
var beakerSolution = beaker.GetComponent<SolutionContainerComponent>();
|
||||
if (!EntitySystem.Get<SolutionContainerSystem>().TryGetSolution(beaker, SolutionName, out var beakerSolution))
|
||||
return;
|
||||
|
||||
if (isBuffer)
|
||||
{
|
||||
foreach (var reagent in BufferSolution.Contents)
|
||||
@@ -222,30 +242,33 @@ namespace Content.Server.Chemistry.Components
|
||||
if (reagent.ReagentId == id)
|
||||
{
|
||||
ReagentUnit actualAmount;
|
||||
if (amount == ReagentUnit.New(-1)) //amount is ReagentUnit.New(-1) when the client sends a message requesting to remove all solution from the container
|
||||
if (
|
||||
amount == ReagentUnit
|
||||
.New(-1)) //amount is ReagentUnit.New(-1) when the client sends a message requesting to remove all solution from the container
|
||||
{
|
||||
actualAmount = ReagentUnit.Min(reagent.Quantity, beakerSolution.EmptyVolume);
|
||||
actualAmount = ReagentUnit.Min(reagent.Quantity, beakerSolution.AvailableVolume);
|
||||
}
|
||||
else
|
||||
{
|
||||
actualAmount = ReagentUnit.Min(reagent.Quantity, amount, beakerSolution.EmptyVolume);
|
||||
actualAmount = ReagentUnit.Min(reagent.Quantity, amount, beakerSolution.AvailableVolume);
|
||||
}
|
||||
|
||||
|
||||
BufferSolution.RemoveReagent(id, actualAmount);
|
||||
if (_bufferModeTransfer)
|
||||
{
|
||||
beakerSolution.TryAddReagent(id, actualAmount, out var _);
|
||||
EntitySystem.Get<SolutionContainerSystem>()
|
||||
.TryAddReagent(beaker.Uid, beakerSolution, id, actualAmount, out var _);
|
||||
// beakerSolution.Solution.AddReagent(id, actualAmount);
|
||||
}
|
||||
|
||||
break;
|
||||
}
|
||||
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
foreach (var reagent in beakerSolution.Solution.Contents)
|
||||
foreach (var reagent in beakerSolution.Contents)
|
||||
{
|
||||
if (reagent.ReagentId == id)
|
||||
{
|
||||
@@ -258,7 +281,8 @@ namespace Content.Server.Chemistry.Components
|
||||
{
|
||||
actualAmount = ReagentUnit.Min(reagent.Quantity, amount);
|
||||
}
|
||||
beakerSolution.TryRemoveReagent(id, actualAmount);
|
||||
|
||||
EntitySystem.Get<SolutionContainerSystem>().TryRemoveReagent(beaker.Uid, beakerSolution, id, actualAmount);
|
||||
BufferSolution.AddReagent(id, actualAmount);
|
||||
break;
|
||||
}
|
||||
@@ -285,9 +309,9 @@ namespace Content.Server.Chemistry.Components
|
||||
var bottle = Owner.EntityManager.SpawnEntity("ChemistryEmptyBottle01", Owner.Transform.Coordinates);
|
||||
|
||||
var bufferSolution = BufferSolution.SplitSolution(actualVolume);
|
||||
var bottleSolution = EntitySystem.Get<SolutionContainerSystem>().EnsureSolution(bottle, "bottle");
|
||||
|
||||
bottle.TryGetComponent<SolutionContainerComponent>(out var bottleSolution);
|
||||
bottleSolution?.TryAddSolution(bufferSolution);
|
||||
EntitySystem.Get<SolutionContainerSystem>().TryAddSolution(bottle.Uid, bottleSolution, bufferSolution);
|
||||
|
||||
//Try to give them the bottle
|
||||
if (user.TryGetComponent<HandsComponent>(out var hands) &&
|
||||
@@ -305,7 +329,6 @@ namespace Content.Server.Chemistry.Components
|
||||
//Give it an offset
|
||||
bottle.RandomOffset(0.2f);
|
||||
}
|
||||
|
||||
}
|
||||
else //Pills
|
||||
{
|
||||
@@ -320,8 +343,8 @@ namespace Content.Server.Chemistry.Components
|
||||
|
||||
var bufferSolution = BufferSolution.SplitSolution(actualVolume);
|
||||
|
||||
pill.TryGetComponent<SolutionContainerComponent>(out var pillSolution);
|
||||
pillSolution?.TryAddSolution(bufferSolution);
|
||||
var pillSolution = EntitySystem.Get<SolutionContainerSystem>().EnsureSolution(pill, "pill");
|
||||
EntitySystem.Get<SolutionContainerSystem>().TryAddSolution(pill.Uid, pillSolution, bufferSolution);
|
||||
|
||||
//Try to give them the bottle
|
||||
if (user.TryGetComponent<HandsComponent>(out var hands) &&
|
||||
@@ -332,7 +355,6 @@ namespace Content.Server.Chemistry.Components
|
||||
hands.PutInHand(item);
|
||||
continue;
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
//Put it on the floor
|
||||
@@ -371,7 +393,7 @@ namespace Content.Server.Chemistry.Components
|
||||
|
||||
/// <summary>
|
||||
/// Called when you click the owner entity with something in your active hand. If the entity in your hand
|
||||
/// contains a <see cref="SolutionContainerComponent"/>, if you have hands, and if the chem master doesn't already
|
||||
/// contains a <see cref="Solution"/>, if you have hands, and if the chem master doesn't already
|
||||
/// hold a container, it will be added to the chem master.
|
||||
/// </summary>
|
||||
/// <param name="args">Data relevant to the event such as the actor which triggered it.</param>
|
||||
@@ -391,16 +413,18 @@ namespace Content.Server.Chemistry.Components
|
||||
}
|
||||
|
||||
var activeHandEntity = hands.GetActiveHand.Owner;
|
||||
if (activeHandEntity.TryGetComponent<SolutionContainerComponent>(out var solution))
|
||||
if (activeHandEntity.HasComponent<SolutionContainerManagerComponent>())
|
||||
{
|
||||
if (HasBeaker)
|
||||
{
|
||||
Owner.PopupMessage(args.User, Loc.GetString("chem-master-component-has-beaker-already-message"));
|
||||
}
|
||||
else if (!solution.CanUseWithChemDispenser)
|
||||
else if (!activeHandEntity.HasComponent<FitsInDispenserComponent>())
|
||||
{
|
||||
//If it can't fit in the chem master, don't put it in. For example, buckets and mop buckets can't fit.
|
||||
Owner.PopupMessage(args.User, Loc.GetString("chem-master-component-container-too-large-message",("container", activeHandEntity)));
|
||||
Owner.PopupMessage(args.User,
|
||||
Loc.GetString("chem-master-component-container-too-large-message",
|
||||
("container", activeHandEntity)));
|
||||
}
|
||||
else
|
||||
{
|
||||
@@ -410,14 +434,13 @@ namespace Content.Server.Chemistry.Components
|
||||
}
|
||||
else
|
||||
{
|
||||
Owner.PopupMessage(args.User, Loc.GetString("chem-master-component-cannot-put-entity-message", ("entity", activeHandEntity)));
|
||||
Owner.PopupMessage(args.User,
|
||||
Loc.GetString("chem-master-component-cannot-put-entity-message", ("entity", activeHandEntity)));
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
void ISolutionChange.SolutionChanged(SolutionChangeEventArgs eventArgs) => UpdateUserInterface();
|
||||
|
||||
private void ClickSound()
|
||||
{
|
||||
SoundSystem.Play(Filter.Pvs(Owner), _clickSound.GetSound(), Owner, AudioParams.Default.WithVolume(-2f));
|
||||
@@ -446,6 +469,5 @@ namespace Content.Server.Chemistry.Components
|
||||
component.TryEject(user);
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,6 +1,8 @@
|
||||
using Content.Server.Body.Circulatory;
|
||||
using Content.Server.Inventory.Components;
|
||||
using Content.Server.Items;
|
||||
using Content.Shared.Chemistry;
|
||||
using Content.Shared.Chemistry.EntitySystems;
|
||||
using Content.Shared.Chemistry.Reagent;
|
||||
using Content.Shared.Foam;
|
||||
using Content.Shared.Inventory;
|
||||
@@ -15,21 +17,22 @@ namespace Content.Server.Chemistry.Components
|
||||
public class FoamSolutionAreaEffectComponent : SolutionAreaEffectComponent
|
||||
{
|
||||
public override string Name => "FoamSolutionAreaEffect";
|
||||
public static string SolutionName = "foam";
|
||||
|
||||
[DataField("foamedMetalPrototype")] private string? _foamedMetalPrototype;
|
||||
|
||||
protected override void UpdateVisuals()
|
||||
{
|
||||
if (Owner.TryGetComponent(out AppearanceComponent? appearance) &&
|
||||
SolutionContainerComponent != null)
|
||||
EntitySystem.Get<SolutionContainerSystem>().TryGetSolution(Owner, SolutionName, out var solution))
|
||||
{
|
||||
appearance.SetData(FoamVisuals.Color, SolutionContainerComponent.Color.WithAlpha(0.80f));
|
||||
appearance.SetData(FoamVisuals.Color, solution.Color.WithAlpha(0.80f));
|
||||
}
|
||||
}
|
||||
|
||||
protected override void ReactWithEntity(IEntity entity, double solutionFraction)
|
||||
{
|
||||
if (SolutionContainerComponent == null)
|
||||
if (!EntitySystem.Get<SolutionContainerSystem>().TryGetSolution(Owner, SolutionName, out var solution))
|
||||
return;
|
||||
|
||||
if (!entity.TryGetComponent(out BloodstreamComponent? bloodstream))
|
||||
@@ -53,8 +56,9 @@ namespace Content.Server.Chemistry.Components
|
||||
}
|
||||
}
|
||||
|
||||
var cloneSolution = SolutionContainerComponent.Solution.Clone();
|
||||
var transferAmount = ReagentUnit.Min(cloneSolution.TotalVolume * solutionFraction * (1 - protection), bloodstream.EmptyVolume);
|
||||
var cloneSolution = solution.Clone();
|
||||
var transferAmount = ReagentUnit.Min(cloneSolution.TotalVolume * solutionFraction * (1 - protection),
|
||||
bloodstream.EmptyVolume);
|
||||
var transferSolution = cloneSolution.SplitSolution(transferAmount);
|
||||
|
||||
bloodstream.TryTransferSolution(transferSolution);
|
||||
@@ -68,12 +72,14 @@ namespace Content.Server.Chemistry.Components
|
||||
{
|
||||
appearance.SetData(FoamVisuals.State, true);
|
||||
}
|
||||
|
||||
Owner.SpawnTimer(600, () =>
|
||||
{
|
||||
if (!string.IsNullOrEmpty(_foamedMetalPrototype))
|
||||
{
|
||||
Owner.EntityManager.SpawnEntity(_foamedMetalPrototype, Owner.Transform.Coordinates);
|
||||
}
|
||||
|
||||
Owner.QueueDelete();
|
||||
});
|
||||
}
|
||||
|
||||
@@ -1,8 +1,8 @@
|
||||
using Content.Server.Interaction.Components;
|
||||
using Content.Server.MobState.States;
|
||||
using Content.Server.Weapon.Melee;
|
||||
using Content.Shared.Chemistry;
|
||||
using Content.Shared.Chemistry.Components;
|
||||
using Content.Shared.Chemistry.EntitySystems;
|
||||
using Content.Shared.Chemistry.Reagent;
|
||||
using Content.Shared.Notification.Managers;
|
||||
using Content.Shared.Sound;
|
||||
@@ -18,7 +18,7 @@ using Robust.Shared.ViewVariables;
|
||||
namespace Content.Server.Chemistry.Components
|
||||
{
|
||||
[RegisterComponent]
|
||||
public sealed class HyposprayComponent : SharedHyposprayComponent, ISolutionChange
|
||||
public sealed class HyposprayComponent : SharedHyposprayComponent
|
||||
{
|
||||
[DataField("ClumsyFailChance")]
|
||||
[ViewVariables(VVAccess.ReadWrite)]
|
||||
@@ -31,8 +31,6 @@ namespace Content.Server.Chemistry.Components
|
||||
[DataField("InjectSound")]
|
||||
private SoundSpecifier _injectSound = new SoundPathSpecifier("/Audio/Items/hypospray.ogg");
|
||||
|
||||
[ComponentDependency] private readonly SolutionContainerComponent? _solution = default!;
|
||||
|
||||
protected override void Initialize()
|
||||
{
|
||||
base.Initialize();
|
||||
@@ -57,13 +55,24 @@ namespace Content.Server.Chemistry.Components
|
||||
target = user;
|
||||
}
|
||||
|
||||
if (_solution == null || _solution.CurrentVolume == 0)
|
||||
var solutionsSys = EntitySystem.Get<SolutionContainerSystem>();
|
||||
solutionsSys.TryGetSolution(Owner, SolutionName, out var hypoSpraySolution);
|
||||
|
||||
if (hypoSpraySolution == null || hypoSpraySolution.CurrentVolume == 0)
|
||||
{
|
||||
user.PopupMessageCursor(Loc.GetString("hypospray-component-empty-message"));
|
||||
return true;
|
||||
}
|
||||
|
||||
user.PopupMessage(Loc.GetString(msgFormat ?? "hypospray-component-inject-other-message",("other", target)));
|
||||
if (!solutionsSys.TryGetInjectableSolution(target.Uid, out var targetSolution))
|
||||
{
|
||||
user.PopupMessage(user,
|
||||
Loc.GetString("hypospray-cant-inject", ("target", target)));
|
||||
return false;
|
||||
}
|
||||
|
||||
user.PopupMessage(Loc.GetString(msgFormat ?? "hypospray-component-inject-other-message",
|
||||
("other", target)));
|
||||
if (target != user)
|
||||
{
|
||||
target.PopupMessage(Loc.GetString("hypospray-component-feel-prick-message"));
|
||||
@@ -74,19 +83,21 @@ namespace Content.Server.Chemistry.Components
|
||||
|
||||
SoundSystem.Play(Filter.Pvs(user), _injectSound.GetSound(), user);
|
||||
|
||||
var targetSolution = target.GetComponent<SolutionContainerComponent>();
|
||||
|
||||
// Get transfer amount. May be smaller than _transferAmount if not enough room
|
||||
var realTransferAmount = ReagentUnit.Min(TransferAmount, targetSolution.EmptyVolume);
|
||||
var realTransferAmount = ReagentUnit.Min(TransferAmount, targetSolution.AvailableVolume);
|
||||
|
||||
if (realTransferAmount <= 0)
|
||||
{
|
||||
user.PopupMessage(user, Loc.GetString("hypospray-component-transfer-already-full-message ",("owner", targetSolution.Owner)));
|
||||
user.PopupMessage(user,
|
||||
Loc.GetString("hypospray-component-transfer-already-full-message",
|
||||
("owner", target)));
|
||||
return true;
|
||||
}
|
||||
|
||||
// Move units from attackSolution to targetSolution
|
||||
var removedSolution = _solution.SplitSolution(realTransferAmount);
|
||||
var removedSolution =
|
||||
EntitySystem.Get<SolutionContainerSystem>()
|
||||
.SplitSolution(Owner.Uid, hypoSpraySolution, realTransferAmount);
|
||||
|
||||
if (!targetSolution.CanAddSolution(removedSolution))
|
||||
{
|
||||
@@ -95,29 +106,26 @@ namespace Content.Server.Chemistry.Components
|
||||
|
||||
removedSolution.DoEntityReaction(target, ReactionMethod.Injection);
|
||||
|
||||
targetSolution.TryAddSolution(removedSolution);
|
||||
EntitySystem.Get<SolutionContainerSystem>().TryAddSolution(target.Uid, targetSolution, removedSolution);
|
||||
|
||||
static bool EligibleEntity(IEntity entity)
|
||||
{
|
||||
// TODO: Does checking for BodyComponent make sense as a "can be hypospray'd" tag?
|
||||
// In SS13 the hypospray ONLY works on mobs, NOT beakers or anything else.
|
||||
return entity.HasComponent<SolutionContainerComponent>() && entity.HasComponent<MobStateComponent>();
|
||||
|
||||
return entity.HasComponent<SharedChemMasterComponent>()
|
||||
&& entity.HasComponent<MobStateComponent>();
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
void ISolutionChange.SolutionChanged(SolutionChangeEventArgs eventArgs)
|
||||
{
|
||||
Dirty();
|
||||
}
|
||||
|
||||
public override ComponentState GetComponentState(ICommonSession player)
|
||||
{
|
||||
if (_solution == null)
|
||||
return new HyposprayComponentState(ReagentUnit.Zero, ReagentUnit.Zero);
|
||||
|
||||
return new HyposprayComponentState(_solution.CurrentVolume, _solution.MaxVolume);
|
||||
var solutionSys = Owner.EntityManager.EntitySysManager.GetEntitySystem<SolutionContainerSystem>();
|
||||
return solutionSys.TryGetSolution(Owner, SolutionName, out var solution)
|
||||
? new HyposprayComponentState(solution.CurrentVolume, solution.MaxVolume)
|
||||
: new HyposprayComponentState(ReagentUnit.Zero, ReagentUnit.Zero);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,10 +1,11 @@
|
||||
using System;
|
||||
using System.Threading.Tasks;
|
||||
using Content.Server.Body.Circulatory;
|
||||
using Content.Shared.Chemistry;
|
||||
using Content.Shared.Body.Networks;
|
||||
using Content.Shared.Chemistry.Components;
|
||||
using Content.Shared.Chemistry.Components.SolutionManager;
|
||||
using Content.Shared.Chemistry.EntitySystems;
|
||||
using Content.Shared.Chemistry.Reagent;
|
||||
using Content.Shared.Chemistry.Solution.Components;
|
||||
using Content.Shared.Interaction;
|
||||
using Content.Shared.Interaction.Helpers;
|
||||
using Content.Shared.Notification.Managers;
|
||||
@@ -22,8 +23,10 @@ namespace Content.Server.Chemistry.Components
|
||||
/// containers, and can directly inject into a mobs bloodstream.
|
||||
/// </summary>
|
||||
[RegisterComponent]
|
||||
public class InjectorComponent : SharedInjectorComponent, IAfterInteract, IUse, ISolutionChange
|
||||
public class InjectorComponent : SharedInjectorComponent, IAfterInteract, IUse
|
||||
{
|
||||
public const string SolutionName = "injector";
|
||||
|
||||
/// <summary>
|
||||
/// Whether or not the injector is able to draw from containers or if it's a single use
|
||||
/// device that can only inject.
|
||||
@@ -109,48 +112,47 @@ namespace Content.Server.Chemistry.Components
|
||||
if (!eventArgs.InRangeUnobstructed(ignoreInsideBlocker: true, popup: true))
|
||||
return false;
|
||||
|
||||
var solutionsSys = EntitySystem.Get<SolutionContainerSystem>();
|
||||
//Make sure we have the attacking entity
|
||||
if (eventArgs.Target == null || !Owner.HasComponent<SolutionContainerComponent>())
|
||||
if (eventArgs.Target == null || !Owner.HasComponent<SolutionContainerManagerComponent>())
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
var targetEntity = eventArgs.Target;
|
||||
|
||||
|
||||
// Handle injecting/drawing for solutions
|
||||
if (targetEntity.TryGetComponent<ISolutionInteractionsComponent>(out var targetSolution))
|
||||
{
|
||||
if (ToggleState == InjectorToggleMode.Inject)
|
||||
{
|
||||
if (targetSolution.CanInject)
|
||||
if (solutionsSys.TryGetInjectableSolution(targetEntity.Uid, out var injectableSolution))
|
||||
{
|
||||
TryInject(targetSolution, eventArgs.User);
|
||||
TryInject(targetEntity, injectableSolution, eventArgs.User);
|
||||
}
|
||||
else if (targetEntity.TryGetComponent(out BloodstreamComponent? bloodstream))
|
||||
{
|
||||
TryInjectIntoBloodstream(bloodstream, eventArgs.User);
|
||||
}
|
||||
else
|
||||
{
|
||||
eventArgs.User.PopupMessage(eventArgs.User,
|
||||
Loc.GetString("injector-component-cannot-transfer-message", ("owner", targetSolution.Owner)));
|
||||
Loc.GetString("injector-component-cannot-transfer-message",
|
||||
("owner", eventArgs.User)));
|
||||
}
|
||||
}
|
||||
else if (ToggleState == InjectorToggleMode.Draw)
|
||||
{
|
||||
if (targetSolution.CanDraw)
|
||||
if (solutionsSys.TryGetDrawableSolution(targetEntity, out var drawableSolution))
|
||||
{
|
||||
TryDraw(targetSolution, eventArgs.User);
|
||||
TryDraw(targetEntity, drawableSolution, eventArgs.User);
|
||||
}
|
||||
else
|
||||
{
|
||||
eventArgs.User.PopupMessage(eventArgs.User,
|
||||
Loc.GetString("injector-component-cannot-draw-message", ("owner", targetSolution.Owner)));
|
||||
Loc.GetString("injector-component-cannot-draw-message",
|
||||
("owner", eventArgs.User)));
|
||||
}
|
||||
}
|
||||
}
|
||||
// Handle injecting into bloodstream
|
||||
else if (targetEntity.TryGetComponent(out BloodstreamComponent? bloodstream) &&
|
||||
ToggleState == InjectorToggleMode.Inject)
|
||||
{
|
||||
TryInjectIntoBloodstream(bloodstream, eventArgs.User);
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
@@ -168,10 +170,10 @@ namespace Content.Server.Chemistry.Components
|
||||
|
||||
private void TryInjectIntoBloodstream(BloodstreamComponent targetBloodstream, IEntity user)
|
||||
{
|
||||
if (!Owner.TryGetComponent(out SolutionContainerComponent? solution) || solution.CurrentVolume == 0)
|
||||
{
|
||||
if (!EntitySystem.Get<SolutionContainerSystem>()
|
||||
.TryGetSolution(user, SharedBloodstreamComponent.DefaultSolutionName, out var bloodstream)
|
||||
|| bloodstream.CurrentVolume == 0)
|
||||
return;
|
||||
}
|
||||
|
||||
// Get transfer amount. May be smaller than _transferAmount if not enough room
|
||||
var realTransferAmount = ReagentUnit.Min(_transferAmount, targetBloodstream.EmptyVolume);
|
||||
@@ -184,18 +186,19 @@ namespace Content.Server.Chemistry.Components
|
||||
}
|
||||
|
||||
// Move units from attackSolution to targetSolution
|
||||
var removedSolution = solution.SplitSolution(realTransferAmount);
|
||||
var removedSolution =
|
||||
EntitySystem.Get<SolutionContainerSystem>().SplitSolution(user.Uid, bloodstream, realTransferAmount);
|
||||
|
||||
if (!solution.CanAddSolution(removedSolution))
|
||||
if (!bloodstream.CanAddSolution(removedSolution))
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
// TODO: Account for partial transfer.
|
||||
var bloodsStreamEntity = Owner.EntityManager.GetEntity(user.Uid);
|
||||
removedSolution.DoEntityReaction(bloodsStreamEntity, ReactionMethod.Injection);
|
||||
|
||||
removedSolution.DoEntityReaction(solution.Owner, ReactionMethod.Injection);
|
||||
|
||||
solution.TryAddSolution(removedSolution);
|
||||
EntitySystem.Get<SolutionContainerSystem>().TryAddSolution(user.Uid, bloodstream, removedSolution);
|
||||
|
||||
removedSolution.DoEntityReaction(targetBloodstream.Owner, ReactionMethod.Injection);
|
||||
|
||||
@@ -207,33 +210,36 @@ namespace Content.Server.Chemistry.Components
|
||||
AfterInject();
|
||||
}
|
||||
|
||||
private void TryInject(ISolutionInteractionsComponent targetSolution, IEntity user)
|
||||
private void TryInject(IEntity targetEntity, Solution targetSolution, IEntity user)
|
||||
{
|
||||
if (!Owner.TryGetComponent(out SolutionContainerComponent? solution) || solution.CurrentVolume == 0)
|
||||
if (!EntitySystem.Get<SolutionContainerSystem>().TryGetSolution(Owner, SolutionName, out var solution)
|
||||
|| solution.CurrentVolume == 0)
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
// Get transfer amount. May be smaller than _transferAmount if not enough room
|
||||
var realTransferAmount = ReagentUnit.Min(_transferAmount, targetSolution.InjectSpaceAvailable);
|
||||
var realTransferAmount = ReagentUnit.Min(_transferAmount, targetSolution.AvailableVolume);
|
||||
|
||||
if (realTransferAmount <= 0)
|
||||
{
|
||||
Owner.PopupMessage(user, Loc.GetString("injector-component-target-already-full-message", ("target", targetSolution.Owner)));
|
||||
Owner.PopupMessage(user,
|
||||
Loc.GetString("injector-component-target-already-full-message", ("target", targetEntity)));
|
||||
return;
|
||||
}
|
||||
|
||||
// Move units from attackSolution to targetSolution
|
||||
var removedSolution = solution.SplitSolution(realTransferAmount);
|
||||
var removedSolution = EntitySystem.Get<SolutionContainerSystem>().SplitSolution(Owner.Uid, solution, realTransferAmount);
|
||||
|
||||
removedSolution.DoEntityReaction(targetSolution.Owner, ReactionMethod.Injection);
|
||||
removedSolution.DoEntityReaction(targetEntity, ReactionMethod.Injection);
|
||||
|
||||
targetSolution.Inject(removedSolution);
|
||||
EntitySystem.Get<SolutionContainerSystem>()
|
||||
.Inject(targetEntity.Uid, targetSolution, removedSolution);
|
||||
|
||||
Owner.PopupMessage(user,
|
||||
Loc.GetString("injector-component-transfer-success-message",
|
||||
("amount", removedSolution.TotalVolume),
|
||||
("target", targetSolution.Owner)));
|
||||
("target", targetEntity)));
|
||||
Dirty();
|
||||
AfterInject();
|
||||
}
|
||||
@@ -241,15 +247,27 @@ namespace Content.Server.Chemistry.Components
|
||||
private void AfterInject()
|
||||
{
|
||||
// Automatically set syringe to draw after completely draining it.
|
||||
if (Owner.GetComponent<SolutionContainerComponent>().CurrentVolume == 0)
|
||||
if (EntitySystem.Get<SolutionContainerSystem>().TryGetSolution(Owner, SolutionName, out var solution)
|
||||
&& solution.CurrentVolume == 0)
|
||||
{
|
||||
ToggleState = InjectorToggleMode.Draw;
|
||||
}
|
||||
}
|
||||
|
||||
private void TryDraw(ISolutionInteractionsComponent targetSolution, IEntity user)
|
||||
private void AfterDraw()
|
||||
{
|
||||
if (!Owner.TryGetComponent(out SolutionContainerComponent? solution) || solution.EmptyVolume == 0)
|
||||
// Automatically set syringe to inject after completely filling it.
|
||||
if (EntitySystem.Get<SolutionContainerSystem>().TryGetSolution(Owner, SolutionName, out var solution)
|
||||
&& solution.AvailableVolume == 0)
|
||||
{
|
||||
ToggleState = InjectorToggleMode.Inject;
|
||||
}
|
||||
}
|
||||
|
||||
private void TryDraw(IEntity targetEntity, Solution targetSolution, IEntity user)
|
||||
{
|
||||
if (!EntitySystem.Get<SolutionContainerSystem>().TryGetSolution(Owner, SolutionName, out var solution)
|
||||
|| solution.AvailableVolume == 0)
|
||||
{
|
||||
return;
|
||||
}
|
||||
@@ -259,14 +277,16 @@ namespace Content.Server.Chemistry.Components
|
||||
|
||||
if (realTransferAmount <= 0)
|
||||
{
|
||||
Owner.PopupMessage(user, Loc.GetString("injector-component-target-is-empty-message",("target", targetSolution.Owner)));
|
||||
Owner.PopupMessage(user,
|
||||
Loc.GetString("injector-component-target-is-empty-message", ("target", targetEntity)));
|
||||
return;
|
||||
}
|
||||
|
||||
// Move units from attackSolution to targetSolution
|
||||
var removedSolution = targetSolution.Draw(realTransferAmount);
|
||||
var removedSolution = EntitySystem.Get<SolutionContainerSystem>()
|
||||
.Draw(targetEntity.Uid, targetSolution, realTransferAmount);
|
||||
|
||||
if (!solution.TryAddSolution(removedSolution))
|
||||
if (!EntitySystem.Get<SolutionContainerSystem>().TryAddSolution(targetEntity.Uid, solution, removedSolution))
|
||||
{
|
||||
return;
|
||||
}
|
||||
@@ -274,28 +294,16 @@ namespace Content.Server.Chemistry.Components
|
||||
Owner.PopupMessage(user,
|
||||
Loc.GetString("injector-component-draw-success-message",
|
||||
("amount", removedSolution.TotalVolume),
|
||||
("target", targetSolution.Owner)));
|
||||
("target", targetEntity)));
|
||||
Dirty();
|
||||
AfterDraw();
|
||||
}
|
||||
|
||||
private void AfterDraw()
|
||||
{
|
||||
// Automatically set syringe to inject after completely filling it.
|
||||
if (Owner.GetComponent<SolutionContainerComponent>().EmptyVolume == 0)
|
||||
{
|
||||
ToggleState = InjectorToggleMode.Inject;
|
||||
}
|
||||
}
|
||||
|
||||
void ISolutionChange.SolutionChanged(SolutionChangeEventArgs eventArgs)
|
||||
{
|
||||
Dirty();
|
||||
}
|
||||
|
||||
public override ComponentState GetComponentState(ICommonSession player)
|
||||
{
|
||||
Owner.TryGetComponent(out SolutionContainerComponent? solution);
|
||||
Owner.EntityManager.EntitySysManager.GetEntitySystem<SolutionContainerSystem>()
|
||||
.TryGetSolution(Owner, SolutionName, out var solution);
|
||||
|
||||
var currentVolume = solution?.CurrentVolume ?? ReagentUnit.Zero;
|
||||
var maxVolume = solution?.MaxVolume ?? ReagentUnit.Zero;
|
||||
|
||||
@@ -7,10 +7,10 @@ using Content.Server.Items;
|
||||
using Content.Server.Power.Components;
|
||||
using Content.Server.UserInterface;
|
||||
using Content.Shared.ActionBlocker;
|
||||
using Content.Shared.Chemistry;
|
||||
using Content.Shared.Chemistry.Components;
|
||||
using Content.Shared.Chemistry.Dispenser;
|
||||
using Content.Shared.Chemistry.EntitySystems;
|
||||
using Content.Shared.Chemistry.Reagent;
|
||||
using Content.Shared.Chemistry.Solution;
|
||||
using Content.Shared.Interaction;
|
||||
using Content.Shared.Notification.Managers;
|
||||
using Content.Shared.Sound;
|
||||
@@ -38,9 +38,10 @@ namespace Content.Server.Chemistry.Components
|
||||
[RegisterComponent]
|
||||
[ComponentReference(typeof(IActivate))]
|
||||
[ComponentReference(typeof(IInteractUsing))]
|
||||
public class ReagentDispenserComponent : SharedReagentDispenserComponent, IActivate, IInteractUsing, ISolutionChange
|
||||
public class ReagentDispenserComponent : SharedReagentDispenserComponent, IActivate, IInteractUsing
|
||||
{
|
||||
private static ReagentInventoryComparer _comparer = new();
|
||||
public static string SolutionName = "reagent";
|
||||
|
||||
[Dependency] private readonly IPrototypeManager _prototypeManager = default!;
|
||||
|
||||
@@ -52,9 +53,20 @@ namespace Content.Server.Chemistry.Components
|
||||
|
||||
[ViewVariables] private bool HasBeaker => _beakerContainer.ContainedEntity != null;
|
||||
[ViewVariables] private ReagentUnit _dispenseAmount = ReagentUnit.New(10);
|
||||
[UsedImplicitly] [ViewVariables] private SolutionContainerComponent? Solution => _beakerContainer.ContainedEntity?.GetComponent<SolutionContainerComponent>();
|
||||
|
||||
[ViewVariables] private bool Powered => !Owner.TryGetComponent(out ApcPowerReceiverComponent? receiver) || receiver.Powered;
|
||||
[UsedImplicitly]
|
||||
[ViewVariables]
|
||||
private Solution? Solution
|
||||
{
|
||||
get
|
||||
{
|
||||
EntitySystem.Get<SolutionContainerSystem>().TryGetSolution(Owner, SolutionName, out var solution);
|
||||
return solution;
|
||||
}
|
||||
}
|
||||
|
||||
[ViewVariables]
|
||||
private bool Powered => !Owner.TryGetComponent(out ApcPowerReceiverComponent? receiver) || receiver.Powered;
|
||||
|
||||
[ViewVariables] private BoundUserInterface? UserInterface => Owner.GetUIOrNull(ReagentDispenserUiKey.Key);
|
||||
|
||||
@@ -216,25 +228,27 @@ namespace Content.Server.Chemistry.Components
|
||||
private ReagentDispenserBoundUserInterfaceState GetUserInterfaceState()
|
||||
{
|
||||
var beaker = _beakerContainer.ContainedEntity;
|
||||
if (beaker == null)
|
||||
if (beaker == null ||
|
||||
!EntitySystem.Get<SolutionContainerSystem>().TryGetSolution(beaker, "beaker", out var solution))
|
||||
{
|
||||
return new ReagentDispenserBoundUserInterfaceState(Powered, false, ReagentUnit.New(0), ReagentUnit.New(0),
|
||||
return new ReagentDispenserBoundUserInterfaceState(Powered, false, ReagentUnit.New(0),
|
||||
ReagentUnit.New(0),
|
||||
string.Empty, Inventory, Owner.Name, null, _dispenseAmount);
|
||||
}
|
||||
|
||||
var solution = beaker.GetComponent<SolutionContainerComponent>();
|
||||
return new ReagentDispenserBoundUserInterfaceState(Powered, true, solution.CurrentVolume, solution.MaxVolume,
|
||||
beaker.Name, Inventory, Owner.Name, solution.ReagentList.ToList(), _dispenseAmount);
|
||||
return new ReagentDispenserBoundUserInterfaceState(Powered, true, solution.CurrentVolume,
|
||||
solution.MaxVolume,
|
||||
beaker.Name, Inventory, Owner.Name, solution.Contents.ToList(), _dispenseAmount);
|
||||
}
|
||||
|
||||
private void UpdateUserInterface()
|
||||
public void UpdateUserInterface()
|
||||
{
|
||||
var state = GetUserInterfaceState();
|
||||
UserInterface?.SetState(state);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// If this component contains an entity with a <see cref="SolutionContainerComponent"/>, eject it.
|
||||
/// If this component contains an entity with a <see cref="SolutionHolder"/>, eject it.
|
||||
/// Tries to eject into user's hands first, then ejects onto dispenser if both hands are full.
|
||||
/// </summary>
|
||||
private void TryEject(IEntity user)
|
||||
@@ -249,40 +263,42 @@ namespace Content.Server.Chemistry.Components
|
||||
_beakerContainer.Remove(beaker);
|
||||
UpdateUserInterface();
|
||||
|
||||
if(!user.TryGetComponent<HandsComponent>(out var hands) || !beaker.TryGetComponent<ItemComponent>(out var item))
|
||||
if (!user.TryGetComponent<HandsComponent>(out var hands) ||
|
||||
!beaker.TryGetComponent<ItemComponent>(out var item))
|
||||
return;
|
||||
if (hands.CanPutInHand(item))
|
||||
hands.PutInHand(item);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// If this component contains an entity with a <see cref="SolutionContainerComponent"/>, remove all of it's reagents / solutions.
|
||||
/// If this component contains an entity with a <see cref="SolutionHolder"/>, remove all of it's reagents / solutions.
|
||||
/// </summary>
|
||||
private void TryClear()
|
||||
{
|
||||
if (!HasBeaker) return;
|
||||
var solution = _beakerContainer.ContainedEntity?.GetComponent<SolutionContainerComponent>();
|
||||
if(solution is null)
|
||||
if (!HasBeaker ||
|
||||
!EntitySystem.Get<SolutionContainerSystem>()
|
||||
.TryGetSolution(_beakerContainer.ContainedEntity, "beaker", out var solution))
|
||||
return;
|
||||
|
||||
solution.RemoveAllSolution();
|
||||
EntitySystem.Get<SolutionContainerSystem>().RemoveAllSolution(_beakerContainer.ContainedEntity!.Uid, solution);
|
||||
|
||||
UpdateUserInterface();
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// If this component contains an entity with a <see cref="SolutionContainerComponent"/>, attempt to dispense the specified reagent to it.
|
||||
/// If this component contains an entity with a <see cref="SolutionHolder"/>, attempt to dispense the specified reagent to it.
|
||||
/// </summary>
|
||||
/// <param name="dispenseIndex">The index of the reagent in <c>Inventory</c>.</param>
|
||||
private void TryDispense(int dispenseIndex)
|
||||
{
|
||||
if (!HasBeaker) return;
|
||||
|
||||
var solution = _beakerContainer.ContainedEntity?.GetComponent<SolutionContainerComponent>();
|
||||
if (solution is null)
|
||||
return;
|
||||
if (_beakerContainer.ContainedEntity == null
|
||||
|| !EntitySystem.Get<SolutionContainerSystem>()
|
||||
.TryGetSolution(_beakerContainer.ContainedEntity, "beaker", out var solution)) return;
|
||||
|
||||
solution.TryAddReagent(Inventory[dispenseIndex].ID, _dispenseAmount, out _);
|
||||
EntitySystem.Get<SolutionContainerSystem>()
|
||||
.TryAddReagent(_beakerContainer.ContainedEntity.Uid, solution, Inventory[dispenseIndex].ID, _dispenseAmount, out _);
|
||||
|
||||
UpdateUserInterface();
|
||||
}
|
||||
@@ -313,7 +329,7 @@ namespace Content.Server.Chemistry.Components
|
||||
|
||||
/// <summary>
|
||||
/// Called when you click the owner entity with something in your active hand. If the entity in your hand
|
||||
/// contains a <see cref="SolutionContainerComponent"/>, if you have hands, and if the dispenser doesn't already
|
||||
/// contains a <see cref="SolutionHolder"/>, if you have hands, and if the dispenser doesn't already
|
||||
/// hold a container, it will be added to the dispenser.
|
||||
/// </summary>
|
||||
/// <param name="args">Data relevant to the event such as the actor which triggered it.</param>
|
||||
@@ -328,18 +344,21 @@ namespace Content.Server.Chemistry.Components
|
||||
|
||||
if (hands.GetActiveHand == null)
|
||||
{
|
||||
Owner.PopupMessage(args.User, Loc.GetString("reagent-dispenser-component-interact-using-nothing-in-hands"));
|
||||
Owner.PopupMessage(args.User,
|
||||
Loc.GetString("reagent-dispenser-component-interact-using-nothing-in-hands"));
|
||||
return false;
|
||||
}
|
||||
|
||||
var solutionSys = EntitySystem.Get<SolutionContainerSystem>();
|
||||
var activeHandEntity = hands.GetActiveHand.Owner;
|
||||
if (activeHandEntity.TryGetComponent<SolutionContainerComponent>(out var solution))
|
||||
if (solutionSys.TryGetSolution(activeHandEntity, "beaker", out _))
|
||||
{
|
||||
if (HasBeaker)
|
||||
{
|
||||
Owner.PopupMessage(args.User, Loc.GetString("reagent-dispenser-component-has-container-already-message"));
|
||||
Owner.PopupMessage(args.User,
|
||||
Loc.GetString("reagent-dispenser-component-has-container-already-message"));
|
||||
}
|
||||
else if ((solution.Capabilities & SolutionContainerCaps.FitsInDispenser) == 0)
|
||||
else if (!solutionSys.HasFitsInDispenser(activeHandEntity))
|
||||
{
|
||||
//If it can't fit in the dispenser, don't put it in. For example, buckets and mop buckets can't fit.
|
||||
Owner.PopupMessage(args.User, Loc.GetString("reagent-dispenser-component-cannot-fit-message"));
|
||||
@@ -352,14 +371,14 @@ namespace Content.Server.Chemistry.Components
|
||||
}
|
||||
else
|
||||
{
|
||||
Owner.PopupMessage(args.User, Loc.GetString("reagent-dispenser-component-cannot-put-entity-message", ("entity", activeHandEntity)));
|
||||
Owner.PopupMessage(args.User,
|
||||
Loc.GetString("reagent-dispenser-component-cannot-put-entity-message",
|
||||
("entity", activeHandEntity)));
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
void ISolutionChange.SolutionChanged(SolutionChangeEventArgs eventArgs) => UpdateUserInterface();
|
||||
|
||||
private void ClickSound()
|
||||
{
|
||||
SoundSystem.Play(Filter.Pvs(Owner), _clickSound.GetSound(), Owner, AudioParams.Default.WithVolume(-2f));
|
||||
|
||||
@@ -1,8 +1,4 @@
|
||||
using Content.Server.Notification;
|
||||
using Content.Shared.Chemistry;
|
||||
using Content.Shared.Chemistry.Reagent;
|
||||
using Robust.Shared.GameObjects;
|
||||
using Robust.Shared.Localization;
|
||||
using Robust.Shared.Serialization.Manager.Attributes;
|
||||
using Robust.Shared.ViewVariables;
|
||||
|
||||
@@ -13,44 +9,18 @@ namespace Content.Server.Chemistry.Components
|
||||
/// But specifically, this component deletes the entity and spawns in a new entity when the entity is exposed to a given reagent.
|
||||
/// </summary>
|
||||
[RegisterComponent]
|
||||
[ComponentReference(typeof(ISolutionChange))]
|
||||
public class RehydratableComponent : Component, ISolutionChange
|
||||
public class RehydratableComponent : Component
|
||||
{
|
||||
public override string Name => "Rehydratable";
|
||||
|
||||
[ViewVariables]
|
||||
[DataField("catalyst")]
|
||||
private string _catalystPrototype = "Water";
|
||||
internal string CatalystPrototype = "Water";
|
||||
|
||||
[ViewVariables]
|
||||
[DataField("target")]
|
||||
private string? _targetPrototype = default!;
|
||||
internal string? TargetPrototype = default!;
|
||||
|
||||
private bool _expanding;
|
||||
|
||||
void ISolutionChange.SolutionChanged(SolutionChangeEventArgs eventArgs)
|
||||
{
|
||||
var solution = eventArgs.Owner.GetComponent<SolutionContainerComponent>();
|
||||
if (solution.Solution.GetReagentQuantity(_catalystPrototype) > ReagentUnit.Zero)
|
||||
{
|
||||
Expand();
|
||||
}
|
||||
}
|
||||
|
||||
// Try not to make this public if you can help it.
|
||||
private void Expand()
|
||||
{
|
||||
if (_expanding)
|
||||
{
|
||||
return;
|
||||
}
|
||||
_expanding = true;
|
||||
Owner.PopupMessageEveryone(Loc.GetString("rehydratable-component-expands-message",("owner", Owner)));
|
||||
if (!string.IsNullOrEmpty(_targetPrototype))
|
||||
{
|
||||
var ent = Owner.EntityManager.SpawnEntity(_targetPrototype, Owner.Transform.Coordinates);
|
||||
ent.Transform.AttachToGridOrMap();
|
||||
}
|
||||
Owner.Delete();
|
||||
}
|
||||
internal bool Expanding;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
using System.Linq;
|
||||
using Content.Server.Body.Circulatory;
|
||||
using Content.Server.Body.Circulatory;
|
||||
using Content.Server.Body.Respiratory;
|
||||
using Content.Shared.Chemistry;
|
||||
using Content.Shared.Chemistry.EntitySystems;
|
||||
using Content.Shared.Chemistry.Reagent;
|
||||
using Content.Shared.Smoking;
|
||||
using Robust.Server.GameObjects;
|
||||
@@ -14,19 +14,20 @@ namespace Content.Server.Chemistry.Components
|
||||
public class SmokeSolutionAreaEffectComponent : SolutionAreaEffectComponent
|
||||
{
|
||||
public override string Name => "SmokeSolutionAreaEffect";
|
||||
public const string SolutionName = "smoke";
|
||||
|
||||
protected override void UpdateVisuals()
|
||||
{
|
||||
if (Owner.TryGetComponent(out AppearanceComponent? appearance) &&
|
||||
SolutionContainerComponent != null)
|
||||
EntitySystem.Get<SolutionContainerSystem>().TryGetSolution(Owner, SolutionName, out var solution))
|
||||
{
|
||||
appearance.SetData(SmokeVisuals.Color, SolutionContainerComponent.Color);
|
||||
appearance.SetData(SmokeVisuals.Color, solution.Color);
|
||||
}
|
||||
}
|
||||
|
||||
protected override void ReactWithEntity(IEntity entity, double solutionFraction)
|
||||
{
|
||||
if (SolutionContainerComponent == null)
|
||||
if (!EntitySystem.Get<SolutionContainerSystem>().TryGetSolution(Owner, SolutionName, out var solution))
|
||||
return;
|
||||
|
||||
if (!entity.TryGetComponent(out BloodstreamComponent? bloodstream))
|
||||
@@ -37,7 +38,7 @@ namespace Content.Server.Chemistry.Components
|
||||
return;
|
||||
|
||||
var chemistry = EntitySystem.Get<ChemistrySystem>();
|
||||
var cloneSolution = SolutionContainerComponent.Solution.Clone();
|
||||
var cloneSolution = solution.Clone();
|
||||
var transferAmount = ReagentUnit.Min(cloneSolution.TotalVolume * solutionFraction, bloodstream.EmptyVolume);
|
||||
var transferSolution = cloneSolution.SplitSolution(transferAmount);
|
||||
|
||||
|
||||
@@ -3,8 +3,9 @@ using System.Linq;
|
||||
using Content.Server.Atmos.Components;
|
||||
using Content.Server.Coordinates.Helpers;
|
||||
using Content.Shared.Chemistry;
|
||||
using Content.Shared.Chemistry.Components;
|
||||
using Content.Shared.Chemistry.EntitySystems;
|
||||
using Content.Shared.Chemistry.Reagent;
|
||||
using Content.Shared.Chemistry.Solution;
|
||||
using Robust.Shared.GameObjects;
|
||||
using Robust.Shared.IoC;
|
||||
using Robust.Shared.Log;
|
||||
@@ -20,10 +21,10 @@ namespace Content.Server.Chemistry.Components
|
||||
/// </summary>
|
||||
public abstract class SolutionAreaEffectComponent : Component
|
||||
{
|
||||
public const string SolutionName = "solutionArea";
|
||||
|
||||
[Dependency] protected readonly IMapManager MapManager = default!;
|
||||
[Dependency] protected readonly IPrototypeManager PrototypeManager = default!;
|
||||
|
||||
[ComponentDependency] protected readonly SolutionContainerComponent? SolutionContainerComponent = default!;
|
||||
public int Amount { get; set; }
|
||||
public SolutionAreaEffectInceptionComponent? Inception { get; set; }
|
||||
|
||||
@@ -67,10 +68,12 @@ namespace Content.Server.Chemistry.Components
|
||||
var coords = Owner.Transform.Coordinates;
|
||||
foreach (var neighbor in grid.GetInDir(coords, dir))
|
||||
{
|
||||
if (Owner.EntityManager.ComponentManager.TryGetComponent(neighbor, out SolutionAreaEffectComponent? comp) && comp.Inception == Inception)
|
||||
if (Owner.EntityManager.ComponentManager.TryGetComponent(neighbor,
|
||||
out SolutionAreaEffectComponent? comp) && comp.Inception == Inception)
|
||||
return;
|
||||
|
||||
if (Owner.EntityManager.ComponentManager.TryGetComponent(neighbor, out AirtightComponent? airtight) && airtight.AirBlocked)
|
||||
if (Owner.EntityManager.ComponentManager.TryGetComponent(neighbor,
|
||||
out AirtightComponent? airtight) && airtight.AirBlocked)
|
||||
return;
|
||||
}
|
||||
|
||||
@@ -82,9 +85,9 @@ namespace Content.Server.Chemistry.Components
|
||||
return;
|
||||
}
|
||||
|
||||
if (SolutionContainerComponent != null)
|
||||
if (EntitySystem.Get<SolutionContainerSystem>().TryGetSolution(Owner, SolutionName, out var solution))
|
||||
{
|
||||
effectComponent.TryAddSolution(SolutionContainerComponent.Solution.Clone());
|
||||
effectComponent.TryAddSolution(solution.Clone());
|
||||
}
|
||||
|
||||
effectComponent.Amount = Amount - 1;
|
||||
@@ -95,7 +98,6 @@ namespace Content.Server.Chemistry.Components
|
||||
SpreadToDir(Direction.East);
|
||||
SpreadToDir(Direction.South);
|
||||
SpreadToDir(Direction.West);
|
||||
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
@@ -120,7 +122,7 @@ namespace Content.Server.Chemistry.Components
|
||||
/// with the other area effects from the inception.</param>
|
||||
public void React(float averageExposures)
|
||||
{
|
||||
if (SolutionContainerComponent == null)
|
||||
if (!EntitySystem.Get<SolutionContainerSystem>().TryGetSolution(Owner, SolutionName, out var solution))
|
||||
return;
|
||||
|
||||
var chemistry = EntitySystem.Get<ChemistrySystem>();
|
||||
@@ -129,7 +131,7 @@ namespace Content.Server.Chemistry.Components
|
||||
|
||||
var solutionFraction = 1 / Math.Floor(averageExposures);
|
||||
|
||||
foreach (var reagentQuantity in SolutionContainerComponent.ReagentList.ToArray())
|
||||
foreach (var reagentQuantity in solution.Contents)
|
||||
{
|
||||
if (reagentQuantity.Quantity == ReagentUnit.Zero) continue;
|
||||
var reagent = PrototypeManager.Index<ReagentPrototype>(reagentQuantity.ReagentId);
|
||||
@@ -140,7 +142,8 @@ namespace Content.Server.Chemistry.Components
|
||||
// Touch every entity on the tile
|
||||
foreach (var entity in tile.GetEntitiesInTileFast().ToArray())
|
||||
{
|
||||
chemistry.ReactionEntity(entity, ReactionMethod.Touch, reagent, reagentQuantity.Quantity * solutionFraction, SolutionContainerComponent.Solution);
|
||||
chemistry.ReactionEntity(entity, ReactionMethod.Touch, reagent,
|
||||
reagentQuantity.Quantity * solutionFraction, solution);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -157,13 +160,13 @@ namespace Content.Server.Chemistry.Components
|
||||
if (solution.TotalVolume == 0)
|
||||
return;
|
||||
|
||||
if (SolutionContainerComponent == null)
|
||||
if (!EntitySystem.Get<SolutionContainerSystem>().TryGetSolution(Owner, SolutionName, out var solutionArea))
|
||||
return;
|
||||
|
||||
var addSolution =
|
||||
solution.SplitSolution(ReagentUnit.Min(solution.TotalVolume, SolutionContainerComponent.EmptyVolume));
|
||||
solution.SplitSolution(ReagentUnit.Min(solution.TotalVolume, solutionArea.AvailableVolume));
|
||||
|
||||
SolutionContainerComponent.TryAddSolution(addSolution);
|
||||
EntitySystem.Get<SolutionContainerSystem>().TryAddSolution(Owner.Uid, solutionArea, addSolution);
|
||||
|
||||
UpdateVisuals();
|
||||
}
|
||||
|
||||
@@ -1,12 +0,0 @@
|
||||
using Content.Shared.Chemistry.Solution.Components;
|
||||
using Robust.Shared.GameObjects;
|
||||
|
||||
namespace Content.Server.Chemistry.Components
|
||||
{
|
||||
[RegisterComponent]
|
||||
[ComponentReference(typeof(SharedSolutionContainerComponent))]
|
||||
[ComponentReference(typeof(ISolutionInteractionsComponent))]
|
||||
public class SolutionContainerComponent : SharedSolutionContainerComponent
|
||||
{
|
||||
}
|
||||
}
|
||||
@@ -3,12 +3,15 @@ using System.Threading.Tasks;
|
||||
using Content.Server.UserInterface;
|
||||
using Content.Shared.ActionBlocker;
|
||||
using Content.Shared.Chemistry;
|
||||
using Content.Shared.Chemistry.Components;
|
||||
using Content.Shared.Chemistry.Components.SolutionManager;
|
||||
using Content.Shared.Chemistry.EntitySystems;
|
||||
using Content.Shared.Chemistry.Reagent;
|
||||
using Content.Shared.Chemistry.Solution.Components;
|
||||
using Content.Shared.Interaction;
|
||||
using Content.Shared.Interaction.Helpers;
|
||||
using Content.Shared.Notification.Managers;
|
||||
using Content.Shared.Verbs;
|
||||
using Microsoft.EntityFrameworkCore.Metadata.Builders;
|
||||
using Robust.Server.GameObjects;
|
||||
using Robust.Shared.GameObjects;
|
||||
using Robust.Shared.Localization;
|
||||
@@ -108,7 +111,8 @@ namespace Content.Server.Chemistry.Components
|
||||
var amount = Math.Clamp(sval, MinimumTransferAmount.Float(),
|
||||
MaximumTransferAmount.Float());
|
||||
|
||||
serverMsg.Session.AttachedEntity?.PopupMessage(Loc.GetString("comp-solution-transfer-set-amount", ("amount", amount)));
|
||||
serverMsg.Session.AttachedEntity?.PopupMessage(Loc.GetString("comp-solution-transfer-set-amount",
|
||||
("amount", amount)));
|
||||
SetTransferAmount(ReagentUnit.New(amount));
|
||||
break;
|
||||
}
|
||||
@@ -116,43 +120,50 @@ namespace Content.Server.Chemistry.Components
|
||||
|
||||
public void SetTransferAmount(ReagentUnit amount)
|
||||
{
|
||||
amount = ReagentUnit.New(Math.Clamp(amount.Int(), MinimumTransferAmount.Int(), MaximumTransferAmount.Int()));
|
||||
amount = ReagentUnit.New(Math.Clamp(amount.Int(), MinimumTransferAmount.Int(),
|
||||
MaximumTransferAmount.Int()));
|
||||
TransferAmount = amount;
|
||||
}
|
||||
|
||||
async Task<bool> IAfterInteract.AfterInteract(AfterInteractEventArgs eventArgs)
|
||||
{
|
||||
var solutionsSys = EntitySystem.Get<SolutionContainerSystem>();
|
||||
|
||||
if (!eventArgs.InRangeUnobstructed() || eventArgs.Target == null)
|
||||
return false;
|
||||
|
||||
if (!Owner.TryGetComponent(out ISolutionInteractionsComponent? ownerSolution))
|
||||
if (!Owner.HasComponent<SolutionContainerManagerComponent>())
|
||||
return false;
|
||||
|
||||
var target = eventArgs.Target;
|
||||
if (!target.TryGetComponent(out ISolutionInteractionsComponent? targetSolution))
|
||||
var target = eventArgs.Target!;
|
||||
if (!target.HasComponent<SolutionContainerManagerComponent>())
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
|
||||
if (CanReceive && target.TryGetComponent(out ReagentTankComponent? tank)
|
||||
&& ownerSolution.CanRefill && targetSolution.CanDrain)
|
||||
&& solutionsSys.TryGetRefillableSolution(Owner.Uid, out var ownerRefill)
|
||||
&& solutionsSys.TryGetDrainableSolution(eventArgs.Target.Uid, out var targetDrain))
|
||||
{
|
||||
var transferred = DoTransfer(targetSolution, ownerSolution, tank.TransferAmount, eventArgs.User);
|
||||
var transferred = DoTransfer(eventArgs.User, eventArgs.Target, targetDrain, Owner, ownerRefill, tank.TransferAmount);
|
||||
if (transferred > 0)
|
||||
{
|
||||
var toTheBrim = ownerSolution.RefillSpaceAvailable == 0;
|
||||
var toTheBrim = ownerRefill.AvailableVolume == 0;
|
||||
var msg = toTheBrim
|
||||
? "comp-solution-transfer-fill-fully"
|
||||
: "comp-solution-transfer-fill-normal";
|
||||
|
||||
target.PopupMessage(eventArgs.User, Loc.GetString(msg,("owner", Owner),("amount", transferred),("target", target)));
|
||||
target.PopupMessage(eventArgs.User,
|
||||
Loc.GetString(msg, ("owner", eventArgs.Target), ("amount", transferred), ("target", Owner)));
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
||||
if (CanSend && targetSolution.CanRefill && ownerSolution.CanDrain)
|
||||
if (CanSend && solutionsSys.TryGetRefillableSolution(eventArgs.Target.Uid, out var targetRefill)
|
||||
&& solutionsSys.TryGetDrainableSolution(Owner.Uid, out var ownerDrain))
|
||||
{
|
||||
var transferred = DoTransfer(ownerSolution, targetSolution, TransferAmount, eventArgs.User);
|
||||
var transferred = DoTransfer(eventArgs.User, Owner, ownerDrain, target, targetRefill, TransferAmount);
|
||||
|
||||
if (transferred > 0)
|
||||
{
|
||||
@@ -169,29 +180,33 @@ namespace Content.Server.Chemistry.Components
|
||||
}
|
||||
|
||||
/// <returns>The actual amount transferred.</returns>
|
||||
private static ReagentUnit DoTransfer(
|
||||
ISolutionInteractionsComponent source,
|
||||
ISolutionInteractionsComponent target,
|
||||
ReagentUnit amount,
|
||||
IEntity user)
|
||||
private static ReagentUnit DoTransfer(IEntity user,
|
||||
IEntity sourceEntity,
|
||||
Solution source,
|
||||
IEntity targetEntity,
|
||||
Solution target,
|
||||
ReagentUnit amount)
|
||||
{
|
||||
|
||||
if (source.DrainAvailable == 0)
|
||||
{
|
||||
source.Owner.PopupMessage(user, Loc.GetString("comp-solution-transfer-is-empty", ("target", source.Owner)));
|
||||
sourceEntity.PopupMessage(user,
|
||||
Loc.GetString("comp-solution-transfer-is-empty", ("target", sourceEntity)));
|
||||
return ReagentUnit.Zero;
|
||||
}
|
||||
|
||||
if (target.RefillSpaceAvailable == 0)
|
||||
if (target.AvailableVolume == 0)
|
||||
{
|
||||
target.Owner.PopupMessage(user, Loc.GetString("comp-solution-transfer-is-full", ("target", target.Owner)));
|
||||
targetEntity.PopupMessage(user,
|
||||
Loc.GetString("comp-solution-transfer-is-full", ("target", targetEntity)));
|
||||
return ReagentUnit.Zero;
|
||||
}
|
||||
|
||||
var actualAmount =
|
||||
ReagentUnit.Min(amount, ReagentUnit.Min(source.DrainAvailable, target.RefillSpaceAvailable));
|
||||
ReagentUnit.Min(amount, ReagentUnit.Min(source.DrainAvailable, target.AvailableVolume));
|
||||
|
||||
var solution = source.Drain(actualAmount);
|
||||
target.Refill(solution);
|
||||
var solution = EntitySystem.Get<SolutionContainerSystem>().Drain(sourceEntity.Uid, source, actualAmount);
|
||||
EntitySystem.Get<SolutionContainerSystem>().Refill(targetEntity.Uid, target, solution);
|
||||
|
||||
return actualAmount;
|
||||
}
|
||||
@@ -251,7 +266,8 @@ namespace Content.Server.Chemistry.Components
|
||||
protected override void Activate(IEntity user, SolutionTransferComponent component)
|
||||
{
|
||||
component.TransferAmount = component.SubjectiveBestTransferAmount();
|
||||
user.PopupMessage(Loc.GetString("comp-solution-transfer-set-amount", ("amount", component.TransferAmount.Int())));
|
||||
user.PopupMessage(Loc.GetString("comp-solution-transfer-set-amount",
|
||||
("amount", component.TransferAmount.Int())));
|
||||
}
|
||||
}
|
||||
|
||||
@@ -275,7 +291,8 @@ namespace Content.Server.Chemistry.Components
|
||||
protected override void Activate(IEntity user, SolutionTransferComponent component)
|
||||
{
|
||||
component.TransferAmount = component.MaximumTransferAmount;
|
||||
user.PopupMessage(Loc.GetString("comp-solution-transfer-set-amount", ("amount", component.TransferAmount.Int())));
|
||||
user.PopupMessage(Loc.GetString("comp-solution-transfer-set-amount",
|
||||
("amount", component.TransferAmount.Int())));
|
||||
}
|
||||
}
|
||||
|
||||
@@ -303,6 +320,7 @@ namespace Content.Server.Chemistry.Components
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
component.UserInterface?.Open(actor.PlayerSession);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,27 +1,22 @@
|
||||
using Content.Shared.Chemistry;
|
||||
using Content.Shared.Chemistry.Components.SolutionManager;
|
||||
using Content.Shared.Chemistry.Reagent;
|
||||
using Content.Shared.Chemistry.Solution;
|
||||
using Robust.Server.GameObjects;
|
||||
using Robust.Shared.GameObjects;
|
||||
using Robust.Shared.IoC;
|
||||
using Robust.Shared.Prototypes;
|
||||
using Robust.Shared.Utility;
|
||||
|
||||
namespace Content.Server.Chemistry.Components
|
||||
{
|
||||
[RegisterComponent]
|
||||
public class TransformableContainerComponent : Component, ISolutionChange
|
||||
public class TransformableContainerComponent : Component
|
||||
{
|
||||
[Dependency] private readonly IPrototypeManager _prototypeManager = default!;
|
||||
|
||||
public override string Name => "TransformableContainer";
|
||||
|
||||
private SpriteSpecifier? _initialSprite;
|
||||
private string _initialName = default!;
|
||||
private string _initialDescription = default!;
|
||||
private ReagentPrototype? _currentReagent;
|
||||
public SpriteSpecifier? InitialSprite;
|
||||
public string InitialName = default!;
|
||||
public string InitialDescription = default!;
|
||||
public ReagentPrototype? CurrentReagent;
|
||||
|
||||
public bool Transformed { get; private set; }
|
||||
public bool Transformed { get; internal set; }
|
||||
|
||||
protected override void Initialize()
|
||||
{
|
||||
@@ -30,72 +25,19 @@ namespace Content.Server.Chemistry.Components
|
||||
if (Owner.TryGetComponent(out SpriteComponent? sprite) &&
|
||||
sprite.BaseRSIPath != null)
|
||||
{
|
||||
_initialSprite = new SpriteSpecifier.Rsi(new ResourcePath(sprite.BaseRSIPath), "icon");
|
||||
InitialSprite = new SpriteSpecifier.Rsi(new ResourcePath(sprite.BaseRSIPath), "icon");
|
||||
}
|
||||
|
||||
_initialName = Owner.Name;
|
||||
_initialDescription = Owner.Description;
|
||||
InitialName = Owner.Name;
|
||||
InitialDescription = Owner.Description;
|
||||
}
|
||||
|
||||
protected override void Startup()
|
||||
{
|
||||
base.Startup();
|
||||
|
||||
Owner.EnsureComponentWarn(out SolutionContainerComponent solution);
|
||||
|
||||
solution.Capabilities |= SolutionContainerCaps.FitsInDispenser;
|
||||
}
|
||||
|
||||
public void CancelTransformation()
|
||||
{
|
||||
_currentReagent = null;
|
||||
Transformed = false;
|
||||
|
||||
if (Owner.TryGetComponent(out SpriteComponent? sprite) &&
|
||||
_initialSprite != null)
|
||||
{
|
||||
sprite.LayerSetSprite(0, _initialSprite);
|
||||
}
|
||||
|
||||
Owner.Name = _initialName;
|
||||
Owner.Description = _initialDescription;
|
||||
}
|
||||
|
||||
void ISolutionChange.SolutionChanged(SolutionChangeEventArgs eventArgs)
|
||||
{
|
||||
var solution = eventArgs.Owner.GetComponent<SolutionContainerComponent>();
|
||||
//Transform container into initial state when emptied
|
||||
if (_currentReagent != null && solution.ReagentList.Count == 0)
|
||||
{
|
||||
CancelTransformation();
|
||||
}
|
||||
|
||||
//the biggest reagent in the solution decides the appearance
|
||||
var reagentId = solution.Solution.GetPrimaryReagentId();
|
||||
|
||||
//If biggest reagent didn't changed - don't change anything at all
|
||||
if (_currentReagent != null && _currentReagent.ID == reagentId)
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
//Only reagents with spritePath property can change appearance of transformable containers!
|
||||
if (!string.IsNullOrWhiteSpace(reagentId) &&
|
||||
_prototypeManager.TryIndex(reagentId, out ReagentPrototype? proto) &&
|
||||
!string.IsNullOrWhiteSpace(proto.SpriteReplacementPath))
|
||||
{
|
||||
var spriteSpec = new SpriteSpecifier.Rsi(new ResourcePath("Objects/Consumable/Drinks/" + proto.SpriteReplacementPath),"icon");
|
||||
|
||||
if (Owner.TryGetComponent(out SpriteComponent? sprite))
|
||||
{
|
||||
sprite?.LayerSetSprite(0, spriteSpec);
|
||||
}
|
||||
|
||||
Owner.Name = proto.Name + " glass";
|
||||
Owner.Description = proto.Description;
|
||||
_currentReagent = proto;
|
||||
Transformed = true;
|
||||
}
|
||||
Owner.EnsureComponentWarn<SolutionContainerManagerComponent>();
|
||||
Owner.EnsureComponentWarn<FitsInDispenserComponent>();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
24
Content.Server/Chemistry/EntitySystems/ChemMasterSystem.cs
Normal file
24
Content.Server/Chemistry/EntitySystems/ChemMasterSystem.cs
Normal file
@@ -0,0 +1,24 @@
|
||||
using Content.Server.Chemistry.Components;
|
||||
using Content.Shared.Chemistry.EntitySystems;
|
||||
using JetBrains.Annotations;
|
||||
using Robust.Shared.GameObjects;
|
||||
|
||||
namespace Content.Server.Chemistry.EntitySystems
|
||||
{
|
||||
[UsedImplicitly]
|
||||
public class ChemMasterSystem : EntitySystem
|
||||
{
|
||||
public override void Initialize()
|
||||
{
|
||||
base.Initialize();
|
||||
|
||||
SubscribeLocalEvent<ChemMasterComponent, SolutionChangedEvent>(OnSolutionChange);
|
||||
}
|
||||
|
||||
private void OnSolutionChange(EntityUid uid, ChemMasterComponent component,
|
||||
SolutionChangedEvent solutionChanged)
|
||||
{
|
||||
component.UpdateUserInterface();
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -1,3 +1,4 @@
|
||||
using Content.Shared.Chemistry.Components;
|
||||
using Content.Shared.Chemistry.Reaction;
|
||||
using Content.Shared.Chemistry.Reagent;
|
||||
using Robust.Shared.Audio;
|
||||
@@ -8,9 +9,9 @@ namespace Content.Server.Chemistry.EntitySystems
|
||||
{
|
||||
public class ChemicalReactionSystem : SharedChemicalReactionSystem
|
||||
{
|
||||
protected override void OnReaction(ReactionPrototype reaction, IEntity owner, ReagentUnit unitReactions)
|
||||
protected override void OnReaction(Solution solution, ReactionPrototype reaction, IEntity owner, ReagentUnit unitReactions)
|
||||
{
|
||||
base.OnReaction(reaction, owner, unitReactions);
|
||||
base.OnReaction(solution, reaction, owner, unitReactions);
|
||||
|
||||
SoundSystem.Play(Filter.Pvs(owner), reaction.Sound.GetSound(), owner);
|
||||
}
|
||||
|
||||
@@ -1,10 +1,13 @@
|
||||
using Content.Server.Chemistry.Components;
|
||||
using Content.Shared.Chemistry.EntitySystems;
|
||||
using Content.Shared.Interaction;
|
||||
using Content.Shared.Weapons.Melee;
|
||||
using JetBrains.Annotations;
|
||||
using Robust.Shared.GameObjects;
|
||||
|
||||
namespace Content.Server.Chemistry.EntitySystems
|
||||
{
|
||||
[UsedImplicitly]
|
||||
public class HypospraySystem : EntitySystem
|
||||
{
|
||||
public override void Initialize()
|
||||
@@ -13,6 +16,12 @@ namespace Content.Server.Chemistry.EntitySystems
|
||||
|
||||
SubscribeLocalEvent<HyposprayComponent, AfterInteractEvent>(OnAfterInteract);
|
||||
SubscribeLocalEvent<HyposprayComponent, ClickAttackEvent>(OnClickAttack);
|
||||
SubscribeLocalEvent<HyposprayComponent, SolutionChangedEvent>(OnSolutionChange);
|
||||
}
|
||||
|
||||
private void OnSolutionChange(EntityUid uid, HyposprayComponent component, SolutionChangedEvent args)
|
||||
{
|
||||
component.Dirty();
|
||||
}
|
||||
|
||||
public void OnAfterInteract(EntityUid uid, HyposprayComponent comp, AfterInteractEvent args)
|
||||
|
||||
23
Content.Server/Chemistry/EntitySystems/InjectorSystem.cs
Normal file
23
Content.Server/Chemistry/EntitySystems/InjectorSystem.cs
Normal file
@@ -0,0 +1,23 @@
|
||||
using Content.Server.Chemistry.Components;
|
||||
using Content.Shared.Chemistry.EntitySystems;
|
||||
using JetBrains.Annotations;
|
||||
using Robust.Shared.GameObjects;
|
||||
|
||||
namespace Content.Server.Chemistry.EntitySystems
|
||||
{
|
||||
[UsedImplicitly]
|
||||
public class InjectorSystem : EntitySystem
|
||||
{
|
||||
public override void Initialize()
|
||||
{
|
||||
base.Initialize();
|
||||
|
||||
SubscribeLocalEvent<InjectorComponent, SolutionChangedEvent>(OnSolutionChange);
|
||||
}
|
||||
|
||||
private void OnSolutionChange(EntityUid uid, InjectorComponent component, SolutionChangedEvent args)
|
||||
{
|
||||
component.Dirty();
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,23 @@
|
||||
using Content.Server.Chemistry.Components;
|
||||
using Content.Shared.Chemistry.EntitySystems;
|
||||
using JetBrains.Annotations;
|
||||
using Robust.Shared.GameObjects;
|
||||
|
||||
namespace Content.Server.Chemistry.EntitySystems
|
||||
{
|
||||
[UsedImplicitly]
|
||||
public class ReagentDispenserSystem : EntitySystem
|
||||
{
|
||||
public override void Initialize()
|
||||
{
|
||||
base.Initialize();
|
||||
|
||||
SubscribeLocalEvent<ReagentDispenserComponent, SolutionChangedEvent>(OnSolutionChange);
|
||||
}
|
||||
|
||||
private void OnSolutionChange(EntityUid uid, ReagentDispenserComponent component, SolutionChangedEvent args)
|
||||
{
|
||||
component.UpdateUserInterface();
|
||||
}
|
||||
}
|
||||
}
|
||||
51
Content.Server/Chemistry/EntitySystems/RehydratableSystem.cs
Normal file
51
Content.Server/Chemistry/EntitySystems/RehydratableSystem.cs
Normal file
@@ -0,0 +1,51 @@
|
||||
using Content.Server.Chemistry.Components;
|
||||
using Content.Server.Notification;
|
||||
using Content.Shared.Chemistry.EntitySystems;
|
||||
using Content.Shared.Chemistry.Reagent;
|
||||
using JetBrains.Annotations;
|
||||
using Robust.Shared.GameObjects;
|
||||
using Robust.Shared.IoC;
|
||||
using Robust.Shared.Localization;
|
||||
|
||||
namespace Content.Server.Chemistry.EntitySystems
|
||||
{
|
||||
[UsedImplicitly]
|
||||
public class RehydratableSystem : EntitySystem
|
||||
{
|
||||
[Dependency] private readonly SolutionContainerSystem _solutionsSystem = default!;
|
||||
public override void Initialize()
|
||||
{
|
||||
base.Initialize();
|
||||
|
||||
SubscribeLocalEvent<RehydratableComponent, SolutionChangedEvent>(OnSolutionChange);
|
||||
}
|
||||
|
||||
private void OnSolutionChange(EntityUid uid, RehydratableComponent component, SolutionChangedEvent args)
|
||||
{
|
||||
if (_solutionsSystem.GetReagentQuantity(uid, component.CatalystPrototype) > ReagentUnit.Zero)
|
||||
{
|
||||
Expand(component, component.Owner);
|
||||
}
|
||||
}
|
||||
|
||||
// Try not to make this public if you can help it.
|
||||
private void Expand(RehydratableComponent component, IEntity owner)
|
||||
{
|
||||
if (component.Expanding)
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
component.Expanding = true;
|
||||
owner.PopupMessageEveryone(Loc.GetString("rehydratable-component-expands-message", ("owner", owner)));
|
||||
if (!string.IsNullOrEmpty(component.TargetPrototype))
|
||||
{
|
||||
var ent = component.Owner.EntityManager.SpawnEntity(component.TargetPrototype,
|
||||
owner.Transform.Coordinates);
|
||||
ent.Transform.AttachToGridOrMap();
|
||||
}
|
||||
|
||||
owner.QueueDelete();
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -1,7 +1,10 @@
|
||||
using Content.Server.Body.Circulatory;
|
||||
using Content.Server.Chemistry.Components;
|
||||
using Content.Shared.Chemistry.Components.SolutionManager;
|
||||
using Content.Shared.Chemistry.EntitySystems;
|
||||
using JetBrains.Annotations;
|
||||
using Robust.Shared.GameObjects;
|
||||
using Robust.Shared.IoC;
|
||||
using Robust.Shared.Physics.Dynamics;
|
||||
|
||||
namespace Content.Server.Chemistry.EntitySystems
|
||||
@@ -9,6 +12,7 @@ namespace Content.Server.Chemistry.EntitySystems
|
||||
[UsedImplicitly]
|
||||
internal sealed class SolutionInjectOnCollideSystem : EntitySystem
|
||||
{
|
||||
[Dependency] private readonly SolutionContainerSystem _solutionsSystem = default!;
|
||||
public override void Initialize()
|
||||
{
|
||||
base.Initialize();
|
||||
@@ -18,15 +22,15 @@ namespace Content.Server.Chemistry.EntitySystems
|
||||
|
||||
private void HandleInit(EntityUid uid, SolutionInjectOnCollideComponent component, ComponentInit args)
|
||||
{
|
||||
component.Owner.EnsureComponentWarn<SolutionContainerComponent>($"{nameof(SolutionInjectOnCollideComponent)} requires a SolutionContainer on {component.Owner}!");
|
||||
component.Owner
|
||||
.EnsureComponentWarn<SolutionContainerManagerComponent>($"{nameof(SolutionInjectOnCollideComponent)} requires a SolutionContainerManager on {component.Owner}!");
|
||||
}
|
||||
|
||||
private void HandleInjection(EntityUid uid, SolutionInjectOnCollideComponent component, StartCollideEvent args)
|
||||
{
|
||||
if (!args.OtherFixture.Body.Owner.TryGetComponent<BloodstreamComponent>(out var bloodstream) ||
|
||||
!ComponentManager.TryGetComponent(uid, out SolutionContainerComponent? solutionContainer)) return;
|
||||
!_solutionsSystem.TryGetInjectableSolution(component.Owner.Uid, out var solution)) return;
|
||||
|
||||
var solution = solutionContainer.Solution;
|
||||
var solRemoved = solution.SplitSolution(component.TransferAmount);
|
||||
var solRemovedVol = solRemoved.TotalVolume;
|
||||
|
||||
|
||||
@@ -0,0 +1,82 @@
|
||||
using Content.Server.Chemistry.Components;
|
||||
using Content.Shared.Chemistry.EntitySystems;
|
||||
using Content.Shared.Chemistry.Reagent;
|
||||
using JetBrains.Annotations;
|
||||
using Robust.Server.GameObjects;
|
||||
using Robust.Shared.GameObjects;
|
||||
using Robust.Shared.IoC;
|
||||
using Robust.Shared.Prototypes;
|
||||
using Robust.Shared.Utility;
|
||||
|
||||
namespace Content.Server.Chemistry.EntitySystems
|
||||
{
|
||||
[UsedImplicitly]
|
||||
public class TransformableContainerSystem : EntitySystem
|
||||
{
|
||||
[Dependency] private readonly IPrototypeManager _prototypeManager = default!;
|
||||
[Dependency] private readonly SolutionContainerSystem _solutionsSystem = default!;
|
||||
|
||||
public override void Initialize()
|
||||
{
|
||||
base.Initialize();
|
||||
|
||||
SubscribeLocalEvent<TransformableContainerComponent, SolutionChangedEvent>(OnSolutionChange);
|
||||
}
|
||||
|
||||
private void OnSolutionChange(EntityUid uid, TransformableContainerComponent component,
|
||||
SolutionChangedEvent args)
|
||||
{
|
||||
if (!_solutionsSystem.TryGetFitsInDispenser(uid, out var solution))
|
||||
return;
|
||||
//Transform container into initial state when emptied
|
||||
if (component.CurrentReagent != null && solution.Contents.Count == 0)
|
||||
{
|
||||
CancelTransformation(component);
|
||||
}
|
||||
|
||||
//the biggest reagent in the solution decides the appearance
|
||||
var reagentId = solution.GetPrimaryReagentId();
|
||||
|
||||
//If biggest reagent didn't changed - don't change anything at all
|
||||
if (component.CurrentReagent != null && component.CurrentReagent.ID == reagentId)
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
//Only reagents with spritePath property can change appearance of transformable containers!
|
||||
if (!string.IsNullOrWhiteSpace(reagentId)
|
||||
&& _prototypeManager.TryIndex(reagentId, out ReagentPrototype? proto)
|
||||
&& !string.IsNullOrWhiteSpace(proto.SpriteReplacementPath))
|
||||
{
|
||||
var spriteSpec =
|
||||
new SpriteSpecifier.Rsi(
|
||||
new ResourcePath("Objects/Consumable/Drinks/" + proto.SpriteReplacementPath), "icon");
|
||||
var ownerEntity = EntityManager.GetEntity(uid);
|
||||
if (ownerEntity.TryGetComponent(out SpriteComponent? sprite))
|
||||
{
|
||||
sprite?.LayerSetSprite(0, spriteSpec);
|
||||
}
|
||||
|
||||
ownerEntity.Name = proto.Name + " glass";
|
||||
ownerEntity.Description = proto.Description;
|
||||
component.CurrentReagent = proto;
|
||||
component.Transformed = true;
|
||||
}
|
||||
}
|
||||
|
||||
private void CancelTransformation(TransformableContainerComponent component)
|
||||
{
|
||||
component.CurrentReagent = null;
|
||||
component.Transformed = false;
|
||||
|
||||
if (component.Owner.TryGetComponent(out SpriteComponent? sprite) &&
|
||||
component.InitialSprite != null)
|
||||
{
|
||||
sprite.LayerSetSprite(0, component.InitialSprite);
|
||||
}
|
||||
|
||||
component.Owner.Name = component.InitialName;
|
||||
component.Owner.Description = component.InitialDescription;
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -1,8 +1,10 @@
|
||||
using System.Linq;
|
||||
using Content.Server.Chemistry.Components;
|
||||
using Content.Server.Chemistry.Components;
|
||||
using Content.Shared.Chemistry.Components;
|
||||
using Content.Shared.Chemistry.Components.SolutionManager;
|
||||
using Content.Shared.Chemistry.EntitySystems;
|
||||
using Content.Shared.Chemistry.Reagent;
|
||||
using Content.Shared.Chemistry.Solution;
|
||||
using Content.Shared.Physics;
|
||||
using Content.Shared.Vapor;
|
||||
using JetBrains.Annotations;
|
||||
using Robust.Shared.GameObjects;
|
||||
using Robust.Shared.IoC;
|
||||
@@ -18,6 +20,7 @@ namespace Content.Server.Chemistry.EntitySystems
|
||||
{
|
||||
[Dependency] private readonly IMapManager _mapManager = default!;
|
||||
[Dependency] private readonly IPrototypeManager _protoManager = default!;
|
||||
[Dependency] private readonly SolutionContainerSystem _solutionContainerSystem = default!;
|
||||
|
||||
private const float ReactTime = 0.125f;
|
||||
|
||||
@@ -29,9 +32,12 @@ namespace Content.Server.Chemistry.EntitySystems
|
||||
|
||||
private void HandleCollide(EntityUid uid, VaporComponent component, StartCollideEvent args)
|
||||
{
|
||||
if (!ComponentManager.TryGetComponent(uid, out SolutionContainerComponent? contents)) return;
|
||||
if (!ComponentManager.TryGetComponent(uid, out SolutionContainerManagerComponent? contents)) return;
|
||||
|
||||
contents.Solution.DoEntityReaction(args.OtherFixture.Body.Owner, ReactionMethod.Touch);
|
||||
foreach (var (_, value) in contents.Solutions)
|
||||
{
|
||||
value.DoEntityReaction(args.OtherFixture.Body.Owner, ReactionMethod.Touch);
|
||||
}
|
||||
|
||||
// Check for collision with a impassable object (e.g. wall) and stop
|
||||
if ((args.OtherFixture.CollisionLayer & (int) CollisionGroup.Impassable) != 0 && args.OtherFixture.Hard)
|
||||
@@ -60,14 +66,7 @@ namespace Content.Server.Chemistry.EntitySystems
|
||||
return false;
|
||||
}
|
||||
|
||||
if (!vapor.Owner.TryGetComponent(out SolutionContainerComponent? contents))
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
var result = contents.TryAddSolution(solution);
|
||||
|
||||
if (!result)
|
||||
if (!_solutionContainerSystem.TryGetSolution(vapor.Owner, SharedVaporComponent.SolutionName, out _))
|
||||
{
|
||||
return false;
|
||||
}
|
||||
@@ -77,13 +76,17 @@ namespace Content.Server.Chemistry.EntitySystems
|
||||
|
||||
public override void Update(float frameTime)
|
||||
{
|
||||
foreach (var (vaporComp, solution) in ComponentManager.EntityQuery<VaporComponent, SolutionContainerComponent>(true))
|
||||
foreach (var (vaporComp, solution) in ComponentManager
|
||||
.EntityQuery<VaporComponent, SolutionContainerManagerComponent>(true))
|
||||
{
|
||||
Update(frameTime, vaporComp, solution);
|
||||
foreach (var (_, value) in solution.Solutions)
|
||||
{
|
||||
Update(frameTime, vaporComp, value);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private void Update(float frameTime, VaporComponent vapor, SolutionContainerComponent contents)
|
||||
private void Update(float frameTime, VaporComponent vapor, Solution contents)
|
||||
{
|
||||
if (!vapor.Active)
|
||||
return;
|
||||
@@ -99,16 +102,19 @@ namespace Content.Server.Chemistry.EntitySystems
|
||||
var mapGrid = _mapManager.GetGrid(entity.Transform.GridID);
|
||||
|
||||
var tile = mapGrid.GetTileRef(entity.Transform.Coordinates.ToVector2i(EntityManager, _mapManager));
|
||||
foreach (var reagentQuantity in contents.ReagentList.ToArray())
|
||||
foreach (var reagentQuantity in contents.Contents.ToArray())
|
||||
{
|
||||
if (reagentQuantity.Quantity == ReagentUnit.Zero) continue;
|
||||
var reagent = _protoManager.Index<ReagentPrototype>(reagentQuantity.ReagentId);
|
||||
contents.TryRemoveReagent(reagentQuantity.ReagentId, reagent.ReactionTile(tile, (reagentQuantity.Quantity / vapor.TransferAmount) * 0.25f));
|
||||
_solutionContainerSystem.TryRemoveReagent(vapor.Owner.Uid, contents, reagentQuantity.ReagentId,
|
||||
reagent.ReactionTile(tile, (reagentQuantity.Quantity / vapor.TransferAmount) * 0.25f));
|
||||
}
|
||||
}
|
||||
|
||||
// Check if we've reached our target.
|
||||
if(!vapor.Reached && vapor.Target.TryDistance(EntityManager, entity.Transform.Coordinates, out var distance) && distance <= 0.5f)
|
||||
if (!vapor.Reached &&
|
||||
vapor.Target.TryDistance(EntityManager, entity.Transform.Coordinates, out var distance) &&
|
||||
distance <= 0.5f)
|
||||
{
|
||||
vapor.Reached = true;
|
||||
}
|
||||
|
||||
@@ -1,7 +1,9 @@
|
||||
using System;
|
||||
using System;
|
||||
using Content.Server.Chemistry.Components;
|
||||
using Content.Server.Coordinates.Helpers;
|
||||
using Content.Shared.Audio;
|
||||
using Content.Shared.Chemistry.Components;
|
||||
using Content.Shared.Chemistry.EntitySystems;
|
||||
using Content.Shared.Chemistry.Reaction;
|
||||
using Content.Shared.Sound;
|
||||
using JetBrains.Annotations;
|
||||
@@ -87,12 +89,9 @@ namespace Content.Server.Chemistry.ReactionEffects
|
||||
IoCManager.InjectDependencies(this);
|
||||
}
|
||||
|
||||
public void React(IEntity solutionEntity, double intensity)
|
||||
public void React(Solution solution, IEntity solutionEntity, double intensity)
|
||||
{
|
||||
if (!solutionEntity.TryGetComponent(out SolutionContainerComponent? contents))
|
||||
return;
|
||||
|
||||
var solution = contents.SplitSolution(contents.MaxVolume);
|
||||
var splitSolution = EntitySystem.Get<SolutionContainerSystem>().SplitSolution(solutionEntity.Uid, solution, solution.MaxVolume);
|
||||
// We take the square root so it becomes harder to reach higher amount values
|
||||
var amount = (int) Math.Round(_rangeConstant + _rangeMultiplier*Math.Sqrt(intensity));
|
||||
amount = Math.Min(amount, _maxRange);
|
||||
@@ -115,7 +114,7 @@ namespace Content.Server.Chemistry.ReactionEffects
|
||||
solutionFraction = amount * (1 - _reagentMaxConcentrationFactor) / _reagentDilutionStart +
|
||||
_reagentMaxConcentrationFactor;
|
||||
}
|
||||
solution.RemoveSolution(solution.TotalVolume * solutionFraction);
|
||||
splitSolution.RemoveSolution(splitSolution.TotalVolume * solutionFraction);
|
||||
}
|
||||
|
||||
if (!_mapManager.TryFindGridAt(solutionEntity.Transform.MapPosition, out var grid)) return;
|
||||
@@ -133,7 +132,7 @@ namespace Content.Server.Chemistry.ReactionEffects
|
||||
return;
|
||||
}
|
||||
|
||||
areaEffectComponent.TryAddSolution(solution);
|
||||
areaEffectComponent.TryAddSolution(splitSolution);
|
||||
areaEffectComponent.Start(amount, _duration, _spreadDelay, _removeDelay);
|
||||
|
||||
SoundSystem.Play(Filter.Pvs(solutionEntity), _sound.GetSound(), solutionEntity, AudioHelpers.WithVariation(0.125f));
|
||||
|
||||
@@ -1,6 +1,8 @@
|
||||
using System;
|
||||
using Content.Server.Chemistry.Components;
|
||||
using Content.Server.Explosion;
|
||||
using Content.Shared.Chemistry.Components;
|
||||
using Content.Shared.Chemistry.Components.SolutionManager;
|
||||
using Content.Shared.Chemistry.Reaction;
|
||||
using Robust.Shared.GameObjects;
|
||||
using Robust.Shared.Serialization.Manager.Attributes;
|
||||
@@ -26,12 +28,11 @@ namespace Content.Server.Chemistry.ReactionEffects
|
||||
/// </summary>
|
||||
[DataField("maxScale")] private float _maxScale = 1;
|
||||
|
||||
public void React(IEntity? solutionEntity, double intensity)
|
||||
public void React(Solution solution, IEntity solutionEntity, double intensity)
|
||||
{
|
||||
var floatIntensity = (float) intensity;
|
||||
if (solutionEntity == null)
|
||||
return;
|
||||
if (!solutionEntity.HasComponent<SolutionContainerComponent>())
|
||||
|
||||
if (!solutionEntity.HasComponent<SolutionContainerManagerComponent>())
|
||||
return;
|
||||
|
||||
//Handle scaling
|
||||
|
||||
@@ -1,6 +1,5 @@
|
||||
using Content.Shared.Body.Components;
|
||||
using Content.Shared.Chemistry.Components;
|
||||
using Content.Shared.Chemistry.Reagent;
|
||||
using Content.Shared.Chemistry.Solution;
|
||||
using Robust.Shared.GameObjects;
|
||||
using Robust.Shared.Serialization.Manager.Attributes;
|
||||
|
||||
|
||||
@@ -1,6 +1,5 @@
|
||||
using Content.Shared.Chemistry;
|
||||
using Content.Shared.Chemistry.Components;
|
||||
using Content.Shared.Chemistry.Reagent;
|
||||
using Content.Shared.Chemistry.Solution;
|
||||
using Robust.Shared.GameObjects;
|
||||
using Robust.Shared.Serialization.Manager.Attributes;
|
||||
using Content.Shared.Damage;
|
||||
|
||||
@@ -1,12 +1,11 @@
|
||||
using Content.Shared.Chemistry.Reagent;
|
||||
using Robust.Shared.GameObjects;
|
||||
using Robust.Shared.Serialization.Manager.Attributes;
|
||||
using Content.Shared.Movement.Components;
|
||||
using Content.Shared.Chemistry.Components;
|
||||
using Robust.Shared.Timing;
|
||||
using Robust.Shared.IoC;
|
||||
using System;
|
||||
using Content.Shared.Chemistry.Solution;
|
||||
using Content.Shared.Chemistry.Components;
|
||||
using Content.Shared.Chemistry.Reagent;
|
||||
using Content.Shared.Movement.Components;
|
||||
using Robust.Shared.GameObjects;
|
||||
using Robust.Shared.IoC;
|
||||
using Robust.Shared.Serialization.Manager.Attributes;
|
||||
using Robust.Shared.Timing;
|
||||
|
||||
namespace Content.Server.Chemistry.ReagentEffects
|
||||
{
|
||||
|
||||
@@ -1,7 +1,6 @@
|
||||
using Content.Server.Nutrition.Components;
|
||||
using Content.Shared.Chemistry;
|
||||
using Content.Shared.Chemistry.Components;
|
||||
using Content.Shared.Chemistry.Reagent;
|
||||
using Content.Shared.Chemistry.Solution;
|
||||
using Robust.Shared.GameObjects;
|
||||
using Robust.Shared.Serialization.Manager.Attributes;
|
||||
|
||||
|
||||
@@ -1,7 +1,6 @@
|
||||
using Content.Server.Nutrition.Components;
|
||||
using Content.Shared.Chemistry;
|
||||
using Content.Shared.Chemistry.Components;
|
||||
using Content.Shared.Chemistry.Reagent;
|
||||
using Content.Shared.Chemistry.Solution;
|
||||
using Robust.Shared.GameObjects;
|
||||
using Robust.Shared.Serialization.Manager.Attributes;
|
||||
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
using System.Collections.Generic;
|
||||
using Content.Server.Chemistry.Components;
|
||||
using Content.Shared.Chemistry.Components;
|
||||
using Content.Shared.Chemistry.EntitySystems;
|
||||
using Content.Shared.Chemistry.Reagent;
|
||||
using Content.Shared.Chemistry.Solution;
|
||||
using JetBrains.Annotations;
|
||||
using Robust.Shared.GameObjects;
|
||||
using Robust.Shared.Serialization.Manager.Attributes;
|
||||
@@ -18,9 +18,13 @@ namespace Content.Server.Chemistry.ReagentEntityReactions
|
||||
|
||||
protected override void React(IEntity entity, ReagentPrototype reagent, ReagentUnit volume, Solution? source)
|
||||
{
|
||||
if (!entity.TryGetComponent(out SolutionContainerComponent? solutionContainer) || (_reagents.Count > 0 && !_reagents.Contains(reagent.ID))) return;
|
||||
// TODO see if this is correct
|
||||
if (!EntitySystem.Get<SolutionContainerSystem>()
|
||||
.TryGetSolution(entity, "reagents", out var solutionContainer)
|
||||
|| (_reagents.Count > 0 && !_reagents.Contains(reagent.ID))) return;
|
||||
|
||||
if(solutionContainer.TryAddReagent(reagent.ID, volume, out var accepted))
|
||||
if (EntitySystem.Get<SolutionContainerSystem>()
|
||||
.TryAddReagent(entity.Uid, solutionContainer, reagent.ID, volume, out var accepted))
|
||||
source?.RemoveReagent(reagent.ID, accepted);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
using System.Collections.Generic;
|
||||
using Content.Server.Atmos.Components;
|
||||
using Content.Shared.Chemistry.Components;
|
||||
using Content.Shared.Chemistry.Reagent;
|
||||
using Content.Shared.Chemistry.Solution;
|
||||
using JetBrains.Annotations;
|
||||
using Robust.Shared.GameObjects;
|
||||
using Robust.Shared.Serialization.Manager.Attributes;
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
using System.Collections.Generic;
|
||||
using Content.Server.Atmos.Components;
|
||||
using Content.Shared.Chemistry.Components;
|
||||
using Content.Shared.Chemistry.Reagent;
|
||||
using Content.Shared.Chemistry.Solution;
|
||||
using JetBrains.Annotations;
|
||||
using Robust.Shared.GameObjects;
|
||||
using Robust.Shared.Serialization.Manager.Attributes;
|
||||
|
||||
@@ -1,8 +1,7 @@
|
||||
using System.Collections.Generic;
|
||||
using Content.Server.Nutrition.Components;
|
||||
using Content.Server.Nutrition.EntitySystems;
|
||||
using Content.Shared.Chemistry.Components;
|
||||
using Content.Shared.Chemistry.Reagent;
|
||||
using Content.Shared.Chemistry.Solution;
|
||||
using Content.Shared.Nutrition.Components;
|
||||
using JetBrains.Annotations;
|
||||
using Robust.Shared.GameObjects;
|
||||
|
||||
@@ -1,8 +1,7 @@
|
||||
using Content.Server.Fluids.Components;
|
||||
using Content.Shared.Chemistry;
|
||||
using Content.Shared.Chemistry.Components;
|
||||
using Content.Shared.Chemistry.Reaction;
|
||||
using Content.Shared.Chemistry.Reagent;
|
||||
using Content.Shared.Chemistry.Solution;
|
||||
using JetBrains.Annotations;
|
||||
using Robust.Shared.Map;
|
||||
using Robust.Shared.Serialization.Manager.Attributes;
|
||||
|
||||
@@ -1,8 +1,7 @@
|
||||
using Content.Server.Fluids.Components;
|
||||
using Content.Shared.Chemistry;
|
||||
using Content.Shared.Chemistry.Components;
|
||||
using Content.Shared.Chemistry.Reaction;
|
||||
using Content.Shared.Chemistry.Reagent;
|
||||
using Content.Shared.Chemistry.Solution;
|
||||
using Content.Shared.Slippery;
|
||||
using JetBrains.Annotations;
|
||||
using Robust.Shared.Map;
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
using Content.Server.Chemistry.Components;
|
||||
using Content.Server.Fluids.Components;
|
||||
using Content.Shared.Chemistry.EntitySystems;
|
||||
using JetBrains.Annotations;
|
||||
using Robust.Shared.GameObjects;
|
||||
using Robust.Shared.Serialization.Manager.Attributes;
|
||||
@@ -12,10 +12,11 @@ namespace Content.Server.Destructible.Thresholds.Behaviors
|
||||
{
|
||||
public void Execute(IEntity owner, DestructibleSystem system)
|
||||
{
|
||||
if (!owner.TryGetComponent(out SolutionContainerComponent? solutionContainer))
|
||||
// TODO see if this is correct
|
||||
if (!EntitySystem.Get<SolutionContainerSystem>().TryGetSolution(owner, SpillableComponent.SolutionName, out var solution))
|
||||
return;
|
||||
|
||||
solutionContainer.Solution.SpillAt(owner.Transform.Coordinates, "PuddleSmear", false);
|
||||
solution.SpillAt(owner.Transform.Coordinates, "PuddleSmear", false);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,9 +1,8 @@
|
||||
using System.Threading.Tasks;
|
||||
using Content.Server.Chemistry.Components;
|
||||
using Content.Shared.Chemistry.EntitySystems;
|
||||
using Content.Shared.Chemistry.Reagent;
|
||||
using Content.Shared.Chemistry.Solution.Components;
|
||||
using Content.Shared.Interaction;
|
||||
using Content.Shared.Notification;
|
||||
using Content.Shared.Notification.Managers;
|
||||
using Content.Shared.Sound;
|
||||
using Robust.Shared.Audio;
|
||||
@@ -12,13 +11,13 @@ using Robust.Shared.Localization;
|
||||
using Robust.Shared.Player;
|
||||
using Robust.Shared.Serialization.Manager.Attributes;
|
||||
|
||||
|
||||
namespace Content.Server.Extinguisher
|
||||
{
|
||||
[RegisterComponent]
|
||||
public class FireExtinguisherComponent : Component, IAfterInteract
|
||||
{
|
||||
public override string Name => "FireExtinguisher";
|
||||
private const string SolutionName = "fireExtinguisher";
|
||||
|
||||
[DataField("refillSound")] SoundSpecifier _refillSound = new SoundPathSpecifier("/Audio/Effects/refill.ogg");
|
||||
|
||||
@@ -27,24 +26,26 @@ namespace Content.Server.Extinguisher
|
||||
|
||||
async Task<bool> IAfterInteract.AfterInteract(AfterInteractEventArgs eventArgs)
|
||||
{
|
||||
var solutionContainerSystem = EntitySystem.Get<SolutionContainerSystem>();
|
||||
if (eventArgs.Target == null || !eventArgs.CanReach)
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
if (eventArgs.Target.TryGetComponent(out ReagentTankComponent? tank)
|
||||
&& eventArgs.Target.TryGetComponent(out ISolutionInteractionsComponent? targetSolution)
|
||||
&& targetSolution.CanDrain
|
||||
&& Owner.TryGetComponent(out SolutionContainerComponent? container))
|
||||
var targetEntity = eventArgs.Target;
|
||||
if (eventArgs.Target.HasComponent<ReagentTankComponent>()
|
||||
&& solutionContainerSystem.TryGetDrainableSolution(targetEntity.Uid, out var targetSolution)
|
||||
&& solutionContainerSystem.TryGetSolution(Owner, SolutionName, out var container))
|
||||
{
|
||||
var trans = ReagentUnit.Min(container.EmptyVolume, targetSolution.DrainAvailable);
|
||||
if (trans > 0)
|
||||
var transfer = ReagentUnit.Min(container.AvailableVolume, targetSolution.DrainAvailable);
|
||||
if (transfer > 0)
|
||||
{
|
||||
var drained = targetSolution.Drain(trans);
|
||||
container.TryAddSolution(drained);
|
||||
var drained = solutionContainerSystem.Drain(targetEntity.Uid, targetSolution, transfer);
|
||||
solutionContainerSystem.TryAddSolution(Owner.Uid, container, drained);
|
||||
|
||||
SoundSystem.Play(Filter.Pvs(Owner), _refillSound.GetSound(), Owner);
|
||||
eventArgs.Target.PopupMessage(eventArgs.User, Loc.GetString("fire-extinguisher-component-after-interact-refilled-message",("owner", Owner)));
|
||||
eventArgs.Target.PopupMessage(eventArgs.User,
|
||||
Loc.GetString("fire-extinguisher-component-after-interact-refilled-message", ("owner", Owner)));
|
||||
}
|
||||
|
||||
return true;
|
||||
|
||||
@@ -1,11 +1,10 @@
|
||||
using System.Collections.Generic;
|
||||
using System.Threading.Tasks;
|
||||
using Content.Server.Chemistry.Components;
|
||||
using Content.Server.DoAfter;
|
||||
using Content.Shared.Chemistry.EntitySystems;
|
||||
using Content.Shared.Chemistry.Reagent;
|
||||
using Content.Shared.Interaction;
|
||||
using Content.Shared.Interaction.Helpers;
|
||||
using Content.Shared.Notification;
|
||||
using Content.Shared.Notification.Managers;
|
||||
using Content.Shared.Sound;
|
||||
using Robust.Shared.Audio;
|
||||
@@ -23,38 +22,37 @@ namespace Content.Server.Fluids.Components
|
||||
public class BucketComponent : Component, IInteractUsing
|
||||
{
|
||||
public override string Name => "Bucket";
|
||||
public const string SolutionName = "bucket";
|
||||
|
||||
private List<EntityUid> _currentlyUsing = new();
|
||||
|
||||
public ReagentUnit MaxVolume
|
||||
{
|
||||
get => Owner.TryGetComponent(out SolutionContainerComponent? solution) ? solution.MaxVolume : ReagentUnit.Zero;
|
||||
get =>
|
||||
EntitySystem.Get<SolutionContainerSystem>().TryGetSolution(Owner, SolutionName, out var solution)
|
||||
? solution.MaxVolume
|
||||
: ReagentUnit.Zero;
|
||||
set
|
||||
{
|
||||
if (Owner.TryGetComponent(out SolutionContainerComponent? solution))
|
||||
if (EntitySystem.Get<SolutionContainerSystem>().TryGetSolution(Owner, SolutionName, out var solution))
|
||||
{
|
||||
solution.MaxVolume = value;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public ReagentUnit CurrentVolume => Owner.TryGetComponent(out SolutionContainerComponent? solution)
|
||||
public ReagentUnit CurrentVolume => EntitySystem.Get<SolutionContainerSystem>().TryGetSolution(Owner, SolutionName, out var solution)
|
||||
? solution.CurrentVolume
|
||||
: ReagentUnit.Zero;
|
||||
|
||||
[DataField("sound")]
|
||||
private SoundSpecifier _sound = new SoundPathSpecifier("/Audio/Effects/Fluids/watersplash.ogg");
|
||||
|
||||
/// <inheritdoc />
|
||||
protected override void Initialize()
|
||||
{
|
||||
base.Initialize();
|
||||
Owner.EnsureComponentWarn<SolutionContainerComponent>();
|
||||
}
|
||||
|
||||
async Task<bool> IInteractUsing.InteractUsing(InteractUsingEventArgs eventArgs)
|
||||
{
|
||||
if (!Owner.TryGetComponent(out SolutionContainerComponent? contents) ||
|
||||
var solutionsSys = EntitySystem.Get<SolutionContainerSystem>();
|
||||
if (!solutionsSys.TryGetSolution(Owner, SolutionName, out var contents) ||
|
||||
_currentlyUsing.Contains(eventArgs.Using.Uid) ||
|
||||
!eventArgs.Using.TryGetComponent(out MopComponent? mopComponent) ||
|
||||
mopComponent.Mopping)
|
||||
@@ -101,15 +99,15 @@ namespace Content.Server.Fluids.Components
|
||||
return false;
|
||||
}
|
||||
|
||||
var mopContents = mopComponent.Contents;
|
||||
var mopContents = mopComponent.MopSolution;
|
||||
|
||||
if (mopContents == null)
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
var solution = contents.SplitSolution(transferAmount);
|
||||
if (!mopContents.TryAddSolution(solution))
|
||||
var solution = solutionsSys.SplitSolution(Owner.Uid, contents, transferAmount);
|
||||
if (!solutionsSys.TryAddSolution(mopComponent.Owner.Uid, mopContents, solution))
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
@@ -1,10 +1,10 @@
|
||||
using System.Threading.Tasks;
|
||||
using Content.Server.Chemistry.Components;
|
||||
using Content.Server.DoAfter;
|
||||
using Content.Shared.Chemistry.Components;
|
||||
using Content.Shared.Chemistry.EntitySystems;
|
||||
using Content.Shared.Chemistry.Reagent;
|
||||
using Content.Shared.Interaction;
|
||||
using Content.Shared.Interaction.Helpers;
|
||||
using Content.Shared.Notification;
|
||||
using Content.Shared.Notification.Managers;
|
||||
using Content.Shared.Sound;
|
||||
using Robust.Shared.Audio;
|
||||
@@ -23,28 +23,36 @@ namespace Content.Server.Fluids.Components
|
||||
public class MopComponent : Component, IAfterInteract
|
||||
{
|
||||
public override string Name => "Mop";
|
||||
public const string SolutionName = "mop";
|
||||
|
||||
/// <summary>
|
||||
/// Used to prevent do_after spam if we're currently mopping.
|
||||
/// </summary>
|
||||
public bool Mopping { get; private set; }
|
||||
|
||||
public SolutionContainerComponent? Contents => Owner.GetComponentOrNull<SolutionContainerComponent>();
|
||||
public Solution? MopSolution
|
||||
{
|
||||
get
|
||||
{
|
||||
EntitySystem.Get<SolutionContainerSystem>().TryGetSolution(Owner, SolutionName, out var solution);
|
||||
return solution;
|
||||
}
|
||||
}
|
||||
|
||||
public ReagentUnit MaxVolume
|
||||
{
|
||||
get => Owner.GetComponentOrNull<SolutionContainerComponent>()?.MaxVolume ?? ReagentUnit.Zero;
|
||||
get => MopSolution?.MaxVolume ?? ReagentUnit.Zero;
|
||||
set
|
||||
{
|
||||
if (Owner.TryGetComponent(out SolutionContainerComponent? solution))
|
||||
var solution = MopSolution;
|
||||
if (solution != null)
|
||||
{
|
||||
solution.MaxVolume = value;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public ReagentUnit CurrentVolume =>
|
||||
Owner.GetComponentOrNull<SolutionContainerComponent>()?.CurrentVolume ?? ReagentUnit.Zero;
|
||||
public ReagentUnit CurrentVolume => MopSolution?.CurrentVolume ?? ReagentUnit.Zero;
|
||||
|
||||
// Currently there's a separate amount for pickup and dropoff so
|
||||
// Picking up a puddle requires multiple clicks
|
||||
@@ -60,15 +68,7 @@ namespace Content.Server.Fluids.Components
|
||||
/// Multiplier for the do_after delay for how fast the mop works.
|
||||
/// </summary>
|
||||
[ViewVariables]
|
||||
[DataField("speed")]
|
||||
private float _mopSpeed = 1;
|
||||
|
||||
protected override void Initialize()
|
||||
{
|
||||
base.Initialize();
|
||||
|
||||
Owner.EnsureComponentWarn(out SolutionContainerComponent _);
|
||||
}
|
||||
[DataField("speed")] private float _mopSpeed = 1;
|
||||
|
||||
async Task<bool> IAfterInteract.AfterInteract(AfterInteractEventArgs eventArgs)
|
||||
{
|
||||
@@ -80,7 +80,7 @@ namespace Content.Server.Fluids.Components
|
||||
* will spill some of the mop's solution onto the puddle which will evaporate eventually.
|
||||
*/
|
||||
|
||||
if (!Owner.TryGetComponent(out SolutionContainerComponent? contents) ||
|
||||
if (!EntitySystem.Get<SolutionContainerSystem>().TryGetSolution(Owner, SolutionName, out var contents ) ||
|
||||
Mopping ||
|
||||
!eventArgs.InRangeUnobstructed(ignoreInsideBlocker: true, popup: true))
|
||||
{
|
||||
@@ -94,7 +94,8 @@ namespace Content.Server.Fluids.Components
|
||||
if (currentVolume > 0)
|
||||
{
|
||||
// Drop the liquid on the mop on to the ground
|
||||
contents.SplitSolution(CurrentVolume).SpillAt(eventArgs.ClickLocation, "PuddleSmear");
|
||||
EntitySystem.Get<SolutionContainerSystem>().SplitSolution(Owner.Uid, contents, CurrentVolume)
|
||||
.SpillAt(eventArgs.ClickLocation, "PuddleSmear");
|
||||
return true;
|
||||
}
|
||||
|
||||
@@ -117,7 +118,8 @@ namespace Content.Server.Fluids.Components
|
||||
Mopping = true;
|
||||
|
||||
// So if the puddle has 20 units we mop in 2 seconds. Don't just store CurrentVolume given it can change so need to re-calc it anyway.
|
||||
var doAfterArgs = new DoAfterEventArgs(eventArgs.User, _mopSpeed * puddleVolume.Float() / 10.0f, target: eventArgs.Target)
|
||||
var doAfterArgs = new DoAfterEventArgs(eventArgs.User, _mopSpeed * puddleVolume.Float() / 10.0f,
|
||||
target: eventArgs.Target)
|
||||
{
|
||||
BreakOnUserMove = true,
|
||||
BreakOnStun = true,
|
||||
@@ -138,7 +140,9 @@ namespace Content.Server.Fluids.Components
|
||||
|
||||
if (transferAmount == 0)
|
||||
{
|
||||
if (puddleComponent.EmptyHolder) //The puddle doesn't actually *have* reagents, for example vomit because there's no "vomit" reagent.
|
||||
if (
|
||||
puddleComponent
|
||||
.EmptyHolder) //The puddle doesn't actually *have* reagents, for example vomit because there's no "vomit" reagent.
|
||||
{
|
||||
puddleComponent.Owner.Delete();
|
||||
transferAmount = ReagentUnit.Min(ReagentUnit.New(5), CurrentVolume);
|
||||
@@ -154,13 +158,15 @@ namespace Content.Server.Fluids.Components
|
||||
puddleComponent.SplitSolution(transferAmount);
|
||||
}
|
||||
|
||||
if (puddleCleaned) //After cleaning the puddle, make a new puddle with solution from the mop as a "wet floor". Then evaporate it slowly.
|
||||
if (
|
||||
puddleCleaned) //After cleaning the puddle, make a new puddle with solution from the mop as a "wet floor". Then evaporate it slowly.
|
||||
{
|
||||
contents.SplitSolution(transferAmount).SpillAt(eventArgs.ClickLocation, "PuddleSmear");
|
||||
EntitySystem.Get<SolutionContainerSystem>().SplitSolution(Owner.Uid, contents, transferAmount)
|
||||
.SpillAt(eventArgs.ClickLocation, "PuddleSmear");
|
||||
}
|
||||
else
|
||||
{
|
||||
contents.SplitSolution(transferAmount);
|
||||
EntitySystem.Get<SolutionContainerSystem>().SplitSolution(Owner.Uid, contents, transferAmount);
|
||||
}
|
||||
|
||||
SoundSystem.Play(Filter.Pvs(Owner), _pickupSound.GetSound(), Owner);
|
||||
|
||||
@@ -3,9 +3,9 @@ using System.Collections.Generic;
|
||||
using System.Diagnostics.CodeAnalysis;
|
||||
using System.Linq;
|
||||
using System.Threading;
|
||||
using Content.Server.Chemistry.Components;
|
||||
using Content.Shared.Chemistry.Components;
|
||||
using Content.Shared.Chemistry.EntitySystems;
|
||||
using Content.Shared.Chemistry.Reagent;
|
||||
using Content.Shared.Chemistry.Solution;
|
||||
using Content.Shared.Directions;
|
||||
using Content.Shared.Examine;
|
||||
using Content.Shared.Maps;
|
||||
@@ -50,15 +50,22 @@ namespace Content.Server.Fluids.Components
|
||||
[Dependency] private readonly IRobustRandom _random = default!;
|
||||
public override string Name => "Puddle";
|
||||
|
||||
public const string DefaultSolutionName = "puddle";
|
||||
|
||||
private CancellationTokenSource? _evaporationToken;
|
||||
[DataField("evaporate_threshold")]
|
||||
private ReagentUnit _evaporateThreshold = ReagentUnit.New(20); // How few <Solution Quantity> we can hold prior to self-destructing
|
||||
|
||||
[DataField("evaporate_threshold")] private ReagentUnit
|
||||
_evaporateThreshold =
|
||||
ReagentUnit.New(20); // How few <Solution Quantity> we can hold prior to self-destructing
|
||||
|
||||
public ReagentUnit EvaporateThreshold
|
||||
{
|
||||
get => _evaporateThreshold;
|
||||
set => _evaporateThreshold = value;
|
||||
}
|
||||
|
||||
private ReagentUnit _slipThreshold = ReagentUnit.New(3);
|
||||
|
||||
public ReagentUnit SlipThreshold
|
||||
{
|
||||
get => _slipThreshold;
|
||||
@@ -83,40 +90,44 @@ namespace Content.Server.Fluids.Components
|
||||
|
||||
public ReagentUnit MaxVolume
|
||||
{
|
||||
get => _contents.MaxVolume;
|
||||
set => _contents.MaxVolume = value;
|
||||
get => PuddleSolution?.MaxVolume ?? ReagentUnit.Zero;
|
||||
set
|
||||
{
|
||||
if (PuddleSolution != null)
|
||||
{
|
||||
PuddleSolution.MaxVolume = value;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
[ViewVariables]
|
||||
public ReagentUnit CurrentVolume => _contents.CurrentVolume;
|
||||
[ViewVariables] public ReagentUnit CurrentVolume => PuddleSolution?.CurrentVolume ?? ReagentUnit.Zero;
|
||||
|
||||
// Volume at which the fluid will try to spill to adjacent components
|
||||
// Currently a random number, potentially change
|
||||
public ReagentUnit OverflowVolume => _overflowVolume;
|
||||
[ViewVariables]
|
||||
[DataField("overflow_volume")]
|
||||
|
||||
[ViewVariables] [DataField("overflow_volume")]
|
||||
private ReagentUnit _overflowVolume = ReagentUnit.New(20);
|
||||
|
||||
private ReagentUnit OverflowLeft => CurrentVolume - OverflowVolume;
|
||||
|
||||
private SolutionContainerComponent _contents = default!;
|
||||
public bool EmptyHolder => _contents.ReagentList.Count == 0;
|
||||
[DataField("variants")]
|
||||
private int _spriteVariants = 1;
|
||||
// Whether the underlying solution color should be used
|
||||
[DataField("recolor")]
|
||||
private bool _recolor = default;
|
||||
public bool EmptyHolder => PuddleSolution?.Contents.Count == 0;
|
||||
|
||||
[DataField("state")]
|
||||
private string _spriteState = "puddle";
|
||||
[DataField("variants")] private int _spriteVariants = 1;
|
||||
|
||||
// Whether the underlying solution color should be used
|
||||
[DataField("recolor")] private bool _recolor = default;
|
||||
|
||||
[DataField("state")] private string _spriteState = "puddle";
|
||||
|
||||
private bool Slippery => Owner.TryGetComponent(out SlipperyComponent? slippery) && slippery.Slippery;
|
||||
|
||||
private Solution? PuddleSolution => EntitySystem.Get<SolutionContainerSystem>().EnsureSolution(Owner, DefaultSolutionName);
|
||||
|
||||
protected override void Initialize()
|
||||
{
|
||||
base.Initialize();
|
||||
|
||||
_contents = Owner.EnsureComponentWarn<SolutionContainerComponent>();
|
||||
|
||||
// Smaller than 1m^3 for now but realistically this shouldn't be hit
|
||||
MaxVolume = ReagentUnit.New(1000);
|
||||
|
||||
@@ -160,13 +171,15 @@ namespace Content.Server.Fluids.Components
|
||||
}
|
||||
|
||||
// Flow rate should probably be controlled globally so this is it for now
|
||||
internal bool TryAddSolution(Solution solution, bool sound = true, bool checkForEvaporate = true, bool checkForOverflow = true)
|
||||
internal bool TryAddSolution(Solution solution, bool sound = true, bool checkForEvaporate = true,
|
||||
bool checkForOverflow = true)
|
||||
{
|
||||
if (solution.TotalVolume == 0)
|
||||
{
|
||||
return false;
|
||||
}
|
||||
var result = _contents.TryAddSolution(solution);
|
||||
|
||||
var result = EntitySystem.Get<SolutionContainerSystem>().TryAddSolution(Owner.Uid, PuddleSolution, solution);
|
||||
if (!result)
|
||||
{
|
||||
return false;
|
||||
@@ -194,12 +207,15 @@ namespace Content.Server.Fluids.Components
|
||||
return true;
|
||||
}
|
||||
|
||||
internal Solution SplitSolution(ReagentUnit quantity)
|
||||
internal void SplitSolution(ReagentUnit quantity)
|
||||
{
|
||||
var split = _contents.SplitSolution(quantity);
|
||||
if (PuddleSolution != null)
|
||||
{
|
||||
EntitySystem.Get<SolutionContainerSystem>().SplitSolution(Owner.Uid, PuddleSolution, quantity);
|
||||
CheckEvaporate();
|
||||
UpdateAppearance();
|
||||
return split;
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
public void CheckEvaporate()
|
||||
@@ -212,7 +228,12 @@ namespace Content.Server.Fluids.Components
|
||||
|
||||
public void Evaporate()
|
||||
{
|
||||
_contents.SplitSolution(ReagentUnit.Min(ReagentUnit.New(1), _contents.CurrentVolume));
|
||||
if (PuddleSolution != null)
|
||||
{
|
||||
EntitySystem.Get<SolutionContainerSystem>().SplitSolution(Owner.Uid, PuddleSolution,
|
||||
ReagentUnit.Min(ReagentUnit.New(1), PuddleSolution.CurrentVolume));
|
||||
}
|
||||
|
||||
if (CurrentVolume == 0)
|
||||
{
|
||||
Owner.Delete();
|
||||
@@ -262,15 +283,16 @@ namespace Content.Server.Fluids.Components
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
// Opacity based on level of fullness to overflow
|
||||
// Hard-cap lower bound for visibility reasons
|
||||
var volumeScale = (CurrentVolume.Float() / OverflowVolume.Float()) * 0.75f + 0.25f;
|
||||
var cappedScale = Math.Min(1.0f, volumeScale);
|
||||
// Color based on the underlying solutioncomponent
|
||||
Color newColor;
|
||||
if (_recolor)
|
||||
if (_recolor && PuddleSolution != null)
|
||||
{
|
||||
newColor = _contents.Color.WithAlpha(cappedScale);
|
||||
newColor = PuddleSolution.Color.WithAlpha(cappedScale);
|
||||
}
|
||||
else
|
||||
{
|
||||
@@ -287,10 +309,8 @@ namespace Content.Server.Fluids.Components
|
||||
/// </summary>
|
||||
private void CheckOverflow()
|
||||
{
|
||||
if (CurrentVolume <= _overflowVolume || _overflown)
|
||||
{
|
||||
if (PuddleSolution == null || CurrentVolume <= _overflowVolume || _overflown)
|
||||
return;
|
||||
}
|
||||
|
||||
var nextPuddles = new List<PuddleComponent>() { this };
|
||||
var overflownPuddles = new List<PuddleComponent>();
|
||||
@@ -321,7 +341,7 @@ namespace Content.Server.Fluids.Components
|
||||
{
|
||||
var adjacentPuddle = adjacent();
|
||||
var quantity = ReagentUnit.Min(overflowSplit, adjacentPuddle.OverflowVolume);
|
||||
var spillAmount = _contents.SplitSolution(quantity);
|
||||
var spillAmount = EntitySystem.Get<SolutionContainerSystem>().SplitSolution(Owner.Uid, PuddleSolution, quantity);
|
||||
|
||||
adjacentPuddle.TryAddSolution(spillAmount, false, false, false);
|
||||
nextPuddles.Add(adjacentPuddle);
|
||||
@@ -390,7 +410,9 @@ namespace Content.Server.Fluids.Components
|
||||
|
||||
if (puddle == default)
|
||||
{
|
||||
puddle = () => Owner.EntityManager.SpawnEntity(Owner.Prototype?.ID, mapGrid.DirectionToGrid(coords, direction)).GetComponent<PuddleComponent>();
|
||||
puddle = () =>
|
||||
Owner.EntityManager.SpawnEntity(Owner.Prototype?.ID, mapGrid.DirectionToGrid(coords, direction))
|
||||
.GetComponent<PuddleComponent>();
|
||||
}
|
||||
|
||||
return true;
|
||||
|
||||
@@ -1,9 +1,9 @@
|
||||
using System.Diagnostics.CodeAnalysis;
|
||||
using System.Linq;
|
||||
using Content.Server.Coordinates.Helpers;
|
||||
using Content.Shared.Chemistry.Components;
|
||||
using Content.Shared.Chemistry.EntitySystems;
|
||||
using Content.Shared.Chemistry.Reagent;
|
||||
using Content.Shared.Chemistry.Solution;
|
||||
using Content.Shared.Chemistry.Solution.Components;
|
||||
using Robust.Server.GameObjects;
|
||||
using Robust.Shared.GameObjects;
|
||||
using Robust.Shared.IoC;
|
||||
@@ -23,7 +23,8 @@ namespace Content.Server.Fluids.Components
|
||||
/// <param name="prototype">The prototype to use.</param>
|
||||
/// <param name="sound">Play the spill sound.</param>
|
||||
/// <returns>The puddle if one was created, null otherwise.</returns>
|
||||
public static PuddleComponent? SpillAt(this Solution solution, IEntity entity, string prototype, bool sound = true)
|
||||
public static PuddleComponent? SpillAt(this Solution solution, IEntity entity, string prototype,
|
||||
bool sound = true)
|
||||
{
|
||||
return solution.SpillAt(entity.Transform.Coordinates, prototype, sound);
|
||||
}
|
||||
@@ -39,7 +40,8 @@ namespace Content.Server.Fluids.Components
|
||||
/// <param name="puddle">The puddle if one was created, null otherwise.</param>
|
||||
/// <param name="sound">Play the spill sound.</param>
|
||||
/// <returns>True if a puddle was created, false otherwise.</returns>
|
||||
public static bool TrySpillAt(this Solution solution, IEntity entity, string prototype, [NotNullWhen(true)] out PuddleComponent? puddle, bool sound = true)
|
||||
public static bool TrySpillAt(this Solution solution, IEntity entity, string prototype,
|
||||
[NotNullWhen(true)] out PuddleComponent? puddle, bool sound = true)
|
||||
{
|
||||
puddle = solution.SpillAt(entity, prototype, sound);
|
||||
return puddle != null;
|
||||
@@ -53,14 +55,16 @@ namespace Content.Server.Fluids.Components
|
||||
/// <param name="prototype">The prototype to use.</param>
|
||||
/// <param name="sound">Whether or not to play the spill sound.</param>
|
||||
/// <returns>The puddle if one was created, null otherwise.</returns>
|
||||
public static PuddleComponent? SpillAt(this Solution solution, EntityCoordinates coordinates, string prototype, bool overflow = true, bool sound = true)
|
||||
public static PuddleComponent? SpillAt(this Solution solution, EntityCoordinates coordinates, string prototype,
|
||||
bool overflow = true, bool sound = true)
|
||||
{
|
||||
if (solution.TotalVolume == 0) return null;
|
||||
|
||||
var mapManager = IoCManager.Resolve<IMapManager>();
|
||||
var entityManager = IoCManager.Resolve<IEntityManager>();
|
||||
|
||||
if (!mapManager.TryGetGrid(coordinates.GetGridId(entityManager), out var mapGrid)) return null; // Let's not spill to space.
|
||||
if (!mapManager.TryGetGrid(coordinates.GetGridId(entityManager), out var mapGrid))
|
||||
return null; // Let's not spill to space.
|
||||
|
||||
return SpillAt(mapGrid.GetTileRef(coordinates), solution, prototype, overflow, sound);
|
||||
}
|
||||
@@ -74,13 +78,15 @@ namespace Content.Server.Fluids.Components
|
||||
/// <param name="puddle">The puddle if one was created, null otherwise.</param>
|
||||
/// <param name="sound">Play the spill sound.</param>
|
||||
/// <returns>True if a puddle was created, false otherwise.</returns>
|
||||
public static bool TrySpillAt(this Solution solution, EntityCoordinates coordinates, string prototype, [NotNullWhen(true)] out PuddleComponent? puddle, bool sound = true)
|
||||
public static bool TrySpillAt(this Solution solution, EntityCoordinates coordinates, string prototype,
|
||||
[NotNullWhen(true)] out PuddleComponent? puddle, bool sound = true)
|
||||
{
|
||||
puddle = solution.SpillAt(coordinates, prototype, sound);
|
||||
return puddle != null;
|
||||
}
|
||||
|
||||
public static bool TryGetPuddle(this TileRef tileRef, GridTileLookupSystem? gridTileLookupSystem, [NotNullWhen(true)] out PuddleComponent? puddle)
|
||||
public static bool TryGetPuddle(this TileRef tileRef, GridTileLookupSystem? gridTileLookupSystem,
|
||||
[NotNullWhen(true)] out PuddleComponent? puddle)
|
||||
{
|
||||
foreach (var entity in tileRef.GetEntitiesInTileFast(gridTileLookupSystem))
|
||||
{
|
||||
@@ -95,7 +101,8 @@ namespace Content.Server.Fluids.Components
|
||||
return false;
|
||||
}
|
||||
|
||||
public static PuddleComponent? SpillAt(this TileRef tileRef, Solution solution, string prototype, bool overflow = true, bool sound = true)
|
||||
public static PuddleComponent? SpillAt(this TileRef tileRef, Solution solution, string prototype,
|
||||
bool overflow = true, bool sound = true)
|
||||
{
|
||||
if (solution.TotalVolume <= 0) return null;
|
||||
|
||||
@@ -116,14 +123,17 @@ namespace Content.Server.Fluids.Components
|
||||
PuddleComponent? puddle = null;
|
||||
var spilt = false;
|
||||
|
||||
var spillEntities = IoCManager.Resolve<IEntityLookup>().GetEntitiesIntersecting(mapGrid.ParentMapId, spillGridCoords.Position).ToArray();
|
||||
var spillEntities = IoCManager.Resolve<IEntityLookup>()
|
||||
.GetEntitiesIntersecting(mapGrid.ParentMapId, spillGridCoords.Position).ToArray();
|
||||
foreach (var spillEntity in spillEntities)
|
||||
{
|
||||
if (spillEntity.TryGetComponent(out ISolutionInteractionsComponent? solutionContainerComponent) &&
|
||||
solutionContainerComponent.CanRefill)
|
||||
if (EntitySystem.Get<SolutionContainerSystem>()
|
||||
.TryGetRefillableSolution(spillEntity.Uid, out var solutionContainerComponent))
|
||||
{
|
||||
solutionContainerComponent.Refill(
|
||||
solution.SplitSolution(ReagentUnit.Min(solutionContainerComponent.RefillSpaceAvailable, solutionContainerComponent.MaxSpillRefill))
|
||||
EntitySystem.Get<SolutionContainerSystem>().Refill(spillEntity.Uid, solutionContainerComponent,
|
||||
solution.SplitSolution(ReagentUnit.Min(
|
||||
solutionContainerComponent.AvailableVolume,
|
||||
solutionContainerComponent.MaxSpillRefill))
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,9 +1,8 @@
|
||||
using Content.Shared.ActionBlocker;
|
||||
using Content.Shared.Chemistry.Components.SolutionManager;
|
||||
using Content.Shared.Chemistry.EntitySystems;
|
||||
using Content.Shared.Chemistry.Reagent;
|
||||
using Content.Shared.Chemistry.Solution.Components;
|
||||
using Content.Shared.DragDrop;
|
||||
using Content.Shared.Interaction;
|
||||
using Content.Shared.Interaction.Events;
|
||||
using Content.Shared.Notification.Managers;
|
||||
using Content.Shared.Verbs;
|
||||
using Robust.Shared.GameObjects;
|
||||
@@ -15,6 +14,7 @@ namespace Content.Server.Fluids.Components
|
||||
public class SpillableComponent : Component, IDropped
|
||||
{
|
||||
public override string Name => "Spillable";
|
||||
public const string SolutionName = "puddle";
|
||||
|
||||
/// <summary>
|
||||
/// Transfers solution from the held container to the floor.
|
||||
@@ -25,8 +25,8 @@ namespace Content.Server.Fluids.Components
|
||||
protected override void GetData(IEntity user, SpillableComponent component, VerbData data)
|
||||
{
|
||||
if (!EntitySystem.Get<ActionBlockerSystem>().CanInteract(user) ||
|
||||
!component.Owner.TryGetComponent(out ISolutionInteractionsComponent? solutionComponent) ||
|
||||
!solutionComponent.CanDrain)
|
||||
!EntitySystem.Get<SolutionContainerSystem>()
|
||||
.TryGetDrainableSolution(component.Owner.Uid, out var solutionComponent))
|
||||
{
|
||||
data.Visibility = VerbVisibility.Invisible;
|
||||
return;
|
||||
@@ -40,30 +40,40 @@ namespace Content.Server.Fluids.Components
|
||||
|
||||
protected override void Activate(IEntity user, SpillableComponent component)
|
||||
{
|
||||
if (component.Owner.TryGetComponent<ISolutionInteractionsComponent>(out var solutionComponent))
|
||||
var solutionsSys = EntitySystem.Get<SolutionContainerSystem>();
|
||||
if (component.Owner.HasComponent<SolutionContainerManagerComponent>())
|
||||
{
|
||||
if (!solutionComponent.CanDrain)
|
||||
if (solutionsSys.TryGetDrainableSolution(component.Owner.Uid, out var solutionComponent))
|
||||
{
|
||||
user.PopupMessage(user,
|
||||
Loc.GetString("spill-target-verb-activate-cannot-drain-message",("owner", component.Owner)));
|
||||
}
|
||||
|
||||
if (solutionComponent.DrainAvailable <= 0)
|
||||
{
|
||||
user.PopupMessage(user, Loc.GetString("spill-target-verb-activate-is-empty-message",("owner", component.Owner)));
|
||||
user.PopupMessage(user,
|
||||
Loc.GetString("spill-target-verb-activate-is-empty-message", ("owner", component.Owner)));
|
||||
}
|
||||
|
||||
// Need this as when we split the component's owner may be deleted
|
||||
solutionComponent.Drain(solutionComponent.DrainAvailable).SpillAt(component.Owner.Transform.Coordinates, "PuddleSmear");
|
||||
EntitySystem.Get<SolutionContainerSystem>()
|
||||
.Drain(component.Owner.Uid, solutionComponent, solutionComponent.DrainAvailable)
|
||||
.SpillAt(component.Owner.Transform.Coordinates, "PuddleSmear");
|
||||
}
|
||||
else
|
||||
{
|
||||
user.PopupMessage(user,
|
||||
Loc.GetString("spill-target-verb-activate-cannot-drain-message",
|
||||
("owner", component.Owner)));
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void IDropped.Dropped(DroppedEventArgs eventArgs)
|
||||
{
|
||||
if (!eventArgs.Intentional && Owner.TryGetComponent(out ISolutionInteractionsComponent? solutionComponent))
|
||||
if (!eventArgs.Intentional
|
||||
&& EntitySystem.Get<SolutionContainerSystem>().TryGetSolution(Owner, SolutionName, out var solutionComponent))
|
||||
{
|
||||
solutionComponent.Drain(solutionComponent.DrainAvailable).SpillAt(Owner.Transform.Coordinates, "PuddleSmear");
|
||||
EntitySystem.Get<SolutionContainerSystem>()
|
||||
.Drain(Owner.Uid, solutionComponent, solutionComponent.DrainAvailable)
|
||||
.SpillAt(Owner.Transform.Coordinates, "PuddleSmear");
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -4,9 +4,9 @@ using Content.Server.Chemistry.Components;
|
||||
using Content.Server.Chemistry.EntitySystems;
|
||||
using Content.Shared.ActionBlocker;
|
||||
using Content.Shared.Audio;
|
||||
using Content.Shared.Chemistry.EntitySystems;
|
||||
using Content.Shared.Chemistry.Reagent;
|
||||
using Content.Shared.Cooldown;
|
||||
using Content.Shared.DragDrop;
|
||||
using Content.Shared.Fluids;
|
||||
using Content.Shared.Interaction;
|
||||
using Content.Shared.Notification.Managers;
|
||||
@@ -30,6 +30,7 @@ namespace Content.Server.Fluids.Components
|
||||
internal sealed class SprayComponent : SharedSprayComponent, IAfterInteract, IUse, IActivate, IDropped
|
||||
{
|
||||
public const float SprayDistance = 3f;
|
||||
public const string SolutionName = "spray";
|
||||
|
||||
[Dependency] private readonly IGameTiming _gameTiming = default!;
|
||||
|
||||
@@ -82,14 +83,19 @@ namespace Content.Server.Fluids.Components
|
||||
[DataField("safetySound")]
|
||||
public SoundSpecifier SafetySound { get; } = new SoundPathSpecifier("/Audio/Machines/button.ogg");
|
||||
|
||||
public ReagentUnit CurrentVolume => Owner.GetComponentOrNull<SolutionContainerComponent>()?.CurrentVolume ?? ReagentUnit.Zero;
|
||||
|
||||
public ReagentUnit CurrentVolume {
|
||||
get
|
||||
{
|
||||
EntitySystem.Get<SolutionContainerSystem>().TryGetSolution(Owner, SolutionName, out var solution);
|
||||
return solution?.CurrentVolume ?? ReagentUnit.Zero;
|
||||
}
|
||||
}
|
||||
|
||||
protected override void Initialize()
|
||||
{
|
||||
base.Initialize();
|
||||
|
||||
Owner.EnsureComponentWarn(out SolutionContainerComponent _);
|
||||
|
||||
if (_hasSafety)
|
||||
{
|
||||
SetSafety(Owner, _safety);
|
||||
@@ -124,7 +130,7 @@ namespace Content.Server.Fluids.Components
|
||||
if (eventArgs.ClickLocation.GetGridId(entManager) != playerPos.GetGridId(entManager))
|
||||
return true;
|
||||
|
||||
if (!Owner.TryGetComponent(out SolutionContainerComponent? contents))
|
||||
if (!EntitySystem.Get<SolutionContainerSystem>().TryGetSolution(Owner, SolutionName, out var contents))
|
||||
return true;
|
||||
|
||||
var direction = (eventArgs.ClickLocation.Position - playerPos.Position).Normalized;
|
||||
@@ -148,7 +154,7 @@ namespace Content.Server.Fluids.Components
|
||||
if (target.TryDistance(Owner.EntityManager, playerPos, out var distance) && distance > SprayDistance)
|
||||
target = eventArgs.User.Transform.Coordinates.Offset(diffNorm * SprayDistance);
|
||||
|
||||
var solution = contents.SplitSolution(_transferAmount);
|
||||
var solution = EntitySystem.Get<SolutionContainerSystem>().SplitSolution(Owner.Uid, contents, _transferAmount);
|
||||
|
||||
if (solution.TotalVolume <= ReagentUnit.Zero)
|
||||
break;
|
||||
|
||||
27
Content.Server/Kitchen/Components/ExtractableComponent.cs
Normal file
27
Content.Server/Kitchen/Components/ExtractableComponent.cs
Normal file
@@ -0,0 +1,27 @@
|
||||
using Content.Server.Kitchen.EntitySystems;
|
||||
using Content.Shared.Chemistry.Components;
|
||||
using Robust.Shared.Analyzers;
|
||||
using Robust.Shared.GameObjects;
|
||||
using Robust.Shared.Serialization.Manager.Attributes;
|
||||
using Robust.Shared.ViewVariables;
|
||||
|
||||
namespace Content.Server.Kitchen.Components
|
||||
{
|
||||
/// <summary>
|
||||
/// Tag component that denotes an entity as Extractable
|
||||
/// </summary>
|
||||
[RegisterComponent]
|
||||
[Friend(typeof(ReagentGrinderSystem))]
|
||||
public class ExtractableComponent : Component
|
||||
{
|
||||
public override string Name => "Extractable";
|
||||
|
||||
[ViewVariables]
|
||||
[DataField("result")]
|
||||
public Solution ResultSolution = new();
|
||||
|
||||
[ViewVariables]
|
||||
[DataField("extractableSolution")]
|
||||
public string? GrindableSolution;
|
||||
}
|
||||
}
|
||||
@@ -1,17 +0,0 @@
|
||||
using Content.Shared.Chemistry.Solution;
|
||||
using Robust.Shared.GameObjects;
|
||||
using Robust.Shared.Serialization.Manager.Attributes;
|
||||
using Robust.Shared.ViewVariables;
|
||||
|
||||
namespace Content.Server.Kitchen.Components
|
||||
{
|
||||
/// <summary>
|
||||
/// Tag component that denotes an entity as Juiceable
|
||||
/// </summary>
|
||||
[RegisterComponent]
|
||||
public class JuiceableComponent : Component
|
||||
{
|
||||
public override string Name => "Juiceable";
|
||||
[ViewVariables] [DataField("result")] public Solution JuiceResultSolution = new();
|
||||
}
|
||||
}
|
||||
@@ -1,4 +1,3 @@
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
using System.Threading.Tasks;
|
||||
@@ -13,14 +12,12 @@ using Content.Server.UserInterface;
|
||||
using Content.Shared.Acts;
|
||||
using Content.Shared.Body.Components;
|
||||
using Content.Shared.Body.Part;
|
||||
using Content.Shared.Chemistry;
|
||||
using Content.Shared.Chemistry.Components;
|
||||
using Content.Shared.Chemistry.EntitySystems;
|
||||
using Content.Shared.Chemistry.Reagent;
|
||||
using Content.Shared.Chemistry.Solution;
|
||||
using Content.Shared.Chemistry.Solution.Components;
|
||||
using Content.Shared.Interaction;
|
||||
using Content.Shared.Kitchen;
|
||||
using Content.Shared.Kitchen.Components;
|
||||
using Content.Shared.Notification;
|
||||
using Content.Shared.Notification.Managers;
|
||||
using Content.Shared.Power;
|
||||
using Content.Shared.Sound;
|
||||
@@ -38,27 +35,28 @@ namespace Content.Server.Kitchen.Components
|
||||
{
|
||||
[RegisterComponent]
|
||||
[ComponentReference(typeof(IActivate))]
|
||||
public class MicrowaveComponent : SharedMicrowaveComponent, IActivate, IInteractUsing, ISolutionChange, ISuicideAct, IBreakAct
|
||||
public class MicrowaveComponent : SharedMicrowaveComponent, IActivate, IInteractUsing, ISuicideAct, IBreakAct
|
||||
{
|
||||
[Dependency] private readonly RecipeManager _recipeManager = default!;
|
||||
|
||||
#region YAMLSERIALIZE
|
||||
[DataField("cookTime")]
|
||||
private uint _cookTimeDefault = 5;
|
||||
[DataField("cookTimeMultiplier")]
|
||||
private int _cookTimeMultiplier = 1000; //For upgrades and stuff I guess?
|
||||
[DataField("failureResult")]
|
||||
private string _badRecipeName = "FoodBadRecipe";
|
||||
[DataField("beginCookingSound")]
|
||||
private SoundSpecifier _startCookingSound = new SoundPathSpecifier("/Audio/Machines/microwave_start_beep.ogg");
|
||||
[DataField("foodDoneSound")]
|
||||
private SoundSpecifier _cookingCompleteSound = new SoundPathSpecifier("/Audio/Machines/microwave_done_beep.ogg");
|
||||
|
||||
[DataField("cookTime")] private uint _cookTimeDefault = 5;
|
||||
[DataField("cookTimeMultiplier")] private int _cookTimeMultiplier = 1000; //For upgrades and stuff I guess?
|
||||
[DataField("failureResult")] private string _badRecipeName = "FoodBadRecipe";
|
||||
|
||||
[DataField("beginCookingSound")] private SoundSpecifier _startCookingSound =
|
||||
new SoundPathSpecifier("/Audio/Machines/microwave_start_beep.ogg");
|
||||
|
||||
[DataField("foodDoneSound")] private SoundSpecifier _cookingCompleteSound =
|
||||
new SoundPathSpecifier("/Audio/Machines/microwave_done_beep.ogg");
|
||||
|
||||
[DataField("clickSound")]
|
||||
private SoundSpecifier _clickSound = new SoundPathSpecifier("/Audio/Machines/machine_switch.ogg");
|
||||
|
||||
#endregion YAMLSERIALIZE
|
||||
|
||||
[ViewVariables]
|
||||
private bool _busy = false;
|
||||
[ViewVariables] private bool _busy = false;
|
||||
private bool _broken;
|
||||
|
||||
/// <summary>
|
||||
@@ -66,18 +64,25 @@ namespace Content.Server.Kitchen.Components
|
||||
/// The cook times for all recipes should be divisible by 5,with a minimum of 1 second.
|
||||
/// For right now, I don't think any recipe cook time should be greater than 60 seconds.
|
||||
/// </summary>
|
||||
[ViewVariables]
|
||||
private uint _currentCookTimerTime = 1;
|
||||
[ViewVariables] private uint _currentCookTimerTime = 1;
|
||||
|
||||
private bool Powered => !Owner.TryGetComponent(out ApcPowerReceiverComponent? receiver) || receiver.Powered;
|
||||
private bool _hasContents => Owner.TryGetComponent(out SolutionContainerComponent? solution) && (solution.ReagentList.Count > 0 || _storage.ContainedEntities.Count > 0);
|
||||
private bool _uiDirty = true;
|
||||
private bool _lostPower = false;
|
||||
private int _currentCookTimeButtonIndex = 0;
|
||||
|
||||
void ISolutionChange.SolutionChanged(SolutionChangeEventArgs eventArgs) => _uiDirty = true;
|
||||
private AudioSystem _audioSystem = default!;
|
||||
private bool HasContents => EntitySystem.Get<SolutionContainerSystem>()
|
||||
.TryGetSolution(Owner, SolutionName, out var solution) &&
|
||||
(solution.Contents.Count > 0 || _storage.ContainedEntities.Count > 0);
|
||||
|
||||
private bool _uiDirty = true;
|
||||
private bool _lostPower;
|
||||
private int _currentCookTimeButtonIndex;
|
||||
|
||||
public void DirtyUi()
|
||||
{
|
||||
_uiDirty = true;
|
||||
}
|
||||
|
||||
private Container _storage = default!;
|
||||
private const string SolutionName = "microwave";
|
||||
|
||||
[ViewVariables] private BoundUserInterface? UserInterface => Owner.GetUIOrNull(MicrowaveUiKey.Key);
|
||||
|
||||
@@ -87,10 +92,10 @@ namespace Content.Server.Kitchen.Components
|
||||
|
||||
_currentCookTimerTime = _cookTimeDefault;
|
||||
|
||||
Owner.EnsureComponent<SolutionContainerComponent>();
|
||||
EntitySystem.Get<SolutionContainerSystem>().EnsureSolution(Owner, SolutionName);
|
||||
|
||||
_storage = ContainerHelpers.EnsureContainer<Container>(Owner, "microwave_entity_container", out var existed);
|
||||
_audioSystem = EntitySystem.Get<AudioSystem>();
|
||||
_storage = ContainerHelpers.EnsureContainer<Container>(Owner, "microwave_entity_container",
|
||||
out _);
|
||||
|
||||
if (UserInterface != null)
|
||||
{
|
||||
@@ -107,33 +112,36 @@ namespace Content.Server.Kitchen.Components
|
||||
|
||||
switch (message.Message)
|
||||
{
|
||||
case MicrowaveStartCookMessage msg:
|
||||
case MicrowaveStartCookMessage:
|
||||
Wzhzhzh();
|
||||
break;
|
||||
case MicrowaveEjectMessage msg:
|
||||
if (_hasContents)
|
||||
case MicrowaveEjectMessage:
|
||||
if (HasContents)
|
||||
{
|
||||
VaporizeReagents();
|
||||
EjectSolids();
|
||||
ClickSound();
|
||||
_uiDirty = true;
|
||||
}
|
||||
|
||||
break;
|
||||
case MicrowaveEjectSolidIndexedMessage msg:
|
||||
if (_hasContents)
|
||||
if (HasContents)
|
||||
{
|
||||
EjectSolid(msg.EntityID);
|
||||
ClickSound();
|
||||
_uiDirty = true;
|
||||
}
|
||||
|
||||
break;
|
||||
case MicrowaveVaporizeReagentIndexedMessage msg:
|
||||
if (_hasContents)
|
||||
if (HasContents)
|
||||
{
|
||||
VaporizeReagentQuantity(msg.ReagentQuantity);
|
||||
ClickSound();
|
||||
_uiDirty = true;
|
||||
}
|
||||
|
||||
break;
|
||||
case MicrowaveSelectCookTimeMessage msg:
|
||||
_currentCookTimeButtonIndex = msg.ButtonIndex;
|
||||
@@ -142,12 +150,10 @@ namespace Content.Server.Kitchen.Components
|
||||
_uiDirty = true;
|
||||
break;
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
public void OnUpdate()
|
||||
{
|
||||
|
||||
if (!Powered)
|
||||
{
|
||||
//TODO:If someone cuts power currently, microwave magically keeps going. FIX IT!
|
||||
@@ -175,11 +181,12 @@ namespace Content.Server.Kitchen.Components
|
||||
_uiDirty = true;
|
||||
}
|
||||
|
||||
if (_uiDirty && Owner.TryGetComponent(out SolutionContainerComponent? solution))
|
||||
if (_uiDirty && EntitySystem.Get<SolutionContainerSystem>()
|
||||
.TryGetSolution(Owner, SolutionName, out var solution))
|
||||
{
|
||||
UserInterface?.SetState(new MicrowaveUpdateUserInterfaceState
|
||||
(
|
||||
solution.Solution.Contents.ToArray(),
|
||||
solution.Contents.ToArray(),
|
||||
_storage.ContainedEntities.Select(item => item.Uid).ToArray(),
|
||||
_busy,
|
||||
_currentCookTimeButtonIndex,
|
||||
@@ -244,28 +251,30 @@ namespace Content.Server.Kitchen.Components
|
||||
|
||||
if (itemEntity.TryGetComponent<SolutionTransferComponent>(out var attackPourable))
|
||||
{
|
||||
if (!itemEntity.TryGetComponent<ISolutionInteractionsComponent>(out var attackSolution)
|
||||
|| !attackSolution.CanDrain)
|
||||
var solutionsSystem = EntitySystem.Get<SolutionContainerSystem>();
|
||||
if (!solutionsSystem.TryGetDrainableSolution(itemEntity.Uid, out var attackSolution))
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
if (!Owner.TryGetComponent(out SolutionContainerComponent? solution))
|
||||
if (!solutionsSystem.TryGetSolution(Owner, SolutionName, out var solution))
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
//Get transfer amount. May be smaller than _transferAmount if not enough room
|
||||
var realTransferAmount = ReagentUnit.Min(attackPourable.TransferAmount, solution.EmptyVolume);
|
||||
var realTransferAmount = ReagentUnit.Min(attackPourable.TransferAmount, solution.AvailableVolume);
|
||||
if (realTransferAmount <= 0) //Special message if container is full
|
||||
{
|
||||
Owner.PopupMessage(eventArgs.User, Loc.GetString("microwave-component-interact-using-container-full"));
|
||||
Owner.PopupMessage(eventArgs.User,
|
||||
Loc.GetString("microwave-component-interact-using-container-full"));
|
||||
return false;
|
||||
}
|
||||
|
||||
//Move units from attackSolution to targetSolution
|
||||
var removedSolution = attackSolution.Drain(realTransferAmount);
|
||||
if (!solution.TryAddSolution(removedSolution))
|
||||
var removedSolution = EntitySystem.Get<SolutionContainerSystem>()
|
||||
.Drain(itemEntity.Uid, attackSolution, realTransferAmount);
|
||||
if (!EntitySystem.Get<SolutionContainerSystem>().TryAddSolution(Owner.Uid, solution, removedSolution))
|
||||
{
|
||||
return false;
|
||||
}
|
||||
@@ -277,7 +286,6 @@ namespace Content.Server.Kitchen.Components
|
||||
|
||||
if (!itemEntity.TryGetComponent(typeof(ItemComponent), out var food))
|
||||
{
|
||||
|
||||
Owner.PopupMessage(eventArgs.User, "microwave-component-interact-using-transfer-fail");
|
||||
return false;
|
||||
}
|
||||
@@ -292,7 +300,7 @@ namespace Content.Server.Kitchen.Components
|
||||
// ReSharper disable once IdentifierTypo
|
||||
private void Wzhzhzh()
|
||||
{
|
||||
if (!_hasContents)
|
||||
if (!HasContents)
|
||||
{
|
||||
return;
|
||||
}
|
||||
@@ -331,14 +339,15 @@ namespace Content.Server.Kitchen.Components
|
||||
|
||||
// Check recipes
|
||||
FoodRecipePrototype? recipeToCook = null;
|
||||
foreach (var r in _recipeManager.Recipes.Where(r => CanSatisfyRecipe(r, solidsDict) == MicrowaveSuccessState.RecipePass))
|
||||
foreach (var r in _recipeManager.Recipes.Where(r =>
|
||||
CanSatisfyRecipe(r, solidsDict) == MicrowaveSuccessState.RecipePass))
|
||||
{
|
||||
recipeToCook = r;
|
||||
}
|
||||
|
||||
SetAppearance(MicrowaveVisualState.Cooking);
|
||||
SoundSystem.Play(Filter.Pvs(Owner), _startCookingSound.GetSound(), Owner, AudioParams.Default);
|
||||
Owner.SpawnTimer((int) (_currentCookTimerTime * _cookTimeMultiplier), (Action) (() =>
|
||||
Owner.SpawnTimer((int) (_currentCookTimerTime * _cookTimeMultiplier), () =>
|
||||
{
|
||||
if (_lostPower)
|
||||
{
|
||||
@@ -372,24 +381,25 @@ namespace Content.Server.Kitchen.Components
|
||||
_busy = false;
|
||||
|
||||
_uiDirty = true;
|
||||
}));
|
||||
});
|
||||
_lostPower = false;
|
||||
_uiDirty = true;
|
||||
}
|
||||
|
||||
private void VaporizeReagents()
|
||||
{
|
||||
if (Owner.TryGetComponent(out SolutionContainerComponent? solution))
|
||||
if (EntitySystem.Get<SolutionContainerSystem>().TryGetSolution(Owner, SolutionName, out var solution))
|
||||
{
|
||||
solution.RemoveAllSolution();
|
||||
EntitySystem.Get<SolutionContainerSystem>().RemoveAllSolution(Owner.Uid, solution);
|
||||
}
|
||||
}
|
||||
|
||||
private void VaporizeReagentQuantity(Solution.ReagentQuantity reagentQuantity)
|
||||
{
|
||||
if (Owner.TryGetComponent(out SolutionContainerComponent? solution))
|
||||
if (EntitySystem.Get<SolutionContainerSystem>().TryGetSolution(Owner, SolutionName, out var solution))
|
||||
{
|
||||
solution?.TryRemoveReagent(reagentQuantity.ReagentId, reagentQuantity.Quantity);
|
||||
EntitySystem.Get<SolutionContainerSystem>()
|
||||
.TryRemoveReagent(Owner.Uid, solution, reagentQuantity.ReagentId, reagentQuantity.Quantity);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -405,32 +415,32 @@ namespace Content.Server.Kitchen.Components
|
||||
|
||||
private void EjectSolids()
|
||||
{
|
||||
|
||||
for (var i = _storage.ContainedEntities.Count - 1; i >= 0; i--)
|
||||
{
|
||||
_storage.Remove(_storage.ContainedEntities.ElementAt(i));
|
||||
}
|
||||
}
|
||||
|
||||
private void EjectSolid(EntityUid entityID)
|
||||
private void EjectSolid(EntityUid entityId)
|
||||
{
|
||||
if (Owner.EntityManager.EntityExists(entityID))
|
||||
if (Owner.EntityManager.EntityExists(entityId))
|
||||
{
|
||||
_storage.Remove(Owner.EntityManager.GetEntity(entityID));
|
||||
_storage.Remove(Owner.EntityManager.GetEntity(entityId));
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
private void SubtractContents(FoodRecipePrototype recipe)
|
||||
{
|
||||
if (!Owner.TryGetComponent(out SolutionContainerComponent? solution))
|
||||
var solutionUid = Owner.Uid;
|
||||
if (!EntitySystem.Get<SolutionContainerSystem>().TryGetSolution(Owner, SolutionName, out var solution))
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
foreach (var recipeReagent in recipe.IngredientsReagents)
|
||||
{
|
||||
solution?.TryRemoveReagent(recipeReagent.Key, ReagentUnit.New(recipeReagent.Value));
|
||||
EntitySystem.Get<SolutionContainerSystem>()
|
||||
.TryRemoveReagent(solutionUid, solution, recipeReagent.Key, ReagentUnit.New(recipeReagent.Value));
|
||||
}
|
||||
|
||||
foreach (var recipeSolid in recipe.IngredientsSolids)
|
||||
@@ -453,7 +463,6 @@ namespace Content.Server.Kitchen.Components
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
private MicrowaveSuccessState CanSatisfyRecipe(FoodRecipePrototype recipe, Dictionary<string, int> solids)
|
||||
@@ -463,14 +472,14 @@ namespace Content.Server.Kitchen.Components
|
||||
return MicrowaveSuccessState.RecipeFail;
|
||||
}
|
||||
|
||||
if (!Owner.TryGetComponent(out SolutionContainerComponent? solution))
|
||||
if (!EntitySystem.Get<SolutionContainerSystem>().TryGetSolution(Owner, SolutionName, out var solution))
|
||||
{
|
||||
return MicrowaveSuccessState.RecipeFail;
|
||||
}
|
||||
|
||||
foreach (var reagent in recipe.IngredientsReagents)
|
||||
{
|
||||
if (!solution.Solution.ContainsReagent(reagent.Key, out var amount))
|
||||
if (!solution.ContainsReagent(reagent.Key, out var amount))
|
||||
{
|
||||
return MicrowaveSuccessState.RecipeFail;
|
||||
}
|
||||
|
||||
@@ -1,5 +1,4 @@
|
||||
using Content.Server.Chemistry.Components;
|
||||
using Content.Shared.Interaction;
|
||||
using Content.Shared.Chemistry.Components;
|
||||
using Content.Shared.Kitchen.Components;
|
||||
using Content.Shared.Sound;
|
||||
using Robust.Shared.Containers;
|
||||
@@ -9,7 +8,6 @@ using Robust.Shared.ViewVariables;
|
||||
|
||||
namespace Content.Server.Kitchen.Components
|
||||
{
|
||||
|
||||
/// <summary>
|
||||
/// The combo reagent grinder/juicer. The reason why grinding and juicing are seperate is simple,
|
||||
/// think of grinding as a utility to break an object down into its reagents. Think of juicing as
|
||||
@@ -24,7 +22,7 @@ namespace Content.Server.Kitchen.Components
|
||||
/// <summary>
|
||||
/// Can be null since we won't always have a beaker in the grinder.
|
||||
/// </summary>
|
||||
[ViewVariables] public SolutionContainerComponent? HeldBeaker = default!;
|
||||
[ViewVariables] public Solution? HeldBeaker = default!;
|
||||
|
||||
/// <summary>
|
||||
/// Contains the things that are going to be ground or juiced.
|
||||
|
||||
@@ -1,4 +1,5 @@
|
||||
using Content.Server.Kitchen.Components;
|
||||
using Content.Shared.Chemistry.EntitySystems;
|
||||
using JetBrains.Annotations;
|
||||
using Robust.Shared.GameObjects;
|
||||
|
||||
@@ -7,6 +8,18 @@ namespace Content.Server.Kitchen.EntitySystems
|
||||
[UsedImplicitly]
|
||||
internal sealed class MicrowaveSystem : EntitySystem
|
||||
{
|
||||
public override void Initialize()
|
||||
{
|
||||
base.Initialize();
|
||||
|
||||
SubscribeLocalEvent<MicrowaveComponent, SolutionChangedEvent>(OnSolutionChange);
|
||||
}
|
||||
|
||||
private void OnSolutionChange(EntityUid uid, MicrowaveComponent component, SolutionChangedEvent args)
|
||||
{
|
||||
component.DirtyUi();
|
||||
}
|
||||
|
||||
public override void Update(float frameTime)
|
||||
{
|
||||
base.Update(frameTime);
|
||||
|
||||
@@ -1,20 +1,18 @@
|
||||
using System;
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
using Content.Server.Chemistry.Components;
|
||||
using Content.Server.Hands.Components;
|
||||
using Content.Server.Items;
|
||||
using Content.Server.Kitchen.Components;
|
||||
using Content.Server.Kitchen.Events;
|
||||
using Content.Server.Power.Components;
|
||||
using Content.Server.Stack;
|
||||
using Content.Server.UserInterface;
|
||||
using Content.Server.Kitchen.Events;
|
||||
using Content.Shared.Chemistry.Solution;
|
||||
using Content.Shared.Chemistry.EntitySystems;
|
||||
using Content.Shared.Interaction;
|
||||
using Content.Shared.Kitchen.Components;
|
||||
using Content.Shared.Notification.Managers;
|
||||
using Content.Shared.Random.Helpers;
|
||||
using Content.Shared.Tag;
|
||||
using JetBrains.Annotations;
|
||||
using Robust.Server.GameObjects;
|
||||
using Robust.Shared.Audio;
|
||||
@@ -30,7 +28,7 @@ namespace Content.Server.Kitchen.EntitySystems
|
||||
[UsedImplicitly]
|
||||
internal sealed class ReagentGrinderSystem : EntitySystem
|
||||
{
|
||||
[Dependency] private readonly IEntityManager _entityManager = default!;
|
||||
[Dependency] private readonly SolutionContainerSystem _solutionsSystem = default!;
|
||||
|
||||
private Queue<ReagentGrinderComponent> _uiUpdateQueue = new();
|
||||
|
||||
@@ -39,13 +37,14 @@ namespace Content.Server.Kitchen.EntitySystems
|
||||
base.Initialize();
|
||||
|
||||
SubscribeLocalEvent<ReagentGrinderComponent, ComponentInit>(OnComponentInit);
|
||||
SubscribeLocalEvent<ReagentGrinderComponent, PowerChangedEvent>((_, component, _) => EnqueueUiUpdate(component));
|
||||
SubscribeLocalEvent<ReagentGrinderComponent, PowerChangedEvent>((_, component, _) =>
|
||||
EnqueueUiUpdate(component));
|
||||
SubscribeLocalEvent<ReagentGrinderComponent, InteractHandEvent>(OnInteractHand);
|
||||
SubscribeLocalEvent<ReagentGrinderComponent, InteractUsingEvent>(OnInteractUsing);
|
||||
SubscribeLocalEvent<StackComponent, JuiceableScalingEvent>(JuiceableScaling);
|
||||
SubscribeLocalEvent<StackComponent, ExtractableScalingEvent>(ExtractableScaling);
|
||||
}
|
||||
|
||||
private void JuiceableScaling(EntityUid uid, StackComponent component, JuiceableScalingEvent args)
|
||||
private void ExtractableScaling(EntityUid uid, StackComponent component, ExtractableScalingEvent args)
|
||||
{
|
||||
args.Scalar *= component.Count; // multiply scalar by amount of items in stack
|
||||
}
|
||||
@@ -54,9 +53,10 @@ namespace Content.Server.Kitchen.EntitySystems
|
||||
{
|
||||
if (args.Handled) return;
|
||||
|
||||
if (!args.User.TryGetComponent(out IHandsComponent? hands))
|
||||
if (!args.User.HasComponent<IHandsComponent>())
|
||||
{
|
||||
component.Owner.PopupMessage(args.User, Loc.GetString("reagent-grinder-component-interact-using-no-hands"));
|
||||
component.Owner.PopupMessage(args.User,
|
||||
Loc.GetString("reagent-grinder-component-interact-using-no-hands"));
|
||||
args.Handled = true;
|
||||
return;
|
||||
}
|
||||
@@ -66,7 +66,7 @@ namespace Content.Server.Kitchen.EntitySystems
|
||||
// First, check if user is trying to insert a beaker.
|
||||
// No promise it will be a beaker right now, but whatever.
|
||||
// Maybe this should whitelist "beaker" in the prototype id of heldEnt?
|
||||
if (heldEnt.TryGetComponent(out SolutionContainerComponent? beaker) && beaker.Capabilities.HasFlag(SolutionContainerCaps.FitsInDispenser))
|
||||
if (_solutionsSystem.TryGetFitsInDispenser(heldEnt.Uid, out var beaker))
|
||||
{
|
||||
component.BeakerContainer.Insert(heldEnt);
|
||||
component.HeldBeaker = beaker;
|
||||
@@ -74,15 +74,17 @@ namespace Content.Server.Kitchen.EntitySystems
|
||||
//We are done, return. Insert the beaker and exit!
|
||||
if (component.Owner.TryGetComponent(out AppearanceComponent? appearance))
|
||||
{
|
||||
appearance.SetData(SharedReagentGrinderComponent.ReagentGrinderVisualState.BeakerAttached, component.BeakerContainer.ContainedEntity != null);
|
||||
appearance.SetData(SharedReagentGrinderComponent.ReagentGrinderVisualState.BeakerAttached,
|
||||
component.BeakerContainer.ContainedEntity != null);
|
||||
}
|
||||
|
||||
ClickSound(component);
|
||||
args.Handled = true;
|
||||
return;
|
||||
}
|
||||
|
||||
//Next, see if the user is trying to insert something they want to be ground/juiced.
|
||||
if (!heldEnt.HasTag("Grindable") && !heldEnt.TryGetComponent(out JuiceableComponent? juice))
|
||||
if (!heldEnt.TryGetComponent(out ExtractableComponent? juice))
|
||||
{
|
||||
//Entity did NOT pass the whitelist for grind/juice.
|
||||
//Wouldn't want the clown grinding up the Captain's ID card now would you?
|
||||
@@ -114,8 +116,10 @@ namespace Content.Server.Kitchen.EntitySystems
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
EnqueueUiUpdate(component);
|
||||
component.Owner.GetUIOrNull(SharedReagentGrinderComponent.ReagentGrinderUiKey.Key)?.Toggle(actor.PlayerSession);
|
||||
component.Owner.GetUIOrNull(SharedReagentGrinderComponent.ReagentGrinderUiKey.Key)
|
||||
?.Toggle(actor.PlayerSession);
|
||||
args.Handled = true;
|
||||
}
|
||||
|
||||
@@ -130,11 +134,13 @@ namespace Content.Server.Kitchen.EntitySystems
|
||||
|
||||
//A slot for the beaker where the grounds/juices will go.
|
||||
component.BeakerContainer =
|
||||
ContainerHelpers.EnsureContainer<ContainerSlot>(component.Owner, $"{component.Name}-reagentContainerContainer");
|
||||
ContainerHelpers.EnsureContainer<ContainerSlot>(component.Owner,
|
||||
$"{component.Name}-reagentContainerContainer");
|
||||
|
||||
//A container for the things that WILL be ground/juiced. Useful for ejecting them instead of deleting them from the hands of the user.
|
||||
component.Chamber =
|
||||
ContainerHelpers.EnsureContainer<Container>(component.Owner, $"{component.Name}-entityContainerContainer");
|
||||
ContainerHelpers.EnsureContainer<Container>(component.Owner,
|
||||
$"{component.Name}-entityContainerContainer");
|
||||
|
||||
var bui = component.Owner.GetUIOrNull(SharedReagentGrinderComponent.ReagentGrinderUiKey.Key);
|
||||
if (bui != null)
|
||||
@@ -154,15 +160,19 @@ namespace Content.Server.Kitchen.EntitySystems
|
||||
switch (message.Message)
|
||||
{
|
||||
case SharedReagentGrinderComponent.ReagentGrinderGrindStartMessage msg:
|
||||
if (!component.Owner.TryGetComponent(out ApcPowerReceiverComponent? receiver) || !receiver.Powered) break;
|
||||
if (!component.Owner.TryGetComponent(out ApcPowerReceiverComponent? receiver) ||
|
||||
!receiver.Powered) break;
|
||||
ClickSound(component);
|
||||
DoWork(component, message.Session.AttachedEntity!, SharedReagentGrinderComponent.GrinderProgram.Grind);
|
||||
DoWork(component, message.Session.AttachedEntity!,
|
||||
SharedReagentGrinderComponent.GrinderProgram.Grind);
|
||||
break;
|
||||
|
||||
case SharedReagentGrinderComponent.ReagentGrinderJuiceStartMessage msg:
|
||||
if (!component.Owner.TryGetComponent(out ApcPowerReceiverComponent? receiver2) || !receiver2.Powered) break;
|
||||
if (!component.Owner.TryGetComponent(out ApcPowerReceiverComponent? receiver2) ||
|
||||
!receiver2.Powered) break;
|
||||
ClickSound(component);
|
||||
DoWork(component, message.Session.AttachedEntity!, SharedReagentGrinderComponent.GrinderProgram.Juice);
|
||||
DoWork(component, message.Session.AttachedEntity!,
|
||||
SharedReagentGrinderComponent.GrinderProgram.Juice);
|
||||
break;
|
||||
|
||||
case SharedReagentGrinderComponent.ReagentGrinderEjectChamberAllMessage msg:
|
||||
@@ -175,8 +185,10 @@ namespace Content.Server.Kitchen.EntitySystems
|
||||
component.Chamber.Remove(entity);
|
||||
entity.RandomOffset(0.4f);
|
||||
}
|
||||
|
||||
EnqueueUiUpdate(component);
|
||||
}
|
||||
|
||||
break;
|
||||
|
||||
case SharedReagentGrinderComponent.ReagentGrinderEjectChamberContentMessage msg:
|
||||
@@ -187,6 +199,7 @@ namespace Content.Server.Kitchen.EntitySystems
|
||||
EnqueueUiUpdate(component);
|
||||
ClickSound(component);
|
||||
}
|
||||
|
||||
break;
|
||||
|
||||
case SharedReagentGrinderComponent.ReagentGrinderEjectBeakerMessage msg:
|
||||
@@ -212,13 +225,15 @@ namespace Content.Server.Kitchen.EntitySystems
|
||||
{
|
||||
foreach (var entity in comp.Chamber.ContainedEntities)
|
||||
{
|
||||
if (!canJuice && entity.HasComponent<JuiceableComponent>()) canJuice = true;
|
||||
if (!canGrind && entity.HasTag("Grindable")) canGrind = true;
|
||||
if (canJuice && canGrind) break;
|
||||
if (canJuice || !entity.TryGetComponent(out ExtractableComponent? component)) continue;
|
||||
|
||||
canJuice = component.GrindableSolution == null;
|
||||
canGrind = component.GrindableSolution != null;
|
||||
}
|
||||
}
|
||||
|
||||
comp.Owner.GetUIOrNull(SharedReagentGrinderComponent.ReagentGrinderUiKey.Key)?.SetState(new ReagentGrinderInterfaceState
|
||||
comp.Owner.GetUIOrNull(SharedReagentGrinderComponent.ReagentGrinderUiKey.Key)?.SetState(
|
||||
new ReagentGrinderInterfaceState
|
||||
(
|
||||
comp.Busy,
|
||||
comp.BeakerContainer.ContainedEntity != null,
|
||||
@@ -227,7 +242,7 @@ namespace Content.Server.Kitchen.EntitySystems
|
||||
canGrind,
|
||||
comp.Chamber.ContainedEntities.Select(item => item.Uid).ToArray(),
|
||||
//Remember the beaker can be null!
|
||||
comp.HeldBeaker?.Solution.Contents.ToArray()
|
||||
comp.HeldBeaker?.Contents.ToArray()
|
||||
));
|
||||
}
|
||||
}
|
||||
@@ -247,7 +262,8 @@ namespace Content.Server.Kitchen.EntitySystems
|
||||
|
||||
component.BeakerContainer.Remove(beaker);
|
||||
|
||||
if (user == null || !user.TryGetComponent<HandsComponent>(out var hands) || !component.HeldBeaker.Owner.TryGetComponent<ItemComponent>(out var item))
|
||||
if (user == null || !user.TryGetComponent<HandsComponent>(out var hands) ||
|
||||
!beaker.TryGetComponent<ItemComponent>(out var item))
|
||||
return;
|
||||
hands.PutInHandOrDrop(item);
|
||||
|
||||
@@ -255,7 +271,8 @@ namespace Content.Server.Kitchen.EntitySystems
|
||||
EnqueueUiUpdate(component);
|
||||
if (component.Owner.TryGetComponent(out AppearanceComponent? appearance))
|
||||
{
|
||||
appearance.SetData(SharedReagentGrinderComponent.ReagentGrinderVisualState.BeakerAttached, component.BeakerContainer.ContainedEntity != null);
|
||||
appearance.SetData(SharedReagentGrinderComponent.ReagentGrinderVisualState.BeakerAttached,
|
||||
component.BeakerContainer.ContainedEntity != null);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -263,10 +280,13 @@ namespace Content.Server.Kitchen.EntitySystems
|
||||
/// The wzhzhzh of the grinder. Processes the contents of the grinder and puts the output in the beaker.
|
||||
/// </summary>
|
||||
/// <param name="isJuiceIntent">true for wanting to juice, false for wanting to grind.</param>
|
||||
private void DoWork(ReagentGrinderComponent component, IEntity user, SharedReagentGrinderComponent.GrinderProgram program)
|
||||
private void DoWork(ReagentGrinderComponent component, IEntity user,
|
||||
SharedReagentGrinderComponent.GrinderProgram program)
|
||||
{
|
||||
//Have power, are we busy, chamber has anything to grind, a beaker for the grounds to go?
|
||||
if (!component.Owner.TryGetComponent(out ApcPowerReceiverComponent? receiver) || !receiver.Powered || component.Busy || component.Chamber.ContainedEntities.Count <= 0 || component.BeakerContainer.ContainedEntity == null || component.HeldBeaker == null)
|
||||
if (!component.Owner.TryGetComponent(out ApcPowerReceiverComponent? receiver) || !receiver.Powered ||
|
||||
component.Busy || component.Chamber.ContainedEntities.Count <= 0 ||
|
||||
component.BeakerContainer.ContainedEntity == null || component.HeldBeaker == null)
|
||||
{
|
||||
return;
|
||||
}
|
||||
@@ -275,25 +295,31 @@ namespace Content.Server.Kitchen.EntitySystems
|
||||
|
||||
var bui = component.Owner.GetUIOrNull(SharedReagentGrinderComponent.ReagentGrinderUiKey.Key);
|
||||
bui?.SendMessage(new SharedReagentGrinderComponent.ReagentGrinderWorkStartedMessage(program));
|
||||
var beakerEntity = component.BeakerContainer.ContainedEntity;
|
||||
switch (program)
|
||||
{
|
||||
case SharedReagentGrinderComponent.GrinderProgram.Grind:
|
||||
SoundSystem.Play(Filter.Pvs(component.Owner), component.GrindSound.GetSound(), component.Owner, AudioParams.Default);
|
||||
//Get each item inside the chamber and get the reagents it contains. Transfer those reagents to the beaker, given we have one in.
|
||||
// Get each item inside the chamber and get the reagents it contains.
|
||||
// Transfer those reagents to the beaker, given we have one in.
|
||||
component.Owner.SpawnTimer(component.WorkTime, (Action) (() =>
|
||||
{
|
||||
foreach (var item in component.Chamber.ContainedEntities.ToList())
|
||||
{
|
||||
if (!item.HasTag("Grindable")) continue;
|
||||
if (!item.TryGetComponent<SolutionContainerComponent>(out var solution)) continue;
|
||||
var juiceEvent = new JuiceableScalingEvent(); // default of scalar is always 1.0
|
||||
RaiseLocalEvent<JuiceableScalingEvent>(item.Uid, juiceEvent, false);
|
||||
if (component.HeldBeaker.CurrentVolume + solution.CurrentVolume * juiceEvent.Scalar > component.HeldBeaker.MaxVolume) continue;
|
||||
solution.Solution.ScaleSolution(juiceEvent.Scalar);
|
||||
component.HeldBeaker.TryAddSolution(solution.Solution);
|
||||
solution.RemoveAllSolution();
|
||||
if (!item.TryGetComponent(out ExtractableComponent? extract)
|
||||
|| extract.GrindableSolution == null
|
||||
|| !_solutionsSystem.TryGetSolution(item, extract.GrindableSolution, out var solution)) continue;
|
||||
|
||||
var juiceEvent = new ExtractableScalingEvent(); // default of scalar is always 1.0
|
||||
RaiseLocalEvent(item.Uid, juiceEvent, false);
|
||||
if (component.HeldBeaker.CurrentVolume + solution.CurrentVolume * juiceEvent.Scalar >
|
||||
component.HeldBeaker.MaxVolume) continue;
|
||||
solution.ScaleSolution(juiceEvent.Scalar);
|
||||
_solutionsSystem.TryAddSolution(beakerEntity.Uid, component.HeldBeaker, solution);
|
||||
_solutionsSystem.RemoveAllSolution(beakerEntity.Uid, solution);
|
||||
item.Delete();
|
||||
}
|
||||
|
||||
component.Busy = false;
|
||||
EnqueueUiUpdate(component);
|
||||
bui?.SendMessage(new SharedReagentGrinderComponent.ReagentGrinderWorkCompleteMessage());
|
||||
@@ -306,17 +332,21 @@ namespace Content.Server.Kitchen.EntitySystems
|
||||
{
|
||||
foreach (var item in component.Chamber.ContainedEntities.ToList())
|
||||
{
|
||||
if (!item.TryGetComponent<JuiceableComponent>(out var juiceMe)) continue;
|
||||
var juiceEvent = new JuiceableScalingEvent(); // default of scalar is always 1.0
|
||||
if (!item.TryGetComponent<ExtractableComponent>(out var juiceMe)) continue;
|
||||
var juiceEvent = new ExtractableScalingEvent(); // default of scalar is always 1.0
|
||||
if (item.HasComponent<StackComponent>())
|
||||
{
|
||||
RaiseLocalEvent<JuiceableScalingEvent>(item.Uid, juiceEvent);
|
||||
RaiseLocalEvent(item.Uid, juiceEvent);
|
||||
}
|
||||
if (component.HeldBeaker.CurrentVolume + juiceMe.JuiceResultSolution.TotalVolume * juiceEvent.Scalar > component.HeldBeaker.MaxVolume) continue;
|
||||
juiceMe.JuiceResultSolution.ScaleSolution(juiceEvent.Scalar);
|
||||
component.HeldBeaker.TryAddSolution(juiceMe.JuiceResultSolution);
|
||||
|
||||
if (component.HeldBeaker.CurrentVolume +
|
||||
juiceMe.ResultSolution.TotalVolume * juiceEvent.Scalar >
|
||||
component.HeldBeaker.MaxVolume) continue;
|
||||
juiceMe.ResultSolution.ScaleSolution(juiceEvent.Scalar);
|
||||
_solutionsSystem.TryAddSolution(beakerEntity.Uid, component.HeldBeaker, juiceMe.ResultSolution);
|
||||
item.Delete();
|
||||
}
|
||||
|
||||
bui?.SendMessage(new SharedReagentGrinderComponent.ReagentGrinderWorkCompleteMessage());
|
||||
component.Busy = false;
|
||||
EnqueueUiUpdate(component);
|
||||
|
||||
@@ -5,10 +5,10 @@ namespace Content.Server.Kitchen.Events
|
||||
/// <summary>
|
||||
/// Used in scaling amount of solution to extract in juicing
|
||||
/// </summary>
|
||||
public class JuiceableScalingEvent : EntityEventArgs
|
||||
public class ExtractableScalingEvent : EntityEventArgs
|
||||
{
|
||||
|
||||
public JuiceableScalingEvent()
|
||||
public ExtractableScalingEvent()
|
||||
{
|
||||
Scalar = 1f;
|
||||
}
|
||||
@@ -2,16 +2,13 @@ using System.Linq;
|
||||
using System.Threading.Tasks;
|
||||
using Content.Server.Body.Behavior;
|
||||
using Content.Server.Fluids.Components;
|
||||
using Content.Shared.Audio;
|
||||
using Content.Shared.Body.Components;
|
||||
using Content.Shared.Chemistry;
|
||||
using Content.Shared.Chemistry.Components.SolutionManager;
|
||||
using Content.Shared.Chemistry.EntitySystems;
|
||||
using Content.Shared.Chemistry.Reagent;
|
||||
using Content.Shared.Chemistry.Solution;
|
||||
using Content.Shared.Chemistry.Solution.Components;
|
||||
using Content.Shared.Examine;
|
||||
using Content.Shared.Interaction;
|
||||
using Content.Shared.Interaction.Helpers;
|
||||
using Content.Shared.Notification;
|
||||
using Content.Shared.Notification.Managers;
|
||||
using Content.Shared.Nutrition.Components;
|
||||
using Content.Shared.Sound;
|
||||
@@ -23,7 +20,6 @@ using Robust.Shared.GameObjects;
|
||||
using Robust.Shared.IoC;
|
||||
using Robust.Shared.Localization;
|
||||
using Robust.Shared.Player;
|
||||
using Robust.Shared.Prototypes;
|
||||
using Robust.Shared.Random;
|
||||
using Robust.Shared.Serialization.Manager.Attributes;
|
||||
using Robust.Shared.Utility;
|
||||
@@ -32,11 +28,14 @@ using Robust.Shared.ViewVariables;
|
||||
namespace Content.Server.Nutrition.Components
|
||||
{
|
||||
[RegisterComponent]
|
||||
public class DrinkComponent : Component, IUse, IAfterInteract, ISolutionChange, IExamine, ILand
|
||||
public class DrinkComponent : Component, IUse, IAfterInteract, IExamine, ILand
|
||||
{
|
||||
[Dependency] private readonly IPrototypeManager _prototypeManager = default!;
|
||||
[Dependency] private readonly IRobustRandom _random = default!;
|
||||
|
||||
[DataField("solution")]
|
||||
public string SolutionName { get; set; } = DefaultSolutionName;
|
||||
public const string DefaultSolutionName = "drink";
|
||||
|
||||
public override string Name => "Drink";
|
||||
|
||||
int IAfterInteract.Priority => 10;
|
||||
@@ -50,7 +49,7 @@ namespace Content.Server.Nutrition.Components
|
||||
|
||||
[ViewVariables]
|
||||
[DataField("isOpen")]
|
||||
private bool _defaultToOpened;
|
||||
internal bool DefaultToOpened;
|
||||
|
||||
[ViewVariables(VVAccess.ReadWrite)]
|
||||
public ReagentUnit TransferAmount { get; [UsedImplicitly] private set; } = ReagentUnit.New(5);
|
||||
@@ -71,8 +70,14 @@ namespace Content.Server.Nutrition.Components
|
||||
}
|
||||
}
|
||||
|
||||
[ViewVariables]
|
||||
public bool Empty => Owner.GetComponentOrNull<ISolutionInteractionsComponent>()?.DrainAvailable <= 0;
|
||||
[ViewVariables] public bool Empty => IsEmpty();
|
||||
|
||||
private bool IsEmpty()
|
||||
{
|
||||
var drainAvailable = EntitySystem.Get<SolutionContainerSystem>()
|
||||
.DrainAvailable(Owner);
|
||||
return drainAvailable <= 0;
|
||||
}
|
||||
|
||||
[DataField("openSounds")]
|
||||
private SoundSpecifier _openSounds = new SoundCollectionSpecifier("canOpenSounds");
|
||||
@@ -81,45 +86,39 @@ namespace Content.Server.Nutrition.Components
|
||||
[DataField("burstSound")]
|
||||
private SoundSpecifier _burstSound = new SoundPathSpecifier("/Audio/Effects/flash_bang.ogg");
|
||||
|
||||
protected override void Initialize()
|
||||
{
|
||||
base.Initialize();
|
||||
|
||||
Opened = _defaultToOpened;
|
||||
UpdateAppearance();
|
||||
}
|
||||
|
||||
private void OpenedChanged()
|
||||
{
|
||||
if (!Owner.TryGetComponent(out SharedSolutionContainerComponent? contents))
|
||||
var solutionSys = EntitySystem.Get<SolutionContainerSystem>();
|
||||
if (!solutionSys.TryGetSolution(Owner, SolutionName, out _))
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
if (Opened)
|
||||
{
|
||||
contents.Capabilities |= SolutionContainerCaps.Refillable | SolutionContainerCaps.Drainable;
|
||||
var refillable = Owner.EnsureComponent<RefillableSolutionComponent>();
|
||||
refillable.Solution = SolutionName;
|
||||
var drainable = Owner.EnsureComponent<DrainableSolutionComponent>();
|
||||
drainable.Solution = SolutionName;
|
||||
}
|
||||
else
|
||||
{
|
||||
contents.Capabilities &= ~(SolutionContainerCaps.Refillable | SolutionContainerCaps.Drainable);
|
||||
Owner.RemoveComponent<RefillableSolutionComponent>();
|
||||
Owner.RemoveComponent<DrainableSolutionComponent>();
|
||||
}
|
||||
}
|
||||
|
||||
void ISolutionChange.SolutionChanged(SolutionChangeEventArgs eventArgs)
|
||||
{
|
||||
UpdateAppearance();
|
||||
}
|
||||
|
||||
private void UpdateAppearance()
|
||||
// TODO move to DrinkSystem
|
||||
public void UpdateAppearance()
|
||||
{
|
||||
if (!Owner.TryGetComponent(out AppearanceComponent? appearance) ||
|
||||
!Owner.TryGetComponent(out ISolutionInteractionsComponent? contents))
|
||||
!Owner.HasComponent<SolutionContainerManagerComponent>())
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
appearance.SetData(SharedFoodComponent.FoodVisuals.Visual, contents.DrainAvailable.Float());
|
||||
var drainAvailable = EntitySystem.Get<SolutionContainerSystem>().DrainAvailable(Owner);
|
||||
appearance.SetData(SharedFoodComponent.FoodVisuals.Visual, drainAvailable.Float());
|
||||
}
|
||||
|
||||
bool IUse.UseEntity(UseEntityEventArgs args)
|
||||
@@ -133,8 +132,8 @@ namespace Content.Server.Nutrition.Components
|
||||
return false;
|
||||
}
|
||||
|
||||
if (!Owner.TryGetComponent(out ISolutionInteractionsComponent? contents) ||
|
||||
contents.DrainAvailable <= 0)
|
||||
if (!Owner.HasComponent<SolutionContainerManagerComponent>() ||
|
||||
EntitySystem.Get<SolutionContainerSystem>().DrainAvailable(Owner) <= 0)
|
||||
{
|
||||
args.User.PopupMessage(Loc.GetString("drink-component-on-use-is-empty", ("owner", Owner)));
|
||||
return true;
|
||||
@@ -160,8 +159,10 @@ namespace Content.Server.Nutrition.Components
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
var color = Empty ? "gray" : "yellow";
|
||||
var openedText = Loc.GetString(Empty ? "drink-component-on-examine-is-empty" : "drink-component-on-examine-is-opened");
|
||||
var openedText =
|
||||
Loc.GetString(Empty ? "drink-component-on-examine-is-empty" : "drink-component-on-examine-is-opened");
|
||||
message.AddMarkup(Loc.GetString("drink-component-on-examine-details-text", ("colorName", color), ("text", openedText)));
|
||||
}
|
||||
|
||||
@@ -173,8 +174,7 @@ namespace Content.Server.Nutrition.Components
|
||||
return false;
|
||||
}
|
||||
|
||||
if (!Owner.TryGetComponent(out ISolutionInteractionsComponent? interactions) ||
|
||||
!interactions.CanDrain ||
|
||||
if (!EntitySystem.Get<SolutionContainerSystem>().TryGetDrainableSolution(Owner.Uid, out var interactions) ||
|
||||
interactions.DrainAvailable <= 0)
|
||||
{
|
||||
if (!forced)
|
||||
@@ -199,8 +199,9 @@ namespace Content.Server.Nutrition.Components
|
||||
return false;
|
||||
}
|
||||
|
||||
var solutionContainerSystem = EntitySystem.Get<SolutionContainerSystem>();
|
||||
var transferAmount = ReagentUnit.Min(TransferAmount, interactions.DrainAvailable);
|
||||
var drain = interactions.Drain(transferAmount);
|
||||
var drain = solutionContainerSystem.Drain(Owner.Uid, interactions, transferAmount);
|
||||
var firstStomach = stomachs.FirstOrDefault(stomach => stomach.CanTransferSolution(drain));
|
||||
|
||||
// All stomach are full or can't handle whatever solution we have.
|
||||
@@ -208,13 +209,14 @@ namespace Content.Server.Nutrition.Components
|
||||
{
|
||||
target.PopupMessage(Loc.GetString("drink-component-try-use-drink-had-enough", ("owner", Owner)));
|
||||
|
||||
if (!interactions.CanRefill)
|
||||
if (Owner.EntityManager.TryGetEntity(Owner.Uid, out var interactionEntity)
|
||||
&& !interactionEntity.HasComponent<RefillableSolutionComponent>())
|
||||
{
|
||||
drain.SpillAt(target, "PuddleSmear");
|
||||
return false;
|
||||
}
|
||||
|
||||
interactions.Refill(drain);
|
||||
solutionContainerSystem.Refill(Owner.Uid, interactions, drain);
|
||||
return false;
|
||||
}
|
||||
|
||||
@@ -237,16 +239,12 @@ namespace Content.Server.Nutrition.Components
|
||||
if (_pressurized &&
|
||||
!Opened &&
|
||||
_random.Prob(0.25f) &&
|
||||
Owner.TryGetComponent(out ISolutionInteractionsComponent? interactions))
|
||||
EntitySystem.Get<SolutionContainerSystem>().TryGetDrainableSolution(Owner.Uid, out var interactions))
|
||||
{
|
||||
Opened = true;
|
||||
|
||||
if (!interactions.CanDrain)
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
var solution = interactions.Drain(interactions.DrainAvailable);
|
||||
var solution = EntitySystem.Get<SolutionContainerSystem>()
|
||||
.Drain(Owner.Uid, interactions, interactions.DrainAvailable);
|
||||
solution.SpillAt(Owner, "PuddleSmear");
|
||||
|
||||
SoundSystem.Play(Filter.Pvs(Owner), _burstSound.GetSound(), Owner, AudioParams.Default.WithVolume(-4));
|
||||
|
||||
@@ -3,10 +3,10 @@ using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
using System.Threading.Tasks;
|
||||
using Content.Server.Body.Behavior;
|
||||
using Content.Server.Chemistry.Components;
|
||||
using Content.Server.Hands.Components;
|
||||
using Content.Server.Items;
|
||||
using Content.Shared.Body.Components;
|
||||
using Content.Shared.Chemistry.EntitySystems;
|
||||
using Content.Shared.Chemistry.Reagent;
|
||||
using Content.Shared.Interaction;
|
||||
using Content.Shared.Interaction.Helpers;
|
||||
@@ -28,6 +28,7 @@ namespace Content.Server.Nutrition.Components
|
||||
public class FoodComponent : Component, IUse, IAfterInteract
|
||||
{
|
||||
public override string Name => "Food";
|
||||
public static string SolutionName = "food";
|
||||
|
||||
[ViewVariables]
|
||||
[DataField("useSound")]
|
||||
@@ -52,7 +53,7 @@ namespace Content.Server.Nutrition.Components
|
||||
{
|
||||
get
|
||||
{
|
||||
if (!Owner.TryGetComponent(out SolutionContainerComponent? solution))
|
||||
if (!EntitySystem.Get<SolutionContainerSystem>().TryGetSolution(Owner, SolutionName, out var solution))
|
||||
{
|
||||
return 0;
|
||||
}
|
||||
@@ -69,7 +70,7 @@ namespace Content.Server.Nutrition.Components
|
||||
protected override void Initialize()
|
||||
{
|
||||
base.Initialize();
|
||||
Owner.EnsureComponentWarn<SolutionContainerComponent>();
|
||||
// Owner.EnsureComponentWarn<SolutionContainerManager>();
|
||||
}
|
||||
|
||||
bool IUse.UseEntity(UseEntityEventArgs eventArgs)
|
||||
@@ -97,7 +98,8 @@ namespace Content.Server.Nutrition.Components
|
||||
|
||||
public bool TryUseFood(IEntity? user, IEntity? target, UtensilComponent? utensilUsed = null)
|
||||
{
|
||||
if (!Owner.TryGetComponent(out SolutionContainerComponent? solution))
|
||||
var solutionContainerSys = EntitySystem.Get<SolutionContainerSystem>();
|
||||
if (!solutionContainerSys.TryGetSolution(Owner, SolutionName, out var solution))
|
||||
{
|
||||
return false;
|
||||
}
|
||||
@@ -147,7 +149,8 @@ namespace Content.Server.Nutrition.Components
|
||||
|
||||
if (!types.HasFlag(_utensilsNeeded))
|
||||
{
|
||||
trueTarget.PopupMessage(user, Loc.GetString("food-you-need-to-hold-utensil", ("utensil", _utensilsNeeded)));
|
||||
trueTarget.PopupMessage(user,
|
||||
Loc.GetString("food-you-need-to-hold-utensil", ("utensil", _utensilsNeeded)));
|
||||
return false;
|
||||
}
|
||||
}
|
||||
@@ -158,12 +161,12 @@ namespace Content.Server.Nutrition.Components
|
||||
}
|
||||
|
||||
var transferAmount = TransferAmount != null ? ReagentUnit.Min((ReagentUnit)TransferAmount, solution.CurrentVolume) : solution.CurrentVolume;
|
||||
var split = solution.SplitSolution(transferAmount);
|
||||
var split = solutionContainerSys.SplitSolution(Owner.Uid, solution, transferAmount);
|
||||
var firstStomach = stomachs.FirstOrDefault(stomach => stomach.CanTransferSolution(split));
|
||||
|
||||
if (firstStomach == null)
|
||||
{
|
||||
solution.TryAddSolution(split);
|
||||
solutionContainerSys.TryAddSolution(Owner.Uid, solution, split);
|
||||
trueTarget.PopupMessage(user, Loc.GetString("food-you-cannot-eat-any-more"));
|
||||
return false;
|
||||
}
|
||||
@@ -203,6 +206,8 @@ namespace Content.Server.Nutrition.Components
|
||||
return true;
|
||||
}
|
||||
|
||||
|
||||
|
||||
private void DeleteAndSpawnTrash(IEntity user)
|
||||
{
|
||||
//We're empty. Become trash.
|
||||
|
||||
@@ -1,7 +1,8 @@
|
||||
using System.Threading.Tasks;
|
||||
using Content.Server.Chemistry.Components;
|
||||
using Content.Server.Hands.Components;
|
||||
using Content.Server.Items;
|
||||
using Content.Shared.Chemistry.Components.SolutionManager;
|
||||
using Content.Shared.Chemistry.EntitySystems;
|
||||
using Content.Shared.Chemistry.Reagent;
|
||||
using Content.Shared.Examine;
|
||||
using Content.Shared.Interaction;
|
||||
@@ -36,14 +37,16 @@ namespace Content.Server.Nutrition.Components
|
||||
[ViewVariables(VVAccess.ReadWrite)]
|
||||
private ushort _totalCount = 5;
|
||||
|
||||
[ViewVariables(VVAccess.ReadWrite)] public ushort Count;
|
||||
[ViewVariables(VVAccess.ReadWrite)]
|
||||
public ushort Count;
|
||||
|
||||
protected override void Initialize()
|
||||
{
|
||||
base.Initialize();
|
||||
Count = _totalCount;
|
||||
Owner.EnsureComponent<FoodComponent>();
|
||||
Owner.EnsureComponent<SolutionContainerComponent>();
|
||||
Owner.EnsureComponent<SolutionContainerManagerComponent>();
|
||||
EntitySystem.Get<SolutionContainerSystem>().EnsureSolution(Owner, FoodComponent.SolutionName);
|
||||
}
|
||||
|
||||
async Task<bool> IInteractUsing.InteractUsing(InteractUsingEventArgs eventArgs)
|
||||
@@ -52,10 +55,12 @@ namespace Content.Server.Nutrition.Components
|
||||
{
|
||||
return false;
|
||||
}
|
||||
if (!Owner.TryGetComponent(out SolutionContainerComponent? solution))
|
||||
|
||||
if (!EntitySystem.Get<SolutionContainerSystem>().TryGetSolution(Owner, FoodComponent.SolutionName, out var solution))
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
if (!eventArgs.Using.TryGetComponent(out UtensilComponent? utensil) || !utensil.HasType(UtensilType.Knife))
|
||||
{
|
||||
return false;
|
||||
@@ -79,7 +84,9 @@ namespace Content.Server.Nutrition.Components
|
||||
Owner.Delete();
|
||||
return true;
|
||||
}
|
||||
solution.TryRemoveReagent("Nutriment", solution.CurrentVolume / ReagentUnit.New(Count + 1));
|
||||
|
||||
EntitySystem.Get<SolutionContainerSystem>().TryRemoveReagent(Owner.Uid, solution, "Nutriment",
|
||||
solution.CurrentVolume / ReagentUnit.New(Count + 1));
|
||||
return true;
|
||||
}
|
||||
|
||||
|
||||
@@ -1,7 +1,8 @@
|
||||
using Content.Server.Chemistry.Components;
|
||||
using Content.Server.Fluids.Components;
|
||||
using Content.Server.Notification;
|
||||
using Content.Server.Nutrition.Components;
|
||||
using Content.Shared.Audio;
|
||||
using Content.Shared.Chemistry.EntitySystems;
|
||||
using Content.Shared.Notification.Managers;
|
||||
using Content.Shared.Nutrition.Components;
|
||||
using Content.Shared.Nutrition.EntitySystems;
|
||||
@@ -9,6 +10,7 @@ using Content.Shared.Throwing;
|
||||
using JetBrains.Annotations;
|
||||
using Robust.Shared.Audio;
|
||||
using Robust.Shared.GameObjects;
|
||||
using Robust.Shared.IoC;
|
||||
using Robust.Shared.Localization;
|
||||
using Robust.Shared.Player;
|
||||
|
||||
@@ -17,13 +19,15 @@ namespace Content.Server.Nutrition.EntitySystems
|
||||
[UsedImplicitly]
|
||||
public class CreamPieSystem : SharedCreamPieSystem
|
||||
{
|
||||
[Dependency] private readonly SolutionContainerSystem _solutionsSystem = default!;
|
||||
|
||||
protected override void SplattedCreamPie(EntityUid uid, CreamPieComponent creamPie)
|
||||
{
|
||||
SoundSystem.Play(Filter.Pvs(creamPie.Owner), creamPie.Sound.GetSound(), creamPie.Owner, AudioHelpers.WithVariation(0.125f));
|
||||
|
||||
if (ComponentManager.TryGetComponent(uid, out SolutionContainerComponent? solution))
|
||||
if (_solutionsSystem.TryGetSolution(creamPie.Owner, FoodComponent.SolutionName, out var solution))
|
||||
{
|
||||
solution.Solution.SpillAt(creamPie.Owner, "PuddleSmear", false);
|
||||
solution.SpillAt(creamPie.Owner, "PuddleSmear", false);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
47
Content.Server/Nutrition/EntitySystems/DrinkSystem.cs
Normal file
47
Content.Server/Nutrition/EntitySystems/DrinkSystem.cs
Normal file
@@ -0,0 +1,47 @@
|
||||
using Content.Server.Nutrition.Components;
|
||||
using Content.Shared.Chemistry.Components.SolutionManager;
|
||||
using Content.Shared.Chemistry.EntitySystems;
|
||||
using JetBrains.Annotations;
|
||||
using Robust.Shared.GameObjects;
|
||||
using Robust.Shared.IoC;
|
||||
|
||||
namespace Content.Server.Nutrition.EntitySystems
|
||||
{
|
||||
[UsedImplicitly]
|
||||
public class DrinkSystem : EntitySystem
|
||||
{
|
||||
[Dependency] private readonly SolutionContainerSystem _solutionContainerSystem = default!;
|
||||
|
||||
public override void Initialize()
|
||||
{
|
||||
base.Initialize();
|
||||
|
||||
SubscribeLocalEvent<DrinkComponent, SolutionChangedEvent>(OnSolutionChange);
|
||||
SubscribeLocalEvent<DrinkComponent, ComponentInit>(OnDrinkInit);
|
||||
}
|
||||
|
||||
private void OnDrinkInit(EntityUid uid, DrinkComponent component, ComponentInit args)
|
||||
{
|
||||
component.Opened = component.DefaultToOpened;
|
||||
|
||||
var owner = EntityManager.GetEntity(uid);
|
||||
if (owner.TryGetComponent(out DrainableSolutionComponent? existingDrainable))
|
||||
{
|
||||
// Beakers have Drink component but they should use the existing Drainable
|
||||
component.SolutionName = existingDrainable.Solution;
|
||||
}
|
||||
else
|
||||
{
|
||||
_solutionContainerSystem.EnsureSolution(owner, component.SolutionName);
|
||||
}
|
||||
|
||||
component.UpdateAppearance();
|
||||
}
|
||||
|
||||
|
||||
private void OnSolutionChange(EntityUid uid, DrinkComponent component, SolutionChangedEvent args)
|
||||
{
|
||||
component.UpdateAppearance();
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -21,15 +21,16 @@ namespace Content.Server.PowerCell.Components
|
||||
/// </summary>
|
||||
[RegisterComponent]
|
||||
[ComponentReference(typeof(BatteryComponent))]
|
||||
public class PowerCellComponent : BatteryComponent, IExamine, ISolutionChange
|
||||
public class PowerCellComponent : BatteryComponent, IExamine
|
||||
{
|
||||
public override string Name => "PowerCell";
|
||||
public const string SolutionName = "powerCell";
|
||||
|
||||
[ViewVariables] public PowerCellSize CellSize => _cellSize;
|
||||
[DataField("cellSize")]
|
||||
private PowerCellSize _cellSize = PowerCellSize.Small;
|
||||
|
||||
[ViewVariables] public bool IsRigged { get; private set; }
|
||||
[ViewVariables] public bool IsRigged { get; set; }
|
||||
|
||||
protected override void Initialize()
|
||||
{
|
||||
@@ -96,13 +97,6 @@ namespace Content.Server.PowerCell.Components
|
||||
message.AddMarkup(Loc.GetString("power-cell-component-examine-details", ("currentCharge", $"{CurrentCharge / MaxCharge * 100:F0}")));
|
||||
}
|
||||
}
|
||||
|
||||
void ISolutionChange.SolutionChanged(SolutionChangeEventArgs eventArgs)
|
||||
{
|
||||
IsRigged = Owner.TryGetComponent(out SolutionContainerComponent? solution)
|
||||
&& solution.Solution.ContainsReagent("Plasma", out var plasma)
|
||||
&& plasma >= 5;
|
||||
}
|
||||
}
|
||||
|
||||
public enum PowerCellSize
|
||||
|
||||
28
Content.Server/PowerCell/PowerCellSystem.cs
Normal file
28
Content.Server/PowerCell/PowerCellSystem.cs
Normal file
@@ -0,0 +1,28 @@
|
||||
using Content.Server.PowerCell.Components;
|
||||
using Content.Shared.Chemistry.EntitySystems;
|
||||
using JetBrains.Annotations;
|
||||
using Robust.Shared.GameObjects;
|
||||
using Robust.Shared.IoC;
|
||||
|
||||
namespace Content.Server.PowerCell
|
||||
{
|
||||
[UsedImplicitly]
|
||||
public class PowerCellSystem : EntitySystem
|
||||
{
|
||||
[Dependency] private readonly SolutionContainerSystem _solutionsSystem = default!;
|
||||
|
||||
public override void Initialize()
|
||||
{
|
||||
base.Initialize();
|
||||
|
||||
SubscribeLocalEvent<PowerCellComponent, SolutionChangedEvent>(OnSolutionChange);
|
||||
}
|
||||
|
||||
private void OnSolutionChange(EntityUid uid, PowerCellComponent component, SolutionChangedEvent args)
|
||||
{
|
||||
component.IsRigged = _solutionsSystem.TryGetSolution(uid, PowerCellComponent.SolutionName, out var solution)
|
||||
&& solution.ContainsReagent("Plasma", out var plasma)
|
||||
&& plasma >= 5;
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -8,10 +8,10 @@ using Content.Server.Explosion;
|
||||
using Content.Server.Items;
|
||||
using Content.Server.Notification;
|
||||
using Content.Shared.Audio;
|
||||
using Content.Shared.Chemistry;
|
||||
using Content.Shared.Chemistry.Components;
|
||||
using Content.Shared.Chemistry.Components.SolutionManager;
|
||||
using Content.Shared.Chemistry.EntitySystems;
|
||||
using Content.Shared.Chemistry.Reagent;
|
||||
using Content.Shared.Chemistry.Solution.Components;
|
||||
using Content.Shared.Examine;
|
||||
using Content.Shared.Interaction;
|
||||
using Content.Shared.Notification.Managers;
|
||||
using Content.Shared.Sound;
|
||||
@@ -26,7 +26,6 @@ using Robust.Shared.Localization;
|
||||
using Robust.Shared.Player;
|
||||
using Robust.Shared.Players;
|
||||
using Robust.Shared.Serialization.Manager.Attributes;
|
||||
using Robust.Shared.Utility;
|
||||
using Robust.Shared.ViewVariables;
|
||||
|
||||
namespace Content.Server.Tools.Components
|
||||
@@ -36,12 +35,14 @@ namespace Content.Server.Tools.Components
|
||||
[ComponentReference(typeof(IToolComponent))]
|
||||
[ComponentReference(typeof(IHotItem))]
|
||||
[NetworkedComponent()]
|
||||
public class WelderComponent : ToolComponent, IExamine, IUse, ISuicideAct, ISolutionChange, IHotItem, IAfterInteract
|
||||
public class WelderComponent : ToolComponent, IUse, ISuicideAct, IHotItem, IAfterInteract
|
||||
{
|
||||
[Dependency] private readonly IEntitySystemManager _entitySystemManager = default!;
|
||||
|
||||
public override string Name => "Welder";
|
||||
|
||||
public const string SolutionName = "welder";
|
||||
|
||||
/// <summary>
|
||||
/// Default Cost of using the welder fuel for an action
|
||||
/// </summary>
|
||||
@@ -55,7 +56,6 @@ namespace Content.Server.Tools.Components
|
||||
private bool _welderLit;
|
||||
private WelderSystem _welderSystem = default!;
|
||||
private SpriteComponent? _spriteComponent;
|
||||
private SolutionContainerComponent? _solutionComponent;
|
||||
private PointLightComponent? _pointLightComponent;
|
||||
|
||||
[DataField("weldSounds")]
|
||||
@@ -70,11 +70,19 @@ namespace Content.Server.Tools.Components
|
||||
[DataField("welderRefill")]
|
||||
private SoundSpecifier WelderRefill { get; set; } = new SoundPathSpecifier("/Audio/Effects/refill.ogg");
|
||||
|
||||
[ViewVariables]
|
||||
public float Fuel => _solutionComponent?.Solution?.GetReagentQuantity("WeldingFuel").Float() ?? 0f;
|
||||
[ViewVariables] public float Fuel => WelderSolution?.GetReagentQuantity("WeldingFuel").Float() ?? 0f;
|
||||
|
||||
[ViewVariables]
|
||||
public float FuelCapacity => _solutionComponent?.MaxVolume.Float() ?? 0f;
|
||||
[ViewVariables] public float FuelCapacity => WelderSolution?.MaxVolume.Float() ?? 0f;
|
||||
|
||||
private Solution? WelderSolution
|
||||
{
|
||||
get
|
||||
{
|
||||
Owner.EntityManager.EntitySysManager.GetEntitySystem<SolutionContainerSystem>()
|
||||
.TryGetSolution(Owner, SolutionName, out var solution);
|
||||
return solution;
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Status of welder, whether it is ignited
|
||||
@@ -103,9 +111,10 @@ namespace Content.Server.Tools.Components
|
||||
|
||||
_welderSystem = _entitySystemManager.GetEntitySystem<WelderSystem>();
|
||||
|
||||
Owner.TryGetComponent(out _solutionComponent);
|
||||
Owner.EnsureComponent<SolutionContainerManagerComponent>();
|
||||
Owner.TryGetComponent(out _spriteComponent);
|
||||
Owner.TryGetComponent(out _pointLightComponent);
|
||||
EntitySystem.Get<SolutionContainerSystem>().EnsureSolution(Owner, "welder");
|
||||
}
|
||||
|
||||
public override ComponentState GetComponentState(ICommonSession player)
|
||||
@@ -113,7 +122,8 @@ namespace Content.Server.Tools.Components
|
||||
return new WelderComponentState(FuelCapacity, Fuel, WelderLit);
|
||||
}
|
||||
|
||||
public override async Task<bool> UseTool(IEntity user, IEntity? target, float doAfterDelay, ToolQuality toolQualityNeeded, Func<bool>? doAfterCheck = null)
|
||||
public override async Task<bool> UseTool(IEntity user, IEntity? target, float doAfterDelay,
|
||||
ToolQuality toolQualityNeeded, Func<bool>? doAfterCheck = null)
|
||||
{
|
||||
bool ExtraCheck()
|
||||
{
|
||||
@@ -134,7 +144,8 @@ namespace Content.Server.Tools.Components
|
||||
return toolQualityNeeded.HasFlag(ToolQuality.Welding) ? canUse && TryWeld(DefaultFuelCost, user) : canUse;
|
||||
}
|
||||
|
||||
public async Task<bool> UseTool(IEntity user, IEntity target, float doAfterDelay, ToolQuality toolQualityNeeded, float fuelConsumed, Func<bool>? doAfterCheck = null)
|
||||
public async Task<bool> UseTool(IEntity user, IEntity target, float doAfterDelay, ToolQuality toolQualityNeeded,
|
||||
float fuelConsumed, Func<bool>? doAfterCheck = null)
|
||||
{
|
||||
bool ExtraCheck()
|
||||
{
|
||||
@@ -143,7 +154,8 @@ namespace Content.Server.Tools.Components
|
||||
return extraCheck && CanWeld(fuelConsumed);
|
||||
}
|
||||
|
||||
return await base.UseTool(user, target, doAfterDelay, toolQualityNeeded, ExtraCheck) && TryWeld(fuelConsumed, user);
|
||||
return await base.UseTool(user, target, doAfterDelay, toolQualityNeeded, ExtraCheck) &&
|
||||
TryWeld(fuelConsumed, user);
|
||||
}
|
||||
|
||||
private bool TryWeld(float value, IEntity? user = null, bool silent = false)
|
||||
@@ -164,15 +176,17 @@ namespace Content.Server.Tools.Components
|
||||
return false;
|
||||
}
|
||||
|
||||
if (_solutionComponent == null)
|
||||
if (WelderSolution == null)
|
||||
return false;
|
||||
|
||||
var succeeded = _solutionComponent.TryRemoveReagent("WeldingFuel", ReagentUnit.New(value));
|
||||
var succeeded = EntitySystem.Get<SolutionContainerSystem>()
|
||||
.TryRemoveReagent(Owner.Uid, WelderSolution, "WeldingFuel", ReagentUnit.New(value));
|
||||
|
||||
if (succeeded && !silent)
|
||||
{
|
||||
PlaySound(WeldSounds);
|
||||
}
|
||||
|
||||
return succeeded;
|
||||
}
|
||||
|
||||
@@ -232,26 +246,6 @@ namespace Content.Server.Tools.Components
|
||||
return ToggleWelderStatus(eventArgs.User);
|
||||
}
|
||||
|
||||
public void Examine(FormattedMessage message, bool inDetailsRange)
|
||||
{
|
||||
if (WelderLit)
|
||||
{
|
||||
message.AddMarkup(Loc.GetString("welder-component-on-examine-welder-lit-message") + "\n");
|
||||
}
|
||||
else
|
||||
{
|
||||
message.AddText(Loc.GetString("welder-component-on-examine-welder-not-lit-message") + "\n");
|
||||
}
|
||||
|
||||
if (inDetailsRange)
|
||||
{
|
||||
message.AddMarkup(Loc.GetString("welder-component-on-examine-detailed-message",
|
||||
("colorName", Fuel < FuelCapacity / 4f ? "darkorange" : "orange"),
|
||||
("fuelLeft", Math.Round(Fuel)),
|
||||
("fuelCapacity", FuelCapacity)));
|
||||
}
|
||||
}
|
||||
|
||||
protected override void Shutdown()
|
||||
{
|
||||
base.Shutdown();
|
||||
@@ -263,13 +257,13 @@ namespace Content.Server.Tools.Components
|
||||
if (!HasQuality(ToolQuality.Welding) || !WelderLit || Owner.Deleted)
|
||||
return;
|
||||
|
||||
_solutionComponent?.TryRemoveReagent("WeldingFuel", ReagentUnit.New(FuelLossRate * frameTime));
|
||||
EntitySystem.Get<SolutionContainerSystem>().TryRemoveReagent(Owner.Uid, WelderSolution, "WeldingFuel",
|
||||
ReagentUnit.New(FuelLossRate * frameTime));
|
||||
|
||||
EntitySystem.Get<AtmosphereSystem>().HotspotExpose(Owner.Transform.Coordinates, 700, 50, true);
|
||||
|
||||
if (Fuel == 0)
|
||||
ToggleWelderStatus();
|
||||
|
||||
}
|
||||
|
||||
SuicideKind ISuicideAct.Suicide(IEntity victim, IChatManager chat)
|
||||
@@ -301,12 +295,6 @@ namespace Content.Server.Tools.Components
|
||||
return SuicideKind.Blunt;
|
||||
}
|
||||
|
||||
public void SolutionChanged(SolutionChangeEventArgs eventArgs)
|
||||
{
|
||||
Dirty();
|
||||
}
|
||||
|
||||
|
||||
async Task<bool> IAfterInteract.AfterInteract(AfterInteractEventArgs eventArgs)
|
||||
{
|
||||
if (eventArgs.Target == null || !eventArgs.CanReach)
|
||||
@@ -316,9 +304,9 @@ namespace Content.Server.Tools.Components
|
||||
|
||||
if (eventArgs.Target.TryGetComponent(out ReagentTankComponent? tank)
|
||||
&& tank.TankType == ReagentTankType.Fuel
|
||||
&& eventArgs.Target.TryGetComponent(out ISolutionInteractionsComponent? targetSolution)
|
||||
&& targetSolution.CanDrain
|
||||
&& _solutionComponent != null)
|
||||
&& EntitySystem.Get<SolutionContainerSystem>()
|
||||
.TryGetDrainableSolution(eventArgs.Target.Uid, out var targetSolution)
|
||||
&& WelderSolution != null)
|
||||
{
|
||||
if (WelderLit)
|
||||
{
|
||||
@@ -327,13 +315,14 @@ namespace Content.Server.Tools.Components
|
||||
return true;
|
||||
}
|
||||
|
||||
var trans = ReagentUnit.Min(_solutionComponent.EmptyVolume, targetSolution.DrainAvailable);
|
||||
var trans = ReagentUnit.Min(WelderSolution.AvailableVolume, targetSolution.DrainAvailable);
|
||||
if (trans > 0)
|
||||
{
|
||||
var drained = targetSolution.Drain(trans);
|
||||
_solutionComponent.TryAddSolution(drained);
|
||||
var drained = EntitySystem.Get<SolutionContainerSystem>().Drain(eventArgs.Target.Uid, targetSolution, trans);
|
||||
EntitySystem.Get<SolutionContainerSystem>().TryAddSolution(Owner.Uid, WelderSolution, drained);
|
||||
SoundSystem.Play(Filter.Pvs(Owner), WelderRefill.GetSound(), Owner);
|
||||
eventArgs.Target.PopupMessage(eventArgs.User, Loc.GetString("welder-component-after-interact-refueled-message"));
|
||||
eventArgs.Target.PopupMessage(eventArgs.User,
|
||||
Loc.GetString("welder-component-after-interact-refueled-message"));
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -1,7 +1,11 @@
|
||||
using System.Collections.Generic;
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
using Content.Server.Tools.Components;
|
||||
using Content.Shared.Chemistry.EntitySystems;
|
||||
using Content.Shared.Examine;
|
||||
using Robust.Shared.GameObjects;
|
||||
using Robust.Shared.Localization;
|
||||
|
||||
namespace Content.Server.Tools
|
||||
{
|
||||
@@ -12,6 +16,39 @@ namespace Content.Server.Tools
|
||||
{
|
||||
private readonly HashSet<WelderComponent> _activeWelders = new();
|
||||
|
||||
public override void Initialize()
|
||||
{
|
||||
base.Initialize();
|
||||
|
||||
SubscribeLocalEvent<WelderComponent, SolutionChangedEvent>(OnSolutionChange);
|
||||
SubscribeLocalEvent<WelderComponent, ExaminedEvent>(OnExamine);
|
||||
}
|
||||
|
||||
private void OnExamine(EntityUid uid, WelderComponent component, ExaminedEvent args)
|
||||
{
|
||||
if (component.WelderLit)
|
||||
{
|
||||
args.Message.AddMarkup(Loc.GetString("welder-component-on-examine-welder-lit-message") + "\n");
|
||||
}
|
||||
else
|
||||
{
|
||||
args.Message.AddText(Loc.GetString("welder-component-on-examine-welder-not-lit-message") + "\n");
|
||||
}
|
||||
|
||||
if (args.IsInDetailsRange)
|
||||
{
|
||||
args.Message.AddMarkup(Loc.GetString("welder-component-on-examine-detailed-message",
|
||||
("colorName", component.Fuel < component.FuelCapacity / 4f ? "darkorange" : "orange"),
|
||||
("fuelLeft", Math.Round(component.Fuel)),
|
||||
("fuelCapacity", component.FuelCapacity)));
|
||||
}
|
||||
}
|
||||
|
||||
private void OnSolutionChange(EntityUid uid, WelderComponent component, SolutionChangedEvent args)
|
||||
{
|
||||
component.Dirty();
|
||||
}
|
||||
|
||||
public bool Subscribe(WelderComponent welder)
|
||||
{
|
||||
return _activeWelders.Add(welder);
|
||||
|
||||
@@ -5,6 +5,7 @@ using Content.Server.Body.Circulatory;
|
||||
using Content.Server.Chemistry.Components;
|
||||
using Content.Server.Cooldown;
|
||||
using Content.Server.Weapon.Melee.Components;
|
||||
using Content.Shared.Chemistry.EntitySystems;
|
||||
using Content.Shared.Damage.Components;
|
||||
using Content.Shared.Hands;
|
||||
using Content.Shared.Interaction;
|
||||
@@ -16,7 +17,6 @@ using Robust.Shared.IoC;
|
||||
using Robust.Shared.Map;
|
||||
using Robust.Shared.Maths;
|
||||
using Robust.Shared.Physics;
|
||||
using Robust.Shared.Physics.Broadphase;
|
||||
using Robust.Shared.Player;
|
||||
using Robust.Shared.Timing;
|
||||
|
||||
@@ -25,7 +25,7 @@ namespace Content.Server.Weapon.Melee
|
||||
public sealed class MeleeWeaponSystem : EntitySystem
|
||||
{
|
||||
[Dependency] private IGameTiming _gameTiming = default!;
|
||||
|
||||
[Dependency] private SolutionContainerSystem _solutionsSystem = default!;
|
||||
|
||||
public override void Initialize()
|
||||
{
|
||||
@@ -219,7 +219,7 @@ namespace Content.Server.Weapon.Melee
|
||||
for (var i = 0; i < increments; i++)
|
||||
{
|
||||
var castAngle = new Angle(baseAngle + increment * i);
|
||||
var res = EntitySystem.Get<SharedBroadphaseSystem>().IntersectRay(mapId,
|
||||
var res = Get<SharedBroadphaseSystem>().IntersectRay(mapId,
|
||||
new CollisionRay(position, castAngle.ToWorldVec(),
|
||||
(int) (CollisionGroup.Impassable | CollisionGroup.MobImpassable)), range, ignore).ToList();
|
||||
|
||||
@@ -234,7 +234,8 @@ namespace Content.Server.Weapon.Melee
|
||||
|
||||
private void OnChemicalInjectorHit(EntityUid uid, MeleeChemicalInjectorComponent comp, MeleeHitEvent args)
|
||||
{
|
||||
if (!ComponentManager.TryGetComponent<SolutionContainerComponent>(uid, out var solutionContainer))
|
||||
IEntity owner = EntityManager.GetEntity(uid);
|
||||
if (!_solutionsSystem.TryGetInjectableSolution(owner.Uid, out var solutionContainer))
|
||||
return;
|
||||
|
||||
var hitBloodstreams = new List<BloodstreamComponent>();
|
||||
@@ -250,7 +251,7 @@ namespace Content.Server.Weapon.Melee
|
||||
if (hitBloodstreams.Count < 1)
|
||||
return;
|
||||
|
||||
var removedSolution = solutionContainer.Solution.SplitSolution(comp.TransferAmount * hitBloodstreams.Count);
|
||||
var removedSolution = solutionContainer.SplitSolution(comp.TransferAmount * hitBloodstreams.Count);
|
||||
var removedVol = removedSolution.TotalVolume;
|
||||
var solutionToInject = removedSolution.SplitSolution(removedVol * comp.TransferEfficiency);
|
||||
var volPerBloodstream = solutionToInject.TotalVolume * (1 / hitBloodstreams.Count);
|
||||
|
||||
@@ -1,8 +1,10 @@
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
using Content.Server.Chemistry.Components;
|
||||
using Content.Server.Weapon.Ranged.Barrels.Components;
|
||||
using Content.Shared.Chemistry.Components;
|
||||
using Content.Shared.Chemistry.EntitySystems;
|
||||
using Robust.Shared.GameObjects;
|
||||
using Robust.Shared.Serialization.Manager.Attributes;
|
||||
|
||||
namespace Content.Server.Weapon.Ranged.Ammunition.Components
|
||||
{
|
||||
@@ -10,6 +12,10 @@ namespace Content.Server.Weapon.Ranged.Ammunition.Components
|
||||
public class ChemicalAmmoComponent : Component
|
||||
{
|
||||
public override string Name => "ChemicalAmmo";
|
||||
public const string DefaultSolutionName = "ammo";
|
||||
|
||||
[DataField("solution")]
|
||||
public string SolutionName { get; set; } = DefaultSolutionName;
|
||||
|
||||
public override void HandleMessage(ComponentMessage message, IComponent? component)
|
||||
{
|
||||
@@ -24,32 +30,34 @@ namespace Content.Server.Weapon.Ranged.Ammunition.Components
|
||||
|
||||
private void TransferSolution(BarrelFiredMessage barrelFired)
|
||||
{
|
||||
if (!Owner.TryGetComponent<SolutionContainerComponent>(out var ammoSolutionContainer))
|
||||
if (!EntitySystem.Get<SolutionContainerSystem>().TryGetSolution(Owner, SolutionName, out var ammoSolution))
|
||||
return;
|
||||
|
||||
var projectiles = barrelFired.FiredProjectiles;
|
||||
var solutionContainerSystem = EntitySystem.Get<SolutionContainerSystem>();
|
||||
|
||||
var projectileSolutionContainers = new List<SolutionContainerComponent>();
|
||||
var projectileSolutionContainers = new List<(EntityUid, Solution)>();
|
||||
foreach (var projectile in projectiles)
|
||||
{
|
||||
if (projectile.TryGetComponent<SolutionContainerComponent>(out var projectileSolutionContainer))
|
||||
if (EntitySystem.Get<SolutionContainerSystem>()
|
||||
.TryGetSolution(projectile, SolutionName, out var projectileSolutionContainer))
|
||||
{
|
||||
projectileSolutionContainers.Add(projectileSolutionContainer);
|
||||
projectileSolutionContainers.Add((projectile.Uid, projectileSolutionContainer));
|
||||
}
|
||||
}
|
||||
|
||||
if (!projectileSolutionContainers.Any())
|
||||
return;
|
||||
|
||||
var solutionPerProjectile = ammoSolutionContainer.CurrentVolume * (1 / projectileSolutionContainers.Count);
|
||||
var solutionPerProjectile = ammoSolution.CurrentVolume * (1 / projectileSolutionContainers.Count);
|
||||
|
||||
foreach (var projectileSolutionContainer in projectileSolutionContainers)
|
||||
foreach (var (projectileUid, projectileSolution) in projectileSolutionContainers)
|
||||
{
|
||||
var solutionToTransfer = ammoSolutionContainer.SplitSolution(solutionPerProjectile);
|
||||
projectileSolutionContainer.TryAddSolution(solutionToTransfer);
|
||||
var solutionToTransfer = solutionContainerSystem.SplitSolution(Owner.Uid, ammoSolution, solutionPerProjectile);
|
||||
solutionContainerSystem.TryAddSolution(projectileUid, projectileSolution, solutionToTransfer);
|
||||
}
|
||||
|
||||
ammoSolutionContainer.RemoveAllSolution();
|
||||
solutionContainerSystem.RemoveAllSolution(Owner.Uid, ammoSolution);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
using Content.Shared.Chemistry.Solution;
|
||||
using Content.Shared.Chemistry.Components;
|
||||
using Robust.Shared.GameObjects;
|
||||
|
||||
namespace Content.Shared.Body.Networks
|
||||
@@ -12,5 +12,7 @@ namespace Content.Shared.Body.Networks
|
||||
/// <param name="solution">The solution to be transferred.</param>
|
||||
/// <returns>Whether or not transfer was successful.</returns>
|
||||
public abstract bool TryTransferSolution(Solution solution);
|
||||
|
||||
public const string DefaultSolutionName = "bloodstream";
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,5 +1,3 @@
|
||||
using System;
|
||||
using System.Linq;
|
||||
using Content.Shared.Chemistry.Reaction;
|
||||
using Content.Shared.Chemistry.Reagent;
|
||||
using JetBrains.Annotations;
|
||||
@@ -9,51 +7,20 @@ using Robust.Shared.Prototypes;
|
||||
|
||||
namespace Content.Shared.Chemistry
|
||||
{
|
||||
/// <summary>
|
||||
/// This interface gives components behavior on whether entities solution (implying SolutionComponent is in place) is changed
|
||||
/// </summary>
|
||||
public interface ISolutionChange
|
||||
{
|
||||
/// <summary>
|
||||
/// Called when solution is mixed with some other solution, or when some part of the solution is removed
|
||||
/// </summary>
|
||||
void SolutionChanged(SolutionChangeEventArgs eventArgs);
|
||||
}
|
||||
|
||||
public class SolutionChangeEventArgs : EventArgs
|
||||
{
|
||||
public IEntity Owner { get; set; } = default!;
|
||||
}
|
||||
|
||||
[UsedImplicitly]
|
||||
public class ChemistrySystem : EntitySystem
|
||||
public partial class ChemistrySystem : EntitySystem
|
||||
{
|
||||
[Dependency] private readonly IPrototypeManager _prototypeManager = default!;
|
||||
|
||||
public void HandleSolutionChange(IEntity owner)
|
||||
{
|
||||
var eventArgs = new SolutionChangeEventArgs
|
||||
{
|
||||
Owner = owner,
|
||||
};
|
||||
var solutionChangeArgs = owner.GetAllComponents<ISolutionChange>().ToList();
|
||||
|
||||
foreach (var solutionChangeArg in solutionChangeArgs)
|
||||
{
|
||||
solutionChangeArg.SolutionChanged(eventArgs);
|
||||
|
||||
if (owner.Deleted)
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
public void ReactionEntity(IEntity? entity, ReactionMethod method, string reagentId, ReagentUnit reactVolume, Solution.Solution? source)
|
||||
public void ReactionEntity(IEntity? entity, ReactionMethod method, string reagentId, ReagentUnit reactVolume,
|
||||
Components.Solution? source)
|
||||
{
|
||||
// We throw if the reagent specified doesn't exist.
|
||||
ReactionEntity(entity, method, _prototypeManager.Index<ReagentPrototype>(reagentId), reactVolume, source);
|
||||
}
|
||||
|
||||
public void ReactionEntity(IEntity? entity, ReactionMethod method, ReagentPrototype reagent, ReagentUnit reactVolume, Solution.Solution? source)
|
||||
public void ReactionEntity(IEntity? entity, ReactionMethod method, ReagentPrototype reagent,
|
||||
ReagentUnit reactVolume, Components.Solution? source)
|
||||
{
|
||||
if (entity == null || entity.Deleted || !entity.TryGetComponent(out ReactiveComponent? reactive))
|
||||
return;
|
||||
|
||||
@@ -1,5 +1,6 @@
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Collections.ObjectModel;
|
||||
using Content.Shared.Chemistry.Reagent;
|
||||
using Content.Shared.Cloning;
|
||||
using Robust.Shared.GameObjects;
|
||||
@@ -14,6 +15,7 @@ namespace Content.Shared.Chemistry.Components
|
||||
public class SharedChemMasterComponent : Component
|
||||
{
|
||||
public override string Name => "ChemMaster";
|
||||
public const string SolutionName = "buffer";
|
||||
|
||||
[Serializable, NetSerializable]
|
||||
public class ChemMasterBoundUserInterfaceState : BoundUserInterfaceState
|
||||
@@ -27,11 +29,11 @@ namespace Content.Shared.Chemistry.Components
|
||||
/// <summary>
|
||||
/// A list of the reagents and their amounts within the beaker/reagent container, if applicable.
|
||||
/// </summary>
|
||||
public readonly IReadOnlyList<Solution.Solution.ReagentQuantity> ContainerReagents;
|
||||
public readonly IReadOnlyList<Solution.ReagentQuantity> ContainerReagents;
|
||||
/// <summary>
|
||||
/// A list of the reagents and their amounts within the buffer, if applicable.
|
||||
/// </summary>
|
||||
public readonly IReadOnlyList<Solution.Solution.ReagentQuantity> BufferReagents;
|
||||
public readonly IReadOnlyList<Solution.ReagentQuantity> BufferReagents;
|
||||
public readonly string DispenserName;
|
||||
|
||||
public readonly bool BufferModeTransfer;
|
||||
@@ -39,7 +41,7 @@ namespace Content.Shared.Chemistry.Components
|
||||
public readonly ReagentUnit BufferCurrentVolume;
|
||||
|
||||
public ChemMasterBoundUserInterfaceState(bool hasPower, bool hasBeaker, ReagentUnit beakerCurrentVolume, ReagentUnit beakerMaxVolume, string containerName,
|
||||
string dispenserName, IReadOnlyList<Solution.Solution.ReagentQuantity> containerReagents, IReadOnlyList<Solution.Solution.ReagentQuantity> bufferReagents, bool bufferModeTransfer, ReagentUnit bufferCurrentVolume)
|
||||
string dispenserName, IReadOnlyList<Solution.ReagentQuantity> containerReagents, IReadOnlyList<Solution.ReagentQuantity> bufferReagents, bool bufferModeTransfer, ReagentUnit bufferCurrentVolume)
|
||||
{
|
||||
HasPower = hasPower;
|
||||
HasBeaker = hasBeaker;
|
||||
|
||||
@@ -10,6 +10,7 @@ namespace Content.Shared.Chemistry.Components
|
||||
public abstract class SharedHyposprayComponent : Component
|
||||
{
|
||||
public sealed override string Name => "Hypospray";
|
||||
public const string SolutionName = "hypospray";
|
||||
|
||||
[Serializable, NetSerializable]
|
||||
protected sealed class HyposprayComponentState : ComponentState
|
||||
|
||||
50
Content.Shared/Chemistry/Components/Solution.Managerial.cs
Normal file
50
Content.Shared/Chemistry/Components/Solution.Managerial.cs
Normal file
@@ -0,0 +1,50 @@
|
||||
using Content.Shared.Chemistry.Reagent;
|
||||
using Robust.Shared.GameObjects;
|
||||
using Robust.Shared.Serialization.Manager.Attributes;
|
||||
using Robust.Shared.ViewVariables;
|
||||
|
||||
namespace Content.Shared.Chemistry.Components
|
||||
{
|
||||
public partial class Solution
|
||||
{
|
||||
|
||||
/// <summary>
|
||||
/// If reactions will be checked for when adding reagents to the container.
|
||||
/// </summary>
|
||||
[ViewVariables(VVAccess.ReadWrite)]
|
||||
[DataField("canReact")]
|
||||
public bool CanReact { get; set; } = true;
|
||||
|
||||
/// <summary>
|
||||
/// Volume needed to fill this container.
|
||||
/// </summary>
|
||||
[ViewVariables]
|
||||
public ReagentUnit AvailableVolume => MaxVolume - CurrentVolume;
|
||||
|
||||
public ReagentUnit DrawAvailable => CurrentVolume;
|
||||
public ReagentUnit DrainAvailable => CurrentVolume;
|
||||
|
||||
/// <summary>
|
||||
/// Checks if a solution can fit into the container.
|
||||
/// </summary>
|
||||
/// <param name="solution">The solution that is trying to be added.</param>
|
||||
/// <returns>If the solution can be fully added.</returns>
|
||||
public bool CanAddSolution(Solution solution)
|
||||
{
|
||||
return solution.TotalVolume <= AvailableVolume;
|
||||
}
|
||||
|
||||
[DataField("maxSpillRefill")]
|
||||
public ReagentUnit MaxSpillRefill { get; set; }
|
||||
|
||||
[ViewVariables(VVAccess.ReadWrite)]
|
||||
[DataField("maxVol")]
|
||||
public ReagentUnit MaxVolume { get; set; } = ReagentUnit.Zero;
|
||||
|
||||
[ViewVariables]
|
||||
public ReagentUnit CurrentVolume => TotalVolume;
|
||||
|
||||
// [ViewVariables]
|
||||
// public EntityUid OwnerUid { get; set; }
|
||||
}
|
||||
}
|
||||
@@ -14,27 +14,25 @@ using Robust.Shared.Serialization.TypeSerializers.Implementations.Custom.Prototy
|
||||
using Robust.Shared.Utility;
|
||||
using Robust.Shared.ViewVariables;
|
||||
|
||||
namespace Content.Shared.Chemistry.Solution
|
||||
namespace Content.Shared.Chemistry.Components
|
||||
{
|
||||
/// <summary>
|
||||
/// A solution of reagents.
|
||||
/// </summary>
|
||||
[Serializable, NetSerializable]
|
||||
[DataDefinition]
|
||||
public class Solution : IEnumerable<Solution.ReagentQuantity>, ISerializationHooks
|
||||
public partial class Solution : IEnumerable<Solution.ReagentQuantity>, ISerializationHooks
|
||||
{
|
||||
// Most objects on the station hold only 1 or 2 reagents
|
||||
[ViewVariables]
|
||||
[DataField("reagents")]
|
||||
private List<ReagentQuantity> _contents = new(2);
|
||||
|
||||
public IReadOnlyList<ReagentQuantity> Contents => _contents;
|
||||
public List<ReagentQuantity> Contents = new(2);
|
||||
|
||||
/// <summary>
|
||||
/// The calculated total volume of all reagents in the solution (ex. Total volume of liquid in beaker).
|
||||
/// </summary>
|
||||
[ViewVariables]
|
||||
public ReagentUnit TotalVolume { get; private set; }
|
||||
public ReagentUnit TotalVolume { get; set; }
|
||||
|
||||
public Color Color => GetColor();
|
||||
|
||||
@@ -56,7 +54,7 @@ namespace Content.Shared.Chemistry.Solution
|
||||
void ISerializationHooks.AfterDeserialization()
|
||||
{
|
||||
TotalVolume = ReagentUnit.Zero;
|
||||
_contents.ForEach(reagent => TotalVolume += reagent.Quantity);
|
||||
Contents.ForEach(reagent => TotalVolume += reagent.Quantity);
|
||||
}
|
||||
|
||||
public bool ContainsReagent(string reagentId)
|
||||
@@ -100,18 +98,18 @@ namespace Content.Shared.Chemistry.Solution
|
||||
if (quantity <= 0)
|
||||
return;
|
||||
|
||||
for (var i = 0; i < _contents.Count; i++)
|
||||
for (var i = 0; i < Contents.Count; i++)
|
||||
{
|
||||
var reagent = _contents[i];
|
||||
var reagent = Contents[i];
|
||||
if (reagent.ReagentId != reagentId)
|
||||
continue;
|
||||
|
||||
_contents[i] = new ReagentQuantity(reagentId, reagent.Quantity + quantity);
|
||||
Contents[i] = new ReagentQuantity(reagentId, reagent.Quantity + quantity);
|
||||
TotalVolume += quantity;
|
||||
return;
|
||||
}
|
||||
|
||||
_contents.Add(new ReagentQuantity(reagentId, quantity));
|
||||
Contents.Add(new ReagentQuantity(reagentId, quantity));
|
||||
TotalVolume += quantity;
|
||||
}
|
||||
|
||||
@@ -122,7 +120,7 @@ namespace Content.Shared.Chemistry.Solution
|
||||
public void ScaleSolution(float scale)
|
||||
{
|
||||
if (scale == 1) return;
|
||||
var tempContents = new List<ReagentQuantity>(_contents);
|
||||
var tempContents = new List<ReagentQuantity>(Contents);
|
||||
foreach(ReagentQuantity current in tempContents)
|
||||
{
|
||||
if(scale > 1)
|
||||
@@ -143,10 +141,10 @@ namespace Content.Shared.Chemistry.Solution
|
||||
/// <returns>The quantity in milli-units.</returns>
|
||||
public ReagentUnit GetReagentQuantity(string reagentId)
|
||||
{
|
||||
for (var i = 0; i < _contents.Count; i++)
|
||||
for (var i = 0; i < Contents.Count; i++)
|
||||
{
|
||||
if (_contents[i].ReagentId == reagentId)
|
||||
return _contents[i].Quantity;
|
||||
if (Contents[i].ReagentId == reagentId)
|
||||
return Contents[i].Quantity;
|
||||
}
|
||||
|
||||
return ReagentUnit.New(0);
|
||||
@@ -157,9 +155,9 @@ namespace Content.Shared.Chemistry.Solution
|
||||
if(quantity <= 0)
|
||||
return;
|
||||
|
||||
for (var i = 0; i < _contents.Count; i++)
|
||||
for (var i = 0; i < Contents.Count; i++)
|
||||
{
|
||||
var reagent = _contents[i];
|
||||
var reagent = Contents[i];
|
||||
if(reagent.ReagentId != reagentId)
|
||||
continue;
|
||||
|
||||
@@ -168,12 +166,12 @@ namespace Content.Shared.Chemistry.Solution
|
||||
var newQuantity = curQuantity - quantity;
|
||||
if (newQuantity <= 0)
|
||||
{
|
||||
_contents.RemoveSwap(i);
|
||||
Contents.RemoveSwap(i);
|
||||
TotalVolume -= curQuantity;
|
||||
}
|
||||
else
|
||||
{
|
||||
_contents[i] = new ReagentQuantity(reagentId, newQuantity);
|
||||
Contents[i] = new ReagentQuantity(reagentId, newQuantity);
|
||||
TotalVolume -= quantity;
|
||||
}
|
||||
|
||||
@@ -198,16 +196,16 @@ namespace Content.Shared.Chemistry.Solution
|
||||
return;
|
||||
}
|
||||
|
||||
for (var i = 0; i < _contents.Count; i++)
|
||||
for (var i = 0; i < Contents.Count; i++)
|
||||
{
|
||||
var reagent = _contents[i];
|
||||
var reagent = Contents[i];
|
||||
var oldQuantity = reagent.Quantity;
|
||||
|
||||
// quantity taken is always a little greedy, so fractional quantities get rounded up to the nearest
|
||||
// whole unit. This should prevent little bits of chemical remaining because of float rounding errors.
|
||||
var newQuantity = oldQuantity * ratio;
|
||||
|
||||
_contents[i] = new ReagentQuantity(reagent.ReagentId, newQuantity);
|
||||
Contents[i] = new ReagentQuantity(reagent.ReagentId, newQuantity);
|
||||
}
|
||||
|
||||
TotalVolume = TotalVolume * ratio;
|
||||
@@ -215,7 +213,7 @@ namespace Content.Shared.Chemistry.Solution
|
||||
|
||||
public void RemoveAllSolution()
|
||||
{
|
||||
_contents.Clear();
|
||||
Contents.Clear();
|
||||
TotalVolume = ReagentUnit.New(0);
|
||||
}
|
||||
|
||||
@@ -237,17 +235,17 @@ namespace Content.Shared.Chemistry.Solution
|
||||
var newTotalVolume = ReagentUnit.New(0);
|
||||
var remainingVolume = TotalVolume;
|
||||
|
||||
for (var i = 0; i < _contents.Count; i++)
|
||||
for (var i = 0; i < Contents.Count; i++)
|
||||
{
|
||||
var reagent = _contents[i];
|
||||
var reagent = Contents[i];
|
||||
var ratio = (remainingVolume - quantity).Double() / remainingVolume.Double();
|
||||
remainingVolume -= reagent.Quantity;
|
||||
|
||||
var newQuantity = reagent.Quantity * ratio;
|
||||
var splitQuantity = reagent.Quantity - newQuantity;
|
||||
|
||||
_contents[i] = new ReagentQuantity(reagent.ReagentId, newQuantity);
|
||||
newSolution._contents.Add(new ReagentQuantity(reagent.ReagentId, splitQuantity));
|
||||
Contents[i] = new ReagentQuantity(reagent.ReagentId, newQuantity);
|
||||
newSolution.Contents.Add(new ReagentQuantity(reagent.ReagentId, splitQuantity));
|
||||
newTotalVolume += splitQuantity;
|
||||
quantity -= splitQuantity;
|
||||
}
|
||||
@@ -260,25 +258,25 @@ namespace Content.Shared.Chemistry.Solution
|
||||
|
||||
public void AddSolution(Solution otherSolution)
|
||||
{
|
||||
for (var i = 0; i < otherSolution._contents.Count; i++)
|
||||
for (var i = 0; i < otherSolution.Contents.Count; i++)
|
||||
{
|
||||
var otherReagent = otherSolution._contents[i];
|
||||
var otherReagent = otherSolution.Contents[i];
|
||||
|
||||
var found = false;
|
||||
for (var j = 0; j < _contents.Count; j++)
|
||||
for (var j = 0; j < Contents.Count; j++)
|
||||
{
|
||||
var reagent = _contents[j];
|
||||
var reagent = Contents[j];
|
||||
if (reagent.ReagentId == otherReagent.ReagentId)
|
||||
{
|
||||
found = true;
|
||||
_contents[j] = new ReagentQuantity(reagent.ReagentId, reagent.Quantity + otherReagent.Quantity);
|
||||
Contents[j] = new ReagentQuantity(reagent.ReagentId, reagent.Quantity + otherReagent.Quantity);
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
if (!found)
|
||||
{
|
||||
_contents.Add(new ReagentQuantity(otherReagent.ReagentId, otherReagent.Quantity));
|
||||
Contents.Add(new ReagentQuantity(otherReagent.ReagentId, otherReagent.Quantity));
|
||||
}
|
||||
}
|
||||
|
||||
@@ -322,10 +320,10 @@ namespace Content.Shared.Chemistry.Solution
|
||||
var volume = ReagentUnit.New(0);
|
||||
var newSolution = new Solution();
|
||||
|
||||
for (var i = 0; i < _contents.Count; i++)
|
||||
for (var i = 0; i < Contents.Count; i++)
|
||||
{
|
||||
var reagent = _contents[i];
|
||||
newSolution._contents.Add(reagent);
|
||||
var reagent = Contents[i];
|
||||
newSolution.Contents.Add(reagent);
|
||||
volume += reagent.Quantity;
|
||||
}
|
||||
|
||||
@@ -337,7 +335,7 @@ namespace Content.Shared.Chemistry.Solution
|
||||
{
|
||||
var chemistry = EntitySystem.Get<ChemistrySystem>();
|
||||
|
||||
foreach (var (reagentId, quantity) in _contents.ToArray())
|
||||
foreach (var (reagentId, quantity) in Contents.ToArray())
|
||||
{
|
||||
chemistry.ReactionEntity(entity, method, reagentId, quantity, this);
|
||||
}
|
||||
@@ -377,7 +375,7 @@ namespace Content.Shared.Chemistry.Solution
|
||||
|
||||
public IEnumerator<ReagentQuantity> GetEnumerator()
|
||||
{
|
||||
return _contents.GetEnumerator();
|
||||
return Contents.GetEnumerator();
|
||||
}
|
||||
|
||||
IEnumerator IEnumerable.GetEnumerator()
|
||||
@@ -0,0 +1,23 @@
|
||||
using Robust.Shared.GameObjects;
|
||||
using Robust.Shared.Serialization.Manager.Attributes;
|
||||
using Robust.Shared.ViewVariables;
|
||||
|
||||
namespace Content.Shared.Chemistry.Components.SolutionManager
|
||||
{
|
||||
/// <summary>
|
||||
/// Denotes the solution that can be easily removed through any reagent container.
|
||||
/// Think pouring this or draining from a water tank.
|
||||
/// </summary>
|
||||
[RegisterComponent]
|
||||
public class DrainableSolutionComponent : Component
|
||||
{
|
||||
public override string Name => "DrainableSolution";
|
||||
|
||||
/// <summary>
|
||||
/// Solution name that can be drained.
|
||||
/// </summary>
|
||||
[ViewVariables(VVAccess.ReadWrite)]
|
||||
[DataField("solution")]
|
||||
public string Solution { get; set; } = "default";
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,22 @@
|
||||
using Robust.Shared.GameObjects;
|
||||
using Robust.Shared.Serialization.Manager.Attributes;
|
||||
using Robust.Shared.ViewVariables;
|
||||
|
||||
namespace Content.Shared.Chemistry.Components.SolutionManager
|
||||
{
|
||||
/// <summary>
|
||||
/// Denotes the solution that can removed be with syringes.
|
||||
/// </summary>
|
||||
[RegisterComponent]
|
||||
public class DrawableSolutionComponent : Component
|
||||
{
|
||||
public override string Name => "DrawableSolution";
|
||||
|
||||
/// <summary>
|
||||
/// Solution name that can be removed with syringes.
|
||||
/// </summary>
|
||||
[ViewVariables(VVAccess.ReadWrite)]
|
||||
[DataField("solution")]
|
||||
public string Solution { get; set; } = "default";
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,16 @@
|
||||
using Robust.Shared.GameObjects;
|
||||
using Robust.Shared.Serialization.Manager.Attributes;
|
||||
using Robust.Shared.ViewVariables;
|
||||
|
||||
namespace Content.Shared.Chemistry.Components.SolutionManager
|
||||
{
|
||||
[RegisterComponent]
|
||||
public class ExaminableSolutionComponent: Component
|
||||
{
|
||||
public override string Name => "ExaminableSolution";
|
||||
|
||||
[ViewVariables(VVAccess.ReadWrite)]
|
||||
[DataField("solution")]
|
||||
public string Solution { get; set; } = "default";
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,24 @@
|
||||
using Robust.Shared.GameObjects;
|
||||
using Robust.Shared.Serialization.Manager.Attributes;
|
||||
using Robust.Shared.ViewVariables;
|
||||
|
||||
namespace Content.Shared.Chemistry.Components.SolutionManager
|
||||
{
|
||||
/// Allows the entity with this component to be placed in a <c>SharedReagentDispenserComponent</c>.
|
||||
/// <para>Otherwise it's considered to be too large or the improper shape to fit.</para>
|
||||
/// <para>Allows us to have obscenely large containers that are harder to abuse in chem dispensers
|
||||
/// since they can't be placed directly in them.</para>
|
||||
/// <see cref="Content.Shared.Chemistry.Dispenser.SharedReagentDispenserComponent"/>
|
||||
[RegisterComponent]
|
||||
public class FitsInDispenserComponent : Component
|
||||
{
|
||||
public override string Name => "FitsInDispenser";
|
||||
|
||||
/// <summary>
|
||||
/// Solution name that will interact with ReagentDispenserComponent.
|
||||
/// </summary>
|
||||
[ViewVariables(VVAccess.ReadWrite)]
|
||||
[DataField("solution")]
|
||||
public string Solution { get; set; } = "default";
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,22 @@
|
||||
using Robust.Shared.GameObjects;
|
||||
using Robust.Shared.Serialization.Manager.Attributes;
|
||||
using Robust.Shared.ViewVariables;
|
||||
|
||||
namespace Content.Shared.Chemistry.Components.SolutionManager
|
||||
{
|
||||
/// <summary>
|
||||
/// Denotes a solution which can be added with syringes.
|
||||
/// </summary>
|
||||
[RegisterComponent]
|
||||
public class InjectableSolutionComponent : Component
|
||||
{
|
||||
public override string Name => "InjectableSolution";
|
||||
|
||||
/// <summary>
|
||||
/// Solution name which can be added with syringes.
|
||||
/// </summary>
|
||||
[ViewVariables(VVAccess.ReadWrite)]
|
||||
[DataField("solution")]
|
||||
public string Solution { get; set; } = "default";
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,25 @@
|
||||
using Robust.Shared.GameObjects;
|
||||
using Robust.Shared.Serialization.Manager.Attributes;
|
||||
using Robust.Shared.ViewVariables;
|
||||
|
||||
namespace Content.Shared.Chemistry.Components.SolutionManager
|
||||
{
|
||||
/// <summary>
|
||||
/// Reagents that can be added easily. For example like
|
||||
/// pouring something into another beaker, glass, or into the gas
|
||||
/// tank of a car.
|
||||
/// </summary>
|
||||
[RegisterComponent]
|
||||
public class RefillableSolutionComponent : Component
|
||||
{
|
||||
public override string Name => "RefillableSolution";
|
||||
|
||||
/// <summary>
|
||||
/// Solution name that can added to easily.
|
||||
/// </summary>
|
||||
[ViewVariables(VVAccess.ReadWrite)]
|
||||
[DataField("solution")]
|
||||
public string Solution { get; set; } = "default";
|
||||
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,23 @@
|
||||
using System.Collections.Generic;
|
||||
using Content.Shared.Chemistry.EntitySystems;
|
||||
using Robust.Shared.Analyzers;
|
||||
using Robust.Shared.GameObjects;
|
||||
using Robust.Shared.GameStates;
|
||||
using Robust.Shared.Serialization.Manager.Attributes;
|
||||
using Robust.Shared.ViewVariables;
|
||||
|
||||
namespace Content.Shared.Chemistry.Components.SolutionManager
|
||||
{
|
||||
[NetworkedComponent()]
|
||||
[RegisterComponent]
|
||||
[DataDefinition]
|
||||
[Friend(typeof(SolutionContainerSystem))]
|
||||
public class SolutionContainerManagerComponent : Component
|
||||
{
|
||||
public override string Name => "SolutionContainerManager";
|
||||
|
||||
[ViewVariables]
|
||||
[DataField("solutions")]
|
||||
public readonly Dictionary<string, Solution> Solutions = new();
|
||||
}
|
||||
}
|
||||
@@ -36,12 +36,12 @@ namespace Content.Shared.Chemistry.Dispenser
|
||||
/// <summary>
|
||||
/// A list of the reagents and their amounts within the beaker/reagent container, if applicable.
|
||||
/// </summary>
|
||||
public readonly List<Solution.Solution.ReagentQuantity>? ContainerReagents;
|
||||
public readonly List<Components.Solution.ReagentQuantity>? ContainerReagents;
|
||||
public readonly string DispenserName;
|
||||
public readonly ReagentUnit SelectedDispenseAmount;
|
||||
|
||||
public ReagentDispenserBoundUserInterfaceState(bool hasPower, bool hasBeaker, ReagentUnit beakerCurrentVolume, ReagentUnit beakerMaxVolume, string containerName,
|
||||
List<ReagentDispenserInventoryEntry> inventory, string dispenserName, List<Solution.Solution.ReagentQuantity>? containerReagents, ReagentUnit selectedDispenseAmount)
|
||||
List<ReagentDispenserInventoryEntry> inventory, string dispenserName, List<Components.Solution.ReagentQuantity>? containerReagents, ReagentUnit selectedDispenseAmount)
|
||||
{
|
||||
HasPower = hasPower;
|
||||
HasBeaker = hasBeaker;
|
||||
|
||||
@@ -0,0 +1,132 @@
|
||||
using System.Diagnostics.CodeAnalysis;
|
||||
using Content.Shared.Chemistry.Components;
|
||||
using Content.Shared.Chemistry.Components.SolutionManager;
|
||||
using Content.Shared.Chemistry.Reagent;
|
||||
using Robust.Shared.GameObjects;
|
||||
|
||||
namespace Content.Shared.Chemistry.EntitySystems
|
||||
{
|
||||
public partial class SolutionContainerSystem
|
||||
{
|
||||
public void Refill(EntityUid targetUid, Solution targetSolution, Solution addedSolution)
|
||||
{
|
||||
if (!ComponentManager.HasComponent<RefillableSolutionComponent>(targetUid))
|
||||
return;
|
||||
|
||||
TryAddSolution(targetUid, targetSolution, addedSolution);
|
||||
}
|
||||
|
||||
public void Inject(EntityUid targetUid, Solution targetSolution, Solution addedSolution)
|
||||
{
|
||||
if (!ComponentManager.HasComponent<InjectableSolutionComponent>(targetUid))
|
||||
return;
|
||||
|
||||
TryAddSolution(targetUid, targetSolution, addedSolution);
|
||||
}
|
||||
|
||||
public Solution Draw(EntityUid targetUid, Solution solution, ReagentUnit amount)
|
||||
{
|
||||
if (!ComponentManager.HasComponent<DrawableSolutionComponent>(targetUid))
|
||||
{
|
||||
return new Solution();
|
||||
}
|
||||
|
||||
return SplitSolution(targetUid, solution, amount);
|
||||
}
|
||||
|
||||
public Solution Drain(EntityUid targetUid, Solution targetSolution, ReagentUnit amount)
|
||||
{
|
||||
if (!ComponentManager.HasComponent<DrainableSolutionComponent>(targetUid))
|
||||
{
|
||||
return new Solution();
|
||||
}
|
||||
|
||||
return SplitSolution(targetUid, targetSolution, amount);
|
||||
}
|
||||
|
||||
public bool TryGetInjectableSolution(EntityUid targetUid,
|
||||
[NotNullWhen(true)] out Solution? solution)
|
||||
{
|
||||
if (ComponentManager.TryGetComponent(targetUid, out InjectableSolutionComponent? injectable) &&
|
||||
ComponentManager.TryGetComponent(targetUid, out SolutionContainerManagerComponent? manager) &&
|
||||
manager.Solutions.TryGetValue(injectable.Solution, out solution))
|
||||
{
|
||||
return true;
|
||||
}
|
||||
|
||||
solution = null;
|
||||
return false;
|
||||
}
|
||||
|
||||
public bool TryGetRefillableSolution(EntityUid targetUid,
|
||||
[NotNullWhen(true)] out Solution? solution)
|
||||
{
|
||||
if (ComponentManager.TryGetComponent(targetUid, out RefillableSolutionComponent? refillable) &&
|
||||
ComponentManager.TryGetComponent(targetUid, out SolutionContainerManagerComponent? manager) &&
|
||||
manager.Solutions.TryGetValue(refillable.Solution, out var refillableSolution))
|
||||
{
|
||||
solution = refillableSolution;
|
||||
return true;
|
||||
}
|
||||
|
||||
solution = null;
|
||||
return false;
|
||||
}
|
||||
|
||||
public bool TryGetDrainableSolution(EntityUid targetUid,
|
||||
[NotNullWhen(true)] out Solution? solution)
|
||||
{
|
||||
if (ComponentManager.TryGetComponent(targetUid,out DrainableSolutionComponent? drainable) &&
|
||||
ComponentManager.TryGetComponent(targetUid,out SolutionContainerManagerComponent? manager) &&
|
||||
manager.Solutions.TryGetValue(drainable.Solution, out solution))
|
||||
{
|
||||
return true;
|
||||
}
|
||||
|
||||
solution = null;
|
||||
return false;
|
||||
}
|
||||
|
||||
public bool TryGetDrawableSolution(IEntity owner,
|
||||
[NotNullWhen(true)] out Solution? solution)
|
||||
{
|
||||
if (owner.TryGetComponent(out DrawableSolutionComponent? drawable) &&
|
||||
owner.TryGetComponent(out SolutionContainerManagerComponent? manager) &&
|
||||
manager.Solutions.TryGetValue(drawable.Solution, out solution))
|
||||
{
|
||||
return true;
|
||||
}
|
||||
|
||||
solution = null;
|
||||
return false;
|
||||
}
|
||||
|
||||
public ReagentUnit DrainAvailable(IEntity? owner)
|
||||
{
|
||||
if (owner == null || !TryGetDrainableSolution(owner.Uid, out var solution))
|
||||
return ReagentUnit.Zero;
|
||||
|
||||
return solution.CurrentVolume;
|
||||
}
|
||||
|
||||
public bool HasFitsInDispenser(IEntity owner)
|
||||
{
|
||||
return !owner.Deleted && owner.HasComponent<FitsInDispenserComponent>();
|
||||
}
|
||||
|
||||
public bool TryGetFitsInDispenser(EntityUid owner,
|
||||
[NotNullWhen(true)] out Solution? solution)
|
||||
{
|
||||
if (EntityManager.TryGetEntity(owner, out var ownerEntity) &&
|
||||
ownerEntity.TryGetComponent(out FitsInDispenserComponent? dispenserFits) &&
|
||||
ownerEntity.TryGetComponent(out SolutionContainerManagerComponent? manager) &&
|
||||
manager.Solutions.TryGetValue(dispenserFits.Solution, out solution))
|
||||
{
|
||||
return true;
|
||||
}
|
||||
|
||||
solution = null;
|
||||
return false;
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,342 @@
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Diagnostics.CodeAnalysis;
|
||||
using Content.Shared.Chemistry.Components;
|
||||
using Content.Shared.Chemistry.Components.SolutionManager;
|
||||
using Content.Shared.Chemistry.Reaction;
|
||||
using Content.Shared.Chemistry.Reagent;
|
||||
using Content.Shared.Examine;
|
||||
using JetBrains.Annotations;
|
||||
using Robust.Shared.GameObjects;
|
||||
using Robust.Shared.IoC;
|
||||
using Robust.Shared.Localization;
|
||||
using Robust.Shared.Log;
|
||||
using Robust.Shared.Maths;
|
||||
using Robust.Shared.Prototypes;
|
||||
using Robust.Shared.Serialization;
|
||||
using Robust.Shared.Utility;
|
||||
|
||||
namespace Content.Shared.Chemistry.EntitySystems
|
||||
{
|
||||
/// <summary>
|
||||
/// This event alerts system that the solution was changed
|
||||
/// </summary>
|
||||
public class SolutionChangedEvent : EntityEventArgs
|
||||
{
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Part of Chemistry system deal with SolutionContainers
|
||||
/// </summary>
|
||||
[UsedImplicitly]
|
||||
public partial class SolutionContainerSystem : EntitySystem
|
||||
{
|
||||
[Dependency]
|
||||
private readonly SharedChemicalReactionSystem _chemistrySystem = default!;
|
||||
|
||||
[Dependency]
|
||||
private readonly IPrototypeManager _prototypeManager = default!;
|
||||
|
||||
public override void Initialize()
|
||||
{
|
||||
base.Initialize();
|
||||
|
||||
SubscribeLocalEvent<SolutionContainerManagerComponent, ComponentInit>(InitSolution);
|
||||
SubscribeLocalEvent<ExaminableSolutionComponent, ExaminedEvent>(OnExamineSolution);
|
||||
}
|
||||
|
||||
private void InitSolution(EntityUid uid, SolutionContainerManagerComponent component, ComponentInit args)
|
||||
{
|
||||
foreach (var keyValue in component.Solutions)
|
||||
{
|
||||
var solutionHolder = keyValue.Value;
|
||||
// solutionHolder.OwnerUid = component.Owner.Uid;
|
||||
if (solutionHolder.MaxVolume == ReagentUnit.Zero && solutionHolder.TotalVolume > solutionHolder.MaxVolume)
|
||||
{
|
||||
solutionHolder.MaxVolume = solutionHolder.TotalVolume;
|
||||
}
|
||||
|
||||
UpdateAppearance(uid, solutionHolder);
|
||||
}
|
||||
}
|
||||
|
||||
private void OnExamineSolution(EntityUid uid, ExaminableSolutionComponent examinableComponent,
|
||||
ExaminedEvent args)
|
||||
{
|
||||
if (!args.Examined.TryGetComponent(out SolutionContainerManagerComponent? solutionsManager)
|
||||
|| !solutionsManager.Solutions.TryGetValue(examinableComponent.Solution, out var solutionHolder))
|
||||
return;
|
||||
|
||||
if (solutionHolder.Contents.Count == 0)
|
||||
{
|
||||
args.Message.AddText(Loc.GetString("shared-solution-container-component-on-examine-empty-container"));
|
||||
return;
|
||||
}
|
||||
|
||||
var primaryReagent = solutionHolder.GetPrimaryReagentId();
|
||||
|
||||
if (!_prototypeManager.TryIndex(primaryReagent, out ReagentPrototype? proto))
|
||||
{
|
||||
Logger.Error(
|
||||
$"{nameof(Solution)} could not find the prototype associated with {primaryReagent}.");
|
||||
return;
|
||||
}
|
||||
|
||||
var colorHex = solutionHolder.Color
|
||||
.ToHexNoAlpha(); //TODO: If the chem has a dark color, the examine text becomes black on a black background, which is unreadable.
|
||||
var messageString = "shared-solution-container-component-on-examine-main-text";
|
||||
|
||||
args.Message.AddMarkup(Loc.GetString(messageString,
|
||||
("color", colorHex),
|
||||
("wordedAmount", Loc.GetString(solutionHolder.Contents.Count == 1
|
||||
? "shared-solution-container-component-on-examine-worded-amount-one-reagent"
|
||||
: "shared-solution-container-component-on-examine-worded-amount-multiple-reagents")),
|
||||
("desc", Loc.GetString(proto.PhysicalDescription))));
|
||||
}
|
||||
|
||||
private void UpdateAppearance(EntityUid uid, Solution solution)
|
||||
{
|
||||
if (!EntityManager.TryGetEntity(uid, out var solutionEntity)
|
||||
|| solutionEntity.Deleted
|
||||
|| !solutionEntity.TryGetComponent<SharedAppearanceComponent>(out var appearance))
|
||||
return;
|
||||
|
||||
var filledVolumeFraction = solution.CurrentVolume.Float() / solution.MaxVolume.Float();
|
||||
appearance.SetData(SolutionContainerVisuals.VisualState, new SolutionContainerVisualState(solution.Color, filledVolumeFraction));
|
||||
solutionEntity.Dirty();
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Removes part of the solution in the container.
|
||||
/// </summary>
|
||||
/// <param name="targetUid"></param>
|
||||
/// <param name="solutionHolder"></param>
|
||||
/// <param name="quantity">the volume of solution to remove.</param>
|
||||
/// <returns>The solution that was removed.</returns>
|
||||
public Solution SplitSolution(EntityUid targetUid, Solution solutionHolder, ReagentUnit quantity)
|
||||
{
|
||||
var splitSol = solutionHolder.SplitSolution(quantity);
|
||||
UpdateChemicals(targetUid, solutionHolder);
|
||||
return splitSol;
|
||||
}
|
||||
|
||||
private void UpdateChemicals(EntityUid uid, Solution solutionHolder, bool needsReactionsProcessing = false)
|
||||
{
|
||||
// Process reactions
|
||||
if (needsReactionsProcessing && solutionHolder.CanReact)
|
||||
{
|
||||
_chemistrySystem
|
||||
.FullyReactSolution(solutionHolder, EntityManager.GetEntity(uid), solutionHolder.MaxVolume);
|
||||
}
|
||||
|
||||
UpdateAppearance(uid, solutionHolder);
|
||||
RaiseLocalEvent(uid, new SolutionChangedEvent());
|
||||
}
|
||||
|
||||
public void RemoveAllSolution(EntityUid uid, Solution solutionHolder)
|
||||
{
|
||||
if (solutionHolder.CurrentVolume == 0)
|
||||
return;
|
||||
|
||||
solutionHolder.RemoveAllSolution();
|
||||
UpdateChemicals(uid, solutionHolder);
|
||||
}
|
||||
|
||||
public void RemoveAllSolution(EntityUid uid)
|
||||
{
|
||||
if (!ComponentManager.TryGetComponent(uid, out SolutionContainerManagerComponent? solutionContainerManager))
|
||||
return;
|
||||
|
||||
foreach (var solution in solutionContainerManager.Solutions.Values)
|
||||
{
|
||||
RemoveAllSolution(uid, solution);
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Adds reagent of an Id to the container.
|
||||
/// </summary>
|
||||
/// <param name="targetUid"></param>
|
||||
/// <param name="targetSolution">Container to which we are adding reagent</param>
|
||||
/// <param name="reagentId">The Id of the reagent to add.</param>
|
||||
/// <param name="quantity">The amount of reagent to add.</param>
|
||||
/// <param name="acceptedQuantity">The amount of reagent successfully added.</param>
|
||||
/// <returns>If all the reagent could be added.</returns>
|
||||
public bool TryAddReagent(EntityUid targetUid, Solution targetSolution, string reagentId, ReagentUnit quantity,
|
||||
out ReagentUnit acceptedQuantity)
|
||||
{
|
||||
acceptedQuantity = targetSolution.AvailableVolume > quantity ? quantity : targetSolution.AvailableVolume;
|
||||
targetSolution.AddReagent(reagentId, acceptedQuantity);
|
||||
|
||||
if (acceptedQuantity > 0)
|
||||
UpdateChemicals(targetUid, targetSolution, true);
|
||||
|
||||
return acceptedQuantity == quantity;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Removes reagent of an Id to the container.
|
||||
/// </summary>
|
||||
/// <param name="targetUid"></param>
|
||||
/// <param name="container">Solution container from which we are removing reagent</param>
|
||||
/// <param name="reagentId">The Id of the reagent to remove.</param>
|
||||
/// <param name="quantity">The amount of reagent to remove.</param>
|
||||
/// <returns>If the reagent to remove was found in the container.</returns>
|
||||
public bool TryRemoveReagent(EntityUid targetUid, Solution? container, string reagentId, ReagentUnit quantity)
|
||||
{
|
||||
if (container == null || !container.ContainsReagent(reagentId))
|
||||
return false;
|
||||
|
||||
container.RemoveReagent(reagentId, quantity);
|
||||
UpdateChemicals(targetUid, container);
|
||||
return true;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Adds a solution to the container, if it can fully fit.
|
||||
/// </summary>
|
||||
/// <param name="targetUid"></param>
|
||||
/// <param name="targetSolution">The container to which we try to add.</param>
|
||||
/// <param name="solution">The solution to try to add.</param>
|
||||
/// <returns>If the solution could be added.</returns>
|
||||
public bool TryAddSolution(EntityUid targetUid, Solution? targetSolution, Solution solution)
|
||||
{
|
||||
if (targetSolution == null || !targetSolution.CanAddSolution(solution) || solution.TotalVolume == 0)
|
||||
return false;
|
||||
|
||||
targetSolution.AddSolution(solution);
|
||||
UpdateChemicals(targetUid, targetSolution, true);
|
||||
return true;
|
||||
}
|
||||
|
||||
public bool TryGetSolution(IEntity? target, string name,
|
||||
[NotNullWhen(true)] out Solution? solution)
|
||||
{
|
||||
if (target == null || target.Deleted)
|
||||
{
|
||||
solution = null;
|
||||
return false;
|
||||
}
|
||||
|
||||
return TryGetSolution(target.Uid, name, out solution);
|
||||
}
|
||||
|
||||
public bool TryGetSolution(EntityUid uid, string name,
|
||||
[NotNullWhen(true)] out Solution? solution)
|
||||
{
|
||||
if (!ComponentManager.TryGetComponent(uid, out SolutionContainerManagerComponent? solutionsMgr))
|
||||
{
|
||||
solution = null;
|
||||
return false;
|
||||
}
|
||||
|
||||
return solutionsMgr.Solutions.TryGetValue(name, out solution);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Will ensure a solution is added to given entity even if it's missing solutionContainerManager
|
||||
/// </summary>
|
||||
/// <param name="owner">Entity to which to add solution</param>
|
||||
/// <param name="name">name for the solution</param>
|
||||
/// <returns>solution</returns>
|
||||
public Solution EnsureSolution(IEntity owner, string name)
|
||||
{
|
||||
var solutionsMgr = owner.EnsureComponent<SolutionContainerManagerComponent>();
|
||||
if (!solutionsMgr.Solutions.ContainsKey(name))
|
||||
{
|
||||
var newSolution = new Solution();
|
||||
solutionsMgr.Solutions.Add(name, newSolution);
|
||||
}
|
||||
|
||||
return solutionsMgr.Solutions[name];
|
||||
}
|
||||
|
||||
public string[] RemoveEachReagent(Solution solution, ReagentUnit quantity)
|
||||
{
|
||||
var removedReagent = new string[solution.Contents.Count];
|
||||
if (quantity <= 0)
|
||||
return Array.Empty<string>();
|
||||
|
||||
var pos = 0;
|
||||
for (var i = 0; i < solution.Contents.Count; i++)
|
||||
{
|
||||
var (reagentId, curQuantity) = solution.Contents[i];
|
||||
removedReagent[pos++] = reagentId;
|
||||
|
||||
var newQuantity = curQuantity - quantity;
|
||||
if (newQuantity <= 0)
|
||||
{
|
||||
solution.Contents.RemoveSwap(i);
|
||||
solution.TotalVolume -= curQuantity;
|
||||
}
|
||||
else
|
||||
{
|
||||
solution.Contents[i] = new Solution.ReagentQuantity(reagentId, newQuantity);
|
||||
solution.TotalVolume -= quantity;
|
||||
}
|
||||
}
|
||||
|
||||
return removedReagent;
|
||||
}
|
||||
|
||||
public void TryRemoveAllReagents(Solution solution, List<Solution.ReagentQuantity> removeReagents)
|
||||
{
|
||||
foreach (var reagent in removeReagents)
|
||||
{
|
||||
solution.RemoveReagent(reagent.ReagentId, reagent.Quantity);
|
||||
}
|
||||
}
|
||||
|
||||
public ReagentUnit GetReagentQuantity(EntityUid ownerUid, string reagentId)
|
||||
{
|
||||
var reagentQuantity = ReagentUnit.New(0);
|
||||
if (EntityManager.TryGetEntity(ownerUid, out var owner)
|
||||
&& owner.TryGetComponent(out SolutionContainerManagerComponent? managerComponent))
|
||||
{
|
||||
foreach (var solution in managerComponent.Solutions.Values)
|
||||
{
|
||||
reagentQuantity += solution.GetReagentQuantity(reagentId);
|
||||
}
|
||||
}
|
||||
|
||||
return reagentQuantity;
|
||||
}
|
||||
}
|
||||
|
||||
[Serializable, NetSerializable]
|
||||
public enum SolutionContainerVisuals : byte
|
||||
{
|
||||
VisualState
|
||||
}
|
||||
|
||||
[Serializable, NetSerializable]
|
||||
public class SolutionContainerVisualState
|
||||
{
|
||||
public readonly Color Color;
|
||||
|
||||
/// <summary>
|
||||
/// Represents how full the container is, as a fraction equivalent to <see cref="FilledVolumeFraction"/>/<see cref="byte.MaxValue"/>.
|
||||
/// </summary>
|
||||
public readonly byte FilledVolumeFraction;
|
||||
|
||||
// do we really need this just to save three bytes?
|
||||
public float FilledVolumePercent => (float) FilledVolumeFraction / byte.MaxValue;
|
||||
|
||||
/// <summary>
|
||||
/// Sets the solution state of a container.
|
||||
/// </summary>
|
||||
/// <param name="color"></param>
|
||||
/// <param name="filledVolumeFraction">The fraction of the container's volume that is filled.</param>
|
||||
public SolutionContainerVisualState(Color color, float filledVolumeFraction)
|
||||
{
|
||||
Color = color;
|
||||
FilledVolumeFraction = (byte) (byte.MaxValue * filledVolumeFraction);
|
||||
}
|
||||
}
|
||||
|
||||
public enum SolutionContainerLayers : byte
|
||||
{
|
||||
Fill,
|
||||
Base
|
||||
}
|
||||
}
|
||||
@@ -1,3 +1,4 @@
|
||||
using Content.Shared.Chemistry.Components;
|
||||
using Robust.Shared.GameObjects;
|
||||
|
||||
namespace Content.Shared.Chemistry.Reaction
|
||||
@@ -7,6 +8,6 @@ namespace Content.Shared.Chemistry.Reaction
|
||||
/// </summary>
|
||||
public interface IReactionEffect
|
||||
{
|
||||
void React(IEntity solutionEntity, double intensity);
|
||||
void React(Solution solution, IEntity solutionEntity, double intensity);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,4 +1,5 @@
|
||||
using System.Collections.Generic;
|
||||
using Content.Shared.Chemistry.Components;
|
||||
using Content.Shared.Chemistry.Reagent;
|
||||
using Robust.Shared.GameObjects;
|
||||
using Robust.Shared.IoC;
|
||||
@@ -28,7 +29,7 @@ namespace Content.Shared.Chemistry.Reaction
|
||||
/// <param name="reaction">The reaction to check.</param>
|
||||
/// <param name="lowestUnitReactions">How many times this reaction can occur.</param>
|
||||
/// <returns></returns>
|
||||
private static bool CanReact(Solution.Solution solution, ReactionPrototype reaction, out ReagentUnit lowestUnitReactions)
|
||||
private static bool CanReact(Solution solution, ReactionPrototype reaction, out ReagentUnit lowestUnitReactions)
|
||||
{
|
||||
lowestUnitReactions = ReagentUnit.MaxValue;
|
||||
|
||||
@@ -54,7 +55,7 @@ namespace Content.Shared.Chemistry.Reaction
|
||||
/// Perform a reaction on a solution. This assumes all reaction criteria are met.
|
||||
/// Removes the reactants from the solution, then returns a solution with all products.
|
||||
/// </summary>
|
||||
private Solution.Solution PerformReaction(Solution.Solution solution, IEntity owner, ReactionPrototype reaction, ReagentUnit unitReactions)
|
||||
private Solution PerformReaction(Solution solution, IEntity owner, ReactionPrototype reaction, ReagentUnit unitReactions)
|
||||
{
|
||||
//Remove reactants
|
||||
foreach (var reactant in reaction.Reactants)
|
||||
@@ -67,23 +68,23 @@ namespace Content.Shared.Chemistry.Reaction
|
||||
}
|
||||
|
||||
//Create products
|
||||
var products = new Solution.Solution();
|
||||
var products = new Solution();
|
||||
foreach (var product in reaction.Products)
|
||||
{
|
||||
products.AddReagent(product.Key, product.Value * unitReactions);
|
||||
}
|
||||
|
||||
// Trigger reaction effects
|
||||
OnReaction(reaction, owner, unitReactions);
|
||||
OnReaction(solution, reaction, owner, unitReactions);
|
||||
|
||||
return products;
|
||||
}
|
||||
|
||||
protected virtual void OnReaction(ReactionPrototype reaction, IEntity owner, ReagentUnit unitReactions)
|
||||
protected virtual void OnReaction(Solution solution, ReactionPrototype reaction, IEntity owner, ReagentUnit unitReactions)
|
||||
{
|
||||
foreach (var effect in reaction.Effects)
|
||||
{
|
||||
effect.React(owner, unitReactions.Double());
|
||||
effect.React(solution, owner, unitReactions.Double());
|
||||
}
|
||||
}
|
||||
|
||||
@@ -92,10 +93,10 @@ namespace Content.Shared.Chemistry.Reaction
|
||||
/// Removes the reactants from the solution, then returns a solution with all products.
|
||||
/// WARNING: Does not trigger reactions between solution and new products.
|
||||
/// </summary>
|
||||
private Solution.Solution ProcessReactions(Solution.Solution solution, IEntity owner)
|
||||
private Solution ProcessReactions(Solution solution, IEntity owner)
|
||||
{
|
||||
//TODO: make a hashmap at startup and then look up reagents in the contents for a reaction
|
||||
var overallProducts = new Solution.Solution();
|
||||
var overallProducts = new Solution();
|
||||
foreach (var reaction in _reactions)
|
||||
{
|
||||
if (CanReact(solution, reaction, out var unitReactions))
|
||||
@@ -111,7 +112,7 @@ namespace Content.Shared.Chemistry.Reaction
|
||||
/// <summary>
|
||||
/// Continually react a solution until no more reactions occur.
|
||||
/// </summary>
|
||||
public void FullyReactSolution(Solution.Solution solution, IEntity owner)
|
||||
public void FullyReactSolution(Solution solution, IEntity owner)
|
||||
{
|
||||
for (var i = 0; i < MaxReactionIterations; i++)
|
||||
{
|
||||
@@ -122,14 +123,14 @@ namespace Content.Shared.Chemistry.Reaction
|
||||
|
||||
solution.AddSolution(products);
|
||||
}
|
||||
Logger.Error($"{nameof(Solution.Solution)} on {owner} (Uid: {owner.Uid}) could not finish reacting in under {MaxReactionIterations} loops.");
|
||||
Logger.Error($"{nameof(Solution)} on {owner} (Uid: {owner.Uid}) could not finish reacting in under {MaxReactionIterations} loops.");
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Continually react a solution until no more reactions occur, with a volume constraint.
|
||||
/// If a reaction's products would exceed the max volume, some product is deleted.
|
||||
/// </summary>
|
||||
public void FullyReactSolution(Solution.Solution solution, IEntity owner, ReagentUnit maxVolume)
|
||||
public void FullyReactSolution(Solution solution, IEntity owner, ReagentUnit maxVolume)
|
||||
{
|
||||
for (var i = 0; i < MaxReactionIterations; i++)
|
||||
{
|
||||
@@ -148,7 +149,7 @@ namespace Content.Shared.Chemistry.Reaction
|
||||
|
||||
solution.AddSolution(products);
|
||||
}
|
||||
Logger.Error($"{nameof(Solution.Solution)} on {owner} (Uid: {owner.Uid}) could not finish reacting in under {MaxReactionIterations} loops.");
|
||||
Logger.Error($"{nameof(Solution)} on {owner} (Uid: {owner.Uid}) could not finish reacting in under {MaxReactionIterations} loops.");
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -18,6 +18,6 @@ namespace Content.Shared.Chemistry.Reagent
|
||||
[DataField("conditions")]
|
||||
public ReagentEffectCondition[]? Conditions;
|
||||
|
||||
public abstract void Metabolize(IEntity solutionEntity, Solution.Solution.ReagentQuantity amount);
|
||||
public abstract void Metabolize(IEntity solutionEntity, Components.Solution.ReagentQuantity amount);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,5 +1,4 @@
|
||||
using Content.Shared.Body.Components;
|
||||
using Content.Shared.Chemistry.Solution;
|
||||
using Content.Shared.Chemistry.Components;
|
||||
using Robust.Shared.GameObjects;
|
||||
using Robust.Shared.Serialization.Manager.Attributes;
|
||||
|
||||
@@ -8,6 +7,6 @@ namespace Content.Shared.Chemistry.Reagent
|
||||
[ImplicitDataDefinitionForInheritors]
|
||||
public abstract class ReagentEffectCondition
|
||||
{
|
||||
public abstract bool Condition(IEntity solutionEntity, Solution.Solution.ReagentQuantity reagent);
|
||||
public abstract bool Condition(IEntity solutionEntity, Solution.ReagentQuantity reagent);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -27,7 +27,7 @@ namespace Content.Shared.Chemistry.Reagent
|
||||
[DataField("ingestion")]
|
||||
public bool Ingestion { get; } = false;
|
||||
|
||||
public void React(ReactionMethod method, IEntity entity, ReagentPrototype reagent, ReagentUnit volume, Solution.Solution? source)
|
||||
public void React(ReactionMethod method, IEntity entity, ReagentPrototype reagent, ReagentUnit volume, Components.Solution? source)
|
||||
{
|
||||
switch (method)
|
||||
{
|
||||
@@ -50,6 +50,6 @@ namespace Content.Shared.Chemistry.Reagent
|
||||
React(entity, reagent, volume, source);
|
||||
}
|
||||
|
||||
protected abstract void React(IEntity entity, ReagentPrototype reagent, ReagentUnit volume, Solution.Solution? source);
|
||||
protected abstract void React(IEntity entity, ReagentPrototype reagent, ReagentUnit volume, Components.Solution? source);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,89 +0,0 @@
|
||||
using Content.Shared.Chemistry.Reagent;
|
||||
using Robust.Shared.GameObjects;
|
||||
|
||||
namespace Content.Shared.Chemistry.Solution.Components
|
||||
{
|
||||
/// <summary>
|
||||
/// High-level solution transferring operations like "what happens when a syringe tries to inject this entity."
|
||||
/// </summary>
|
||||
/// <remarks>
|
||||
/// This interface is most often implemented by using <see cref="SharedSolutionContainerComponent"/>
|
||||
/// and setting the appropriate <see cref="SolutionContainerCaps"/>
|
||||
/// </remarks>
|
||||
public interface ISolutionInteractionsComponent : IComponent
|
||||
{
|
||||
//
|
||||
// INJECTING
|
||||
//
|
||||
|
||||
/// <summary>
|
||||
/// Whether we CAN POTENTIALLY be injected with solutions by items like syringes.
|
||||
/// </summary>
|
||||
/// <remarks>
|
||||
/// <para>
|
||||
/// This should NOT change to communicate behavior like "the container is full".
|
||||
/// Change <see cref="InjectSpaceAvailable"/> to 0 for that.
|
||||
/// </para>
|
||||
/// <para>
|
||||
/// If refilling is allowed (<see cref="CanRefill"/>) you should also always allow injecting.
|
||||
/// </para>
|
||||
/// </remarks>
|
||||
bool CanInject => false;
|
||||
|
||||
/// <summary>
|
||||
/// The amount of solution space available for injecting.
|
||||
/// </summary>
|
||||
ReagentUnit InjectSpaceAvailable => ReagentUnit.Zero;
|
||||
|
||||
/// <summary>
|
||||
/// Actually inject reagents.
|
||||
/// </summary>
|
||||
void Inject(Solution solution)
|
||||
{
|
||||
|
||||
}
|
||||
|
||||
//
|
||||
// DRAWING
|
||||
//
|
||||
|
||||
bool CanDraw => false;
|
||||
ReagentUnit DrawAvailable => ReagentUnit.Zero;
|
||||
|
||||
Solution Draw(ReagentUnit amount)
|
||||
{
|
||||
return new();
|
||||
}
|
||||
|
||||
|
||||
|
||||
//
|
||||
// REFILLING
|
||||
//
|
||||
|
||||
bool CanRefill => false;
|
||||
ReagentUnit RefillSpaceAvailable => ReagentUnit.Zero;
|
||||
|
||||
/// <summary>
|
||||
/// The amount that will transfer if something is spilled on the container.
|
||||
/// </summary>
|
||||
ReagentUnit MaxSpillRefill => ReagentUnit.Zero;
|
||||
|
||||
void Refill(Solution solution)
|
||||
{
|
||||
|
||||
}
|
||||
|
||||
//
|
||||
// DRAINING
|
||||
//
|
||||
|
||||
bool CanDrain => false;
|
||||
ReagentUnit DrainAvailable => ReagentUnit.Zero;
|
||||
|
||||
Solution Drain(ReagentUnit amount)
|
||||
{
|
||||
return new();
|
||||
}
|
||||
}
|
||||
}
|
||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user