Add ItemStatus to energy weapons (#1921)

* -ItemStatus for BatteryGuns
-EjectCell Verb
-Using the gun throws the battery also on the ground

* Copy the flashlight and call it a day

* Name a red fruit

* Remove SoundGunshot
This commit is contained in:
Exp
2020-08-27 16:31:29 +02:00
committed by GitHub
parent 26de86058e
commit fc6ec5a7b9
6 changed files with 263 additions and 29 deletions

View File

@@ -0,0 +1,160 @@
using Content.Client.UserInterface.Stylesheets;
using Content.Shared.GameObjects;
using Content.Shared.GameObjects.Components.Weapons.Ranged.Barrels;
using Robust.Client.Graphics.Drawing;
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 ClientBatteryBarrelComponent : Component, IItemStatus
{
public override string Name => "BatteryBarrel";
public override uint? NetID => ContentNetIDs.BATTERY_BARREL;
private StatusControl _statusControl;
/// <summary>
/// Count of bullets in the magazine.
/// </summary>
/// <remarks>
/// Null if no magazine is inserted.
/// </remarks>
[ViewVariables]
public (int count, int max)? MagazineCount { get; private set; }
public override void HandleComponentState(ComponentState curState, ComponentState nextState)
{
if (!(curState is BatteryBarrelComponentState cast))
return;
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 ClientBatteryBarrelComponent _parent;
private readonly HBoxContainer _bulletsList;
private readonly Label _noBatteryLabel;
private readonly Label _ammoCount;
public StatusControl(ClientBatteryBarrelComponent parent)
{
_parent = parent;
SizeFlagsHorizontal = SizeFlags.FillExpand;
SizeFlagsVertical = SizeFlags.ShrinkCenter;
AddChild(new HBoxContainer
{
SizeFlagsHorizontal = SizeFlags.FillExpand,
Children =
{
new Control
{
SizeFlagsHorizontal = SizeFlags.FillExpand,
Children =
{
(_bulletsList = new HBoxContainer
{
SizeFlagsVertical = SizeFlags.ShrinkCenter,
SeparationOverride = 4
}),
(_noBatteryLabel = new Label
{
Text = "No Battery!",
StyleClasses = {StyleNano.StyleClassItemStatus}
})
}
},
new Control() { CustomMinimumSize = (5,0) },
(_ammoCount = new Label
{
StyleClasses = {StyleNano.StyleClassItemStatus},
SizeFlagsHorizontal = SizeFlags.ShrinkEnd,
}),
}
});
}
public void Update()
{
_bulletsList.RemoveAllChildren();
if (_parent.MagazineCount == null)
{
_noBatteryLabel.Visible = true;
_ammoCount.Visible = false;
return;
}
var (count, capacity) = _parent.MagazineCount.Value;
_noBatteryLabel.Visible = false;
_ammoCount.Visible = true;
_ammoCount.Text = $"x{count:00}";
capacity = Math.Min(capacity, 8);
FillBulletRow(_bulletsList, count, capacity);
}
private static void FillBulletRow(Control container, int count, int capacity)
{
var colorGone = Color.FromHex("#000000");
var color = Color.FromHex("#E00000");
// Draw the empty ones
for (var i = count; i < capacity; i++)
{
container.AddChild(new PanelContainer
{
PanelOverride = new StyleBoxFlat()
{
BackgroundColor = colorGone,
},
CustomMinimumSize = (10, 15),
});
}
// 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 PanelContainer
{
PanelOverride = new StyleBoxFlat()
{
BackgroundColor = color,
},
CustomMinimumSize = (10, 15),
});
}
}
protected override Vector2 CalculateMinimumSize()
{
return Vector2.ComponentMax((0, 15), base.CalculateMinimumSize());
}
}
}
}

View File

@@ -105,7 +105,6 @@
"SecureEntityStorage", "SecureEntityStorage",
"PresetIdCard", "PresetIdCard",
"SolarControlConsole", "SolarControlConsole",
"BatteryBarrel",
"FlashExplosive", "FlashExplosive",
"FlashProjectile", "FlashProjectile",
"Utensil", "Utensil",

View File

