using Content.Shared.Mobs;
using Content.Shared.Mobs.Components;
using Content.Shared.Mobs.Systems;
+using Content.Shared.StatusEffectNew;
using Content.Shared.Traits.Assorted;
using JetBrains.Annotations;
using Robust.Client.Graphics;
[Dependency] private readonly IPlayerManager _playerManager = default!;
[UISystemDependency] private readonly MobThresholdSystem _mobThresholdSystem = default!;
+ [UISystemDependency] private readonly StatusEffectsSystem _statusEffects = default!;
private Overlays.DamageOverlay _overlay = default!;
public override void Initialize()
FixedPoint2 painLevel = 0;
_overlay.PainLevel = 0;
- if (!EntityManager.HasComponent<PainNumbnessComponent>(entity))
+ if (!_statusEffects.TryEffectsWithComp<PainNumbnessStatusEffectComponent>(entity, out _))
{
foreach (var painDamageType in damageable.PainDamageGroups)
{
using Content.Shared.Implants.Components;
using Content.Shared.NameModifier.EntitySystems;
using Content.Shared.StatusEffect;
+using Content.Shared.StatusEffectNew.Components;
using Content.Shared.Storage;
using Content.Shared.Storage.EntitySystems;
using Content.Shared.Whitelist;
[Dependency] private readonly SharedStorageSystem _storage = default!;
[Dependency] private readonly SharedSubdermalImplantSystem _subdermalImplant = default!;
[Dependency] private readonly NameModifierSystem _nameMod = default!;
+ [Dependency] private readonly Shared.StatusEffectNew.StatusEffectsSystem _statusEffects = default!; //TODO: This system has to support both the old and new status effect systems, until the old is able to be fully removed.
/// <summary>
/// Spawns a clone of the given humanoid mob at the specified location or in nullspace.
if (settings.CopyImplants)
CopyImplants(original, clone.Value, settings.CopyInternalStorage, settings.Whitelist, settings.Blacklist);
+ // Copy permanent status effects
+ if (settings.CopyStatusEffects)
+ CopyStatusEffects(original, clone.Value);
+
var originalName = _nameMod.GetBaseName(original);
// Set the clone's name. The raised events will also adjust their PDA and ID card names.
}
}
+
+ /// <summary>
+ /// Scans all permanent status effects applied to the original entity and transfers them to the clone.
+ /// </summary>
+ public void CopyStatusEffects(Entity<StatusEffectContainerComponent?> original, Entity<StatusEffectContainerComponent?> target)
+ {
+ if (!Resolve(original, ref original.Comp, false))
+ return;
+
+ if (original.Comp.ActiveStatusEffects is null)
+ return;
+
+ foreach (var effect in original.Comp.ActiveStatusEffects.ContainedEntities)
+ {
+ if (!TryComp<StatusEffectComponent>(effect, out var effectComp))
+ continue;
+
+ //We are not interested in temporary effects, only permanent ones.
+ if (effectComp.EndEffectTime is not null)
+ continue;
+
+ var effectProto = Prototype(effect);
+
+ if (effectProto is null)
+ continue;
+
+ _statusEffects.TrySetStatusEffectDuration(target, effectProto);
+ }
+ }
}
--- /dev/null
+using Content.Shared.Roles;
+using Content.Shared.StatusEffectNew;
+using JetBrains.Annotations;
+using Robust.Shared.Prototypes;
+
+namespace Content.Server.Jobs;
+
+/// <summary>
+/// Adds permanent status effects to the entity.
+/// TODO: Move this, and other JobSpecials, from Server to Shared.
+/// </summary>
+[UsedImplicitly]
+public sealed partial class ApplyStatusEffectSpecial : JobSpecial
+{
+ [DataField(required: true)]
+ public HashSet<EntProtoId> StatusEffects { get; private set; } = new();
+
+ public override void AfterEquip(EntityUid mob)
+ {
+ var entMan = IoCManager.Resolve<IEntityManager>();
+ var statusSystem = entMan.System<StatusEffectsSystem>();
+ foreach (var effect in StatusEffects)
+ {
+ statusSystem.TrySetStatusEffectDuration(mob, effect);
+ }
+ }
+}
using Content.Shared.Hands.Components;
using Content.Shared.Hands.EntitySystems;
using Content.Shared.Roles;
+using Content.Shared.StatusEffectNew;
using Content.Shared.Traits;
using Content.Shared.Whitelist;
using Robust.Shared.Prototypes;
[Dependency] private readonly IPrototypeManager _prototypeManager = default!;
[Dependency] private readonly SharedHandsSystem _sharedHandsSystem = default!;
[Dependency] private readonly EntityWhitelistSystem _whitelistSystem = default!;
+ [Dependency] private readonly StatusEffectsSystem _statusEffects = default!;
public override void Initialize()
{
continue;
// Add all components required by the prototype
- EntityManager.AddComponents(args.Mob, traitPrototype.Components, false);
+ if (traitPrototype.Components.Count > 0)
+ EntityManager.AddComponents(args.Mob, traitPrototype.Components, false);
+
+ // Add all JobSpecials required by the prototype
+ foreach (var special in traitPrototype.Specials)
+ {
+ special.AfterEquip(args.Mob);
+ }
// Add item required by the trait
if (traitPrototype.TraitGear == null)
[DataField]
public bool CopyImplants = true;
+ /// <summary>
+ /// Should infinite status effects applied to an entity be copied or not?
+ /// </summary>
+ [DataField]
+ public bool CopyStatusEffects = true;
+
/// <summary>
/// Whitelist for the equipment allowed to be copied.
/// </summary>
namespace Content.Shared.Roles
{
/// <summary>
- /// Provides special hooks for when jobs get spawned in/equipped.
+ /// Provides special hooks for when jobs get spawned in/equipped.
+ /// TODO: This is being/should be utilized by more than jobs, and is really just a way to assign components/implants/status effects upon spawning. Rename this class and its derivatives in the future!
+ /// TODO: Move derivatives from Server to Shared, probably.
/// </summary>
[ImplicitDataDefinitionForInheritors]
public abstract partial class JobSpecial
/// <summary>
/// Returns all status effects that have the specified component.
/// </summary>
+ /// <returns>Returns true if any entity with the specified component is found.</returns>
public bool TryEffectsWithComp<T>(EntityUid? target, [NotNullWhen(true)] out HashSet<Entity<T, StatusEffectComponent>>? effects) where T : IComponent
{
effects = null;
+using Content.Shared.Damage.Events;
+using Content.Shared.Mobs.Events;
using Content.Shared.Movement.Events;
using Content.Shared.Movement.Systems;
using Content.Shared.Rejuvenate;
SubscribeLocalEvent<StatusEffectContainerComponent, StandUpAttemptEvent>(RefRelayStatusEffectEvent);
SubscribeLocalEvent<StatusEffectContainerComponent, StunEndAttemptEvent>(RefRelayStatusEffectEvent);
+ SubscribeLocalEvent<StatusEffectContainerComponent, BeforeForceSayEvent>(RelayStatusEffectEvent);
+ SubscribeLocalEvent<StatusEffectContainerComponent, BeforeAlertSeverityCheckEvent>(RelayStatusEffectEvent);
+
SubscribeLocalEvent<StatusEffectContainerComponent, AccentGetEvent>(RelayStatusEffectEvent);
}
+++ /dev/null
-using Content.Shared.Dataset;
-using Robust.Shared.GameStates;
-using Robust.Shared.Prototypes;
-
-namespace Content.Shared.Traits.Assorted;
-
-[RegisterComponent, NetworkedComponent]
-public sealed partial class PainNumbnessComponent : Component
-{
- /// <summary>
- /// The fluent string prefix to use when picking a random suffix
- /// This is only active for those who have the pain numbness component
- /// </summary>
- [DataField]
- public ProtoId<LocalizedDatasetPrototype> ForceSayNumbDataset = "ForceSayNumbDataset";
-}
--- /dev/null
+using Content.Shared.Dataset;
+using Robust.Shared.GameStates;
+using Robust.Shared.Prototypes;
+
+namespace Content.Shared.Traits.Assorted;
+
+/// <summary>
+/// Hides the damage overlay and displays the health alert for the client controlling the entity as full.
+/// Has to be applied as a status effect.
+/// </summary>
+[RegisterComponent, NetworkedComponent]
+public sealed partial class PainNumbnessStatusEffectComponent : Component
+{
+ /// <summary>
+ /// The fluent string prefix to use when picking a random suffix upon taking damage.
+ /// This is only active for those who have the pain numbness status effect. Set to null to prevent changing.
+ /// </summary>
+ [DataField]
+ public ProtoId<LocalizedDatasetPrototype>? ForceSayNumbDataset = "ForceSayNumbDataset";
+}
using Content.Shared.Mobs.Components;
using Content.Shared.Mobs.Events;
using Content.Shared.Mobs.Systems;
+using Content.Shared.StatusEffectNew;
namespace Content.Shared.Traits.Assorted;
public override void Initialize()
{
- SubscribeLocalEvent<PainNumbnessComponent, ComponentInit>(OnComponentInit);
- SubscribeLocalEvent<PainNumbnessComponent, ComponentRemove>(OnComponentRemove);
- SubscribeLocalEvent<PainNumbnessComponent, BeforeForceSayEvent>(OnChangeForceSay);
- SubscribeLocalEvent<PainNumbnessComponent, BeforeAlertSeverityCheckEvent>(OnAlertSeverityCheck);
+ SubscribeLocalEvent<PainNumbnessStatusEffectComponent, StatusEffectAppliedEvent>(OnEffectApplied);
+ SubscribeLocalEvent<PainNumbnessStatusEffectComponent, StatusEffectRemovedEvent>(OnEffectRemoved);
+ SubscribeLocalEvent<PainNumbnessStatusEffectComponent, StatusEffectRelayedEvent<BeforeForceSayEvent>>(OnChangeForceSay);
+ SubscribeLocalEvent<PainNumbnessStatusEffectComponent, StatusEffectRelayedEvent<BeforeAlertSeverityCheckEvent>>(OnAlertSeverityCheck);
}
- private void OnComponentRemove(EntityUid uid, PainNumbnessComponent component, ComponentRemove args)
+ private void OnEffectApplied(Entity<PainNumbnessStatusEffectComponent> ent, ref StatusEffectAppliedEvent args)
{
- if (!HasComp<MobThresholdsComponent>(uid))
+ if (!HasComp<MobThresholdsComponent>(args.Target))
return;
- _mobThresholdSystem.VerifyThresholds(uid);
+ _mobThresholdSystem.VerifyThresholds(args.Target);
}
- private void OnComponentInit(EntityUid uid, PainNumbnessComponent component, ComponentInit args)
+ private void OnEffectRemoved(Entity<PainNumbnessStatusEffectComponent> ent, ref StatusEffectRemovedEvent args)
{
- if (!HasComp<MobThresholdsComponent>(uid))
+ if (!HasComp<MobThresholdsComponent>(args.Target))
return;
- _mobThresholdSystem.VerifyThresholds(uid);
+ _mobThresholdSystem.VerifyThresholds(args.Target);
}
- private void OnChangeForceSay(Entity<PainNumbnessComponent> ent, ref BeforeForceSayEvent args)
+ private void OnChangeForceSay(Entity<PainNumbnessStatusEffectComponent> ent, ref StatusEffectRelayedEvent<BeforeForceSayEvent> args)
{
- args.Prefix = ent.Comp.ForceSayNumbDataset;
+ if (ent.Comp.ForceSayNumbDataset != null)
+ args.Args.Prefix = ent.Comp.ForceSayNumbDataset.Value;
}
- private void OnAlertSeverityCheck(Entity<PainNumbnessComponent> ent, ref BeforeAlertSeverityCheckEvent args)
+ private void OnAlertSeverityCheck(Entity<PainNumbnessStatusEffectComponent> ent, ref StatusEffectRelayedEvent<BeforeAlertSeverityCheckEvent> args)
{
- if (args.CurrentAlert == "HumanHealth")
- args.CancelUpdate = true;
+ if (args.Args.CurrentAlert == "HumanHealth")
+ args.Args.CancelUpdate = true;
}
}
+using Content.Shared.Roles;
using Content.Shared.Whitelist;
using Robust.Shared.Prototypes;
/// <summary>
/// The components that get added to the player, when they pick this trait.
+ /// NOTE: When implementing a new trait, it's preferable to add it as a status effect instead if possible.
/// </summary>
[DataField]
- public ComponentRegistry Components { get; private set; } = default!;
+ [Obsolete("Use JobSpecial instead.")]
+ public ComponentRegistry Components { get; private set; } = new();
+
+ /// <summary>
+ /// Special effects applied to the player who takes this Trait.
+ /// </summary>
+ [DataField(serverOnly: true)]
+ public List<JobSpecial> Specials { get; private set; } = new();
/// <summary>
/// Gear that is given to the player, when they pick this trait.
- Muted
- Narcolepsy
- Pacified
- - PainNumbness
- Paracusia
- PermanentBlindness
- Snoring
id: BloodstreamStatusEffectBase
abstract: true
components:
- - type: StatusEffect
- whitelist:
- components:
- - Bloodstream
+ - type: StatusEffect
+ whitelist:
+ components:
+ - Bloodstream
- type: entity
parent: [ BloodstreamStatusEffectBase ]
id: StatusEffectBloodloss
name: bloodloss
components:
- - type: StutteringAccent
- - type: DrunkStatusEffect
- - type: RejuvenateRemovedStatusEffect
+ - type: StutteringAccent
+ - type: DrunkStatusEffect
+ - type: RejuvenateRemovedStatusEffect
+
+- type: entity
+ parent: MobStatusEffectBase
+ id: PainNumbnessTraitStatusEffect
+ components:
+ - type: StatusEffect
+ whitelist:
+ components:
+ - MobState
+ - MobThresholds
+ - type: PainNumbnessStatusEffect
name: trait-painnumbness-name
description: trait-painnumbness-desc
category: Disabilities
- components:
- - type: PainNumbness
+ specials:
+ - !type:ApplyStatusEffectSpecial
+ statusEffects:
+ - PainNumbnessTraitStatusEffect
- type: trait
id: Hemophilia