Add basic chemical reactions (#376)

* Add basic chemical reaction system

What it adds:
- Reactions defined in yaml with an arbitrary amount of reactants (can be catalysts), products, and effects.

What it doesn't add:
- Temperature dependent reactions
- Metabolism or other medical/health effects

* Add many common SS13 chemicals and reactions

Added many of the common SS13 medicines and other chemicals, and their chemical reactions. Note that many of them are lacking their effects since we don't have medical yet.

* Add ExplosiveReactionEffect

Shows how IReactionEffect can be implemented to have effects that occur with a reaction by adding ExplosionReactionEffect and the potassium + water explosion reaction.

* Move ReactionSystem logic into SolutionComponent

No need for this to be a system currently so the behavior for reaction checking has been moved into SolutionComponent. Now it only checks for reactions when a reagent or solution is added to a solution. Also fixed a bug with SolutionValidReaction incorrectly returning true.

* Move explosion logic out of ExplosiveComponent

Allows you to create explosions without needing to add an ExplosiveComponent to an entity first.

* Add SolutionComponent.SolutionChanged event. Trigger dispenser ui updates with it.

This removes the need for SolutionComponent having a reference to the dispenser it's in. Instead the dispenser subscribes to the event and updates it's UI whenever the event is triggered.

* Add forgotten checks

`SolutionComponent.TryAddReagent` and `SolutionComponent.TryAddSolution` now check to see if `skipReactionCheck` is false before checking for a chemical reaction to avoid unnecessarily checking themselves for a reaction again when `SolutionComponent.PerformReaction` calls either of them.

* Change SolutionComponent.SolutionChanged to an Action

The arguments for event handler have no use here, and any class that can access SolutionChanged can access the SolutionComponent so it can just be an Action with no arguments instead.
This commit is contained in:
moneyl
2019-10-11 16:57:16 -04:00
committed by Pieter-Jan Briers
parent bd5a4e33ab
commit 427836fec9
16 changed files with 1498 additions and 166 deletions

View File

@@ -129,6 +129,7 @@ namespace Content.Client
factory.Register<SharedReagentDispenserComponent>();
prototypes.RegisterIgnore("material");
prototypes.RegisterIgnore("reaction"); //Chemical reactions only needed by server. Reactions checks are server-side.
IoCManager.Register<IGameHud, GameHud>();
IoCManager.Register<IClientNotifyManager, ClientNotifyManager>();

View File

@@ -0,0 +1,70 @@
using System;
using Content.Server.Explosions;
using Content.Server.GameObjects.Components.Explosive;
using Content.Server.GameObjects.EntitySystems;
using Content.Server.GameObjects.Components.Chemistry;
using Content.Shared.Interfaces;
using Robust.Shared.Interfaces.GameObjects;
using Robust.Shared.IoC;
using Robust.Shared.Serialization;
namespace Content.Server.Chemistry
{
class ExplosionReactionEffect : IReactionEffect
{
private float _devastationRange;
private float _heavyImpactRange;
private float _lightImpactRange;
private float _flashRange;
/// <summary>
/// If true, then scale ranges by intensity. If not, the ranges are the same regardless of reactant amount.
/// </summary>
private bool _scaled;
/// <summary>
/// Maximum scaling on ranges. For example, if it equals 5, then it won't scaled anywhere past
/// 5 times the minimum reactant amount.
/// </summary>
private float _maxScale;
public void ExposeData(ObjectSerializer serializer)
{
serializer.DataField(ref _devastationRange, "devastationRange", 1);
serializer.DataField(ref _heavyImpactRange, "heavyImpactRange", 2);
serializer.DataField(ref _lightImpactRange, "lightImpactRange", 3);
serializer.DataField(ref _flashRange, "flashRange", 0);
serializer.DataField(ref _scaled, "scaled", false);
serializer.DataField(ref _maxScale, "maxScale", 1);
}
public void React(IEntity solutionEntity, int intensity)
{
float floatIntensity = intensity; //Use float to avoid truncation in scaling
if (solutionEntity == null)
return;
if(!solutionEntity.TryGetComponent(out SolutionComponent solution))
return;
//Handle scaling
if (_scaled)
{
floatIntensity = Math.Min(floatIntensity, _maxScale);
}
else
{
floatIntensity = 1;
}
//Calculate intensities
int finalDevastationRange = (int)Math.Round(_devastationRange * floatIntensity);
int finalHeavyImpactRange = (int)Math.Round(_heavyImpactRange * floatIntensity);
int finalLightImpactRange = (int)Math.Round(_lightImpactRange * floatIntensity);
int finalFlashRange = (int)Math.Round(_flashRange * floatIntensity);
ExplosionHelper.SpawnExplosion(solutionEntity.Transform.GridPosition, finalDevastationRange,
finalHeavyImpactRange, finalLightImpactRange, finalFlashRange);
}
}
}

View File

@@ -0,0 +1,72 @@
using System.Collections.Generic;
using Content.Shared.Interfaces;
using Robust.Shared.Interfaces.Serialization;
using Robust.Shared.Prototypes;
using Robust.Shared.Serialization;
using YamlDotNet.RepresentationModel;
namespace Content.Server.Chemistry
{
/// <summary>
/// Prototype for chemical reaction definitions
/// </summary>
[Prototype("reaction")]
public class ReactionPrototype : IPrototype, IIndexedPrototype
{
private string _id;
private string _name;
private Dictionary<string, ReactantPrototype> _reactants;
private Dictionary<string, uint> _products;
private List<IReactionEffect> _effects;
public string ID => _id;
public string Name => _name;
/// <summary>
/// Reactants required for the reaction to occur.
/// </summary>
public IReadOnlyDictionary<string, ReactantPrototype> Reactants => _reactants;
/// <summary>
/// Reagents created when the reaction occurs.
/// </summary>
public IReadOnlyDictionary<string, uint> Products => _products;
/// <summary>
/// Effects to be triggered when the reaction occurs.
/// </summary>
public IReadOnlyList<IReactionEffect> Effects => _effects;
public void LoadFrom(YamlMappingNode mapping)
{
var serializer = YamlObjectSerializer.NewReader(mapping);
serializer.DataField(ref _id, "id", string.Empty);
serializer.DataField(ref _name, "name", string.Empty);
serializer.DataField(ref _reactants, "reactants", new Dictionary<string, ReactantPrototype>());
serializer.DataField(ref _products, "products", new Dictionary<string, uint>());
serializer.DataField(ref _effects, "effects", new List<IReactionEffect>());
}
}
/// <summary>
/// Prototype for chemical reaction reactants.
/// </summary>
public class ReactantPrototype : IExposeData
{
private int _amount;
private bool _catalyst;
/// <summary>
/// Minimum amount of the reactant needed for the reaction to occur.
/// </summary>
public int Amount => _amount;
/// <summary>
/// Whether or not the reactant is a catalyst. Catalysts aren't removed when a reaction occurs.
/// </summary>
public bool Catalyst => _catalyst;
public void ExposeData(ObjectSerializer serializer)
{
serializer.DataField(ref _amount, "amount", 1);
serializer.DataField(ref _catalyst, "catalyst", false);
}
}
}

View File

@@ -0,0 +1,148 @@
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
using Content.Server.GameObjects.Components.Mobs;
using Content.Server.GameObjects.EntitySystems;
using Content.Shared.Maps;
using Robust.Server.GameObjects.EntitySystems;
using Robust.Server.Interfaces.GameObjects;
using Robust.Server.Interfaces.Player;
using Robust.Shared.GameObjects.EntitySystemMessages;
using Robust.Shared.Interfaces.GameObjects;
using Robust.Shared.Interfaces.Map;
using Robust.Shared.Interfaces.Random;
using Robust.Shared.Interfaces.Timing;
using Robust.Shared.IoC;
using Robust.Shared.Map;
using Robust.Shared.Maths;
using Robust.Shared.Random;
namespace Content.Server.Explosions
{
public static class ExplosionHelper
{
public static void SpawnExplosion(GridCoordinates coords, int devastationRange, int heavyImpactRange, int lightImpactRange, int flashRange)
{
var tileDefinitionManager = IoCManager.Resolve<ITileDefinitionManager>();
var serverEntityManager = IoCManager.Resolve<IServerEntityManager>();
var entitySystemManager = IoCManager.Resolve<IEntitySystemManager>();
var mapManager = IoCManager.Resolve<IMapManager>();
var robustRandom = IoCManager.Resolve<IRobustRandom>();
var maxRange = MathHelper.Max(devastationRange, heavyImpactRange, lightImpactRange, 0f);
//Entity damage calculation
var entitiesAll = serverEntityManager.GetEntitiesInRange(coords, maxRange).ToList();
foreach (var entity in entitiesAll)
{
//if (entity == Owner)
// continue;
if (!entity.Transform.IsMapTransform)
continue;
var distanceFromEntity = (int)entity.Transform.GridPosition.Distance(mapManager, coords);
var exAct = entitySystemManager.GetEntitySystem<ActSystem>();
var severity = ExplosionSeverity.Destruction;
if (distanceFromEntity < devastationRange)
{
severity = ExplosionSeverity.Destruction;
}
else if (distanceFromEntity < heavyImpactRange)
{
severity = ExplosionSeverity.Heavy;
}
else if (distanceFromEntity < lightImpactRange)
{
severity = ExplosionSeverity.Light;
}
else
{
continue;
}
//exAct.HandleExplosion(Owner, entity, severity);
exAct.HandleExplosion(null, entity, severity);
}
//Tile damage calculation mockup
//TODO: make it into some sort of actual damage component or whatever the boys think is appropriate
var mapGrid = mapManager.GetGrid(coords.GridID);
var circle = new Circle(coords.Position, maxRange);
var tiles = mapGrid.GetTilesIntersecting(circle);
foreach (var tile in tiles)
{
var tileLoc = mapGrid.GridTileToLocal(tile.GridIndices);
var tileDef = (ContentTileDefinition)tileDefinitionManager[tile.Tile.TypeId];
var distanceFromTile = (int)tileLoc.Distance(mapManager, coords);
if (!string.IsNullOrWhiteSpace(tileDef.SubFloor))
{
if (distanceFromTile < devastationRange)
mapGrid.SetTile(tileLoc, new Tile(tileDefinitionManager["space"].TileId));
if (distanceFromTile < heavyImpactRange)
{
if (robustRandom.Prob(80))
{
mapGrid.SetTile(tileLoc, new Tile(tileDefinitionManager[tileDef.SubFloor].TileId));
}
else
{
mapGrid.SetTile(tileLoc, new Tile(tileDefinitionManager["space"].TileId));
}
}
if (distanceFromTile < lightImpactRange)
{
if (robustRandom.Prob(50))
{
mapGrid.SetTile(tileLoc, new Tile(tileDefinitionManager[tileDef.SubFloor].TileId));
}
}
}
}
//Effects and sounds
var time = IoCManager.Resolve<IGameTiming>().CurTime;
var message = new EffectSystemMessage
{
EffectSprite = "Effects/explosion.rsi",
RsiState = "explosionfast",
Born = time,
DeathTime = time + TimeSpan.FromSeconds(5),
Size = new Vector2(flashRange / 2, flashRange / 2),
Coordinates = coords,
//Rotated from east facing
Rotation = 0f,
ColorDelta = new Vector4(0, 0, 0, -1500f),
Color = Vector4.Multiply(new Vector4(255, 255, 255, 750), 0.5f),
Shaded = false
};
entitySystemManager.GetEntitySystem<EffectSystem>().CreateParticle(message);
entitySystemManager.GetEntitySystem<AudioSystem>().Play("/Audio/effects/explosion.ogg", coords);
// Knock back cameras of all players in the area.
var playerManager = IoCManager.Resolve<IPlayerManager>();
//var selfPos = Owner.Transform.WorldPosition; //vec2
var selfPos = coords.ToWorld(mapManager).Position;
foreach (var player in playerManager.GetAllPlayers())
{
if (player.AttachedEntity == null
|| player.AttachedEntity.Transform.MapID != mapGrid.ParentMapId
|| !player.AttachedEntity.TryGetComponent(out CameraRecoilComponent recoil))
{
continue;
}
var playerPos = player.AttachedEntity.Transform.WorldPosition;
var delta = selfPos - playerPos;
var distance = delta.LengthSquared;
var effect = 1 / (1 + 0.2f * distance);
if (effect > 0.01f)
{
var kick = -delta.Normalized * effect;
recoil.Kick(kick);
}
}
}
}
}

View File

@@ -9,7 +9,6 @@ using Robust.Server.GameObjects.Components.Container;
using Robust.Server.GameObjects.Components.UserInterface;
using Robust.Server.Interfaces.GameObjects;
using Robust.Shared.GameObjects;
using Robust.Shared.Interfaces.GameObjects;
using Robust.Shared.IoC;
using Robust.Shared.Localization;
using Robust.Shared.Prototypes;
@@ -40,6 +39,8 @@ namespace Content.Server.GameObjects.Components.Chemistry
public bool HasBeaker => _beakerContainer.ContainedEntity != null;
public int DispenseAmount = 10;
private SolutionComponent _solution => _beakerContainer.ContainedEntity.GetComponent<SolutionComponent>();
/// <summary>
/// Shows the serializer how to save/load this components yaml prototype.
/// </summary>
@@ -153,7 +154,7 @@ namespace Content.Server.GameObjects.Components.Chemistry
/// <summary>
/// Gets current component data as a <see cref="SharedReagentDispenserComponent.ReagentDispenserBoundUserInterfaceState"/> and sends it to the client.
/// </summary>
private void UpdateUserInterface()
public void UpdateUserInterface()
{
var state = GetUserInterfaceState();
_userInterface.SetState(state);
@@ -162,9 +163,10 @@ namespace Content.Server.GameObjects.Components.Chemistry
/// <summary>
/// If this component contains an entity with a <see cref="SolutionComponent"/>, eject it.
/// </summary>
private void TryEject()
public void TryEject()
{
if(!HasBeaker) return;
_solution.SolutionChanged -= HandleSolutionChangedEvent;
_beakerContainer.Remove(_beakerContainer.ContainedEntity);
UpdateUserInterface();
@@ -253,6 +255,7 @@ namespace Content.Server.GameObjects.Components.Chemistry
else
{
_beakerContainer.Insert(activeHandEntity);
_solution.SolutionChanged += HandleSolutionChangedEvent;
UpdateUserInterface();
}
}
@@ -264,5 +267,10 @@ namespace Content.Server.GameObjects.Components.Chemistry
return true;
}
void HandleSolutionChangedEvent()
{
UpdateUserInterface();
}
}
}

