Metabolism tweaks / metabolism 4.0 (#5246)

* Metabolism tweaks

* use MetabolismArgs and convert ReagentEntityReactions into ReagentEffects

* fork forgor 💀

* fixes
This commit is contained in:
mirrorcult
2021-11-10 03:11:28 -07:00
committed by GitHub
parent 6486e07077
commit 747f02f3f3
41 changed files with 328 additions and 291 deletions

View File

@@ -52,11 +52,26 @@ namespace Content.Server.Body.Metabolism
[DataField("metabolizerTypes", customTypeSerializer:typeof(PrototypeIdHashSetSerializer<MetabolizerTypePrototype>))] [DataField("metabolizerTypes", customTypeSerializer:typeof(PrototypeIdHashSetSerializer<MetabolizerTypePrototype>))]
public HashSet<string>? MetabolizerTypes = null; public HashSet<string>? MetabolizerTypes = null;
/// <summary>
/// Should this metabolizer remove chemicals that have no metabolisms defined?
/// As a stop-gap, basically.
/// </summary>
[DataField("removeEmpty")]
public bool RemoveEmpty = false;
/// <summary>
/// How many reagents can this metabolizer process at once?
/// Used to nerf 'stacked poisons' where having 5+ different poisons in a syringe, even at low
/// quantity, would be muuuuch better than just one poison acting.
/// </summary>
[DataField("maxReagents")]
public int MaxReagentsProcessable = 3;
/// <summary> /// <summary>
/// A list of metabolism groups that this metabolizer will act on, in order of precedence. /// A list of metabolism groups that this metabolizer will act on, in order of precedence.
/// </summary> /// </summary>
[DataField("groups", required: true)] [DataField("groups")]
public List<MetabolismGroupEntry> MetabolismGroups = default!; public List<MetabolismGroupEntry>? MetabolismGroups = default!;
} }
/// <summary> /// <summary>

View File

