Liquid anomaly (#20626)

Co-authored-by: Kara <lunarautomaton6@gmail.com>
This commit is contained in:
Ed
2023-10-07 03:26:34 +03:00
committed by GitHub
parent 15fee3f944
commit b5e8c57978
17 changed files with 847 additions and 1 deletions

View File

@@ -0,0 +1,47 @@
using Content.Server.Anomaly.Effects;
using Robust.Shared.Prototypes;
namespace Content.Server.Anomaly.Components;
/// <summary>
/// This component allows the anomaly to inject liquid from the SolutionContainer
/// into the surrounding entities with the InjectionSolution component
/// </summary>
[RegisterComponent, Access(typeof(InjectionAnomalySystem))]
public sealed partial class InjectionAnomalyComponent : Component
{
/// <summary>
/// the maximum amount of injection of a substance into an entity per pulsation
/// scales with Severity
/// </summary>
[DataField, ViewVariables(VVAccess.ReadWrite)]
public float MaxSolutionInjection = 15;
/// <summary>
/// the maximum amount of injection of a substance into an entity in the supercritical phase
/// </summary>
[DataField, ViewVariables(VVAccess.ReadWrite)]
public float SuperCriticalSolutionInjection = 50;
/// <summary>
/// The maximum radius in which the anomaly injects reagents into the surrounding containers.
/// </summary>
[DataField, ViewVariables(VVAccess.ReadWrite)]
public float InjectRadius = 3;
/// <summary>
/// The maximum radius in which the anomaly injects reagents into the surrounding containers.
/// </summary>
[DataField, ViewVariables(VVAccess.ReadWrite)]
public float SuperCriticalInjectRadius = 15;
/// <summary>
/// The name of the prototype of the special effect that appears above the entities into which the injection was carried out
/// </summary>
[DataField, ViewVariables(VVAccess.ReadOnly)]
public EntProtoId VisualEffectPrototype = "PuddleSparkle";
/// <summary>
/// Solution name that can be drained.
/// </summary>
[DataField, ViewVariables(VVAccess.ReadWrite)]
public string Solution { get; set; } = "default";
}

View File

@@ -0,0 +1,29 @@
using Content.Server.Anomaly.Effects;
namespace Content.Server.Anomaly.Components;
/// <summary>
/// This component allows the anomaly to create puddles from the solutionContainer
/// </summary>
[RegisterComponent, Access(typeof(PuddleCreateAnomalySystem))]
public sealed partial class PuddleCreateAnomalyComponent : Component
{
/// <summary>
/// The maximum amount of solution that an anomaly can splash out of the storage on the floor during pulsation.
/// Scales with Severity.
/// </summary>
[DataField, ViewVariables(VVAccess.ReadWrite)]
public float MaxPuddleSize = 100;
/// <summary>
/// The maximum amount of solution that an anomaly can splash out of the storage on the floor during supercritical event
/// </summary>
[DataField, ViewVariables(VVAccess.ReadWrite)]
public float SuperCriticalPuddleSize = 1000;
/// <summary>
/// Solution name that can be drained.
/// </summary>
[DataField, ViewVariables(VVAccess.ReadWrite)]
public string Solution { get; set; } = "default";
}

View File

@@ -0,0 +1,94 @@
using Content.Server.Anomaly.Effects;
using Content.Shared.Chemistry.Reagent;
using Robust.Shared.Audio;
using Robust.Shared.Prototypes;
using System.Numerics;
namespace Content.Server.Anomaly.Components;
/// <summary>
/// This component allows the anomaly to generate a random type of reagent in the specified SolutionContainer.
/// With the increasing severity of the anomaly, the type of reagent produced may change.
/// The higher the severity of the anomaly, the higher the chance of dangerous or useful reagents.
/// </summary>
[RegisterComponent, Access(typeof(ReagentProducerAnomalySystem))]
public sealed partial class ReagentProducerAnomalyComponent : Component
{
//the addition of the reagent will occur instantly when an anomaly appears,
//and there will not be the first three seconds of a white empty anomaly.
public float AccumulatedFrametime = 3.0f;
/// <summary>
/// How frequently should this reagent generation update, in seconds?
/// </summary>
[DataField, ViewVariables(VVAccess.ReadWrite)]
public float UpdateInterval = 3.0f;
/// <summary>
/// The spread of the random weight of the choice of this category, depending on the severity.
/// </summary>
[DataField, ViewVariables(VVAccess.ReadWrite)]
public Vector2 WeightSpreadDangerous = new(5.0f, 9.0f);
/// <summary>
/// The spread of the random weight of the choice of this category, depending on the severity.
/// </summary>
[DataField, ViewVariables(VVAccess.ReadWrite)]
public Vector2 WeightSpreadFun = new(3.0f, 0.0f);
/// <summary>
/// The spread of the random weight of the choice of this category, depending on the severity.
/// </summary>
[DataField, ViewVariables(VVAccess.ReadWrite)]
public Vector2 WeightSpreadUseful = new(1.0f, 1.0f);
/// <summary>
/// Category of dangerous reagents for injection. Various toxins and poisons
/// </summary>
[DataField, ViewVariables(VVAccess.ReadWrite)]
public List<ProtoId<ReagentPrototype>> DangerousChemicals = new();
/// <summary>
/// Category of useful reagents for injection. Medicine and other things that players WANT to get
/// </summary>
[DataField, ViewVariables(VVAccess.ReadWrite)]
public List<ProtoId<ReagentPrototype>> UsefulChemicals = new();
/// <summary>
/// Category of fun reagents for injection. Glue, drugs, beer. Something that will bring fun.
/// </summary>
[DataField, ViewVariables(VVAccess.ReadWrite)]
public List<ProtoId<ReagentPrototype>> FunChemicals = new();
/// <summary>
/// Noise made when anomaly pulse.
/// </summary>
[DataField, ViewVariables(VVAccess.ReadWrite)]
public SoundSpecifier ChangeSound = new SoundPathSpecifier("/Audio/Effects/waterswirl.ogg");
/// <summary>
/// The component will repaint the sprites of the object to match the current color of the solution,
/// if the RandomSprite component is hung correctly.
/// Ideally, this should be put into a separate component, but I suffered for 4 hours,
/// and nothing worked out for me. So for now it will be like this.
/// </summary>
[DataField, ViewVariables(VVAccess.ReadOnly)]
public bool NeedRecolor = false;
/// <summary>
/// the maximum amount of reagent produced per second
/// </summary>
[DataField, ViewVariables(VVAccess.ReadWrite)]
public float MaxReagentProducing = 1.5f;
/// <summary>
/// how much does the reagent production increase before entering the supercritical state
/// </summary>
[DataField, ViewVariables(VVAccess.ReadWrite)]
public float SupercriticalReagentProducingModifier = 100f;
/// <summary>
/// The name of the reagent that the anomaly produces.
/// </summary>
[DataField, ViewVariables(VVAccess.ReadWrite)]
public ProtoId<ReagentPrototype> ProducingReagent = "Water";
/// <summary>
/// Solution name where the substance is generated
/// </summary>
[ViewVariables(VVAccess.ReadWrite)]
[DataField("solution")]
public string Solution = "default";
}

View File

@@ -0,0 +1,67 @@
using System.Linq;
using Content.Server.Anomaly.Components;
using Content.Server.Chemistry.Components.SolutionManager;
using Content.Server.Chemistry.EntitySystems;
using Content.Shared.Anomaly.Components;
namespace Content.Server.Anomaly.Effects;
/// <summary>
/// This component allows the anomaly to inject liquid from the SolutionContainer
/// into the surrounding entities with the InjectionSolution component
/// </summary>
///
/// <see cref="InjectionAnomalyComponent"/>
public sealed class InjectionAnomalySystem : EntitySystem
{
[Dependency] private readonly EntityLookupSystem _lookup = default!;
[Dependency] private readonly SolutionContainerSystem _solutionContainer = default!;
private EntityQuery<InjectableSolutionComponent> _injectableQuery;
public override void Initialize()
{
SubscribeLocalEvent<InjectionAnomalyComponent, AnomalyPulseEvent>(OnPulse);
SubscribeLocalEvent<InjectionAnomalyComponent, AnomalySupercriticalEvent>(OnSupercritical, before: new[] { typeof(SolutionContainerSystem) });
_injectableQuery = GetEntityQuery<InjectableSolutionComponent>();
}
private void OnPulse(EntityUid uid, InjectionAnomalyComponent component, ref AnomalyPulseEvent args)
{
PulseScalableEffect(uid, component, component.InjectRadius, component.MaxSolutionInjection * args.Severity);
}
private void OnSupercritical(EntityUid uid, InjectionAnomalyComponent component, ref AnomalySupercriticalEvent args)
{
PulseScalableEffect(uid, component, component.SuperCriticalInjectRadius, component.SuperCriticalSolutionInjection);
}
private void PulseScalableEffect(EntityUid uid, InjectionAnomalyComponent component, float injectRadius, float maxInject)
{
if (!_solutionContainer.TryGetSolution(uid, component.Solution, out var sol))
return;
//We get all the entity in the radius into which the reagent will be injected.
var xformQuery = GetEntityQuery<TransformComponent>();
var xform = xformQuery.GetComponent(uid);
var allEnts = _lookup.GetComponentsInRange<InjectableSolutionComponent>(xform.MapPosition, injectRadius)
.Select(x => x.Owner).ToList();
//for each matching entity found
foreach (var ent in allEnts)
{
if (!_solutionContainer.TryGetInjectableSolution(ent, out var injectable))
continue;
if (_injectableQuery.TryGetComponent(ent, out var injEnt))
{
var buffer = sol;
_solutionContainer.TryTransferSolution(ent, injectable, buffer, maxInject);
//Spawn Effect
var uidXform = Transform(ent);
Spawn(component.VisualEffectPrototype, uidXform.Coordinates);
}
}
}
}

View File

@@ -0,0 +1,39 @@
using Content.Server.Anomaly.Components;
using Content.Server.Chemistry.EntitySystems;
using Content.Shared.Anomaly.Components;
using Content.Server.Fluids.EntitySystems;
namespace Content.Server.Anomaly.Effects;
/// <summary>
/// This component allows the anomaly to create puddles from SolutionContainer.
/// </summary>
public sealed class PuddleCreateAnomalySystem : EntitySystem
{
[Dependency] private readonly PuddleSystem _puddle = default!;
[Dependency] private readonly SolutionContainerSystem _solutionContainer = default!;
public override void Initialize()
{
SubscribeLocalEvent<PuddleCreateAnomalyComponent, AnomalyPulseEvent>(OnPulse);
SubscribeLocalEvent<PuddleCreateAnomalyComponent, AnomalySupercriticalEvent>(OnSupercritical, before: new[] { typeof(InjectionAnomalySystem) });
}
private void OnPulse(EntityUid uid, PuddleCreateAnomalyComponent component, ref AnomalyPulseEvent args)
{
if (!_solutionContainer.TryGetSolution(uid, component.Solution, out var sol))
return;
var xform = Transform(uid);
var puddleSol = _solutionContainer.SplitSolution(uid, sol, component.MaxPuddleSize * args.Severity);
_puddle.TrySplashSpillAt(uid, xform.Coordinates, puddleSol, out _);
}
private void OnSupercritical(EntityUid uid, PuddleCreateAnomalyComponent component, ref AnomalySupercriticalEvent args)
{
if (!_solutionContainer.TryGetSolution(uid, component.Solution, out var sol))
return;
var buffer = sol;
var xform = Transform(uid);
_puddle.TrySpillAt(xform.Coordinates, buffer, out _);
}
}

View File

@@ -0,0 +1,152 @@
using Content.Server.Anomaly.Components;
using Content.Server.Chemistry.EntitySystems;
using Content.Shared.Anomaly.Components;
using Robust.Shared.Random;
using Content.Shared.Chemistry.Components;
using Robust.Shared.Prototypes;
using Content.Shared.Sprite;
using Robust.Server.GameObjects;
namespace Content.Server.Anomaly.Effects;
/// <see cref="ReagentProducerAnomalyComponent"/>
public sealed class ReagentProducerAnomalySystem : EntitySystem
{
//The idea is to divide substances into several categories.
//The anomaly will choose one of the categories with a given chance based on severity.
//Then a random substance will be selected from the selected category.
//There are the following categories:
//Dangerous:
//selected most often. A list of substances that are extremely unpleasant for injection.
//Fun:
//Funny things have an increased chance of appearing in an anomaly.
//Useful:
//Those reagents that the players are hunting for. Very low percentage of loss.
[Dependency] private readonly SolutionContainerSystem _solutionContainer = default!;
[Dependency] private readonly IRobustRandom _random = default!;
[Dependency] private readonly PointLightSystem _light = default!;
[Dependency] private readonly IPrototypeManager _prototypeManager = default!;
[Dependency] private readonly SharedAudioSystem _audio = default!;
public const string FallbackReagent = "Water";
public override void Initialize()
{
SubscribeLocalEvent<ReagentProducerAnomalyComponent, AnomalyPulseEvent>(OnPulse);
SubscribeLocalEvent<ReagentProducerAnomalyComponent, MapInitEvent>(OnMapInit);
}
private void OnPulse(EntityUid uid, ReagentProducerAnomalyComponent component, ref AnomalyPulseEvent args)
{
if (_random.NextFloat(0.0f, 1.0f) > args.Stability)
ChangeReagent(uid, component, args.Severity);
}
private void ChangeReagent(EntityUid uid, ReagentProducerAnomalyComponent component, float severity)
{
var reagent = GetRandomReagentType(uid, component, severity);
component.ProducingReagent = reagent;
_audio.PlayPvs(component.ChangeSound, uid);
}
//reagent realtime generation
public override void Update(float frameTime)
{
base.Update(frameTime);
var query = EntityQueryEnumerator<ReagentProducerAnomalyComponent, AnomalyComponent>();
while (query.MoveNext(out var uid, out var component, out var anomaly))
{
component.AccumulatedFrametime += frameTime;
if (component.AccumulatedFrametime < component.UpdateInterval)
continue;
if (!_solutionContainer.TryGetSolution(uid, component.Solution, out var producerSol))
continue;
Solution newSol = new();
var reagentProducingAmount = anomaly.Stability * component.MaxReagentProducing * component.AccumulatedFrametime;
if (anomaly.Severity >= 0.97) reagentProducingAmount *= component.SupercriticalReagentProducingModifier;
newSol.AddReagent(component.ProducingReagent, reagentProducingAmount);
_solutionContainer.TryAddSolution(uid, producerSol, newSol); //TO DO - the container is not fully filled.
component.AccumulatedFrametime = 0;
// The component will repaint the sprites of the object to match the current color of the solution,
// if the RandomSprite component is hung correctly.
// Ideally, this should be put into a separate component, but I suffered for 4 hours,
// and nothing worked out for me. So for now it will be like this.
if (component.NeedRecolor)
{
var color = producerSol.GetColor(_prototypeManager);
_light.SetColor(uid, color);
if (TryComp<RandomSpriteComponent>(uid, out var randomSprite))
{
foreach (var ent in randomSprite.Selected)
{
var state = randomSprite.Selected[ent.Key];
state.Color = color;
randomSprite.Selected[ent.Key] = state;
}
Dirty(uid, randomSprite);
}
}
}
}
private void OnMapInit(EntityUid uid, ReagentProducerAnomalyComponent component, MapInitEvent args)
{
ChangeReagent(uid, component, 0.1f); //MapInit Reagent 100% change
}
// returns a random reagent based on a system of random weights.
// First, the category is selected: The category has a minimum and maximum weight,
// the current value depends on severity.
// Accordingly, with the strengthening of the anomaly,
// the chances of falling out of some categories grow, and some fall.
//
// After that, a random reagent in the selected category is selected.
//
// Such a system is made to control the danger and interest of the anomaly more.
private string GetRandomReagentType(EntityUid uid, ReagentProducerAnomalyComponent component, float severity)
{
//Category Weight Randomization
var currentWeightDangerous = MathHelper.Lerp(component.WeightSpreadDangerous.X, component.WeightSpreadDangerous.Y, severity);
var currentWeightFun = MathHelper.Lerp(component.WeightSpreadFun.X, component.WeightSpreadFun.Y, severity);
var currentWeightUseful = MathHelper.Lerp(component.WeightSpreadUseful.X, component.WeightSpreadUseful.Y, severity);
var sumWeight = currentWeightDangerous + currentWeightFun + currentWeightUseful;
var rnd = _random.NextFloat(0f, sumWeight);
//Dangerous
if (rnd <= currentWeightDangerous && component.DangerousChemicals.Count > 0)
{
var reagent = _random.Pick(component.DangerousChemicals);
return reagent;
}
else rnd -= currentWeightDangerous;
//Fun
if (rnd <= currentWeightFun && component.FunChemicals.Count > 0)
{
var reagent = _random.Pick(component.FunChemicals);
return reagent;
}
else rnd -= currentWeightFun;
//Useful
if (rnd <= currentWeightUseful && component.UsefulChemicals.Count > 0)
{
var reagent = _random.Pick(component.UsefulChemicals);
return reagent;
}
//We should never end up here.
//Maybe Log Error?
return FallbackReagent;
}
}

View File

@@ -57,3 +57,8 @@
license: "CC-BY-SA-3.0" license: "CC-BY-SA-3.0"
copyright: "Taken from tgstation" copyright: "Taken from tgstation"
source: "https://github.com/tgstation/tgstation/blob/e3a835b96043fad1269ee7b0c3a6cb340a466f3a/sound/effects/break_stone.ogg" source: "https://github.com/tgstation/tgstation/blob/e3a835b96043fad1269ee7b0c3a6cb340a466f3a/sound/effects/break_stone.ogg"
- files: ["waterswirl.ogg"]
license: "CC0-1.0"
copyright: "Taken from InspectorJ via freesound.org and mixed from stereo to mono."
source: "https://freesound.org/people/InspectorJ/sounds/398703/"

Binary file not shown.

View File

@@ -17,4 +17,5 @@
- AnomalyBluespace - AnomalyBluespace
- AnomalyIce - AnomalyIce
- AnomalyRock - AnomalyRock
- AnomalyLiquid
chance: 1 chance: 1

View File

@@ -206,3 +206,232 @@
radius: 2 radius: 2
energy: 3 energy: 3
color: "#06DF24" color: "#06DF24"
- type: entity
name: Reagent slime
id: ReagentSlime
suffix: Water
parent: MobAdultSlimes
description: It consists of a liquid, and it wants to dissolve you in itself.
components:
- type: NpcFactionMember
factions:
- SimpleHostile
- type: Sprite
drawdepth: Mobs
sprite: Mobs/Aliens/elemental.rsi
layers:
- map: [ "enum.DamageStateVisualLayers.Base" ]
state: alive
color: "#75b1f0"
- type: PointLight
radius: 2.0
energy: 3.5
color: "#75b1f0" # Edited through the LiquidAnomalySystem
- type: MobState
allowedStates:
- Alive
- Dead
- type: MobThresholds
thresholds:
0: Alive
150: Dead
- type: SlowOnDamage
speedModifierThresholds:
50: 0.4
- type: Bloodstream
bloodReagent: Water
chemicalMaxVolume: 100
- type: StatusEffects
allowed:
- SlowedDown
- Electrocution
- type: MeleeWeapon
soundHit:
collection: AlienClaw
animation: WeaponArcBite
damage:
types:
Slash: 15
- type: MeleeChemicalInjector
solution: bloodstream
transferAmount: 5
- type: DamageStateVisuals
rotate: true
states:
Alive:
Base: alive
Dead:
Base: dead
- type: Tag
tags:
- FootstepSound
- CannotSuicide
- DoorBumpOpener
- type: NoSlip
- type: ZombieImmune
- type: ExaminableSolution
solution: bloodstream
- type: InjectableSolution
solution: bloodstream
- type: DrainableSolution
solution: bloodstream
- type: entity
name: Reagent Slime Spawner
id: ReagentSlimeSpawner
parent: MarkerBase
components:
- type: Sprite
layers:
- state: red
- sprite: Mobs/Aliens/elemental.rsi
state: alive
- type: RandomSpawner
prototypes:
- ReagentSlime
- ReagentSlimeBeer
- ReagentSlimePax
- ReagentSlimeNocturine
- ReagentSlimeTHC
- ReagentSlimeBicaridine
- ReagentSlimeToxin
- ReagentSlimeNapalm
- ReagentSlimeOmnizine
chance: 1
- type: entity
id: ReagentSlimeBeer
parent: ReagentSlime
suffix: Beer
components:
- type: Bloodstream
bloodReagent: Beer
- type: PointLight
color: "#cfa85f"
- type: Sprite
drawdepth: Mobs
sprite: Mobs/Aliens/elemental.rsi
layers:
- map: [ "enum.DamageStateVisualLayers.Base" ]
state: alive
color: "#cfa85f"
- type: entity
id: ReagentSlimePax
parent: ReagentSlime
suffix: Pax
components:
- type: Bloodstream
bloodReagent: Pax
- type: PointLight
color: "#AAAAAA"
- type: Sprite
drawdepth: Mobs
sprite: Mobs/Aliens/elemental.rsi
layers:
- map: [ "enum.DamageStateVisualLayers.Base" ]
state: alive
color: "#AAAAAA"
- type: entity
id: ReagentSlimeNocturine
parent: ReagentSlime
suffix: Nocturine
components:
- type: Bloodstream
bloodReagent: Nocturine
- type: PointLight
color: "#128e80"
- type: Sprite
drawdepth: Mobs
sprite: Mobs/Aliens/elemental.rsi
layers:
- map: [ "enum.DamageStateVisualLayers.Base" ]
state: alive
color: "#128e80"
- type: entity
id: ReagentSlimeTHC
parent: ReagentSlime
suffix: THC
components:
- type: Bloodstream
bloodReagent: THC
- type: PointLight
color: "#808080"
- type: Sprite
drawdepth: Mobs
sprite: Mobs/Aliens/elemental.rsi
layers:
- map: [ "enum.DamageStateVisualLayers.Base" ]
state: alive
color: "#808080"
- type: entity
id: ReagentSlimeBicaridine
parent: ReagentSlime
suffix: Bicaridine
components:
- type: Bloodstream
bloodReagent: Bicaridine
- type: PointLight
color: "#ffaa00"
- type: Sprite
drawdepth: Mobs
sprite: Mobs/Aliens/elemental.rsi
layers:
- map: [ "enum.DamageStateVisualLayers.Base" ]
state: alive
color: "#ffaa00"
- type: entity
id: ReagentSlimeToxin
parent: ReagentSlime
suffix: Toxin
components:
- type: Bloodstream
bloodReagent: Toxin
- type: PointLight
color: "#cf3600"
- type: Sprite
drawdepth: Mobs
sprite: Mobs/Aliens/elemental.rsi
layers:
- map: [ "enum.DamageStateVisualLayers.Base" ]
state: alive
color: "#cf3600"
- type: entity
id: ReagentSlimeNapalm
parent: ReagentSlime
suffix: Napalm
components:
- type: Bloodstream
bloodReagent: Napalm
- type: PointLight
color: "#FA00AF"
- type: Sprite
drawdepth: Mobs
sprite: Mobs/Aliens/elemental.rsi
layers:
- map: [ "enum.DamageStateVisualLayers.Base" ]
state: alive
color: "#FA00AF"
- type: entity
id: ReagentSlimeOmnizine
parent: ReagentSlime
suffix: Omnizine
components:
- type: Bloodstream
bloodReagent: Omnizine
- type: PointLight
color: "#fcf7f9"
- type: Sprite
drawdepth: Mobs
sprite: Mobs/Aliens/elemental.rsi
layers:
- map: [ "enum.DamageStateVisualLayers.Base" ]
state: alive
color: "#fcf7f9"

View File

@@ -263,3 +263,109 @@
superCriticalSpawns: superCriticalSpawns:
- AsteroidRock - AsteroidRock
- SpawnMobOreCrab - SpawnMobOreCrab
- type: entity
id: AnomalyLiquid
parent: BaseAnomaly
suffix: Liquid
components:
- type: Sprite
sprite: Structures/Specific/Anomalies/liquid_anom.rsi
layers:
- state: anom
map: ["enum.AnomalyVisualLayers.Base"]
- state: pulse
map: ["enum.AnomalyVisualLayers.Animated"]
visible: false
- type: RandomSprite
selected: # Initialized layer values. Edited through the ReagentProducerAnomalySystem
enum.AnomalyVisualLayers.Base:
anom: "#ffffff"
enum.AnomalyVisualLayers.Animated:
pulse: "#ffffff"
- type: PointLight
radius: 4.0
energy: 3.5
color: "#bbbbbb"
- type: BadFood
- type: Anomaly
anomalyContactDamage:
types:
Slash: 1
- type: EntitySpawnAnomaly
superCriticalSpawns:
- ReagentSlimeSpawner
spawns:
- PuddleSparkle
- type: SolutionContainerManager
solutions:
anomaly:
maxVol: 1500
- type: PuddleCreateAnomaly
solution: anomaly
- type: InjectionAnomaly
solution: anomaly
- type: ReagentProducerAnomaly
solution: anomaly
needRecolor: true
dangerousChemicals:
- UnstableMutagen
- Mold
- PolytrinicAcid
- FerrochromicAcid
- FluorosulfuricAcid
- SulfuricAcid
- HeartbreakerToxin
- VentCrud
- UncookedAnimalProteins
- Thermite
- Napalm
- Phlogiston
- ChlorineTrifluoride
- FoamingAgent
- BuzzochloricBees
- RobustHarvest
usefulChemicals:
- Cryptobiolin
- Dylovene
- Arithrazine
- Bicaridine
- Cryoxadone
- Dermaline
- Dexalin
- DexalinPlus
- Epinephrine
- Leporazine
- Ambuzol
- Tricordrazine
- Artifexium
- Ethylredoxrazine
funChemicals:
- Desoxyephedrine
- Ephedrine
- THC
- THCOil
- SpaceDrugs
- Nocturine
- MuteToxin
- NorepinephricAcid
- Pax
- Ipecac
- Cognizine
- Beer
- SpaceGlue
- CogChamp
- Honk
- Carpetium
- type: Drink
solution: anomaly
- type: DrainableSolution
solution: anomaly
- type: DrawableSolution
solution: anomaly
- type: ExaminableSolution
solution: anomaly
- type: RefillableSolution
solution: anomaly
- type: InjectableSolution
solution: beaker

Binary file not shown.

After

Width:  |  Height:  |  Size: 4.1 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 510 B

View File

@@ -0,0 +1,44 @@
{
"version": 1,
"license": "CC0-1.0",
"copyright": "Created by TheShuEd (github) for Space Station 14.",
"size": {
"x": 32,
"y": 32
},
"states": [
{
"name": "dead"
},
{
"name": "alive",
"directions": 4,
"delays": [
[
0.2,
0.2,
0.2,
0.2
],
[
0.2,
0.2,
0.2,
0.2
],
[
0.2,
0.2,
0.2,
0.2
],
[
0.2,
0.2,
0.2,
0.2
]
]
}
]
}

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.2 KiB

View File

@@ -0,0 +1,33 @@
{
"version": 1,
"license": "CC0-1.0",
"copyright": "Created by TheShuEd (github) for ss14",
"size": {
"x": 32,
"y": 32
},
"states": [
{
"name": "anom",
"delays": [
[
0.18625,
0.18625,
0.18625,
0.18625
]
]
},
{
"name": "pulse",
"delays": [
[
0.15625,
0.15625,
0.15625,
0.15625
]
]
}
]
}

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.5 KiB