View File

@@ -1,7 +1,10 @@
using System;
using System.Collections.Generic;
using Content.Server.Chemistry;
using Content.Server.GameObjects.EntitySystems;
using Content.Shared.Chemistry;
using Content.Shared.GameObjects;
using Robust.Server.GameObjects.EntitySystems;
using Robust.Shared.GameObjects;
using Robust.Shared.Interfaces.GameObjects;
using Robust.Shared.IoC;
@@ -20,8 +23,20 @@ namespace Content.Server.GameObjects.Components.Chemistry
#pragma warning disable 649
[Dependency] private readonly IPrototypeManager _prototypeManager;
[Dependency] private readonly ILocalizationManager _localizationManager;
[Dependency] private readonly IEntitySystemManager _entitySystemManager;
#pragma warning restore 649
private IEnumerable<ReactionPrototype> _reactions;
private AudioSystem _audioSystem;
protected override void Startup()
{
base.Startup();
_reactions = _prototypeManager.EnumeratePrototypes<ReactionPrototype>();
_audioSystem = _entitySystemManager.GetEntitySystem<AudioSystem>();
}
/// <summary>
/// Transfers solution from the held container to the target container.
/// </summary>
@@ -164,5 +179,116 @@ namespace Content.Server.GameObjects.Components.Chemistry
handSolutionComp.TryAddSolution(transferSolution);
}
}
private void CheckForReaction()
{
//Check the solution for every reaction
foreach (var reaction in _reactions)
{
if (SolutionValidReaction(reaction, out int unitReactions))
{
PerformReaction(reaction, unitReactions);
break; //Only perform one reaction per solution per update.
}
}
}
public bool TryAddReagent(string reagentId, int quantity, out int acceptedQuantity, bool skipReactionCheck = false)
{
if (quantity > _maxVolume - _containedSolution.TotalVolume)
{
acceptedQuantity = _maxVolume - _containedSolution.TotalVolume;
if (acceptedQuantity == 0) return false;
}
else
{
acceptedQuantity = quantity;
}
_containedSolution.AddReagent(reagentId, acceptedQuantity);
RecalculateColor();
if(!skipReactionCheck)
CheckForReaction();
OnSolutionChanged();
return true;
}
public bool TryAddSolution(Solution solution, bool skipReactionCheck = false)
{
if (solution.TotalVolume > (_maxVolume - _containedSolution.TotalVolume))
return false;
_containedSolution.AddSolution(solution);
RecalculateColor();
if(!skipReactionCheck)
CheckForReaction();
OnSolutionChanged();
return true;
}
/// <summary>
/// Checks if a solution has the reactants required to cause a specified reaction.
/// </summary>
/// <param name="solution">The solution to check for reaction conditions.</param>
/// <param name="reaction">The reaction whose reactants will be checked for in the solution.</param>
/// <param name="unitReactions">The number of times the reaction can occur with the given solution.</param>
/// <returns></returns>
private bool SolutionValidReaction(ReactionPrototype reaction, out int unitReactions)
{
unitReactions = int.MaxValue; //Set to some impossibly large number initially
foreach (var reactant in reaction.Reactants)
{
if (!ContainsReagent(reactant.Key, out int reagentQuantity))
{
return false;
}
int currentUnitReactions = reagentQuantity / reactant.Value.Amount;
if (currentUnitReactions < unitReactions)
{
unitReactions = currentUnitReactions;
}
}
if (unitReactions == 0)
{
return false;
}
else
{
return true;
}
}
/// <summary>
/// Perform a reaction on a solution. This assumes all reaction criteria have already been checked and are met.
/// </summary>
/// <param name="solution">Solution to be reacted.</param>
/// <param name="reaction">Reaction to occur.</param>
/// <param name="unitReactions">The number of times to cause this reaction.</param>
private void PerformReaction(ReactionPrototype reaction, int unitReactions)
{
//Remove non-catalysts
foreach (var reactant in reaction.Reactants)
{
if (!reactant.Value.Catalyst)
{
int amountToRemove = unitReactions * reactant.Value.Amount;
TryRemoveReagent(reactant.Key, amountToRemove);
}
}
//Add products
foreach (var product in reaction.Products)
{
TryAddReagent(product.Key, (int)(unitReactions * product.Value), out int acceptedQuantity, true);
}
//Trigger reaction effects
foreach (var effect in reaction.Effects)
{
effect.React(Owner, unitReactions);
}
//Play reaction sound client-side
_audioSystem.Play("/Audio/effects/chemistry/bubbles.ogg", Owner.Transform.GridPosition);
}
}
}

