Chem guidebook (#17123)

* im good at atomizing. welcome to half-finished chem guides.

* wagh

* e

* save work

* aa

* woweee UI

* finishing the last of it

* don't actually update the engine :(

---------

Co-authored-by: moonheart08 <moonheart08@users.noreply.github.com>
This commit is contained in:
Nemanja
2023-06-04 16:45:02 -04:00
committed by GitHub
parent 1e6dbd0b67
commit b9fb66f005
72 changed files with 1411 additions and 12 deletions

View File

@@ -0,0 +1,29 @@
using Content.Shared.Chemistry;
namespace Content.Client.Chemistry.EntitySystems;
/// <inheritdoc/>
public sealed class ChemistryGuideDataSystem : SharedChemistryGuideDataSystem
{
/// <inheritdoc/>
public override void Initialize()
{
base.Initialize();
SubscribeNetworkEvent<ReagentGuideRegistryChangedEvent>(OnReceiveRegistryUpdate);
}
private void OnReceiveRegistryUpdate(ReagentGuideRegistryChangedEvent message)
{
var data = message.Changeset;
foreach (var remove in data.Removed)
{
Registry.Remove(remove);
}
foreach (var (key, val) in data.GuideEntries)
{
Registry[key] = val;
}
}
}

View File

@@ -0,0 +1,58 @@
<BoxContainer xmlns="https://spacestation14.io"
xmlns:gfx="clr-namespace:Robust.Client.Graphics;assembly=Robust.Client"
Orientation="Vertical"
Margin="5 5 5 5">
<PanelContainer HorizontalExpand="True">
<PanelContainer.PanelOverride>
<gfx:StyleBoxFlat BorderThickness="1" BorderColor="#777777"/>
</PanelContainer.PanelOverride>
<BoxContainer Orientation="Vertical">
<PanelContainer Name="NameBackground" HorizontalExpand="True" VerticalExpand="False">
<RichTextLabel Name="ReagentName" HorizontalAlignment="Center"/>
</PanelContainer>
<BoxContainer Name="RecipesContainer" HorizontalExpand="True">
<Collapsible Orientation="Vertical" HorizontalExpand="True">
<CollapsibleHeading Title="{Loc 'guidebook-reagent-recipes-header'}"/>
<CollapsibleBody>
<BoxContainer Name="RecipesDescriptionContainer"
Margin="10 0 10 0"
Orientation="Horizontal"
HorizontalAlignment="Stretch"
HorizontalExpand="True">
<BoxContainer Orientation="Vertical" HorizontalExpand="True" VerticalAlignment="Center">
<RichTextLabel Name="ReactantsLabel"
HorizontalAlignment="Center"
VerticalAlignment="Center"/>
</BoxContainer>
<BoxContainer Orientation="Vertical" VerticalAlignment="Center">
<TextureRect TexturePath="/Textures/Interface/Misc/beakerlarge.png"/>
<Label Text="{Loc 'guidebook-reagent-recipes-mix'}"
HorizontalAlignment="Center"/>
</BoxContainer>
<BoxContainer Orientation="Vertical" HorizontalExpand="True" VerticalAlignment="Center">
<RichTextLabel Name="ProductsLabel"
HorizontalAlignment="Center"
VerticalAlignment="Center"/>
</BoxContainer>
</BoxContainer>
</CollapsibleBody>
</Collapsible>
</BoxContainer>
<BoxContainer Name="EffectsContainer" HorizontalExpand="True">
<Collapsible Orientation="Vertical">
<CollapsibleHeading Title="{Loc 'guidebook-reagent-effects-header'}"/>
<CollapsibleBody>
<BoxContainer Name="EffectsDescriptionContainer"
Orientation="Vertical"
Margin="10 0 10 0"
HorizontalExpand="True"/>
</CollapsibleBody>
</Collapsible>
</BoxContainer>
<BoxContainer Margin="10 5 10 10" HorizontalExpand="True">
<!-- The troublemaker !-->
<RichTextLabel Name="ReagentDescription" HorizontalAlignment="Left"/>
</BoxContainer>
</BoxContainer>
</PanelContainer>
</BoxContainer>

View File

@@ -0,0 +1,176 @@
using System.Diagnostics.CodeAnalysis;
using System.Linq;
using Content.Client.Chemistry.EntitySystems;
using Content.Client.Guidebook.Richtext;
using Content.Client.Message;
using Content.Shared.Chemistry.Reaction;
using Content.Shared.Chemistry.Reagent;
using JetBrains.Annotations;
using Robust.Client.AutoGenerated;
using Robust.Client.Graphics;
using Robust.Client.UserInterface;
using Robust.Client.UserInterface.Controls;
using Robust.Client.UserInterface.XAML;
using Robust.Shared.Prototypes;
using Robust.Shared.Utility;
namespace Content.Client.Guidebook.Controls;
/// <summary>
/// Control for embedding a reagent into a guidebook.
/// </summary>
[UsedImplicitly, GenerateTypedNameReferences]
public sealed partial class GuideReagentEmbed : BoxContainer, IDocumentTag
{
[Dependency] private readonly IEntitySystemManager _systemManager = default!;
[Dependency] private readonly IPrototypeManager _prototype = default!;
private readonly ChemistryGuideDataSystem _chemistryGuideData;
public GuideReagentEmbed()
{
RobustXamlLoader.Load(this);
IoCManager.InjectDependencies(this);
_chemistryGuideData = _systemManager.GetEntitySystem<ChemistryGuideDataSystem>();
MouseFilter = MouseFilterMode.Stop;
}
public GuideReagentEmbed(string reagent) : this()
{
GenerateControl(_prototype.Index<ReagentPrototype>(reagent));
}
public GuideReagentEmbed(ReagentPrototype reagent) : this()
{
GenerateControl(reagent);
}
public bool TryParseTag(Dictionary<string, string> args, [NotNullWhen(true)] out Control? control)
{
control = null;
if (!args.TryGetValue("Reagent", out var id))
{
Logger.Error("Reagent embed tag is missing reagent prototype argument");
return false;
}
if (!_prototype.TryIndex<ReagentPrototype>(id, out var reagent))
{
Logger.Error($"Specified reagent prototype \"{id}\" is not a valid reagent prototype");
return false;
}
GenerateControl(reagent);
control = this;
return true;
}
private void GenerateControl(ReagentPrototype reagent)
{
NameBackground.PanelOverride = new StyleBoxFlat
{
BackgroundColor = reagent.SubstanceColor
};
var textColor = Color.ToHsl(reagent.SubstanceColor).Z > 0.45
? Color.Black
: Color.White;
ReagentName.SetMarkup(Loc.GetString("guidebook-reagent-name",
("color", textColor), ("name", reagent.LocalizedName)));
#region Recipe
// by default, we assume that the reaction has the same ID as the reagent.
// if this isn't true, we'll loop through reactions.
if (!_prototype.TryIndex<ReactionPrototype>(reagent.ID, out var reactionPrototype))
{
reactionPrototype = _prototype.EnumeratePrototypes<ReactionPrototype>()
.FirstOrDefault(p => p.Products.ContainsKey(reagent.ID));
}
if (reactionPrototype != null)
{
var reactantMsg = new FormattedMessage();
var reactantsCount = reactionPrototype.Reactants.Count;
var i = 0;
foreach (var (product, reactant) in reactionPrototype.Reactants)
{
reactantMsg.AddMarkup(Loc.GetString("guidebook-reagent-recipes-reagent-display",
("reagent", _prototype.Index<ReagentPrototype>(product).LocalizedName), ("ratio", reactant.Amount)));
i++;
if (i < reactantsCount)
reactantMsg.PushNewline();
}
reactantMsg.Pop();
ReactantsLabel.SetMessage(reactantMsg);
var productMsg = new FormattedMessage();
var productCount = reactionPrototype.Products.Count;
var u = 0;
foreach (var (product, ratio) in reactionPrototype.Products)
{
productMsg.AddMarkup(Loc.GetString("guidebook-reagent-recipes-reagent-display",
("reagent", _prototype.Index<ReagentPrototype>(product).LocalizedName), ("ratio", ratio)));
u++;
if (u < productCount)
productMsg.PushNewline();
}
productMsg.Pop();
ProductsLabel.SetMessage(productMsg);
}
else
{
RecipesContainer.Visible = false;
}
#endregion
#region Effects
if (_chemistryGuideData.ReagentGuideRegistry.TryGetValue(reagent.ID, out var guideEntryRegistry) &&
guideEntryRegistry.GuideEntries != null &&
guideEntryRegistry.GuideEntries.Values.Any(pair => pair.EffectDescriptions.Any()))
{
EffectsDescriptionContainer.Children.Clear();
foreach (var (group, effect) in guideEntryRegistry.GuideEntries)
{
if (!effect.EffectDescriptions.Any())
continue;
var groupLabel = new RichTextLabel();
groupLabel.SetMarkup(Loc.GetString("guidebook-reagent-effects-metabolism-group-rate",
("group", group), ("rate", effect.MetabolismRate)));
var descriptionLabel = new RichTextLabel
{
Margin = new Thickness(25, 0, 10, 0)
};
var descMsg = new FormattedMessage();
var descriptionsCount = effect.EffectDescriptions.Length;
var i = 0;
foreach (var effectString in effect.EffectDescriptions)
{
descMsg.AddMarkup(effectString);
i++;
if (i < descriptionsCount)
descMsg.PushNewline();
}
descriptionLabel.SetMessage(descMsg);
EffectsDescriptionContainer.AddChild(groupLabel);
EffectsDescriptionContainer.AddChild(descriptionLabel);
}
}
else
{
EffectsContainer.Visible = false;
}
#endregion
FormattedMessage description = new();
description.AddText(reagent.LocalizedDescription);
description.PushNewline();
description.AddText(Loc.GetString("guidebook-reagent-physical-description",
("description", reagent.LocalizedPhysicalDescription)));
ReagentDescription.SetMessage(description);
}
}

View File

@@ -0,0 +1,4 @@
<BoxContainer xmlns="https://spacestation14.io"
Orientation="Vertical">
<BoxContainer Name="GroupContainer" Orientation="Vertical"/>
</BoxContainer>

View File

@@ -0,0 +1,60 @@
using System.Diagnostics.CodeAnalysis;
using System.Linq;
using Content.Client.Guidebook.Richtext;
using Content.Shared.Chemistry.Reagent;
using JetBrains.Annotations;
using Robust.Client.AutoGenerated;
using Robust.Client.UserInterface;
using Robust.Client.UserInterface.Controls;
using Robust.Client.UserInterface.XAML;
using Robust.Shared.Prototypes;
namespace Content.Client.Guidebook.Controls;
/// <summary>
/// Control for embedding a reagent into a guidebook.
/// </summary>
[UsedImplicitly, GenerateTypedNameReferences]
public sealed partial class GuideReagentGroupEmbed : BoxContainer, IDocumentTag
{
[Dependency] private readonly IPrototypeManager _prototype = default!;
public GuideReagentGroupEmbed()
{
RobustXamlLoader.Load(this);
IoCManager.InjectDependencies(this);
MouseFilter = MouseFilterMode.Stop;
}
public GuideReagentGroupEmbed(string group) : this()
{
var prototypes = _prototype.EnumeratePrototypes<ReagentPrototype>()
.Where(p => p.Group.Equals(group)).OrderBy(p => p.LocalizedName);
foreach (var reagent in prototypes)
{
var embed = new GuideReagentEmbed(reagent);
GroupContainer.AddChild(embed);
}
}
public bool TryParseTag(Dictionary<string, string> args, [NotNullWhen(true)] out Control? control)
{
control = null;
if (!args.TryGetValue("Group", out var group))
{
Logger.Error("Reagent group embed tag is missing group argument");
return false;
}
var prototypes = _prototype.EnumeratePrototypes<ReagentPrototype>()
.Where(p => p.Group.Equals(group)).OrderBy(p => p.LocalizedName);
foreach (var reagent in prototypes)
{
var embed = new GuideReagentEmbed(reagent);
GroupContainer.AddChild(embed);
}
control = this;
return true;
}
}

View File

@@ -0,0 +1,47 @@
using Content.Server.Administration;
using Content.Shared.Administration;
using Content.Shared.Chemistry.Reagent;
using Robust.Shared.Console;
using Robust.Shared.Prototypes;
namespace Content.Server.Chemistry.Commands;
[AdminCommand(AdminFlags.Debug)]
public sealed class DumpReagentGuideText : IConsoleCommand
{
[Dependency] private readonly IPrototypeManager _prototype = default!;
[Dependency] private readonly IEntitySystemManager _entSys = default!;
public string Command => "dumpreagentguidetext";
public string Description => "Dumps the guidebook text for a reagent to the console";
public string Help => "dumpreagentguidetext <reagent>";
public void Execute(IConsoleShell shell, string argStr, string[] args)
{
if (args.Length != 1)
{
shell.WriteError("Must have only 1 argument");
return;
}
if (!_prototype.TryIndex<ReagentPrototype>(args[0], out var reagent))
{
shell.WriteError($"Invalid prototype: {args[0]}");
return;
}
if (reagent.Metabolisms is null)
{
shell.WriteLine("Nothing to dump.");
return;
}
foreach (var (_, entry) in reagent.Metabolisms)
{
foreach (var effect in entry.Effects)
{
shell.WriteLine(effect.GuidebookEffectDescription(_prototype, _entSys) ?? $"[skipped effect of type {effect.GetType()}]");
}
}
}
}

View File

@@ -0,0 +1,67 @@
using Content.Shared.Chemistry;
using Content.Shared.Chemistry.Reagent;
using Robust.Server.Player;
using Robust.Shared.Enums;
using Robust.Shared.Prototypes;
namespace Content.Server.Chemistry.EntitySystems;
public sealed class ChemistryGuideDataSystem : SharedChemistryGuideDataSystem
{
[Dependency] private readonly IPlayerManager _player = default!;
/// <inheritdoc/>
public override void Initialize()
{
base.Initialize();
PrototypeManager.PrototypesReloaded += PrototypeManagerReload;
_player.PlayerStatusChanged += OnPlayerStatusChanged;
InitializeServerRegistry();
}
private void InitializeServerRegistry()
{
var changeset = new ReagentGuideChangeset(new Dictionary<string, ReagentGuideEntry>(), new HashSet<string>());
foreach (var proto in PrototypeManager.EnumeratePrototypes<ReagentPrototype>())
{
var entry = new ReagentGuideEntry(proto, PrototypeManager, EntityManager.EntitySysManager);
changeset.GuideEntries.Add(proto.ID, entry);
Registry[proto.ID] = entry;
}
var ev = new ReagentGuideRegistryChangedEvent(changeset);
RaiseNetworkEvent(ev);
}
private void OnPlayerStatusChanged(object? sender, SessionStatusEventArgs e)
{
if (e.NewStatus != SessionStatus.Connected)
return;
var sendEv = new ReagentGuideRegistryChangedEvent(new ReagentGuideChangeset(Registry, new HashSet<string>()));
RaiseNetworkEvent(sendEv, e.Session);
}
private void PrototypeManagerReload(PrototypesReloadedEventArgs obj)
{
if (!obj.ByType.TryGetValue(typeof(ReagentPrototype), out var reagents))
return;
var changeset = new ReagentGuideChangeset(new Dictionary<string, ReagentGuideEntry>(), new HashSet<string>());
foreach (var (id, proto) in reagents.Modified)
{
var reagentProto = (ReagentPrototype) proto;
var entry = new ReagentGuideEntry(reagentProto, PrototypeManager, EntityManager.EntitySysManager);
changeset.GuideEntries.Add(id, entry);
Registry[id] = entry;
}
var ev = new ReagentGuideRegistryChangedEvent(changeset);
RaiseNetworkEvent(ev);
}
}

View File

@@ -45,6 +45,10 @@ namespace Content.Server.Chemistry.ReactionEffects
[DataField("sound", required: true)] private SoundSpecifier _sound = default!;
public override bool ShouldLog => true;
protected override string ReagentEffectGuidebookText(IPrototypeManager prototype, IEntitySystemManager entSys)
=> Loc.GetString("reagent-effect-guidebook-missing");
public override LogImpact LogImpact => LogImpact.High;
public override void Effect(ReagentEffectArgs args)

View File

@@ -19,6 +19,12 @@ public sealed class CreateEntityReactionEffect : ReagentEffect
[DataField("number")]
public uint Number = 1;
protected override string? ReagentEffectGuidebookText(IPrototypeManager prototype, IEntitySystemManager entSys)
=> Loc.GetString("reagent-effect-guidebook-create-entity-reaction-effect",
("chance", Probability),
("entname", IoCManager.Resolve<IPrototypeManager>().Index<EntityPrototype>(Entity).Name),
("amount", Number));
public override void Effect(ReagentEffectArgs args)
{
var transform = args.EntityManager.GetComponent<TransformComponent>(args.SolutionEntity);

View File

@@ -3,6 +3,7 @@ using Content.Server.Explosion.EntitySystems;
using Content.Shared.Chemistry.Reagent;
using Content.Shared.Database;
using Content.Shared.Explosion;
using Robust.Shared.Prototypes;
using Robust.Shared.Serialization.TypeSerializers.Implementations.Custom.Prototype;
namespace Content.Server.Chemistry.ReactionEffects
@@ -24,7 +25,7 @@ namespace Content.Server.Chemistry.ReactionEffects
[DataField("maxIntensity")]
[JsonIgnore]
public float MaxIntensity = 5;
/// <summary>
/// How quickly intensity drops off as you move away from the epicenter
/// </summary>
@@ -51,6 +52,9 @@ namespace Content.Server.Chemistry.ReactionEffects
public float IntensityPerUnit = 1;
public override bool ShouldLog => true;
protected override string? ReagentEffectGuidebookText(IPrototypeManager prototype, IEntitySystemManager entSys)
=> Loc.GetString("reagent-effect-guidebook-explosion-reaction-effect", ("chance", Probability));
public override LogImpact LogImpact => LogImpact.High;
public override void Effect(ReagentEffectArgs args)

View File

@@ -0,0 +1 @@


View File

@@ -16,6 +16,10 @@ namespace Content.Server.Chemistry.ReactionEffects
/// </summary>
[DataField("temperature", required: true)] private float _temperature;
protected override string? ReagentEffectGuidebookText(IPrototypeManager prototype, IEntitySystemManager entSys)
=> Loc.GetString("reagent-effect-guidebook-set-solution-temperature-effect",
("chance", Probability), ("temperature", _temperature));
public override void Effect(ReagentEffectArgs args)
{
var solution = args.Source;
@@ -52,6 +56,10 @@ namespace Content.Server.Chemistry.ReactionEffects
/// </summary>
[DataField("scaled")] private bool _scaled;
protected override string? ReagentEffectGuidebookText(IPrototypeManager prototype, IEntitySystemManager entSys)
=> Loc.GetString("reagent-effect-guidebook-adjust-solution-temperature-effect",
("chance", Probability), ("deltasign", MathF.Sign(_delta)), ("mintemp", _minTemp), ("maxtemp", _maxTemp));
public override void Effect(ReagentEffectArgs args)
{
var solution = args.Source;
@@ -101,11 +109,15 @@ namespace Content.Server.Chemistry.ReactionEffects
var heatCap = solution.GetHeatCapacity(null);
var deltaT = _scaled
? _delta / heatCap * (float) args.Quantity
? _delta / heatCap * (float) args.Quantity
: _delta / heatCap;
solution.Temperature = Math.Clamp(solution.Temperature + deltaT, _minTemp, _maxTemp);
}
protected override string? ReagentEffectGuidebookText(IPrototypeManager prototype, IEntitySystemManager entSys)
=> Loc.GetString("reagent-effect-guidebook-adjust-solution-temperature-effect",
("chance", Probability), ("deltasign", MathF.Sign(_delta)), ("mintemp", _minTemp), ("maxtemp", _maxTemp));
}
}

View File

@@ -1,5 +1,6 @@
using Content.Server.Temperature.Components;
using Content.Shared.Chemistry.Reagent;
using Robust.Shared.Prototypes;
namespace Content.Server.Chemistry.ReagentEffectConditions
{
@@ -13,7 +14,7 @@ namespace Content.Server.Chemistry.ReagentEffectConditions
public float Min = 0;
[DataField("max")]
public float Max = float.MaxValue;
public float Max = float.PositiveInfinity;
public override bool Condition(ReagentEffectArgs args)
{
if (args.EntityManager.TryGetComponent(args.SolutionEntity, out TemperatureComponent? temp))
@@ -24,5 +25,12 @@ namespace Content.Server.Chemistry.ReagentEffectConditions
return false;
}
public override string GuidebookExplanation(IPrototypeManager prototype)
{
return Loc.GetString("reagent-effect-condition-guidebook-body-temperature",
("max", float.IsPositiveInfinity(Max) ? (float) int.MaxValue : Max),
("min", Min));
}
}
}

