Skip to content

Commit

Permalink
pain system + addictions + traits (#2192)
Browse files Browse the repository at this point in the history
* the nightmare continues unchecked

* HOLY FUCKING SHITCODE

* Update traits.ftl

* makes the yaml linter happy

* we'll regret this later

* what why
  • Loading branch information
MilonPL authored Nov 20, 2024
1 parent ef7d1bf commit f1c859a
Show file tree
Hide file tree
Showing 22 changed files with 592 additions and 166 deletions.
52 changes: 52 additions & 0 deletions Content.Client/DeltaV/Overlays/PainOverlay.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,52 @@
using System.Numerics;
using Content.Shared.DeltaV.Pain;
using Robust.Client.Graphics;
using Robust.Client.Player;
using Robust.Shared.Enums;
using Robust.Shared.Prototypes;

namespace Content.Client.DeltaV.Overlays;

public sealed partial class PainOverlay : 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()
{
IoCManager.InjectDependencies(this);
_painShader = _prototype.Index(_shaderProto).Instance().Duplicate();
}

protected override bool BeforeDraw(in OverlayDrawArgs args)
{
if (_player.LocalEntity is not { Valid: true } player
|| !_entity.HasComponent<PainComponent>(player))
{
return false;
}

return base.BeforeDraw(in args);
}

protected override void Draw(in OverlayDrawArgs args)
{
if (ScreenTexture is null)
return;

_painShader.SetParameter("SCREEN_TEXTURE", ScreenTexture);

var worldHandle = args.WorldHandle;
var viewport = args.WorldBounds;
worldHandle.SetTransform(Matrix3x2.Identity);
worldHandle.UseShader(_painShader);
worldHandle.DrawRect(viewport, Color.White);
worldHandle.UseShader(null);
}
}
65 changes: 65 additions & 0 deletions Content.Client/DeltaV/Overlays/PainSystem.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,65 @@
using Content.Shared.DeltaV.Pain;
using Robust.Client.Graphics;
using Robust.Shared.Player;

namespace Content.Client.DeltaV.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);
}
}
13 changes: 13 additions & 0 deletions Content.Server/DeltaV/Addictions/AddictionSystem.cs
Original file line number Diff line number Diff line change
Expand Up @@ -30,6 +30,19 @@ public override void Initialize()
SubscribeLocalEvent<AddictedComponent, ComponentStartup>(OnInit);
}

protected override void UpdateAddictionSuppression(Entity<AddictedComponent> ent, float duration)
{
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.SuppressionEndTime = newEndTime;
UpdateSuppressed(ent.Comp);
}

