mirror of
https://github.com/wega-team/ss14-wega.git
synced 2026-06-09 10:06:49 +02:00
Solutions Refactor Part 1 - Solution Prototypes (#43412)
* Everything except the YAML slop... * She solution on my manager till I sajfslkahfsjakfhaskjfshajksafhfsakhfasjfas * another 1000 lines * fix chem dispenser size * go my shnelf * Implicit ass * rider is being mean for some reason * dasdas * borger * better formatting go! * clothes/bloodstream * Cartons * cups and bottles * mmm soder * bar drinks * Spray bottles and some size tweaks with hindsight * 99 bottles of beer on the wall I hate YAML * push that shit * mmm burger * Sneed * sheets * condiments * mmm yummy * fridge yummyfood * meat... * sub 300 * burger... * bready * let them eat cake * does she know how to make a grilled cheese? * pizza pie! * misc shit * soup or salad * Food and drinks, vanquished * the cubes!!! * Almost free from YAML... * fix test fails and some yaml issues * fix prediction, almost done * fix all test fails * remove master merge artifacts and undo autonetworking * review and compatibility * graaah * unfuck master merge I hate github * merg conflicts * sfsa * ehoop * sadas * afsafsaasf * merge conflicts * fucked up the merge conflicts * merge conflict is fucked I might need to completely redo this branch * test fail * no calcium???? --------- Co-authored-by: Princess Cheeseballs <66055347+Pronana@users.noreply.github.com>
This commit is contained in:
committed by
GitHub
parent
d74f891288
commit
ef3a0ecc2a
@@ -1,7 +1,8 @@
|
||||
using Content.Shared.Chemistry.Components;
|
||||
using Content.Shared.Chemistry.Components.SolutionManager;
|
||||
using Content.Shared.Chemistry.EntitySystems;
|
||||
using Robust.Shared.GameStates;
|
||||
|
||||
namespace Content.Client.Chemistry.Containers.EntitySystems;
|
||||
|
||||
public sealed partial class SolutionContainerSystem : SharedSolutionContainerSystem
|
||||
{
|
||||
}
|
||||
public sealed partial class SolutionContainerSystem : SharedSolutionContainerSystem;
|
||||
|
||||
@@ -117,8 +117,7 @@ public sealed class ChemistryGuideDataSystem : SharedChemistryGuideDataSystem
|
||||
|
||||
|
||||
if (extractableComponent.GrindableSolutionName is { } grindableSolutionId &&
|
||||
entProto.TryGetComponent<SolutionContainerManagerComponent>(out var manager, EntityManager.ComponentFactory) &&
|
||||
_solutionContainer.TryGetSolution(manager, grindableSolutionId, out var grindableSolution))
|
||||
_solutionContainer.TryGetSolution(entProto, grindableSolutionId, out var grindableSolution))
|
||||
{
|
||||
var data = new ReagentEntitySourceData(
|
||||
new() { DefaultGrindCategory },
|
||||
|
||||
@@ -23,15 +23,15 @@ public sealed class DrainTest : InteractionTest
|
||||
- type: entity
|
||||
parent: Puddle
|
||||
id: PuddleBloodTest
|
||||
suffix: Blood (30u)
|
||||
suffix: Blood
|
||||
components:
|
||||
- type: SolutionContainerManager
|
||||
solutions:
|
||||
puddle:
|
||||
maxVol: 1000
|
||||
reagents:
|
||||
- ReagentId: {BloodReagent}
|
||||
Quantity: {PuddleVolume}
|
||||
- type: Solution
|
||||
id: puddle
|
||||
solution:
|
||||
maxVol: 1000
|
||||
reagents:
|
||||
- ReagentId: {BloodReagent}
|
||||
Quantity: {PuddleVolume}
|
||||
";
|
||||
|
||||
|
||||
|
||||
@@ -21,10 +21,10 @@ public sealed class SolutionRoundingTest : GameTest
|
||||
- type: entity
|
||||
id: SolutionRoundingTestContainer
|
||||
components:
|
||||
- type: SolutionContainerManager
|
||||
solutions:
|
||||
beaker:
|
||||
maxVol: 100
|
||||
- type: Solution
|
||||
id: beaker
|
||||
solution:
|
||||
maxVol: 100
|
||||
|
||||
# This is the Chloral Hydrate recipe fyi.
|
||||
- type: reagent
|
||||
|
||||
@@ -20,10 +20,10 @@ public sealed class SolutionSystemTests : GameTest
|
||||
- type: entity
|
||||
id: SolutionTarget
|
||||
components:
|
||||
- type: SolutionContainerManager
|
||||
solutions:
|
||||
beaker:
|
||||
maxVol: 50
|
||||
- type: Solution
|
||||
id: beaker
|
||||
solution:
|
||||
maxVol: 50
|
||||
|
||||
- type: reagent
|
||||
id: TestReagentA
|
||||
|
||||
@@ -20,11 +20,10 @@ namespace Content.IntegrationTests.Tests.Chemistry
|
||||
- type: entity
|
||||
id: TestSolutionContainer
|
||||
components:
|
||||
- type: SolutionContainerManager
|
||||
solutions:
|
||||
beaker:
|
||||
maxVol: 50
|
||||
canMix: true";
|
||||
- type: Solution
|
||||
id: beaker
|
||||
solution:
|
||||
maxVol: 120";
|
||||
|
||||
private static string[] _reactions = GameDataScrounger.PrototypesOfKind<ReactionPrototype>();
|
||||
|
||||
|
||||
@@ -35,19 +35,19 @@ public sealed class AbsorbentTest : GameTest
|
||||
components:
|
||||
- type: Absorbent
|
||||
useAbsorberSolution: true
|
||||
- type: SolutionContainerManager
|
||||
solutions:
|
||||
absorbed:
|
||||
maxVol: 100
|
||||
- type: Solution
|
||||
id: absorbed
|
||||
solution:
|
||||
maxVol: 100
|
||||
|
||||
- type: entity
|
||||
name: {RefillableDummyId}
|
||||
id: {RefillableDummyId}
|
||||
components:
|
||||
- type: SolutionContainerManager
|
||||
solutions:
|
||||
refillable:
|
||||
maxVol: 200
|
||||
- type: Solution
|
||||
id: refillable
|
||||
solution:
|
||||
maxVol: 200
|
||||
- type: RefillableSolution
|
||||
solution: refillable
|
||||
|
||||
@@ -55,10 +55,10 @@ public sealed class AbsorbentTest : GameTest
|
||||
name: {SmallRefillableDummyId}
|
||||
id: {SmallRefillableDummyId}
|
||||
components:
|
||||
- type: SolutionContainerManager
|
||||
solutions:
|
||||
refillable:
|
||||
maxVol: 20
|
||||
- type: Solution
|
||||
id: refillable
|
||||
solution:
|
||||
maxVol: 20
|
||||
- type: RefillableSolution
|
||||
solution: refillable
|
||||
";
|
||||
|
||||
@@ -36,16 +36,17 @@ namespace Content.Server.Administration.Commands
|
||||
return;
|
||||
}
|
||||
|
||||
if (!_entManager.TryGetComponent(uid, out SolutionContainerManagerComponent? man))
|
||||
{
|
||||
shell.WriteLine($"Entity does not have any solutions.");
|
||||
return;
|
||||
}
|
||||
|
||||
var solutionContainerSystem = _entManager.System<SharedSolutionContainerSystem>();
|
||||
if (!solutionContainerSystem.TryGetSolution((uid.Value, man), args[1], out var solution))
|
||||
if (!solutionContainerSystem.TryGetSolution(uid.Value, args[1], out var solution))
|
||||
{
|
||||
var validSolutions = string.Join(", ", solutionContainerSystem.EnumerateSolutions((uid.Value, man)).Select(s => s.Name));
|
||||
var solutions = solutionContainerSystem.EnumerateSolutions(uid.Value).ToArray();
|
||||
if (!solutions.Any())
|
||||
{
|
||||
shell.WriteLine("Entity does not have any solutions!");
|
||||
return;
|
||||
}
|
||||
|
||||
var validSolutions = string.Join(", ", solutions.Select(s => s.Name));
|
||||
shell.WriteLine($"Entity does not have a \"{args[1]}\" solution. Valid solutions are:\n{validSolutions}");
|
||||
return;
|
||||
}
|
||||
|
||||
@@ -24,22 +24,23 @@ namespace Content.Server.Administration.Commands
|
||||
return;
|
||||
}
|
||||
|
||||
if (!NetEntity.TryParse(args[0], out var uidNet))
|
||||
if (!NetEntity.TryParse(args[0], out var uidNet) || !_entManager.TryGetEntity(uidNet, out var uid))
|
||||
{
|
||||
shell.WriteLine($"Invalid entity id.");
|
||||
return;
|
||||
}
|
||||
|
||||
if (!_entManager.TryGetEntity(uidNet, out var uid) || !_entManager.TryGetComponent(uid, out SolutionContainerManagerComponent? man))
|
||||
{
|
||||
shell.WriteLine($"Entity does not have any solutions.");
|
||||
return;
|
||||
}
|
||||
|
||||
var solutionContainerSystem = _entManager.System<SharedSolutionContainerSystem>();
|
||||
if (!solutionContainerSystem.TryGetSolution((uid.Value, man), args[1], out var solution))
|
||||
if (!solutionContainerSystem.TryGetSolution(uid.Value, args[1], out var solution))
|
||||
{
|
||||
var validSolutions = string.Join(", ", solutionContainerSystem.EnumerateSolutions((uid.Value, man)).Select(s => s.Name));
|
||||
var solutions = solutionContainerSystem.EnumerateSolutions(uid.Value).ToArray();
|
||||
if (!solutions.Any())
|
||||
{
|
||||
shell.WriteLine("Entity does not have any solutions!");
|
||||
return;
|
||||
}
|
||||
|
||||
var validSolutions = string.Join(", ", solutions.Select(s => s.Name));
|
||||
shell.WriteLine($"Entity does not have a \"{args[1]}\" solution. Valid solutions are:\n{validSolutions}");
|
||||
return;
|
||||
}
|
||||
|
||||
@@ -29,16 +29,17 @@ namespace Content.Server.Administration.Commands
|
||||
return;
|
||||
}
|
||||
|
||||
if (!_entManager.TryGetComponent(uid, out SolutionContainerManagerComponent? man))
|
||||
{
|
||||
shell.WriteLine($"Entity does not have any solutions.");
|
||||
return;
|
||||
}
|
||||
|
||||
var solutionContainerSystem = _entManager.System<SharedSolutionContainerSystem>();
|
||||
if (!solutionContainerSystem.TryGetSolution((uid.Value, man), args[1], out var solution))
|
||||
if (!solutionContainerSystem.TryGetSolution(uid.Value, args[1], out var solution))
|
||||
{
|
||||
var validSolutions = string.Join(", ", solutionContainerSystem.EnumerateSolutions((uid.Value, man)).Select(s => s.Name));
|
||||
var solutions = solutionContainerSystem.EnumerateSolutions(uid.Value).ToArray();
|
||||
if (!solutions.Any())
|
||||
{
|
||||
shell.WriteLine("Entity does not have any solutions!");
|
||||
return;
|
||||
}
|
||||
|
||||
var validSolutions = string.Join(", ", solutions.Select(s => s.Name));
|
||||
shell.WriteLine($"Entity does not have a \"{args[1]}\" solution. Valid solutions are:\n{validSolutions}");
|
||||
return;
|
||||
}
|
||||
|
||||
@@ -29,16 +29,17 @@ namespace Content.Server.Administration.Commands
|
||||
return;
|
||||
}
|
||||
|
||||
if (!_entManager.TryGetComponent(uid, out SolutionContainerManagerComponent? man))
|
||||
{
|
||||
shell.WriteLine($"Entity does not have any solutions.");
|
||||
return;
|
||||
}
|
||||
|
||||
var solutionContainerSystem = _entManager.System<SharedSolutionContainerSystem>();
|
||||
if (!solutionContainerSystem.TryGetSolution((uid.Value, man), args[1], out var solutionEnt, out var solution))
|
||||
if (!solutionContainerSystem.TryGetSolution(uid.Value, args[1], out var solutionEnt, out var solution))
|
||||
{
|
||||
var validSolutions = string.Join(", ", solutionContainerSystem.EnumerateSolutions((uid.Value, man)).Select(s => s.Name));
|
||||
var solutions = solutionContainerSystem.EnumerateSolutions(uid.Value).ToArray();
|
||||
if (!solutions.Any())
|
||||
{
|
||||
shell.WriteLine("Entity does not have any solutions!");
|
||||
return;
|
||||
}
|
||||
|
||||
var validSolutions = string.Join(", ", solutions.Select(s => s.Name));
|
||||
shell.WriteLine($"Entity does not have a \"{args[1]}\" solution. Valid solutions are:\n{validSolutions}");
|
||||
return;
|
||||
}
|
||||
|
||||
@@ -35,6 +35,7 @@ using Robust.Shared.Timing;
|
||||
using Robust.Shared.Toolshed;
|
||||
using Robust.Shared.Utility;
|
||||
using System.Linq;
|
||||
using Content.Shared.Chemistry.Components;
|
||||
using static Content.Shared.Configurable.ConfigurationComponent;
|
||||
|
||||
namespace Content.Server.Administration.Systems
|
||||
@@ -73,7 +74,10 @@ namespace Content.Server.Administration.Systems
|
||||
{
|
||||
SubscribeLocalEvent<GetVerbsEvent<Verb>>(GetVerbs);
|
||||
SubscribeLocalEvent<RoundRestartCleanupEvent>(Reset);
|
||||
SubscribeLocalEvent<SolutionContainerManagerComponent, SolutionContainerChangedEvent>(OnSolutionChanged);
|
||||
|
||||
// TODO: This is genuinely terrible, solutions are already networked and we shouldn't need to update the BUI like this.
|
||||
SubscribeLocalEvent<SolutionComponent, SolutionChangedEvent>((x, ref _) => OnSolutionChanged(x.Owner));
|
||||
SubscribeLocalEvent<SolutionManagerComponent, SolutionChangedEvent>((x, ref _) => OnSolutionChanged(x.Owner));
|
||||
}
|
||||
|
||||
private void GetVerbs(GetVerbsEvent<Verb> ev)
|
||||
@@ -445,7 +449,7 @@ namespace Content.Server.Administration.Systems
|
||||
}
|
||||
|
||||
// Control mob verb
|
||||
if ((_toolshed.ActivePermissionController?.CheckInvokable(new CommandSpec(_toolshed.DefaultEnvironment.GetCommand("mind"), "control"), player, out _) ?? false) &&
|
||||
if (_toolshed.ActivePermissionController?.CheckInvokable(new CommandSpec(_toolshed.DefaultEnvironment.GetCommand("mind"), "control"), player, out _) ?? false &&
|
||||
args.User != args.Target)
|
||||
{
|
||||
Verb verb = new()
|
||||
@@ -573,7 +577,7 @@ namespace Content.Server.Administration.Systems
|
||||
|
||||
// Add verb to open Solution Editor
|
||||
if (_groupController.CanCommand(player, "addreagent") &&
|
||||
HasComp<SolutionContainerManagerComponent>(args.Target))
|
||||
(HasComp<SolutionManagerComponent>(args.Target) || HasComp<SolutionComponent>(args.Target)))
|
||||
{
|
||||
Verb verb = new()
|
||||
{
|
||||
@@ -588,13 +592,13 @@ namespace Content.Server.Administration.Systems
|
||||
}
|
||||
|
||||
#region SolutionsEui
|
||||
private void OnSolutionChanged(Entity<SolutionContainerManagerComponent> entity, ref SolutionContainerChangedEvent args)
|
||||
private void OnSolutionChanged(EntityUid uid)
|
||||
{
|
||||
foreach (var list in _openSolutionUis.Values)
|
||||
{
|
||||
foreach (var eui in list)
|
||||
{
|
||||
if (eui.Target == entity.Owner)
|
||||
if (eui.Target == uid)
|
||||
eui.StateDirty();
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,12 +1,9 @@
|
||||
using Content.Server.Chemistry.Containers.EntitySystems;
|
||||
using Content.Shared.Administration;
|
||||
using Content.Shared.Administration;
|
||||
using Content.Shared.Chemistry.Components;
|
||||
using Content.Shared.Chemistry.Reagent;
|
||||
using Content.Shared.FixedPoint;
|
||||
using Content.Shared.Chemistry.EntitySystems;
|
||||
using Robust.Shared.Toolshed;
|
||||
using Robust.Shared.Toolshed.Syntax;
|
||||
using Robust.Shared.Toolshed.TypeParsers;
|
||||
using System.Linq;
|
||||
using Robust.Shared.Prototypes;
|
||||
|
||||
|
||||
@@ -1,5 +1,4 @@
|
||||
using Content.Server.Administration.Systems;
|
||||
using Content.Server.Chemistry.Containers.EntitySystems;
|
||||
using Content.Server.EUI;
|
||||
using Content.Shared.Administration;
|
||||
using Content.Shared.Chemistry.Components.SolutionManager;
|
||||
@@ -42,21 +41,15 @@ namespace Content.Server.Administration.UI
|
||||
|
||||
public override EuiStateBase GetNewState()
|
||||
{
|
||||
List<(string Name, NetEntity Solution)>? netSolutions;
|
||||
List<(string Name, NetEntity Solution)>? netSolutions = new();
|
||||
|
||||
if (_entityManager.TryGetComponent(Target, out SolutionContainerManagerComponent? container) && container.Containers.Count > 0)
|
||||
foreach (var (name, solution) in _solutionContainerSystem.EnumerateSolutions(Target))
|
||||
{
|
||||
netSolutions = new();
|
||||
foreach (var (name, solution) in _solutionContainerSystem.EnumerateSolutions((Target, container)))
|
||||
{
|
||||
if (name is null || !_entityManager.TryGetNetEntity(solution, out var netSolution))
|
||||
continue;
|
||||
if (name is null || !_entityManager.TryGetNetEntity(solution, out var netSolution))
|
||||
continue;
|
||||
|
||||
netSolutions.Add((name, netSolution.Value));
|
||||
}
|
||||
netSolutions.Add((name, netSolution.Value));
|
||||
}
|
||||
else
|
||||
netSolutions = null;
|
||||
|
||||
return new EditSolutionsEuiState(_entityManager.GetNetEntity(Target), netSolutions, _gameTiming.CurTick);
|
||||
}
|
||||
|
||||
@@ -11,38 +11,9 @@ public sealed class BloodstreamSystem : SharedBloodstreamSystem
|
||||
{
|
||||
base.Initialize();
|
||||
|
||||
SubscribeLocalEvent<BloodstreamComponent, ComponentInit>(OnComponentInit);
|
||||
SubscribeLocalEvent<BloodstreamComponent, GenerateDnaEvent>(OnDnaGenerated);
|
||||
}
|
||||
|
||||
// not sure if we can move this to shared or not
|
||||
// it would certainly help if SolutionContainer was documented
|
||||
// but since we usually don't add the component dynamically to entities we can keep this unpredicted for now
|
||||
private void OnComponentInit(Entity<BloodstreamComponent> entity, ref ComponentInit args)
|
||||
{
|
||||
if (!SolutionContainer.EnsureSolution(entity.Owner,
|
||||
entity.Comp.BloodSolutionName,
|
||||
out var bloodSolution) ||
|
||||
!SolutionContainer.EnsureSolution(entity.Owner,
|
||||
entity.Comp.BloodTemporarySolutionName,
|
||||
out var tempSolution) ||
|
||||
!SolutionContainer.EnsureSolution(entity.Owner,
|
||||
entity.Comp.MetabolitesSolutionName,
|
||||
out var metabolitesSolution))
|
||||
return;
|
||||
|
||||
bloodSolution.MaxVolume = entity.Comp.BloodReferenceSolution.Volume * entity.Comp.MaxVolumeModifier;
|
||||
metabolitesSolution.MaxVolume = bloodSolution.MaxVolume;
|
||||
tempSolution.MaxVolume = entity.Comp.BleedPuddleThreshold * 4; // give some leeway, for chemstream as well
|
||||
entity.Comp.BloodReferenceSolution.SetReagentData(GetEntityBloodData((entity, entity.Comp)));
|
||||
|
||||
// Fill blood solution with BLOOD
|
||||
// The DNA string might not be initialized yet, but the reagent data gets updated in the GenerateDnaEvent subscription
|
||||
var solution = entity.Comp.BloodReferenceSolution.Clone();
|
||||
solution.ScaleTo(entity.Comp.BloodReferenceSolution.Volume - bloodSolution.Volume);
|
||||
bloodSolution.AddSolution(solution, PrototypeManager);
|
||||
}
|
||||
|
||||
// forensics is not predicted yet
|
||||
private void OnDnaGenerated(Entity<BloodstreamComponent> entity, ref GenerateDnaEvent args)
|
||||
{
|
||||
|
||||
@@ -20,21 +20,17 @@ public sealed partial class BotanySystem
|
||||
_entityEffects.TryApplyEffect(uid, mutation.Effect);
|
||||
}
|
||||
|
||||
if (!_solutionContainerSystem.EnsureSolution(uid,
|
||||
produce.SolutionName,
|
||||
out var solutionContainer,
|
||||
FixedPoint2.Zero))
|
||||
return;
|
||||
_solutionContainerSystem.EnsureSolution(uid, produce.SolutionName, out var solution);
|
||||
|
||||
solutionContainer.RemoveAllSolution();
|
||||
solution.Comp.Solution.RemoveAllSolution();
|
||||
foreach (var (chem, quantity) in seed.Chemicals)
|
||||
{
|
||||
var amount = quantity.Min;
|
||||
if (quantity.PotencyDivisor > 0 && seed.Potency > 0)
|
||||
amount += seed.Potency / quantity.PotencyDivisor;
|
||||
amount = FixedPoint2.Clamp(amount, quantity.Min, quantity.Max);
|
||||
solutionContainer.MaxVolume += amount;
|
||||
solutionContainer.AddReagent(chem, amount);
|
||||
solution.Comp.Solution.MaxVolume += amount;
|
||||
solution.Comp.Solution.AddReagent(chem, amount);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -130,14 +130,11 @@ public sealed class PricingSystem : EntitySystem
|
||||
args.Price += entity.Comp.RandomPrice ?? 0;
|
||||
}
|
||||
|
||||
private double GetSolutionPrice(Entity<SolutionContainerManagerComponent> entity)
|
||||
private double GetSolutionPrice(EntityUid entity)
|
||||
{
|
||||
if (Comp<MetaDataComponent>(entity).EntityLifeStage < EntityLifeStage.MapInitialized)
|
||||
return GetSolutionPrice(entity.Comp);
|
||||
|
||||
var price = 0.0;
|
||||
|
||||
foreach (var (_, soln) in _solutionContainerSystem.EnumerateSolutions((entity.Owner, entity.Comp)))
|
||||
foreach (var (_, soln) in _solutionContainerSystem.EnumerateSolutions(entity))
|
||||
{
|
||||
var solution = soln.Comp.Solution;
|
||||
foreach (var (reagent, quantity) in solution.Contents)
|
||||
@@ -153,25 +150,6 @@ public sealed class PricingSystem : EntitySystem
|
||||
return price;
|
||||
}
|
||||
|
||||
private double GetSolutionPrice(SolutionContainerManagerComponent component)
|
||||
{
|
||||
var price = 0.0;
|
||||
|
||||
foreach (var (_, prototype) in _solutionContainerSystem.EnumerateSolutions(component))
|
||||
{
|
||||
foreach (var (reagent, quantity) in prototype.Contents)
|
||||
{
|
||||
if (!_prototypeManager.TryIndex<ReagentPrototype>(reagent.Prototype, out var reagentProto))
|
||||
continue;
|
||||
|
||||
// TODO check ReagentData for price information?
|
||||
price += (float) quantity * reagentProto.PricePerUnit;
|
||||
}
|
||||
}
|
||||
|
||||
return price;
|
||||
}
|
||||
|
||||
private double GetMaterialPrice(PhysicalCompositionComponent component)
|
||||
{
|
||||
double price = 0;
|
||||
@@ -319,22 +297,43 @@ public sealed class PricingSystem : EntitySystem
|
||||
{
|
||||
var price = 0.0;
|
||||
|
||||
if (TryComp<SolutionContainerManagerComponent>(uid, out var solComp))
|
||||
var meta = MetaData(uid);
|
||||
if (meta.EntityLifeStage < EntityLifeStage.MapInitialized)
|
||||
return GetSolutionsPrice(meta.EntityPrototype);
|
||||
|
||||
foreach (var (_, soln) in _solutionContainerSystem.EnumerateSolutions(uid))
|
||||
{
|
||||
price += GetSolutionPrice((uid, solComp));
|
||||
var solution = soln.Comp.Solution;
|
||||
foreach (var (reagent, quantity) in solution.Contents)
|
||||
{
|
||||
if (!_prototypeManager.TryIndex<ReagentPrototype>(reagent.Prototype, out var reagentProto))
|
||||
continue;
|
||||
|
||||
// TODO check ReagentData for price information?
|
||||
price += (float) quantity * reagentProto.PricePerUnit;
|
||||
}
|
||||
}
|
||||
|
||||
return price;
|
||||
}
|
||||
|
||||
private double GetSolutionsPrice(EntityPrototype prototype)
|
||||
private double GetSolutionsPrice(EntityPrototype? prototype)
|
||||
{
|
||||
var price = 0.0;
|
||||
|
||||
if (prototype.Components.TryGetValue(Factory.GetComponentName<SolutionContainerManagerComponent>(), out var solManager))
|
||||
if (prototype == null)
|
||||
return price;
|
||||
|
||||
foreach (var (_, solution) in _solutionContainerSystem.EnumerateSolutions(prototype))
|
||||
{
|
||||
var solComp = (SolutionContainerManagerComponent) solManager.Component;
|
||||
price += GetSolutionPrice(solComp);
|
||||
foreach (var (reagent, quantity) in solution.Contents)
|
||||
{
|
||||
if (!_prototypeManager.TryIndex<ReagentPrototype>(reagent.Prototype, out var reagentProto))
|
||||
continue;
|
||||
|
||||
// TODO check ReagentData for price information?
|
||||
price += (float) quantity * reagentProto.PricePerUnit;
|
||||
}
|
||||
}
|
||||
|
||||
return price;
|
||||
|
||||
@@ -1,45 +0,0 @@
|
||||
using Content.Shared.Chemistry.Components;
|
||||
using Content.Shared.Chemistry.Components.SolutionManager;
|
||||
using Content.Shared.Chemistry.EntitySystems;
|
||||
using Content.Shared.FixedPoint;
|
||||
using Robust.Shared.Containers;
|
||||
using Robust.Shared.Map;
|
||||
using Robust.Shared.Utility;
|
||||
using System.Numerics;
|
||||
|
||||
namespace Content.Server.Chemistry.Containers.EntitySystems;
|
||||
|
||||
[Obsolete("This is being depreciated. Use SharedSolutionContainerSystem instead!")]
|
||||
public sealed partial class SolutionContainerSystem : SharedSolutionContainerSystem
|
||||
{
|
||||
[Obsolete("This is being depreciated. Use the ensure methods in SharedSolutionContainerSystem instead!")]
|
||||
public Solution EnsureSolution(Entity<MetaDataComponent?> entity, string name)
|
||||
=> EnsureSolution(entity, name, out _);
|
||||
|
||||
[Obsolete("This is being depreciated. Use the ensure methods in SharedSolutionContainerSystem instead!")]
|
||||
public Solution EnsureSolution(Entity<MetaDataComponent?> entity, string name, out bool existed)
|
||||
=> EnsureSolution(entity, name, FixedPoint2.Zero, out existed);
|
||||
|
||||
[Obsolete("This is being depreciated. Use the ensure methods in SharedSolutionContainerSystem instead!")]
|
||||
public Solution EnsureSolution(Entity<MetaDataComponent?> entity, string name, FixedPoint2 maxVol, out bool existed)
|
||||
=> EnsureSolution(entity, name, maxVol, null, out existed);
|
||||
|
||||
[Obsolete("This is being depreciated. Use the ensure methods in SharedSolutionContainerSystem instead!")]
|
||||
public Solution EnsureSolution(Entity<MetaDataComponent?> entity, string name, FixedPoint2 maxVol, Solution? prototype, out bool existed)
|
||||
{
|
||||
EnsureSolution(entity, name, maxVol, prototype, out existed, out var solution);
|
||||
return solution!;//solution is only ever null on the client, so we can suppress this
|
||||
}
|
||||
|
||||
[Obsolete("This is being depreciated. Use the ensure methods in SharedSolutionContainerSystem instead!")]
|
||||
public Entity<SolutionComponent> EnsureSolutionEntity(
|
||||
Entity<SolutionContainerManagerComponent?> entity,
|
||||
string name,
|
||||
FixedPoint2 maxVol,
|
||||
Solution? prototype,
|
||||
out bool existed)
|
||||
{
|
||||
EnsureSolutionEntity(entity, name, out existed, out var solEnt, maxVol, prototype);
|
||||
return solEnt!.Value;//solEnt is only ever null on the client, so we can suppress this
|
||||
}
|
||||
}
|
||||
@@ -47,7 +47,7 @@ namespace Content.Server.Chemistry.EntitySystems
|
||||
base.Initialize();
|
||||
|
||||
SubscribeLocalEvent<ChemMasterComponent, ComponentStartup>(SubscribeUpdateUiState);
|
||||
SubscribeLocalEvent<ChemMasterComponent, SolutionContainerChangedEvent>(SubscribeUpdateUiState);
|
||||
SubscribeLocalEvent<ChemMasterComponent, SolutionChangedEvent>(SubscribeUpdateUiState);
|
||||
SubscribeLocalEvent<ChemMasterComponent, EntInsertedIntoContainerMessage>(SubscribeUpdateUiState);
|
||||
SubscribeLocalEvent<ChemMasterComponent, EntRemovedFromContainerMessage>(SubscribeUpdateUiState);
|
||||
// Subscribing to DragDropTargetEvent is a quick fix to ensure the UI updates when fluids are dragged and dropped into the ChemMaster, since Shared.Fluids.EntitySystems.SolutionDumpingSystem.cs bypasses UpdateChemicals().
|
||||
@@ -235,22 +235,17 @@ namespace Content.Server.Chemistry.EntitySystems
|
||||
_storageSystem.Insert(container, item, out _, user: user, storage);
|
||||
_labelSystem.Label(item, message.Label);
|
||||
|
||||
_solutionContainerSystem.EnsureSolutionEntity(item,
|
||||
SharedChemMaster.PillSolutionName,
|
||||
out var itemSolution,
|
||||
message.Dosage);
|
||||
if (!itemSolution.HasValue)
|
||||
return;
|
||||
_solutionContainerSystem.EnsureSolution(item, SharedChemMaster.PillSolutionName, out var itemSolution);
|
||||
itemSolution.Comp.Solution.MaxVolume = message.Dosage;
|
||||
|
||||
_solutionContainerSystem.TryAddSolution(itemSolution.Value, withdrawal.SplitSolution(message.Dosage));
|
||||
_solutionContainerSystem.TryAddSolution(itemSolution, withdrawal.SplitSolution(message.Dosage));
|
||||
|
||||
var pill = EnsureComp<PillComponent>(item);
|
||||
pill.PillType = chemMaster.Comp.PillType;
|
||||
Dirty(item, pill);
|
||||
|
||||
// Log pill creation by a user
|
||||
_adminLogger.Add(LogType.Action, LogImpact.Low,
|
||||
$"{ToPrettyString(user):user} printed {ToPrettyString(item):pill} {SharedSolutionContainerSystem.ToPrettyString(itemSolution.Value.Comp.Solution)}");
|
||||
_adminLogger.Add(LogType.Action, LogImpact.Low, $"{ToPrettyString(user):user} printed {ToPrettyString(item):pill} {SharedSolutionContainerSystem.ToPrettyString(itemSolution.Comp.Solution)}");
|
||||
}
|
||||
|
||||
UpdateUiState(chemMaster);
|
||||
|
||||
@@ -12,7 +12,7 @@ namespace Content.Server.Chemistry.EntitySystems.DeleteOnSolutionEmptySystem
|
||||
{
|
||||
base.Initialize();
|
||||
SubscribeLocalEvent<DeleteOnSolutionEmptyComponent, ComponentStartup>(OnStartup);
|
||||
SubscribeLocalEvent<DeleteOnSolutionEmptyComponent, SolutionContainerChangedEvent>(OnSolutionChange);
|
||||
SubscribeLocalEvent<DeleteOnSolutionEmptyComponent, SolutionChangedEvent>(OnSolutionChange);
|
||||
}
|
||||
|
||||
public void OnStartup(Entity<DeleteOnSolutionEmptyComponent> entity, ref ComponentStartup args)
|
||||
@@ -20,19 +20,23 @@ namespace Content.Server.Chemistry.EntitySystems.DeleteOnSolutionEmptySystem
|
||||
CheckSolutions(entity);
|
||||
}
|
||||
|
||||
public void OnSolutionChange(Entity<DeleteOnSolutionEmptyComponent> entity, ref SolutionContainerChangedEvent args)
|
||||
public void OnSolutionChange(Entity<DeleteOnSolutionEmptyComponent> entity, ref SolutionChangedEvent args)
|
||||
{
|
||||
CheckSolutions(entity);
|
||||
var solution = args.Solution.Comp.Solution;
|
||||
if (args.Solution.Comp.Id != entity.Comp.Solution)
|
||||
return;
|
||||
|
||||
if (solution.Volume <= 0)
|
||||
QueueDel(entity);
|
||||
}
|
||||
|
||||
public void CheckSolutions(Entity<DeleteOnSolutionEmptyComponent> entity)
|
||||
{
|
||||
if (!TryComp(entity, out SolutionContainerManagerComponent? solutions))
|
||||
if (!_solutionContainerSystem.TryGetSolution(entity.Owner, entity.Comp.Solution, out _, out var solution))
|
||||
return;
|
||||
|
||||
if (_solutionContainerSystem.TryGetSolution((entity.Owner, solutions), entity.Comp.Solution, out _, out var solution))
|
||||
if (solution.Volume <= 0)
|
||||
QueueDel(entity);
|
||||
if (solution.Volume <= 0)
|
||||
QueueDel(entity);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,6 +1,5 @@
|
||||
using System.Linq;
|
||||
using Content.Server.Chemistry.Components;
|
||||
using Content.Server.Chemistry.Containers.EntitySystems;
|
||||
using Content.Shared.Chemistry;
|
||||
using Content.Shared.Chemistry.EntitySystems;
|
||||
using Content.Shared.Containers.ItemSlots;
|
||||
@@ -40,7 +39,7 @@ namespace Content.Server.Chemistry.EntitySystems
|
||||
base.Initialize();
|
||||
|
||||
SubscribeLocalEvent<ReagentDispenserComponent, ComponentStartup>(SubscribeUpdateUiState);
|
||||
SubscribeLocalEvent<ReagentDispenserComponent, SolutionContainerChangedEvent>(SubscribeUpdateUiState);
|
||||
SubscribeLocalEvent<ReagentDispenserComponent, SolutionChangedEvent>(SubscribeUpdateUiState);
|
||||
SubscribeLocalEvent<ReagentDispenserComponent, EntInsertedIntoContainerMessage>(SubscribeUpdateUiState, after: [typeof(SharedStorageSystem)]);
|
||||
SubscribeLocalEvent<ReagentDispenserComponent, EntRemovedFromContainerMessage>(SubscribeUpdateUiState, after: [typeof(SharedStorageSystem)]);
|
||||
SubscribeLocalEvent<ReagentDispenserComponent, BoundUIOpenedEvent>(SubscribeUpdateUiState);
|
||||
|
||||
@@ -0,0 +1,5 @@
|
||||
using Content.Shared.Chemistry.EntitySystems;
|
||||
|
||||
namespace Content.Server.Chemistry.EntitySystems;
|
||||
|
||||
public sealed class SolutionContainerSystem : SharedSolutionContainerSystem;
|
||||
@@ -81,13 +81,10 @@ public sealed class SolutionHeaterSystem : EntitySystem
|
||||
var query = EntityQueryEnumerator<ActiveSolutionHeaterComponent, SolutionHeaterComponent, ItemPlacerComponent>();
|
||||
while (query.MoveNext(out _, out _, out var heater, out var placer))
|
||||
{
|
||||
var energy = heater.HeatPerSecond * frameTime;
|
||||
foreach (var heatingEntity in placer.PlacedEntities)
|
||||
{
|
||||
if (!TryComp<SolutionContainerManagerComponent>(heatingEntity, out var container))
|
||||
continue;
|
||||
|
||||
var energy = heater.HeatPerSecond * frameTime;
|
||||
foreach (var (_, soln) in _solutionContainer.EnumerateSolutions((heatingEntity, container)))
|
||||
foreach (var (_, soln) in _solutionContainer.EnumerateSolutions(heatingEntity))
|
||||
{
|
||||
_solutionContainer.AddThermalEnergy(soln, energy);
|
||||
}
|
||||
|
||||
@@ -3,6 +3,7 @@ using Content.Shared.Chemistry.EntitySystems;
|
||||
using Content.Shared.Chemistry.Reagent;
|
||||
using Content.Shared.Random;
|
||||
using Content.Shared.Random.Helpers;
|
||||
using Content.Shared.Storage.Components;
|
||||
using Robust.Shared.Prototypes;
|
||||
using Robust.Shared.Random;
|
||||
|
||||
@@ -37,8 +38,10 @@ public sealed class SolutionRandomFillSystem : EntitySystem
|
||||
return;
|
||||
}
|
||||
|
||||
_solutionsSystem.EnsureSolutionEntity(entity.Owner, entity.Comp.Solution, out var target , pick.quantity);
|
||||
if(target.HasValue)
|
||||
_solutionsSystem.TryAddReagent(target.Value, reagent, quantity);
|
||||
_solutionsSystem.EnsureSolution(entity.Owner, entity.Comp.Solution, out var target);
|
||||
if (target.Comp.Solution.AvailableVolume < quantity)
|
||||
Log.Error($"A random solution fill {entity.Comp.WeightedRandomId} tried to put {pick.quantity} of {pick.reagent} into {ToPrettyString(target)} but there was not enough space!");
|
||||
|
||||
_solutionsSystem.TryAddReagent(target, reagent, quantity);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -18,7 +18,7 @@ public sealed class TransformableContainerSystem : EntitySystem
|
||||
base.Initialize();
|
||||
|
||||
SubscribeLocalEvent<TransformableContainerComponent, MapInitEvent>(OnMapInit);
|
||||
SubscribeLocalEvent<TransformableContainerComponent, SolutionContainerChangedEvent>(OnSolutionChange);
|
||||
SubscribeLocalEvent<TransformableContainerComponent, SolutionChangedEvent>(OnSolutionChange);
|
||||
SubscribeLocalEvent<TransformableContainerComponent, RefreshNameModifiersEvent>(OnRefreshNameModifiers);
|
||||
}
|
||||
|
||||
@@ -31,7 +31,7 @@ public sealed class TransformableContainerSystem : EntitySystem
|
||||
}
|
||||
}
|
||||
|
||||
private void OnSolutionChange(Entity<TransformableContainerComponent> entity, ref SolutionContainerChangedEvent args)
|
||||
private void OnSolutionChange(Entity<TransformableContainerComponent> entity, ref SolutionChangedEvent args)
|
||||
{
|
||||
if (!_solutionsSystem.TryGetFitsInDispenser(entity.Owner, out _, out var solution))
|
||||
return;
|
||||
|
||||
@@ -39,9 +39,7 @@ namespace Content.Server.Chemistry.EntitySystems
|
||||
|
||||
private void HandleCollide(Entity<VaporComponent> entity, ref StartCollideEvent args)
|
||||
{
|
||||
if (!TryComp(entity.Owner, out SolutionContainerManagerComponent? contents)) return;
|
||||
|
||||
foreach (var (_, soln) in _solutionContainerSystem.EnumerateSolutions((entity.Owner, contents)))
|
||||
foreach (var (_, soln) in _solutionContainerSystem.EnumerateSolutions(entity.Owner))
|
||||
{
|
||||
var solution = soln.Comp.Solution;
|
||||
_reactive.DoEntityReaction(args.OtherEntity, solution, ReactionMethod.Touch);
|
||||
@@ -102,7 +100,8 @@ namespace Content.Server.Chemistry.EntitySystems
|
||||
base.Update(frameTime);
|
||||
|
||||
// Enumerate over all VaporComponents
|
||||
var query = EntityQueryEnumerator<VaporComponent, SolutionContainerManagerComponent, TransformComponent>();
|
||||
// TODO: Vapor should just use SolutionComponent and not be capable of having multiple solutions.
|
||||
var query = EntityQueryEnumerator<VaporComponent, SolutionManagerComponent, TransformComponent>();
|
||||
while (query.MoveNext(out var uid, out var vaporComp, out var container, out var xform))
|
||||
{
|
||||
// Return early if we're not active
|
||||
|
||||
@@ -30,7 +30,8 @@ public sealed partial class SpillBehavior : IThresholdBehavior
|
||||
var coordinates = system.EntityManager.GetComponent<TransformComponent>(owner).Coordinates;
|
||||
|
||||
// Spill the solution that was drained/split
|
||||
if (solutionContainer.TryGetSolution(owner, Solution, out _, out var solution))
|
||||
// TODO: ??? Top 10 reasons for solution entity prototypes right here bruh.
|
||||
if (Solution != null && solutionContainer.TryGetSolution(owner, Solution, out _, out var solution))
|
||||
puddleSystem.TrySplashSpillAt(owner, coordinates, solution, out _, false, cause);
|
||||
else
|
||||
puddleSystem.TrySplashSpillAt(owner, coordinates, out _, out _, false, cause);
|
||||
|
||||
@@ -294,20 +294,13 @@ public sealed partial class PuddleSystem : SharedPuddleSystem
|
||||
Solution addedSolution,
|
||||
bool sound = true,
|
||||
bool checkForOverflow = true,
|
||||
PuddleComponent? puddleComponent = null,
|
||||
SolutionContainerManagerComponent? sol = null)
|
||||
PuddleComponent? puddleComponent = null)
|
||||
{
|
||||
if (!Resolve(puddleUid, ref puddleComponent, ref sol))
|
||||
if (!Resolve(puddleUid, ref puddleComponent))
|
||||
return false;
|
||||
|
||||
_solutionContainerSystem.EnsureAllSolutions((puddleUid, sol));
|
||||
|
||||
if (addedSolution.Volume == 0 ||
|
||||
!_solutionContainerSystem.ResolveSolution(puddleUid, puddleComponent.SolutionName,
|
||||
ref puddleComponent.Solution))
|
||||
{
|
||||
if (addedSolution.Volume == 0 || !_solutionContainerSystem.ResolveSolution(puddleUid, puddleComponent.SolutionName, ref puddleComponent.Solution))
|
||||
return false;
|
||||
}
|
||||
|
||||
_solutionContainerSystem.AddSolution(puddleComponent.Solution.Value, addedSolution);
|
||||
|
||||
|
||||
@@ -204,7 +204,7 @@ public sealed class SmokeSystem : EntitySystem
|
||||
|
||||
private void OnReactionAttempt(Entity<SmokeComponent> entity, ref SolutionRelayEvent<ReactionAttemptEvent> args)
|
||||
{
|
||||
if (args.Name == SmokeComponent.SolutionName)
|
||||
if (args.Solution.Comp.Id == SmokeComponent.SolutionName)
|
||||
OnReactionAttempt(entity, ref args.Event);
|
||||
}
|
||||
|
||||
|
||||
@@ -13,9 +13,7 @@ using Content.Shared.Tag;
|
||||
using Robust.Shared.Audio.Systems;
|
||||
using Robust.Server.GameObjects;
|
||||
using Robust.Shared.Audio;
|
||||
using Robust.Shared.Player;
|
||||
using Robust.Shared.Timing;
|
||||
using Content.Server.Chemistry.Containers.EntitySystems;
|
||||
using Robust.Shared.Prototypes;
|
||||
// todo: remove this stinky LINQy
|
||||
|
||||
|
||||
@@ -47,11 +47,11 @@ namespace Content.Server.Forensics
|
||||
SubscribeLocalEvent<CleansForensicsComponent, AfterInteractEvent>(OnAfterInteract, after: new[] { typeof(AbsorbentSystem) });
|
||||
SubscribeLocalEvent<ForensicsComponent, CleanForensicsDoAfterEvent>(OnCleanForensicsDoAfter);
|
||||
SubscribeLocalEvent<DnaComponent, TransferDnaEvent>(OnTransferDnaEvent);
|
||||
SubscribeLocalEvent<DnaSubstanceTraceComponent, SolutionContainerChangedEvent>(OnSolutionChanged);
|
||||
SubscribeLocalEvent<DnaSubstanceTraceComponent, SolutionChangedEvent>(OnSolutionChanged);
|
||||
SubscribeLocalEvent<CleansForensicsComponent, GetVerbsEvent<UtilityVerb>>(OnUtilityVerb);
|
||||
}
|
||||
|
||||
private void OnSolutionChanged(Entity<DnaSubstanceTraceComponent> ent, ref SolutionContainerChangedEvent ev)
|
||||
private void OnSolutionChanged(Entity<DnaSubstanceTraceComponent> ent, ref SolutionChangedEvent ev)
|
||||
{
|
||||
var soln = GetSolutionsDNA(ev.Solution);
|
||||
if (soln.Count > 0)
|
||||
@@ -152,12 +152,9 @@ namespace Content.Server.Forensics
|
||||
public List<string> GetSolutionsDNA(EntityUid uid)
|
||||
{
|
||||
List<string> list = new();
|
||||
if (TryComp<SolutionContainerManagerComponent>(uid, out var comp))
|
||||
foreach (var (_, soln) in _solutionContainerSystem.EnumerateSolutions(uid))
|
||||
{
|
||||
foreach (var (_, soln) in _solutionContainerSystem.EnumerateSolutions((uid, comp)))
|
||||
{
|
||||
list.AddRange(GetSolutionsDNA(soln.Comp.Solution));
|
||||
}
|
||||
list.AddRange(GetSolutionsDNA(soln.Comp.Solution));
|
||||
}
|
||||
return list;
|
||||
}
|
||||
|
||||
@@ -78,7 +78,7 @@ namespace Content.Server.Kitchen.EntitySystems
|
||||
|
||||
SubscribeLocalEvent<MicrowaveComponent, ComponentInit>(OnInit);
|
||||
SubscribeLocalEvent<MicrowaveComponent, MapInitEvent>(OnMapInit);
|
||||
SubscribeLocalEvent<MicrowaveComponent, SolutionContainerChangedEvent>(OnSolutionChange);
|
||||
SubscribeLocalEvent<MicrowaveComponent, SolutionChangedEvent>(OnSolutionChange);
|
||||
SubscribeLocalEvent<MicrowaveComponent, EntInsertedIntoContainerMessage>(OnContentUpdate);
|
||||
SubscribeLocalEvent<MicrowaveComponent, EntRemovedFromContainerMessage>(OnContentUpdate);
|
||||
SubscribeLocalEvent<MicrowaveComponent, InteractUsingEvent>(OnInteractUsing, after: new[] { typeof(AnchorableSystem) });
|
||||
@@ -181,9 +181,7 @@ namespace Content.Server.Kitchen.EntitySystems
|
||||
if (TryComp<TemperatureComponent>(entity, out var tempComp))
|
||||
_temperature.ChangeHeat(entity, heatToAdd * component.ObjectHeatMultiplier, false, tempComp);
|
||||
|
||||
if (!TryComp<SolutionContainerManagerComponent>(entity, out var solutions))
|
||||
continue;
|
||||
foreach (var (_, soln) in _solutionContainer.EnumerateSolutions((entity, solutions)))
|
||||
foreach (var (_, soln) in _solutionContainer.EnumerateSolutions(entity))
|
||||
{
|
||||
var solution = soln.Comp.Solution;
|
||||
if (solution.Temperature > component.TemperatureUpperThreshold)
|
||||
@@ -319,7 +317,7 @@ namespace Content.Server.Kitchen.EntitySystems
|
||||
args.Handled = true;
|
||||
}
|
||||
|
||||
private void OnSolutionChange(Entity<MicrowaveComponent> ent, ref SolutionContainerChangedEvent args)
|
||||
private void OnSolutionChange(Entity<MicrowaveComponent> ent, ref SolutionChangedEvent args)
|
||||
{
|
||||
UpdateUserInterfaceState(ent, ent.Comp);
|
||||
}
|
||||
|
||||
@@ -69,20 +69,19 @@ public sealed class MaterialReclaimerSystem : SharedMaterialReclaimerSystem
|
||||
|
||||
private void OnInteractUsing(Entity<MaterialReclaimerComponent> entity, ref InteractUsingEvent args)
|
||||
{
|
||||
if (args.Handled)
|
||||
if (args.Handled || entity.Comp.SolutionContainerId == null)
|
||||
return;
|
||||
|
||||
// if we're trying to get a solution out of the reclaimer, don't destroy it
|
||||
if (_solutionContainer.TryGetSolution(entity.Owner, entity.Comp.SolutionContainerId, out _, out var outputSolution) && outputSolution.Contents.Any())
|
||||
{
|
||||
if (TryComp<SolutionContainerManagerComponent>(args.Used, out var managerComponent) &&
|
||||
_solutionContainer.EnumerateSolutions((args.Used, managerComponent)).Any(s => s.Solution.Comp.Solution.AvailableVolume > 0))
|
||||
if (_solutionContainer.EnumerateSolutions(args.Used).Any(s => s.Solution.Comp.Solution.AvailableVolume > 0))
|
||||
{
|
||||
if (_openable.IsClosed(args.Used))
|
||||
return;
|
||||
|
||||
if (TryComp<SolutionTransferComponent>(args.Used, out var transfer) &&
|
||||
transfer.CanReceive)
|
||||
transfer.CanSend)
|
||||
return;
|
||||
}
|
||||
}
|
||||
@@ -250,7 +249,7 @@ public sealed class MaterialReclaimerSystem : SharedMaterialReclaimerSystem
|
||||
TransformComponent? xform = null,
|
||||
PhysicalCompositionComponent? composition = null)
|
||||
{
|
||||
if (!Resolve(reclaimer, ref reclaimerComponent, ref xform))
|
||||
if (!Resolve(reclaimer, ref reclaimerComponent, ref xform) || reclaimerComponent.SolutionContainerId == null)
|
||||
return;
|
||||
|
||||
efficiency *= reclaimerComponent.Efficiency;
|
||||
|
||||
@@ -34,7 +34,7 @@ public sealed class SliceableFoodSystem : EntitySystem
|
||||
|
||||
SubscribeLocalEvent<SliceableFoodComponent, InteractUsingEvent>(OnInteractUsing);
|
||||
SubscribeLocalEvent<SliceableFoodComponent, SliceFoodDoAfterEvent>(OnSlicedoAfter);
|
||||
SubscribeLocalEvent<SliceableFoodComponent, ComponentStartup>(OnComponentStartup);
|
||||
SubscribeLocalEvent<SliceableFoodComponent, MapInitEvent>(OnMapInit);
|
||||
}
|
||||
|
||||
private void OnInteractUsing(Entity<SliceableFoodComponent> entity, ref InteractUsingEvent args)
|
||||
@@ -156,10 +156,9 @@ public sealed class SliceableFoodSystem : EntitySystem
|
||||
_solutionContainer.TryAddSolution(itsSoln.Value, lostSolutionPart);
|
||||
}
|
||||
|
||||
private void OnComponentStartup(Entity<SliceableFoodComponent> entity, ref ComponentStartup args)
|
||||
private void OnMapInit(Entity<SliceableFoodComponent> entity, ref MapInitEvent args)
|
||||
{
|
||||
// TODO: When Food Component is fully kill delete this awful method
|
||||
// This exists just to make tests fail I guess, awesome!
|
||||
// This exists just to make tests fail!
|
||||
// If you're here because your test just failed, make sure that:
|
||||
// Your food has the edible component
|
||||
// The solution listed in the edible component exists
|
||||
|
||||
@@ -81,11 +81,10 @@ namespace Content.Server.Nutrition.EntitySystems
|
||||
|
||||
EntityUid contents = entity.Comp.BowlSlot.Item.Value;
|
||||
|
||||
if (!TryComp<SolutionContainerManagerComponent>(contents, out var reagents) ||
|
||||
!_solutionContainerSystem.TryGetSolution(smokable.Owner, smokable.Comp.Solution, out var pipeSolution, out _))
|
||||
if (!_solutionContainerSystem.TryGetSolution(smokable.Owner, smokable.Comp.Solution, out var pipeSolution, out _))
|
||||
return false;
|
||||
|
||||
foreach (var (_, soln) in _solutionContainerSystem.EnumerateSolutions((contents, reagents)))
|
||||
foreach (var (_, soln) in _solutionContainerSystem.EnumerateSolutions(contents))
|
||||
{
|
||||
var reagentSolution = soln.Comp.Solution;
|
||||
_solutionContainerSystem.TryAddSolution(pipeSolution.Value, reagentSolution);
|
||||
|
||||
@@ -18,7 +18,7 @@ namespace Content.Server.Nutrition.EntitySystems
|
||||
{
|
||||
base.Initialize();
|
||||
SubscribeLocalEvent<TrashOnSolutionEmptyComponent, MapInitEvent>(OnMapInit);
|
||||
SubscribeLocalEvent<TrashOnSolutionEmptyComponent, SolutionContainerChangedEvent>(OnSolutionChange);
|
||||
SubscribeLocalEvent<TrashOnSolutionEmptyComponent, SolutionChangedEvent>(OnSolutionChange);
|
||||
}
|
||||
|
||||
public void OnMapInit(Entity<TrashOnSolutionEmptyComponent> entity, ref MapInitEvent args)
|
||||
@@ -26,16 +26,13 @@ namespace Content.Server.Nutrition.EntitySystems
|
||||
CheckSolutions(entity);
|
||||
}
|
||||
|
||||
public void OnSolutionChange(Entity<TrashOnSolutionEmptyComponent> entity, ref SolutionContainerChangedEvent args)
|
||||
public void OnSolutionChange(Entity<TrashOnSolutionEmptyComponent> entity, ref SolutionChangedEvent args)
|
||||
{
|
||||
CheckSolutions(entity);
|
||||
}
|
||||
|
||||
public void CheckSolutions(Entity<TrashOnSolutionEmptyComponent> entity)
|
||||
{
|
||||
if (!HasComp<SolutionContainerManagerComponent>(entity))
|
||||
return;
|
||||
|
||||
if (_solutionContainerSystem.TryGetSolution(entity.Owner, entity.Comp.Solution, out _, out var solution))
|
||||
UpdateTags(entity, solution);
|
||||
}
|
||||
|
||||
@@ -25,7 +25,7 @@ public sealed class RiggableSystem : EntitySystem
|
||||
base.Initialize();
|
||||
SubscribeLocalEvent<RiggableComponent, RejuvenateEvent>(OnRejuvenate);
|
||||
SubscribeLocalEvent<RiggableComponent, BeingMicrowavedEvent>(OnMicrowaved);
|
||||
SubscribeLocalEvent<RiggableComponent, SolutionContainerChangedEvent>(OnSolutionChanged);
|
||||
SubscribeLocalEvent<RiggableComponent, SolutionChangedEvent>(OnSolutionChanged);
|
||||
SubscribeLocalEvent<RiggableComponent, ChargeChangedEvent>(OnChargeChanged);
|
||||
}
|
||||
|
||||
@@ -47,13 +47,14 @@ public sealed class RiggableSystem : EntitySystem
|
||||
}
|
||||
}
|
||||
|
||||
private void OnSolutionChanged(Entity<RiggableComponent> entity, ref SolutionContainerChangedEvent args)
|
||||
private void OnSolutionChanged(Entity<RiggableComponent> entity, ref SolutionChangedEvent args)
|
||||
{
|
||||
if (args.SolutionId != entity.Comp.Solution)
|
||||
if (args.Solution.Comp.Id != entity.Comp.Solution)
|
||||
return;
|
||||
|
||||
var wasRigged = entity.Comp.IsRigged;
|
||||
var quantity = args.Solution.GetReagentQuantity(entity.Comp.RequiredQuantity.Reagent);
|
||||
var solution = args.Solution.Comp.Solution;
|
||||
var quantity = solution.GetReagentQuantity(entity.Comp.RequiredQuantity.Reagent);
|
||||
entity.Comp.IsRigged = quantity >= entity.Comp.RequiredQuantity.Quantity;
|
||||
|
||||
if (entity.Comp.IsRigged && !wasRigged)
|
||||
|
||||
@@ -27,7 +27,7 @@ public sealed partial class ChemicalFuelGeneratorAdapterComponent : Component
|
||||
public string SolutionName = "tank";
|
||||
|
||||
/// <summary>
|
||||
/// The solution on the <see cref="SolutionContainerManagerComponent"/> to use.
|
||||
/// The solution to use.
|
||||
/// </summary>
|
||||
[ViewVariables]
|
||||
public Entity<SolutionComponent>? Solution = null;
|
||||
|
||||
@@ -26,7 +26,7 @@ namespace Content.Server.Stunnable.Systems
|
||||
base.Initialize();
|
||||
|
||||
SubscribeLocalEvent<StunbatonComponent, ExaminedEvent>(OnExamined);
|
||||
SubscribeLocalEvent<StunbatonComponent, SolutionContainerChangedEvent>(OnSolutionChange);
|
||||
SubscribeLocalEvent<StunbatonComponent, SolutionChangedEvent>(OnSolutionChange);
|
||||
SubscribeLocalEvent<StunbatonComponent, StaminaDamageOnHitAttemptEvent>(OnStaminaHitAttempt);
|
||||
SubscribeLocalEvent<StunbatonComponent, ChargeChangedEvent>(OnChargeChanged);
|
||||
}
|
||||
@@ -75,7 +75,7 @@ namespace Content.Server.Stunnable.Systems
|
||||
}
|
||||
|
||||
// https://github.com/space-wizards/space-station-14/pull/17288#discussion_r1241213341
|
||||
private void OnSolutionChange(Entity<StunbatonComponent> entity, ref SolutionContainerChangedEvent args)
|
||||
private void OnSolutionChange(Entity<StunbatonComponent> entity, ref SolutionChangedEvent args)
|
||||
{
|
||||
// Explode if baton is activated and rigged.
|
||||
if (!TryComp<RiggableComponent>(entity, out var riggable) ||
|
||||
|
||||
@@ -18,7 +18,7 @@ public sealed partial class GunSystem
|
||||
base.InitializeSolution();
|
||||
|
||||
SubscribeLocalEvent<SolutionAmmoProviderComponent, MapInitEvent>(OnSolutionMapInit);
|
||||
SubscribeLocalEvent<SolutionAmmoProviderComponent, SolutionContainerChangedEvent>(OnSolutionChanged);
|
||||
SubscribeLocalEvent<SolutionAmmoProviderComponent, SolutionChangedEvent>(OnSolutionChanged);
|
||||
}
|
||||
|
||||
private void OnSolutionMapInit(Entity<SolutionAmmoProviderComponent> entity, ref MapInitEvent args)
|
||||
@@ -26,10 +26,10 @@ public sealed partial class GunSystem
|
||||
UpdateSolutionShots(entity);
|
||||
}
|
||||
|
||||
private void OnSolutionChanged(Entity<SolutionAmmoProviderComponent> entity, ref SolutionContainerChangedEvent args)
|
||||
private void OnSolutionChanged(Entity<SolutionAmmoProviderComponent> entity, ref SolutionChangedEvent args)
|
||||
{
|
||||
if (args.Solution.Name == entity.Comp.SolutionId)
|
||||
UpdateSolutionShots(entity, args.Solution);
|
||||
if (args.Solution.Comp.Id == entity.Comp.SolutionId)
|
||||
UpdateSolutionShots(entity, args.Solution.Comp.Solution);
|
||||
}
|
||||
|
||||
protected override void UpdateSolutionShots(Entity<SolutionAmmoProviderComponent> ent, Solution? solution = null)
|
||||
|
||||
@@ -11,7 +11,7 @@ namespace Content.Shared.Administration
|
||||
public readonly List<(string, NetEntity)>? Solutions;
|
||||
public readonly GameTick Tick;
|
||||
|
||||
public EditSolutionsEuiState(NetEntity target, List<(string, NetEntity)>? solutions, GameTick tick)
|
||||
public EditSolutionsEuiState(NetEntity target, List<(string, NetEntity)> solutions, GameTick tick)
|
||||
{
|
||||
Target = target;
|
||||
Solutions = solutions;
|
||||
|
||||
@@ -148,10 +148,10 @@ public sealed partial class BloodstreamComponent : Component
|
||||
/// Defines which reagents are considered as 'blood' and how much of it is normal.
|
||||
/// </summary>
|
||||
/// <remarks>
|
||||
/// Slime-people might use slime as their blood or something like that.
|
||||
/// Default is human blood at 5 liters (600u) of blood.
|
||||
/// </remarks>
|
||||
[DataField, AutoNetworkedField]
|
||||
public Solution BloodReferenceSolution = new([new("Blood", 300)]);
|
||||
public Solution BloodReferenceSolution = new([new("Blood", 600)]);
|
||||
|
||||
/// <summary>
|
||||
/// Caches the blood data of an entity.
|
||||
|
||||
@@ -20,7 +20,7 @@ public sealed class LungSystem : EntitySystem
|
||||
public override void Initialize()
|
||||
{
|
||||
base.Initialize();
|
||||
SubscribeLocalEvent<LungComponent, ComponentInit>(OnComponentInit);
|
||||
SubscribeLocalEvent<LungComponent, MapInitEvent>(OnMapInit);
|
||||
SubscribeLocalEvent<BreathToolComponent, GotEquippedEvent>(OnGotEquipped);
|
||||
SubscribeLocalEvent<BreathToolComponent, GotUnequippedEvent>(OnGotUnequipped);
|
||||
}
|
||||
@@ -44,13 +44,12 @@ public sealed class LungSystem : EntitySystem
|
||||
}
|
||||
}
|
||||
|
||||
private void OnComponentInit(Entity<LungComponent> entity, ref ComponentInit args)
|
||||
private void OnMapInit(Entity<LungComponent> entity, ref MapInitEvent args)
|
||||
{
|
||||
if (_solutionContainerSystem.EnsureSolution(entity.Owner, entity.Comp.SolutionName, out var solution))
|
||||
{
|
||||
solution.MaxVolume = 100.0f;
|
||||
solution.CanReact = false; // No dexalin lungs
|
||||
}
|
||||
_solutionContainerSystem.EnsureSolution(entity.Owner, entity.Comp.SolutionName, out var solution);
|
||||
|
||||
solution.Comp.Solution.MaxVolume = 100.0f;
|
||||
solution.Comp.Solution.CanReact = false; // No dexalin lungs
|
||||
}
|
||||
|
||||
// TODO: JUST METABOLIZE GASES DIRECTLY DON'T CONVERT TO REAGENTS!!! (Needs Metabolism refactor :B)
|
||||
|
||||
@@ -110,10 +110,25 @@ public abstract class SharedBloodstreamSystem : EntitySystem
|
||||
}
|
||||
}
|
||||
|
||||
private void OnMapInit(Entity<BloodstreamComponent> ent, ref MapInitEvent args)
|
||||
private void OnMapInit(Entity<BloodstreamComponent> entity, ref MapInitEvent args)
|
||||
{
|
||||
ent.Comp.NextUpdate = _timing.CurTime + ent.Comp.AdjustedUpdateInterval;
|
||||
DirtyField(ent, ent.Comp, nameof(BloodstreamComponent.NextUpdate));
|
||||
entity.Comp.NextUpdate = _timing.CurTime + entity.Comp.AdjustedUpdateInterval;
|
||||
DirtyField(entity, entity.Comp, nameof(BloodstreamComponent.NextUpdate));
|
||||
|
||||
SolutionContainer.EnsureSolution(entity.Owner, entity.Comp.BloodSolutionName, out var bloodSolution);
|
||||
SolutionContainer.EnsureSolution(entity.Owner, entity.Comp.BloodTemporarySolutionName, out var tempSolution);
|
||||
SolutionContainer.EnsureSolution(entity.Owner, entity.Comp.MetabolitesSolutionName, out var metabolitesSolution);
|
||||
|
||||
bloodSolution.Comp.Solution.MaxVolume = entity.Comp.BloodReferenceSolution.Volume * entity.Comp.MaxVolumeModifier;
|
||||
metabolitesSolution.Comp.Solution.MaxVolume = bloodSolution.Comp.Solution.MaxVolume;
|
||||
tempSolution.Comp.Solution.MaxVolume = entity.Comp.BleedPuddleThreshold * 4; // give some leeway, for chemstream as well
|
||||
entity.Comp.BloodReferenceSolution.SetReagentData(GetEntityBloodData((entity, entity.Comp)));
|
||||
|
||||
// Fill blood solution with BLOOD
|
||||
// The DNA string might not be initialized yet, but the reagent data gets updated in the GenerateDnaEvent subscription
|
||||
var solution = entity.Comp.BloodReferenceSolution.Clone();
|
||||
solution.ScaleTo(entity.Comp.BloodReferenceSolution.Volume - bloodSolution.Comp.Solution.Volume);
|
||||
bloodSolution.Comp.Solution.AddSolution(solution, PrototypeManager);
|
||||
}
|
||||
|
||||
// prevent the infamous UdderSystem debug assert, see https://github.com/space-wizards/space-station-14/pull/35314
|
||||
@@ -157,8 +172,8 @@ public abstract class SharedBloodstreamSystem : EntitySystem
|
||||
|
||||
private void OnReactionAttempt(Entity<BloodstreamComponent> ent, ref SolutionRelayEvent<ReactionAttemptEvent> args)
|
||||
{
|
||||
if (args.Name != ent.Comp.BloodSolutionName
|
||||
&& args.Name != ent.Comp.BloodTemporarySolutionName)
|
||||
if (args.Solution.Comp.Id != ent.Comp.BloodSolutionName
|
||||
&& args.Solution.Comp.Id != ent.Comp.BloodTemporarySolutionName)
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
@@ -2,7 +2,6 @@ using Content.Shared.Body.Components;
|
||||
using Content.Shared.Chemistry.Components;
|
||||
using Content.Shared.Chemistry.Components.SolutionManager;
|
||||
using Content.Shared.Chemistry.EntitySystems;
|
||||
using Robust.Shared.Utility;
|
||||
|
||||
namespace Content.Shared.Body.Systems;
|
||||
|
||||
@@ -12,33 +11,22 @@ public sealed class StomachSystem : EntitySystem
|
||||
|
||||
public const string DefaultSolutionName = "stomach";
|
||||
|
||||
public bool CanTransferSolution(
|
||||
EntityUid uid,
|
||||
Solution solution,
|
||||
StomachComponent? stomach = null,
|
||||
SolutionContainerManagerComponent? solutions = null)
|
||||
public bool CanTransferSolution(Entity<StomachComponent?, SolutionManagerComponent?> entity, Solution solution)
|
||||
{
|
||||
return Resolve(uid, ref stomach, ref solutions, logMissing: false)
|
||||
&& _solutionContainerSystem.ResolveSolution((uid, solutions), DefaultSolutionName, ref stomach.Solution, out var stomachSolution)
|
||||
return Resolve(entity, ref entity.Comp1, logMissing: false)
|
||||
&& _solutionContainerSystem.ResolveSolution((entity, entity.Comp2), DefaultSolutionName, ref entity.Comp1.Solution, out var stomachSolution)
|
||||
// TODO: For now no partial transfers. Potentially change by design
|
||||
&& stomachSolution.CanAddSolution(solution);
|
||||
}
|
||||
|
||||
public bool TryTransferSolution(
|
||||
EntityUid uid,
|
||||
Solution solution,
|
||||
StomachComponent? stomach = null,
|
||||
SolutionContainerManagerComponent? solutions = null)
|
||||
public bool TryTransferSolution(Entity<StomachComponent?, SolutionManagerComponent?> entity, Solution solution)
|
||||
{
|
||||
if (!Resolve(uid, ref stomach, ref solutions, logMissing: false)
|
||||
|| !_solutionContainerSystem.ResolveSolution((uid, solutions), DefaultSolutionName, ref stomach.Solution)
|
||||
|| !CanTransferSolution(uid, solution, stomach, solutions))
|
||||
{
|
||||
if (!Resolve(entity, ref entity.Comp1, logMissing: false)
|
||||
|| !_solutionContainerSystem.ResolveSolution((entity, entity.Comp2), DefaultSolutionName, ref entity.Comp1.Solution)
|
||||
|| !CanTransferSolution(entity, solution))
|
||||
return false;
|
||||
}
|
||||
|
||||
_solutionContainerSystem.TryAddSolution(stomach.Solution.Value, solution);
|
||||
|
||||
_solutionContainerSystem.TryAddSolution(entity.Comp1.Solution.Value, solution);
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,6 +1,5 @@
|
||||
using System.Collections;
|
||||
using System.Linq;
|
||||
using Content.Shared.Chemistry.Components.SolutionManager;
|
||||
using Content.Shared.Chemistry.Reagent;
|
||||
using Content.Shared.FixedPoint;
|
||||
using JetBrains.Annotations;
|
||||
@@ -59,12 +58,6 @@ namespace Content.Shared.Chemistry.Components
|
||||
[DataField]
|
||||
public float Temperature { get; set; } = 293.15f;
|
||||
|
||||
/// <summary>
|
||||
/// The name of this solution, if it is contained in some <see cref="SolutionContainerManagerComponent"/>
|
||||
/// </summary>
|
||||
[DataField]
|
||||
public string? Name;
|
||||
|
||||
/// <summary>
|
||||
/// Checks if a solution can fit into the container.
|
||||
/// </summary>
|
||||
@@ -205,7 +198,7 @@ namespace Content.Shared.Chemistry.Components
|
||||
DebugTools.Assert(!Contents.Any(x => x.Quantity <= FixedPoint2.Zero));
|
||||
|
||||
// No duplicate reagents iDs
|
||||
DebugTools.Assert(Contents.Select(x => x.Reagent).ToHashSet().Count == Contents.Count);
|
||||
DebugTools.Assert(Contents.Select(x => x.Reagent).ToHashSet().Count == Contents.Count, $"Solution: {this}, contained duplcate contents {Contents}");
|
||||
|
||||
// If it isn't flagged as dirty, check heat capacity is correct.
|
||||
if (!_heatCapacityDirty)
|
||||
|
||||
@@ -1,6 +1,7 @@
|
||||
using Content.Shared.Chemistry.Components.SolutionManager;
|
||||
using Content.Shared.Chemistry.EntitySystems;
|
||||
using Content.Shared.Materials;
|
||||
using Content.Shared.Temperature.Components;
|
||||
using Robust.Shared.GameStates;
|
||||
using Robust.Shared.Serialization;
|
||||
|
||||
@@ -8,23 +9,33 @@ namespace Content.Shared.Chemistry.Components;
|
||||
|
||||
/// <summary>
|
||||
/// <para>Holds the composition of an entity made from reagents and its reagent temperature.</para>
|
||||
/// <para>If the entity is used to represent a collection of reagents inside of a container such as a beaker, syringe, bloodstream, food, or similar the entity is tracked by a <see cref="SolutionContainerManagerComponent"/> on the container and has a <see cref="ContainedSolutionComponent"/> tracking which container it's in.</para>
|
||||
/// <para>If the entity is used to represent a collection of reagents inside of a container such as a beaker, syringe, bloodstream, food, or similar the entity is tracked by a <see cref="SolutionManagerComponent"/> on the container and has a <see cref="ContainedSolutionComponent"/> tracking which container it's in.</para>
|
||||
/// </summary>
|
||||
/// <remarks>
|
||||
/// <para>Once reagents and materials have been merged this component should be depricated in favor of using a combination of <see cref="PhysicalCompositionComponent"/> and <see cref="Content.Server.Temperature.Components.TemperatureComponent"/>. May require minor reworks to both.</para>
|
||||
/// <para>Once reagents and materials have been merged this component should be depricated in favor of using a combination of <see cref="PhysicalCompositionComponent"/> and <see cref="TemperatureComponent"/>. May require minor reworks to both.</para>
|
||||
/// </remarks>
|
||||
[RegisterComponent, NetworkedComponent]
|
||||
[Access(typeof(SharedSolutionContainerSystem))]
|
||||
public sealed partial class SolutionComponent : Component
|
||||
{
|
||||
public const string DefaultSolutionId = "solution";
|
||||
|
||||
/// <summary>
|
||||
/// The name of this solution. This value should *never* change once the solution is initialized.
|
||||
/// </summary>
|
||||
[DataField]
|
||||
[Access(typeof(SharedSolutionContainerSystem))]
|
||||
public string Id = DefaultSolutionId;
|
||||
|
||||
/// <summary>
|
||||
/// <para>The reagents the entity is composed of and their temperature.</para>
|
||||
/// </summary>
|
||||
[DataField]
|
||||
[DataField, AlwaysPushInheritance]
|
||||
public Solution Solution = new();
|
||||
}
|
||||
|
||||
|
||||
/// <remarks>
|
||||
/// We manually network the component state as it raises one less event and therefore is better performance wise.
|
||||
/// </remarks>
|
||||
[Serializable, NetSerializable]
|
||||
public sealed class SolutionComponentState(Solution solution) : ComponentState
|
||||
{
|
||||
|
||||
@@ -12,6 +12,7 @@ namespace Content.Shared.Chemistry.Components.SolutionManager;
|
||||
/// The <see cref="Solution.MaxVolume"/> field should then be extracted out into this component.
|
||||
/// Solution entities would just become an apporpriately composed entity hanging out in the container.
|
||||
/// Will probably require entities in components being given a relation to associate themselves with their container.
|
||||
/// TODO: Proper relations system so this can be initialized with a SolutionComponent attached to it :)
|
||||
/// </remarks>
|
||||
[RegisterComponent, NetworkedComponent, AutoGenerateComponentState]
|
||||
[Access(typeof(SharedSolutionContainerSystem))]
|
||||
@@ -20,12 +21,6 @@ public sealed partial class ContainedSolutionComponent : Component
|
||||
/// <summary>
|
||||
/// The entity that the solution is contained in.
|
||||
/// </summary>
|
||||
[DataField(required: true), AutoNetworkedField]
|
||||
[DataField, AutoNetworkedField]
|
||||
public EntityUid Container;
|
||||
|
||||
/// <summary>
|
||||
/// The name/key of the container the solution is located in.
|
||||
/// </summary>
|
||||
[DataField(required: true), AutoNetworkedField]
|
||||
public string ContainerName = default!;
|
||||
}
|
||||
|
||||
+11
-8
@@ -4,11 +4,14 @@ using Robust.Shared.GameStates;
|
||||
|
||||
namespace Content.Shared.Chemistry.Components.SolutionManager;
|
||||
|
||||
/// <summary>
|
||||
/// <para>A map of the solution entities contained within this entity.</para>
|
||||
/// <para>Every solution entity this maps should have a <see cref="SolutionComponent"/> to track its state and a <see cref="ContainedSolutionComponent"/> to track its container.</para>
|
||||
/// </summary>
|
||||
[RegisterComponent, NetworkedComponent, AutoGenerateComponentState]
|
||||
/// <remarks>
|
||||
/// Exists for simple backwards compatibility.
|
||||
/// On <see cref="ComponentInit"/> this component will transfer all its data where it can to a <see cref="SolutionManagerComponent"/>
|
||||
/// Then it will delete itself.
|
||||
/// This component will be deleted in the indeterminate future.
|
||||
/// </remarks>
|
||||
[Obsolete]
|
||||
[RegisterComponent]
|
||||
[Access(typeof(SharedSolutionContainerSystem))]
|
||||
public sealed partial class SolutionContainerManagerComponent : Component
|
||||
{
|
||||
@@ -22,7 +25,7 @@ public sealed partial class SolutionContainerManagerComponent : Component
|
||||
/// The names of each solution container attached to this entity.
|
||||
/// Actually accessing them must be done via <see cref="ContainerManagerComponent"/>.
|
||||
/// </summary>
|
||||
[DataField, AutoNetworkedField]
|
||||
[DataField]
|
||||
public HashSet<string> Containers = new(DefaultCapacity);
|
||||
|
||||
/// <summary>
|
||||
@@ -31,6 +34,6 @@ public sealed partial class SolutionContainerManagerComponent : Component
|
||||
/// <remarks>
|
||||
/// Should be null after mapinit.
|
||||
/// </remarks>
|
||||
[DataField, AutoNetworkedField]
|
||||
public Dictionary<string, Solution>? Solutions = null;
|
||||
[DataField]
|
||||
public Dictionary<string, Solution>? Solutions;
|
||||
}
|
||||
|
||||
@@ -0,0 +1,37 @@
|
||||
using Content.Shared.Chemistry.EntitySystems;
|
||||
using Robust.Shared.GameStates;
|
||||
using Robust.Shared.Prototypes;
|
||||
|
||||
namespace Content.Shared.Chemistry.Components.SolutionManager;
|
||||
|
||||
/// <summary>
|
||||
/// <para>Allows for an entity to have and manage multiple solutions.</para>
|
||||
/// <para>Spawns additional solutions from their prototypes, and stores them in a container.</para>
|
||||
/// <para>Also used in the case another component spawns a solution for this entity.</para>
|
||||
/// <para>Every solution entity this maps should have a <see cref="SolutionComponent"/> to track its state and a <see cref="ContainedSolutionComponent"/> to track its container.</para>
|
||||
/// </summary>
|
||||
[RegisterComponent, NetworkedComponent, AutoGenerateComponentState]
|
||||
[Access(typeof(SharedSolutionContainerSystem))]
|
||||
public sealed partial class SolutionManagerComponent : Component
|
||||
{
|
||||
public static readonly string DefaultContainerId = "solutions";
|
||||
|
||||
/// <summary>
|
||||
/// The names of the container for solutions attached to this entity.
|
||||
/// </summary>
|
||||
[DataField, AutoNetworkedField]
|
||||
public string Container = DefaultContainerId;
|
||||
|
||||
/// <summary>
|
||||
/// A cache of solutions currently attached to this entity.
|
||||
/// </summary>
|
||||
[ViewVariables]
|
||||
[Access(typeof(SharedSolutionContainerSystem), Other = AccessPermissions.None)]
|
||||
public Dictionary<string, Entity<SolutionComponent>> Solutions = new ();
|
||||
|
||||
/// <summary>
|
||||
/// A list of solution entities to spawn when this component starts up.
|
||||
/// </summary>
|
||||
[DataField("solutions", readOnly: true)]
|
||||
public List<EntProtoId> SolutionEnts = new ();
|
||||
}
|
||||
@@ -25,7 +25,7 @@ public sealed partial class SolutionTransferComponent : Component
|
||||
/// The maximum amount of solution that can be transferred at once from this solution.
|
||||
/// </summary>
|
||||
[DataField("maxTransferAmount"), AutoNetworkedField]
|
||||
public FixedPoint2 MaximumTransferAmount = FixedPoint2.New(100);
|
||||
public FixedPoint2 MaximumTransferAmount = FixedPoint2.New(120);
|
||||
|
||||
/// <summary>
|
||||
/// Can this entity take reagent from reagent tanks?
|
||||
|
||||
@@ -17,7 +17,7 @@ public sealed class ReactiveContainerSystem : EntitySystem
|
||||
base.Initialize();
|
||||
|
||||
SubscribeLocalEvent<ReactiveContainerComponent, EntInsertedIntoContainerMessage>(OnInserted);
|
||||
SubscribeLocalEvent<ReactiveContainerComponent, SolutionContainerChangedEvent>(OnSolutionChange);
|
||||
SubscribeLocalEvent<ReactiveContainerComponent, SolutionChangedEvent>(OnSolutionChange);
|
||||
}
|
||||
|
||||
private void OnInserted(EntityUid uid, ReactiveContainerComponent comp, EntInsertedIntoContainerMessage args)
|
||||
@@ -34,7 +34,7 @@ public sealed class ReactiveContainerSystem : EntitySystem
|
||||
_reactiveSystem.DoEntityReaction(args.Entity, solution, ReactionMethod.Touch);
|
||||
}
|
||||
|
||||
private void OnSolutionChange(EntityUid uid, ReactiveContainerComponent comp, SolutionContainerChangedEvent args)
|
||||
private void OnSolutionChange(EntityUid uid, ReactiveContainerComponent comp, SolutionChangedEvent args)
|
||||
{
|
||||
// The changes are already networked as part of the same game state.
|
||||
if (_timing.ApplyingState)
|
||||
@@ -42,10 +42,10 @@ public sealed class ReactiveContainerSystem : EntitySystem
|
||||
|
||||
if (!_solutionContainerSystem.TryGetSolution(uid, comp.Solution, out _, out var solution))
|
||||
return;
|
||||
|
||||
if (solution.Volume == 0)
|
||||
return;
|
||||
if (!TryComp<ContainerManagerComponent>(uid, out var manager))
|
||||
return;
|
||||
|
||||
if (!_containerSystem.TryGetContainer(uid, comp.Container, out var container))
|
||||
return;
|
||||
|
||||
|
||||
@@ -23,17 +23,17 @@ public sealed class RehydratableSystem : EntitySystem
|
||||
{
|
||||
base.Initialize();
|
||||
|
||||
SubscribeLocalEvent<RehydratableComponent, SolutionContainerChangedEvent>(OnSolutionChange);
|
||||
SubscribeLocalEvent<RehydratableComponent, SolutionChangedEvent>(OnSolutionChange);
|
||||
}
|
||||
|
||||
private void OnSolutionChange(Entity<RehydratableComponent> ent, ref SolutionContainerChangedEvent args)
|
||||
private void OnSolutionChange(Entity<RehydratableComponent> ent, ref SolutionChangedEvent args)
|
||||
{
|
||||
// The changes are already networked as part of the same game state.
|
||||
if (_timing.ApplyingState)
|
||||
return;
|
||||
|
||||
var quantity = _solutions.GetTotalPrototypeQuantity(ent, ent.Comp.CatalystPrototype);
|
||||
_adminLogger.Add(LogType.Action, LogImpact.Medium, $"{ToPrettyString(ent.Owner)} was hydrated, now contains a solution of: {SharedSolutionContainerSystem.ToPrettyString(args.Solution)}.");
|
||||
var quantity = _solutions.GetTotalPrototypeQuantity(ent.Owner, ent.Comp.CatalystPrototype);
|
||||
_adminLogger.Add(LogType.Action, LogImpact.Medium, $"{ToPrettyString(ent.Owner)} was hydrated, now contains a solution of: {SharedSolutionContainerSystem.ToPrettyString(args.Solution.Comp.Solution)}.");
|
||||
if (quantity != FixedPoint2.Zero && quantity >= ent.Comp.CatalystMinimum)
|
||||
{
|
||||
Expand(ent);
|
||||
|
||||
+17
-13
@@ -11,7 +11,7 @@ public abstract partial class SharedSolutionContainerSystem
|
||||
{
|
||||
#region Solution Accessors
|
||||
|
||||
public bool TryGetRefillableSolution(Entity<RefillableSolutionComponent?, SolutionContainerManagerComponent?> entity, [NotNullWhen(true)] out Entity<SolutionComponent>? soln, [NotNullWhen(true)] out Solution? solution)
|
||||
public bool TryGetRefillableSolution(Entity<RefillableSolutionComponent?, SolutionManagerComponent?> entity, [NotNullWhen(true)] out Entity<SolutionComponent>? soln, [NotNullWhen(true)] out Solution? solution)
|
||||
{
|
||||
if (!Resolve(entity, ref entity.Comp1, logMissing: false))
|
||||
{
|
||||
@@ -22,7 +22,7 @@ public abstract partial class SharedSolutionContainerSystem
|
||||
return TryGetSolution((entity.Owner, entity.Comp2), entity.Comp1.Solution, out soln, out solution);
|
||||
}
|
||||
|
||||
public bool TryGetDrainableSolution(Entity<DrainableSolutionComponent?, SolutionContainerManagerComponent?> entity, [NotNullWhen(true)] out Entity<SolutionComponent>? soln, [NotNullWhen(true)] out Solution? solution)
|
||||
public bool TryGetDrainableSolution(Entity<DrainableSolutionComponent?, SolutionManagerComponent?> entity, [NotNullWhen(true)] out Entity<SolutionComponent>? soln, [NotNullWhen(true)] out Solution? solution)
|
||||
{
|
||||
if (!Resolve(entity, ref entity.Comp1, logMissing: false))
|
||||
{
|
||||
@@ -33,9 +33,9 @@ public abstract partial class SharedSolutionContainerSystem
|
||||
return TryGetSolution((entity.Owner, entity.Comp2), entity.Comp1.Solution, out soln, out solution);
|
||||
}
|
||||
|
||||
public bool TryGetExtractableSolution(Entity<ExtractableComponent?, SolutionContainerManagerComponent?> entity, [NotNullWhen(true)] out Entity<SolutionComponent>? soln, [NotNullWhen(true)] out Solution? solution)
|
||||
public bool TryGetExtractableSolution(Entity<ExtractableComponent?, SolutionManagerComponent?> entity, [NotNullWhen(true)] out Entity<SolutionComponent>? soln, [NotNullWhen(true)] out Solution? solution)
|
||||
{
|
||||
if (!Resolve(entity, ref entity.Comp1, logMissing: false))
|
||||
if (!Resolve(entity, ref entity.Comp1, logMissing: false) || entity.Comp1.GrindableSolutionName == null)
|
||||
{
|
||||
(soln, solution) = (default!, null);
|
||||
return false;
|
||||
@@ -44,7 +44,7 @@ public abstract partial class SharedSolutionContainerSystem
|
||||
return TryGetSolution((entity.Owner, entity.Comp2), entity.Comp1.GrindableSolutionName, out soln, out solution);
|
||||
}
|
||||
|
||||
public bool TryGetDumpableSolution(Entity<DumpableSolutionComponent?, SolutionContainerManagerComponent?> entity, [NotNullWhen(true)] out Entity<SolutionComponent>? soln, [NotNullWhen(true)] out Solution? solution)
|
||||
public bool TryGetDumpableSolution(Entity<DumpableSolutionComponent?, SolutionManagerComponent?> entity, [NotNullWhen(true)] out Entity<SolutionComponent>? soln, [NotNullWhen(true)] out Solution? solution)
|
||||
{
|
||||
if (!Resolve(entity, ref entity.Comp1, logMissing: false))
|
||||
{
|
||||
@@ -55,7 +55,7 @@ public abstract partial class SharedSolutionContainerSystem
|
||||
return TryGetSolution((entity.Owner, entity.Comp2), entity.Comp1.Solution, out soln, out solution);
|
||||
}
|
||||
|
||||
public bool TryGetDrawableSolution(Entity<DrawableSolutionComponent?, SolutionContainerManagerComponent?> entity, [NotNullWhen(true)] out Entity<SolutionComponent>? soln, [NotNullWhen(true)] out Solution? solution)
|
||||
public bool TryGetDrawableSolution(Entity<DrawableSolutionComponent?, SolutionManagerComponent?> entity, [NotNullWhen(true)] out Entity<SolutionComponent>? soln, [NotNullWhen(true)] out Solution? solution)
|
||||
{
|
||||
if (!Resolve(entity, ref entity.Comp1, logMissing: false))
|
||||
{
|
||||
@@ -66,7 +66,7 @@ public abstract partial class SharedSolutionContainerSystem
|
||||
return TryGetSolution((entity.Owner, entity.Comp2), entity.Comp1.Solution, out soln, out solution);
|
||||
}
|
||||
|
||||
public bool TryGetInjectableSolution(Entity<InjectableSolutionComponent?, SolutionContainerManagerComponent?> entity, [NotNullWhen(true)] out Entity<SolutionComponent>? soln, [NotNullWhen(true)] out Solution? solution)
|
||||
public bool TryGetInjectableSolution(Entity<InjectableSolutionComponent?, SolutionManagerComponent?> entity, [NotNullWhen(true)] out Entity<SolutionComponent>? soln, [NotNullWhen(true)] out Solution? solution)
|
||||
{
|
||||
if (!Resolve(entity, ref entity.Comp1, logMissing: false))
|
||||
{
|
||||
@@ -77,7 +77,7 @@ public abstract partial class SharedSolutionContainerSystem
|
||||
return TryGetSolution((entity.Owner, entity.Comp2), entity.Comp1.Solution, out soln, out solution);
|
||||
}
|
||||
|
||||
public bool TryGetFitsInDispenser(Entity<FitsInDispenserComponent?, SolutionContainerManagerComponent?> entity, [NotNullWhen(true)] out Entity<SolutionComponent>? soln, [NotNullWhen(true)] out Solution? solution)
|
||||
public bool TryGetFitsInDispenser(Entity<FitsInDispenserComponent?, SolutionManagerComponent?> entity, [NotNullWhen(true)] out Entity<SolutionComponent>? soln, [NotNullWhen(true)] out Solution? solution)
|
||||
{
|
||||
if (!Resolve(entity, ref entity.Comp1, logMissing: false))
|
||||
{
|
||||
@@ -88,7 +88,7 @@ public abstract partial class SharedSolutionContainerSystem
|
||||
return TryGetSolution((entity.Owner, entity.Comp2), entity.Comp1.Solution, out soln, out solution);
|
||||
}
|
||||
|
||||
public bool TryGetMixableSolution(Entity<MixableSolutionComponent?, SolutionContainerManagerComponent?> entity, [NotNullWhen(true)] out Entity<SolutionComponent>? soln, [NotNullWhen(true)] out Solution? solution)
|
||||
public bool TryGetMixableSolution(Entity<MixableSolutionComponent?, SolutionManagerComponent?> entity, [NotNullWhen(true)] out Entity<SolutionComponent>? soln, [NotNullWhen(true)] out Solution? solution)
|
||||
{
|
||||
if (!Resolve(entity, ref entity.Comp1, logMissing: false))
|
||||
{
|
||||
@@ -148,13 +148,17 @@ public abstract partial class SharedSolutionContainerSystem
|
||||
|
||||
#region Static Methods
|
||||
|
||||
public static string ToPrettyString(SolutionComponent solution)
|
||||
{
|
||||
var sb = new StringBuilder($"{solution.Id}:");
|
||||
sb.Append(ToPrettyString(solution.Solution));
|
||||
return sb.ToString();
|
||||
}
|
||||
|
||||
public static string ToPrettyString(Solution solution)
|
||||
{
|
||||
var sb = new StringBuilder();
|
||||
if (solution.Name == null)
|
||||
sb.Append("[");
|
||||
else
|
||||
sb.Append($"{solution.Name}:[");
|
||||
sb.Append("[");
|
||||
var first = true;
|
||||
foreach (var (id, quantity) in solution.Contents)
|
||||
{
|
||||
|
||||
@@ -0,0 +1,79 @@
|
||||
using Content.Shared.Chemistry.Components.SolutionManager;
|
||||
using Robust.Shared.Containers;
|
||||
|
||||
namespace Content.Shared.Chemistry.EntitySystems;
|
||||
|
||||
/// <summary>
|
||||
/// This exists so that entity prototypes and maps with <see cref="SolutionContainerManagerComponent"/> can load their solutions.
|
||||
/// This system is extremely simple and will not have compatibility for new features that come as a result of future refactors.
|
||||
/// This compatibility layer will degrade over time and eventually break as more solution logic relies on well-defined prototypes.
|
||||
/// This is only here to give you more time to port your solutions to the new system. It is going to be deleted eventually.
|
||||
/// You have been warned.
|
||||
/// </summary>
|
||||
public abstract partial class SharedSolutionContainerSystem
|
||||
{
|
||||
public void InitializeContainerManager()
|
||||
{
|
||||
SubscribeLocalEvent<SolutionContainerManagerComponent, MapInitEvent>(OnSolutionContainerInit);
|
||||
}
|
||||
|
||||
private void OnSolutionContainerInit(Entity<SolutionContainerManagerComponent> container, ref MapInitEvent args)
|
||||
{
|
||||
// Create the manager, this should also create a container, so we ensure it exists.
|
||||
EnsureComp<SolutionManagerComponent>(container, out var manager);
|
||||
var solutionContainer = ContainerSystem.EnsureContainer<Container>(container, manager.Container);
|
||||
|
||||
// First, if this entity was saved with an entity in a container, try to put it in the SolutionManager
|
||||
foreach (var name in container.Comp.Containers)
|
||||
{
|
||||
if (ContainerSystem.GetContainer(container, $"solution@{name}") is not ContainerSlot slot || slot.ContainedEntity is not { } solutionUid)
|
||||
continue;
|
||||
|
||||
if (!SolutionQuery.TryComp(solutionUid, out var solution))
|
||||
continue;
|
||||
|
||||
if (TryGetSolution(container.Owner, name, out var solutionEnt))
|
||||
{
|
||||
// Only a warning so tests don't fail. If you're using this to find maps/prototypes which need porting, change this to Log.Error so tests fail.
|
||||
Log.Warning($"Attempted to port a solution id: {name} entity: {ToPrettyString(solutionUid)} " +
|
||||
$"from a {nameof(SolutionContainerManagerComponent)} on {ToPrettyString(container)}, {MetaData(container).EntityPrototype}, " +
|
||||
$"but the entity already had a solution with that id.");
|
||||
solutionEnt.Value.Comp.Solution = solution.Solution;
|
||||
}
|
||||
else
|
||||
{
|
||||
ContainerSystem.Insert(solutionUid, solutionContainer, force: true);
|
||||
}
|
||||
|
||||
// We don't need it anymore
|
||||
ContainerSystem.ShutdownContainer(slot);
|
||||
}
|
||||
|
||||
if (container.Comp.Solutions == null)
|
||||
{
|
||||
RemCompDeferred<SolutionManagerComponent>(container);
|
||||
return;
|
||||
}
|
||||
|
||||
|
||||
// Next, if this entity was never initialized, create its solutions.
|
||||
foreach (var (name, solution) in container.Comp.Solutions)
|
||||
{
|
||||
// Solution already exists so we ignore it.
|
||||
if (EnsureSolution(container.Owner, name, out var solutionEnt))
|
||||
{
|
||||
// Only a warning so tests don't fail. If you're using this to find maps/prototypes which need porting, change this to Log.Error so tests fail.
|
||||
Log.Warning($"Attempted to port a solution id: {name} " +
|
||||
$"from a {nameof(SolutionContainerManagerComponent)} on {ToPrettyString(container)}, {MetaData(container).EntityPrototype}, " +
|
||||
$"but the entity already had a solution with that id.");
|
||||
}
|
||||
|
||||
// Clone the solution to the component.
|
||||
solutionEnt.Comp.Solution = solution;
|
||||
}
|
||||
|
||||
// Clear its data
|
||||
container.Comp.Solutions = null;
|
||||
RemCompDeferred<SolutionManagerComponent>(container);
|
||||
}
|
||||
}
|
||||
@@ -7,23 +7,6 @@ namespace Content.Shared.Chemistry.EntitySystems;
|
||||
|
||||
#region Events
|
||||
|
||||
/// <summary>
|
||||
/// Raised on the container of the solution entity when the contained solution is changed.
|
||||
/// If you want to subscribe with the solution entity itself
|
||||
/// then use <see cref="SolutionChangedEvent"/> instead.
|
||||
/// </summary>
|
||||
/// <remarks>
|
||||
/// This is always raised on the client when handling the component state so that we can update UIs accordingly.
|
||||
/// You might need an IGameTiming.ApplyingState guard to prevent mispredicts if the changes from your subscription are
|
||||
/// networked with the same game state.
|
||||
/// </remarks>
|
||||
[ByRefEvent]
|
||||
public record struct SolutionContainerChangedEvent(Solution Solution, string SolutionId)
|
||||
{
|
||||
public readonly Solution Solution = Solution;
|
||||
public readonly string SolutionId = SolutionId;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// An event raised when more reagents are added to a (managed) solution than it can hold.
|
||||
/// </summary>
|
||||
@@ -47,13 +30,11 @@ public record struct SolutionContainerOverflowEvent(EntityUid SolutionEnt, Solut
|
||||
/// </summary>
|
||||
/// <typeparam name="TEvent"></typeparam>
|
||||
/// <param name="Event">The event that is being relayed.</param>
|
||||
/// <param name="ContainerEnt">The container entity that the event is being relayed to.</param>
|
||||
/// <param name="Name">The name of the solution entity that the event is being relayed from.</param>
|
||||
/// <param name="Solution">The container entity that the event is being relayed to.</param>
|
||||
[ByRefEvent]
|
||||
public record struct SolutionRelayEvent<TEvent>(TEvent Event, EntityUid ContainerEnt, string Name)
|
||||
public record struct SolutionRelayEvent<TEvent>(TEvent Event, Entity<SolutionComponent> Solution)
|
||||
{
|
||||
public readonly EntityUid ContainerEnt = ContainerEnt;
|
||||
public readonly string Name = Name;
|
||||
public readonly Entity<SolutionComponent> Solution = Solution;
|
||||
public TEvent Event = Event;
|
||||
}
|
||||
|
||||
@@ -78,28 +59,12 @@ public abstract partial class SharedSolutionContainerSystem
|
||||
{
|
||||
protected void InitializeRelays()
|
||||
{
|
||||
SubscribeLocalEvent<ContainedSolutionComponent, SolutionChangedEvent>(OnSolutionChanged);
|
||||
SubscribeLocalEvent<ContainedSolutionComponent, SolutionOverflowEvent>(OnSolutionOverflow);
|
||||
SubscribeLocalEvent<ContainedSolutionComponent, ReactionAttemptEvent>(RelaySolutionRefEvent);
|
||||
}
|
||||
|
||||
#region Event Handlers
|
||||
|
||||
protected virtual void OnSolutionChanged(Entity<ContainedSolutionComponent> entity, ref SolutionChangedEvent args)
|
||||
{
|
||||
var (solutionId, solutionComp) = args.Solution;
|
||||
var solution = solutionComp.Solution;
|
||||
|
||||
var relayEvent = new SolutionContainerChangedEvent(solution, entity.Comp.ContainerName);
|
||||
RaiseLocalEvent(entity.Comp.Container, ref relayEvent);
|
||||
|
||||
// The appearance changes are already networked as part of the same game state.
|
||||
if (_timing.ApplyingState)
|
||||
return;
|
||||
|
||||
UpdateAppearance(entity.Comp.Container, (solutionId, solutionComp, entity.Comp));
|
||||
}
|
||||
|
||||
protected virtual void OnSolutionOverflow(Entity<ContainedSolutionComponent> entity, ref SolutionOverflowEvent args)
|
||||
{
|
||||
var solution = args.Solution.Comp.Solution;
|
||||
@@ -117,18 +82,20 @@ public abstract partial class SharedSolutionContainerSystem
|
||||
|
||||
private void RelaySolutionValEvent<TEvent>(EntityUid uid, ContainedSolutionComponent comp, TEvent @event)
|
||||
{
|
||||
var relayEvent = new SolutionRelayEvent<TEvent>(@event, uid, comp.ContainerName);
|
||||
var solution = Comp<SolutionComponent>(uid);
|
||||
var relayEvent = new SolutionRelayEvent<TEvent>(@event, (uid, solution));
|
||||
RaiseLocalEvent(comp.Container, ref relayEvent);
|
||||
}
|
||||
|
||||
private void RelaySolutionRefEvent<TEvent>(Entity<ContainedSolutionComponent> entity, ref TEvent @event)
|
||||
{
|
||||
var relayEvent = new SolutionRelayEvent<TEvent>(@event, entity.Owner, entity.Comp.ContainerName);
|
||||
var solution = Comp<SolutionComponent>(entity);
|
||||
var relayEvent = new SolutionRelayEvent<TEvent>(@event, (entity, solution));
|
||||
RaiseLocalEvent(entity.Comp.Container, ref relayEvent);
|
||||
@event = relayEvent.Event;
|
||||
}
|
||||
|
||||
private void RelaySolutionContainerEvent<TEvent>(EntityUid uid, SolutionContainerManagerComponent comp, TEvent @event)
|
||||
private void RelaySolutionContainerEvent<TEvent>(EntityUid uid, SolutionManagerComponent comp, TEvent @event)
|
||||
{
|
||||
foreach (var (name, soln) in EnumerateSolutions((uid, comp)))
|
||||
{
|
||||
@@ -137,7 +104,7 @@ public abstract partial class SharedSolutionContainerSystem
|
||||
}
|
||||
}
|
||||
|
||||
private void RelaySolutionContainerEvent<TEvent>(Entity<SolutionContainerManagerComponent> entity, ref TEvent @event)
|
||||
private void RelaySolutionContainerEvent<TEvent>(Entity<SolutionManagerComponent> entity, ref TEvent @event)
|
||||
{
|
||||
foreach (var (name, soln) in EnumerateSolutions((entity.Owner, entity.Comp)))
|
||||
{
|
||||
|
||||
@@ -1,6 +1,5 @@
|
||||
using System.Diagnostics.CodeAnalysis;
|
||||
using System.Linq;
|
||||
using System.Numerics;
|
||||
using System.Runtime.CompilerServices;
|
||||
using Content.Shared.Chemistry.Components;
|
||||
using Content.Shared.Chemistry.Components.SolutionManager;
|
||||
@@ -16,7 +15,6 @@ using Content.Shared.Verbs;
|
||||
using JetBrains.Annotations;
|
||||
using Robust.Shared.Containers;
|
||||
using Robust.Shared.GameStates;
|
||||
using Robust.Shared.Map;
|
||||
using Robust.Shared.Network;
|
||||
using Robust.Shared.Prototypes;
|
||||
using Robust.Shared.Timing;
|
||||
@@ -27,9 +25,8 @@ namespace Content.Shared.Chemistry.EntitySystems;
|
||||
|
||||
/// <summary>
|
||||
/// The event raised whenever a solution entity is modified.
|
||||
/// Raised on the solution entity itself.
|
||||
/// If you want to subscribe with the entity containing the solution entity
|
||||
/// then use <see cref="SolutionContainerChangedEvent"/> instead.
|
||||
/// This event is raised on the owner of the solution.
|
||||
/// If the changed solution is contained in a <see cref="SolutionManagerComponent"/>, it will be raised on the owner of that component.
|
||||
/// </summary>
|
||||
/// <remarks>
|
||||
/// Raised after chemcial reactions and <see cref="SolutionOverflowEvent"/> are handled.
|
||||
@@ -69,38 +66,42 @@ public partial record struct SolutionAccessAttemptEvent(string SolutionName)
|
||||
[UsedImplicitly]
|
||||
public abstract partial class SharedSolutionContainerSystem : EntitySystem
|
||||
{
|
||||
public static readonly EntProtoId DefaultSolution = "Solution";
|
||||
|
||||
[Dependency] protected readonly IGameTiming Timing = default!;
|
||||
[Dependency] protected readonly INetManager Net = default!;
|
||||
[Dependency] protected readonly IPrototypeManager PrototypeManager = default!;
|
||||
[Dependency] protected readonly ChemicalReactionSystem ChemicalReactionSystem = default!;
|
||||
[Dependency] protected readonly ExamineSystemShared ExamineSystem = default!;
|
||||
[Dependency] protected readonly OpenableSystem Openable = default!;
|
||||
[Dependency] protected readonly SharedAppearanceSystem AppearanceSystem = default!;
|
||||
[Dependency] protected readonly SharedHandsSystem Hands = default!;
|
||||
[Dependency] protected readonly SharedContainerSystem ContainerSystem = default!;
|
||||
[Dependency] protected readonly MetaDataSystem MetaDataSys = default!;
|
||||
[Dependency] protected readonly INetManager NetManager = default!;
|
||||
[Dependency] private readonly IGameTiming _timing = default!;
|
||||
[Dependency] protected readonly SharedHandsSystem Hands = default!;
|
||||
|
||||
[Dependency] protected readonly EntityQuery<ContainedSolutionComponent> ContainedQuery = default!;
|
||||
[Dependency] protected readonly EntityQuery<SolutionComponent> SolutionQuery = default!;
|
||||
[Dependency] protected readonly EntityQuery<SolutionManagerComponent> SolutionManagerQuery = default!;
|
||||
|
||||
public override void Initialize()
|
||||
{
|
||||
base.Initialize();
|
||||
|
||||
InitializeRelays();
|
||||
InitializeContainerManager();
|
||||
|
||||
SubscribeLocalEvent<SolutionComponent, ComponentGetState>(OnSolutionGetState);
|
||||
SubscribeLocalEvent<SolutionComponent, ComponentHandleState>(OnSolutionHandleState);
|
||||
SubscribeLocalEvent<SolutionComponent, ComponentInit>(OnComponentInit);
|
||||
SubscribeLocalEvent<SolutionComponent, ComponentStartup>(OnSolutionStartup);
|
||||
SubscribeLocalEvent<SolutionComponent, MapInitEvent>(OnSolutionInit);
|
||||
SubscribeLocalEvent<SolutionComponent, ComponentShutdown>(OnSolutionShutdown);
|
||||
SubscribeLocalEvent<SolutionContainerManagerComponent, ComponentInit>(OnContainerManagerInit);
|
||||
|
||||
SubscribeLocalEvent<ExaminableSolutionComponent, ExaminedEvent>(OnExamineSolution);
|
||||
SubscribeLocalEvent<ExaminableSolutionComponent, GetVerbsEvent<ExamineVerb>>(OnSolutionExaminableVerb);
|
||||
SubscribeLocalEvent<SolutionContainerManagerComponent, MapInitEvent>(OnMapInit);
|
||||
|
||||
if (NetManager.IsServer)
|
||||
{
|
||||
SubscribeLocalEvent<SolutionContainerManagerComponent, ComponentShutdown>(OnContainerManagerShutdown);
|
||||
SubscribeLocalEvent<ContainedSolutionComponent, ComponentShutdown>(OnContainedSolutionShutdown);
|
||||
}
|
||||
SubscribeLocalEvent<SolutionManagerComponent, MapInitEvent>(OnManagerInit);
|
||||
SubscribeLocalEvent<SolutionManagerComponent, ComponentShutdown>(OnManagerShutdown);
|
||||
SubscribeLocalEvent<SolutionManagerComponent, EntInsertedIntoContainerMessage>(OnSolutionAdded);
|
||||
SubscribeLocalEvent<SolutionManagerComponent, EntRemovedFromContainerMessage>(OnSolutionRemoved);
|
||||
}
|
||||
|
||||
private void OnSolutionGetState(Entity<SolutionComponent> ent, ref ComponentGetState args)
|
||||
@@ -118,32 +119,37 @@ public abstract partial class SharedSolutionContainerSystem : EntitySystem
|
||||
// Always raise the event on the client so that we can update UIs accordingly.
|
||||
var changedEv = new SolutionChangedEvent(ent);
|
||||
RaiseLocalEvent(ent, ref changedEv);
|
||||
|
||||
if (!ContainedQuery.TryComp(ent, out var contained) || !SolutionManagerQuery.TryComp(contained.Container, out var manager))
|
||||
return;
|
||||
|
||||
manager.Solutions[ent.Comp.Id] = ent;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Attempts to resolve a solution associated with an entity.
|
||||
/// </summary>
|
||||
/// <param name="container">The entity that holdes the container the solution entity is in.</param>
|
||||
/// <param name="entity">The entity that holdes the container the solution entity is in.</param>
|
||||
/// <param name="name">The name of the solution entities container.</param>
|
||||
/// <param name="entity">A reference to a solution entity to load the associated solution entity into. Will be unchanged if not null.</param>
|
||||
/// <param name="solutionEnt">A reference to a solution entity to load the associated solution entity into. Will be unchanged if not null.</param>
|
||||
/// <param name="solution">Returns the solution state of the solution entity.</param>
|
||||
/// <returns>Whether the solution was successfully resolved.</returns>
|
||||
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
||||
public bool ResolveSolution(Entity<SolutionContainerManagerComponent?> container, string? name, [NotNullWhen(true)] ref Entity<SolutionComponent>? entity, [NotNullWhen(true)] out Solution? solution)
|
||||
public bool ResolveSolution(Entity<SolutionManagerComponent?> entity, string name, [NotNullWhen(true)] ref Entity<SolutionComponent>? solutionEnt, [NotNullWhen(true)] out Solution? solution)
|
||||
{
|
||||
if (!ResolveSolution(container, name, ref entity))
|
||||
if (!ResolveSolution(entity, name, ref solutionEnt))
|
||||
{
|
||||
solution = null;
|
||||
return false;
|
||||
}
|
||||
|
||||
solution = entity.Value.Comp.Solution;
|
||||
solution = solutionEnt.Value.Comp.Solution;
|
||||
return true;
|
||||
}
|
||||
|
||||
/// <inheritdoc cref="ResolveSolution"/>
|
||||
/// <inheritdoc cref="ResolveSolution(Entity{SolutionManagerComponent?}, string, ref Entity{SolutionComponent}?, out Solution?)"/>
|
||||
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
||||
public bool ResolveSolution(Entity<SolutionContainerManagerComponent?> container, string? name, [NotNullWhen(true)] ref Entity<SolutionComponent>? entity)
|
||||
public bool ResolveSolution(Entity<SolutionManagerComponent?> container, string name, [NotNullWhen(true)] ref Entity<SolutionComponent>? entity)
|
||||
{
|
||||
if (entity is not null)
|
||||
{
|
||||
@@ -159,171 +165,217 @@ public abstract partial class SharedSolutionContainerSystem : EntitySystem
|
||||
/// Attempts to fetch a solution entity associated with an entity.
|
||||
/// </summary>
|
||||
/// <remarks>
|
||||
/// If the solution entity will be frequently accessed please use the equivalent <see cref="ResolveSolution"/> method and cache the result.
|
||||
/// If the solution entity will be frequently accessed please use the equivalent
|
||||
/// <see cref="ResolveSolution(Entity{SolutionManagerComponent?}, string, ref Entity{SolutionComponent}?, out Solution?)"/>
|
||||
/// method and cache the result.
|
||||
/// </remarks>
|
||||
/// <param name="container">The entity the solution entity should be associated with.</param>
|
||||
/// <param name="entity">The entity the solution entity should be associated with.</param>
|
||||
/// <param name="name">The name of the solution entity to fetch.</param>
|
||||
/// <param name="entity">Returns the solution entity that was fetched.</param>
|
||||
/// <param name="solutionEnt">Returns the solution entity that was fetched.</param>
|
||||
/// <param name="solution">Returns the solution state of the solution entity that was fetched.</param>
|
||||
/// /// <param name="errorOnMissing">Should we print an error if the solution specified by name is missing</param>
|
||||
/// <returns></returns>
|
||||
public bool TryGetSolution(
|
||||
Entity<SolutionContainerManagerComponent?> container,
|
||||
string? name,
|
||||
[NotNullWhen(true)] out Entity<SolutionComponent>? entity,
|
||||
Entity<SolutionManagerComponent?> entity,
|
||||
string name,
|
||||
[NotNullWhen(true)] out Entity<SolutionComponent>? solutionEnt,
|
||||
[NotNullWhen(true)] out Solution? solution,
|
||||
bool errorOnMissing = false)
|
||||
{
|
||||
if (!TryGetSolution(container, name, out entity, errorOnMissing: errorOnMissing))
|
||||
if (!TryGetSolution(entity, name, out solutionEnt, errorOnMissing: errorOnMissing))
|
||||
{
|
||||
solution = null;
|
||||
return false;
|
||||
}
|
||||
|
||||
solution = entity.Value.Comp.Solution;
|
||||
solution = solutionEnt.Value.Comp.Solution;
|
||||
return true;
|
||||
}
|
||||
|
||||
/// <inheritdoc cref="TryGetSolution"/>
|
||||
/// <inheritdoc cref="TryGetSolution(Entity{SolutionManagerComponent?},string,out Entity{SolutionComponent}?, out Solution?, bool)"/>
|
||||
public bool TryGetSolution(
|
||||
Entity<SolutionContainerManagerComponent?> container,
|
||||
string? name,
|
||||
[NotNullWhen(true)] out Entity<SolutionComponent>? entity,
|
||||
Entity<SolutionManagerComponent?> entity,
|
||||
string name,
|
||||
[NotNullWhen(true)] out Entity<SolutionComponent>? solutionEnt,
|
||||
bool errorOnMissing = false)
|
||||
{
|
||||
// use connected container instead of entity from arguments, if it exists.
|
||||
var ev = new GetConnectedContainerEvent();
|
||||
RaiseLocalEvent(container, ref ev);
|
||||
if (ev.ContainerEntity.HasValue)
|
||||
container = ev.ContainerEntity.Value;
|
||||
solutionEnt = null;
|
||||
|
||||
EntityUid uid;
|
||||
if (name is null)
|
||||
uid = container;
|
||||
else if (
|
||||
ContainerSystem.TryGetContainer(container, $"solution@{name}", out var solutionContainer) &&
|
||||
solutionContainer is ContainerSlot solutionSlot &&
|
||||
solutionSlot.ContainedEntity is { } containedSolution
|
||||
)
|
||||
var ev = new GetConnectedContainerEvent();
|
||||
RaiseLocalEvent(entity, ref ev);
|
||||
if (ev.ContainerEntity.HasValue)
|
||||
entity = ev.ContainerEntity.Value;
|
||||
|
||||
if (SolutionQuery.TryComp(entity, out var comp) && comp.Id == name)
|
||||
{
|
||||
solutionEnt = (entity.Owner, comp);
|
||||
return true;
|
||||
}
|
||||
|
||||
if (!SolutionManagerQuery.Resolve(entity, ref entity.Comp, errorOnMissing))
|
||||
return false;
|
||||
|
||||
if (entity.Comp.Solutions.TryGetValue(name, out var solution))
|
||||
{
|
||||
var attemptEv = new SolutionAccessAttemptEvent(name);
|
||||
RaiseLocalEvent(container, ref attemptEv);
|
||||
RaiseLocalEvent(entity, ref attemptEv);
|
||||
|
||||
if (attemptEv.Cancelled)
|
||||
{
|
||||
entity = null;
|
||||
return false;
|
||||
}
|
||||
|
||||
uid = containedSolution;
|
||||
}
|
||||
else
|
||||
{
|
||||
entity = null;
|
||||
if (!errorOnMissing)
|
||||
return false;
|
||||
Log.Error($"{ToPrettyString(container)} does not have a solution with ID: {name}");
|
||||
return false;
|
||||
solutionEnt = solution;
|
||||
return true;
|
||||
}
|
||||
|
||||
if (!TryComp(uid, out SolutionComponent? comp))
|
||||
{
|
||||
entity = null;
|
||||
if (!errorOnMissing)
|
||||
return false;
|
||||
Log.Error($"{ToPrettyString(container)} does not have a solution with ID: {name}");
|
||||
return false;
|
||||
}
|
||||
if (errorOnMissing)
|
||||
Log.Error($"{ToPrettyString(entity)} does not have a solution with ID: {name}");
|
||||
|
||||
entity = (uid, comp);
|
||||
return true;
|
||||
return false;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Version of TryGetSolution that doesn't take or return an entity.
|
||||
/// Used for prototypes and with old code parity.
|
||||
public bool TryGetSolution(SolutionContainerManagerComponent container,
|
||||
/// Used for prototypes.
|
||||
/// </summary>
|
||||
public bool TryGetSolution(EntProtoId entProtoId,
|
||||
string name,
|
||||
[NotNullWhen(true)] out Solution? solution,
|
||||
bool errorOnMissing = false)
|
||||
{
|
||||
solution = null;
|
||||
if (container.Solutions != null)
|
||||
return container.Solutions.TryGetValue(name, out solution);
|
||||
if (!errorOnMissing)
|
||||
|
||||
if (!PrototypeManager.Resolve(entProtoId, out var proto))
|
||||
return false;
|
||||
Log.Error($"{container} does not have a solution with ID: {name}");
|
||||
|
||||
return TryGetSolution(proto, name, out solution, errorOnMissing);
|
||||
}
|
||||
|
||||
public bool TryGetSolution(EntityPrototype entProto,
|
||||
string name,
|
||||
[NotNullWhen(true)] out Solution? solution,
|
||||
bool errorOnMissing = false)
|
||||
{
|
||||
solution = null;
|
||||
|
||||
if (!TryGetSolutionFill(entProto, out var solutions))
|
||||
return false;
|
||||
|
||||
foreach (var protoId in solutions)
|
||||
{
|
||||
if (!PrototypeManager.Resolve(protoId, out var proto))
|
||||
continue;
|
||||
|
||||
if (!proto.TryGetComponent<SolutionComponent>(out var sol, Factory))
|
||||
{
|
||||
Log.Error($"Entity prototype {proto}, tried to spawn in a solution container in prototype {entProto.ID}, but had no {nameof(SolutionComponent)}");
|
||||
continue;
|
||||
}
|
||||
|
||||
if (sol.Id != name)
|
||||
continue;
|
||||
|
||||
solution = sol.Solution;
|
||||
return true;
|
||||
}
|
||||
|
||||
if (errorOnMissing)
|
||||
Log.Error($"{entProto.ID} does not have a solution with ID: {name}");
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
public IEnumerable<(string? Name, Entity<SolutionComponent> Solution)> EnumerateSolutions(Entity<SolutionContainerManagerComponent?> container, bool includeSelf = true)
|
||||
public IEnumerable<(string? Name, Entity<SolutionComponent> Solution)> EnumerateSolutions(Entity<SolutionManagerComponent?> entity, bool includeSelf = true)
|
||||
{
|
||||
if (includeSelf && TryComp(container, out SolutionComponent? solutionComp))
|
||||
yield return (null, (container.Owner, solutionComp));
|
||||
if (includeSelf && SolutionQuery.TryComp(entity, out var solutionComp))
|
||||
yield return (solutionComp.Id, (entity.Owner, solutionComp));
|
||||
|
||||
if (!Resolve(container, ref container.Comp, logMissing: false))
|
||||
if (!SolutionManagerQuery.Resolve(entity, ref entity.Comp, logMissing: false))
|
||||
yield break;
|
||||
|
||||
foreach (var name in container.Comp.Containers)
|
||||
foreach (var (id, solution) in entity.Comp.Solutions)
|
||||
{
|
||||
var attemptEv = new SolutionAccessAttemptEvent(name);
|
||||
RaiseLocalEvent(container, ref attemptEv);
|
||||
var attemptEv = new SolutionAccessAttemptEvent(id);
|
||||
RaiseLocalEvent(entity, ref attemptEv);
|
||||
|
||||
if (attemptEv.Cancelled)
|
||||
continue;
|
||||
|
||||
if (ContainerSystem.GetContainer(container, $"solution@{name}") is ContainerSlot slot && slot.ContainedEntity is { } solutionId)
|
||||
yield return (name, (solutionId, Comp<SolutionComponent>(solutionId)));
|
||||
yield return (id, solution);
|
||||
}
|
||||
}
|
||||
|
||||
public IEnumerable<(string Name, Solution Solution)> EnumerateSolutions(SolutionContainerManagerComponent container)
|
||||
public IEnumerable<(string Id, Solution Solution)> EnumerateSolutions(EntityPrototype entProto)
|
||||
{
|
||||
if (container.Solutions is not { Count: > 0 } solutions)
|
||||
if (!TryGetSolutionFill(entProto, out var solutions))
|
||||
yield break;
|
||||
|
||||
foreach (var (name, solution) in solutions)
|
||||
foreach (var protoId in solutions)
|
||||
{
|
||||
yield return (name, solution);
|
||||
if (!PrototypeManager.Resolve(protoId, out var proto))
|
||||
continue;
|
||||
|
||||
if (!proto.TryGetComponent<SolutionComponent>(out var sol, Factory))
|
||||
{
|
||||
Log.Error($"Entity prototype {proto}, tried to spawn in a solution container in prototype {entProto.ID}, but had no {nameof(SolutionComponent)}");
|
||||
continue;
|
||||
}
|
||||
|
||||
yield return (sol.Id, sol.Solution);
|
||||
}
|
||||
}
|
||||
|
||||
private bool TryGetSolutionFill(Entity<SolutionManagerComponent?> entity, [NotNullWhen(true)] out List<EntProtoId>? fill)
|
||||
{
|
||||
fill = null;
|
||||
if (!SolutionManagerQuery.Resolve(entity, ref entity.Comp))
|
||||
return false;
|
||||
|
||||
protected void UpdateAppearance(Entity<AppearanceComponent?> container, Entity<SolutionComponent, ContainedSolutionComponent> soln)
|
||||
fill = entity.Comp.SolutionEnts;
|
||||
return true;
|
||||
}
|
||||
|
||||
private bool TryGetSolutionFill(EntityPrototype entProto, [NotNullWhen(true)] out List<EntProtoId>? fill)
|
||||
{
|
||||
fill = null;
|
||||
if (!entProto.TryGetComponent<SolutionManagerComponent>(out var manager, Factory))
|
||||
return false;
|
||||
|
||||
fill = manager.SolutionEnts;
|
||||
return true;
|
||||
}
|
||||
|
||||
protected void UpdateAppearance(Entity<AppearanceComponent?> container, Entity<SolutionComponent> soln)
|
||||
{
|
||||
var (uid, appearanceComponent) = container;
|
||||
if (!HasComp<SolutionContainerVisualsComponent>(uid) || !Resolve(uid, ref appearanceComponent, logMissing: false))
|
||||
return;
|
||||
|
||||
var (_, comp, relation) = soln;
|
||||
var solution = comp.Solution;
|
||||
var solution = soln.Comp.Solution;
|
||||
|
||||
AppearanceSystem.SetData(uid, SolutionContainerVisuals.FillFraction, solution.FillFraction, appearanceComponent);
|
||||
AppearanceSystem.SetData(uid, SolutionContainerVisuals.Color, solution.GetColor(PrototypeManager), appearanceComponent);
|
||||
AppearanceSystem.SetData(uid, SolutionContainerVisuals.SolutionName, relation.ContainerName, appearanceComponent);
|
||||
AppearanceSystem.SetData(uid, SolutionContainerVisuals.SolutionName, soln.Comp.Id, appearanceComponent);
|
||||
|
||||
if (solution.GetPrimaryReagentId() is { } reagent)
|
||||
AppearanceSystem.SetData(uid, SolutionContainerVisuals.BaseOverride, reagent.ToString(), appearanceComponent);
|
||||
}
|
||||
|
||||
|
||||
public FixedPoint2 GetTotalPrototypeQuantity(EntityUid owner, string reagentId)
|
||||
public FixedPoint2 GetTotalPrototypeQuantity(Entity<SolutionManagerComponent?> owner, string reagentId)
|
||||
{
|
||||
var reagentQuantity = FixedPoint2.New(0);
|
||||
if (Exists(owner)
|
||||
&& TryComp(owner, out SolutionContainerManagerComponent? managerComponent))
|
||||
if (Exists(owner))
|
||||
{
|
||||
foreach (var (_, soln) in EnumerateSolutions((owner, managerComponent)))
|
||||
foreach (var (_, solution) in EnumerateSolutions(owner))
|
||||
{
|
||||
var solution = soln.Comp.Solution;
|
||||
reagentQuantity += solution.GetTotalPrototypeQuantity(reagentId);
|
||||
reagentQuantity += solution.Comp.Solution.GetTotalPrototypeQuantity(reagentId);
|
||||
}
|
||||
}
|
||||
|
||||
return reagentQuantity;
|
||||
}
|
||||
|
||||
|
||||
/// <summary>
|
||||
/// Dirties a solution entity that has been modified and prompts updates to chemical reactions and overflow state.
|
||||
/// Should be invoked whenever a solution entity is modified.
|
||||
@@ -331,46 +383,37 @@ public abstract partial class SharedSolutionContainerSystem : EntitySystem
|
||||
/// <remarks>
|
||||
/// 90% of this system is ensuring that this proc is invoked whenever a solution entity is changed. The other 10% <i>is</i> this proc.
|
||||
/// </remarks>
|
||||
/// <param name="soln"></param>
|
||||
/// <param name="solution"></param>
|
||||
/// <param name="needsReactionsProcessing"></param>
|
||||
/// <param name="mixerComponent"></param>
|
||||
public void UpdateChemicals(Entity<SolutionComponent> soln, bool needsReactionsProcessing = true, ReactionMixerComponent? mixerComponent = null)
|
||||
public void UpdateChemicals(Entity<SolutionComponent> solution, bool needsReactionsProcessing = true, ReactionMixerComponent? mixerComponent = null)
|
||||
{
|
||||
Dirty(soln);
|
||||
|
||||
var (uid, comp) = soln;
|
||||
var solution = comp.Solution;
|
||||
|
||||
// Process reactions
|
||||
if (needsReactionsProcessing && solution.CanReact)
|
||||
ChemicalReactionSystem.FullyReactSolution(soln, mixerComponent);
|
||||
if (needsReactionsProcessing && solution.Comp.Solution.CanReact)
|
||||
ChemicalReactionSystem.FullyReactSolution(solution, mixerComponent);
|
||||
|
||||
var overflow = solution.Volume - solution.MaxVolume;
|
||||
var overflow = solution.Comp.Solution.Volume - solution.Comp.Solution.MaxVolume;
|
||||
if (overflow > FixedPoint2.Zero)
|
||||
{
|
||||
var overflowEv = new SolutionOverflowEvent(soln, overflow);
|
||||
RaiseLocalEvent(uid, ref overflowEv);
|
||||
var overflowEv = new SolutionOverflowEvent(solution, overflow);
|
||||
RaiseLocalEvent(solution, ref overflowEv);
|
||||
}
|
||||
|
||||
UpdateAppearance((uid, comp, null));
|
||||
var owner = GetSolutionOwner(solution);
|
||||
|
||||
var changedEv = new SolutionChangedEvent(soln);
|
||||
RaiseLocalEvent(uid, ref changedEv);
|
||||
}
|
||||
var changedEv = new SolutionChangedEvent(solution);
|
||||
RaiseLocalEvent(owner, ref changedEv);
|
||||
Dirty(solution);
|
||||
|
||||
public void UpdateAppearance(Entity<SolutionComponent, AppearanceComponent?> soln)
|
||||
{
|
||||
var (uid, comp, appearanceComponent) = soln;
|
||||
var solution = comp.Solution;
|
||||
|
||||
if (!Exists(uid) || !Resolve(uid, ref appearanceComponent, false))
|
||||
if (Timing.ApplyingState)
|
||||
return;
|
||||
|
||||
AppearanceSystem.SetData(uid, SolutionContainerVisuals.FillFraction, solution.FillFraction, appearanceComponent);
|
||||
AppearanceSystem.SetData(uid, SolutionContainerVisuals.Color, solution.GetColor(PrototypeManager), appearanceComponent);
|
||||
UpdateAppearance(owner, solution);
|
||||
}
|
||||
|
||||
if (solution.GetPrimaryReagentId() is { } reagent)
|
||||
AppearanceSystem.SetData(uid, SolutionContainerVisuals.BaseOverride, reagent.ToString(), appearanceComponent);
|
||||
public EntityUid GetSolutionOwner(Entity<SolutionComponent> entity)
|
||||
{
|
||||
return ContainedQuery.CompOrNull(entity)?.Container ?? entity.Owner;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
@@ -830,27 +873,16 @@ public abstract partial class SharedSolutionContainerSystem : EntitySystem
|
||||
entity.Comp.Solution.ValidateSolution();
|
||||
}
|
||||
|
||||
private void OnSolutionStartup(Entity<SolutionComponent> entity, ref ComponentStartup args)
|
||||
private void OnSolutionInit(Entity<SolutionComponent> entity, ref MapInitEvent args)
|
||||
{
|
||||
UpdateChemicals(entity);
|
||||
}
|
||||
|
||||
private void OnSolutionShutdown(Entity<SolutionComponent> entity, ref ComponentShutdown args)
|
||||
{
|
||||
RemoveAllSolution(entity);
|
||||
}
|
||||
|
||||
private void OnContainerManagerInit(Entity<SolutionContainerManagerComponent> entity, ref ComponentInit args)
|
||||
{
|
||||
if (entity.Comp.Containers is not { Count: > 0 } containers)
|
||||
return;
|
||||
|
||||
var containerManager = EnsureComp<ContainerManagerComponent>(entity);
|
||||
foreach (var name in containers)
|
||||
{
|
||||
// The actual solution entity should be directly held within the corresponding slot.
|
||||
ContainerSystem.EnsureContainer<ContainerSlot>(entity.Owner, $"solution@{name}", containerManager);
|
||||
}
|
||||
// If we are contained within another entity, update that entity. Otherwise, don't update if we're being deleted.
|
||||
if (ContainedQuery.HasComp(entity) || !Terminating(entity))
|
||||
RemoveAllSolution(entity);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
@@ -1049,211 +1081,132 @@ public abstract partial class SharedSolutionContainerSystem : EntitySystem
|
||||
return true;
|
||||
}
|
||||
|
||||
private void OnMapInit(Entity<SolutionContainerManagerComponent> entity, ref MapInitEvent args)
|
||||
/// <remarks>
|
||||
/// We want all our solutions spawned before MapInit.
|
||||
/// They should only ever be attached to this entity so spawning them before MapInit should be fine.
|
||||
/// </remarks>
|
||||
private void OnManagerInit(Entity<SolutionManagerComponent> entity, ref MapInitEvent args)
|
||||
{
|
||||
EnsureAllSolutions(entity);
|
||||
var container = ContainerSystem.EnsureContainer<Container>(entity.Owner, entity.Comp.Container);
|
||||
foreach (var solution in entity.Comp.SolutionEnts)
|
||||
{
|
||||
CreateSolution(solution, container);
|
||||
}
|
||||
}
|
||||
|
||||
private void OnContainerManagerShutdown(Entity<SolutionContainerManagerComponent> entity, ref ComponentShutdown args)
|
||||
private void OnManagerShutdown(Entity<SolutionManagerComponent> entity, ref ComponentShutdown args)
|
||||
{
|
||||
foreach (var name in entity.Comp.Containers)
|
||||
{
|
||||
if (ContainerSystem.TryGetContainer(entity, $"solution@{name}", out var solutionContainer))
|
||||
ContainerSystem.ShutdownContainer(solutionContainer);
|
||||
}
|
||||
entity.Comp.Containers.Clear();
|
||||
}
|
||||
|
||||
private void OnContainedSolutionShutdown(Entity<ContainedSolutionComponent> entity, ref ComponentShutdown args)
|
||||
{
|
||||
if (TryComp(entity.Comp.Container, out SolutionContainerManagerComponent? container))
|
||||
{
|
||||
container.Containers.Remove(entity.Comp.ContainerName);
|
||||
Dirty(entity.Comp.Container, container);
|
||||
}
|
||||
|
||||
if (ContainerSystem.TryGetContainer(entity, $"solution@{entity.Comp.ContainerName}", out var solutionContainer))
|
||||
if (ContainerSystem.TryGetContainer(entity, entity.Comp.Container, out var solutionContainer))
|
||||
ContainerSystem.ShutdownContainer(solutionContainer);
|
||||
}
|
||||
|
||||
private void OnSolutionAdded(Entity<SolutionManagerComponent> entity, ref EntInsertedIntoContainerMessage args)
|
||||
{
|
||||
// Container networking boilerplate
|
||||
if (args.Container.ID != entity.Comp.Container || !SolutionQuery.TryComp(args.Entity, out var solution))
|
||||
return;
|
||||
|
||||
// Don't add a solution entity with the same id as this entity's solution if it exists!
|
||||
DebugTools.Assert(!TryComp<SolutionComponent>(entity, out var sol) || sol.Id != solution.Id, $"Tried to add a solution {MetaData(args.Entity).EntityPrototype} {solution.Id} to {ToPrettyString(entity)} but it itself was a solution with a matching id!");
|
||||
|
||||
EnsureComp<ContainedSolutionComponent>(args.Entity, out var contained);
|
||||
contained.Container = entity.Owner;
|
||||
|
||||
// Throw if we already have a solution with the same ID. Only throw on server to avoid prediction causing issues.
|
||||
if (!entity.Comp.Solutions.TryAdd(solution.Id, (args.Entity, solution)) && Net.IsServer)
|
||||
Log.Error($"Solution {ToPrettyString(entity)}, tried to add a solution with a duplicate id: {solution.Id}");
|
||||
}
|
||||
|
||||
private void OnSolutionRemoved(Entity<SolutionManagerComponent> entity, ref EntRemovedFromContainerMessage args)
|
||||
{
|
||||
// Container networking jank
|
||||
if (args.Container.ID != entity.Comp.Container || !SolutionQuery.TryComp(args.Entity, out var solution))
|
||||
return;
|
||||
|
||||
RemComp<ContainedSolutionComponent>(args.Entity);
|
||||
entity.Comp.Solutions.Remove(solution.Id);
|
||||
}
|
||||
|
||||
#endregion Event Handlers
|
||||
|
||||
/// <summary>
|
||||
/// A method which ensures a solution with a given ID exists.
|
||||
/// </summary>
|
||||
/// <param name="entity">Entity we're trying to attach a new solution to.</param>
|
||||
/// <param name="name">Name of the new solution.</param>
|
||||
/// <param name="solutionEntity">Solution entity found or created.</param>
|
||||
/// <returns>Returns true if the solution already existed, and false if it had to create a new solution.</returns>
|
||||
/// <remarks>
|
||||
/// Only run this after the entity is already initialized.
|
||||
/// If you're running this when your entity is created, it is recommended to run on <see cref="MapInitEvent"/>
|
||||
/// Deviance from these instructions may prevent your game from building. YOU HAVE BEEN WARNED.
|
||||
/// </remarks>
|
||||
public bool EnsureSolution(
|
||||
Entity<MetaDataComponent?> entity,
|
||||
Entity<SolutionManagerComponent?> entity,
|
||||
string name,
|
||||
[NotNullWhen(true)] out Solution? solution,
|
||||
FixedPoint2 maxVol = default)
|
||||
out Entity<SolutionComponent> solutionEntity)
|
||||
{
|
||||
return EnsureSolution(entity, name, maxVol, null, out _, out solution);
|
||||
}
|
||||
|
||||
public bool EnsureSolution(
|
||||
Entity<MetaDataComponent?> entity,
|
||||
string name,
|
||||
out bool existed,
|
||||
[NotNullWhen(true)] out Solution? solution,
|
||||
FixedPoint2 maxVol = default)
|
||||
{
|
||||
return EnsureSolution(entity, name, maxVol, null, out existed, out solution);
|
||||
}
|
||||
|
||||
public bool EnsureSolution(
|
||||
Entity<MetaDataComponent?> entity,
|
||||
string name,
|
||||
FixedPoint2 maxVol,
|
||||
Solution? prototype,
|
||||
out bool existed,
|
||||
[NotNullWhen(true)] out Solution? solution)
|
||||
{
|
||||
solution = null;
|
||||
existed = false;
|
||||
|
||||
var (uid, meta) = entity;
|
||||
if (!Resolve(uid, ref meta))
|
||||
throw new InvalidOperationException("Attempted to ensure solution on invalid entity.");
|
||||
var manager = EnsureComp<SolutionContainerManagerComponent>(uid);
|
||||
if (meta.EntityLifeStage >= EntityLifeStage.MapInitialized)
|
||||
if (SolutionQuery.TryComp(entity, out var comp) && comp.Id == name)
|
||||
{
|
||||
EnsureSolutionEntity((uid, manager), name, out existed,
|
||||
out var solEnt, maxVol, prototype);
|
||||
solution = solEnt!.Value.Comp.Solution;
|
||||
solutionEntity = (entity.Owner, comp);
|
||||
return true;
|
||||
}
|
||||
solution = EnsureSolutionPrototype((uid, manager), name, maxVol, prototype, out existed);
|
||||
return true;
|
||||
}
|
||||
|
||||
public void EnsureAllSolutions(Entity<SolutionContainerManagerComponent> entity)
|
||||
{
|
||||
if (NetManager.IsClient)
|
||||
return;
|
||||
// Ensure we have a SolutionManagerComponent
|
||||
// EnsureComp should ensure a container and fill that container with default spawns!
|
||||
if (entity.Comp == null)
|
||||
EnsureComp<SolutionManagerComponent>(entity, out entity.Comp);
|
||||
|
||||
if (entity.Comp.Solutions is not { } prototypes)
|
||||
return;
|
||||
|
||||
foreach (var (name, prototype) in prototypes)
|
||||
// Check the cache first, even if the component didn't exist before, creating one may have spawned and cached solutions!
|
||||
if (entity.Comp.Solutions.TryGetValue(name, out var solution))
|
||||
{
|
||||
EnsureSolutionEntity((entity.Owner, entity.Comp), name, out _, out _, prototype.MaxVolume, prototype);
|
||||
solutionEntity = solution;
|
||||
return true;
|
||||
}
|
||||
|
||||
entity.Comp.Solutions = null;
|
||||
Dirty(entity);
|
||||
// Create a default entity if one doesn't already exist!
|
||||
solutionEntity = CreateDefaultSolution((entity, entity.Comp), name);
|
||||
return false;
|
||||
}
|
||||
|
||||
public bool EnsureSolutionEntity(
|
||||
Entity<SolutionContainerManagerComponent?> entity,
|
||||
/// <remarks>This is private since you should really be specifying a solution prototype to create.</remarks>
|
||||
private Entity<SolutionComponent> CreateDefaultSolution(
|
||||
Entity<SolutionManagerComponent> entity,
|
||||
string name)
|
||||
{
|
||||
var container = ContainerSystem.EnsureContainer<Container>(entity.Owner, entity.Comp.Container);
|
||||
return CreateDefaultSolution(name, container);
|
||||
}
|
||||
|
||||
private Entity<SolutionComponent> CreateDefaultSolution(
|
||||
string name,
|
||||
[NotNullWhen(true)] out Entity<SolutionComponent>? solutionEntity,
|
||||
FixedPoint2 maxVol = default) =>
|
||||
EnsureSolutionEntity(entity, name, out _, out solutionEntity, maxVol);
|
||||
|
||||
public bool EnsureSolutionEntity(
|
||||
Entity<SolutionContainerManagerComponent?> entity,
|
||||
string name,
|
||||
out bool existed,
|
||||
[NotNullWhen(true)] out Entity<SolutionComponent>? solutionEntity,
|
||||
FixedPoint2 maxVol = default,
|
||||
Solution? prototype = null
|
||||
)
|
||||
Container container)
|
||||
{
|
||||
existed = true;
|
||||
solutionEntity = null;
|
||||
|
||||
var (uid, container) = entity;
|
||||
|
||||
var solutionSlot = ContainerSystem.EnsureContainer<ContainerSlot>(uid, $"solution@{name}", out existed);
|
||||
if (!Resolve(uid, ref container, logMissing: false))
|
||||
{
|
||||
existed = false;
|
||||
container = AddComp<SolutionContainerManagerComponent>(uid);
|
||||
container.Containers.Add(name);
|
||||
if (NetManager.IsClient)
|
||||
return false;
|
||||
}
|
||||
else if (!existed)
|
||||
{
|
||||
container.Containers.Add(name);
|
||||
Dirty(uid, container);
|
||||
}
|
||||
|
||||
var needsInit = false;
|
||||
SolutionComponent solutionComp;
|
||||
if (solutionSlot.ContainedEntity is not { } solutionId)
|
||||
{
|
||||
if (NetManager.IsClient)
|
||||
return false;
|
||||
prototype ??= new() { MaxVolume = maxVol };
|
||||
prototype.Name = name;
|
||||
(solutionId, solutionComp, _) = SpawnSolutionUninitialized(solutionSlot, name, maxVol, prototype);
|
||||
existed = false;
|
||||
needsInit = true;
|
||||
Dirty(uid, container);
|
||||
}
|
||||
else
|
||||
{
|
||||
solutionComp = Comp<SolutionComponent>(solutionId);
|
||||
DebugTools.Assert(TryComp(solutionId, out ContainedSolutionComponent? relation) && relation.Container == uid && relation.ContainerName == name);
|
||||
DebugTools.Assert(solutionComp.Solution.Name == name);
|
||||
|
||||
var solution = solutionComp.Solution;
|
||||
solution.MaxVolume = FixedPoint2.Max(solution.MaxVolume, maxVol);
|
||||
|
||||
// Depending on MapInitEvent order some systems can ensure solution empty solutions and conflict with the prototype solutions.
|
||||
// We want the reagents from the prototype to exist even if something else already created the solution.
|
||||
if (prototype is { Volume.Value: > 0 })
|
||||
solution.AddSolution(prototype, PrototypeManager);
|
||||
|
||||
Dirty(solutionId, solutionComp);
|
||||
}
|
||||
|
||||
if (needsInit)
|
||||
EntityManager.InitializeAndStartEntity(solutionId, Transform(solutionId).MapID);
|
||||
solutionEntity = (solutionId, solutionComp);
|
||||
return true;
|
||||
}
|
||||
|
||||
private Solution EnsureSolutionPrototype(Entity<SolutionContainerManagerComponent?> entity, string name, FixedPoint2 maxVol, Solution? prototype, out bool existed)
|
||||
{
|
||||
existed = true;
|
||||
|
||||
var (uid, container) = entity;
|
||||
if (!Resolve(uid, ref container, logMissing: false))
|
||||
{
|
||||
container = AddComp<SolutionContainerManagerComponent>(uid);
|
||||
existed = false;
|
||||
}
|
||||
|
||||
if (container.Solutions is null)
|
||||
container.Solutions = new(SolutionContainerManagerComponent.DefaultCapacity);
|
||||
|
||||
if (!container.Solutions.TryGetValue(name, out var solution))
|
||||
{
|
||||
solution = prototype ?? new() { Name = name, MaxVolume = maxVol };
|
||||
container.Solutions.Add(name, solution);
|
||||
existed = false;
|
||||
}
|
||||
else
|
||||
solution.MaxVolume = FixedPoint2.Max(solution.MaxVolume, maxVol);
|
||||
|
||||
Dirty(uid, container);
|
||||
var solution = SpawnSolutionUninitialized(DefaultSolution);
|
||||
solution.Comp.Id = name;
|
||||
ContainerSystem.Insert(solution.Owner, container, force: true);
|
||||
EntityManager.InitializeAndStartEntity(solution);
|
||||
return solution;
|
||||
}
|
||||
|
||||
private Entity<SolutionComponent, ContainedSolutionComponent> SpawnSolutionUninitialized(ContainerSlot container, string name, FixedPoint2 maxVol, Solution prototype)
|
||||
public Entity<SolutionComponent> CreateSolution(
|
||||
EntProtoId proto,
|
||||
Container container)
|
||||
{
|
||||
var coords = new EntityCoordinates(container.Owner, Vector2.Zero);
|
||||
var uid = EntityManager.CreateEntityUninitialized(null, coords, null);
|
||||
// TODO: Replace this with an engine bound method when e#6192 is merged.
|
||||
var solution = SpawnSolutionUninitialized(proto);
|
||||
ContainerSystem.Insert(solution.Owner, container, force: true);
|
||||
EntityManager.InitializeAndStartEntity(solution);
|
||||
return solution;
|
||||
}
|
||||
|
||||
var solution = new SolutionComponent() { Solution = prototype };
|
||||
AddComp(uid, solution);
|
||||
private Entity<SolutionComponent> SpawnSolutionUninitialized(EntProtoId solution)
|
||||
{
|
||||
var uid = EntityManager.CreateEntityUninitialized(solution);
|
||||
|
||||
var relation = new ContainedSolutionComponent() { Container = container.Owner, ContainerName = name };
|
||||
AddComp(uid, relation);
|
||||
|
||||
MetaDataSys.SetEntityName(uid, $"solution - {name}", raiseEvents: false);
|
||||
ContainerSystem.Insert(uid, container, force: true);
|
||||
|
||||
return (uid, solution, relation);
|
||||
// If you pass in a ProtoId without a SolutionComponent that's your own damn fault!
|
||||
var comp = SolutionQuery.Comp(uid);
|
||||
return (uid, comp);
|
||||
}
|
||||
|
||||
public void AdjustDissolvedReagent(
|
||||
|
||||
@@ -27,7 +27,8 @@ public sealed class SolutionPurgeSystem : EntitySystem
|
||||
{
|
||||
base.Update(frameTime);
|
||||
|
||||
var query = EntityQueryEnumerator<SolutionPurgeComponent, SolutionContainerManagerComponent>();
|
||||
// TODO: SolutionPurgeComponent on Solution Entities!
|
||||
var query = EntityQueryEnumerator<SolutionPurgeComponent, SolutionManagerComponent>();
|
||||
while (query.MoveNext(out var uid, out var purge, out var manager))
|
||||
{
|
||||
if (_timing.CurTime < purge.NextPurgeTime)
|
||||
|
||||
@@ -38,7 +38,8 @@ public sealed class SolutionRegenerationSystem : EntitySystem
|
||||
{
|
||||
base.Update(frameTime);
|
||||
|
||||
var query = EntityQueryEnumerator<SolutionRegenerationComponent, SolutionContainerManagerComponent>();
|
||||
// TODO: SolutionRegenerationComponent on Solution Entities!
|
||||
var query = EntityQueryEnumerator<SolutionRegenerationComponent, SolutionManagerComponent>();
|
||||
while (query.MoveNext(out var uid, out var regen, out var manager))
|
||||
{
|
||||
if (_timing.CurTime < regen.NextRegenTime)
|
||||
|
||||
@@ -26,7 +26,7 @@ public sealed class SolutionSpikerSystem : EntitySystem
|
||||
|
||||
private void OnInteractUsing(Entity<RefillableSolutionComponent> entity, ref InteractUsingEvent args)
|
||||
{
|
||||
if (TrySpike(args.Used, args.Target, args.User, entity.Comp))
|
||||
if (TrySpike(args.Used, (args.Target, entity.Comp), args.User))
|
||||
args.Handled = true;
|
||||
}
|
||||
|
||||
@@ -37,31 +37,29 @@ public sealed class SolutionSpikerSystem : EntitySystem
|
||||
/// <param name="source">Source of the solution.</param>
|
||||
/// <param name="target">Target to spike with the solution from source.</param>
|
||||
/// <param name="user">User spiking the target solution.</param>
|
||||
private bool TrySpike(EntityUid source, EntityUid target, EntityUid user, RefillableSolutionComponent? spikableTarget = null,
|
||||
SolutionSpikerComponent? spikableSource = null,
|
||||
SolutionContainerManagerComponent? managerSource = null,
|
||||
SolutionContainerManagerComponent? managerTarget = null)
|
||||
private bool TrySpike(Entity<SolutionSpikerComponent?> source,
|
||||
Entity<RefillableSolutionComponent?> target,
|
||||
EntityUid user)
|
||||
{
|
||||
if (!Resolve(source, ref spikableSource, ref managerSource, false)
|
||||
|| !Resolve(target, ref spikableTarget, ref managerTarget, false)
|
||||
|| !_solution.TryGetRefillableSolution((target, spikableTarget, managerTarget), out var targetSoln, out var targetSolution)
|
||||
|| !_solution.TryGetSolution((source, managerSource), spikableSource.SourceSolution, out _, out var sourceSolution))
|
||||
if (!Resolve(source, ref source.Comp, false)
|
||||
|| !_solution.TryGetRefillableSolution((target, target.Comp), out var targetSoln, out var targetSolution)
|
||||
|| !_solution.TryGetSolution(source.Owner, source.Comp.SourceSolution, out _, out var sourceSolution))
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
if (targetSolution.Volume == 0 && !spikableSource.IgnoreEmpty)
|
||||
if (targetSolution.Volume == 0 && !source.Comp.IgnoreEmpty)
|
||||
{
|
||||
_popup.PopupClient(Loc.GetString(spikableSource.PopupEmpty, ("spiked-entity", target), ("spike-entity", source)), user, user);
|
||||
_popup.PopupClient(Loc.GetString(source.Comp.PopupEmpty, ("spiked-entity", target), ("spike-entity", source)), user, user);
|
||||
return false;
|
||||
}
|
||||
|
||||
if (!_solution.ForceAddSolution(targetSoln.Value, sourceSolution))
|
||||
return false;
|
||||
|
||||
_popup.PopupClient(Loc.GetString(spikableSource.Popup, ("spiked-entity", target), ("spike-entity", source)), user, user);
|
||||
_popup.PopupClient(Loc.GetString(source.Comp.Popup, ("spiked-entity", target), ("spike-entity", source)), user, user);
|
||||
sourceSolution.RemoveAllSolution();
|
||||
if (spikableSource.Delete)
|
||||
if (source.Comp.Delete)
|
||||
QueueDel(source);
|
||||
|
||||
return true;
|
||||
|
||||
@@ -29,7 +29,8 @@ public sealed class SolutionTransferSystem : EntitySystem
|
||||
/// <summary>
|
||||
/// Default transfer amounts for the set-transfer verb.
|
||||
/// </summary>
|
||||
public static readonly FixedPoint2[] DefaultTransferAmounts = new FixedPoint2[] { 1, 5, 10, 25, 50, 100, 250, 500, 1000 };
|
||||
/// TODO: Turn this into a prototype just like with injectors.
|
||||
public static readonly FixedPoint2[] DefaultTransferAmounts = new FixedPoint2[] { 1, 5, 10, 15, 30, 60, 120, 240, 480, 960 };
|
||||
|
||||
public override void Initialize()
|
||||
{
|
||||
|
||||
+3
-3
@@ -11,11 +11,11 @@ namespace Content.Shared.EntityEffects.Effects.Solution;
|
||||
/// Quantity is modified by scale.
|
||||
/// </summary>
|
||||
/// <inheritdoc cref="EntityEffectSystem{T,TEffect}"/>
|
||||
public sealed class AddReagentToSolutionEntityEffectSystem : EntityEffectSystem<SolutionContainerManagerComponent, AddReagentToSolution>
|
||||
public sealed class AddReagentToSolutionEntityEffectSystem : EntityEffectSystem<SolutionManagerComponent, AddReagentToSolution>
|
||||
{
|
||||
[Dependency] private readonly SharedSolutionContainerSystem _solutionContainer = default!;
|
||||
|
||||
protected override void Effect(Entity<SolutionContainerManagerComponent> entity, ref EntityEffectEvent<AddReagentToSolution> args)
|
||||
protected override void Effect(Entity<SolutionManagerComponent> entity, ref EntityEffectEvent<AddReagentToSolution> args)
|
||||
{
|
||||
var solution = args.Effect.Solution;
|
||||
var reagent = args.Effect.Reagent;
|
||||
@@ -40,7 +40,7 @@ public sealed partial class AddReagentToSolution : EntityEffectBase<AddReagentTo
|
||||
/// Solution we're looking for
|
||||
/// </summary>
|
||||
[DataField(required: true)]
|
||||
public string? Solution = "reagents";
|
||||
public string Solution = "reagents";
|
||||
|
||||
///<summary>
|
||||
/// A modifier for how much reagent we're creating.
|
||||
|
||||
@@ -1,5 +1,4 @@
|
||||
using Content.Shared.Audio;
|
||||
using Content.Shared.Chemistry.Components.SolutionManager;
|
||||
using Content.Shared.Chemistry.EntitySystems;
|
||||
using Content.Shared.Database;
|
||||
using Content.Shared.DoAfter;
|
||||
@@ -23,6 +22,7 @@ namespace Content.Shared.Fluids.EntitySystems;
|
||||
|
||||
/// <summary>
|
||||
/// Handles the draining of solutions from containers into drains.
|
||||
/// TODO: This system is very bad, and needs to be rewritten.
|
||||
/// </summary>
|
||||
public sealed class DrainSystem : EntitySystem
|
||||
{
|
||||
@@ -130,9 +130,9 @@ public sealed class DrainSystem : EntitySystem
|
||||
{
|
||||
base.Update(frameTime);
|
||||
|
||||
var query = EntityQueryEnumerator<DrainComponent, SolutionContainerManagerComponent>();
|
||||
var query = EntityQueryEnumerator<DrainComponent>();
|
||||
var curTime = _timing.CurTime;
|
||||
while (query.MoveNext(out var uid, out var drain, out var manager))
|
||||
while (query.MoveNext(out var uid, out var drain))
|
||||
{
|
||||
if (curTime < drain.NextUpdate)
|
||||
continue;
|
||||
@@ -141,7 +141,7 @@ public sealed class DrainSystem : EntitySystem
|
||||
Dirty(uid, drain);
|
||||
|
||||
// Best to do this one every second rather than once every tick...
|
||||
if (!_solutionContainerSystem.ResolveSolution((uid, manager), DrainComponent.SolutionName, ref drain.Solution, out var drainSolution))
|
||||
if (!_solutionContainerSystem.ResolveSolution(uid, DrainComponent.SolutionName, ref drain.Solution, out var drainSolution))
|
||||
continue;
|
||||
|
||||
if (drainSolution.Volume <= 0 && !drain.AutoDrain)
|
||||
@@ -201,12 +201,9 @@ public sealed class DrainSystem : EntitySystem
|
||||
|
||||
private void OnExamined(Entity<DrainComponent> ent, ref ExaminedEvent args)
|
||||
{
|
||||
if (!args.IsInDetailsRange ||
|
||||
!HasComp<SolutionContainerManagerComponent>(ent) ||
|
||||
!_solutionContainerSystem.ResolveSolution(ent.Owner, DrainComponent.SolutionName, ref ent.Comp.Solution, out var drainSolution))
|
||||
{
|
||||
if (!args.IsInDetailsRange
|
||||
|| !_solutionContainerSystem.ResolveSolution(ent.Owner, DrainComponent.SolutionName, ref ent.Comp.Solution, out var drainSolution))
|
||||
return;
|
||||
}
|
||||
|
||||
var text = drainSolution.AvailableVolume != 0
|
||||
? Loc.GetString("drain-component-examine-volume", ("volume", drainSolution.AvailableVolume))
|
||||
|
||||
@@ -38,7 +38,7 @@ public abstract class SharedAbsorbentSystem : EntitySystem
|
||||
|
||||
SubscribeLocalEvent<AbsorbentComponent, AfterInteractEvent>(OnAfterInteract);
|
||||
SubscribeLocalEvent<AbsorbentComponent, UserActivateInWorldEvent>(OnActivateInWorld);
|
||||
SubscribeLocalEvent<AbsorbentComponent, SolutionContainerChangedEvent>(OnAbsorbentSolutionChange);
|
||||
SubscribeLocalEvent<AbsorbentComponent, SolutionChangedEvent>(OnAbsorbentSolutionChange);
|
||||
}
|
||||
|
||||
private void OnActivateInWorld(Entity<AbsorbentComponent> ent, ref UserActivateInWorldEvent args)
|
||||
@@ -59,7 +59,7 @@ public abstract class SharedAbsorbentSystem : EntitySystem
|
||||
args.Handled = true;
|
||||
}
|
||||
|
||||
private void OnAbsorbentSolutionChange(Entity<AbsorbentComponent> ent, ref SolutionContainerChangedEvent args)
|
||||
private void OnAbsorbentSolutionChange(Entity<AbsorbentComponent> ent, ref SolutionChangedEvent args)
|
||||
{
|
||||
// The changes are already networked as part of the same game state.
|
||||
if (_timing.ApplyingState)
|
||||
|
||||
@@ -66,7 +66,7 @@ public abstract partial class SharedPuddleSystem : EntitySystem
|
||||
base.Initialize();
|
||||
// Shouldn't need re-anchoring.
|
||||
SubscribeLocalEvent<PuddleComponent, AnchorStateChangedEvent>(OnAnchorChanged);
|
||||
SubscribeLocalEvent<PuddleComponent, SolutionContainerChangedEvent>(OnSolutionUpdate);
|
||||
SubscribeLocalEvent<PuddleComponent, SolutionChangedEvent>(OnSolutionUpdate);
|
||||
SubscribeLocalEvent<PuddleComponent, GetFootstepSoundEvent>(OnGetFootstepSound);
|
||||
SubscribeLocalEvent<PuddleComponent, ExaminedEvent>(HandlePuddleExamined);
|
||||
SubscribeLocalEvent<PuddleComponent, EntRemovedFromContainerMessage>(OnEntRemoved);
|
||||
@@ -110,25 +110,25 @@ public abstract partial class SharedPuddleSystem : EntitySystem
|
||||
_standoutReagents = [.. _prototypeManager.EnumeratePrototypes<ReagentPrototype>().Where(x => x.Standsout).Select(x => x.ID)];
|
||||
}
|
||||
|
||||
private void OnSolutionUpdate(Entity<PuddleComponent> entity, ref SolutionContainerChangedEvent args)
|
||||
private void OnSolutionUpdate(Entity<PuddleComponent> entity, ref SolutionChangedEvent args)
|
||||
{
|
||||
// The changes are already networked as part of the same game state.
|
||||
if (_timing.ApplyingState)
|
||||
return;
|
||||
|
||||
if (args.SolutionId != entity.Comp.SolutionName)
|
||||
if (args.Solution.Comp.Id != entity.Comp.SolutionName)
|
||||
return;
|
||||
|
||||
if (args.Solution.Volume <= 0)
|
||||
if (args.Solution.Comp.Solution.Volume <= 0)
|
||||
{
|
||||
_deletionQueue.Add(entity);
|
||||
return;
|
||||
}
|
||||
|
||||
_deletionQueue.Remove(entity);
|
||||
UpdateSlip((entity, entity.Comp), args.Solution);
|
||||
UpdateSlow(entity, args.Solution);
|
||||
UpdateEvaporation(entity, args.Solution);
|
||||
UpdateSlip((entity, entity.Comp), args.Solution.Comp.Solution);
|
||||
UpdateSlow(entity, args.Solution.Comp.Solution);
|
||||
UpdateEvaporation(entity, args.Solution.Comp.Solution);
|
||||
UpdateAppearance((entity, entity.Comp));
|
||||
}
|
||||
|
||||
|
||||
@@ -41,7 +41,7 @@ public abstract class SharedReagentGrinderSystem : EntitySystem
|
||||
{
|
||||
base.Initialize();
|
||||
|
||||
SubscribeLocalEvent<InsideReagentGrinderComponent, SolutionContainerChangedEvent>(OnBeakerSolutionContainerChanged);
|
||||
SubscribeLocalEvent<InsideReagentGrinderComponent, SolutionChangedEvent>(OnBeakerSolutionContainerChanged);
|
||||
|
||||
SubscribeLocalEvent<ReagentGrinderComponent, ComponentStartup>(OnGrinderStartup);
|
||||
SubscribeLocalEvent<ReagentGrinderComponent, ContainerIsRemovingAttemptEvent>(OnEntRemovingAttempt);
|
||||
@@ -56,7 +56,7 @@ public abstract class SharedReagentGrinderSystem : EntitySystem
|
||||
SubscribeLocalEvent<ReagentGrinderComponent, ReagentGrinderEjectChamberContentMessage>(OnEjectChamberContentMessage);
|
||||
}
|
||||
|
||||
private void OnBeakerSolutionContainerChanged(Entity<InsideReagentGrinderComponent> ent, ref SolutionContainerChangedEvent args)
|
||||
private void OnBeakerSolutionContainerChanged(Entity<InsideReagentGrinderComponent> ent, ref SolutionChangedEvent args)
|
||||
{
|
||||
// Update the UI if the reagents inside the beaker are changed.
|
||||
// This is needed in case the component state for the container is applied before that of the solution container
|
||||
@@ -356,10 +356,11 @@ public abstract class SharedReagentGrinderSystem : EntitySystem
|
||||
switch (program)
|
||||
{
|
||||
case GrinderProgram.Grind:
|
||||
if (_solutionContainersSystem.TryGetSolution(ent.Owner, ent.Comp.GrindableSolutionName, out _, out var solution))
|
||||
{
|
||||
if (ent.Comp.GrindableSolutionName is not { } solutionId)
|
||||
return null;
|
||||
|
||||
if (_solutionContainersSystem.TryGetSolution(ent.Owner, solutionId, out _, out var solution))
|
||||
return solution;
|
||||
}
|
||||
break;
|
||||
case GrinderProgram.Juice:
|
||||
return ent.Comp.JuiceSolution;
|
||||
|
||||
@@ -5,7 +5,6 @@ using Content.Shared.Body.Components;
|
||||
using Content.Shared.Body.Systems;
|
||||
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.Climbing.Systems;
|
||||
@@ -57,8 +56,6 @@ public abstract partial class SharedCryoPodSystem : EntitySystem
|
||||
[Dependency] private readonly EntityQuery<BloodstreamComponent> _bloodstreamQuery = default!;
|
||||
[Dependency] private readonly EntityQuery<ItemSlotsComponent> _itemSlotsQuery = default!;
|
||||
[Dependency] private readonly EntityQuery<FitsInDispenserComponent> _dispenserQuery = default!;
|
||||
[Dependency] private readonly EntityQuery<SolutionContainerManagerComponent> _solutionContainerQuery = default!;
|
||||
|
||||
|
||||
public override void Initialize()
|
||||
{
|
||||
@@ -111,9 +108,8 @@ public abstract partial class SharedCryoPodSystem : EntitySystem
|
||||
var patient = entity.Comp.BodyContainer.ContainedEntity;
|
||||
|
||||
if (patient == null
|
||||
|| !_solutionContainerQuery.TryComp(entity, out var podSolutionManager)
|
||||
|| !_solutionContainer.TryGetSolution(
|
||||
(entity.Owner, podSolutionManager),
|
||||
entity.Owner,
|
||||
CryoPodComponent.InjectionBufferSolutionName,
|
||||
out var injectingSolution,
|
||||
out _)
|
||||
@@ -352,14 +348,12 @@ public abstract partial class SharedCryoPodSystem : EntitySystem
|
||||
if (beaker == null
|
||||
|| !beaker.Value.Valid
|
||||
|| !_dispenserQuery.TryComp(beaker, out var fitsInDispenserComponent)
|
||||
|| !_solutionContainerQuery.TryComp(beaker, out var beakerSolutionManager)
|
||||
|| !_solutionContainerQuery.TryComp(cryoPod, out var podSolutionManager)
|
||||
|| !_solutionContainer.TryGetFitsInDispenser(
|
||||
(beaker.Value, fitsInDispenserComponent, beakerSolutionManager),
|
||||
(beaker.Value, fitsInDispenserComponent),
|
||||
out var beakerSolution,
|
||||
out _)
|
||||
|| !_solutionContainer.TryGetSolution(
|
||||
(cryoPod.Owner, podSolutionManager),
|
||||
cryoPod.Owner,
|
||||
CryoPodComponent.InjectionBufferSolutionName,
|
||||
out var injectionSolutionComp,
|
||||
out var injectionSolution))
|
||||
@@ -377,9 +371,8 @@ public abstract partial class SharedCryoPodSystem : EntitySystem
|
||||
|
||||
public void ClearInjectionBuffer(Entity<CryoPodComponent> cryoPod)
|
||||
{
|
||||
if (_solutionContainerQuery.TryComp(cryoPod, out var podSolutionManager)
|
||||
&& _solutionContainer.TryGetSolution(
|
||||
(cryoPod.Owner, podSolutionManager),
|
||||
if (_solutionContainer.TryGetSolution(
|
||||
cryoPod.Owner,
|
||||
CryoPodComponent.InjectionBufferSolutionName,
|
||||
out var injectingSolution,
|
||||
out _))
|
||||
@@ -402,9 +395,8 @@ public abstract partial class SharedCryoPodSystem : EntitySystem
|
||||
if (beaker == null
|
||||
|| !beaker.Value.Valid
|
||||
|| !_dispenserQuery.TryComp(beaker, out var fitsInDispenserComponent)
|
||||
|| !_solutionContainerQuery.TryComp(beaker, out var solutionContainerManagerComponent)
|
||||
|| !_solutionContainer.TryGetFitsInDispenser(
|
||||
(beaker.Value, fitsInDispenserComponent, solutionContainerManagerComponent),
|
||||
(beaker.Value, fitsInDispenserComponent),
|
||||
out var containerSolution,
|
||||
out _))
|
||||
return (null, null);
|
||||
@@ -419,9 +411,8 @@ public abstract partial class SharedCryoPodSystem : EntitySystem
|
||||
|
||||
protected List<ReagentQuantity>? GetInjectingReagents(Entity<CryoPodComponent> entity)
|
||||
{
|
||||
if (!_solutionContainerQuery.TryComp(entity, out var solutionManager)
|
||||
|| !_solutionContainer.TryGetSolution(
|
||||
(entity.Owner, solutionManager),
|
||||
if (!_solutionContainer.TryGetSolution(
|
||||
entity.Owner,
|
||||
CryoPodComponent.InjectionBufferSolutionName,
|
||||
out var injectingSolution,
|
||||
out _))
|
||||
|
||||
@@ -32,7 +32,7 @@ public sealed class MetabolizerSystem : EntitySystem
|
||||
[Dependency] private readonly SharedSolutionContainerSystem _solutionContainerSystem = default!;
|
||||
|
||||
[Dependency] private readonly EntityQuery<OrganComponent> _organQuery = default!;
|
||||
[Dependency] private readonly EntityQuery<SolutionContainerManagerComponent> _solutionQuery = default!;
|
||||
[Dependency] private readonly EntityQuery<SolutionManagerComponent> _solutionQuery = default!;
|
||||
|
||||
public override void Initialize()
|
||||
{
|
||||
@@ -87,7 +87,7 @@ public sealed class MetabolizerSystem : EntitySystem
|
||||
}
|
||||
|
||||
private bool LookupSolution(
|
||||
Entity<MetabolizerComponent, OrganComponent?, SolutionContainerManagerComponent?> ent,
|
||||
Entity<MetabolizerComponent, OrganComponent?, SolutionManagerComponent?> ent,
|
||||
MetabolismSolutionEntry solutionData,
|
||||
bool lookupTransfer,
|
||||
[NotNullWhen(true)] out Solution? solution,
|
||||
@@ -106,28 +106,24 @@ public sealed class MetabolizerSystem : EntitySystem
|
||||
|
||||
if (lookupTransfer ? solutionData.TransferSolutionOnBody : solutionData.SolutionOnBody)
|
||||
{
|
||||
if (ent.Comp2?.Body is { } body)
|
||||
{
|
||||
if (!_solutionQuery.TryComp(body, out var bodySolution))
|
||||
return false;
|
||||
|
||||
solutionOwner = body;
|
||||
return _solutionContainerSystem.TryGetSolution((body, bodySolution), solutionName, out solutionEntity, out solution);
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
if (!_solutionQuery.Resolve(ent, ref ent.Comp3, logMissing: false))
|
||||
if (ent.Comp2?.Body is not { } body)
|
||||
return false;
|
||||
|
||||
solutionOwner = ent;
|
||||
return _solutionContainerSystem.TryGetSolution((ent, ent), solutionName, out solutionEntity, out solution);
|
||||
if (!_solutionContainerSystem.TryGetSolution(body, solutionName, out solutionEntity, out solution))
|
||||
return false;
|
||||
|
||||
solutionOwner = body;
|
||||
return true;
|
||||
}
|
||||
|
||||
return false;
|
||||
if (!_solutionContainerSystem.TryGetSolution((ent, ent.Comp3), solutionName, out solutionEntity, out solution))
|
||||
return false;
|
||||
|
||||
solutionOwner = ent;
|
||||
return true;
|
||||
}
|
||||
|
||||
private void TryMetabolizeStage(Entity<MetabolizerComponent, OrganComponent?, SolutionContainerManagerComponent?> ent, ProtoId<MetabolismStagePrototype> stage)
|
||||
private void TryMetabolizeStage(Entity<MetabolizerComponent, OrganComponent?, SolutionManagerComponent?> ent, ProtoId<MetabolismStagePrototype> stage)
|
||||
{
|
||||
if (!ent.Comp1.Solutions.TryGetValue(stage, out var solutionData))
|
||||
return;
|
||||
@@ -261,9 +257,10 @@ public sealed class MetabolizerSystem : EntitySystem
|
||||
}
|
||||
}
|
||||
|
||||
private void TryMetabolize(Entity<MetabolizerComponent, OrganComponent?, SolutionContainerManagerComponent?> ent)
|
||||
private void TryMetabolize(Entity<MetabolizerComponent, OrganComponent?, SolutionManagerComponent?> ent)
|
||||
{
|
||||
_organQuery.Resolve(ent, ref ent.Comp2, logMissing: false);
|
||||
_solutionQuery.Resolve(ent, ref ent.Comp3, logMissing: false);
|
||||
|
||||
foreach (var stage in ent.Comp1.Stages)
|
||||
{
|
||||
|
||||
@@ -298,7 +298,7 @@ public sealed partial class IngestionSystem
|
||||
/// <param name="user">The entity trying to make the ingestion happening, not necessarily the one eating</param>
|
||||
/// <param name="solution">Solution we're returning</param>
|
||||
/// <param name="time">The time it takes us to eat this entity</param>
|
||||
public bool CanAccessSolution(Entity<SolutionContainerManagerComponent?> ingested,
|
||||
public bool CanAccessSolution(EntityUid ingested,
|
||||
EntityUid user,
|
||||
[NotNullWhen(true)] out Entity<SolutionComponent>? solution,
|
||||
out TimeSpan? time)
|
||||
@@ -306,19 +306,20 @@ public sealed partial class IngestionSystem
|
||||
solution = null;
|
||||
time = null;
|
||||
|
||||
if (!Resolve(ingested, ref ingested.Comp))
|
||||
{
|
||||
_popup.PopupClient(Loc.GetString("ingestion-try-use-is-empty", ("entity", ingested)), ingested, user);
|
||||
return false;
|
||||
}
|
||||
|
||||
// TODO: Relay this event to solutions using solution relay
|
||||
var ev = new EdibleEvent(user);
|
||||
RaiseLocalEvent(ingested, ref ev);
|
||||
|
||||
solution = ev.Solution;
|
||||
time = ev.Time;
|
||||
|
||||
return !ev.Cancelled && solution != null;
|
||||
if (solution == null)
|
||||
{
|
||||
_popup.PopupClient(Loc.GetString("ingestion-try-use-is-empty", ("entity", ingested)), ingested, user);
|
||||
return false;
|
||||
}
|
||||
|
||||
return !ev.Cancelled;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
|
||||
@@ -23,6 +23,7 @@ using Content.Shared.UserInterface;
|
||||
using Content.Shared.Verbs;
|
||||
using Content.Shared.Whitelist;
|
||||
using Robust.Shared.Audio.Systems;
|
||||
using Robust.Shared.Network;
|
||||
using Robust.Shared.Prototypes;
|
||||
|
||||
namespace Content.Shared.Nutrition.EntitySystems;
|
||||
@@ -66,7 +67,7 @@ public sealed partial class IngestionSystem : EntitySystem
|
||||
{
|
||||
base.Initialize();
|
||||
|
||||
SubscribeLocalEvent<EdibleComponent, ComponentInit>(OnEdibleInit);
|
||||
SubscribeLocalEvent<EdibleComponent, MapInitEvent>(OnEdibleInit);
|
||||
|
||||
// Interactions
|
||||
SubscribeLocalEvent<EdibleComponent, UseInHandEvent>(OnUseEdibleInHand, after: [typeof(OpenableSystem), typeof(InventorySystem), typeof(ActivatableUISystem)]);
|
||||
@@ -83,7 +84,7 @@ public sealed partial class IngestionSystem : EntitySystem
|
||||
|
||||
// Verbs
|
||||
SubscribeLocalEvent<EdibleComponent, GetVerbsEvent<AlternativeVerb>>(AddEdibleVerbs);
|
||||
SubscribeLocalEvent<EdibleComponent, SolutionContainerChangedEvent>(OnSolutionContainerChanged);
|
||||
SubscribeLocalEvent<EdibleComponent, SolutionChangedEvent>(OnSolutionContainerChanged);
|
||||
|
||||
// Misc
|
||||
SubscribeLocalEvent<EdibleComponent, AttemptShakeEvent>(OnAttemptShake);
|
||||
@@ -135,7 +136,7 @@ public sealed partial class IngestionSystem : EntitySystem
|
||||
return ingestionEv.Handled;
|
||||
}
|
||||
|
||||
private void OnEdibleInit(Entity<EdibleComponent> entity, ref ComponentInit args)
|
||||
private void OnEdibleInit(Entity<EdibleComponent> entity, ref MapInitEvent args)
|
||||
{
|
||||
// Beakers, Soap and other items have drainable, and we should be able to eat that solution.
|
||||
// This ensures that tests fail when you configured the yaml from and EdibleComponent uses the wrong solution,
|
||||
@@ -161,7 +162,7 @@ public sealed partial class IngestionSystem : EntitySystem
|
||||
_appearance.SetData(entity, FoodVisuals.Visual, drainAvailable.Float(), entity.Comp2);
|
||||
}
|
||||
|
||||
private void OnSolutionContainerChanged(Entity<EdibleComponent> entity, ref SolutionContainerChangedEvent args)
|
||||
private void OnSolutionContainerChanged(Entity<EdibleComponent> entity, ref SolutionChangedEvent args)
|
||||
{
|
||||
// The changes are already networked as part of the same game state.
|
||||
if (_timing.ApplyingState)
|
||||
@@ -381,7 +382,7 @@ public sealed partial class IngestionSystem : EntitySystem
|
||||
var afterEv = new IngestedEvent(args.User, entity, split, forceFed, beforeEv.Transfer >= beforeEv.Max);
|
||||
RaiseLocalEvent(food, ref afterEv);
|
||||
|
||||
_stomach.TryTransferSolution(stomachToUse.Value.Owner, split, stomachToUse);
|
||||
_stomach.TryTransferSolution((stomachToUse.Value, stomachToUse.Value.Comp), split);
|
||||
|
||||
if (!afterEv.Destroy)
|
||||
{
|
||||
|
||||
@@ -30,11 +30,10 @@ public sealed partial class PressurizedSolutionSystem : EntitySystem
|
||||
{
|
||||
base.Initialize();
|
||||
|
||||
SubscribeLocalEvent<PressurizedSolutionComponent, MapInitEvent>(OnMapInit);
|
||||
SubscribeLocalEvent<PressurizedSolutionComponent, ShakeEvent>(OnShake);
|
||||
SubscribeLocalEvent<PressurizedSolutionComponent, OpenableOpenedEvent>(OnOpened);
|
||||
SubscribeLocalEvent<PressurizedSolutionComponent, LandEvent>(OnLand);
|
||||
SubscribeLocalEvent<PressurizedSolutionComponent, SolutionContainerChangedEvent>(OnSolutionUpdate);
|
||||
SubscribeLocalEvent<PressurizedSolutionComponent, SolutionChangedEvent>(OnSolutionUpdate);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
@@ -242,10 +241,6 @@ public sealed partial class PressurizedSolutionSystem : EntitySystem
|
||||
#endregion
|
||||
|
||||
#region Event Handlers
|
||||
private void OnMapInit(Entity<PressurizedSolutionComponent> entity, ref MapInitEvent args)
|
||||
{
|
||||
RollSprayThreshold(entity);
|
||||
}
|
||||
|
||||
private void OnOpened(Entity<PressurizedSolutionComponent> entity, ref OpenableOpenedEvent args)
|
||||
{
|
||||
@@ -265,13 +260,13 @@ public sealed partial class PressurizedSolutionSystem : EntitySystem
|
||||
SprayOrAddFizziness(entity, entity.Comp.SprayChanceModOnLand, entity.Comp.FizzinessAddedOnLand);
|
||||
}
|
||||
|
||||
private void OnSolutionUpdate(Entity<PressurizedSolutionComponent> entity, ref SolutionContainerChangedEvent args)
|
||||
private void OnSolutionUpdate(Entity<PressurizedSolutionComponent> entity, ref SolutionChangedEvent args)
|
||||
{
|
||||
// The changes are already networked as part of the same game state.
|
||||
if (_timing.ApplyingState)
|
||||
return;
|
||||
|
||||
if (args.SolutionId != entity.Comp.Solution)
|
||||
if (args.Solution.Comp.Id != entity.Comp.Solution)
|
||||
return;
|
||||
|
||||
// If the solution is no longer capable of being fizzy, clear any built up fizziness
|
||||
|
||||
@@ -136,9 +136,6 @@ public sealed partial class FoodHasReagent : FoodMetamorphRule
|
||||
|
||||
public override bool Check(IPrototypeManager protoMan, EntityManager entMan, EntityUid food, List<FoodSequenceVisualLayer> ingredients)
|
||||
{
|
||||
if (!entMan.TryGetComponent<SolutionContainerManagerComponent>(food, out var solMan))
|
||||
return false;
|
||||
|
||||
var solutionMan = entMan.System<SharedSolutionContainerSystem>();
|
||||
|
||||
if (!solutionMan.TryGetSolution(food, Solution, out var foodSoln, out var foodSolution))
|
||||
|
||||
@@ -41,7 +41,7 @@ public sealed partial class WelderComponent : Component
|
||||
/// Name of the fuel solution.
|
||||
/// </summary>
|
||||
[DataField]
|
||||
public string FuelSolutionName = "Welder";
|
||||
public string FuelSolutionName = "welder";
|
||||
|
||||
/// <summary>
|
||||
/// Reagent that will be used as fuel for welding.
|
||||
|
||||
@@ -57,9 +57,9 @@ public abstract partial class SharedToolSystem
|
||||
Dirty(entity, entity.Comp);
|
||||
}
|
||||
|
||||
public (FixedPoint2 fuel, FixedPoint2 capacity) GetWelderFuelAndCapacity(EntityUid uid, WelderComponent? welder = null, SolutionContainerManagerComponent? solutionContainer = null)
|
||||
public (FixedPoint2 fuel, FixedPoint2 capacity) GetWelderFuelAndCapacity(EntityUid uid, WelderComponent? welder = null, SolutionManagerComponent? solutionContainer = null)
|
||||
{
|
||||
if (!Resolve(uid, ref welder, ref solutionContainer))
|
||||
if (!Resolve(uid, ref welder))
|
||||
return default;
|
||||
|
||||
if (!SolutionContainerSystem.TryGetSolution(
|
||||
@@ -209,7 +209,8 @@ public abstract partial class SharedToolSystem
|
||||
|
||||
private void UpdateWelders()
|
||||
{
|
||||
var query = EntityQueryEnumerator<WelderComponent, SolutionContainerManagerComponent>();
|
||||
// TODO: Same as the other EntityQueryEnumerators...
|
||||
var query = EntityQueryEnumerator<WelderComponent, SolutionManagerComponent>();
|
||||
var curTime = _timing.CurTime;
|
||||
while (query.MoveNext(out var uid, out var welder, out var solutionContainer))
|
||||
{
|
||||
|
||||
@@ -10,8 +10,8 @@ reagent-desc-buzzochloric-bees = Liquid bees. Oh god it's LIQUID BEES NO-
|
||||
reagent-name-ground-bee = ground Bee
|
||||
reagent-desc-ground-bee = Bee grounds. Gross.
|
||||
|
||||
reagent-name-saxoite = saxoite
|
||||
reagent-desc-saxoite = Smells like jazz.
|
||||
reagent-name-brass = brass
|
||||
reagent-desc-brass = Smells like clockwork.
|
||||
|
||||
reagent-name-licoxide = licoxide
|
||||
reagent-desc-licoxide = A synthetic battery acid. It looks... electrifying.
|
||||
|
||||
@@ -9,10 +9,18 @@
|
||||
- Ruminant
|
||||
- Wheat
|
||||
- BananaPeel
|
||||
- type: SolutionContainerManager
|
||||
- type: SolutionManager
|
||||
solutions:
|
||||
stomach:
|
||||
maxVol: 80
|
||||
- SolutionStomachRuminant
|
||||
|
||||
- type: entity
|
||||
parent: SolutionStomach
|
||||
id: SolutionStomachRuminant
|
||||
categories: [ HideSpawnMenu ]
|
||||
components:
|
||||
- type: Solution
|
||||
solution:
|
||||
maxVol: 240
|
||||
|
||||
- type: entity
|
||||
id: BaseMobRuminant
|
||||
|
||||
@@ -112,7 +112,7 @@
|
||||
bloodReferenceSolution:
|
||||
reagents:
|
||||
- ReagentId: CopperBlood
|
||||
Quantity: 300
|
||||
Quantity: 600
|
||||
- type: MeleeWeapon
|
||||
animation: WeaponArcBite
|
||||
soundHit:
|
||||
|
||||
@@ -101,7 +101,7 @@
|
||||
bloodReferenceSolution:
|
||||
reagents:
|
||||
- ReagentId: Sap
|
||||
Quantity: 300
|
||||
Quantity: 600
|
||||
- type: Reactive
|
||||
groups:
|
||||
Flammable: [ Touch ]
|
||||
|
||||
@@ -60,9 +60,9 @@
|
||||
bloodReferenceSolution:
|
||||
reagents:
|
||||
- ReagentId: Sugar
|
||||
Quantity: 100
|
||||
- ReagentId: Butter
|
||||
Quantity: 200
|
||||
- ReagentId: Butter
|
||||
Quantity: 400
|
||||
- type: Fixtures
|
||||
fixtures:
|
||||
fix1:
|
||||
|
||||
@@ -157,7 +157,7 @@
|
||||
bloodReferenceSolution:
|
||||
reagents:
|
||||
- ReagentId: InsectBlood
|
||||
Quantity: 300
|
||||
Quantity: 600
|
||||
- type: DamageVisuals
|
||||
damageOverlayGroups:
|
||||
Brute:
|
||||
|
||||
@@ -134,7 +134,7 @@
|
||||
bloodReferenceSolution: # TODO Color slime blood based on their slime color or smth
|
||||
reagents:
|
||||
- ReagentId: Slime
|
||||
Quantity: 300
|
||||
Quantity: 600
|
||||
- type: Barotrauma
|
||||
damage:
|
||||
types:
|
||||
@@ -255,10 +255,9 @@
|
||||
id: OrganSlimePersonCore
|
||||
name: sentient slime core
|
||||
components:
|
||||
- type: SolutionContainerManager
|
||||
- type: SolutionManager
|
||||
solutions:
|
||||
stomach:
|
||||
maxVol: 50.0
|
||||
- SolutionStomach
|
||||
- type: Stomach
|
||||
- type: Metabolizer
|
||||
maxReagents: 6
|
||||
|
||||
@@ -247,7 +247,7 @@
|
||||
bloodReferenceSolution:
|
||||
reagents:
|
||||
- ReagentId: AmmoniaBlood
|
||||
Quantity: 300
|
||||
Quantity: 600
|
||||
- type: MeleeWeapon
|
||||
soundHit:
|
||||
collection: AlienClaw
|
||||
|
||||
@@ -204,7 +204,7 @@
|
||||
bloodReferenceSolution:
|
||||
reagents:
|
||||
- ReagentId: SulfurBlood
|
||||
Quantity: 300
|
||||
Quantity: 600
|
||||
- type: CreamPied
|
||||
sprite:
|
||||
sprite: Effects/creampie.rsi
|
||||
|
||||
@@ -334,11 +334,9 @@
|
||||
- type: Lung
|
||||
- type: Metabolizer
|
||||
stages: [ Respiration ]
|
||||
- type: SolutionContainerManager
|
||||
- type: SolutionManager
|
||||
solutions:
|
||||
Lung:
|
||||
maxVol: 100.0
|
||||
canReact: false
|
||||
- SolutionLungGas
|
||||
- type: Sprite
|
||||
layers:
|
||||
- state: lung-l
|
||||
@@ -346,6 +344,17 @@
|
||||
- type: Item
|
||||
heldPrefix: lungs
|
||||
|
||||
- type: entity
|
||||
parent: Solution
|
||||
id: SolutionLungGas # TODO: Metabolize directly from gasses :(
|
||||
categories: [ HideSpawnMenu ]
|
||||
components:
|
||||
- type: Solution
|
||||
id: Lung
|
||||
solution:
|
||||
maxVol: 100.0
|
||||
canReact: false
|
||||
|
||||
- type: entity
|
||||
parent: OrganBase
|
||||
id: OrganBaseHeart
|
||||
@@ -372,10 +381,9 @@
|
||||
- type: Organ
|
||||
category: Stomach
|
||||
- type: Stomach
|
||||
- type: SolutionContainerManager
|
||||
- type: SolutionManager
|
||||
solutions:
|
||||
stomach:
|
||||
maxVol: 50
|
||||
- SolutionStomach
|
||||
- type: Metabolizer
|
||||
maxReagents: 3
|
||||
stages: [ Digestion ]
|
||||
@@ -385,6 +393,16 @@
|
||||
- type: Item
|
||||
heldPrefix: stomach
|
||||
|
||||
- type: entity
|
||||
parent: Solution
|
||||
id: SolutionStomach
|
||||
categories: [ HideSpawnMenu ]
|
||||
components:
|
||||
- type: Solution
|
||||
id: stomach
|
||||
solution:
|
||||
maxVol: 150 # 1.25 liters which is average for a human
|
||||
|
||||
- type: entity
|
||||
parent: OrganBase
|
||||
id: OrganBaseLiver
|
||||
|
||||
@@ -45,13 +45,9 @@
|
||||
storagebase: !type:AllSelector
|
||||
children:
|
||||
- id: JugBicaridine
|
||||
- id: JugPuncturase
|
||||
- id: JugDermaline
|
||||
- id: JugDylovene
|
||||
- id: JugHyronalin
|
||||
- id: JugSaline
|
||||
- id: JugDexalinPlus
|
||||
- id: JugTranexamicAcid
|
||||
- id: JugPunctTranex
|
||||
- id: JugPyraDerma
|
||||
- id: JugDexPlusSaline
|
||||
|
||||
- type: entity
|
||||
parent: ClothingBackpackDuffelSyndicateBundle
|
||||
|
||||
@@ -48,7 +48,8 @@
|
||||
- 5
|
||||
- 10
|
||||
- 15
|
||||
- 50
|
||||
- 30
|
||||
- 60
|
||||
|
||||
- type: injectorMode
|
||||
parent: [ BaseSyringeMode, BaseInjectMode ]
|
||||
@@ -66,14 +67,6 @@
|
||||
parent: [ BaseBluespaceSyringeMode, BaseDrawMode ]
|
||||
id: BluespaceSyringeDrawMode
|
||||
|
||||
- type: injectorMode
|
||||
parent: [ BaseCryostasisSyringeMode, BaseInjectMode ]
|
||||
id: CryostasisSyringeInjectMode
|
||||
|
||||
- type: injectorMode
|
||||
parent: [ BaseCryostasisSyringeMode, BaseDrawMode ]
|
||||
id: CryostasisSyringeDrawMode
|
||||
|
||||
## Dropper
|
||||
- type: injectorMode
|
||||
abstract: true
|
||||
|
||||
@@ -20,7 +20,7 @@
|
||||
type: ChameleonBoundUserInterface
|
||||
|
||||
- type: entity
|
||||
parent: [Clothing, ClothingSlotBase]
|
||||
parent: [SolutionTank, SolutionGinormous, Clothing, ClothingSlotBase]
|
||||
id: ClothingBackpackWaterTank
|
||||
name: backpack water tank
|
||||
description: Holds a large amount of fluids. Supplies to spray nozzles in your hands, and has a slot on the side for said spray nozzles.
|
||||
@@ -54,10 +54,6 @@
|
||||
- type: SolutionAmmoProvider
|
||||
solutionId: tank
|
||||
proto: BulletWaterShot
|
||||
- type: SolutionContainerManager
|
||||
solutions:
|
||||
tank:
|
||||
maxVol: 1000 #much water
|
||||
- type: SolutionTransfer
|
||||
transferAmount: 50
|
||||
maxTransferAmount: 100
|
||||
|
||||
@@ -107,8 +107,8 @@
|
||||
- WhitelistChameleon
|
||||
|
||||
- type: entity
|
||||
parent: ClothingEyesHudBase
|
||||
id: ClothingEyesHudFriedOnion
|
||||
parent: [SolutionFood, SolutionSmall, ClothingEyesHudBase]
|
||||
id: ClothingEyesHudFriedOnion # It's been too long since I've had good onion rings...
|
||||
name: fried onion goggles
|
||||
description: Filler
|
||||
components:
|
||||
@@ -118,13 +118,11 @@
|
||||
sprite: Clothing/Eyes/Hud/friedonion.rsi
|
||||
- type: ShowHungerIcons
|
||||
- type: Edible
|
||||
- type: SolutionContainerManager
|
||||
solutions:
|
||||
food:
|
||||
maxVol: 3
|
||||
reagents:
|
||||
- ReagentId: Nutriment
|
||||
Quantity: 3
|
||||
- type: Solution
|
||||
solution:
|
||||
reagents:
|
||||
- ReagentId: Nutriment
|
||||
Quantity: 30
|
||||
- type: FlavorProfile
|
||||
flavors:
|
||||
- onion
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
- type: entity
|
||||
abstract: true
|
||||
parent: Clothing
|
||||
parent: [SolutionFood, SolutionToolSmall, Clothing]
|
||||
id: ClothingHandsBase
|
||||
components:
|
||||
- type: Sprite
|
||||
@@ -12,13 +12,11 @@
|
||||
- type: Item
|
||||
size: Small
|
||||
storedRotation: -90
|
||||
- type: SolutionContainerManager
|
||||
solutions:
|
||||
food:
|
||||
maxVol: 10
|
||||
reagents:
|
||||
- ReagentId: Fiber
|
||||
Quantity: 10
|
||||
- type: Solution
|
||||
solution:
|
||||
reagents:
|
||||
- ReagentId: Fiber
|
||||
Quantity: 15
|
||||
- type: Tag
|
||||
tags:
|
||||
- ClothMade
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
- type: entity
|
||||
abstract: true
|
||||
parent: Clothing
|
||||
parent: [SolutionFood, SolutionToolSmall, Clothing]
|
||||
id: ClothingHeadBase
|
||||
components:
|
||||
- type: Clothing
|
||||
@@ -13,13 +13,11 @@
|
||||
storedRotation: -90
|
||||
- type: Edible
|
||||
requiresSpecialDigestion: true
|
||||
- type: SolutionContainerManager
|
||||
solutions:
|
||||
food:
|
||||
maxVol: 10
|
||||
reagents:
|
||||
- ReagentId: Fiber
|
||||
Quantity: 10
|
||||
- type: Solution
|
||||
solution:
|
||||
reagents:
|
||||
- ReagentId: Fiber
|
||||
Quantity: 15
|
||||
- type: Tag
|
||||
tags:
|
||||
- ClothMade
|
||||
@@ -61,7 +59,7 @@
|
||||
equippedPrefix: off
|
||||
- type: Item
|
||||
heldPrefix: off
|
||||
size: Normal
|
||||
size: Normal # I'm not updating the solution because these probably shouldn't be edible...
|
||||
- type: ToggleableVisuals
|
||||
spriteLayer: light
|
||||
- type: ItemTogglePointLight
|
||||
|
||||
@@ -302,7 +302,7 @@
|
||||
- FacialHair
|
||||
|
||||
- type: entity
|
||||
parent: Clothing
|
||||
parent: [SolutionDrink, Clothing]
|
||||
id: WaterDropletHat
|
||||
name: water droplet
|
||||
description: Makes 8-eyed friends 8 times more adorable!
|
||||
@@ -328,13 +328,12 @@
|
||||
- water
|
||||
- type: DrainableSolution
|
||||
solution: drink
|
||||
- type: SolutionContainerManager
|
||||
solutions:
|
||||
drink:
|
||||
maxVol: 2
|
||||
reagents:
|
||||
- ReagentId: Water
|
||||
Quantity: 2
|
||||
- type: Solution
|
||||
solution:
|
||||
maxVol: 2 # Droplets are pretty small.
|
||||
reagents:
|
||||
- ReagentId: Water
|
||||
Quantity: 2
|
||||
- type: DamageOnHighSpeedImpact
|
||||
minimumSpeed: 0.1
|
||||
damage:
|
||||
|
||||
@@ -32,9 +32,9 @@
|
||||
event: !type:ToggleMaskEvent
|
||||
|
||||
- type: entity
|
||||
id: ClothingMaskBaseButcherable
|
||||
parent: ClothingMaskBase
|
||||
abstract: true
|
||||
parent: [SolutionFood, SolutionToolSmall, ClothingMaskBase]
|
||||
id: ClothingMaskBaseButcherable
|
||||
components:
|
||||
- type: Butcherable
|
||||
butcheringType: Knife
|
||||
@@ -46,13 +46,11 @@
|
||||
Cloth: 50
|
||||
- type: Edible
|
||||
requiresSpecialDigestion: true
|
||||
- type: SolutionContainerManager
|
||||
solutions:
|
||||
food:
|
||||
maxVol: 10
|
||||
reagents:
|
||||
- ReagentId: Fiber
|
||||
Quantity: 10
|
||||
- type: Solution
|
||||
solution:
|
||||
reagents:
|
||||
- ReagentId: Fiber
|
||||
Quantity: 15
|
||||
- type: Tag
|
||||
tags:
|
||||
- ClothMade
|
||||
|
||||
@@ -1,9 +1,9 @@
|
||||
- type: entity
|
||||
abstract: true
|
||||
parent: [UnsensoredClothingUniformBase, ClothingHeadBase, ClothingBeltBase]
|
||||
id: BaseTowel
|
||||
name: base towel
|
||||
abstract: true
|
||||
description: If you want to survive out here, you gotta know where your towel is.
|
||||
parent: [ UnsensoredClothingUniformBase, ClothingHeadBase, ClothingBeltBase ]
|
||||
components:
|
||||
- type: Sprite
|
||||
sprite: Clothing/Multiple/towel.rsi
|
||||
@@ -21,15 +21,9 @@
|
||||
spillWhenThrown: false
|
||||
- type: Absorbent
|
||||
pickupAmount: 15
|
||||
- type: SolutionContainerManager
|
||||
- type: SolutionManager
|
||||
solutions:
|
||||
food:
|
||||
maxVol: 30
|
||||
reagents:
|
||||
- ReagentId: Fiber
|
||||
Quantity: 30
|
||||
absorbed:
|
||||
maxVol: 30
|
||||
- SolutionMopSmall
|
||||
- type: UseDelay
|
||||
delay: 1
|
||||
- type: Fiber
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
- type: entity
|
||||
abstract: true
|
||||
parent: Clothing
|
||||
parent: [SolutionFood, SolutionToolSmall, Clothing]
|
||||
id: ClothingNeckBase
|
||||
components:
|
||||
- type: Item
|
||||
@@ -21,13 +21,11 @@
|
||||
Cloth: 100
|
||||
- type: Edible
|
||||
requiresSpecialDigestion: true
|
||||
- type: SolutionContainerManager
|
||||
solutions:
|
||||
food:
|
||||
maxVol: 10
|
||||
reagents:
|
||||
- ReagentId: Fiber
|
||||
Quantity: 10
|
||||
- type: Solution
|
||||
solution:
|
||||
reagents:
|
||||
- ReagentId: Fiber
|
||||
Quantity: 15
|
||||
- type: Tag
|
||||
tags:
|
||||
- ClothMade
|
||||
|
||||
@@ -263,7 +263,7 @@
|
||||
equippedPrefix: goldautism
|
||||
|
||||
- type: entity
|
||||
parent: BaseItem
|
||||
parent: [SolutionSpray, SolutionTiny, BaseItem]
|
||||
id: SprayFlowerPin
|
||||
name: flower pin
|
||||
description: A cute flower pin. Something seems off with it...
|
||||
@@ -281,13 +281,11 @@
|
||||
- neck
|
||||
- type: EquipSpray
|
||||
verbLocId: equip-spray-verb-press
|
||||
- type: SolutionContainerManager
|
||||
solutions:
|
||||
spray:
|
||||
maxVol: 30
|
||||
reagents:
|
||||
- ReagentId: Water
|
||||
Quantity: 30
|
||||
- type: Solution
|
||||
solution:
|
||||
reagents:
|
||||
- ReagentId: Water
|
||||
Quantity: 30
|
||||
- type: RefillableSolution
|
||||
solution: spray
|
||||
- type: DrainableSolution
|
||||
|
||||
@@ -48,6 +48,19 @@
|
||||
- type: StaticPrice
|
||||
price: 70
|
||||
|
||||
- type: entity
|
||||
abstract: true
|
||||
parent: [SolutionFood, SolutionNormal, ClothingOuterStorageBase]
|
||||
id: ClothingOuterStorageEdible
|
||||
components:
|
||||
- type: Edible
|
||||
requiresSpecialDigestion: true
|
||||
- type: Solution
|
||||
solution:
|
||||
reagents:
|
||||
- ReagentId: Fiber
|
||||
Quantity: 60
|
||||
|
||||
- type: entity
|
||||
abstract: true
|
||||
parent: [ClothingOuterStorageBase, BaseFoldable]
|
||||
|
||||
@@ -123,7 +123,7 @@
|
||||
id: SyndieHandyFlag
|
||||
|
||||
- type: entity
|
||||
parent: ClothingOuterStorageBase
|
||||
parent: ClothingOuterStorageEdible
|
||||
id: ClothingOuterCoatTrench
|
||||
name: trench coat
|
||||
description: A comfy trench coat.
|
||||
@@ -139,15 +139,6 @@
|
||||
modifiers:
|
||||
coefficients:
|
||||
Slash: 0.95
|
||||
- type: Edible
|
||||
requiresSpecialDigestion: true
|
||||
- type: SolutionContainerManager
|
||||
solutions:
|
||||
food:
|
||||
maxVol: 20
|
||||
reagents:
|
||||
- ReagentId: Fiber
|
||||
Quantity: 20
|
||||
|
||||
- type: entity
|
||||
parent: ClothingOuterStorageFoldableBase
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
- type: entity
|
||||
parent: ClothingOuterStorageBase
|
||||
parent: ClothingOuterStorageEdible
|
||||
id: ClothingOuterWinterCoat
|
||||
name: winter coat
|
||||
description: A heavy jacket made from 'synthetic' animal furs.
|
||||
@@ -21,15 +21,6 @@
|
||||
priceMultiplier: 0
|
||||
- type: ZombificationResistance
|
||||
zombificationResistanceCoefficient: 0.55
|
||||
- type: Edible
|
||||
requiresSpecialDigestion: true
|
||||
- type: SolutionContainerManager
|
||||
solutions:
|
||||
food:
|
||||
maxVol: 30
|
||||
reagents:
|
||||
- ReagentId: Fiber
|
||||
Quantity: 30
|
||||
- type: Tag
|
||||
tags:
|
||||
- ClothMade
|
||||
@@ -875,13 +866,11 @@
|
||||
- cobwebs
|
||||
ignoreReagents:
|
||||
- Fiber
|
||||
- type: SolutionContainerManager
|
||||
solutions: # 15 (3 (fiber count of web) * 5 (to craft)) + 5 (magical crafting bonus)
|
||||
food:
|
||||
maxVol: 20
|
||||
reagents:
|
||||
- ReagentId: Fiber
|
||||
Quantity: 20
|
||||
- type: Solution
|
||||
solution:
|
||||
reagents:
|
||||
- ReagentId: Fiber
|
||||
Quantity: 50 # 50 (10 (fiber count of web) * 5 (to craft))
|
||||
- type: ToggleableClothing
|
||||
clothingPrototype: ClothingHeadHatHoodWinterWeb
|
||||
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
- type: entity
|
||||
abstract: true
|
||||
parent: Clothing
|
||||
parent: [SolutionFood, SolutionToolNormal, Clothing]
|
||||
id: ClothingShoesBase
|
||||
components:
|
||||
- type: Clothing
|
||||
@@ -12,13 +12,11 @@
|
||||
size: Normal
|
||||
- type: Edible
|
||||
requiresSpecialDigestion: true
|
||||
- type: SolutionContainerManager
|
||||
solutions:
|
||||
food:
|
||||
maxVol: 10
|
||||
reagents:
|
||||
- ReagentId: Fiber
|
||||
Quantity: 10
|
||||
- type: Solution
|
||||
solution:
|
||||
reagents:
|
||||
- ReagentId: Fiber
|
||||
Quantity: 30
|
||||
- type: Tag
|
||||
tags:
|
||||
- ClothMade
|
||||
|
||||
@@ -190,13 +190,11 @@
|
||||
- cobwebs
|
||||
ignoreReagents:
|
||||
- Fiber
|
||||
- type: SolutionContainerManager
|
||||
solutions: # 6 (3 (fiber count of web) * 2 (to craft)) + 4 (magical crafting bonus)
|
||||
food:
|
||||
maxVol: 10
|
||||
reagents:
|
||||
- ReagentId: Fiber
|
||||
Quantity: 10
|
||||
- type: Solution
|
||||
solution: # 20 (10 (fiber count of web) * 2 (to craft))
|
||||
reagents:
|
||||
- ReagentId: Fiber
|
||||
Quantity: 20
|
||||
- type: Butcherable
|
||||
spawned:
|
||||
- id: MaterialWebSilk1
|
||||
|
||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user