View File

@@ -1,22 +1,12 @@
using System;
using System.Linq;
using Content.Server.GameObjects.Components.Mobs;
using Content.Server.GameObjects.EntitySystems;
using Content.Shared.Maps;
using Robust.Server.GameObjects.EntitySystems;
using Content.Server.GameObjects.EntitySystems;
using Robust.Server.Interfaces.GameObjects;
using Robust.Server.Interfaces.Player;
using Robust.Shared.GameObjects;
using Robust.Shared.GameObjects.EntitySystemMessages;
using Robust.Shared.Interfaces.GameObjects;
using Robust.Shared.Interfaces.Map;
using Robust.Shared.Interfaces.Random;
using Robust.Shared.Interfaces.Timing;
using Robust.Shared.IoC;
using Robust.Shared.Map;
using Robust.Shared.Maths;
using Robust.Shared.Random;
using Robust.Shared.Serialization;
using Content.Server.Explosions;
namespace Content.Server.GameObjects.Components.Explosive
{
@@ -50,121 +40,13 @@ namespace Content.Server.GameObjects.Components.Explosive
serializer.DataField(ref FlashRange, "flashRange", 0);
}
private bool Explosion()
public bool Explosion()
{
//Prevent adjacent explosives from infinitely blowing each other up.
if (_beingExploded) return true;
_beingExploded = true;
var maxRange = MathHelper.Max(DevastationRange, HeavyImpactRange, LightImpactRange, 0f);
//Entity damage calculation
var entitiesAll = _serverEntityManager.GetEntitiesInRange(Owner.Transform.GridPosition, maxRange).ToList();
foreach (var entity in entitiesAll)
{
if (entity == Owner)
continue;
if (!entity.Transform.IsMapTransform)
continue;
var distanceFromEntity = (int)entity.Transform.GridPosition.Distance(_mapManager, Owner.Transform.GridPosition);
var exAct = _entitySystemManager.GetEntitySystem<ActSystem>();
var severity = ExplosionSeverity.Destruction;
if (distanceFromEntity < DevastationRange)
{
severity = ExplosionSeverity.Destruction;
}
else if (distanceFromEntity < HeavyImpactRange)
{
severity = ExplosionSeverity.Heavy;
}
else if (distanceFromEntity < LightImpactRange)
{
severity = ExplosionSeverity.Light;
}
else
{
continue;
}
exAct.HandleExplosion(Owner, entity, severity);
}
//Tile damage calculation mockup
//TODO: make it into some sort of actual damage component or whatever the boys think is appropriate
var mapGrid = _mapManager.GetGrid(Owner.Transform.GridPosition.GridID);
var circle = new Circle(Owner.Transform.GridPosition.Position, maxRange);
var tiles = mapGrid.GetTilesIntersecting(circle);
foreach (var tile in tiles)
{
var tileLoc = mapGrid.GridTileToLocal(tile.GridIndices);
var tileDef = (ContentTileDefinition)_tileDefinitionManager[tile.Tile.TypeId];
var distanceFromTile = (int)tileLoc.Distance(_mapManager, Owner.Transform.GridPosition);
if (!string.IsNullOrWhiteSpace(tileDef.SubFloor)) {
if (distanceFromTile < DevastationRange)
mapGrid.SetTile(tileLoc, new Tile(_tileDefinitionManager["space"].TileId));
if (distanceFromTile < HeavyImpactRange)
{
if (_robustRandom.Prob(80))
{
mapGrid.SetTile(tileLoc, new Tile(_tileDefinitionManager[tileDef.SubFloor].TileId));
}
else
{
mapGrid.SetTile(tileLoc, new Tile(_tileDefinitionManager["space"].TileId));
}
}
if (distanceFromTile < LightImpactRange)
{
if (_robustRandom.Prob(50))
{
mapGrid.SetTile(tileLoc, new Tile(_tileDefinitionManager[tileDef.SubFloor].TileId));
}
}
}
}
//Effects and sounds
var time = IoCManager.Resolve<IGameTiming>().CurTime;
var message = new EffectSystemMessage
{
EffectSprite = "Effects/explosion.rsi",
RsiState = "explosionfast",
Born = time,
DeathTime = time + TimeSpan.FromSeconds(5),
Size = new Vector2(FlashRange / 2, FlashRange / 2),
Coordinates = Owner.Transform.GridPosition,
//Rotated from east facing
Rotation = 0f,
ColorDelta = new Vector4(0, 0, 0, -1500f),
Color = Vector4.Multiply(new Vector4(255, 255, 255, 750), 0.5f),
Shaded = false
};
_entitySystemManager.GetEntitySystem<EffectSystem>().CreateParticle(message);
_entitySystemManager.GetEntitySystem<AudioSystem>().Play("/Audio/effects/explosion.ogg", Owner);
// Knock back cameras of all players in the area.
var playerManager = IoCManager.Resolve<IPlayerManager>();
var selfPos = Owner.Transform.WorldPosition;
foreach (var player in playerManager.GetAllPlayers())
{
if (player.AttachedEntity == null
|| player.AttachedEntity.Transform.MapID != mapGrid.ParentMapId
|| !player.AttachedEntity.TryGetComponent(out CameraRecoilComponent recoil))
{
continue;
}
var playerPos = player.AttachedEntity.Transform.WorldPosition;
var delta = selfPos - playerPos;
var distance = delta.LengthSquared;
var effect = 1 / (1 + 0.2f * distance);
if (effect > 0.01f)
{
var kick = -delta.Normalized * effect;
recoil.Kick(kick);
}
}
ExplosionHelper.SpawnExplosion(Owner.Transform.GridPosition, DevastationRange, HeavyImpactRange, LightImpactRange, FlashRange);
Owner.Delete();
return true;

View File

@@ -0,0 +1,13 @@
using Robust.Shared.Interfaces.GameObjects;
using Robust.Shared.Interfaces.Serialization;
namespace Content.Shared.Interfaces
{
/// <summary>
/// Chemical reaction effect on the world such as an explosion, EMP, or fire.
/// </summary>
public interface IReactionEffect : IExposeData
{
void React(IEntity solutionEntity, int intensity );
}
}

View File

@@ -17,10 +17,15 @@ namespace Content.Shared.GameObjects.Components.Chemistry
#pragma warning restore 649
[ViewVariables]
private Solution _containedSolution;
private int _maxVolume;
protected Solution _containedSolution;
protected int _maxVolume;
private SolutionCaps _capabilities;
/// <summary>
/// Triggered when the solution contents change.
/// </summary>
public event Action SolutionChanged;
/// <summary>
/// The maximum volume of the container.
/// </summary>
@@ -94,41 +99,35 @@ namespace Content.Shared.GameObjects.Components.Chemistry
public void RemoveAllSolution()
{
_containedSolution.RemoveAllSolution();
OnSolutionChanged();
}
public bool TryAddReagent(string reagentId, int quantity, out int acceptedQuantity)
public bool TryRemoveReagent(string reagentId, int quantity)
{
if (quantity > _maxVolume - _containedSolution.TotalVolume)
{
acceptedQuantity = _maxVolume - _containedSolution.TotalVolume;
if (acceptedQuantity == 0) return false;
}
else
{
acceptedQuantity = quantity;
}
if (!ContainsReagent(reagentId, out var currentQuantity)) return false;
_containedSolution.AddReagent(reagentId, acceptedQuantity);
RecalculateColor();
_containedSolution.RemoveReagent(reagentId, quantity);
OnSolutionChanged();
return true;
}
public bool TryAddSolution(Solution solution)
public bool TryRemoveSolution(int quantity)
{
if (solution.TotalVolume > (_maxVolume - _containedSolution.TotalVolume))
return false;
if (CurrentVolume == 0) return false;
_containedSolution.AddSolution(solution);
RecalculateColor();
_containedSolution.RemoveSolution(quantity);
OnSolutionChanged();
return true;
}
public Solution SplitSolution(int quantity)
{
return _containedSolution.SplitSolution(quantity);
var solutionSplit = _containedSolution.SplitSolution(quantity);
OnSolutionChanged();
return solutionSplit;
}
private void RecalculateColor()
protected void RecalculateColor()
{
if(_containedSolution.TotalVolume == 0)
SubstanceColor = Color.White;
@@ -184,5 +183,30 @@ namespace Content.Shared.GameObjects.Components.Chemistry
{
public SolutionComponentState() : base(ContentNetIDs.SOLUTION) { }
}
/// <summary>
/// Check if the solution contains the specified reagent.
/// </summary>
/// <param name="reagentId">The reagent to check for.</param>
/// <param name="quantity">Output the quantity of the reagent if it is contained, 0 if it isn't.</param>
/// <returns>Return true if the solution contains the reagent.</returns>
public bool ContainsReagent(string reagentId, out int quantity)
{
foreach (var reagent in _containedSolution.Contents)
{
if (reagent.ReagentId == reagentId)
{
quantity = reagent.Quantity;
return true;
}
}
quantity = 0;
return false;
}
protected virtual void OnSolutionChanged()
{
SolutionChanged?.Invoke();
}
}
}