@@ -9,10 +9,12 @@ using Content.Shared.Body.Mechanism;
using Content.Shared.Chemistry.Components; using Content.Shared.Chemistry.Components;
using Content.Shared.Chemistry.Reagent; using Content.Shared.Chemistry.Reagent;
using Content.Shared.FixedPoint; using Content.Shared.FixedPoint;
using Content.Shared.MobState.Components;
using JetBrains.Annotations; using JetBrains.Annotations;
using Robust.Shared.GameObjects; using Robust.Shared.GameObjects;
using Robust.Shared.IoC; using Robust.Shared.IoC;
using Robust.Shared.Prototypes; using Robust.Shared.Prototypes;
using Robust.Shared.Random;
namespace Content.Server.Body.Metabolism namespace Content.Server.Body.Metabolism
{ {
@@ -22,6 +24,7 @@ namespace Content.Server.Body.Metabolism
{ {
[Dependency] private readonly SolutionContainerSystem _solutionContainerSystem = default!; [Dependency] private readonly SolutionContainerSystem _solutionContainerSystem = default!;
[Dependency] private readonly IPrototypeManager _prototypeManager = default!; [Dependency] private readonly IPrototypeManager _prototypeManager = default!;
[Dependency] private readonly IRobustRandom _random = default!;
public override void Initialize() public override void Initialize()
{ {
@@ -102,17 +105,35 @@ namespace Content.Server.Body.Metabolism
if (solutionEntityUid == null || solution == null) if (solutionEntityUid == null || solution == null)
return; return;
// we found our guy
foreach (var reagent in solution.Contents.ToArray()) // randomize the reagent list so we don't have any weird quirks
// like alphabetical order or insertion order mattering for processing
var list = solution.Contents.ToArray();
_random.Shuffle(list);
int reagents = 0;
foreach (var reagent in list)
{ {
if (!_prototypeManager.TryIndex<ReagentPrototype>(reagent.ReagentId, out var proto)) if (!_prototypeManager.TryIndex<ReagentPrototype>(reagent.ReagentId, out var proto))
continue; continue;
FixedPoint2 mostToRemove = FixedPoint2.Zero;
if (proto.Metabolisms == null) if (proto.Metabolisms == null)
{
if (meta.RemoveEmpty)
_solutionContainerSystem.TryRemoveReagent(solutionEntityUid.Value, solution, reagent.ReagentId, FixedPoint2.New(1));
continue; continue;
}
// we're done here entirely if this is true
if (reagents >= meta.MaxReagentsProcessable)
return;
reagents += 1;
// loop over all our groups and see which ones apply // loop over all our groups and see which ones apply
FixedPoint2 mostToRemove = FixedPoint2.Zero; if (meta.MetabolismGroups == null)
continue;
foreach (var group in meta.MetabolismGroups) foreach (var group in meta.MetabolismGroups)
{ {
if (!proto.Metabolisms.Keys.Contains(group.Id)) if (!proto.Metabolisms.Keys.Contains(group.Id))
@@ -124,16 +145,27 @@ namespace Content.Server.Body.Metabolism
if (entry.MetabolismRate > mostToRemove) if (entry.MetabolismRate > mostToRemove)
mostToRemove = entry.MetabolismRate; mostToRemove = entry.MetabolismRate;
// if it's possible for them to be dead, and they are,
// then we shouldn't process any effects, but should probably
// still remove reagents
if (EntityManager.TryGetComponent<MobStateComponent>(solutionEntityUid.Value, out var state))
{
if (state.IsDead())
continue;
}
var args = new ReagentEffectArgs(solutionEntityUid.Value, meta.OwnerUid, solution, proto, entry.MetabolismRate,
EntityManager, null);
// do all effects, if conditions apply // do all effects, if conditions apply
foreach (var effect in entry.Effects) foreach (var effect in entry.Effects)
{ {
bool failed = false; bool failed = false;
var quant = new Solution.ReagentQuantity(reagent.ReagentId, reagent.Quantity);
if (effect.Conditions != null) if (effect.Conditions != null)
{ {
foreach (var cond in effect.Conditions) foreach (var cond in effect.Conditions)
{ {
if (!cond.Condition(solutionEntityUid.Value, meta.OwnerUid, quant, EntityManager)) if (!cond.Condition(args))
failed = true; failed = true;
} }
@@ -141,7 +173,7 @@ namespace Content.Server.Body.Metabolism
continue; continue;
} }
effect.Metabolize(solutionEntityUid.Value, meta.OwnerUid, quant, EntityManager); effect.Metabolize(args);
} }
} }

View File

@@ -566,7 +566,7 @@ namespace Content.Server.Botany.Components
foreach (var reagent in solutionSystem.RemoveEachReagent(OwnerUid, solution, amt)) foreach (var reagent in solutionSystem.RemoveEachReagent(OwnerUid, solution, amt))
{ {
var reagentProto = _prototypeManager.Index<ReagentPrototype>(reagent); var reagentProto = _prototypeManager.Index<ReagentPrototype>(reagent);
reagentProto.ReactionPlant(OwnerUid, new Solution.ReagentQuantity(reagent, amt)); reagentProto.ReactionPlant(OwnerUid, new Solution.ReagentQuantity(reagent, amt), solution);
} }
} }

View File

@@ -22,9 +22,12 @@ namespace Content.Server.Chemistry.ReagentEffectConditions
[DataField("shouldHave")] [DataField("shouldHave")]
public bool ShouldHave = true; public bool ShouldHave = true;
public override bool Condition(EntityUid solutionEntity, EntityUid organEntity, Solution.ReagentQuantity reagent, IEntityManager entityManager) public override bool Condition(ReagentEffectArgs args)
{ {
if (entityManager.TryGetComponent<MetabolizerComponent>(organEntity, out var metabolizer) if (args.OrganEntity == null)
return false;
if (args.EntityManager.TryGetComponent<MetabolizerComponent>(args.OrganEntity.Value, out var metabolizer)
&& metabolizer.MetabolizerTypes != null && metabolizer.MetabolizerTypes != null
&& metabolizer.MetabolizerTypes.Contains(Type)) && metabolizer.MetabolizerTypes.Contains(Type))
return ShouldHave; return ShouldHave;

View File

@@ -18,9 +18,15 @@ namespace Content.Server.Chemistry.ReagentEffectConditions
[DataField("max")] [DataField("max")]
public FixedPoint2 Max = FixedPoint2.MaxValue; public FixedPoint2 Max = FixedPoint2.MaxValue;
public override bool Condition(EntityUid solutionEntity, EntityUid organEntity, Solution.ReagentQuantity reagent, IEntityManager entityManager) public override bool Condition(ReagentEffectArgs args)
{ {
return reagent.Quantity >= Min && reagent.Quantity < Max; if (args.Source != null)
{
var quant = args.Source.GetReagentQuantity(args.Reagent.ID);
return quant >= Min && quant <= Max;
}
return false;
} }
} }
} }

View File

@@ -0,0 +1,31 @@
using System.Collections.Generic;
using Content.Server.Chemistry.EntitySystems;
using Content.Shared.Chemistry.Components;
using Content.Shared.Chemistry.Reagent;
using Content.Shared.FixedPoint;
using JetBrains.Annotations;
using Robust.Shared.GameObjects;
using Robust.Shared.Serialization.Manager.Attributes;
using Robust.Shared.Serialization.TypeSerializers.Implementations.Custom.Prototype.Set;
namespace Content.Server.Chemistry.ReagentEffects
{
[UsedImplicitly]
public class AddToSolutionReaction : ReagentEffect
{
[DataField("solution")]
private string _solution = "reagents";
public override void Metabolize(ReagentEffectArgs args)
{
// TODO see if this is correct
if (!EntitySystem.Get<SolutionContainerSystem>()
.TryGetSolution(args.SolutionEntity, _solution, out var solutionContainer))
return;
if (EntitySystem.Get<SolutionContainerSystem>()
.TryAddReagent(args.SolutionEntity, solutionContainer, args.Reagent.ID, args.Metabolizing, out var accepted))
args.Source?.RemoveReagent(args.Reagent.ID, accepted);
}
}
}

View File

@@ -0,0 +1,26 @@
using System.Collections.Generic;
using Content.Server.Atmos.Components;
using Content.Server.Atmos.EntitySystems;
using Content.Shared.Chemistry.Components;
using Content.Shared.Chemistry.Reagent;
using Content.Shared.FixedPoint;
using JetBrains.Annotations;
using Robust.Shared.GameObjects;
using Robust.Shared.Serialization.Manager.Attributes;
using Robust.Shared.Serialization.TypeSerializers.Implementations.Custom.Prototype.Set;
namespace Content.Server.Chemistry.ReagentEffects
{
[UsedImplicitly]
public class ExtinguishReaction : ReagentEffect
{
public override void Metabolize(ReagentEffectArgs args)
{
if (!args.EntityManager.TryGetComponent(args.SolutionEntity, out FlammableComponent? flammable)) return;
var flammableSystem = EntitySystem.Get<FlammableSystem>();
flammableSystem.Extinguish(args.SolutionEntity, flammable);
flammableSystem.AdjustFireStacks(args.SolutionEntity, -1.5f * (float) args.Metabolizing, flammable);
}
}
}

View File

@@ -0,0 +1,25 @@
using System.Collections.Generic;
using Content.Server.Atmos.Components;
using Content.Server.Atmos.EntitySystems;
using Content.Shared.Chemistry.Components;
using Content.Shared.Chemistry.Reagent;
using Content.Shared.FixedPoint;
using JetBrains.Annotations;
using Robust.Shared.GameObjects;
using Robust.Shared.Serialization.Manager.Attributes;
using Robust.Shared.Serialization.TypeSerializers.Implementations.Custom.Prototype.Set;
namespace Content.Server.Chemistry.ReagentEffects
{
[UsedImplicitly]
public class FlammableReaction : ReagentEffect
{
public override void Metabolize(ReagentEffectArgs args)
{
if (!args.EntityManager.TryGetComponent(args.SolutionEntity, out FlammableComponent? flammable)) return;
EntitySystem.Get<FlammableSystem>().AdjustFireStacks(args.SolutionEntity, args.Metabolizing.Float() / 5f, flammable);
args.Source?.RemoveReagent(args.Reagent.ID, args.Metabolizing);
}
}
}

View File

@@ -19,9 +19,9 @@ namespace Content.Server.Chemistry.ReagentEffects
[DataField("damage", required: true)] [DataField("damage", required: true)]
public DamageSpecifier Damage = default!; public DamageSpecifier Damage = default!;
public override void Metabolize(EntityUid solutionEntity, EntityUid organEntity, Solution.ReagentQuantity reagent, IEntityManager entityManager) public override void Metabolize(ReagentEffectArgs args)
{ {
EntitySystem.Get<DamageableSystem>().TryChangeDamage(solutionEntity, Damage, true); EntitySystem.Get<DamageableSystem>().TryChangeDamage(args.SolutionEntity, Damage * args.Metabolizing, true);
} }
} }
} }

