using Content.Server.Atmos.EntitySystems; using Content.Server.Storage.EntitySystems; using Content.Server.Stunnable; using Content.Server.Weapons.Ranged.Systems; using Content.Shared.Atmos.Components; using Content.Shared.Containers.ItemSlots; using Content.Shared.Interaction; using Content.Shared.PneumaticCannon; using Content.Shared.Tools.Systems; using Content.Shared.Weapons.Ranged.Components; using Content.Shared.Weapons.Ranged.Events; using Content.Shared.Weapons.Ranged.Systems; using Robust.Shared.Containers; namespace Content.Server.PneumaticCannon; public sealed class PneumaticCannonSystem : SharedPneumaticCannonSystem { [Dependency] private readonly AtmosphereSystem _atmos = default!; [Dependency] private readonly GasTankSystem _gasTank = default!; [Dependency] private readonly GunSystem _gun = default!; [Dependency] private readonly StunSystem _stun = default!; [Dependency] private readonly ItemSlotsSystem _slots = default!; [Dependency] private readonly SharedToolSystem _toolSystem = default!; public override void Initialize() { base.Initialize(); SubscribeLocalEvent(OnInteractUsing, before: new []{ typeof(StorageSystem) }); SubscribeLocalEvent(OnShoot); SubscribeLocalEvent(OnContainerInserting); SubscribeLocalEvent(OnGunRefreshModifiers); } private void OnInteractUsing(EntityUid uid, PneumaticCannonComponent component, InteractUsingEvent args) { if (args.Handled) return; if (!_toolSystem.HasQuality(args.Used, component.ToolModifyPower)) return; var val = (int) component.Power; val = (val + 1) % (int) PneumaticCannonPower.Len; component.Power = (PneumaticCannonPower) val; Popup.PopupEntity(Loc.GetString("pneumatic-cannon-component-change-power", ("power", component.Power.ToString())), uid, args.User); component.ProjectileSpeed = GetProjectileSpeedFromPower(component); if (TryComp(uid, out var gun)) _gun.RefreshModifiers((uid, gun)); args.Handled = true; } private void OnContainerInserting(EntityUid uid, PneumaticCannonComponent component, ContainerIsInsertingAttemptEvent args) { if (args.Container.ID != PneumaticCannonComponent.TankSlotId) return; if (!TryComp(args.EntityUid, out var gas)) return; // only accept tanks if it uses gas if (gas.Air.TotalMoles >= component.GasUsage && component.GasUsage > 0f) return; args.Cancel(); } private void OnShoot(Entity cannon, ref GunShotEvent args) { var (uid, component) = cannon; // require a gas tank if it uses gas var gas = GetGas(cannon); if (gas == null && component.GasUsage > 0f) return; if (component.Power == PneumaticCannonPower.High && _stun.TryUpdateParalyzeDuration(args.User, TimeSpan.FromSeconds(component.HighPowerStunTime))) { Popup.PopupEntity(Loc.GetString("pneumatic-cannon-component-power-stun", ("cannon", uid)), cannon, args.User); } // ignore gas stuff if the cannon doesn't use any if (gas == null) return; // this should always be possible, as we'll eject the gas tank when it no longer is var environment = _atmos.GetContainingMixture(cannon.Owner, false, true); var removed = _gasTank.RemoveAir(gas.Value, component.GasUsage); if (environment != null && removed != null) { _atmos.Merge(environment, removed); } if (gas.Value.Comp.Air.TotalMoles >= component.GasUsage) return; // eject gas tank _slots.TryEject(uid, PneumaticCannonComponent.TankSlotId, args.User, out _); } private void OnGunRefreshModifiers(Entity ent, ref GunRefreshModifiersEvent args) { if (ent.Comp.ProjectileSpeed is { } speed) args.ProjectileSpeed = speed; } /// /// Returns whether the pneumatic cannon has enough gas to shoot an item, as well as the tank itself. /// private Entity? GetGas(EntityUid uid) { if (!Container.TryGetContainer(uid, PneumaticCannonComponent.TankSlotId, out var container) || container is not ContainerSlot slot || slot.ContainedEntity is not {} contained) return null; return TryComp(contained, out var gasTank) ? (contained, gasTank) : null; } private float GetProjectileSpeedFromPower(PneumaticCannonComponent component) { return component.Power switch { PneumaticCannonPower.High => component.BaseProjectileSpeed * 4f, PneumaticCannonPower.Medium => component.BaseProjectileSpeed, PneumaticCannonPower.Low or _ => component.BaseProjectileSpeed * 0.5f, }; } }