@@ -6,7 +6,10 @@ using Content.Server.GameObjects.Components.Items.Storage;
using Content.Server.GameObjects.Components.Power; using Content.Server.GameObjects.Components.Power;
using Content.Server.GameObjects.Components.Projectiles; using Content.Server.GameObjects.Components.Projectiles;
using Content.Shared.Damage; using Content.Shared.Damage;
using Content.Shared.GameObjects;
using Content.Shared.GameObjects.Components.Weapons.Ranged.Barrels; using Content.Shared.GameObjects.Components.Weapons.Ranged.Barrels;
using Content.Shared.GameObjects.EntitySystems;
using Content.Shared.GameObjects.Verbs;
using Content.Shared.Interfaces.GameObjects.Components; using Content.Shared.Interfaces.GameObjects.Components;
using Robust.Server.GameObjects; using Robust.Server.GameObjects;
using Robust.Server.GameObjects.Components.Container; using Robust.Server.GameObjects.Components.Container;
@@ -25,6 +28,7 @@ namespace Content.Server.GameObjects.Components.Weapon.Ranged.Barrels
public sealed class ServerBatteryBarrelComponent : ServerRangedBarrelComponent public sealed class ServerBatteryBarrelComponent : ServerRangedBarrelComponent
{ {
public override string Name => "BatteryBarrel"; public override string Name => "BatteryBarrel";
public override uint? NetID => ContentNetIDs.BATTERY_BARREL;
// The minimum change we need before we can fire // The minimum change we need before we can fire
[ViewVariables] private float _lowerChargeLimit; [ViewVariables] private float _lowerChargeLimit;
@@ -88,6 +92,15 @@ namespace Content.Server.GameObjects.Components.Weapon.Ranged.Barrels
serializer.DataField(ref _soundPowerCellEject, "soundPowerCellEject", null); serializer.DataField(ref _soundPowerCellEject, "soundPowerCellEject", null);
} }
public override ComponentState GetComponentState()
{
(int, int)? count = (ShotsLeft, Capacity);
return new BatteryBarrelComponentState(
FireRateSelector,
count);
}
public override void Initialize() public override void Initialize()
{ {
base.Initialize(); base.Initialize();
@@ -108,6 +121,7 @@ namespace Content.Server.GameObjects.Components.Weapon.Ranged.Barrels
_appearanceComponent = appearanceComponent; _appearanceComponent = appearanceComponent;
} }
Dirty();
UpdateAppearance(); UpdateAppearance();
} }
@@ -188,8 +202,8 @@ namespace Content.Server.GameObjects.Components.Weapon.Ranged.Barrels
throw new InvalidOperationException("Ammo doesn't have hitscan or projectile?"); throw new InvalidOperationException("Ammo doesn't have hitscan or projectile?");
} }
Dirty();
UpdateAppearance(); UpdateAppearance();
//Dirty();
return entity; return entity;
} }
@@ -211,30 +225,12 @@ namespace Content.Server.GameObjects.Components.Weapon.Ranged.Barrels
} }
_powerCellContainer.Insert(entity); _powerCellContainer.Insert(entity);
Dirty();
UpdateAppearance(); UpdateAppearance();
//Dirty();
return true; return true;
} }
private IEntity RemovePowerCell()
{
if (!_powerCellRemovable || _powerCellContainer.ContainedEntity == null)
{
return null;
}
var entity = _powerCellContainer.ContainedEntity;
_powerCellContainer.Remove(entity);
if (_soundPowerCellEject != null)
{
EntitySystem.Get<AudioSystem>().PlayAtCoords(_soundPowerCellEject, Owner.Transform.GridPosition, AudioParams.Default.WithVolume(-2));
}
UpdateAppearance();
//Dirty();
return entity;
}
public override bool UseEntity(UseEntityEventArgs eventArgs) public override bool UseEntity(UseEntityEventArgs eventArgs)
{ {
if (!_powerCellRemovable) if (!_powerCellRemovable)
@@ -242,22 +238,44 @@ namespace Content.Server.GameObjects.Components.Weapon.Ranged.Barrels
return false; return false;
} }
if (!eventArgs.User.TryGetComponent(out HandsComponent handsComponent) || if (PowerCellEntity == null)
PowerCellEntity == null)
{ {
return false; return false;
} }
var itemComponent = PowerCellEntity.GetComponent<ItemComponent>(); return TryEjectCell(eventArgs.User);
if (!handsComponent.CanPutInHand(itemComponent)) }
private bool TryEjectCell(IEntity user)
{
if (PowerCell == null || !_powerCellRemovable)
{ {
return false; return false;
} }
var powerCell = RemovePowerCell(); if (!user.TryGetComponent(out HandsComponent hands))
handsComponent.PutInHand(itemComponent); {
powerCell.Transform.GridPosition = eventArgs.User.Transform.GridPosition; return false;
}
var cell = PowerCell;
if (!_powerCellContainer.Remove(cell.Owner))
{
return false;
}
Dirty();
UpdateAppearance();
if (!hands.PutInHand(cell.Owner.GetComponent<ItemComponent>()))
{
cell.Owner.Transform.GridPosition = user.Transform.GridPosition;
}
if (_soundPowerCellEject != null)
{
EntitySystem.Get<AudioSystem>().PlayAtCoords(_soundPowerCellEject, Owner.Transform.GridPosition, AudioParams.Default.WithVolume(-2));
}
return true; return true;
} }
@@ -270,5 +288,33 @@ namespace Content.Server.GameObjects.Components.Weapon.Ranged.Barrels
return TryInsertPowerCell(eventArgs.Using); return TryInsertPowerCell(eventArgs.Using);
} }
[Verb]
public sealed class EjectCellVerb : Verb<ServerBatteryBarrelComponent>
{
protected override void GetData(IEntity user, ServerBatteryBarrelComponent component, VerbData data)
{
if (!ActionBlockerSystem.CanInteract(user) || !component._powerCellRemovable)
{
data.Visibility = VerbVisibility.Invisible;
return;
}
if (component.PowerCell == null)
{
data.Text = "Eject cell (cell missing)";
data.Visibility = VerbVisibility.Disabled;
}
else
{
data.Text = "Eject cell";
}
}
protected override void Activate(IEntity user, ServerBatteryBarrelComponent component)
{
component.TryEjectCell(user);
}
}
} }
} }

