using System; using Content.Server.GameObjects.Components.Mobs; using Content.Server.GameObjects.Components.Weapon.Ranged.Barrels; using Content.Server.GameObjects.EntitySystems; using Content.Server.Interfaces.GameObjects.Components.Interaction; using Content.Shared.GameObjects.Components.Weapons.Ranged; using Content.Shared.GameObjects.EntitySystems; using Robust.Shared.GameObjects; using Robust.Shared.Interfaces.GameObjects; using Robust.Shared.Interfaces.Network; using Robust.Shared.Interfaces.Timing; using Robust.Shared.IoC; using Robust.Shared.Log; using Robust.Shared.Map; using Robust.Shared.Players; namespace Content.Server.GameObjects.Components.Weapon.Ranged { [RegisterComponent] public sealed class ServerRangedWeaponComponent : SharedRangedWeaponComponent, IHandSelected { private TimeSpan _lastFireTime; public Func WeaponCanFireHandler; public Func UserCanFireHandler; public Action FireHandler; public ServerRangedBarrelComponent Barrel { get => _barrel; set { if (_barrel != null && value != null) { Logger.Error("Tried setting Barrel on RangedWeapon that already has one"); throw new InvalidOperationException(); } _barrel = value; Dirty(); } } private ServerRangedBarrelComponent _barrel; private FireRateSelector FireRateSelector => _barrel?.FireRateSelector ?? FireRateSelector.Safety; private bool WeaponCanFire() { return WeaponCanFireHandler == null || WeaponCanFireHandler(); } private bool UserCanFire(IEntity user) { return (UserCanFireHandler == null || UserCanFireHandler(user)) && ActionBlockerSystem.CanAttack(user); } public override void HandleNetworkMessage(ComponentMessage message, INetChannel channel, ICommonSession session = null) { base.HandleNetworkMessage(message, channel, session); if (session == null) { throw new ArgumentNullException(nameof(session)); } switch (message) { case FirePosComponentMessage msg: var user = session.AttachedEntity; if (user == null) { return; } _tryFire(user, msg.Target); break; } } public override ComponentState GetComponentState() { return new RangedWeaponComponentState(FireRateSelector); } private void _tryFire(IEntity user, GridCoordinates coordinates) { if (!user.TryGetComponent(out HandsComponent hands) || hands.GetActiveHand?.Owner != Owner) { return; } if(!user.TryGetComponent(out CombatModeComponent combat) || !combat.IsInCombatMode) { return; } if (!UserCanFire(user) || !WeaponCanFire()) { return; } var curTime = IoCManager.Resolve().CurTime; var span = curTime - _lastFireTime; if (span.TotalSeconds < 1 / _barrel.FireRate) { return; } _lastFireTime = curTime; FireHandler?.Invoke(user, coordinates); } // Probably a better way to do this. void IHandSelected.HandSelected(HandSelectedEventArgs eventArgs) { Dirty(); } } }