Binary file not shown.

View File

@@ -31,14 +31,24 @@
- type: reagentDispenserInventory
id: ChemDispenserStandardInventory
inventory:
- chem.H2
- chem.O2
- chem.S8
- chem.C
- chem.Cu
- chem.N2
- chem.Fe
- chem.F2
- chem.Al
- chem.H2SO4
- chem.H2O
- chem.Ethanol
- chem.Glucose
- chem.H
- chem.O
- chem.S
- chem.C
- chem.Cu
- chem.N
- chem.Fe
- chem.F
- chem.Al
- chem.Si
- chem.Cl
- chem.Li
- chem.Hg
- chem.P
- chem.K
- chem.Ra
- chem.Na

View File

@@ -0,0 +1,136 @@
- type: reaction
id: react.H2O
reactants:
chem.H:
amount: 1
chem.O:
amount: 1
products:
chem.H2O: 2
- type: reaction
id: react.Ammonia
reactants:
chem.H:
amount: 3
chem.N:
amount: 1
products:
chem.Ammonia: 4
- type: reaction
id: react.Bleach
reactants:
chem.TableSalt:
amount: 2
chem.SpaceCleaner:
amount: 2
chem.O:
amount: 1
products:
chem.Bleach: 5
- type: reaction
id: react.Diethylamine
reactants:
chem.Ammonia:
amount: 1
chem.Ethanol:
amount: 1
products:
chem.Diethylamine: 2
- type: reaction
id: react.FoamingAgent
reactants:
chem.Li:
amount: 1
chem.H:
amount: 1
products:
chem.FoamingAgent: 2
- type: reaction
id: react.PolytrinicAcid
reactants:
chem.H2SO4:
amount: 1
chem.Cl:
amount: 1
chem.K:
amount: 1
products:
chem.PolytrinicAcid: 3
- type: reaction
id: react.SpaceCleaner
reactants:
chem.Ammonia:
amount: 1
chem.H2O:
amount: 1
products:
chem.SpaceCleaner: 2
- type: reaction
id: react.SpaceLube
reactants:
chem.H2O:
amount: 1
chem.Si:
amount: 1
chem.O:
amount: 1
products:
chem.SpaceLube: 3
- type: reaction
id: react.TableSalt
reactants:
chem.Cl:
amount: 1
chem.Na:
amount: 1
products:
chem.TableSalt: 2
- type: reaction
id: react.Thermite
reactants:
chem.Fe:
amount: 1
chem.Al:
amount: 1
chem.O:
amount: 1
products:
chem.Thermite: 3
- type: reaction
id: react.UnstableMutagen
reactants:
chem.Ra:
amount: 1
chem.P:
amount: 1
chem.Cl:
amount: 1
products:
chem.UnstableMutagen: 3
- type: reaction
id: react.PotassiumExplosion
reactants:
chem.H2O:
amount: 1
chem.K:
amount: 1
effects:
- !type:ExplosionReactionEffect
#Ranges used when 1 potassium + 1 water react (A unit reaction)
devastationRange: 0.05
heavyImpactRange: 0.1
lightImpactRange: 0.15
flashRange: 0.2
scaled: true #Scaled proportionally to amount of potassium and water
maxScale: 30 #Explosion strength stops scaling at 30 potassium + 30 water

