| 123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337 |
- using System.Linq;
- using System.Numerics;
- using Content.Client.Animations;
- using Content.Client.Gameplay;
- using Content.Client.Items;
- using Content.Client.Weapons.Ranged.Components;
- using Content.Shared._RMC14.Weapons.Ranged.Prediction;
- using Content.Shared.CombatMode;
- using Content.Shared.Weapons.Ranged.Events;
- using Content.Shared.Weapons.Ranged.Systems;
- using Robust.Client.Animations;
- using Robust.Client.GameObjects;
- using Robust.Client.Graphics;
- using Robust.Client.Input;
- using Robust.Client.Physics;
- using Robust.Client.Player;
- using Robust.Client.State;
- using Robust.Shared.Animations;
- using Robust.Shared.Input;
- using Robust.Shared.Map;
- using Robust.Shared.Map.Components;
- using Robust.Shared.Prototypes;
- using Robust.Shared.Utility;
- using SharedGunSystem = Content.Shared.Weapons.Ranged.Systems.SharedGunSystem;
- using TimedDespawnComponent = Robust.Shared.Spawners.TimedDespawnComponent;
- namespace Content.Client.Weapons.Ranged.Systems;
- public sealed partial class GunSystem : SharedGunSystem
- {
- [Dependency] private readonly IComponentFactory _factory = default!;
- [Dependency] private readonly IEyeManager _eyeManager = default!;
- [Dependency] private readonly IInputManager _inputManager = default!;
- [Dependency] private readonly IPlayerManager _player = default!;
- [Dependency] private readonly IStateManager _state = default!;
- [Dependency] private readonly AnimationPlayerSystem _animPlayer = default!;
- [Dependency] private readonly InputSystem _inputSystem = default!;
- [Dependency] private readonly SharedMapSystem _maps = default!;
- [Dependency] private readonly PhysicsSystem _physics = default!;
- [ValidatePrototypeId<EntityPrototype>]
- public const string HitscanProto = "HitscanEffect";
- public bool SpreadOverlay
- {
- get => _spreadOverlay;
- set
- {
- if (_spreadOverlay == value)
- return;
- _spreadOverlay = value;
- var overlayManager = IoCManager.Resolve<IOverlayManager>();
- if (_spreadOverlay)
- {
- overlayManager.AddOverlay(new GunSpreadOverlay(
- EntityManager,
- _eyeManager,
- Timing,
- _inputManager,
- _player,
- this,
- TransformSystem));
- }
- else
- {
- overlayManager.RemoveOverlay<GunSpreadOverlay>();
- }
- }
- }
- private bool _spreadOverlay;
- public override void Initialize()
- {
- base.Initialize();
- UpdatesOutsidePrediction = true;
- SubscribeLocalEvent<AmmoCounterComponent, ItemStatusCollectMessage>(OnAmmoCounterCollect);
- SubscribeAllEvent<MuzzleFlashEvent>(OnMuzzleFlash);
- // Plays animated effects on the client.
- SubscribeNetworkEvent<HitscanEvent>(OnHitscan);
- InitializeMagazineVisuals();
- InitializeSpentAmmo();
- }
- private void OnMuzzleFlash(MuzzleFlashEvent args)
- {
- var gunUid = GetEntity(args.Uid);
- CreateEffect(gunUid, args, gunUid, _player.LocalEntity);
- }
- private void OnHitscan(HitscanEvent ev)
- {
- // ALL I WANT IS AN ANIMATED EFFECT
- foreach (var a in ev.Sprites)
- {
- if (a.Sprite is not SpriteSpecifier.Rsi rsi)
- continue;
- var coords = GetCoordinates(a.coordinates);
- if (Deleted(coords.EntityId))
- continue;
- var ent = Spawn(HitscanProto, coords);
- var sprite = Comp<SpriteComponent>(ent);
- var xform = Transform(ent);
- xform.LocalRotation = a.angle;
- sprite[EffectLayers.Unshaded].AutoAnimated = false;
- sprite.LayerSetSprite(EffectLayers.Unshaded, rsi);
- sprite.LayerSetState(EffectLayers.Unshaded, rsi.RsiState);
- sprite.Scale = new Vector2(a.Distance, 1f);
- sprite[EffectLayers.Unshaded].Visible = true;
- var anim = new Animation()
- {
- Length = TimeSpan.FromSeconds(0.48f),
- AnimationTracks =
- {
- new AnimationTrackSpriteFlick()
- {
- LayerKey = EffectLayers.Unshaded,
- KeyFrames =
- {
- new AnimationTrackSpriteFlick.KeyFrame(rsi.RsiState, 0f),
- }
- }
- }
- };
- _animPlayer.Play(ent, anim, "hitscan-effect");
- }
- }
- public override void Update(float frameTime)
- {
- if (!Timing.IsFirstTimePredicted)
- return;
- var entityNull = _player.LocalEntity;
- if (entityNull == null || !TryComp<CombatModeComponent>(entityNull, out var combat) || !combat.IsInCombatMode)
- {
- return;
- }
- var entity = entityNull.Value;
- if (!TryGetGun(entity, out var gunUid, out var gun))
- {
- return;
- }
- var useKey = gun.UseKey ? EngineKeyFunctions.Use : EngineKeyFunctions.UseSecondary;
- if (_inputSystem.CmdStates.GetState(useKey) != BoundKeyState.Down)
- {
- if (gun.ShotCounter != 0)
- EntityManager.RaisePredictiveEvent(new RequestStopShootEvent { Gun = GetNetEntity(gunUid) });
- return;
- }
- if (gun.NextFire > Timing.CurTime)
- return;
- var mousePos = _eyeManager.PixelToMap(_inputManager.MouseScreenPosition);
- if (mousePos.MapId == MapId.Nullspace)
- {
- if (gun.ShotCounter != 0)
- EntityManager.RaisePredictiveEvent(new RequestStopShootEvent { Gun = GetNetEntity(gunUid) });
- return;
- }
- // Define target coordinates relative to gun entity, so that network latency on moving grids doesn't fuck up the target location.
- var coordinates = TransformSystem.ToCoordinates(entity, mousePos);
- NetEntity? target = null;
- if (_state.CurrentState is GameplayStateBase screen)
- target = GetNetEntity(screen.GetClickedEntity(mousePos));
- if (_player.LocalSession is not { } session)
- return;
- Log.Debug($"Sending shoot request tick {Timing.CurTick} / {Timing.CurTime}");
- var projectiles = ShootRequested(GetNetEntity(gunUid), GetNetCoordinates(coordinates), target, null, session);
- RaisePredictiveEvent(new RequestShootEvent()
- {
- Target = target,
- Coordinates = GetNetCoordinates(coordinates),
- Gun = GetNetEntity(gunUid),
- Shot = projectiles?.Select(e => e.Id).ToList(),
- });
- }
- protected override void Popup(string message, EntityUid? uid, EntityUid? user)
- {
- if (uid == null || user == null || !Timing.IsFirstTimePredicted)
- return;
- PopupSystem.PopupEntity(message, uid.Value, user.Value);
- }
- protected override void CreateEffect(EntityUid gunUid, MuzzleFlashEvent message, EntityUid? tracked = null, EntityUid? player = null)
- {
- if (!Timing.IsFirstTimePredicted)
- return;
- // EntityUid check added to stop throwing exceptions due to https://github.com/space-wizards/space-station-14/issues/28252
- // TODO: Check to see why invalid entities are firing effects.
- if (gunUid == EntityUid.Invalid)
- {
- Log.Debug($"Invalid Entity sent MuzzleFlashEvent (proto: {message.Prototype}, gun: {ToPrettyString(gunUid)})");
- return;
- }
- var gunXform = Transform(gunUid);
- var gridUid = gunXform.GridUid;
- EntityCoordinates coordinates;
- if (TryComp(gridUid, out MapGridComponent? mapGrid))
- {
- coordinates = new EntityCoordinates(gridUid.Value, _maps.LocalToGrid(gridUid.Value, mapGrid, gunXform.Coordinates));
- }
- else if (gunXform.MapUid != null)
- {
- coordinates = new EntityCoordinates(gunXform.MapUid.Value, TransformSystem.GetWorldPosition(gunXform));
- }
- else
- {
- return;
- }
- var ent = Spawn(message.Prototype, coordinates);
- TransformSystem.SetWorldRotationNoLerp(ent, message.Angle);
- if (tracked != null)
- {
- var track = EnsureComp<TrackUserComponent>(ent);
- track.User = tracked;
- track.Offset = Vector2.UnitX / 2f;
- }
- var lifetime = 0.4f;
- if (TryComp<TimedDespawnComponent>(gunUid, out var despawn))
- {
- lifetime = despawn.Lifetime;
- }
- var anim = new Animation()
- {
- Length = TimeSpan.FromSeconds(lifetime),
- AnimationTracks =
- {
- new AnimationTrackComponentProperty
- {
- ComponentType = typeof(SpriteComponent),
- Property = nameof(SpriteComponent.Color),
- InterpolationMode = AnimationInterpolationMode.Linear,
- KeyFrames =
- {
- new AnimationTrackProperty.KeyFrame(Color.White.WithAlpha(1f), 0),
- new AnimationTrackProperty.KeyFrame(Color.White.WithAlpha(0f), lifetime)
- }
- }
- }
- };
- _animPlayer.Play(ent, anim, "muzzle-flash");
- if (!TryComp(gunUid, out PointLightComponent? light))
- {
- light = (PointLightComponent)_factory.GetComponent(typeof(PointLightComponent));
- light.NetSyncEnabled = false;
- AddComp(gunUid, light);
- }
- Lights.SetEnabled(gunUid, true, light);
- Lights.SetRadius(gunUid, 2f, light);
- Lights.SetColor(gunUid, Color.FromHex("#cc8e2b"), light);
- Lights.SetEnergy(gunUid, 5f, light);
- var animTwo = new Animation()
- {
- Length = TimeSpan.FromSeconds(lifetime),
- AnimationTracks =
- {
- new AnimationTrackComponentProperty
- {
- ComponentType = typeof(PointLightComponent),
- Property = nameof(PointLightComponent.Energy),
- InterpolationMode = AnimationInterpolationMode.Linear,
- KeyFrames =
- {
- new AnimationTrackProperty.KeyFrame(5f, 0),
- new AnimationTrackProperty.KeyFrame(0f, lifetime)
- }
- },
- new AnimationTrackComponentProperty
- {
- ComponentType = typeof(PointLightComponent),
- Property = nameof(PointLightComponent.AnimatedEnable),
- InterpolationMode = AnimationInterpolationMode.Linear,
- KeyFrames =
- {
- new AnimationTrackProperty.KeyFrame(true, 0),
- new AnimationTrackProperty.KeyFrame(false, lifetime)
- }
- }
- }
- };
- var uidPlayer = EnsureComp<AnimationPlayerComponent>(gunUid);
- _animPlayer.Stop(gunUid, uidPlayer, "muzzle-flash-light");
- _animPlayer.Play((gunUid, uidPlayer), animTwo, "muzzle-flash-light");
- }
- public override void ShootProjectile(EntityUid uid,
- Vector2 direction,
- Vector2 gunVelocity,
- EntityUid gunUid,
- EntityUid? user = null,
- float speed = 20)
- {
- EnsureComp<PredictedProjectileClientComponent>(uid);
- _physics.UpdateIsPredicted(uid);
- base.ShootProjectile(uid, direction, gunVelocity, gunUid, user, speed);
- }
- }
|