public override void Update(float frameTime)
{
base.Update(frameTime);
Expand Down
9 changes: 5 additions & 4 deletions Content.Server/DeltaV/EntityEffects/Effects/Addicted.cs
Original file line number Diff line number Diff line change
Expand Up @@ -7,19 +7,20 @@ namespace Content.Server.EntityEffects.Effects;
public sealed partial class Addicted : EntityEffect
{
/// <summary>
/// How long should each metabolism cycle make the effect last for.
/// How long should each metabolism cycle make the effect last for.
/// </summary>
[DataField]
public float AddictionTime = 3f;
public float AddictionTime = 5f;

protected override string? ReagentEffectGuidebookText(IPrototypeManager prototype, IEntitySystemManager entSys)
protected override string ReagentEffectGuidebookText(IPrototypeManager prototype, IEntitySystemManager entSys)
=> Loc.GetString("reagent-effect-guidebook-addicted", ("chance", Probability));

public override void Effect(EntityEffectBaseArgs args)
{
var addictionTime = AddictionTime;

if (args is EntityEffectReagentArgs reagentArgs) {
if (args is EntityEffectReagentArgs reagentArgs)
{
addictionTime *= reagentArgs.Scale.Float();
}

Expand Down
30 changes: 30 additions & 0 deletions Content.Server/DeltaV/EntityEffects/Effects/InPain.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,30 @@
using Content.Shared.DeltaV.Pain;
using Content.Shared.EntityEffects;
using Robust.Shared.Prototypes;

namespace Content.Server.EntityEffects.Effects;

public sealed partial class InPain : EntityEffect
{
/// <summary>
/// How long should each metabolism cycle make the effect last for.
/// </summary>
[DataField]
public float PainTime = 5f;

protected override string? ReagentEffectGuidebookText(IPrototypeManager prototype, IEntitySystemManager entSys)
=> Loc.GetString("reagent-effect-guidebook-addicted", ("chance", Probability));

public override void Effect(EntityEffectBaseArgs args)
{
var painTime = PainTime;

if (args is EntityEffectReagentArgs reagentArgs)
{
painTime *= reagentArgs.Scale.Float();
}

var painSystem = args.EntityManager.System<SharedPainSystem>();
painSystem.TryApplyPain(args.TargetEntity, painTime);
}
}
31 changes: 31 additions & 0 deletions Content.Server/DeltaV/EntityEffects/Effects/SuppressAddiction.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,31 @@
using Content.Shared.DeltaV.Addictions;
using Content.Shared.EntityEffects;
using Robust.Shared.Prototypes;

namespace Content.Server.EntityEffects.Effects;

public sealed partial class SuppressAddiction : EntityEffect
{
/// <summary>
/// How long should the addiction suppression last for each metabolism cycle
/// </summary>
[DataField]
public float SuppressionTime = 30f;

protected override string ReagentEffectGuidebookText(IPrototypeManager prototype, IEntitySystemManager entSys)
=> Loc.GetString("reagent-effect-guidebook-addiction-suppression",
("chance", Probability));

public override void Effect(EntityEffectBaseArgs args)
{
var suppressionTime = SuppressionTime;

if (args is EntityEffectReagentArgs reagentArgs)
{
suppressionTime *= reagentArgs.Scale.Float();
}

var addictionSystem = args.EntityManager.System<SharedAddictionSystem>();
addictionSystem.TrySuppressAddiction(args.TargetEntity, suppressionTime);
}
}
38 changes: 38 additions & 0 deletions Content.Server/DeltaV/EntityEffects/Effects/SuppressPain.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,38 @@
using Content.Shared.DeltaV.Pain;
using Content.Shared.EntityEffects;
using Robust.Shared.Prototypes;

namespace Content.Server.EntityEffects.Effects;

public sealed partial class SuppressPain : EntityEffect
{
/// <summary>
/// How long should the pain suppression last for each metabolism cycle
/// </summary>
[DataField]
public float SuppressionTime = 30f;

/// <summary>
/// The strength level of the pain suppression
/// </summary>
[DataField]
public PainSuppressionLevel SuppressionLevel = PainSuppressionLevel.Normal;

protected override string ReagentEffectGuidebookText(IPrototypeManager prototype, IEntitySystemManager entSys)
=> Loc.GetString("reagent-effect-guidebook-pain-suppression",
("chance", Probability),
("level", SuppressionLevel.ToString().ToLowerInvariant()));

public override void Effect(EntityEffectBaseArgs args)
{
var suppressionTime = SuppressionTime;

if (args is EntityEffectReagentArgs reagentArgs)
{
suppressionTime *= reagentArgs.Scale.Float();
}

var painSystem = args.EntityManager.System<SharedPainSystem>();
painSystem.TrySuppressPain(args.TargetEntity, suppressionTime, SuppressionLevel);
}
}
90 changes: 90 additions & 0 deletions Content.Server/DeltaV/Pain/PainSystem.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,90 @@
using Content.Shared.DeltaV.Pain;
using Content.Shared.Popups;
using Robust.Shared.Prototypes;
using Robust.Shared.Random;
using Robust.Shared.Timing;

namespace Content.Server.DeltaV.Pain;

public sealed class PainSystem : SharedPainSystem
{
[Dependency] private readonly IGameTiming _timing = default!;
[Dependency] private readonly IPrototypeManager _prototype = default!;
[Dependency] private readonly IRobustRandom _random = default!;
[Dependency] private readonly SharedPopupSystem _popup = default!;

public override void Initialize()
{
base.Initialize();
SubscribeLocalEvent<PainComponent, MapInitEvent>(OnMapInit);
}

private void OnMapInit(Entity<PainComponent> ent, ref MapInitEvent args)
{
ent.Comp.NextUpdateTime = _timing.CurTime;
ent.Comp.NextPopupTime = _timing.CurTime;
}

protected override 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 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.Owner);

// 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);
}
}
}
13 changes: 13 additions & 0 deletions Content.Shared/DeltaV/Addictions/SharedAddictionSystem.cs
Original file line number Diff line number Diff line change
Expand Up @@ -27,4 +27,17 @@ public virtual void TryApplyAddiction(EntityUid uid, float addictionTime, Status
_statusEffects.TryAddTime(uid, StatusEffectKey, TimeSpan.FromSeconds(addictionTime), status);
}
}

public virtual void TrySuppressAddiction(EntityUid uid, float duration)
{
if (!TryComp<AddictedComponent>(uid, out var comp))
return;

var ent = new Entity<AddictedComponent>(uid, comp);
UpdateAddictionSuppression(ent, duration);
}

protected virtual void UpdateAddictionSuppression(Entity<AddictedComponent> ent, float duration)
{
}
}
Loading

0 comments on commit f1c859a

Please sign in to comment.