View File

@@ -0,0 +1,471 @@
- type: reaction
id: react.Alkycosine
reactants:
chem.Alkysine:
amount: 1
chem.Bleach:
amount: 1
chem.Dylovene:
amount: 1
products:
chem.Alkycosine: 3
- type: reaction
id: react.Alkysine
reactants:
chem.Cl:
amount: 1
chem.N:
amount: 1
chem.Dylovene:
amount: 1
products:
chem.Alkysine: 3
- type: reaction
id: react.Dylovene
reactants:
chem.Si:
amount: 1
chem.N:
amount: 1
chem.K:
amount: 1
products:
chem.Dylovene: 3
- type: reaction
id: react.Arithrazine
reactants:
chem.Hyronalin:
amount: 1
chem.H:
amount: 1
products:
chem.Arithrazine: 2
- type: reaction
id: react.Bicaridine
reactants:
chem.Inaprovaline:
amount: 1
chem.C:
amount: 1
products:
chem.Bicaridine: 2
- type: reaction
id: react.Cryoxadone
reactants:
chem.Dexalin:
amount: 1
chem.H2O:
amount: 1
chem.O:
amount: 1
products:
chem.Cryoxadone: 3
- type: reaction
id: react.Clonexadone
reactants:
chem.Cryoxadone:
amount: 1
chem.Na:
amount: 1
chem.Plasma:
amount: 5
catalyst: true
products:
chem.Clonexadone: 2
- type: reaction
id: react.Citalopram
reactants:
chem.MindbreakerToxin:
amount: 5
chem.C:
amount: 5
products:
chem.Citalopram: 10
- type: reaction
id: react.Dermaline
reactants:
chem.Kelotane:
amount: 1
chem.O:
amount: 1
chem.P:
amount: 1
products:
chem.Dermaline: 3
- type: reaction
id: react.Dexalin
reactants:
chem.O:
amount: 2
chem.Plasma:
amount: 5
catalyst: true
products:
chem.Dexalin: 3
- type: reaction
id: react.DexalinPlus
reactants:
chem.Dexalin:
amount: 1
chem.C:
amount: 1
chem.Fe:
amount: 1
products:
chem.DexalinPlus: 3
- type: reaction
id: react.Ethylredoxrazine
reactants:
chem.O:
amount: 1
chem.Dylovene:
amount: 1
chem.C:
amount: 1
products:
chem.Ethylredoxrazine: 3
- type: reaction
id: react.Hyperzine
reactants:
chem.Glucose:
amount: 1
chem.P:
amount: 1
chem.S:
amount: 1
products:
chem.Hyperzine: 3
- type: reaction
id: react.Hyronalin
reactants:
chem.Ra:
amount: 1
chem.Dylovene:
amount: 1
products:
chem.Hyronalin: 2
- type: reaction
id: react.Imidazoline
reactants:
chem.H:
amount: 1
chem.C:
amount: 1
chem.Dylovene:
amount: 1
products:
chem.Imidazoline: 3
- type: reaction
id: react.Inacusiate
reactants:
chem.Dylovene:
amount: 1
chem.C:
amount: 1
chem.H2O:
amount: 1
products:
chem.Inacusiate: 3
- type: reaction
id: react.Inaprovaline
reactants:
chem.O:
amount: 1
chem.C:
amount: 1
chem.Glucose:
amount: 1
products:
chem.Inaprovaline: 3
- type: reaction
id: react.Kelotane
reactants:
chem.Si:
amount: 1
chem.C:
amount: 1
products:
chem.Kelotane: 2
- type: reaction
id: react.Leporazine
reactants:
chem.Si:
amount: 1
chem.Cu:
amount: 1
chem.Plasma:
amount: 5
catalyst: true
products:
chem.Leporazine: 2
- type: reaction
id: react.Methylin
reactants:
chem.H:
amount: 1
chem.Cl:
amount: 1
chem.Ethanol:
amount: 1
chem.F:
amount: 5
catalyst: true
products:
chem.Methylin: 3
- type: reaction
id: react.Oxycodone
reactants:
chem.Ethanol:
amount: 1
chem.Tramadol:
amount: 1
chem.Plasma:
amount: 1
products:
chem.Oxycodone: 3
- type: reaction
id: react.Phalanximine
reactants:
chem.Hyronalin:
amount: 1
chem.Ethanol:
amount: 1
chem.UnstableMutagen:
amount: 1
products:
chem.Phalanximine: 3
- type: reaction
id: react.Paroxetine
reactants:
chem.MindbreakerToxin:
amount: 5
chem.O:
amount: 5
chem.Inaprovaline:
amount: 5
products:
chem.Paroxetine: 15
- type: reaction
id: react.Ryetalyn
reactants:
chem.Arithrazine:
amount: 1
chem.C:
amount: 1
products:
chem.Ryetalyn: 2
- type: reaction
id: react.Spaceacillin
reactants:
chem.Cryptobiolin:
amount: 1
chem.Inaprovaline:
amount: 1
products:
chem.Spaceacillin: 2
- type: reaction
id: react.Synaptizine
reactants:
chem.Li:
amount: 1
chem.Glucose:
amount: 1
chem.H2O:
amount: 1
products:
chem.Synaptizine: 3
- type: reaction
id: react.Tramadol
reactants:
chem.Inaprovaline:
amount: 1
chem.Ethanol:
amount: 1
chem.O:
amount: 1
products:
chem.Tramadol: 3
- type: reaction
id: react.Tricordrazine
reactants:
chem.Inaprovaline:
amount: 1
chem.Dylovene:
amount: 1
products:
chem.Tricordrazine: 2
- type: reaction
id: react.Vaccine
reactants:
chem.Al:
amount: 1
chem.Glucose:
amount: 1
chem.H2O:
amount: 1
products:
chem.Vaccine: 3
- type: reaction
id: react.Albuterol
reactants:
chem.Hyperzine:
amount: 1
chem.Tramadol:
amount: 1
products:
chem.Albuterol: 2
- type: reaction
id: react.ChloralHydrate
reactants:
chem.Ethanol:
amount: 1
chem.Cl:
amount: 3
chem.H2O:
amount: 1
products:
chem.ChloralHydrate: 5
- type: reaction
id: react.Creatine #Add nutriment as ingredient (amount = 1) once that's a thing
reactants:
chem.Bicaridine:
amount: 1
chem.Hyperzine:
amount: 1
chem.UnstableMutagen:
amount: 1
products:
chem.Creatine: 3
- type: reaction
id: react.Cryptobiolin
reactants:
chem.K:
amount: 1
chem.O:
amount: 1
chem.Glucose:
amount: 1
products:
chem.Cryptobiolin: 3
- type: reaction
id: react.HeartbreakerToxin #heat to 37C once temperatures supported
reactants:
chem.Dexalin:
amount: 1
chem.MindbreakerToxin:
amount: 1
products:
chem.HeartbreakerToxin: 2
- type: reaction
id: react.Impedrezene
reactants:
chem.Hg:
amount: 1
chem.O:
amount: 1
chem.Glucose:
amount: 1
products:
chem.Impedrezene: 3
- type: reaction
id: react.Lexorin
reactants:
chem.Ammonia:
amount: 1
chem.Plasma:
amount: 1
products:
chem.Lexorin: 2
- type: reaction
id: react.Lipozine
reactants:
chem.TableSalt:
amount: 1
chem.Ethanol:
amount: 1
chem.Ra:
amount: 1
products:
chem.Lipozine: 3
- type: reaction
id: react.MindbreakerToxin
reactants:
chem.Si:
amount: 1
chem.H:
amount: 1
chem.Dylovene:
amount: 1
products:
chem.MindbreakerToxin: 3
- type: reaction
id: react.Soporific
reactants:
chem.ChloralHydrate:
amount: 1
chem.Glucose:
amount: 4
products:
chem.Soporific: 5
- type: reaction
id: react.Sterilizine
reactants:
chem.Ethanol:
amount: 1
chem.Dylovene:
amount: 1
chem.Cl:
amount: 1
products:
chem.Sterilizine: 3
- type: reaction
id: react.SpaceDrugs
reactants:
chem.Hg:
amount: 1
chem.Glucose:
amount: 1
chem.Li:
amount: 1
products:
chem.SpaceDrugs: 3

