diff --git a/Content.Client/Weapons/Ranged/Systems/GunSystem.cs b/Content.Client/Weapons/Ranged/Systems/GunSystem.cs
index 511d319655..15af9dc981 100644
--- a/Content.Client/Weapons/Ranged/Systems/GunSystem.cs
+++ b/Content.Client/Weapons/Ranged/Systems/GunSystem.cs
@@ -9,8 +9,6 @@ using Content.Shared._Starlight.Weapon.Components;
using Content.Shared._Sunrise.SunriseCCVars;
using Content.Shared.Camera;
using Content.Shared.CombatMode;
-using Content.Shared.Damage;
-using Content.Shared.Weapons.Hitscan.Components;
using Content.Shared.Mech.Components;
using Content.Shared.Weapons.Ranged;
using Content.Shared.Weapons.Ranged.Components;
@@ -25,7 +23,6 @@ using Robust.Client.Input;
using Robust.Client.Player;
using Robust.Client.State;
using Robust.Shared.Animations;
-using Robust.Shared.Audio;
using Robust.Shared.Input;
using Robust.Shared.Map;
using Robust.Shared.Map.Components;
@@ -456,7 +453,6 @@ public sealed partial class GunSystem : SharedGunSystem
continue;
}
- // TODO: Clean this up in a gun refactor at some point - too much copy pasting
switch (shootable)
{
//🌟Starlight🌟
@@ -509,7 +505,7 @@ public sealed partial class GunSystem : SharedGunSystem
else
RemoveShootable(ent.Value);
break;
- case HitscanAmmoComponent:
+ case HitscanPrototype:
Audio.PlayPredicted(gun.SoundGunshotModified, gunUid, user);
Recoil(user, direction, gun.CameraRecoilScalarModified);
break;
@@ -647,7 +643,4 @@ public sealed partial class GunSystem : SharedGunSystem
_animPlayer.Stop(gunUid, uidPlayer, "muzzle-flash-light");
_animPlayer.Play((gunUid, uidPlayer), animTwo, "muzzle-flash-light");
}
-
- // TODO: Move RangedDamageSoundComponent to shared so this can be predicted.
- public override void PlayImpactSound(EntityUid otherEntity, DamageSpecifier? modifiedDamage, SoundSpecifier? weaponSound, bool forceWeaponSound) {}
}
diff --git a/Content.Server/Weapons/Ranged/Systems/GunSystem.cs b/Content.Server/Weapons/Ranged/Systems/GunSystem.cs
index 5d605e03c0..29363e6252 100644
--- a/Content.Server/Weapons/Ranged/Systems/GunSystem.cs
+++ b/Content.Server/Weapons/Ranged/Systems/GunSystem.cs
@@ -1,3 +1,4 @@
+using System.Linq;
using System.Numerics;
using Content.Server.Atmos.EntitySystems;
using Content.Server.Atmos.Components;
@@ -22,10 +23,11 @@ using Content.Shared.Weapons.Ranged;
using Content.Shared.Weapons.Ranged.Components;
using Content.Shared.Weapons.Ranged.Events;
using Content.Shared.Weapons.Ranged.Systems;
-using Content.Shared.Weapons.Hitscan.Components;
-using Content.Shared.Weapons.Hitscan.Events;
+using Content.Shared.Weapons.Reflect;
+using Content.Shared.Damage.Components;
using Robust.Shared.Audio;
using Robust.Shared.Map;
+using Robust.Shared.Physics;
using Robust.Shared.Player;
using Robust.Shared.Prototypes;
using Robust.Shared.Utility;
@@ -153,7 +155,6 @@ public sealed partial class GunSystem : SharedGunSystem
continue;
}
- // TODO: Clean this up in a gun refactor at some point - too much copy pasting
switch (shootable)
{
//🌟Starlight🌟
@@ -242,7 +243,7 @@ public sealed partial class GunSystem : SharedGunSystem
CreateAndFireProjectiles(ent.Value, newAmmo);
break;
- case HitscanAmmoComponent hitscan:
+ case HitscanPrototype hitscan:
EntityUid? lastHit = null;
List<(EntityCoordinates fromCoordinates, float distance, Angle mapDirection, EntityUid? hitEntity)> effects = [];
@@ -296,7 +297,14 @@ public sealed partial class GunSystem : SharedGunSystem
if (!ev.Reflected)
break;
- var hitscanEv = new HitscanTraceEvent
+ fromEffect = Transform(hit).Coordinates;
+ from = TransformSystem.ToMapCoordinates(fromEffect);
+ dir = ev.Direction;
+ lastUser = hit;
+ }
+ }
+
+ if (lastHit != null)
{
var hitEntity = lastHit.Value;
if (hitscan.StaminaDamage > 0f)
@@ -678,7 +686,7 @@ public sealed partial class GunSystem : SharedGunSystem
RaiseNetworkEvent(message, filter);
}
- public override void PlayImpactSound(EntityUid otherEntity, DamageSpecifier? modifiedDamage, SoundSpecifier? weaponSound, bool forceWeaponSound)
+ public void PlayImpactSound(EntityUid otherEntity, DamageSpecifier? modifiedDamage, SoundSpecifier? weaponSound, bool forceWeaponSound)
{
DebugTools.Assert(!Deleted(otherEntity), "Impact sound entity was deleted");
diff --git a/Content.Shared/Weapons/Hitscan/Components/HitscanAmmoComponent.cs b/Content.Shared/Weapons/Hitscan/Components/HitscanAmmoComponent.cs
deleted file mode 100644
index 3993d5ad20..0000000000
--- a/Content.Shared/Weapons/Hitscan/Components/HitscanAmmoComponent.cs
+++ /dev/null
@@ -1,11 +0,0 @@
-using Content.Shared.Weapons.Ranged;
-using Robust.Shared.GameStates;
-
-namespace Content.Shared.Weapons.Hitscan.Components;
-
-///
-/// This component is used to indicate an entity is shootable from a hitscan weapon.
-/// This is placed on the laser entity being shot, not the gun itself.
-///
-[RegisterComponent, NetworkedComponent]
-public sealed partial class HitscanAmmoComponent : Component, IShootable;
diff --git a/Content.Shared/Weapons/Hitscan/Components/HitscanBasicDamageComponent.cs b/Content.Shared/Weapons/Hitscan/Components/HitscanBasicDamageComponent.cs
deleted file mode 100644
index dcbb19265e..0000000000
--- a/Content.Shared/Weapons/Hitscan/Components/HitscanBasicDamageComponent.cs
+++ /dev/null
@@ -1,17 +0,0 @@
-using Content.Shared.Damage;
-using Robust.Shared.GameStates;
-
-namespace Content.Shared.Weapons.Hitscan.Components;
-
-///
-/// Hitscan entities that have this component will do the damage specified to hit targets (Who didn't reflect it).
-///
-[RegisterComponent, NetworkedComponent]
-public sealed partial class HitscanBasicDamageComponent : Component
-{
- ///
- /// How much damage the hitscan weapon will do when hitting a target.
- ///
- [DataField(required: true)]
- public DamageSpecifier Damage;
-}
diff --git a/Content.Shared/Weapons/Hitscan/Components/HitscanBasicEffectsComponent.cs b/Content.Shared/Weapons/Hitscan/Components/HitscanBasicEffectsComponent.cs
deleted file mode 100644
index 05209f3b9e..0000000000
--- a/Content.Shared/Weapons/Hitscan/Components/HitscanBasicEffectsComponent.cs
+++ /dev/null
@@ -1,29 +0,0 @@
-using Robust.Shared.Audio;
-using Robust.Shared.GameStates;
-
-namespace Content.Shared.Weapons.Hitscan.Components;
-
-///
-/// System or basic "effects" like sounds and hit markers for hitscans.
-///
-[RegisterComponent, NetworkedComponent]
-public sealed partial class HitscanBasicEffectsComponent : Component
-{
- ///
- /// This will turn hit entities this color briefly.
- ///
- [DataField]
- public Color? HitColor = Color.Red;
-
- ///
- /// Sound that plays upon the thing being hit.
- ///
- [DataField]
- public SoundSpecifier? Sound;
-
- ///
- /// Force the hitscan sound to play rather than playing the entity's override sound (if it exists).
- ///
- [DataField]
- public bool ForceSound;
-}
diff --git a/Content.Shared/Weapons/Hitscan/Components/HitscanBasicRaycastComponent.cs b/Content.Shared/Weapons/Hitscan/Components/HitscanBasicRaycastComponent.cs
deleted file mode 100644
index e04ab0da4f..0000000000
--- a/Content.Shared/Weapons/Hitscan/Components/HitscanBasicRaycastComponent.cs
+++ /dev/null
@@ -1,23 +0,0 @@
-using Content.Shared.Physics;
-using Robust.Shared.GameStates;
-
-namespace Content.Shared.Weapons.Hitscan.Components;
-
-///
-/// A basic raycast system that will shoot in a straight line when triggered.
-///
-[RegisterComponent, NetworkedComponent]
-public sealed partial class HitscanBasicRaycastComponent : Component
-{
- ///
- /// Maximum distance the raycast will travel before giving up. Reflections will reset the distance traveled
- ///
- [DataField]
- public float MaxDistance = 20.0f;
-
- ///
- /// The collision mask the hitscan ray uses to collide with other objects. See the enum for more information
- ///
- [DataField]
- public CollisionGroup CollisionMask = CollisionGroup.Opaque;
-}
diff --git a/Content.Shared/Weapons/Hitscan/Components/HitscanBasicVisualsComponent.cs b/Content.Shared/Weapons/Hitscan/Components/HitscanBasicVisualsComponent.cs
deleted file mode 100644
index 519425ece3..0000000000
--- a/Content.Shared/Weapons/Hitscan/Components/HitscanBasicVisualsComponent.cs
+++ /dev/null
@@ -1,29 +0,0 @@
-using Robust.Shared.GameStates;
-using Robust.Shared.Utility;
-
-namespace Content.Shared.Weapons.Hitscan.Components;
-
-///
-/// Provides basic visuals for hitscan weapons - works with
-///
-[RegisterComponent, NetworkedComponent]
-public sealed partial class HitscanBasicVisualsComponent : Component
-{
- ///
- /// The muzzle flash from the hitscan weapon.
- ///
- [DataField]
- public SpriteSpecifier? MuzzleFlash;
-
- ///
- /// The "travel" sprite, this gets repeated until it hits the target.
- ///
- [DataField]
- public SpriteSpecifier? TravelFlash;
-
- ///
- /// The sprite that gets shown on the impact of the laser.
- ///
- [DataField]
- public SpriteSpecifier? ImpactFlash;
-}
diff --git a/Content.Shared/Weapons/Hitscan/Components/HitscanReflectComponent.cs b/Content.Shared/Weapons/Hitscan/Components/HitscanReflectComponent.cs
deleted file mode 100644
index a63f3ab670..0000000000
--- a/Content.Shared/Weapons/Hitscan/Components/HitscanReflectComponent.cs
+++ /dev/null
@@ -1,29 +0,0 @@
-using Content.Shared.Weapons.Reflect;
-using Robust.Shared.GameStates;
-
-namespace Content.Shared.Weapons.Hitscan.Components;
-
-///
-/// Hitscan entities with this component will get reflected by certain things (E.G energy swords).
-///
-[RegisterComponent, NetworkedComponent]
-public sealed partial class HitscanReflectComponent : Component
-{
- ///
- /// The reflective type, will only reflect from entities that have a matching reflection type.
- ///
- [DataField]
- public ReflectType ReflectiveType = ReflectType.Energy;
-
- ///
- /// The maximum number of reflections the laser will make.
- ///
- [DataField]
- public int MaxReflections = 3;
-
- ///
- /// Current number of times this hitscan entity was reflected. Will not be more than
- ///
- [DataField]
- public int CurrentReflections;
-}
diff --git a/Content.Shared/Weapons/Hitscan/Components/HitscanStaminaDamageComponent.cs b/Content.Shared/Weapons/Hitscan/Components/HitscanStaminaDamageComponent.cs
deleted file mode 100644
index 35eb398682..0000000000
--- a/Content.Shared/Weapons/Hitscan/Components/HitscanStaminaDamageComponent.cs
+++ /dev/null
@@ -1,16 +0,0 @@
-using Robust.Shared.GameStates;
-
-namespace Content.Shared.Weapons.Hitscan.Components;
-
-///
-/// Hitscan entities that have this component will deal stamina damage to the target.
-///
-[RegisterComponent, NetworkedComponent]
-public sealed partial class HitscanStaminaDamageComponent : Component
-{
- ///
- /// How much stamania damage the hitscan weapon will do when hitting a target.
- ///
- [DataField]
- public float StaminaDamage = 10.0f;
-}
diff --git a/Content.Shared/Weapons/Hitscan/Events/HitscanEvents.cs b/Content.Shared/Weapons/Hitscan/Events/HitscanEvents.cs
deleted file mode 100644
index 0ebc18079a..0000000000
--- a/Content.Shared/Weapons/Hitscan/Events/HitscanEvents.cs
+++ /dev/null
@@ -1,110 +0,0 @@
-using System.Numerics;
-using Content.Shared.Damage;
-using Robust.Shared.Map;
-
-namespace Content.Shared.Weapons.Hitscan.Events;
-
-///
-/// Raised on the hitscan entity when "fired". This could be from reflections or from the gun. This is the catalyst that
-/// other systems will listen for to actually shoot the gun.
-///
-[ByRefEvent]
-public record struct HitscanTraceEvent
-{
- ///
- /// Location the hitscan was fired from.
- ///
- public EntityCoordinates FromCoordinates;
-
- ///
- /// Direction that the ray was fired towards.
- ///
- public Vector2 ShotDirection;
-
- ///
- /// Gun that was fired - this will always be the original weapon even if reflected.
- ///
- public EntityUid Gun;
-
- ///
- /// Player who shot the gun, if null the gun was fired by itself.
- ///
- public EntityUid? Shooter;
-
- ///
- /// Target that was being aimed at (Not necessarily hit).
- ///
- public EntityUid? Target;
-}
-
-///
-/// All data known data for when a hitscan is actually fired.
-///
-public record struct HitscanRaycastFiredData
-{
- ///
- /// Direction that the ray was fired towards.
- ///
- public Vector2 ShotDirection;
-
- ///
- /// The entity that got hit, if null the raycast didn't hit anyone.
- ///
- public EntityUid? HitEntity;
-
- ///
- /// Gun that fired the raycast.
- ///
- public EntityUid Gun;
-
- ///
- /// Player who shot the gun, if null the gun was fired by itself.
- ///
- public EntityUid? Shooter;
-}
-
-///
-/// Try to hit the targeted entity with a hitscan laser. Stuff like the reflection system should listen for this and
-/// cancel the event if the laser was reflected.
-///
-[ByRefEvent]
-public struct AttemptHitscanRaycastFiredEvent
-{
- ///
- /// Data for the hitscan that was fired.
- ///
- public HitscanRaycastFiredData Data;
-
- ///
- /// Set to true the hitscan is cancelled (e.g. due to reflection).
- /// Cancelled hitscans should not apply damage or trigger follow-up effects.
- ///
- public bool Cancelled;
-}
-
-///
-/// Results of a hitscan raycast and will be raised on the raycast entity on itself. Stuff like the damage system should
-/// listen for this. At this point we KNOW the laser hit the entity.
-///
-[ByRefEvent]
-public struct HitscanRaycastFiredEvent
-{
- ///
- /// Data for the hitscan that was fired.
- ///
- public HitscanRaycastFiredData Data;
-}
-
-[ByRefEvent]
-public record struct HitscanDamageDealtEvent
-{
- ///
- /// Target that was dealt damage.
- ///
- public EntityUid Target;
-
- ///
- /// The amount of damage that the target was dealt.
- ///
- public DamageSpecifier DamageDealt;
-}
diff --git a/Content.Shared/Weapons/Hitscan/Systems/HitscanBasicDamageSystem.cs b/Content.Shared/Weapons/Hitscan/Systems/HitscanBasicDamageSystem.cs
deleted file mode 100644
index 130687469a..0000000000
--- a/Content.Shared/Weapons/Hitscan/Systems/HitscanBasicDamageSystem.cs
+++ /dev/null
@@ -1,38 +0,0 @@
-using Content.Shared.Damage;
-using Content.Shared.Weapons.Hitscan.Components;
-using Content.Shared.Weapons.Hitscan.Events;
-
-namespace Content.Shared.Weapons.Hitscan.Systems;
-
-public sealed class HitscanBasicDamageSystem : EntitySystem
-{
- [Dependency] private readonly DamageableSystem _damage = default!;
-
- public override void Initialize()
- {
- base.Initialize();
-
- SubscribeLocalEvent(OnHitscanHit);
- }
-
- private void OnHitscanHit(Entity ent, ref HitscanRaycastFiredEvent args)
- {
- if (args.Data.HitEntity == null)
- return;
-
- var dmg = ent.Comp.Damage * _damage.UniversalHitscanDamageModifier;
-
- var damageDealt = _damage.TryChangeDamage(args.Data.HitEntity, dmg, origin: args.Data.Gun);
-
- if (damageDealt == null)
- return;
-
- var damageEvent = new HitscanDamageDealtEvent
- {
- Target = args.Data.HitEntity.Value,
- DamageDealt = damageDealt,
- };
-
- RaiseLocalEvent(ent, ref damageEvent);
- }
-}
diff --git a/Content.Shared/Weapons/Hitscan/Systems/HitscanBasicEffectsSystem.cs b/Content.Shared/Weapons/Hitscan/Systems/HitscanBasicEffectsSystem.cs
deleted file mode 100644
index 4713734be7..0000000000
--- a/Content.Shared/Weapons/Hitscan/Systems/HitscanBasicEffectsSystem.cs
+++ /dev/null
@@ -1,36 +0,0 @@
-using Content.Shared.Damage;
-using Content.Shared.Effects;
-using Content.Shared.Weapons.Hitscan.Components;
-using Content.Shared.Weapons.Hitscan.Events;
-using Content.Shared.Weapons.Ranged.Systems;
-using Robust.Shared.Player;
-
-namespace Content.Shared.Weapons.Hitscan.Systems;
-
-public sealed class HitscanBasicEffectsSystem : EntitySystem
-{
- [Dependency] private readonly SharedColorFlashEffectSystem _color = default!;
- [Dependency] private readonly SharedGunSystem _gun = default!;
-
- public override void Initialize()
- {
- base.Initialize();
-
- SubscribeLocalEvent(OnHitscanDamageDealt);
- }
-
- private void OnHitscanDamageDealt(Entity ent, ref HitscanDamageDealtEvent args)
- {
- if (Deleted(args.Target))
- return;
-
- if (ent.Comp.HitColor != null && args.DamageDealt.GetTotal() != 0)
- {
- _color.RaiseEffect(ent.Comp.HitColor.Value,
- new List { args.Target },
- Filter.Pvs(args.Target, entityManager: EntityManager));
- }
-
- _gun.PlayImpactSound(args.Target, args.DamageDealt, ent.Comp.Sound, ent.Comp.ForceSound);
- }
-}
diff --git a/Content.Shared/Weapons/Hitscan/Systems/HitscanBasicRaycastSystem.cs b/Content.Shared/Weapons/Hitscan/Systems/HitscanBasicRaycastSystem.cs
deleted file mode 100644
index 4bcfe8a69f..0000000000
--- a/Content.Shared/Weapons/Hitscan/Systems/HitscanBasicRaycastSystem.cs
+++ /dev/null
@@ -1,150 +0,0 @@
-using System.Numerics;
-using Content.Shared.Administration.Logs;
-using Content.Shared.Damage.Components;
-using Content.Shared.Database;
-using Content.Shared.Weapons.Hitscan.Components;
-using Content.Shared.Weapons.Hitscan.Events;
-using Content.Shared.Weapons.Ranged.Systems;
-using Robust.Shared.Containers;
-using Robust.Shared.Map;
-using Robust.Shared.Physics;
-using Robust.Shared.Physics.Systems;
-using Robust.Shared.Player;
-using Robust.Shared.Utility;
-
-namespace Content.Shared.Weapons.Hitscan.Systems;
-
-public sealed class HitscanBasicRaycastSystem : EntitySystem
-{
- [Dependency] private readonly SharedPhysicsSystem _physics = default!;
- [Dependency] private readonly SharedContainerSystem _container = default!;
- [Dependency] private readonly ISharedAdminLogManager _log = default!;
- [Dependency] private readonly SharedTransformSystem _transform = default!;
-
- private EntityQuery _visualsQuery;
-
- public override void Initialize()
- {
- base.Initialize();
-
- _visualsQuery = GetEntityQuery();
-
- SubscribeLocalEvent(OnHitscanFired);
- }
-
- private void OnHitscanFired(Entity ent, ref HitscanTraceEvent args)
- {
- var shooter = args.Shooter ?? args.Gun;
- var mapCords = _transform.ToMapCoordinates(args.FromCoordinates);
- var ray = new CollisionRay(mapCords.Position, args.ShotDirection, (int) ent.Comp.CollisionMask);
- var rayCastResults = _physics.IntersectRay(mapCords.MapId, ray, ent.Comp.MaxDistance, shooter, false);
-
- var target = args.Target;
- // If you are in a container, use the raycast result
- // Otherwise:
- // 1.) Hit the first entity that you targeted.
- // 2.) Hit the first entity that doesn't require you to aim at it specifically to be hit.
- var result = _container.IsEntityOrParentInContainer(shooter)
- ? rayCastResults.FirstOrNull()
- : rayCastResults.FirstOrNull(hit => hit.HitEntity == target
- || CompOrNull(hit.HitEntity)?.Active != true);
-
- var distanceTried = result?.Distance ?? ent.Comp.MaxDistance;
-
- // Do visuals without an event. They should always happen and putting it on the attempt event is weird!
- // If more stuff gets added here, it should probably be turned into an event.
- FireEffects(args.FromCoordinates, distanceTried, args.ShotDirection.ToAngle(), ent.Owner);
-
- // Admin logging
- if (result?.HitEntity != null)
- {
- _log.Add(LogType.HitScanHit,
- $"{ToPrettyString(shooter):user} hit {ToPrettyString(result.Value.HitEntity):target}"
- + $" using {ToPrettyString(args.Gun):entity}.");
- }
-
- var data = new HitscanRaycastFiredData
- {
- ShotDirection = args.ShotDirection,
- Gun = args.Gun,
- Shooter = args.Shooter,
- HitEntity = result?.HitEntity,
- };
-
- var attemptEvent = new AttemptHitscanRaycastFiredEvent { Data = data };
- RaiseLocalEvent(ent, ref attemptEvent);
-
- if (attemptEvent.Cancelled)
- return;
-
- var hitEvent = new HitscanRaycastFiredEvent { Data = data };
- RaiseLocalEvent(ent, ref hitEvent);
- }
-
- ///
- /// Create visual effects for the fired hitscan weapon.
- ///
- /// Location to start the effect.
- /// Distance of the hitscan shot.
- /// Angle of the shot.
- /// The hitscan entity itself.
- private void FireEffects(EntityCoordinates fromCoordinates, float distance, Angle shotAngle, EntityUid hitscanUid)
- {
- if (distance == 0 || !_visualsQuery.TryComp(hitscanUid, out var vizComp))
- return;
-
- var sprites = new List<(NetCoordinates coordinates, Angle angle, SpriteSpecifier sprite, float scale)>();
- var fromXform = Transform(fromCoordinates.EntityId);
-
- // We'll get the effects relative to the grid / map of the firer
- // Look you could probably optimise this a bit with redundant transforms at this point.
-
- var gridUid = fromXform.GridUid;
- if (gridUid != fromCoordinates.EntityId && TryComp(gridUid, out TransformComponent? gridXform))
- {
- var (_, gridRot, gridInvMatrix) = _transform.GetWorldPositionRotationInvMatrix(gridXform);
- var map = _transform.ToMapCoordinates(fromCoordinates);
- fromCoordinates = new EntityCoordinates(gridUid.Value, Vector2.Transform(map.Position, gridInvMatrix));
- shotAngle -= gridRot;
- }
- else
- {
- shotAngle -= _transform.GetWorldRotation(fromXform);
- }
-
- if (distance >= 1f)
- {
- if (vizComp.MuzzleFlash != null)
- {
- var coords = fromCoordinates.Offset(shotAngle.ToVec().Normalized() / 2);
- var netCoords = GetNetCoordinates(coords);
-
- sprites.Add((netCoords, shotAngle, vizComp.MuzzleFlash, 1f));
- }
-
- if (vizComp.TravelFlash != null)
- {
- var coords = fromCoordinates.Offset(shotAngle.ToVec() * (distance + 0.5f) / 2);
- var netCoords = GetNetCoordinates(coords);
-
- sprites.Add((netCoords, shotAngle, vizComp.TravelFlash, distance - 1.5f));
- }
- }
-
- if (vizComp.ImpactFlash != null)
- {
- var coords = fromCoordinates.Offset(shotAngle.ToVec() * distance);
- var netCoords = GetNetCoordinates(coords);
-
- sprites.Add((netCoords, shotAngle.FlipPositive(), vizComp.ImpactFlash, 1f));
- }
-
- if (sprites.Count > 0)
- {
- RaiseNetworkEvent(new SharedGunSystem.HitscanEvent
- {
- Sprites = sprites,
- }, Filter.Pvs(fromCoordinates, entityMan: EntityManager));
- }
- }
-}
diff --git a/Content.Shared/Weapons/Hitscan/Systems/HitscanReflectSystem.cs b/Content.Shared/Weapons/Hitscan/Systems/HitscanReflectSystem.cs
deleted file mode 100644
index 389e944942..0000000000
--- a/Content.Shared/Weapons/Hitscan/Systems/HitscanReflectSystem.cs
+++ /dev/null
@@ -1,50 +0,0 @@
-using Content.Shared.Weapons.Hitscan.Components;
-using Content.Shared.Weapons.Hitscan.Events;
-using Content.Shared.Weapons.Ranged.Events;
-using Content.Shared.Weapons.Reflect;
-using Robust.Shared.Random;
-
-namespace Content.Shared.Weapons.Hitscan.Systems;
-
-public sealed class HitscanReflectSystem : EntitySystem
-{
- public override void Initialize()
- {
- base.Initialize();
-
- SubscribeLocalEvent(OnHitscanHit);
- }
-
- private void OnHitscanHit(Entity hitscan, ref AttemptHitscanRaycastFiredEvent args)
- {
- var data = args.Data;
-
- if (hitscan.Comp.ReflectiveType == ReflectType.None || data.HitEntity == null)
- return;
-
- if (hitscan.Comp.CurrentReflections >= hitscan.Comp.MaxReflections)
- return;
-
- var ev = new HitScanReflectAttemptEvent(data.Shooter ?? data.Gun, data.Gun, hitscan.Comp.ReflectiveType, data.ShotDirection, false);
- RaiseLocalEvent(data.HitEntity.Value, ref ev);
-
- if (!ev.Reflected)
- return;
-
- hitscan.Comp.CurrentReflections++;
-
- args.Cancelled = true;
-
- var fromEffect = Transform(data.HitEntity.Value).Coordinates;
-
- var hitFiredEvent = new HitscanTraceEvent
- {
- FromCoordinates = fromEffect,
- ShotDirection = ev.Direction,
- Gun = data.Gun,
- Shooter = data.HitEntity.Value,
- };
-
- RaiseLocalEvent(hitscan, ref hitFiredEvent);
- }
-}
diff --git a/Content.Shared/Weapons/Hitscan/Systems/HitscanStunSystem.cs b/Content.Shared/Weapons/Hitscan/Systems/HitscanStunSystem.cs
deleted file mode 100644
index 8c1d1b45fb..0000000000
--- a/Content.Shared/Weapons/Hitscan/Systems/HitscanStunSystem.cs
+++ /dev/null
@@ -1,25 +0,0 @@
-using Content.Shared.Damage.Systems;
-using Content.Shared.Weapons.Hitscan.Components;
-using Content.Shared.Weapons.Hitscan.Events;
-
-namespace Content.Shared.Weapons.Hitscan.Systems;
-
-public sealed class HitscanStunSystem : EntitySystem
-{
- [Dependency] private readonly SharedStaminaSystem _stamina = default!;
-
- public override void Initialize()
- {
- base.Initialize();
-
- SubscribeLocalEvent(OnHitscanHit);
- }
-
- private void OnHitscanHit(Entity hitscan, ref HitscanRaycastFiredEvent args)
- {
- if (args.Data.HitEntity == null)
- return;
-
- _stamina.TakeStaminaDamage(args.Data.HitEntity.Value, hitscan.Comp.StaminaDamage, source: args.Data.Shooter ?? args.Data.Gun);
- }
-}
diff --git a/Content.Shared/Weapons/Ranged/Components/HitscanBatteryAmmoProviderComponent.cs b/Content.Shared/Weapons/Ranged/Components/HitscanBatteryAmmoProviderComponent.cs
index cdbf51456e..be9d9805a8 100644
--- a/Content.Shared/Weapons/Ranged/Components/HitscanBatteryAmmoProviderComponent.cs
+++ b/Content.Shared/Weapons/Ranged/Components/HitscanBatteryAmmoProviderComponent.cs
@@ -1,11 +1,11 @@
using Robust.Shared.GameStates;
-using Robust.Shared.Prototypes;
+using Robust.Shared.Serialization.TypeSerializers.Implementations.Custom.Prototype;
namespace Content.Shared.Weapons.Ranged.Components;
[RegisterComponent, NetworkedComponent]
public sealed partial class HitscanBatteryAmmoProviderComponent : BatteryAmmoProviderComponent
{
- [DataField("proto", required: true)]
- public EntProtoId HitscanEntityProto;
+ [ViewVariables(VVAccess.ReadWrite), DataField("proto", required: true, customTypeSerializer: typeof(PrototypeIdSerializer))]
+ public string Prototype = default!;
}
diff --git a/Content.Shared/Weapons/Ranged/HitscanPrototype.cs b/Content.Shared/Weapons/Ranged/HitscanPrototype.cs
new file mode 100644
index 0000000000..3e0c15b879
--- /dev/null
+++ b/Content.Shared/Weapons/Ranged/HitscanPrototype.cs
@@ -0,0 +1,57 @@
+using Content.Shared.Damage;
+using Content.Shared.Physics;
+using Content.Shared.Weapons.Reflect;
+using Robust.Shared.Audio;
+using Robust.Shared.Prototypes;
+using Robust.Shared.Utility;
+
+namespace Content.Shared.Weapons.Ranged;
+
+[Prototype]
+public sealed partial class HitscanPrototype : IPrototype, IShootable
+{
+ [ViewVariables]
+ [IdDataField]
+ public string ID { get; private set; } = default!;
+
+ [ViewVariables(VVAccess.ReadWrite), DataField("staminaDamage")]
+ public float StaminaDamage;
+
+ [ViewVariables(VVAccess.ReadWrite), DataField("damage")]
+ public DamageSpecifier? Damage;
+
+ [ViewVariables(VVAccess.ReadOnly), DataField("muzzleFlash")]
+ public SpriteSpecifier? MuzzleFlash;
+
+ [ViewVariables(VVAccess.ReadOnly), DataField("travelFlash")]
+ public SpriteSpecifier? TravelFlash;
+
+ [ViewVariables(VVAccess.ReadOnly), DataField("impactFlash")]
+ public SpriteSpecifier? ImpactFlash;
+
+ [DataField("collisionMask")]
+ public int CollisionMask = (int) CollisionGroup.Opaque;
+
+ ///
+ /// What we count as for reflection.
+ ///
+ [DataField("reflective")] public ReflectType Reflective = ReflectType.Energy;
+
+ ///
+ /// Sound that plays upon the thing being hit.
+ ///
+ [DataField("sound")]
+ public SoundSpecifier? Sound;
+
+ ///
+ /// Force the hitscan sound to play rather than potentially playing the entity's sound.
+ ///
+ [ViewVariables(VVAccess.ReadWrite), DataField("forceSound")]
+ public bool ForceSound;
+
+ ///
+ /// Try not to set this too high.
+ ///
+ [DataField("maxLength")]
+ public float MaxLength = 20f;
+}
diff --git a/Content.Shared/Weapons/Ranged/Systems/SharedGunSystem.Battery.cs b/Content.Shared/Weapons/Ranged/Systems/SharedGunSystem.Battery.cs
index 663f5f1faa..cb4e42f3cf 100644
--- a/Content.Shared/Weapons/Ranged/Systems/SharedGunSystem.Battery.cs
+++ b/Content.Shared/Weapons/Ranged/Systems/SharedGunSystem.Battery.cs
@@ -2,7 +2,6 @@ using Content.Shared.Damage;
using Content.Shared.Damage.Events;
using Content.Shared.Examine;
using Content.Shared.Projectiles;
-using Content.Shared.Weapons.Hitscan.Components;
using Content.Shared.Weapons.Ranged.Components;
using Content.Shared.Weapons.Ranged.Events;
using Robust.Shared.GameStates;
@@ -81,10 +80,11 @@ public abstract partial class SharedGunSystem
{
if (component is ProjectileBatteryAmmoProviderComponent battery)
{
- if (ProtoManager.Index(battery.Prototype).Components
+ if (ProtoManager.Index(battery.Prototype)
+ .Components
.TryGetValue(Factory.GetComponentName(), out var projectile))
{
- var p = (ProjectileComponent) projectile.Component;
+ var p = (ProjectileComponent)projectile.Component;
if (!p.Damage.Empty)
{
@@ -97,11 +97,8 @@ public abstract partial class SharedGunSystem
if (component is HitscanBatteryAmmoProviderComponent hitscan)
{
- var dmg = ProtoManager.Index(hitscan.HitscanEntityProto);
- if (!dmg.TryGetComponent(out var basicDamageComp, Factory))
- return null;
-
- return basicDamageComp.Damage * Damageable.UniversalHitscanDamageModifier;
+ var dmg = ProtoManager.Index(hitscan.Prototype).Damage;
+ return dmg == null ? dmg : dmg * Damageable.UniversalHitscanDamageModifier;
}
return null;
@@ -158,8 +155,7 @@ public abstract partial class SharedGunSystem
var ent = Spawn(proj.Prototype, coordinates);
return (ent, EnsureShootable(ent));
case HitscanBatteryAmmoProviderComponent hitscan:
- var hitscanEnt = Spawn(hitscan.HitscanEntityProto);
- return (hitscanEnt, EnsureShootable(hitscanEnt));
+ return (null, ProtoManager.Index(hitscan.Prototype));
default:
throw new ArgumentOutOfRangeException();
}
diff --git a/Content.Shared/Weapons/Ranged/Systems/SharedGunSystem.cs b/Content.Shared/Weapons/Ranged/Systems/SharedGunSystem.cs
index cf6b38ee5c..f9f2fee2fe 100644
--- a/Content.Shared/Weapons/Ranged/Systems/SharedGunSystem.cs
+++ b/Content.Shared/Weapons/Ranged/Systems/SharedGunSystem.cs
@@ -21,7 +21,6 @@ using Content.Shared.Tag;
using Content.Shared.Throwing;
using Content.Shared.Timing;
using Content.Shared.Verbs;
-using Content.Shared.Weapons.Hitscan.Components;
using Content.Shared.Weapons.Melee;
using Content.Shared.Weapons.Melee.Events;
using Content.Shared.Weapons.Ranged.Components;
@@ -605,9 +604,6 @@ public abstract partial class SharedGunSystem : EntitySystem
if (TryComp(uid, out var cartridge))
return cartridge;
- if (TryComp(uid, out var hitscanAmmo))
- return hitscanAmmo;
-
return EnsureComp(uid);
}
@@ -722,8 +718,6 @@ public abstract partial class SharedGunSystem : EntitySystem
protected abstract void CreateEffect(EntityUid gunUid, MuzzleFlashEvent message, EntityUid? user = null);
- public abstract void PlayImpactSound(EntityUid otherEntity, DamageSpecifier? modifiedDamage, SoundSpecifier? weaponSound, bool forceWeaponSound);
-
///
/// Used for animated effects on the client.
///
diff --git a/Content.Shared/Weapons/Reflect/ReflectComponent.cs b/Content.Shared/Weapons/Reflect/ReflectComponent.cs
index db68e2b2d2..1bb7dcb8fa 100644
--- a/Content.Shared/Weapons/Reflect/ReflectComponent.cs
+++ b/Content.Shared/Weapons/Reflect/ReflectComponent.cs
@@ -61,10 +61,6 @@ public sealed partial class ReflectComponent : Component
// Sunrise-End
}
-///
-/// Used for both the projectiles being reflected and the entities reflecting. If there is ever overlap between the
-/// reflection types, the projectile will be reflected.
-///
[Flags, Serializable, NetSerializable]
public enum ReflectType : byte
{
diff --git a/Resources/Prototypes/Entities/Objects/Weapons/Guns/Projectiles/hitscan.yml b/Resources/Prototypes/Entities/Objects/Weapons/Guns/Projectiles/hitscan.yml
index 003c9cadab..47f2dc2ef6 100644
--- a/Resources/Prototypes/Entities/Objects/Weapons/Guns/Projectiles/hitscan.yml
+++ b/Resources/Prototypes/Entities/Objects/Weapons/Guns/Projectiles/hitscan.yml
@@ -17,145 +17,125 @@
- Laser
- type: AnimationPlayer
-- type: entity
- id: BasicHitscan
- categories: [ HideSpawnMenu ]
- components:
- - type: HitscanAmmo
- - type: HitscanBasicRaycast
- - type: HitscanBasicVisuals
- muzzleFlash:
- sprite: Objects/Weapons/Guns/Projectiles/projectiles.rsi
- state: muzzle_laser
- travelFlash:
- sprite: Objects/Weapons/Guns/Projectiles/projectiles.rsi
- state: beam
- impactFlash:
- sprite: Objects/Weapons/Guns/Projectiles/projectiles.rsi
- state: impact_laser
- - type: HitscanReflect
- - type: HitscanBasicEffects
-
-- type: entity
- parent: BasicHitscan
- id: RedLightLaser
- categories: [ HideSpawnMenu ]
- components:
- - type: HitscanBasicDamage
- damage:
- types:
- Heat: 7
-
-- type: entity
- parent: BasicHitscan
+- type: hitscan
id: RedLaser
- categories: [ HideSpawnMenu ]
- components:
- - type: HitscanBasicDamage
- damage:
- types:
- Heat: 14
+ damage:
+ types:
+ Heat: 14
+ muzzleFlash:
+ sprite: Objects/Weapons/Guns/Projectiles/projectiles.rsi
+ state: muzzle_laser
+ travelFlash:
+ sprite: Objects/Weapons/Guns/Projectiles/projectiles.rsi
+ state: beam
+ impactFlash:
+ sprite: Objects/Weapons/Guns/Projectiles/projectiles.rsi
+ state: impact_laser
-- type: entity
- parent: BasicHitscan
- id: RedMediumLaser
- categories: [ HideSpawnMenu ]
- components:
- - type: HitscanBasicDamage
- damage:
- types:
- Heat: 17
-
-- type: entity
- parent: BasicHitscan
- id: RedHeavyLaser
- categories: [ HideSpawnMenu ]
- components:
- - type: HitscanBasicDamage
- damage:
- types:
- Heat: 28
- - type: HitscanBasicVisuals
- muzzleFlash:
- sprite: Objects/Weapons/Guns/Projectiles/projectiles.rsi
- state: muzzle_beam_heavy
- travelFlash:
- sprite: Objects/Weapons/Guns/Projectiles/projectiles.rsi
- state: beam_heavy
- impactFlash:
- sprite: Objects/Weapons/Guns/Projectiles/projectiles.rsi
- state: impact_beam_heavy
-
-- type: entity
- parent: BasicHitscan
+- type: hitscan
id: RedLaserPractice
- categories: [ HideSpawnMenu ]
- components:
- - type: HitscanBasicDamage
- damage:
- types:
- Heat: 1
+ damage:
+ types:
+ Heat: 1
+ muzzleFlash:
+ sprite: Objects/Weapons/Guns/Projectiles/projectiles.rsi
+ state: muzzle_laser
+ travelFlash:
+ sprite: Objects/Weapons/Guns/Projectiles/projectiles.rsi
+ state: beam
+ impactFlash:
+ sprite: Objects/Weapons/Guns/Projectiles/projectiles.rsi
+ state: impact_laser
-- type: entity
- parent: BasicHitscan
+- type: hitscan
+ id: RedMediumLaser
+ damage:
+ types:
+ Heat: 17
+ muzzleFlash:
+ sprite: Objects/Weapons/Guns/Projectiles/projectiles.rsi
+ state: muzzle_laser
+ travelFlash:
+ sprite: Objects/Weapons/Guns/Projectiles/projectiles.rsi
+ state: beam
+ impactFlash:
+ sprite: Objects/Weapons/Guns/Projectiles/projectiles.rsi
+ state: impact_laser
+
+- type: hitscan
+ id: RedLightLaser
+ damage:
+ types:
+ Heat: 7
+ muzzleFlash:
+ sprite: Objects/Weapons/Guns/Projectiles/projectiles.rsi
+ state: muzzle_laser
+ travelFlash:
+ sprite: Objects/Weapons/Guns/Projectiles/projectiles.rsi
+ state: beam
+ impactFlash:
+ sprite: Objects/Weapons/Guns/Projectiles/projectiles.rsi
+ state: impact_laser
+
+- type: hitscan
id: XrayLaser
- categories: [ HideSpawnMenu ]
- components:
- - type: HitscanBasicDamage
- damage:
- types:
- Heat: 10
- Radiation: 10
- - type: HitscanBasicVisuals
- muzzleFlash:
- sprite: Objects/Weapons/Guns/Projectiles/projectiles.rsi
- state: muzzle_xray
- travelFlash:
- sprite: Objects/Weapons/Guns/Projectiles/projectiles.rsi
- state: xray
- impactFlash:
- sprite: Objects/Weapons/Guns/Projectiles/projectiles.rsi
- state: impact_xray
+ damage:
+ types:
+ Heat: 10
+ Radiation: 10
+ muzzleFlash:
+ sprite: Objects/Weapons/Guns/Projectiles/projectiles.rsi
+ state: muzzle_xray
+ travelFlash:
+ sprite: Objects/Weapons/Guns/Projectiles/projectiles.rsi
+ state: xray
+ impactFlash:
+ sprite: Objects/Weapons/Guns/Projectiles/projectiles.rsi
+ state: impact_xray
-- type: entity
- parent: BasicHitscan
+- type: hitscan
+ id: RedHeavyLaser
+ damage:
+ types:
+ Heat: 28
+ muzzleFlash:
+ sprite: Objects/Weapons/Guns/Projectiles/projectiles.rsi
+ state: muzzle_beam_heavy
+ travelFlash:
+ sprite: Objects/Weapons/Guns/Projectiles/projectiles.rsi
+ state: beam_heavy
+ impactFlash:
+ sprite: Objects/Weapons/Guns/Projectiles/projectiles.rsi
+ state: impact_beam_heavy
+
+- type: hitscan
id: Pulse
- categories: [ HideSpawnMenu ]
- components:
- - type: HitscanBasicDamage
- damage:
- types:
- Heat: 35
- - type: HitscanBasicVisuals
- muzzleFlash:
- sprite: Objects/Weapons/Guns/Projectiles/projectiles.rsi
- state: muzzle_blue
- travelFlash:
- sprite: Objects/Weapons/Guns/Projectiles/projectiles.rsi
- state: beam_blue
- impactFlash:
- sprite: Objects/Weapons/Guns/Projectiles/projectiles.rsi
- state: impact_blue
+ damage:
+ types:
+ Heat: 35
+ muzzleFlash:
+ sprite: Objects/Weapons/Guns/Projectiles/projectiles.rsi
+ state: muzzle_blue
+ travelFlash:
+ sprite: Objects/Weapons/Guns/Projectiles/projectiles.rsi
+ state: beam_blue
+ impactFlash:
+ sprite: Objects/Weapons/Guns/Projectiles/projectiles.rsi
+ state: impact_blue
-- type: entity
- parent: BasicHitscan
+- type: hitscan
id: RedShuttleLaser
- categories: [ HideSpawnMenu ]
- components:
- - type: HitscanBasicRaycast
- maxDistance: 60.0
- - type: HitscanBasicDamage
- damage:
- types:
- Heat: 45
- Structural: 10
- - type: HitscanBasicVisuals
- muzzleFlash:
- sprite: Objects/Weapons/Guns/Projectiles/projectiles.rsi
- state: muzzle_beam_heavy2
- travelFlash:
- sprite: Objects/Weapons/Guns/Projectiles/projectiles.rsi
- state: beam_heavy2
- impactFlash:
- sprite: Objects/Weapons/Guns/Projectiles/projectiles.rsi
- state: impact_beam_heavy2
+ maxLength: 60
+ damage:
+ types:
+ Heat: 45
+ Structural: 10
+ muzzleFlash:
+ sprite: Objects/Weapons/Guns/Projectiles/projectiles.rsi
+ state: muzzle_beam_heavy2
+ travelFlash:
+ sprite: Objects/Weapons/Guns/Projectiles/projectiles.rsi
+ state: beam_heavy2
+ impactFlash:
+ sprite: Objects/Weapons/Guns/Projectiles/projectiles.rsi
+ state: impact_beam_heavy2