diff --git a/Content.Server/Chemistry/ReagentEffects/MovespeedModifier.cs b/Content.Server/Chemistry/ReagentEffects/MovespeedModifier.cs
new file mode 100644
index 0000000000..9031737443
--- /dev/null
+++ b/Content.Server/Chemistry/ReagentEffects/MovespeedModifier.cs
@@ -0,0 +1,69 @@
+using Content.Shared.Chemistry.Reagent;
+using Robust.Shared.GameObjects;
+using Robust.Shared.Serialization.Manager.Attributes;
+using Content.Shared.Movement.Components;
+using Content.Shared.Chemistry.Components;
+using Robust.Shared.Timing;
+using Robust.Shared.IoC;
+using System;
+using Content.Shared.Chemistry.Solution;
+
+namespace Content.Server.Chemistry.ReagentEffects
+{
+ ///
+ /// Default metabolism for stimulants and tranqs. Attempts to find a MovementSpeedModifier on the target,
+ /// adding one if not there and to change the movespeed
+ ///
+ public class MovespeedModifier : ReagentEffect
+ {
+ ///
+ /// How much the entities' walk speed is multiplied by.
+ ///
+ [DataField("walkSpeedModifier")]
+ public float WalkSpeedModifier { get; set; } = 1;
+
+ ///
+ /// How much the entities' run speed is multiplied by.
+ ///
+ [DataField("sprintSpeedModifier")]
+ public float SprintSpeedModifier { get; set; } = 1;
+
+ ///
+ /// How long the modifier applies (in seconds) when metabolized.
+ ///
+ [DataField("statusLifetime")]
+ public float StatusLifetime = 2f;
+
+ ///
+ /// Remove reagent at set rate, changes the movespeed modifiers and adds a MovespeedModifierMetabolismComponent if not already there.
+ ///
+ public override void Metabolize(IEntity solutionEntity, Solution.ReagentQuantity amount)
+ {
+ if (!solutionEntity.TryGetComponent(out MovementSpeedModifierComponent? movement)) return;
+
+ solutionEntity.EnsureComponent(out MovespeedModifierMetabolismComponent status);
+
+ // Only refresh movement if we need to.
+ var modified = !status.WalkSpeedModifier.Equals(WalkSpeedModifier) ||
+ !status.SprintSpeedModifier.Equals(SprintSpeedModifier);
+
+ status.WalkSpeedModifier = WalkSpeedModifier;
+ status.SprintSpeedModifier = SprintSpeedModifier;
+
+ IncreaseTimer(status, StatusLifetime * amount.Quantity.Float());
+
+ if (modified)
+ movement.RefreshMovementSpeedModifiers();
+
+ }
+ public void IncreaseTimer(MovespeedModifierMetabolismComponent status, float time)
+ {
+ var gameTiming = IoCManager.Resolve();
+
+ var offsetTime = Math.Max(status.ModifierTimer.TotalSeconds, gameTiming.CurTime.TotalSeconds);
+
+ status.ModifierTimer = TimeSpan.FromSeconds(offsetTime + time);
+ status.Dirty();
+ }
+ }
+}
diff --git a/Content.Shared/Chemistry/Components/MovespeedModifierMetabolismComponent.cs b/Content.Shared/Chemistry/Components/MovespeedModifierMetabolismComponent.cs
new file mode 100644
index 0000000000..afcd0f3f42
--- /dev/null
+++ b/Content.Shared/Chemistry/Components/MovespeedModifierMetabolismComponent.cs
@@ -0,0 +1,54 @@
+using Content.Shared.Movement.Components;
+using Robust.Shared.GameObjects;
+using Robust.Shared.Players;
+using Robust.Shared.Serialization;
+using Robust.Shared.ViewVariables;
+using System;
+using Robust.Shared.GameStates;
+using Robust.Shared.Timing;
+using Robust.Shared.IoC;
+
+namespace Content.Shared.Chemistry.Components
+{
+ //TODO: refactor movement modifier component because this is a pretty poor solution
+ [RegisterComponent]
+ [NetworkedComponent]
+ public sealed class MovespeedModifierMetabolismComponent : Component, IMoveSpeedModifier
+ {
+ [ViewVariables]
+ public override string Name => "MovespeedModifierMetabolism";
+
+ [ViewVariables]
+ public float WalkSpeedModifier { get; set; }
+
+ [ViewVariables]
+ public float SprintSpeedModifier { get; set; }
+
+ ///
+ /// When the current modifier is expected to end.
+ ///
+ [ViewVariables]
+ public TimeSpan ModifierTimer { get; set; } = TimeSpan.Zero;
+
+ public override ComponentState GetComponentState(ICommonSession player)
+ {
+ return new MovespeedModifierMetabolismComponentState(WalkSpeedModifier, SprintSpeedModifier, ModifierTimer);
+ }
+
+ [Serializable, NetSerializable]
+ public class MovespeedModifierMetabolismComponentState : ComponentState
+ {
+ public float WalkSpeedModifier { get; }
+ public float SprintSpeedModifier { get; }
+ public TimeSpan ModifierTimer { get; }
+
+ public MovespeedModifierMetabolismComponentState(float walkSpeedModifier, float sprintSpeedModifier, TimeSpan modifierTimer)
+ {
+ WalkSpeedModifier = walkSpeedModifier;
+ SprintSpeedModifier = sprintSpeedModifier;
+ ModifierTimer = modifierTimer;
+ }
+ }
+ }
+}
+
diff --git a/Content.Shared/Chemistry/MetabolismMovespeedModifierSystem.cs b/Content.Shared/Chemistry/MetabolismMovespeedModifierSystem.cs
new file mode 100644
index 0000000000..e67f5ae7da
--- /dev/null
+++ b/Content.Shared/Chemistry/MetabolismMovespeedModifierSystem.cs
@@ -0,0 +1,77 @@
+using Content.Shared.Chemistry.Components;
+using Robust.Shared.GameObjects;
+using Robust.Shared.GameStates;
+using Robust.Shared.IoC;
+using Robust.Shared.Timing;
+using System.Collections.Generic;
+using System.Linq;
+using Content.Shared.Movement.Components;
+using static Content.Shared.Chemistry.Components.MovespeedModifierMetabolismComponent;
+
+namespace Content.Shared.Chemistry
+{
+ public class MetabolismMovespeedModifierSystem : EntitySystem
+ {
+ [Dependency] private readonly IGameTiming _gameTiming = default!;
+
+ private readonly List _components = new();
+
+ public override void Initialize()
+ {
+ base.Initialize();
+
+ SubscribeLocalEvent(OnMovespeedHandleState);
+ SubscribeLocalEvent(AddComponent);
+ }
+
+ private void OnMovespeedHandleState(EntityUid uid, MovespeedModifierMetabolismComponent component, ComponentHandleState args)
+ {
+ if (args.Current is not MovespeedModifierMetabolismComponentState cast)
+ return;
+
+ if (ComponentManager.TryGetComponent(uid, out var modifier) &&
+ (!component.WalkSpeedModifier.Equals(cast.WalkSpeedModifier) ||
+ !component.SprintSpeedModifier.Equals(cast.SprintSpeedModifier)))
+ {
+ modifier.RefreshMovementSpeedModifiers();
+ }
+
+ component.WalkSpeedModifier = cast.WalkSpeedModifier;
+ component.SprintSpeedModifier = cast.SprintSpeedModifier;
+ component.ModifierTimer = cast.ModifierTimer;
+
+ }
+ private void AddComponent(EntityUid uid, MovespeedModifierMetabolismComponent component, ComponentStartup args)
+ {
+ _components.Add(component);
+ }
+
+ public override void Update(float frameTime)
+ {
+ base.Update(frameTime);
+
+ var currentTime = _gameTiming.CurTime;
+
+ for (var i = _components.Count - 1; i >= 0; i--)
+ {
+ var component = _components[i];
+
+ if (component.Deleted)
+ {
+ _components.RemoveAt(i);
+ continue;
+ }
+
+ if (component.ModifierTimer > currentTime) continue;
+
+ _components.RemoveAt(i);
+ ComponentManager.RemoveComponent(component.Owner.Uid);
+
+ if (component.Owner.TryGetComponent(out MovementSpeedModifierComponent? modifier))
+ {
+ modifier.RefreshMovementSpeedModifiers();
+ }
+ }
+ }
+ }
+}
diff --git a/Resources/Prototypes/Body/Mechanisms/human.yml b/Resources/Prototypes/Body/Mechanisms/human.yml
index 8de6180529..44652e4e65 100644
--- a/Resources/Prototypes/Body/Mechanisms/human.yml
+++ b/Resources/Prototypes/Body/Mechanisms/human.yml
@@ -121,11 +121,6 @@
# You're technically 'immune to poison' without a heart, but.. uhh, you'll have bigger problems on your hands.
- type: Metabolizer
metabolisms:
- Dylovene:
- effects:
- - !type:HealthChange
- damageClass: Toxin
- healthChange: -1
Arithrazine:
effects:
- !type:HealthChange
@@ -154,26 +149,39 @@
- !type:HealthChange
damageClass: Airloss
healthChange: -3
- Kelotane:
+ Dylovene:
effects:
- - !type:HealthChange
- damageClass: Burn
- healthChange: -1
- Synaptizine:
+ - !type:HealthChange
+ damageClass: Toxin
+ healthChange: -1
+ Ephedrine:
effects:
- - !type:HealthChange
- damageClass: Toxin
- healthChange: 0.5
+ - !type:MovespeedModifier
+ walkSpeedModifier: 1.2
+ sprintSpeedModifier: 1.2
HeartbreakerToxin:
effects:
- !type:HealthChange
damageClass: Airloss
healthChange: 4
+ Kelotane:
+ effects:
+ - !type:HealthChange
+ damageClass: Burn
+ healthChange: -1
Lexorin:
effects:
- !type:HealthChange
damageClass: Airloss
healthChange: 7
+ Meth:
+ effects:
+ - !type:HealthChange
+ healthChange: 2.5
+ damageClass: Toxin
+ - !type:MovespeedModifier
+ walkSpeedModifier: 1.3
+ sprintSpeedModifier: 1.3
Omnizine:
effects:
- !type:HealthChange
@@ -188,6 +196,11 @@
- !type:HealthChange
healthChange: -2
damageClass: Brute
+ Synaptizine:
+ effects:
+ - !type:HealthChange
+ damageClass: Toxin
+ healthChange: 0.5
- type: entity
id: OrganHumanStomach
diff --git a/Resources/Prototypes/Catalog/ReagentDispensers/chemical.yml b/Resources/Prototypes/Catalog/ReagentDispensers/chemical.yml
index 70073d0dde..a13d46df4b 100644
--- a/Resources/Prototypes/Catalog/ReagentDispensers/chemical.yml
+++ b/Resources/Prototypes/Catalog/ReagentDispensers/chemical.yml
@@ -9,6 +9,7 @@
- Fluorine
- Glucose
- Hydrogen
+ - Iodine
- Iron
- Lithium
- Mercury
@@ -25,3 +26,4 @@
- SulfuricAcid
- Uranium
- Water
+
diff --git a/Resources/Prototypes/Reagents/chemicals.yml b/Resources/Prototypes/Reagents/chemicals.yml
index ff882e463c..fb5be33970 100644
--- a/Resources/Prototypes/Reagents/chemicals.yml
+++ b/Resources/Prototypes/Reagents/chemicals.yml
@@ -222,6 +222,33 @@
- !type:AdjustWater
amount: 1
+- type: reagent
+ id: Meth
+ name: meth
+ desc: Methamphetamine, more commonly know as meth, is a potent stimulant, with dangerous side-effects if too much is consumed.
+ physicalDesc: translucent
+ color: "#FAFAFA"
+ boilingPoint: 212.0 #Meth vape when?
+ meltingPoint: 170.0
+
+- type: reagent
+ id: Iodine
+ name: iodine
+ desc: Commonly added to table salt as a nutrient. On its own it tastes far less pleasing.
+ physicalDesc: Dark Brown
+ color: "#BC8A00"
+ boilingPoint: 184.3
+ meltingPoint: 113.7
+
+- type: reagent
+ id: Ephedrine
+ name: ephedrine
+ desc: Increases stun resistance and movement speed, giving you hand cramps. Overdose deals toxin damage and inhibits breathing
+ physicalDesc: Bone white
+ color: "#D2FFFA"
+ boilingPoint: 255.0
+ meltingPoint: 36.0
+
- type: reagent
id: Oil
name: oil
diff --git a/Resources/Prototypes/Recipes/Reactions/chemicals.yml b/Resources/Prototypes/Recipes/Reactions/chemicals.yml
index e719e598fe..511edf22e3 100644
--- a/Resources/Prototypes/Recipes/Reactions/chemicals.yml
+++ b/Resources/Prototypes/Recipes/Reactions/chemicals.yml
@@ -226,3 +226,31 @@
amount: 1
products:
Fluorosurfactant: 5
+
+- type: reaction
+ id: Meth
+ reactants:
+ Ephedrine:
+ amount: 1
+ Carbon:
+ amount: 1
+ Iodine:
+ amount: 1
+ Phosphorus:
+ amount: 1
+ products:
+ Meth: 4 #I kinda remember having to heat this up, and if you heated it up too much, it went boom, I can't remember the specific values tho.
+
+- type: reaction
+ id: Ephedrine
+ reactants:
+ Oil:
+ amount: 1
+ Hydrogen:
+ amount: 1
+ Sugar:
+ amount: 1
+ Diethylamine:
+ amount: 1
+ products:
+ Ephedrine: 4