Make BatteryBarrelComponent use item slots. (#5591)

This commit is contained in:
Leon Friedrich
2021-12-05 16:23:47 +13:00
committed by GitHub
parent f96b8a8b0f
commit e8b2d0a844
11 changed files with 184 additions and 334 deletions

View File

@@ -1,6 +1,7 @@
using System; using System;
using Content.Client.Items.Components; using Content.Client.Items.Components;
using Content.Client.Stylesheets; using Content.Client.Stylesheets;
using Content.Shared.Containers.ItemSlots;
using Content.Shared.Weapons.Ranged.Barrels.Components; using Content.Shared.Weapons.Ranged.Barrels.Components;
using Robust.Client.Graphics; using Robust.Client.Graphics;
using Robust.Client.UserInterface; using Robust.Client.UserInterface;
@@ -8,6 +9,7 @@ using Robust.Client.UserInterface.Controls;
using Robust.Shared.GameObjects; using Robust.Shared.GameObjects;
using Robust.Shared.GameStates; using Robust.Shared.GameStates;
using Robust.Shared.Maths; using Robust.Shared.Maths;
using Robust.Shared.Serialization.Manager.Attributes;
using Robust.Shared.ViewVariables; using Robust.Shared.ViewVariables;
using static Robust.Client.UserInterface.Controls.BoxContainer; using static Robust.Client.UserInterface.Controls.BoxContainer;
@@ -21,6 +23,9 @@ namespace Content.Client.Weapons.Ranged.Barrels.Components
private StatusControl? _statusControl; private StatusControl? _statusControl;
[DataField("cellSlot", required: true)]
public ItemSlot CellSlot = default!;
/// <summary> /// <summary>
/// Count of bullets in the magazine. /// Count of bullets in the magazine.
/// </summary> /// </summary>
@@ -30,6 +35,18 @@ namespace Content.Client.Weapons.Ranged.Barrels.Components
[ViewVariables] [ViewVariables]
public (int count, int max)? MagazineCount { get; private set; } public (int count, int max)? MagazineCount { get; private set; }
protected override void Initialize()
{
base.Initialize();
EntitySystem.Get<ItemSlotsSystem>().AddItemSlot(OwnerUid, $"{Name}-powercell-container", CellSlot);
}
protected override void OnRemove()
{
base.OnRemove();
EntitySystem.Get<ItemSlotsSystem>().RemoveItemSlot(OwnerUid, CellSlot);
}
public override void HandleComponentState(ComponentState? curState, ComponentState? nextState) public override void HandleComponentState(ComponentState? curState, ComponentState? nextState)
{ {
base.HandleComponentState(curState, nextState); base.HandleComponentState(curState, nextState);

View File

@@ -3,9 +3,11 @@ using Content.Server.Weapon.Ranged.Barrels.Components;
using Content.Shared.ActionBlocker; using Content.Shared.ActionBlocker;
using Content.Shared.Popups; using Content.Shared.Popups;
using Content.Shared.Verbs; using Content.Shared.Verbs;
using Robust.Shared.Containers;
using Robust.Shared.GameObjects; using Robust.Shared.GameObjects;
using Robust.Shared.IoC; using Robust.Shared.IoC;
using Robust.Shared.Localization; using Robust.Shared.Localization;
using System;
namespace Content.Server.Weapon.Ranged.Barrels namespace Content.Server.Weapon.Ranged.Barrels
{ {
@@ -19,8 +21,8 @@ namespace Content.Server.Weapon.Ranged.Barrels
SubscribeLocalEvent<RevolverBarrelComponent, GetAlternativeVerbsEvent>(AddSpinVerb); SubscribeLocalEvent<RevolverBarrelComponent, GetAlternativeVerbsEvent>(AddSpinVerb);
SubscribeLocalEvent<ServerBatteryBarrelComponent, GetAlternativeVerbsEvent>(AddEjectCellVerb); SubscribeLocalEvent<ServerBatteryBarrelComponent, EntInsertedIntoContainerMessage>(OnCellSlotUpdated);
SubscribeLocalEvent<ServerBatteryBarrelComponent, GetInteractionVerbsEvent>(AddInsertCellVerb); SubscribeLocalEvent<ServerBatteryBarrelComponent, EntRemovedFromContainerMessage>(OnCellSlotUpdated);
SubscribeLocalEvent<BoltActionBarrelComponent, GetInteractionVerbsEvent>(AddToggleBoltVerb); SubscribeLocalEvent<BoltActionBarrelComponent, GetInteractionVerbsEvent>(AddToggleBoltVerb);
@@ -28,6 +30,12 @@ namespace Content.Server.Weapon.Ranged.Barrels
SubscribeLocalEvent<ServerMagazineBarrelComponent, GetAlternativeVerbsEvent>(AddEjectMagazineVerb); SubscribeLocalEvent<ServerMagazineBarrelComponent, GetAlternativeVerbsEvent>(AddEjectMagazineVerb);
} }
private void OnCellSlotUpdated(EntityUid uid, ServerBatteryBarrelComponent component, ContainerModifiedMessage args)
{
if (args.Container.ID == component.CellSlot.ID)
component.UpdateAppearance();
}
private void AddSpinVerb(EntityUid uid, RevolverBarrelComponent component, GetAlternativeVerbsEvent args) private void AddSpinVerb(EntityUid uid, RevolverBarrelComponent component, GetAlternativeVerbsEvent args)
{ {
if (args.Hands == null || !args.CanAccess || !args.CanInteract) if (args.Hands == null || !args.CanAccess || !args.CanInteract)
@@ -62,42 +70,6 @@ namespace Content.Server.Weapon.Ranged.Barrels
args.Verbs.Add(verb); args.Verbs.Add(verb);
} }
// TODO VERBS EJECTABLES Standardize eject/insert verbs into a single system?
// Really, why isn't this just PowerCellSlotComponent?
private void AddEjectCellVerb(EntityUid uid, ServerBatteryBarrelComponent component, GetAlternativeVerbsEvent args)
{
if (args.Hands == null ||
!args.CanAccess ||
!args.CanInteract ||
!component.PowerCellRemovable ||
component.PowerCell == null ||
!_actionBlockerSystem.CanPickup(args.User.Uid))
return;
Verb verb = new();
verb.Text = component.PowerCell.Owner.Name;
verb.Category = VerbCategory.Eject;
verb.Act = () => component.TryEjectCell(args.User);
args.Verbs.Add(verb);
}
private void AddInsertCellVerb(EntityUid uid, ServerBatteryBarrelComponent component, GetInteractionVerbsEvent args)
{
if (args.Using == null ||
!args.CanAccess ||
!args.CanInteract ||
component.PowerCell != null ||
!args.Using.HasComponent<BatteryComponent>() ||
!_actionBlockerSystem.CanDrop(args.User.Uid))
return;
Verb verb = new();
verb.Text = args.Using.Name;
verb.Category = VerbCategory.Insert;
verb.Act = () => component.TryInsertPowerCell(args.Using);
args.Verbs.Add(verb);
}
private void AddEjectMagazineVerb(EntityUid uid, ServerMagazineBarrelComponent component, GetAlternativeVerbsEvent args) private void AddEjectMagazineVerb(EntityUid uid, ServerMagazineBarrelComponent component, GetAlternativeVerbsEvent args)
{ {
if (args.Hands == null || if (args.Hands == null ||

View File

@@ -26,7 +26,7 @@ namespace Content.Server.Weapon.Ranged.Barrels.Components
[RegisterComponent] [RegisterComponent]
[NetworkedComponent()] [NetworkedComponent()]
#pragma warning disable 618 #pragma warning disable 618
public sealed class BoltActionBarrelComponent : ServerRangedBarrelComponent, IMapInit, IExamine public sealed class BoltActionBarrelComponent : ServerRangedBarrelComponent, IUse, IInteractUsing, IMapInit, IExamine
#pragma warning restore 618 #pragma warning restore 618
{ {
// Originally I had this logic shared with PumpBarrel and used a couple of variables to control things // Originally I had this logic shared with PumpBarrel and used a couple of variables to control things
@@ -267,7 +267,7 @@ namespace Content.Server.Weapon.Ranged.Barrels.Components
return false; return false;
} }
public override bool UseEntity(UseEntityEventArgs eventArgs) public bool UseEntity(UseEntityEventArgs eventArgs)
{ {
if (BoltOpen) if (BoltOpen)
{ {
@@ -281,7 +281,7 @@ namespace Content.Server.Weapon.Ranged.Barrels.Components
return true; return true;
} }
public override async Task<bool> InteractUsing(InteractUsingEventArgs eventArgs) public async Task<bool> InteractUsing(InteractUsingEventArgs eventArgs)
{ {
return TryInsertBullet(eventArgs.User, eventArgs.Using); return TryInsertBullet(eventArgs.User, eventArgs.Using);
} }

View File

@@ -25,7 +25,7 @@ namespace Content.Server.Weapon.Ranged.Barrels.Components
/// </summary> /// </summary>
[RegisterComponent] [RegisterComponent]
[NetworkedComponent()] [NetworkedComponent()]
public sealed class PumpBarrelComponent : ServerRangedBarrelComponent, IMapInit, ISerializationHooks public sealed class PumpBarrelComponent : ServerRangedBarrelComponent, IUse, IInteractUsing, IMapInit, ISerializationHooks
{ {
public override string Name => "PumpBarrel"; public override string Name => "PumpBarrel";
@@ -223,13 +223,13 @@ namespace Content.Server.Weapon.Ranged.Barrels.Components
return false; return false;
} }
public override bool UseEntity(UseEntityEventArgs eventArgs) public bool UseEntity(UseEntityEventArgs eventArgs)
{ {
Cycle(true); Cycle(true);
return true; return true;
} }
public override async Task<bool> InteractUsing(InteractUsingEventArgs eventArgs) public async Task<bool> InteractUsing(InteractUsingEventArgs eventArgs)
{ {
return TryInsertBullet(eventArgs); return TryInsertBullet(eventArgs);
} }

View File

@@ -23,7 +23,7 @@ namespace Content.Server.Weapon.Ranged.Barrels.Components
{ {
[RegisterComponent] [RegisterComponent]
[NetworkedComponent()] [NetworkedComponent()]
public sealed class RevolverBarrelComponent : ServerRangedBarrelComponent, ISerializationHooks public sealed class RevolverBarrelComponent : ServerRangedBarrelComponent, IUse, IInteractUsing, ISerializationHooks
{ {
[Dependency] private readonly IRobustRandom _random = default!; [Dependency] private readonly IRobustRandom _random = default!;
@@ -253,7 +253,7 @@ namespace Content.Server.Weapon.Ranged.Barrels.Components
/// <param name="eventArgs"></param> /// <param name="eventArgs"></param>
/// <returns></returns> /// <returns></returns>
/// <exception cref="NotImplementedException"></exception> /// <exception cref="NotImplementedException"></exception>
public override bool UseEntity(UseEntityEventArgs eventArgs) public bool UseEntity(UseEntityEventArgs eventArgs)
{ {
EjectAllSlots(); EjectAllSlots();
Dirty(); Dirty();
@@ -261,7 +261,7 @@ namespace Content.Server.Weapon.Ranged.Barrels.Components
return true; return true;
} }
public override async Task<bool> InteractUsing(InteractUsingEventArgs eventArgs) public async Task<bool> InteractUsing(InteractUsingEventArgs eventArgs)
{ {
return TryInsertBullet(eventArgs.User, eventArgs.Using); return TryInsertBullet(eventArgs.User, eventArgs.Using);
} }

View File

@@ -1,18 +1,12 @@
using System; using System;
using System.Threading.Tasks;
using Content.Server.Hands.Components;
using Content.Server.Items;
using Content.Server.Power.Components; using Content.Server.Power.Components;
using Content.Server.Projectiles.Components; using Content.Server.Projectiles.Components;
using Content.Shared.Interaction; using Content.Shared.Containers.ItemSlots;
using Content.Shared.Sound;
using Content.Shared.Weapons.Ranged.Barrels.Components; using Content.Shared.Weapons.Ranged.Barrels.Components;
using Robust.Shared.Audio;
using Robust.Shared.Containers; using Robust.Shared.Containers;
using Robust.Shared.GameObjects; using Robust.Shared.GameObjects;
using Robust.Shared.GameStates; using Robust.Shared.GameStates;
using Robust.Shared.Map; using Robust.Shared.Map;
using Robust.Shared.Player;
using Robust.Shared.Players; using Robust.Shared.Players;
using Robust.Shared.Serialization.Manager.Attributes; using Robust.Shared.Serialization.Manager.Attributes;
using Robust.Shared.ViewVariables; using Robust.Shared.ViewVariables;
@@ -25,6 +19,9 @@ namespace Content.Server.Weapon.Ranged.Barrels.Components
{ {
public override string Name => "BatteryBarrel"; public override string Name => "BatteryBarrel";
[DataField("cellSlot", required: true)]
public ItemSlot CellSlot = new();
// The minimum change we need before we can fire // The minimum change we need before we can fire
[DataField("lowerChargeLimit")] [DataField("lowerChargeLimit")]
[ViewVariables] private float _lowerChargeLimit = 10; [ViewVariables] private float _lowerChargeLimit = 10;
@@ -34,20 +31,14 @@ namespace Content.Server.Weapon.Ranged.Barrels.Components
[DataField("ammoPrototype")] [DataField("ammoPrototype")]
[ViewVariables] private string? _ammoPrototype; [ViewVariables] private string? _ammoPrototype;
[ViewVariables] public IEntity? PowerCellEntity => _powerCellContainer.ContainedEntity; public BatteryComponent? PowerCell => CellSlot.Item?.GetComponentOrNull<BatteryComponent>();
public BatteryComponent? PowerCell => _powerCellContainer.ContainedEntity?.GetComponentOrNull<BatteryComponent>();
private ContainerSlot _powerCellContainer = default!;
private ContainerSlot _ammoContainer = default!; private ContainerSlot _ammoContainer = default!;
[DataField("powerCellPrototype")]
private string? _powerCellPrototype = default;
[DataField("powerCellRemovable")]
[ViewVariables] public bool PowerCellRemovable = default;
public override int ShotsLeft public override int ShotsLeft
{ {
get get
{ {
var powerCell = _powerCellContainer.ContainedEntity; var powerCell = CellSlot.Item;
if (powerCell == null) if (powerCell == null)
{ {
@@ -62,7 +53,7 @@ namespace Content.Server.Weapon.Ranged.Barrels.Components
{ {
get get
{ {
var powerCell = _powerCellContainer.ContainedEntity; var powerCell = CellSlot.Item;
if (powerCell == null) if (powerCell == null)
{ {
@@ -75,12 +66,6 @@ namespace Content.Server.Weapon.Ranged.Barrels.Components
private AppearanceComponent? _appearanceComponent; private AppearanceComponent? _appearanceComponent;
// Sounds
[DataField("soundPowerCellInsert", required: true)]
private SoundSpecifier _soundPowerCellInsert = default!;
[DataField("soundPowerCellEject", required: true)]
private SoundSpecifier _soundPowerCellEject = default!;
public override ComponentState GetComponentState() public override ComponentState GetComponentState()
{ {
(int, int)? count = (ShotsLeft, Capacity); (int, int)? count = (ShotsLeft, Capacity);
@@ -93,12 +78,7 @@ namespace Content.Server.Weapon.Ranged.Barrels.Components
protected override void Initialize() protected override void Initialize()
{ {
base.Initialize(); base.Initialize();
_powerCellContainer = ContainerHelpers.EnsureContainer<ContainerSlot>(Owner, $"{Name}-powercell-container", out var existing); EntitySystem.Get<ItemSlotsSystem>().AddItemSlot(OwnerUid, $"{Name}-powercell-container", CellSlot);
if (!existing && _powerCellPrototype != null)
{
var powerCellEntity = Owner.EntityManager.SpawnEntity(_powerCellPrototype, Owner.Transform.Coordinates);
_powerCellContainer.Insert(powerCellEntity);
}
if (_ammoPrototype != null) if (_ammoPrototype != null)
{ {
@@ -112,6 +92,12 @@ namespace Content.Server.Weapon.Ranged.Barrels.Components
Dirty(); Dirty();
} }
protected override void OnRemove()
{
base.OnRemove();
EntitySystem.Get<ItemSlotsSystem>().RemoveItemSlot(OwnerUid, CellSlot);
}
protected override void Startup() protected override void Startup()
{ {
base.Startup(); base.Startup();
@@ -120,7 +106,7 @@ namespace Content.Server.Weapon.Ranged.Barrels.Components
public void UpdateAppearance() public void UpdateAppearance()
{ {
_appearanceComponent?.SetData(MagazineBarrelVisuals.MagLoaded, _powerCellContainer.ContainedEntity != null); _appearanceComponent?.SetData(MagazineBarrelVisuals.MagLoaded, CellSlot.HasItem);
_appearanceComponent?.SetData(AmmoVisuals.AmmoCount, ShotsLeft); _appearanceComponent?.SetData(AmmoVisuals.AmmoCount, ShotsLeft);
_appearanceComponent?.SetData(AmmoVisuals.AmmoMax, Capacity); _appearanceComponent?.SetData(AmmoVisuals.AmmoMax, Capacity);
Dirty(); Dirty();
@@ -142,7 +128,7 @@ namespace Content.Server.Weapon.Ranged.Barrels.Components
public override IEntity? TakeProjectile(EntityCoordinates spawnAt) public override IEntity? TakeProjectile(EntityCoordinates spawnAt)
{ {
var powerCellEntity = _powerCellContainer.ContainedEntity; var powerCellEntity = CellSlot.Item;
if (powerCellEntity == null) if (powerCellEntity == null)
{ {
@@ -197,81 +183,5 @@ namespace Content.Server.Weapon.Ranged.Barrels.Components
UpdateAppearance(); UpdateAppearance();
return entity; return entity;
} }
public bool TryInsertPowerCell(IEntity entity)
{
if (_powerCellContainer.ContainedEntity != null)
{
return false;
}
if (!entity.HasComponent<BatteryComponent>())
{
return false;
}
SoundSystem.Play(Filter.Pvs(Owner), _soundPowerCellInsert.GetSound(), Owner, AudioParams.Default.WithVolume(-2));
_powerCellContainer.Insert(entity);
Dirty();
UpdateAppearance();
return true;
}
public override bool UseEntity(UseEntityEventArgs eventArgs)
{
if (!PowerCellRemovable)
{
return false;
}
if (PowerCellEntity == null)
{
return false;
}
return TryEjectCell(eventArgs.User);
}
public bool TryEjectCell(IEntity user)
{
if (PowerCell == null || !PowerCellRemovable)
{
return false;
}
if (!user.TryGetComponent(out HandsComponent? hands))
{
return false;
}
var cell = PowerCell;
if (!_powerCellContainer.Remove(cell.Owner))
{
return false;
}
Dirty();
UpdateAppearance();
if (!hands.PutInHand(cell.Owner.GetComponent<ItemComponent>()))
{
cell.Owner.Transform.Coordinates = user.Transform.Coordinates;
}
SoundSystem.Play(Filter.Pvs(Owner), _soundPowerCellEject.GetSound(), Owner, AudioParams.Default.WithVolume(-2));
return true;
}
public override async Task<bool> InteractUsing(InteractUsingEventArgs eventArgs)
{
if (!eventArgs.Using.HasComponent<BatteryComponent>())
{
return false;
}
return TryInsertPowerCell(eventArgs.Using);
}
} }
} }

View File

@@ -27,7 +27,7 @@ namespace Content.Server.Weapon.Ranged.Barrels.Components
[RegisterComponent] [RegisterComponent]
[NetworkedComponent()] [NetworkedComponent()]
#pragma warning disable 618 #pragma warning disable 618
public sealed class ServerMagazineBarrelComponent : ServerRangedBarrelComponent, IExamine public sealed class ServerMagazineBarrelComponent : ServerRangedBarrelComponent, IUse, IInteractUsing, IExamine
#pragma warning restore 618 #pragma warning restore 618
{ {
public override string Name => "MagazineBarrel"; public override string Name => "MagazineBarrel";
@@ -248,7 +248,7 @@ namespace Content.Server.Weapon.Ranged.Barrels.Components
_appearanceComponent?.SetData(AmmoVisuals.AmmoMax, Capacity); _appearanceComponent?.SetData(AmmoVisuals.AmmoMax, Capacity);
} }
public override bool UseEntity(UseEntityEventArgs eventArgs) public bool UseEntity(UseEntityEventArgs eventArgs)
{ {
// Behavior: // Behavior:
// If bolt open just close it // If bolt open just close it
@@ -393,7 +393,7 @@ namespace Content.Server.Weapon.Ranged.Barrels.Components
UpdateAppearance(); UpdateAppearance();
} }
public override async Task<bool> InteractUsing(InteractUsingEventArgs eventArgs) public async Task<bool> InteractUsing(InteractUsingEventArgs eventArgs)
{ {
if (CanInsertMagazine(eventArgs.User, eventArgs.Using, quiet: false)) if (CanInsertMagazine(eventArgs.User, eventArgs.Using, quiet: false))
{ {

View File

@@ -38,7 +38,7 @@ namespace Content.Server.Weapon.Ranged.Barrels.Components
/// Only difference between them is how they retrieve a projectile to shoot (battery, magazine, etc.) /// Only difference between them is how they retrieve a projectile to shoot (battery, magazine, etc.)
/// </summary> /// </summary>
#pragma warning disable 618 #pragma warning disable 618
public abstract class ServerRangedBarrelComponent : SharedRangedBarrelComponent, IUse, IInteractUsing, IExamine, ISerializationHooks public abstract class ServerRangedBarrelComponent : SharedRangedBarrelComponent, IExamine, ISerializationHooks
#pragma warning restore 618 #pragma warning restore 618
{ {
// There's still some of py01 and PJB's work left over, especially in underlying shooting logic, // There's still some of py01 and PJB's work left over, especially in underlying shooting logic,
@@ -135,9 +135,9 @@ namespace Content.Server.Weapon.Ranged.Barrels.Components
} }
} }
protected override void OnAdd() protected override void Initialize()
{ {
base.OnAdd(); base.Initialize();
Owner.EnsureComponentWarn(out ServerRangedWeaponComponent rangedWeaponComponent); Owner.EnsureComponentWarn(out ServerRangedWeaponComponent rangedWeaponComponent);
@@ -169,10 +169,6 @@ namespace Content.Server.Weapon.Ranged.Barrels.Components
return angle; return angle;
} }
public abstract bool UseEntity(UseEntityEventArgs eventArgs);
public abstract Task<bool> InteractUsing(InteractUsingEventArgs eventArgs);
public void ChangeFireSelector(FireRateSelector rateSelector) public void ChangeFireSelector(FireRateSelector rateSelector)
{ {
if ((rateSelector & AllRateSelectors) != 0) if ((rateSelector & AllRateSelectors) != 0)

View File

@@ -1,6 +1,7 @@
using Content.Shared.Sound; using Content.Shared.Sound;
using Content.Shared.Whitelist; using Content.Shared.Whitelist;
using Robust.Shared.Analyzers; using Robust.Shared.Analyzers;
using Robust.Shared.Audio;
using Robust.Shared.Containers; using Robust.Shared.Containers;
using Robust.Shared.GameObjects; using Robust.Shared.GameObjects;
using Robust.Shared.Prototypes; using Robust.Shared.Prototypes;
@@ -81,6 +82,12 @@ namespace Content.Shared.Containers.ItemSlots
public SoundSpecifier? EjectSound; public SoundSpecifier? EjectSound;
// maybe default to /Audio/Machines/id_swipe.ogg? // maybe default to /Audio/Machines/id_swipe.ogg?
/// <summary>
/// Options used for playing the insert/eject sounds.
/// </summary>
[DataField("soundOptions")]
public AudioParams SoundOptions = AudioParams.Default;
/// <summary> /// <summary>
/// The name of this item slot. This will be shown to the user in the verb menu. /// The name of this item slot. This will be shown to the user in the verb menu.
/// </summary> /// </summary>
@@ -116,6 +123,18 @@ namespace Content.Shared.Containers.ItemSlots
[DataField("ejectOnInteract")] [DataField("ejectOnInteract")]
public bool EjectOnInteract = false; public bool EjectOnInteract = false;
/// <summary>
/// If true, and if this slot is attached to an item, then it will attempt to eject slot when to the slot is
/// used in the user's hands.
/// </summary>
/// <remarks>
/// Desirable for things like ranged weapons ('Z' to eject), but not desirable for others (e.g., PDA uses
/// 'Z' to open UI). Unlike <see cref="EjectOnInteract"/>, this will not make any changes to the context
/// menu, nor will it disable alt-click interactions.
/// </remarks>
[DataField("ejectOnUse")]
public bool EjectOnUse = false;
/// <summary> /// <summary>
/// Override the insert verb text. Defaults to [insert category] -> [item-name]. If not null, the verb will /// Override the insert verb text. Defaults to [insert category] -> [item-name]. If not null, the verb will
/// not be given a category. /// not be given a category.

View File

@@ -34,6 +34,7 @@ namespace Content.Shared.Containers.ItemSlots
SubscribeLocalEvent<ItemSlotsComponent, InteractUsingEvent>(OnInteractUsing); SubscribeLocalEvent<ItemSlotsComponent, InteractUsingEvent>(OnInteractUsing);
SubscribeLocalEvent<ItemSlotsComponent, InteractHandEvent>(OnInteractHand); SubscribeLocalEvent<ItemSlotsComponent, InteractHandEvent>(OnInteractHand);
SubscribeLocalEvent<ItemSlotsComponent, UseInHandEvent>(OnUseInHand);
SubscribeLocalEvent<ItemSlotsComponent, GetAlternativeVerbsEvent>(AddEjectVerbs); SubscribeLocalEvent<ItemSlotsComponent, GetAlternativeVerbsEvent>(AddEjectVerbs);
SubscribeLocalEvent<ItemSlotsComponent, GetInteractionVerbsEvent>(AddInteractionVerbsVerbs); SubscribeLocalEvent<ItemSlotsComponent, GetInteractionVerbsEvent>(AddInteractionVerbsVerbs);
@@ -125,6 +126,25 @@ namespace Content.Shared.Containers.ItemSlots
} }
} }
/// <summary>
/// Attempt to eject an item from the first valid item slot.
/// </summary>
private void OnUseInHand(EntityUid uid, ItemSlotsComponent itemSlots, UseInHandEvent args)
{
if (args.Handled)
return;
foreach (var slot in itemSlots.Slots.Values)
{
if (slot.Locked || !slot.EjectOnUse || slot.Item == null)
continue;
args.Handled = true;
TryEjectToHands(uid, slot, args.UserUid);
break;
}
}
/// <summary> /// <summary>
/// Tries to insert a held item in any fitting item slot. If a valid slot already contains an item, it will /// Tries to insert a held item in any fitting item slot. If a valid slot already contains an item, it will
/// swap it out and place the old one in the user's hand. /// swap it out and place the old one in the user's hand.
@@ -172,7 +192,7 @@ namespace Content.Shared.Containers.ItemSlots
// ContainerSlot automatically raises a directed EntInsertedIntoContainerMessage // ContainerSlot automatically raises a directed EntInsertedIntoContainerMessage
if (slot.InsertSound != null) if (slot.InsertSound != null)
SoundSystem.Play(Filter.Pvs(uid), slot.InsertSound.GetSound(), uid); SoundSystem.Play(Filter.Pvs(uid), slot.InsertSound.GetSound(), uid, slot.SoundOptions);
} }
/// <summary> /// <summary>
@@ -267,7 +287,7 @@ namespace Content.Shared.Containers.ItemSlots
// ContainerSlot automatically raises a directed EntRemovedFromContainerMessage // ContainerSlot automatically raises a directed EntRemovedFromContainerMessage
if (slot.EjectSound != null) if (slot.EjectSound != null)
SoundSystem.Play(Filter.Pvs(uid), slot.EjectSound.GetSound(), uid); SoundSystem.Play(Filter.Pvs(uid), slot.EjectSound.GetSound(), uid, slot.SoundOptions);
} }
/// <summary> /// <summary>
@@ -317,7 +337,7 @@ namespace Content.Shared.Containers.ItemSlots
return false; return false;
if (user != null && EntityManager.TryGetComponent(user.Value, out SharedHandsComponent? hands)) if (user != null && EntityManager.TryGetComponent(user.Value, out SharedHandsComponent? hands))
hands.TryPutInAnyHand(item); hands.TryPutInActiveHandOrAny(item);
return true; return true;
} }

View File

@@ -1,6 +1,41 @@
- type: entity
id: BatteryGunBase
parent: BaseItem
abstract: true
components:
- type: RangedWeapon
- type: BatteryBarrel
fireRate: 2
minAngle: 0
maxAngle: 45
angleIncrease: 15
angleDecay: 45
ammoPrototype: RedLaser
currentSelector: Single
allSelectors:
- Single
soundGunshot:
path: /Audio/Weapons/Guns/Gunshots/laser.ogg
cellSlot:
ejectOnUse: true
insertSound: /Audio/Weapons/Guns/MagIn/revolver_magin.ogg
ejectSound: /Audio/Weapons/Guns/MagOut/revolver_magout.ogg
soundOptions:
volume: -2
startingItem: PowerCellSmallStandard
whitelist:
components:
- Battery
- type: Appearance
visuals:
- type: MagVisualizer
magState: mag
steps: 5
zeroVisible: false
- type: entity - type: entity
name: retro laser gun name: retro laser gun
parent: BaseItem parent: BatteryGunBase
id: LaserGun id: LaserGun
description: A weapon using light amplified by the stimulated emission of radiation. description: A weapon using light amplified by the stimulated emission of radiation.
components: components:
@@ -19,23 +54,7 @@
- type: RangedWeapon - type: RangedWeapon
- type: BatteryBarrel - type: BatteryBarrel
minAngle: 10 minAngle: 10
maxAngle: 45
angleIncrease: 15
angleDecay: 45
currentSelector: Single
allSelectors:
- Single
fireRate: 2
powerCellPrototype: PowerCellSmallStandard
powerCellRemovable: true
fireCost: 40 fireCost: 40
ammoPrototype: RedLaser
soundGunshot:
path: /Audio/Weapons/Guns/Gunshots/laser.ogg
soundPowerCellInsert:
path: /Audio/Weapons/Guns/MagIn/revolver_magin.ogg
soundPowerCellEject:
path: /Audio/Weapons/Guns/MagOut/revolver_magout.ogg
- type: Appearance - type: Appearance
visuals: visuals:
- type: MagVisualizer - type: MagVisualizer
@@ -45,7 +64,7 @@
- type: entity - type: entity
name: makeshift laser gun name: makeshift laser gun
parent: BaseItem parent: BatteryGunBase
id: MakeshiftLaser id: MakeshiftLaser
description: Better pray it won't burn your hands off. description: Better pray it won't burn your hands off.
components: components:
@@ -61,36 +80,12 @@
- type: Item - type: Item
size: 24 size: 24
sprite: Objects/Weapons/Guns/Battery/makeshift.rsi sprite: Objects/Weapons/Guns/Battery/makeshift.rsi
- type: RangedWeapon
- type: BatteryBarrel - type: BatteryBarrel
currentSelector: Single
allSelectors:
- Single
fireRate: 2
minAngle: 0
maxAngle: 45
angleIncrease: 15
angleDecay: 45
powerCellPrototype: PowerCellSmallStandard
powerCellRemovable: true
fireCost: 40 fireCost: 40
ammoPrototype: RedLaser
soundGunshot:
path: /Audio/Weapons/Guns/Gunshots/laser.ogg
soundPowerCellInsert:
path: /Audio/Weapons/Guns/MagIn/revolver_magin.ogg
soundPowerCellEject:
path: /Audio/Weapons/Guns/MagOut/revolver_magout.ogg
- type: Appearance
visuals:
- type: MagVisualizer
magState: mag
steps: 5
zeroVisible: false
- type: entity - type: entity
name: svalinn laser pistol name: svalinn laser pistol
parent: BaseItem parent: BatteryGunBase
id: LaserPistolSvalinn id: LaserPistolSvalinn
description: A cheap and widely used laser pistol. description: A cheap and widely used laser pistol.
components: components:
@@ -106,36 +101,12 @@
- type: Item - type: Item
size: 24 size: 24
sprite: Objects/Weapons/Guns/Battery/svalinn.rsi sprite: Objects/Weapons/Guns/Battery/svalinn.rsi
- type: RangedWeapon
- type: BatteryBarrel - type: BatteryBarrel
currentSelector: Single
allSelectors:
- Single
fireRate: 2
minAngle: 0
maxAngle: 45
angleIncrease: 15
angleDecay: 45
powerCellPrototype: PowerCellSmallStandard
powerCellRemovable: true
fireCost: 20 fireCost: 20
ammoPrototype: RedLaser
soundGunshot:
path: /Audio/Weapons/Guns/Gunshots/laser.ogg
soundPowerCellInsert:
path: /Audio/Weapons/Guns/MagIn/revolver_magin.ogg
soundPowerCellEject:
path: /Audio/Weapons/Guns/MagOut/revolver_magout.ogg
- type: Appearance
visuals:
- type: MagVisualizer
magState: mag
steps: 5
zeroVisible: false
- type: entity - type: entity
name: cog laser carbine name: cog laser carbine
parent: BaseItem parent: BatteryGunBase
id: LaserRifleCog id: LaserRifleCog
description: Favoured by Nanotrasen Security for being cheap and easy to use. description: Favoured by Nanotrasen Security for being cheap and easy to use.
components: components:
@@ -151,36 +122,12 @@
- type: Item - type: Item
size: 24 size: 24
sprite: Objects/Weapons/Guns/Battery/cog.rsi sprite: Objects/Weapons/Guns/Battery/cog.rsi
- type: RangedWeapon
- type: BatteryBarrel - type: BatteryBarrel
currentSelector: Single
allSelectors:
- Single
fireRate: 2
minAngle: 0
maxAngle: 45
angleIncrease: 15
angleDecay: 45
powerCellPrototype: PowerCellSmallStandard
powerCellRemovable: true
fireCost: 10 fireCost: 10
ammoPrototype: RedLaser
soundGunshot:
path: /Audio/Weapons/Guns/Gunshots/laser.ogg
soundPowerCellInsert:
path: /Audio/Weapons/Guns/MagIn/revolver_magin.ogg
soundPowerCellEject:
path: /Audio/Weapons/Guns/MagOut/revolver_magout.ogg
- type: Appearance
visuals:
- type: MagVisualizer
magState: mag
steps: 5
zeroVisible: false
- type: entity - type: entity
name: laser cannon name: laser cannon
parent: BaseItem parent: BatteryGunBase
id: LaserCannon id: LaserCannon
description: A heavy duty, high powered laser weapon. description: A heavy duty, high powered laser weapon.
components: components:
@@ -196,36 +143,25 @@
- type: Item - type: Item
size: 24 size: 24
sprite: Objects/Weapons/Guns/Battery/laser_cannon.rsi sprite: Objects/Weapons/Guns/Battery/laser_cannon.rsi
- type: RangedWeapon
- type: BatteryBarrel - type: BatteryBarrel
currentSelector: Single
allSelectors:
- Single
fireRate: 2
minAngle: 0
maxAngle: 45
angleIncrease: 15
angleDecay: 45
powerCellPrototype: PowerCellSmallSuper
powerCellRemovable: true
fireCost: 600 fireCost: 600
ammoPrototype: RedHeavyLaser ammoPrototype: RedHeavyLaser
soundGunshot: soundGunshot:
path: /Audio/Weapons/Guns/Gunshots/laser_cannon.ogg path: /Audio/Weapons/Guns/Gunshots/laser_cannon.ogg
soundPowerCellInsert: cellSlot:
path: /Audio/Weapons/Guns/MagIn/revolver_magin.ogg ejectOnUse: true
soundPowerCellEject: insertSound: /Audio/Weapons/Guns/MagIn/revolver_magin.ogg
path: /Audio/Weapons/Guns/MagOut/revolver_magout.ogg ejectSound: /Audio/Weapons/Guns/MagOut/revolver_magout.ogg
- type: Appearance soundOptions:
visuals: volume: -2
- type: MagVisualizer startingItem: PowerCellSmallSuper
magState: mag whitelist:
steps: 5 components:
zeroVisible: false - Battery
- type: entity - type: entity
name: x-ray cannon name: x-ray cannon
parent: BaseItem parent: BatteryGunBase
id: XrayCannon id: XrayCannon
description: An experimental weapon that uses concentrated x-ray energy against its target. description: An experimental weapon that uses concentrated x-ray energy against its target.
components: components:
@@ -241,26 +177,21 @@
- type: Item - type: Item
size: 24 size: 24
sprite: Objects/Weapons/Guns/Battery/xray.rsi sprite: Objects/Weapons/Guns/Battery/xray.rsi
- type: RangedWeapon
- type: BatteryBarrel - type: BatteryBarrel
currentSelector: Single
allSelectors:
- Single
fireRate: 2
minAngle: 0
maxAngle: 45
angleIncrease: 15
angleDecay: 45
powerCellPrototype: PowerCellSmallSuper
powerCellRemovable: true
fireCost: 600 fireCost: 600
ammoPrototype: XrayLaser ammoPrototype: XrayLaser
soundGunshot: soundGunshot:
path: /Audio/Weapons/Guns/Gunshots/laser3.ogg path: /Audio/Weapons/Guns/Gunshots/laser3.ogg
soundPowerCellInsert: cellSlot:
path: /Audio/Weapons/Guns/MagIn/revolver_magin.ogg ejectOnUse: true
soundPowerCellEject: insertSound: /Audio/Weapons/Guns/MagIn/revolver_magin.ogg
path: /Audio/Weapons/Guns/MagOut/revolver_magout.ogg ejectSound: /Audio/Weapons/Guns/MagOut/revolver_magout.ogg
soundOptions:
volume: -2
startingItem: PowerCellSmallSuper
whitelist:
components:
- Battery
- type: Appearance - type: Appearance
visuals: visuals:
- type: MagVisualizer - type: MagVisualizer
@@ -270,7 +201,7 @@
- type: entity - type: entity
name: taser name: taser
parent: BaseItem parent: BatteryGunBase
id: TaserGun id: TaserGun
description: A low-capacity, energy-based stun gun used by security teams to subdue targets at range. description: A low-capacity, energy-based stun gun used by security teams to subdue targets at range.
components: components:
@@ -293,26 +224,23 @@
Slots: Slots:
- Belt - Belt
HeldPrefix: taser4 HeldPrefix: taser4
- type: RangedWeapon
- type: BatteryBarrel - type: BatteryBarrel
currentSelector: Single
allSelectors:
- Single
fireRate: 2
minAngle: 5 minAngle: 5
maxAngle: 45
angleIncrease: 20 angleIncrease: 20
angleDecay: 15
powerCellPrototype: PowerCellSmallStandard
powerCellRemovable: false
fireCost: 80 fireCost: 80
ammoPrototype: BulletTaser ammoPrototype: BulletTaser
soundGunshot: soundGunshot:
path: /Audio/Weapons/Guns/Gunshots/taser.ogg path: /Audio/Weapons/Guns/Gunshots/taser.ogg
soundPowerCellInsert: cellSlot:
path: /Audio/Weapons/Guns/MagIn/revolver_magin.ogg insertSound: /Audio/Weapons/Guns/MagIn/revolver_magin.ogg
soundPowerCellEject: ejectSound: /Audio/Weapons/Guns/MagOut/revolver_magout.ogg
path: /Audio/Weapons/Guns/MagOut/revolver_magout.ogg soundOptions:
volume: -2
locked: true
startingItem: PowerCellSmallStandard
whitelist:
components:
- Battery
- type: Appearance - type: Appearance
visuals: visuals:
- type: MagVisualizer - type: MagVisualizer
@@ -322,7 +250,7 @@
- type: entity - type: entity
name: laser gun name: laser gun
parent: BaseItem parent: BatteryGunBase
id: LaserSecGun id: LaserSecGun
description: A laser gun. description: A laser gun.
components: components:
@@ -338,28 +266,16 @@
- type: Item - type: Item
size: 24 size: 24
sprite: Objects/Weapons/Guns/Battery/laser_gun.rsi sprite: Objects/Weapons/Guns/Battery/laser_gun.rsi
- type: RangedWeapon
- type: BatteryBarrel - type: BatteryBarrel
minAngle: 10 minAngle: 1
maxAngle: 45
angleIncrease: 15
angleDecay: 45
currentSelector: Single
allSelectors:
- Single
fireRate: 6 fireRate: 6
powerCellPrototype: PowerCellMediumStandard cellSlot:
powerCellRemovable: true ejectOnUse: true
ammoPrototype: RedLaser insertSound: /Audio/Weapons/Guns/MagIn/revolver_magin.ogg
soundGunshot: ejectSound: /Audio/Weapons/Guns/MagOut/revolver_magout.ogg
path: /Audio/Weapons/Guns/Gunshots/laser.ogg soundOptions:
soundPowerCellInsert: volume: -2
path: /Audio/Weapons/Guns/MagIn/revolver_magin.ogg startingItem: PowerCellMediumStandard
soundPowerCellEject: whitelist:
path: /Audio/Weapons/Guns/MagOut/revolver_magout.ogg components:
- type: Appearance - Battery
visuals:
- type: MagVisualizer
magState: mag
steps: 5
zeroVisible: false