Give reaction prototypes a priority (nerf ClF3 + smoke) (#7268)

This commit is contained in:
Leon Friedrich
2022-03-31 02:29:01 +13:00
committed by GitHub
parent 5447edbabf
commit e90f7f4330
4 changed files with 86 additions and 42 deletions

View File

@@ -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>

View File

@@ -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.");
} }
} }

View File

@@ -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

View File

@@ -30,6 +30,7 @@
- type: reaction - type: reaction
id: ChlorineTrifluoride id: ChlorineTrifluoride
priority: 20
reactants: reactants:
Chlorine: Chlorine:
amount: 1 amount: 1