View File

@@ -13,3 +13,80 @@
name: Ice
desc: Frozen water.
color: "#bed8e6"
- type: reagent
id: chem.Plasma
name: Plasma
desc: Funky, space-magic pixie dust. You probably shouldn't eat this, but we both know you will anyways.
- type: reagent
id: chem.Ethanol
name: Ethanol
desc: A simple alcohol, makes you drunk if consumed, flammable.
color: "#b05b3c"
- type: reagent
id: chem.Glucose
name: Glucose
desc: A simple sugar found in many foods.
color: "#ffffff"
- type: reagent
id: chem.Ammonia
name: Ammonia
desc: An effective fertilizer which is better than what is available to the botanist initially, though it isn't as powerful as Diethylamine
color: "#77b58e"
- type: reagent
id: chem.Bleach
name: Bleach
desc: Heavy duty cleaner that can clean tiles the same as Space Cleaner and also decontaminate clothes. Extremely toxic when ingested.
color: "#a1000b"
- type: reagent
id: chem.Diethylamine
name: Diethylamine
desc: A very potent fertilizer.
color: "#a1000b"
- type: reagent
id: chem.FoamingAgent
name: Foaming Agent
desc: Makes foam such as that required in metal foam grenades
color: "#215263"
- type: reagent
id: chem.PolytrinicAcid
name: Polytrinic Acid
desc: An extremely corrosive chemical substance. The slightest touch of it will melt off most masks and headgear, and it deals extreme damage to anyone who comes directly into contact with it. Spraying it on other items will usually melt them too, which does make it useful if the clown has covered the entire hallway in banana peels.
color: "#a1000b"
- type: reagent
id: chem.SpaceCleaner
name: Space Cleaner
desc: This is able to clean almost all surfaces of almost anything that may dirty them. The janitor is likely to appreciate refills.
color: "#215263"
- type: reagent
id: chem.SpaceLube
name: Space Lube
desc: Space Lube is a high performance lubricant intended for maintenance of extremely complex mechanical equipment (and certainly not used to make people slip).
color: "#77b58e"
- type: reagent
id: chem.TableSalt
name: Table Salt
desc: Commonly known as salt, Sodium Chloride is often used to season food or kill borers instantly.
color: "#a1000b"
- type: reagent
id: chem.Thermite
name: Thermite
desc: A mixture that becomes extremely hot when ignited, and which can burn straight through walls when applied and ignited. It'll slowly inflict burn damage to anybody dumb enough to ingest it, but can't be ignited inside inside said dumb person.
color: "#77b58e"
- type: reagent
id: chem.UnstableMutagen
name: Unstable Mutagen
desc: Causes mutations when injected into living people or plants. High doses may be lethal, especially in humans.
color: "#77b58e"