View File

@@ -1,6 +1,7 @@
using Content.Shared.Chemistry.Reagent;
using Content.Shared.Tag;
using JetBrains.Annotations;
using Robust.Shared.Prototypes;
using Robust.Shared.Serialization.TypeSerializers.Implementations.Custom.Prototype;
namespace Content.Server.Chemistry.ReagentEffectConditions;
@@ -21,4 +22,10 @@ public sealed class HasTag : ReagentEffectCondition
return false;
}
public override string GuidebookExplanation(IPrototypeManager prototype)
{
// this should somehow be made (much) nicer.
return Loc.GetString("reagent-effect-condition-guidebook-has-tag", ("tag", Tag), ("invert", Invert));
}
}

View File

@@ -1,6 +1,7 @@
using Content.Shared.Chemistry.Reagent;
using Content.Shared.Mobs;
using Content.Shared.Mobs.Components;
using Robust.Shared.Prototypes;
namespace Content.Server.Chemistry.ReagentEffectConditions
{
@@ -21,6 +22,11 @@ namespace Content.Server.Chemistry.ReagentEffectConditions
return false;
}
public override string GuidebookExplanation(IPrototypeManager prototype)
{
return Loc.GetString("reagent-effect-condition-guidebook-mob-state-condition", ("state", mobstate));
}
}
}

