Add haloperidol, potassium iodide (#27454)

* add haloperidol, potassium iodide

* review fixes

* review and tuning

* shader review

* use timespan and AutoPausedField
This commit is contained in:
slarticodefast
2024-08-02 19:12:08 +02:00
committed by GitHub
parent ea6be02ea3
commit 577f30fb13
26 changed files with 622 additions and 19 deletions

View File

@@ -0,0 +1,80 @@
using Content.Shared.Drowsiness;
using Content.Shared.StatusEffect;
using Robust.Client.Graphics;
using Robust.Client.Player;
using Robust.Shared.Enums;
using Robust.Shared.Prototypes;
using Robust.Shared.Timing;
namespace Content.Client.Drowsiness;
public sealed class DrowsinessOverlay : Overlay
{
[Dependency] private readonly IEntityManager _entityManager = default!;
[Dependency] private readonly IPrototypeManager _prototypeManager = default!;
[Dependency] private readonly IPlayerManager _playerManager = default!;
[Dependency] private readonly IEntitySystemManager _sysMan = default!;
[Dependency] private readonly IGameTiming _timing = default!;
public override OverlaySpace Space => OverlaySpace.WorldSpace;
public override bool RequestScreenTexture => true;
private readonly ShaderInstance _drowsinessShader;
public float CurrentPower = 0.0f;
private const float PowerDivisor = 250.0f;
private const float Intensity = 0.2f; // for adjusting the visual scale
private float _visualScale = 0; // between 0 and 1
public DrowsinessOverlay()
{
IoCManager.InjectDependencies(this);
_drowsinessShader = _prototypeManager.Index<ShaderPrototype>("Drowsiness").InstanceUnique();
}
protected override void FrameUpdate(FrameEventArgs args)
{
var playerEntity = _playerManager.LocalEntity;
if (playerEntity == null)
return;
if (!_entityManager.HasComponent<DrowsinessComponent>(playerEntity)
|| !_entityManager.TryGetComponent<StatusEffectsComponent>(playerEntity, out var status))
return;
var statusSys = _sysMan.GetEntitySystem<StatusEffectsSystem>();
if (!statusSys.TryGetTime(playerEntity.Value, SharedDrowsinessSystem.DrowsinessKey, out var time, status))
return;
var curTime = _timing.CurTime;
var timeLeft = (float)(time.Value.Item2 - curTime).TotalSeconds;
CurrentPower += 8f * (0.5f * timeLeft - CurrentPower) * args.DeltaSeconds / (timeLeft + 1);
}
protected override bool BeforeDraw(in OverlayDrawArgs args)
{
if (!_entityManager.TryGetComponent(_playerManager.LocalEntity, out EyeComponent? eyeComp))
return false;
if (args.Viewport.Eye != eyeComp.Eye)
return false;
_visualScale = Math.Clamp(CurrentPower / PowerDivisor, 0.0f, 1.0f);
return _visualScale > 0;
}
protected override void Draw(in OverlayDrawArgs args)
{
if (ScreenTexture == null)
return;
var handle = args.WorldHandle;
_drowsinessShader.SetParameter("SCREEN_TEXTURE", ScreenTexture);
_drowsinessShader.SetParameter("Strength", _visualScale * Intensity);
handle.UseShader(_drowsinessShader);
handle.DrawRect(args.WorldBounds, Color.White);
handle.UseShader(null);
}
}

View File

@@ -0,0 +1,53 @@
using Content.Shared.Drowsiness;
using Robust.Client.Graphics;
using Robust.Client.Player;
using Robust.Shared.Player;
namespace Content.Client.Drowsiness;
public sealed class DrowsinessSystem : SharedDrowsinessSystem
{
[Dependency] private readonly IPlayerManager _player = default!;
[Dependency] private readonly IOverlayManager _overlayMan = default!;
private DrowsinessOverlay _overlay = default!;
public override void Initialize()
{
base.Initialize();
SubscribeLocalEvent<DrowsinessComponent, ComponentInit>(OnDrowsinessInit);
SubscribeLocalEvent<DrowsinessComponent, ComponentShutdown>(OnDrowsinessShutdown);
SubscribeLocalEvent<DrowsinessComponent, LocalPlayerAttachedEvent>(OnPlayerAttached);
SubscribeLocalEvent<DrowsinessComponent, LocalPlayerDetachedEvent>(OnPlayerDetached);
_overlay = new();
}
private void OnPlayerAttached(EntityUid uid, DrowsinessComponent component, LocalPlayerAttachedEvent args)
{
_overlayMan.AddOverlay(_overlay);
}
private void OnPlayerDetached(EntityUid uid, DrowsinessComponent component, LocalPlayerDetachedEvent args)
{
_overlay.CurrentPower = 0;
_overlayMan.RemoveOverlay(_overlay);
}
private void OnDrowsinessInit(EntityUid uid, DrowsinessComponent component, ComponentInit args)
{
if (_player.LocalEntity == uid)
_overlayMan.AddOverlay(_overlay);
}
private void OnDrowsinessShutdown(EntityUid uid, DrowsinessComponent component, ComponentShutdown args)
{
if (_player.LocalEntity == uid)
{
_overlay.CurrentPower = 0;
_overlayMan.RemoveOverlay(_overlay);
}
}
}

View File

@@ -0,0 +1,50 @@
using Content.Shared.Bed.Sleep;
using Content.Shared.Drowsiness;
using Content.Shared.StatusEffect;
using Robust.Shared.Random;
using Robust.Shared.Timing;
namespace Content.Server.Drowsiness;
public sealed class DrowsinessSystem : SharedDrowsinessSystem
{
[ValidatePrototypeId<StatusEffectPrototype>]
private const string SleepKey = "ForcedSleep"; // Same one used by N2O and other sleep chems.
[Dependency] private readonly IGameTiming _timing = default!;
[Dependency] private readonly IRobustRandom _random = default!;
[Dependency] private readonly StatusEffectsSystem _statusEffects = default!;
/// <inheritdoc/>
public override void Initialize()
{
SubscribeLocalEvent<DrowsinessComponent, ComponentStartup>(OnInit);
}
private void OnInit(EntityUid uid, DrowsinessComponent component, ComponentStartup args)
{
component.NextIncidentTime = _timing.CurTime + TimeSpan.FromSeconds(_random.NextFloat(component.TimeBetweenIncidents.X, component.TimeBetweenIncidents.Y));
}
public override void Update(float frameTime)
{
base.Update(frameTime);
var query = EntityQueryEnumerator<DrowsinessComponent>();
while (query.MoveNext(out var uid, out var component))
{
if (_timing.CurTime < component.NextIncidentTime)
continue;
// Set the new time.
component.NextIncidentTime = _timing.CurTime + TimeSpan.FromSeconds(_random.NextFloat(component.TimeBetweenIncidents.X, component.TimeBetweenIncidents.Y));
// sleep duration
var duration = TimeSpan.FromSeconds(_random.NextFloat(component.DurationOfIncident.X, component.DurationOfIncident.Y));
// Make sure the sleep time doesn't cut into the time to next incident.
component.NextIncidentTime += duration;
_statusEffects.TryAddStatusEffect<ForcedSleepingComponent>(uid, SleepKey, duration, false);
}
}
}

View File

@@ -0,0 +1,18 @@
using Robust.Shared.Prototypes;
using Content.Shared.Damage.Prototypes;
namespace Content.Server.Radiation.Components;
/// <summary>
/// Exists for use as a status effect.
/// Adds the DamageProtectionBuffComponent to the entity and adds the specified DamageModifierSet to its list of modifiers.
/// </summary>
[RegisterComponent]
public sealed partial class RadiationProtectionComponent : Component
{
/// <summary>
/// The radiation damage modifier for entities with this component.
/// </summary>
[DataField("modifier")]
public ProtoId<DamageModifierSetPrototype> RadiationProtectionModifierSetId = "PotassiumIodide";
}

View File

@@ -0,0 +1,38 @@
using Content.Server.Radiation.Components;
using Content.Shared.Damage.Components;
using Robust.Shared.Prototypes;
namespace Content.Server.Radiation.EntitySystems;
public sealed class RadiationProtectionSystem : EntitySystem
{
[Dependency] private readonly IPrototypeManager _prototypeManager = default!;
public override void Initialize()
{
base.Initialize();
SubscribeLocalEvent<RadiationProtectionComponent, ComponentInit>(OnInit);
SubscribeLocalEvent<RadiationProtectionComponent, ComponentShutdown>(OnShutdown);
}
private void OnInit(EntityUid uid, RadiationProtectionComponent component, ComponentInit args)
{
if (!_prototypeManager.TryIndex(component.RadiationProtectionModifierSetId, out var modifier))
return;
var buffComp = EnsureComp<DamageProtectionBuffComponent>(uid);
// add the damage modifier if it isn't in the dict yet
if (!buffComp.Modifiers.ContainsKey(component.RadiationProtectionModifierSetId))
buffComp.Modifiers.Add(component.RadiationProtectionModifierSetId, modifier);
}
private void OnShutdown(EntityUid uid, RadiationProtectionComponent component, ComponentShutdown args)
{
if (!TryComp<DamageProtectionBuffComponent>(uid, out var buffComp))
return;
// remove the damage modifier from the dict
buffComp.Modifiers.Remove(component.RadiationProtectionModifierSetId);
// if the dict is empty now, remove the buff component
if (buffComp.Modifiers.Count == 0)
RemComp<DamageProtectionBuffComponent>(uid);
}
}

View File

@@ -0,0 +1,17 @@
using Content.Shared.Damage.Prototypes;
using Robust.Shared.GameStates;
namespace Content.Shared.Damage.Components;
/// <summary>
/// Applies the specified DamageModifierSets when the entity takes damage.
/// </summary>
[RegisterComponent, NetworkedComponent]
public sealed partial class DamageProtectionBuffComponent : Component
{
/// <summary>
/// The damage modifiers for entities with this component.
/// </summary>
[DataField]
public Dictionary<string, DamageModifierSetPrototype> Modifiers = new();
}

View File

@@ -0,0 +1,19 @@
using Content.Shared.Damage.Components;
namespace Content.Shared.Damage.Systems;
public sealed class DamageProtectionBuffSystem : EntitySystem
{
public override void Initialize()
{
base.Initialize();
SubscribeLocalEvent<DamageProtectionBuffComponent, DamageModifyEvent>(OnDamageModify);
}
private void OnDamageModify(EntityUid uid, DamageProtectionBuffComponent component, DamageModifyEvent args)
{
foreach (var modifier in component.Modifiers.Values)
args.Damage = DamageSpecifier.ApplyModifierSet(args.Damage, modifier);
}
}

View File

@@ -0,0 +1,28 @@
using System.Numerics;
using Robust.Shared.GameStates;
using Robust.Shared.Serialization.TypeSerializers.Implementations.Custom;
namespace Content.Shared.Drowsiness;
/// <summary>
/// Exists for use as a status effect. Adds a shader to the client that scales with the effect duration.
/// </summary>
[RegisterComponent, NetworkedComponent, AutoGenerateComponentPause]
public sealed partial class DrowsinessComponent : Component
{
/// <summary>
/// The random time between sleeping incidents, (min, max).
/// </summary>
[DataField(required: true)]
public Vector2 TimeBetweenIncidents = new Vector2(5f, 60f);
/// <summary>
/// The duration of sleeping incidents, (min, max).
/// </summary>
[DataField(required: true)]
public Vector2 DurationOfIncident = new Vector2(2, 5);
[DataField(customTypeSerializer: typeof(TimeOffsetSerializer))]
[AutoPausedField]
public TimeSpan NextIncidentTime = TimeSpan.Zero;
}

View File

@@ -0,0 +1,9 @@
using Content.Shared.StatusEffect;
namespace Content.Shared.Drowsiness;
public abstract class SharedDrowsinessSystem : EntitySystem
{
[ValidatePrototypeId<StatusEffectPrototype>]
public const string DrowsinessKey = "Drowsiness";
}

View File

@@ -11,3 +11,5 @@ reagent-effect-status-effect-PressureImmunity = pressure immunity
reagent-effect-status-effect-Pacified = combat pacification
reagent-effect-status-effect-RatvarianLanguage = ratvarian language patterns
reagent-effect-status-effect-StaminaModifier = modified stamina
reagent-effect-status-effect-RadiationProtection = radiation protection
reagent-effect-status-effect-Drowsiness = drowsiness

View File

@@ -141,3 +141,9 @@ reagent-desc-mannitol = Efficiently restores brain damage.
reagent-name-psicodine = psicodine
reagent-desc-psicodine = Suppresses anxiety and other various forms of mental distress. Overdose causes hallucinations and minor toxin damage.
reagent-name-potassium-iodide = potassium iodide
reagent-desc-potassium-iodide = Will reduce the damaging effects of radiation by 90%. Prophylactic use only.
reagent-name-haloperidol = haloperidol
reagent-desc-haloperidol = Removes most stimulating and hallucinogenic drugs. Reduces druggy effects and jitteriness. Causes drowsiness.

View File

@@ -71,7 +71,7 @@
contents:
- id: SyringePhalanximine
- id: RadAutoInjector
- id: EmergencyMedipen
- id: PillCanisterPotassiumIodide
- id: PillCanisterHyronalin
- type: entity

View File

@@ -296,3 +296,9 @@
Cellular: 0.0
Heat: 2.5
Caustic: 0.0
# protects against radiation
- type: damageModifierSet
id: PotassiumIodide
coefficients:
Radiation: 0.1

View File

@@ -26,6 +26,8 @@
- TemporaryBlindness
- Pacified
- Flashed
- RadiationProtection
- Drowsiness
- type: Buckle
- type: StandingState
- type: Tag
@@ -101,6 +103,8 @@
- Pacified
- StaminaModifier
- Flashed
- RadiationProtection
- Drowsiness
- type: Bloodstream
bloodMaxVolume: 150
- type: MobPrice

View File

@@ -132,6 +132,8 @@
- Pacified
- StaminaModifier
- Flashed
- RadiationProtection
- Drowsiness
- type: Body
prototype: Human
requiredLegs: 2
@@ -234,7 +236,6 @@
groups:
Brute: -0.07
- type: Fingerprint
- type: Blindable
# Other
- type: Temperature

View File

@@ -445,6 +445,39 @@
- id: PillHyronalin
amount: 5
- type: entity
name: pill
suffix: Potassium iodide 10u
parent: Pill
id: PillPotassiumIodide
components:
- type: Pill
pillType: 8
- type: Sprite
state: pill9
- type: Label
currentLabel: potassium iodide 10u
- type: SolutionContainerManager
solutions:
food:
maxVol: 20
reagents:
- ReagentId: PotassiumIodide
Quantity: 10
- type: entity
name: pill canister
parent: PillCanister
id: PillCanisterPotassiumIodide
suffix: Potassium iodide 10u, 5
components:
- type: Label
currentLabel: potassium iodide 10u
- type: StorageFill
contents:
- id: PillPotassiumIodide
amount: 5
- type: entity
name: pill
suffix: Iron 10u
@@ -751,6 +784,10 @@
prob: 0.10
maxAmount: 7
orGroup: RandomPill
- id: PillPotassiumIodide
prob: 0.10
maxAmount: 7
orGroup: RandomPill
- id: PillIron
prob: 0.10
maxAmount: 7

View File

@@ -1795,6 +1795,10 @@
- !type:AdjustReagent
reagent: Theobromine
amount: 0.05
- !type:GenericStatusEffect
key: Drowsiness
time: 1.0
type: Remove
fizziness: 0.25
- type: reagent
@@ -1822,6 +1826,10 @@
- !type:AdjustReagent
reagent: Theobromine
amount: 0.05
- !type:GenericStatusEffect
key: Drowsiness
time: 1.0
type: Remove
fizziness: 0.15
- type: reagent
@@ -1849,6 +1857,10 @@
- !type:AdjustReagent
reagent: Theobromine
amount: 0.05
- !type:GenericStatusEffect
key: Drowsiness
time: 1.0
type: Remove
fizziness: 0.15
- type: reagent
@@ -1876,6 +1888,10 @@
- !type:AdjustReagent
reagent: Theobromine
amount: 0.05
- !type:GenericStatusEffect
key: Drowsiness
time: 1.0
type: Remove
fizziness: 0.25
- type: reagent
@@ -1903,6 +1919,10 @@
- !type:AdjustReagent
reagent: Theobromine
amount: 0.05
- !type:GenericStatusEffect
key: Drowsiness
time: 1.0
type: Remove
fizziness: 0.15
- type: reagent
@@ -1930,4 +1950,8 @@
- !type:AdjustReagent
reagent: Theobromine
amount: 0.05
- !type:GenericStatusEffect
key: Drowsiness
time: 1.0
type: Remove
fizziness: 0.25

View File

@@ -12,6 +12,10 @@
effects:
- !type:SatiateThirst
factor: 2
- !type:GenericStatusEffect
key: Drowsiness
time: 2.0
type: Remove
- !type:AdjustReagent
reagent: Theobromine
amount: 0.05
@@ -96,6 +100,15 @@
metamorphicMaxFillLevels: 1
metamorphicFillBaseName: fill-
metamorphicChangeColor: false
metabolisms:
Drink:
effects:
- !type:SatiateThirst
factor: 2
- !type:GenericStatusEffect
key: Drowsiness
time: 2.0
type: Remove
- type: reagent
id: GreenTea
@@ -145,6 +158,15 @@
metamorphicMaxFillLevels: 5
metamorphicFillBaseName: fill-
metamorphicChangeColor: false
metabolisms:
Drink:
effects:
- !type:SatiateThirst
factor: 2
- !type:GenericStatusEffect
key: Drowsiness
time: 2.0
type: Remove
- type: reagent
id: IcedGreenTea
@@ -322,6 +344,10 @@
effects:
- !type:SatiateThirst
factor: 6
- !type:GenericStatusEffect
key: Drowsiness
time: 3.0
type: Remove
Poison:
effects:
- !type:HealthChange
@@ -354,6 +380,15 @@
metamorphicMaxFillLevels: 1
metamorphicFillBaseName: fill-
metamorphicChangeColor: false
metabolisms:
Drink:
effects:
- !type:SatiateThirst
factor: 2
- !type:GenericStatusEffect
key: Drowsiness
time: 2.0
type: Remove
- type: reagent
id: Tea

View File

@@ -13,6 +13,15 @@
metamorphicMaxFillLevels: 5
metamorphicFillBaseName: fill-
metamorphicChangeColor: false
metabolisms:
Drink:
effects:
- !type:SatiateThirst
factor: 2
- !type:GenericStatusEffect
key: Drowsiness
time: 1.0
type: Remove
- type: reagent
id: RoyRogers
@@ -68,6 +77,10 @@
effects:
- !type:SatiateThirst
factor: 2
- !type:GenericStatusEffect
key: Drowsiness
time: 2.0
type: Remove
- !type:AdjustReagent
reagent: Theobromine
amount: 0.1

View File

@@ -891,6 +891,10 @@
- !type:GenericStatusEffect
key: Stutter
component: StutteringAccent
- !type:GenericStatusEffect
key: Drowsiness
time: 10
type: Remove
- !type:ResetNarcolepsy
conditions:
- !type:ReagentThreshold
@@ -912,6 +916,10 @@
metabolisms:
Medicine:
effects:
- !type:GenericStatusEffect
key: Drowsiness
time: 10
type: Remove
- !type:ResetNarcolepsy
conditions:
- !type:ReagentThreshold
@@ -1284,3 +1292,82 @@
- "psicodine-effect-anxieties-wash-away"
- "psicodine-effect-at-peace"
probability: 0.2
- type: reagent
id: PotassiumIodide
name: reagent-name-potassium-iodide
group: Medicine
desc: reagent-desc-potassium-iodide
physicalDesc: reagent-physical-desc-grainy
flavor: medicine
color: "#baa15d"
metabolisms:
Medicine:
effects:
- !type:GenericStatusEffect
key: RadiationProtection
component: RadiationProtection
time: 2
type: Add
refresh: false
- !type:HealthChange
conditions:
- !type:ReagentThreshold
min: 20
damage:
types:
Poison: 1
- type: reagent
id: Haloperidol
name: reagent-name-haloperidol
group: Medicine
desc: reagent-desc-haloperidol
physicalDesc: reagent-physical-desc-crystalline
flavor: medicine
color: "#27870a"
metabolisms:
Medicine:
effects:
- !type:Emote
emote: Yawn
showInChat: true
probability: 0.1
- !type:GenericStatusEffect
key: Drowsiness
component: Drowsiness
time: 4
type: Add
refresh: false
- !type:GenericStatusEffect
key: Jitter
time: 4.0
type: Remove
- !type:GenericStatusEffect
key: SeeingRainbows
time: 10.0
type: Remove
- !type:AdjustReagent
reagent: Desoxyephedrine
amount: -3.0
- !type:AdjustReagent
reagent: Ephedrine
amount: -3.0
- !type:AdjustReagent
reagent: Stimulants
amount: -3.0
- !type:AdjustReagent
reagent: THC
amount: -3.0
- !type:AdjustReagent
reagent: SpaceDrugs
amount: -3.0
- !type:AdjustReagent
reagent: Bananadine
amount: -3.0
- !type:AdjustReagent
reagent: SpaceGlue
amount: -3.0
- !type:AdjustReagent
reagent: MindbreakerToxin
amount: -3.0

View File

@@ -40,6 +40,14 @@
key: KnockedDown
time: 3
type: Remove
- !type:GenericStatusEffect
conditions:
- !type:ReagentThreshold
reagent: Haloperidol
max: 0.01
key: Drowsiness
time: 10
type: Remove
Medicine:
effects:
- !type:ResetNarcolepsy
@@ -80,6 +88,14 @@
key: KnockedDown
time: 1
type: Remove
- !type:GenericStatusEffect
conditions:
- !type:ReagentThreshold
reagent: Haloperidol
max: 0.01
key: Drowsiness
time: 10
type: Remove
- !type:PopupMessage
visualType: Medium
messages: ["ephedrine-effect-tight-pain", "ephedrine-effect-heart-pounds"]
@@ -140,6 +156,14 @@
key: ForcedSleep
time: 3
type: Remove
- !type:GenericStatusEffect
conditions:
- !type:ReagentThreshold
reagent: Haloperidol
max: 0.01
key: Drowsiness
time: 10
type: Remove
Medicine:
metabolismRate: 1.0
effects:

View File

@@ -56,18 +56,19 @@
metabolisms:
Poison:
effects:
- !type:Emote
emote: Yawn
showInChat: true
probability: 0.1
- !type:MovespeedModifier
walkSpeedModifier: 0.65
sprintSpeedModifier: 0.65
- !type:GenericStatusEffect
conditions:
- !type:ReagentThreshold
reagent: ChloralHydrate
min: 10
key: ForcedSleep
component: ForcedSleeping
refresh: false
key: Drowsiness
component: Drowsiness
time: 4
type: Add
refresh: false
- !type:HealthChange
conditions:
- !type:ReagentThreshold

View File

@@ -636,3 +636,29 @@
catalyst: true
products:
Happiness: 4
- type: reaction
id: PotassiumIodide
reactants:
Potassium:
amount: 1
Iodine:
amount: 1
products:
PotassiumIodide: 2
- type: reaction
id: Haloperidol
reactants:
Aluminium:
amount: 1
Chlorine:
amount: 1
Fluorine:
amount: 1
Oil:
amount: 1
PotassiumIodide:
amount: 1
products:
Haloperidol: 5

View File

@@ -57,6 +57,11 @@
kind: source
path: "/Textures/Shaders/drunk.swsl"
- type: shader
id: Drowsiness
kind: source
path: "/Textures/Shaders/radial_blur.swsl"
- type: shader
id: Texture
kind: source

View File

@@ -62,3 +62,9 @@
- type: statusEffect
id: Flashed
- type: statusEffect
id: RadiationProtection
- type: statusEffect
id: Drowsiness #blurs your vision and makes you randomly fall asleep

View File

@@ -0,0 +1,14 @@
uniform sampler2D SCREEN_TEXTURE;
uniform highp float Strength;
const highp int SampleCount = 10; // a higher number makes the shader look better, but has a big performance impact
// a simple radial blur
void fragment() {
highp vec2 uv = FRAGCOORD.xy * SCREEN_PIXEL_SIZE.xy;
highp vec2 direction = vec2(0.5, 0.5) - uv;
for (int i=1; i <= SampleCount; i++)
{
COLOR += zTextureSpec(SCREEN_TEXTURE, uv + float(i) * Strength / float(SampleCount) * direction);
}
COLOR = COLOR / float(SampleCount);
}