diff --git a/Content.Client/GameObjects/Components/Weapons/Ranged/Barrels/ClientBoltActionBarrelComponent.cs b/Content.Client/GameObjects/Components/Weapons/Ranged/Barrels/ClientBoltActionBarrelComponent.cs new file mode 100644 index 0000000000..778cb19599 --- /dev/null +++ b/Content.Client/GameObjects/Components/Weapons/Ranged/Barrels/ClientBoltActionBarrelComponent.cs @@ -0,0 +1,206 @@ +using Content.Client.UserInterface.Stylesheets; +using Content.Client.Utility; +using Content.Shared.GameObjects; +using Content.Shared.GameObjects.Components.Weapons.Ranged.Barrels; +using Robust.Client.Graphics; +using Robust.Client.UserInterface; +using Robust.Client.UserInterface.Controls; +using Robust.Shared.GameObjects; +using Robust.Shared.Maths; +using Robust.Shared.ViewVariables; +using System; + +namespace Content.Client.GameObjects.Components.Weapons.Ranged.Barrels +{ + [RegisterComponent] + public class ClientBoltActionBarrelComponent : Component, IItemStatus + { + public override string Name => "BoltActionBarrel"; + public override uint? NetID => ContentNetIDs.BOLTACTION_BARREL; + + private StatusControl _statusControl; + + /// + /// chambered is true when a bullet is chambered + /// spent is true when the chambered bullet is spent + /// + [ViewVariables] + public (bool chambered, bool spent) Chamber { get; private set; } + + /// + /// Count of bullets in the magazine. + /// + /// + /// Null if no magazine is inserted. + /// + [ViewVariables] + public (int count, int max)? MagazineCount { get; private set; } + + public override void HandleComponentState(ComponentState curState, ComponentState nextState) + { + if (!(curState is BoltActionBarrelComponentState cast)) + return; + + Chamber = cast.Chamber; + MagazineCount = cast.Magazine; + _statusControl?.Update(); + } + + public Control MakeControl() + { + _statusControl = new StatusControl(this); + _statusControl.Update(); + return _statusControl; + } + + public void DestroyControl(Control control) + { + if (_statusControl == control) + { + _statusControl = null; + } + } + + private sealed class StatusControl : Control + { + private readonly ClientBoltActionBarrelComponent _parent; + private readonly HBoxContainer _bulletsListTop; + private readonly HBoxContainer _bulletsListBottom; + private readonly TextureRect _chamberedBullet; + private readonly Label _noMagazineLabel; + + public StatusControl(ClientBoltActionBarrelComponent parent) + { + _parent = parent; + SizeFlagsHorizontal = SizeFlags.FillExpand; + SizeFlagsVertical = SizeFlags.ShrinkCenter; + AddChild(new VBoxContainer + { + SizeFlagsHorizontal = SizeFlags.FillExpand, + SizeFlagsVertical = SizeFlags.ShrinkCenter, + SeparationOverride = 0, + Children = + { + (_bulletsListTop = new HBoxContainer {SeparationOverride = 0}), + new HBoxContainer + { + SizeFlagsHorizontal = SizeFlags.FillExpand, + Children = + { + new Control + { + SizeFlagsHorizontal = SizeFlags.FillExpand, + Children = + { + (_bulletsListBottom = new HBoxContainer + { + SizeFlagsVertical = SizeFlags.ShrinkCenter, + SeparationOverride = 0 + }), + (_noMagazineLabel = new Label + { + Text = "No Magazine!", + StyleClasses = {StyleNano.StyleClassItemStatus} + }) + } + }, + (_chamberedBullet = new TextureRect + { + Texture = StaticIoC.ResC.GetTexture("/Textures/Interface/ItemStatus/Bullets/chambered.png"), + SizeFlagsVertical = SizeFlags.ShrinkCenter, + SizeFlagsHorizontal = SizeFlags.ShrinkEnd | SizeFlags.Fill, + }) + } + } + } + }); + } + + public void Update() + { + _chamberedBullet.ModulateSelfOverride = + _parent.Chamber.chambered ? + _parent.Chamber.spent ? Color.Red : Color.FromHex("#d7df60") + : Color.Black; + + _bulletsListTop.RemoveAllChildren(); + _bulletsListBottom.RemoveAllChildren(); + + if (_parent.MagazineCount == null) + { + _noMagazineLabel.Visible = true; + return; + } + + var (count, capacity) = _parent.MagazineCount.Value; + + _noMagazineLabel.Visible = false; + + string texturePath; + if (capacity <= 20) + { + texturePath = "/Textures/Interface/ItemStatus/Bullets/normal.png"; + } + else if (capacity <= 30) + { + texturePath = "/Textures/Interface/ItemStatus/Bullets/small.png"; + } + else + { + texturePath = "/Textures/Interface/ItemStatus/Bullets/tiny.png"; + } + + var texture = StaticIoC.ResC.GetTexture(texturePath); + + const int tinyMaxRow = 60; + + if (capacity > tinyMaxRow) + { + FillBulletRow(_bulletsListBottom, Math.Min(tinyMaxRow, count), tinyMaxRow, texture); + FillBulletRow(_bulletsListTop, Math.Max(0, count - tinyMaxRow), capacity - tinyMaxRow, texture); + } + else + { + FillBulletRow(_bulletsListBottom, count, capacity, texture); + } + } + + private static void FillBulletRow(Control container, int count, int capacity, Texture texture) + { + var colorA = Color.FromHex("#b68f0e"); + var colorB = Color.FromHex("#d7df60"); + var colorGoneA = Color.FromHex("#000000"); + var colorGoneB = Color.FromHex("#222222"); + + var altColor = false; + + for (var i = count; i < capacity; i++) + { + container.AddChild(new TextureRect + { + Texture = texture, + ModulateSelfOverride = altColor ? colorGoneA : colorGoneB + }); + + altColor ^= true; + } + + for (var i = 0; i < count; i++) + { + container.AddChild(new TextureRect + { + Texture = texture, + ModulateSelfOverride = altColor ? colorA : colorB + }); + + altColor ^= true; + } + } + + protected override Vector2 CalculateMinimumSize() + { + return Vector2.ComponentMax((0, 15), base.CalculateMinimumSize()); + } + } + } +} diff --git a/Content.Client/GameObjects/Components/Weapons/Ranged/Barrels/ClientMagazineBarrelComponent.cs b/Content.Client/GameObjects/Components/Weapons/Ranged/Barrels/ClientMagazineBarrelComponent.cs index f879c7c8f0..f02d184cad 100644 --- a/Content.Client/GameObjects/Components/Weapons/Ranged/Barrels/ClientMagazineBarrelComponent.cs +++ b/Content.Client/GameObjects/Components/Weapons/Ranged/Barrels/ClientMagazineBarrelComponent.cs @@ -1,4 +1,4 @@ -using System; +using System; using Content.Client.Animations; using Content.Client.UserInterface.Stylesheets; using Content.Client.Utility; @@ -138,54 +138,52 @@ namespace Content.Client.GameObjects.Components.Weapons.Ranged.Barrels private sealed class StatusControl : Control { private readonly ClientMagazineBarrelComponent _parent; - private readonly HBoxContainer _bulletsListTop; - private readonly HBoxContainer _bulletsListBottom; + private readonly HBoxContainer _bulletsList; private readonly TextureRect _chamberedBullet; private readonly Label _noMagazineLabel; + private readonly Label _ammoCount; public StatusControl(ClientMagazineBarrelComponent parent) { _parent = parent; SizeFlagsHorizontal = SizeFlags.FillExpand; SizeFlagsVertical = SizeFlags.ShrinkCenter; - AddChild(new VBoxContainer + + AddChild(new HBoxContainer { SizeFlagsHorizontal = SizeFlags.FillExpand, - SizeFlagsVertical = SizeFlags.ShrinkCenter, - SeparationOverride = 0, Children = { - (_bulletsListTop = new HBoxContainer {SeparationOverride = 0}), - new HBoxContainer + (_chamberedBullet = new TextureRect + { + Texture = StaticIoC.ResC.GetTexture("/Textures/Interface/ItemStatus/Bullets/chambered_rotated.png"), + SizeFlagsVertical = SizeFlags.ShrinkCenter, + SizeFlagsHorizontal = SizeFlags.ShrinkEnd | SizeFlags.Fill, + }), + new Control() { CustomMinimumSize = (5,0) }, + new Control { SizeFlagsHorizontal = SizeFlags.FillExpand, Children = { - new Control + (_bulletsList = new HBoxContainer { - SizeFlagsHorizontal = SizeFlags.FillExpand, - Children = - { - (_bulletsListBottom = new HBoxContainer - { - SizeFlagsVertical = SizeFlags.ShrinkCenter, - SeparationOverride = 0 - }), - (_noMagazineLabel = new Label - { - Text = "No Magazine!", - StyleClasses = {StyleNano.StyleClassItemStatus} - }) - } - }, - (_chamberedBullet = new TextureRect - { - Texture = StaticIoC.ResC.GetTexture("/Textures/Interface/ItemStatus/Bullets/chambered.png"), SizeFlagsVertical = SizeFlags.ShrinkCenter, - SizeFlagsHorizontal = SizeFlags.ShrinkEnd | SizeFlags.Fill, + SeparationOverride = 0 + }), + (_noMagazineLabel = new Label + { + Text = "No Magazine!", + StyleClasses = {StyleNano.StyleClassItemStatus} }) } - } + }, + new Control() { CustomMinimumSize = (5,0) }, + (_ammoCount = new Label + { + StyleClasses = {StyleNano.StyleClassItemStatus}, + SizeFlagsHorizontal = SizeFlags.ShrinkEnd, + }), } }); } @@ -195,46 +193,26 @@ namespace Content.Client.GameObjects.Components.Weapons.Ranged.Barrels _chamberedBullet.ModulateSelfOverride = _parent.Chambered ? Color.FromHex("#d7df60") : Color.Black; - _bulletsListTop.RemoveAllChildren(); - _bulletsListBottom.RemoveAllChildren(); + _bulletsList.RemoveAllChildren(); if (_parent.MagazineCount == null) { _noMagazineLabel.Visible = true; + _ammoCount.Visible = false; return; } var (count, capacity) = _parent.MagazineCount.Value; _noMagazineLabel.Visible = false; + _ammoCount.Visible = true; - string texturePath; - if (capacity <= 20) - { - texturePath = "/Textures/Interface/ItemStatus/Bullets/normal.png"; - } - else if (capacity <= 30) - { - texturePath = "/Textures/Interface/ItemStatus/Bullets/small.png"; - } - else - { - texturePath = "/Textures/Interface/ItemStatus/Bullets/tiny.png"; - } - + var texturePath = "/Textures/Interface/ItemStatus/Bullets/normal.png"; var texture = StaticIoC.ResC.GetTexture(texturePath); - const int tinyMaxRow = 60; - - if (capacity > tinyMaxRow) - { - FillBulletRow(_bulletsListBottom, Math.Min(tinyMaxRow, count), tinyMaxRow, texture); - FillBulletRow(_bulletsListTop, Math.Max(0, count - tinyMaxRow), capacity - tinyMaxRow, texture); - } - else - { - FillBulletRow(_bulletsListBottom, count, capacity, texture); - } + _ammoCount.Text = $"x{count:00}"; + capacity = Math.Min(capacity, 20); + FillBulletRow(_bulletsList, count, capacity, texture); } private static void FillBulletRow(Control container, int count, int capacity, Texture texture) @@ -246,23 +224,32 @@ namespace Content.Client.GameObjects.Components.Weapons.Ranged.Barrels var altColor = false; + // Draw the empty ones for (var i = count; i < capacity; i++) { container.AddChild(new TextureRect { Texture = texture, - ModulateSelfOverride = altColor ? colorGoneA : colorGoneB + ModulateSelfOverride = altColor ? colorGoneA : colorGoneB, + SizeFlagsHorizontal = SizeFlags.Fill, + SizeFlagsVertical = SizeFlags.Fill, + Stretch = TextureRect.StretchMode.KeepCentered }); altColor ^= true; } + // Draw the full ones, but limit the count to the capacity + count = Math.Min(count, capacity); for (var i = 0; i < count; i++) { container.AddChild(new TextureRect { Texture = texture, - ModulateSelfOverride = altColor ? colorA : colorB + ModulateSelfOverride = altColor ? colorA : colorB, + SizeFlagsHorizontal = SizeFlags.Fill, + SizeFlagsVertical = SizeFlags.Fill, + Stretch = TextureRect.StretchMode.KeepCentered }); altColor ^= true; @@ -281,4 +268,4 @@ namespace Content.Client.GameObjects.Components.Weapons.Ranged.Barrels } } } -} \ No newline at end of file +} diff --git a/Content.Client/GameObjects/Components/Weapons/Ranged/Barrels/ClientPumpBarrelComponent.cs b/Content.Client/GameObjects/Components/Weapons/Ranged/Barrels/ClientPumpBarrelComponent.cs new file mode 100644 index 0000000000..a2c58b8bbb --- /dev/null +++ b/Content.Client/GameObjects/Components/Weapons/Ranged/Barrels/ClientPumpBarrelComponent.cs @@ -0,0 +1,208 @@ +using Content.Client.UserInterface.Stylesheets; +using Content.Client.Utility; +using Content.Shared.GameObjects; +using Content.Shared.GameObjects.Components.Weapons.Ranged.Barrels; +using Robust.Client.Graphics; +using Robust.Client.UserInterface; +using Robust.Client.UserInterface.Controls; +using Robust.Shared.GameObjects; +using Robust.Shared.Maths; +using Robust.Shared.ViewVariables; +using System; +using System.Collections.Generic; +using System.Text; + +namespace Content.Client.GameObjects.Components.Weapons.Ranged.Barrels +{ + [RegisterComponent] + public class ClientPumpBarrelComponent : Component, IItemStatus + { + public override string Name => "PumpBarrel"; + public override uint? NetID => ContentNetIDs.PUMP_BARREL; + + private StatusControl _statusControl; + + /// + /// chambered is true when a bullet is chambered + /// spent is true when the chambered bullet is spent + /// + [ViewVariables] + public (bool chambered, bool spent) Chamber { get; private set; } + + /// + /// Count of bullets in the magazine. + /// + /// + /// Null if no magazine is inserted. + /// + [ViewVariables] + public (int count, int max)? MagazineCount { get; private set; } + + public override void HandleComponentState(ComponentState curState, ComponentState nextState) + { + if (!(curState is PumpBarrelComponentState cast)) + return; + + Chamber = cast.Chamber; + MagazineCount = cast.Magazine; + _statusControl?.Update(); + } + + public Control MakeControl() + { + _statusControl = new StatusControl(this); + _statusControl.Update(); + return _statusControl; + } + + public void DestroyControl(Control control) + { + if (_statusControl == control) + { + _statusControl = null; + } + } + + private sealed class StatusControl : Control + { + private readonly ClientPumpBarrelComponent _parent; + private readonly HBoxContainer _bulletsListTop; + private readonly HBoxContainer _bulletsListBottom; + private readonly TextureRect _chamberedBullet; + private readonly Label _noMagazineLabel; + + public StatusControl(ClientPumpBarrelComponent parent) + { + _parent = parent; + SizeFlagsHorizontal = SizeFlags.FillExpand; + SizeFlagsVertical = SizeFlags.ShrinkCenter; + AddChild(new VBoxContainer + { + SizeFlagsHorizontal = SizeFlags.FillExpand, + SizeFlagsVertical = SizeFlags.ShrinkCenter, + SeparationOverride = 0, + Children = + { + (_bulletsListTop = new HBoxContainer {SeparationOverride = 0}), + new HBoxContainer + { + SizeFlagsHorizontal = SizeFlags.FillExpand, + Children = + { + new Control + { + SizeFlagsHorizontal = SizeFlags.FillExpand, + Children = + { + (_bulletsListBottom = new HBoxContainer + { + SizeFlagsVertical = SizeFlags.ShrinkCenter, + SeparationOverride = 0 + }), + (_noMagazineLabel = new Label + { + Text = "No Magazine!", + StyleClasses = {StyleNano.StyleClassItemStatus} + }) + } + }, + (_chamberedBullet = new TextureRect + { + Texture = StaticIoC.ResC.GetTexture("/Textures/Interface/ItemStatus/Bullets/chambered.png"), + SizeFlagsVertical = SizeFlags.ShrinkCenter, + SizeFlagsHorizontal = SizeFlags.ShrinkEnd | SizeFlags.Fill, + }) + } + } + } + }); + } + + public void Update() + { + _chamberedBullet.ModulateSelfOverride = + _parent.Chamber.chambered ? + _parent.Chamber.spent ? Color.Red : Color.FromHex("#d7df60") + : Color.Black; + + _bulletsListTop.RemoveAllChildren(); + _bulletsListBottom.RemoveAllChildren(); + + if (_parent.MagazineCount == null) + { + _noMagazineLabel.Visible = true; + return; + } + + var (count, capacity) = _parent.MagazineCount.Value; + + _noMagazineLabel.Visible = false; + + string texturePath; + if (capacity <= 20) + { + texturePath = "/Textures/Interface/ItemStatus/Bullets/normal.png"; + } + else if (capacity <= 30) + { + texturePath = "/Textures/Interface/ItemStatus/Bullets/small.png"; + } + else + { + texturePath = "/Textures/Interface/ItemStatus/Bullets/tiny.png"; + } + + var texture = StaticIoC.ResC.GetTexture(texturePath); + + const int tinyMaxRow = 60; + + if (capacity > tinyMaxRow) + { + FillBulletRow(_bulletsListBottom, Math.Min(tinyMaxRow, count), tinyMaxRow, texture); + FillBulletRow(_bulletsListTop, Math.Max(0, count - tinyMaxRow), capacity - tinyMaxRow, texture); + } + else + { + FillBulletRow(_bulletsListBottom, count, capacity, texture); + } + } + + private static void FillBulletRow(Control container, int count, int capacity, Texture texture) + { + var colorA = Color.FromHex("#b68f0e"); + var colorB = Color.FromHex("#d7df60"); + var colorGoneA = Color.FromHex("#000000"); + var colorGoneB = Color.FromHex("#222222"); + + var altColor = false; + + for (var i = count; i < capacity; i++) + { + container.AddChild(new TextureRect + { + Texture = texture, + ModulateSelfOverride = altColor ? colorGoneA : colorGoneB + }); + + altColor ^= true; + } + + for (var i = 0; i < count; i++) + { + container.AddChild(new TextureRect + { + Texture = texture, + ModulateSelfOverride = altColor ? colorA : colorB + }); + + altColor ^= true; + } + } + + protected override Vector2 CalculateMinimumSize() + { + return Vector2.ComponentMax((0, 15), base.CalculateMinimumSize()); + } + } + } +} diff --git a/Content.Client/GameObjects/Components/Weapons/Ranged/Barrels/ClientRevolverBarrelComponent.cs b/Content.Client/GameObjects/Components/Weapons/Ranged/Barrels/ClientRevolverBarrelComponent.cs new file mode 100644 index 0000000000..3d5d94f5a8 --- /dev/null +++ b/Content.Client/GameObjects/Components/Weapons/Ranged/Barrels/ClientRevolverBarrelComponent.cs @@ -0,0 +1,175 @@ +using Content.Client.UserInterface.Stylesheets; +using Content.Client.Utility; +using Content.Shared.GameObjects; +using Content.Shared.GameObjects.Components.Weapons.Ranged.Barrels; +using Robust.Client.Graphics; +using Robust.Client.UserInterface; +using Robust.Client.UserInterface.Controls; +using Robust.Shared.GameObjects; +using Robust.Shared.Maths; +using Robust.Shared.ViewVariables; +using System; +using System.Collections.Generic; +using System.Runtime.InteropServices.ComTypes; +using System.Text; + +namespace Content.Client.GameObjects.Components.Weapons.Ranged.Barrels +{ + [RegisterComponent] + public class ClientRevolverBarrelComponent : Component, IItemStatus + { + public override string Name => "RevolverBarrel"; + public override uint? NetID => ContentNetIDs.REVOLVER_BARREL; + + private StatusControl _statusControl; + + /// + /// A array that lists the bullet states + /// true means a spent bullet + /// false means a "shootable" bullet + /// null means no bullet + /// + [ViewVariables] + public bool?[] Bullets { get; private set; } + + [ViewVariables] + public int CurrentSlot { get; private set; } + + public override void HandleComponentState(ComponentState curState, ComponentState nextState) + { + if (!(curState is RevolverBarrelComponentState cast)) + return; + + CurrentSlot = cast.CurrentSlot; + Bullets = cast.Bullets; + _statusControl?.Update(); + } + + public Control MakeControl() + { + _statusControl = new StatusControl(this); + _statusControl.Update(); + return _statusControl; + } + + public void DestroyControl(Control control) + { + if (_statusControl == control) + { + _statusControl = null; + } + } + + private sealed class StatusControl : Control + { + private readonly ClientRevolverBarrelComponent _parent; + private readonly HBoxContainer _bulletsList; + + public StatusControl(ClientRevolverBarrelComponent parent) + { + _parent = parent; + SizeFlagsHorizontal = SizeFlags.FillExpand; + SizeFlagsVertical = SizeFlags.ShrinkCenter; + AddChild((_bulletsList = new HBoxContainer + { + SizeFlagsHorizontal = SizeFlags.FillExpand, + SizeFlagsVertical = SizeFlags.ShrinkCenter, + SeparationOverride = 0 + })); + } + + public void Update() + { + _bulletsList.RemoveAllChildren(); + + var capacity = _parent.Bullets.Length; + + string texturePath; + if (capacity <= 20) + { + texturePath = "/Textures/Interface/ItemStatus/Bullets/normal.png"; + } + else if (capacity <= 30) + { + texturePath = "/Textures/Interface/ItemStatus/Bullets/small.png"; + } + else + { + texturePath = "/Textures/Interface/ItemStatus/Bullets/tiny.png"; + } + + var texture = StaticIoC.ResC.GetTexture(texturePath); + var spentTexture = StaticIoC.ResC.GetTexture("/Textures/Interface/ItemStatus/Bullets/empty.png"); + + FillBulletRow(_bulletsList, texture, spentTexture); + } + + private void FillBulletRow(Control container, Texture texture, Texture emptyTexture) + { + var colorA = Color.FromHex("#b68f0e"); + var colorB = Color.FromHex("#d7df60"); + var colorSpentA = Color.FromHex("#b50e25"); + var colorSpentB = Color.FromHex("#d3745f"); + var colorGoneA = Color.FromHex("#000000"); + var colorGoneB = Color.FromHex("#222222"); + + var altColor = false; + var scale = 1.3f; + + for (var i = 0; i < _parent.Bullets.Length; i++) + { + var bulletSpent = _parent.Bullets[i]; + // Add a outline + var box = new Control() + { + CustomMinimumSize = texture.Size * scale, + }; + if (i == _parent.CurrentSlot) + { + box.AddChild(new TextureRect + { + Texture = texture, + TextureScale = (scale, scale), + ModulateSelfOverride = Color.Green, + }); + } + Color color; + Texture bulletTexture = texture; + + if (bulletSpent.HasValue) + { + if (bulletSpent.Value) + { + color = altColor ? colorSpentA : colorSpentB; + bulletTexture = emptyTexture; + } + else + { + color = altColor ? colorA : colorB; + } + } + else + { + color = altColor ? colorGoneA : colorGoneB; + } + + box.AddChild(new TextureRect + { + SizeFlagsHorizontal = SizeFlags.Fill, + SizeFlagsVertical = SizeFlags.Fill, + Stretch = TextureRect.StretchMode.KeepCentered, + Texture = bulletTexture, + ModulateSelfOverride = color, + }); + altColor ^= true; + container.AddChild(box); + } + } + + protected override Vector2 CalculateMinimumSize() + { + return Vector2.ComponentMax((0, 15), base.CalculateMinimumSize()); + } + } + } +} diff --git a/Content.Client/IgnoredComponents.cs b/Content.Client/IgnoredComponents.cs index 72785bce6f..9a72a27e22 100644 --- a/Content.Client/IgnoredComponents.cs +++ b/Content.Client/IgnoredComponents.cs @@ -86,9 +86,6 @@ "CanSpill", "SpeedLoader", "Hitscan", - "BoltActionBarrel", - "PumpBarrel", - "RevolverBarrel", "ExplosiveProjectile", "StunnableProjectile", "RandomPottedPlant", diff --git a/Content.Server/GameObjects/Components/Weapon/Ranged/Barrels/BoltActionBarrelComponent.cs b/Content.Server/GameObjects/Components/Weapon/Ranged/Barrels/BoltActionBarrelComponent.cs index cc0d0d74b2..3e4b6362fc 100644 --- a/Content.Server/GameObjects/Components/Weapon/Ranged/Barrels/BoltActionBarrelComponent.cs +++ b/Content.Server/GameObjects/Components/Weapon/Ranged/Barrels/BoltActionBarrelComponent.cs @@ -1,6 +1,7 @@ using System.Collections.Generic; using System.Threading.Tasks; using Content.Server.GameObjects.Components.Weapon.Ranged.Ammunition; +using Content.Shared.GameObjects; using Content.Shared.GameObjects.Components.Weapons.Ranged.Barrels; using Content.Shared.GameObjects.EntitySystems; using Content.Shared.GameObjects.Verbs; @@ -32,6 +33,7 @@ namespace Content.Server.GameObjects.Components.Weapon.Ranged.Barrels // but it felt a lot messier to play around with, especially when adding verbs public override string Name => "BoltActionBarrel"; + public override uint? NetID => ContentNetIDs.BOLTACTION_BARREL; public override int ShotsLeft { @@ -123,6 +125,24 @@ namespace Content.Server.GameObjects.Components.Weapon.Ranged.Barrels UpdateAppearance(); } + public override ComponentState GetComponentState() + { + (int, int)? count = (ShotsLeft, Capacity); + var chamberedExists = _chamberContainer.ContainedEntity != null; + // (Is one chambered?, is the bullet spend) + var chamber = (chamberedExists, false); + if (chamberedExists && _chamberContainer.ContainedEntity.TryGetComponent(out var ammo)) + { + chamber.Item2 = ammo.Spent; + } + + return new BoltActionBarrelComponentState( + chamber, + FireRateSelector, + count, + SoundGunshot); + } + public override void Initialize() { // TODO: Add existing ammo support on revolvers @@ -170,6 +190,11 @@ namespace Content.Server.GameObjects.Components.Weapon.Ranged.Barrels { Cycle(); } + else + { + Dirty(); + } + return chamberEntity?.GetComponent().TakeBullet(spawnAtGrid, spawnAtMap); } @@ -256,7 +281,7 @@ namespace Content.Server.GameObjects.Components.Weapon.Ranged.Barrels { EntitySystem.Get().PlayAtCoords(_soundInsert, Owner.Transform.GridPosition, AudioParams.Default.WithVolume(-2)); } - // Dirty(); + Dirty(); UpdateAppearance(); return true; } @@ -269,7 +294,7 @@ namespace Content.Server.GameObjects.Components.Weapon.Ranged.Barrels { EntitySystem.Get().PlayAtCoords(_soundInsert, Owner.Transform.GridPosition, AudioParams.Default.WithVolume(-2)); } - // Dirty(); + Dirty(); UpdateAppearance(); return true; } diff --git a/Content.Server/GameObjects/Components/Weapon/Ranged/Barrels/PumpBarrelComponent.cs b/Content.Server/GameObjects/Components/Weapon/Ranged/Barrels/PumpBarrelComponent.cs index 6859c0c258..9dd3d9c7b5 100644 --- a/Content.Server/GameObjects/Components/Weapon/Ranged/Barrels/PumpBarrelComponent.cs +++ b/Content.Server/GameObjects/Components/Weapon/Ranged/Barrels/PumpBarrelComponent.cs @@ -1,6 +1,7 @@ using System.Collections.Generic; using System.Threading.Tasks; using Content.Server.GameObjects.Components.Weapon.Ranged.Ammunition; +using Content.Shared.GameObjects; using Content.Shared.GameObjects.Components.Weapons.Ranged.Barrels; using Content.Shared.GameObjects.EntitySystems; using Content.Shared.Interfaces; @@ -27,6 +28,7 @@ namespace Content.Server.GameObjects.Components.Weapon.Ranged.Barrels public sealed class PumpBarrelComponent : ServerRangedBarrelComponent, IMapInit, IExamine { public override string Name => "PumpBarrel"; + public override uint? NetID => ContentNetIDs.PUMP_BARREL; public override int ShotsLeft { @@ -81,6 +83,23 @@ namespace Content.Server.GameObjects.Components.Weapon.Ranged.Barrels UpdateAppearance(); } + public override ComponentState GetComponentState() + { + (int, int)? count = (ShotsLeft, Capacity); + var chamberedExists = _chamberContainer.ContainedEntity != null; + // (Is one chambered?, is the bullet spend) + var chamber = (chamberedExists, false); + if (chamberedExists && _chamberContainer.ContainedEntity.TryGetComponent(out var ammo)) + { + chamber.Item2 = ammo.Spent; + } + return new PumpBarrelComponentState( + chamber, + FireRateSelector, + count, + SoundGunshot); + } + public override void Initialize() { base.Initialize(); @@ -110,6 +129,7 @@ namespace Content.Server.GameObjects.Components.Weapon.Ranged.Barrels } _appearanceComponent?.SetData(MagazineBarrelVisuals.MagLoaded, true); + Dirty(); UpdateAppearance(); } @@ -131,6 +151,11 @@ namespace Content.Server.GameObjects.Components.Weapon.Ranged.Barrels { Cycle(); } + else + { + Dirty(); + } + return chamberEntity?.GetComponent().TakeBullet(spawnAtGrid, spawnAtMap); } @@ -168,7 +193,7 @@ namespace Content.Server.GameObjects.Components.Weapon.Ranged.Barrels } } - // Dirty(); + Dirty(); UpdateAppearance(); } @@ -189,7 +214,7 @@ namespace Content.Server.GameObjects.Components.Weapon.Ranged.Barrels { _ammoContainer.Insert(eventArgs.Using); _spawnedAmmo.Push(eventArgs.Using); - // Dirty(); + Dirty(); UpdateAppearance(); if (_soundInsert != null) { diff --git a/Content.Server/GameObjects/Components/Weapon/Ranged/Barrels/RevolverBarrelComponent.cs b/Content.Server/GameObjects/Components/Weapon/Ranged/Barrels/RevolverBarrelComponent.cs index 004e742b71..e976c6aeb3 100644 --- a/Content.Server/GameObjects/Components/Weapon/Ranged/Barrels/RevolverBarrelComponent.cs +++ b/Content.Server/GameObjects/Components/Weapon/Ranged/Barrels/RevolverBarrelComponent.cs @@ -1,6 +1,7 @@ using System; using System.Threading.Tasks; using Content.Server.GameObjects.Components.Weapon.Ranged.Ammunition; +using Content.Shared.GameObjects; using Content.Shared.GameObjects.Components.Weapons.Ranged.Barrels; using Content.Shared.GameObjects.EntitySystems; using Content.Shared.GameObjects.Verbs; @@ -25,6 +26,8 @@ namespace Content.Server.GameObjects.Components.Weapon.Ranged.Barrels public sealed class RevolverBarrelComponent : ServerRangedBarrelComponent { public override string Name => "RevolverBarrel"; + public override uint? NetID => ContentNetIDs.REVOLVER_BARREL; + private BallisticCaliber _caliber; private Container _ammoContainer; private int _currentSlot = 0; @@ -60,6 +63,26 @@ namespace Content.Server.GameObjects.Components.Weapon.Ranged.Barrels serializer.DataField(ref _soundSpin, "soundSpin", "/Audio/Weapons/Guns/Misc/revolver_spin.ogg"); } + public override ComponentState GetComponentState() + { + var slotsSpent = new bool?[Capacity]; + for (var i = 0; i < Capacity; i++) + { + slotsSpent[i] = null; + if (_ammoSlots[i] != null && _ammoSlots[i].TryGetComponent(out AmmoComponent ammo)) + { + slotsSpent[i] = ammo.Spent; + } + } + + //TODO: make yaml var to not sent currentSlot/UI? (for russian roulette) + return new RevolverBarrelComponentState( + _currentSlot, + FireRateSelector, + slotsSpent, + SoundGunshot); + } + public override void Initialize() { base.Initialize(); @@ -90,6 +113,7 @@ namespace Content.Server.GameObjects.Components.Weapon.Ranged.Barrels } _appearanceComponent?.SetData(MagazineBarrelVisuals.MagLoaded, true); + Dirty(); } private void UpdateAppearance() @@ -129,7 +153,7 @@ namespace Content.Server.GameObjects.Components.Weapon.Ranged.Barrels EntitySystem.Get().PlayAtCoords(_soundInsert, Owner.Transform.GridPosition, AudioParams.Default.WithVolume(-2)); } - // Dirty(); + Dirty(); UpdateAppearance(); return true; } @@ -143,7 +167,7 @@ namespace Content.Server.GameObjects.Components.Weapon.Ranged.Barrels { // Move up a slot _currentSlot = (_currentSlot + 1) % _ammoSlots.Length; - // Dirty(); + Dirty(); UpdateAppearance(); } @@ -158,6 +182,7 @@ namespace Content.Server.GameObjects.Components.Weapon.Ranged.Barrels { EntitySystem.Get().PlayAtCoords(_soundSpin, Owner.Transform.GridPosition, AudioParams.Default.WithVolume(-2)); } + Dirty(); } public override IEntity PeekAmmo() @@ -227,7 +252,7 @@ namespace Content.Server.GameObjects.Components.Weapon.Ranged.Barrels public override bool UseEntity(UseEntityEventArgs eventArgs) { EjectAllSlots(); - //Dirty(); + Dirty(); UpdateAppearance(); return true; } diff --git a/Content.Shared/GameObjects/Components/Weapons/Ranged/Barrels/SharedBoltActionBarrelComponent.cs b/Content.Shared/GameObjects/Components/Weapons/Ranged/Barrels/SharedBoltActionBarrelComponent.cs new file mode 100644 index 0000000000..f2e8e52b80 --- /dev/null +++ b/Content.Shared/GameObjects/Components/Weapons/Ranged/Barrels/SharedBoltActionBarrelComponent.cs @@ -0,0 +1,30 @@ +using Robust.Shared.GameObjects; +using Robust.Shared.Serialization; +using System; +using System.Collections.Generic; +using System.Text; + +namespace Content.Shared.GameObjects.Components.Weapons.Ranged.Barrels +{ + [Serializable, NetSerializable] + public class BoltActionBarrelComponentState : ComponentState + { + public (bool chambered, bool spent) Chamber { get; } + public FireRateSelector FireRateSelector { get; } + public (int count, int max)? Magazine { get; } + public string SoundGunshot { get; } + + public BoltActionBarrelComponentState( + (bool chambered, bool spent) chamber, + FireRateSelector fireRateSelector, + (int count, int max)? magazine, + string soundGunshot) : + base(ContentNetIDs.BOLTACTION_BARREL) + { + Chamber = chamber; + FireRateSelector = fireRateSelector; + Magazine = magazine; + SoundGunshot = soundGunshot; + } + } +} diff --git a/Content.Shared/GameObjects/Components/Weapons/Ranged/Barrels/SharedPumpBarrelComponent.cs b/Content.Shared/GameObjects/Components/Weapons/Ranged/Barrels/SharedPumpBarrelComponent.cs new file mode 100644 index 0000000000..a577e11ad0 --- /dev/null +++ b/Content.Shared/GameObjects/Components/Weapons/Ranged/Barrels/SharedPumpBarrelComponent.cs @@ -0,0 +1,30 @@ +using Robust.Shared.GameObjects; +using Robust.Shared.Serialization; +using System; +using System.Collections.Generic; +using System.Text; + +namespace Content.Shared.GameObjects.Components.Weapons.Ranged.Barrels +{ + [Serializable, NetSerializable] + public class PumpBarrelComponentState : ComponentState + { + public (bool chambered, bool spent) Chamber { get; } + public FireRateSelector FireRateSelector { get; } + public (int count, int max)? Magazine { get; } + public string SoundGunshot { get; } + + public PumpBarrelComponentState( + (bool chambered, bool spent) chamber, + FireRateSelector fireRateSelector, + (int count, int max)? magazine, + string soundGunshot) : + base(ContentNetIDs.PUMP_BARREL) + { + Chamber = chamber; + FireRateSelector = fireRateSelector; + Magazine = magazine; + SoundGunshot = soundGunshot; + } + } +} diff --git a/Content.Shared/GameObjects/Components/Weapons/Ranged/Barrels/SharedRevolverBarrelComponent.cs b/Content.Shared/GameObjects/Components/Weapons/Ranged/Barrels/SharedRevolverBarrelComponent.cs new file mode 100644 index 0000000000..3ed085101b --- /dev/null +++ b/Content.Shared/GameObjects/Components/Weapons/Ranged/Barrels/SharedRevolverBarrelComponent.cs @@ -0,0 +1,30 @@ +using Robust.Shared.GameObjects; +using Robust.Shared.Serialization; +using System; +using System.Collections.Generic; +using System.Text; + +namespace Content.Shared.GameObjects.Components.Weapons.Ranged.Barrels +{ + [Serializable, NetSerializable] + public class RevolverBarrelComponentState : ComponentState + { + public int CurrentSlot { get; } + public FireRateSelector FireRateSelector { get; } + public bool?[] Bullets { get; } + public string SoundGunshot { get; } + + public RevolverBarrelComponentState( + int currentSlot, + FireRateSelector fireRateSelector, + bool?[] bullets, + string soundGunshot) : + base(ContentNetIDs.REVOLVER_BARREL) + { + CurrentSlot = currentSlot; + FireRateSelector = fireRateSelector; + Bullets = bullets; + SoundGunshot = soundGunshot; + } + } +} diff --git a/Content.Shared/GameObjects/ContentNetIDs.cs b/Content.Shared/GameObjects/ContentNetIDs.cs index 7d7bcfdc4f..2eb1e59b58 100644 --- a/Content.Shared/GameObjects/ContentNetIDs.cs +++ b/Content.Shared/GameObjects/ContentNetIDs.cs @@ -65,6 +65,9 @@ public const uint RADIATION_PULSE = 1059; public const uint BODY_MANAGER = 1060; public const uint CLIMBING = 1061; + public const uint BOLTACTION_BARREL = 1062; + public const uint PUMP_BARREL = 1063; + public const uint REVOLVER_BARREL = 1064; // Net IDs for integration tests. public const uint PREDICTION_TEST = 10001; diff --git a/Resources/Textures/Interface/ItemStatus/Bullets/chambered_rotated.png b/Resources/Textures/Interface/ItemStatus/Bullets/chambered_rotated.png new file mode 100644 index 0000000000..44d7290469 Binary files /dev/null and b/Resources/Textures/Interface/ItemStatus/Bullets/chambered_rotated.png differ diff --git a/Resources/Textures/Interface/ItemStatus/Bullets/empty.png b/Resources/Textures/Interface/ItemStatus/Bullets/empty.png new file mode 100644 index 0000000000..564f5bf851 Binary files /dev/null and b/Resources/Textures/Interface/ItemStatus/Bullets/empty.png differ