View File

@@ -37,9 +37,9 @@ namespace Content.Server.Chemistry.ReagentEffects
/// <summary> /// <summary>
/// Remove reagent at set rate, changes the movespeed modifiers and adds a MovespeedModifierMetabolismComponent if not already there. /// Remove reagent at set rate, changes the movespeed modifiers and adds a MovespeedModifierMetabolismComponent if not already there.
/// </summary> /// </summary>
public override void Metabolize(EntityUid solutionEntity, EntityUid organEntity, Solution.ReagentQuantity reagent, IEntityManager entityManager) public override void Metabolize(ReagentEffectArgs args)
{ {
var status = entityManager.EnsureComponent<MovespeedModifierMetabolismComponent>(solutionEntity); var status = args.EntityManager.EnsureComponent<MovespeedModifierMetabolismComponent>(args.SolutionEntity);
// Only refresh movement if we need to. // Only refresh movement if we need to.
var modified = !status.WalkSpeedModifier.Equals(WalkSpeedModifier) || var modified = !status.WalkSpeedModifier.Equals(WalkSpeedModifier) ||
@@ -48,10 +48,10 @@ namespace Content.Server.Chemistry.ReagentEffects
status.WalkSpeedModifier = WalkSpeedModifier; status.WalkSpeedModifier = WalkSpeedModifier;
status.SprintSpeedModifier = SprintSpeedModifier; status.SprintSpeedModifier = SprintSpeedModifier;
IncreaseTimer(status, StatusLifetime * reagent.Quantity.Float()); IncreaseTimer(status, StatusLifetime * args.Metabolizing.Float());
if (modified) if (modified)
EntitySystem.Get<MovementSpeedModifierSystem>().RefreshMovementSpeedModifiers(solutionEntity); EntitySystem.Get<MovementSpeedModifierSystem>().RefreshMovementSpeedModifiers(args.SolutionEntity);
} }
public void IncreaseTimer(MovespeedModifierMetabolismComponent status, float time) public void IncreaseTimer(MovespeedModifierMetabolismComponent status, float time)

View File