View File

@@ -0,0 +1,24 @@
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 BatteryBarrelComponentState : ComponentState
{
public FireRateSelector FireRateSelector { get; }
public (int count, int max)? Magazine { get; }
public BatteryBarrelComponentState(
FireRateSelector fireRateSelector,
(int count, int max)? magazine) :
base(ContentNetIDs.BATTERY_BARREL)
{
FireRateSelector = fireRateSelector;
Magazine = magazine;
}
}
}

View File

@@ -70,6 +70,7 @@
public const uint REVOLVER_BARREL = 1064; public const uint REVOLVER_BARREL = 1064;
public const uint CUFFED = 1065; public const uint CUFFED = 1065;
public const uint HANDCUFFS = 1066; public const uint HANDCUFFS = 1066;
public const uint BATTERY_BARREL = 1067;
// Net IDs for integration tests. // Net IDs for integration tests.
public const uint PREDICTION_TEST = 10001; public const uint PREDICTION_TEST = 10001;

View File

@@ -30,6 +30,7 @@
- Single - Single
fireRate: 2 fireRate: 2
powerCellPrototype: PowerCellSmallStandard powerCellPrototype: PowerCellSmallStandard
powerCellRemovable: true
ammoPrototype: RedLaser ammoPrototype: RedLaser
soundGunshot: /Audio/Weapons/Guns/Gunshots/laser.ogg soundGunshot: /Audio/Weapons/Guns/Gunshots/laser.ogg
- type: Appearance - type: Appearance
@@ -71,6 +72,7 @@
angleIncrease: 15 angleIncrease: 15
angleDecay: 45 angleDecay: 45
powerCellPrototype: PowerCellSmallSuper powerCellPrototype: PowerCellSmallSuper
powerCellRemovable: true
ammoPrototype: RedHeavyLaser ammoPrototype: RedHeavyLaser
soundGunshot: /Audio/Weapons/Guns/Gunshots/laser_cannon.ogg soundGunshot: /Audio/Weapons/Guns/Gunshots/laser_cannon.ogg
- type: Appearance - type: Appearance
@@ -112,6 +114,7 @@
angleIncrease: 15 angleIncrease: 15
angleDecay: 45 angleDecay: 45
powerCellPrototype: PowerCellSmallSuper powerCellPrototype: PowerCellSmallSuper
powerCellRemovable: true
base_fire_cost: 600 base_fire_cost: 600
ammoPrototype: XrayLaser ammoPrototype: XrayLaser
soundGunshot: /Audio/Weapons/Guns/Gunshots/laser3.ogg soundGunshot: /Audio/Weapons/Guns/Gunshots/laser3.ogg
@@ -155,6 +158,7 @@
angleIncrease: 20 angleIncrease: 20
angleDecay: 15 angleDecay: 15
powerCellPrototype: PowerCellSmallStandard powerCellPrototype: PowerCellSmallStandard
powerCellRemovable: false
ammoPrototype: BulletTaser ammoPrototype: BulletTaser
soundGunshot: /Audio/Weapons/Guns/Gunshots/taser.ogg soundGunshot: /Audio/Weapons/Guns/Gunshots/taser.ogg
- type: Appearance - type: Appearance