From 5e400e7b01505d0ef913e42c9c09ec30072243fe Mon Sep 17 00:00:00 2001 From: metalgearsloth Date: Sat, 22 Jun 2024 14:45:53 +1000 Subject: [PATCH 1/4] Revert "Weapon Reflection Movement Mechanic (#27219)" This reverts commit b90373356e7f4f0eee693732964eac9c9eaa1f02. # Conflicts: # Content.Shared/Alert/AlertType.cs # Content.Shared/Weapons/Reflect/ReflectSystem.cs --- .../Weapons/Reflect/ReflectComponent.cs | 35 ++-------- .../Weapons/Reflect/ReflectSystem.cs | 64 +++++++----------- Resources/Locale/en-US/alerts/alerts.ftl | 3 - Resources/Prototypes/Alerts/alerts.yml | 9 --- Resources/Prototypes/Anomaly/behaviours.yml | 1 - .../Entities/Clothing/OuterClothing/armor.yml | 1 - .../Entities/Mobs/NPCs/hellspawn.yml | 1 - .../Entities/Objects/Shields/shields.yml | 4 +- .../Objects/Weapons/Melee/e_sword.yml | 7 +- .../Entities/Objects/Weapons/Melee/sword.yml | 21 ++---- .../Alerts/deflecting.rsi/deflecting0.png | Bin 1761 -> 0 bytes .../Interface/Alerts/deflecting.rsi/meta.json | 14 ---- 12 files changed, 39 insertions(+), 121 deletions(-) delete mode 100644 Resources/Textures/Interface/Alerts/deflecting.rsi/deflecting0.png delete mode 100644 Resources/Textures/Interface/Alerts/deflecting.rsi/meta.json diff --git a/Content.Shared/Weapons/Reflect/ReflectComponent.cs b/Content.Shared/Weapons/Reflect/ReflectComponent.cs index 5d8432ac776c..8e7b8975d9d1 100644 --- a/Content.Shared/Weapons/Reflect/ReflectComponent.cs +++ b/Content.Shared/Weapons/Reflect/ReflectComponent.cs @@ -21,42 +21,17 @@ public sealed partial class ReflectComponent : Component [ViewVariables(VVAccess.ReadWrite), DataField("reflects")] public ReflectType Reflects = ReflectType.Energy | ReflectType.NonEnergy; - [DataField("spread"), ViewVariables(VVAccess.ReadWrite), AutoNetworkedField] - public Angle Spread = Angle.FromDegrees(45); - - [DataField("soundOnReflect")] - public SoundSpecifier? SoundOnReflect = new SoundPathSpecifier("/Audio/Weapons/Guns/Hits/laser_sear_wall.ogg"); - /// - /// Is the deflection an innate power or something actively maintained? If true, this component grants a flat - /// deflection chance rather than a chance that degrades when moving/weightless/stunned/etc. - /// - [DataField] - public bool Innate = false; - - /// - /// Maximum probability for a projectile to be reflected. + /// Probability for a projectile to be reflected. /// [DataField("reflectProb"), ViewVariables(VVAccess.ReadWrite), AutoNetworkedField] public float ReflectProb = 0.25f; - /// - /// The maximum velocity a wielder can move at before losing effectiveness. - /// - [DataField] - public float VelocityBeforeNotMaxProb = 2.5f; // Walking speed for a human. Suitable for a weightless deflector like an e-sword. - - /// - /// The velocity a wielder has to be moving at to use the minimum effectiveness value. - /// - [DataField] - public float VelocityBeforeMinProb = 4.5f; // Sprinting speed for a human. Suitable for a weightless deflector like an e-sword. + [DataField("spread"), ViewVariables(VVAccess.ReadWrite), AutoNetworkedField] + public Angle Spread = Angle.FromDegrees(45); - /// - /// Minimum probability for a projectile to be reflected. - /// - [DataField] - public float MinReflectProb = 0.1f; + [DataField("soundOnReflect")] + public SoundSpecifier? SoundOnReflect = new SoundPathSpecifier("/Audio/Weapons/Guns/Hits/laser_sear_wall.ogg"); } [Flags] diff --git a/Content.Shared/Weapons/Reflect/ReflectSystem.cs b/Content.Shared/Weapons/Reflect/ReflectSystem.cs index 9b89be6202ad..208dfb47ffff 100644 --- a/Content.Shared/Weapons/Reflect/ReflectSystem.cs +++ b/Content.Shared/Weapons/Reflect/ReflectSystem.cs @@ -1,20 +1,17 @@ using System.Diagnostics.CodeAnalysis; using System.Numerics; using Content.Shared.Administration.Logs; -using Content.Shared.Alert; using Content.Shared.Audio; -using Content.Shared.Damage.Components; using Content.Shared.Database; -using Content.Shared.Gravity; using Content.Shared.Hands; using Content.Shared.Inventory; using Content.Shared.Inventory.Events; using Content.Shared.Item.ItemToggle.Components; using Content.Shared.Popups; using Content.Shared.Projectiles; -using Content.Shared.Standing; 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; @@ -38,9 +35,6 @@ public sealed class ReflectSystem : EntitySystem [Dependency] private readonly SharedAudioSystem _audio = default!; [Dependency] private readonly SharedTransformSystem _transform = default!; [Dependency] private readonly InventorySystem _inventorySystem = default!; - [Dependency] private readonly SharedGravitySystem _gravity = default!; - [Dependency] private readonly StandingStateSystem _standing = default!; - [Dependency] private readonly AlertsSystem _alerts = default!; [ValidatePrototypeId] private const string DeflectingAlert = "Deflecting"; @@ -145,24 +139,15 @@ private bool UserCanReflect(Entity user, [NotNullWhen(true private bool TryReflectProjectile(EntityUid user, Entity reflector, Entity projectile) { - if ( - // Is it on? - !reflector.Comp.Enabled || - // Is the projectile deflectable? + if (!Resolve(reflector, ref reflect, false) || + !reflect.Enabled || !TryComp(projectile, out var reflective) || - // Does the deflector deflect the type of projecitle? - (reflector.Comp.Reflects & reflective.Reflective) == 0x0 || - // Is the projectile correctly set up with physics? - !TryComp(projectile, out var physics) || - // If the user of the reflector is a mob with stamina, is it capable of deflecting? - TryComp(user, out var staminaComponent) && staminaComponent.Critical || - _standing.IsDown(reflector) - ) - return false; - - // If this dice roll fails, the shot isn't deflected - if (!_random.Prob(GetReflectChance(reflector))) + (reflect.Reflects & reflective.Reflective) == 0x0 || + !_random.Prob(reflect.ReflectProb) || + !TryComp(projectile, out var physics)) + { return false; + } // Below handles what happens after being deflected. var rotation = _random.NextAngle(-reflector.Comp.Spread / 2, reflector.Comp.Spread / 2).Opposite(); @@ -194,6 +179,21 @@ private bool TryReflectProjectile(EntityUid user, Entity refle return true; } + private void OnReflectHitscan(EntityUid uid, ReflectComponent component, ref HitScanReflectAttemptEvent args) + { + if (args.Reflected || + (component.Reflects & args.Reflective) == 0x0) + { + return; + } + + if (TryReflectHitscan(uid, uid, args.Shooter, args.SourceItem, args.Direction, out var dir)) + { + args.Direction = dir.Value; + args.Reflected = true; + } + } + private bool TryReflectHitscan( EntityUid user, Entity reflector, @@ -202,19 +202,9 @@ private bool TryReflectHitscan( Vector2 direction, [NotNullWhen(true)] out Vector2? newDirection) { - if ( - // Is the reflector enabled? - !reflector.Comp.Enabled || - // If the user is a mob with stamina, is it capable of deflecting? - TryComp(user, out var staminaComponent) && staminaComponent.Critical || - _standing.IsDown(user)) - { - newDirection = null; - return false; - } - - // If this dice roll fails, the shot is not deflected. - if (!_random.Prob(GetReflectChance(reflector))) + if (!TryComp(reflector, out var reflect) || + !reflect.Enabled || + !_random.Prob(reflect.ReflectProb)) { newDirection = null; return false; @@ -322,8 +312,6 @@ private void RefreshReflectUser(EntityUid user) continue; EnsureComp(user); - EnableAlert(user); - return; } diff --git a/Resources/Locale/en-US/alerts/alerts.ftl b/Resources/Locale/en-US/alerts/alerts.ftl index 24bc60cbf153..319809da40a2 100644 --- a/Resources/Locale/en-US/alerts/alerts.ftl +++ b/Resources/Locale/en-US/alerts/alerts.ftl @@ -107,6 +107,3 @@ alerts-revenant-essence-desc = The power of souls. It sustains you and is used f alerts-revenant-corporeal-name = Corporeal alerts-revenant-corporeal-desc = You have manifested physically. People around you can see and hurt you. - -alerts-deflecting-name = Deflecting -alerts-deflecting-desc = You have a chance to deflect incoming projectiles. Standing still or moving slowly will increase this chance. diff --git a/Resources/Prototypes/Alerts/alerts.yml b/Resources/Prototypes/Alerts/alerts.yml index 72412fde7cc3..c08453460f4f 100644 --- a/Resources/Prototypes/Alerts/alerts.yml +++ b/Resources/Prototypes/Alerts/alerts.yml @@ -24,7 +24,6 @@ - category: Thirst - alertType: Magboots - alertType: Pacified - - alertType: Deflecting - type: entity id: AlertSpriteView @@ -475,11 +474,3 @@ state: critical name: Debug6 description: Debug - -- type: alert - id: Deflecting - icons: - - sprite: /Textures/Interface/Alerts/deflecting.rsi - state: deflecting0 - name: alerts-deflecting-name - description: alerts-deflecting-desc diff --git a/Resources/Prototypes/Anomaly/behaviours.yml b/Resources/Prototypes/Anomaly/behaviours.yml index e39933c365c9..dea1ddb69c36 100644 --- a/Resources/Prototypes/Anomaly/behaviours.yml +++ b/Resources/Prototypes/Anomaly/behaviours.yml @@ -84,7 +84,6 @@ description: anomaly-behavior-reflect components: - type: Reflect - innate: true reflectProb: 0.5 reflects: - Energy diff --git a/Resources/Prototypes/Entities/Clothing/OuterClothing/armor.yml b/Resources/Prototypes/Entities/Clothing/OuterClothing/armor.yml index 825e729115cd..606af0b127ab 100644 --- a/Resources/Prototypes/Entities/Clothing/OuterClothing/armor.yml +++ b/Resources/Prototypes/Entities/Clothing/OuterClothing/armor.yml @@ -96,7 +96,6 @@ Heat: 0.4 # this technically means it protects against fires pretty well? -heat is just for lasers and stuff, not atmos temperature - type: Reflect reflectProb: 1 - innate: true # armor grants a passive shield that does not require concentration to maintain reflects: - Energy diff --git a/Resources/Prototypes/Entities/Mobs/NPCs/hellspawn.yml b/Resources/Prototypes/Entities/Mobs/NPCs/hellspawn.yml index e0a3a7645619..45fb6a12bdd4 100644 --- a/Resources/Prototypes/Entities/Mobs/NPCs/hellspawn.yml +++ b/Resources/Prototypes/Entities/Mobs/NPCs/hellspawn.yml @@ -56,7 +56,6 @@ - type: Perishable - type: Reflect reflectProb: 0.7 - innate: true reflects: - Energy - type: Fixtures diff --git a/Resources/Prototypes/Entities/Objects/Shields/shields.yml b/Resources/Prototypes/Entities/Objects/Shields/shields.yml index e7ebb1b98d45..b794e42ff7d3 100644 --- a/Resources/Prototypes/Entities/Objects/Shields/shields.yml +++ b/Resources/Prototypes/Entities/Objects/Shields/shields.yml @@ -313,7 +313,7 @@ name: mirror shield parent: BaseShield id: MirrorShield - description: Glows an eerie red. You hear the Geometer whispering... + description: Eerily glows red... you hear the geometer whispering components: - type: Sprite state: mirror-icon @@ -321,7 +321,6 @@ heldPrefix: mirror - type: Reflect reflectProb: 0.95 - innate: true reflects: - Energy - type: Blocking #Mirror shield reflects heat/laser, but is relatively weak to everything else. @@ -409,7 +408,6 @@ - type: Reflect enabled: false reflectProb: 0.95 - innate: true reflects: - Energy - type: Blocking diff --git a/Resources/Prototypes/Entities/Objects/Weapons/Melee/e_sword.yml b/Resources/Prototypes/Entities/Objects/Weapons/Melee/e_sword.yml index 592fe56b4758..fbf8b1003c35 100644 --- a/Resources/Prototypes/Entities/Objects/Weapons/Melee/e_sword.yml +++ b/Resources/Prototypes/Entities/Objects/Weapons/Melee/e_sword.yml @@ -78,8 +78,6 @@ malus: 0 - type: Reflect enabled: false - reflectProb: 0.5 - minReflectProb: 0.25 - type: IgnitionSource temperature: 700 @@ -220,7 +218,7 @@ name: double-bladed energy sword parent: EnergySword id: EnergySwordDouble - description: Syndicate Command's intern thought that having only one blade on energy swords was not cool enough. This can be stored in pockets. + description: Syndicate Command Interns thought that having one blade on the energy sword was not enough. This can be stored in pockets. components: - type: EnergySword - type: ItemToggle @@ -271,8 +269,7 @@ size: Small sprite: Objects/Weapons/Melee/e_sword_double-inhands.rsi - type: Reflect - reflectProb: .80 - minReflectProb: .65 + reflectProb: .75 spread: 75 - type: UseDelay delay: 1 diff --git a/Resources/Prototypes/Entities/Objects/Weapons/Melee/sword.yml b/Resources/Prototypes/Entities/Objects/Weapons/Melee/sword.yml index 11e7f983e054..7cc33b715501 100644 --- a/Resources/Prototypes/Entities/Objects/Weapons/Melee/sword.yml +++ b/Resources/Prototypes/Entities/Objects/Weapons/Melee/sword.yml @@ -13,17 +13,12 @@ attackRate: 1.5 damage: types: - Slash: 15 + Slash: 17 #cmon, it has to be at least BETTER than the rest. soundHit: path: /Audio/Weapons/bladeslice.ogg - type: Reflect enabled: true - # Design intent: a robust captain or tot can sacrifice movement to make the most of this weapon, but they have to - # really restrict themselves to walking speed or less. - reflectProb: 0.5 - velocityBeforeNotMaxProb: 1.0 - velocityBeforeMinProb: 3.0 - minReflectProb: 0.1 + reflectProb: .1 spread: 90 - type: Item size: Normal @@ -88,9 +83,6 @@ - Back - Belt - type: Reflect - reflectProb: 0.3 - velocityBeforeNotMaxProb: 6.0 # don't punish ninjas for being ninjas - velocityBeforeMinProb: 10.0 - type: entity name: machete @@ -160,7 +152,7 @@ wideAnimationRotation: -135 damage: types: - Slash: 15 + Slash: 16 soundHit: path: /Audio/Weapons/bladeslice.ogg - type: Item @@ -172,7 +164,7 @@ name: The Throngler parent: BaseItem id: Throngler - description: Why would someone make this? + description: Why would you make this? components: - type: Sharp - type: Sprite @@ -193,10 +185,7 @@ path: /Audio/Effects/explosion_small1.ogg - type: Reflect enabled: true - reflectProb: 0.5 # In robust hands, deflects as well as an e-sword - velocityBeforeNotMaxProb: 1.0 - velocityBeforeMinProb: 3.0 - minReflectProb: 0.1 + reflectProb: .25 spread: 90 - type: Item size: Ginormous diff --git a/Resources/Textures/Interface/Alerts/deflecting.rsi/deflecting0.png b/Resources/Textures/Interface/Alerts/deflecting.rsi/deflecting0.png deleted file mode 100644 index 37404e77f76444946eaa481b8f4ec861f85736c6..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 1761 zcmeAS@N?(olHy`uVBq!ia0vp^3LwnE1|*BCs=fdz#^NA%Cx&(BWL^R}e5nzhX}-P; zT0k}j5QD&_;K@Lev%n*=n1MmW7law7oa)tQU|_Dx42dX-@b$4u&d=3LOvz75)vL%Y z0PC`;umUo3Q%e#RDspr3imfVamB1>jfNYSkzLEl1NlCV?QiN}Sf^&XRs)CuGfu4bq z9hZWFf=y9MnpKdC8&o@xXRDM^Qc_^0uU}qXu2*iXmtT~wZ)j<0sc&GUZ)BtkRH0j3 znOBlnp_^B%3^4>|j!SBBa#3bMNoIbY0?6FNr2NtnTO}osMQ{LdXG${Mo`TY%9I!1Z z$@-}|sky0nCB^!NdWQPg^p#|$AzYYO3=Ixo!03ZyfZ7bOYV#~8Nj3q7lxqdhJy8Dv z9hwZbx40xlA4!3}k%57Qu7Q!Rk)=M|e?aHkq$FFFWR~Qlf&&ijA8-gd=9Hj{g4Bb8 zASV+PvQ{~XdFi%F6}l;@X^EvdB}#Uod0?Yb6da36%JYk|ZS*0kQB8q}q8e_akHsA} zAm3X>2Bj9~=ahoN-_Fq3$OarHD58j%far+8ssmXRT}MDhen~zsWff&6d*+p-78Mi$ zQyJJsn0>fapqquTJTxz}#13WvnlO?sq*$_o23!a@MlgdDr&6eKkf23Mv5@Qljs{?U zu;a4PhvzLjuD|Ez{a|2VdgSTi7!twxHf(>ll%q)9>g)3aH%&4L?C@0f=sKY3^-_5x?PXMg=&dwlOg7bU|>CT~x9 zb_T6CWpZ@TUF_5_Lklimk-JuS7}Ko>mE&-XwT%@ zz~AnCsWjGS_c!$k_qyy%=bv{x)ZwDkSM>CArNyI_f|-FzCbQ0}DK0pe;IJ{`jA%$K z$3xdA@?HlRJyy6KS>*1mHaTQ%m}f)%-=FRGo-dZ+d604WWl4U)jbbK=2$gdI!WH>j zqh6HV`q?dPS6*4`o*(~j>3RD+3qv0!n~cdU8!k@z*R#?f>E!~ag&Ur}|8vr7UvkaQ z=;b+Pr=RmN9(OaAd3jBxQDi~KqKg_A7l)U0m1^@YVz=nh@z^ISVL63sg*fk&6Deo1 z6F1+ypsT#ltU+0H_0+W;yW~=II3G+hT(|GUqiho?6S@AK`V$0rA1qp_DOl|Id#y;u z%5t^N%geXT-<=&@XLj20rd8Jjj+DHd=yt)~@iJ*|HCHywVB%a6_`e{Ld3DQ-g`9e2 zJHC5NO!Nqp7KGu&fThp?^U)%zxfj8D>H6YSo*o)!HwtY_Bk?~ z3617rwSO<0-|6{uH|pctX{S|7cgLQ8@i3*mA^1zdvC9PwTy;L%{TO><_x)_sd|sTk z@bjThlf;icpI+^l)7}u_&|l5Ce9_&!$2uEl8U5J%o_$~c7pCKr88jzx3*G2gzbKlU ziBEnW%e&>y6Ao;Tv%SAUaD|Y_o#_q1Q<_}#rY|nA(CNRW_~66)T7N6U4bP6eznPTy lZ%sw4nOvUSG&zw4^;={Tb>7Fkn+K|1JYD@<);T3K0RY#fl2ZTx diff --git a/Resources/Textures/Interface/Alerts/deflecting.rsi/meta.json b/Resources/Textures/Interface/Alerts/deflecting.rsi/meta.json deleted file mode 100644 index f5d94c891a73..000000000000 --- a/Resources/Textures/Interface/Alerts/deflecting.rsi/meta.json +++ /dev/null @@ -1,14 +0,0 @@ -{ - "version": 1, - "license": "CC-BY-SA-3.0", - "copyright": "Deflecting icon by Ubaser", - "size": { - "x": 32, - "y": 32 - }, - "states": [ - { - "name": "deflecting0" - } - ] -} From c375d11b88be5870902c0c8f3e00dde65ae4a200 Mon Sep 17 00:00:00 2001 From: metalgearsloth Date: Sat, 22 Jun 2024 14:48:27 +1000 Subject: [PATCH 2/4] Add myself to codeowners --- .github/CODEOWNERS | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/.github/CODEOWNERS b/.github/CODEOWNERS index da9d4d693a81..036d202e6209 100644 --- a/.github/CODEOWNERS +++ b/.github/CODEOWNERS @@ -55,3 +55,7 @@ #Jezi /Content.*/Medical @Jezithyr /Content.*/Body @Jezithyr + +# Sloth +/Content.*/Shuttles @metalgearsloth +/Content.*/Weapons @metalgearsloth \ No newline at end of file From 734dad893111fb97dde18b42ef515cccbcdcc208 Mon Sep 17 00:00:00 2001 From: metalgearsloth Date: Sat, 22 Jun 2024 14:50:07 +1000 Subject: [PATCH 3/4] Add myself to codeowners --- .github/CODEOWNERS | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/.github/CODEOWNERS b/.github/CODEOWNERS index 036d202e6209..2b6d556117ef 100644 --- a/.github/CODEOWNERS +++ b/.github/CODEOWNERS @@ -57,5 +57,8 @@ /Content.*/Body @Jezithyr # Sloth +/Content.*/Audio @metalgearsloth +/Content.*/Movement @metalgearsloth +/Content.*/NPC @metalgearsloth /Content.*/Shuttles @metalgearsloth -/Content.*/Weapons @metalgearsloth \ No newline at end of file +/Content.*/Weapons @metalgearsloth From b6e47648502e1a7ad1a8016c55bee76d03e6387c Mon Sep 17 00:00:00 2001 From: metalgearsloth Date: Sat, 22 Jun 2024 15:00:03 +1000 Subject: [PATCH 4/4] Also the alerts --- .../Weapons/Reflect/ReflectSystem.cs | 196 +++++------------- 1 file changed, 51 insertions(+), 145 deletions(-) diff --git a/Content.Shared/Weapons/Reflect/ReflectSystem.cs b/Content.Shared/Weapons/Reflect/ReflectSystem.cs index 208dfb47ffff..7a2e733bf7c7 100644 --- a/Content.Shared/Weapons/Reflect/ReflectSystem.cs +++ b/Content.Shared/Weapons/Reflect/ReflectSystem.cs @@ -1,6 +1,7 @@ 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; @@ -36,108 +37,60 @@ public sealed class ReflectSystem : EntitySystem [Dependency] private readonly SharedTransformSystem _transform = default!; [Dependency] private readonly InventorySystem _inventorySystem = default!; - [ValidatePrototypeId] - private const string DeflectingAlert = "Deflecting"; - public override void Initialize() { base.Initialize(); - SubscribeLocalEvent(OnObjectReflectProjectileAttempt); - SubscribeLocalEvent(OnObjectReflectHitscanAttempt); + SubscribeLocalEvent(OnReflectCollide); + SubscribeLocalEvent(OnReflectHitscan); SubscribeLocalEvent(OnReflectEquipped); SubscribeLocalEvent(OnReflectUnequipped); SubscribeLocalEvent(OnReflectHandEquipped); SubscribeLocalEvent(OnReflectHandUnequipped); SubscribeLocalEvent(OnToggleReflect); - SubscribeLocalEvent(OnUserProjectileReflectAttempt); - SubscribeLocalEvent(OnUserHitscanReflectAttempt); + SubscribeLocalEvent(OnReflectUserCollide); + SubscribeLocalEvent(OnReflectUserHitscan); } - private void OnUserHitscanReflectAttempt(Entity user, ref HitScanReflectAttemptEvent args) + private void OnReflectUserHitscan(EntityUid uid, ReflectUserComponent component, ref HitScanReflectAttemptEvent args) { if (args.Reflected) return; - if (!UserCanReflect(user, out var bestReflectorUid)) - return; - - if (!TryReflectHitscan(user.Owner, bestReflectorUid.Value, args.Shooter, args.SourceItem, args.Direction, out var dir)) - return; - - args.Direction = dir.Value; - args.Reflected = true; - } - - private void OnUserProjectileReflectAttempt(Entity user, ref ProjectileReflectAttemptEvent args) - { - if (args.Cancelled) - return; - - if (!TryComp(args.ProjUid, out var reflectiveComponent)) - return; - - if (!UserCanReflect(user, out var bestReflectorUid, (args.ProjUid, reflectiveComponent))) - return; - - if (!TryReflectProjectile(user, bestReflectorUid.Value, (args.ProjUid, args.Component))) - return; + foreach (var ent in _inventorySystem.GetHandOrInventoryEntities(uid, SlotFlags.All & ~SlotFlags.POCKET)) + { + if (!TryReflectHitscan(uid, ent, args.Shooter, args.SourceItem, args.Direction, out var dir)) + continue; - args.Cancelled = true; + args.Direction = dir.Value; + args.Reflected = true; + break; + } } - private void OnObjectReflectHitscanAttempt(Entity obj, ref HitScanReflectAttemptEvent args) + private void OnReflectUserCollide(EntityUid uid, ReflectUserComponent component, ref ProjectileReflectAttemptEvent args) { - if (args.Reflected || (obj.Comp.Reflects & args.Reflective) == 0x0) - return; - - if (!TryReflectHitscan(obj, obj, args.Shooter, args.SourceItem, args.Direction, out var dir)) - return; + foreach (var ent in _inventorySystem.GetHandOrInventoryEntities(uid, SlotFlags.All & ~SlotFlags.POCKET)) + { + if (!TryReflectProjectile(uid, ent, args.ProjUid)) + continue; - args.Direction = dir.Value; - args.Reflected = true; + args.Cancelled = true; + break; + } } - private void OnObjectReflectProjectileAttempt(Entity obj, ref ProjectileReflectAttemptEvent args) + private void OnReflectCollide(EntityUid uid, ReflectComponent component, ref ProjectileReflectAttemptEvent args) { if (args.Cancelled) return; - if (!TryReflectProjectile(obj, obj, (args.ProjUid, args.Component))) - return; - - args.Cancelled = true; - } - - /// - /// Can a user reflect something that's hit them? Returns true if so, and the best reflector available in the user's equipment. - /// - private bool UserCanReflect(Entity user, [NotNullWhen(true)] out Entity? bestReflector, Entity? projectile = null) - { - bestReflector = null; - - foreach (var entityUid in _inventorySystem.GetHandOrInventoryEntities(user.Owner, SlotFlags.WITHOUT_POCKET)) - { - if (!TryComp(entityUid, out var comp)) - continue; - - if (!comp.Enabled) - continue; - - if (bestReflector != null && bestReflector.Value.Comp.ReflectProb >= comp.ReflectProb) - continue; - - if (projectile != null && (comp.Reflects & projectile.Value.Comp.Reflective) == 0x0) - continue; - - bestReflector = (entityUid, comp); - } - - return bestReflector != null; + if (TryReflectProjectile(uid, uid, args.ProjUid, reflect: component)) + args.Cancelled = true; } - private bool TryReflectProjectile(EntityUid user, Entity reflector, Entity projectile) + private bool TryReflectProjectile(EntityUid user, EntityUid reflector, EntityUid projectile, ProjectileComponent? projectileComp = null, ReflectComponent? reflect = null) { if (!Resolve(reflector, ref reflect, false) || !reflect.Enabled || @@ -149,8 +102,7 @@ private bool TryReflectProjectile(EntityUid user, Entity refle return false; } - // Below handles what happens after being deflected. - var rotation = _random.NextAngle(-reflector.Comp.Spread / 2, reflector.Comp.Spread / 2).Opposite(); + var rotation = _random.NextAngle(-reflect.Spread / 2, reflect.Spread / 2).Opposite(); var existingVelocity = _physics.GetMapLinearVelocity(projectile, component: physics); var relativeVelocity = existingVelocity - _physics.GetMapLinearVelocity(user); var newVelocity = rotation.RotateVec(relativeVelocity); @@ -167,14 +119,21 @@ private bool TryReflectProjectile(EntityUid user, Entity refle if (_netManager.IsServer) { _popup.PopupEntity(Loc.GetString("reflect-shot"), user); - _audio.PlayPvs(reflector.Comp.SoundOnReflect, user, AudioHelpers.WithVariation(0.05f, _random)); + _audio.PlayPvs(reflect.SoundOnReflect, user, AudioHelpers.WithVariation(0.05f, _random)); } - _adminLogger.Add(LogType.BulletHit, LogImpact.Medium, $"{ToPrettyString(user)} reflected {ToPrettyString(projectile)} from {ToPrettyString(projectile.Comp.Weapon)} shot by {projectile.Comp.Shooter}"); + if (Resolve(projectile, ref projectileComp, false)) + { + _adminLogger.Add(LogType.BulletHit, LogImpact.Medium, $"{ToPrettyString(user)} reflected {ToPrettyString(projectile)} from {ToPrettyString(projectileComp.Weapon)} shot by {projectileComp.Shooter}"); - projectile.Comp.Shooter = user; - projectile.Comp.Weapon = user; - Dirty(projectile); + projectileComp.Shooter = user; + projectileComp.Weapon = user; + Dirty(projectile, projectileComp); + } + else + { + _adminLogger.Add(LogType.BulletHit, LogImpact.Medium, $"{ToPrettyString(user)} reflected {ToPrettyString(projectile)}"); + } return true; } @@ -196,7 +155,7 @@ private void OnReflectHitscan(EntityUid uid, ReflectComponent component, ref Hit private bool TryReflectHitscan( EntityUid user, - Entity reflector, + EntityUid reflector, EntityUid? shooter, EntityUid shotSource, Vector2 direction, @@ -210,14 +169,13 @@ private bool TryReflectHitscan( return false; } - // Below handles what happens after being deflected. if (_netManager.IsServer) { _popup.PopupEntity(Loc.GetString("reflect-shot"), user); - _audio.PlayPvs(reflector.Comp.SoundOnReflect, user, AudioHelpers.WithVariation(0.05f, _random)); + _audio.PlayPvs(reflect.SoundOnReflect, user, AudioHelpers.WithVariation(0.05f, _random)); } - var spread = _random.NextAngle(-reflector.Comp.Spread / 2, reflector.Comp.Spread / 2); + var spread = _random.NextAngle(-reflect.Spread / 2, reflect.Spread / 2); newDirection = -spread.RotateVec(direction); if (shooter != null) @@ -228,85 +186,44 @@ private bool TryReflectHitscan( return true; } - private float GetReflectChance(Entity reflector) - { - /* - * The rules of deflection are as follows: - * If you innately reflect things via magic, biology etc., you always have a full chance. - * If you are standing up and standing still, you're prepared to deflect and have full chance. - * If you have velocity, your deflection chance depends on your velocity, clamped. - * If you are floating, your chance is the minimum value possible. - */ - - if (reflector.Comp.Innate) - return reflector.Comp.ReflectProb; - - if (_gravity.IsWeightless(reflector)) - return reflector.Comp.MinReflectProb; - - if (!TryComp(reflector, out var reflectorPhysics)) - return reflector.Comp.ReflectProb; - - return MathHelper.Lerp( - reflector.Comp.MinReflectProb, - reflector.Comp.ReflectProb, - // Inverse progression between velocities fed in as progression between probabilities. We go high -> low so the output here needs to be _inverted_. - 1 - Math.Clamp((reflectorPhysics.LinearVelocity.Length() - reflector.Comp.VelocityBeforeNotMaxProb) / (reflector.Comp.VelocityBeforeMinProb - reflector.Comp.VelocityBeforeNotMaxProb), 0, 1) - ); - } - - private void OnReflectEquipped(Entity reflector, ref GotEquippedEvent args) + private void OnReflectEquipped(EntityUid uid, ReflectComponent component, GotEquippedEvent args) { if (_gameTiming.ApplyingState) return; EnsureComp(args.Equipee); - - if (reflector.Comp.Enabled) - EnableAlert(args.Equipee); } - private void OnReflectUnequipped(Entity reflector, ref GotUnequippedEvent args) + private void OnReflectUnequipped(EntityUid uid, ReflectComponent comp, GotUnequippedEvent args) { RefreshReflectUser(args.Equipee); } - private void OnReflectHandEquipped(Entity reflector, ref GotEquippedHandEvent args) + private void OnReflectHandEquipped(EntityUid uid, ReflectComponent component, GotEquippedHandEvent args) { if (_gameTiming.ApplyingState) return; EnsureComp(args.User); - - if (reflector.Comp.Enabled) - EnableAlert(args.User); } - private void OnReflectHandUnequipped(Entity reflector, ref GotUnequippedHandEvent args) + private void OnReflectHandUnequipped(EntityUid uid, ReflectComponent component, GotUnequippedHandEvent args) { RefreshReflectUser(args.User); } - private void OnToggleReflect(Entity reflector, ref ItemToggledEvent args) + private void OnToggleReflect(EntityUid uid, ReflectComponent comp, ref ItemToggledEvent args) { - reflector.Comp.Enabled = args.Activated; - Dirty(reflector); - - if (args.User == null) - return; - - if (reflector.Comp.Enabled) - EnableAlert(args.User.Value); - else - DisableAlert(args.User.Value); + comp.Enabled = args.Activated; + Dirty(uid, comp); } /// - /// Refreshes whether someone has reflection potential, so we can raise directed events on them. + /// Refreshes whether someone has reflection potential so we can raise directed events on them. /// private void RefreshReflectUser(EntityUid user) { - foreach (var ent in _inventorySystem.GetHandOrInventoryEntities(user, SlotFlags.WITHOUT_POCKET)) + foreach (var ent in _inventorySystem.GetHandOrInventoryEntities(user, SlotFlags.All & ~SlotFlags.POCKET)) { if (!HasComp(ent)) continue; @@ -316,16 +233,5 @@ private void RefreshReflectUser(EntityUid user) } RemCompDeferred(user); - DisableAlert(user); - } - - private void EnableAlert(EntityUid alertee) - { - _alerts.ShowAlert(alertee, DeflectingAlert); - } - - private void DisableAlert(EntityUid alertee) - { - _alerts.ClearAlert(alertee, DeflectingAlert); } }