]> git.smokeofanarchy.ru Git - space-station-14.git/commitdiff
Minor ReflectionSystem refactor (#37039)
authorslarticodefast <161409025+slarticodefast@users.noreply.github.com>
Wed, 30 Apr 2025 14:10:54 +0000 (16:10 +0200)
committerGitHub <noreply@github.com>
Wed, 30 Apr 2025 14:10:54 +0000 (00:10 +1000)
* ReflectComponentLogicFix

Added bool InRightPlace and updated relevant system

* Using SlotFlags

* edits

* refactor

* add missing relay

---------

Co-authored-by: BIGZi0348 <svalker0348@gmail.com>
Content.Shared/Hands/EntitySystems/SharedHandsSystem.Relay.cs
Content.Shared/Inventory/InventorySystem.Relay.cs
Content.Shared/Projectiles/SharedProjectileSystem.cs
Content.Shared/Weapons/Ranged/Events/HitScanReflectAttempt.cs
Content.Shared/Weapons/Reflect/ReflectComponent.cs
Content.Shared/Weapons/Reflect/ReflectSystem.cs
Content.Shared/Weapons/Reflect/ReflectUserComponent.cs [deleted file]
Resources/Prototypes/Entities/Clothing/OuterClothing/armor.yml
Resources/Prototypes/Entities/Objects/Weapons/Melee/sword.yml

index 742e63250101fcf054915f994ac56767868f927d..aa367f71f72f8f856919e46d8b4b3ce40be7b74a 100644 (file)
@@ -2,6 +2,8 @@ using Content.Shared.Atmos;
 using Content.Shared.Camera;
 using Content.Shared.Hands.Components;
 using Content.Shared.Movement.Systems;
+using Content.Shared.Projectiles;
+using Content.Shared.Weapons.Ranged.Events;
 
 namespace Content.Shared.Hands.EntitySystems;
 
@@ -15,6 +17,8 @@ public abstract partial class SharedHandsSystem
 
         // By-ref events.
         SubscribeLocalEvent<HandsComponent, ExtinguishEvent>(RefRelayEvent);
+        SubscribeLocalEvent<HandsComponent, ProjectileReflectAttemptEvent>(RefRelayEvent);
+        SubscribeLocalEvent<HandsComponent, HitScanReflectAttemptEvent>(RefRelayEvent);
     }
 
     private void RelayEvent<T>(Entity<HandsComponent> entity, ref T args) where T : EntityEventArgs
index ce245992a9f60be21ff729cba83932edfca7e694..ed456ed47b1960dca8522046b797b0e931e5b372 100644 (file)
@@ -17,6 +17,7 @@ using Content.Shared.Movement.Events;
 using Content.Shared.Movement.Systems;
 using Content.Shared.NameModifier.EntitySystems;
 using Content.Shared.Overlays;
+using Content.Shared.Projectiles;
 using Content.Shared.Radio;
 using Content.Shared.Slippery;
 using Content.Shared.Strip.Components;
@@ -56,6 +57,8 @@ public partial class InventorySystem
         SubscribeLocalEvent<InventoryComponent, GetSlowedOverSlipperyModifierEvent>(RefRelayInventoryEvent);
         SubscribeLocalEvent<InventoryComponent, ModifySlowOnDamageSpeedEvent>(RefRelayInventoryEvent);
         SubscribeLocalEvent<InventoryComponent, ExtinguishEvent>(RefRelayInventoryEvent);
+        SubscribeLocalEvent<InventoryComponent, ProjectileReflectAttemptEvent>(RefRelayInventoryEvent);
+        SubscribeLocalEvent<InventoryComponent, HitScanReflectAttemptEvent>(RefRelayInventoryEvent);
         SubscribeLocalEvent<InventoryComponent, GetContrabandDetailsEvent>(RefRelayInventoryEvent);
 
         // Eye/vision events
index 3fcff5ae56e9205390cf40eb945d85dfb0a3193a..20d1e051a2995e6e3269b13e075198558083976f 100644 (file)
@@ -4,6 +4,7 @@ using Content.Shared.Damage;
 using Content.Shared.DoAfter;
 using Content.Shared.Hands.EntitySystems;
 using Content.Shared.Interaction;
