Migrate Chronic Pain to the New Status Effect System (#5180)

* Renamed InPain to ChronicPain. Changed references.

* Updated meds to use the name status effect system for chronic pain suppression. Standardized popup chances for all meds.

* Migrated ChronicPain to the new Status effect system. Basically re-wrote it and re-organized it.

* Cleanup and tweaks.

* [pre-commit.ci] auto fixes from pre-commit.com hooks

for more information, see https://pre-commit.ci

* Fixed cloning traits for pain.

* Addressing Milon-san's feedback uwu

* Apply suggestions from code review

Co-authored-by: Tobias Berger <toby@tobot.dev>
Signed-off-by: Vanessa <908648+ShepardToTheStars@users.noreply.github.com>

* Turned floats into TimeStamps.

* Derp

* Forgot the trait lol

---------

Signed-off-by: Vanessa <908648+ShepardToTheStars@users.noreply.github.com>
Co-authored-by: pre-commit-ci[bot] <66853113+pre-commit-ci[bot]@users.noreply.github.com>
Co-authored-by: Tobias Berger <toby@tobot.dev>
This commit is contained in:
Vanessa
2026-01-23 10:24:32 +00:00
committed by GitHub
co-authored by Tobias Berger pre-commit-ci[bot]
parent 989b978712
commit 8f130cb0bb
22 changed files with 335 additions and 416 deletions
@@ -0,0 +1,73 @@
using Content.Client._DV.ChronicPain.Overlays;
using Content.Shared._DV.ChronicPain.Components;
using Content.Shared._DV.ChronicPain.EntitySystems;
using Robust.Client.Graphics;
using Robust.Shared.Player;
namespace Content.Client._DV.ChronicPain.EntitySystems;
public sealed class ChronicPainSystem : SharedChronicPainSystem
{
[Dependency] private readonly IOverlayManager _overlayMan = default!;
[Dependency] private readonly ISharedPlayerManager _playerManager = default!;
private ChronicPainOverlay _overlay = default!;
public override void Initialize()
{
base.Initialize();
_overlay = new();
}
protected override void OnChronicPainInit(Entity<ChronicPainComponent> entity, ref ComponentInit args)
{
if (entity.Owner != _playerManager.LocalEntity)
return;
_overlayMan.AddOverlay(_overlay);
}
protected override void OnChronicPainShutdown(Entity<ChronicPainComponent> entity, ref ComponentShutdown args)
{
if (entity.Owner != _playerManager.LocalEntity)
return;
_overlayMan.RemoveOverlay(_overlay);
}
protected override void OnPlayerAttached(Entity<ChronicPainComponent> entity, ref LocalPlayerAttachedEvent args)
{
if (!IsChronicPainSuppressed((entity.Owner, entity.Comp)))
_overlayMan.AddOverlay(_overlay);
}
protected override void OnPlayerDetached(Entity<ChronicPainComponent> entity, ref LocalPlayerDetachedEvent args)
{
_overlayMan.RemoveOverlay(_overlay);
}
public override void Update(float frameTime)
{
base.Update(frameTime);
// Handle showing/hiding overlay based on suppression status
if (_playerManager.LocalEntity is not { } player)
return;
if (!TryComp<ChronicPainComponent>(player, out var comp))
return;
var isSuppressed = IsChronicPainSuppressed((player, comp));
if (isSuppressed && _overlayMan.TryGetOverlay<ChronicPainOverlay>(out var overlay))
_overlayMan.RemoveOverlay(overlay);
if (isSuppressed) // If its suppressed and we don't have an overlay, just return
return;
if (!isSuppressed && !_overlayMan.HasOverlay<ChronicPainOverlay>())
_overlayMan.AddOverlay(_overlay);
}
}
@@ -1,36 +1,31 @@
using System.Numerics;
using Content.Shared._DV.Pain;
using Robust.Client.Graphics;
using Robust.Client.Player;
using Robust.Shared.Enums;
using Robust.Shared.Prototypes;
namespace Content.Client._DV.Overlays;
namespace Content.Client._DV.ChronicPain.Overlays;
public sealed partial class PainOverlay : Overlay
public sealed partial class ChronicPainOverlay : Overlay
{
[Dependency] private readonly IPrototypeManager _prototype = default!;
[Dependency] private readonly IPlayerManager _player = default!;
[Dependency] private readonly IEntityManager _entity = default!;
public override bool RequestScreenTexture => true;
public override OverlaySpace Space => OverlaySpace.WorldSpace;
private readonly ShaderInstance _painShader;
private readonly ProtoId<ShaderPrototype> _shaderProto = "ChromaticAberration";
public PainOverlay()
public ChronicPainOverlay()
{
IoCManager.InjectDependencies(this);
_painShader = _prototype.Index(_shaderProto).Instance().Duplicate();
_painShader = _prototype.Index(_shaderProto).InstanceUnique();
}
protected override bool BeforeDraw(in OverlayDrawArgs args)
{
if (_player.LocalEntity is not { Valid: true } player
|| !_entity.HasComponent<PainComponent>(player))
{
if (_player.LocalEntity is not { Valid: true })
return false;
}
return base.BeforeDraw(in args);
}
-65
View File
@@ -1,65 +0,0 @@
using Content.Shared._DV.Pain;
using Robust.Client.Graphics;
using Robust.Shared.Player;
namespace Content.Client._DV.Overlays;
public sealed partial class PainSystem : EntitySystem
{
[Dependency] private readonly IOverlayManager _overlayMan = default!;
[Dependency] private readonly ISharedPlayerManager _playerMan = default!;
private PainOverlay _overlay = default!;
public override void Initialize()
{
base.Initialize();
SubscribeLocalEvent<PainComponent, ComponentInit>(OnPainInit);
SubscribeLocalEvent<PainComponent, ComponentShutdown>(OnPainShutdown);
SubscribeLocalEvent<PainComponent, LocalPlayerAttachedEvent>(OnPlayerAttached);
SubscribeLocalEvent<PainComponent, LocalPlayerDetachedEvent>(OnPlayerDetached);
_overlay = new();
}
private void OnPainInit(Entity<PainComponent> ent, ref ComponentInit args)
{
if (ent.Owner == _playerMan.LocalEntity && !ent.Comp.Suppressed)
_overlayMan.AddOverlay(_overlay);
}
private void OnPainShutdown(Entity<PainComponent> ent, ref ComponentShutdown args)
{
if (ent.Owner == _playerMan.LocalEntity)
_overlayMan.RemoveOverlay(_overlay);
}
private void OnPlayerAttached(Entity<PainComponent> ent, ref LocalPlayerAttachedEvent args)
{
if (!ent.Comp.Suppressed)
_overlayMan.AddOverlay(_overlay);
}
private void OnPlayerDetached(Entity<PainComponent> ent, ref LocalPlayerDetachedEvent args)
{
_overlayMan.RemoveOverlay(_overlay);
}
public override void Update(float frameTime)
{
base.Update(frameTime);
// Handle showing/hiding overlay based on suppression status
if (_playerMan.LocalEntity is not { } player)
return;
if (!TryComp<PainComponent>(player, out var comp))
return;
if (comp.Suppressed && _overlayMan.HasOverlay<PainOverlay>())
_overlayMan.RemoveOverlay(_overlay);
else if (!comp.Suppressed && !_overlayMan.HasOverlay<PainOverlay>())
_overlayMan.AddOverlay(_overlay);
}
}
-5
View File
@@ -1,5 +0,0 @@
using Content.Shared._DV.Pain;
namespace Content.Client._DV.Pain;
public sealed class PainSystem : SharedPainSystem;
@@ -0,0 +1,5 @@
using Content.Shared._DV.ChronicPain.EntitySystems;
namespace Content.Server._DV.ChronicPain.EntitySystems;
public sealed class ChronicPainSystem : SharedChronicPainSystem;
-5
View File
@@ -1,5 +0,0 @@
using Content.Shared._DV.Pain;
namespace Content.Server._DV.Pain;
public sealed class PainSystem : SharedPainSystem;
@@ -0,0 +1,62 @@
using Content.Shared.Dataset;
using Robust.Shared.GameStates;
using Robust.Shared.Prototypes;
using Robust.Shared.Serialization.TypeSerializers.Implementations.Custom;
namespace Content.Shared._DV.ChronicPain.Components;
/// <summary>
/// This component is used to mark people who have the chronic pain trait.
///
/// If someone is currently the chronic pain status effect // TODO
/// </summary>
[RegisterComponent, NetworkedComponent]
[AutoGenerateComponentState, AutoGenerateComponentPause]
public sealed partial class ChronicPainComponent : Component
{
/// <summary>
/// If pain is trying to be suppressed and no suppression time is given, it should default to this time.
/// </summary>
[DataField]
public TimeSpan DefaultSuppressionTime = TimeSpan.FromSeconds(30);
/// <summary>
/// The default suppression time on map init, so people don't have to eat a pill right away.
/// </summary>
[DataField]
public TimeSpan DefaultSuppressionTimeOnInit = TimeSpan.FromMinutes(5);
/// <summary>
/// When to next update this component
/// </summary>
[DataField(customTypeSerializer: typeof(TimeOffsetSerializer))]
[AutoPausedField]
[AutoNetworkedField]
public TimeSpan NextUpdateTime;
/// <summary>
/// When to show the next pain effect popup.
/// </summary>
[DataField(customTypeSerializer: typeof(TimeOffsetSerializer))]
[AutoPausedField]
[AutoNetworkedField]
public TimeSpan NextPopupTime;
/// <summary>
/// The dataset of pain effect messages to display.
/// </summary>
[DataField]
public ProtoId<LocalizedDatasetPrototype> DatasetPrototype = "PainEffects";
/// <summary>
/// Minimum time between pain popups.
/// </summary>
[DataField]
public TimeSpan MinimumPopupDelay = TimeSpan.FromSeconds(5);
/// <summary>
/// Maximum time between pain popups.
/// </summary>
[DataField]
public TimeSpan MaximumPopupDelay = TimeSpan.FromSeconds(40);
}
@@ -0,0 +1,10 @@
using Content.Shared.StatusEffectNew.Components;
using Robust.Shared.GameStates;
namespace Content.Shared._DV.ChronicPain.Components;
/// <summary>
/// Prevents the Chronic Pain overlay and popups from showing up. Use only in conjunction with <see cref="StatusEffectComponent"/>, on the status effect entity.
/// </summary>
[NetworkedComponent, RegisterComponent]
public sealed partial class ChronicPainSuppressedStatusEffectComponent : Component;
@@ -0,0 +1,122 @@
using Content.Shared._DV.ChronicPain.Components;
using Content.Shared.StatusEffectNew;
using Content.Shared.Popups;
using Robust.Shared.Player;
using Robust.Shared.Prototypes;
using Robust.Shared.Random;
using Robust.Shared.Timing;
using JetBrains.Annotations;
namespace Content.Shared._DV.ChronicPain.EntitySystems;
public abstract partial class SharedChronicPainSystem : EntitySystem
{
[Dependency] private readonly IGameTiming _timing = default!;
[Dependency] protected readonly IPrototypeManager ProtoManager = default!;
[Dependency] protected readonly IRobustRandom RobustRandom = default!;
[Dependency] protected readonly SharedPopupSystem Popup = default!;
[Dependency] protected readonly StatusEffectsSystem StatusEffects = default!;
public static readonly EntProtoId ChronicPainStatusEffect = "StatusEffectChronicPainSuppressed";
public override void Initialize()
{
base.Initialize();
SubscribeLocalEvent<ChronicPainComponent, MapInitEvent>(OnMapInit);
SubscribeLocalEvent<ChronicPainComponent, ComponentInit>(OnChronicPainInit);
SubscribeLocalEvent<ChronicPainComponent, ComponentShutdown>(OnChronicPainShutdown);
SubscribeLocalEvent<ChronicPainComponent, LocalPlayerAttachedEvent>(OnPlayerAttached);
SubscribeLocalEvent<ChronicPainComponent, LocalPlayerDetachedEvent>(OnPlayerDetached);
}
[PublicAPI]
public bool IsChronicPainSuppressed(Entity<ChronicPainComponent?> entity)
{
// If they don't have the Chronic Pain trait, technically their pain was suppressed
if (!Resolve(entity, ref entity.Comp, false))
return true;
return StatusEffects.HasEffectComp<ChronicPainSuppressedStatusEffectComponent>(entity.Owner);
}
[PublicAPI]
public bool TrySuppressChronicPain(Entity<ChronicPainComponent?> entity, TimeSpan? duration)
{
// If they don't have the Chronic Pain trait, technically their pain was suppressed
if (!Resolve(entity, ref entity.Comp, false))
return true;
if (!duration.HasValue)
duration = entity.Comp.DefaultSuppressionTime;
return StatusEffects.TryAddStatusEffectDuration(entity.Owner, ChronicPainStatusEffect, duration.Value);
}
protected void OnMapInit(Entity<ChronicPainComponent> entity, ref MapInitEvent args)
{
entity.Comp.NextUpdateTime = _timing.CurTime;
entity.Comp.NextPopupTime = _timing.CurTime;
}
protected virtual void OnChronicPainInit(Entity<ChronicPainComponent> entity, ref ComponentInit args)
{
// Give the player a bit of time before they have to take a pill
if (TrySuppressChronicPain((entity.Owner, entity.Comp), entity.Comp.DefaultSuppressionTimeOnInit))
entity.Comp.NextUpdateTime = _timing.CurTime + entity.Comp.DefaultSuppressionTimeOnInit;
}
protected virtual void OnChronicPainShutdown(Entity<ChronicPainComponent> entity, ref ComponentShutdown args)
{
StatusEffects.TryRemoveStatusEffect(entity, ChronicPainStatusEffect);
}
protected virtual void OnPlayerAttached(Entity<ChronicPainComponent> entity, ref LocalPlayerAttachedEvent args)
{
// Used by the client to add the overlay if not suppressed
}
protected virtual void OnPlayerDetached(Entity<ChronicPainComponent> entity, ref LocalPlayerDetachedEvent args)
{
// Used by the client to get rid of the overlay
}
protected void ShowPainPopup(Entity<ChronicPainComponent> entity)
{
// Don't notify
if (IsChronicPainSuppressed((entity.Owner, entity.Comp)))
return;
if (!ProtoManager.TryIndex(entity.Comp.DatasetPrototype, out var dataset))
return;
var effects = dataset.Values;
if (effects.Count == 0)
return;
var effect = RobustRandom.Pick(effects);
Popup.PopupPredicted(Loc.GetString(effect), entity, entity);
// Set next popup time
var delay = RobustRandom.Next(entity.Comp.MinimumPopupDelay, entity.Comp.MaximumPopupDelay);
entity.Comp.NextPopupTime = _timing.CurTime + delay;
}
public override void Update(float frameTime)
{
base.Update(frameTime);
var curTime = _timing.CurTime;
var query = EntityQueryEnumerator<ChronicPainComponent>();
while (query.MoveNext(out var uid, out var component))
{
if (curTime < component.NextUpdateTime)
continue;
if (curTime >= component.NextPopupTime)
ShowPainPopup((uid, component));
component.NextUpdateTime = curTime + TimeSpan.FromSeconds(5);
}
}
}
@@ -1,35 +0,0 @@
using Content.Shared._DV.Pain;
using Content.Shared.EntityEffects;
using Content.Shared.StatusEffect;
using Robust.Shared.Prototypes;
namespace Content.Shared._DV.EntityEffects.Effects.Pain;
// TODO: When Pain is moved to new Status, make this use StatusEffectsContainerComponent.
/// <summary>
/// Applies the pain status effect for an amount of time based on how much of the painful reagent is in the system.
/// </summary>
/// <inheritdoc cref="EntityEffectSystem{T, TEffect}"/>
public sealed partial class InPainEntityEffectSystem : EntityEffectSystem<StatusEffectsComponent, InPain>
{
[Dependency] private readonly SharedPainSystem _pain = default!;
protected override void Effect(Entity<StatusEffectsComponent> entity, ref EntityEffectEvent<InPain> args)
{
var painTime = args.Effect.Time * args.Scale;
_pain.TryApplyPain(entity, painTime);
}
}
/// <inheritdoc cref="EntityEffect"/>
public sealed partial class InPain : EntityEffectBase<InPain>
{
/// <summary>
/// How long should the addiction be per 1u of the reagent.
/// </summary>
[DataField]
public float Time = 5f;
public override string? EntityEffectGuidebookText(IPrototypeManager prototype, IEntitySystemManager entSys)
=> Loc.GetString("reagent-effect-guidebook-addicted", ("chance", Probability));
}
@@ -1,43 +0,0 @@
using Content.Shared._DV.Pain;
using Content.Shared.EntityEffects;
using Content.Shared.StatusEffect;
using Robust.Shared.Prototypes;
namespace Content.Shared._DV.EntityEffects.Effects.Pain;
// TODO: When Pain is moved to new Status, make this use StatusEffectsContainerComponent.
/// <summary>
/// Supresses pain based on how much of the pain suppressing reagent is in the system.
/// </summary>
/// <inheritdoc cref="EntityEffectSystem{T, TEffect}"/>
public sealed partial class SuppressPainEntityEffectSystem : EntityEffectSystem<StatusEffectsComponent, SuppressPain>
{
[Dependency] private readonly SharedPainSystem _pain = default!;
protected override void Effect(Entity<StatusEffectsComponent> entity, ref EntityEffectEvent<SuppressPain> args)
{
var suppressionTime = args.Effect.Time * args.Scale;
_pain.TrySuppressPain(entity, suppressionTime, args.Effect.Level);
}
}
/// <inheritdoc cref="EntityEffect"/>
public sealed partial class SuppressPain : EntityEffectBase<SuppressPain>
{
/// <summary>
/// How long should the pain suppression last for each metabolism cycle
/// </summary>
[DataField]
public float Time = 30f;
/// <summary>
/// The strength level of the pain suppression.
/// </summary>
[DataField]
public PainSuppressionLevel Level = PainSuppressionLevel.Normal;
public override string? EntityEffectGuidebookText(IPrototypeManager prototype, IEntitySystemManager entSys)
=> Loc.GetString("reagent-effect-guidebook-pain-suppression",
("chance", Probability),
("level", Level.ToString().ToLowerInvariant()));
}
-69
View File
@@ -1,69 +0,0 @@
using Content.Shared.Dataset;
using Robust.Shared.GameStates;
using Robust.Shared.Prototypes;
using Robust.Shared.Serialization.TypeSerializers.Implementations.Custom;
namespace Content.Shared._DV.Pain;
[RegisterComponent, NetworkedComponent]
[AutoGenerateComponentState, AutoGenerateComponentPause]
public sealed partial class PainComponent : Component
{
/// <summary>
/// Whether pain effects are currently suppressed by painkillers
/// </summary>
[DataField, AutoNetworkedField]
public bool Suppressed;
/// <summary>
/// The current level of pain suppression
/// </summary>
[DataField]
public PainSuppressionLevel CurrentSuppressionLevel = PainSuppressionLevel.Normal;
/// <summary>
/// The last time painkillers were administered
/// </summary>
[DataField(customTypeSerializer: typeof(TimeOffsetSerializer))]
[AutoPausedField]
public TimeSpan LastPainkillerTime;
/// <summary>
/// When the pain suppression effect ends
/// </summary>
[DataField(customTypeSerializer: typeof(TimeOffsetSerializer))]
[AutoPausedField]
public TimeSpan SuppressionEndTime;
/// <summary>
/// When to next update this component
/// </summary>
[DataField(customTypeSerializer: typeof(TimeOffsetSerializer))]
[AutoPausedField]
public TimeSpan NextUpdateTime;
/// <summary>
/// When to show the next pain effect popup
/// </summary>
[DataField(customTypeSerializer: typeof(TimeOffsetSerializer))]
[AutoPausedField]
public TimeSpan NextPopupTime;
/// <summary>
/// The dataset of pain effect messages to display
/// </summary>
[DataField]
public ProtoId<LocalizedDatasetPrototype> DatasetPrototype = "PainEffects";
/// <summary>
/// Minimum time between pain popups in seconds
/// </summary>
[DataField]
public float MinimumPopupDelay = 1f;
/// <summary>
/// Maximum time between pain popups in seconds
/// </summary>
[DataField]
public float MaximumPopupDelay = 40f;
}
-124
View File
@@ -1,124 +0,0 @@
using Content.Shared.StatusEffect;
using Content.Shared.Popups;
using Robust.Shared.Prototypes;
using Robust.Shared.Random;
using Robust.Shared.Timing;
namespace Content.Shared._DV.Pain;
public abstract class SharedPainSystem : EntitySystem
{
[Dependency] private readonly IGameTiming _timing = default!;
[Dependency] private readonly IPrototypeManager _prototype = default!;
[Dependency] private readonly IRobustRandom _random = default!;
[Dependency] private readonly SharedPopupSystem _popup = default!;
[Dependency] private readonly StatusEffectsSystem _statusEffects = default!;
public ProtoId<StatusEffectPrototype> StatusEffectKey = "InPain";
public override void Initialize()
{
SubscribeLocalEvent<PainComponent, MapInitEvent>(OnMapInit);
}
public void TryApplyPain(EntityUid uid, float painTime, StatusEffectsComponent? status = null)
{
if (!Resolve(uid, ref status, false))
return;
if (!_statusEffects.HasStatusEffect(uid, StatusEffectKey, status))
{
_statusEffects.TryAddStatusEffect<PainComponent>(uid, StatusEffectKey, TimeSpan.FromSeconds(painTime), true, status);
}
else
{
_statusEffects.TryAddTime(uid, StatusEffectKey, TimeSpan.FromSeconds(painTime), status);
}
}
public void TrySuppressPain(EntityUid uid, float duration, PainSuppressionLevel level = PainSuppressionLevel.Normal)
{
if (!TryComp<PainComponent>(uid, out var comp))
return;
UpdatePainSuppression((uid, comp), duration, level);
}
protected void UpdatePainSuppression(Entity<PainComponent> ent, float duration, PainSuppressionLevel level)
{
var curTime = _timing.CurTime;
var newEndTime = curTime + TimeSpan.FromSeconds(duration);
// Only update if this would extend the suppression
if (newEndTime <= ent.Comp.SuppressionEndTime)
return;
ent.Comp.LastPainkillerTime = curTime;
ent.Comp.SuppressionEndTime = newEndTime;
UpdateSuppressed(ent);
}
private void UpdateSuppressed(Entity<PainComponent> ent)
{
ent.Comp.Suppressed = (_timing.CurTime < ent.Comp.SuppressionEndTime);
Dirty(ent);
}
private void OnMapInit(Entity<PainComponent> ent, ref MapInitEvent args)
{
ent.Comp.NextUpdateTime = _timing.CurTime;
ent.Comp.NextPopupTime = _timing.CurTime;
}
private void ShowPainPopup(Entity<PainComponent> ent)
{
if (!_prototype.TryIndex(ent.Comp.DatasetPrototype, out var dataset))
return;
var effects = dataset.Values;
if (effects.Count == 0)
return;
var effect = _random.Pick(effects);
_popup.PopupEntity(Loc.GetString(effect), ent, ent);
// Set next popup time
var delay = _random.NextFloat(ent.Comp.MinimumPopupDelay, ent.Comp.MaximumPopupDelay);
ent.Comp.NextPopupTime = _timing.CurTime + TimeSpan.FromSeconds(delay);
}
public override void Update(float frameTime)
{
base.Update(frameTime);
var curTime = _timing.CurTime;
var query = EntityQueryEnumerator<PainComponent>();
while (query.MoveNext(out var uid, out var component))
{
if (curTime < component.NextUpdateTime)
continue;
var ent = new Entity<PainComponent>(uid, component);
if (component.Suppressed)
{
UpdateSuppressed(ent);
}
else if (curTime >= component.NextPopupTime)
{
ShowPainPopup(ent);
}
component.NextUpdateTime = curTime + TimeSpan.FromSeconds(1);
}
}
}
// Used by the StatusEffect
public enum PainSuppressionLevel : byte
{
Mild,
Normal,
Strong,
}
@@ -9,9 +9,3 @@ reagent-effect-guidebook-addiction-suppression =
[1] Suppresses
*[other] suppress
} active addictions
reagent-effect-guidebook-pain-suppression =
{ $chance ->
[1] Suppresses
*[other] suppress
} pain at { $level } strength
+3 -3
View File
@@ -19,7 +19,7 @@ trait-ultravision-desc = Whether through custom bionic eyes, random mutation,
trait-deuteranopia-name = Deuteranopia
trait-deuteranopia-desc = Whether through custom bionic eyes, random mutation,
or being a Vulpkanin, you have red–green colour blindness.
or being a Vulpkanin, you have red-green colour blindness.
trait-hushed-name = Hushed
trait-hushed-desc = You are unable to speak louder than a whisper.
@@ -27,8 +27,8 @@ trait-hushed-desc = You are unable to speak louder than a whisper.
trait-uncloneable-name = Uncloneable
trait-uncloneable-desc = You cannot be cloned.
trait-inpain-name = Chronic pain
trait-inpain-desc = You’re constantly in discomfort. You need painkillers to function.
trait-chronic-pain-name = Chronic pain
trait-chronic-pain-desc = You're constantly in discomfort. You need painkillers to function.
trait-addicted-name = Addicted
trait-addicted-desc = You crave the substance, and your thoughts keep drifting back to it. Without it, you feel incomplete, anxious, and on edge.
@@ -69,7 +69,7 @@
- Hushed
- IrishAccent
- MobsterAccent
- Pain
- ChronicPain
- ScottishAccent
- UltraVision
- Unborgable
@@ -136,8 +136,7 @@
# Begin DeltaV additions
- Addicted # Psych med addictions system
- SuppressAddiction # Psych med addictions system
- InPain # Pain system
- SuppressPain # Pain system
- ChronicPain # Pain system
- Anesthesia # Anesthesia
- PainNumbness # Pain numbness status effect
- ScrambledSpeech
@@ -222,6 +222,10 @@
reagent: THC
min: 0.25
- !type:SuppressAddiction
- !type:ModifyStatusEffect # suppresses chronic pain mildly
effectProto: StatusEffectChronicPainSuppressed
time: 25 # 4.16 minutes of pain suppression per 5u (5u / 0.5 * 25 / 60)
type: Add
# END DeltaV
- type: reagent
+34 -28
View File
@@ -12,7 +12,7 @@
effects:
- !type:PopupMessage
type: Local
visualType: Medium
visualType: Small
messages:
- "reagent-effect-antidepressant-mild1"
- "reagent-effect-antidepressant-mild2"
@@ -58,13 +58,13 @@
effects:
- !type:PopupMessage
type: Local
visualType: Large
visualType: Medium
messages:
- "reagent-effect-antidepressant-normal1"
- "reagent-effect-antidepressant-normal2"
- "reagent-effect-antidepressant-normal3"
- "reagent-effect-antidepressant-normal4"
probability: 0.045
probability: 0.035
conditions:
- !type:ReagentCondition
reagent: Neurozenium
@@ -128,13 +128,13 @@
- !type:SuppressAddiction
- !type:PopupMessage
type: Local
visualType: Large
visualType: Medium
messages:
- "reagent-effect-antidepressant-strong1"
- "reagent-effect-antidepressant-strong2"
- "reagent-effect-antidepressant-strong3"
- "reagent-effect-antidepressant-strong4"
probability: 0.055
probability: 0.035
conditions:
- !type:ReagentCondition
reagent: Blissifylovene
@@ -218,7 +218,7 @@
Poison: 0.5
- !type:PopupMessage
type: Local
visualType: Medium
visualType: Large
messages:
- "reagent-effect-antidepressant-fade" # A decent chance to notify the player when their meds run out, but could happen silently.
probability: 0.2
@@ -242,7 +242,7 @@
effects:
- !type:PopupMessage
type: Local
visualType: Medium
visualType: Small
messages:
- "reagent-effect-anxietymed-mild1"
- "reagent-effect-anxietymed-mild2"
@@ -259,7 +259,7 @@
amount: -1.2
- !type:PopupMessage
type: Local
visualType: Small
visualType: Large
messages:
- "reagent-effect-anxietymed-fade" # A decent chance to notify the player when their meds run out, but could happen silently.
probability: 0.35
@@ -282,14 +282,14 @@
effects:
- !type:PopupMessage
type: Local
visualType: Large
visualType: Small
messages:
- "reagent-effect-anxietymed-normal1"
- "reagent-effect-anxietymed-normal2"
- "reagent-effect-anxietymed-normal3"
- "reagent-effect-anxietymed-normal4"
- "reagent-effect-anxietymed-normal5"
probability: 0.04
probability: 0.035
conditions:
- !type:ReagentCondition
reagent: Tranquinase
@@ -341,7 +341,7 @@
Poison: 1
- !type:PopupMessage
type: Local
visualType: Small
visualType: Large
messages:
- "reagent-effect-anxietymed-fade" # A decent chance to notify the player when their meds run out, but could happen silently.
probability: 0.32
@@ -364,7 +364,7 @@
effects:
- !type:PopupMessage
type: Local
visualType: Large
visualType: Small
messages:
- "reagent-effect-anxietymed-strong1"
- "reagent-effect-anxietymed-strong2"
@@ -373,7 +373,7 @@
- "reagent-effect-anxietymed-strong5"
- "reagent-effect-anxietymed-strong6"
- "reagent-effect-anxietymed-strong7"
probability: 0.04
probability: 0.035
conditions:
- !type:ReagentCondition
reagent: Equilibrazine
@@ -425,7 +425,7 @@
amount: -1.5
- !type:PopupMessage
type: Local
visualType: Small
visualType: Large
messages:
- "reagent-effect-anxietymed-fade" # A decent chance to notify the player when their meds run out, but could happen silently.
probability: 0.2
@@ -446,17 +446,19 @@
Medicine:
metabolismRate : 0.02
effects:
- !type:SuppressPain
level: Mild
- !type:ModifyStatusEffect
effectProto: StatusEffectChronicPainSuppressed
time: 2 # less effective than soretizone - 8.3 minutes of pain suppression per 5u (5u / 0.02 * 2 / 60)
type: Update
- !type:PopupMessage
type: Local
visualType: Medium
visualType: Small
messages:
- "reagent-effect-painkiller-mild1"
- "reagent-effect-painkiller-mild2"
- "reagent-effect-painkiller-mild3"
- "reagent-effect-painkiller-mild4"
probability: 0.08
probability: 0.035
- !type:Vomit
probability: 0.4
conditions:
@@ -483,7 +485,7 @@
min: 2
- !type:PopupMessage
type: Local
visualType: Small
visualType: Large
messages:
- "reagent-effect-painkiller-fade" # A decent chance to notify the player when their meds run out
probability: 0.35
@@ -504,18 +506,20 @@
Medicine:
metabolismRate : 0.01
effects:
- !type:SuppressPain
level: Normal
- !type:ModifyStatusEffect
effectProto: StatusEffectChronicPainSuppressed
time: 1.5 # 12.5 minutes of pain suppression per 5u (5u / 0.01 * 1.5 / 60)
type: Add
- !type:SuppressAddiction
- !type:PopupMessage
type: Local
visualType: Large
visualType: Small
messages:
- "reagent-effect-painkiller-normal1"
- "reagent-effect-painkiller-normal2"
- "reagent-effect-painkiller-normal3"
- "reagent-effect-painkiller-normal4"
probability: 0.085
probability: 0.035
- !type:AdjustReagent
conditions:
- !type:ReagentCondition
@@ -531,7 +535,7 @@
min: 9
- !type:PopupMessage
type: Local
visualType: Medium # stronger painkiller = more noticable fading
visualType: Large # stronger painkiller = more noticable fading
messages:
- "reagent-effect-painkiller-fade" # A decent chance to notify the player when their meds run out
probability: 0.4
@@ -552,12 +556,14 @@
Medicine:
metabolismRate : 0.01
effects:
- !type:SuppressPain
level: Strong
- !type:ModifyStatusEffect
effectProto: StatusEffectChronicPainSuppressed
time: 3 # More effective than soretizone - 25 minutes of pain suppression per 5u (5u / 0.01 * 3 / 60)
type: Update
- !type:SuppressAddiction
- !type:PopupMessage
type: Local
visualType: Large
visualType: Small
messages:
- "reagent-effect-painkiller-strong1"
- "reagent-effect-painkiller-strong2"
@@ -565,7 +571,7 @@
- "reagent-effect-painkiller-strong4"
- "reagent-effect-painkiller-strong5"
- "reagent-effect-painkiller-strong6"
probability: 0.1
probability: 0.35
- !type:AdjustReagent
conditions:
- !type:ReagentCondition
@@ -1,11 +1,8 @@
# TODO: Migrate the following OLD status effects
# TODO: Migrate the following OLD status effects located in Resources/Prototypes/_DV/status_effects.yml
# * addicted
# *
#- type: entity
# parent: MobStatusEffectDebuff
# id: StatusEffectAddicted
# name: addicted
# components:
# - type: AddictedStatusEffect
- type: entity
parent: MobStatusEffectDebuff
id: StatusEffectChronicPainSuppressed
name: chronic pain suppression
components:
- type: ChronicPainSuppressedStatusEffect
+4 -4
View File
@@ -15,14 +15,14 @@
minDurationOfIncident: 10
- type: trait
id: InPain
name: trait-inpain-name
description: trait-inpain-desc
id: ChronicPain
name: trait-chronic-pain-name
description: trait-chronic-pain-desc
category: Medical
effects:
- !type:AddCompsEffect
components:
- type: Pain
- type: ChronicPain
- !type:SpawnItemInHandEffect
item: PillCanisterSoretizone
+4 -6
View File
@@ -1,15 +1,13 @@
# DO NOT ADD ANY NEW STATUS EFFECTS HERE.
# Use the new status effects system.
- type: statusEffect
id: Addicted
- type: statusEffect
id: SuppressAddiction
- type: statusEffect
id: InPain
- type: statusEffect
id: SuppressPain
- type: statusEffect
id: Anesthesia