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;
/// </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!;
[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);
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
{
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)
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);
}
}