+using Content.Shared.Inventory;
 using Content.Shared.Mobs.Components;
 using Content.Shared.Throwing;
 using Robust.Shared.Audio.Systems;
@@ -238,7 +239,10 @@ public sealed class ImpactEffectEvent : EntityEventArgs
 /// Raised when an entity is just about to be hit with a projectile but can reflect it
 /// </summary>
 [ByRefEvent]
-public record struct ProjectileReflectAttemptEvent(EntityUid ProjUid, ProjectileComponent Component, bool Cancelled);
+public record struct ProjectileReflectAttemptEvent(EntityUid ProjUid, ProjectileComponent Component, bool Cancelled) : IInventoryRelayEvent
+{
+    SlotFlags IInventoryRelayEvent.TargetSlots => SlotFlags.WITHOUT_POCKET;
+}
 
 /// <summary>
 /// Raised when a projectile hits an entity
index 31c9c4cdd44db7127c2fe7fc97d1c567668c0860..f1a675d5c653bf8a4b13765628ebdda052e9a52d 100644 (file)
@@ -1,4 +1,5 @@
 using System.Numerics;
+using Content.Shared.Inventory;
 using Content.Shared.Weapons.Reflect;
 
 namespace Content.Shared.Weapons.Ranged.Events;
@@ -8,4 +9,7 @@ namespace Content.Shared.Weapons.Ranged.Events;
 /// and changing <see cref="Direction"/> where shot will go next
 /// </summary>
 [ByRefEvent]
-public record struct HitScanReflectAttemptEvent(EntityUid? Shooter, EntityUid SourceItem, ReflectType Reflective, Vector2 Direction, bool Reflected);
+public record struct HitScanReflectAttemptEvent(EntityUid? Shooter, EntityUid SourceItem, ReflectType Reflective, Vector2 Direction, bool Reflected) : IInventoryRelayEvent
+{
+    SlotFlags IInventoryRelayEvent.TargetSlots => SlotFlags.WITHOUT_POCKET;
+}
index 703d8904dc9a533bfe95d154efb76695e91bfed8..ea906810a55feb912c9ef75067c7838ef8554276 100644 (file)
@@ -1,5 +1,7 @@
+using Content.Shared.Inventory;
 using Robust.Shared.Audio;
 using Robust.Shared.GameStates;
+using Robust.Shared.Serialization;
 
 namespace Content.Shared.Weapons.Reflect;
 
@@ -13,23 +15,48 @@ public sealed partial class ReflectComponent : Component
     /// <summary>
     /// What we reflect.
     /// </summary>
-    [ViewVariables(VVAccess.ReadWrite), DataField]
+    [DataField]
     public ReflectType Reflects = ReflectType.Energy | ReflectType.NonEnergy;
 
+    /// <summary>
+    /// Select in which inventory slots it will reflect.
+    /// By default, it will reflect in any inventory position, except pockets.
+    /// </summary>
+    [DataField]
+    public SlotFlags SlotFlags = SlotFlags.WITHOUT_POCKET;
+
+    /// <summary>
+    /// Is it allowed to reflect while being in hands.
+    /// </summary>
+    [DataField, AutoNetworkedField]
+    public bool ReflectingInHands = true;
+
+    /// <summary>
+    /// Can only reflect when placed correctly.
+    /// </summary>
+    [DataField, AutoNetworkedField]
+    public bool InRightPlace;
+
     /// <summary>
     /// Probability for a projectile to be reflected.
     /// </summary>
-    [DataField, ViewVariables(VVAccess.ReadWrite), AutoNetworkedField]
+    [DataField, AutoNetworkedField]
     public float ReflectProb = 0.25f;
 
-    [DataField, ViewVariables(VVAccess.ReadWrite), AutoNetworkedField]
+    /// <summary>
+    /// Probability for a projectile to be reflected.
+    /// </summary>
+    [DataField, AutoNetworkedField]
     public Angle Spread = Angle.FromDegrees(45);
 
