Allow solutions to store extra reagent data (#19323)

This commit is contained in:
Leon Friedrich
2023-09-05 09:55:10 +12:00
committed by GitHub
parent a6b81058d0
commit e4ca6f4fb9
52 changed files with 932 additions and 538 deletions

View File

@@ -23,9 +23,11 @@ namespace Content.Server.Chemistry.EntitySystems;
public sealed class SolutionChangedEvent : EntityEventArgs
{
public readonly Solution Solution;
public readonly string SolutionId;
public SolutionChangedEvent(Solution solution)
public SolutionChangedEvent(Solution solution, string solutionId)
{
SolutionId = solutionId;
Solution = solution;
}
}
@@ -103,7 +105,7 @@ public sealed partial class SolutionContainerSystem : EntitySystem
{
var msg = new FormattedMessage();
if (solution.Contents.Count == 0) //TODO: better way to see if empty?
if (solution.Volume == 0)
{
msg.AddMarkup(Loc.GetString("scannable-solution-empty-container"));
return msg;
@@ -111,17 +113,13 @@ public sealed partial class SolutionContainerSystem : EntitySystem
msg.AddMarkup(Loc.GetString("scannable-solution-main-text"));
foreach (var reagent in solution)
foreach (var (proto, quantity) in solution.GetReagentPrototypes(_prototypeManager))
{
if (!_prototypeManager.TryIndex<ReagentPrototype>(reagent.ReagentId, out var proto))
{
continue;
}
msg.PushNewline();
msg.AddMarkup(Loc.GetString("scannable-solution-chemical"
, ("type", proto.LocalizedName)
, ("color", proto.SubstanceColor.ToHexNoAlpha())
, ("amount", reagent.Quantity)));
, ("amount", quantity)));
}
return msg;
@@ -132,46 +130,41 @@ public sealed partial class SolutionContainerSystem : EntitySystem
{
SolutionContainerManagerComponent? solutionsManager = null;
if (!Resolve(args.Examined, ref solutionsManager)
|| !solutionsManager.Solutions.TryGetValue(examinableComponent.Solution, out var solutionHolder))
|| !solutionsManager.Solutions.TryGetValue(examinableComponent.Solution, out var solution))
{
return;
}
var primaryReagent = solutionHolder.GetPrimaryReagentId();
var primaryReagent = solution.GetPrimaryReagentId();
if (string.IsNullOrEmpty(primaryReagent))
if (string.IsNullOrEmpty(primaryReagent?.Prototype))
{
args.PushText(Loc.GetString("shared-solution-container-component-on-examine-empty-container"));
return;
}
if (!_prototypeManager.TryIndex(primaryReagent, out ReagentPrototype? primary))
if (!_prototypeManager.TryIndex(primaryReagent.Value.Prototype, out ReagentPrototype? primary))
{
Logger.Error(
$"{nameof(Solution)} could not find the prototype associated with {primaryReagent}.");
return;
}
var colorHex = solutionHolder.GetColor(_prototypeManager)
var colorHex = solution.GetColor(_prototypeManager)
.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.PushMarkup(Loc.GetString(messageString,
("color", colorHex),
("wordedAmount", Loc.GetString(solutionHolder.Contents.Count == 1
("wordedAmount", Loc.GetString(solution.Contents.Count == 1
? "shared-solution-container-component-on-examine-worded-amount-one-reagent"
: "shared-solution-container-component-on-examine-worded-amount-multiple-reagents")),
("desc", primary.LocalizedPhysicalDescription)));
// Add descriptions of immediately recognizable reagents, like water or beer
var recognized = new List<ReagentPrototype>();
foreach (var (id, _) in solutionHolder)
foreach (var proto in solution.GetReagentPrototypes(_prototypeManager).Keys)
{
if (!_prototypeManager.TryIndex<ReagentPrototype>(id, out var proto))
{
continue;
}
if (!proto.Recognizable)
{
continue;
@@ -278,7 +271,7 @@ public sealed partial class SolutionContainerSystem : EntitySystem
}
UpdateAppearance(uid, solutionHolder);
RaiseLocalEvent(uid, new SolutionChangedEvent(solutionHolder));
RaiseLocalEvent(uid, new SolutionChangedEvent(solutionHolder, solutionHolder.Name));
}
public void RemoveAllSolution(EntityUid uid, Solution solutionHolder)
@@ -324,46 +317,111 @@ public sealed partial class SolutionContainerSystem : EntitySystem
/// </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="reagentQuantity">The 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, ReagentQuantity reagentQuantity,
out FixedPoint2 acceptedQuantity, float? temperature = null)
{
acceptedQuantity = targetSolution.AvailableVolume > reagentQuantity.Quantity
? reagentQuantity.Quantity
: targetSolution.AvailableVolume;
if (acceptedQuantity <= 0)
return reagentQuantity.Quantity == 0;
if (temperature == null)
{
targetSolution.AddReagent(reagentQuantity.Reagent, acceptedQuantity);
}
else
{
var proto = _prototypeManager.Index<ReagentPrototype>(reagentQuantity.Reagent.Prototype);
targetSolution.AddReagent(proto, acceptedQuantity, temperature.Value, _prototypeManager);
}
UpdateChemicals(targetUid, targetSolution, true);
return acceptedQuantity == reagentQuantity.Quantity;
}
/// <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="prototype">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, FixedPoint2 quantity,
out FixedPoint2 acceptedQuantity, float? temperature = null)
public bool TryAddReagent(EntityUid targetUid, Solution targetSolution, string prototype, FixedPoint2 quantity,
out FixedPoint2 acceptedQuantity, float? temperature = null, ReagentData? data = null)
{
acceptedQuantity = targetSolution.AvailableVolume > quantity ? quantity : targetSolution.AvailableVolume;
if (acceptedQuantity <= 0)
return quantity == 0;
if (temperature == null)
targetSolution.AddReagent(reagentId, acceptedQuantity);
else
targetSolution.AddReagent(_prototypeManager.Index<ReagentPrototype>(reagentId), acceptedQuantity, temperature.Value, _prototypeManager);
UpdateChemicals(targetUid, targetSolution, true);
return acceptedQuantity == quantity;
var reagent = new ReagentQuantity(prototype, quantity, data);
return TryAddReagent(targetUid, targetSolution, reagent, out acceptedQuantity, temperature);
}
/// <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 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, ReagentId reagentId, FixedPoint2 quantity,
out FixedPoint2 acceptedQuantity, float? temperature = null)
{
var quant = new ReagentQuantity(reagentId, quantity);
return TryAddReagent(targetUid, targetSolution, quant, out acceptedQuantity, temperature);
}
/// <summary>
/// Removes reagent of an Id to the container.
/// Removes reagent from a 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>
/// <param name="reagentQuantity">The 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, FixedPoint2 quantity)
public bool RemoveReagent(EntityUid targetUid, Solution? container, ReagentQuantity reagentQuantity)
{
if (container == null || !container.ContainsReagent(reagentId))
if (container == null)
return false;
var quant = container.RemoveReagent(reagentQuantity);
if (quant <= FixedPoint2.Zero)
return false;
container.RemoveReagent(reagentId, quantity);
UpdateChemicals(targetUid, container);
return true;
}
/// <summary>
/// Removes reagent from a container.
/// </summary>
/// <param name="targetUid"></param>
/// <param name="container">Solution container from which we are removing reagent</param>
/// <param name="prototype">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 RemoveReagent(EntityUid targetUid, Solution? container, string prototype, FixedPoint2 quantity, ReagentData? data = null)
{
return RemoveReagent(targetUid, container, new ReagentQuantity(prototype, quantity, data));
}
/// <summary>
/// Removes reagent from a container.
/// </summary>
/// <param name="targetUid"></param>
/// <param name="container">Solution container from which we are removing reagent</param>
/// <param name="reagentId">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 RemoveReagent(EntityUid targetUid, Solution? container, ReagentId reagentId, FixedPoint2 quantity)
{
return RemoveReagent(targetUid, container, new ReagentQuantity(reagentId, quantity));
}
/// <summary>
/// Adds a solution to the container, if it can fully fit.
/// </summary>
@@ -391,19 +449,35 @@ public sealed partial class SolutionContainerSystem : EntitySystem
/// <param name="target">target solution</param>
/// <param name="quantity">quantity of solution to move from source to target. If this is a negative number, the source & target roles are reversed.</param>
public bool TryTransferSolution(EntityUid sourceUid, EntityUid targetUid, Solution source, Solution target, FixedPoint2 quantity)
{
if (!TryTransferSolution(targetUid, target, source, quantity))
return false;
UpdateChemicals(sourceUid, source, false);
return true;
}
/// <summary>
/// Moves some quantity of a solution from one solution to another.
/// </summary>
/// <param name="sourceUid">entity holding the source solution</param>
/// <param name="targetUid">entity holding the target solution</param>
/// <param name="source">source solution</param>
/// <param name="target">target solution</param>
/// <param name="quantity">quantity of solution to move from source to target. If this is a negative number, the source & target roles are reversed.</param>
public bool TryTransferSolution(EntityUid targetUid, Solution target, Solution source, FixedPoint2 quantity)
{
if (quantity < 0)
return TryTransferSolution(targetUid, sourceUid, target, source, -quantity);
throw new InvalidOperationException("Quantity must be positive");
quantity = FixedPoint2.Min(quantity, target.AvailableVolume, source.Volume);
if (quantity == 0)
return false;
// TODO This should be made into a function that directly transfers reagents. currently this is quite
// inefficient.
// TODO This should be made into a function that directly transfers reagents.
// Currently this is quite inefficient.
target.AddSolution(source.SplitSolution(quantity), _prototypeManager);
UpdateChemicals(sourceUid, source, false);
UpdateChemicals(targetUid, target, true);
return true;
}
@@ -540,7 +614,7 @@ public sealed partial class SolutionContainerSystem : EntitySystem
}
public Solution EnsureSolution(EntityUid uid, string name,
IEnumerable<Solution.ReagentQuantity> reagents,
IEnumerable<ReagentQuantity> reagents,
bool setMaxVol = true,
SolutionContainerManagerComponent? solutionsMgr = null)
{
@@ -574,19 +648,16 @@ public sealed partial class SolutionContainerSystem : EntitySystem
// RemoveReagent does a RemoveSwap, meaning we don't have to copy the list if we iterate it backwards.
for (var i = solution.Contents.Count-1; i >= 0; i--)
{
var (reagentId, _) = solution.Contents[i];
var removedQuantity = solution.RemoveReagent(reagentId, quantity);
if(removedQuantity > 0)
removedSolution.AddReagent(reagentId, removedQuantity);
var (reagent, _) = solution.Contents[i];
var removedQuantity = solution.RemoveReagent(reagent, quantity);
removedSolution.AddReagent(reagent, removedQuantity);
}
UpdateChemicals(uid, solution);
return removedSolution;
}
public FixedPoint2 GetReagentQuantity(EntityUid owner, string reagentId)
public FixedPoint2 GetTotalPrototypeQuantity(EntityUid owner, string reagentId)
{
var reagentQuantity = FixedPoint2.New(0);
if (EntityManager.EntityExists(owner)
@@ -594,7 +665,7 @@ public sealed partial class SolutionContainerSystem : EntitySystem
{
foreach (var solution in managerComponent.Solutions.Values)
{
reagentQuantity += solution.GetReagentQuantity(reagentId);
reagentQuantity += solution.GetTotalPrototypeQuantity(reagentId);
}
}
@@ -640,21 +711,21 @@ public sealed partial class SolutionContainerSystem : EntitySystem
if (component.Solutions.Count == 0)
return null;
var reagentCounts = new Dictionary<string, FixedPoint2>();
var reagentCounts = new Dictionary<ReagentId, FixedPoint2>();
foreach (var solution in component.Solutions.Values)
{
foreach (var reagent in solution.Contents)
foreach (var (reagent, quantity) in solution.Contents)
{
reagentCounts.TryGetValue(reagent.ReagentId, out var existing);
existing += reagent.Quantity;
reagentCounts[reagent.ReagentId] = existing;
reagentCounts.TryGetValue(reagent, out var existing);
existing += quantity;
reagentCounts[reagent] = existing;
}
}
var max = reagentCounts.Max();
return _prototypeManager.Index<ReagentPrototype>(max.Key);
return _prototypeManager.Index<ReagentPrototype>(max.Key.Prototype);
}
public SoundSpecifier? GetSound(SolutionContainerManagerComponent component)