View File

@@ -1,6 +1,7 @@
using Content.Server.Body.Components;
using Content.Shared.Body.Prototypes;
using Content.Shared.Chemistry.Reagent;
using Robust.Shared.Prototypes;
using Robust.Shared.Serialization.TypeSerializers.Implementations.Custom.Prototype;
namespace Content.Server.Chemistry.ReagentEffectConditions
@@ -30,5 +31,12 @@ namespace Content.Server.Chemistry.ReagentEffectConditions
return ShouldHave;
return !ShouldHave;
}
public override string GuidebookExplanation(IPrototypeManager prototype)
{
return Loc.GetString("reagent-effect-condition-guidebook-organ-type",
("name", prototype.Index<MetabolizerTypePrototype>(Type).Name),
("shouldhave", ShouldHave));
}
}
}

View File

@@ -1,5 +1,6 @@
using Content.Shared.Chemistry.Reagent;
using Content.Shared.FixedPoint;
using Robust.Shared.Prototypes;
namespace Content.Server.Chemistry.ReagentEffectConditions
{
@@ -35,5 +36,17 @@ namespace Content.Server.Chemistry.ReagentEffectConditions
return quant >= Min && quant <= Max;
}
public override string GuidebookExplanation(IPrototypeManager prototype)
{
ReagentPrototype? reagentProto = null;
if (Reagent is not null)
prototype.TryIndex(Reagent, out reagentProto);
return Loc.GetString("reagent-effect-condition-guidebook-reagent-threshold",
("reagent", reagentProto?.LocalizedName ?? "this reagent"),
("max", Max == FixedPoint2.MaxValue ? (float) int.MaxValue : Max.Float()),
("min", Min.Float()));
}
}
}

View File

@@ -1,4 +1,5 @@
using Content.Shared.Chemistry.Reagent;
using Robust.Shared.Prototypes;
namespace Content.Server.Chemistry.ReagentEffectConditions
{
@@ -24,5 +25,12 @@ namespace Content.Server.Chemistry.ReagentEffectConditions
return true;
}
public override string GuidebookExplanation(IPrototypeManager prototype)
{
return Loc.GetString("reagent-effect-condition-guidebook-solution-temperature",
("max", float.IsPositiveInfinity(Max) ? (float) int.MaxValue : Max),
("min", Min));
}
}
}

View File

@@ -1,6 +1,7 @@
using Content.Shared.Chemistry.Reagent;
using Content.Shared.Damage;
using Content.Shared.FixedPoint;
using Robust.Shared.Prototypes;
namespace Content.Server.Chemistry.ReagentEffectConditions
{
@@ -23,5 +24,12 @@ namespace Content.Server.Chemistry.ReagentEffectConditions
return false;
}
public override string GuidebookExplanation(IPrototypeManager prototype)
{
return Loc.GetString("reagent-effect-condition-guidebook-total-damage",
("max", Max == FixedPoint2.MaxValue ? (float) int.MaxValue : Max.Float()),
("min", Min.Float()));
}
}
}

View File

@@ -1,5 +1,6 @@
using Content.Server.Xenoarchaeology.XenoArtifacts;
using Content.Shared.Chemistry.Reagent;
using Robust.Shared.Prototypes;
namespace Content.Server.Chemistry.ReagentEffects;
@@ -10,4 +11,7 @@ public sealed class ActivateArtifact : ReagentEffect
var artifact = args.EntityManager.EntitySysManager.GetEntitySystem<ArtifactSystem>();
artifact.TryActivateArtifact(args.SolutionEntity);
}
protected override string? ReagentEffectGuidebookText(IPrototypeManager prototype, IEntitySystemManager entSys) =>
Loc.GetString("reagent-effect-guidebook-activate-artifact", ("chance", Probability));
}

View File

@@ -1,6 +1,7 @@
using Content.Server.Chemistry.EntitySystems;
using Content.Shared.Chemistry.Reagent;
using JetBrains.Annotations;
using Robust.Shared.Prototypes;
namespace Content.Server.Chemistry.ReagentEffects
{
@@ -24,5 +25,8 @@ namespace Content.Server.Chemistry.ReagentEffects
.TryAddReagent(args.SolutionEntity, solutionContainer, args.Reagent.ID, args.Quantity, out var accepted))
args.Source?.RemoveReagent(args.Reagent.ID, accepted);
}
protected override string? ReagentEffectGuidebookText(IPrototypeManager prototype, IEntitySystemManager entSys) =>
Loc.GetString("reagent-effect-guidebook-missing", ("chance", Probability));
}
}

View File

@@ -1,5 +1,6 @@
using Content.Shared.Alert;
using Content.Shared.Chemistry.Reagent;
using Robust.Shared.Prototypes;
using Robust.Shared.Timing;
namespace Content.Server.Chemistry.ReagentEffects;
@@ -18,6 +19,9 @@ public sealed class AdjustAlert : ReagentEffect
[DataField("time")]
public float Time;
//JUSTIFICATION: This just changes some visuals, doesn't need to be documented.
protected override string? ReagentEffectGuidebookText(IPrototypeManager prototype, IEntitySystemManager entSys) => null;
public override void Effect(ReagentEffectArgs args)
{
var alertSys = EntitySystem.Get<AlertsSystem>();

View File

@@ -60,5 +60,27 @@ namespace Content.Server.Chemistry.ReagentEffects
}
}
}
protected override string? ReagentEffectGuidebookText(IPrototypeManager prototype, IEntitySystemManager entSys)
{
if (Reagent is not null && prototype.TryIndex(Reagent, out ReagentPrototype? reagentProto))
{
return Loc.GetString("reagent-effect-guidebook-adjust-reagent-reagent",
("chance", Probability),
("deltasign", MathF.Sign(Amount.Float())),
("reagent", reagentProto.LocalizedName),
("amount", MathF.Abs(Amount.Float())));
}
else if (Group is not null && prototype.TryIndex(Group, out MetabolismGroupPrototype? groupProto))
{
return Loc.GetString("reagent-effect-guidebook-adjust-reagent-group",
("chance", Probability),
("deltasign", MathF.Sign(Amount.Float())),
("group", groupProto.ID),
("amount", MathF.Abs(Amount.Float())));
}
throw new NotImplementedException();
}
}
}

View File

