Give reaction prototypes a priority (nerf ClF3 + smoke) (#7268)
This commit is contained in:
@@ -15,7 +15,7 @@ namespace Content.Shared.Chemistry.Reaction
|
|||||||
/// Prototype for chemical reaction definitions
|
/// Prototype for chemical reaction definitions
|
||||||
/// </summary>
|
/// </summary>
|
||||||
[Prototype("reaction")]
|
[Prototype("reaction")]
|
||||||
public sealed class ReactionPrototype : IPrototype
|
public sealed class ReactionPrototype : IPrototype, IComparable<ReactionPrototype>
|
||||||
{
|
{
|
||||||
[ViewVariables]
|
[ViewVariables]
|
||||||
[DataField("id", required: true)]
|
[DataField("id", required: true)]
|
||||||
@@ -67,6 +67,33 @@ namespace Content.Shared.Chemistry.Reaction
|
|||||||
/// enough reactants, the reaction does not occur. Useful for spawn-entity reactions (e.g. creating cheese).
|
/// enough reactants, the reaction does not occur. Useful for spawn-entity reactions (e.g. creating cheese).
|
||||||
/// </summary>
|
/// </summary>
|
||||||
[DataField("quantized")] public bool Quantized = false;
|
[DataField("quantized")] public bool Quantized = false;
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Determines the order in which reactions occur. This should used to ensure that (in general) descriptive /
|
||||||
|
/// pop-up generating and explosive reactions occur before things like foam/area effects.
|
||||||
|
/// </summary>
|
||||||
|
[DataField("priority")]
|
||||||
|
public int Priority;
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Comparison for creating a sorted set of reactions. Determines the order in which reactions occur.
|
||||||
|
/// </summary>
|
||||||
|
public int CompareTo(ReactionPrototype? other)
|
||||||
|
{
|
||||||
|
if (other == null)
|
||||||
|
return -1;
|
||||||
|
|
||||||
|
if (Priority != other.Priority)
|
||||||
|
return other.Priority - Priority;
|
||||||
|
|
||||||
|
// Prioritize reagents that don't generate products. This should reduce instances where a solution
|
||||||
|
// temporarily overflows and discards products simply due to the order in which the reactions occurred.
|
||||||
|
// Basically: Make space in the beaker before adding new products.
|
||||||
|
if (Products.Count != other.Products.Count)
|
||||||
|
return Products.Count - other.Products.Count;
|
||||||
|
|
||||||
|
return ID.CompareTo(other.ID);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
|
|||||||
@@ -1,5 +1,6 @@
|
|||||||
using System.Collections.Generic;
|
using System.Collections.Generic;
|
||||||
using System.Diagnostics.CodeAnalysis;
|
using System.Diagnostics.CodeAnalysis;
|
||||||
|
using System.Linq;
|
||||||
using Content.Shared.Administration;
|
using Content.Shared.Administration;
|
||||||
using Content.Shared.Administration.Logs;
|
using Content.Shared.Administration.Logs;
|
||||||
using Content.Shared.Chemistry.Components;
|
using Content.Shared.Chemistry.Components;
|
||||||
@@ -211,44 +212,56 @@ namespace Content.Shared.Chemistry.Reaction
|
|||||||
/// Removes the reactants from the solution, then returns a solution with all products.
|
/// Removes the reactants from the solution, then returns a solution with all products.
|
||||||
/// WARNING: Does not trigger reactions between solution and new products.
|
/// WARNING: Does not trigger reactions between solution and new products.
|
||||||
/// </summary>
|
/// </summary>
|
||||||
private bool ProcessReactions(Solution solution, EntityUid owner, [MaybeNullWhen(false)] out Solution productSolution)
|
private bool ProcessReactions(Solution solution, EntityUid owner, FixedPoint2 maxVolume, SortedSet<ReactionPrototype> reactions)
|
||||||
{
|
{
|
||||||
foreach(var reactant in solution.Contents)
|
HashSet<ReactionPrototype> toRemove = new();
|
||||||
{
|
Solution? products = null;
|
||||||
if (!_reactions.TryGetValue(reactant.ReagentId, out var reactions))
|
|
||||||
continue;
|
|
||||||
|
|
||||||
foreach(var reaction in reactions)
|
// attempt to perform any applicable reaction
|
||||||
|
foreach (var reaction in reactions)
|
||||||
{
|
{
|
||||||
if (!CanReact(solution, reaction, out var unitReactions))
|
if (!CanReact(solution, reaction, out var unitReactions))
|
||||||
|
{
|
||||||
|
toRemove.Add(reaction);
|
||||||
continue;
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
productSolution = PerformReaction(solution, owner, reaction, unitReactions);
|
products = PerformReaction(solution, owner, reaction, unitReactions);
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
|
// did any reaction occur?
|
||||||
|
if (products == null)
|
||||||
|
return false; ;
|
||||||
|
|
||||||
|
// Remove any reactions that were not applicable. Avoids re-iterating over them in future.
|
||||||
|
reactions.Except(toRemove);
|
||||||
|
|
||||||
|
if (products.TotalVolume <= 0)
|
||||||
return true;
|
return true;
|
||||||
}
|
|
||||||
|
// remove excess product
|
||||||
|
// TODO spill excess?
|
||||||
|
var excessVolume = solution.TotalVolume + products.TotalVolume - maxVolume;
|
||||||
|
if (excessVolume > 0)
|
||||||
|
products.RemoveSolution(excessVolume);
|
||||||
|
|
||||||
|
// Add any reactions associated with the new products. This may re-add reactions that were already iterated
|
||||||
|
// over previously. The new product may mean the reactions are applicable again and need to be processed.
|
||||||
|
foreach (var reactant in products.Contents)
|
||||||
|
{
|
||||||
|
if (_reactions.TryGetValue(reactant.ReagentId, out var reactantReactions))
|
||||||
|
reactions.UnionWith(reactantReactions);
|
||||||
}
|
}
|
||||||
|
|
||||||
productSolution = null;
|
solution.AddSolution(products);
|
||||||
return false;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// Continually react a solution until no more reactions occur.
|
/// Continually react a solution until no more reactions occur.
|
||||||
/// </summary>
|
/// </summary>
|
||||||
public void FullyReactSolution(Solution solution, EntityUid owner)
|
public void FullyReactSolution(Solution solution, EntityUid owner) => FullyReactSolution(solution, owner, FixedPoint2.MaxValue);
|
||||||
{
|
|
||||||
for (var i = 0; i < MaxReactionIterations; i++)
|
|
||||||
{
|
|
||||||
if (!ProcessReactions(solution, owner, out var products))
|
|
||||||
return;
|
|
||||||
|
|
||||||
if (products.TotalVolume <= 0)
|
|
||||||
return;
|
|
||||||
|
|
||||||
solution.AddSolution(products);
|
|
||||||
}
|
|
||||||
Logger.Error($"{nameof(Solution)} {owner} could not finish reacting in under {MaxReactionIterations} loops.");
|
|
||||||
}
|
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// Continually react a solution until no more reactions occur, with a volume constraint.
|
/// Continually react a solution until no more reactions occur, with a volume constraint.
|
||||||
@@ -256,24 +269,22 @@ namespace Content.Shared.Chemistry.Reaction
|
|||||||
/// </summary>
|
/// </summary>
|
||||||
public void FullyReactSolution(Solution solution, EntityUid owner, FixedPoint2 maxVolume)
|
public void FullyReactSolution(Solution solution, EntityUid owner, FixedPoint2 maxVolume)
|
||||||
{
|
{
|
||||||
|
// construct the initial set of reactions to check.
|
||||||
|
SortedSet<ReactionPrototype> reactions = new();
|
||||||
|
foreach (var reactant in solution.Contents)
|
||||||
|
{
|
||||||
|
if (_reactions.TryGetValue(reactant.ReagentId, out var reactantReactions))
|
||||||
|
reactions.UnionWith(reactantReactions);
|
||||||
|
}
|
||||||
|
|
||||||
|
// Repeatedly attempt to perform reactions, ending when there are no more applicable reactions, or when we
|
||||||
|
// exceed the iteration limit.
|
||||||
for (var i = 0; i < MaxReactionIterations; i++)
|
for (var i = 0; i < MaxReactionIterations; i++)
|
||||||
{
|
{
|
||||||
if (!ProcessReactions(solution, owner, out var products))
|
if (!ProcessReactions(solution, owner, maxVolume, reactions))
|
||||||
return;
|
return;
|
||||||
|
|
||||||
if (products.TotalVolume <= 0)
|
|
||||||
return;
|
|
||||||
|
|
||||||
var totalVolume = solution.TotalVolume + products.TotalVolume;
|
|
||||||
var excessVolume = totalVolume - maxVolume;
|
|
||||||
|
|
||||||
if (excessVolume > 0)
|
|
||||||
{
|
|
||||||
products.RemoveSolution(excessVolume); //excess product is deleted to fit under volume limit
|
|
||||||
}
|
}
|
||||||
|
|
||||||
solution.AddSolution(products);
|
|
||||||
}
|
|
||||||
Logger.Error($"{nameof(Solution)} {owner} could not finish reacting in under {MaxReactionIterations} loops.");
|
Logger.Error($"{nameof(Solution)} {owner} could not finish reacting in under {MaxReactionIterations} loops.");
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -81,6 +81,7 @@
|
|||||||
- type: reaction
|
- type: reaction
|
||||||
id: PotassiumExplosion
|
id: PotassiumExplosion
|
||||||
impact: High
|
impact: High
|
||||||
|
priority: 20
|
||||||
reactants:
|
reactants:
|
||||||
Water:
|
Water:
|
||||||
amount: 1
|
amount: 1
|
||||||
@@ -98,6 +99,7 @@
|
|||||||
|
|
||||||
- type: reaction
|
- type: reaction
|
||||||
id: Smoke
|
id: Smoke
|
||||||
|
priority: 10
|
||||||
impact: High
|
impact: High
|
||||||
reactants:
|
reactants:
|
||||||
Phosphorus:
|
Phosphorus:
|
||||||
@@ -121,6 +123,7 @@
|
|||||||
|
|
||||||
- type: reaction
|
- type: reaction
|
||||||
id: Foam
|
id: Foam
|
||||||
|
priority: 10
|
||||||
impact: High
|
impact: High
|
||||||
reactants:
|
reactants:
|
||||||
Fluorosurfactant:
|
Fluorosurfactant:
|
||||||
@@ -146,6 +149,7 @@
|
|||||||
- type: reaction
|
- type: reaction
|
||||||
id: IronMetalFoam
|
id: IronMetalFoam
|
||||||
impact: High
|
impact: High
|
||||||
|
priority: 10
|
||||||
reactants:
|
reactants:
|
||||||
Iron:
|
Iron:
|
||||||
amount: 3
|
amount: 3
|
||||||
@@ -172,6 +176,7 @@
|
|||||||
- type: reaction
|
- type: reaction
|
||||||
id: AluminiumMetalFoam
|
id: AluminiumMetalFoam
|
||||||
impact: High
|
impact: High
|
||||||
|
priority: 10
|
||||||
reactants:
|
reactants:
|
||||||
Aluminium:
|
Aluminium:
|
||||||
amount: 3
|
amount: 3
|
||||||
|
|||||||
@@ -30,6 +30,7 @@
|
|||||||
|
|
||||||
- type: reaction
|
- type: reaction
|
||||||
id: ChlorineTrifluoride
|
id: ChlorineTrifluoride
|
||||||
|
priority: 20
|
||||||
reactants:
|
reactants:
|
||||||
Chlorine:
|
Chlorine:
|
||||||
amount: 1
|
amount: 1
|
||||||
|
|||||||
Reference in New Issue
Block a user