+    /// <summary>
+    /// The sound to play when reflecting.
+    /// </summary>
     [DataField]
     public SoundSpecifier? SoundOnReflect = new SoundPathSpecifier("/Audio/Weapons/Guns/Hits/laser_sear_wall.ogg", AudioParams.Default.WithVariation(0.05f));
 }
 
-[Flags]
+[Flags, Serializable, NetSerializable]
 public enum ReflectType : byte
 {
     None = 0,
index 3d3ded99ceb1a528d63882613f557cca2400c4dc..1ca2cef4e453f4dfb2620d62012201f518c78068 100644 (file)
@@ -1,25 +1,20 @@
 using System.Diagnostics.CodeAnalysis;
 using System.Numerics;
 using Content.Shared.Administration.Logs;
-using Content.Shared.Alert;
-using Content.Shared.Audio;
 using Content.Shared.Database;
 using Content.Shared.Hands;
 using Content.Shared.Inventory;
 using Content.Shared.Inventory.Events;
 using Content.Shared.Item.ItemToggle;
-using Content.Shared.Item.ItemToggle.Components;
 using Content.Shared.Popups;
 using Content.Shared.Projectiles;
 using Content.Shared.Weapons.Ranged.Components;
 using Content.Shared.Weapons.Ranged.Events;
-using Robust.Shared.Audio;
 using Robust.Shared.Audio.Systems;
 using Robust.Shared.Network;
 using Robust.Shared.Physics.Components;
 using Robust.Shared.Physics.Systems;
 using Robust.Shared.Random;
-using Robust.Shared.Timing;
 
 namespace Content.Shared.Weapons.Reflect;
 
@@ -28,7 +23,6 @@ namespace Content.Shared.Weapons.Reflect;
 /// </summary>
 public sealed class ReflectSystem : EntitySystem
 {
-    [Dependency] private readonly IGameTiming _gameTiming = default!;
     [Dependency] private readonly INetManager _netManager = default!;
     [Dependency] private readonly IRobustRandom _random = default!;
     [Dependency] private readonly ISharedAdminLogManager _adminLogger = default!;
@@ -37,74 +31,82 @@ public sealed class ReflectSystem : EntitySystem
     [Dependency] private readonly SharedPhysicsSystem _physics = default!;
     [Dependency] private readonly SharedAudioSystem _audio = default!;
     [Dependency] private readonly SharedTransformSystem _transform = default!;
-    [Dependency] private readonly InventorySystem _inventorySystem = default!;
 
     public override void Initialize()
     {
         base.Initialize();
 
+        Subs.SubscribeWithRelay<ReflectComponent, ProjectileReflectAttemptEvent>(OnReflectUserCollide, baseEvent: false);
+        Subs.SubscribeWithRelay<ReflectComponent, HitScanReflectAttemptEvent>(OnReflectUserHitscan, baseEvent: false);
         SubscribeLocalEvent<ReflectComponent, ProjectileReflectAttemptEvent>(OnReflectCollide);
         SubscribeLocalEvent<ReflectComponent, HitScanReflectAttemptEvent>(OnReflectHitscan);
+
         SubscribeLocalEvent<ReflectComponent, GotEquippedEvent>(OnReflectEquipped);
         SubscribeLocalEvent<ReflectComponent, GotUnequippedEvent>(OnReflectUnequipped);
         SubscribeLocalEvent<ReflectComponent, GotEquippedHandEvent>(OnReflectHandEquipped);
         SubscribeLocalEvent<ReflectComponent, GotUnequippedHandEvent>(OnReflectHandUnequipped);
-        SubscribeLocalEvent<ReflectComponent, ItemToggledEvent>(OnToggleReflect);
+    }
+
+    private void OnReflectUserCollide(Entity<ReflectComponent> ent, ref ProjectileReflectAttemptEvent args)
+    {
+        if (args.Cancelled)
+            return;
 
-        SubscribeLocalEvent<ReflectUserComponent, ProjectileReflectAttemptEvent>(OnReflectUserCollide);
-        SubscribeLocalEvent<ReflectUserComponent, HitScanReflectAttemptEvent>(OnReflectUserHitscan);
+        if (!ent.Comp.InRightPlace)
+            return; // only reflect when equipped correctly
+
+        if (TryReflectProjectile(ent, ent.Owner, args.ProjUid))
+            args.Cancelled = true;
     }
 
-    private void OnReflectUserHitscan(EntityUid uid, ReflectUserComponent component, ref HitScanReflectAttemptEvent args)
+    private void OnReflectUserHitscan(Entity<ReflectComponent> ent, ref HitScanReflectAttemptEvent args)
     {
         if (args.Reflected)
             return;
 
-        foreach (var ent in _inventorySystem.GetHandOrInventoryEntities(uid, SlotFlags.All & ~SlotFlags.POCKET))
-        {
-            if (!TryReflectHitscan(uid, ent, args.Shooter, args.SourceItem, args.Direction, args.Reflective, out var dir))
-                continue;
+        if (!ent.Comp.InRightPlace)
+            return; // only reflect when equipped correctly
 
+        if (TryReflectHitscan(ent, ent.Owner, args.Shooter, args.SourceItem, args.Direction, args.Reflective, out var dir))
+        {
             args.Direction = dir.Value;
             args.Reflected = true;
-            break;
         }
     }
 
-    private void OnReflectUserCollide(EntityUid uid, ReflectUserComponent component, ref ProjectileReflectAttemptEvent args)
+    private void OnReflectCollide(Entity<ReflectComponent> ent, ref ProjectileReflectAttemptEvent args)
     {
-        foreach (var ent in _inventorySystem.GetHandOrInventoryEntities(uid, SlotFlags.All & ~SlotFlags.POCKET))
-        {
-            if (!TryReflectProjectile(uid, ent, args.ProjUid))
-                continue;
+        if (args.Cancelled)
+            return;
 
+        if (TryReflectProjectile(ent, ent.Owner, args.ProjUid))
             args.Cancelled = true;
-            break;
-        }
     }
 
-    private void OnReflectCollide(EntityUid uid, ReflectComponent component, ref ProjectileReflectAttemptEvent args)
+    private void OnReflectHitscan(Entity<ReflectComponent> ent, ref HitScanReflectAttemptEvent args)
     {
-        if (args.Cancelled)
+        if (args.Reflected)
             return;
 
-        if (TryReflectProjectile(uid, uid, args.ProjUid, reflect: component))
-            args.Cancelled = true;
+        if (TryReflectHitscan(ent, ent.Owner, args.Shooter, args.SourceItem, args.Direction, args.Reflective, out var dir))
+        {
+            args.Direction = dir.Value;
+            args.Reflected = true;
+        }
     }
 
-    private bool TryReflectProjectile(EntityUid user, EntityUid reflector, EntityUid projectile, ProjectileComponent? projectileComp = null, ReflectComponent? reflect = null)
+    private bool TryReflectProjectile(Entity<ReflectComponent> reflector, EntityUid user, Entity<ProjectileComponent?> projectile)
     {
-        if (!Resolve(reflector, ref reflect, false) ||
-            !TryComp<ReflectiveComponent>(projectile, out var reflective) ||
-            (reflect.Reflects & reflective.Reflective) == 0x0 ||
-            !_toggle.IsActivated(reflector) ||
-            !_random.Prob(reflect.ReflectProb) ||
+        if (!TryComp<ReflectiveComponent>(projectile, out var reflective) ||
+            (reflector.Comp.Reflects & reflective.Reflective) == 0x0 ||
+            !_toggle.IsActivated(reflector.Owner) ||
+            !_random.Prob(reflector.Comp.ReflectProb) ||
             !TryComp<PhysicsComponent>(projectile, out var physics))
         {
             return false;
         }
 
-        var rotation = _random.NextAngle(-reflect.Spread / 2, reflect.Spread / 2).Opposite();
+        var rotation = _random.NextAngle(-reflector.Comp.Spread / 2, reflector.Comp.Spread / 2).Opposite();
         var existingVelocity = _physics.GetMapLinearVelocity(projectile, component: physics);
         var relativeVelocity = existingVelocity - _physics.GetMapLinearVelocity(user);
         var newVelocity = rotation.RotateVec(relativeVelocity);
@@ -118,15 +120,15 @@ public sealed class ReflectSystem : EntitySystem
         var newRot = rotation.RotateVec(locRot.ToVec());
         _transform.SetLocalRotation(projectile, newRot.ToAngle());
 
-        PlayAudioAndPopup(reflect, user);
+        PlayAudioAndPopup(reflector.Comp, user);
 
-        if (Resolve(projectile, ref projectileComp, false))
+        if (Resolve(projectile, ref projectile.Comp, false))
         {
-            _adminLogger.Add(LogType.BulletHit, LogImpact.Medium, $"{ToPrettyString(user)} reflected {ToPrettyString(projectile)} from {ToPrettyString(projectileComp.Weapon)} shot by {projectileComp.Shooter}");
+            _adminLogger.Add(LogType.BulletHit, LogImpact.Medium, $"{ToPrettyString(user)} reflected {ToPrettyString(projectile)} from {ToPrettyString(projectile.Comp.Weapon)} shot by {projectile.Comp.Shooter}");
 
-            projectileComp.Shooter = user;
-            projectileComp.Weapon = user;
-            Dirty(projectile, projectileComp);
+            projectile.Comp.Shooter = user;
+            projectile.Comp.Weapon = user;
+            Dirty(projectile, projectile.Comp);
         }
         else
         {
@@ -135,52 +137,26 @@ public sealed class ReflectSystem : EntitySystem
 
         return true;
     }
-
-    private void OnReflectHitscan(EntityUid uid, ReflectComponent component, ref HitScanReflectAttemptEvent args)
-    {
-        if (args.Reflected)
-        {
-            return;
-        }
-
-        if (TryReflectHitscan(uid, uid, args.Shooter, args.SourceItem, args.Direction, args.Reflective, out var dir))
-        {
-            args.Direction = dir.Value;
-            args.Reflected = true;
-        }
-    }
-
-    private void PlayAudioAndPopup(ReflectComponent reflect, EntityUid user)
-    {
-        // Can probably be changed for prediction
-        if (_netManager.IsServer)
-        {
-            _popup.PopupEntity(Loc.GetString("reflect-shot"), user);
-            _audio.PlayPvs(reflect.SoundOnReflect, user);
-        }
-    }
-
     private bool TryReflectHitscan(
+        Entity<ReflectComponent> reflector,
         EntityUid user,
-        EntityUid reflector,
         EntityUid? shooter,
         EntityUid shotSource,
         Vector2 direction,
         ReflectType hitscanReflectType,
         [NotNullWhen(true)] out Vector2? newDirection)
     {
-        if (!TryComp<ReflectComponent>(reflector, out var reflect) ||
-            (reflect.Reflects & hitscanReflectType) == 0x0 ||
-            !_toggle.IsActivated(reflector) ||
-            !_random.Prob(reflect.ReflectProb))
+        if ((reflector.Comp.Reflects & hitscanReflectType) == 0x0 ||
+            !_toggle.IsActivated(reflector.Owner) ||
+            !_random.Prob(reflector.Comp.ReflectProb))
         {
             newDirection = null;
             return false;
         }
 
-        PlayAudioAndPopup(reflect, user);
+        PlayAudioAndPopup(reflector.Comp, user);
 
-        var spread = _random.NextAngle(-reflect.Spread / 2, reflect.Spread / 2);
+        var spread = _random.NextAngle(-reflector.Comp.Spread / 2, reflector.Comp.Spread / 2);
         newDirection = -spread.RotateVec(direction);
 
         if (shooter != null)
@@ -191,52 +167,37 @@ public sealed class ReflectSystem : EntitySystem
         return true;
     }
 
-    private void OnReflectEquipped(EntityUid uid, ReflectComponent component, GotEquippedEvent args)
-    {
-        if (_gameTiming.ApplyingState)
-            return;
-
-        EnsureComp<ReflectUserComponent>(args.Equipee);
-    }
-
-    private void OnReflectUnequipped(EntityUid uid, ReflectComponent comp, GotUnequippedEvent args)
+    private void PlayAudioAndPopup(ReflectComponent reflect, EntityUid user)
     {
-        RefreshReflectUser(args.Equipee);
+        // Can probably be changed for prediction
+        if (_netManager.IsServer)
+        {
+            _popup.PopupEntity(Loc.GetString("reflect-shot"), user);
+            _audio.PlayPvs(reflect.SoundOnReflect, user);
+        }
     }
 
-    private void OnReflectHandEquipped(EntityUid uid, ReflectComponent component, GotEquippedHandEvent args)
+    private void OnReflectEquipped(Entity<ReflectComponent> ent, ref GotEquippedEvent args)
     {
-        if (_gameTiming.ApplyingState)
-            return;
-
-        EnsureComp<ReflectUserComponent>(args.User);
+        ent.Comp.InRightPlace = (ent.Comp.SlotFlags & args.SlotFlags) == args.SlotFlags;
+        Dirty(ent);
     }
 
-    private void OnReflectHandUnequipped(EntityUid uid, ReflectComponent component, GotUnequippedHandEvent args)
+    private void OnReflectUnequipped(Entity<ReflectComponent> ent, ref GotUnequippedEvent args)
     {
-        RefreshReflectUser(args.User);
+        ent.Comp.InRightPlace = false;
+        Dirty(ent);
     }
 
-    private void OnToggleReflect(EntityUid uid, ReflectComponent comp, ref ItemToggledEvent args)
+    private void OnReflectHandEquipped(Entity<ReflectComponent> ent, ref GotEquippedHandEvent args)
     {
-        if (args.User is {} user)
-            RefreshReflectUser(user);
+        ent.Comp.InRightPlace = ent.Comp.ReflectingInHands;
+        Dirty(ent);
     }
 
-    /// <summary>
-    /// Refreshes whether someone has reflection potential so we can raise directed events on them.
-    /// </summary>
-    private void RefreshReflectUser(EntityUid user)
+    private void OnReflectHandUnequipped(Entity<ReflectComponent> ent, ref GotUnequippedHandEvent args)
     {
-        foreach (var ent in _inventorySystem.GetHandOrInventoryEntities(user, SlotFlags.All & ~SlotFlags.POCKET))
-        {
-            if (!HasComp<ReflectComponent>(ent) || !_toggle.IsActivated(ent))
-                continue;
-
-            EnsureComp<ReflectUserComponent>(user);
-            return;
-        }
-
-        RemCompDeferred<ReflectUserComponent>(user);
+        ent.Comp.InRightPlace = false;
+        Dirty(ent);
     }
 }
diff --git a/Content.Shared/Weapons/Reflect/ReflectUserComponent.cs b/Content.Shared/Weapons/Reflect/ReflectUserComponent.cs
deleted file mode 100644 (file)
index 44ef481..0000000
+++ /dev/null
@@ -1,10 +0,0 @@
-using Robust.Shared.GameStates;
-
-namespace Content.Shared.Weapons.Reflect;
-
-/// <summary>
-/// Added to an entity if it equips a reflection item in a hand slot or into its clothing.
-/// Reflection events will then be relayed.
-/// </summary>
-[RegisterComponent, NetworkedComponent]
-public sealed partial class ReflectUserComponent : Component;
index 243759c8714ba7de13118b9967934e6433500ca9..07bf9ad35189fd70782189bbc4a608f1abff0235 100644 (file)
@@ -83,6 +83,7 @@
     reflectProb: 1
     reflects:
       - Energy
+    reflectingInHands: false
 
 #Detective's vest
 - type: entity
index f4d4c5a918c7b091ae38158724e8e72f2d8a89ef..086052f77a212629cc02ad40f28d1c1afa068fa5 100644 (file)
     - Back
     - Belt
   - type: Reflect
+    slotFlags:
+    - NONE
 
 #Greatswords
 - type: entity