@@ -1,4 +1,5 @@
using Content.Shared.Chemistry.Components; using Content.Shared.Chemistry.Components;
using Content.Shared.Chemistry.Reagent;
using JetBrains.Annotations; using JetBrains.Annotations;
using Robust.Shared.GameObjects; using Robust.Shared.GameObjects;
@@ -6,9 +7,9 @@ namespace Content.Server.Chemistry.ReagentEffects.PlantMetabolism
{ {
public class PlantAdjustHealth : PlantAdjustAttribute public class PlantAdjustHealth : PlantAdjustAttribute
{ {
public override void Metabolize(EntityUid plantHolder, EntityUid organEntity, Solution.ReagentQuantity reagent, IEntityManager entityManager) public override void Metabolize(ReagentEffectArgs args)
{ {
if (!CanMetabolize(plantHolder, out var plantHolderComp, entityManager)) if (!CanMetabolize(args.SolutionEntity, out var plantHolderComp, args.EntityManager))
return; return;
plantHolderComp.Health += Amount; plantHolderComp.Health += Amount;

View File

@@ -1,4 +1,5 @@
using Content.Shared.Chemistry.Components; using Content.Shared.Chemistry.Components;
using Content.Shared.Chemistry.Reagent;
using JetBrains.Annotations; using JetBrains.Annotations;
using Robust.Shared.GameObjects; using Robust.Shared.GameObjects;
@@ -6,9 +7,9 @@ namespace Content.Server.Chemistry.ReagentEffects.PlantMetabolism
{ {
public class PlantAdjustMutationLevel : PlantAdjustAttribute public class PlantAdjustMutationLevel : PlantAdjustAttribute
{ {
public override void Metabolize(EntityUid plantHolder, EntityUid organEntity, Solution.ReagentQuantity reagent, IEntityManager entityManager) public override void Metabolize(ReagentEffectArgs args)
{ {
if (!CanMetabolize(plantHolder, out var plantHolderComp, entityManager)) if (!CanMetabolize(args.SolutionEntity, out var plantHolderComp, args.EntityManager))
return; return;
plantHolderComp.MutationLevel += Amount * plantHolderComp.MutationMod; plantHolderComp.MutationLevel += Amount * plantHolderComp.MutationMod;

View File

@@ -1,4 +1,5 @@
using Content.Shared.Chemistry.Components; using Content.Shared.Chemistry.Components;
using Content.Shared.Chemistry.Reagent;
using JetBrains.Annotations; using JetBrains.Annotations;
using Robust.Shared.GameObjects; using Robust.Shared.GameObjects;
@@ -7,9 +8,9 @@ namespace Content.Server.Chemistry.ReagentEffects.PlantMetabolism
[UsedImplicitly] [UsedImplicitly]
public class PlantAdjustMutationMod : PlantAdjustAttribute public class PlantAdjustMutationMod : PlantAdjustAttribute
{ {
public override void Metabolize(EntityUid plantHolder, EntityUid organEntity, Solution.ReagentQuantity reagent, IEntityManager entityManager) public override void Metabolize(ReagentEffectArgs args)
{ {
if (!CanMetabolize(plantHolder, out var plantHolderComp, entityManager)) if (!CanMetabolize(args.SolutionEntity, out var plantHolderComp, args.EntityManager))
return; return;
plantHolderComp.MutationMod += Amount; plantHolderComp.MutationMod += Amount;

View File

@@ -1,4 +1,5 @@
using Content.Shared.Chemistry.Components; using Content.Shared.Chemistry.Components;
using Content.Shared.Chemistry.Reagent;
using JetBrains.Annotations; using JetBrains.Annotations;
using Robust.Shared.GameObjects; using Robust.Shared.GameObjects;
@@ -7,9 +8,9 @@ namespace Content.Server.Chemistry.ReagentEffects.PlantMetabolism
[UsedImplicitly] [UsedImplicitly]
public class PlantAdjustNutrition : PlantAdjustAttribute public class PlantAdjustNutrition : PlantAdjustAttribute
{ {
public override void Metabolize(EntityUid plantHolder, EntityUid organEntity, Solution.ReagentQuantity reagent, IEntityManager entityManager) public override void Metabolize(ReagentEffectArgs args)
{ {
if (!CanMetabolize(plantHolder, out var plantHolderComp, entityManager)) if (!CanMetabolize(args.SolutionEntity, out var plantHolderComp, args.EntityManager))
return; return;
plantHolderComp.AdjustNutrient(Amount); plantHolderComp.AdjustNutrient(Amount);

View File

@@ -1,4 +1,5 @@
using Content.Shared.Chemistry.Components; using Content.Shared.Chemistry.Components;
using Content.Shared.Chemistry.Reagent;
using JetBrains.Annotations; using JetBrains.Annotations;
using Robust.Shared.GameObjects; using Robust.Shared.GameObjects;
@@ -7,9 +8,9 @@ namespace Content.Server.Chemistry.ReagentEffects.PlantMetabolism
[UsedImplicitly] [UsedImplicitly]
public class PlantAdjustPests : PlantAdjustAttribute public class PlantAdjustPests : PlantAdjustAttribute
{ {
public override void Metabolize(EntityUid plantHolder, EntityUid organEntity, Solution.ReagentQuantity reagent, IEntityManager entityManager) public override void Metabolize(ReagentEffectArgs args)
{ {
if (!CanMetabolize(plantHolder, out var plantHolderComp, entityManager)) if (!CanMetabolize(args.SolutionEntity, out var plantHolderComp, args.EntityManager))
return; return;
plantHolderComp.PestLevel += Amount; plantHolderComp.PestLevel += Amount;

View File

@@ -1,4 +1,5 @@
using Content.Shared.Chemistry.Components; using Content.Shared.Chemistry.Components;
using Content.Shared.Chemistry.Reagent;
using JetBrains.Annotations; using JetBrains.Annotations;
using Robust.Shared.GameObjects; using Robust.Shared.GameObjects;
@@ -7,9 +8,9 @@ namespace Content.Server.Chemistry.ReagentEffects.PlantMetabolism
[UsedImplicitly] [UsedImplicitly]
public class PlantAdjustToxins : PlantAdjustAttribute public class PlantAdjustToxins : PlantAdjustAttribute
{ {
public override void Metabolize(EntityUid plantHolder, EntityUid organEntity, Solution.ReagentQuantity reagent, IEntityManager entityManager) public override void Metabolize(ReagentEffectArgs args)
{ {
if (!CanMetabolize(plantHolder, out var plantHolderComp, entityManager)) if (!CanMetabolize(args.SolutionEntity, out var plantHolderComp, args.EntityManager))
return; return;
plantHolderComp.Toxins += Amount; plantHolderComp.Toxins += Amount;

View File

@@ -1,4 +1,5 @@
using Content.Shared.Chemistry.Components; using Content.Shared.Chemistry.Components;
using Content.Shared.Chemistry.Reagent;
using JetBrains.Annotations; using JetBrains.Annotations;
using Robust.Shared.GameObjects; using Robust.Shared.GameObjects;
@@ -7,9 +8,9 @@ namespace Content.Server.Chemistry.ReagentEffects.PlantMetabolism
[UsedImplicitly] [UsedImplicitly]
public class PlantAdjustWater : PlantAdjustAttribute public class PlantAdjustWater : PlantAdjustAttribute
{ {
public override void Metabolize(EntityUid plantHolder, EntityUid organEntity, Solution.ReagentQuantity reagent, IEntityManager entityManager) public override void Metabolize(ReagentEffectArgs args)
{ {
if (!CanMetabolize(plantHolder, out var plantHolderComp, entityManager)) if (!CanMetabolize(args.SolutionEntity, out var plantHolderComp, args.EntityManager))
return; return;
plantHolderComp.AdjustWater(Amount); plantHolderComp.AdjustWater(Amount);

View File

@@ -1,4 +1,5 @@
using Content.Shared.Chemistry.Components; using Content.Shared.Chemistry.Components;
using Content.Shared.Chemistry.Reagent;
using JetBrains.Annotations; using JetBrains.Annotations;
using Robust.Shared.GameObjects; using Robust.Shared.GameObjects;
@@ -7,9 +8,9 @@ namespace Content.Server.Chemistry.ReagentEffects.PlantMetabolism
[UsedImplicitly] [UsedImplicitly]
public class PlantAdjustWeeds : PlantAdjustAttribute public class PlantAdjustWeeds : PlantAdjustAttribute
{ {
public override void Metabolize(EntityUid plantHolder, EntityUid organEntity, Solution.ReagentQuantity reagent, IEntityManager entityManager) public override void Metabolize(ReagentEffectArgs args)
{ {
if (!CanMetabolize(plantHolder, out var plantHolderComp, entityManager)) if (!CanMetabolize(args.SolutionEntity, out var plantHolderComp, args.EntityManager))
return; return;
plantHolderComp.WeedLevel += Amount; plantHolderComp.WeedLevel += Amount;

View File

@@ -1,4 +1,5 @@
using Content.Shared.Chemistry.Components; using Content.Shared.Chemistry.Components;
using Content.Shared.Chemistry.Reagent;
using JetBrains.Annotations; using JetBrains.Annotations;
using Robust.Shared.GameObjects; using Robust.Shared.GameObjects;
@@ -7,9 +8,9 @@ namespace Content.Server.Chemistry.ReagentEffects.PlantMetabolism
[UsedImplicitly] [UsedImplicitly]
public class PlantAffectGrowth : PlantAdjustAttribute public class PlantAffectGrowth : PlantAdjustAttribute
{ {
public override void Metabolize(EntityUid plantHolder, EntityUid organEntity, Solution.ReagentQuantity reagent, IEntityManager entityManager) public override void Metabolize(ReagentEffectArgs args)
{ {
if (!CanMetabolize(plantHolder, out var plantHolderComp, entityManager)) if (!CanMetabolize(args.SolutionEntity, out var plantHolderComp, args.EntityManager))
return; return;
plantHolderComp.AffectGrowth((int) Amount); plantHolderComp.AffectGrowth((int) Amount);

View File

@@ -15,9 +15,9 @@ namespace Content.Server.Chemistry.ReagentEffects.PlantMetabolism
[DataDefinition] [DataDefinition]
public class PlantClonexadone : ReagentEffect public class PlantClonexadone : ReagentEffect
{ {
public override void Metabolize(EntityUid plantHolder, EntityUid organEntity, Solution.ReagentQuantity reagent, IEntityManager entityManager) public override void Metabolize(ReagentEffectArgs args)
{ {
if (!entityManager.TryGetComponent(plantHolder, out PlantHolderComponent? plantHolderComp) if (!args.EntityManager.TryGetComponent(args.SolutionEntity, out PlantHolderComponent? plantHolderComp)
|| plantHolderComp.Seed == null || plantHolderComp.Dead) || plantHolderComp.Seed == null || plantHolderComp.Dead)
return; return;

View File

@@ -15,9 +15,9 @@ namespace Content.Server.Chemistry.ReagentEffects.PlantMetabolism
[DataDefinition] [DataDefinition]
public class PlantDiethylamine : ReagentEffect public class PlantDiethylamine : ReagentEffect
{ {
public override void Metabolize(EntityUid plantHolder, EntityUid organEntity, Solution.ReagentQuantity reagent, IEntityManager entityManager) public override void Metabolize(ReagentEffectArgs args)
{ {
if (!entityManager.TryGetComponent(plantHolder, out PlantHolderComponent? plantHolderComp) if (!args.EntityManager.TryGetComponent(args.SolutionEntity, out PlantHolderComponent? plantHolderComp)
|| plantHolderComp.Seed == null || plantHolderComp.Dead || || plantHolderComp.Seed == null || plantHolderComp.Dead ||
plantHolderComp.Seed.Immutable) plantHolderComp.Seed.Immutable)
return; return;

View File

@@ -15,9 +15,9 @@ namespace Content.Server.Chemistry.ReagentEffects.PlantMetabolism
[DataDefinition] [DataDefinition]
public class RobustHarvest : ReagentEffect public class RobustHarvest : ReagentEffect
{ {
public override void Metabolize(EntityUid plantHolder, EntityUid organEntity, Solution.ReagentQuantity reagent, IEntityManager entityManager) public override void Metabolize(ReagentEffectArgs args)
{ {
if (!entityManager.TryGetComponent(plantHolder, out PlantHolderComponent? plantHolderComp) if (!args.EntityManager.TryGetComponent(args.SolutionEntity, out PlantHolderComponent? plantHolderComp)
|| plantHolderComp.Seed == null || plantHolderComp.Dead || || plantHolderComp.Seed == null || plantHolderComp.Dead ||
plantHolderComp.Seed.Immutable) plantHolderComp.Seed.Immutable)
return; return;

View File

@@ -18,10 +18,10 @@ namespace Content.Server.Chemistry.ReagentEffects
[DataField("factor")] public float NutritionFactor { get; set; } = 3.0f; [DataField("factor")] public float NutritionFactor { get; set; } = 3.0f;
//Remove reagent at set rate, satiate hunger if a HungerComponent can be found //Remove reagent at set rate, satiate hunger if a HungerComponent can be found
public override void Metabolize(EntityUid solutionEntity, EntityUid organEntity, Solution.ReagentQuantity reagent, IEntityManager entityManager) public override void Metabolize(ReagentEffectArgs args)
{ {
if (entityManager.TryGetComponent(solutionEntity, out HungerComponent? hunger)) if (args.EntityManager.TryGetComponent(args.SolutionEntity, out HungerComponent? hunger))
hunger.UpdateFood(NutritionFactor); hunger.UpdateFood(NutritionFactor * (float) args.Metabolizing);
} }
} }
} }

View File

@@ -18,10 +18,10 @@ namespace Content.Server.Chemistry.ReagentEffects
public float HydrationFactor { get; set; } = 3.0f; public float HydrationFactor { get; set; } = 3.0f;
/// Satiate thirst if a ThirstComponent can be found /// Satiate thirst if a ThirstComponent can be found
public override void Metabolize(EntityUid solutionEntity, EntityUid organEntity, Solution.ReagentQuantity reagent, IEntityManager entityManager) public override void Metabolize(ReagentEffectArgs args)
{ {
if (entityManager.TryGetComponent(solutionEntity, out ThirstComponent? thirst)) if (args.EntityManager.TryGetComponent(args.SolutionEntity, out ThirstComponent? thirst))
thirst.UpdateThirst(HydrationFactor); thirst.UpdateThirst(HydrationFactor * (float) args.Metabolizing);
} }
} }
} }

View File

@@ -0,0 +1,24 @@
using System.Collections.Generic;
using Content.Server.Nutrition.EntitySystems;
using Content.Shared.Chemistry.Components;
using Content.Shared.Chemistry.Reagent;
using Content.Shared.FixedPoint;
using Content.Shared.Nutrition.Components;
using JetBrains.Annotations;
using Robust.Shared.GameObjects;
using Robust.Shared.Serialization.Manager.Attributes;
using Robust.Shared.Serialization.TypeSerializers.Implementations.Custom.Prototype.Set;
namespace Content.Server.Chemistry.ReagentEffects
{
[UsedImplicitly]
public class WashCreamPieReaction : ReagentEffect
{
public override void Metabolize(ReagentEffectArgs args)
{
if (!args.EntityManager.TryGetComponent(args.SolutionEntity, out CreamPiedComponent? creamPied)) return;
EntitySystem.Get<CreamPieSystem>().SetCreamPied(args.SolutionEntity, creamPied, false);
}
}
}

View File

@@ -1,35 +0,0 @@
using System.Collections.Generic;
using Content.Server.Chemistry.EntitySystems;
using Content.Shared.Chemistry.Components;
using Content.Shared.Chemistry.Reagent;
using Content.Shared.FixedPoint;
using JetBrains.Annotations;
using Robust.Shared.GameObjects;
using Robust.Shared.Serialization.Manager.Attributes;
using Robust.Shared.Serialization.TypeSerializers.Implementations.Custom.Prototype.Set;
namespace Content.Server.Chemistry.ReagentEntityReactions
{
[UsedImplicitly]
public class AddToSolutionReaction : ReagentEntityReaction
{
[DataField("solution")]
private string _solution = "reagents";
[DataField("reagents", true, customTypeSerializer: typeof(PrototypeIdHashSetSerializer<ReagentPrototype>))]
// ReSharper disable once CollectionNeverUpdated.Local
private readonly HashSet<string> _reagents = new();
protected override void React(EntityUid uid, ReagentPrototype reagent, FixedPoint2 volume, Solution? source, IEntityManager entityManager)
{
// TODO see if this is correct
if (!EntitySystem.Get<SolutionContainerSystem>()
.TryGetSolution(uid, _solution, out var solutionContainer)
|| (_reagents.Count > 0 && !_reagents.Contains(reagent.ID))) return;
if (EntitySystem.Get<SolutionContainerSystem>()
.TryAddReagent(uid, solutionContainer, reagent.ID, volume, out var accepted))
source?.RemoveReagent(reagent.ID, accepted);
}
}
}

View File

@@ -1,30 +0,0 @@
using System.Collections.Generic;
using Content.Server.Atmos.Components;
using Content.Server.Atmos.EntitySystems;
using Content.Shared.Chemistry.Components;
using Content.Shared.Chemistry.Reagent;
using Content.Shared.FixedPoint;
using JetBrains.Annotations;
using Robust.Shared.GameObjects;
using Robust.Shared.Serialization.Manager.Attributes;
using Robust.Shared.Serialization.TypeSerializers.Implementations.Custom.Prototype.Set;
namespace Content.Server.Chemistry.ReagentEntityReactions
{
[UsedImplicitly]
public class ExtinguishReaction : ReagentEntityReaction
{
[DataField("reagents", true, customTypeSerializer:typeof(PrototypeIdHashSetSerializer<ReagentPrototype>))]
// ReSharper disable once CollectionNeverUpdated.Local
private readonly HashSet<string> _reagents = new ();
protected override void React(EntityUid uid, ReagentPrototype reagent, FixedPoint2 volume, Solution? source, IEntityManager entityManager)
{
if (!entityManager.TryGetComponent(uid, out FlammableComponent? flammable) || !_reagents.Contains(reagent.ID)) return;
var flammableSystem = EntitySystem.Get<FlammableSystem>();
flammableSystem.Extinguish(uid, flammable);
flammableSystem.AdjustFireStacks(uid, -1.5f, flammable);
}
}
}

View File

@@ -1,29 +0,0 @@
using System.Collections.Generic;
using Content.Server.Atmos.Components;
using Content.Server.Atmos.EntitySystems;
using Content.Shared.Chemistry.Components;
using Content.Shared.Chemistry.Reagent;
using Content.Shared.FixedPoint;
using JetBrains.Annotations;
using Robust.Shared.GameObjects;
using Robust.Shared.Serialization.Manager.Attributes;
using Robust.Shared.Serialization.TypeSerializers.Implementations.Custom.Prototype.Set;
namespace Content.Server.Chemistry.ReagentEntityReactions
{
[UsedImplicitly]
public class FlammableReaction : ReagentEntityReaction
{
[DataField("reagents", true, customTypeSerializer:typeof(PrototypeIdHashSetSerializer<ReagentPrototype>))]
// ReSharper disable once CollectionNeverUpdated.Local
private readonly HashSet<string> _reagents = new ();
protected override void React(EntityUid uid, ReagentPrototype reagent, FixedPoint2 volume, Solution? source, IEntityManager entityManager)
{
if (!entityManager.TryGetComponent(uid, out FlammableComponent? flammable) || !_reagents.Contains(reagent.ID)) return;
EntitySystem.Get<FlammableSystem>().AdjustFireStacks(uid, volume.Float() / 10f, flammable);
source?.RemoveReagent(reagent.ID, volume);
}
}
}

View File

@@ -1,28 +0,0 @@
using System.Collections.Generic;
using Content.Server.Nutrition.EntitySystems;
using Content.Shared.Chemistry.Components;
using Content.Shared.Chemistry.Reagent;
using Content.Shared.FixedPoint;
using Content.Shared.Nutrition.Components;
using JetBrains.Annotations;
using Robust.Shared.GameObjects;
using Robust.Shared.Serialization.Manager.Attributes;
using Robust.Shared.Serialization.TypeSerializers.Implementations.Custom.Prototype.Set;
namespace Content.Server.Chemistry.ReagentEntityReactions
{
[UsedImplicitly]
public class WashCreamPieReaction : ReagentEntityReaction
{
[DataField("reagents", true, customTypeSerializer:typeof(PrototypeIdHashSetSerializer<ReagentPrototype>))]
// ReSharper disable once CollectionNeverUpdated.Local
private readonly HashSet<string> _reagents = new ();
protected override void React(EntityUid uid, ReagentPrototype reagent, FixedPoint2 volume, Solution? source, IEntityManager entityManager)
{
if (!entityManager.TryGetComponent(uid, out CreamPiedComponent? creamPied) || !_reagents.Contains(reagent.ID)) return;
EntitySystem.Get<CreamPieSystem>().SetCreamPied(uid, creamPied, false);
}
}
}

View File

@@ -34,14 +34,36 @@ namespace Content.Shared.Chemistry
if (!EntityManager.TryGetComponent(uid, out ReactiveComponent? reactive)) if (!EntityManager.TryGetComponent(uid, out ReactiveComponent? reactive))
return; return;
foreach (var reaction in reactive.Reactions) // If we have a source solution, use the reagent quantity we have left. Otherwise, use the reaction volume specified.
{ var args = new ReagentEffectArgs(uid, null, source, reagent,
// If we have a source solution, use the reagent quantity we have left. Otherwise, use the reaction volume specified. source?.GetReagentQuantity(reagent.ID) ?? reactVolume, EntityManager, method);
reaction.React(method, uid, reagent, source?.GetReagentQuantity(reagent.ID) ?? reactVolume, source, EntityManager);
// Make sure we still have enough reagent to go... foreach (var entry in reactive.Reactions)
if (source != null && !source.ContainsReagent(reagent.ID)) {
break; if (!entry.Methods.Contains(method))
continue;
if (entry.Reagents != null && !entry.Reagents.Contains(reagent.ID))
continue;
foreach (var effect in entry.Effects)
{
bool failed = false;
foreach (var cond in effect.Conditions ?? new ReagentEffectCondition[] { })
{
if (!cond.Condition(args))
failed = true;
}
if (failed)
continue;
effect.Metabolize(args);
// Make sure we still have enough reagent to go...
if (source != null && !source.ContainsReagent(reagent.ID))
break;
}
} }
} }
} }

View File

@@ -1,7 +1,9 @@
using System; using System;
using System.Collections.Generic;
using Content.Shared.Chemistry.Reagent; using Content.Shared.Chemistry.Reagent;
using Robust.Shared.GameObjects; using Robust.Shared.GameObjects;
using Robust.Shared.Serialization.Manager.Attributes; using Robust.Shared.Serialization.Manager.Attributes;
using Robust.Shared.Serialization.TypeSerializers.Implementations.Custom.Prototype.Set;
namespace Content.Shared.Chemistry.Reaction namespace Content.Shared.Chemistry.Reaction
{ {
@@ -10,7 +12,20 @@ namespace Content.Shared.Chemistry.Reaction
{ {
public override string Name => "Reactive"; public override string Name => "Reactive";
[DataField("reactions", true, serverOnly:true)] [DataField("reactions", required: true, readOnly: true, serverOnly: true)]
public ReagentEntityReaction[] Reactions { get; } = Array.Empty<ReagentEntityReaction>(); public List<ReactiveReagentEffectEntry> Reactions { get; } = default!;
}
[DataDefinition]
public class ReactiveReagentEffectEntry
{
[DataField("methods")]
public HashSet<ReactionMethod> Methods = default!;
[DataField("reagents", customTypeSerializer: typeof(PrototypeIdHashSetSerializer<ReagentPrototype>))]
public HashSet<string>? Reagents = null;
[DataField("effects", required: true)]
public List<ReagentEffect> Effects = default!;
} }
} }

View File

@@ -1,6 +1,7 @@
using System.Collections.Generic; using System.Collections.Generic;
using Content.Shared.Chemistry.Components; using Content.Shared.Chemistry.Components;
using Content.Shared.Chemistry.Reagent; using Content.Shared.Chemistry.Reagent;
using Content.Shared.FixedPoint;
using JetBrains.Annotations; using JetBrains.Annotations;
using Robust.Shared.GameObjects; using Robust.Shared.GameObjects;
using Robust.Shared.Serialization.Manager.Attributes; using Robust.Shared.Serialization.Manager.Attributes;
@@ -21,6 +22,23 @@ namespace Content.Shared.Chemistry.Reagent
[DataField("conditions")] [DataField("conditions")]
public ReagentEffectCondition[]? Conditions; public ReagentEffectCondition[]? Conditions;
public abstract void Metabolize(EntityUid solutionEntity, EntityUid organEntity, Solution.ReagentQuantity reagent, IEntityManager entityManager); public abstract void Metabolize(ReagentEffectArgs args);
} }
public enum ReactionMethod
{
Touch,
Injection,
Ingestion,
}
public readonly record struct ReagentEffectArgs(
EntityUid SolutionEntity,
EntityUid? OrganEntity,
Solution? Source,
ReagentPrototype Reagent,
FixedPoint2 Metabolizing,
IEntityManager EntityManager,
ReactionMethod? Method
);
} }

View File

@@ -9,6 +9,6 @@ namespace Content.Shared.Chemistry.Reagent
[MeansImplicitUse] [MeansImplicitUse]
public abstract class ReagentEffectCondition public abstract class ReagentEffectCondition
{ {
public abstract bool Condition(EntityUid solutionEntity, EntityUid organEntity, Solution.ReagentQuantity reagent, IEntityManager entityManager); public abstract bool Condition(ReagentEffectArgs args);
} }
} }

View File

@@ -1,56 +0,0 @@
using System;
using Content.Shared.FixedPoint;
using Robust.Shared.GameObjects;
using Robust.Shared.Serialization.Manager.Attributes;
using Robust.Shared.ViewVariables;
namespace Content.Shared.Chemistry.Reagent
{
public enum ReactionMethod
{
Touch,
Injection,
Ingestion,
}
[ImplicitDataDefinitionForInheritors]
public abstract class ReagentEntityReaction
{
[ViewVariables]
[DataField("touch")]
public bool Touch { get; } = false;
[ViewVariables]
[DataField("injection")]
public bool Injection { get; } = false;
[ViewVariables]
[DataField("ingestion")]
public bool Ingestion { get; } = false;
public void React(ReactionMethod method, EntityUid uid, ReagentPrototype reagent, FixedPoint2 volume, Components.Solution? source, IEntityManager entityManager)
{
switch (method)
{
case ReactionMethod.Touch:
if (!Touch)
return;
break;
case ReactionMethod.Injection:
if(!Injection)
return;
break;
case ReactionMethod.Ingestion:
if(!Ingestion)
return;
break;
default:
throw new ArgumentOutOfRangeException(nameof(method), method, null);
}
React(uid, reagent, volume, source, entityManager);
}
protected abstract void React(EntityUid uid, ReagentPrototype reagent, FixedPoint2 volume, Components.Solution? source, IEntityManager entityManager);
}
}

View File

@@ -106,7 +106,7 @@ namespace Content.Shared.Chemistry.Reagent
return removed; return removed;
} }
public void ReactionPlant(EntityUid? plantHolder, Solution.ReagentQuantity amount) public void ReactionPlant(EntityUid? plantHolder, Solution.ReagentQuantity amount, Solution solution)
{ {
if (plantHolder == null) if (plantHolder == null)
return; return;
@@ -114,7 +114,9 @@ namespace Content.Shared.Chemistry.Reagent
var entMan = IoCManager.Resolve<IEntityManager>(); var entMan = IoCManager.Resolve<IEntityManager>();
foreach (var plantMetabolizable in _plantMetabolism) foreach (var plantMetabolizable in _plantMetabolism)
{ {
plantMetabolizable.Metabolize(plantHolder.Value, plantHolder.Value, amount, entMan); plantMetabolizable.Metabolize(
new ReagentEffectArgs(plantHolder.Value, null, solution, this, amount.Quantity, entMan, null)
);
} }
} }
} }

View File

@@ -118,6 +118,7 @@
# This is done because these chemicals need to have some effect even if they aren't being filtered out of your body. # This is done because these chemicals need to have some effect even if they aren't being filtered out of your body.
# You're technically 'immune to poison' without a heart, but.. uhh, you'll have bigger problems on your hands. # You're technically 'immune to poison' without a heart, but.. uhh, you'll have bigger problems on your hands.
- type: Metabolizer - type: Metabolizer
maxReagents: 2
metabolizerTypes: [Human] metabolizerTypes: [Human]
groups: groups:
- id: Medicine - id: Medicine
@@ -147,6 +148,8 @@
# TODO: Have it work off of the ent's solution container, and move this # TODO: Have it work off of the ent's solution container, and move this
# to intestines instead. # to intestines instead.
- type: Metabolizer - type: Metabolizer
# mm yummy
maxReagents: 3
metabolizerTypes: [Human] metabolizerTypes: [Human]
groups: groups:
- id: Food - id: Food
@@ -164,6 +167,7 @@
size: 1 size: 1
compatibility: Biological compatibility: Biological
- type: Metabolizer # The liver metabolizes certain chemicals only, like alcohol. - type: Metabolizer # The liver metabolizes certain chemicals only, like alcohol.
maxReagents: 1
metabolizerTypes: [Human] metabolizerTypes: [Human]
groups: groups:
- id: Alcohol - id: Alcohol
@@ -182,3 +186,8 @@
- type: Mechanism - type: Mechanism
size: 1 size: 1
compatibility: Biological compatibility: Biological
# The kidneys just remove anything that doesn't currently have any metabolisms, as a stopgap.
- type: Metabolizer
maxReagents: 5
metabolizerTypes: [Human]
removeEmpty: true

View File

@@ -6,17 +6,14 @@
components: components:
- type: Reactive - type: Reactive
reactions: reactions:
- reagents: [Water]
methods: [Touch]
effects:
- !type:ExtinguishReaction - !type:ExtinguishReaction
touch: true - reagents: [WeldingFuel, Thermite, Plasma, Ethanol]
reagents: methods: [Touch]
- Water effects:
- !type:FlammableReaction - !type:FlammableReaction
touch: true
reagents:
- WeldingFuel
- Thermite
- Plasma
- Ethanol
- type: UtilityAI - type: UtilityAI
behaviorSets: behaviorSets:
- Clothing - Clothing

View File

@@ -12,23 +12,18 @@
- FootstepSound - FootstepSound
- type: Reactive - type: Reactive
reactions: reactions:
- !type:ExtinguishReaction - reagents: [Water]
touch: true methods: [Touch]
reagents: effects:
- Water - !type:ExtinguishReaction
- !type:FlammableReaction - reagents: [WeldingFuel, Thermite, Plasma, Ethanol]
touch: true methods: [Touch]
reagents: effects:
- WeldingFuel - !type:FlammableReaction
- Thermite - reagents: [Water, SpaceCleaner]
- Plasma methods: [Touch]
- Ethanol effects:
- Oil - !type:WashCreamPieReaction
- !type:WashCreamPieReaction
touch: true
reagents:
- Water
- SpaceCleaner
- type: Flashable - type: Flashable
- type: Hands - type: Hands
- type: MovementSpeedModifier - type: MovementSpeedModifier

View File

@@ -20,13 +20,11 @@
state: cube state: cube
- type: Reactive - type: Reactive
reactions: reactions:
- reagents: [Water]
methods: [Touch, Ingestion, Injection]
effects:
- !type:AddToSolutionReaction - !type:AddToSolutionReaction
solution: cube solution: cube
touch: true
ingestion: true
injection: true
reagents:
- Water
- type: Rehydratable - type: Rehydratable
target: MobMonkey target: MobMonkey
- type: CollisionWake - type: CollisionWake
@@ -62,13 +60,11 @@
solution: plushie solution: plushie
- type: Reactive - type: Reactive
reactions: reactions:
- !type:AddToSolutionReaction - reagents: [Water]
solution: plushie methods: [Touch, Ingestion, Injection]
touch: true effects:
ingestion: true - !type:AddToSolutionReaction
injection: true solution: plushie
reagents:
- Water
- type: Rehydratable - type: Rehydratable
target: MobCarp target: MobCarp
- type: CollisionWake - type: CollisionWake

View File

@@ -46,19 +46,6 @@
damage: damage:
types: types:
Heat : 1 #per second, scales with number of fire 'stacks' Heat : 1 #per second, scales with number of fire 'stacks'
- type: Reactive
reactions:
- !type:ExtinguishReaction
touch: true
reagents:
- Water
- !type:FlammableReaction
touch: true
reagents:
- WeldingFuel
- Thermite
- Plasma
- Ethanol
- type: Appearance - type: Appearance
visuals: visuals:
- type: FireVisualizer - type: FireVisualizer

View File

@@ -50,6 +50,9 @@
anchored: true anchored: true
- type: Reactive - type: Reactive
reactions: reactions:
- reagents: [Water]
methods: [Touch, Ingestion, Injection]
effects:
- !type:AddToSolutionReaction - !type:AddToSolutionReaction
solution: soil solution: soil
- type: Appearance - type: Appearance