View File

@@ -1,15 +1,15 @@
- type: reagent
id: chem.H2
id: chem.H
name: Hydrogen
desc: A light, flammable gas.
- type: reagent
id: chem.O2
id: chem.O
name: Oxygen
desc: An oxidizing, colorless gas.
- type: reagent
id: chem.S8
id: chem.S
name: Sulfur
desc: A yellow, crystalline solid.
color: "#FFFACD"
@@ -33,7 +33,7 @@
color: "#b05b3c"
- type: reagent
id: chem.N2
id: chem.N
name: Nitrogen
desc: A colorless, odorless unreactive gas. Highly stable.
@@ -44,6 +44,54 @@
color: "#434b4d"
- type: reagent
id: chem.F2
id: chem.F
name: Fluorine
desc: A highly toxic pale yellow gas. Extremely reactive.
- type: reagent
id: chem.Si
name: Silicon
desc: A hard and brittle crystalline solid with a blue-grey color.
color: "#364266"
- type: reagent
id: chem.Cl
name: Chlorine
desc: A yellow-green gas which is toxic to humans.
color: "#a2ff00"
- type: reagent
id: chem.Li
name: Lithium
desc: A soft, silvery-white alkali metal. It is highly reactive, and ignites if it makes contact with water.
color: "#c6c8cc"
- type: reagent
id: chem.Hg
name: Mercury
desc: A silver metal which is liquid at room temperature. It is highly toxic to humans.
color: "#929296"
- type: reagent
id: chem.P
name: Phosphorus
desc: A reactive metal used in pyrotechnics and weapons.
color: "#803330"
- type: reagent
id: chem.K
name: Potassium
desc: A soft, silvery-white metal. Even more reactive than lithium.
color: "#c6c8cc"
- type: reagent
id: chem.Ra
name: Radium
desc: A radioactive metal, silvery-white in it's pure form. It glows due to it's radioactivity and is highly toxic.
color: "#00ff04"
- type: reagent
id: chem.Na
name: Sodium
desc: A silvery-white alkali metal. Highly reactive in it's pure form.
color: "#c6c8cc"

View File

