From 45637dc3683744dda9ec370a5396479bad54c268 Mon Sep 17 00:00:00 2001 From: Wardex Date: Sun, 26 Apr 2026 20:58:28 +0300 Subject: [PATCH 01/19] refactor-step-1 --- .../ScpOnSoundVisibilityHudSystem.Overlay.cs} | 18 +- .../ScpOnSoundVisibilityHudSystem.cs | 293 ++++++++++++++++++ .../ScpOnSoundVisibilityResetAlphaOverlay.cs} | 10 +- .../ScpOnSoundVisibilitySetAlphaOverlay.cs} | 16 +- .../_Scp/Scp939/Scp939HudSystem.Visibility.cs | 162 ---------- Content.Client/_Scp/Scp939/Scp939HudSystem.cs | 149 --------- .../ScpOnSoundVisibilitySystem.cs | 197 ++++++++++++ .../_Scp/Scp939/Scp939System.Actions.cs | 4 +- .../_Scp/Scp939/Scp939System.Visibility.cs | 168 ---------- Content.Server/_Scp/Scp939/Scp939System.cs | 29 -- .../ActiveScpOnSoundVisibilityComponent.cs | 25 ++ .../ScpOnSoundVisibilityComponent.cs} | 4 +- ...ScpOnSoundVisibilityProtectionComponent.cs | 10 + .../ScpOnSoundVisibilityViewerComponent.cs | 36 +++ .../Scp939/ActiveScp939VisibilityComponent.cs | 25 -- ...ection.cs => Scp939ProtectionComponent.cs} | 0 Content.Shared/_Scp/Scp939/Scp939Component.cs | 18 +- .../Entities/Mobs/Player/Scp/Main/scp939.yml | 1 + 18 files changed, 589 insertions(+), 576 deletions(-) rename Content.Client/_Scp/{Scp939/Scp939HudSystem.Overlay.cs => Other/ScpOnSoundVisibility/ScpOnSoundVisibilityHudSystem.Overlay.cs} (52%) create mode 100644 Content.Client/_Scp/Other/ScpOnSoundVisibility/ScpOnSoundVisibilityHudSystem.cs rename Content.Client/_Scp/{Scp939/Scp939ResetAlphaOverlay.cs => Other/ScpOnSoundVisibility/ScpOnSoundVisibilityResetAlphaOverlay.cs} (63%) rename Content.Client/_Scp/{Scp939/Scp939SetAlphaOverlay.cs => Other/ScpOnSoundVisibility/ScpOnSoundVisibilitySetAlphaOverlay.cs} (70%) delete mode 100644 Content.Client/_Scp/Scp939/Scp939HudSystem.Visibility.cs delete mode 100644 Content.Client/_Scp/Scp939/Scp939HudSystem.cs create mode 100644 Content.Server/_Scp/Other/ScpOnSoundVisibility/ScpOnSoundVisibilitySystem.cs delete mode 100644 Content.Server/_Scp/Scp939/Scp939System.Visibility.cs create mode 100644 Content.Shared/_Scp/Other/ScpOnSoundVisibility/ActiveScpOnSoundVisibilityComponent.cs rename Content.Shared/_Scp/{Scp939/Scp939VisabilityComponent.cs => Other/ScpOnSoundVisibility/ScpOnSoundVisibilityComponent.cs} (75%) create mode 100644 Content.Shared/_Scp/Other/ScpOnSoundVisibility/ScpOnSoundVisibilityProtectionComponent.cs create mode 100644 Content.Shared/_Scp/Other/ScpOnSoundVisibility/ScpOnSoundVisibilityViewerComponent.cs delete mode 100644 Content.Shared/_Scp/Scp939/ActiveScp939VisibilityComponent.cs rename Content.Shared/_Scp/Scp939/Protection/{Scp939Protection.cs => Scp939ProtectionComponent.cs} (100%) diff --git a/Content.Client/_Scp/Scp939/Scp939HudSystem.Overlay.cs b/Content.Client/_Scp/Other/ScpOnSoundVisibility/ScpOnSoundVisibilityHudSystem.Overlay.cs similarity index 52% rename from Content.Client/_Scp/Scp939/Scp939HudSystem.Overlay.cs rename to Content.Client/_Scp/Other/ScpOnSoundVisibility/ScpOnSoundVisibilityHudSystem.Overlay.cs index e14458dfc9..2fca6755a9 100644 --- a/Content.Client/_Scp/Scp939/Scp939HudSystem.Overlay.cs +++ b/Content.Client/_Scp/Other/ScpOnSoundVisibility/ScpOnSoundVisibilityHudSystem.Overlay.cs @@ -1,28 +1,28 @@ -using Content.Shared._Scp.Scp939; +using Content.Shared._Scp.Other.ScpOnSoundVisibility; using Robust.Client.Graphics; using Robust.Shared.Player; -namespace Content.Client._Scp.Scp939; +namespace Content.Client._Scp.Other.ScpOnSoundVisibility; -public sealed partial class Scp939HudSystem +public sealed partial class ScpOnSoundVisibilityHudSystem { [Dependency] private readonly IOverlayManager _overlayManager = default!; private void InitializeOverlay() { - SubscribeLocalEvent(OnPlayerAttached); - SubscribeLocalEvent(OnPlayerDetached); + SubscribeLocalEvent(OnPlayerAttached); + SubscribeLocalEvent(OnPlayerDetached); } - private void OnPlayerAttached(Entity ent, ref LocalPlayerAttachedEvent args) + private void OnPlayerAttached(Entity ent, ref LocalPlayerAttachedEvent args) { - _scp939Component = ent.Comp; + _viewerComponent = ent.Comp; AddOverlays(); } - private void OnPlayerDetached(Entity ent, ref LocalPlayerDetachedEvent args) + private void OnPlayerDetached(Entity ent, ref LocalPlayerDetachedEvent args) { - _scp939Component = null; + _viewerComponent = null; } private void AddOverlays() diff --git a/Content.Client/_Scp/Other/ScpOnSoundVisibility/ScpOnSoundVisibilityHudSystem.cs b/Content.Client/_Scp/Other/ScpOnSoundVisibility/ScpOnSoundVisibilityHudSystem.cs new file mode 100644 index 0000000000..449aa419bb --- /dev/null +++ b/Content.Client/_Scp/Other/ScpOnSoundVisibility/ScpOnSoundVisibilityHudSystem.cs @@ -0,0 +1,293 @@ +using Content.Client.Overlays; +using Content.Client.SSDIndicator; +using Content.Shared._Scp.Other.ScpOnSoundVisibility; +using Content.Shared.Examine; +using Content.Shared.Inventory.Events; +using Content.Shared.Movement.Components; +using Content.Shared.Standing; +using Content.Shared.StatusIcon.Components; +using Content.Shared.Throwing; +using Content.Shared.Weapons.Melee.Events; +using Content.Shared.Whitelist; +using Robust.Client.GameObjects; +using Robust.Client.Graphics; +using Robust.Client.Player; +using Robust.Shared.Physics.Components; +using Robust.Shared.Physics.Events; +using Robust.Shared.Random; + +namespace Content.Client._Scp.Other.ScpOnSoundVisibility; + +public sealed partial class ScpOnSoundVisibilityHudSystem : EquipmentHudSystem +{ + [Dependency] private readonly EntityWhitelistSystem _whitelist = default!; + [Dependency] private readonly IRobustRandom _random = default!; + + [Dependency] private readonly IPlayerManager _playerManager = default!; + [Dependency] private readonly SpriteSystem _sprite = default!; + + internal readonly List<(Entity Ent, float BaseAlpha)> CachedBaseAlphas = new(64); + + private EntityQuery _eyeQuery; + private EntityQuery _movementSpeedQuery; + private EntityQuery _physicsQuery; + + private ScpOnSoundVisibilitySetAlphaOverlay _setAlphaOverlay = default!; + private ScpOnSoundVisibilityResetAlphaOverlay _resetAlphaOverlay = default!; + + private ScpOnSoundVisibilityViewerComponent? _viewerComponent; + + private bool _overlaysPresented; + private float _lastUpdateTime; + private const float UpdateInterval = 0.05f; + + public override void Initialize() + { + base.Initialize(); + + InitializeOverlay(); + + SubscribeLocalEvent(OnGetStatusIcons, after: [typeof(SSDIndicatorSystem)]); + SubscribeLocalEvent(OnExamine); + + SubscribeLocalEvent((Entity ent, ref StartCollideEvent args) + => OnCollide(ent, args.OtherEntity)); + SubscribeLocalEvent((Entity ent, ref EndCollideEvent args) + => OnCollide(ent, args.OtherEntity)); + SubscribeLocalEvent(OnVisibilityStateUpdated); + + SubscribeLocalEvent(OnMove); + + SubscribeLocalEvent(OnThrow); + SubscribeLocalEvent(OnStood); + SubscribeLocalEvent(OnMeleeAttack); + SubscribeLocalEvent(OnDown); + + _eyeQuery = GetEntityQuery(); + _movementSpeedQuery = GetEntityQuery(); + _physicsQuery = GetEntityQuery(); + + _setAlphaOverlay = new(); + _resetAlphaOverlay = new(); + } + + public override void Shutdown() + { + RestoreCachedBaseAlphas(); + RemoveOverlays(); + + _setAlphaOverlay.Dispose(); + _resetAlphaOverlay.Dispose(); + + base.Shutdown(); + } + + private void OnVisibilityStateUpdated(Entity ent, ref AfterAutoHandleStateEvent args) + { + if (ent.Comp.LastHandledVisibilityResetCounter == ent.Comp.VisibilityResetCounter) + return; + + ent.Comp.LastHandledVisibilityResetCounter = ent.Comp.VisibilityResetCounter; + ent.Comp.VisibilityAcc = ScpOnSoundVisibilityComponent.InitialVisibilityAcc; + } + + private void OnExamine(Entity ent, ref ExamineAttemptEvent args) + { + if (!IsActive) + return; + + var visibility = GetVisibility(ent); + + if (visibility < 0.2f) + args.Cancel(); + } + + private void OnGetStatusIcons(Entity ent, ref GetStatusIconsEvent args) + { + if (!IsActive) + return; + + var visibility = GetVisibility(ent); + + if (visibility <= 0.5f) + args.StatusIcons.Clear(); + } + + protected override void UpdateInternal(RefreshEquipmentHudEvent args) + { + base.UpdateInternal(args); + + _viewerComponent = args.Components.Count > 0 ? args.Components[0] : null; + AddOverlays(); + } + + protected override void DeactivateInternal() + { + base.DeactivateInternal(); + + _viewerComponent = null; + _lastUpdateTime = 0f; + + RestoreCachedBaseAlphas(); + RemoveOverlays(); + } + + public override void Update(float frameTime) + { + base.Update(frameTime); + + if (!IsActive) + return; + + _lastUpdateTime += frameTime; + if (_lastUpdateTime < UpdateInterval) + return; + + var delta = _lastUpdateTime; + _lastUpdateTime = 0f; + + var query = EntityQueryEnumerator(); + while (query.MoveNext(out _, out var visibilityComponent)) + { + if (visibilityComponent.VisibilityAcc >= visibilityComponent.HideTime) + continue; + + visibilityComponent.VisibilityAcc = MathF.Min(visibilityComponent.VisibilityAcc + delta, visibilityComponent.HideTime); + } + } + + internal bool CanDraw(in OverlayDrawArgs args) + { + if (!IsActive) + return false; + + if (_playerManager.LocalEntity is not { } player) + return false; + + if (!_eyeQuery.TryComp(player, out var eye)) + return false; + + return args.Viewport.Eye == eye.Eye; + } + + internal void RestoreCachedBaseAlphas() + { + foreach (var (ent, baseAlpha) in CachedBaseAlphas) + { + if (!EntityManager.EntityExists(ent)) + continue; + + _sprite.SetColor(ent.AsNullable(), ent.Comp.Color.WithAlpha(baseAlpha)); + } + + CachedBaseAlphas.Clear(); + } + + internal static float GetVisibility(Entity ent) + { + var acc = ent.Comp.VisibilityAcc; + + if (acc > ent.Comp.HideTime) + return 0; + + return Math.Clamp(1f - (acc / ent.Comp.HideTime), 0f, 1f); + } + + private void OnMove(Entity ent, ref MoveEvent args) + { + if (!IsActive) + return; + + // В зависимости от наличие защит или проблем со зрением у 939 изменяется то, насколько хорошо мы видим жертву + if (ModifyAcc(ent.Comp, out var modifier)) // Если зрение затруднено + { + ent.Comp.VisibilityAcc *= modifier; + } + else if (!TryComp(ent, out var viewerComp) || + !_whitelist.IsWhitelistPass(viewerComp.Protections, ent)) // Если имеется защита(тихое хождение) + { + return; + } + else // Если со зрением все ок + { + ent.Comp.VisibilityAcc = 0; + } + + if (!_movementSpeedQuery.TryComp(ent, out var speedModifierComponent) + || !_physicsQuery.TryComp(ent, out var physicsComponent)) + { + return; + } + + var currentVelocity = physicsComponent.LinearVelocity.Length(); + + if (speedModifierComponent.BaseWalkSpeed > currentVelocity) + ent.Comp.VisibilityAcc = ent.Comp.HideTime / 2f; + } + + + private void OnCollide(Entity ent, EntityUid otherEntity) + { + if (!IsActive) + return; + + if (!HasComp(otherEntity)) + return; + + MobDidSomething(ent); + } + + private void OnThrow(Entity ent, ref ThrowEvent args) + { + if (!IsActive) + return; + + MobDidSomething(ent); + } + + private void OnStood(Entity ent, ref StoodEvent args) + { + if (!IsActive) + return; + + MobDidSomething(ent); + } + + private void OnMeleeAttack(Entity ent, ref MeleeAttackEvent args) + { + if (!IsActive) + return; + + MobDidSomething(ent); + } + + private void OnDown(Entity ent, ref DownedEvent args) + { + if (!IsActive) + return; + + MobDidSomething(ent); + } + + private void MobDidSomething(Entity ent) + { + ent.Comp.VisibilityAcc = ScpOnSoundVisibilityComponent.InitialVisibilityAcc; + } + + // TODO: Переделать под статус эффект и добавить его в панель статус эффектов, а то непонятно игруну + private bool ModifyAcc(ActiveScpOnSoundVisibilityComponent visibilityComponent, out int modifier) + { + // 1 = отсутствие модификатора + modifier = 1; + + if (_viewerComponent == null) + return false; + + if (!_viewerComponent.PoorEyesight) + return false; + + modifier = _random.Next(visibilityComponent.MinValue, visibilityComponent.MaxValue); + + return true; + } +} diff --git a/Content.Client/_Scp/Scp939/Scp939ResetAlphaOverlay.cs b/Content.Client/_Scp/Other/ScpOnSoundVisibility/ScpOnSoundVisibilityResetAlphaOverlay.cs similarity index 63% rename from Content.Client/_Scp/Scp939/Scp939ResetAlphaOverlay.cs rename to Content.Client/_Scp/Other/ScpOnSoundVisibility/ScpOnSoundVisibilityResetAlphaOverlay.cs index 2eb83579c8..3538ebe2a4 100644 --- a/Content.Client/_Scp/Scp939/Scp939ResetAlphaOverlay.cs +++ b/Content.Client/_Scp/Other/ScpOnSoundVisibility/ScpOnSoundVisibilityResetAlphaOverlay.cs @@ -1,21 +1,21 @@ using Robust.Client.Graphics; using Robust.Shared.Enums; -namespace Content.Client._Scp.Scp939; +namespace Content.Client._Scp.Other.ScpOnSoundVisibility; -public sealed class Scp939ResetAlphaOverlay : Overlay +public sealed class ScpOnSoundVisibilityResetAlphaOverlay : Overlay { [Dependency] private readonly IEntityManager _ent = default!; - private readonly Scp939HudSystem _hud; + private readonly ScpOnSoundVisibilityHudSystem _hud; public override OverlaySpace Space => OverlaySpace.WorldSpace; - public Scp939ResetAlphaOverlay() + public ScpOnSoundVisibilityResetAlphaOverlay() { IoCManager.InjectDependencies(this); - _hud = _ent.System(); + _hud = _ent.System(); } protected override bool BeforeDraw(in OverlayDrawArgs args) diff --git a/Content.Client/_Scp/Scp939/Scp939SetAlphaOverlay.cs b/Content.Client/_Scp/Other/ScpOnSoundVisibility/ScpOnSoundVisibilitySetAlphaOverlay.cs similarity index 70% rename from Content.Client/_Scp/Scp939/Scp939SetAlphaOverlay.cs rename to Content.Client/_Scp/Other/ScpOnSoundVisibility/ScpOnSoundVisibilitySetAlphaOverlay.cs index d081f9afa4..3fcba67ddf 100644 --- a/Content.Client/_Scp/Scp939/Scp939SetAlphaOverlay.cs +++ b/Content.Client/_Scp/Other/ScpOnSoundVisibility/ScpOnSoundVisibilitySetAlphaOverlay.cs @@ -1,25 +1,25 @@ -using Content.Shared._Scp.Scp939; +using Content.Shared._Scp.Other.ScpOnSoundVisibility; using Robust.Client.GameObjects; using Robust.Client.Graphics; using Robust.Shared.Enums; -namespace Content.Client._Scp.Scp939; +namespace Content.Client._Scp.Other.ScpOnSoundVisibility; -public sealed class Scp939SetAlphaOverlay : Overlay +public sealed class ScpOnSoundVisibilitySetAlphaOverlay : Overlay { [Dependency] private readonly IEntityManager _ent = default!; - private readonly Scp939HudSystem _hud; + private readonly ScpOnSoundVisibilityHudSystem _hud; private readonly TransformSystem _transform; private readonly SpriteSystem _sprite; public override OverlaySpace Space => OverlaySpace.WorldSpaceBelowEntities; - public Scp939SetAlphaOverlay() + public ScpOnSoundVisibilitySetAlphaOverlay() { IoCManager.InjectDependencies(this); - _hud = _ent.System(); + _hud = _ent.System(); _transform = _ent.System(); _sprite = _ent.System(); } @@ -33,7 +33,7 @@ protected override void Draw(in OverlayDrawArgs args) { _hud.CachedBaseAlphas.Clear(); - var query = _ent.EntityQueryEnumerator(); + var query = _ent.EntityQueryEnumerator(); while (query.MoveNext(out var uid, out var visibility, out var sprite, out var xform)) { if (xform.MapID != args.MapId || !sprite.Visible) @@ -43,7 +43,7 @@ protected override void Draw(in OverlayDrawArgs args) if (!args.WorldBounds.Contains(worldPosition)) continue; - var targetAlpha = Scp939HudSystem.GetVisibility((uid, visibility)); + var targetAlpha = ScpOnSoundVisibilityHudSystem.GetVisibility((uid, visibility)); if (MathF.Abs(sprite.Color.A - targetAlpha) <= 0.01f) continue; diff --git a/Content.Client/_Scp/Scp939/Scp939HudSystem.Visibility.cs b/Content.Client/_Scp/Scp939/Scp939HudSystem.Visibility.cs deleted file mode 100644 index dc111bf2bf..0000000000 --- a/Content.Client/_Scp/Scp939/Scp939HudSystem.Visibility.cs +++ /dev/null @@ -1,162 +0,0 @@ -using Content.Shared._Scp.Scp939; -using Content.Shared.Standing; -using Content.Shared.Throwing; -using Content.Shared.Weapons.Melee.Events; -using Robust.Shared.Physics.Events; -using Robust.Shared.Random; - -namespace Content.Client._Scp.Scp939; - -public sealed partial class Scp939HudSystem -{ - [Dependency] private readonly IRobustRandom _random = default!; - - private void InitializeVisibility() - { - SubscribeLocalEvent((Entity ent, ref StartCollideEvent args) - => OnCollide(ent, args.OtherEntity)); - SubscribeLocalEvent((Entity ent, ref EndCollideEvent args) - => OnCollide(ent, args.OtherEntity)); - SubscribeLocalEvent(OnVisibilityStateUpdated); - - SubscribeLocalEvent(OnMove); - - SubscribeLocalEvent(OnThrow); - SubscribeLocalEvent(OnStood); - SubscribeLocalEvent(OnMeleeAttack); - SubscribeLocalEvent(OnDown); - } - - private void OnVisibilityStateUpdated(Entity ent, ref AfterAutoHandleStateEvent args) - { - if (ent.Comp.LastHandledVisibilityResetCounter == ent.Comp.VisibilityResetCounter) - return; - - ent.Comp.LastHandledVisibilityResetCounter = ent.Comp.VisibilityResetCounter; - ent.Comp.VisibilityAcc = Scp939VisibilityComponent.InitialVisibilityAcc; - } - - public override void Update(float frameTime) - { - base.Update(frameTime); - - if (!IsActive) - return; - - _lastUpdateTime += frameTime; - if (_lastUpdateTime < UpdateInterval) - return; - - var delta = _lastUpdateTime; - _lastUpdateTime = 0f; - - var query = EntityQueryEnumerator(); - while (query.MoveNext(out _, out var visibilityComponent)) - { - if (visibilityComponent.VisibilityAcc >= visibilityComponent.HideTime) - continue; - - visibilityComponent.VisibilityAcc = MathF.Min(visibilityComponent.VisibilityAcc + delta, visibilityComponent.HideTime); - } - } - - private void OnMove(Entity ent, ref MoveEvent args) - { - if (!IsActive) - return; - - // В зависимости от наличие защит или проблем со зрением у 939 изменяется то, насколько хорошо мы видим жертву - if (ModifyAcc(ent.Comp, out var modifier)) // Если зрение затруднено - { - ent.Comp.VisibilityAcc *= modifier; - } - else if (_scp939ProtectionQuery.HasComp(ent)) // Если имеется защита(тихое хождение) - { - return; - } - else // Если со зрением все ок - { - ent.Comp.VisibilityAcc = 0; - } - - if (!_movementSpeedQuery.TryComp(ent, out var speedModifierComponent) - || !_physicsQuery.TryComp(ent, out var physicsComponent)) - { - return; - } - - var currentVelocity = physicsComponent.LinearVelocity.Length(); - - if (speedModifierComponent.BaseWalkSpeed > currentVelocity) - ent.Comp.VisibilityAcc = ent.Comp.HideTime / 2f; - } - - - private void OnCollide(Entity ent, EntityUid otherEntity) - { - if (!IsActive) - return; - - if (!HasComp(otherEntity)) - return; - - MobDidSomething(ent); - } - - private void OnThrow(Entity ent, ref ThrowEvent args) - { - if (!IsActive) - return; - - MobDidSomething(ent); - } - - private void OnStood(Entity ent, ref StoodEvent args) - { - if (!IsActive) - return; - - MobDidSomething(ent); - } - - private void OnMeleeAttack(Entity ent, ref MeleeAttackEvent args) - { - if (!IsActive) - return; - - MobDidSomething(ent); - } - - private void OnDown(Entity ent, ref DownedEvent args) - { - if (!IsActive) - return; - - MobDidSomething(ent); - } - - private void MobDidSomething(Entity ent) - { - ent.Comp.VisibilityAcc = Scp939VisibilityComponent.InitialVisibilityAcc; - } - - // TODO: Переделать под статус эффект и добавить его в панель статус эффектов, а то непонятно игруну - /// - /// Если вдруг собачка плохо видит - /// - private bool ModifyAcc(ActiveScp939VisibilityComponent visibilityComponent, out int modifier) - { - // 1 = отсутствие модификатора - modifier = 1; - - if (_scp939Component == null) - return false; - - if (!_scp939Component.PoorEyesight) - return false; - - modifier = _random.Next(visibilityComponent.MinValue, visibilityComponent.MaxValue); - - return true; - } -} diff --git a/Content.Client/_Scp/Scp939/Scp939HudSystem.cs b/Content.Client/_Scp/Scp939/Scp939HudSystem.cs deleted file mode 100644 index a0136d8844..0000000000 --- a/Content.Client/_Scp/Scp939/Scp939HudSystem.cs +++ /dev/null @@ -1,149 +0,0 @@ -using Content.Client.Overlays; -using Content.Client.SSDIndicator; -using Content.Client.Stealth; -using Content.Shared._Scp.Scp939; -using Content.Shared._Scp.Scp939.Protection; -using Content.Shared.Examine; -using Content.Shared.Inventory.Events; -using Content.Shared.Movement.Components; -using Content.Shared.StatusIcon.Components; -using Robust.Client.GameObjects; -using Robust.Client.Graphics; -using Robust.Client.Player; -using Robust.Shared.Physics.Components; - -namespace Content.Client._Scp.Scp939; - -public sealed partial class Scp939HudSystem : EquipmentHudSystem -{ - [Dependency] private readonly IPlayerManager _playerManager = default!; - [Dependency] private readonly SpriteSystem _sprite = default!; - - internal readonly List<(Entity Ent, float BaseAlpha)> CachedBaseAlphas = new(64); - - private Scp939SetAlphaOverlay _setAlphaOverlay = default!; - private Scp939ResetAlphaOverlay _resetAlphaOverlay = default!; - - // TODO: Выделить значения плохого зрения в отдельный компонент, не связанный с 939 - private Scp939Component? _scp939Component; - - private EntityQuery _eyeQuery; - private EntityQuery _scp939ProtectionQuery; - private EntityQuery _movementSpeedQuery; - private EntityQuery _physicsQuery; - - private bool _overlaysPresented; - private float _lastUpdateTime; - - private const float UpdateInterval = 0.05f; - - public override void Initialize() - { - base.Initialize(); - - InitializeVisibility(); - InitializeOverlay(); - - SubscribeLocalEvent(OnGetStatusIcons, after: [typeof(SSDIndicatorSystem)] ); - SubscribeLocalEvent(OnExamine); - - _eyeQuery = GetEntityQuery(); - _scp939ProtectionQuery = GetEntityQuery(); - _movementSpeedQuery = GetEntityQuery(); - _physicsQuery = GetEntityQuery(); - - _setAlphaOverlay = new(); - _resetAlphaOverlay = new(); - - UpdatesAfter.Add(typeof(StealthSystem)); - } - - public override void Shutdown() - { - RestoreCachedBaseAlphas(); - RemoveOverlays(); - - _setAlphaOverlay.Dispose(); - _resetAlphaOverlay.Dispose(); - - base.Shutdown(); - } - - private void OnExamine(Entity ent, ref ExamineAttemptEvent args) - { - if (!IsActive) - return; - - var visibility = GetVisibility(ent); - - if (visibility < 0.2f) - args.Cancel(); - } - - private void OnGetStatusIcons(Entity ent, ref GetStatusIconsEvent args) - { - if (!IsActive) - return; - - var visibility = GetVisibility(ent); - - if (visibility <= 0.5f) - args.StatusIcons.Clear(); - } - - protected override void UpdateInternal(RefreshEquipmentHudEvent args) - { - base.UpdateInternal(args); - - _scp939Component = args.Components.Count > 0 ? args.Components[0] : null; - AddOverlays(); - } - - protected override void DeactivateInternal() - { - base.DeactivateInternal(); - - _scp939Component = null; - _lastUpdateTime = 0f; - - RestoreCachedBaseAlphas(); - RemoveOverlays(); - } - - internal bool CanDraw(in OverlayDrawArgs args) - { - if (!IsActive) - return false; - - if (_playerManager.LocalEntity is not { } player) - return false; - - if (!_eyeQuery.TryComp(player, out var eye)) - return false; - - return args.Viewport.Eye == eye.Eye; - } - - internal void RestoreCachedBaseAlphas() - { - foreach (var (ent, baseAlpha) in CachedBaseAlphas) - { - if (!EntityManager.EntityExists(ent)) - continue; - - _sprite.SetColor(ent.AsNullable(), ent.Comp.Color.WithAlpha(baseAlpha)); - } - - CachedBaseAlphas.Clear(); - } - - internal static float GetVisibility(Entity ent) - { - var acc = ent.Comp.VisibilityAcc; - - if (acc > ent.Comp.HideTime) - return 0; - - return Math.Clamp(1f - (acc / ent.Comp.HideTime), 0f, 1f); - } -} diff --git a/Content.Server/_Scp/Other/ScpOnSoundVisibility/ScpOnSoundVisibilitySystem.cs b/Content.Server/_Scp/Other/ScpOnSoundVisibility/ScpOnSoundVisibilitySystem.cs new file mode 100644 index 0000000000..eaca32dbd5 --- /dev/null +++ b/Content.Server/_Scp/Other/ScpOnSoundVisibility/ScpOnSoundVisibilitySystem.cs @@ -0,0 +1,197 @@ +using Content.Server.Popups; +using Content.Shared._Scp.Other.ScpOnSoundVisibility; +using Content.Shared.Chat; +using Content.Shared.Flash; +using Content.Shared.Item; +using Content.Shared.Mobs.Components; +using Content.Shared.Popups; +using Content.Shared.Weapons.Ranged.Systems; +using Robust.Shared.Map; +using Robust.Shared.Timing; + +namespace Content.Server._Scp.Other.ScpOnSoundVisibility; + +public sealed partial class ScpOnSoundVisibilitySystem : EntitySystem +{ + [Dependency] private readonly PopupSystem _popup = default!; + [Dependency] private readonly EntityLookupSystem _entityLookup = default!; + [Dependency] private readonly IGameTiming _timing = default!; + + private static readonly TimeSpan VisibilityRefreshInterval = TimeSpan.FromSeconds(0.2f); + + private TimeSpan _nextVisibilityRefresh = TimeSpan.Zero; + private readonly HashSet _visibilityActiveTargets = []; + private readonly HashSet> _visibilityCandidates = []; + private readonly List _visibilityRemovalQueue = []; + + private EntityQuery _activeQuery; + + public override void Initialize() + { + base.Initialize(); + + SubscribeLocalEvent(OnMobStartup); + + SubscribeLocalEvent(OnTargetSpoke); + SubscribeLocalEvent(OnTargetEmote); + SubscribeLocalEvent(OnShot); + + SubscribeLocalEvent(OnFlash); + + _activeQuery = GetEntityQuery(); + } + + private void OnFlash(Entity ent, ref AfterFlashedEvent args) + { + ent.Comp.PoorEyesight = true; + ent.Comp.PoorEyesightTimeStart = _timing.CurTime; + + if (ent.Comp.OnFlashMessage != null) + { + var message = Loc.GetString(ent.Comp.OnFlashMessage, ("time", ent.Comp.PoorEyesightTime)); + _popup.PopupEntity(message, ent, ent, PopupType.MediumCaution); + } + + Dirty(ent); + } + + private void OnTargetEmote(Entity ent, ref EmoteEvent args) + { + MobDidSomething(ent); + } + + private void OnShot(Entity ent, ref GunShotEvent args) + { + if (!_activeQuery.TryComp(args.User, out var visibilityComponent)) + return; + + MobDidSomething((args.User, visibilityComponent)); + } + + private void OnMobStartup(Entity ent, ref ComponentStartup args) + { + if (HasComp(ent)) + return; + + EnsureComp(ent); + } + + private void OnTargetSpoke(Entity ent, ref EntitySpokeEvent args) + { + MobDidSomething(ent); + //TryRememberPhrase(ent, args.Message); + } + + private void MobDidSomething(Entity ent) + { + ent.Comp.VisibilityResetCounter++; + DirtyField(ent, ent.Comp, nameof(ActiveScpOnSoundVisibilityComponent.VisibilityResetCounter)); + } + + public override void Update(float frameTime) + { + base.Update(frameTime); + + UpdateVisibilityTargets(); + + var querySimple = EntityQueryEnumerator(); + while (querySimple.MoveNext(out var uid, out var viewerComp)) + { + if (!viewerComp.PoorEyesight) + continue; + + if (viewerComp.PoorEyesightTimeStart == null) + continue; + + var timeDifference = _timing.CurTime - viewerComp.PoorEyesightTimeStart.Value; + + if (timeDifference > TimeSpan.FromSeconds(viewerComp.PoorEyesightTime)) + { + viewerComp.PoorEyesight = false; + viewerComp.PoorEyesightTimeStart = null; + + Dirty(uid, viewerComp); + } + } + } + + private void UpdateVisibilityTargets() + { + if (_timing.CurTime < _nextVisibilityRefresh) + return; + + _nextVisibilityRefresh = _timing.CurTime + VisibilityRefreshInterval; + _visibilityActiveTargets.Clear(); + + var scpQuery = EntityQueryEnumerator(); + while (scpQuery.MoveNext(out var uid, out var viewer, out var xform)) + { + if (xform.MapID == MapId.Nullspace) + continue; + + _visibilityCandidates.Clear(); + _entityLookup.GetEntitiesInRange(xform.Coordinates, + viewer.VisibilityActivationRange, + _visibilityCandidates, + LookupFlags.Dynamic | LookupFlags.Approximate); + + foreach (var target in _visibilityCandidates) + { + if (target.Owner == uid) + continue; + + _visibilityActiveTargets.Add(target); + EnsureActiveVisibility(target); + } + } + + _visibilityRemovalQueue.Clear(); + + var activeQuery = EntityQueryEnumerator(); + while (activeQuery.MoveNext(out var uid, out _)) + { + if (_visibilityActiveTargets.Contains(uid)) + continue; + + _visibilityRemovalQueue.Add(uid); + } + + foreach (var uid in _visibilityRemovalQueue) + { + RemComp(uid); + } + + _visibilityCandidates.Clear(); + _visibilityRemovalQueue.Clear(); + } + + private void EnsureActiveVisibility(Entity ent) + { + if (!_activeQuery.TryComp(ent, out var active)) + { + active = AddComp(ent); + active.HideTime = ent.Comp.HideTime; + active.MinValue = ent.Comp.MinValue; + active.MaxValue = ent.Comp.MaxValue; + return; + } + + if (!MathHelper.CloseTo(active.HideTime, ent.Comp.HideTime)) + { + active.HideTime = ent.Comp.HideTime; + DirtyField(ent, active, nameof(ActiveScpOnSoundVisibilityComponent.HideTime)); + } + + if (active.MinValue != ent.Comp.MinValue) + { + active.MinValue = ent.Comp.MinValue; + DirtyField(ent, active, nameof(ActiveScpOnSoundVisibilityComponent.MinValue)); + } + + if (active.MaxValue != ent.Comp.MaxValue) + { + active.MaxValue = ent.Comp.MaxValue; + DirtyField(ent, active, nameof(ActiveScpOnSoundVisibilityComponent.MaxValue)); + } + } +} diff --git a/Content.Server/_Scp/Scp939/Scp939System.Actions.cs b/Content.Server/_Scp/Scp939/Scp939System.Actions.cs index fd97893262..49743413a7 100644 --- a/Content.Server/_Scp/Scp939/Scp939System.Actions.cs +++ b/Content.Server/_Scp/Scp939/Scp939System.Actions.cs @@ -91,7 +91,7 @@ private void OnMimic(Entity ent, ref Scp939MimicActionEvent arg /// /// Запоминание последних сказанных возле 939 слов /// - private void TryRememberPhrase(Entity ent, string message) + /*private void TryRememberPhrase(Entity ent, string message) { using var scp939Set = HashSetPoolEntity.Rent(); _entityLookup.GetEntitiesInRange(Transform(ent).Coordinates, 16f, scp939Set.Value, LookupFlags.Dynamic | LookupFlags.Approximate); @@ -117,5 +117,5 @@ private void TryRememberPhrase(Entity ent, stri var username = Identity.Name(ent, EntityManager); scp.Comp.RememberedMessages.TryAdd(message, new(username, voicePrototype)); } - } + }*/ } diff --git a/Content.Server/_Scp/Scp939/Scp939System.Visibility.cs b/Content.Server/_Scp/Scp939/Scp939System.Visibility.cs deleted file mode 100644 index e8817310db..0000000000 --- a/Content.Server/_Scp/Scp939/Scp939System.Visibility.cs +++ /dev/null @@ -1,168 +0,0 @@ -using Content.Server.Popups; -using Content.Shared._Scp.Scp939; -using Content.Shared.Chat; -using Content.Shared.Flash; -using Content.Shared.Item; -using Content.Shared.Mobs.Components; -using Content.Shared.Popups; -using Content.Shared.Weapons.Ranged.Systems; -using Robust.Shared.Map; -using Robust.Shared.Timing; - -namespace Content.Server._Scp.Scp939; - -public sealed partial class Scp939System -{ - [Dependency] private readonly PopupSystem _popup = default!; - [Dependency] private readonly IGameTiming _timing = default!; - - private static readonly TimeSpan VisibilityRefreshInterval = TimeSpan.FromSeconds(0.2f); - - private TimeSpan _nextVisibilityRefresh = TimeSpan.Zero; - private readonly HashSet _visibilityActiveTargets = []; - private readonly HashSet> _visibilityCandidates = []; - private readonly List _visibilityRemovalQueue = []; - - private EntityQuery _activeQuery; - - private void InitializeVisibility() - { - SubscribeLocalEvent(OnMobStartup); - - SubscribeLocalEvent(OnTargetSpoke); - SubscribeLocalEvent(OnTargetEmote); - SubscribeLocalEvent(OnShot); - - SubscribeLocalEvent(OnFlash); - - _activeQuery = GetEntityQuery(); - } - - private void OnFlash(Entity ent, ref AfterFlashedEvent args) - { - ent.Comp.PoorEyesight = true; - ent.Comp.PoorEyesightTimeStart = _timing.CurTime; - - var message = Loc.GetString("scp939-flashed", ("time", ent.Comp.PoorEyesightTime)); - _popup.PopupEntity(message, ent, ent, PopupType.MediumCaution); - - DirtyFields(ent, - ent.Comp, - null, - nameof(Scp939Component.PoorEyesight), - nameof(Scp939Component.PoorEyesightTimeStart)); - } - - private void OnTargetEmote(Entity ent, ref EmoteEvent args) - { - MobDidSomething(ent); - } - - private void OnShot(Entity ent, ref GunShotEvent args) - { - if (!_activeQuery.TryComp(args.User, out var visibilityComponent)) - return; - - MobDidSomething((args.User, visibilityComponent)); - } - - private void OnMobStartup(Entity ent, ref ComponentStartup args) - { - if (HasComp(ent)) - return; - - EnsureComp(ent); - } - - private void OnTargetSpoke(Entity ent, ref EntitySpokeEvent args) - { - MobDidSomething(ent); - TryRememberPhrase(ent, args.Message); - } - - private void MobDidSomething(Entity ent) - { - ent.Comp.VisibilityResetCounter++; - DirtyField(ent, ent.Comp, nameof(ActiveScp939VisibilityComponent.VisibilityResetCounter)); - } - - private void UpdateVisibilityTargets() - { - if (_timing.CurTime < _nextVisibilityRefresh) - return; - - _nextVisibilityRefresh = _timing.CurTime + VisibilityRefreshInterval; - _visibilityActiveTargets.Clear(); - - var scpQuery = EntityQueryEnumerator(); - while (scpQuery.MoveNext(out var uid, out var scp939, out var xform)) - { - if (xform.MapID == MapId.Nullspace) - continue; - - _visibilityCandidates.Clear(); - _entityLookup.GetEntitiesInRange(xform.Coordinates, - scp939.VisibilityActivationRange, - _visibilityCandidates, - LookupFlags.Dynamic | LookupFlags.Approximate); - - foreach (var target in _visibilityCandidates) - { - if (target.Owner == uid) - continue; - - _visibilityActiveTargets.Add(target); - EnsureActiveVisibility(target); - } - } - - _visibilityRemovalQueue.Clear(); - - var activeQuery = EntityQueryEnumerator(); - while (activeQuery.MoveNext(out var uid, out _)) - { - if (_visibilityActiveTargets.Contains(uid)) - continue; - - _visibilityRemovalQueue.Add(uid); - } - - foreach (var uid in _visibilityRemovalQueue) - { - RemComp(uid); - } - - _visibilityCandidates.Clear(); - _visibilityRemovalQueue.Clear(); - } - - private void EnsureActiveVisibility(Entity ent) - { - if (!_activeQuery.TryComp(ent, out var active)) - { - active = AddComp(ent); - active.HideTime = ent.Comp.HideTime; - active.MinValue = ent.Comp.MinValue; - active.MaxValue = ent.Comp.MaxValue; - return; - } - - if (!MathHelper.CloseTo(active.HideTime, ent.Comp.HideTime)) - { - active.HideTime = ent.Comp.HideTime; - DirtyField(ent, active, nameof(ActiveScp939VisibilityComponent.HideTime)); - } - - if (active.MinValue != ent.Comp.MinValue) - { - active.MinValue = ent.Comp.MinValue; - DirtyField(ent, active, nameof(ActiveScp939VisibilityComponent.MinValue)); - } - - if (active.MaxValue != ent.Comp.MaxValue) - { - active.MaxValue = ent.Comp.MaxValue; - DirtyField(ent, active, nameof(ActiveScp939VisibilityComponent.MaxValue)); - } - } -} diff --git a/Content.Server/_Scp/Scp939/Scp939System.cs b/Content.Server/_Scp/Scp939/Scp939System.cs index f671db2575..116d3fa0d0 100644 --- a/Content.Server/_Scp/Scp939/Scp939System.cs +++ b/Content.Server/_Scp/Scp939/Scp939System.cs @@ -33,9 +33,6 @@ public override void Initialize() SubscribeLocalEvent(OnSleepChanged); SubscribeLocalEvent(OnMobStateChanged); - - - InitializeVisibility(); } private void OnMobStateChanged(Entity ent, ref MobStateChangedEvent args) @@ -66,37 +63,11 @@ public override void Update(float frameTime) { base.Update(frameTime); - UpdateVisibilityTargets(); - var querySleeping = EntityQueryEnumerator(); while (querySleeping.MoveNext(out var uid, out var scp939Component, out _)) { _damageableSystem.TryChangeDamage(uid, scp939Component.HibernationHealingRate * frameTime); } - - var querySimple = EntityQueryEnumerator(); - while (querySimple.MoveNext(out var uid, out var scp939Component)) - { - if (!scp939Component.PoorEyesight) - continue; - - if (scp939Component.PoorEyesightTimeStart == null) - continue; - - var timeDifference = _timing.CurTime - scp939Component.PoorEyesightTimeStart.Value; - - if (timeDifference > TimeSpan.FromSeconds(scp939Component.PoorEyesightTime)) - { - scp939Component.PoorEyesight = false; - scp939Component.PoorEyesightTimeStart = null; - - DirtyFields(uid, - scp939Component, - null, - nameof(Scp939Component.PoorEyesight), - nameof(Scp939Component.PoorEyesightTimeStart)); - } - } } private void OnInit(Entity ent, ref ComponentInit args) diff --git a/Content.Shared/_Scp/Other/ScpOnSoundVisibility/ActiveScpOnSoundVisibilityComponent.cs b/Content.Shared/_Scp/Other/ScpOnSoundVisibility/ActiveScpOnSoundVisibilityComponent.cs new file mode 100644 index 0000000000..a92db1f245 --- /dev/null +++ b/Content.Shared/_Scp/Other/ScpOnSoundVisibility/ActiveScpOnSoundVisibilityComponent.cs @@ -0,0 +1,25 @@ +using Robust.Shared.GameStates; + +namespace Content.Shared._Scp.Other.ScpOnSoundVisibility; + +[RegisterComponent, NetworkedComponent, AutoGenerateComponentState(raiseAfterAutoHandleState: true, fieldDeltas: true)] +public sealed partial class ActiveScpOnSoundVisibilityComponent : Component +{ + [ViewVariables] + public float VisibilityAcc = ScpOnSoundVisibilityComponent.InitialVisibilityAcc; + + [AutoNetworkedField] + public uint VisibilityResetCounter; + + [DataField, AutoNetworkedField] + public float HideTime = ScpOnSoundVisibilityComponent.DefaultHideTime; + + [DataField, AutoNetworkedField] + public int MinValue = ScpOnSoundVisibilityComponent.DefaultMinValue; + + [DataField, AutoNetworkedField] + public int MaxValue = ScpOnSoundVisibilityComponent.DefaultMaxValue; + + [NonSerialized] + public uint LastHandledVisibilityResetCounter; +} diff --git a/Content.Shared/_Scp/Scp939/Scp939VisabilityComponent.cs b/Content.Shared/_Scp/Other/ScpOnSoundVisibility/ScpOnSoundVisibilityComponent.cs similarity index 75% rename from Content.Shared/_Scp/Scp939/Scp939VisabilityComponent.cs rename to Content.Shared/_Scp/Other/ScpOnSoundVisibility/ScpOnSoundVisibilityComponent.cs index 94a85140e3..cc605b5008 100644 --- a/Content.Shared/_Scp/Scp939/Scp939VisabilityComponent.cs +++ b/Content.Shared/_Scp/Other/ScpOnSoundVisibility/ScpOnSoundVisibilityComponent.cs @@ -1,7 +1,7 @@ -namespace Content.Shared._Scp.Scp939; +namespace Content.Shared._Scp.Other.ScpOnSoundVisibility; [RegisterComponent] -public sealed partial class Scp939VisibilityComponent : Component +public sealed partial class ScpOnSoundVisibilityComponent : Component { public const float InitialVisibilityAcc = 0.001f; public const float DefaultHideTime = 2.5f; diff --git a/Content.Shared/_Scp/Other/ScpOnSoundVisibility/ScpOnSoundVisibilityProtectionComponent.cs b/Content.Shared/_Scp/Other/ScpOnSoundVisibility/ScpOnSoundVisibilityProtectionComponent.cs new file mode 100644 index 0000000000..4465a75e8f --- /dev/null +++ b/Content.Shared/_Scp/Other/ScpOnSoundVisibility/ScpOnSoundVisibilityProtectionComponent.cs @@ -0,0 +1,10 @@ + +using Robust.Shared.GameStates; + +namespace Content.Shared._Scp.Other.ScpOnSoundVisibility; + +[RegisterComponent, NetworkedComponent] +public sealed partial class ScpOnSoundVisibilityProtectionComponent : Component +{ + +} diff --git a/Content.Shared/_Scp/Other/ScpOnSoundVisibility/ScpOnSoundVisibilityViewerComponent.cs b/Content.Shared/_Scp/Other/ScpOnSoundVisibility/ScpOnSoundVisibilityViewerComponent.cs new file mode 100644 index 0000000000..88a6cae6c4 --- /dev/null +++ b/Content.Shared/_Scp/Other/ScpOnSoundVisibility/ScpOnSoundVisibilityViewerComponent.cs @@ -0,0 +1,36 @@ + +using Content.Shared.Whitelist; +using Robust.Shared.GameStates; + +namespace Content.Shared._Scp.Other.ScpOnSoundVisibility; + +[RegisterComponent, NetworkedComponent, AutoGenerateComponentState] +public sealed partial class ScpOnSoundVisibilityViewerComponent : Component +{ + [DataField, AutoNetworkedField] + public bool PoorEyesOnFlash = false; + + [DataField, AutoNetworkedField] + public bool PoorEyesight; + + [DataField, AutoNetworkedField] + public float PoorEyesightTime = 10f; // Секунды + + [AutoNetworkedField] + public TimeSpan? PoorEyesightTimeStart; // Когда начали плохо видеть + + [DataField, ViewVariables(VVAccess.ReadWrite)] + public float VisibilityActivationRange = 20f; + + [DataField, AutoNetworkedField] + public LocId? OnFlashMessage; + + [DataField, AutoNetworkedField] + public EntityWhitelist Protections = new() + { + Components = new[] + { + "ScpOnSoundVisibilityProtection" + } + }; +} diff --git a/Content.Shared/_Scp/Scp939/ActiveScp939VisibilityComponent.cs b/Content.Shared/_Scp/Scp939/ActiveScp939VisibilityComponent.cs deleted file mode 100644 index d28eeba9bf..0000000000 --- a/Content.Shared/_Scp/Scp939/ActiveScp939VisibilityComponent.cs +++ /dev/null @@ -1,25 +0,0 @@ -using Robust.Shared.GameStates; - -namespace Content.Shared._Scp.Scp939; - -[RegisterComponent, NetworkedComponent, AutoGenerateComponentState(raiseAfterAutoHandleState: true, fieldDeltas: true)] -public sealed partial class ActiveScp939VisibilityComponent : Component -{ - [ViewVariables] - public float VisibilityAcc = Scp939VisibilityComponent.InitialVisibilityAcc; - - [AutoNetworkedField] - public uint VisibilityResetCounter; - - [DataField, AutoNetworkedField] - public float HideTime = Scp939VisibilityComponent.DefaultHideTime; - - [DataField, AutoNetworkedField] - public int MinValue = Scp939VisibilityComponent.DefaultMinValue; - - [DataField, AutoNetworkedField] - public int MaxValue = Scp939VisibilityComponent.DefaultMaxValue; - - [NonSerialized] - public uint LastHandledVisibilityResetCounter; -} diff --git a/Content.Shared/_Scp/Scp939/Protection/Scp939Protection.cs b/Content.Shared/_Scp/Scp939/Protection/Scp939ProtectionComponent.cs similarity index 100% rename from Content.Shared/_Scp/Scp939/Protection/Scp939Protection.cs rename to Content.Shared/_Scp/Scp939/Protection/Scp939ProtectionComponent.cs diff --git a/Content.Shared/_Scp/Scp939/Scp939Component.cs b/Content.Shared/_Scp/Scp939/Scp939Component.cs index 3b6052b1f0..ff9778f8e3 100644 --- a/Content.Shared/_Scp/Scp939/Scp939Component.cs +++ b/Content.Shared/_Scp/Scp939/Scp939Component.cs @@ -5,7 +5,7 @@ namespace Content.Shared._Scp.Scp939; -[RegisterComponent, NetworkedComponent, AutoGenerateComponentState(fieldDeltas: true)] +[RegisterComponent, NetworkedComponent] public sealed partial class Scp939Component : Component { [DataField] @@ -52,22 +52,6 @@ public sealed partial class Scp939Component : Component } }; - #region Vision - - [DataField, AutoNetworkedField] - public bool PoorEyesight; - - [DataField, AutoNetworkedField] - public float PoorEyesightTime = 10f; // Секунды - - [AutoNetworkedField] - public TimeSpan? PoorEyesightTimeStart; // Когда начали плохо видеть - - [DataField, ViewVariables(VVAccess.ReadWrite)] - public float VisibilityActivationRange = 20f; - - #endregion - [DataField] public int MaxRememberedMessages = 20; diff --git a/Resources/Prototypes/_Scp/Entities/Mobs/Player/Scp/Main/scp939.yml b/Resources/Prototypes/_Scp/Entities/Mobs/Player/Scp/Main/scp939.yml index f0c45c1cdf..a023a05672 100644 --- a/Resources/Prototypes/_Scp/Entities/Mobs/Player/Scp/Main/scp939.yml +++ b/Resources/Prototypes/_Scp/Entities/Mobs/Player/Scp/Main/scp939.yml @@ -21,6 +21,7 @@ state: alive - type: Scp class: Keter + - type: ScpOnSoundVisibilityViewer - type: Scp939 - type: MeleeWeapon altDisarm: false From 263c9e42283cc71842924c3955af3c5f3b753509 Mon Sep 17 00:00:00 2001 From: Wardex Date: Sun, 26 Apr 2026 21:21:23 +0300 Subject: [PATCH 02/19] =?UTF-8?q?=D0=BF=D1=85=D0=B5=D1=85?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../ScpOnSoundVisibility/ScpOnSoundVisibilityHudSystem.cs | 2 +- Content.Server/_Scp/Scp939/Scp939System.Actions.cs | 1 + .../Prototypes/_Scp/Entities/Mobs/Player/Scp/Main/scp939.yml | 4 ++++ 3 files changed, 6 insertions(+), 1 deletion(-) diff --git a/Content.Client/_Scp/Other/ScpOnSoundVisibility/ScpOnSoundVisibilityHudSystem.cs b/Content.Client/_Scp/Other/ScpOnSoundVisibility/ScpOnSoundVisibilityHudSystem.cs index 449aa419bb..4eedc930da 100644 --- a/Content.Client/_Scp/Other/ScpOnSoundVisibility/ScpOnSoundVisibilityHudSystem.cs +++ b/Content.Client/_Scp/Other/ScpOnSoundVisibility/ScpOnSoundVisibilityHudSystem.cs @@ -198,7 +198,7 @@ private void OnMove(Entity ent, ref MoveEve if (!IsActive) return; - // В зависимости от наличие защит или проблем со зрением у 939 изменяется то, насколько хорошо мы видим жертву + // В зависимости от наличие защит или проблем со зрением изменяется то, насколько хорошо мы видим жертву if (ModifyAcc(ent.Comp, out var modifier)) // Если зрение затруднено { ent.Comp.VisibilityAcc *= modifier; diff --git a/Content.Server/_Scp/Scp939/Scp939System.Actions.cs b/Content.Server/_Scp/Scp939/Scp939System.Actions.cs index 49743413a7..22e06540e0 100644 --- a/Content.Server/_Scp/Scp939/Scp939System.Actions.cs +++ b/Content.Server/_Scp/Scp939/Scp939System.Actions.cs @@ -88,6 +88,7 @@ private void OnMimic(Entity ent, ref Scp939MimicActionEvent arg args.Handled = true; } + // TODO: Перекочевать в отдельную систему /// /// Запоминание последних сказанных возле 939 слов /// diff --git a/Resources/Prototypes/_Scp/Entities/Mobs/Player/Scp/Main/scp939.yml b/Resources/Prototypes/_Scp/Entities/Mobs/Player/Scp/Main/scp939.yml index a023a05672..bf712ea69e 100644 --- a/Resources/Prototypes/_Scp/Entities/Mobs/Player/Scp/Main/scp939.yml +++ b/Resources/Prototypes/_Scp/Entities/Mobs/Player/Scp/Main/scp939.yml @@ -22,6 +22,10 @@ - type: Scp class: Keter - type: ScpOnSoundVisibilityViewer + poorEyesOnFlash: true + protections: + components: + - Scp939Protection - type: Scp939 - type: MeleeWeapon altDisarm: false From 5f2985e6baeb98b16c0e2f32fd4330e71e87c5d3 Mon Sep 17 00:00:00 2001 From: Wardex Date: Sun, 26 Apr 2026 21:31:07 +0300 Subject: [PATCH 03/19] =?UTF-8?q?=D0=BF=D1=83=D0=BF=D1=83=D0=BF=D1=83?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../Other/ScpOnSoundVisibility/ScpOnSoundVisibilitySystem.cs | 3 +++ .../Prototypes/_Scp/Entities/Mobs/Player/Scp/Main/scp939.yml | 1 + 2 files changed, 4 insertions(+) diff --git a/Content.Server/_Scp/Other/ScpOnSoundVisibility/ScpOnSoundVisibilitySystem.cs b/Content.Server/_Scp/Other/ScpOnSoundVisibility/ScpOnSoundVisibilitySystem.cs index eaca32dbd5..36846c7a19 100644 --- a/Content.Server/_Scp/Other/ScpOnSoundVisibility/ScpOnSoundVisibilitySystem.cs +++ b/Content.Server/_Scp/Other/ScpOnSoundVisibility/ScpOnSoundVisibilitySystem.cs @@ -43,6 +43,9 @@ public override void Initialize() private void OnFlash(Entity ent, ref AfterFlashedEvent args) { + if (!ent.Comp.PoorEyesOnFlash) + return; + ent.Comp.PoorEyesight = true; ent.Comp.PoorEyesightTimeStart = _timing.CurTime; diff --git a/Resources/Prototypes/_Scp/Entities/Mobs/Player/Scp/Main/scp939.yml b/Resources/Prototypes/_Scp/Entities/Mobs/Player/Scp/Main/scp939.yml index bf712ea69e..565b5ac2a9 100644 --- a/Resources/Prototypes/_Scp/Entities/Mobs/Player/Scp/Main/scp939.yml +++ b/Resources/Prototypes/_Scp/Entities/Mobs/Player/Scp/Main/scp939.yml @@ -23,6 +23,7 @@ class: Keter - type: ScpOnSoundVisibilityViewer poorEyesOnFlash: true + onFlashMessage: scp939-flashed protections: components: - Scp939Protection From 902f1e56b1fb7a9ef3244112d5bd360d82bb04fd Mon Sep 17 00:00:00 2001 From: Wardex Date: Tue, 28 Apr 2026 09:53:28 +0300 Subject: [PATCH 04/19] =?UTF-8?q?=D0=B3=D1=80=D0=B5=D0=B1=D0=B0=D0=BD?= =?UTF-8?q?=D1=8B=D0=B5=20=D0=B0=D0=BA=D1=88=D0=B8=D0=BE=D0=BD=D1=8B=20939?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../_Scp/Other/ScpSleep/ScpSleepVisualizer.cs | 40 ++++++ .../_Scp/Scp939/Scp939Visualizer.cs | 41 ------ .../ScpOnSoundVisibilitySystem.cs | 1 - .../ScpReleaseGas/ScpReleaseGasSystem.cs | 43 ++++++ .../ScpRememberPhraseSystem.cs | 94 ++++++++++++++ .../_Scp/Other/ScpSleep/ScpSleepSystem.cs | 98 ++++++++++++++ .../Scp939/Sleep/ArtifactScp939SleepSystem.cs | 10 +- .../_Scp/Scp939/Scp939System.Actions.cs | 122 ------------------ Content.Server/_Scp/Scp939/Scp939System.cs | 80 ------------ .../ScpReleaseGas/ScpReleaseGasActionEvent.cs | 5 + .../ScpReleaseGas/ScpReleaseGasComponent.cs | 26 ++++ .../ScpRememberPhraseActionEvent.cs | 5 + .../ScpRememberPhraseComponent.cs | 22 ++++ .../Other/ScpSleep/ScpSleepActionEvent.cs | 5 + .../_Scp/Other/ScpSleep/ScpSleepComponent.cs | 55 ++++++++ .../_Scp/Other/ScpSleep/SharedScpSleepData.cs | 15 +++ Content.Shared/_Scp/Scp939/Scp939Component.cs | 20 ++- .../_Scp/Scp939/SharedScp939Data.cs | 7 - Resources/Prototypes/_Scp/Actions/scp939.yml | 26 +--- .../Prototypes/_Scp/Actions/scp_actions.yml | 37 ++++++ .../Entities/Mobs/Player/Scp/Main/scp939.yml | 10 +- .../meta.json | 0 .../mimic.png | Bin .../sleep.png | Bin .../smaug.png | Bin 25 files changed, 480 insertions(+), 282 deletions(-) create mode 100644 Content.Client/_Scp/Other/ScpSleep/ScpSleepVisualizer.cs delete mode 100644 Content.Client/_Scp/Scp939/Scp939Visualizer.cs create mode 100644 Content.Server/_Scp/Other/ScpReleaseGas/ScpReleaseGasSystem.cs create mode 100644 Content.Server/_Scp/Other/ScpRememberPhrase/ScpRememberPhraseSystem.cs create mode 100644 Content.Server/_Scp/Other/ScpSleep/ScpSleepSystem.cs delete mode 100644 Content.Server/_Scp/Scp939/Scp939System.Actions.cs delete mode 100644 Content.Server/_Scp/Scp939/Scp939System.cs create mode 100644 Content.Shared/_Scp/Other/ScpReleaseGas/ScpReleaseGasActionEvent.cs create mode 100644 Content.Shared/_Scp/Other/ScpReleaseGas/ScpReleaseGasComponent.cs create mode 100644 Content.Shared/_Scp/Other/ScpRememberPhrase/ScpRememberPhraseActionEvent.cs create mode 100644 Content.Shared/_Scp/Other/ScpRememberPhrase/ScpRememberPhraseComponent.cs create mode 100644 Content.Shared/_Scp/Other/ScpSleep/ScpSleepActionEvent.cs create mode 100644 Content.Shared/_Scp/Other/ScpSleep/ScpSleepComponent.cs create mode 100644 Content.Shared/_Scp/Other/ScpSleep/SharedScpSleepData.cs create mode 100644 Resources/Prototypes/_Scp/Actions/scp_actions.yml rename Resources/Textures/_Scp/Actions/{scp-939.rsi => scp-actions.rsi}/meta.json (100%) rename Resources/Textures/_Scp/Actions/{scp-939.rsi => scp-actions.rsi}/mimic.png (100%) rename Resources/Textures/_Scp/Actions/{scp-939.rsi => scp-actions.rsi}/sleep.png (100%) rename Resources/Textures/_Scp/Actions/{scp-939.rsi => scp-actions.rsi}/smaug.png (100%) diff --git a/Content.Client/_Scp/Other/ScpSleep/ScpSleepVisualizer.cs b/Content.Client/_Scp/Other/ScpSleep/ScpSleepVisualizer.cs new file mode 100644 index 0000000000..e80ac25882 --- /dev/null +++ b/Content.Client/_Scp/Other/ScpSleep/ScpSleepVisualizer.cs @@ -0,0 +1,40 @@ +using Content.Shared._Scp.Other.ScpSleep; +using Content.Shared.Mobs; +using Content.Shared.Mobs.Components; +using Robust.Client.GameObjects; + +namespace Content.Client._Scp.Other.ScpSleep; + +public sealed class Scp939Visualizer : VisualizerSystem +{ + [Dependency] private readonly SpriteSystem _spriteSystem = default!; + [Dependency] private readonly SharedAppearanceSystem _appearanceSystem = default!; + + protected override void OnAppearanceChange(EntityUid uid, ScpSleepComponent component, ref AppearanceChangeEvent args) + { + base.OnAppearanceChange(uid, component, ref args); + + UpdateSprite(uid, args.Component, args.Sprite); + } + + private void UpdateSprite(EntityUid uid, AppearanceComponent appearanceComponent, SpriteComponent? spriteComponent = null, MobStateComponent? mobStateComponent = null) + { + if (!TryComp(uid, out spriteComponent) || + !TryComp(uid, out mobStateComponent) || + !_spriteSystem.LayerMapTryGet((uid, spriteComponent), ScpSleepLayers.Base, out var layerId, false)) + return; + + if (mobStateComponent.CurrentState is MobState.Dead or MobState.Critical) + { + _spriteSystem.LayerSetRsiState(uid, layerId, "dead"); + return; + } + + _spriteSystem.LayerSetRsiState(uid, layerId, "alive"); + + if (_appearanceSystem.TryGetData(uid, ScpSleepVisuals.Sleeping, out var sleeping) && sleeping) + { + _spriteSystem.LayerSetRsiState(uid, layerId, "asleep"); + } + } +} diff --git a/Content.Client/_Scp/Scp939/Scp939Visualizer.cs b/Content.Client/_Scp/Scp939/Scp939Visualizer.cs deleted file mode 100644 index 06753acfc6..0000000000 --- a/Content.Client/_Scp/Scp939/Scp939Visualizer.cs +++ /dev/null @@ -1,41 +0,0 @@ -using Content.Shared._Scp.Scp939; -using Content.Shared.Mobs; -using Content.Shared.Mobs.Components; -using Robust.Client.GameObjects; - -namespace Content.Client._Scp.Scp939; - -public sealed class Scp939Visualizer : VisualizerSystem -{ - [Dependency] private readonly SharedAppearanceSystem _appearanceSystem = default!; - - protected override void OnAppearanceChange(EntityUid uid, Scp939Component component, ref AppearanceChangeEvent args) - { - base.OnAppearanceChange(uid, component, ref args); - - UpdateSprite(uid, args.Component, args.Sprite); - } - - private void UpdateSprite(EntityUid uid, AppearanceComponent appearanceComponent, SpriteComponent? spriteComponent = null, MobStateComponent? mobStateComponent = null) - { - if (!Resolve(uid, ref spriteComponent) || - !Resolve(uid, ref mobStateComponent) || - !spriteComponent.LayerMapTryGet(Scp939Layers.Base, out var layerId)) - { - return; - } - - if (mobStateComponent.CurrentState is MobState.Dead or MobState.Critical) - { - spriteComponent.LayerSetState(layerId, "dead"); - return; - } - - spriteComponent.LayerSetState(layerId, "alive"); - - if (_appearanceSystem.TryGetData(uid, Scp939Visuals.Sleeping, out var sleeping) && sleeping) - { - spriteComponent.LayerSetState(layerId, "asleep"); - } - } -} diff --git a/Content.Server/_Scp/Other/ScpOnSoundVisibility/ScpOnSoundVisibilitySystem.cs b/Content.Server/_Scp/Other/ScpOnSoundVisibility/ScpOnSoundVisibilitySystem.cs index 36846c7a19..4b735ea6b7 100644 --- a/Content.Server/_Scp/Other/ScpOnSoundVisibility/ScpOnSoundVisibilitySystem.cs +++ b/Content.Server/_Scp/Other/ScpOnSoundVisibility/ScpOnSoundVisibilitySystem.cs @@ -82,7 +82,6 @@ private void OnMobStartup(Entity ent, ref ComponentStartup ar private void OnTargetSpoke(Entity ent, ref EntitySpokeEvent args) { MobDidSomething(ent); - //TryRememberPhrase(ent, args.Message); } private void MobDidSomething(Entity ent) diff --git a/Content.Server/_Scp/Other/ScpReleaseGas/ScpReleaseGasSystem.cs b/Content.Server/_Scp/Other/ScpReleaseGas/ScpReleaseGasSystem.cs new file mode 100644 index 0000000000..44d8b5e9db --- /dev/null +++ b/Content.Server/_Scp/Other/ScpReleaseGas/ScpReleaseGasSystem.cs @@ -0,0 +1,43 @@ +using Content.Server.Actions; +using Content.Server.Fluids.EntitySystems; +using Content.Shared._Scp.Other.ScpReleaseGas; +using Content.Shared._Scp.ScpMask; +using Content.Shared.Coordinates.Helpers; + +namespace Content.Server._Scp.Scp939; + +public sealed class ScpReleaseGasSystem : EntitySystem +{ + [Dependency] private readonly ScpMaskSystem _scpMask = default!; + [Dependency] private readonly SmokeSystem _smokeSystem = default!; + [Dependency] private readonly ActionsSystem _actionsSystem = default!; + + public override void Initialize() + { + base.Initialize(); + + SubscribeLocalEvent(OnInit); + SubscribeLocalEvent(OnGasAction); + } + + public void OnInit(Entity ent, ref ComponentInit args) + { + _actionsSystem.AddAction(ent, ent.Comp.ActionProto); + } + + private void OnGasAction(Entity ent, ref ScpReleaseGasActionEvent args) + { + if (_scpMask.TryGetScpMask(ent, out var scpMask)) + { + _scpMask.TryCreatePopup(ent, scpMask); + return; + } + + var xform = Transform(ent); + var smokeEntity = Spawn(ent.Comp.SmokeProtoId, xform.Coordinates.SnapToGrid()); + + _smokeSystem.StartSmoke(smokeEntity, ent.Comp.SmokeSolution, ent.Comp.SmokeDuration, ent.Comp.SmokeSpreadRadius); + + args.Handled = true; + } +} diff --git a/Content.Server/_Scp/Other/ScpRememberPhrase/ScpRememberPhraseSystem.cs b/Content.Server/_Scp/Other/ScpRememberPhrase/ScpRememberPhraseSystem.cs new file mode 100644 index 0000000000..e63b7d5c95 --- /dev/null +++ b/Content.Server/_Scp/Other/ScpRememberPhrase/ScpRememberPhraseSystem.cs @@ -0,0 +1,94 @@ +using Content.Server.Actions; +using Content.Server.Chat.Systems; +using Content.Server.Examine; +using Content.Shared._Scp.Helpers; +using Content.Shared._Scp.Other.ScpRememberPhrase; +using Content.Shared._Sunrise.TTS; +using Content.Shared.Chat; +using Content.Shared.IdentityManagement; +using Robust.Shared.Random; +using Content.Shared._Scp.Other.ScpOnSoundVisibility; + +namespace Content.Server._Scp.Other.ScpRememberPhrase; + +public sealed class ScpRememberPhraseSystem : EntitySystem +{ + [Dependency] private readonly ActionsSystem _actionsSystem = default!; + [Dependency] private readonly ExamineSystem _examine = default!; + [Dependency] private readonly ChatSystem _chat = default!; + [Dependency] private readonly EntityLookupSystem _entityLookup = default!; + [Dependency] private readonly IRobustRandom _random = default!; + + public override void Initialize() + { + base.Initialize(); + + SubscribeLocalEvent(OnInit); + SubscribeLocalEvent(OnSpoke); // TODO: Переделать под другой какой-то общий компонент / ивент + SubscribeLocalEvent(OnMimic); + } + + private void OnInit(Entity ent, ref ComponentInit args) + { + _actionsSystem.AddAction(ent, ent.Comp.ActionProto); + } + + private void OnSpoke(Entity ent, ref EntitySpokeEvent args) + { + TryRememberPhrase(ent, args.Message); + } + + private void OnMimic(Entity ent, ref ScpRememberPhraseActionEvent args) + { + if (ent.Comp.RememberedMessages.Count == 0) + return; + + var messagePair = _random.Pick(ent.Comp.RememberedMessages); + + if (TryComp(ent, out var ttsComponent)) + { + ttsComponent.VoicePrototypeId = messagePair.Value.Value; + Dirty(ent, ttsComponent); + } + + _chat.TrySendInGameICMessage(ent, + messagePair.Key, + InGameICChatType.Speak, + ChatTransmitRange.Normal, + nameOverride: messagePair.Value.Key, + ignoreActionBlocker: true); + + args.Handled = true; + } + + /// + /// Запоминание последних сказанных в округе + /// + private void TryRememberPhrase(Entity ent, string message) + { + using var rememberSet = HashSetPoolEntity.Rent(); + _entityLookup.GetEntitiesInRange(Transform(ent).Coordinates, 16f, rememberSet.Value, LookupFlags.Dynamic | LookupFlags.Approximate); + if (rememberSet.Value.Count == 0) + return; + + string? voicePrototype = null; + + if (TryComp(ent, out var ttsComponent)) + voicePrototype = ttsComponent.VoicePrototypeId; + + foreach (var rememberEnt in rememberSet.Value) + { + if (!_examine.InRangeUnOccluded(ent, rememberEnt)) + continue; + + if (rememberEnt.Comp.RememberedMessages.Count >= rememberEnt.Comp.MaxRememberedMessages) + { + var randomKey = _random.Pick(rememberEnt.Comp.RememberedMessages.Keys); + rememberEnt.Comp.RememberedMessages.Remove(randomKey); + } + + var username = Identity.Name(ent, EntityManager); + rememberEnt.Comp.RememberedMessages.TryAdd(message, new(username, voicePrototype)); + } + } +} diff --git a/Content.Server/_Scp/Other/ScpSleep/ScpSleepSystem.cs b/Content.Server/_Scp/Other/ScpSleep/ScpSleepSystem.cs new file mode 100644 index 0000000000..7f2c699b39 --- /dev/null +++ b/Content.Server/_Scp/Other/ScpSleep/ScpSleepSystem.cs @@ -0,0 +1,98 @@ +using Content.Server.Actions; +using Content.Shared._Scp.Other.ScpSleep; +using Content.Shared.Bed.Sleep; +using Content.Shared.Body.Components; +using Content.Shared.Damage.Systems; +using Content.Shared.Mobs; +using Content.Shared.StatusEffectNew; +using Robust.Server.Audio; +using Robust.Server.GameObjects; + +namespace Content.Server._Scp.Other.ScpSleep; + +public sealed class ScpSleepSystem : EntitySystem +{ + [Dependency] private readonly ActionsSystem _actionsSystem = default!; + [Dependency] private readonly SleepingSystem _sleepingSystem = default!; + [Dependency] private readonly StatusEffectsSystem _statusEffects = default!; + [Dependency] private readonly DamageableSystem _damageableSystem = default!; + [Dependency] private readonly AppearanceSystem _appearanceSystem = default!; + [Dependency] private readonly AudioSystem _audio = default!; + public override void Initialize() + { + base.Initialize(); + + SubscribeLocalEvent(OnInit); + SubscribeLocalEvent(OnSleepAction); + SubscribeLocalEvent(OnMobStateChanged); + SubscribeLocalEvent(OnSleepChanged); + } + public void OnInit(Entity ent, ref ComponentInit args) + { + _actionsSystem.AddAction(ent, ent.Comp.ActionProto); + } + + private void OnSleepAction(Entity ent, ref ScpSleepActionEvent args) + { + args.Handled = TrySleep(ent); + } + + private void OnMobStateChanged(Entity ent, ref MobStateChangedEvent args) + { + if (!ent.Comp.HibernationOnCriticalState) + return; + + if (args.NewMobState != MobState.Critical) + return; + + TrySleep(ent, ent.Comp.HibernationDurationOnCriticalState); + + if (ent.Comp.CritSound == null) + return; + + _audio.PlayPvs(ent.Comp.CritSound, ent); + } + + private void OnSleepChanged(Entity ent, ref SleepStateChangedEvent args) + { + if (TryComp(ent, out var bloodstreamComponent)) + { + if (args.FellAsleep) + bloodstreamComponent.BloodRefreshAmount = 20; + else + bloodstreamComponent.BloodRefreshAmount = 1; + + Dirty(ent, bloodstreamComponent); + } + + _appearanceSystem.SetData(ent, ScpSleepVisuals.Sleeping, args.FellAsleep); + } + + public bool TrySleep(Entity ent, float hibernationDuration = 0) + { + if (HasComp(ent)) + return false; + + if (!_sleepingSystem.TrySleeping(ent.Owner)) + return false; + + hibernationDuration = hibernationDuration == 0 ? ent.Comp.HibernationDuration : hibernationDuration; + _statusEffects.TryAddStatusEffectDuration(ent, ent.Comp.StatusEffect, TimeSpan.FromSeconds(hibernationDuration)); + + return true; + } + + public override void Update(float frameTime) + { + base.Update(frameTime); + + var querySleeping = EntityQueryEnumerator(); + while (querySleeping.MoveNext(out var uid, out var scpSleepComponent, out _)) + { + if (!scpSleepComponent.HibernationHealing) + continue; + + _damageableSystem.TryChangeDamage(uid, scpSleepComponent.HibernationHealingRate * frameTime); + } + } +} diff --git a/Content.Server/_Scp/Research/Artifacts/Effects/_ScpSpecific/Scp939/Sleep/ArtifactScp939SleepSystem.cs b/Content.Server/_Scp/Research/Artifacts/Effects/_ScpSpecific/Scp939/Sleep/ArtifactScp939SleepSystem.cs index b856b0b460..d3f89b5ae3 100644 --- a/Content.Server/_Scp/Research/Artifacts/Effects/_ScpSpecific/Scp939/Sleep/ArtifactScp939SleepSystem.cs +++ b/Content.Server/_Scp/Research/Artifacts/Effects/_ScpSpecific/Scp939/Sleep/ArtifactScp939SleepSystem.cs @@ -1,23 +1,23 @@ -using Content.Server._Scp.Scp939; -using Content.Shared._Scp.Scp939; +using Content.Server._Scp.Other.ScpSleep; using Content.Shared.Xenoarchaeology.Artifact; using Content.Shared.Xenoarchaeology.Artifact.XAE; using Robust.Shared.Random; +using Content.Shared._Scp.Other.ScpSleep; namespace Content.Server._Scp.Research.Artifacts.Effects._ScpSpecific.Scp939.Sleep; public sealed class ArtifactScp939SleepSystem : BaseXAESystem { - [Dependency] private readonly Scp939System _scp939 = default!; + [Dependency] private readonly ScpSleepSystem _scpSleepSystem = default!; [Dependency] private readonly IRobustRandom _random = default!; protected override void OnActivated(Entity ent, ref XenoArtifactNodeActivatedEvent args) { - if (!TryComp(ent, out var scp939Component)) + if (!TryComp(ent, out var scpSleepComponent)) return; var time = _random.NextFloat(ent.Comp.MinSleepTime, ent.Comp.MaxSleepTime); - _scp939.TrySleep((ent, scp939Component), time); + _scpSleepSystem.TrySleep((ent, scpSleepComponent), time); } } diff --git a/Content.Server/_Scp/Scp939/Scp939System.Actions.cs b/Content.Server/_Scp/Scp939/Scp939System.Actions.cs deleted file mode 100644 index 22e06540e0..0000000000 --- a/Content.Server/_Scp/Scp939/Scp939System.Actions.cs +++ /dev/null @@ -1,122 +0,0 @@ -using Content.Server.Chat.Systems; -using Content.Server.Examine; -using Content.Shared._Scp.Helpers; -using Content.Shared._Scp.Scp939; -using Content.Shared._Scp.ScpMask; -using Content.Shared._Sunrise.TTS; -using Content.Shared.Bed.Sleep; -using Content.Shared.Chat; -using Content.Shared.Coordinates.Helpers; -using Content.Shared.IdentityManagement; -using Robust.Shared.Prototypes; -using Robust.Shared.Random; - -namespace Content.Server._Scp.Scp939; - -public sealed partial class Scp939System -{ - [Dependency] private readonly ExamineSystem _examine = default!; - [Dependency] private readonly EntityLookupSystem _entityLookup = default!; - [Dependency] private readonly ChatSystem _chat = default!; - [Dependency] private readonly ScpMaskSystem _scpMask = default!; - [Dependency] private readonly IRobustRandom _random = default!; - - private static readonly EntProtoId StatusEffect = "StatusEffectForcedSleeping"; - - private void InitializeActions() - { - SubscribeLocalEvent(OnSleepAction); - SubscribeLocalEvent(OnGasAction); - SubscribeLocalEvent(OnMimic); - } - - private void OnSleepAction(Entity ent, ref Scp939SleepAction args) - { - args.Handled = TrySleep(ent); - } - - public bool TrySleep(Entity ent, float hibernationDuration = 0) - { - if (HasComp(ent)) - return false; - - if (!_sleepingSystem.TrySleeping(ent.Owner)) - return false; - - hibernationDuration = hibernationDuration == 0 ? ent.Comp.HibernationDuration : hibernationDuration; - _statusEffects.TryAddStatusEffectDuration(ent, StatusEffect, TimeSpan.FromSeconds(hibernationDuration)); - - return true; - } - - private void OnGasAction(Entity ent, ref Scp939GasAction args) - { - if (_scpMask.TryGetScpMask(ent, out var scpMask)) - { - _scpMask.TryCreatePopup(ent, scpMask); - return; - } - - var xform = Transform(ent); - var smokeEntity = Spawn(ent.Comp.SmokeProtoId, xform.Coordinates.SnapToGrid()); - - _smokeSystem.StartSmoke(smokeEntity, ent.Comp.SmokeSolution, ent.Comp.SmokeDuration, ent.Comp.SmokeSpreadRadius); - - args.Handled = true; - } - - private void OnMimic(Entity ent, ref Scp939MimicActionEvent args) - { - if (ent.Comp.RememberedMessages.Count == 0) - return; - - var messagePair = _random.Pick(ent.Comp.RememberedMessages); - - if (TryComp(ent, out var ttsComponent)) - { - ttsComponent.VoicePrototypeId = messagePair.Value.Value; - Dirty(ent, ttsComponent); - } - - _chat.TrySendInGameICMessage(ent, - messagePair.Key, - InGameICChatType.Speak, - ChatTransmitRange.Normal, - nameOverride: messagePair.Value.Key, - ignoreActionBlocker: true); - - args.Handled = true; - } - - // TODO: Перекочевать в отдельную систему - /// - /// Запоминание последних сказанных возле 939 слов - /// - /*private void TryRememberPhrase(Entity ent, string message) - { - using var scp939Set = HashSetPoolEntity.Rent(); - _entityLookup.GetEntitiesInRange(Transform(ent).Coordinates, 16f, scp939Set.Value, LookupFlags.Dynamic | LookupFlags.Approximate); - if (scp939Set.Value.Count == 0) - return; - - string? voicePrototype = null; - - if (TryComp(ent, out var ttsComponent)) - voicePrototype = ttsComponent.VoicePrototypeId; - - foreach (var scp in scp939Set.Value) - { - if (!_examine.InRangeUnOccluded(ent, scp)) - continue; - - if (scp.Comp.RememberedMessages.Count >= scp.Comp.MaxRememberedMessages) - { - var randomKey = _random.Pick(scp.Comp.RememberedMessages.Keys); - scp.Comp.RememberedMessages.Remove(randomKey); - } - - var username = Identity.Name(ent, EntityManager); - scp.Comp.RememberedMessages.TryAdd(message, new(username, voicePrototype)); - } - }*/ -} diff --git a/Content.Server/_Scp/Scp939/Scp939System.cs b/Content.Server/_Scp/Scp939/Scp939System.cs deleted file mode 100644 index 116d3fa0d0..0000000000 --- a/Content.Server/_Scp/Scp939/Scp939System.cs +++ /dev/null @@ -1,80 +0,0 @@ -using Content.Server.Actions; -using Content.Server.Fluids.EntitySystems; -using Content.Shared._Scp.Scp939; -using Content.Shared.Bed.Sleep; -using Content.Shared.Body.Components; -using Content.Shared.Damage.Systems; -using Content.Shared.Mobs; -using Content.Shared.StatusEffectNew; -using Robust.Server.Audio; -using Robust.Server.GameObjects; -using Robust.Shared.Audio; - -namespace Content.Server._Scp.Scp939; - -public sealed partial class Scp939System : EntitySystem -{ - [Dependency] private readonly SmokeSystem _smokeSystem = default!; - [Dependency] private readonly SleepingSystem _sleepingSystem = default!; - [Dependency] private readonly ActionsSystem _actionsSystem = default!; - [Dependency] private readonly StatusEffectsSystem _statusEffects = default!; - [Dependency] private readonly DamageableSystem _damageableSystem = default!; - [Dependency] private readonly AppearanceSystem _appearanceSystem = default!; - [Dependency] private readonly AudioSystem _audio = default!; - - private readonly SoundSpecifier _critSound = new SoundPathSpecifier("/Audio/_Scp/Scp939/crit.ogg"); - - public override void Initialize() - { - base.Initialize(); - InitializeActions(); - - SubscribeLocalEvent(OnInit); - - SubscribeLocalEvent(OnSleepChanged); - SubscribeLocalEvent(OnMobStateChanged); - } - - private void OnMobStateChanged(Entity ent, ref MobStateChangedEvent args) - { - if (args.NewMobState != MobState.Critical) - return; - - TrySleep(ent, 360f); - _audio.PlayPvs(_critSound, ent); - } - - private void OnSleepChanged(Entity ent, ref SleepStateChangedEvent args) - { - if (TryComp(ent, out var bloodstreamComponent)) - { - if (args.FellAsleep) - bloodstreamComponent.BloodRefreshAmount = 20; - else - bloodstreamComponent.BloodRefreshAmount = 1; - - Dirty(ent, bloodstreamComponent); - } - - _appearanceSystem.SetData(ent, Scp939Visuals.Sleeping, args.FellAsleep); - } - - public override void Update(float frameTime) - { - base.Update(frameTime); - - var querySleeping = EntityQueryEnumerator(); - while (querySleeping.MoveNext(out var uid, out var scp939Component, out _)) - { - _damageableSystem.TryChangeDamage(uid, scp939Component.HibernationHealingRate * frameTime); - } - } - - private void OnInit(Entity ent, ref ComponentInit args) - { - foreach (var action in ent.Comp.Actions) - { - _actionsSystem.AddAction(ent, action); - } - } -} diff --git a/Content.Shared/_Scp/Other/ScpReleaseGas/ScpReleaseGasActionEvent.cs b/Content.Shared/_Scp/Other/ScpReleaseGas/ScpReleaseGasActionEvent.cs new file mode 100644 index 0000000000..c05bf19f41 --- /dev/null +++ b/Content.Shared/_Scp/Other/ScpReleaseGas/ScpReleaseGasActionEvent.cs @@ -0,0 +1,5 @@ +using Content.Shared.Actions; + +namespace Content.Shared._Scp.Other.ScpReleaseGas; + +public sealed partial class ScpReleaseGasActionEvent : InstantActionEvent; diff --git a/Content.Shared/_Scp/Other/ScpReleaseGas/ScpReleaseGasComponent.cs b/Content.Shared/_Scp/Other/ScpReleaseGas/ScpReleaseGasComponent.cs new file mode 100644 index 0000000000..e6ecc426e2 --- /dev/null +++ b/Content.Shared/_Scp/Other/ScpReleaseGas/ScpReleaseGasComponent.cs @@ -0,0 +1,26 @@ + +using Content.Shared.Actions.Components; +using Content.Shared.Chemistry.Components; +using Robust.Shared.GameStates; +using Robust.Shared.Prototypes; + +namespace Content.Shared._Scp.Other.ScpReleaseGas; + +[RegisterComponent, NetworkedComponent] +public sealed partial class ScpReleaseGasComponent : Component +{ + [DataField] + public EntProtoId ActionProto = "ScpSmokeAction"; + + [DataField] + public Solution SmokeSolution = new("АМН-С227", 40); + + [DataField] + public float SmokeDuration = 30.0f; + + [DataField] + public int SmokeSpreadRadius = 10; + + [DataField] + public EntProtoId SmokeProtoId = "АМН-С227Smoke"; +} diff --git a/Content.Shared/_Scp/Other/ScpRememberPhrase/ScpRememberPhraseActionEvent.cs b/Content.Shared/_Scp/Other/ScpRememberPhrase/ScpRememberPhraseActionEvent.cs new file mode 100644 index 0000000000..efde67e5fe --- /dev/null +++ b/Content.Shared/_Scp/Other/ScpRememberPhrase/ScpRememberPhraseActionEvent.cs @@ -0,0 +1,5 @@ +using Content.Shared.Actions; + +namespace Content.Shared._Scp.Other.ScpRememberPhrase; + +public sealed partial class ScpRememberPhraseActionEvent : InstantActionEvent; diff --git a/Content.Shared/_Scp/Other/ScpRememberPhrase/ScpRememberPhraseComponent.cs b/Content.Shared/_Scp/Other/ScpRememberPhrase/ScpRememberPhraseComponent.cs new file mode 100644 index 0000000000..1f42a31d34 --- /dev/null +++ b/Content.Shared/_Scp/Other/ScpRememberPhrase/ScpRememberPhraseComponent.cs @@ -0,0 +1,22 @@ + +using Content.Shared.Actions.Components; +using Robust.Shared.GameStates; +using Robust.Shared.Prototypes; + +namespace Content.Shared._Scp.Other.ScpRememberPhrase; + +[RegisterComponent, NetworkedComponent] +public sealed partial class ScpRememberPhraseComponent : Component +{ + [DataField] + public EntProtoId ActionProto = "RememberPhraseAction"; + + [DataField] + public int MaxRememberedMessages = 20; + + /// + /// Запомненые объектом слова. Ключ - сказанная фраза, значение - пара, в которой ключ имя сказавшего и значение прототип его ттса + /// + [ViewVariables] + public Dictionary> RememberedMessages = new(); +} diff --git a/Content.Shared/_Scp/Other/ScpSleep/ScpSleepActionEvent.cs b/Content.Shared/_Scp/Other/ScpSleep/ScpSleepActionEvent.cs new file mode 100644 index 0000000000..3d2cedea74 --- /dev/null +++ b/Content.Shared/_Scp/Other/ScpSleep/ScpSleepActionEvent.cs @@ -0,0 +1,5 @@ +using Content.Shared.Actions; + +namespace Content.Shared._Scp.Other.ScpSleep; + +public sealed partial class ScpSleepActionEvent : InstantActionEvent; diff --git a/Content.Shared/_Scp/Other/ScpSleep/ScpSleepComponent.cs b/Content.Shared/_Scp/Other/ScpSleep/ScpSleepComponent.cs new file mode 100644 index 0000000000..cccce46f41 --- /dev/null +++ b/Content.Shared/_Scp/Other/ScpSleep/ScpSleepComponent.cs @@ -0,0 +1,55 @@ + +using Content.Shared.Actions.Components; +using Content.Shared.Damage; +using Robust.Shared.Audio; +using Robust.Shared.GameStates; +using Robust.Shared.Prototypes; + +namespace Content.Shared._Scp.Other.ScpSleep; + +[RegisterComponent, NetworkedComponent] +public sealed partial class ScpSleepComponent : Component +{ + [DataField] + public EntProtoId ActionProto = "ScpSleepAction"; + + [DataField] + public EntProtoId StatusEffect = "StatusEffectForcedSleeping"; + + [DataField] + public SoundSpecifier? CritSound; + + [DataField] + public float HibernationDurationOnCriticalState = 360f; + + [DataField] + public float HibernationDuration = 60f; + + [DataField] + public bool HibernationOnCriticalState = false; + + + [DataField] + public bool HibernationHealing = false; + + [DataField] + public DamageSpecifier HibernationHealingRate = new() + { + DamageDict = new() + { + { "Blunt", -20f }, + { "Slash", -20f }, + { "Piercing", -20f }, + { "Heat", -20f }, + { "Shock", -20f }, + { "Bloodloss", -20f}, + { "Genetic", -20f }, + { "Toxin", -20f }, + { "Airloss", -20f }, + { "Asphyxiation", -20f }, + { "Poison", -20f }, + { "Radiation", -20f }, + { "Cellular", -20f} + } + }; +} diff --git a/Content.Shared/_Scp/Other/ScpSleep/SharedScpSleepData.cs b/Content.Shared/_Scp/Other/ScpSleep/SharedScpSleepData.cs new file mode 100644 index 0000000000..a31dfa7302 --- /dev/null +++ b/Content.Shared/_Scp/Other/ScpSleep/SharedScpSleepData.cs @@ -0,0 +1,15 @@ +using Robust.Shared.Serialization; + +namespace Content.Shared._Scp.Other.ScpSleep; + +[Serializable, NetSerializable] +public enum ScpSleepLayers : byte +{ + Base = 0 +} + +[Serializable, NetSerializable] +public enum ScpSleepVisuals : byte +{ + Sleeping = 0, +} diff --git a/Content.Shared/_Scp/Scp939/Scp939Component.cs b/Content.Shared/_Scp/Scp939/Scp939Component.cs index ff9778f8e3..2069b1cd94 100644 --- a/Content.Shared/_Scp/Scp939/Scp939Component.cs +++ b/Content.Shared/_Scp/Scp939/Scp939Component.cs @@ -1,11 +1,11 @@ -using Content.Shared.Chemistry.Components; +using Content.Shared.Chemistry.Components; using Content.Shared.Damage; using Robust.Shared.GameStates; using Robust.Shared.Prototypes; namespace Content.Shared._Scp.Scp939; -[RegisterComponent, NetworkedComponent] +[RegisterComponent, NetworkedComponent, AutoGenerateComponentState(fieldDeltas: true)] public sealed partial class Scp939Component : Component { [DataField] @@ -52,6 +52,22 @@ public sealed partial class Scp939Component : Component } }; + #region Vision + + [DataField, AutoNetworkedField] + public bool PoorEyesight; + + [DataField, AutoNetworkedField] + public float PoorEyesightTime = 10f; // Секунды + + [AutoNetworkedField] + public TimeSpan? PoorEyesightTimeStart; // Когда начали плохо видеть + + [DataField, ViewVariables(VVAccess.ReadWrite)] + public float VisibilityActivationRange = 20f; + + #endregion + [DataField] public int MaxRememberedMessages = 20; diff --git a/Content.Shared/_Scp/Scp939/SharedScp939Data.cs b/Content.Shared/_Scp/Scp939/SharedScp939Data.cs index 35729d80ff..fc8ee533f0 100644 --- a/Content.Shared/_Scp/Scp939/SharedScp939Data.cs +++ b/Content.Shared/_Scp/Scp939/SharedScp939Data.cs @@ -1,12 +1,5 @@ -using Content.Shared.Actions; using Robust.Shared.Serialization; -namespace Content.Shared._Scp.Scp939; - -public sealed partial class Scp939GasAction : InstantActionEvent; -public sealed partial class Scp939SleepAction : InstantActionEvent; -public sealed partial class Scp939MimicActionEvent : InstantActionEvent; - [Serializable, NetSerializable] public enum Scp939Layers : byte { diff --git a/Resources/Prototypes/_Scp/Actions/scp939.yml b/Resources/Prototypes/_Scp/Actions/scp939.yml index ca1cdfbb32..238216cfba 100644 --- a/Resources/Prototypes/_Scp/Actions/scp939.yml +++ b/Resources/Prototypes/_Scp/Actions/scp939.yml @@ -1,37 +1,17 @@ - type: entity + parent: RememberPhraseAction id: Scp939Mimic name: Voice Mimicry description: Allows SCP-939 to mimic voices to lure prey. - components: - - type: Action - useDelay: 5 - icon: - sprite: _Scp/Actions/scp-939.rsi - state: mimic - - type: InstantAction - event: !type:Scp939MimicActionEvent - type: entity + parent: ScpSmokeAction id: Scp939Smoke name: Amnestic Gas description: Releases a cloud of amnestic gas to disorient victims. - components: - - type: Action - useDelay: 300 - icon: - sprite: _Scp/Actions/scp-939.rsi - state: smaug - - type: InstantAction - event: !type:Scp939GasAction - type: entity + parent: ScpSleepAction id: Scp939Sleep name: Regenerative Slumber description: Allows SCP-939 to enter a deep sleep for rapid healing. - components: - - type: Action - icon: - sprite: _Scp/Actions/scp-939.rsi - state: sleep - - type: InstantAction - event: !type:Scp939SleepAction diff --git a/Resources/Prototypes/_Scp/Actions/scp_actions.yml b/Resources/Prototypes/_Scp/Actions/scp_actions.yml new file mode 100644 index 0000000000..da3f5c30b9 --- /dev/null +++ b/Resources/Prototypes/_Scp/Actions/scp_actions.yml @@ -0,0 +1,37 @@ +- type: entity + id: RememberPhraseAction + name: Voice Mimicry + description: Allows to mimic voices to lure prey. + components: + - type: Action + useDelay: 5 + icon: + sprite: _Scp/Actions/scp-actions.rsi + state: mimic + - type: InstantAction + event: !type:ScpRememberPhraseActionEvent + +- type: entity + id: ScpSmokeAction + name: Release Gas + description: Releases a cloud of gas to disorient victims. + components: + - type: Action + useDelay: 300 + icon: + sprite: _Scp/Actions/scp-actions.rsi + state: smaug + - type: InstantAction + event: !type:ScpReleaseGasActionEvent + +- type: entity + id: ScpSleepAction + name: Slumber + description: Allows to enter a deep sleep. + components: + - type: Action + icon: + sprite: _Scp/Actions/scp-actions.rsi + state: sleep + - type: InstantAction + event: !type:ScpSleepActionEvent diff --git a/Resources/Prototypes/_Scp/Entities/Mobs/Player/Scp/Main/scp939.yml b/Resources/Prototypes/_Scp/Entities/Mobs/Player/Scp/Main/scp939.yml index 565b5ac2a9..061185d15a 100644 --- a/Resources/Prototypes/_Scp/Entities/Mobs/Player/Scp/Main/scp939.yml +++ b/Resources/Prototypes/_Scp/Entities/Mobs/Player/Scp/Main/scp939.yml @@ -17,7 +17,7 @@ drawdepth: Mobs sprite: _Scp/Mobs/Scp/scp-939.rsi layers: - - map: [ "enum.Scp939Layers.Base" ] + - map: [ "enum.ScpSleepLayers.Base" ] state: alive - type: Scp class: Keter @@ -27,6 +27,14 @@ protections: components: - Scp939Protection + - type: ScpSleep + actionProto: Scp939Sleep + hibernationHealing: true + hibernationOnCriticalState: true + - type: ScpRememberPhrase + actionProto: Scp939Mimic + - type: ScpReleaseGas + actionProto: Scp939Smoke - type: Scp939 - type: MeleeWeapon altDisarm: false diff --git a/Resources/Textures/_Scp/Actions/scp-939.rsi/meta.json b/Resources/Textures/_Scp/Actions/scp-actions.rsi/meta.json similarity index 100% rename from Resources/Textures/_Scp/Actions/scp-939.rsi/meta.json rename to Resources/Textures/_Scp/Actions/scp-actions.rsi/meta.json diff --git a/Resources/Textures/_Scp/Actions/scp-939.rsi/mimic.png b/Resources/Textures/_Scp/Actions/scp-actions.rsi/mimic.png similarity index 100% rename from Resources/Textures/_Scp/Actions/scp-939.rsi/mimic.png rename to Resources/Textures/_Scp/Actions/scp-actions.rsi/mimic.png diff --git a/Resources/Textures/_Scp/Actions/scp-939.rsi/sleep.png b/Resources/Textures/_Scp/Actions/scp-actions.rsi/sleep.png similarity index 100% rename from Resources/Textures/_Scp/Actions/scp-939.rsi/sleep.png rename to Resources/Textures/_Scp/Actions/scp-actions.rsi/sleep.png diff --git a/Resources/Textures/_Scp/Actions/scp-939.rsi/smaug.png b/Resources/Textures/_Scp/Actions/scp-actions.rsi/smaug.png similarity index 100% rename from Resources/Textures/_Scp/Actions/scp-939.rsi/smaug.png rename to Resources/Textures/_Scp/Actions/scp-actions.rsi/smaug.png From 762fdf38e93c13514174cc012086a6b1b23cb507 Mon Sep 17 00:00:00 2001 From: Wardex Date: Tue, 28 Apr 2026 11:35:29 +0300 Subject: [PATCH 05/19] =?UTF-8?q?=D0=94=D0=BE=D1=80=D0=B0=D0=B1=D0=BE?= =?UTF-8?q?=D1=82=D0=BA=D0=B8?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../ScpOnSoundVisibilityHudSystem.Overlay.cs | 1 + .../_Scp/Other/ScpSleep/ScpSleepVisualizer.cs | 10 +-- .../ScpReleaseGas/ScpReleaseGasSystem.cs | 16 +++- .../ScpRememberPhraseSystem.cs | 39 ++++++--- .../_Scp/Other/ScpSleep/ScpSleepSystem.cs | 17 +++- .../ScpOnSoundVisibilityComponent.cs | 3 + .../ScpReleaseGas/ScpReleaseGasComponent.cs | 3 + .../ScpRememberPhraseComponent.cs | 13 ++- .../_Scp/Other/ScpSleep/ScpSleepComponent.cs | 9 +++ Content.Shared/_Scp/Scp939/Scp939Component.cs | 80 ------------------- .../Entities/Mobs/Player/Scp/Main/scp939.yml | 1 - 11 files changed, 85 insertions(+), 107 deletions(-) delete mode 100644 Content.Shared/_Scp/Scp939/Scp939Component.cs diff --git a/Content.Client/_Scp/Other/ScpOnSoundVisibility/ScpOnSoundVisibilityHudSystem.Overlay.cs b/Content.Client/_Scp/Other/ScpOnSoundVisibility/ScpOnSoundVisibilityHudSystem.Overlay.cs index 2fca6755a9..19ba99ffdb 100644 --- a/Content.Client/_Scp/Other/ScpOnSoundVisibility/ScpOnSoundVisibilityHudSystem.Overlay.cs +++ b/Content.Client/_Scp/Other/ScpOnSoundVisibility/ScpOnSoundVisibilityHudSystem.Overlay.cs @@ -23,6 +23,7 @@ private void OnPlayerAttached(Entity ent, r private void OnPlayerDetached(Entity ent, ref LocalPlayerDetachedEvent args) { _viewerComponent = null; + RemoveOverlays(); } private void AddOverlays() diff --git a/Content.Client/_Scp/Other/ScpSleep/ScpSleepVisualizer.cs b/Content.Client/_Scp/Other/ScpSleep/ScpSleepVisualizer.cs index e80ac25882..b48bf506cc 100644 --- a/Content.Client/_Scp/Other/ScpSleep/ScpSleepVisualizer.cs +++ b/Content.Client/_Scp/Other/ScpSleep/ScpSleepVisualizer.cs @@ -5,7 +5,7 @@ namespace Content.Client._Scp.Other.ScpSleep; -public sealed class Scp939Visualizer : VisualizerSystem +public sealed class ScpSleepVisualizer : VisualizerSystem { [Dependency] private readonly SpriteSystem _spriteSystem = default!; [Dependency] private readonly SharedAppearanceSystem _appearanceSystem = default!; @@ -14,13 +14,13 @@ protected override void OnAppearanceChange(EntityUid uid, ScpSleepComponent comp { base.OnAppearanceChange(uid, component, ref args); - UpdateSprite(uid, args.Component, args.Sprite); + UpdateSprite(uid); } - private void UpdateSprite(EntityUid uid, AppearanceComponent appearanceComponent, SpriteComponent? spriteComponent = null, MobStateComponent? mobStateComponent = null) + private void UpdateSprite(EntityUid uid) { - if (!TryComp(uid, out spriteComponent) || - !TryComp(uid, out mobStateComponent) || + if (!TryComp(uid, out var spriteComponent) || + !TryComp(uid, out var mobStateComponent) || !_spriteSystem.LayerMapTryGet((uid, spriteComponent), ScpSleepLayers.Base, out var layerId, false)) return; diff --git a/Content.Server/_Scp/Other/ScpReleaseGas/ScpReleaseGasSystem.cs b/Content.Server/_Scp/Other/ScpReleaseGas/ScpReleaseGasSystem.cs index 44d8b5e9db..abba5f1078 100644 --- a/Content.Server/_Scp/Other/ScpReleaseGas/ScpReleaseGasSystem.cs +++ b/Content.Server/_Scp/Other/ScpReleaseGas/ScpReleaseGasSystem.cs @@ -4,7 +4,7 @@ using Content.Shared._Scp.ScpMask; using Content.Shared.Coordinates.Helpers; -namespace Content.Server._Scp.Scp939; +namespace Content.Server._Scp.Other.ScpReleaseGas; public sealed class ScpReleaseGasSystem : EntitySystem { @@ -17,12 +17,21 @@ public override void Initialize() base.Initialize(); SubscribeLocalEvent(OnInit); + SubscribeLocalEvent(OnShutdown); + SubscribeLocalEvent(OnGasAction); } - public void OnInit(Entity ent, ref ComponentInit args) + private void OnInit(Entity ent, ref ComponentInit args) + { + var actionEnt = _actionsSystem.AddAction(ent, ent.Comp.ActionProto); + ent.Comp.ActionEnt = actionEnt; + } + + private void OnShutdown(Entity ent, ref ComponentShutdown args) { - _actionsSystem.AddAction(ent, ent.Comp.ActionProto); + _actionsSystem.RemoveAction(ent.Owner, ent.Comp.ActionEnt); + ent.Comp.ActionEnt = null; } private void OnGasAction(Entity ent, ref ScpReleaseGasActionEvent args) @@ -30,6 +39,7 @@ private void OnGasAction(Entity ent, ref ScpReleaseGasAc if (_scpMask.TryGetScpMask(ent, out var scpMask)) { _scpMask.TryCreatePopup(ent, scpMask); + args.Handled = true; return; } diff --git a/Content.Server/_Scp/Other/ScpRememberPhrase/ScpRememberPhraseSystem.cs b/Content.Server/_Scp/Other/ScpRememberPhrase/ScpRememberPhraseSystem.cs index e63b7d5c95..58ba2e8a9a 100644 --- a/Content.Server/_Scp/Other/ScpRememberPhrase/ScpRememberPhraseSystem.cs +++ b/Content.Server/_Scp/Other/ScpRememberPhrase/ScpRememberPhraseSystem.cs @@ -8,6 +8,8 @@ using Content.Shared.IdentityManagement; using Robust.Shared.Random; using Content.Shared._Scp.Other.ScpOnSoundVisibility; +using System.Linq; +using NetCord.Gateway; namespace Content.Server._Scp.Other.ScpRememberPhrase; @@ -24,21 +26,30 @@ public override void Initialize() base.Initialize(); SubscribeLocalEvent(OnInit); + SubscribeLocalEvent(OnShutdown); + SubscribeLocalEvent(OnSpoke); // TODO: Переделать под другой какой-то общий компонент / ивент SubscribeLocalEvent(OnMimic); } private void OnInit(Entity ent, ref ComponentInit args) { - _actionsSystem.AddAction(ent, ent.Comp.ActionProto); + var actionEnt = _actionsSystem.AddAction(ent, ent.Comp.ActionProto); + ent.Comp.ActionEnt = actionEnt; + } + + private void OnShutdown(Entity ent, ref ComponentShutdown args) + { + _actionsSystem.RemoveAction(ent.Owner, ent.Comp.ActionEnt); + ent.Comp.ActionEnt = null; } - private void OnSpoke(Entity ent, ref EntitySpokeEvent args) + public void OnSpoke(Entity ent, ref EntitySpokeEvent args) { TryRememberPhrase(ent, args.Message); } - private void OnMimic(Entity ent, ref ScpRememberPhraseActionEvent args) + public void OnMimic(Entity ent, ref ScpRememberPhraseActionEvent args) { if (ent.Comp.RememberedMessages.Count == 0) return; @@ -47,15 +58,15 @@ private void OnMimic(Entity ent, ref ScpRememberPhra if (TryComp(ent, out var ttsComponent)) { - ttsComponent.VoicePrototypeId = messagePair.Value.Value; + ttsComponent.VoicePrototypeId = messagePair.TtsVoice; Dirty(ent, ttsComponent); } _chat.TrySendInGameICMessage(ent, - messagePair.Key, + messagePair.Message, InGameICChatType.Speak, ChatTransmitRange.Normal, - nameOverride: messagePair.Value.Key, + nameOverride: messagePair.SpeakerName, ignoreActionBlocker: true); args.Handled = true; @@ -64,10 +75,10 @@ private void OnMimic(Entity ent, ref ScpRememberPhra /// /// Запоминание последних сказанных в округе /// - private void TryRememberPhrase(Entity ent, string message) + public void TryRememberPhrase(Entity ent, string message) { using var rememberSet = HashSetPoolEntity.Rent(); - _entityLookup.GetEntitiesInRange(Transform(ent).Coordinates, 16f, rememberSet.Value, LookupFlags.Dynamic | LookupFlags.Approximate); + _entityLookup.GetEntitiesInRange(Transform(ent).Coordinates, ent.Comp.SharePhraseRadius, rememberSet.Value, LookupFlags.Dynamic | LookupFlags.Approximate); if (rememberSet.Value.Count == 0) return; @@ -82,13 +93,15 @@ private void TryRememberPhrase(Entity ent, string continue; if (rememberEnt.Comp.RememberedMessages.Count >= rememberEnt.Comp.MaxRememberedMessages) - { - var randomKey = _random.Pick(rememberEnt.Comp.RememberedMessages.Keys); - rememberEnt.Comp.RememberedMessages.Remove(randomKey); - } + rememberEnt.Comp.RememberedMessages.RemoveAt(0); var username = Identity.Name(ent, EntityManager); - rememberEnt.Comp.RememberedMessages.TryAdd(message, new(username, voicePrototype)); + rememberEnt.Comp.RememberedMessages.Add(new RememberedMessage + { + Message = message, + SpeakerName = username, + TtsVoice = voicePrototype + }); } } } diff --git a/Content.Server/_Scp/Other/ScpSleep/ScpSleepSystem.cs b/Content.Server/_Scp/Other/ScpSleep/ScpSleepSystem.cs index 7f2c699b39..927a4f875d 100644 --- a/Content.Server/_Scp/Other/ScpSleep/ScpSleepSystem.cs +++ b/Content.Server/_Scp/Other/ScpSleep/ScpSleepSystem.cs @@ -23,13 +23,22 @@ public override void Initialize() base.Initialize(); SubscribeLocalEvent(OnInit); + SubscribeLocalEvent(OnShutdown); + SubscribeLocalEvent(OnSleepAction); SubscribeLocalEvent(OnMobStateChanged); SubscribeLocalEvent(OnSleepChanged); } - public void OnInit(Entity ent, ref ComponentInit args) + private void OnInit(Entity ent, ref ComponentInit args) + { + var actionEnt = _actionsSystem.AddAction(ent, ent.Comp.ActionProto); + ent.Comp.ActionEnt = actionEnt; + } + + private void OnShutdown(Entity ent, ref ComponentShutdown args) { - _actionsSystem.AddAction(ent, ent.Comp.ActionProto); + _actionsSystem.RemoveAction(ent.Owner, ent.Comp.ActionEnt); + ent.Comp.ActionEnt = null; } private void OnSleepAction(Entity ent, ref ScpSleepActionEvent args) @@ -58,9 +67,9 @@ private void OnSleepChanged(Entity ent, ref SleepStateChanged if (TryComp(ent, out var bloodstreamComponent)) { if (args.FellAsleep) - bloodstreamComponent.BloodRefreshAmount = 20; + bloodstreamComponent.BloodRefreshAmount = ent.Comp.FellAsleepBloodRefreshAmount; else - bloodstreamComponent.BloodRefreshAmount = 1; + bloodstreamComponent.BloodRefreshAmount = ent.Comp.BaseBloodRefreshAmount; Dirty(ent, bloodstreamComponent); } diff --git a/Content.Shared/_Scp/Other/ScpOnSoundVisibility/ScpOnSoundVisibilityComponent.cs b/Content.Shared/_Scp/Other/ScpOnSoundVisibility/ScpOnSoundVisibilityComponent.cs index cc605b5008..6226808c62 100644 --- a/Content.Shared/_Scp/Other/ScpOnSoundVisibility/ScpOnSoundVisibilityComponent.cs +++ b/Content.Shared/_Scp/Other/ScpOnSoundVisibility/ScpOnSoundVisibilityComponent.cs @@ -16,4 +16,7 @@ public sealed partial class ScpOnSoundVisibilityComponent : Component [DataField] public int MaxValue = DefaultMaxValue; + + [DataField] + public float SharePhraseRadius = 16f; } diff --git a/Content.Shared/_Scp/Other/ScpReleaseGas/ScpReleaseGasComponent.cs b/Content.Shared/_Scp/Other/ScpReleaseGas/ScpReleaseGasComponent.cs index e6ecc426e2..98834fb4d6 100644 --- a/Content.Shared/_Scp/Other/ScpReleaseGas/ScpReleaseGasComponent.cs +++ b/Content.Shared/_Scp/Other/ScpReleaseGas/ScpReleaseGasComponent.cs @@ -23,4 +23,7 @@ public sealed partial class ScpReleaseGasComponent : Component [DataField] public EntProtoId SmokeProtoId = "АМН-С227Smoke"; + + [ViewVariables] + public EntityUid? ActionEnt; } diff --git a/Content.Shared/_Scp/Other/ScpRememberPhrase/ScpRememberPhraseComponent.cs b/Content.Shared/_Scp/Other/ScpRememberPhrase/ScpRememberPhraseComponent.cs index 1f42a31d34..bf216292e7 100644 --- a/Content.Shared/_Scp/Other/ScpRememberPhrase/ScpRememberPhraseComponent.cs +++ b/Content.Shared/_Scp/Other/ScpRememberPhrase/ScpRememberPhraseComponent.cs @@ -18,5 +18,16 @@ public sealed partial class ScpRememberPhraseComponent : Component /// Запомненые объектом слова. Ключ - сказанная фраза, значение - пара, в которой ключ имя сказавшего и значение прототип его ттса /// [ViewVariables] - public Dictionary> RememberedMessages = new(); + public List RememberedMessages = new(); + + [ViewVariables] + public EntityUid? ActionEnt; +} + +[DataDefinition] +public partial struct RememberedMessage +{ + public string Message; + public string SpeakerName; + public string? TtsVoice; } diff --git a/Content.Shared/_Scp/Other/ScpSleep/ScpSleepComponent.cs b/Content.Shared/_Scp/Other/ScpSleep/ScpSleepComponent.cs index cccce46f41..6d4c01a764 100644 --- a/Content.Shared/_Scp/Other/ScpSleep/ScpSleepComponent.cs +++ b/Content.Shared/_Scp/Other/ScpSleep/ScpSleepComponent.cs @@ -52,4 +52,13 @@ public sealed partial class ScpSleepComponent : Component { "Cellular", -20f} } }; + + [DataField] + public int BaseBloodRefreshAmount = 1; + + [DataField] + public int FellAsleepBloodRefreshAmount = 20; + + [ViewVariables] + public EntityUid? ActionEnt; } diff --git a/Content.Shared/_Scp/Scp939/Scp939Component.cs b/Content.Shared/_Scp/Scp939/Scp939Component.cs deleted file mode 100644 index 2069b1cd94..0000000000 --- a/Content.Shared/_Scp/Scp939/Scp939Component.cs +++ /dev/null @@ -1,80 +0,0 @@ -using Content.Shared.Chemistry.Components; -using Content.Shared.Damage; -using Robust.Shared.GameStates; -using Robust.Shared.Prototypes; - -namespace Content.Shared._Scp.Scp939; - -[RegisterComponent, NetworkedComponent, AutoGenerateComponentState(fieldDeltas: true)] -public sealed partial class Scp939Component : Component -{ - [DataField] - public Solution SmokeSolution = new("АМН-С227", 40); - - [DataField] - public float SmokeDuration = 30.0f; - - [DataField] - public int SmokeSpreadRadius = 10; - - [DataField] - public EntProtoId SmokeProtoId = "АМН-С227Smoke"; - - [DataField] - public List Actions = new() - { - "Scp939Mimic", - "Scp939Smoke", - "Scp939Sleep", - }; - - [DataField] - public float HibernationDuration = 60f; - - [DataField] - public DamageSpecifier HibernationHealingRate = new() - { - DamageDict = new() - { - { "Blunt", -20f }, - { "Slash", -20f }, - { "Piercing", -20f }, - { "Heat", -20f }, - { "Shock", -20f }, - { "Bloodloss", -20f}, - { "Genetic", -20f }, - { "Toxin", -20f }, - { "Airloss", -20f }, - { "Asphyxiation", -20f }, - { "Poison", -20f }, - { "Radiation", -20f }, - { "Cellular", -20f} - } - }; - - #region Vision - - [DataField, AutoNetworkedField] - public bool PoorEyesight; - - [DataField, AutoNetworkedField] - public float PoorEyesightTime = 10f; // Секунды - - [AutoNetworkedField] - public TimeSpan? PoorEyesightTimeStart; // Когда начали плохо видеть - - [DataField, ViewVariables(VVAccess.ReadWrite)] - public float VisibilityActivationRange = 20f; - - #endregion - - [DataField] - public int MaxRememberedMessages = 20; - - /// - /// Запомненые объектом слова. Ключ - сказанная фраза, значение - пара, в которой ключ имя сказавшего и значение прототип его ттса - /// - [ViewVariables] - public Dictionary> RememberedMessages = new(); - -} diff --git a/Resources/Prototypes/_Scp/Entities/Mobs/Player/Scp/Main/scp939.yml b/Resources/Prototypes/_Scp/Entities/Mobs/Player/Scp/Main/scp939.yml index 061185d15a..7c32c730ec 100644 --- a/Resources/Prototypes/_Scp/Entities/Mobs/Player/Scp/Main/scp939.yml +++ b/Resources/Prototypes/_Scp/Entities/Mobs/Player/Scp/Main/scp939.yml @@ -35,7 +35,6 @@ actionProto: Scp939Mimic - type: ScpReleaseGas actionProto: Scp939Smoke - - type: Scp939 - type: MeleeWeapon altDisarm: false soundHit: From f39e0fb94b089266251e4b7be998bccf5b76d480 Mon Sep 17 00:00:00 2001 From: Wardex Date: Tue, 28 Apr 2026 12:29:25 +0300 Subject: [PATCH 06/19] =?UTF-8?q?=D0=BF=D1=83=D0=BF=D1=83=D0=BF=D1=83?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../Prototypes/_Scp/Entities/Mobs/Player/Scp/Main/scp939.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Resources/Prototypes/_Scp/Entities/Mobs/Player/Scp/Main/scp939.yml b/Resources/Prototypes/_Scp/Entities/Mobs/Player/Scp/Main/scp939.yml index 7c32c730ec..fe40a4f364 100644 --- a/Resources/Prototypes/_Scp/Entities/Mobs/Player/Scp/Main/scp939.yml +++ b/Resources/Prototypes/_Scp/Entities/Mobs/Player/Scp/Main/scp939.yml @@ -105,7 +105,7 @@ collection: DistanceKillScp939 originWhitelist: components: - - Scp939 + - ScpOnSoundVisibilityViewer - type: ScpExaminableDamage mode: MobToCritical generalMessages: ScpScp939Messages From 638f5b2a521c88a7ef887bddd2452a550776f09d Mon Sep 17 00:00:00 2001 From: Wardex Date: Thu, 30 Apr 2026 20:58:20 +0300 Subject: [PATCH 07/19] bruuh --- .../ScpOnSoundVisibilityHudSystem.Overlay.cs | 2 - .../ScpOnSoundVisibilityHudSystem.cs | 106 +++++++++++------- .../_Scp/Other/ScpSleep/ScpSleepVisualizer.cs | 40 ------- .../ScpOnSoundVisibilitySystem.cs | 60 +++------- .../ScpReleaseGas/ScpReleaseGasSystem.cs | 29 +++-- .../ScpRememberPhraseSystem.cs | 52 ++++----- .../_Scp/Other/ScpSleep/ScpSleepSystem.cs | 25 +++-- .../Sleep/ArtifactScp939SleepComponent.cs | 4 +- .../Scp939/Sleep/ArtifactScp939SleepSystem.cs | 2 +- .../_Scp/Other/Events/ScpSystemsEvents.cs | 12 ++ .../ActiveScpOnSoundVisibilityComponent.cs | 11 +- .../ActiveScpPoorEyesightComponent.cs | 5 + .../ScpOnSoundVisibilityComponent.cs | 8 +- ...ScpOnSoundVisibilityProtectionComponent.cs | 5 +- .../ScpOnSoundVisibilityViewerComponent.cs | 20 ++-- .../ScpReleaseGas/ScpReleaseGasActionEvent.cs | 5 - .../ScpReleaseGas/ScpReleaseGasComponent.cs | 11 +- .../ScpRememberPhraseActionEvent.cs | 5 - .../ScpRememberPhraseComponent.cs | 3 +- .../Other/ScpSleep/ScpSleepActionEvent.cs | 5 - .../_Scp/Other/ScpSleep/ScpSleepComponent.cs | 14 ++- Resources/Prototypes/Entities/Mobs/base.yml | 1 + .../Entities/Mobs/Player/Scp/Main/scp939.yml | 23 +++- 23 files changed, 211 insertions(+), 237 deletions(-) delete mode 100644 Content.Client/_Scp/Other/ScpSleep/ScpSleepVisualizer.cs create mode 100644 Content.Shared/_Scp/Other/Events/ScpSystemsEvents.cs create mode 100644 Content.Shared/_Scp/Other/ScpOnSoundVisibility/ActiveScpPoorEyesightComponent.cs delete mode 100644 Content.Shared/_Scp/Other/ScpReleaseGas/ScpReleaseGasActionEvent.cs delete mode 100644 Content.Shared/_Scp/Other/ScpRememberPhrase/ScpRememberPhraseActionEvent.cs delete mode 100644 Content.Shared/_Scp/Other/ScpSleep/ScpSleepActionEvent.cs diff --git a/Content.Client/_Scp/Other/ScpOnSoundVisibility/ScpOnSoundVisibilityHudSystem.Overlay.cs b/Content.Client/_Scp/Other/ScpOnSoundVisibility/ScpOnSoundVisibilityHudSystem.Overlay.cs index 19ba99ffdb..18b1ec11db 100644 --- a/Content.Client/_Scp/Other/ScpOnSoundVisibility/ScpOnSoundVisibilityHudSystem.Overlay.cs +++ b/Content.Client/_Scp/Other/ScpOnSoundVisibility/ScpOnSoundVisibilityHudSystem.Overlay.cs @@ -16,13 +16,11 @@ private void InitializeOverlay() private void OnPlayerAttached(Entity ent, ref LocalPlayerAttachedEvent args) { - _viewerComponent = ent.Comp; AddOverlays(); } private void OnPlayerDetached(Entity ent, ref LocalPlayerDetachedEvent args) { - _viewerComponent = null; RemoveOverlays(); } diff --git a/Content.Client/_Scp/Other/ScpOnSoundVisibility/ScpOnSoundVisibilityHudSystem.cs b/Content.Client/_Scp/Other/ScpOnSoundVisibility/ScpOnSoundVisibilityHudSystem.cs index 4eedc930da..eddbaac4ba 100644 --- a/Content.Client/_Scp/Other/ScpOnSoundVisibility/ScpOnSoundVisibilityHudSystem.cs +++ b/Content.Client/_Scp/Other/ScpOnSoundVisibility/ScpOnSoundVisibilityHudSystem.cs @@ -1,3 +1,4 @@ +using System.Diagnostics.CodeAnalysis; using Content.Client.Overlays; using Content.Client.SSDIndicator; using Content.Shared._Scp.Other.ScpOnSoundVisibility; @@ -15,14 +16,15 @@ using Robust.Shared.Physics.Components; using Robust.Shared.Physics.Events; using Robust.Shared.Random; +using Robust.Shared.Timing; namespace Content.Client._Scp.Other.ScpOnSoundVisibility; public sealed partial class ScpOnSoundVisibilityHudSystem : EquipmentHudSystem { [Dependency] private readonly EntityWhitelistSystem _whitelist = default!; + [Dependency] private readonly IGameTiming _timing = default!; [Dependency] private readonly IRobustRandom _random = default!; - [Dependency] private readonly IPlayerManager _playerManager = default!; [Dependency] private readonly SpriteSystem _sprite = default!; @@ -31,15 +33,14 @@ public sealed partial class ScpOnSoundVisibilityHudSystem : EquipmentHudSystem _eyeQuery; private EntityQuery _movementSpeedQuery; private EntityQuery _physicsQuery; + private EntityQuery _viewerQuery; private ScpOnSoundVisibilitySetAlphaOverlay _setAlphaOverlay = default!; private ScpOnSoundVisibilityResetAlphaOverlay _resetAlphaOverlay = default!; - private ScpOnSoundVisibilityViewerComponent? _viewerComponent; - private bool _overlaysPresented; - private float _lastUpdateTime; - private const float UpdateInterval = 0.05f; + private TimeSpan _nextUpdateTime; + private readonly TimeSpan UpdateInterval = TimeSpan.FromSeconds(0.05); public override void Initialize() { @@ -50,11 +51,8 @@ public override void Initialize() SubscribeLocalEvent(OnGetStatusIcons, after: [typeof(SSDIndicatorSystem)]); SubscribeLocalEvent(OnExamine); - SubscribeLocalEvent((Entity ent, ref StartCollideEvent args) - => OnCollide(ent, args.OtherEntity)); - SubscribeLocalEvent((Entity ent, ref EndCollideEvent args) - => OnCollide(ent, args.OtherEntity)); - SubscribeLocalEvent(OnVisibilityStateUpdated); + SubscribeLocalEvent(OnStartCollide); + SubscribeLocalEvent(OnEndCollide); SubscribeLocalEvent(OnMove); @@ -66,6 +64,7 @@ public override void Initialize() _eyeQuery = GetEntityQuery(); _movementSpeedQuery = GetEntityQuery(); _physicsQuery = GetEntityQuery(); + _viewerQuery = GetEntityQuery(); _setAlphaOverlay = new(); _resetAlphaOverlay = new(); @@ -82,23 +81,17 @@ public override void Shutdown() base.Shutdown(); } - private void OnVisibilityStateUpdated(Entity ent, ref AfterAutoHandleStateEvent args) - { - if (ent.Comp.LastHandledVisibilityResetCounter == ent.Comp.VisibilityResetCounter) - return; - - ent.Comp.LastHandledVisibilityResetCounter = ent.Comp.VisibilityResetCounter; - ent.Comp.VisibilityAcc = ScpOnSoundVisibilityComponent.InitialVisibilityAcc; - } - private void OnExamine(Entity ent, ref ExamineAttemptEvent args) { if (!IsActive) return; + if (!TryGetLocalViewer(out var viewerComp)) + return; + var visibility = GetVisibility(ent); - if (visibility < 0.2f) + if (visibility < viewerComp.ExamineHideThreshold) args.Cancel(); } @@ -107,9 +100,12 @@ private void OnGetStatusIcons(Entity ent, r if (!IsActive) return; + if (!TryGetLocalViewer(out var viewerComp)) + return; + var visibility = GetVisibility(ent); - if (visibility <= 0.5f) + if (visibility <= viewerComp.StatusIconClearThreshold) args.StatusIcons.Clear(); } @@ -117,7 +113,6 @@ protected override void UpdateInternal(RefreshEquipmentHudEvent 0 ? args.Components[0] : null; AddOverlays(); } @@ -125,8 +120,7 @@ protected override void DeactivateInternal() { base.DeactivateInternal(); - _viewerComponent = null; - _lastUpdateTime = 0f; + _nextUpdateTime = TimeSpan.Zero; RestoreCachedBaseAlphas(); RemoveOverlays(); @@ -139,16 +133,25 @@ public override void Update(float frameTime) if (!IsActive) return; - _lastUpdateTime += frameTime; + /*_lastUpdateTime += _timing.RealTime; if (_lastUpdateTime < UpdateInterval) return; var delta = _lastUpdateTime; - _lastUpdateTime = 0f; + _lastUpdateTime = 0f;*/ + + if (_timing.RealTime < _nextUpdateTime) + return; + + var delta = (float)(_timing.RealTime - (_nextUpdateTime - UpdateInterval)).TotalSeconds; + _nextUpdateTime = _timing.RealTime + UpdateInterval; var query = EntityQueryEnumerator(); while (query.MoveNext(out _, out var visibilityComponent)) { + if (visibilityComponent.OnCollide) + return; + if (visibilityComponent.VisibilityAcc >= visibilityComponent.HideTime) continue; @@ -156,7 +159,7 @@ public override void Update(float frameTime) } } - internal bool CanDraw(in OverlayDrawArgs args) + public bool CanDraw(in OverlayDrawArgs args) { if (!IsActive) return false; @@ -170,7 +173,7 @@ internal bool CanDraw(in OverlayDrawArgs args) return args.Viewport.Eye == eye.Eye; } - internal void RestoreCachedBaseAlphas() + public void RestoreCachedBaseAlphas() { foreach (var (ent, baseAlpha) in CachedBaseAlphas) { @@ -183,7 +186,7 @@ internal void RestoreCachedBaseAlphas() CachedBaseAlphas.Clear(); } - internal static float GetVisibility(Entity ent) + public static float GetVisibility(Entity ent) { var acc = ent.Comp.VisibilityAcc; @@ -198,13 +201,15 @@ private void OnMove(Entity ent, ref MoveEve if (!IsActive) return; + if (!TryGetLocalViewer(out var viewerComp)) + return; + // В зависимости от наличие защит или проблем со зрением изменяется то, насколько хорошо мы видим жертву - if (ModifyAcc(ent.Comp, out var modifier)) // Если зрение затруднено + if (ModifyAcc(ent, out var modifier)) // Если зрение затруднено { ent.Comp.VisibilityAcc *= modifier; } - else if (!TryComp(ent, out var viewerComp) || - !_whitelist.IsWhitelistPass(viewerComp.Protections, ent)) // Если имеется защита(тихое хождение) + else if (!_whitelist.IsWhitelistPass(viewerComp.Protections, ent)) // Если имеется защита(тихое хождение) { return; } @@ -225,15 +230,21 @@ private void OnMove(Entity ent, ref MoveEve ent.Comp.VisibilityAcc = ent.Comp.HideTime / 2f; } - - private void OnCollide(Entity ent, EntityUid otherEntity) + private void OnStartCollide(Entity ent, ref StartCollideEvent args) { if (!IsActive) return; - if (!HasComp(otherEntity)) + ent.Comp.OnCollide = true; + MobDidSomething(ent); + } + + private void OnEndCollide(Entity ent, ref EndCollideEvent args) + { + if (!IsActive) return; + ent.Comp.OnCollide = false; MobDidSomething(ent); } @@ -275,19 +286,34 @@ private void MobDidSomething(Entity ent) } // TODO: Переделать под статус эффект и добавить его в панель статус эффектов, а то непонятно игруну - private bool ModifyAcc(ActiveScpOnSoundVisibilityComponent visibilityComponent, out int modifier) + private bool ModifyAcc(Entity ent, out float modifier) { // 1 = отсутствие модификатора - modifier = 1; + modifier = 1f; + + if (!TryGetLocalViewer(out var viewerComp)) + return false; - if (_viewerComponent == null) + if (!viewerComp.PoorEyesight) return false; - if (!_viewerComponent.PoorEyesight) + modifier = _random.NextFloat(ent.Comp.MinValue, ent.Comp.MaxValue); + + return true; + } + + private bool TryGetLocalViewer([NotNullWhen(true)] out ScpOnSoundVisibilityViewerComponent? viewerComp) + { + viewerComp = null; + var localPlayer = _playerManager.LocalEntity; + + if (localPlayer == null) return false; - modifier = _random.Next(visibilityComponent.MinValue, visibilityComponent.MaxValue); + if (!_viewerQuery.TryComp(localPlayer, out var comp)) + return false; + viewerComp = comp; return true; } } diff --git a/Content.Client/_Scp/Other/ScpSleep/ScpSleepVisualizer.cs b/Content.Client/_Scp/Other/ScpSleep/ScpSleepVisualizer.cs deleted file mode 100644 index b48bf506cc..0000000000 --- a/Content.Client/_Scp/Other/ScpSleep/ScpSleepVisualizer.cs +++ /dev/null @@ -1,40 +0,0 @@ -using Content.Shared._Scp.Other.ScpSleep; -using Content.Shared.Mobs; -using Content.Shared.Mobs.Components; -using Robust.Client.GameObjects; - -namespace Content.Client._Scp.Other.ScpSleep; - -public sealed class ScpSleepVisualizer : VisualizerSystem -{ - [Dependency] private readonly SpriteSystem _spriteSystem = default!; - [Dependency] private readonly SharedAppearanceSystem _appearanceSystem = default!; - - protected override void OnAppearanceChange(EntityUid uid, ScpSleepComponent component, ref AppearanceChangeEvent args) - { - base.OnAppearanceChange(uid, component, ref args); - - UpdateSprite(uid); - } - - private void UpdateSprite(EntityUid uid) - { - if (!TryComp(uid, out var spriteComponent) || - !TryComp(uid, out var mobStateComponent) || - !_spriteSystem.LayerMapTryGet((uid, spriteComponent), ScpSleepLayers.Base, out var layerId, false)) - return; - - if (mobStateComponent.CurrentState is MobState.Dead or MobState.Critical) - { - _spriteSystem.LayerSetRsiState(uid, layerId, "dead"); - return; - } - - _spriteSystem.LayerSetRsiState(uid, layerId, "alive"); - - if (_appearanceSystem.TryGetData(uid, ScpSleepVisuals.Sleeping, out var sleeping) && sleeping) - { - _spriteSystem.LayerSetRsiState(uid, layerId, "asleep"); - } - } -} diff --git a/Content.Server/_Scp/Other/ScpOnSoundVisibility/ScpOnSoundVisibilitySystem.cs b/Content.Server/_Scp/Other/ScpOnSoundVisibility/ScpOnSoundVisibilitySystem.cs index 4b735ea6b7..43093177e7 100644 --- a/Content.Server/_Scp/Other/ScpOnSoundVisibility/ScpOnSoundVisibilitySystem.cs +++ b/Content.Server/_Scp/Other/ScpOnSoundVisibility/ScpOnSoundVisibilitySystem.cs @@ -1,11 +1,7 @@ using Content.Server.Popups; using Content.Shared._Scp.Other.ScpOnSoundVisibility; -using Content.Shared.Chat; using Content.Shared.Flash; -using Content.Shared.Item; -using Content.Shared.Mobs.Components; using Content.Shared.Popups; -using Content.Shared.Weapons.Ranged.Systems; using Robust.Shared.Map; using Robust.Shared.Timing; @@ -30,12 +26,6 @@ public override void Initialize() { base.Initialize(); - SubscribeLocalEvent(OnMobStartup); - - SubscribeLocalEvent(OnTargetSpoke); - SubscribeLocalEvent(OnTargetEmote); - SubscribeLocalEvent(OnShot); - SubscribeLocalEvent(OnFlash); _activeQuery = GetEntityQuery(); @@ -55,39 +45,11 @@ private void OnFlash(Entity ent, ref AfterF _popup.PopupEntity(message, ent, ent, PopupType.MediumCaution); } - Dirty(ent); - } - - private void OnTargetEmote(Entity ent, ref EmoteEvent args) - { - MobDidSomething(ent); - } - - private void OnShot(Entity ent, ref GunShotEvent args) - { - if (!_activeQuery.TryComp(args.User, out var visibilityComponent)) - return; - - MobDidSomething((args.User, visibilityComponent)); - } - - private void OnMobStartup(Entity ent, ref ComponentStartup args) - { - if (HasComp(ent)) - return; - - EnsureComp(ent); - } - - private void OnTargetSpoke(Entity ent, ref EntitySpokeEvent args) - { - MobDidSomething(ent); - } - - private void MobDidSomething(Entity ent) - { - ent.Comp.VisibilityResetCounter++; - DirtyField(ent, ent.Comp, nameof(ActiveScpOnSoundVisibilityComponent.VisibilityResetCounter)); + EnsureComp(ent); + DirtyFields(ent, ent.Comp, null, [ + nameof(ScpOnSoundVisibilityViewerComponent.PoorEyesight), + nameof(ScpOnSoundVisibilityViewerComponent.PoorEyesightTimeStart) + ]); } public override void Update(float frameTime) @@ -96,8 +58,8 @@ public override void Update(float frameTime) UpdateVisibilityTargets(); - var querySimple = EntityQueryEnumerator(); - while (querySimple.MoveNext(out var uid, out var viewerComp)) + var querySimple = EntityQueryEnumerator(); + while (querySimple.MoveNext(out var uid, out _, out var viewerComp)) { if (!viewerComp.PoorEyesight) continue; @@ -112,7 +74,11 @@ public override void Update(float frameTime) viewerComp.PoorEyesight = false; viewerComp.PoorEyesightTimeStart = null; - Dirty(uid, viewerComp); + RemCompDeferred(uid); + DirtyFields(uid, viewerComp, null, [ + nameof(ScpOnSoundVisibilityViewerComponent.PoorEyesight), + nameof(ScpOnSoundVisibilityViewerComponent.PoorEyesightTimeStart) + ]); } } } @@ -160,7 +126,7 @@ private void UpdateVisibilityTargets() foreach (var uid in _visibilityRemovalQueue) { - RemComp(uid); + RemCompDeferred(uid); } _visibilityCandidates.Clear(); diff --git a/Content.Server/_Scp/Other/ScpReleaseGas/ScpReleaseGasSystem.cs b/Content.Server/_Scp/Other/ScpReleaseGas/ScpReleaseGasSystem.cs index abba5f1078..55756fdd66 100644 --- a/Content.Server/_Scp/Other/ScpReleaseGas/ScpReleaseGasSystem.cs +++ b/Content.Server/_Scp/Other/ScpReleaseGas/ScpReleaseGasSystem.cs @@ -1,28 +1,28 @@ using Content.Server.Actions; -using Content.Server.Fluids.EntitySystems; using Content.Shared._Scp.Other.ScpReleaseGas; +using Content.Shared._Scp.Other.Events; using Content.Shared._Scp.ScpMask; -using Content.Shared.Coordinates.Helpers; +using Content.Shared.Trigger.Systems; namespace Content.Server._Scp.Other.ScpReleaseGas; public sealed class ScpReleaseGasSystem : EntitySystem { - [Dependency] private readonly ScpMaskSystem _scpMask = default!; - [Dependency] private readonly SmokeSystem _smokeSystem = default!; [Dependency] private readonly ActionsSystem _actionsSystem = default!; + [Dependency] private readonly ScpMaskSystem _scpMask = default!; + [Dependency] private readonly TriggerSystem _trigger = default!; public override void Initialize() { base.Initialize(); - SubscribeLocalEvent(OnInit); + SubscribeLocalEvent(OnMapInit); SubscribeLocalEvent(OnShutdown); SubscribeLocalEvent(OnGasAction); } - private void OnInit(Entity ent, ref ComponentInit args) + private void OnMapInit(Entity ent, ref MapInitEvent args) { var actionEnt = _actionsSystem.AddAction(ent, ent.Comp.ActionProto); ent.Comp.ActionEnt = actionEnt; @@ -34,19 +34,26 @@ private void OnShutdown(Entity ent, ref ComponentShutdow ent.Comp.ActionEnt = null; } - private void OnGasAction(Entity ent, ref ScpReleaseGasActionEvent args) + public void OnGasActionAttempt(Entity ent, ref ScpReleaseGasActionAttemptEvent args) { if (_scpMask.TryGetScpMask(ent, out var scpMask)) { _scpMask.TryCreatePopup(ent, scpMask); - args.Handled = true; + args.Cancel(); return; } + } - var xform = Transform(ent); - var smokeEntity = Spawn(ent.Comp.SmokeProtoId, xform.Coordinates.SnapToGrid()); + private void OnGasAction(Entity ent, ref ScpReleaseGasActionEvent args) + { + var ev = new ScpReleaseGasActionAttemptEvent(); + RaiseLocalEvent(ent, ref ev); + + if (ev.Cancelled) + return; - _smokeSystem.StartSmoke(smokeEntity, ent.Comp.SmokeSolution, ent.Comp.SmokeDuration, ent.Comp.SmokeSpreadRadius); + foreach (var key in ent.Comp.KeysOut) + _trigger.Trigger(ent, args.Performer, key, false); args.Handled = true; } diff --git a/Content.Server/_Scp/Other/ScpRememberPhrase/ScpRememberPhraseSystem.cs b/Content.Server/_Scp/Other/ScpRememberPhrase/ScpRememberPhraseSystem.cs index 58ba2e8a9a..b6dbc3de48 100644 --- a/Content.Server/_Scp/Other/ScpRememberPhrase/ScpRememberPhraseSystem.cs +++ b/Content.Server/_Scp/Other/ScpRememberPhrase/ScpRememberPhraseSystem.cs @@ -1,15 +1,13 @@ using Content.Server.Actions; using Content.Server.Chat.Systems; using Content.Server.Examine; -using Content.Shared._Scp.Helpers; using Content.Shared._Scp.Other.ScpRememberPhrase; +using Content.Shared._Scp.Other.Events; using Content.Shared._Sunrise.TTS; using Content.Shared.Chat; using Content.Shared.IdentityManagement; using Robust.Shared.Random; -using Content.Shared._Scp.Other.ScpOnSoundVisibility; -using System.Linq; -using NetCord.Gateway; +using Content.Shared.Speech; namespace Content.Server._Scp.Other.ScpRememberPhrase; @@ -25,14 +23,14 @@ public override void Initialize() { base.Initialize(); - SubscribeLocalEvent(OnInit); + SubscribeLocalEvent(OnMapInit); SubscribeLocalEvent(OnShutdown); - SubscribeLocalEvent(OnSpoke); // TODO: Переделать под другой какой-то общий компонент / ивент + SubscribeLocalEvent(OnListen); SubscribeLocalEvent(OnMimic); } - private void OnInit(Entity ent, ref ComponentInit args) + private void OnMapInit(Entity ent, ref MapInitEvent args) { var actionEnt = _actionsSystem.AddAction(ent, ent.Comp.ActionProto); ent.Comp.ActionEnt = actionEnt; @@ -44,9 +42,9 @@ private void OnShutdown(Entity ent, ref ComponentShu ent.Comp.ActionEnt = null; } - public void OnSpoke(Entity ent, ref EntitySpokeEvent args) + public void OnListen(Entity ent, ref ListenEvent args) { - TryRememberPhrase(ent, args.Message); + TryRememberPhrase(ent, args.Source, args.Message); } public void OnMimic(Entity ent, ref ScpRememberPhraseActionEvent args) @@ -75,33 +73,25 @@ public void OnMimic(Entity ent, ref ScpRememberPhras /// /// Запоминание последних сказанных в округе /// - public void TryRememberPhrase(Entity ent, string message) + public void TryRememberPhrase(Entity ent, EntityUid speaker, string message) { - using var rememberSet = HashSetPoolEntity.Rent(); - _entityLookup.GetEntitiesInRange(Transform(ent).Coordinates, ent.Comp.SharePhraseRadius, rememberSet.Value, LookupFlags.Dynamic | LookupFlags.Approximate); - if (rememberSet.Value.Count == 0) - return; - string? voicePrototype = null; - if (TryComp(ent, out var ttsComponent)) + if (ent.Owner == speaker) + return; + + if (TryComp(speaker, out var ttsComponent)) voicePrototype = ttsComponent.VoicePrototypeId; - foreach (var rememberEnt in rememberSet.Value) + if (ent.Comp.RememberedMessages.Count >= ent.Comp.MaxRememberedMessages) + ent.Comp.RememberedMessages.RemoveAt(0); + + var username = Identity.Name(speaker, EntityManager); + ent.Comp.RememberedMessages.Add(new RememberedMessage { - if (!_examine.InRangeUnOccluded(ent, rememberEnt)) - continue; - - if (rememberEnt.Comp.RememberedMessages.Count >= rememberEnt.Comp.MaxRememberedMessages) - rememberEnt.Comp.RememberedMessages.RemoveAt(0); - - var username = Identity.Name(ent, EntityManager); - rememberEnt.Comp.RememberedMessages.Add(new RememberedMessage - { - Message = message, - SpeakerName = username, - TtsVoice = voicePrototype - }); - } + Message = message, + SpeakerName = username, + TtsVoice = voicePrototype + }); } } diff --git a/Content.Server/_Scp/Other/ScpSleep/ScpSleepSystem.cs b/Content.Server/_Scp/Other/ScpSleep/ScpSleepSystem.cs index 927a4f875d..a5674ac600 100644 --- a/Content.Server/_Scp/Other/ScpSleep/ScpSleepSystem.cs +++ b/Content.Server/_Scp/Other/ScpSleep/ScpSleepSystem.cs @@ -1,5 +1,6 @@ using Content.Server.Actions; using Content.Shared._Scp.Other.ScpSleep; +using Content.Shared._Scp.Other.Events; using Content.Shared.Bed.Sleep; using Content.Shared.Body.Components; using Content.Shared.Damage.Systems; @@ -22,15 +23,19 @@ public override void Initialize() { base.Initialize(); - SubscribeLocalEvent(OnInit); + SubscribeLocalEvent(OnMapInit); SubscribeLocalEvent(OnShutdown); SubscribeLocalEvent(OnSleepAction); SubscribeLocalEvent(OnMobStateChanged); SubscribeLocalEvent(OnSleepChanged); } - private void OnInit(Entity ent, ref ComponentInit args) + + private void OnMapInit(Entity ent, ref MapInitEvent args) { + if (!ent.Comp.AddAction) + return; + var actionEnt = _actionsSystem.AddAction(ent, ent.Comp.ActionProto); ent.Comp.ActionEnt = actionEnt; } @@ -43,18 +48,19 @@ private void OnShutdown(Entity ent, ref ComponentShutdown arg private void OnSleepAction(Entity ent, ref ScpSleepActionEvent args) { - args.Handled = TrySleep(ent); + args.Handled = TrySleep(ent, ent.Comp.HibernationDuration); } private void OnMobStateChanged(Entity ent, ref MobStateChangedEvent args) { - if (!ent.Comp.HibernationOnCriticalState) + if (!ent.Comp.HibernationOnHibernationState) return; - if (args.NewMobState != MobState.Critical) + if (!ent.Comp.HibernationStates.Contains(args.NewMobState)) return; - TrySleep(ent, ent.Comp.HibernationDurationOnCriticalState); + if (!TrySleep(ent, ent.Comp.HibernationDurationOnCriticalState)) + return; if (ent.Comp.CritSound == null) return; @@ -71,13 +77,13 @@ private void OnSleepChanged(Entity ent, ref SleepStateChanged else bloodstreamComponent.BloodRefreshAmount = ent.Comp.BaseBloodRefreshAmount; - Dirty(ent, bloodstreamComponent); + DirtyField(ent, bloodstreamComponent, nameof(BloodstreamComponent.BloodRefreshAmount)); } _appearanceSystem.SetData(ent, ScpSleepVisuals.Sleeping, args.FellAsleep); } - public bool TrySleep(Entity ent, float hibernationDuration = 0) + public bool TrySleep(Entity ent, TimeSpan hibernationDuration) { if (HasComp(ent)) return false; @@ -85,8 +91,7 @@ public bool TrySleep(Entity ent, float hibernationDuration = if (!_sleepingSystem.TrySleeping(ent.Owner)) return false; - hibernationDuration = hibernationDuration == 0 ? ent.Comp.HibernationDuration : hibernationDuration; - _statusEffects.TryAddStatusEffectDuration(ent, ent.Comp.StatusEffect, TimeSpan.FromSeconds(hibernationDuration)); + _statusEffects.TryAddStatusEffectDuration(ent, ent.Comp.StatusEffect, hibernationDuration); return true; } diff --git a/Content.Server/_Scp/Research/Artifacts/Effects/_ScpSpecific/Scp939/Sleep/ArtifactScp939SleepComponent.cs b/Content.Server/_Scp/Research/Artifacts/Effects/_ScpSpecific/Scp939/Sleep/ArtifactScp939SleepComponent.cs index 10afd8777d..062e7a5dba 100644 --- a/Content.Server/_Scp/Research/Artifacts/Effects/_ScpSpecific/Scp939/Sleep/ArtifactScp939SleepComponent.cs +++ b/Content.Server/_Scp/Research/Artifacts/Effects/_ScpSpecific/Scp939/Sleep/ArtifactScp939SleepComponent.cs @@ -4,8 +4,8 @@ public sealed partial class ArtifactScp939SleepComponent : Component { [DataField] - public float MinSleepTime = 20f; + public TimeSpan MinSleepTime = TimeSpan.FromSeconds(20); [DataField] - public float MaxSleepTime = 80f; + public TimeSpan MaxSleepTime = TimeSpan.FromSeconds(80); } diff --git a/Content.Server/_Scp/Research/Artifacts/Effects/_ScpSpecific/Scp939/Sleep/ArtifactScp939SleepSystem.cs b/Content.Server/_Scp/Research/Artifacts/Effects/_ScpSpecific/Scp939/Sleep/ArtifactScp939SleepSystem.cs index d3f89b5ae3..8b8a19f9d5 100644 --- a/Content.Server/_Scp/Research/Artifacts/Effects/_ScpSpecific/Scp939/Sleep/ArtifactScp939SleepSystem.cs +++ b/Content.Server/_Scp/Research/Artifacts/Effects/_ScpSpecific/Scp939/Sleep/ArtifactScp939SleepSystem.cs @@ -16,7 +16,7 @@ protected override void OnActivated(Entity ent, re if (!TryComp(ent, out var scpSleepComponent)) return; - var time = _random.NextFloat(ent.Comp.MinSleepTime, ent.Comp.MaxSleepTime); + var time = _random.Next(ent.Comp.MinSleepTime, ent.Comp.MaxSleepTime); _scpSleepSystem.TrySleep((ent, scpSleepComponent), time); } diff --git a/Content.Shared/_Scp/Other/Events/ScpSystemsEvents.cs b/Content.Shared/_Scp/Other/Events/ScpSystemsEvents.cs new file mode 100644 index 0000000000..85a428bb20 --- /dev/null +++ b/Content.Shared/_Scp/Other/Events/ScpSystemsEvents.cs @@ -0,0 +1,12 @@ +using Content.Shared.Actions; + +namespace Content.Shared._Scp.Other.Events; + +[ByRefEvent] +public sealed class ScpReleaseGasActionAttemptEvent : CancellableEntityEventArgs; + +public sealed partial class ScpReleaseGasActionEvent : InstantActionEvent; + +public sealed partial class ScpRememberPhraseActionEvent : InstantActionEvent; + +public sealed partial class ScpSleepActionEvent : InstantActionEvent; diff --git a/Content.Shared/_Scp/Other/ScpOnSoundVisibility/ActiveScpOnSoundVisibilityComponent.cs b/Content.Shared/_Scp/Other/ScpOnSoundVisibility/ActiveScpOnSoundVisibilityComponent.cs index a92db1f245..40862386aa 100644 --- a/Content.Shared/_Scp/Other/ScpOnSoundVisibility/ActiveScpOnSoundVisibilityComponent.cs +++ b/Content.Shared/_Scp/Other/ScpOnSoundVisibility/ActiveScpOnSoundVisibilityComponent.cs @@ -8,18 +8,15 @@ public sealed partial class ActiveScpOnSoundVisibilityComponent : Component [ViewVariables] public float VisibilityAcc = ScpOnSoundVisibilityComponent.InitialVisibilityAcc; - [AutoNetworkedField] - public uint VisibilityResetCounter; - [DataField, AutoNetworkedField] public float HideTime = ScpOnSoundVisibilityComponent.DefaultHideTime; [DataField, AutoNetworkedField] - public int MinValue = ScpOnSoundVisibilityComponent.DefaultMinValue; + public float MinValue = ScpOnSoundVisibilityComponent.DefaultMinValue; [DataField, AutoNetworkedField] - public int MaxValue = ScpOnSoundVisibilityComponent.DefaultMaxValue; + public float MaxValue = ScpOnSoundVisibilityComponent.DefaultMaxValue; - [NonSerialized] - public uint LastHandledVisibilityResetCounter; + [ViewVariables] + public bool OnCollide; } diff --git a/Content.Shared/_Scp/Other/ScpOnSoundVisibility/ActiveScpPoorEyesightComponent.cs b/Content.Shared/_Scp/Other/ScpOnSoundVisibility/ActiveScpPoorEyesightComponent.cs new file mode 100644 index 0000000000..cc525f0353 --- /dev/null +++ b/Content.Shared/_Scp/Other/ScpOnSoundVisibility/ActiveScpPoorEyesightComponent.cs @@ -0,0 +1,5 @@ + +namespace Content.Shared._Scp.Other.ScpOnSoundVisibility; + +[RegisterComponent] +public sealed partial class ActiveScpPoorEyesightComponent : Component; diff --git a/Content.Shared/_Scp/Other/ScpOnSoundVisibility/ScpOnSoundVisibilityComponent.cs b/Content.Shared/_Scp/Other/ScpOnSoundVisibility/ScpOnSoundVisibilityComponent.cs index 6226808c62..da6ce4671b 100644 --- a/Content.Shared/_Scp/Other/ScpOnSoundVisibility/ScpOnSoundVisibilityComponent.cs +++ b/Content.Shared/_Scp/Other/ScpOnSoundVisibility/ScpOnSoundVisibilityComponent.cs @@ -5,17 +5,17 @@ public sealed partial class ScpOnSoundVisibilityComponent : Component { public const float InitialVisibilityAcc = 0.001f; public const float DefaultHideTime = 2.5f; - public const int DefaultMinValue = 40; - public const int DefaultMaxValue = 400; + public const float DefaultMinValue = 40f; + public const float DefaultMaxValue = 400f; [DataField] public float HideTime = DefaultHideTime; [DataField] - public int MinValue = DefaultMinValue; + public float MinValue = DefaultMinValue; [DataField] - public int MaxValue = DefaultMaxValue; + public float MaxValue = DefaultMaxValue; [DataField] public float SharePhraseRadius = 16f; diff --git a/Content.Shared/_Scp/Other/ScpOnSoundVisibility/ScpOnSoundVisibilityProtectionComponent.cs b/Content.Shared/_Scp/Other/ScpOnSoundVisibility/ScpOnSoundVisibilityProtectionComponent.cs index 4465a75e8f..b3c8250f78 100644 --- a/Content.Shared/_Scp/Other/ScpOnSoundVisibility/ScpOnSoundVisibilityProtectionComponent.cs +++ b/Content.Shared/_Scp/Other/ScpOnSoundVisibility/ScpOnSoundVisibilityProtectionComponent.cs @@ -4,7 +4,4 @@ namespace Content.Shared._Scp.Other.ScpOnSoundVisibility; [RegisterComponent, NetworkedComponent] -public sealed partial class ScpOnSoundVisibilityProtectionComponent : Component -{ - -} +public sealed partial class ScpOnSoundVisibilityProtectionComponent : Component; diff --git a/Content.Shared/_Scp/Other/ScpOnSoundVisibility/ScpOnSoundVisibilityViewerComponent.cs b/Content.Shared/_Scp/Other/ScpOnSoundVisibility/ScpOnSoundVisibilityViewerComponent.cs index 88a6cae6c4..18a445cb2e 100644 --- a/Content.Shared/_Scp/Other/ScpOnSoundVisibility/ScpOnSoundVisibilityViewerComponent.cs +++ b/Content.Shared/_Scp/Other/ScpOnSoundVisibility/ScpOnSoundVisibilityViewerComponent.cs @@ -4,28 +4,28 @@ namespace Content.Shared._Scp.Other.ScpOnSoundVisibility; -[RegisterComponent, NetworkedComponent, AutoGenerateComponentState] +[RegisterComponent, NetworkedComponent, AutoGenerateComponentState(fieldDeltas: true)] public sealed partial class ScpOnSoundVisibilityViewerComponent : Component { - [DataField, AutoNetworkedField] - public bool PoorEyesOnFlash = false; + [DataField] + public bool PoorEyesOnFlash; [DataField, AutoNetworkedField] public bool PoorEyesight; - [DataField, AutoNetworkedField] + [DataField] public float PoorEyesightTime = 10f; // Секунды - [AutoNetworkedField] + [ViewVariables, AutoNetworkedField] public TimeSpan? PoorEyesightTimeStart; // Когда начали плохо видеть [DataField, ViewVariables(VVAccess.ReadWrite)] public float VisibilityActivationRange = 20f; - [DataField, AutoNetworkedField] + [DataField] public LocId? OnFlashMessage; - [DataField, AutoNetworkedField] + [DataField] public EntityWhitelist Protections = new() { Components = new[] @@ -33,4 +33,10 @@ public sealed partial class ScpOnSoundVisibilityViewerComponent : Component "ScpOnSoundVisibilityProtection" } }; + + [DataField] + public float ExamineHideThreshold = 0.2f; + + [DataField] + public float StatusIconClearThreshold = 0.5f; } diff --git a/Content.Shared/_Scp/Other/ScpReleaseGas/ScpReleaseGasActionEvent.cs b/Content.Shared/_Scp/Other/ScpReleaseGas/ScpReleaseGasActionEvent.cs deleted file mode 100644 index c05bf19f41..0000000000 --- a/Content.Shared/_Scp/Other/ScpReleaseGas/ScpReleaseGasActionEvent.cs +++ /dev/null @@ -1,5 +0,0 @@ -using Content.Shared.Actions; - -namespace Content.Shared._Scp.Other.ScpReleaseGas; - -public sealed partial class ScpReleaseGasActionEvent : InstantActionEvent; diff --git a/Content.Shared/_Scp/Other/ScpReleaseGas/ScpReleaseGasComponent.cs b/Content.Shared/_Scp/Other/ScpReleaseGas/ScpReleaseGasComponent.cs index 98834fb4d6..ced9d51e5b 100644 --- a/Content.Shared/_Scp/Other/ScpReleaseGas/ScpReleaseGasComponent.cs +++ b/Content.Shared/_Scp/Other/ScpReleaseGas/ScpReleaseGasComponent.cs @@ -1,6 +1,5 @@ using Content.Shared.Actions.Components; -using Content.Shared.Chemistry.Components; using Robust.Shared.GameStates; using Robust.Shared.Prototypes; @@ -13,16 +12,10 @@ public sealed partial class ScpReleaseGasComponent : Component public EntProtoId ActionProto = "ScpSmokeAction"; [DataField] - public Solution SmokeSolution = new("АМН-С227", 40); + public EntProtoId SmokeProtoId = "Smoke"; [DataField] - public float SmokeDuration = 30.0f; - - [DataField] - public int SmokeSpreadRadius = 10; - - [DataField] - public EntProtoId SmokeProtoId = "АМН-С227Smoke"; + public List KeysOut = new() { "scpSmokeTrigger" }; [ViewVariables] public EntityUid? ActionEnt; diff --git a/Content.Shared/_Scp/Other/ScpRememberPhrase/ScpRememberPhraseActionEvent.cs b/Content.Shared/_Scp/Other/ScpRememberPhrase/ScpRememberPhraseActionEvent.cs deleted file mode 100644 index efde67e5fe..0000000000 --- a/Content.Shared/_Scp/Other/ScpRememberPhrase/ScpRememberPhraseActionEvent.cs +++ /dev/null @@ -1,5 +0,0 @@ -using Content.Shared.Actions; - -namespace Content.Shared._Scp.Other.ScpRememberPhrase; - -public sealed partial class ScpRememberPhraseActionEvent : InstantActionEvent; diff --git a/Content.Shared/_Scp/Other/ScpRememberPhrase/ScpRememberPhraseComponent.cs b/Content.Shared/_Scp/Other/ScpRememberPhrase/ScpRememberPhraseComponent.cs index bf216292e7..02be97288c 100644 --- a/Content.Shared/_Scp/Other/ScpRememberPhrase/ScpRememberPhraseComponent.cs +++ b/Content.Shared/_Scp/Other/ScpRememberPhrase/ScpRememberPhraseComponent.cs @@ -24,8 +24,7 @@ public sealed partial class ScpRememberPhraseComponent : Component public EntityUid? ActionEnt; } -[DataDefinition] -public partial struct RememberedMessage +public partial record struct RememberedMessage { public string Message; public string SpeakerName; diff --git a/Content.Shared/_Scp/Other/ScpSleep/ScpSleepActionEvent.cs b/Content.Shared/_Scp/Other/ScpSleep/ScpSleepActionEvent.cs deleted file mode 100644 index 3d2cedea74..0000000000 --- a/Content.Shared/_Scp/Other/ScpSleep/ScpSleepActionEvent.cs +++ /dev/null @@ -1,5 +0,0 @@ -using Content.Shared.Actions; - -namespace Content.Shared._Scp.Other.ScpSleep; - -public sealed partial class ScpSleepActionEvent : InstantActionEvent; diff --git a/Content.Shared/_Scp/Other/ScpSleep/ScpSleepComponent.cs b/Content.Shared/_Scp/Other/ScpSleep/ScpSleepComponent.cs index 6d4c01a764..f94d512095 100644 --- a/Content.Shared/_Scp/Other/ScpSleep/ScpSleepComponent.cs +++ b/Content.Shared/_Scp/Other/ScpSleep/ScpSleepComponent.cs @@ -1,6 +1,7 @@ using Content.Shared.Actions.Components; using Content.Shared.Damage; +using Content.Shared.Mobs; using Robust.Shared.Audio; using Robust.Shared.GameStates; using Robust.Shared.Prototypes; @@ -13,6 +14,9 @@ public sealed partial class ScpSleepComponent : Component [DataField] public EntProtoId ActionProto = "ScpSleepAction"; + [DataField] + public bool AddAction = true; + [DataField] public EntProtoId StatusEffect = "StatusEffectForcedSleeping"; @@ -20,17 +24,19 @@ public sealed partial class ScpSleepComponent : Component public SoundSpecifier? CritSound; [DataField] - public float HibernationDurationOnCriticalState = 360f; + public TimeSpan HibernationDurationOnCriticalState = TimeSpan.FromSeconds(360); [DataField] - public float HibernationDuration = 60f; + public TimeSpan HibernationDuration = TimeSpan.FromSeconds(60); [DataField] - public bool HibernationOnCriticalState = false; + public bool HibernationOnHibernationState; + [DataField] + public List HibernationStates = new() { MobState.Critical }; [DataField] - public bool HibernationHealing = false; + public bool HibernationHealing; [DataField] public DamageSpecifier HibernationHealingRate = new() diff --git a/Resources/Prototypes/Entities/Mobs/base.yml b/Resources/Prototypes/Entities/Mobs/base.yml index 99001664f5..9c0d4971d0 100644 --- a/Resources/Prototypes/Entities/Mobs/base.yml +++ b/Resources/Prototypes/Entities/Mobs/base.yml @@ -108,6 +108,7 @@ - type: MobMover - type: MovementSpeedModifier - type: LagCompensation + - type: ScpOnSoundVisibility # Fire Station edit - type: entity save: false diff --git a/Resources/Prototypes/_Scp/Entities/Mobs/Player/Scp/Main/scp939.yml b/Resources/Prototypes/_Scp/Entities/Mobs/Player/Scp/Main/scp939.yml index fe40a4f364..7245c09a11 100644 --- a/Resources/Prototypes/_Scp/Entities/Mobs/Player/Scp/Main/scp939.yml +++ b/Resources/Prototypes/_Scp/Entities/Mobs/Player/Scp/Main/scp939.yml @@ -17,8 +17,20 @@ drawdepth: Mobs sprite: _Scp/Mobs/Scp/scp-939.rsi layers: - - map: [ "enum.ScpSleepLayers.Base" ] + - map: [ "base" ] state: alive + - type: GenericVisualizer + visuals: + enum.MobStateVisuals.State: + base: + Dead: { state: dead } + Critical: { state: dead } + Alive: { state: alive } + enum.ScpSleepVisuals.Sleeping: + base: + True: { state: asleep } + False: { state: alive } + - type: ActiveListener - type: Scp class: Keter - type: ScpOnSoundVisibilityViewer @@ -35,6 +47,15 @@ actionProto: Scp939Mimic - type: ScpReleaseGas actionProto: Scp939Smoke + - type: SmokeOnTrigger + keysIn: + - scpSmokeTrigger + solution: + reagents: + - ReagentId: АМН-С227 + Quantity: 40 + spreadAmount: 10 + smokePrototype: АМН-С227Smoke - type: MeleeWeapon altDisarm: false soundHit: From 834c45cab62254ecf9cf61d9c8b8106811e3ded8 Mon Sep 17 00:00:00 2001 From: Wardex Date: Thu, 30 Apr 2026 21:01:10 +0300 Subject: [PATCH 08/19] =?UTF-8?q?=D0=BE=D0=B9=D0=B5,=20=D0=BE=D1=81=D1=82?= =?UTF-8?q?=D0=B0=D1=82=D0=BA=D0=B8?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../ScpOnSoundVisibility/ScpOnSoundVisibilityHudSystem.cs | 7 ------- 1 file changed, 7 deletions(-) diff --git a/Content.Client/_Scp/Other/ScpOnSoundVisibility/ScpOnSoundVisibilityHudSystem.cs b/Content.Client/_Scp/Other/ScpOnSoundVisibility/ScpOnSoundVisibilityHudSystem.cs index eddbaac4ba..3d3704ab7c 100644 --- a/Content.Client/_Scp/Other/ScpOnSoundVisibility/ScpOnSoundVisibilityHudSystem.cs +++ b/Content.Client/_Scp/Other/ScpOnSoundVisibility/ScpOnSoundVisibilityHudSystem.cs @@ -133,13 +133,6 @@ public override void Update(float frameTime) if (!IsActive) return; - /*_lastUpdateTime += _timing.RealTime; - if (_lastUpdateTime < UpdateInterval) - return; - - var delta = _lastUpdateTime; - _lastUpdateTime = 0f;*/ - if (_timing.RealTime < _nextUpdateTime) return; From 61a66bc7642a1b32fc8e497f571d18661bf2f656 Mon Sep 17 00:00:00 2001 From: Wardex Date: Thu, 30 Apr 2026 21:03:49 +0300 Subject: [PATCH 09/19] =?UTF-8?q?=D0=92=D0=B8=D1=88=D0=B8=D0=BD=D0=BA?= =?UTF-8?q?=D0=B0=20=D0=BD=D0=B0=20=D1=82=D0=BE=D1=80=D1=82=D0=B5?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../ScpOnSoundVisibility/ScpOnSoundVisibilitySystem.cs | 6 +++--- .../_Scp/Other/ScpReleaseGas/ScpReleaseGasSystem.cs | 3 ++- Content.Server/_Scp/Other/ScpSleep/ScpSleepSystem.cs | 6 +++--- 3 files changed, 8 insertions(+), 7 deletions(-) diff --git a/Content.Server/_Scp/Other/ScpOnSoundVisibility/ScpOnSoundVisibilitySystem.cs b/Content.Server/_Scp/Other/ScpOnSoundVisibility/ScpOnSoundVisibilitySystem.cs index 43093177e7..265c6d1957 100644 --- a/Content.Server/_Scp/Other/ScpOnSoundVisibility/ScpOnSoundVisibilitySystem.cs +++ b/Content.Server/_Scp/Other/ScpOnSoundVisibility/ScpOnSoundVisibilitySystem.cs @@ -31,7 +31,7 @@ public override void Initialize() _activeQuery = GetEntityQuery(); } - private void OnFlash(Entity ent, ref AfterFlashedEvent args) + public void OnFlash(Entity ent, ref AfterFlashedEvent args) { if (!ent.Comp.PoorEyesOnFlash) return; @@ -83,7 +83,7 @@ public override void Update(float frameTime) } } - private void UpdateVisibilityTargets() + public void UpdateVisibilityTargets() { if (_timing.CurTime < _nextVisibilityRefresh) return; @@ -133,7 +133,7 @@ private void UpdateVisibilityTargets() _visibilityRemovalQueue.Clear(); } - private void EnsureActiveVisibility(Entity ent) + public void EnsureActiveVisibility(Entity ent) { if (!_activeQuery.TryComp(ent, out var active)) { diff --git a/Content.Server/_Scp/Other/ScpReleaseGas/ScpReleaseGasSystem.cs b/Content.Server/_Scp/Other/ScpReleaseGas/ScpReleaseGasSystem.cs index 55756fdd66..1b7f4cb3a0 100644 --- a/Content.Server/_Scp/Other/ScpReleaseGas/ScpReleaseGasSystem.cs +++ b/Content.Server/_Scp/Other/ScpReleaseGas/ScpReleaseGasSystem.cs @@ -19,6 +19,7 @@ public override void Initialize() SubscribeLocalEvent(OnMapInit); SubscribeLocalEvent(OnShutdown); + SubscribeLocalEvent(OnGasActionAttempt); SubscribeLocalEvent(OnGasAction); } @@ -44,7 +45,7 @@ public void OnGasActionAttempt(Entity ent, ref ScpReleas } } - private void OnGasAction(Entity ent, ref ScpReleaseGasActionEvent args) + public void OnGasAction(Entity ent, ref ScpReleaseGasActionEvent args) { var ev = new ScpReleaseGasActionAttemptEvent(); RaiseLocalEvent(ent, ref ev); diff --git a/Content.Server/_Scp/Other/ScpSleep/ScpSleepSystem.cs b/Content.Server/_Scp/Other/ScpSleep/ScpSleepSystem.cs index a5674ac600..345400cdb6 100644 --- a/Content.Server/_Scp/Other/ScpSleep/ScpSleepSystem.cs +++ b/Content.Server/_Scp/Other/ScpSleep/ScpSleepSystem.cs @@ -46,12 +46,12 @@ private void OnShutdown(Entity ent, ref ComponentShutdown arg ent.Comp.ActionEnt = null; } - private void OnSleepAction(Entity ent, ref ScpSleepActionEvent args) + public void OnSleepAction(Entity ent, ref ScpSleepActionEvent args) { args.Handled = TrySleep(ent, ent.Comp.HibernationDuration); } - private void OnMobStateChanged(Entity ent, ref MobStateChangedEvent args) + public void OnMobStateChanged(Entity ent, ref MobStateChangedEvent args) { if (!ent.Comp.HibernationOnHibernationState) return; @@ -68,7 +68,7 @@ private void OnMobStateChanged(Entity ent, ref MobStateChange _audio.PlayPvs(ent.Comp.CritSound, ent); } - private void OnSleepChanged(Entity ent, ref SleepStateChangedEvent args) + public void OnSleepChanged(Entity ent, ref SleepStateChangedEvent args) { if (TryComp(ent, out var bloodstreamComponent)) { From 300c5c4f147d6a89bb922523bc77c5a110ae7c44 Mon Sep 17 00:00:00 2001 From: Wardex Date: Thu, 30 Apr 2026 22:12:22 +0300 Subject: [PATCH 10/19] =?UTF-8?q?=D0=B4=D0=B0=20=D1=91=D0=BF?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- Content.Server/_Scp/Other/ScpSleep/ScpSleepSystem.cs | 2 +- Content.Shared/_Scp/Other/ScpSleep/ScpSleepComponent.cs | 2 +- Content.Shared/_Scp/Scp939/Scp939Component.cs | 9 +++++++++ .../_Scp/Entities/Mobs/Player/Scp/Main/scp939.yml | 3 ++- 4 files changed, 13 insertions(+), 3 deletions(-) create mode 100644 Content.Shared/_Scp/Scp939/Scp939Component.cs diff --git a/Content.Server/_Scp/Other/ScpSleep/ScpSleepSystem.cs b/Content.Server/_Scp/Other/ScpSleep/ScpSleepSystem.cs index 345400cdb6..382cbff099 100644 --- a/Content.Server/_Scp/Other/ScpSleep/ScpSleepSystem.cs +++ b/Content.Server/_Scp/Other/ScpSleep/ScpSleepSystem.cs @@ -59,7 +59,7 @@ public void OnMobStateChanged(Entity ent, ref MobStateChanged if (!ent.Comp.HibernationStates.Contains(args.NewMobState)) return; - if (!TrySleep(ent, ent.Comp.HibernationDurationOnCriticalState)) + if (!TrySleep(ent, ent.Comp.HibernationDurationOnHibernationState)) return; if (ent.Comp.CritSound == null) diff --git a/Content.Shared/_Scp/Other/ScpSleep/ScpSleepComponent.cs b/Content.Shared/_Scp/Other/ScpSleep/ScpSleepComponent.cs index f94d512095..6ca25606f4 100644 --- a/Content.Shared/_Scp/Other/ScpSleep/ScpSleepComponent.cs +++ b/Content.Shared/_Scp/Other/ScpSleep/ScpSleepComponent.cs @@ -24,7 +24,7 @@ public sealed partial class ScpSleepComponent : Component public SoundSpecifier? CritSound; [DataField] - public TimeSpan HibernationDurationOnCriticalState = TimeSpan.FromSeconds(360); + public TimeSpan HibernationDurationOnHibernationState = TimeSpan.FromSeconds(360); [DataField] public TimeSpan HibernationDuration = TimeSpan.FromSeconds(60); diff --git a/Content.Shared/_Scp/Scp939/Scp939Component.cs b/Content.Shared/_Scp/Scp939/Scp939Component.cs new file mode 100644 index 0000000000..a188f25486 --- /dev/null +++ b/Content.Shared/_Scp/Scp939/Scp939Component.cs @@ -0,0 +1,9 @@ +using Robust.Shared.GameStates; + +namespace Content.Shared._Scp.Scp939; + +/// +/// Пометочный компонент для вайт-листов +/// +[RegisterComponent, NetworkedComponent] +public sealed partial class Scp939Component : Component; diff --git a/Resources/Prototypes/_Scp/Entities/Mobs/Player/Scp/Main/scp939.yml b/Resources/Prototypes/_Scp/Entities/Mobs/Player/Scp/Main/scp939.yml index 7245c09a11..849eb9847c 100644 --- a/Resources/Prototypes/_Scp/Entities/Mobs/Player/Scp/Main/scp939.yml +++ b/Resources/Prototypes/_Scp/Entities/Mobs/Player/Scp/Main/scp939.yml @@ -33,6 +33,7 @@ - type: ActiveListener - type: Scp class: Keter + - type: Scp939 - type: ScpOnSoundVisibilityViewer poorEyesOnFlash: true onFlashMessage: scp939-flashed @@ -42,7 +43,7 @@ - type: ScpSleep actionProto: Scp939Sleep hibernationHealing: true - hibernationOnCriticalState: true + hibernationOnHibernationState: true - type: ScpRememberPhrase actionProto: Scp939Mimic - type: ScpReleaseGas From 1d450bcaae13ed6f31c7142239b7c62d0a041f85 Mon Sep 17 00:00:00 2001 From: Wardex Date: Fri, 1 May 2026 17:23:09 +0300 Subject: [PATCH 11/19] =?UTF-8?q?=D0=B1=D1=80=D1=83=D1=83=D1=83=D0=BC?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../ScpOnSoundVisibilityHudSystem.cs | 31 ++++++++++++++----- .../ScpOnSoundVisibilitySystem.cs | 7 ++--- .../ScpReleaseGas/ScpReleaseGasSystem.cs | 4 +-- .../ScpRememberPhraseSystem.cs | 10 +++--- .../_Scp/Other/ScpSleep/ScpSleepSystem.cs | 13 ++++---- .../_Scp/Other/ScpSleep/ScpSleepComponent.cs | 28 ++--------------- .../Entities/Mobs/Player/Scp/Main/scp939.yml | 23 +++++++++++--- 7 files changed, 60 insertions(+), 56 deletions(-) diff --git a/Content.Client/_Scp/Other/ScpOnSoundVisibility/ScpOnSoundVisibilityHudSystem.cs b/Content.Client/_Scp/Other/ScpOnSoundVisibility/ScpOnSoundVisibilityHudSystem.cs index 3d3704ab7c..2fd99b4dc5 100644 --- a/Content.Client/_Scp/Other/ScpOnSoundVisibility/ScpOnSoundVisibilityHudSystem.cs +++ b/Content.Client/_Scp/Other/ScpOnSoundVisibility/ScpOnSoundVisibilityHudSystem.cs @@ -86,7 +86,7 @@ private void OnExamine(Entity ent, ref Exam if (!IsActive) return; - if (!TryGetLocalViewer(out var viewerComp)) + if (!TryGetLocalViewer(out _, out var viewerComp)) return; var visibility = GetVisibility(ent); @@ -100,7 +100,7 @@ private void OnGetStatusIcons(Entity ent, r if (!IsActive) return; - if (!TryGetLocalViewer(out var viewerComp)) + if (!TryGetLocalViewer(out _, out var viewerComp)) return; var visibility = GetVisibility(ent); @@ -136,14 +136,20 @@ public override void Update(float frameTime) if (_timing.RealTime < _nextUpdateTime) return; + if (!TryGetLocalViewer(out var viewer, out var _)) + return; + var delta = (float)(_timing.RealTime - (_nextUpdateTime - UpdateInterval)).TotalSeconds; _nextUpdateTime = _timing.RealTime + UpdateInterval; var query = EntityQueryEnumerator(); - while (query.MoveNext(out _, out var visibilityComponent)) + while (query.MoveNext(out var visibilityEnt, out var visibilityComponent)) { if (visibilityComponent.OnCollide) - return; + continue; + + if (viewer == visibilityEnt) + continue; if (visibilityComponent.VisibilityAcc >= visibilityComponent.HideTime) continue; @@ -194,7 +200,13 @@ private void OnMove(Entity ent, ref MoveEve if (!IsActive) return; - if (!TryGetLocalViewer(out var viewerComp)) + if (!TryGetLocalViewer(out var viewer, out var viewerComp)) + return; + + if (ent.Comp.OnCollide) + return; + + if (ent == viewer) return; // В зависимости от наличие защит или проблем со зрением изменяется то, насколько хорошо мы видим жертву @@ -202,7 +214,7 @@ private void OnMove(Entity ent, ref MoveEve { ent.Comp.VisibilityAcc *= modifier; } - else if (!_whitelist.IsWhitelistPass(viewerComp.Protections, ent)) // Если имеется защита(тихое хождение) + else if (_whitelist.IsWhitelistPass(viewerComp.Protections, ent)) // Если имеется защита(тихое хождение) { return; } @@ -284,7 +296,7 @@ private bool ModifyAcc(Entity ent, out floa // 1 = отсутствие модификатора modifier = 1f; - if (!TryGetLocalViewer(out var viewerComp)) + if (!TryGetLocalViewer(out _, out var viewerComp)) return false; if (!viewerComp.PoorEyesight) @@ -295,8 +307,9 @@ private bool ModifyAcc(Entity ent, out floa return true; } - private bool TryGetLocalViewer([NotNullWhen(true)] out ScpOnSoundVisibilityViewerComponent? viewerComp) + private bool TryGetLocalViewer([NotNullWhen(true)] out EntityUid? viewer, [NotNullWhen(true)] out ScpOnSoundVisibilityViewerComponent? viewerComp) { + viewer = null; viewerComp = null; var localPlayer = _playerManager.LocalEntity; @@ -306,7 +319,9 @@ private bool TryGetLocalViewer([NotNullWhen(true)] out ScpOnSoundVisibilityViewe if (!_viewerQuery.TryComp(localPlayer, out var comp)) return false; + viewer = localPlayer; viewerComp = comp; + return true; } } diff --git a/Content.Server/_Scp/Other/ScpOnSoundVisibility/ScpOnSoundVisibilitySystem.cs b/Content.Server/_Scp/Other/ScpOnSoundVisibility/ScpOnSoundVisibilitySystem.cs index 265c6d1957..a702d7e107 100644 --- a/Content.Server/_Scp/Other/ScpOnSoundVisibility/ScpOnSoundVisibilitySystem.cs +++ b/Content.Server/_Scp/Other/ScpOnSoundVisibility/ScpOnSoundVisibilitySystem.cs @@ -31,7 +31,7 @@ public override void Initialize() _activeQuery = GetEntityQuery(); } - public void OnFlash(Entity ent, ref AfterFlashedEvent args) + private void OnFlash(Entity ent, ref AfterFlashedEvent args) { if (!ent.Comp.PoorEyesOnFlash) return; @@ -92,7 +92,7 @@ public void UpdateVisibilityTargets() _visibilityActiveTargets.Clear(); var scpQuery = EntityQueryEnumerator(); - while (scpQuery.MoveNext(out var uid, out var viewer, out var xform)) + while (scpQuery.MoveNext(out var _, out var viewer, out var xform)) { if (xform.MapID == MapId.Nullspace) continue; @@ -105,9 +105,6 @@ public void UpdateVisibilityTargets() foreach (var target in _visibilityCandidates) { - if (target.Owner == uid) - continue; - _visibilityActiveTargets.Add(target); EnsureActiveVisibility(target); } diff --git a/Content.Server/_Scp/Other/ScpReleaseGas/ScpReleaseGasSystem.cs b/Content.Server/_Scp/Other/ScpReleaseGas/ScpReleaseGasSystem.cs index 1b7f4cb3a0..a6987553d2 100644 --- a/Content.Server/_Scp/Other/ScpReleaseGas/ScpReleaseGasSystem.cs +++ b/Content.Server/_Scp/Other/ScpReleaseGas/ScpReleaseGasSystem.cs @@ -35,7 +35,7 @@ private void OnShutdown(Entity ent, ref ComponentShutdow ent.Comp.ActionEnt = null; } - public void OnGasActionAttempt(Entity ent, ref ScpReleaseGasActionAttemptEvent args) + private void OnGasActionAttempt(Entity ent, ref ScpReleaseGasActionAttemptEvent args) { if (_scpMask.TryGetScpMask(ent, out var scpMask)) { @@ -45,7 +45,7 @@ public void OnGasActionAttempt(Entity ent, ref ScpReleas } } - public void OnGasAction(Entity ent, ref ScpReleaseGasActionEvent args) + private void OnGasAction(Entity ent, ref ScpReleaseGasActionEvent args) { var ev = new ScpReleaseGasActionAttemptEvent(); RaiseLocalEvent(ent, ref ev); diff --git a/Content.Server/_Scp/Other/ScpRememberPhrase/ScpRememberPhraseSystem.cs b/Content.Server/_Scp/Other/ScpRememberPhrase/ScpRememberPhraseSystem.cs index b6dbc3de48..15db81ffc5 100644 --- a/Content.Server/_Scp/Other/ScpRememberPhrase/ScpRememberPhraseSystem.cs +++ b/Content.Server/_Scp/Other/ScpRememberPhrase/ScpRememberPhraseSystem.cs @@ -1,6 +1,5 @@ using Content.Server.Actions; using Content.Server.Chat.Systems; -using Content.Server.Examine; using Content.Shared._Scp.Other.ScpRememberPhrase; using Content.Shared._Scp.Other.Events; using Content.Shared._Sunrise.TTS; @@ -8,15 +7,14 @@ using Content.Shared.IdentityManagement; using Robust.Shared.Random; using Content.Shared.Speech; +using Content.Shared.Speech.Components; namespace Content.Server._Scp.Other.ScpRememberPhrase; public sealed class ScpRememberPhraseSystem : EntitySystem { [Dependency] private readonly ActionsSystem _actionsSystem = default!; - [Dependency] private readonly ExamineSystem _examine = default!; [Dependency] private readonly ChatSystem _chat = default!; - [Dependency] private readonly EntityLookupSystem _entityLookup = default!; [Dependency] private readonly IRobustRandom _random = default!; public override void Initialize() @@ -34,6 +32,8 @@ private void OnMapInit(Entity ent, ref MapInitEvent { var actionEnt = _actionsSystem.AddAction(ent, ent.Comp.ActionProto); ent.Comp.ActionEnt = actionEnt; + + EnsureComp(ent); } private void OnShutdown(Entity ent, ref ComponentShutdown args) @@ -42,12 +42,12 @@ private void OnShutdown(Entity ent, ref ComponentShu ent.Comp.ActionEnt = null; } - public void OnListen(Entity ent, ref ListenEvent args) + private void OnListen(Entity ent, ref ListenEvent args) { TryRememberPhrase(ent, args.Source, args.Message); } - public void OnMimic(Entity ent, ref ScpRememberPhraseActionEvent args) + private void OnMimic(Entity ent, ref ScpRememberPhraseActionEvent args) { if (ent.Comp.RememberedMessages.Count == 0) return; diff --git a/Content.Server/_Scp/Other/ScpSleep/ScpSleepSystem.cs b/Content.Server/_Scp/Other/ScpSleep/ScpSleepSystem.cs index 382cbff099..7ce5813f14 100644 --- a/Content.Server/_Scp/Other/ScpSleep/ScpSleepSystem.cs +++ b/Content.Server/_Scp/Other/ScpSleep/ScpSleepSystem.cs @@ -46,14 +46,14 @@ private void OnShutdown(Entity ent, ref ComponentShutdown arg ent.Comp.ActionEnt = null; } - public void OnSleepAction(Entity ent, ref ScpSleepActionEvent args) + private void OnSleepAction(Entity ent, ref ScpSleepActionEvent args) { args.Handled = TrySleep(ent, ent.Comp.HibernationDuration); } - public void OnMobStateChanged(Entity ent, ref MobStateChangedEvent args) + private void OnMobStateChanged(Entity ent, ref MobStateChangedEvent args) { - if (!ent.Comp.HibernationOnHibernationState) + if (ent.Comp.HibernationStates == null) return; if (!ent.Comp.HibernationStates.Contains(args.NewMobState)) @@ -68,7 +68,7 @@ public void OnMobStateChanged(Entity ent, ref MobStateChanged _audio.PlayPvs(ent.Comp.CritSound, ent); } - public void OnSleepChanged(Entity ent, ref SleepStateChangedEvent args) + private void OnSleepChanged(Entity ent, ref SleepStateChangedEvent args) { if (TryComp(ent, out var bloodstreamComponent)) { @@ -91,7 +91,8 @@ public bool TrySleep(Entity ent, TimeSpan hibernationDuration if (!_sleepingSystem.TrySleeping(ent.Owner)) return false; - _statusEffects.TryAddStatusEffectDuration(ent, ent.Comp.StatusEffect, hibernationDuration); + if (!_statusEffects.TryAddStatusEffectDuration(ent, ent.Comp.StatusEffect, hibernationDuration)) + return false; return true; } @@ -103,7 +104,7 @@ public override void Update(float frameTime) var querySleeping = EntityQueryEnumerator(); while (querySleeping.MoveNext(out var uid, out var scpSleepComponent, out _)) { - if (!scpSleepComponent.HibernationHealing) + if (scpSleepComponent.HibernationHealingRate == null) continue; _damageableSystem.TryChangeDamage(uid, scpSleepComponent.HibernationHealingRate * frameTime); diff --git a/Content.Shared/_Scp/Other/ScpSleep/ScpSleepComponent.cs b/Content.Shared/_Scp/Other/ScpSleep/ScpSleepComponent.cs index 6ca25606f4..5803d82f74 100644 --- a/Content.Shared/_Scp/Other/ScpSleep/ScpSleepComponent.cs +++ b/Content.Shared/_Scp/Other/ScpSleep/ScpSleepComponent.cs @@ -30,34 +30,10 @@ public sealed partial class ScpSleepComponent : Component public TimeSpan HibernationDuration = TimeSpan.FromSeconds(60); [DataField] - public bool HibernationOnHibernationState; + public List? HibernationStates; [DataField] - public List HibernationStates = new() { MobState.Critical }; - - [DataField] - public bool HibernationHealing; - - [DataField] - public DamageSpecifier HibernationHealingRate = new() - { - DamageDict = new() - { - { "Blunt", -20f }, - { "Slash", -20f }, - { "Piercing", -20f }, - { "Heat", -20f }, - { "Shock", -20f }, - { "Bloodloss", -20f}, - { "Genetic", -20f }, - { "Toxin", -20f }, - { "Airloss", -20f }, - { "Asphyxiation", -20f }, - { "Poison", -20f }, - { "Radiation", -20f }, - { "Cellular", -20f} - } - }; + public DamageSpecifier? HibernationHealingRate; [DataField] public int BaseBloodRefreshAmount = 1; diff --git a/Resources/Prototypes/_Scp/Entities/Mobs/Player/Scp/Main/scp939.yml b/Resources/Prototypes/_Scp/Entities/Mobs/Player/Scp/Main/scp939.yml index 4f5db118ac..4ad2f3537a 100644 --- a/Resources/Prototypes/_Scp/Entities/Mobs/Player/Scp/Main/scp939.yml +++ b/Resources/Prototypes/_Scp/Entities/Mobs/Player/Scp/Main/scp939.yml @@ -29,8 +29,8 @@ enum.ScpSleepVisuals.Sleeping: base: True: { state: asleep } - False: { state: alive } - type: ActiveListener + range: 20 - type: Scp class: Keter - type: Scp939 @@ -42,8 +42,23 @@ - Scp939Protection - type: ScpSleep actionProto: Scp939Sleep - hibernationHealing: true - hibernationOnHibernationState: true + hibernationHealingRate: + types: + Blunt: -20 + Slash: -20 + Piercing: -20 + Heat: -20 + Shock: -20 + Bloodloss: -20 + Genetic: -20 + Toxin: -20 + Airloss: -20 + Asphyxiation: -20 + Poison: -20 + Radiation: -20 + Cellular: -20 + hibernationStates: + - Critical - type: ScpRememberPhrase actionProto: Scp939Mimic - type: ScpReleaseGas @@ -85,7 +100,7 @@ - type: MobThresholds thresholds: 0: Alive - 1000: Critical + 10: Critical # ДЕБАГ ЗНАЧЕНИЕ!!! Старое: "1000: Critical" - type: Inventory templateId: scp939 - type: ToggleableNightVision From 1b285426102ff7759ac8dafbb495fa50c5ddb7ab Mon Sep 17 00:00:00 2001 From: Wardex Date: Sat, 2 May 2026 20:02:35 +0300 Subject: [PATCH 12/19] =?UTF-8?q?=D0=BF=D1=83=D0=BF=D1=83=D0=BF=D1=83?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../ScpOnSoundVisibilityHudSystem.cs | 33 +++++++------- .../ScpHealOnSleepSystem.cs} | 43 ++++++++++--------- .../ScpOnSoundVisibilitySystem.cs | 2 +- .../ScpReleaseGas/ScpReleaseGasSystem.cs | 31 +++++-------- .../Scp939/Sleep/ArtifactScp939SleepSystem.cs | 4 +- .../_Scp/Other/Events/ScpSystemsEvents.cs | 5 --- .../ScpHealOnSleepComponent.cs} | 3 +- .../SharedScpHealOnSleepData.cs} | 4 +- .../ScpOnSoundVisibilityComponent.cs | 7 ++- .../ScpOnSoundVisibilityViewerComponent.cs | 7 +-- .../ScpReleaseGas/ScpReleaseGasComponent.cs | 5 +-- .../ScpRememberPhraseComponent.cs | 13 +++--- Resources/Prototypes/Entities/Mobs/base.yml | 2 +- .../Prototypes/_Scp/Actions/scp_actions.yml | 4 +- .../Entities/Mobs/Player/Scp/Main/scp939.yml | 6 +-- 15 files changed, 71 insertions(+), 98 deletions(-) rename Content.Server/_Scp/Other/{ScpSleep/ScpSleepSystem.cs => ScpHealOnSleep/ScpHealOnSleepSystem.cs} (57%) rename Content.Shared/_Scp/Other/{ScpSleep/ScpSleepComponent.cs => ScpHealOnSleep/ScpHealOnSleepComponent.cs} (94%) rename Content.Shared/_Scp/Other/{ScpSleep/SharedScpSleepData.cs => ScpHealOnSleep/SharedScpHealOnSleepData.cs} (69%) diff --git a/Content.Client/_Scp/Other/ScpOnSoundVisibility/ScpOnSoundVisibilityHudSystem.cs b/Content.Client/_Scp/Other/ScpOnSoundVisibility/ScpOnSoundVisibilityHudSystem.cs index 2fd99b4dc5..f6361a2095 100644 --- a/Content.Client/_Scp/Other/ScpOnSoundVisibility/ScpOnSoundVisibilityHudSystem.cs +++ b/Content.Client/_Scp/Other/ScpOnSoundVisibility/ScpOnSoundVisibilityHudSystem.cs @@ -28,7 +28,7 @@ public sealed partial class ScpOnSoundVisibilityHudSystem : EquipmentHudSystem Ent, float BaseAlpha)> CachedBaseAlphas = new(64); + public readonly List<(Entity Ent, float BaseAlpha)> CachedBaseAlphas = new(64); private EntityQuery _eyeQuery; private EntityQuery _movementSpeedQuery; @@ -86,12 +86,12 @@ private void OnExamine(Entity ent, ref Exam if (!IsActive) return; - if (!TryGetLocalViewer(out _, out var viewerComp)) + if (!TryGetLocalViewer(out var viewer)) return; var visibility = GetVisibility(ent); - if (visibility < viewerComp.ExamineHideThreshold) + if (visibility < viewer.Value.Comp.ExamineHideThreshold) args.Cancel(); } @@ -100,12 +100,12 @@ private void OnGetStatusIcons(Entity ent, r if (!IsActive) return; - if (!TryGetLocalViewer(out _, out var viewerComp)) + if (!TryGetLocalViewer(out var viewer)) return; var visibility = GetVisibility(ent); - if (visibility <= viewerComp.StatusIconClearThreshold) + if (visibility <= viewer.Value.Comp.StatusIconClearThreshold) args.StatusIcons.Clear(); } @@ -136,7 +136,7 @@ public override void Update(float frameTime) if (_timing.RealTime < _nextUpdateTime) return; - if (!TryGetLocalViewer(out var viewer, out var _)) + if (!TryGetLocalViewer(out var viewer)) return; var delta = (float)(_timing.RealTime - (_nextUpdateTime - UpdateInterval)).TotalSeconds; @@ -148,7 +148,7 @@ public override void Update(float frameTime) if (visibilityComponent.OnCollide) continue; - if (viewer == visibilityEnt) + if (viewer.Value.Owner == visibilityEnt) continue; if (visibilityComponent.VisibilityAcc >= visibilityComponent.HideTime) @@ -200,13 +200,13 @@ private void OnMove(Entity ent, ref MoveEve if (!IsActive) return; - if (!TryGetLocalViewer(out var viewer, out var viewerComp)) + if (!TryGetLocalViewer(out var viewer)) return; if (ent.Comp.OnCollide) return; - if (ent == viewer) + if (ent.Owner == viewer.Value.Owner) return; // В зависимости от наличие защит или проблем со зрением изменяется то, насколько хорошо мы видим жертву @@ -214,7 +214,7 @@ private void OnMove(Entity ent, ref MoveEve { ent.Comp.VisibilityAcc *= modifier; } - else if (_whitelist.IsWhitelistPass(viewerComp.Protections, ent)) // Если имеется защита(тихое хождение) + else if (_whitelist.IsWhitelistPass(viewer.Value.Comp.Protections, ent)) // Если имеется защита(тихое хождение) { return; } @@ -296,10 +296,10 @@ private bool ModifyAcc(Entity ent, out floa // 1 = отсутствие модификатора modifier = 1f; - if (!TryGetLocalViewer(out _, out var viewerComp)) + if (!TryGetLocalViewer(out var viewer)) return false; - if (!viewerComp.PoorEyesight) + if (!viewer.Value.Comp.PoorEyesight) return false; modifier = _random.NextFloat(ent.Comp.MinValue, ent.Comp.MaxValue); @@ -307,10 +307,9 @@ private bool ModifyAcc(Entity ent, out floa return true; } - private bool TryGetLocalViewer([NotNullWhen(true)] out EntityUid? viewer, [NotNullWhen(true)] out ScpOnSoundVisibilityViewerComponent? viewerComp) + private bool TryGetLocalViewer([NotNullWhen(true)] out Entity? ent) { - viewer = null; - viewerComp = null; + ent = null; var localPlayer = _playerManager.LocalEntity; if (localPlayer == null) @@ -319,9 +318,7 @@ private bool TryGetLocalViewer([NotNullWhen(true)] out EntityUid? viewer, [NotNu if (!_viewerQuery.TryComp(localPlayer, out var comp)) return false; - viewer = localPlayer; - viewerComp = comp; - + ent = (localPlayer.Value, comp); return true; } } diff --git a/Content.Server/_Scp/Other/ScpSleep/ScpSleepSystem.cs b/Content.Server/_Scp/Other/ScpHealOnSleep/ScpHealOnSleepSystem.cs similarity index 57% rename from Content.Server/_Scp/Other/ScpSleep/ScpSleepSystem.cs rename to Content.Server/_Scp/Other/ScpHealOnSleep/ScpHealOnSleepSystem.cs index 7ce5813f14..8e8205c176 100644 --- a/Content.Server/_Scp/Other/ScpSleep/ScpSleepSystem.cs +++ b/Content.Server/_Scp/Other/ScpHealOnSleep/ScpHealOnSleepSystem.cs @@ -13,45 +13,46 @@ namespace Content.Server._Scp.Other.ScpSleep; public sealed class ScpSleepSystem : EntitySystem { - [Dependency] private readonly ActionsSystem _actionsSystem = default!; - [Dependency] private readonly SleepingSystem _sleepingSystem = default!; + [Dependency] private readonly ActionsSystem _actions = default!; + [Dependency] private readonly SleepingSystem _sleeping = default!; [Dependency] private readonly StatusEffectsSystem _statusEffects = default!; - [Dependency] private readonly DamageableSystem _damageableSystem = default!; - [Dependency] private readonly AppearanceSystem _appearanceSystem = default!; + [Dependency] private readonly DamageableSystem _damageable = default!; + [Dependency] private readonly AppearanceSystem _appearance = default!; [Dependency] private readonly AudioSystem _audio = default!; + public override void Initialize() { base.Initialize(); - SubscribeLocalEvent(OnMapInit); - SubscribeLocalEvent(OnShutdown); + SubscribeLocalEvent(OnMapInit); + SubscribeLocalEvent(OnShutdown); - SubscribeLocalEvent(OnSleepAction); - SubscribeLocalEvent(OnMobStateChanged); - SubscribeLocalEvent(OnSleepChanged); + SubscribeLocalEvent(OnSleepAction); + SubscribeLocalEvent(OnMobStateChanged); + SubscribeLocalEvent(OnSleepChanged); } - private void OnMapInit(Entity ent, ref MapInitEvent args) + private void OnMapInit(Entity ent, ref MapInitEvent args) { if (!ent.Comp.AddAction) return; - var actionEnt = _actionsSystem.AddAction(ent, ent.Comp.ActionProto); + var actionEnt = _actions.AddAction(ent, ent.Comp.ActionProto); ent.Comp.ActionEnt = actionEnt; } - private void OnShutdown(Entity ent, ref ComponentShutdown args) + private void OnShutdown(Entity ent, ref ComponentShutdown args) { - _actionsSystem.RemoveAction(ent.Owner, ent.Comp.ActionEnt); + _actions.RemoveAction(ent.Owner, ent.Comp.ActionEnt); ent.Comp.ActionEnt = null; } - private void OnSleepAction(Entity ent, ref ScpSleepActionEvent args) + private void OnSleepAction(Entity ent, ref ScpSleepActionEvent args) { args.Handled = TrySleep(ent, ent.Comp.HibernationDuration); } - private void OnMobStateChanged(Entity ent, ref MobStateChangedEvent args) + private void OnMobStateChanged(Entity ent, ref MobStateChangedEvent args) { if (ent.Comp.HibernationStates == null) return; @@ -68,7 +69,7 @@ private void OnMobStateChanged(Entity ent, ref MobStateChange _audio.PlayPvs(ent.Comp.CritSound, ent); } - private void OnSleepChanged(Entity ent, ref SleepStateChangedEvent args) + private void OnSleepChanged(Entity ent, ref SleepStateChangedEvent args) { if (TryComp(ent, out var bloodstreamComponent)) { @@ -80,15 +81,15 @@ private void OnSleepChanged(Entity ent, ref SleepStateChanged DirtyField(ent, bloodstreamComponent, nameof(BloodstreamComponent.BloodRefreshAmount)); } - _appearanceSystem.SetData(ent, ScpSleepVisuals.Sleeping, args.FellAsleep); + _appearance.SetData(ent, ScpHealOnSleepVisuals.Sleeping, args.FellAsleep); } - public bool TrySleep(Entity ent, TimeSpan hibernationDuration) + public bool TrySleep(Entity ent, TimeSpan hibernationDuration) { if (HasComp(ent)) return false; - if (!_sleepingSystem.TrySleeping(ent.Owner)) + if (!_sleeping.TrySleeping(ent.Owner)) return false; if (!_statusEffects.TryAddStatusEffectDuration(ent, ent.Comp.StatusEffect, hibernationDuration)) @@ -101,13 +102,13 @@ public override void Update(float frameTime) { base.Update(frameTime); - var querySleeping = EntityQueryEnumerator(); + var querySleeping = EntityQueryEnumerator(); while (querySleeping.MoveNext(out var uid, out var scpSleepComponent, out _)) { if (scpSleepComponent.HibernationHealingRate == null) continue; - _damageableSystem.TryChangeDamage(uid, scpSleepComponent.HibernationHealingRate * frameTime); + _damageable.TryChangeDamage(uid, scpSleepComponent.HibernationHealingRate * frameTime); } } } diff --git a/Content.Server/_Scp/Other/ScpOnSoundVisibility/ScpOnSoundVisibilitySystem.cs b/Content.Server/_Scp/Other/ScpOnSoundVisibility/ScpOnSoundVisibilitySystem.cs index a702d7e107..e07aa39d70 100644 --- a/Content.Server/_Scp/Other/ScpOnSoundVisibility/ScpOnSoundVisibilitySystem.cs +++ b/Content.Server/_Scp/Other/ScpOnSoundVisibility/ScpOnSoundVisibilitySystem.cs @@ -69,7 +69,7 @@ public override void Update(float frameTime) var timeDifference = _timing.CurTime - viewerComp.PoorEyesightTimeStart.Value; - if (timeDifference > TimeSpan.FromSeconds(viewerComp.PoorEyesightTime)) + if (timeDifference > viewerComp.PoorEyesightTime) { viewerComp.PoorEyesight = false; viewerComp.PoorEyesightTimeStart = null; diff --git a/Content.Server/_Scp/Other/ScpReleaseGas/ScpReleaseGasSystem.cs b/Content.Server/_Scp/Other/ScpReleaseGas/ScpReleaseGasSystem.cs index a6987553d2..cc321fe39f 100644 --- a/Content.Server/_Scp/Other/ScpReleaseGas/ScpReleaseGasSystem.cs +++ b/Content.Server/_Scp/Other/ScpReleaseGas/ScpReleaseGasSystem.cs @@ -1,8 +1,7 @@ using Content.Server.Actions; using Content.Shared._Scp.Other.ScpReleaseGas; -using Content.Shared._Scp.Other.Events; using Content.Shared._Scp.ScpMask; -using Content.Shared.Trigger.Systems; +using Content.Shared.Trigger; namespace Content.Server._Scp.Other.ScpReleaseGas; @@ -10,7 +9,6 @@ public sealed class ScpReleaseGasSystem : EntitySystem { [Dependency] private readonly ActionsSystem _actionsSystem = default!; [Dependency] private readonly ScpMaskSystem _scpMask = default!; - [Dependency] private readonly TriggerSystem _trigger = default!; public override void Initialize() { @@ -19,8 +17,7 @@ public override void Initialize() SubscribeLocalEvent(OnMapInit); SubscribeLocalEvent(OnShutdown); - SubscribeLocalEvent(OnGasActionAttempt); - SubscribeLocalEvent(OnGasAction); + SubscribeLocalEvent(OnAttemptTriggerEvent); } private void OnMapInit(Entity ent, ref MapInitEvent args) @@ -35,27 +32,19 @@ private void OnShutdown(Entity ent, ref ComponentShutdow ent.Comp.ActionEnt = null; } - private void OnGasActionAttempt(Entity ent, ref ScpReleaseGasActionAttemptEvent args) + private void OnAttemptTriggerEvent(Entity ent, ref AttemptTriggerEvent args) { + if (args.Key == null) + return; + + if (!ent.Comp.TriggerKeys.Contains(args.Key)) + return; + if (_scpMask.TryGetScpMask(ent, out var scpMask)) { _scpMask.TryCreatePopup(ent, scpMask); - args.Cancel(); + args.Cancelled = true; return; } } - - private void OnGasAction(Entity ent, ref ScpReleaseGasActionEvent args) - { - var ev = new ScpReleaseGasActionAttemptEvent(); - RaiseLocalEvent(ent, ref ev); - - if (ev.Cancelled) - return; - - foreach (var key in ent.Comp.KeysOut) - _trigger.Trigger(ent, args.Performer, key, false); - - args.Handled = true; - } } diff --git a/Content.Server/_Scp/Research/Artifacts/Effects/_ScpSpecific/Scp939/Sleep/ArtifactScp939SleepSystem.cs b/Content.Server/_Scp/Research/Artifacts/Effects/_ScpSpecific/Scp939/Sleep/ArtifactScp939SleepSystem.cs index 8b8a19f9d5..624953e678 100644 --- a/Content.Server/_Scp/Research/Artifacts/Effects/_ScpSpecific/Scp939/Sleep/ArtifactScp939SleepSystem.cs +++ b/Content.Server/_Scp/Research/Artifacts/Effects/_ScpSpecific/Scp939/Sleep/ArtifactScp939SleepSystem.cs @@ -8,7 +8,7 @@ namespace Content.Server._Scp.Research.Artifacts.Effects._ScpSpecific.Scp939.Sle public sealed class ArtifactScp939SleepSystem : BaseXAESystem { - [Dependency] private readonly ScpSleepSystem _scpSleepSystem = default!; + [Dependency] private readonly ScpSleepSystem _scpSleep = default!; [Dependency] private readonly IRobustRandom _random = default!; protected override void OnActivated(Entity ent, ref XenoArtifactNodeActivatedEvent args) @@ -18,6 +18,6 @@ protected override void OnActivated(Entity ent, re var time = _random.Next(ent.Comp.MinSleepTime, ent.Comp.MaxSleepTime); - _scpSleepSystem.TrySleep((ent, scpSleepComponent), time); + _scpSleep.TrySleep((ent, scpSleepComponent), time); } } diff --git a/Content.Shared/_Scp/Other/Events/ScpSystemsEvents.cs b/Content.Shared/_Scp/Other/Events/ScpSystemsEvents.cs index 85a428bb20..469666c345 100644 --- a/Content.Shared/_Scp/Other/Events/ScpSystemsEvents.cs +++ b/Content.Shared/_Scp/Other/Events/ScpSystemsEvents.cs @@ -2,11 +2,6 @@ namespace Content.Shared._Scp.Other.Events; -[ByRefEvent] -public sealed class ScpReleaseGasActionAttemptEvent : CancellableEntityEventArgs; - -public sealed partial class ScpReleaseGasActionEvent : InstantActionEvent; - public sealed partial class ScpRememberPhraseActionEvent : InstantActionEvent; public sealed partial class ScpSleepActionEvent : InstantActionEvent; diff --git a/Content.Shared/_Scp/Other/ScpSleep/ScpSleepComponent.cs b/Content.Shared/_Scp/Other/ScpHealOnSleep/ScpHealOnSleepComponent.cs similarity index 94% rename from Content.Shared/_Scp/Other/ScpSleep/ScpSleepComponent.cs rename to Content.Shared/_Scp/Other/ScpHealOnSleep/ScpHealOnSleepComponent.cs index 5803d82f74..c51a61309e 100644 --- a/Content.Shared/_Scp/Other/ScpSleep/ScpSleepComponent.cs +++ b/Content.Shared/_Scp/Other/ScpHealOnSleep/ScpHealOnSleepComponent.cs @@ -1,4 +1,3 @@ - using Content.Shared.Actions.Components; using Content.Shared.Damage; using Content.Shared.Mobs; @@ -9,7 +8,7 @@ namespace Content.Shared._Scp.Other.ScpSleep; [RegisterComponent, NetworkedComponent] -public sealed partial class ScpSleepComponent : Component +public sealed partial class ScpHealOnSleepComponent : Component { [DataField] public EntProtoId ActionProto = "ScpSleepAction"; diff --git a/Content.Shared/_Scp/Other/ScpSleep/SharedScpSleepData.cs b/Content.Shared/_Scp/Other/ScpHealOnSleep/SharedScpHealOnSleepData.cs similarity index 69% rename from Content.Shared/_Scp/Other/ScpSleep/SharedScpSleepData.cs rename to Content.Shared/_Scp/Other/ScpHealOnSleep/SharedScpHealOnSleepData.cs index a31dfa7302..2d25165f36 100644 --- a/Content.Shared/_Scp/Other/ScpSleep/SharedScpSleepData.cs +++ b/Content.Shared/_Scp/Other/ScpHealOnSleep/SharedScpHealOnSleepData.cs @@ -3,13 +3,13 @@ namespace Content.Shared._Scp.Other.ScpSleep; [Serializable, NetSerializable] -public enum ScpSleepLayers : byte +public enum ScpHealOnSleepLayers : byte { Base = 0 } [Serializable, NetSerializable] -public enum ScpSleepVisuals : byte +public enum ScpHealOnSleepVisuals : byte { Sleeping = 0, } diff --git a/Content.Shared/_Scp/Other/ScpOnSoundVisibility/ScpOnSoundVisibilityComponent.cs b/Content.Shared/_Scp/Other/ScpOnSoundVisibility/ScpOnSoundVisibilityComponent.cs index da6ce4671b..d038621e81 100644 --- a/Content.Shared/_Scp/Other/ScpOnSoundVisibility/ScpOnSoundVisibilityComponent.cs +++ b/Content.Shared/_Scp/Other/ScpOnSoundVisibility/ScpOnSoundVisibilityComponent.cs @@ -1,6 +1,8 @@ +using Robust.Shared.GameStates; + namespace Content.Shared._Scp.Other.ScpOnSoundVisibility; -[RegisterComponent] +[RegisterComponent, NetworkedComponent] public sealed partial class ScpOnSoundVisibilityComponent : Component { public const float InitialVisibilityAcc = 0.001f; @@ -16,7 +18,4 @@ public sealed partial class ScpOnSoundVisibilityComponent : Component [DataField] public float MaxValue = DefaultMaxValue; - - [DataField] - public float SharePhraseRadius = 16f; } diff --git a/Content.Shared/_Scp/Other/ScpOnSoundVisibility/ScpOnSoundVisibilityViewerComponent.cs b/Content.Shared/_Scp/Other/ScpOnSoundVisibility/ScpOnSoundVisibilityViewerComponent.cs index 18a445cb2e..f2aca7ae6f 100644 --- a/Content.Shared/_Scp/Other/ScpOnSoundVisibility/ScpOnSoundVisibilityViewerComponent.cs +++ b/Content.Shared/_Scp/Other/ScpOnSoundVisibility/ScpOnSoundVisibilityViewerComponent.cs @@ -14,7 +14,7 @@ public sealed partial class ScpOnSoundVisibilityViewerComponent : Component public bool PoorEyesight; [DataField] - public float PoorEyesightTime = 10f; // Секунды + public TimeSpan PoorEyesightTime = TimeSpan.FromSeconds(10f); [ViewVariables, AutoNetworkedField] public TimeSpan? PoorEyesightTimeStart; // Когда начали плохо видеть @@ -28,10 +28,7 @@ public sealed partial class ScpOnSoundVisibilityViewerComponent : Component [DataField] public EntityWhitelist Protections = new() { - Components = new[] - { - "ScpOnSoundVisibilityProtection" - } + Components = ["ScpOnSoundVisibilityProtection"] }; [DataField] diff --git a/Content.Shared/_Scp/Other/ScpReleaseGas/ScpReleaseGasComponent.cs b/Content.Shared/_Scp/Other/ScpReleaseGas/ScpReleaseGasComponent.cs index ced9d51e5b..13b4d769c3 100644 --- a/Content.Shared/_Scp/Other/ScpReleaseGas/ScpReleaseGasComponent.cs +++ b/Content.Shared/_Scp/Other/ScpReleaseGas/ScpReleaseGasComponent.cs @@ -12,10 +12,7 @@ public sealed partial class ScpReleaseGasComponent : Component public EntProtoId ActionProto = "ScpSmokeAction"; [DataField] - public EntProtoId SmokeProtoId = "Smoke"; - - [DataField] - public List KeysOut = new() { "scpSmokeTrigger" }; + public HashSet TriggerKeys = ["scpSmokeTrigger"]; [ViewVariables] public EntityUid? ActionEnt; diff --git a/Content.Shared/_Scp/Other/ScpRememberPhrase/ScpRememberPhraseComponent.cs b/Content.Shared/_Scp/Other/ScpRememberPhrase/ScpRememberPhraseComponent.cs index 02be97288c..a62e3f5045 100644 --- a/Content.Shared/_Scp/Other/ScpRememberPhrase/ScpRememberPhraseComponent.cs +++ b/Content.Shared/_Scp/Other/ScpRememberPhrase/ScpRememberPhraseComponent.cs @@ -18,15 +18,14 @@ public sealed partial class ScpRememberPhraseComponent : Component /// Запомненые объектом слова. Ключ - сказанная фраза, значение - пара, в которой ключ имя сказавшего и значение прототип его ттса /// [ViewVariables] - public List RememberedMessages = new(); + public List RememberedMessages = []; [ViewVariables] public EntityUid? ActionEnt; } -public partial record struct RememberedMessage -{ - public string Message; - public string SpeakerName; - public string? TtsVoice; -} +public record struct RememberedMessage( + string Message, + string SpeakerName, + string? TtsVoice +); diff --git a/Resources/Prototypes/Entities/Mobs/base.yml b/Resources/Prototypes/Entities/Mobs/base.yml index 9c0d4971d0..27b5cc58ef 100644 --- a/Resources/Prototypes/Entities/Mobs/base.yml +++ b/Resources/Prototypes/Entities/Mobs/base.yml @@ -108,7 +108,7 @@ - type: MobMover - type: MovementSpeedModifier - type: LagCompensation - - type: ScpOnSoundVisibility # Fire Station edit + - type: ScpOnSoundVisibility # Fire edit - type: entity save: false diff --git a/Resources/Prototypes/_Scp/Actions/scp_actions.yml b/Resources/Prototypes/_Scp/Actions/scp_actions.yml index da3f5c30b9..4919e16f1f 100644 --- a/Resources/Prototypes/_Scp/Actions/scp_actions.yml +++ b/Resources/Prototypes/_Scp/Actions/scp_actions.yml @@ -21,8 +21,8 @@ icon: sprite: _Scp/Actions/scp-actions.rsi state: smaug - - type: InstantAction - event: !type:ScpReleaseGasActionEvent + - type: TriggerOnActivate + keyOut: "scpSmokeTrigger" - type: entity id: ScpSleepAction diff --git a/Resources/Prototypes/_Scp/Entities/Mobs/Player/Scp/Main/scp939.yml b/Resources/Prototypes/_Scp/Entities/Mobs/Player/Scp/Main/scp939.yml index 4ad2f3537a..82ac9b0049 100644 --- a/Resources/Prototypes/_Scp/Entities/Mobs/Player/Scp/Main/scp939.yml +++ b/Resources/Prototypes/_Scp/Entities/Mobs/Player/Scp/Main/scp939.yml @@ -26,7 +26,7 @@ Dead: { state: dead } Critical: { state: dead } Alive: { state: alive } - enum.ScpSleepVisuals.Sleeping: + enum.ScpHealOnSleepVisuals.Sleeping: base: True: { state: asleep } - type: ActiveListener @@ -100,7 +100,7 @@ - type: MobThresholds thresholds: 0: Alive - 10: Critical # ДЕБАГ ЗНАЧЕНИЕ!!! Старое: "1000: Critical" + 1000: Critical - type: Inventory templateId: scp939 - type: ToggleableNightVision @@ -144,7 +144,7 @@ collection: DistanceKillScp939 originWhitelist: components: - - ScpOnSoundVisibilityViewer + - Scp939 - type: ScpExaminableDamage mode: MobToCritical generalMessages: ScpScp939Messages From c45219a71f16df96979ebc2bb47efafc2d1e6ccf Mon Sep 17 00:00:00 2001 From: Wardex Date: Sat, 2 May 2026 20:25:57 +0300 Subject: [PATCH 13/19] =?UTF-8?q?=D0=BE=D0=B9?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../_Scp/Other/ScpHealOnSleep/ScpHealOnSleepSystem.cs | 2 +- .../_ScpSpecific/Scp939/Sleep/ArtifactScp939SleepSystem.cs | 4 ++-- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/Content.Server/_Scp/Other/ScpHealOnSleep/ScpHealOnSleepSystem.cs b/Content.Server/_Scp/Other/ScpHealOnSleep/ScpHealOnSleepSystem.cs index 8e8205c176..1653d47b32 100644 --- a/Content.Server/_Scp/Other/ScpHealOnSleep/ScpHealOnSleepSystem.cs +++ b/Content.Server/_Scp/Other/ScpHealOnSleep/ScpHealOnSleepSystem.cs @@ -11,7 +11,7 @@ namespace Content.Server._Scp.Other.ScpSleep; -public sealed class ScpSleepSystem : EntitySystem +public sealed class ScpHealOnSleepSystem : EntitySystem { [Dependency] private readonly ActionsSystem _actions = default!; [Dependency] private readonly SleepingSystem _sleeping = default!; diff --git a/Content.Server/_Scp/Research/Artifacts/Effects/_ScpSpecific/Scp939/Sleep/ArtifactScp939SleepSystem.cs b/Content.Server/_Scp/Research/Artifacts/Effects/_ScpSpecific/Scp939/Sleep/ArtifactScp939SleepSystem.cs index 624953e678..e6a16dd180 100644 --- a/Content.Server/_Scp/Research/Artifacts/Effects/_ScpSpecific/Scp939/Sleep/ArtifactScp939SleepSystem.cs +++ b/Content.Server/_Scp/Research/Artifacts/Effects/_ScpSpecific/Scp939/Sleep/ArtifactScp939SleepSystem.cs @@ -8,12 +8,12 @@ namespace Content.Server._Scp.Research.Artifacts.Effects._ScpSpecific.Scp939.Sle public sealed class ArtifactScp939SleepSystem : BaseXAESystem { - [Dependency] private readonly ScpSleepSystem _scpSleep = default!; + [Dependency] private readonly ScpHealOnSleepSystem _scpSleep = default!; [Dependency] private readonly IRobustRandom _random = default!; protected override void OnActivated(Entity ent, ref XenoArtifactNodeActivatedEvent args) { - if (!TryComp(ent, out var scpSleepComponent)) + if (!TryComp(ent, out var scpSleepComponent)) return; var time = _random.Next(ent.Comp.MinSleepTime, ent.Comp.MaxSleepTime); From 630b585cf9898b8aea0a3c5a9a084714720158d9 Mon Sep 17 00:00:00 2001 From: Wardex Date: Sat, 2 May 2026 20:30:04 +0300 Subject: [PATCH 14/19] =?UTF-8?q?=D0=BE=D0=B9=20x2?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../Prototypes/_Scp/Entities/Mobs/Player/Scp/Main/scp939.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Resources/Prototypes/_Scp/Entities/Mobs/Player/Scp/Main/scp939.yml b/Resources/Prototypes/_Scp/Entities/Mobs/Player/Scp/Main/scp939.yml index 82ac9b0049..af4a5e5ceb 100644 --- a/Resources/Prototypes/_Scp/Entities/Mobs/Player/Scp/Main/scp939.yml +++ b/Resources/Prototypes/_Scp/Entities/Mobs/Player/Scp/Main/scp939.yml @@ -40,7 +40,7 @@ protections: components: - Scp939Protection - - type: ScpSleep + - type: ScpHealOnSleep actionProto: Scp939Sleep hibernationHealingRate: types: From ef050d6742c59b2479811ad12cf23ca478e005b9 Mon Sep 17 00:00:00 2001 From: Wardex Date: Sat, 2 May 2026 20:54:47 +0300 Subject: [PATCH 15/19] =?UTF-8?q?=D0=B0=20=D1=82=D1=8B=20=D0=BF=D1=80?= =?UTF-8?q?=D0=B0=D0=B2?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../_Scp/Other/ScpHealOnSleep/ScpHealOnSleepComponent.cs | 5 ++--- 1 file changed, 2 insertions(+), 3 deletions(-) rename {Content.Shared => Content.Server}/_Scp/Other/ScpHealOnSleep/ScpHealOnSleepComponent.cs (89%) diff --git a/Content.Shared/_Scp/Other/ScpHealOnSleep/ScpHealOnSleepComponent.cs b/Content.Server/_Scp/Other/ScpHealOnSleep/ScpHealOnSleepComponent.cs similarity index 89% rename from Content.Shared/_Scp/Other/ScpHealOnSleep/ScpHealOnSleepComponent.cs rename to Content.Server/_Scp/Other/ScpHealOnSleep/ScpHealOnSleepComponent.cs index c51a61309e..2051d49329 100644 --- a/Content.Shared/_Scp/Other/ScpHealOnSleep/ScpHealOnSleepComponent.cs +++ b/Content.Server/_Scp/Other/ScpHealOnSleep/ScpHealOnSleepComponent.cs @@ -2,12 +2,11 @@ using Content.Shared.Damage; using Content.Shared.Mobs; using Robust.Shared.Audio; -using Robust.Shared.GameStates; using Robust.Shared.Prototypes; -namespace Content.Shared._Scp.Other.ScpSleep; +namespace Content.Server._Scp.Other.ScpSleep; -[RegisterComponent, NetworkedComponent] +[RegisterComponent] public sealed partial class ScpHealOnSleepComponent : Component { [DataField] From ea9c550bcf69680835a72878e6eb5d557244b092 Mon Sep 17 00:00:00 2001 From: Wardex Date: Sat, 2 May 2026 22:50:16 +0300 Subject: [PATCH 16/19] ScpTriggerOnAction --- .../ScpReleaseGas/ScpReleaseGasSystem.cs | 15 +----- Content.Shared/Actions/SharedActionsSystem.cs | 9 ++++ .../_Scp/Other/Events/ScpTriggerEvents.cs | 5 ++ .../ScpReleaseGas/ScpReleaseGasComponent.cs | 3 -- .../ScpTriggerOnActionComponent.cs | 14 ++++++ .../ScpTriggerOnActionSystem.cs | 48 +++++++++++++++++++ .../Prototypes/_Scp/Actions/scp_actions.yml | 4 +- 7 files changed, 79 insertions(+), 19 deletions(-) create mode 100644 Content.Shared/_Scp/Other/Events/ScpTriggerEvents.cs create mode 100644 Content.Shared/_Scp/Trigger/ScpTriggerOnAction/ScpTriggerOnActionComponent.cs create mode 100644 Content.Shared/_Scp/Trigger/ScpTriggerOnAction/ScpTriggerOnActionSystem.cs diff --git a/Content.Server/_Scp/Other/ScpReleaseGas/ScpReleaseGasSystem.cs b/Content.Server/_Scp/Other/ScpReleaseGas/ScpReleaseGasSystem.cs index cc321fe39f..24ff33a08d 100644 --- a/Content.Server/_Scp/Other/ScpReleaseGas/ScpReleaseGasSystem.cs +++ b/Content.Server/_Scp/Other/ScpReleaseGas/ScpReleaseGasSystem.cs @@ -1,6 +1,5 @@ using Content.Server.Actions; using Content.Shared._Scp.Other.ScpReleaseGas; -using Content.Shared._Scp.ScpMask; using Content.Shared.Trigger; namespace Content.Server._Scp.Other.ScpReleaseGas; @@ -8,7 +7,6 @@ namespace Content.Server._Scp.Other.ScpReleaseGas; public sealed class ScpReleaseGasSystem : EntitySystem { [Dependency] private readonly ActionsSystem _actionsSystem = default!; - [Dependency] private readonly ScpMaskSystem _scpMask = default!; public override void Initialize() { @@ -34,17 +32,6 @@ private void OnShutdown(Entity ent, ref ComponentShutdow private void OnAttemptTriggerEvent(Entity ent, ref AttemptTriggerEvent args) { - if (args.Key == null) - return; - - if (!ent.Comp.TriggerKeys.Contains(args.Key)) - return; - - if (_scpMask.TryGetScpMask(ent, out var scpMask)) - { - _scpMask.TryCreatePopup(ent, scpMask); - args.Cancelled = true; - return; - } + } } diff --git a/Content.Shared/Actions/SharedActionsSystem.cs b/Content.Shared/Actions/SharedActionsSystem.cs index 823a7064b8..d2756c5c4f 100644 --- a/Content.Shared/Actions/SharedActionsSystem.cs +++ b/Content.Shared/Actions/SharedActionsSystem.cs @@ -1,5 +1,6 @@ using System.Diagnostics.CodeAnalysis; using System.Linq; +using Content.Shared._Scp.Other.Events; // Fire edit using Content.Shared._Sunrise.Antags.Abductor; using Content.Shared.ActionBlocker; using Content.Shared.Actions.Components; @@ -298,6 +299,14 @@ private bool TryPerformAction(RequestPerformActionEvent ev, EntityUid user, bool return false; } + // Fire edit start + var checkEv = new ScpActionTriggerAttemptEvent(); + RaiseLocalEvent(user, ref checkEv); + + if (checkEv.Cancelled) + return true; + // Fire edit end + if (GetAction(actionEnt) is not {} action) return false; diff --git a/Content.Shared/_Scp/Other/Events/ScpTriggerEvents.cs b/Content.Shared/_Scp/Other/Events/ScpTriggerEvents.cs new file mode 100644 index 0000000000..5241ee9ae0 --- /dev/null +++ b/Content.Shared/_Scp/Other/Events/ScpTriggerEvents.cs @@ -0,0 +1,5 @@ + +namespace Content.Shared._Scp.Other.Events; + +[ByRefEvent] +public sealed partial class ScpActionTriggerAttemptEvent : CancellableEntityEventArgs; diff --git a/Content.Shared/_Scp/Other/ScpReleaseGas/ScpReleaseGasComponent.cs b/Content.Shared/_Scp/Other/ScpReleaseGas/ScpReleaseGasComponent.cs index 13b4d769c3..df78486b44 100644 --- a/Content.Shared/_Scp/Other/ScpReleaseGas/ScpReleaseGasComponent.cs +++ b/Content.Shared/_Scp/Other/ScpReleaseGas/ScpReleaseGasComponent.cs @@ -11,9 +11,6 @@ public sealed partial class ScpReleaseGasComponent : Component [DataField] public EntProtoId ActionProto = "ScpSmokeAction"; - [DataField] - public HashSet TriggerKeys = ["scpSmokeTrigger"]; - [ViewVariables] public EntityUid? ActionEnt; } diff --git a/Content.Shared/_Scp/Trigger/ScpTriggerOnAction/ScpTriggerOnActionComponent.cs b/Content.Shared/_Scp/Trigger/ScpTriggerOnAction/ScpTriggerOnActionComponent.cs new file mode 100644 index 0000000000..bbafc70f4f --- /dev/null +++ b/Content.Shared/_Scp/Trigger/ScpTriggerOnAction/ScpTriggerOnActionComponent.cs @@ -0,0 +1,14 @@ +using Content.Shared.Trigger.Systems; +using Robust.Shared.GameStates; + +namespace Content.Shared._Scp.Trigger.ScpTriggerOnAction; + +[RegisterComponent, NetworkedComponent] +public sealed partial class ScpTriggerOnActionComponent : Component +{ + [DataField] + public string? KeyOut = TriggerSystem.DefaultTriggerKey; + + [DataField] + public bool CheckScpMask; +} diff --git a/Content.Shared/_Scp/Trigger/ScpTriggerOnAction/ScpTriggerOnActionSystem.cs b/Content.Shared/_Scp/Trigger/ScpTriggerOnAction/ScpTriggerOnActionSystem.cs new file mode 100644 index 0000000000..790ce7cde0 --- /dev/null +++ b/Content.Shared/_Scp/Trigger/ScpTriggerOnAction/ScpTriggerOnActionSystem.cs @@ -0,0 +1,48 @@ +using Content.Shared._Scp.Other.Events; +using Content.Shared.Trigger; +using Content.Shared.Trigger.Systems; +using Content.Shared._Scp.ScpMask; + +namespace Content.Shared._Scp.Trigger.ScpTriggerOnAction; + +public sealed class ScpTriggerOnActionSystem : EntitySystem +{ + [Dependency] private readonly TriggerSystem _trigger = default!; + [Dependency] private readonly ScpMaskSystem _scpMask = default!; + public override void Initialize() + { + base.Initialize(); + + SubscribeLocalEvent(OnTriggerAttempt); + SubscribeLocalEvent(OnAction); + } + + private void OnTriggerAttempt(Entity ent, ref AttemptTriggerEvent args) + { + if (!ent.Comp.CheckScpMask) + return; + + if (args.Key == null) + return; + + if (ent.Comp.KeyOut == null) + return; + + if (ent.Comp.KeyOut != args.Key) + return; + + if (_scpMask.TryGetScpMask(ent, out var scpMask)) + { + _scpMask.TryCreatePopup(ent, scpMask); + args.Cancelled = true; + return; + } + } + + private void OnAction(Entity ent, ref ScpActionTriggerAttemptEvent args) + { + _trigger.Trigger(ent, ent, ent.Comp.KeyOut); + + args.Cancel(); + } +} diff --git a/Resources/Prototypes/_Scp/Actions/scp_actions.yml b/Resources/Prototypes/_Scp/Actions/scp_actions.yml index 4919e16f1f..bea5568ca6 100644 --- a/Resources/Prototypes/_Scp/Actions/scp_actions.yml +++ b/Resources/Prototypes/_Scp/Actions/scp_actions.yml @@ -21,8 +21,8 @@ icon: sprite: _Scp/Actions/scp-actions.rsi state: smaug - - type: TriggerOnActivate - keyOut: "scpSmokeTrigger" + - type: ScpTriggerOnAction + keyOut: scpSmokeTrigger - type: entity id: ScpSleepAction From 65303590b1ca032b52faf6a49d03d21aabdd9993 Mon Sep 17 00:00:00 2001 From: Wardex Date: Thu, 7 May 2026 15:59:57 +0300 Subject: [PATCH 17/19] =?UTF-8?q?=D0=BA=D1=85=D0=BC?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../ScpReleaseGas/ScpReleaseGasSystem.cs | 37 ------------------- Content.Shared/Actions/SharedActionsSystem.cs | 9 ----- .../_Scp/Other/Events/ScpTriggerEvents.cs | 8 +++- .../ScpReleaseGas/ScpReleaseGasComponent.cs | 16 -------- .../_Scp/ScpMask/ScpMaskChekerComponent.cs | 10 +++++ .../_Scp/ScpMask/ScpMaskSystem.Checker.cs | 24 ++++++++++++ Content.Shared/_Scp/ScpMask/ScpMaskSystem.cs | 1 + .../ScpTriggerOnActionComponent.cs | 14 ------- .../ScpTriggerOnActionListenerComponent.cs | 9 +++++ .../ScpTriggerOnActionSystem.cs | 34 ++--------------- Resources/Prototypes/_Scp/Actions/scp939.yml | 6 +-- .../Prototypes/_Scp/Actions/scp_actions.yml | 5 ++- .../Entities/Mobs/Player/Scp/Main/scp939.yml | 11 ++++-- 13 files changed, 66 insertions(+), 118 deletions(-) delete mode 100644 Content.Server/_Scp/Other/ScpReleaseGas/ScpReleaseGasSystem.cs delete mode 100644 Content.Shared/_Scp/Other/ScpReleaseGas/ScpReleaseGasComponent.cs create mode 100644 Content.Shared/_Scp/ScpMask/ScpMaskChekerComponent.cs create mode 100644 Content.Shared/_Scp/ScpMask/ScpMaskSystem.Checker.cs delete mode 100644 Content.Shared/_Scp/Trigger/ScpTriggerOnAction/ScpTriggerOnActionComponent.cs create mode 100644 Content.Shared/_Scp/Trigger/ScpTriggerOnAction/ScpTriggerOnActionListenerComponent.cs diff --git a/Content.Server/_Scp/Other/ScpReleaseGas/ScpReleaseGasSystem.cs b/Content.Server/_Scp/Other/ScpReleaseGas/ScpReleaseGasSystem.cs deleted file mode 100644 index 24ff33a08d..0000000000 --- a/Content.Server/_Scp/Other/ScpReleaseGas/ScpReleaseGasSystem.cs +++ /dev/null @@ -1,37 +0,0 @@ -using Content.Server.Actions; -using Content.Shared._Scp.Other.ScpReleaseGas; -using Content.Shared.Trigger; - -namespace Content.Server._Scp.Other.ScpReleaseGas; - -public sealed class ScpReleaseGasSystem : EntitySystem -{ - [Dependency] private readonly ActionsSystem _actionsSystem = default!; - - public override void Initialize() - { - base.Initialize(); - - SubscribeLocalEvent(OnMapInit); - SubscribeLocalEvent(OnShutdown); - - SubscribeLocalEvent(OnAttemptTriggerEvent); - } - - private void OnMapInit(Entity ent, ref MapInitEvent args) - { - var actionEnt = _actionsSystem.AddAction(ent, ent.Comp.ActionProto); - ent.Comp.ActionEnt = actionEnt; - } - - private void OnShutdown(Entity ent, ref ComponentShutdown args) - { - _actionsSystem.RemoveAction(ent.Owner, ent.Comp.ActionEnt); - ent.Comp.ActionEnt = null; - } - - private void OnAttemptTriggerEvent(Entity ent, ref AttemptTriggerEvent args) - { - - } -} diff --git a/Content.Shared/Actions/SharedActionsSystem.cs b/Content.Shared/Actions/SharedActionsSystem.cs index d2756c5c4f..823a7064b8 100644 --- a/Content.Shared/Actions/SharedActionsSystem.cs +++ b/Content.Shared/Actions/SharedActionsSystem.cs @@ -1,6 +1,5 @@ using System.Diagnostics.CodeAnalysis; using System.Linq; -using Content.Shared._Scp.Other.Events; // Fire edit using Content.Shared._Sunrise.Antags.Abductor; using Content.Shared.ActionBlocker; using Content.Shared.Actions.Components; @@ -299,14 +298,6 @@ private bool TryPerformAction(RequestPerformActionEvent ev, EntityUid user, bool return false; } - // Fire edit start - var checkEv = new ScpActionTriggerAttemptEvent(); - RaiseLocalEvent(user, ref checkEv); - - if (checkEv.Cancelled) - return true; - // Fire edit end - if (GetAction(actionEnt) is not {} action) return false; diff --git a/Content.Shared/_Scp/Other/Events/ScpTriggerEvents.cs b/Content.Shared/_Scp/Other/Events/ScpTriggerEvents.cs index 5241ee9ae0..89903cc41b 100644 --- a/Content.Shared/_Scp/Other/Events/ScpTriggerEvents.cs +++ b/Content.Shared/_Scp/Other/Events/ScpTriggerEvents.cs @@ -1,5 +1,9 @@ +using Content.Shared.Actions; namespace Content.Shared._Scp.Other.Events; -[ByRefEvent] -public sealed partial class ScpActionTriggerAttemptEvent : CancellableEntityEventArgs; +public sealed partial class ScpTriggerOnActionEvent : InstantActionEvent +{ + [DataField] + public string? KeyOut; +} diff --git a/Content.Shared/_Scp/Other/ScpReleaseGas/ScpReleaseGasComponent.cs b/Content.Shared/_Scp/Other/ScpReleaseGas/ScpReleaseGasComponent.cs deleted file mode 100644 index df78486b44..0000000000 --- a/Content.Shared/_Scp/Other/ScpReleaseGas/ScpReleaseGasComponent.cs +++ /dev/null @@ -1,16 +0,0 @@ - -using Content.Shared.Actions.Components; -using Robust.Shared.GameStates; -using Robust.Shared.Prototypes; - -namespace Content.Shared._Scp.Other.ScpReleaseGas; - -[RegisterComponent, NetworkedComponent] -public sealed partial class ScpReleaseGasComponent : Component -{ - [DataField] - public EntProtoId ActionProto = "ScpSmokeAction"; - - [ViewVariables] - public EntityUid? ActionEnt; -} diff --git a/Content.Shared/_Scp/ScpMask/ScpMaskChekerComponent.cs b/Content.Shared/_Scp/ScpMask/ScpMaskChekerComponent.cs new file mode 100644 index 0000000000..36ff88559c --- /dev/null +++ b/Content.Shared/_Scp/ScpMask/ScpMaskChekerComponent.cs @@ -0,0 +1,10 @@ +using Robust.Shared.GameStates; + +namespace Content.Shared._Scp.ScpMask; + +[RegisterComponent, NetworkedComponent] +public sealed partial class ScpMaskCheckerComponent : Component +{ + [DataField] + public bool BlockTriggers = true; +} diff --git a/Content.Shared/_Scp/ScpMask/ScpMaskSystem.Checker.cs b/Content.Shared/_Scp/ScpMask/ScpMaskSystem.Checker.cs new file mode 100644 index 0000000000..b60241c29d --- /dev/null +++ b/Content.Shared/_Scp/ScpMask/ScpMaskSystem.Checker.cs @@ -0,0 +1,24 @@ +using Content.Shared.Trigger; + +namespace Content.Shared._Scp.ScpMask; + +public sealed partial class ScpMaskSystem +{ + private void InitializeChecker() + { + SubscribeLocalEvent(OnAttemptTrigger); + } + + private void OnAttemptTrigger(Entity ent, ref AttemptTriggerEvent args) + { + if (!ent.Comp.BlockTriggers) + return; + + if (TryGetScpMask(ent, out var scpMask)) + { + TryCreatePopup(ent, scpMask); + args.Cancelled = true; + return; + } + } +} diff --git a/Content.Shared/_Scp/ScpMask/ScpMaskSystem.cs b/Content.Shared/_Scp/ScpMask/ScpMaskSystem.cs index 20b65001c2..eb74d2685d 100644 --- a/Content.Shared/_Scp/ScpMask/ScpMaskSystem.cs +++ b/Content.Shared/_Scp/ScpMask/ScpMaskSystem.cs @@ -43,6 +43,7 @@ public override void Initialize() SubscribeLocalEvent(OnDamage); + InitializeChecker(); InitializeEquipment(); InitializeRestrictions(); } diff --git a/Content.Shared/_Scp/Trigger/ScpTriggerOnAction/ScpTriggerOnActionComponent.cs b/Content.Shared/_Scp/Trigger/ScpTriggerOnAction/ScpTriggerOnActionComponent.cs deleted file mode 100644 index bbafc70f4f..0000000000 --- a/Content.Shared/_Scp/Trigger/ScpTriggerOnAction/ScpTriggerOnActionComponent.cs +++ /dev/null @@ -1,14 +0,0 @@ -using Content.Shared.Trigger.Systems; -using Robust.Shared.GameStates; - -namespace Content.Shared._Scp.Trigger.ScpTriggerOnAction; - -[RegisterComponent, NetworkedComponent] -public sealed partial class ScpTriggerOnActionComponent : Component -{ - [DataField] - public string? KeyOut = TriggerSystem.DefaultTriggerKey; - - [DataField] - public bool CheckScpMask; -} diff --git a/Content.Shared/_Scp/Trigger/ScpTriggerOnAction/ScpTriggerOnActionListenerComponent.cs b/Content.Shared/_Scp/Trigger/ScpTriggerOnAction/ScpTriggerOnActionListenerComponent.cs new file mode 100644 index 0000000000..1b87924348 --- /dev/null +++ b/Content.Shared/_Scp/Trigger/ScpTriggerOnAction/ScpTriggerOnActionListenerComponent.cs @@ -0,0 +1,9 @@ +using Robust.Shared.GameStates; + +namespace Content.Shared._Scp.Trigger.ScpTriggerOnAction; + +[RegisterComponent, NetworkedComponent] +public sealed partial class ScpTriggerOnActionListenerComponent : Component +{ + +} diff --git a/Content.Shared/_Scp/Trigger/ScpTriggerOnAction/ScpTriggerOnActionSystem.cs b/Content.Shared/_Scp/Trigger/ScpTriggerOnAction/ScpTriggerOnActionSystem.cs index 790ce7cde0..41022951c1 100644 --- a/Content.Shared/_Scp/Trigger/ScpTriggerOnAction/ScpTriggerOnActionSystem.cs +++ b/Content.Shared/_Scp/Trigger/ScpTriggerOnAction/ScpTriggerOnActionSystem.cs @@ -1,48 +1,20 @@ using Content.Shared._Scp.Other.Events; -using Content.Shared.Trigger; using Content.Shared.Trigger.Systems; -using Content.Shared._Scp.ScpMask; namespace Content.Shared._Scp.Trigger.ScpTriggerOnAction; public sealed class ScpTriggerOnActionSystem : EntitySystem { [Dependency] private readonly TriggerSystem _trigger = default!; - [Dependency] private readonly ScpMaskSystem _scpMask = default!; public override void Initialize() { base.Initialize(); - SubscribeLocalEvent(OnTriggerAttempt); - SubscribeLocalEvent(OnAction); + SubscribeLocalEvent(OnAction); } - private void OnTriggerAttempt(Entity ent, ref AttemptTriggerEvent args) + private void OnAction(Entity ent, ref ScpTriggerOnActionEvent args) { - if (!ent.Comp.CheckScpMask) - return; - - if (args.Key == null) - return; - - if (ent.Comp.KeyOut == null) - return; - - if (ent.Comp.KeyOut != args.Key) - return; - - if (_scpMask.TryGetScpMask(ent, out var scpMask)) - { - _scpMask.TryCreatePopup(ent, scpMask); - args.Cancelled = true; - return; - } - } - - private void OnAction(Entity ent, ref ScpActionTriggerAttemptEvent args) - { - _trigger.Trigger(ent, ent, ent.Comp.KeyOut); - - args.Cancel(); + _trigger.Trigger(ent, ent, args.KeyOut); } } diff --git a/Resources/Prototypes/_Scp/Actions/scp939.yml b/Resources/Prototypes/_Scp/Actions/scp939.yml index 238216cfba..6711ed5bf1 100644 --- a/Resources/Prototypes/_Scp/Actions/scp939.yml +++ b/Resources/Prototypes/_Scp/Actions/scp939.yml @@ -1,17 +1,17 @@ - type: entity parent: RememberPhraseAction - id: Scp939Mimic + id: Scp939MimicAction name: Voice Mimicry description: Allows SCP-939 to mimic voices to lure prey. - type: entity parent: ScpSmokeAction - id: Scp939Smoke + id: Scp939SmokeAction name: Amnestic Gas description: Releases a cloud of amnestic gas to disorient victims. - type: entity parent: ScpSleepAction - id: Scp939Sleep + id: Scp939SleepAction name: Regenerative Slumber description: Allows SCP-939 to enter a deep sleep for rapid healing. diff --git a/Resources/Prototypes/_Scp/Actions/scp_actions.yml b/Resources/Prototypes/_Scp/Actions/scp_actions.yml index bea5568ca6..78121adf58 100644 --- a/Resources/Prototypes/_Scp/Actions/scp_actions.yml +++ b/Resources/Prototypes/_Scp/Actions/scp_actions.yml @@ -21,8 +21,9 @@ icon: sprite: _Scp/Actions/scp-actions.rsi state: smaug - - type: ScpTriggerOnAction - keyOut: scpSmokeTrigger + - type: InstantAction + event: !type:ScpTriggerOnActionEvent + keyOut: scpSmokeTrigger - type: entity id: ScpSleepAction diff --git a/Resources/Prototypes/_Scp/Entities/Mobs/Player/Scp/Main/scp939.yml b/Resources/Prototypes/_Scp/Entities/Mobs/Player/Scp/Main/scp939.yml index af4a5e5ceb..abf7d9444e 100644 --- a/Resources/Prototypes/_Scp/Entities/Mobs/Player/Scp/Main/scp939.yml +++ b/Resources/Prototypes/_Scp/Entities/Mobs/Player/Scp/Main/scp939.yml @@ -41,7 +41,7 @@ components: - Scp939Protection - type: ScpHealOnSleep - actionProto: Scp939Sleep + actionProto: Scp939SleepAction hibernationHealingRate: types: Blunt: -20 @@ -60,9 +60,8 @@ hibernationStates: - Critical - type: ScpRememberPhrase - actionProto: Scp939Mimic - - type: ScpReleaseGas - actionProto: Scp939Smoke + actionProto: Scp939MimicAction + - type: ScpTriggerOnActionListener - type: SmokeOnTrigger keysIn: - scpSmokeTrigger @@ -72,6 +71,10 @@ Quantity: 40 spreadAmount: 10 smokePrototype: АМН-С227Smoke + - type: ScpMaskChecker + - type: ActionGrant + actions: + - Scp939SmokeAction - type: MeleeWeapon altDisarm: false soundHit: From cd03e517667e13fcfbd3a44ed7f96ed1169b53c1 Mon Sep 17 00:00:00 2001 From: Wardex Date: Thu, 7 May 2026 16:41:42 +0300 Subject: [PATCH 18/19] =?UTF-8?q?=D0=BF=D1=83=D0=BF=D1=83=D0=BF=D1=83?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- ...MaskChekerComponent.cs => ScpMaskCheckerComponent.cs} | 0 Content.Shared/_Scp/ScpMask/ScpMaskSystem.Checker.cs | 9 ++++----- .../ScpTriggerOnAction/ScpTriggerOnActionSystem.cs | 3 ++- .../ru-RU/_prototypes/_scp/actions/scp-actions.ftl | 6 ++++++ .../Locale/ru-RU/_prototypes/_scp/actions/scp939.ftl | 6 +++--- 5 files changed, 15 insertions(+), 9 deletions(-) rename Content.Shared/_Scp/ScpMask/{ScpMaskChekerComponent.cs => ScpMaskCheckerComponent.cs} (100%) create mode 100644 Resources/Locale/ru-RU/_prototypes/_scp/actions/scp-actions.ftl diff --git a/Content.Shared/_Scp/ScpMask/ScpMaskChekerComponent.cs b/Content.Shared/_Scp/ScpMask/ScpMaskCheckerComponent.cs similarity index 100% rename from Content.Shared/_Scp/ScpMask/ScpMaskChekerComponent.cs rename to Content.Shared/_Scp/ScpMask/ScpMaskCheckerComponent.cs diff --git a/Content.Shared/_Scp/ScpMask/ScpMaskSystem.Checker.cs b/Content.Shared/_Scp/ScpMask/ScpMaskSystem.Checker.cs index b60241c29d..e054a3641e 100644 --- a/Content.Shared/_Scp/ScpMask/ScpMaskSystem.Checker.cs +++ b/Content.Shared/_Scp/ScpMask/ScpMaskSystem.Checker.cs @@ -14,11 +14,10 @@ private void OnAttemptTrigger(Entity ent, ref AttemptTr if (!ent.Comp.BlockTriggers) return; - if (TryGetScpMask(ent, out var scpMask)) - { - TryCreatePopup(ent, scpMask); - args.Cancelled = true; + if (!TryGetScpMask(ent, out var scpMask)) return; - } + + TryCreatePopup(ent, scpMask); + args.Cancelled = true; } } diff --git a/Content.Shared/_Scp/Trigger/ScpTriggerOnAction/ScpTriggerOnActionSystem.cs b/Content.Shared/_Scp/Trigger/ScpTriggerOnAction/ScpTriggerOnActionSystem.cs index 41022951c1..5e510e2bae 100644 --- a/Content.Shared/_Scp/Trigger/ScpTriggerOnAction/ScpTriggerOnActionSystem.cs +++ b/Content.Shared/_Scp/Trigger/ScpTriggerOnAction/ScpTriggerOnActionSystem.cs @@ -15,6 +15,7 @@ public override void Initialize() private void OnAction(Entity ent, ref ScpTriggerOnActionEvent args) { - _trigger.Trigger(ent, ent, args.KeyOut); + if (_trigger.Trigger(ent, ent, args.KeyOut)) + args.Handled = true; } } diff --git a/Resources/Locale/ru-RU/_prototypes/_scp/actions/scp-actions.ftl b/Resources/Locale/ru-RU/_prototypes/_scp/actions/scp-actions.ftl new file mode 100644 index 0000000000..ed02133603 --- /dev/null +++ b/Resources/Locale/ru-RU/_prototypes/_scp/actions/scp-actions.ftl @@ -0,0 +1,6 @@ +ent-RememberPhraseAction = Имитация голоса + .desc = Позволяет имитировать голоса. +ent-ScpSmokeAction = Выпуск газа + .desc = Выпускает облако газа. +ent-ScpSleepAction = Регенеративный сон + .desc = Позволяет погрузиться в глубокий сон для быстрого исцеления. diff --git a/Resources/Locale/ru-RU/_prototypes/_scp/actions/scp939.ftl b/Resources/Locale/ru-RU/_prototypes/_scp/actions/scp939.ftl index 42e7d0b5dc..4f7c5cd1dd 100644 --- a/Resources/Locale/ru-RU/_prototypes/_scp/actions/scp939.ftl +++ b/Resources/Locale/ru-RU/_prototypes/_scp/actions/scp939.ftl @@ -1,6 +1,6 @@ -ent-Scp939Mimic = Имитация голоса +ent-Scp939MimicAction = { ent-RememberPhraseAction } .desc = Позволяет SCP-939 имитировать голоса для приманивания жертв. -ent-Scp939Smoke = Амнезиаковый газ +ent-Scp939SmokeAction = Амнезиаковый газ .desc = Выпускает облако амнезиакового газа для дезориентации жертв. -ent-Scp939Sleep = Регенеративный сон +ent-Scp939SleepAction = { ent-ScpSleepAction } .desc = Позволяет SCP-939 погрузиться в глубокий сон для быстрого исцеления. From 6eeef1972d5050cfacbe9d2b0d49de8ddd6176e9 Mon Sep 17 00:00:00 2001 From: Wardex Date: Wed, 15 Jul 2026 17:38:38 +0300 Subject: [PATCH 19/19] fixes --- .../ActiveScpOnSoundVisibilityComponent.cs | 9 +- .../ScpOnSoundVisibilityHudSystem.Overlay.cs | 3 +- .../ScpOnSoundVisibilityHudSystem.cs | 85 ++++++++++++- .../ScpHealOnSleep/ScpHealOnSleepComponent.cs | 8 +- .../ScpOnSoundVisibilitySystem.cs | 118 ++++++++++-------- .../ScpOnSoundVisibilityEvents.cs | 10 ++ 6 files changed, 167 insertions(+), 66 deletions(-) rename {Content.Shared => Content.Client}/_Scp/Other/ScpOnSoundVisibility/ActiveScpOnSoundVisibilityComponent.cs (59%) create mode 100644 Content.Shared/_Scp/Other/ScpOnSoundVisibility/ScpOnSoundVisibilityEvents.cs diff --git a/Content.Shared/_Scp/Other/ScpOnSoundVisibility/ActiveScpOnSoundVisibilityComponent.cs b/Content.Client/_Scp/Other/ScpOnSoundVisibility/ActiveScpOnSoundVisibilityComponent.cs similarity index 59% rename from Content.Shared/_Scp/Other/ScpOnSoundVisibility/ActiveScpOnSoundVisibilityComponent.cs rename to Content.Client/_Scp/Other/ScpOnSoundVisibility/ActiveScpOnSoundVisibilityComponent.cs index 40862386aa..b18dc0967d 100644 --- a/Content.Shared/_Scp/Other/ScpOnSoundVisibility/ActiveScpOnSoundVisibilityComponent.cs +++ b/Content.Client/_Scp/Other/ScpOnSoundVisibility/ActiveScpOnSoundVisibilityComponent.cs @@ -1,20 +1,17 @@ -using Robust.Shared.GameStates; +using Content.Shared._Scp.Other.ScpOnSoundVisibility; -namespace Content.Shared._Scp.Other.ScpOnSoundVisibility; +namespace Content.Client._Scp.Other.ScpOnSoundVisibility; -[RegisterComponent, NetworkedComponent, AutoGenerateComponentState(raiseAfterAutoHandleState: true, fieldDeltas: true)] +[RegisterComponent] public sealed partial class ActiveScpOnSoundVisibilityComponent : Component { [ViewVariables] public float VisibilityAcc = ScpOnSoundVisibilityComponent.InitialVisibilityAcc; - [DataField, AutoNetworkedField] public float HideTime = ScpOnSoundVisibilityComponent.DefaultHideTime; - [DataField, AutoNetworkedField] public float MinValue = ScpOnSoundVisibilityComponent.DefaultMinValue; - [DataField, AutoNetworkedField] public float MaxValue = ScpOnSoundVisibilityComponent.DefaultMaxValue; [ViewVariables] diff --git a/Content.Client/_Scp/Other/ScpOnSoundVisibility/ScpOnSoundVisibilityHudSystem.Overlay.cs b/Content.Client/_Scp/Other/ScpOnSoundVisibility/ScpOnSoundVisibilityHudSystem.Overlay.cs index 18b1ec11db..d1e531a89f 100644 --- a/Content.Client/_Scp/Other/ScpOnSoundVisibility/ScpOnSoundVisibilityHudSystem.Overlay.cs +++ b/Content.Client/_Scp/Other/ScpOnSoundVisibility/ScpOnSoundVisibilityHudSystem.Overlay.cs @@ -21,6 +21,7 @@ private void OnPlayerAttached(Entity ent, r private void OnPlayerDetached(Entity ent, ref LocalPlayerDetachedEvent args) { + ClearLocalActiveVisibility(); RemoveOverlays(); } @@ -43,7 +44,7 @@ private void RemoveOverlays() _overlayManager.RemoveOverlay(_setAlphaOverlay); _overlayManager.RemoveOverlay(_resetAlphaOverlay); - CachedBaseAlphas.Clear(); + RestoreCachedBaseAlphas(); _overlaysPresented = false; } } diff --git a/Content.Client/_Scp/Other/ScpOnSoundVisibility/ScpOnSoundVisibilityHudSystem.cs b/Content.Client/_Scp/Other/ScpOnSoundVisibility/ScpOnSoundVisibilityHudSystem.cs index f6361a2095..54b6c29e0f 100644 --- a/Content.Client/_Scp/Other/ScpOnSoundVisibility/ScpOnSoundVisibilityHudSystem.cs +++ b/Content.Client/_Scp/Other/ScpOnSoundVisibility/ScpOnSoundVisibilityHudSystem.cs @@ -25,12 +25,13 @@ public sealed partial class ScpOnSoundVisibilityHudSystem : EquipmentHudSystem Ent, float BaseAlpha)> CachedBaseAlphas = new(64); private EntityQuery _eyeQuery; + private EntityQuery _visibilityQuery; private EntityQuery _movementSpeedQuery; private EntityQuery _physicsQuery; private EntityQuery _viewerQuery; @@ -41,6 +42,8 @@ public sealed partial class ScpOnSoundVisibilityHudSystem : EquipmentHudSystem _visibilityTargetBuffer = []; + private readonly List _visibilityRemovalQueue = []; public override void Initialize() { @@ -61,7 +64,10 @@ public override void Initialize() SubscribeLocalEvent(OnMeleeAttack); SubscribeLocalEvent(OnDown); + SubscribeNetworkEvent(OnVisibilityTargets); + _eyeQuery = GetEntityQuery(); + _visibilityQuery = GetEntityQuery(); _movementSpeedQuery = GetEntityQuery(); _physicsQuery = GetEntityQuery(); _viewerQuery = GetEntityQuery(); @@ -163,7 +169,7 @@ public bool CanDraw(in OverlayDrawArgs args) if (!IsActive) return false; - if (_playerManager.LocalEntity is not { } player) + if (_player.LocalEntity is not { } player) return false; if (!_eyeQuery.TryComp(player, out var eye)) @@ -307,10 +313,83 @@ private bool ModifyAcc(Entity ent, out floa return true; } + private void OnVisibilityTargets(ScpOnSoundVisibilityTargetsEvent ev) + { + if (_player.LocalEntity is not { Valid: true } localPlayer) + return; + + if (GetEntity(ev.Viewer) is not { Valid: true } viewer || viewer != localPlayer) + return; + + _visibilityTargetBuffer.Clear(); + + foreach (var netTarget in ev.Targets) + { + if (GetEntity(netTarget) is not { Valid: true } target) + continue; + + if (!_visibilityQuery.TryComp(target, out var visibility)) + continue; + + _visibilityTargetBuffer.Add(target); + EnsureLocalActiveVisibility((target, visibility)); + } + + _visibilityRemovalQueue.Clear(); + + var query = EntityQueryEnumerator(); + while (query.MoveNext(out var uid, out _)) + { + if (_visibilityTargetBuffer.Contains(uid)) + continue; + + _visibilityRemovalQueue.Add(uid); + } + + foreach (var uid in _visibilityRemovalQueue) + { + RemComp(uid); + } + + _visibilityRemovalQueue.Clear(); + _visibilityTargetBuffer.Clear(); + } + + private void EnsureLocalActiveVisibility(Entity ent) + { + if (!TryComp(ent, out var active)) + { + active = AddComp(ent); + } + + active.HideTime = ent.Comp.HideTime; + active.MinValue = ent.Comp.MinValue; + active.MaxValue = ent.Comp.MaxValue; + } + + public void ClearLocalActiveVisibility() + { + RestoreCachedBaseAlphas(); + _visibilityRemovalQueue.Clear(); + + var query = EntityQueryEnumerator(); + while (query.MoveNext(out var uid, out _)) + { + _visibilityRemovalQueue.Add(uid); + } + + foreach (var uid in _visibilityRemovalQueue) + { + RemComp(uid); + } + + _visibilityRemovalQueue.Clear(); + } + private bool TryGetLocalViewer([NotNullWhen(true)] out Entity? ent) { ent = null; - var localPlayer = _playerManager.LocalEntity; + var localPlayer = _player.LocalEntity; if (localPlayer == null) return false; diff --git a/Content.Server/_Scp/Other/ScpHealOnSleep/ScpHealOnSleepComponent.cs b/Content.Server/_Scp/Other/ScpHealOnSleep/ScpHealOnSleepComponent.cs index 2051d49329..09c11fed09 100644 --- a/Content.Server/_Scp/Other/ScpHealOnSleep/ScpHealOnSleepComponent.cs +++ b/Content.Server/_Scp/Other/ScpHealOnSleep/ScpHealOnSleepComponent.cs @@ -22,16 +22,16 @@ public sealed partial class ScpHealOnSleepComponent : Component public SoundSpecifier? CritSound; [DataField] - public TimeSpan HibernationDurationOnHibernationState = TimeSpan.FromSeconds(360); + public List? HibernationStates; [DataField] - public TimeSpan HibernationDuration = TimeSpan.FromSeconds(60); + public DamageSpecifier? HibernationHealingRate; [DataField] - public List? HibernationStates; + public TimeSpan HibernationDurationOnHibernationState = TimeSpan.FromSeconds(360); [DataField] - public DamageSpecifier? HibernationHealingRate; + public TimeSpan HibernationDuration = TimeSpan.FromSeconds(60); [DataField] public int BaseBloodRefreshAmount = 1; diff --git a/Content.Server/_Scp/Other/ScpOnSoundVisibility/ScpOnSoundVisibilitySystem.cs b/Content.Server/_Scp/Other/ScpOnSoundVisibility/ScpOnSoundVisibilitySystem.cs index e07aa39d70..85f1010c23 100644 --- a/Content.Server/_Scp/Other/ScpOnSoundVisibility/ScpOnSoundVisibilitySystem.cs +++ b/Content.Server/_Scp/Other/ScpOnSoundVisibility/ScpOnSoundVisibilitySystem.cs @@ -2,7 +2,9 @@ using Content.Shared._Scp.Other.ScpOnSoundVisibility; using Content.Shared.Flash; using Content.Shared.Popups; +using Robust.Server.Player; using Robust.Shared.Map; +using Robust.Shared.Player; using Robust.Shared.Timing; namespace Content.Server._Scp.Other.ScpOnSoundVisibility; @@ -11,24 +13,23 @@ public sealed partial class ScpOnSoundVisibilitySystem : EntitySystem { [Dependency] private readonly PopupSystem _popup = default!; [Dependency] private readonly EntityLookupSystem _entityLookup = default!; + [Dependency] private readonly IPlayerManager _player = default!; [Dependency] private readonly IGameTiming _timing = default!; private static readonly TimeSpan VisibilityRefreshInterval = TimeSpan.FromSeconds(0.2f); private TimeSpan _nextVisibilityRefresh = TimeSpan.Zero; - private readonly HashSet _visibilityActiveTargets = []; + private readonly Dictionary> _viewerActiveTargets = []; private readonly HashSet> _visibilityCandidates = []; - private readonly List _visibilityRemovalQueue = []; - - private EntityQuery _activeQuery; + private readonly HashSet _viewerTargetsBuffer = []; + private readonly List _staleViewers = []; + private readonly List _netTargetsBuffer = []; public override void Initialize() { base.Initialize(); SubscribeLocalEvent(OnFlash); - - _activeQuery = GetEntityQuery(); } private void OnFlash(Entity ent, ref AfterFlashedEvent args) @@ -89,74 +90,87 @@ public void UpdateVisibilityTargets() return; _nextVisibilityRefresh = _timing.CurTime + VisibilityRefreshInterval; - _visibilityActiveTargets.Clear(); + _staleViewers.Clear(); + _staleViewers.AddRange(_viewerActiveTargets.Keys); - var scpQuery = EntityQueryEnumerator(); - while (scpQuery.MoveNext(out var _, out var viewer, out var xform)) + var viewerQuery = EntityQueryEnumerator(); + while (viewerQuery.MoveNext(out var uid, out var viewer, out var xform)) { - if (xform.MapID == MapId.Nullspace) - continue; + _staleViewers.Remove(uid); - _visibilityCandidates.Clear(); - _entityLookup.GetEntitiesInRange(xform.Coordinates, - viewer.VisibilityActivationRange, - _visibilityCandidates, - LookupFlags.Dynamic | LookupFlags.Approximate); - - foreach (var target in _visibilityCandidates) + if (!_player.TryGetSessionByEntity(uid, out var session)) { - _visibilityActiveTargets.Add(target); - EnsureActiveVisibility(target); + _viewerActiveTargets.Remove(uid); + continue; } - } - _visibilityRemovalQueue.Clear(); + _viewerTargetsBuffer.Clear(); - var activeQuery = EntityQueryEnumerator(); - while (activeQuery.MoveNext(out var uid, out _)) - { - if (_visibilityActiveTargets.Contains(uid)) - continue; + if (xform.MapID != MapId.Nullspace) + { + _visibilityCandidates.Clear(); + _entityLookup.GetEntitiesInRange(xform.Coordinates, + viewer.VisibilityActivationRange, + _visibilityCandidates, + LookupFlags.Dynamic | LookupFlags.Approximate); + + foreach (var target in _visibilityCandidates) + { + _viewerTargetsBuffer.Add(target); + } + + _visibilityCandidates.Clear(); + } - _visibilityRemovalQueue.Add(uid); + SyncViewerTargets((uid, viewer), session, _viewerTargetsBuffer); } - foreach (var uid in _visibilityRemovalQueue) + foreach (var viewer in _staleViewers) { - RemCompDeferred(uid); + ClearViewerTargets(viewer); } - _visibilityCandidates.Clear(); - _visibilityRemovalQueue.Clear(); + _viewerTargetsBuffer.Clear(); + _staleViewers.Clear(); } - public void EnsureActiveVisibility(Entity ent) + private void SyncViewerTargets( + Entity viewer, + ICommonSession session, + HashSet nextTargets) { - if (!_activeQuery.TryComp(ent, out var active)) + if (!_viewerActiveTargets.TryGetValue(viewer, out var currentTargets)) { - active = AddComp(ent); - active.HideTime = ent.Comp.HideTime; - active.MinValue = ent.Comp.MinValue; - active.MaxValue = ent.Comp.MaxValue; - return; + currentTargets = []; + _viewerActiveTargets[viewer] = currentTargets; } - if (!MathHelper.CloseTo(active.HideTime, ent.Comp.HideTime)) - { - active.HideTime = ent.Comp.HideTime; - DirtyField(ent, active, nameof(ActiveScpOnSoundVisibilityComponent.HideTime)); - } + if (currentTargets.SetEquals(nextTargets)) + return; - if (active.MinValue != ent.Comp.MinValue) + _netTargetsBuffer.Clear(); + foreach (var target in nextTargets) { - active.MinValue = ent.Comp.MinValue; - DirtyField(ent, active, nameof(ActiveScpOnSoundVisibilityComponent.MinValue)); + _netTargetsBuffer.Add(GetNetEntity(target)); } - if (active.MaxValue != ent.Comp.MaxValue) - { - active.MaxValue = ent.Comp.MaxValue; - DirtyField(ent, active, nameof(ActiveScpOnSoundVisibilityComponent.MaxValue)); - } + RaiseNetworkEvent( + new ScpOnSoundVisibilityTargetsEvent(GetNetEntity(viewer.Owner), _netTargetsBuffer.ToArray()), + session); + + currentTargets.Clear(); + currentTargets.UnionWith(nextTargets); + _netTargetsBuffer.Clear(); + } + + private void ClearViewerTargets(EntityUid viewer) + { + if (!_viewerActiveTargets.Remove(viewer, out _)) + return; + + if (!_player.TryGetSessionByEntity(viewer, out var session)) + return; + + RaiseNetworkEvent(new ScpOnSoundVisibilityTargetsEvent(GetNetEntity(viewer), []), session); } } diff --git a/Content.Shared/_Scp/Other/ScpOnSoundVisibility/ScpOnSoundVisibilityEvents.cs b/Content.Shared/_Scp/Other/ScpOnSoundVisibility/ScpOnSoundVisibilityEvents.cs new file mode 100644 index 0000000000..53a6edeea5 --- /dev/null +++ b/Content.Shared/_Scp/Other/ScpOnSoundVisibility/ScpOnSoundVisibilityEvents.cs @@ -0,0 +1,10 @@ +using Robust.Shared.Serialization; + +namespace Content.Shared._Scp.Other.ScpOnSoundVisibility; + +[Serializable, NetSerializable] +public sealed class ScpOnSoundVisibilityTargetsEvent(NetEntity viewer, NetEntity[] targets) : EntityEventArgs +{ + public readonly NetEntity Viewer = viewer; + public readonly NetEntity[] Targets = targets; +}