To make an entity a solution container, give add a
SolutionContainerManager. For example here is an empty stomach solution:
- type: SolutionContainerManager solutions: stomach: maxVol: 200
and here is for example solution
- type: SolutionContainerManager solutions: drink: maxVol: 20 reagents: - ReagentId: Cola Quantity: 20
type: The component type. Should always be
solutions: A map of solution name and the
Solution described below.
Solutions have several fields, all of them optional.
maxVol: The maximum volume of solution it can hold. Once reagent Quantity sum reaches
maxVol, no more reagents can be added. It's full.
reagents: List of
Reagents it should hold by default. Each
Reagent has a string
Quantity which is a d.
With introduction of
SolutionContainerManager there is no longer a simple 1:1 mapping of solution and list of reagents.
SolutionContainerManager can contain any number of solutions. To solve this problem instead of Capabilities flag, a set of Capability-like components are introduced. Each such capability has a a string
solution that tells
SolutionContainerManager which solution it targets.
Here is a list of them:
DrainableSolutionComponentfor solutions that can be easily removed through any reagent container. E.g. draining water from a water tank
DrawableSolutionComponentfor solutions that can be drawn with syringes. E.g. humans or syringe bottles with rubber caps.
ExaminableSolutionComponentfor solutions that can be examined by hand.
FitsInDispenserComponentnotes that the component fits in dispenser and tells ReagentDispenser/ChemMaster which solution to target.
InjectableSolutionComponentfor solutions that can be injected into with syringes.
RefillableSolutionComponentfor solutions that can be added easily.
Other components like
FoodComponent may have special predefined solutions they target and expect to see. For example a
DrinkComponent will try to create
drink solution if there isn't an easily accessed
DrainableSolutionComponent already available.
Here is a full example of an entity with some Solutions:
- type: entity id: DrinkColaCan name: space cola description: A refreshing beverage. components: - type: Sprite sprite: Objects/Consumable/Drinks/cola.rsi - type: SolutionContainerManager solutions: drink: reagents: - ReagentId: Cola Quantity: 20 maxVol: 20 - type: Drink
Reagents are substances which comprise solution and which can react to create new Reagents.
Their definition consists of:
type: The component name. Should always be
id: Unique id for this reagent. Used in reactions and other locations to identify the reagents. For example
WeldingFuel. They are
name: Id with spaces for displaying in tooltips. E.g.
parent: What reagent to inherit properties from. Used for things like BaseDrink.
desc: A human friendly description of the chemical.
color: Hex color of the reagent. Used for mixing and chemical display in containers.
spritePath: A sprite specifying the icon used for the reagent, starting from
physicalDesc: Vague desscription potentially useful when identifying it.
boilingPoint: Temperature in Celsius, at which reagent goes from liquid into gas.
meltingPoint: Temperature in Celsius, at which reagent goes from solid into liquid.
metabolisms: A dict of MetabolismGroup->List<ReagentEffect> to apply when the reagent is consumed.
- type: reagent id: Lemonade name: lemonade parent: BaseDrink desc: Drink using lemon juice, water, and a sweetener such as cane sugar or honey. physicalDesc: tart color: "#FFFF00" spritePath: lemonadeglass.rsi metabolisms: Drink: effects: - !type:SatiateThirst factor: 2
Metabolism in SS14 is very different to metabolism in SS13. In SS13, reagents simply had a proc
on_mob_life that was called to determine what should happen. Our system is much more modular and allows for things like different organs metabolizing different reagents, and species-specific metabolisms, as well as overdoses or underdoses without any hassle.
In SS14, organs are entities with a
MechanismComponent (mechanism just being a catch-all term for 'organ'.) Organs also have other components that give them decoupled behavior;
RespiratorComponent, and so on. However, the important one here is
Metabolizers (organs with that component) define a list of 'metabolism groups' that they metabolize. 'Metabolism groups' are things like 'Narcotic', or 'Food', or 'Medicine'. This allows us to define how organs metabolize different chemicals.
This is on
OrganHumanStomach. Meaning, the stomach processes any reagents with a
Food metabolism, or a
- type: Metabolizer # mm yummy maxReagents: 3 metabolizerTypes: [Human] groups: - id: Food - id: Drink
Metabolizers can only process so many reagents at once, to avoid the common situation in SS13 where stacking a ton of poisons on one entity would damage it significantly more than just one concentrated poison.
Metabolism rate is defined on the organ. Generally, its once per second, and chems are taken from the bloodstream.
Defining how different species react to different chemicals is quite easy. Metabolizers are tagged with 'organ types', which is essentially a species marker, but localized to the organ (so if you swap out your heart for a gorilla heart, you'll metabolize medicines/poisons/etc in the same way a gorilla does). Then, you can use the
OrganType condition to check. Here's an example in the codebase, of theobromine (a chemical found in chocolate):
metabolisms: Poison: effects: - !type:HealthChange conditions: - !type:ReagentThreshold min: 1 - !type:OrganType type: Animal # Applying damage to the mobs with lower metabolism capabilities damage: types: Poison: 4
As the comment suggests, to humans this chemical does nothing, but to animals (things with animal organs) it can be deadly.
ReagentEffects are described in C#. For example,
HealthChange. This allows you to easily reuse code that has similar effects. Many things use reagent effects--metabolisms, plant metabolisms, and reagent entity reactions (touch/injection-based effects).
ReagentEffects also have a
prob field, which is the chance that the effect will run from 0 to 1. The reason these are so generalized is because many things that need the data can use them.
ReagentEffectConditions are attached to each effect in the metabolizer list. By default, there are none; so they're always ran. However, you can easily add custom behavior to determine when an effect will actually occur. For instance,
ReagentThreshold allows you to very easily define overdose behavior. Here's Methamphetamine:
metabolisms: Poison: effects: - !type:HealthChange damage: types: Poison: 2.5 - !type:HealthChange conditions: - !type:ReagentThreshold min: 10 damage: types: Poison: 4 # this is added to the base damage of the meth. Narcotic: effects: - !type:MovespeedModifier walkSpeedModifier: 1.3 sprintSpeedModifier: 1.3
This means that the overdose behavior HealthChange only occurs when there is at minimum 10u in your system. It has two different groups, 'Poison' and 'Narcotic'. These are both metabolized by the heart, generally.
Reaction prototypes define recipes with ratios of reagents required to cause a chemical reaction. Below are the reaction yaml config values:
type: The prototype type. Should always be
id: Unique id used by the game to identify the reaction. In
reactants: A list of reactants required for the reaction to occur. Each reactant specifies it’s ratio and if it’s a catalyst. See the example below for more info.
products: A list of reagents created as a result of the reaction. They will be add to the solution container the reaction occurs in if there is room.
effects: A list of effects and their properties. Each effect corresponds to a C# class that implements IReactionEffect. See the example below for more info.
- type: reaction id: PotassiumExplosion reactants: Water: amount: 1 Potassium: amount: 1 effects: # Reaction 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
Here’s another example. This one has a catalyst, and some chemical products, but no reaction effect. Note that reactants are only catalysts if directly specified, and will be consumed otherwise.
- type: reaction id: PolytrinicAcid reactants: SulfuricAcid: amount: 1 Chlorine: amount: 1 catalyst: True # False if not specified Potassium: amount: 1 products: PolytrinicAcid: 3