Refactor ExtinguisherCabinet->ItemCabinet and actually maps them in, adds EntityWhitelist (#4154)
* i probably shouldnt have done this in one commit * map nonsense * fix example code * unnecessary * test * reviews * little fix for open datafield * add soul
This commit is contained in:
@@ -1,40 +0,0 @@
|
||||
using Content.Shared.GameObjects.Components;
|
||||
using JetBrains.Annotations;
|
||||
using Robust.Client.GameObjects;
|
||||
|
||||
namespace Content.Client.GameObjects.Components
|
||||
{
|
||||
[UsedImplicitly]
|
||||
public class ExtinguisherCabinetVisualizer : AppearanceVisualizer
|
||||
{
|
||||
public override void OnChangeData(AppearanceComponent component)
|
||||
{
|
||||
base.OnChangeData(component);
|
||||
|
||||
var sprite = component.Owner.GetComponent<ISpriteComponent>();
|
||||
|
||||
if (component.TryGetData(ExtinguisherCabinetVisuals.IsOpen, out bool isOpen))
|
||||
{
|
||||
if (isOpen)
|
||||
{
|
||||
if (component.TryGetData(ExtinguisherCabinetVisuals.ContainsExtinguisher, out bool contains))
|
||||
{
|
||||
if (contains)
|
||||
{
|
||||
sprite.LayerSetState(0, "extinguisher_full");
|
||||
}
|
||||
else
|
||||
{
|
||||
sprite.LayerSetState(0, "extinguisher_empty");
|
||||
}
|
||||
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
sprite.LayerSetState(0, "extinguisher_closed");
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,51 @@
|
||||
using System.ComponentModel.DataAnnotations;
|
||||
using Content.Shared.GameObjects.Components;
|
||||
using JetBrains.Annotations;
|
||||
using Robust.Client.GameObjects;
|
||||
using Robust.Shared.Serialization.Manager.Attributes;
|
||||
|
||||
namespace Content.Client.GameObjects.Components
|
||||
{
|
||||
[UsedImplicitly]
|
||||
public class ItemCabinetVisualizer : AppearanceVisualizer
|
||||
{
|
||||
// TODO proper layering
|
||||
[DataField("fullState", required: true)]
|
||||
private string _fullState = default!;
|
||||
|
||||
[DataField("emptyState", required: true)]
|
||||
private string _emptyState = default!;
|
||||
|
||||
[DataField("closedState", required: true)]
|
||||
private string _closedState = default!;
|
||||
|
||||
public override void OnChangeData(AppearanceComponent component)
|
||||
{
|
||||
base.OnChangeData(component);
|
||||
|
||||
if (component.Owner.TryGetComponent<SpriteComponent>(out var sprite)
|
||||
&& component.TryGetData(ItemCabinetVisuals.IsOpen, out bool isOpen))
|
||||
{
|
||||
if (isOpen)
|
||||
{
|
||||
if (component.TryGetData(ItemCabinetVisuals.ContainsItem, out bool contains))
|
||||
{
|
||||
if (contains)
|
||||
{
|
||||
sprite.LayerSetState(0, _fullState);
|
||||
}
|
||||
else
|
||||
{
|
||||
sprite.LayerSetState(0, _emptyState);
|
||||
}
|
||||
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
sprite.LayerSetState(0, _closedState);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -158,8 +158,7 @@ namespace Content.Client
|
||||
"SignalTransmitter",
|
||||
"SignalButton",
|
||||
"SignalLinker",
|
||||
"ExtinguisherCabinet",
|
||||
"ExtinguisherCabinetFilled",
|
||||
"ItemCabinet",
|
||||
"FireExtinguisher",
|
||||
"Firelock",
|
||||
"AtmosPlaque",
|
||||
|
||||
121
Content.IntegrationTests/Tests/Utility/EntityWhitelistTest.cs
Normal file
121
Content.IntegrationTests/Tests/Utility/EntityWhitelistTest.cs
Normal file
@@ -0,0 +1,121 @@
|
||||
using System.IO;
|
||||
using System.Threading.Tasks;
|
||||
using Content.Server.GameObjects.Components;
|
||||
using Content.Server.GameObjects.Components.Items.Storage;
|
||||
using Content.Shared.GameObjects.Components.Tag;
|
||||
using Content.Shared.Prototypes;
|
||||
using Content.Shared.Utility;
|
||||
using NUnit.Framework;
|
||||
using NUnit.Framework.Internal;
|
||||
using Robust.Shared.GameObjects;
|
||||
using Robust.Shared.IoC;
|
||||
using Robust.Shared.Map;
|
||||
using Robust.Shared.Maths;
|
||||
using Robust.Shared.Prototypes;
|
||||
using Robust.Shared.Serialization.Manager;
|
||||
|
||||
namespace Content.IntegrationTests.Tests.Utility
|
||||
{
|
||||
[TestFixture]
|
||||
[TestOf(typeof(EntityWhitelist))]
|
||||
public class EntityWhitelistTest : ContentIntegrationTest
|
||||
{
|
||||
private const string InvalidComponent = "Sprite";
|
||||
private const string ValidComponent = "Physics";
|
||||
|
||||
private static readonly string Prototypes = $@"
|
||||
- type: Tag
|
||||
id: ValidTag
|
||||
- type: Tag
|
||||
id: InvalidTag
|
||||
|
||||
- type: entity
|
||||
id: WhitelistDummy
|
||||
components:
|
||||
- type: ItemCabinet
|
||||
whitelist:
|
||||
prototypes:
|
||||
- ValidPrototypeDummy
|
||||
components:
|
||||
- {ValidComponent}
|
||||
tags:
|
||||
- ValidTag
|
||||
|
||||
- type: entity
|
||||
id: InvalidComponentDummy
|
||||
components:
|
||||
- type: {InvalidComponent}
|
||||
- type: entity
|
||||
id: InvalidTagDummy
|
||||
components:
|
||||
- type: Tag
|
||||
tags:
|
||||
- InvalidTag
|
||||
|
||||
- type: entity
|
||||
id: ValidComponentDummy
|
||||
components:
|
||||
- type: {ValidComponent}
|
||||
- type: entity
|
||||
id: ValidTagDummy
|
||||
components:
|
||||
- type: Tag
|
||||
tags:
|
||||
- ValidTag";
|
||||
|
||||
[Test]
|
||||
public async Task Test()
|
||||
{
|
||||
var serverOptions = new ServerContentIntegrationOption {ExtraPrototypes = Prototypes};
|
||||
var server = StartServer(serverOptions);
|
||||
|
||||
await server.WaitIdleAsync();
|
||||
var mapManager = server.ResolveDependency<IMapManager>();
|
||||
var entityManager = server.ResolveDependency<IEntityManager>();
|
||||
|
||||
await server.WaitAssertion(() =>
|
||||
{
|
||||
var mapId = new MapId(1);
|
||||
var mapCoordinates = new MapCoordinates(0, 0, mapId);
|
||||
|
||||
var validComponent = entityManager.SpawnEntity("ValidComponentDummy", mapCoordinates);
|
||||
var validTag = entityManager.SpawnEntity("ValidTagDummy", mapCoordinates);
|
||||
|
||||
var invalidComponent = entityManager.SpawnEntity("InvalidComponentDummy", mapCoordinates);
|
||||
var invalidTag = entityManager.SpawnEntity("InvalidTagDummy", mapCoordinates);
|
||||
|
||||
// Test instantiated on its own
|
||||
var whitelistInst = new EntityWhitelist
|
||||
{
|
||||
Components = new[] {$"{ValidComponent}"},
|
||||
Tags = new[] {"ValidTag"}
|
||||
};
|
||||
whitelistInst.UpdateRegistrations();
|
||||
Assert.That(whitelistInst, Is.Not.Null);
|
||||
|
||||
Assert.That(whitelistInst.Components, Is.Not.Null);
|
||||
Assert.That(whitelistInst.Tags, Is.Not.Null);
|
||||
|
||||
Assert.That(whitelistInst.IsValid(validComponent), Is.True);
|
||||
Assert.That(whitelistInst.IsValid(validTag), Is.True);
|
||||
|
||||
Assert.That(whitelistInst.IsValid(invalidComponent), Is.False);
|
||||
Assert.That(whitelistInst.IsValid(invalidTag), Is.False);
|
||||
|
||||
// Test from serialized
|
||||
var dummy = entityManager.SpawnEntity("WhitelistDummy", mapCoordinates);
|
||||
var whitelistSer = dummy.GetComponent<ItemCabinetComponent>().Whitelist;
|
||||
Assert.That(whitelistSer, Is.Not.Null);
|
||||
|
||||
Assert.That(whitelistSer.Components, Is.Not.Null);
|
||||
Assert.That(whitelistSer.Tags, Is.Not.Null);
|
||||
|
||||
Assert.That(whitelistSer.IsValid(validComponent), Is.True);
|
||||
Assert.That(whitelistSer.IsValid(validTag), Is.True);
|
||||
|
||||
Assert.That(whitelistSer.IsValid(invalidComponent), Is.False);
|
||||
Assert.That(whitelistSer.IsValid(invalidTag), Is.False);
|
||||
});
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -1,121 +0,0 @@
|
||||
using System.Threading.Tasks;
|
||||
using Content.Server.GameObjects.Components.GUI;
|
||||
using Content.Server.GameObjects.Components.Items;
|
||||
using Content.Server.GameObjects.Components.Items.Storage;
|
||||
using Content.Server.Interfaces.GameObjects.Components.Items;
|
||||
using Content.Shared.Audio;
|
||||
using Content.Shared.GameObjects.Components;
|
||||
using Content.Shared.Interfaces;
|
||||
using Content.Shared.Interfaces.GameObjects.Components;
|
||||
using Robust.Server.GameObjects;
|
||||
using Robust.Shared.Audio;
|
||||
using Robust.Shared.Containers;
|
||||
using Robust.Shared.GameObjects;
|
||||
using Robust.Shared.Localization;
|
||||
using Robust.Shared.Player;
|
||||
using Robust.Shared.Serialization.Manager.Attributes;
|
||||
using Robust.Shared.ViewVariables;
|
||||
|
||||
namespace Content.Server.GameObjects.Components
|
||||
{
|
||||
[RegisterComponent]
|
||||
[ComponentReference(typeof(IActivate))]
|
||||
public class ExtinguisherCabinetComponent : Component, IInteractUsing, IInteractHand, IActivate
|
||||
{
|
||||
public override string Name => "ExtinguisherCabinet";
|
||||
|
||||
private bool _opened = false;
|
||||
[DataField("doorSound")]
|
||||
private string _doorSound = "/Audio/Machines/machine_switch.ogg";
|
||||
|
||||
[ViewVariables] protected ContainerSlot ItemContainer = default!;
|
||||
[ViewVariables] public string DoorSound => _doorSound;
|
||||
|
||||
public override void Initialize()
|
||||
{
|
||||
base.Initialize();
|
||||
|
||||
ItemContainer =
|
||||
ContainerHelpers.EnsureContainer<ContainerSlot>(Owner, "extinguisher_cabinet", out _);
|
||||
}
|
||||
|
||||
async Task<bool> IInteractUsing.InteractUsing(InteractUsingEventArgs eventArgs)
|
||||
{
|
||||
if (!_opened)
|
||||
{
|
||||
_opened = true;
|
||||
ClickLatchSound();
|
||||
}
|
||||
else
|
||||
{
|
||||
if (ItemContainer.ContainedEntity != null || !eventArgs.Using.HasComponent<FireExtinguisherComponent>())
|
||||
{
|
||||
return false;
|
||||
}
|
||||
var handsComponent = eventArgs.User.GetComponent<IHandsComponent>();
|
||||
|
||||
if (!handsComponent.Drop(eventArgs.Using, ItemContainer))
|
||||
{
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
UpdateVisuals();
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
bool IInteractHand.InteractHand(InteractHandEventArgs eventArgs)
|
||||
{
|
||||
if (_opened)
|
||||
{
|
||||
if (ItemContainer.ContainedEntity == null)
|
||||
{
|
||||
_opened = false;
|
||||
ClickLatchSound();
|
||||
}
|
||||
else if (eventArgs.User.TryGetComponent(out HandsComponent? hands))
|
||||
{
|
||||
Owner.PopupMessage(eventArgs.User,
|
||||
Loc.GetString("You take {0:extinguisherName} from the {1:cabinetName}", ItemContainer.ContainedEntity.Name, Owner.Name));
|
||||
hands.PutInHandOrDrop(ItemContainer.ContainedEntity.GetComponent<ItemComponent>());
|
||||
}
|
||||
else if (ItemContainer.Remove(ItemContainer.ContainedEntity))
|
||||
{
|
||||
ItemContainer.ContainedEntity.Transform.Coordinates = Owner.Transform.Coordinates;
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
_opened = true;
|
||||
ClickLatchSound();
|
||||
}
|
||||
|
||||
UpdateVisuals();
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
void IActivate.Activate(ActivateEventArgs eventArgs)
|
||||
{
|
||||
_opened = !_opened;
|
||||
ClickLatchSound();
|
||||
UpdateVisuals();
|
||||
}
|
||||
|
||||
private void UpdateVisuals()
|
||||
{
|
||||
if (Owner.TryGetComponent(out AppearanceComponent? appearance))
|
||||
{
|
||||
appearance.SetData(ExtinguisherCabinetVisuals.IsOpen, _opened);
|
||||
appearance.SetData(ExtinguisherCabinetVisuals.ContainsExtinguisher, ItemContainer.ContainedEntity != null);
|
||||
}
|
||||
}
|
||||
|
||||
private void ClickLatchSound()
|
||||
{
|
||||
// Don't have original click, this sounds close
|
||||
SoundSystem.Play(Filter.Pvs(Owner), DoorSound, Owner, AudioHelpers.WithVariation(0.15f));
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -1,17 +0,0 @@
|
||||
using Robust.Shared.GameObjects;
|
||||
|
||||
namespace Content.Server.GameObjects.Components
|
||||
{
|
||||
[RegisterComponent]
|
||||
public class ExtinguisherCabinetFilledComponent : ExtinguisherCabinetComponent
|
||||
{
|
||||
public override string Name => "ExtinguisherCabinetFilled";
|
||||
|
||||
public override void Initialize()
|
||||
{
|
||||
base.Initialize();
|
||||
|
||||
ItemContainer.Insert(Owner.EntityManager.SpawnEntity("FireExtinguisher", Owner.Transform.Coordinates));
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,97 @@
|
||||
using System.Collections;
|
||||
using System.Collections.Generic;
|
||||
using Content.Server.GameObjects.EntitySystems;
|
||||
using Content.Shared.GameObjects.EntitySystems.ActionBlocker;
|
||||
using Content.Shared.GameObjects.Verbs;
|
||||
using Content.Shared.Utility;
|
||||
using Robust.Shared.Containers;
|
||||
using Robust.Shared.GameObjects;
|
||||
using Robust.Shared.Localization;
|
||||
using Robust.Shared.Serialization.Manager.Attributes;
|
||||
using Robust.Shared.ViewVariables;
|
||||
|
||||
namespace Content.Server.GameObjects.Components
|
||||
{
|
||||
/// <summary>
|
||||
/// Used for entities that can hold one item that fits the whitelist, which can be extracted by interacting with
|
||||
/// the entity, and can have an item fitting the whitelist placed back inside
|
||||
/// </summary>
|
||||
[RegisterComponent]
|
||||
public class ItemCabinetComponent : Component
|
||||
{
|
||||
public override string Name => "ItemCabinet";
|
||||
|
||||
/// <summary>
|
||||
/// Sound to be played when the cabinet door is opened.
|
||||
/// </summary>
|
||||
[ViewVariables(VVAccess.ReadWrite)]
|
||||
[DataField("doorSound")]
|
||||
public string? DoorSound { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// The prototype that should be spawned inside the cabinet when it is map initialized.
|
||||
/// </summary>
|
||||
[ViewVariables]
|
||||
[DataField("spawnPrototype")]
|
||||
public string? SpawnPrototype { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// A whitelist defining which entities are allowed into the cabinet.
|
||||
/// </summary>
|
||||
[ViewVariables]
|
||||
[DataField("whitelist")]
|
||||
public EntityWhitelist? Whitelist = null;
|
||||
|
||||
[ViewVariables]
|
||||
public ContainerSlot ItemContainer = default!;
|
||||
|
||||
/// <summary>
|
||||
/// Whether the cabinet is currently open or not.
|
||||
/// </summary>
|
||||
[ViewVariables]
|
||||
[DataField("opened")]
|
||||
public bool Opened { get; set; } = false;
|
||||
|
||||
[Verb]
|
||||
public sealed class EjectItemFromCabinetVerb : Verb<ItemCabinetComponent>
|
||||
{
|
||||
protected override void GetData(IEntity user, ItemCabinetComponent component, VerbData data)
|
||||
{
|
||||
if (component.ItemContainer.ContainedEntity == null || !component.Opened || !ActionBlockerSystem.CanInteract(user))
|
||||
data.Visibility = VerbVisibility.Invisible;
|
||||
else
|
||||
{
|
||||
data.Text = Loc.GetString("comp-item-cabinet-eject-verb-text");
|
||||
data.IconTexture = "/Textures/Interface/VerbIcons/eject.svg.192dpi.png";
|
||||
data.Visibility = VerbVisibility.Visible;
|
||||
}
|
||||
}
|
||||
|
||||
protected override void Activate(IEntity user, ItemCabinetComponent component)
|
||||
{
|
||||
component.Owner.EntityManager.EventBus.RaiseLocalEvent(component.Owner.Uid, new TryEjectItemCabinetEvent(user), false);
|
||||
}
|
||||
}
|
||||
|
||||
[Verb]
|
||||
public sealed class ToggleItemCabinetVerb : Verb<ItemCabinetComponent>
|
||||
{
|
||||
protected override void GetData(IEntity user, ItemCabinetComponent component, VerbData data)
|
||||
{
|
||||
if (!ActionBlockerSystem.CanInteract(user))
|
||||
data.Visibility = VerbVisibility.Invisible;
|
||||
else
|
||||
{
|
||||
data.Text = Loc.GetString(component.Opened ? "comp-item-cabinet-close-verb-text" : "comp-item-cabinet-open-verb-text");
|
||||
data.IconTexture = component.Opened ? "/Textures/Interface/VerbIcons/close.svg.192dpi.png" : "/Textures/Interface/VerbIcons/open.svg.192dpi.png";
|
||||
data.Visibility = VerbVisibility.Visible;
|
||||
}
|
||||
}
|
||||
|
||||
protected override void Activate(IEntity user, ItemCabinetComponent component)
|
||||
{
|
||||
component.Owner.EntityManager.EventBus.RaiseLocalEvent(component.Owner.Uid, new ToggleItemCabinetEvent(), false);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -25,6 +25,7 @@ namespace Content.Server.GameObjects.Components.Items.Storage
|
||||
[RegisterComponent]
|
||||
public class StorageCounterComponent : Component, ISerializationHooks
|
||||
{
|
||||
// TODO Convert to EntityWhitelist
|
||||
[DataField("countTag")]
|
||||
private string? _countTag;
|
||||
|
||||
|
||||
@@ -31,10 +31,10 @@ namespace Content.Server.GameObjects.EntitySystems
|
||||
SubscribeLocalEvent<BuckleComponent, EntRemovedFromContainerMessage>(ContainerModifiedBuckle);
|
||||
SubscribeLocalEvent<StrapComponent, EntRemovedFromContainerMessage>(ContainerModifiedStrap);
|
||||
|
||||
SubscribeLocalEvent<BuckleComponent, InteractHandEvent>(HandleAttackHand);
|
||||
SubscribeLocalEvent<BuckleComponent, InteractHandEvent>(HandleInteractHand);
|
||||
}
|
||||
|
||||
private void HandleAttackHand(EntityUid uid, BuckleComponent component, InteractHandEvent args)
|
||||
private void HandleInteractHand(EntityUid uid, BuckleComponent component, InteractHandEvent args)
|
||||
{
|
||||
args.Handled = component.TryUnbuckle(args.User);
|
||||
}
|
||||
|
||||
195
Content.Server/GameObjects/EntitySystems/ItemCabinetSystem.cs
Normal file
195
Content.Server/GameObjects/EntitySystems/ItemCabinetSystem.cs
Normal file
@@ -0,0 +1,195 @@
|
||||
using Content.Server.GameObjects.Components;
|
||||
using Content.Server.GameObjects.Components.GUI;
|
||||
using Content.Server.GameObjects.Components.Items;
|
||||
using Content.Server.GameObjects.Components.Items.Storage;
|
||||
using Content.Shared.Audio;
|
||||
using Content.Shared.GameObjects.Components;
|
||||
using Content.Shared.GameObjects.EntitySystems.ActionBlocker;
|
||||
using Content.Shared.GameObjects.Verbs;
|
||||
using Content.Shared.Interfaces;
|
||||
using Content.Shared.Interfaces.GameObjects.Components;
|
||||
using Microsoft.EntityFrameworkCore;
|
||||
using Robust.Server.GameObjects;
|
||||
using Robust.Shared.Audio;
|
||||
using Robust.Shared.Containers;
|
||||
using Robust.Shared.GameObjects;
|
||||
using Robust.Shared.Localization;
|
||||
using Robust.Shared.Log;
|
||||
using Robust.Shared.Player;
|
||||
|
||||
namespace Content.Server.GameObjects.EntitySystems
|
||||
{
|
||||
public class ItemCabinetSystem : EntitySystem
|
||||
{
|
||||
public override void Initialize()
|
||||
{
|
||||
base.Initialize();
|
||||
|
||||
SubscribeLocalEvent<ItemCabinetComponent, MapInitEvent>(OnMapInitialize);
|
||||
|
||||
SubscribeLocalEvent<ItemCabinetComponent, InteractUsingEvent>(OnInteractUsing);
|
||||
SubscribeLocalEvent<ItemCabinetComponent, InteractHandEvent>(OnInteractHand);
|
||||
SubscribeLocalEvent<ItemCabinetComponent, ActivateInWorldEvent>(OnActivateInWorld);
|
||||
|
||||
SubscribeLocalEvent<ItemCabinetComponent, TryEjectItemCabinetEvent>(OnTryEjectItemCabinet);
|
||||
SubscribeLocalEvent<ItemCabinetComponent, TryInsertItemCabinetEvent>(OnTryInsertItemCabinet);
|
||||
SubscribeLocalEvent<ItemCabinetComponent, ToggleItemCabinetEvent>(OnToggleItemCabinet);
|
||||
}
|
||||
|
||||
private void OnMapInitialize(EntityUid uid, ItemCabinetComponent comp, MapInitEvent args)
|
||||
{
|
||||
var owner = EntityManager.GetEntity(uid);
|
||||
comp.ItemContainer =
|
||||
owner.EnsureContainer<ContainerSlot>("item_cabinet", out _);
|
||||
|
||||
if(comp.SpawnPrototype != null)
|
||||
comp.ItemContainer.Insert(EntityManager.SpawnEntity(comp.SpawnPrototype, owner.Transform.Coordinates));
|
||||
|
||||
UpdateVisuals(comp);
|
||||
}
|
||||
|
||||
private void OnInteractUsing(EntityUid uid, ItemCabinetComponent comp, InteractUsingEvent args)
|
||||
{
|
||||
args.Handled = true;
|
||||
if (!comp.Opened)
|
||||
{
|
||||
RaiseLocalEvent(uid, new ToggleItemCabinetEvent(), false);
|
||||
}
|
||||
else
|
||||
{
|
||||
RaiseLocalEvent(uid, new TryInsertItemCabinetEvent(args.User, args.Used), false);
|
||||
}
|
||||
|
||||
args.Handled = true;
|
||||
}
|
||||
|
||||
private void OnInteractHand(EntityUid uid, ItemCabinetComponent comp, InteractHandEvent args)
|
||||
{
|
||||
args.Handled = true;
|
||||
if (comp.Opened)
|
||||
{
|
||||
if (comp.ItemContainer.ContainedEntity == null)
|
||||
{
|
||||
RaiseLocalEvent(uid, new ToggleItemCabinetEvent(), false);
|
||||
return;
|
||||
}
|
||||
RaiseLocalEvent(uid, new TryEjectItemCabinetEvent(args.User), false);
|
||||
}
|
||||
else
|
||||
{
|
||||
RaiseLocalEvent(uid, new ToggleItemCabinetEvent(), false);
|
||||
}
|
||||
}
|
||||
|
||||
private void OnActivateInWorld(EntityUid uid, ItemCabinetComponent comp, ActivateInWorldEvent args)
|
||||
{
|
||||
args.Handled = true;
|
||||
RaiseLocalEvent(uid, new ToggleItemCabinetEvent(), false);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Toggles the ItemCabinet's state.
|
||||
/// </summary>
|
||||
private void OnToggleItemCabinet(EntityUid uid, ItemCabinetComponent comp, ToggleItemCabinetEvent args)
|
||||
{
|
||||
comp.Opened = !comp.Opened;
|
||||
ClickLatchSound(comp);
|
||||
UpdateVisuals(comp);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Tries to insert an entity into the ItemCabinet's slot from the user's hands.
|
||||
/// </summary>
|
||||
private static void OnTryInsertItemCabinet(EntityUid uid, ItemCabinetComponent comp, TryInsertItemCabinetEvent args)
|
||||
{
|
||||
if (comp.ItemContainer.ContainedEntity != null || args.Cancelled || (comp.Whitelist != null && !comp.Whitelist.IsValid(args.Item)))
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
if (!args.User.TryGetComponent<HandsComponent>(out var hands) || !hands.Drop(args.Item, comp.ItemContainer))
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
UpdateVisuals(comp);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Tries to eject the ItemCabinet's item, either into the user's hands or onto the floor.
|
||||
/// </summary>
|
||||
private static void OnTryEjectItemCabinet(EntityUid uid, ItemCabinetComponent comp, TryEjectItemCabinetEvent args)
|
||||
{
|
||||
if (comp.ItemContainer.ContainedEntity == null || args.Cancelled)
|
||||
return;
|
||||
if (args.User.TryGetComponent(out HandsComponent? hands))
|
||||
{
|
||||
|
||||
if (comp.ItemContainer.ContainedEntity.TryGetComponent<ItemComponent>(out var item))
|
||||
{
|
||||
comp.Owner.PopupMessage(args.User,
|
||||
Loc.GetString("comp-item-cabinet-successfully-taken",
|
||||
("item", comp.ItemContainer.ContainedEntity),
|
||||
("cabinet", comp.Owner)));
|
||||
hands.PutInHandOrDrop(item);
|
||||
}
|
||||
}
|
||||
else if (comp.ItemContainer.Remove(comp.ItemContainer.ContainedEntity))
|
||||
{
|
||||
comp.ItemContainer.ContainedEntity.Transform.Coordinates = args.User.Transform.Coordinates;
|
||||
}
|
||||
UpdateVisuals(comp);
|
||||
}
|
||||
|
||||
private static void UpdateVisuals(ItemCabinetComponent comp)
|
||||
{
|
||||
if (comp.Owner.TryGetComponent(out SharedAppearanceComponent? appearance))
|
||||
{
|
||||
appearance.SetData(ItemCabinetVisuals.IsOpen, comp.Opened);
|
||||
appearance.SetData(ItemCabinetVisuals.ContainsItem, comp.ItemContainer.ContainedEntity != null);
|
||||
}
|
||||
}
|
||||
|
||||
private static void ClickLatchSound(ItemCabinetComponent comp)
|
||||
{
|
||||
if (comp.DoorSound == null) return;
|
||||
SoundSystem.Play(Filter.Pvs(comp.Owner), comp.DoorSound, comp.Owner, AudioHelpers.WithVariation(0.15f));
|
||||
}
|
||||
}
|
||||
|
||||
public class ToggleItemCabinetEvent : EntityEventArgs
|
||||
{
|
||||
}
|
||||
|
||||
public class TryEjectItemCabinetEvent : CancellableEntityEventArgs
|
||||
{
|
||||
/// <summary>
|
||||
/// The user who tried to eject the item.
|
||||
/// </summary>
|
||||
public IEntity User;
|
||||
|
||||
public TryEjectItemCabinetEvent(IEntity user)
|
||||
{
|
||||
User = user;
|
||||
}
|
||||
}
|
||||
|
||||
public class TryInsertItemCabinetEvent : CancellableEntityEventArgs
|
||||
{
|
||||
/// <summary>
|
||||
/// The user who tried to eject the item.
|
||||
/// </summary>
|
||||
public IEntity User;
|
||||
|
||||
/// <summary>
|
||||
/// The item to be inserted.
|
||||
/// </summary>
|
||||
public IEntity Item;
|
||||
|
||||
public TryInsertItemCabinetEvent(IEntity user, IEntity item)
|
||||
{
|
||||
User = user;
|
||||
Item = item;
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -5,9 +5,9 @@ using Robust.Shared.Serialization;
|
||||
namespace Content.Shared.GameObjects.Components
|
||||
{
|
||||
[Serializable, NetSerializable]
|
||||
public enum ExtinguisherCabinetVisuals
|
||||
public enum ItemCabinetVisuals : byte
|
||||
{
|
||||
IsOpen,
|
||||
ContainsExtinguisher
|
||||
ContainsItem
|
||||
}
|
||||
}
|
||||
|
||||
@@ -48,7 +48,7 @@ namespace Content.Shared.Interfaces.GameObjects.Components
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Raised when clicking on another object and no attack event was handled.
|
||||
/// Raised directed on the used object when clicking on another object and no attack event was handled.
|
||||
/// </summary>
|
||||
[PublicAPI]
|
||||
public class AfterInteractEvent : HandledEntityEventArgs
|
||||
|
||||
@@ -15,7 +15,7 @@ namespace Content.Shared.Interfaces.GameObjects.Components
|
||||
/// <summary>
|
||||
/// Called when a player directly interacts with an empty hand when user is in range of the target entity.
|
||||
/// </summary>
|
||||
[Obsolete("Use AttackHandMessage instead")]
|
||||
[Obsolete("Use InteractHandEvent instead")]
|
||||
bool InteractHand(InteractHandEventArgs eventArgs);
|
||||
}
|
||||
|
||||
@@ -32,7 +32,7 @@ namespace Content.Shared.Interfaces.GameObjects.Components
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Raised when a target entity is interacted with by a user with an empty hand.
|
||||
/// Raised directed on a target entity when it is interacted with by a user with an empty hand.
|
||||
/// </summary>
|
||||
[PublicAPI]
|
||||
public class InteractHandEvent : HandledEntityEventArgs
|
||||
|
||||
85
Content.Shared/Utility/EntityWhitelist.cs
Normal file
85
Content.Shared/Utility/EntityWhitelist.cs
Normal file
@@ -0,0 +1,85 @@
|
||||
using System.Collections.Generic;
|
||||
using Content.Shared.GameObjects.Components.Tag;
|
||||
using Robust.Shared.GameObjects;
|
||||
using Robust.Shared.IoC;
|
||||
using Robust.Shared.Log;
|
||||
using Robust.Shared.Serialization;
|
||||
using Robust.Shared.Serialization.Manager.Attributes;
|
||||
|
||||
namespace Content.Shared.Utility
|
||||
{
|
||||
/// <summary>
|
||||
/// Used to determine whether an entity fits a certain whitelist.
|
||||
/// Does not whitelist by prototypes, since that is undesirable; you're better off just adding a tag to all
|
||||
/// entity prototypes that need to be whitelisted, and checking for that.
|
||||
/// </summary>
|
||||
/// <code>
|
||||
/// whitelist:
|
||||
/// tags:
|
||||
/// - Cigarette
|
||||
/// - FirelockElectronics
|
||||
/// components:
|
||||
/// - Buckle
|
||||
/// - AsteroidRock
|
||||
/// </code>
|
||||
[DataDefinition]
|
||||
public class EntityWhitelist : ISerializationHooks
|
||||
{
|
||||
/// <summary>
|
||||
/// Component names that are allowed in the whitelist.
|
||||
/// </summary>
|
||||
[DataField("components")] public string[]? Components = null;
|
||||
|
||||
private List<IComponentRegistration>? _registrations = null;
|
||||
|
||||
/// <summary>
|
||||
/// Tags that are allowed in the whitelist.
|
||||
/// </summary>
|
||||
[DataField("tags")] public string[]? Tags = null;
|
||||
|
||||
void ISerializationHooks.AfterDeserialization()
|
||||
{
|
||||
UpdateRegistrations();
|
||||
}
|
||||
|
||||
public void UpdateRegistrations()
|
||||
{
|
||||
if (Components == null) return;
|
||||
|
||||
var compfact = IoCManager.Resolve<IComponentFactory>();
|
||||
_registrations = new List<IComponentRegistration>();
|
||||
foreach (var name in Components)
|
||||
{
|
||||
if (!compfact.TryGetRegistration(name, out var registration))
|
||||
{
|
||||
Logger.Warning($"Invalid component name {name} passed to EntityWhitelist!");
|
||||
continue;
|
||||
}
|
||||
|
||||
_registrations.Add(registration);
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Returns whether a given entity fits the whitelist.
|
||||
/// </summary>
|
||||
public bool IsValid(IEntity entity)
|
||||
{
|
||||
if (Tags != null)
|
||||
{
|
||||
if (entity.HasAnyTag(Tags))
|
||||
return true;
|
||||
}
|
||||
|
||||
if (_registrations != null)
|
||||
{
|
||||
foreach (var reg in _registrations)
|
||||
{
|
||||
if (entity.TryGetComponent(reg.Type, out _))
|
||||
return true;
|
||||
}
|
||||
}
|
||||
return false;
|
||||
}
|
||||
}
|
||||
}
|
||||
13
Resources/Locale/en-US/components/item-cabinet-component.ftl
Normal file
13
Resources/Locale/en-US/components/item-cabinet-component.ftl
Normal file
@@ -0,0 +1,13 @@
|
||||
### Used for item cabinet (fire extinguisher cabinets)
|
||||
|
||||
## Displayed when the item is successfully taken out of the cabinet.
|
||||
|
||||
comp-item-cabinet-successfully-taken = You take { THE($item) } from { THE($cabinet) }.
|
||||
|
||||
## Displayed in the context menu for the item cabinet.
|
||||
|
||||
comp-item-cabinet-eject-verb-text = Eject item
|
||||
|
||||
comp-item-cabinet-open-verb-text = Open
|
||||
comp-item-cabinet-close-verb-text = Close
|
||||
|
||||
@@ -48006,4 +48006,196 @@ entities:
|
||||
type: Transform
|
||||
- canCollide: False
|
||||
type: Physics
|
||||
- uid: 4921
|
||||
type: ExtinguisherCabinetFilled
|
||||
components:
|
||||
- pos: -26.5,-1.5
|
||||
parent: 853
|
||||
type: Transform
|
||||
- uid: 4922
|
||||
type: ExtinguisherCabinetFilled
|
||||
components:
|
||||
- pos: -26.5,-4.5
|
||||
parent: 853
|
||||
type: Transform
|
||||
- uid: 4923
|
||||
type: ExtinguisherCabinetFilled
|
||||
components:
|
||||
- pos: -15.5,-0.5
|
||||
parent: 853
|
||||
type: Transform
|
||||
- uid: 4924
|
||||
type: ExtinguisherCabinetFilled
|
||||
components:
|
||||
- pos: -18.5,8.5
|
||||
parent: 853
|
||||
type: Transform
|
||||
- uid: 4925
|
||||
type: ExtinguisherCabinetFilled
|
||||
components:
|
||||
- pos: -30.5,10.5
|
||||
parent: 853
|
||||
type: Transform
|
||||
- uid: 4926
|
||||
type: ExtinguisherCabinetFilled
|
||||
components:
|
||||
- pos: -34.5,-2.5
|
||||
parent: 853
|
||||
type: Transform
|
||||
- uid: 4927
|
||||
type: ExtinguisherCabinetFilled
|
||||
components:
|
||||
- pos: -16.5,-8.5
|
||||
parent: 853
|
||||
type: Transform
|
||||
- uid: 4928
|
||||
type: ExtinguisherCabinetFilled
|
||||
components:
|
||||
- pos: -6.5,-16.5
|
||||
parent: 853
|
||||
type: Transform
|
||||
- uid: 4929
|
||||
type: ExtinguisherCabinetFilled
|
||||
components:
|
||||
- pos: -6.5,-18.5
|
||||
parent: 853
|
||||
type: Transform
|
||||
- uid: 4930
|
||||
type: ExtinguisherCabinetFilled
|
||||
components:
|
||||
- pos: 8.5,-17.5
|
||||
parent: 853
|
||||
type: Transform
|
||||
- uid: 4931
|
||||
type: ExtinguisherCabinetFilled
|
||||
components:
|
||||
- pos: 16.5,-16.5
|
||||
parent: 853
|
||||
type: Transform
|
||||
- uid: 4932
|
||||
type: ExtinguisherCabinetFilled
|
||||
components:
|
||||
- pos: 24.5,-16.5
|
||||
parent: 853
|
||||
type: Transform
|
||||
- uid: 4933
|
||||
type: ExtinguisherCabinetFilled
|
||||
components:
|
||||
- pos: 25.5,-7.5
|
||||
parent: 853
|
||||
type: Transform
|
||||
- uid: 4934
|
||||
type: ExtinguisherCabinetFilled
|
||||
components:
|
||||
- pos: 32.5,-5.5
|
||||
parent: 853
|
||||
type: Transform
|
||||
- uid: 4935
|
||||
type: ExtinguisherCabinetFilled
|
||||
components:
|
||||
- pos: 41.5,-1.5
|
||||
parent: 853
|
||||
type: Transform
|
||||
- uid: 4936
|
||||
type: ExtinguisherCabinetFilled
|
||||
components:
|
||||
- pos: 46.5,-11.5
|
||||
parent: 853
|
||||
type: Transform
|
||||
- uid: 4937
|
||||
type: ExtinguisherCabinetFilled
|
||||
components:
|
||||
- pos: 52.5,-11.5
|
||||
parent: 853
|
||||
type: Transform
|
||||
- uid: 4938
|
||||
type: ExtinguisherCabinetFilled
|
||||
components:
|
||||
- pos: 45.5,6.5
|
||||
parent: 853
|
||||
type: Transform
|
||||
- uid: 4939
|
||||
type: ExtinguisherCabinetFilled
|
||||
components:
|
||||
- pos: 17.5,9.5
|
||||
parent: 853
|
||||
type: Transform
|
||||
- uid: 4940
|
||||
type: ExtinguisherCabinetFilled
|
||||
components:
|
||||
- pos: 9.5,12.5
|
||||
parent: 853
|
||||
type: Transform
|
||||
- uid: 4941
|
||||
type: ExtinguisherCabinetFilled
|
||||
components:
|
||||
- pos: -10.5,13.5
|
||||
parent: 853
|
||||
type: Transform
|
||||
- uid: 4942
|
||||
type: ExtinguisherCabinetFilled
|
||||
components:
|
||||
- pos: -6.5,18.5
|
||||
parent: 853
|
||||
type: Transform
|
||||
- uid: 4943
|
||||
type: ExtinguisherCabinetFilled
|
||||
components:
|
||||
- pos: 0.5,16.5
|
||||
parent: 853
|
||||
type: Transform
|
||||
- uid: 4944
|
||||
type: ExtinguisherCabinetFilled
|
||||
components:
|
||||
- pos: 11.5,18.5
|
||||
parent: 853
|
||||
type: Transform
|
||||
- uid: 4945
|
||||
type: ExtinguisherCabinetFilled
|
||||
components:
|
||||
- pos: 5.5,24.5
|
||||
parent: 853
|
||||
type: Transform
|
||||
- uid: 4946
|
||||
type: ExtinguisherCabinetFilled
|
||||
components:
|
||||
- pos: 1.5,26.5
|
||||
parent: 853
|
||||
type: Transform
|
||||
- uid: 4947
|
||||
type: ExtinguisherCabinetFilled
|
||||
components:
|
||||
- pos: -3.5,-25.5
|
||||
parent: 853
|
||||
type: Transform
|
||||
- uid: 4948
|
||||
type: ExtinguisherCabinetFilled
|
||||
components:
|
||||
- pos: -13.5,-23.5
|
||||
parent: 853
|
||||
type: Transform
|
||||
- uid: 4949
|
||||
type: ExtinguisherCabinetFilled
|
||||
components:
|
||||
- pos: 11.5,-28.5
|
||||
parent: 853
|
||||
type: Transform
|
||||
- uid: 4950
|
||||
type: ExtinguisherCabinetFilled
|
||||
components:
|
||||
- pos: 20.5,-20.5
|
||||
parent: 853
|
||||
type: Transform
|
||||
- uid: 4951
|
||||
type: ExtinguisherCabinetFilled
|
||||
components:
|
||||
- pos: 29.5,11.5
|
||||
parent: 853
|
||||
type: Transform
|
||||
- uid: 4952
|
||||
type: ExtinguisherCabinetFilled
|
||||
components:
|
||||
- pos: 44.5,11.5
|
||||
parent: 853
|
||||
type: Transform
|
||||
...
|
||||
|
||||
@@ -1,7 +1,6 @@
|
||||
- type: entity
|
||||
id: ExtinguisherCabinet
|
||||
name: extinguisher cabinet
|
||||
abstract: true
|
||||
description: A small wall mounted cabinet designed to hold a fire extinguisher.
|
||||
components:
|
||||
- type: Clickable
|
||||
@@ -9,15 +8,40 @@
|
||||
- type: Sprite
|
||||
sprite: Constructible/Misc/extinguisher_cabinet.rsi
|
||||
state: extinguisher_closed
|
||||
- type: ExtinguisherCabinet
|
||||
- type: ItemCabinet
|
||||
doorSound: /Audio/Machines/machine_switch.ogg
|
||||
whitelist:
|
||||
components:
|
||||
- FireExtinguisher
|
||||
- type: Appearance
|
||||
visuals:
|
||||
- type: ExtinguisherCabinetVisualizer
|
||||
- type: ItemCabinetVisualizer
|
||||
emptyState: extinguisher_empty
|
||||
fullState: extinguisher_full
|
||||
closedState: extinguisher_closed
|
||||
placement:
|
||||
mode: SnapgridCenter
|
||||
|
||||
- type: entity
|
||||
id: ExtinguisherCabinetOpen
|
||||
parent: ExtinguisherCabinet
|
||||
suffix: Open
|
||||
components:
|
||||
- type: ItemCabinet
|
||||
opened: true
|
||||
|
||||
- type: entity
|
||||
id: ExtinguisherCabinetFilled
|
||||
parent: ExtinguisherCabinet
|
||||
suffix: Filled
|
||||
components:
|
||||
- type: ExtinguisherCabinetFilled
|
||||
- type: ItemCabinet
|
||||
spawnPrototype: FireExtinguisher
|
||||
|
||||
- type: entity
|
||||
id: ExtinguisherCabinetFilledOpen
|
||||
parent: ExtinguisherCabinetFilled
|
||||
suffix: Filled, Open
|
||||
components:
|
||||
- type: ItemCabinet
|
||||
opened: true
|
||||
|
||||
@@ -31,6 +31,10 @@
|
||||
transferAmount: 5
|
||||
impulse: 50.0
|
||||
- type: FireExtinguisher
|
||||
- type: MeleeWeapon
|
||||
damage: 10
|
||||
damageType: Blunt
|
||||
hitSound: /Audio/Weapons/smash.ogg
|
||||
- type: Appearance
|
||||
visuals:
|
||||
- type: SprayVisualizer
|
||||
|
||||
Reference in New Issue
Block a user