@@ -1,6 +1,7 @@
using Content.Server.Temperature.Components;
using Content.Server.Temperature.Systems;
using Content.Shared.Chemistry.Reagent;
using Robust.Shared.Prototypes;
namespace Content.Server.Chemistry.ReagentEffects
{
@@ -9,6 +10,12 @@ namespace Content.Server.Chemistry.ReagentEffects
[DataField("amount")]
public float Amount;
protected override string? ReagentEffectGuidebookText(IPrototypeManager prototype, IEntitySystemManager entSys)
=> Loc.GetString("reagent-effect-guidebook-adjust-temperature",
("chance", Probability),
("deltasign", MathF.Sign(Amount)),
("amount", MathF.Abs(Amount)));
public override void Effect(ReagentEffectArgs args)
{
if (args.EntityManager.TryGetComponent(args.SolutionEntity, out TemperatureComponent? temp))

View File

@@ -1,6 +1,7 @@
using Content.Shared.Chemistry.Reagent;
using JetBrains.Annotations;
using Content.Server.Body.Systems;
using Robust.Shared.Prototypes;
namespace Content.Server.Chemistry.ReactionEffects
{
@@ -12,6 +13,10 @@ namespace Content.Server.Chemistry.ReactionEffects
{
[DataField("cleanseRate")]
public float CleanseRate = 3.0f;
protected override string? ReagentEffectGuidebookText(IPrototypeManager prototype, IEntitySystemManager entSys)
=> Loc.GetString("reagent-effect-guidebook-chem-clean-bloodstream", ("chance", Probability));
public override void Effect(ReagentEffectArgs args)
{
if (args.Source == null || args.Reagent == null)

View File

@@ -2,6 +2,7 @@ using Content.Shared.Chemistry.Reagent;
using Content.Shared.Eye.Blinding;
using Content.Shared.Eye.Blinding.Systems;
using JetBrains.Annotations;
using Robust.Shared.Prototypes;
namespace Content.Server.Chemistry.ReagentEffects
{
@@ -17,6 +18,9 @@ namespace Content.Server.Chemistry.ReagentEffects
[DataField("amount")]
public int Amount = -1;
protected override string? ReagentEffectGuidebookText(IPrototypeManager prototype, IEntitySystemManager entSys)
=> Loc.GetString("reagent-effect-guidebook-cure-eye-damage", ("chance", Probability), ("deltasign", MathF.Sign(Amount)));
public override void Effect(ReagentEffectArgs args)
{
if (args.Scale != 1f) // huh?

View File

@@ -1,6 +1,7 @@
using Content.Shared.Chemistry.Reagent;
using Content.Server.Medical;
using JetBrains.Annotations;
using Robust.Shared.Prototypes;
namespace Content.Server.Chemistry.ReagentEffects
{
@@ -17,6 +18,9 @@ namespace Content.Server.Chemistry.ReagentEffects
[DataField("hungerAmount")]
public float HungerAmount = -40f;
protected override string? ReagentEffectGuidebookText(IPrototypeManager prototype, IEntitySystemManager entSys)
=> Loc.GetString("reagent-effect-guidebook-chem-vomit", ("chance", Probability));
public override void Effect(ReagentEffectArgs args)
{
if (args.Scale != 1f)

View File

@@ -2,6 +2,7 @@
using Content.Shared.Atmos;
using Content.Shared.Chemistry.Reagent;
using Content.Shared.Database;
using Robust.Shared.Prototypes;
namespace Content.Server.Chemistry.ReagentEffects;
@@ -17,6 +18,17 @@ public sealed class CreateGas : ReagentEffect
public float Multiplier = 3f;
public override bool ShouldLog => true;
protected override string? ReagentEffectGuidebookText(IPrototypeManager prototype, IEntitySystemManager entSys)
{
var atmos = entSys.GetEntitySystem<AtmosphereSystem>();
var gasProto = atmos.GetGas(Gas);
return Loc.GetString("reagent-effect-guidebook-create-gas",
("chance", Probability),
("moles", Multiplier),
("gas", gasProto.Name));
}
public override LogImpact LogImpact => LogImpact.High;
public override void Effect(ReagentEffectArgs args)

View File

@@ -1,5 +1,6 @@
using Content.Shared.Chemistry.Reagent;
using Content.Shared.Drunk;
using Robust.Shared.Prototypes;
namespace Content.Server.Chemistry.ReagentEffects;
@@ -17,6 +18,9 @@ public sealed class Drunk : ReagentEffect
[DataField("slurSpeech")]
public bool SlurSpeech = true;
protected override string? ReagentEffectGuidebookText(IPrototypeManager prototype, IEntitySystemManager entSys)
=> Loc.GetString("reagent-effect-guidebook-drunk", ("chance", Probability));
public override void Effect(ReagentEffectArgs args)
{
var boozePower = BoozePower;

View File

@@ -1,5 +1,6 @@
using Content.Server.Electrocution;
using Content.Shared.Chemistry.Reagent;
using Robust.Shared.Prototypes;
namespace Content.Server.Chemistry.ReagentEffects;
@@ -14,6 +15,9 @@ public sealed class Electrocute : ReagentEffect
/// </remarks>
[DataField("refresh")] public bool Refresh = true;
protected override string? ReagentEffectGuidebookText(IPrototypeManager prototype, IEntitySystemManager entSys)
=> Loc.GetString("reagent-effect-guidebook-electrocute", ("chance", Probability), ("time", ElectrocuteTime));
public override bool ShouldLog => true;
public override void Effect(ReagentEffectArgs args)

View File

@@ -2,6 +2,7 @@ using Content.Server.Chat.Systems;
using Content.Shared.Chat.Prototypes;
using Content.Shared.Chemistry.Reagent;
using JetBrains.Annotations;
using Robust.Shared.Prototypes;
using Robust.Shared.Serialization.TypeSerializers.Implementations.Custom.Prototype;
namespace Content.Server.Chemistry.ReagentEffects;
@@ -18,6 +19,10 @@ public sealed class Emote : ReagentEffect
[DataField("showInChat")]
public bool ShowInChat;
// JUSTIFICATION: Emoting is flavor, so same reason popup messages are not in here.
protected override string? ReagentEffectGuidebookText(IPrototypeManager prototype, IEntitySystemManager entSys)
=> null;
public override void Effect(ReagentEffectArgs args)
{
if (EmoteId == null)

View File

@@ -2,12 +2,16 @@ using Content.Server.Atmos.Components;
using Content.Server.Atmos.EntitySystems;
using Content.Shared.Chemistry.Reagent;
using JetBrains.Annotations;
using Robust.Shared.Prototypes;
namespace Content.Server.Chemistry.ReagentEffects
{
[UsedImplicitly]
public sealed class ExtinguishReaction : ReagentEffect
{
protected override string? ReagentEffectGuidebookText(IPrototypeManager prototype, IEntitySystemManager entSys)
=> Loc.GetString("reagent-effect-guidebook-extinguish-reaction", ("chance", Probability));
public override void Effect(ReagentEffectArgs args)
{
if (!args.EntityManager.TryGetComponent(args.SolutionEntity, out FlammableComponent? flammable)) return;

View File

@@ -3,6 +3,7 @@ using Content.Server.Atmos.EntitySystems;
using Content.Shared.Chemistry.Reagent;
using Content.Shared.Database;
using JetBrains.Annotations;
using Robust.Shared.Prototypes;
namespace Content.Server.Chemistry.ReagentEffects
{
@@ -13,6 +14,10 @@ namespace Content.Server.Chemistry.ReagentEffects
public float Multiplier = 0.05f;
public override bool ShouldLog => true;
protected override string? ReagentEffectGuidebookText(IPrototypeManager prototype, IEntitySystemManager entSys)
=> Loc.GetString("reagent-effect-guidebook-flammable-reaction", ("chance", Probability));
public override LogImpact LogImpact => LogImpact.Medium;
public override void Effect(ReagentEffectArgs args)

View File

@@ -1,8 +1,11 @@
using System.Linq;
using System.Text.Json.Serialization;
using Content.Shared.Chemistry.Reagent;
using Content.Shared.Damage;
using Content.Shared.FixedPoint;
using Content.Shared.Localizations;
using JetBrains.Annotations;
using Robust.Shared.Prototypes;
namespace Content.Server.Chemistry.ReagentEffects
{
@@ -31,6 +34,38 @@ namespace Content.Server.Chemistry.ReagentEffects
[JsonPropertyName("ignoreResistances")]
public bool IgnoreResistances = true;
protected override string? ReagentEffectGuidebookText(IPrototypeManager prototype, IEntitySystemManager entSys)
{
var damages = new List<string>();
var heals = false;
var deals = false;
// TODO: This should be smarter. Namely, not showing a damage type as being in a group unless every damage type in the group is present and equal in value.
foreach (var (kind, amount) in Damage.GetDamagePerGroup())
{
var sign = MathF.Sign(amount.Float());
if (sign < 0)
heals = true;
if (sign > 0)
deals = true;
damages.Add(
Loc.GetString("health-change-display",
("kind", kind),
("amount", MathF.Abs(amount.Float())),
("deltasign", sign)
));
}
var healsordeals = heals ? (deals ? "both" : "heals") : (deals ? "deals" : "none");
return Loc.GetString("reagent-effect-guidebook-health-change",
("chance", Probability),
("changes", ContentLocalizationManager.FormatList(damages)),
("healsordeals", healsordeals));
}
public override void Effect(ReagentEffectArgs args)
{
var scale = ScaleByQuantity ? args.Quantity : FixedPoint2.New(1);

View File

@@ -1,6 +1,7 @@
using Content.Server.Atmos.EntitySystems;
using Content.Shared.Chemistry.Reagent;
using Content.Shared.Database;
using Robust.Shared.Prototypes;
namespace Content.Server.Chemistry.ReagentEffects;
@@ -10,6 +11,10 @@ namespace Content.Server.Chemistry.ReagentEffects;
public sealed class Ignite : ReagentEffect
{
public override bool ShouldLog => true;
protected override string? ReagentEffectGuidebookText(IPrototypeManager prototype, IEntitySystemManager entSys)
=> Loc.GetString("reagent-effect-guidebook-ignite", ("chance", Probability));
public override LogImpact LogImpact => LogImpact.Medium;
public override void Effect(ReagentEffectArgs args)

View File

@@ -2,11 +2,16 @@ using Content.Server.Ghost.Roles.Components;
using Content.Server.Mind.Components;
using Content.Server.Speech.Components;
using Content.Shared.Chemistry.Reagent;
using Content.Server.Ghost.Roles.Components;
using Robust.Shared.Prototypes;
namespace Content.Server.Chemistry.ReagentEffects;
public sealed class MakeSentient : ReagentEffect
{
protected override string? ReagentEffectGuidebookText(IPrototypeManager prototype, IEntitySystemManager entSys)
=> Loc.GetString("reagent-effect-guidebook-make-sentient", ("chance", Probability));
public override void Effect(ReagentEffectArgs args)
{
var entityManager = args.EntityManager;

View File

@@ -1,6 +1,7 @@
using Content.Server.Body.Components;
using Content.Server.Body.Systems;
using Content.Shared.Chemistry.Reagent;
using Robust.Shared.Prototypes;
namespace Content.Server.Chemistry.ReagentEffects;
@@ -12,6 +13,10 @@ public sealed class ModifyBleedAmount : ReagentEffect
[DataField("amount")]
public float Amount = -1.0f;
protected override string? ReagentEffectGuidebookText(IPrototypeManager prototype, IEntitySystemManager entSys)
=> Loc.GetString("reagent-effect-guidebook-modify-bleed-amount", ("chance", Probability),
("deltasign", MathF.Sign(Amount)));
public override void Effect(ReagentEffectArgs args)
{
if (args.EntityManager.TryGetComponent<BloodstreamComponent>(args.SolutionEntity, out var blood))

View File

@@ -2,6 +2,7 @@
using Content.Server.Body.Systems;
using Content.Shared.Chemistry.Reagent;
using Content.Shared.FixedPoint;
using Robust.Shared.Prototypes;
namespace Content.Server.Chemistry.ReagentEffects;
@@ -13,6 +14,10 @@ public sealed class ModifyBloodLevel : ReagentEffect
[DataField("amount")]
public FixedPoint2 Amount = 1.0f;
protected override string? ReagentEffectGuidebookText(IPrototypeManager prototype, IEntitySystemManager entSys)
=> Loc.GetString("reagent-effect-guidebook-modify-blood-level", ("chance", Probability),
("deltasign", MathF.Sign(Amount.Float())));
public override void Effect(ReagentEffectArgs args)
{
if (args.EntityManager.TryGetComponent<BloodstreamComponent>(args.SolutionEntity, out var blood))

View File

@@ -1,6 +1,7 @@
using Content.Server.Body.Components;
using Content.Shared.Atmos;
using Content.Shared.Chemistry.Reagent;
using Robust.Shared.Prototypes;
namespace Content.Server.Chemistry.ReagentEffects;
@@ -9,6 +10,10 @@ public sealed class ModifyLungGas : ReagentEffect
[DataField("ratios", required: true)]
private Dictionary<Gas, float> _ratios = default!;
// JUSTIFICATION: This is internal magic that players never directly interact with.
protected override string? ReagentEffectGuidebookText(IPrototypeManager prototype, IEntitySystemManager entSys)
=> null;
public override void Effect(ReagentEffectArgs args)
{
if (args.EntityManager.TryGetComponent<LungComponent>(args.OrganEntity, out var lung))

View File

@@ -1,6 +1,7 @@
using Content.Shared.Chemistry.Components;
using Content.Shared.Chemistry.Reagent;
using Content.Shared.Movement.Systems;
using Robust.Shared.Prototypes;
using Robust.Shared.Timing;
namespace Content.Server.Chemistry.ReagentEffects
@@ -29,6 +30,14 @@ namespace Content.Server.Chemistry.ReagentEffects
[DataField("statusLifetime")]
public float StatusLifetime = 2f;
protected override string? ReagentEffectGuidebookText(IPrototypeManager prototype, IEntitySystemManager entSys)
{
return Loc.GetString("reagent-effect-guidebook-movespeed-modifier",
("chance", Probability),
("walkspeed", WalkSpeedModifier),
("time", StatusLifetime));
}
/// <summary>
/// Remove reagent at set rate, changes the movespeed modifiers and adds a MovespeedModifierMetabolismComponent if not already there.
/// </summary>

View File

@@ -1,6 +1,7 @@
using Content.Server.Body.Components;
using Content.Server.Body.Systems;
using Content.Shared.Chemistry.Reagent;
using Robust.Shared.Prototypes;
namespace Content.Server.Chemistry.ReagentEffects;
@@ -9,6 +10,10 @@ public sealed class Oxygenate : ReagentEffect
[DataField("factor")]
public float Factor = 1f;
// JUSTIFICATION: This is internal magic that players never directly interact with.
protected override string? ReagentEffectGuidebookText(IPrototypeManager prototype, IEntitySystemManager entSys)
=> null;
public override void Effect(ReagentEffectArgs args)
{
if (args.EntityManager.TryGetComponent<RespiratorComponent>(args.SolutionEntity, out var resp))

View File

@@ -1,5 +1,6 @@
using Content.Shared.Chemistry.Reagent;
using Content.Server.Stunnable;
using Robust.Shared.Prototypes;
namespace Content.Server.Chemistry.ReagentEffects;
@@ -12,6 +13,11 @@ public sealed class Paralyze : ReagentEffect
/// </remarks>
[DataField("refresh")] public bool Refresh = true;
protected override string? ReagentEffectGuidebookText(IPrototypeManager prototype, IEntitySystemManager entSys)
=> Loc.GetString("reagent-effect-guidebook-paralyze",
("chance", Probability),
("time", ParalyzeTime));
public override void Effect(ReagentEffectArgs args)
{
var paralyzeTime = ParalyzeTime;

View File

@@ -1,6 +1,7 @@
using System.Diagnostics.CodeAnalysis;
using Content.Server.Botany.Components;
using Content.Shared.Chemistry.Reagent;
using Robust.Shared.Prototypes;
using Robust.Shared.Random;
namespace Content.Server.Chemistry.ReagentEffects.PlantMetabolism
@@ -35,5 +36,7 @@ namespace Content.Server.Chemistry.ReagentEffects.PlantMetabolism
// Dependencies are never injected for reagents if you intend to do that for this.
return !(Prob <= 0f) && IoCManager.Resolve<IRobustRandom>().Prob(Prob);
}
protected override string? ReagentEffectGuidebookText(IPrototypeManager prototype, IEntitySystemManager entSys) => Loc.GetString("reagent-effect-guidebook-missing", ("chance", Probability));
}
}

View File

@@ -1,6 +1,7 @@
using Content.Server.Botany.Components;
using Content.Shared.Chemistry.Reagent;
using JetBrains.Annotations;
using Robust.Shared.Prototypes;
using Robust.Shared.Random;
namespace Content.Server.Chemistry.ReagentEffects.PlantMetabolism
@@ -26,5 +27,7 @@ namespace Content.Server.Chemistry.ReagentEffects.PlantMetabolism
plantHolderComp.SkipAging++;
plantHolderComp.ForceUpdate = true;
}
protected override string? ReagentEffectGuidebookText(IPrototypeManager prototype, IEntitySystemManager entSys) => Loc.GetString("reagent-effect-guidebook-missing", ("chance", Probability));
}
}

View File

@@ -2,6 +2,7 @@ using Content.Server.Botany.Components;
using Content.Server.Botany.Systems;
using Content.Shared.Chemistry.Reagent;
using JetBrains.Annotations;
using Robust.Shared.Prototypes;
using Robust.Shared.Random;
namespace Content.Server.Chemistry.ReagentEffects.PlantMetabolism
@@ -34,5 +35,7 @@ namespace Content.Server.Chemistry.ReagentEffects.PlantMetabolism
plantHolderComp.Seed.Endurance++;
}
}
protected override string? ReagentEffectGuidebookText(IPrototypeManager prototype, IEntitySystemManager entSys) => Loc.GetString("reagent-effect-guidebook-missing", ("chance", Probability));
}
}

View File

@@ -2,6 +2,7 @@ using Content.Server.Botany.Components;
using Content.Server.Botany.Systems;
using Content.Shared.Chemistry.Reagent;
using JetBrains.Annotations;
using Robust.Shared.Prototypes;
using Robust.Shared.Random;
namespace Content.Server.Chemistry.ReagentEffects.PlantMetabolism
@@ -47,5 +48,7 @@ namespace Content.Server.Chemistry.ReagentEffects.PlantMetabolism
plantHolderComp.Seed.Yield--;
}
}
protected override string? ReagentEffectGuidebookText(IPrototypeManager prototype, IEntitySystemManager entSys) => Loc.GetString("reagent-effect-guidebook-missing", ("chance", Probability));
}
}

View File

@@ -1,6 +1,7 @@
using Content.Shared.Chemistry.Reagent;
using Content.Shared.Popups;
using Robust.Shared.Player;
using Robust.Shared.Prototypes;
using Robust.Shared.Random;
namespace Content.Server.Chemistry.ReagentEffects
@@ -16,6 +17,10 @@ namespace Content.Server.Chemistry.ReagentEffects
[DataField("visualType")]
public PopupType VisualType = PopupType.Small;
// JUSTIFICATION: This is purely cosmetic.
protected override string? ReagentEffectGuidebookText(IPrototypeManager prototype, IEntitySystemManager entSys)
=> null;
public override void Effect(ReagentEffectArgs args)
{
var popupSys = args.EntityManager.EntitySysManager.GetEntitySystem<SharedPopupSystem>();

View File

@@ -1,6 +1,7 @@
using Content.Server.Traits.Assorted;
using Content.Shared.Chemistry.Reagent;
using JetBrains.Annotations;
using Robust.Shared.Prototypes;
namespace Content.Server.Chemistry.ReagentEffects;
@@ -16,6 +17,9 @@ public sealed class ResetNarcolepsy : ReagentEffect
[DataField("TimerReset")]
public int TimerReset = 600;
protected override string? ReagentEffectGuidebookText(IPrototypeManager prototype, IEntitySystemManager entSys)
=> Loc.GetString("reagent-effect-guidebook-reset-narcolepsy", ("chance", Probability));
public override void Effect(ReagentEffectArgs args)
{
if (args.Scale != 1f)

View File

@@ -2,6 +2,7 @@
using Content.Shared.Chemistry.Reagent;
using Content.Shared.Nutrition.Components;
using Content.Shared.Nutrition.EntitySystems;
using Robust.Shared.Prototypes;
namespace Content.Server.Chemistry.ReagentEffects
{
@@ -11,10 +12,12 @@ namespace Content.Server.Chemistry.ReagentEffects
/// </summary>
public sealed class SatiateHunger : ReagentEffect
{
private const float DefaultNutritionFactor = 3.0f;
/// <summary>
/// How much hunger is satiated when 1u of the reagent is metabolized
/// </summary>
[DataField("factor")] public float NutritionFactor { get; set; } = 3.0f;
[DataField("factor")] public float NutritionFactor { get; set; } = DefaultNutritionFactor;
//Remove reagent at set rate, satiate hunger if a HungerComponent can be found
public override void Effect(ReagentEffectArgs args)
@@ -24,5 +27,8 @@ namespace Content.Server.Chemistry.ReagentEffects
return;
entman.System<HungerSystem>().ModifyHunger(args.SolutionEntity, NutritionFactor * (float) args.Quantity, hunger);
}
protected override string? ReagentEffectGuidebookText(IPrototypeManager prototype, IEntitySystemManager entSys)
=> Loc.GetString("reagent-effect-guidebook-satiate-hunger", ("chance", Probability), ("relative", NutritionFactor / DefaultNutritionFactor));
}
}

View File

@@ -1,6 +1,7 @@
using Content.Server.Nutrition.Components;
using Content.Shared.Chemistry.Reagent;
using Content.Server.Nutrition.EntitySystems;
using Robust.Shared.Prototypes;
namespace Content.Server.Chemistry.ReagentEffects
{
@@ -10,10 +11,12 @@ namespace Content.Server.Chemistry.ReagentEffects
/// </summary>
public sealed class SatiateThirst : ReagentEffect
{
private const float DefaultHydrationFactor = 3.0f;
/// How much thirst is satiated each metabolism tick. Not currently tied to
/// rate or anything.
[DataField("factor")]
public float HydrationFactor { get; set; } = 3.0f;
public float HydrationFactor { get; set; } = DefaultHydrationFactor;
/// Satiate thirst if a ThirstComponent can be found
public override void Effect(ReagentEffectArgs args)
@@ -21,5 +24,8 @@ namespace Content.Server.Chemistry.ReagentEffects
if (args.EntityManager.TryGetComponent(args.SolutionEntity, out ThirstComponent? thirst))
EntitySystem.Get<ThirstSystem>().UpdateThirst(thirst, HydrationFactor);
}
protected override string? ReagentEffectGuidebookText(IPrototypeManager prototype, IEntitySystemManager entSys)
=> Loc.GetString("reagent-effect-guidebook-satiate-thirst", ("chance", Probability), ("relative", HydrationFactor / DefaultHydrationFactor));
}
}

View File

@@ -1,6 +1,7 @@
using Content.Shared.Chemistry.Reagent;
using Content.Shared.StatusEffect;
using JetBrains.Annotations;
using Robust.Shared.Prototypes;
namespace Content.Server.Chemistry.ReagentEffects.StatusEffects
{
@@ -57,6 +58,13 @@ namespace Content.Server.Chemistry.ReagentEffects.StatusEffects
statusSys.TrySetTime(args.SolutionEntity, Key, TimeSpan.FromSeconds(time));
}
}
protected override string? ReagentEffectGuidebookText(IPrototypeManager prototype, IEntitySystemManager entSys) => Loc.GetString(
"reagent-effect-guidebook-status-effect",
("chance", Probability),
("type", Type),
("time", Time),
("key", $"reagent-effect-status-effect-{Key}"));
}
public enum StatusEffectMetabolismType

View File

@@ -1,5 +1,6 @@
using Content.Shared.Chemistry.Reagent;
using Content.Shared.Jittering;
using Robust.Shared.Prototypes;
namespace Content.Server.Chemistry.ReagentEffects.StatusEffects
{
@@ -33,5 +34,8 @@ namespace Content.Server.Chemistry.ReagentEffects.StatusEffects
args.EntityManager.EntitySysManager.GetEntitySystem<SharedJitteringSystem>()
.DoJitter(args.SolutionEntity, TimeSpan.FromSeconds(time), Refresh, Amplitude, Frequency);
}
protected override string? ReagentEffectGuidebookText(IPrototypeManager prototype, IEntitySystemManager entSys) =>
Loc.GetString("reagent-effect-guidebook-jittering", ("chance", Probability));
}
}

View File

@@ -2,12 +2,16 @@ using Content.Server.Nutrition.EntitySystems;
using Content.Shared.Chemistry.Reagent;
using Content.Shared.Nutrition.Components;
using JetBrains.Annotations;
using Robust.Shared.Prototypes;
namespace Content.Server.Chemistry.ReagentEffects
{
[UsedImplicitly]
public sealed class WashCreamPieReaction : ReagentEffect
{
protected override string? ReagentEffectGuidebookText(IPrototypeManager prototype, IEntitySystemManager entSys)
=> Loc.GetString("reagent-effect-guidebook-wash-cream-pie-reaction", ("chance", Probability));
public override void Effect(ReagentEffectArgs args)
{
if (!args.EntityManager.TryGetComponent(args.SolutionEntity, out CreamPiedComponent? creamPied)) return;

View File

@@ -7,5 +7,8 @@ namespace Content.Shared.Body.Prototypes
{
[IdDataField]
public string ID { get; } = default!;
[DataField("name", required: true)]
public string Name { get; } = default!;
}
}

View File

@@ -1,8 +1,11 @@
using System.Text.Json.Serialization;
using System.Linq;
using System.Text.Json.Serialization;
using Content.Shared.Chemistry.Components;
using Content.Shared.Database;
using Content.Shared.FixedPoint;
using Content.Shared.Localizations;
using JetBrains.Annotations;
using Robust.Shared.Prototypes;
using Robust.Shared.Random;
namespace Content.Shared.Chemistry.Reagent
@@ -23,6 +26,10 @@ namespace Content.Shared.Chemistry.Reagent
[DataField("conditions")]
public ReagentEffectCondition[]? Conditions;
public virtual string ReagentEffectFormat => "guidebook-reagent-effect-description";
protected abstract string? ReagentEffectGuidebookText(IPrototypeManager prototype, IEntitySystemManager entSys); // => Loc.GetString("reagent-effect-guidebook-missing", ("chance", Probability));
/// <summary>
/// What's the chance, from 0 to 1, that this effect will occur?
/// </summary>
@@ -42,6 +49,23 @@ namespace Content.Shared.Chemistry.Reagent
public virtual bool ShouldLog { get; } = false;
public abstract void Effect(ReagentEffectArgs args);
/// <summary>
/// Produces a localized, bbcode'd guidebook description for this effect.
/// </summary>
/// <returns></returns>
public string? GuidebookEffectDescription(IPrototypeManager prototype, IEntitySystemManager entSys)
{
var effect = ReagentEffectGuidebookText(prototype, entSys);
if (effect is null)
return null;
return Loc.GetString(ReagentEffectFormat, ("effect", effect), ("chance", Probability),
("conditionCount", Conditions?.Length ?? 0),
("conditions",
ContentLocalizationManager.FormatList(Conditions?.Select(x => x.GuidebookExplanation(prototype)).ToList() ??
new List<string>())));
}
}
public static class ReagentEffectExt

View File

@@ -1,5 +1,6 @@
using System.Text.Json.Serialization;
using JetBrains.Annotations;
using Robust.Shared.Prototypes;
namespace Content.Shared.Chemistry.Reagent
{
@@ -10,5 +11,12 @@ namespace Content.Shared.Chemistry.Reagent
[JsonPropertyName("id")] private protected string _id => this.GetType().Name;
public abstract bool Condition(ReagentEffectArgs args);
/// <summary>
/// Effect explanations are of the form "[chance to] [action] when [condition] and [condition]"
/// </summary>
/// <param name="prototype"></param>
/// <returns></returns>
public abstract string GuidebookExplanation(IPrototypeManager prototype);
}
}

View File

@@ -1,4 +1,5 @@
using System.Text.Json.Serialization;
using System.Linq;
using System.Text.Json.Serialization;
using Content.Shared.Administration.Logs;
using Content.Shared.Body.Prototypes;
using Content.Shared.Chemistry.Components;
@@ -9,6 +10,7 @@ using Robust.Shared.Audio;
using Robust.Shared.Map;
using Robust.Shared.Prototypes;
using Robust.Shared.Random;
using Robust.Shared.Serialization;
using Robust.Shared.Serialization.TypeSerializers.Implementations.Custom.Prototype;
using Robust.Shared.Serialization.TypeSerializers.Implementations.Custom.Prototype.Array;
using Robust.Shared.Serialization.TypeSerializers.Implementations.Custom.Prototype.Dictionary;
@@ -157,6 +159,23 @@ namespace Content.Shared.Chemistry.Reagent
}
}
[Serializable, NetSerializable]
public struct ReagentGuideEntry
{
public string ReagentPrototype;
public Dictionary<string, ReagentEffectsGuideEntry>? GuideEntries;
public ReagentGuideEntry(ReagentPrototype proto, IPrototypeManager prototype, IEntitySystemManager entSys)
{
ReagentPrototype = proto.ID;
GuideEntries = proto.Metabolisms?
.Select(x => (x.Key, x.Value.MakeGuideEntry(prototype, entSys)))
.ToDictionary(x => x.Key, x => x.Item2);
}
}
[DataDefinition]
public sealed class ReagentEffectsEntry
{
@@ -173,6 +192,30 @@ namespace Content.Shared.Chemistry.Reagent
[JsonPropertyName("effects")]
[DataField("effects", required: true)]
public ReagentEffect[] Effects = default!;
public ReagentEffectsGuideEntry MakeGuideEntry(IPrototypeManager prototype, IEntitySystemManager entSys)
{
return new ReagentEffectsGuideEntry(MetabolismRate,
Effects
.Select(x => x.GuidebookEffectDescription(prototype, entSys)) // hate.
.Where(x => x is not null)
.Select(x => x!)
.ToArray());
}
}
[Serializable, NetSerializable]
public struct ReagentEffectsGuideEntry
{
public FixedPoint2 MetabolismRate;
public string[] EffectDescriptions;
public ReagentEffectsGuideEntry(FixedPoint2 metabolismRate, string[] effectDescriptions)
{
MetabolismRate = metabolismRate;
EffectDescriptions = effectDescriptions;
}
}
[DataDefinition]

View File

@@ -0,0 +1,42 @@
using Content.Shared.Chemistry.Reagent;
using Robust.Shared.Prototypes;
using Robust.Shared.Serialization;
namespace Content.Shared.Chemistry;
/// <summary>
/// This handles the chemistry guidebook and caching it.
/// </summary>
public abstract class SharedChemistryGuideDataSystem : EntitySystem
{
[Dependency] protected readonly IPrototypeManager PrototypeManager = default!;
protected readonly Dictionary<string, ReagentGuideEntry> Registry = new();
public IReadOnlyDictionary<string, ReagentGuideEntry> ReagentGuideRegistry => Registry;
}
[Serializable, NetSerializable]
public sealed class ReagentGuideRegistryChangedEvent : EntityEventArgs
{
public ReagentGuideChangeset Changeset;
public ReagentGuideRegistryChangedEvent(ReagentGuideChangeset changeset)
{
Changeset = changeset;
}
}
[Serializable, NetSerializable]
public sealed class ReagentGuideChangeset
{
public Dictionary<string,ReagentGuideEntry> GuideEntries;
public HashSet<string> Removed;
public ReagentGuideChangeset(Dictionary<string, ReagentGuideEntry> guideEntries, HashSet<string> removed)
{
GuideEntries = guideEntries;
Removed = removed;
}
}

View File

@@ -1,4 +1,7 @@
using System.Globalization;
using System.Linq;
using System.Text.RegularExpressions;
using Robust.Shared.Utility;
namespace Content.Shared.Localizations
{
@@ -31,13 +34,96 @@ namespace Content.Shared.Localizations
_loc.AddFunction(culture, "UNITS", FormatUnits);
_loc.AddFunction(culture, "TOSTRING", args => FormatToString(culture, args));
_loc.AddFunction(culture, "LOC", FormatLoc);
_loc.AddFunction(culture, "NATURALFIXED", FormatNaturalFixed);
_loc.AddFunction(culture, "NATURALPERCENT", FormatNaturalPercent);
/*
* The following language functions are specific to the english localization. When working on your own
* localization you should NOT modify these, instead add new functions specific to your language/culture.
* This ensures the english translations continue to work as expected when fallbacks are needed.
*/
var cultureEn = new CultureInfo("en-US");
_loc.AddFunction(cultureEn, "MAKEPLURAL", FormatMakePlural);
_loc.AddFunction(cultureEn, "MANY", FormatMany);
}
private ILocValue FormatMany(LocArgs args)
{
var count = ((LocValueNumber) args.Args[1]).Value;
if (Math.Abs(count - 1) < 0.0001f)
{
return (LocValueString) args.Args[0];
}
else
{
return (LocValueString) FormatMakePlural(args);
}
}
private ILocValue FormatNaturalPercent(LocArgs args)
{
var number = ((LocValueNumber) args.Args[0]).Value * 100;
var maxDecimals = (int)Math.Floor(((LocValueNumber) args.Args[1]).Value);
var formatter = (NumberFormatInfo)NumberFormatInfo.GetInstance(CultureInfo.GetCultureInfo(Culture)).Clone();
formatter.NumberDecimalDigits = maxDecimals;
return new LocValueString(string.Format(formatter, "{0:N}", number).TrimEnd('0').TrimEnd('.') + "%");
}
private ILocValue FormatNaturalFixed(LocArgs args)
{
var number = ((LocValueNumber) args.Args[0]).Value;
var maxDecimals = (int)Math.Floor(((LocValueNumber) args.Args[1]).Value);
var formatter = (NumberFormatInfo)NumberFormatInfo.GetInstance(CultureInfo.GetCultureInfo(Culture)).Clone();
formatter.NumberDecimalDigits = maxDecimals;
return new LocValueString(string.Format(formatter, "{0:N}", number).TrimEnd('0').TrimEnd('.'));
}
private static readonly Regex PluralEsRule = new("^.*(s|sh|ch|x|z)$");
private ILocValue FormatMakePlural(LocArgs args)
{
var text = ((LocValueString) args.Args[0]).Value;
var split = text.Split(" ", 1);
var firstWord = split[0];
if (PluralEsRule.IsMatch(firstWord))
{
if (split.Length == 1)
return new LocValueString($"{firstWord}es");
else
return new LocValueString($"{firstWord}es {split[1]}");
}
else
{
if (split.Length == 1)
return new LocValueString($"{firstWord}s");
else
return new LocValueString($"{firstWord}s {split[1]}");
}
}
// TODO: allow fluent to take in lists of strings so this can be a format function like it should be.
/// <summary>
/// Formats a list as per english grammar rules.
/// </summary>
public static string FormatList(List<string> list)
{
return list.Count switch
{
<= 0 => string.Empty,
1 => list[0],
2 => $"{list[0]} and {list[1]}",
> 2 => $"{string.Join(", ", list.GetRange(0, list.Count - 2))}, and {list[^1]}"
};
}
private static ILocValue FormatLoc(LocArgs args)
{
var id = ((LocValueString)args.Args[0]).Value;
var id = ((LocValueString) args.Args[0]).Value;
return new LocValueString(Loc.GetString(id));
return new LocValueString(Loc.GetString(id, args.Options.Select(x => (x.Key, x.Value.Value!)).ToArray()));
}
private static ILocValue FormatToString(CultureInfo culture, LocArgs args)
@@ -115,8 +201,8 @@ namespace Content.Shared.Localizations
//
// Note that the closing brace isn't replaced so that format specifiers can be applied.
var res = String.Format(
fmtstr.Replace("{UNIT", "{" + $"{fargs.Length - 1}"),
fargs
fmtstr.Replace("{UNIT", "{" + $"{fargs.Length - 1}"),
fargs
);
return new LocValueString(res);

View File

@@ -0,0 +1,50 @@
reagent-effect-condition-guidebook-total-damage =
{ $max ->
[2147483648] it has at least {NATURALFIXED($min, 2)} total damage
*[other] { $min ->
[0] it has at most {NATURALFIXED($max, 2)} total damage
*[other] it has between {NATURALFIXED($min, 2)} and {NATURALFIXED($max, 2)} total damage
}
}
reagent-effect-condition-guidebook-reagent-threshold =
{ $max ->
[2147483648] there's at least {NATURALFIXED($min, 2)}u of {$reagent}
*[other] { $min ->
[0] there's at most {NATURALFIXED($max, 2)}u of {$reagent}
*[other] there's between {NATURALFIXED($min, 2)}u and {NATURALFIXED($max, 2)}u of {$reagent}
}
}
reagent-effect-condition-guidebook-mob-state-condition =
the mob is { $state }
reagent-effect-condition-guidebook-solution-temperature =
the solution's temperature is { $max ->
[2147483648] at least {NATURALFIXED($min, 2)}k
*[other] { $min ->
[0] at most {NATURALFIXED($max, 2)}k
*[other] between {NATURALFIXED($min, 2)}k and {NATURALFIXED($max, 2)}k
}
}
reagent-effect-condition-guidebook-body-temperature =
the body's temperature is { $max ->
[2147483648] at least {NATURALFIXED($min, 2)}k
*[other] { $min ->
[0] at most {NATURALFIXED($max, 2)}k
*[other] between {NATURALFIXED($min, 2)}k and {NATURALFIXED($max, 2)}k
}
}
reagent-effect-condition-guidebook-organ-type =
the metabolizing organ { $shouldhave ->
[true] is
*[false] is not
} {INDEFINITE($name)} {$name} organ
reagent-effect-condition-guidebook-has-tag =
the target { $invert ->
[true] does not have
*[false] has
} the tag {$tag}

View File

@@ -0,0 +1,16 @@
guidebook-reagent-effect-description =
{$chance ->
[1] { $effect }
*[other] Has a { NATURALPERCENT($chance, 2) } chance to { $effect }
}{ $conditionCount ->
[0] .
*[other] {" "}when { $conditions }.
}
guidebook-reagent-name = [bold][color={$color}]{CAPITALIZE($name)}[/color][/bold]
guidebook-reagent-recipes-header = Recipe
guidebook-reagent-recipes-reagent-display = [bold]{$reagent}[/bold] \[{$ratio}\]
guidebook-reagent-recipes-mix = Mix
guidebook-reagent-effects-header = Effects
guidebook-reagent-effects-metabolism-group-rate = [bold]{$group}[/bold] [color=gray]({$rate} units per second)[/color]
guidebook-reagent-physical-description = Seems to be {$description}.

View File

@@ -0,0 +1,316 @@
-create-3rd-person =
{ $chance ->
[1] Creates
*[other] create
}
-cause-3rd-person =
{ $chance ->
[1] Causes
*[other] cause
}
-satiate-3rd-person =
{ $chance ->
[1] Satiates
*[other] satiate
}
reagent-effect-guidebook-create-entity-reaction-effect =
{ $chance ->
[1] Creates
*[other] create
} { $amount ->
[1] {INDEFINITE($entname)}
*[other] {$amount} {MAKEPLURAL($entname)}
}
reagent-effect-guidebook-explosion-reaction-effect =
{ $chance ->
[1] Causes
*[other] cause
} an explosion
reagent-effect-guidebook-foam-area-reaction-effect =
{ $chance ->
[1] Creates
*[other] create
} large quantities of foam
reagent-effect-guidebook-foam-area-reaction-effect =
{ $chance ->
[1] Creates
*[other] create
} large quantities of smoke
reagent-effect-guidebook-satiate-thirst =
{ $chance ->
[1] Satiates
*[other] satiate
} { $relative ->
[1] thirst averagely
*[other] thirst at {NATURALFIXED($relative, 3)}x the average rate
}
reagent-effect-guidebook-satiate-hunger =
{ $chance ->
[1] Satiates
*[other] satiate
} { $relative ->
[1] hunger averagely
*[other] hunger at {NATURALFIXED($relative, 3)}x the average rate
}
reagent-effect-guidebook-health-change =
{ $chance ->
[1] { $healsordeals ->
[heals] Heals
[deals] Deals
*[both] Modifies health by
}
*[other] { $healsordeals ->
[heals] heal
[deals] deal
*[both] modify health by
}
} { $changes }
reagent-effect-guidebook-status-effect =
{ $type ->
[add] { $chance ->
[1] Causes
*[other] cause
} {LOC($key)} for at least {NATURALFIXED($time, 3)} {MANY("second", $time)} with accumulation
*[set] { $chance ->
[1] Causes
*[other] cause
} {LOC($key)} for at least {NATURALFIXED($time, 3)} {MANY("second", $time)} without accumulation
[remove]{ $chance ->
[1] Removes
*[other] remove
} {NATURALFIXED($time, 3)} {MANY("second", $time)} of {LOC($key)}
}
reagent-effect-guidebook-activate-artifact =
{ $chance ->
[1] Attempts
*[other] attempt
} to activate an artifact
reagent-effect-guidebook-set-solution-temperature-effect =
{ $chance ->
[1] Sets
*[other] set
} the solution temperature to exactly {NATURALFIXED($temperature, 2)}k
reagent-effect-guidebook-adjust-solution-temperature-effect =
{ $chance ->
[1] { $deltasign ->
[1] Adds
*[-1] Removes
}
*[other]
{ $deltasign ->
[1] add
*[-1] remove
}
} heat from the solution until it reaches { $deltasign ->
[1] at most {NATURALFIXED($maxtemp, 2)}k
*[-1] at least {NATURALFIXED($mintemp, 2)}k
}
reagent-effect-guidebook-adjust-reagent-reagent =
{ $chance ->
[1] { $deltasign ->
[1] Adds
*[-1] Removes
}
*[other]
{ $deltasign ->
[1] add
*[-1] remove
}
} {NATURALFIXED($amount, 2)}u of {$reagent} { $deltasign ->
[1] to
*[-1] from
} the solution
reagent-effect-guidebook-adjust-reagent-group =
{ $chance ->
[1] { $deltasign ->
[1] Adds
*[-1] Removes
}
*[other]
{ $deltasign ->
[1] add
*[-1] remove
}
} {NATURALFIXED($amount, 2)}u of reagents in the group {$group} { $deltasign ->
[1] to
*[-1] from
} the solution
reagent-effect-guidebook-adjust-temperature =
{ $chance ->
[1] { $deltasign ->
[1] Adds
*[-1] Removes
}
*[other]
{ $deltasign ->
[1] add
*[-1] remove
}
} {POWERJOULES($amount)} of heat { $deltasign ->
[1] to
*[-1] from
} the body it's in
reagent-effect-guidebook-chem-cause-disease =
{ $chance ->
[1] Causes
*[other] cause
} the disease { $disease }
reagent-effect-guidebook-chem-cause-random-disease =
{ $chance ->
[1] Causes
*[other] cause
} the diseases { $diseases }
reagent-effect-guidebook-jittering =
{ $chance ->
[1] Causes
*[other] cause
} jittering
reagent-effect-guidebook-chem-clean-bloodstream =
{ $chance ->
[1] Cleanses
*[other] cleanse
} the bloodstream of other chemicals
reagent-effect-guidebook-cure-disease =
{ $chance ->
[1] Cures
*[other] cure
} diseases
reagent-effect-guidebook-cure-eye-damage =
{ $chance ->
[1] { $deltasign ->
[1] Heals
*[-1] Deals
}
*[other]
{ $deltasign ->
[1] heal
*[-1] deal
}
} eye damage
reagent-effect-guidebook-chem-vomit =
{ $chance ->
[1] Causes
*[other] cause
} vomiting
reagent-effect-guidebook-create-gas =
{ $chance ->
[1] Creates
*[other] create
} { $moles } { $moles ->
[1] mole
*[other] moles
} of { $gas }
reagent-effect-guidebook-drunk =
{ $chance ->
[1] Causes
*[other] cause
} drunkness
reagent-effect-guidebook-electrocute =
{ $chance ->
[1] Electrocutes
*[other] electrocute
} the metabolizer for {NATURALFIXED($time, 3)} {MANY("second", $time)}
reagent-effect-guidebook-extinguish-reaction =
{ $chance ->
[1] Extinguishes
*[other] extinguish
} fire
reagent-effect-guidebook-flammable-reaction =
{ $chance ->
[1] Increases
*[other] increase
} flammability
reagent-effect-guidebook-ignite =
{ $chance ->
[1] Ignites
*[other] ignite
} the metabolizer
reagent-effect-guidebook-make-sentient =
{ $chance ->
[1] Makes
*[other] make
} the metabolizer sentient
reagent-effect-guidebook-modify-bleed-amount =
{ $chance ->
[1] { $deltasign ->
[1] Induces
*[-1] Reduces
}
*[other] { $deltasign ->
[1] induce
*[-1] reduce
}
} bleeding
reagent-effect-guidebook-modify-blood-level =
{ $chance ->
[1] { $deltasign ->
[1] Increases
*[-1] Decreases
}
*[other] { $deltasign ->
[1] increases
*[-1] decreases
}
} blood level
reagent-effect-guidebook-paralyze =
{ $chance ->
[1] Paralyzes
*[other] paralyze
} the metabolizer for at least {NATURALFIXED($time, 3)} {MANY("second", $time)}
reagent-effect-guidebook-movespeed-modifier =
{ $chance ->
[1] Modifies
*[other] modify
} movement speed by {NATURALFIXED($walkspeed, 3)}x for at least {NATURALFIXED($time, 3)} {MANY("second", $time)}
reagent-effect-guidebook-reset-narcolepsy =
{ $chance ->
[1] Temporarily staves
*[other] temporarily stave
} off narcolepsy
reagent-effect-guidebook-wash-cream-pie-reaction =
{ $chance ->
[1] Washes
*[other] wash
} off cream pie from one's face
reagent-effect-guidebook-missing =
{ $chance ->
[1] Causes
*[other] cause
} an unknown effect as nobody has written this effect yet

View File

@@ -0,0 +1,5 @@
health-change-display =
{ $deltasign ->
[-1] [color=green]{NATURALFIXED($amount, 2)}[/color] {$kind}
*[1] [color=red]{NATURALFIXED($amount, 2)}[/color] {$kind}
}

View File

@@ -0,0 +1,11 @@
reagent-effect-status-effect-Stun = stunning
reagent-effect-status-effect-KnockedDown = knockdown
reagent-effect-status-effect-Jitter = jittering
reagent-effect-status-effect-TemporaryBlindness = blindess
reagent-effect-status-effect-SeeingRainbows = hallucinations
reagent-effect-status-effect-Muted = inability to speak
reagent-effect-status-effect-Stutter = stuttering
reagent-effect-status-effect-ForcedSleep = unconsciousness
reagent-effect-status-effect-Drunk = drunkness
reagent-effect-status-effect-PressureImmunity = pressure immunity
reagent-effect-status-effect-Pacified = combat pacification

View File

@@ -14,6 +14,7 @@ guide-entry-radio = Radio
guide-entry-jobs = Jobs
guide-entry-salvage = Salvage
guide-entry-survival = Survival
guide-entry-chemicals = Chemicals
guide-entry-ss14 = Space Station 14
guide-entry-janitorial = Janitorial

View File

@@ -3,24 +3,32 @@
- type: metabolizerType
id: Animal
name: animal
- type: metabolizerType
id: Dragon
name: dragon
- type: metabolizerType
id: Human
name: human
- type: metabolizerType
id: Slime
name: slime
- type: metabolizerType
id: Vox
name: vox
- type: metabolizerType
id: Rat
name: rat
- type: metabolizerType
id: Plant
name: plant
- type: metabolizerType
id: Dwarf
name: dwarf

View File

@@ -32,3 +32,6 @@
- type: UpgradePowerDraw
powerDrawMultiplier: 0.75
scaling: Exponential
- type: GuideHelp
guides:
- Chemicals

View File

@@ -15,6 +15,11 @@
name: guide-entry-survival
text: "/ServerInfo/Guidebook/Survival.xml"
- type: guideEntry
id: Chemicals
name: guide-entry-chemicals
text: "/ServerInfo/Guidebook/Chemicals.xml"
- type: guideEntry
id: Janitorial
name: guide-entry-janitorial

View File

@@ -6,3 +6,4 @@
- Controls
- Jobs
- Survival
- Chemicals

View File

@@ -0,0 +1,34 @@
<Document>
# Chemicals
Chemicals are a powerful tool that can cause a variety of effects when consumed. Some can be found in plants, purchased from cargo, or be synthesized through combination with other chemicals.
Knowing different types of chemicals and their effects is important for being able to manage injury and danger.
## Elements
<GuideReagentGroupEmbed Group="Elements"/>
## Medicine
<GuideReagentGroupEmbed Group="Medicine"/>
## Narcotics
<GuideReagentGroupEmbed Group="Narcotics"/>
## Pyrotechnics
<GuideReagentGroupEmbed Group="Pyrotechnic"/>
## Toxins
<GuideReagentGroupEmbed Group="Toxins"/>
## Foods
<GuideReagentGroupEmbed Group="Foods"/>
## Botanical
<GuideReagentGroupEmbed Group="Botanical"/>
## Biological
<GuideReagentGroupEmbed Group="Biological"/>
## Other
<GuideReagentGroupEmbed Group="Unknown"/>
</Document>

Binary file not shown.

After

Width:  |  Height:  |  Size: 239 B