DetGadget Hat Revitalization (#35438)
* DetGadget Hat * uh... half-assed item description * Reduce hat range to one tile, you have to stand on someone to steal their hat items * Fix Integration Errors * Only the wearer can access voice commands * init work - handscomp is unable to be pulled * second bit of progress * basic working implementation * nuke storageslots and add adminlogging * disallow trolling nukies or hiding objective items * remove unnecessary tags additions * finish nuking unused tags * death to yamllinter * int tests be damned * milon is a furry * address review * upd desc * address reviews part 2 * address more reviews * remove unused refs * fix order of dependencies * add ShowVerb to SharedStorageSystem.cs This will allow or disallow showing the "Open Storage" verb if defined on the component. * orks is a nerd * add proper locale, fix adminlogging * orks is a nerd 2 --------- Co-authored-by: Coenx-flex <coengmurray@gmail.com>
This commit is contained in:
@@ -53,6 +53,9 @@ namespace Content.Server.Explosion.EntitySystems
|
|||||||
_adminLogger.Add(LogType.Trigger, LogImpact.High,
|
_adminLogger.Add(LogType.Trigger, LogImpact.High,
|
||||||
$"A voice-trigger on {ToPrettyString(ent):entity} was triggered by {ToPrettyString(args.Source):speaker} speaking the key-phrase {component.KeyPhrase}.");
|
$"A voice-trigger on {ToPrettyString(ent):entity} was triggered by {ToPrettyString(args.Source):speaker} speaking the key-phrase {component.KeyPhrase}.");
|
||||||
Trigger(ent, args.Source);
|
Trigger(ent, args.Source);
|
||||||
|
|
||||||
|
var voice = new VoiceTriggeredEvent(args.Source, message);
|
||||||
|
RaiseLocalEvent(ent, ref voice);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -137,3 +140,12 @@ namespace Content.Server.Explosion.EntitySystems
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Raised when a voice trigger is activated, containing the message that triggered it.
|
||||||
|
/// </summary>
|
||||||
|
/// <param name="Source"> The EntityUid of the entity sending the message</param>
|
||||||
|
/// <param name="Message"> The contents of the message</param>
|
||||||
|
[ByRefEvent]
|
||||||
|
public readonly record struct VoiceTriggeredEvent(EntityUid Source, string? Message);
|
||||||
|
|||||||
19
Content.Server/VoiceTrigger/StorageVoiceControlComponent.cs
Normal file
19
Content.Server/VoiceTrigger/StorageVoiceControlComponent.cs
Normal file
@@ -0,0 +1,19 @@
|
|||||||
|
using Content.Shared.Inventory;
|
||||||
|
|
||||||
|
namespace Content.Server.VoiceTrigger;
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Entities with this component, Containers, and TriggerOnVoiceComponent will insert any item or extract the spoken item after the TriggerOnVoiceComponent has been activated
|
||||||
|
/// </summary>
|
||||||
|
[RegisterComponent]
|
||||||
|
public sealed partial class StorageVoiceControlComponent : Component
|
||||||
|
{
|
||||||
|
/// <summary>
|
||||||
|
/// Used to determine which slots the component can be used in.
|
||||||
|
/// <remarks>
|
||||||
|
/// If not set, the component can be used anywhere, even while inside other containers.
|
||||||
|
/// </remarks>
|
||||||
|
/// </summary>
|
||||||
|
[DataField]
|
||||||
|
public SlotFlags? AllowedSlots;
|
||||||
|
}
|
||||||
98
Content.Server/VoiceTrigger/StorageVoiceControlSystem.cs
Normal file
98
Content.Server/VoiceTrigger/StorageVoiceControlSystem.cs
Normal file
@@ -0,0 +1,98 @@
|
|||||||
|
using Content.Server.Hands.Systems;
|
||||||
|
using Content.Server.Storage.EntitySystems;
|
||||||
|
using Content.Shared.Administration.Logs;
|
||||||
|
using Content.Shared.Database;
|
||||||
|
using Content.Shared.Hands.Components;
|
||||||
|
using Content.Shared.Inventory;
|
||||||
|
using Content.Shared.Popups;
|
||||||
|
using Content.Shared.Storage;
|
||||||
|
using Robust.Server.Containers;
|
||||||
|
|
||||||
|
namespace Content.Server.VoiceTrigger;
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Allows storages to be manipulated using voice commands.
|
||||||
|
/// </summary>
|
||||||
|
public sealed class StorageVoiceControlSystem : EntitySystem
|
||||||
|
{
|
||||||
|
[Dependency] private readonly ContainerSystem _container = default!;
|
||||||
|
[Dependency] private readonly HandsSystem _hands = default!;
|
||||||
|
[Dependency] private readonly ISharedAdminLogManager _adminLogger = default!;
|
||||||
|
[Dependency] private readonly InventorySystem _inventory = default!;
|
||||||
|
[Dependency] private readonly SharedPopupSystem _popup = default!;
|
||||||
|
[Dependency] private readonly StorageSystem _storage = default!;
|
||||||
|
|
||||||
|
public override void Initialize()
|
||||||
|
{
|
||||||
|
base.Initialize();
|
||||||
|
SubscribeLocalEvent<StorageVoiceControlComponent, VoiceTriggeredEvent>(VoiceTriggered);
|
||||||
|
}
|
||||||
|
|
||||||
|
private void VoiceTriggered(Entity<StorageVoiceControlComponent> ent, ref VoiceTriggeredEvent args)
|
||||||
|
{
|
||||||
|
// Check if the component has any slot restrictions via AllowedSlots
|
||||||
|
// If it has slot restrictions, check if the item is in a slot that is allowed
|
||||||
|
if (ent.Comp.AllowedSlots != null && _inventory.TryGetContainingSlot(ent.Owner, out var itemSlot) &&
|
||||||
|
(itemSlot.SlotFlags & ent.Comp.AllowedSlots) == 0)
|
||||||
|
return;
|
||||||
|
|
||||||
|
// Don't do anything if there is no message
|
||||||
|
if (args.Message == null)
|
||||||
|
return;
|
||||||
|
|
||||||
|
// Get the storage component
|
||||||
|
if (!TryComp<StorageComponent>(ent, out var storage))
|
||||||
|
return;
|
||||||
|
|
||||||
|
// Get the hands component
|
||||||
|
if (!TryComp<HandsComponent>(args.Source, out var hands))
|
||||||
|
return;
|
||||||
|
|
||||||
|
// If the player has something in their hands, try to insert it into the storage
|
||||||
|
if (hands.ActiveHand != null && hands.ActiveHand.HeldEntity.HasValue)
|
||||||
|
{
|
||||||
|
// Disallow insertion and provide a reason why if the person decides to insert the item into itself
|
||||||
|
if (ent.Owner.Equals(hands.ActiveHand.HeldEntity.Value))
|
||||||
|
{
|
||||||
|
_popup.PopupEntity(Loc.GetString("comp-storagevoicecontrol-self-insert", ("entity", hands.ActiveHand.HeldEntity.Value)), ent, args.Source);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
if (_storage.CanInsert(ent, hands.ActiveHand.HeldEntity.Value, out var failedReason))
|
||||||
|
{
|
||||||
|
// We adminlog before insertion, otherwise the logger will attempt to pull info on an entity that no longer is present and throw an exception
|
||||||
|
_adminLogger.Add(LogType.Action, LogImpact.Low, $"{ToPrettyString(args.Source)} inserted {ToPrettyString(hands.ActiveHand.HeldEntity.Value)} into {ToPrettyString(ent)} via voice control");
|
||||||
|
_storage.Insert(ent, hands.ActiveHand.HeldEntity.Value, out _);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
{
|
||||||
|
// Tell the player the reason why the item couldn't be inserted
|
||||||
|
if (failedReason == null)
|
||||||
|
return;
|
||||||
|
_popup.PopupEntity(Loc.GetString(failedReason), ent, args.Source);
|
||||||
|
_adminLogger.Add(LogType.Action,
|
||||||
|
LogImpact.Low,
|
||||||
|
$"{ToPrettyString(args.Source)} failed to insert {ToPrettyString(hands.ActiveHand.HeldEntity.Value)} into {ToPrettyString(ent)} via voice control");
|
||||||
|
}
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
// If otherwise, we're retrieving an item, so check all the items currently in the attached storage
|
||||||
|
foreach (var item in storage.Container.ContainedEntities)
|
||||||
|
{
|
||||||
|
// Get the item's name
|
||||||
|
var itemName = MetaData(item).EntityName;
|
||||||
|
// The message doesn't match the item name the requestor requested, skip and move on to the next item
|
||||||
|
if (!args.Message.Contains(itemName, StringComparison.InvariantCultureIgnoreCase))
|
||||||
|
continue;
|
||||||
|
|
||||||
|
// We found the item we want, so draw it from storage and place it into the player's hands
|
||||||
|
if (storage.Container.ContainedEntities.Count != 0)
|
||||||
|
{
|
||||||
|
_container.RemoveEntity(ent, item);
|
||||||
|
_adminLogger.Add(LogType.Action, LogImpact.Low, $"{ToPrettyString(args.Source)} retrieved {ToPrettyString(item)} from {ToPrettyString(ent)} via voice control");
|
||||||
|
_hands.TryPickup(args.Source, item, handsComp: hands);
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -282,7 +282,7 @@ public abstract class SharedStorageSystem : EntitySystem
|
|||||||
|
|
||||||
private void AddUiVerb(EntityUid uid, StorageComponent component, GetVerbsEvent<ActivationVerb> args)
|
private void AddUiVerb(EntityUid uid, StorageComponent component, GetVerbsEvent<ActivationVerb> args)
|
||||||
{
|
{
|
||||||
if (!CanInteract(args.User, (uid, component), args.CanAccess && args.CanInteract))
|
if (component.ShowVerb == false || !CanInteract(args.User, (uid, component), args.CanAccess && args.CanInteract))
|
||||||
return;
|
return;
|
||||||
|
|
||||||
// Does this player currently have the storage UI open?
|
// Does this player currently have the storage UI open?
|
||||||
|
|||||||
@@ -146,6 +146,13 @@ namespace Content.Shared.Storage
|
|||||||
{
|
{
|
||||||
Key,
|
Key,
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Allow or disallow showing the "open/close storage" verb.
|
||||||
|
/// This is desired on items that we don't want to be accessed by the player directly.
|
||||||
|
/// </summary>
|
||||||
|
[DataField]
|
||||||
|
public bool ShowVerb = true;
|
||||||
}
|
}
|
||||||
|
|
||||||
[Serializable, NetSerializable]
|
[Serializable, NetSerializable]
|
||||||
|
|||||||
@@ -0,0 +1 @@
|
|||||||
|
comp-storagevoicecontrol-self-insert = You can't insert { THE($entity) } into itself!
|
||||||
@@ -122,7 +122,7 @@
|
|||||||
contents:
|
contents:
|
||||||
- id: ClothingEyesGlassesSecurity
|
- id: ClothingEyesGlassesSecurity
|
||||||
prob: 0.3
|
prob: 0.3
|
||||||
- id: ClothingHeadHatFedoraBrown
|
- id: ClothingHeadHatDetGadget
|
||||||
- id: ClothingNeckTieDet
|
- id: ClothingNeckTieDet
|
||||||
- id: ClothingOuterVestDetective
|
- id: ClothingOuterVestDetective
|
||||||
- id: ClothingOuterCoatDetective
|
- id: ClothingOuterCoatDetective
|
||||||
|
|||||||
@@ -18,3 +18,34 @@
|
|||||||
interfaces:
|
interfaces:
|
||||||
enum.ChameleonUiKey.Key:
|
enum.ChameleonUiKey.Key:
|
||||||
type: ChameleonBoundUserInterface
|
type: ChameleonBoundUserInterface
|
||||||
|
|
||||||
|
- type: entity
|
||||||
|
parent: ClothingHeadHatFedoraBrown
|
||||||
|
id: ClothingHeadHatDetGadget
|
||||||
|
name: go go hat
|
||||||
|
description: A novel hat with a built in toolkit. Automatically stores and retrieves items at the say of a phrase!
|
||||||
|
components:
|
||||||
|
- type: Tag
|
||||||
|
tags: [] # ignore "WhitelistChameleon" tag
|
||||||
|
- type: TriggerOnVoice
|
||||||
|
keyPhrase: "go go gadget"
|
||||||
|
listenRange: 0
|
||||||
|
- type: ActiveListener
|
||||||
|
range: 0
|
||||||
|
- type: StorageVoiceControl
|
||||||
|
allowedSlots:
|
||||||
|
- HEAD
|
||||||
|
- type: Storage
|
||||||
|
showVerb: false
|
||||||
|
grid:
|
||||||
|
- 0,0,6,3
|
||||||
|
maxItemSize: Small
|
||||||
|
blacklist:
|
||||||
|
tags:
|
||||||
|
- HighRiskItem # no hiding objectives or trolling nukies
|
||||||
|
- FakeNukeDisk # no disk checking
|
||||||
|
- QuantumSpinInverter # avoid the morbillionth QSI bug
|
||||||
|
- type: ContainerContainer
|
||||||
|
containers:
|
||||||
|
storagebase: !type:Container
|
||||||
|
ents: [ ]
|
||||||
|
|||||||
Reference in New Issue
Block a user