@@ -0,0 +1,246 @@
- type: reagent
id: chem.Alkycosine
name: Alkycosine
desc: Lessens the damage to neurological tissue. More effective at treating brain damage than alkysine and also a fairly effective painkiller as well. Caution is needed in its creation to avoid mixing bleach and the chlorine needed to make alkysine.
color: "#9e232b"
- type: reagent
id: chem.Alkysine
name: Alkysine
desc: Lessens the damage to neurological tissue, effective even after catastrophic injury. Used for treating brain damage and also a fairly effective painkiller.
color: "#a1000b"
- type: reagent
id: chem.Dylovene
name: Dylovene
desc: A broad-spectrum anti-toxin, which treats toxin damage in the blood stream. Overdosing will cause vomiting, dizzyness and pain.
color: "#3a1d8a"
- type: reagent
id: chem.Arithrazine
name: Arithrazine
desc: A slightly unstable medication used for the most extreme any serious case of radiation poisoning. Lowers radiation level at over twice the rate Hyronalin does and will heal toxin damage at the same time. Deals very minor brute damage to the patient over time, but the patient's body will typically out-regenerate it easily.
color: "#bd5902"
- type: reagent
id: chem.Bicaridine
name: Bicaridine
desc: An analgesic which is highly effective at treating brute damage. It is useful for stabilizing people who have been severely beaten, as well as treating less life-threatening injuries. In the case of bleeding (internal or external), bicaridine will slow down the bleeding heavily. If the dosage exceeds the overdose limit, it'll stop it outright.
color: "#ffaa00"
- type: reagent
id: chem.Cryoxadone
name: Cryoxadone
desc: Required for the proper function of cryogenics. Heals all standard types of damage very swiftly, but only works in temperatures under 170K (usually this means cryo cells). Can also slowly heal clone damage, such as caused by cloning or Slimes.
color: "#0091ff"
- type: reagent
id: chem.Clonexadone
name: Clonexadone
desc: Heals standard damage in the same as Cryoxadone, with the same temperature requirement. Significantly more effective than the former at treating clone damage, although both can be used simultaneously. Best used in cryo cells.
color: "#0091ff"
- type: reagent
id: chem.Citalopram
name: Citalopram
desc: Prevents hallucination slightly.
color: "#21693c"
- type: reagent
id: chem.Dermaline
name: Dermaline
desc: An advanced chemical that is more effective at treating burns damage than Kelotane.
color: "#215263"
- type: reagent
id: chem.Dexalin
name: Dexalin
desc: Used for treating oxygen deprivation. In most cases where it is likely to be needed, the strength of Dexalin Plus will probably be more useful (Results in 1 unit instead of 2).
color: "#0041a8"
- type: reagent
id: chem.DexalinPlus
name: Dexalin Plus
desc: Used in treatment of extreme cases of oxygen deprivation. Even a single unit immediately counters all oxygen loss, which is hugely useful in many circumstances. Any dose beyond this will continue to counter oxygen loss until it is metabolized, essentially removing the need to breathe.
color: "#297691"
- type: reagent
id: chem.Ethylredoxrazine
name: Ethylredoxrazine
desc: Neutralises the effects of alcohol in the blood stream. Though it is commonly needed, it is rarely requested.
color: "#2d5708"
- type: reagent
id: chem.Hyperzine
name: Hyperzine
desc: A highly effective, long lasting muscle stimulant. It allows greater freedom of movement in bulky clothing although it has the side effect of causing some twitching. Dangerous in higher doses.
color: "#17bd61"
- type: reagent
id: chem.Hyronalin
name: Hyronalin
desc: A weak treatment for radiation damage. Considered to be useful mainly for genetic modification, where it reduces radiation levels, and thus the chance of genetic mutations. Largely outclassed by Arithrazine.
color: "#17ac61"
- type: reagent
id: chem.Imidazoline
name: Imidazoline
desc: Effective in treating eye trauma. It heals damage caused by physical or chemical trauma, though it is ineffective in treating genetic defects in the eyes.
color: "#f7ef00"
- type: reagent
id: chem.Inacusiate
name: Inacusiate
desc: You only need 1u for Inacusiate work. Cures deafness instantly. Useful after an explosion.
color: "#f7ef00"
- type: reagent
id: chem.Inaprovaline
name: Inaprovaline
desc: Inaprovaline is a synaptic stimulant and cardiostimulant. Commonly used to stabilize patients- it stops oxygen loss when the patient is in critical health. It'll also slow down bleeding (internal or external) by half while in the body. Acts as a decent painkiller.
color: "#73103b"
- type: reagent
id: chem.Kelotane
name: Kelotane
desc: Treats burn damage and prevents infection.
color: "#bf3d19"
- type: reagent
id: chem.Leporazine
name: Leporazine
desc: This keeps a patient's body temperature stable. High doses can allow short periods of unprotected EVA, but prevents use of the cryogenics tubes.
color: "#ff7db5"
- type: reagent
id: chem.Methylin
name: Methylin
desc: An intelligence enhancer, also used in the treatment of attention deficit hyperactivity disorder. Also known as Ritalin. Allows monkeys (not diona nymphs) to understand human speech and improves their dexterity as long as they have some in their system. Causes toxin and brain damage in higher doses.
color: "#a700c4"
- type: reagent
id: chem.Oxycodone
name: Oxycodone
desc: A very effective painkiller, about 250% as strong as Tramadol.
color: "#c4a300"
- type: reagent
id: chem.Phalanximine
name: Phalanximine
desc: Used in the treatment of cancer, is as effective as Anti-Toxin. Causes moderate radiation and hair loss.
color: "#c8ff75"
- type: reagent
id: chem.Paroxetine
name: Paroxetine
desc: Prevents hallucination, but has a 10% chance of causing intense hallucinations.
color: "#c8ff75"
- type: reagent
id: chem.Ryetalyn
name: Ryetalyn
desc: You only need 1u for Ryetalin to work. Deactivates genetic defects and powers, restoring a patient to an ideal state. May be useful if genetics is unable to function properly. Deactivated effects return if the patient's genes are modified again.
color: "#532fd4"
- type: reagent
id: chem.Spaceacillin
name: Spaceacillin
desc: A theta-lactam antibiotic. A common and very useful medicine, effective against many diseases likely to be encountered in space. Slows progression of diseases.
color: "#7f2fd4"
- type: reagent
id: chem.Synaptizine
name: Synaptizine
desc: Toxic, but treats hallucinations, drowsiness & halves the duration of paralysis, stuns and knockdowns. It is metabolized very slowly. One unit is enough to treat hallucinations; two units is deadly.
color: "#d49a2f"
- type: reagent
id: chem.Tramadol
name: Tramadol
desc: A simple, yet effective painkiller. Very effective for patients in shock.
color: "#2f6ed4"
- type: reagent
id: chem.Tricordrazine
name: Tricordrazine
desc: A wide-spectrum stimulant, originally derived from Cordrazine. Is capable of healing all four main damage types simultaneously, however it only heals at half the rate of conventional healing chemicals. Because of its low potency, it's best used as a supplement to other medicines.
color: "#00e5ff"
- type: reagent
id: chem.Vaccine
name: Vaccine
desc: Introduces antigens to the body, allowing the immune system to produce enough antibodies to combat present or future infections. Is blank by default, vaccine carrying antigen is produced at a centrifuge. Each unit raises the associated antibody concentration by 20% so at most 5 units are needed to cure the strongest diseases.
color: "#ba0b60"
- type: reagent
id: chem.Albuterol
name: Albuterol
desc: A bronchodilator that relaxes muscles in the airways and increases air flow to the lungs. It'll remove mucus from your system as long as it's inside your body. Only useful to people with the Asthma disability.
color: "#00e5ff"
- type: reagent
id: chem.ChloralHydrate
name: Chloral Hydrate
desc: A powerful sedative which causes death in doses upwards of 16.2 units. Sends the patient to sleep almost instantly.
color: "#00e5ff"
- type: reagent
id: chem.Creatine
name: Creatine
desc: A muscle-building drug that grants the user enormous strength, before their muscles seize and tear their own body to shreds. In practical terms, 1-25 units just causes toxin damage, and 26 units turns you into a hulk with all its associated benefits with the side effect of taking a little toxin damage over time. You'll remain as a hulk until it's all metabolized, at which point you'll take 200 brute damage and gib. With the correct administration, it can be the most potent drug there is, but even at the best of times it is best considered a double-edged sword.
color: "#a1000b"
- type: reagent
id: chem.Cryptobiolin
name: Cryptobiolin
desc: Causes confusion and dizziness. This is essential to make Spaceacillin.
color: "#00e5ff"
- type: reagent
id: chem.HeartbreakerToxin
name: Heartbreaker Toxin
desc: A hallucinogenic compound that is illegal under space law. A synthetic drug derived from Mindbreaker toxin, it blocks some neurological signals to the respiratory system which causes choking.
color: "#00e5ff"
- type: reagent
id: chem.Impedrezene
name: Impedrezene
desc: A narcotic that impedes one's ability by slowing down the higher brain cell functions. Causes massive brain damage.
color: "#215263"
- type: reagent
id: chem.Lexorin
name: Lexorin
desc: Temporarily stops respiration and causes tissue damage. Large doses are fatal, and will cause people to pass out very quickly. Dexalin and Dexalin Plus will both remove it, however.
color: "#a1000b"
- type: reagent
id: chem.Lipozine
name: Lipozine
desc: Causes weight loss upon consumption.
color: "#215263"
- type: reagent
id: chem.MindbreakerToxin
name: Mindbreaker Toxin
desc: A potent hallucinogenic compound that is illegal under space law. Formerly known as LSD.
color: "#77b58e"
- type: reagent
id: chem.Soporific
name: Soporific (Sleep-Toxin)
desc: A less powerful sedative that takes a while to work, intended to help insomniacs and patients that are too aggressive to be treated normally. Takes roughly 50 seconds to make the patient fall asleep. Is safe in large quantities. Can be counteracted with Anti-Toxin.
color: "#215263"
- type: reagent
id: chem.Sterilizine
name: Sterilizine
desc: Helps the patient when used during surgery, may also decontaminate objects and surfaces that bear pathogens. Is currently useless due to infections not being a thing.
color: "#215263"
- type: reagent
id: chem.SpaceDrugs
name: Space Drugs
desc: An illegal compound which induces a number of effects such as loss of balance and visual artefacts.
color: "#a1000b"