From 3f51ffbd3c46531142b7472aceb0513781da40bb Mon Sep 17 00:00:00 2001 From: mirrorcult Date: Wed, 3 Nov 2021 14:33:36 -0700 Subject: [PATCH] Pneumatic cannons (#4560) * basics & visuals * pneumatic cannon works perf * loc + popups * gas tank does stuff + queue changes * updates * b * forcefeeding * inhand * crafting! * pie cannon now is a pneumatic cannon * oopy * fix for entman + verbs * pie * change for tools * actual * combat mode + better sounds * reviews --- Content.Client/Entry/IgnoredComponents.cs | 5 +- .../PneumaticCannonVisualizer.cs | 21 + .../Nutrition/Components/FoodComponent.cs | 4 +- .../Components/ForcefeedOnCollideComponent.cs | 24 ++ .../EntitySystems/ForcefeedOnCollideSystem.cs | 36 ++ .../PneumaticCannonComponent.cs | 111 +++++ .../PneumaticCannon/PneumaticCannonSystem.cs | 402 ++++++++++++++++++ .../Components/ServerStorageComponent.cs | 5 + .../Ammunition/Components/AmmoComponent.cs | 1 - .../PneumaticCannon/SharedPneumaticCannon.cs | 18 + Resources/Audio/Effects/thunk.ogg | Bin 0 -> 10975 bytes Resources/Audio/Items/hiss.ogg | Bin 0 -> 14202 bytes .../pneumatic-cannon-component.ftl | 41 ++ .../Objects/Consumable/Food/Baked/pie.yml | 4 - .../Entities/Objects/Misc/handcuffs.yml | 3 + .../Weapons/Guns/Launchers/launchers.yml | 29 -- .../Weapons/Guns/Projectiles/projectiles.yml | 27 -- .../Objects/Weapons/Guns/pneumatic_cannon.yml | 56 +++ .../Structures/Piping/Atmospherics/pipes.yml | 3 + .../Crafting/Graphs/pneumatic_cannon.yml | 24 ++ Resources/Prototypes/Recipes/Crafting/bat.yml | 12 - .../Recipes/Crafting/improvised.yml | 36 ++ .../Recipes/Crafting/makeshifthandcuffs.yml | 11 - Resources/Prototypes/tags.yml | 6 + .../pie_cannon.rsi/inhand-left.png | Bin .../pie_cannon.rsi/inhand-right.png | Bin .../pie_cannon.rsi/meta.json | 0 .../pie_cannon.rsi/piecannon.png | Bin .../pneumatic_cannon.rsi/inhand-left.png | Bin 0 -> 388 bytes .../pneumatic_cannon.rsi/inhand-right.png | Bin 0 -> 391 bytes .../Cannons/pneumatic_cannon.rsi/meta.json | 1 + .../Cannons/pneumatic_cannon.rsi/oxygen.png | Bin 0 -> 799 bytes .../pneumatic_cannon.rsi/pneumaticCannon.png | Bin 0 -> 888 bytes 33 files changed, 791 insertions(+), 89 deletions(-) create mode 100644 Content.Client/PneumaticCannon/PneumaticCannonVisualizer.cs create mode 100644 Content.Server/Nutrition/Components/ForcefeedOnCollideComponent.cs create mode 100644 Content.Server/Nutrition/EntitySystems/ForcefeedOnCollideSystem.cs create mode 100644 Content.Server/PneumaticCannon/PneumaticCannonComponent.cs create mode 100644 Content.Server/PneumaticCannon/PneumaticCannonSystem.cs create mode 100644 Content.Shared/PneumaticCannon/SharedPneumaticCannon.cs create mode 100644 Resources/Audio/Effects/thunk.ogg create mode 100644 Resources/Audio/Items/hiss.ogg create mode 100644 Resources/Locale/en-US/pneumatic-cannon/pneumatic-cannon-component.ftl create mode 100644 Resources/Prototypes/Entities/Objects/Weapons/Guns/pneumatic_cannon.yml create mode 100644 Resources/Prototypes/Recipes/Crafting/Graphs/pneumatic_cannon.yml delete mode 100644 Resources/Prototypes/Recipes/Crafting/bat.yml create mode 100644 Resources/Prototypes/Recipes/Crafting/improvised.yml delete mode 100644 Resources/Prototypes/Recipes/Crafting/makeshifthandcuffs.yml rename Resources/Textures/Objects/Weapons/Guns/{Launchers => Cannons}/pie_cannon.rsi/inhand-left.png (100%) rename Resources/Textures/Objects/Weapons/Guns/{Launchers => Cannons}/pie_cannon.rsi/inhand-right.png (100%) rename Resources/Textures/Objects/Weapons/Guns/{Launchers => Cannons}/pie_cannon.rsi/meta.json (100%) rename Resources/Textures/Objects/Weapons/Guns/{Launchers => Cannons}/pie_cannon.rsi/piecannon.png (100%) create mode 100644 Resources/Textures/Objects/Weapons/Guns/Cannons/pneumatic_cannon.rsi/inhand-left.png create mode 100644 Resources/Textures/Objects/Weapons/Guns/Cannons/pneumatic_cannon.rsi/inhand-right.png create mode 100644 Resources/Textures/Objects/Weapons/Guns/Cannons/pneumatic_cannon.rsi/meta.json create mode 100644 Resources/Textures/Objects/Weapons/Guns/Cannons/pneumatic_cannon.rsi/oxygen.png create mode 100644 Resources/Textures/Objects/Weapons/Guns/Cannons/pneumatic_cannon.rsi/pneumaticCannon.png diff --git a/Content.Client/Entry/IgnoredComponents.cs b/Content.Client/Entry/IgnoredComponents.cs index b31db597b1..720bdcd2e4 100644 --- a/Content.Client/Entry/IgnoredComponents.cs +++ b/Content.Client/Entry/IgnoredComponents.cs @@ -287,9 +287,9 @@ namespace Content.Client.Entry "Uplink", "PDA", "SpawnItemsOnUse", + "AmbientOnPowered", "Wieldable", "IncreaseDamageOnWield", - "AmbientOnPowered", "TabletopGame", "LitOnPowered", "TriggerOnSignalReceived", @@ -304,7 +304,8 @@ namespace Content.Client.Entry "HandLabeler", "Label", "GhostRadio", - "Armor" + "Armor", + "PneumaticCannon" }; } } diff --git a/Content.Client/PneumaticCannon/PneumaticCannonVisualizer.cs b/Content.Client/PneumaticCannon/PneumaticCannonVisualizer.cs new file mode 100644 index 0000000000..de7d6a3068 --- /dev/null +++ b/Content.Client/PneumaticCannon/PneumaticCannonVisualizer.cs @@ -0,0 +1,21 @@ +using Content.Shared.PneumaticCannon; +using Robust.Client.GameObjects; + +namespace Content.Client.PneumaticCannon +{ + public class PneumaticCannonVisualizer : AppearanceVisualizer + { + public override void OnChangeData(AppearanceComponent component) + { + base.OnChangeData(component); + + if (!component.Owner.TryGetComponent(out var sprite)) + return; + + if (component.TryGetData(PneumaticCannonVisuals.Tank, out bool tank)) + { + sprite.LayerSetVisible(PneumaticCannonVisualLayers.Tank, tank); + } + } + } +} diff --git a/Content.Server/Nutrition/Components/FoodComponent.cs b/Content.Server/Nutrition/Components/FoodComponent.cs index d0125d2d34..6e8fcbc22a 100644 --- a/Content.Server/Nutrition/Components/FoodComponent.cs +++ b/Content.Server/Nutrition/Components/FoodComponent.cs @@ -199,7 +199,7 @@ namespace Content.Server.Nutrition.Components if (string.IsNullOrEmpty(TrashPrototype)) { - Owner.Delete(); + Owner.QueueDelete(); return true; } @@ -208,8 +208,6 @@ namespace Content.Server.Nutrition.Components return true; } - - private void DeleteAndSpawnTrash(IEntity user) { //We're empty. Become trash. diff --git a/Content.Server/Nutrition/Components/ForcefeedOnCollideComponent.cs b/Content.Server/Nutrition/Components/ForcefeedOnCollideComponent.cs new file mode 100644 index 0000000000..91d90fcf46 --- /dev/null +++ b/Content.Server/Nutrition/Components/ForcefeedOnCollideComponent.cs @@ -0,0 +1,24 @@ +using Content.Server.Nutrition.EntitySystems; +using Robust.Shared.Analyzers; +using Robust.Shared.GameObjects; +using Robust.Shared.Serialization.Manager.Attributes; + +namespace Content.Server.Nutrition.Components +{ + /// + /// A food item with this component will be forcefully fed to anyone + /// + [RegisterComponent, Friend(typeof(ForcefeedOnCollideSystem))] + public class ForcefeedOnCollideComponent : Component + { + public override string Name => "ForcefeedOnCollide"; + + /// + /// Since this component is primarily used by the pneumatic cannon, which adds this comp on throw start + /// and wants to remove it on throw end, this is set to false. However, you're free to change it if you want + /// something that can -always- be forcefed on collide, or something. + /// + [DataField("removeOnThrowEnd")] + public bool RemoveOnThrowEnd = true; + } +} diff --git a/Content.Server/Nutrition/EntitySystems/ForcefeedOnCollideSystem.cs b/Content.Server/Nutrition/EntitySystems/ForcefeedOnCollideSystem.cs new file mode 100644 index 0000000000..02c7e30133 --- /dev/null +++ b/Content.Server/Nutrition/EntitySystems/ForcefeedOnCollideSystem.cs @@ -0,0 +1,36 @@ +using Content.Server.Nutrition.Components; +using Content.Shared.Throwing; +using Robust.Shared.GameObjects; + +namespace Content.Server.Nutrition.EntitySystems +{ + public class ForcefeedOnCollideSystem : EntitySystem + { + public override void Initialize() + { + base.Initialize(); + + SubscribeLocalEvent(OnThrowDoHit); + SubscribeLocalEvent(OnLand); + } + + private void OnThrowDoHit(EntityUid uid, ForcefeedOnCollideComponent component, ThrowDoHitEvent args) + { + if (!args.Target.HasComponent()) + return; + if (!EntityManager.TryGetComponent(uid, out var food)) + return; + + // the 'target' isnt really the 'user' per se.. but.. + food.TryUseFood(args.Target, args.Target); + } + + private void OnLand(EntityUid uid, ForcefeedOnCollideComponent component, LandEvent args) + { + if (!component.RemoveOnThrowEnd) + return; + + EntityManager.RemoveComponent(uid, component); + } + } +} diff --git a/Content.Server/PneumaticCannon/PneumaticCannonComponent.cs b/Content.Server/PneumaticCannon/PneumaticCannonComponent.cs new file mode 100644 index 0000000000..319b16170c --- /dev/null +++ b/Content.Server/PneumaticCannon/PneumaticCannonComponent.cs @@ -0,0 +1,111 @@ +using System.Collections.Generic; +using Content.Shared.Sound; +using Content.Shared.Tools; +using Content.Shared.Verbs; +using Robust.Shared.Analyzers; +using Robust.Shared.Containers; +using Robust.Shared.GameObjects; +using Robust.Shared.Localization; +using Robust.Shared.Maths; +using Robust.Shared.Serialization.Manager.Attributes; +using Robust.Shared.Serialization.TypeSerializers.Implementations.Custom.Prototype; +using Robust.Shared.ViewVariables; + +namespace Content.Server.PneumaticCannon +{ + // TODO: ideally, this and most of the actual firing code doesn't need to exist, and guns can be flexible enough + // to handle shooting things that aren't ammo (just firing any entity) + [RegisterComponent, Friend(typeof(PneumaticCannonSystem))] + public class PneumaticCannonComponent : Component + { + public override string Name { get; } = "PneumaticCannon"; + + [ViewVariables] + public ContainerSlot GasTankSlot = default!; + + [ViewVariables(VVAccess.ReadWrite)] + public PneumaticCannonPower Power = PneumaticCannonPower.Low; + + [ViewVariables(VVAccess.ReadWrite)] + public PneumaticCannonFireMode Mode = PneumaticCannonFireMode.Single; + + /// + /// Used to fire the pneumatic cannon in intervals rather than all at the same time + /// + public float AccumulatedFrametime; + + public Queue FireQueue = new(); + + [DataField("fireInterval")] + public float FireInterval = 0.1f; + + /// + /// Whether the pneumatic cannon should instantly fire once, or whether it should wait for the + /// fire interval initially. + /// + [DataField("instantFire")] + public bool InstantFire = true; + + [DataField("toolModifyPower", customTypeSerializer:typeof(PrototypeIdSerializer))] + public string ToolModifyPower = "Welding"; + + [DataField("toolModifyMode", customTypeSerializer:typeof(PrototypeIdSerializer))] + public string ToolModifyMode = "Screwing"; + + /// + /// If this value is too high it just straight up stops working for some reason + /// + [DataField("throwStrength")] + [ViewVariables(VVAccess.ReadWrite)] + public float ThrowStrength = 20.0f; + + [DataField("baseThrowRange")] + [ViewVariables(VVAccess.ReadWrite)] + public float BaseThrowRange = 8.0f; + + /// + /// How long to stun for if they shoot the pneumatic cannon at high power. + /// + [DataField("highPowerStunTime")] + [ViewVariables(VVAccess.ReadWrite)] + public float HighPowerStunTime = 3.0f; + + [DataField("gasTankRequired")] + [ViewVariables(VVAccess.ReadWrite)] + public bool GasTankRequired = true; + + [DataField("fireSound")] + [ViewVariables(VVAccess.ReadWrite)] + public SoundSpecifier FireSound = new SoundPathSpecifier("/Audio/Effects/thunk.ogg"); + + public struct FireData + { + public IEntity User; + public float Strength; + public Vector2 Direction; + } + } + + /// + /// How strong the pneumatic cannon should be. + /// Each tier throws items farther and with more speed, but has drawbacks. + /// The highest power knocks the player down for a considerable amount of time. + /// + public enum PneumaticCannonPower : byte + { + Low = 0, + Medium = 1, + High = 2, + Len = 3 // used for length calc + } + + /// + /// Whether to shoot one random item at a time, or all items at the same time. + /// + public enum PneumaticCannonFireMode : byte + { + Single = 0, + All = 1, + Len = 2 // used for length calc + } +} diff --git a/Content.Server/PneumaticCannon/PneumaticCannonSystem.cs b/Content.Server/PneumaticCannon/PneumaticCannonSystem.cs new file mode 100644 index 0000000000..bfa073c4d3 --- /dev/null +++ b/Content.Server/PneumaticCannon/PneumaticCannonSystem.cs @@ -0,0 +1,402 @@ +using System; +using System.Collections.Generic; +using System.Linq; +using Content.Server.Atmos.Components; +using Content.Server.Atmos.EntitySystems; +using Content.Server.Camera; +using Content.Server.CombatMode; +using Content.Server.Hands.Components; +using Content.Server.Items; +using Content.Server.Nutrition.Components; +using Content.Server.Storage.Components; +using Content.Server.Stunnable; +using Content.Server.Stunnable.Components; +using Content.Shared.Interaction; +using Content.Shared.PneumaticCannon; +using Robust.Server.GameObjects; +using Robust.Shared.Containers; +using Robust.Shared.GameObjects; +using Robust.Shared.Map; +using Content.Server.Throwing; +using Content.Server.Tools; +using Content.Server.Tools.Components; +using Content.Shared.CombatMode; +using Content.Shared.Popups; +using Content.Shared.Sound; +using Content.Shared.StatusEffect; +using Content.Shared.Verbs; +using Content.Shared.Weapons.Melee; +using Robust.Shared.Audio; +using Robust.Shared.IoC; +using Robust.Shared.Localization; +using Robust.Shared.Maths; +using Robust.Shared.Player; +using Robust.Shared.Random; + +namespace Content.Server.PneumaticCannon +{ + public class PneumaticCannonSystem : EntitySystem + { + [Dependency] private readonly IRobustRandom _random = default!; + [Dependency] private readonly StunSystem _stun = default!; + [Dependency] private readonly AtmosphereSystem _atmos = default!; + + private HashSet _currentlyFiring = new(); + + public override void Initialize() + { + base.Initialize(); + + SubscribeLocalEvent(OnComponentInit); + SubscribeLocalEvent(OnInteractUsing); + SubscribeLocalEvent(OnAfterInteract); + SubscribeLocalEvent(OnAlternativeVerbs); + SubscribeLocalEvent(OnOtherVerbs); + } + + public override void Update(float frameTime) + { + base.Update(frameTime); + + if (_currentlyFiring.Count == 0) + return; + + foreach (var comp in _currentlyFiring.ToArray()) + { + if (comp.FireQueue.Count == 0) + { + _currentlyFiring.Remove(comp); + // reset acc frametime to the fire interval if we're instant firing + if (comp.InstantFire) + { + comp.AccumulatedFrametime = comp.FireInterval; + } + else + { + comp.AccumulatedFrametime = 0f; + } + return; + } + + comp.AccumulatedFrametime += frameTime; + if (comp.AccumulatedFrametime > comp.FireInterval) + { + var dat = comp.FireQueue.Dequeue(); + Fire(comp, dat); + comp.AccumulatedFrametime -= comp.FireInterval; + } + } + } + + private void OnComponentInit(EntityUid uid, PneumaticCannonComponent component, ComponentInit args) + { + component.GasTankSlot = component.Owner.EnsureContainer($"{component.Name}-gasTank"); + + if (component.InstantFire) + component.AccumulatedFrametime = component.FireInterval; + } + + private void OnInteractUsing(EntityUid uid, PneumaticCannonComponent component, InteractUsingEvent args) + { + args.Handled = true; + if (args.Used.HasComponent() + && component.GasTankSlot.CanInsert(args.Used) + && component.GasTankRequired) + { + component.GasTankSlot.Insert(args.Used); + args.User.PopupMessage(Loc.GetString("pneumatic-cannon-component-gas-tank-insert", + ("tank", args.Used), ("cannon", component.Owner))); + UpdateAppearance(component); + return; + } + + if (args.Used.TryGetComponent(out var tool)) + { + if (tool.Qualities.Contains(component.ToolModifyMode)) + { + // this is kind of ugly but it just cycles the enum + var val = (int) component.Mode; + val = (val + 1) % (int) PneumaticCannonFireMode.Len; + component.Mode = (PneumaticCannonFireMode) val; + args.User.PopupMessage(Loc.GetString("pneumatic-cannon-component-change-fire-mode", + ("mode", component.Mode.ToString()))); + // sound + return; + } + + if (tool.Qualities.Contains(component.ToolModifyPower)) + { + var val = (int) component.Power; + val = (val + 1) % (int) PneumaticCannonPower.Len; + component.Power = (PneumaticCannonPower) val; + args.User.PopupMessage(Loc.GetString("pneumatic-cannon-component-change-power", + ("power", component.Power.ToString()))); + // sound + return; + } + } + + // this overrides the ServerStorageComponent's insertion stuff because + // it's not event-based yet and I can't cancel it, so tools and stuff + // will modify mode/power then get put in anyway + if (args.Used.TryGetComponent(out var item) + && component.Owner.TryGetComponent(out var storage)) + { + if (storage.CanInsert(args.Used)) + { + storage.Insert(args.Used); + args.User.PopupMessage(Loc.GetString("pneumatic-cannon-component-insert-item-success", + ("item", args.Used), ("cannon", component.Owner))); + } + else + { + args.User.PopupMessage(Loc.GetString("pneumatic-cannon-component-insert-item-failure", + ("item", args.Used), ("cannon", component.Owner))); + } + } + } + + private void OnAfterInteract(EntityUid uid, PneumaticCannonComponent component, AfterInteractEvent args) + { + if (EntityManager.TryGetComponent(uid, out var combat) + && !combat.IsInCombatMode) + return; + + args.Handled = true; + + if (!HasGas(component) && component.GasTankRequired) + { + args.User.PopupMessage(Loc.GetString("pneumatic-cannon-component-fire-no-gas", + ("cannon", component.Owner))); + SoundSystem.Play(Filter.Pvs(args.Used.Uid), "/Audio/Items/hiss.ogg"); + return; + } + AddToQueue(component, args.User, args.ClickLocation); + } + + public void AddToQueue(PneumaticCannonComponent comp, IEntity user, EntityCoordinates click) + { + if (!comp.Owner.TryGetComponent(out var storage)) + return; + if (storage.StoredEntities == null) return; + if (storage.StoredEntities.Count == 0) + { + SoundSystem.Play(Filter.Pvs(comp.Owner.Uid), "/Audio/Weapons/click.ogg"); + return; + } + + _currentlyFiring.Add(comp); + + int entCounts = comp.Mode switch + { + PneumaticCannonFireMode.All => storage.StoredEntities.Count, + PneumaticCannonFireMode.Single => 1, + _ => 0 + }; + + for (int i = 0; i < entCounts; i++) + { + var dir = (click.ToMapPos(EntityManager) - user.Transform.WorldPosition).Normalized; + + var randomAngle = GetRandomFireAngleFromPower(comp.Power).RotateVec(dir); + var randomStrengthMult = _random.NextFloat(0.75f, 1.25f); + var throwMult = GetRangeMultFromPower(comp.Power); + + var data = new PneumaticCannonComponent.FireData + { + User = user, + Strength = comp.ThrowStrength * randomStrengthMult, + Direction = (dir + randomAngle).Normalized * comp.BaseThrowRange * throwMult, + }; + comp.FireQueue.Enqueue(data); + } + } + + public void Fire(PneumaticCannonComponent comp, PneumaticCannonComponent.FireData data) + { + if (!HasGas(comp) && comp.GasTankRequired) + { + data.User.PopupMessage(Loc.GetString("pneumatic-cannon-component-fire-no-gas", + ("cannon", comp.Owner))); + SoundSystem.Play(Filter.Pvs(comp.Owner.Uid), "/Audio/Items/hiss.ogg"); + return; + } + + if (!comp.Owner.TryGetComponent(out var storage)) + return; + + if (data.User.Deleted) + return; + + if (storage.StoredEntities == null) return; + if (storage.StoredEntities.Count == 0) return; // click sound? + + IEntity ent = _random.Pick(storage.StoredEntities); + storage.Remove(ent); + + SoundSystem.Play(Filter.Pvs(data.User), comp.FireSound.GetSound()); + if (data.User.TryGetComponent(out var recoil)) + { + recoil.Kick(Vector2.One * data.Strength); + } + + ent.TryThrow(data.Direction, data.Strength, data.User, GetPushbackRatioFromPower(comp.Power)); + + // lasagna, anybody? + ent.EnsureComponent(); + + if(data.User.TryGetComponent(out var status) + && comp.Power == PneumaticCannonPower.High) + { + _stun.TryParalyze(data.User.Uid, TimeSpan.FromSeconds(comp.HighPowerStunTime), status); + data.User.PopupMessage(Loc.GetString("pneumatic-cannon-component-power-stun", + ("cannon", comp.Owner))); + } + + if (comp.GasTankSlot.ContainedEntity != null && comp.GasTankRequired) + { + // we checked for this earlier in HasGas so a GetComp is okay + var gas = comp.GasTankSlot.ContainedEntity.GetComponent(); + var environment = _atmos.GetTileMixture(comp.Owner.Transform.Coordinates, true); + var removed = gas.RemoveAir(GetMoleUsageFromPower(comp.Power)); + if (environment != null && removed != null) + { + _atmos.Merge(environment, removed); + } + } + } + + /// + /// Returns whether the pneumatic cannon has enough gas to shoot an item. + /// + public bool HasGas(PneumaticCannonComponent component) + { + var usage = GetMoleUsageFromPower(component.Power); + + if (component.GasTankSlot.ContainedEntity == null) + return false; + + // not sure how it wouldnt, but it might not! who knows + if (component.GasTankSlot.ContainedEntity.TryGetComponent(out var tank)) + { + if (tank.Air.TotalMoles < usage) + return false; + + return true; + } + + return false; + } + + private void OnAlternativeVerbs(EntityUid uid, PneumaticCannonComponent component, GetAlternativeVerbsEvent args) + { + if (component.GasTankSlot.ContainedEntities.Count == 0 || !component.GasTankRequired) + return; + if (!args.CanInteract) + return; + + Verb ejectTank = new(); + ejectTank.Act = () => TryRemoveGasTank(component, args.User); + ejectTank.Text = Loc.GetString("pneumatic-cannon-component-verb-gas-tank-name"); + args.Verbs.Add(ejectTank); + } + + private void OnOtherVerbs(EntityUid uid, PneumaticCannonComponent component, GetOtherVerbsEvent args) + { + if (!args.CanInteract) + return; + + Verb ejectItems = new(); + ejectItems.Act = () => TryEjectAllItems(component, args.User); + ejectItems.Text = Loc.GetString("pneumatic-cannon-component-verb-eject-items-name"); + args.Verbs.Add(ejectItems); + } + + public void TryRemoveGasTank(PneumaticCannonComponent component, IEntity user) + { + if (component.GasTankSlot.ContainedEntity == null) + { + user.PopupMessage(Loc.GetString("pneumatic-cannon-component-gas-tank-none", + ("cannon", component.Owner))); + return; + } + + var ent = component.GasTankSlot.ContainedEntity; + if (component.GasTankSlot.Remove(ent)) + { + if (user.TryGetComponent(out var hands)) + { + hands.TryPutInActiveHandOrAny(ent); + } + + user.PopupMessage(Loc.GetString("pneumatic-cannon-component-gas-tank-remove", + ("tank", ent), ("cannon", component.Owner))); + UpdateAppearance(component); + } + } + + public void TryEjectAllItems(PneumaticCannonComponent component, IEntity user) + { + if (component.Owner.TryGetComponent(out var storage)) + { + if (storage.StoredEntities == null) return; + foreach (var entity in storage.StoredEntities.ToArray()) + { + storage.Remove(entity); + } + + user.PopupMessage(Loc.GetString("pneumatic-cannon-component-ejected-all", + ("cannon", (component.Owner)))); + } + } + + private void UpdateAppearance(PneumaticCannonComponent component) + { + if (component.Owner.TryGetComponent(out var appearance)) + { + appearance.SetData(PneumaticCannonVisuals.Tank, + component.GasTankSlot.ContainedEntities.Count != 0); + } + } + + private Angle GetRandomFireAngleFromPower(PneumaticCannonPower power) + { + return power switch + { + PneumaticCannonPower.High => _random.NextAngle(-0.3, 0.3), + PneumaticCannonPower.Medium => _random.NextAngle(-0.2, 0.2), + PneumaticCannonPower.Low or _ => _random.NextAngle(-0.1, 0.1), + }; + } + + private float GetRangeMultFromPower(PneumaticCannonPower power) + { + return power switch + { + PneumaticCannonPower.High => 1.6f, + PneumaticCannonPower.Medium => 1.3f, + PneumaticCannonPower.Low or _ => 1.0f, + }; + } + + private float GetMoleUsageFromPower(PneumaticCannonPower power) + { + return power switch + { + PneumaticCannonPower.High => 15f, + PneumaticCannonPower.Medium => 10f, + PneumaticCannonPower.Low or _ => 5f, + }; + } + + private float GetPushbackRatioFromPower(PneumaticCannonPower power) + { + return power switch + { + PneumaticCannonPower.Medium => 8.0f, + PneumaticCannonPower.High => 16.0f, + PneumaticCannonPower.Low or _ => 0f + }; + } + } +} diff --git a/Content.Server/Storage/Components/ServerStorageComponent.cs b/Content.Server/Storage/Components/ServerStorageComponent.cs index 1cc99c4516..d07866f856 100644 --- a/Content.Server/Storage/Components/ServerStorageComponent.cs +++ b/Content.Server/Storage/Components/ServerStorageComponent.cs @@ -52,6 +52,9 @@ namespace Content.Server.Storage.Components [DataField("quickInsert")] private bool _quickInsert = false; // Can insert storables by "attacking" them with the storage entity + [DataField("clickInsert")] + private bool _clickInsert = true; // Can insert stuff by clicking the storage entity with it + [DataField("areaInsert")] private bool _areaInsert = false; // "Attacking" with the storage entity causes it to insert all nearby storables after a delay [DataField("areaInsertRadius")] @@ -480,6 +483,8 @@ namespace Content.Server.Storage.Components /// true if inserted, false otherwise async Task IInteractUsing.InteractUsing(InteractUsingEventArgs eventArgs) { + if (!_clickInsert) + return false; Logger.DebugS(LoggerName, $"Storage (UID {Owner.Uid}) attacked by user (UID {eventArgs.User.Uid}) with entity (UID {eventArgs.Using.Uid})."); if (Owner.HasComponent()) diff --git a/Content.Server/Weapon/Ranged/Ammunition/Components/AmmoComponent.cs b/Content.Server/Weapon/Ranged/Ammunition/Components/AmmoComponent.cs index ec2cde2300..79377077db 100644 --- a/Content.Server/Weapon/Ranged/Ammunition/Components/AmmoComponent.cs +++ b/Content.Server/Weapon/Ranged/Ammunition/Components/AmmoComponent.cs @@ -179,6 +179,5 @@ namespace Content.Server.Weapon.Ranged.Ammunition.Components Dart, // Placeholder Grenade, Energy, - CreamPie, // I can't wait for this enum to be a prototype type... } } diff --git a/Content.Shared/PneumaticCannon/SharedPneumaticCannon.cs b/Content.Shared/PneumaticCannon/SharedPneumaticCannon.cs new file mode 100644 index 0000000000..be65029f20 --- /dev/null +++ b/Content.Shared/PneumaticCannon/SharedPneumaticCannon.cs @@ -0,0 +1,18 @@ +using System; +using Robust.Shared.Serialization; + +namespace Content.Shared.PneumaticCannon +{ + [Serializable, NetSerializable] + public enum PneumaticCannonVisualLayers : byte + { + Base, + Tank + } + + [Serializable, NetSerializable] + public enum PneumaticCannonVisuals + { + Tank + } +} diff --git a/Resources/Audio/Effects/thunk.ogg b/Resources/Audio/Effects/thunk.ogg new file mode 100644 index 0000000000000000000000000000000000000000..c25ff5ce799cb72b0446dfd5b697f2ce74039c72 GIT binary patch literal 10975 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 F{{eim%o_jz literal 0 HcmV?d00001 diff --git a/Resources/Audio/Items/hiss.ogg b/Resources/Audio/Items/hiss.ogg new file mode 100644 index 0000000000000000000000000000000000000000..d589faad7a7d172ecbd589e164e17bce6f61b1a0 GIT binary patch literal 14202 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 literal 0 HcmV?d00001 diff --git a/Resources/Locale/en-US/pneumatic-cannon/pneumatic-cannon-component.ftl b/Resources/Locale/en-US/pneumatic-cannon/pneumatic-cannon-component.ftl new file mode 100644 index 0000000000..85fc625a7f --- /dev/null +++ b/Resources/Locale/en-US/pneumatic-cannon/pneumatic-cannon-component.ftl @@ -0,0 +1,41 @@ +### Loc for the pneumatic cannon. + +pneumatic-cannon-component-verb-gas-tank-name = Eject gas tank +pneumatic-cannon-component-verb-eject-items-name = Eject all items + +## Shown when inserting items into it + +pneumatic-cannon-component-insert-item-success = You insert { THE($item) } into { THE($cannon) }. +pneumatic-cannon-component-insert-item-failure = You can't seem to fit { THE($item) } in { THE($cannon) }. + +## Shown when trying to fire, but no gas + +pneumatic-cannon-component-fire-no-gas = { CAPITALIZE(THE($cannon)) } clicks, but no gas comes out. + +## Shown when changing the fire mode or power. + +pneumatic-cannon-component-change-fire-mode = { $mode -> + [All] You loosen the valves to fire everything at once. + *[Single] You tighten the valves to fire one item at a time. +} + +pneumatic-cannon-component-change-power = { $power -> + [High] You set the limiter to maximum power. It feels a little too powerful... + [Medium] You set the limiter to medium power. + *[Low] You set the limiter to low power. +} + +## Shown when inserting/removing the gas tank. + +pneumatic-cannon-component-gas-tank-insert = You fit { THE($tank) } onto { THE($cannon) }. +pneumatic-cannon-component-gas-tank-remove = You take { THE($tank) } off of { THE($cannon) }. +pneumatic-cannon-component-gas-tank-none = There is no gas tank on { THE($cannon) }! + +## Shown when ejecting every item from the cannon using a verb. + +pneumatic-cannon-component-ejected-all = You eject everything from { THE($cannon) }. + +## Shown when being stunned by having the power too high. + +pneumatic-cannon-component-power-stun = The pure force of { THE($cannon) } knocks you over! + diff --git a/Resources/Prototypes/Entities/Objects/Consumable/Food/Baked/pie.yml b/Resources/Prototypes/Entities/Objects/Consumable/Food/Baked/pie.yml index f2c0d7a002..838941e4d6 100644 --- a/Resources/Prototypes/Entities/Objects/Consumable/Food/Baked/pie.yml +++ b/Resources/Prototypes/Entities/Objects/Consumable/Food/Baked/pie.yml @@ -104,10 +104,6 @@ - state: tin - state: plain - type: CreamPie - - type: Ammo - caliber: CreamPie - caseless: true - projectile: BulletCreampie # Tastes like pie, cream, banana. - type: entity diff --git a/Resources/Prototypes/Entities/Objects/Misc/handcuffs.yml b/Resources/Prototypes/Entities/Objects/Misc/handcuffs.yml index 2fb7397f02..0c883d6e40 100644 --- a/Resources/Prototypes/Entities/Objects/Misc/handcuffs.yml +++ b/Resources/Prototypes/Entities/Objects/Misc/handcuffs.yml @@ -14,6 +14,9 @@ - type: Sprite sprite: Objects/Misc/handcuffs.rsi state: handcuff + - type: Tag + tags: + - Handcuffs - type: entity name: makeshift handcuffs diff --git a/Resources/Prototypes/Entities/Objects/Weapons/Guns/Launchers/launchers.yml b/Resources/Prototypes/Entities/Objects/Weapons/Guns/Launchers/launchers.yml index 7edcff9e9d..59f2cfad25 100644 --- a/Resources/Prototypes/Entities/Objects/Weapons/Guns/Launchers/launchers.yml +++ b/Resources/Prototypes/Entities/Objects/Weapons/Guns/Launchers/launchers.yml @@ -82,32 +82,3 @@ magState: mag steps: 1 zeroVisible: true - -- type: entity - name: pie cannon - parent: LauncherBase - id: LauncherCreamPie - description: Load cream pie for optimal results. - components: - - type: Sprite - sprite: Objects/Weapons/Guns/Launchers/pie_cannon.rsi - state: piecannon - - type: Item - size: 24 - sprite: Objects/Weapons/Guns/Launchers/pie_cannon.rsi - - type: RangedWeapon - clumsyCheck: false - - type: RevolverBarrel - caliber: CreamPie - currentSelector: Single - allSelectors: - - Single - fillPrototype: FoodPieBananaCream - fireRate: 5 - capacity: 5 - soundEmpty: - path: /Audio/Weapons/Guns/Empty/empty.ogg - soundGunshot: - path: /Audio/Effects/bang.ogg - soundInsert: - path: /Audio/Items/bikehorn.ogg diff --git a/Resources/Prototypes/Entities/Objects/Weapons/Guns/Projectiles/projectiles.yml b/Resources/Prototypes/Entities/Objects/Weapons/Guns/Projectiles/projectiles.yml index 8fcb595343..c9f2e29fc7 100644 --- a/Resources/Prototypes/Entities/Objects/Weapons/Guns/Projectiles/projectiles.yml +++ b/Resources/Prototypes/Entities/Objects/Weapons/Guns/Projectiles/projectiles.yml @@ -312,30 +312,3 @@ damage: types: Piercing: 0 - -- type: entity - id: BulletCreampie - name: cream pie - parent: BulletBase - description: get creampied, honk!! - abstract: true - components: - - type: Projectile - deleteOnCollide: false # CreamPie component handles this. - damage: - types: - Blunt: 1 - - type: CreamPie - - type: ThrownItem - - type: Sprite - sprite: Objects/Consumable/Food/Baked/pie.rsi - netsync: false - layers: - - state: tin - - state: plain - - type: SolutionContainerManager - solutions: - food: - reagents: - - ReagentId: Nutriment - Quantity: 8 diff --git a/Resources/Prototypes/Entities/Objects/Weapons/Guns/pneumatic_cannon.yml b/Resources/Prototypes/Entities/Objects/Weapons/Guns/pneumatic_cannon.yml new file mode 100644 index 0000000000..8071cf9eac --- /dev/null +++ b/Resources/Prototypes/Entities/Objects/Weapons/Guns/pneumatic_cannon.yml @@ -0,0 +1,56 @@ +- type: entity + name: improvised pneumatic cannon + parent: BaseItem + id: ImprovisedPneumaticCannon + description: Improvised using nothing but a pipe, some zipties, and a pneumatic cannon. + components: + - type: Sprite + sprite: Objects/Weapons/Guns/Cannons/pneumatic_cannon.rsi + netsync: false + layers: + - state: pneumaticCannon + map: [ "enum.PneumaticCannonVisualLayers.Base" ] + - state: oxygen + map: [ "enum.PneumaticCannonVisualLayers.Tank" ] + visible: false + - type: Item + size: 40 + sprite: Objects/Weapons/Guns/Cannons/pneumatic_cannon.rsi + - type: PneumaticCannon + - type: Storage + # todo mirror pneum replace with ecs/evnts + clickInsert: false + capacity: 30 + - type: Appearance + visuals: + - type: PneumaticCannonVisualizer + - type: Construction + graph: PneumaticCannon + node: cannon + +- type: entity + name: pie cannon + parent: BaseItem + id: LauncherCreamPie + description: Load cream pie for optimal results. + components: + - type: Sprite + sprite: Objects/Weapons/Guns/Cannons/pie_cannon.rsi + layers: + - state: piecannon + - type: Storage + whitelist: + components: + - CreamPie + clickInsert: false + storageSoundCollection: + collection: BikeHorn + capacity: 40 + - type: PneumaticCannon + gasTankRequired: false + throwStrength: 30 + baseThrowRange: 12 + fireInterval: 0.4 + - type: Item + size: 50 + sprite: Objects/Weapons/Guns/Cannons/pie_cannon.rsi diff --git a/Resources/Prototypes/Entities/Structures/Piping/Atmospherics/pipes.yml b/Resources/Prototypes/Entities/Structures/Piping/Atmospherics/pipes.yml index 3cb9ef729d..44cc8d5e11 100644 --- a/Resources/Prototypes/Entities/Structures/Piping/Atmospherics/pipes.yml +++ b/Resources/Prototypes/Entities/Structures/Piping/Atmospherics/pipes.yml @@ -61,6 +61,9 @@ - type: AtmosUnsafeUnanchor - type: AtmosPipeColor - type: SubFloorHide + - type: Tag + tags: + - Pipe #Note: The PipeDirection of the PipeNode should be the south-facing version, because the entity starts at an angle of 0 (south) diff --git a/Resources/Prototypes/Recipes/Crafting/Graphs/pneumatic_cannon.yml b/Resources/Prototypes/Recipes/Crafting/Graphs/pneumatic_cannon.yml new file mode 100644 index 0000000000..48a526abaa --- /dev/null +++ b/Resources/Prototypes/Recipes/Crafting/Graphs/pneumatic_cannon.yml @@ -0,0 +1,24 @@ +- type: constructionGraph + id: PneumaticCannon + start: start + graph: + - node: start + edges: + - to: cannon + steps: + - tag: Pipe + icon: + sprite: Structures/Piping/Atmospherics/pipe.rsi + state: pipeStraight + name: pipe + - tag: Handcuffs + icon: + sprite: Objects/Misc/cablecuffs.rsi + state: cuff + color: red + name: cuffs + - material: Steel + amount: 6 + doAfter: 10 + - node: cannon + entity: ImprovisedPneumaticCannon diff --git a/Resources/Prototypes/Recipes/Crafting/bat.yml b/Resources/Prototypes/Recipes/Crafting/bat.yml deleted file mode 100644 index dcfc03a8bb..0000000000 --- a/Resources/Prototypes/Recipes/Crafting/bat.yml +++ /dev/null @@ -1,12 +0,0 @@ -- type: construction - name: baseball bat - id: bat - graph: WoodenBat - startNode: start - targetNode: bat - category: Weapons - description: A robust baseball bat. - icon: - sprite: Objects/Weapons/Melee/baseball_bat.rsi - state: icon - objectType: Item diff --git a/Resources/Prototypes/Recipes/Crafting/improvised.yml b/Resources/Prototypes/Recipes/Crafting/improvised.yml new file mode 100644 index 0000000000..74fed82636 --- /dev/null +++ b/Resources/Prototypes/Recipes/Crafting/improvised.yml @@ -0,0 +1,36 @@ +- type: construction + name: baseball bat + id: bat + graph: WoodenBat + startNode: start + targetNode: bat + category: Weapons + description: A robust baseball bat. + icon: + sprite: Objects/Weapons/Melee/baseball_bat.rsi + state: icon + objectType: Item + +- type: construction + name: makeshift handcuffs + id: makeshifthandcuffs + graph: makeshifthandcuffs + startNode: start + targetNode: cuffscable + category: Utility + description: "Homemade handcuffs crafted from spare cables." + icon: Objects/Misc/cablecuffs.rsi/cuff.png + objectType: Item + +- type: construction + name: improvised pneumatic cannon + id: pneumaticcannon + graph: PneumaticCannon + startNode: start + targetNode: cannon + category: Weapons + objectType: Item + description: This son of a gun can fire anything that fits in it using just a little gas. + icon: + sprite: Objects/Weapons/Guns/Cannons/pneumatic_cannon.rsi + state: pneumaticCannon diff --git a/Resources/Prototypes/Recipes/Crafting/makeshifthandcuffs.yml b/Resources/Prototypes/Recipes/Crafting/makeshifthandcuffs.yml deleted file mode 100644 index b7b0344f11..0000000000 --- a/Resources/Prototypes/Recipes/Crafting/makeshifthandcuffs.yml +++ /dev/null @@ -1,11 +0,0 @@ -- type: construction - name: makeshift handcuffs - id: makeshifthandcuffs - graph: makeshifthandcuffs - startNode: start - targetNode: cuffscable - category: Utility - description: "Homemade handcuffs crafted from spare cables." - icon: Objects/Misc/cablecuffs.rsi/cuff.png - objectType: Item - diff --git a/Resources/Prototypes/tags.yml b/Resources/Prototypes/tags.yml index fff9c9dff3..e7edf9fede 100644 --- a/Resources/Prototypes/tags.yml +++ b/Resources/Prototypes/tags.yml @@ -112,6 +112,9 @@ - type: Tag id: GlassBeaker +- type: Tag + id: Handcuffs + - type: Tag id: Hoe @@ -148,6 +151,9 @@ - type: Tag id: Pill +- type: Tag + id: Pipe + - type: Tag id: Pizza diff --git a/Resources/Textures/Objects/Weapons/Guns/Launchers/pie_cannon.rsi/inhand-left.png b/Resources/Textures/Objects/Weapons/Guns/Cannons/pie_cannon.rsi/inhand-left.png similarity index 100% rename from Resources/Textures/Objects/Weapons/Guns/Launchers/pie_cannon.rsi/inhand-left.png rename to Resources/Textures/Objects/Weapons/Guns/Cannons/pie_cannon.rsi/inhand-left.png diff --git a/Resources/Textures/Objects/Weapons/Guns/Launchers/pie_cannon.rsi/inhand-right.png b/Resources/Textures/Objects/Weapons/Guns/Cannons/pie_cannon.rsi/inhand-right.png similarity index 100% rename from Resources/Textures/Objects/Weapons/Guns/Launchers/pie_cannon.rsi/inhand-right.png rename to Resources/Textures/Objects/Weapons/Guns/Cannons/pie_cannon.rsi/inhand-right.png diff --git a/Resources/Textures/Objects/Weapons/Guns/Launchers/pie_cannon.rsi/meta.json b/Resources/Textures/Objects/Weapons/Guns/Cannons/pie_cannon.rsi/meta.json similarity index 100% rename from Resources/Textures/Objects/Weapons/Guns/Launchers/pie_cannon.rsi/meta.json rename to Resources/Textures/Objects/Weapons/Guns/Cannons/pie_cannon.rsi/meta.json diff --git a/Resources/Textures/Objects/Weapons/Guns/Launchers/pie_cannon.rsi/piecannon.png b/Resources/Textures/Objects/Weapons/Guns/Cannons/pie_cannon.rsi/piecannon.png similarity index 100% rename from Resources/Textures/Objects/Weapons/Guns/Launchers/pie_cannon.rsi/piecannon.png rename to Resources/Textures/Objects/Weapons/Guns/Cannons/pie_cannon.rsi/piecannon.png diff --git a/Resources/Textures/Objects/Weapons/Guns/Cannons/pneumatic_cannon.rsi/inhand-left.png b/Resources/Textures/Objects/Weapons/Guns/Cannons/pneumatic_cannon.rsi/inhand-left.png new file mode 100644 index 0000000000000000000000000000000000000000..b0d0b96ecc6f8d50b18acbeb8e7610d14a9a5c20 GIT binary patch literal 388 zcmeAS@N?(olHy`uVBq!ia0vp^4j|0J3?w7mbKU|el>na**Z&OvB_$W zDlad;c=6&nbLMQ{zP+QPW7@Q7adB~${{9v~^^7G!e!&b5&u*jvIb~HL5hX6E#mPmP z1tppJc?=8{bArPPib}tK2`>2f^@*0ZuGYCT=Yuzd8eBAf@JQ#pkLF2+qMqIz7REuw zmyNv?<~*8oWKxJiu%@Q^#vLk_hWh&U`gW$<7c2?t>^4^2yh0*{K|DPwFey)~BlpD8 zH4k1rXWGWZaCe2ge!-7hSAZ_)@pN$v@#suWkYHWh5Tn4NCMqexv*EPyuBMd&jN6jd ze0n3qm@sQ=V=-eG>lq>G119Cndg=yc%`0V@EMz&&nsy5=w>iRL!^Y6dkYvNw(-dgH zWzZzopr0A~h`CjbBd literal 0 HcmV?d00001 diff --git a/Resources/Textures/Objects/Weapons/Guns/Cannons/pneumatic_cannon.rsi/inhand-right.png b/Resources/Textures/Objects/Weapons/Guns/Cannons/pneumatic_cannon.rsi/inhand-right.png new file mode 100644 index 0000000000000000000000000000000000000000..d3e9b810db46bcd1e7e9c7f590f12a23fcf517ca GIT binary patch literal 391 zcmeAS@N?(olHy`uVBq!ia0vp^4j|0J3?w7mbKU|el>na**Z&OvV`F0_B_(rma^&UZ zw{G1!XU?3(ix+R-zJ1!XX&oIMadC0X2c`A^)iag^`2{mLJiCzw=P@u;%n1%FC@TH_CAi?@*C$%ux?1PXoDbd*YH-o`!6TjXKAI;Pih6o?SQrNx zUpDqqnDc1Tkx3y6!J3-t8+WK!8tUuY>)V-bU$7*ov)fp8^9qR+2J!T$z@$8_j@%PV z*F1RjoM{^q!`&6~`UO93T>-kF-_yl0#G^AgL4tL0gUNvv4Y79`8a)}#Byb+mIzQ*! zf>#WQRVxxh4Z9bzp1QTL=kTXIj-z*5PM1naE3iE%P+?{cHm_q|;8yU!gdxL)Id5m6 z0hb#i^BNrnW<{RJG`5OJ2Bvjf5k47?Nt;a)0x$F)d3J0WLzGwp<29y6&P0|zCPpA3 j#2P5}Kr^sSmx19~uHeJkhz;35r!jcC`njxgN@xNAaR-t7 literal 0 HcmV?d00001 diff --git a/Resources/Textures/Objects/Weapons/Guns/Cannons/pneumatic_cannon.rsi/meta.json b/Resources/Textures/Objects/Weapons/Guns/Cannons/pneumatic_cannon.rsi/meta.json new file mode 100644 index 0000000000..3aa08b8390 --- /dev/null +++ b/Resources/Textures/Objects/Weapons/Guns/Cannons/pneumatic_cannon.rsi/meta.json @@ -0,0 +1 @@ +{"version":1,"name":1,"size":{"x":32,"y":32},"states":[{"name":"pneumaticCannon","directions":1},{"name":"oxygen","directions":1},{"name":"inhand-left","directions":4},{"name":"inhand-right","directions":4}],"license":"CC-BY-SA-3.0","copyright":"tgstation at b2e5316993806b1524ab81237b1735b0591df2a2"} diff --git a/Resources/Textures/Objects/Weapons/Guns/Cannons/pneumatic_cannon.rsi/oxygen.png b/Resources/Textures/Objects/Weapons/Guns/Cannons/pneumatic_cannon.rsi/oxygen.png new file mode 100644 index 0000000000000000000000000000000000000000..8dc3dbba2f05d18cc81aced4690071f3781adff1 GIT binary patch literal 799 zcmV+)1K|9LP)RCt{2mtAO5VHn5%!n{a`RTp_tREuI@H3CZzTFbUo!;6fu zS(ACu*TrTOeXyG@ycu1!HOftB2yHA%GwniFQIHL?ELj*6)(4S&un4Nd!YG5&dER*r z&Pnt5v4ZvoIlkwpzu)^j?|BcF;V)-iU|wL(1gdqLcsh=RpD5-`ARWqm{Q`Y;4GsBp ziINEre4nKSPKQGh35^7?a!n;0ixMRfAi(z8ItC{oH<^TCixxhw2X9`EPm?HF1qdd> zf3CL*V>@d>>{)c0MBV~?qMVggq2d&o*TjAk?4xx6OU5El)!Gq@^gy$rN|K<@vP-LU$j`d6-gpz0UiscW{U4KN=_nfaQyvyFHo~{7!&56tnh5fS%AO}D0vde zl&CWvBmBcb&~^2ImCanCDxv_vzolkKp`Me3qyIcU2vumeox(ET zWptgf=2Ujs0u&wVbA*+c!g?-9`HVnYU`T*>%7)7lsHj-YpqaZHl^udWKJc6c?gaYT z=3^G_`>Srhbe^g~Joyc6etdcgHBc~Iwm_Oh(enTN dG5Kc&eggsITICMPCkg-n002ovPDHLkV1i@XYzP1V literal 0 HcmV?d00001 diff --git a/Resources/Textures/Objects/Weapons/Guns/Cannons/pneumatic_cannon.rsi/pneumaticCannon.png b/Resources/Textures/Objects/Weapons/Guns/Cannons/pneumatic_cannon.rsi/pneumaticCannon.png new file mode 100644 index 0000000000000000000000000000000000000000..c696911b74ae3129d1bc2d3453e8f3dffc3141e7 GIT binary patch literal 888 zcmV-;1Bd*HP)=*;4l-J~cHZ z=E-ETCxiju^74{l#1Ap*D2d z*_F5>fC@1`KQHELVvdfEQngy8NF*Wv;03JJYBg#!8m>>ymz59@+yiFet&+t}D3HQ8XRtE&PE_MO6I3jp!7GQYXG(Pn05nEH*ZaGg%a zbKzuG36vYV@5^G~)W5UJe^6EC*T?42)hxqTE9r2vn->1RBK~Kdk08;!fk9&|m zL%hPX8ir>;tgWqyZ1+Ok0)T|Wh@D5q$0^5m8RlXa7ZhuQl2x;Sbl zCnvPLy!?NidjjIc$45`a4FE$!L%KMciHC;=nwXfNA|GLXi0c3l-`?KVi;Qe0PCU@DwT9`)OLQ~|M_W$>IlQlH#<8^(P&i6x3;$E;NZY>h=AI9@m>%|kz@y?CMDp|?Rss( z11iMs?ylhjDbCw<@+|}s0w?11^wjbZ64R^8#}+_Bz!PvaW6ZElKG{F_PbS#q)Z(uI O0000