diff --git a/Content.Client/Content.Client.csproj b/Content.Client/Content.Client.csproj index eb6b73f8fc..9091c2f9c1 100644 --- a/Content.Client/Content.Client.csproj +++ b/Content.Client/Content.Client.csproj @@ -77,6 +77,7 @@ + @@ -84,7 +85,10 @@ + + + diff --git a/Content.Client/EntryPoint.cs b/Content.Client/EntryPoint.cs index e293421bc4..6b9bcbdcd4 100644 --- a/Content.Client/EntryPoint.cs +++ b/Content.Client/EntryPoint.cs @@ -25,8 +25,10 @@ using SS14.Shared.Interfaces.GameObjects; using SS14.Shared.IoC; using SS14.Shared.Prototypes; using System; +using Content.Client.GameObjects.Components.Mobs; using Content.Client.UserInterface; using Content.Shared.GameObjects.Components.Markers; +using Content.Shared.GameObjects.Components.Mobs; using SS14.Client.Interfaces.UserInterface; using SS14.Shared.Log; @@ -100,6 +102,9 @@ namespace Content.Client factory.Register(); + factory.Register(); + factory.RegisterReference(); + IoCManager.Register(); IoCManager.Register(); IoCManager.Register(); diff --git a/Content.Client/GameObjects/Components/Mobs/CameraRecoilComponent.cs b/Content.Client/GameObjects/Components/Mobs/CameraRecoilComponent.cs new file mode 100644 index 0000000000..4c25b5b94a --- /dev/null +++ b/Content.Client/GameObjects/Components/Mobs/CameraRecoilComponent.cs @@ -0,0 +1,87 @@ +using System; +using Content.Shared.GameObjects.Components.Mobs; +using SS14.Client.GameObjects; +using SS14.Shared.GameObjects; +using SS14.Shared.Interfaces.GameObjects; +using SS14.Shared.Interfaces.Network; +using SS14.Shared.Maths; + +namespace Content.Client.GameObjects.Components.Mobs +{ + public sealed class CameraRecoilComponent : SharedCameraRecoilComponent + { + // Maximum rate of magnitude restore towards 0 kick. + private const float RestoreRateMax = 1.5f; + + // Minimum rate of magnitude restore towards 0 kick. + private const float RestoreRateMin = 0.5f; + + // Time in seconds since the last kick that lerps RestoreRateMin and RestoreRateMax + private const float RestoreRateRamp = 0.05f; + + // The maximum magnitude of the kick applied to the camera at any point. + private const float KickMagnitudeMax = 0.25f; + + private Vector2 _currentKick; + private float _lastKickTime; + + private EyeComponent _eye; + + public override void Initialize() + { + base.Initialize(); + + _eye = Owner.GetComponent(); + } + + public override void Kick(Vector2 recoil) + { + // Use really bad math to "dampen" kicks when we're already kicked. + var existing = _currentKick.Length; + var dampen = existing/KickMagnitudeMax; + _currentKick += recoil * (1-dampen); + if (_currentKick.Length > KickMagnitudeMax) + { + _currentKick = _currentKick.Normalized * KickMagnitudeMax; + } + + _lastKickTime = 0; + _updateEye(); + } + + public override void HandleMessage(ComponentMessage message, INetChannel netChannel = null, IComponent component = null) + { + base.HandleMessage(message, netChannel, component); + + switch (message) + { + case RecoilKickMessage msg: + Kick(msg.Recoil); + break; + } + } + + public void FrameUpdate(float frameTime) + { + var magnitude = _currentKick.Length; + if (magnitude <= 0.005f) + { + _currentKick = Vector2.Zero; + _updateEye(); + return; + } + + // Continually restore camera to 0. + var normalized = _currentKick.Normalized; + var restoreRate = FloatMath.Lerp(RestoreRateMin, RestoreRateMax, Math.Min(1, _lastKickTime/RestoreRateRamp)); + var restore = normalized * restoreRate * frameTime; + _currentKick -= restore; + _updateEye(); + } + + private void _updateEye() + { + _eye.Offset = _currentKick; + } + } +} diff --git a/Content.Client/GameObjects/Components/Weapons/Ranged/BallisticMagazineVisualizer2D.cs b/Content.Client/GameObjects/Components/Weapons/Ranged/BallisticMagazineVisualizer2D.cs new file mode 100644 index 0000000000..f4a4df10b7 --- /dev/null +++ b/Content.Client/GameObjects/Components/Weapons/Ranged/BallisticMagazineVisualizer2D.cs @@ -0,0 +1,41 @@ +using Content.Shared.GameObjects.Components.Weapons.Ranged; +using Content.Shared.Utility; +using SS14.Client.GameObjects; +using SS14.Client.Interfaces.GameObjects.Components; +using SS14.Shared.Utility; +using YamlDotNet.RepresentationModel; + +namespace Content.Client.GameObjects.Components.Weapons.Ranged +{ + public sealed class BallisticMagazineVisualizer2D : AppearanceVisualizer + { + private string _baseState; + private int _steps; + + public override void LoadData(YamlMappingNode node) + { + base.LoadData(node); + + _baseState = node.GetNode("base_state").AsString(); + _steps = node.GetNode("steps").AsInt(); + } + + public override void OnChangeData(AppearanceComponent component) + { + var sprite = component.Owner.GetComponent(); + + if (!component.TryGetData(BallisticMagazineVisuals.AmmoCapacity, out int capacity)) + { + return; + } + if (!component.TryGetData(BallisticMagazineVisuals.AmmoLeft, out int current)) + { + return; + } + + var step = ContentHelpers.RoundToLevels(current, capacity, _steps); + + sprite.LayerSetState(0, $"{_baseState}-{step}"); + } + } +} diff --git a/Content.Client/GameObjects/Components/Weapons/Ranged/BallisticMagazineWeaponVisualizer2D.cs b/Content.Client/GameObjects/Components/Weapons/Ranged/BallisticMagazineWeaponVisualizer2D.cs new file mode 100644 index 0000000000..6f50eb0eea --- /dev/null +++ b/Content.Client/GameObjects/Components/Weapons/Ranged/BallisticMagazineWeaponVisualizer2D.cs @@ -0,0 +1,50 @@ +using Content.Shared.GameObjects.Components.Weapons.Ranged; +using Content.Shared.Utility; +using SS14.Client.GameObjects; +using SS14.Client.Interfaces.GameObjects.Components; +using SS14.Shared.Utility; +using YamlDotNet.RepresentationModel; + +namespace Content.Client.GameObjects.Components.Weapons.Ranged +{ + public sealed class BallisticMagazineWeaponVisualizer2D : AppearanceVisualizer + { + private string _baseState; + private int _steps; + + public override void LoadData(YamlMappingNode node) + { + base.LoadData(node); + + _baseState = node.GetNode("base_state").AsString(); + _steps = node.GetNode("steps").AsInt(); + } + + public override void OnChangeData(AppearanceComponent component) + { + var sprite = component.Owner.GetComponent(); + + component.TryGetData(BallisticMagazineWeaponVisuals.MagazineLoaded, out bool loaded); + + if (loaded) + { + if (!component.TryGetData(BallisticMagazineWeaponVisuals.AmmoCapacity, out int capacity)) + { + return; + } + if (!component.TryGetData(BallisticMagazineWeaponVisuals.AmmoLeft, out int current)) + { + return; + } + + var step = ContentHelpers.RoundToLevels(current, capacity, _steps); + + sprite.LayerSetState(0, $"{_baseState}-{step}"); + } + else + { + sprite.LayerSetState(0, _baseState); + } + } + } +} diff --git a/Content.Client/GameObjects/EntitySystems/CameraRecoilSystem.cs b/Content.Client/GameObjects/EntitySystems/CameraRecoilSystem.cs new file mode 100644 index 0000000000..e1c7d9c8ee --- /dev/null +++ b/Content.Client/GameObjects/EntitySystems/CameraRecoilSystem.cs @@ -0,0 +1,27 @@ +using Content.Client.GameObjects.Components.Mobs; +using SS14.Shared.GameObjects; +using SS14.Shared.GameObjects.Systems; + +namespace Content.Client.GameObjects.EntitySystems +{ + public sealed class CameraRecoilSystem : EntitySystem + { + public override void Initialize() + { + base.Initialize(); + + EntityQuery = new TypeEntityQuery(typeof(CameraRecoilComponent)); + } + + public override void FrameUpdate(float frameTime) + { + base.FrameUpdate(frameTime); + + foreach (var entity in RelevantEntities) + { + var recoil = entity.GetComponent(); + recoil.FrameUpdate(frameTime); + } + } + } +} diff --git a/Content.Server/Content.Server.csproj b/Content.Server/Content.Server.csproj index 79820211c8..2c678c7603 100644 --- a/Content.Server/Content.Server.csproj +++ b/Content.Server/Content.Server.csproj @@ -81,6 +81,7 @@ + @@ -100,6 +101,10 @@ + + + + diff --git a/Content.Server/EntryPoint.cs b/Content.Server/EntryPoint.cs index 8016d35a2f..9524afa2ad 100644 --- a/Content.Server/EntryPoint.cs +++ b/Content.Server/EntryPoint.cs @@ -41,6 +41,7 @@ using Content.Server.Interfaces; using Content.Server.Interfaces.GameTicking; using Content.Shared.GameObjects.Components.Inventory; using Content.Shared.GameObjects.Components.Markers; +using Content.Shared.GameObjects.Components.Mobs; using Content.Shared.Interfaces; using SS14.Server.Interfaces.ServerStatus; using SS14.Shared.Timing; @@ -98,7 +99,7 @@ namespace Content.Server factory.Register(); factory.Register(); - factory.Register(); + factory.Register(); factory.Register(); factory.Register(); factory.Register(); @@ -127,6 +128,12 @@ namespace Content.Server factory.Register(); factory.RegisterReference(); + factory.Register(); + factory.Register(); + + factory.Register(); + factory.RegisterReference(); + IoCManager.Register(); IoCManager.Register(); IoCManager.Register(); diff --git a/Content.Server/GameObjects/Components/Mobs/CameraRecoilComponent.cs b/Content.Server/GameObjects/Components/Mobs/CameraRecoilComponent.cs new file mode 100644 index 0000000000..356f71cee5 --- /dev/null +++ b/Content.Server/GameObjects/Components/Mobs/CameraRecoilComponent.cs @@ -0,0 +1,14 @@ +using Content.Shared.GameObjects.Components.Mobs; +using SS14.Shared.Maths; + +namespace Content.Server.GameObjects.Components.Mobs +{ + public sealed class CameraRecoilComponent : SharedCameraRecoilComponent + { + public override void Kick(Vector2 recoil) + { + var msg = new RecoilKickMessage(recoil); + SendNetworkMessage(msg); + } + } +} diff --git a/Content.Server/GameObjects/Components/Weapon/Ranged/Projectile/BallisticBulletComponent.cs b/Content.Server/GameObjects/Components/Weapon/Ranged/Projectile/BallisticBulletComponent.cs new file mode 100644 index 0000000000..fd686b84d0 --- /dev/null +++ b/Content.Server/GameObjects/Components/Weapon/Ranged/Projectile/BallisticBulletComponent.cs @@ -0,0 +1,31 @@ +using SS14.Shared.GameObjects; +using SS14.Shared.Serialization; + +namespace Content.Server.GameObjects.Components.Weapon.Ranged.Projectile +{ + public class BallisticBulletComponent : Component + { + public override string Name => "BallisticBullet"; + + private BallisticCaliber _caliber; + private string _projectileType; + private bool _spent; + + public string ProjectileType => _projectileType; + public BallisticCaliber Caliber => _caliber; + public bool Spent + { + get => _spent; + set => _spent = value; + } + + public override void ExposeData(ObjectSerializer serializer) + { + base.ExposeData(serializer); + + serializer.DataField(ref _caliber, "caliber", BallisticCaliber.Unspecified); + serializer.DataField(ref _projectileType, "projectile", null); + serializer.DataField(ref _spent, "spent", false); + } + } +} diff --git a/Content.Server/GameObjects/Components/Weapon/Ranged/Projectile/BallisticMagazineComponent.cs b/Content.Server/GameObjects/Components/Weapon/Ranged/Projectile/BallisticMagazineComponent.cs new file mode 100644 index 0000000000..c1c168e3d0 --- /dev/null +++ b/Content.Server/GameObjects/Components/Weapon/Ranged/Projectile/BallisticMagazineComponent.cs @@ -0,0 +1,105 @@ +using System; +using System.Collections.Generic; +using Content.Shared.GameObjects.Components.Weapons.Ranged; +using SS14.Server.GameObjects; +using SS14.Server.GameObjects.Components.Container; +using SS14.Shared.GameObjects; +using SS14.Shared.Interfaces.GameObjects; +using SS14.Shared.Serialization; + +namespace Content.Server.GameObjects.Components.Weapon.Ranged.Projectile +{ + public class BallisticMagazineComponent : Component + { + public override string Name => "BallisticMagazine"; + + // Stack of loaded bullets. + private readonly Stack _loadedBullets = new Stack(); + private string _fillType; + + private Container _bulletContainer; + private AppearanceComponent _appearance; + + private BallisticMagazineType _magazineType; + private BallisticCaliber _caliber; + private int _capacity; + + public BallisticMagazineType MagazineType => _magazineType; + public BallisticCaliber Caliber => _caliber; + public int Capacity => _capacity; + + public int CountLoaded => _loadedBullets.Count; + + public override void ExposeData(ObjectSerializer serializer) + { + base.ExposeData(serializer); + + serializer.DataField(ref _magazineType, "magazine", BallisticMagazineType.Unspecified); + serializer.DataField(ref _caliber, "caliber", BallisticCaliber.Unspecified); + serializer.DataField(ref _fillType, "fill", null); + serializer.DataField(ref _capacity, "capacity", 20); + } + + public override void Initialize() + { + base.Initialize(); + + _appearance = Owner.GetComponent(); + + _bulletContainer = + ContainerManagerComponent.Ensure("magazine_bullet_container", Owner, out var existed); + + if (!existed && _fillType != null) + { + // Load up bullets from fill. + for (var i = 0; i < Capacity; i++) + { + var bullet = Owner.EntityManager.SpawnEntity(_fillType); + AddBullet(bullet); + } + } + + _appearance.SetData(BallisticMagazineVisuals.AmmoCapacity, Capacity); + } + + public void AddBullet(IEntity bullet) + { + if (!bullet.TryGetComponent(out BallisticBulletComponent component)) + { + throw new ArgumentException("entity isn't a bullet.", nameof(bullet)); + } + + if (component.Caliber != Caliber) + { + throw new ArgumentException("entity is of the wrong caliber.", nameof(bullet)); + } + + _bulletContainer.Insert(bullet); + _loadedBullets.Push(bullet); + _updateAppearance(); + } + + public IEntity TakeBullet() + { + if (_loadedBullets.Count == 0) + { + return null; + } + + var bullet = _loadedBullets.Pop(); + _updateAppearance(); + return bullet; + } + + private void _updateAppearance() + { + _appearance.SetData(BallisticMagazineVisuals.AmmoLeft, CountLoaded); + } + } + + public enum BallisticMagazineType + { + Unspecified = 0, + A12mm, + } +} diff --git a/Content.Server/GameObjects/Components/Weapon/Ranged/Projectile/BallisticMagazineWeaponComponent.cs b/Content.Server/GameObjects/Components/Weapon/Ranged/Projectile/BallisticMagazineWeaponComponent.cs new file mode 100644 index 0000000000..2914d2e65e --- /dev/null +++ b/Content.Server/GameObjects/Components/Weapon/Ranged/Projectile/BallisticMagazineWeaponComponent.cs @@ -0,0 +1,251 @@ +using System; +using Content.Server.GameObjects.EntitySystems; +using Content.Shared.GameObjects; +using Content.Shared.GameObjects.Components.Weapons.Ranged; +using Content.Shared.Interfaces; +using SS14.Server.GameObjects; +using SS14.Server.GameObjects.Components.Container; +using SS14.Server.GameObjects.EntitySystems; +using SS14.Shared.Audio; +using SS14.Shared.Interfaces.GameObjects; +using SS14.Shared.IoC; +using SS14.Shared.Maths; +using SS14.Shared.Serialization; +using SS14.Shared.Utility; + +namespace Content.Server.GameObjects.Components.Weapon.Ranged.Projectile +{ + public class BallisticMagazineWeaponComponent : BallisticWeaponComponent, IUse, IAttackby + { + public override string Name => "BallisticMagazineWeapon"; + + private string _defaultMagazine; + + private ContainerSlot _magazineSlot; + private BallisticMagazineType _magazineType; + + public BallisticMagazineType MagazineType => _magazineType; + private IEntity Magazine => _magazineSlot.ContainedEntity; + + private Random _bulletDropRandom; + private string _magInSound; + private string _magOutSound; + private string _autoEjectSound; + private bool _autoEjectMagazine; + private AppearanceComponent _appearance; + + private static readonly Direction[] _randomBulletDirs = { + Direction.North, + Direction.East, + Direction.South, + Direction.West + }; + + protected override int ChamberCount => 1; + + public override void ExposeData(ObjectSerializer serializer) + { + base.ExposeData(serializer); + + serializer.DataField(ref _magazineType, "magazine", BallisticMagazineType.Unspecified); + serializer.DataField(ref _defaultMagazine, "default_magazine", null); + serializer.DataField(ref _autoEjectMagazine, "auto_eject_magazine", false); + serializer.DataField(ref _autoEjectSound, "sound_auto_eject", null); + serializer.DataField(ref _magInSound, "sound_magazine_in", null); + serializer.DataField(ref _magOutSound, "sound_magazine_out", null); + } + + public override void Initialize() + { + base.Initialize(); + + _appearance = Owner.GetComponent(); + + _magazineSlot = + ContainerManagerComponent.Ensure("ballistic_gun_magazine", Owner, + out var alreadyExisted); + + _bulletDropRandom = new Random(Owner.Uid.GetHashCode() ^ DateTime.Now.GetHashCode()); + + if (!alreadyExisted && _defaultMagazine != null) + { + var magazine = Owner.EntityManager.SpawnEntity(_defaultMagazine); + InsertMagazine(magazine, false); + } + _updateAppearance(); + } + + public bool InsertMagazine(IEntity magazine, bool playSound=true) + { + if (!magazine.TryGetComponent(out BallisticMagazineComponent component)) + { + throw new ArgumentException("Not a magazine", nameof(magazine)); + } + + if (component.MagazineType != MagazineType) + { + throw new ArgumentException("Wrong magazine type", nameof(magazine)); + } + + if (component.Caliber != Caliber) + { + throw new ArgumentException("Wrong caliber", nameof(magazine)); + } + + if (!_magazineSlot.Insert(magazine)) + { + return false; + } + + if (_magInSound != null) + { + var audioSystem = IoCManager.Resolve().GetEntitySystem(); + audioSystem.Play(_magInSound, Owner); + } + + if (GetChambered(0) == null) + { + // No bullet in chamber, load one from magazine. + var bullet = component.TakeBullet(); + if (bullet != null) + { + LoadIntoChamber(0, bullet); + } + } + + _updateAppearance(); + return true; + } + + public bool EjectMagazine(bool playSound=true) + { + var entity = Magazine; + if (entity == null) + { + return false; + } + + if (_magazineSlot.Remove(entity)) + { + entity.Transform.GridPosition = Owner.Transform.GridPosition; + if (_magOutSound != null) + { + var audioSystem = IoCManager.Resolve().GetEntitySystem(); + audioSystem.Play(_magOutSound, Owner); + } + _updateAppearance(); + return true; + } + + _updateAppearance(); + return false; + } + + protected override void CycleChamberedBullet(int chamber) + { + DebugTools.Assert(chamber == 0); + + // Eject chambered bullet. + var entity = RemoveFromChamber(chamber); + entity.Transform.GridPosition = Owner.Transform.GridPosition; + entity.Transform.LocalRotation = _bulletDropRandom.Pick(_randomBulletDirs).ToAngle(); + var audioSystem = IoCManager.Resolve().GetEntitySystem(); + var effect = $"/Audio/items/weapons/casingfall{_bulletDropRandom.Next(1, 4)}.ogg"; + audioSystem.Play(effect, entity, AudioParams.Default.WithVolume(-3)); + + if (Magazine != null) + { + var magComponent = Magazine.GetComponent(); + var bullet = magComponent.TakeBullet(); + if (bullet != null) + { + LoadIntoChamber(0, bullet); + } + + if (magComponent.CountLoaded == 0 && _autoEjectMagazine) + { + EjectMagazine(); + if (_autoEjectSound != null) + { + audioSystem.Play(_autoEjectSound, Owner, AudioParams.Default.WithVolume(-5)); + } + } + } + + _updateAppearance(); + } + + public bool UseEntity(IEntity user) + { + var ret = EjectMagazine(); + if (ret) + { + Owner.PopupMessage(user, "Magazine ejected"); + } + else + { + Owner.PopupMessage(user, "No magazine"); + } + + return true; + } + + public bool Attackby(IEntity user, IEntity attackwith) + { + if (!attackwith.TryGetComponent(out BallisticMagazineComponent component)) + { + return false; + } + + if (Magazine != null) + { + Owner.PopupMessage(user, "Already got a magazine."); + return false; + } + + if (component.MagazineType != MagazineType || component.Caliber != Caliber) + { + Owner.PopupMessage(user, "Magazine doesn't fit."); + return false; + } + + return InsertMagazine(attackwith); + } + + private void _updateAppearance() + { + if (Magazine != null) + { + var comp = Magazine.GetComponent(); + _appearance.SetData(BallisticMagazineWeaponVisuals.AmmoLeft, comp.CountLoaded); + _appearance.SetData(BallisticMagazineWeaponVisuals.AmmoCapacity, comp.Capacity); + _appearance.SetData(BallisticMagazineWeaponVisuals.MagazineLoaded, true); + } + else + { + _appearance.SetData(BallisticMagazineWeaponVisuals.AmmoLeft, 0); + _appearance.SetData(BallisticMagazineWeaponVisuals.AmmoLeft, 0); + _appearance.SetData(BallisticMagazineWeaponVisuals.MagazineLoaded, false); + } + } + + [Verb] + public sealed class EjectMagazineVerb : Verb + { + protected override string GetText(IEntity user, BallisticMagazineWeaponComponent component) + { + return component.Magazine == null ? "Eject magazine (magazine missing)" : "Eject magazine"; + } + + protected override bool IsDisabled(IEntity user, BallisticMagazineWeaponComponent component) + { + return component.Magazine == null; + } + + protected override void Activate(IEntity user, BallisticMagazineWeaponComponent component) + { + component.EjectMagazine(); + } + } + } +} diff --git a/Content.Server/GameObjects/Components/Weapon/Ranged/Projectile/BallisticWeaponComponent.cs b/Content.Server/GameObjects/Components/Weapon/Ranged/Projectile/BallisticWeaponComponent.cs new file mode 100644 index 0000000000..cb23dd4e0e --- /dev/null +++ b/Content.Server/GameObjects/Components/Weapon/Ranged/Projectile/BallisticWeaponComponent.cs @@ -0,0 +1,126 @@ +using System; +using Content.Server.GameObjects.Components.Interactable; +using Content.Shared.GameObjects; +using SS14.Server.Chat; +using SS14.Server.GameObjects.Components.Container; +using SS14.Server.GameObjects.EntitySystems; +using SS14.Shared.Interfaces.GameObjects; +using SS14.Shared.IoC; +using SS14.Shared.Serialization; + +namespace Content.Server.GameObjects.Components.Weapon.Ranged.Projectile +{ + public abstract class BallisticWeaponComponent : ProjectileWeaponComponent + { + private BallisticCaliber _caliber; + private Chamber[] _chambers; + + public BallisticCaliber Caliber => _caliber; + protected abstract int ChamberCount { get; } + + private string _soundGunEmpty; + + public override void ExposeData(ObjectSerializer serializer) + { + base.ExposeData(serializer); + + serializer.DataField(ref _caliber, "caliber", BallisticCaliber.Unspecified); + serializer.DataField(ref _soundGunEmpty, "sound_empty", null); + } + + public override void Initialize() + { + base.Initialize(); + + _chambers = new Chamber[ChamberCount]; + for (var i = 0; i < _chambers.Length; i++) + { + var container = ContainerManagerComponent.Ensure($"ballistics_chamber_{i}", Owner); + _chambers[i] = new Chamber(container); + } + } + + public IEntity GetChambered(int chamber) => _chambers[chamber].Slot.ContainedEntity; + + public bool LoadIntoChamber(int chamber, IEntity bullet) + { + if (!bullet.TryGetComponent(out BallisticBulletComponent component)) + { + throw new ArgumentException("entity isn't a bullet.", nameof(bullet)); + } + + if (component.Caliber != Caliber) + { + throw new ArgumentException("entity is of the wrong caliber.", nameof(bullet)); + } + + if (GetChambered(chamber) != null) + { + return false; + } + + _chambers[chamber].Slot.Insert(bullet); + return true; + } + + protected sealed override IEntity GetFiredProjectile() + { + void PlayEmpty() + { + if (_soundGunEmpty != null) + { + var audioSystem = IoCManager.Resolve().GetEntitySystem(); + audioSystem.Play(_soundGunEmpty, Owner); + } + } + var chambered = GetChambered(0); + if (chambered != null) + { + var bullet = chambered.GetComponent(); + if (bullet.Spent) + { + PlayEmpty(); + return null; + } + + var projectile = Owner.EntityManager.SpawnEntity(bullet.ProjectileType); + bullet.Spent = true; + + CycleChamberedBullet(0); + + // Load a new bullet into the chamber from magazine. + return projectile; + } + + PlayEmpty(); + return null; + } + + protected virtual void CycleChamberedBullet(int chamber) + { + + } + + public IEntity RemoveFromChamber(int chamber) + { + var c = _chambers[chamber]; + var loaded = c.Slot.ContainedEntity; + if (loaded != null) + { + c.Slot.Remove(loaded); + } + return loaded; + } + + private sealed class Chamber + { + public Chamber(ContainerSlot slot) + { + Slot = slot; + } + + public ContainerSlot Slot { get; } + } + + } +} diff --git a/Content.Server/GameObjects/Components/Weapon/Ranged/Projectile/ProjectileWeapon.cs b/Content.Server/GameObjects/Components/Weapon/Ranged/Projectile/ProjectileWeapon.cs index b08b479da5..9fa5d127f4 100644 --- a/Content.Server/GameObjects/Components/Weapon/Ranged/Projectile/ProjectileWeapon.cs +++ b/Content.Server/GameObjects/Components/Weapon/Ranged/Projectile/ProjectileWeapon.cs @@ -1,4 +1,5 @@ using System; +using Content.Server.GameObjects.Components.Mobs; using Content.Server.GameObjects.Components.Projectiles; using SS14.Server.GameObjects; using SS14.Server.GameObjects.EntitySystems; @@ -15,12 +16,8 @@ using SS14.Shared.ViewVariables; namespace Content.Server.GameObjects.Components.Weapon.Ranged.Projectile { - public class ProjectileWeaponComponent : Component + public abstract class ProjectileWeaponComponent : Component { - public override string Name => "ProjectileWeapon"; - - private string _ProjectilePrototype = "ProjectileBullet"; - private float _velocity = 20f; private float _spreadStdDev = 3; private bool _spread = true; @@ -61,16 +58,27 @@ namespace Content.Server.GameObjects.Components.Weapon.Ranged.Projectile private void Fire(IEntity user, GridCoordinates clickLocation) { + var projectile = GetFiredProjectile(); + if (projectile == null) + { + return; + } + var userPosition = user.Transform.GridPosition; //Remember world positions are ephemeral and can only be used instantaneously var angle = new Angle(clickLocation.Position - userPosition.Position); + if (user.TryGetComponent(out CameraRecoilComponent recoil)) + { + var recoilVec = angle.ToVec() * -0.15f; + recoil.Kick(recoilVec); + } + if (Spread) { angle += Angle.FromDegrees(_spreadRandom.NextGaussian(0, SpreadStdDev)); } - //Spawn the projectilePrototype - var projectile = IoCManager.Resolve().ForceSpawnEntityAt(_ProjectilePrototype, userPosition); + projectile.Transform.GridPosition = userPosition; //Give it the velocity we fire from this weapon, and make sure it doesn't shoot our character projectile.GetComponent().IgnoreEntity(user); @@ -84,5 +92,16 @@ namespace Content.Server.GameObjects.Components.Weapon.Ranged.Projectile // Sound! IoCManager.Resolve().GetEntitySystem().Play("/Audio/gunshot_c20.ogg", user); } + + /// + /// Try to get a projectile for firing. If null, nothing will be fired. + /// + protected abstract IEntity GetFiredProjectile(); + } + + public enum BallisticCaliber + { + Unspecified = 0, + A12mm, } } diff --git a/Content.Shared/Content.Shared.csproj b/Content.Shared/Content.Shared.csproj index 2ef69f42e4..dc4fbedaf8 100644 --- a/Content.Shared/Content.Shared.csproj +++ b/Content.Shared/Content.Shared.csproj @@ -69,9 +69,12 @@ + + + diff --git a/Content.Shared/GameObjects/Components/Mobs/SharedCameraRecoilComponent.cs b/Content.Shared/GameObjects/Components/Mobs/SharedCameraRecoilComponent.cs new file mode 100644 index 0000000000..761b1ac692 --- /dev/null +++ b/Content.Shared/GameObjects/Components/Mobs/SharedCameraRecoilComponent.cs @@ -0,0 +1,28 @@ +using System; +using SS14.Shared.GameObjects; +using SS14.Shared.Maths; +using SS14.Shared.Serialization; + +namespace Content.Shared.GameObjects.Components.Mobs +{ + public abstract class SharedCameraRecoilComponent : Component + { + public sealed override string Name => "CameraRecoil"; + + public override uint? NetID => ContentNetIDs.CAMERA_RECOIL; + + public abstract void Kick(Vector2 recoil); + + [Serializable, NetSerializable] + protected class RecoilKickMessage : ComponentMessage + { + public readonly Vector2 Recoil; + + public RecoilKickMessage(Vector2 recoil) + { + Directed = true; + Recoil = recoil; + } + } + } +} diff --git a/Content.Shared/GameObjects/Components/Weapons/Ranged/SharedBallisticMagazineComponent.cs b/Content.Shared/GameObjects/Components/Weapons/Ranged/SharedBallisticMagazineComponent.cs new file mode 100644 index 0000000000..b51136fcb1 --- /dev/null +++ b/Content.Shared/GameObjects/Components/Weapons/Ranged/SharedBallisticMagazineComponent.cs @@ -0,0 +1,12 @@ +using System; +using SS14.Shared.Serialization; + +namespace Content.Shared.GameObjects.Components.Weapons.Ranged +{ + [Serializable, NetSerializable] + public enum BallisticMagazineVisuals + { + AmmoCapacity, + AmmoLeft, + } +} diff --git a/Content.Shared/GameObjects/Components/Weapons/Ranged/SharedBallisticMagazineWeaponComponent.cs b/Content.Shared/GameObjects/Components/Weapons/Ranged/SharedBallisticMagazineWeaponComponent.cs new file mode 100644 index 0000000000..b4500a7f59 --- /dev/null +++ b/Content.Shared/GameObjects/Components/Weapons/Ranged/SharedBallisticMagazineWeaponComponent.cs @@ -0,0 +1,13 @@ +using System; +using SS14.Shared.Serialization; + +namespace Content.Shared.GameObjects.Components.Weapons.Ranged +{ + [Serializable, NetSerializable] + public enum BallisticMagazineWeaponVisuals + { + MagazineLoaded, + AmmoCapacity, + AmmoLeft, + } +} diff --git a/Content.Shared/GameObjects/ContentNetIDs.cs b/Content.Shared/GameObjects/ContentNetIDs.cs index b84b23dbd4..14871ceb3f 100644 --- a/Content.Shared/GameObjects/ContentNetIDs.cs +++ b/Content.Shared/GameObjects/ContentNetIDs.cs @@ -11,7 +11,8 @@ public const uint INVENTORY = 1006; public const uint POWER_DEBUG_TOOL = 1007; public const uint CONSTRUCTOR = 1008; - public const uint RANGED_WEAPON = 1010; public const uint SPECIES = 1009; + public const uint RANGED_WEAPON = 1010; + public const uint CAMERA_RECOIL = 1011; } } diff --git a/Resources/Audio/items/weapons/casingfall1.ogg b/Resources/Audio/items/weapons/casingfall1.ogg new file mode 100644 index 0000000000..72a04c0fca Binary files /dev/null and b/Resources/Audio/items/weapons/casingfall1.ogg differ diff --git a/Resources/Audio/items/weapons/casingfall2.ogg b/Resources/Audio/items/weapons/casingfall2.ogg new file mode 100644 index 0000000000..2317ff5d75 Binary files /dev/null and b/Resources/Audio/items/weapons/casingfall2.ogg differ diff --git a/Resources/Audio/items/weapons/casingfall3.ogg b/Resources/Audio/items/weapons/casingfall3.ogg new file mode 100644 index 0000000000..39639055c2 Binary files /dev/null and b/Resources/Audio/items/weapons/casingfall3.ogg differ diff --git a/Resources/Audio/items/weapons/gun_empty.ogg b/Resources/Audio/items/weapons/gun_empty.ogg new file mode 100644 index 0000000000..817d72e0ed Binary files /dev/null and b/Resources/Audio/items/weapons/gun_empty.ogg differ diff --git a/Resources/Audio/items/weapons/smg_empty_alarm.ogg b/Resources/Audio/items/weapons/smg_empty_alarm.ogg new file mode 100644 index 0000000000..f031b4e1d8 Binary files /dev/null and b/Resources/Audio/items/weapons/smg_empty_alarm.ogg differ diff --git a/Resources/Audio/items/weapons/smg_magin.ogg b/Resources/Audio/items/weapons/smg_magin.ogg new file mode 100644 index 0000000000..2bdb3c99e7 Binary files /dev/null and b/Resources/Audio/items/weapons/smg_magin.ogg differ diff --git a/Resources/Audio/items/weapons/smg_magout.ogg b/Resources/Audio/items/weapons/smg_magout.ogg new file mode 100644 index 0000000000..d85ff5a40c Binary files /dev/null and b/Resources/Audio/items/weapons/smg_magout.ogg differ diff --git a/Resources/Audio/items/weapons/sources.txt b/Resources/Audio/items/weapons/sources.txt new file mode 100644 index 0000000000..5e7f1a1173 --- /dev/null +++ b/Resources/Audio/items/weapons/sources.txt @@ -0,0 +1,7 @@ +gun_empty.ogg: https://github.com/discordia-space/CEV-Eris/blob/fbde37a8647a82587d363da999a94cf02c2e128c/sound/weapons/guns/misc/gun_empty.ogg +smg_magin.ogg: https://github.com/discordia-space/CEV-Eris/blob/fbde37a8647a82587d363da999a94cf02c2e128c/sound/weapons/guns/interact/smg_magin.ogg +smg_magout.ogg: https://github.com/discordia-space/CEV-Eris/blob/fbde37a8647a82587d363da999a94cf02c2e128c/sound/weapons/guns/interact/smg_magout.ogg +smg_empty_alarm.ogg: https://github.com/discordia-space/CEV-Eris/blob/fbde37a8647a82587d363da999a94cf02c2e128c/sound/weapons/smg_empty_alarm.ogg +casingfall1.ogg: https://github.com/discordia-space/CEV-Eris/blob/fbde37a8647a82587d363da999a94cf02c2e128c/sound/weapons/guns/misc/casingfall1.ogg +casingfall2.ogg: https://github.com/discordia-space/CEV-Eris/blob/fbde37a8647a82587d363da999a94cf02c2e128c/sound/weapons/guns/misc/casingfall2.ogg +casingfall3.ogg: https://github.com/discordia-space/CEV-Eris/blob/fbde37a8647a82587d363da999a94cf02c2e128c/sound/weapons/guns/misc/casingfall3.ogg diff --git a/Resources/Maps/stationstation.yml b/Resources/Maps/stationstation.yml index 6a3c92cb67..b0924ee8e8 100644 --- a/Resources/Maps/stationstation.yml +++ b/Resources/Maps/stationstation.yml @@ -286,13 +286,13 @@ entities: pos: -2.015625,-3.859375 rot: -1.570796 rad type: Transform -- type: GUNITEM +- type: smg_c20r components: - grid: 0 pos: -2.890625,-4.484375 rot: -1.570796 rad type: Transform -- type: GUNITEM +- type: smg_c20r components: - grid: 0 pos: -1.984375,-4.484375 diff --git a/Resources/Prototypes/Entities/Mobs.yml b/Resources/Prototypes/Entities/Mobs.yml index 76641d5435..060a008ba3 100644 --- a/Resources/Prototypes/Entities/Mobs.yml +++ b/Resources/Prototypes/Entities/Mobs.yml @@ -55,6 +55,8 @@ - type: Eye zoom: 0.5, 0.5 + - type: CameraRecoil + - type: entity id: MobObserver name: Observer diff --git a/Resources/Prototypes/Entities/Projectiles.yml b/Resources/Prototypes/Entities/Projectiles.yml index db49558a8c..e69de29bb2 100644 --- a/Resources/Prototypes/Entities/Projectiles.yml +++ b/Resources/Prototypes/Entities/Projectiles.yml @@ -1,19 +0,0 @@ -- type: entity - id: ProjectileBullet - name: ProjectileBullet - description: If you can see this you're dead! - components: - - type: Sprite - directional: false - texture: Objects/projectilebullet.png - #rotation: -180 - - - type: Icon - texture: Objects/projectilebullet.png - - type: BoundingBox - aabb: -0.2,-0.2,0.2,0.2 - - type: Physics - edgeslide: false - - type: Projectile - - type: Collidable - hard: false diff --git a/Resources/Prototypes/Entities/Weapons.yml b/Resources/Prototypes/Entities/Weapons.yml index 6af9612483..fe9adcf64d 100644 --- a/Resources/Prototypes/Entities/Weapons.yml +++ b/Resources/Prototypes/Entities/Weapons.yml @@ -19,28 +19,6 @@ sprite: Objects/laser_retro.rsi prefix: 100 - -- type: entity - name: C-20r Sub Machine Gun - parent: BaseItem - id: GUNITEM - description: A rooty tooty point and shooty - components: - - type: Sprite - sprite: Objects/c20r.rsi - state: c20r-20 - - type: Icon - sprite: Objects/c20r.rsi - state: c20r-20 - - type: RangedWeapon - automatic: true - firerate: 8 - - type: ProjectileWeapon - - type: Item - Size: 24 - sprite: Objects/c20r.rsi - - - type: entity name: Spear parent: BaseItem diff --git a/Resources/Prototypes/Entities/weapons/ammunition.yml b/Resources/Prototypes/Entities/weapons/ammunition.yml new file mode 100644 index 0000000000..7e2a273fc2 --- /dev/null +++ b/Resources/Prototypes/Entities/weapons/ammunition.yml @@ -0,0 +1,50 @@ +- type: entity + id: magazine_12mm + name: "12mm magazine" + parent: BaseItem + components: + - type: BallisticMagazine + caliber: A12mm + magazine: A12mm + capacity: 20 + - type: Icon + sprite: Objects/items/magazine_12mm.rsi + state: 12mm-0 + - type: Sprite + netsync: false + sprite: Objects/items/magazine_12mm.rsi + state: 12mm-0 + - type: Appearance + visuals: + - type: BallisticMagazineVisualizer2D + base_state: 12mm + steps: 11 + +- type: entity + id: magazine_12mm_filled + name: "12mm magazine" + parent: magazine_12mm + components: + - type: BallisticMagazine + fill: ammo_casing_12mm + - type: Icon + state: 12mm-10 + - type: Sprite + state: 12mm-10 + +- type: entity + id: ammo_casing_12mm + name: "12mm bullet" + parent: BaseItem + components: + - type: BallisticBullet + caliber: A12mm + projectile: ProjectileBullet + - type: Sprite + sprite: Objects/items/ammo_casing.rsi + state: s-casing + drawdepth: FloorObjects + - type: Icon + sprite: Objects/items/ammo_casing.rsi + state: s-casing + diff --git a/Resources/Prototypes/Entities/weapons/guns.yml b/Resources/Prototypes/Entities/weapons/guns.yml new file mode 100644 index 0000000000..fc9f855527 --- /dev/null +++ b/Resources/Prototypes/Entities/weapons/guns.yml @@ -0,0 +1,35 @@ +- type: entity + name: C-20r Sub Machine Gun + parent: BaseItem + id: smg_c20r + description: A rooty tooty point and shooty. + components: + - type: Sprite + netsync: false + sprite: Objects/c20r.rsi + state: c20r-5 + - type: Icon + sprite: Objects/c20r.rsi + state: c20r-5 + - type: RangedWeapon + automatic: true + firerate: 8 + - type: BallisticMagazineWeapon + caliber: A12mm + magazine: A12mm + default_magazine: magazine_12mm_filled + auto_eject_magazine: true + sound_auto_eject: /Audio/items/weapons/smg_empty_alarm.ogg + sound_magazine_in: /Audio/items/weapons/smg_magin.ogg + sound_magazine_out: /Audio/items/weapons/smg_magout.ogg + sound_empty: /Audio/items/weapons/gun_empty.ogg + + - type: Appearance + visuals: + - type: BallisticMagazineWeaponVisualizer2D + base_state: c20r + steps: 6 + + - type: Item + Size: 24 + sprite: Objects/c20r.rsi diff --git a/Resources/Prototypes/Entities/weapons/projectiles.yml b/Resources/Prototypes/Entities/weapons/projectiles.yml new file mode 100644 index 0000000000..db49558a8c --- /dev/null +++ b/Resources/Prototypes/Entities/weapons/projectiles.yml @@ -0,0 +1,19 @@ +- type: entity + id: ProjectileBullet + name: ProjectileBullet + description: If you can see this you're dead! + components: + - type: Sprite + directional: false + texture: Objects/projectilebullet.png + #rotation: -180 + + - type: Icon + texture: Objects/projectilebullet.png + - type: BoundingBox + aabb: -0.2,-0.2,0.2,0.2 + - type: Physics + edgeslide: false + - type: Projectile + - type: Collidable + hard: false diff --git a/Resources/Textures/Objects/c20r.rsi/c20r-1.png b/Resources/Textures/Objects/c20r.rsi/c20r-1.png new file mode 100644 index 0000000000..29a2fde1f2 Binary files /dev/null and b/Resources/Textures/Objects/c20r.rsi/c20r-1.png differ diff --git a/Resources/Textures/Objects/c20r.rsi/c20r-16.png b/Resources/Textures/Objects/c20r.rsi/c20r-16.png deleted file mode 100644 index a3ecf32636..0000000000 Binary files a/Resources/Textures/Objects/c20r.rsi/c20r-16.png and /dev/null differ diff --git a/Resources/Textures/Objects/c20r.rsi/c20r-8.png b/Resources/Textures/Objects/c20r.rsi/c20r-2.png similarity index 100% rename from Resources/Textures/Objects/c20r.rsi/c20r-8.png rename to Resources/Textures/Objects/c20r.rsi/c20r-2.png diff --git a/Resources/Textures/Objects/c20r.rsi/c20r-12.png b/Resources/Textures/Objects/c20r.rsi/c20r-3.png similarity index 100% rename from Resources/Textures/Objects/c20r.rsi/c20r-12.png rename to Resources/Textures/Objects/c20r.rsi/c20r-3.png diff --git a/Resources/Textures/Objects/c20r.rsi/c20r-4.png b/Resources/Textures/Objects/c20r.rsi/c20r-4.png index 29a2fde1f2..a3ecf32636 100644 Binary files a/Resources/Textures/Objects/c20r.rsi/c20r-4.png and b/Resources/Textures/Objects/c20r.rsi/c20r-4.png differ diff --git a/Resources/Textures/Objects/c20r.rsi/c20r-20.png b/Resources/Textures/Objects/c20r.rsi/c20r-5.png similarity index 100% rename from Resources/Textures/Objects/c20r.rsi/c20r-20.png rename to Resources/Textures/Objects/c20r.rsi/c20r-5.png diff --git a/Resources/Textures/Objects/c20r.rsi/meta.json b/Resources/Textures/Objects/c20r.rsi/meta.json index 02ca7aea30..b243c2673d 100644 --- a/Resources/Textures/Objects/c20r.rsi/meta.json +++ b/Resources/Textures/Objects/c20r.rsi/meta.json @@ -1 +1 @@ -{"version": 1, "size": {"x": 32, "y": 32}, "license": "CC-BY-SA-3.0", "copyright": "Taken from https://github.com/vgstation-coders/vgstation13 at commit 125c975f1b3bf9826b37029e9ab5a5f89e975a7e", "states": [{"name": "c20r", "directions": 1, "delays": [[1.0]]}, {"name": "c20r-0", "directions": 1, "delays": [[1.0]]}, {"name": "c20r-12", "directions": 1, "delays": [[1.0]]}, {"name": "c20r-16", "directions": 1, "delays": [[1.0]]}, {"name": "c20r-20", "directions": 1, "delays": [[1.0]]}, {"name": "c20r-4", "directions": 1, "delays": [[1.0]]}, {"name": "c20r-8", "directions": 1, "delays": [[1.0]]}, {"name": "inhand-left", "directions": 4, "delays": [[1.0], [1.0], [1.0], [1.0]]}, {"name": "inhand-right", "directions": 4, "delays": [[1.0], [1.0], [1.0], [1.0]]}]} \ No newline at end of file +{"version": 1, "size": {"x": 32, "y": 32}, "license": "CC-BY-SA-3.0", "copyright": "Taken from https://github.com/vgstation-coders/vgstation13 at commit 125c975f1b3bf9826b37029e9ab5a5f89e975a7e", "states": [{"name": "c20r", "directions": 1, "delays": [[1.0]]}, {"name": "c20r-0", "directions": 1, "delays": [[1.0]]}, {"name": "c20r-3", "directions": 1, "delays": [[1.0]]}, {"name": "c20r-4", "directions": 1, "delays": [[1.0]]}, {"name": "c20r-5", "directions": 1, "delays": [[1.0]]}, {"name": "c20r-1", "directions": 1, "delays": [[1.0]]}, {"name": "c20r-2", "directions": 1, "delays": [[1.0]]}, {"name": "inhand-left", "directions": 4, "delays": [[1.0], [1.0], [1.0], [1.0]]}, {"name": "inhand-right", "directions": 4, "delays": [[1.0], [1.0], [1.0], [1.0]]}]} diff --git a/Resources/Textures/Objects/items/ammo_casing.rsi/meta.json b/Resources/Textures/Objects/items/ammo_casing.rsi/meta.json new file mode 100644 index 0000000000..1402537a73 --- /dev/null +++ b/Resources/Textures/Objects/items/ammo_casing.rsi/meta.json @@ -0,0 +1 @@ +{"version": 1, "size": {"x": 32, "y": 32}, "license": "CC-BY-SA-3.0", "copyright": "Taken from https://github.com/vgstation-coders/vgstation13/blob/0b3ab17dbad632ddf738b63900ef8df1926bba47/icons/obj/ammo.dmi", "states": [{"name": "s-casing", "directions": 4, "delays": [[1.0], [1.0], [1.0], [1.0]]}]} \ No newline at end of file diff --git a/Resources/Textures/Objects/items/ammo_casing.rsi/s-casing.png b/Resources/Textures/Objects/items/ammo_casing.rsi/s-casing.png new file mode 100644 index 0000000000..1b01712316 Binary files /dev/null and b/Resources/Textures/Objects/items/ammo_casing.rsi/s-casing.png differ diff --git a/Resources/Textures/Objects/items/magazine_12mm.rsi/12mm-0.png b/Resources/Textures/Objects/items/magazine_12mm.rsi/12mm-0.png new file mode 100644 index 0000000000..0f693f78ec Binary files /dev/null and b/Resources/Textures/Objects/items/magazine_12mm.rsi/12mm-0.png differ diff --git a/Resources/Textures/Objects/items/magazine_12mm.rsi/12mm-1.png b/Resources/Textures/Objects/items/magazine_12mm.rsi/12mm-1.png new file mode 100644 index 0000000000..f3bfbadc0d Binary files /dev/null and b/Resources/Textures/Objects/items/magazine_12mm.rsi/12mm-1.png differ diff --git a/Resources/Textures/Objects/items/magazine_12mm.rsi/12mm-10.png b/Resources/Textures/Objects/items/magazine_12mm.rsi/12mm-10.png new file mode 100644 index 0000000000..d73be4d480 Binary files /dev/null and b/Resources/Textures/Objects/items/magazine_12mm.rsi/12mm-10.png differ diff --git a/Resources/Textures/Objects/items/magazine_12mm.rsi/12mm-2.png b/Resources/Textures/Objects/items/magazine_12mm.rsi/12mm-2.png new file mode 100644 index 0000000000..41783f5bdd Binary files /dev/null and b/Resources/Textures/Objects/items/magazine_12mm.rsi/12mm-2.png differ diff --git a/Resources/Textures/Objects/items/magazine_12mm.rsi/12mm-3.png b/Resources/Textures/Objects/items/magazine_12mm.rsi/12mm-3.png new file mode 100644 index 0000000000..25d4402931 Binary files /dev/null and b/Resources/Textures/Objects/items/magazine_12mm.rsi/12mm-3.png differ diff --git a/Resources/Textures/Objects/items/magazine_12mm.rsi/12mm-4.png b/Resources/Textures/Objects/items/magazine_12mm.rsi/12mm-4.png new file mode 100644 index 0000000000..bbc7568ced Binary files /dev/null and b/Resources/Textures/Objects/items/magazine_12mm.rsi/12mm-4.png differ diff --git a/Resources/Textures/Objects/items/magazine_12mm.rsi/12mm-5.png b/Resources/Textures/Objects/items/magazine_12mm.rsi/12mm-5.png new file mode 100644 index 0000000000..c8ea365d96 Binary files /dev/null and b/Resources/Textures/Objects/items/magazine_12mm.rsi/12mm-5.png differ diff --git a/Resources/Textures/Objects/items/magazine_12mm.rsi/12mm-6.png b/Resources/Textures/Objects/items/magazine_12mm.rsi/12mm-6.png new file mode 100644 index 0000000000..20e563a505 Binary files /dev/null and b/Resources/Textures/Objects/items/magazine_12mm.rsi/12mm-6.png differ diff --git a/Resources/Textures/Objects/items/magazine_12mm.rsi/12mm-7.png b/Resources/Textures/Objects/items/magazine_12mm.rsi/12mm-7.png new file mode 100644 index 0000000000..3b737239d5 Binary files /dev/null and b/Resources/Textures/Objects/items/magazine_12mm.rsi/12mm-7.png differ diff --git a/Resources/Textures/Objects/items/magazine_12mm.rsi/12mm-8.png b/Resources/Textures/Objects/items/magazine_12mm.rsi/12mm-8.png new file mode 100644 index 0000000000..cae73c2b31 Binary files /dev/null and b/Resources/Textures/Objects/items/magazine_12mm.rsi/12mm-8.png differ diff --git a/Resources/Textures/Objects/items/magazine_12mm.rsi/12mm-9.png b/Resources/Textures/Objects/items/magazine_12mm.rsi/12mm-9.png new file mode 100644 index 0000000000..13e8fbd0d2 Binary files /dev/null and b/Resources/Textures/Objects/items/magazine_12mm.rsi/12mm-9.png differ diff --git a/Resources/Textures/Objects/items/magazine_12mm.rsi/meta.json b/Resources/Textures/Objects/items/magazine_12mm.rsi/meta.json new file mode 100644 index 0000000000..a0ba166ed1 --- /dev/null +++ b/Resources/Textures/Objects/items/magazine_12mm.rsi/meta.json @@ -0,0 +1 @@ +{"version": 1, "size": {"x": 32, "y": 32}, "license": "CC-BY-SA-3.0", "copyright": "Taken from https://github.com/vgstation-coders/vgstation13/blob/0b3ab17dbad632ddf738b63900ef8df1926bba47/icons/obj/ammo.dmi", "states": [{"name": "12mm-0", "directions": 1, "delays": [[1.0]]}, {"name": "12mm-1", "directions": 1, "delays": [[1.0]]}, {"name": "12mm-10", "directions": 1, "delays": [[1.0]]}, {"name": "12mm-2", "directions": 1, "delays": [[1.0]]}, {"name": "12mm-3", "directions": 1, "delays": [[1.0]]}, {"name": "12mm-4", "directions": 1, "delays": [[1.0]]}, {"name": "12mm-5", "directions": 1, "delays": [[1.0]]}, {"name": "12mm-6", "directions": 1, "delays": [[1.0]]}, {"name": "12mm-7", "directions": 1, "delays": [[1.0]]}, {"name": "12mm-8", "directions": 1, "delays": [[1.0]]}, {"name": "12mm-9", "directions": 1, "delays": [[1.0]]}]} \ No newline at end of file diff --git a/engine b/engine index 9e0c33c9bf..4767b23fb8 160000 --- a/engine +++ b/engine @@ -1 +1 @@ -Subproject commit 9e0c33c9bf12104cd794708c23ebd872052b0557 +Subproject commit 4767b23fb8688b0aada5c41c7a473b546ce32931