Add voice locks to various hidden syndicate items (#39310)
This commit is contained in:
@@ -13,6 +13,7 @@ using Content.Server.Clothing.Systems;
|
||||
using Content.Server.Implants;
|
||||
using Content.Shared.Implants;
|
||||
using Content.Shared.Inventory;
|
||||
using Content.Shared.Lock;
|
||||
using Content.Shared.PDA;
|
||||
|
||||
namespace Content.Server.Access.Systems
|
||||
@@ -25,6 +26,7 @@ namespace Content.Server.Access.Systems
|
||||
[Dependency] private readonly IPrototypeManager _prototypeManager = default!;
|
||||
[Dependency] private readonly ChameleonClothingSystem _chameleon = default!;
|
||||
[Dependency] private readonly ChameleonControllerSystem _chamController = default!;
|
||||
[Dependency] private readonly LockSystem _lock = default!;
|
||||
|
||||
public override void Initialize()
|
||||
{
|
||||
@@ -79,7 +81,8 @@ namespace Content.Server.Access.Systems
|
||||
|
||||
private void OnAfterInteract(EntityUid uid, AgentIDCardComponent component, AfterInteractEvent args)
|
||||
{
|
||||
if (args.Target == null || !args.CanReach || !TryComp<AccessComponent>(args.Target, out var targetAccess) || !HasComp<IdCardComponent>(args.Target))
|
||||
if (args.Target == null || !args.CanReach || _lock.IsLocked(uid) ||
|
||||
!TryComp<AccessComponent>(args.Target, out var targetAccess) || !HasComp<IdCardComponent>(args.Target))
|
||||
return;
|
||||
|
||||
if (!TryComp<AccessComponent>(uid, out var access) || !HasComp<IdCardComponent>(uid))
|
||||
|
||||
@@ -5,11 +5,13 @@ using Content.Shared.Chat;
|
||||
using Content.Shared.Clothing;
|
||||
using Content.Shared.Database;
|
||||
using Content.Shared.Inventory;
|
||||
using Content.Shared.Lock;
|
||||
using Content.Shared.Popups;
|
||||
using Content.Shared.Preferences;
|
||||
using Content.Shared.Speech;
|
||||
using Content.Shared.VoiceMask;
|
||||
using Robust.Shared.Configuration;
|
||||
using Robust.Shared.Containers;
|
||||
using Robust.Shared.Prototypes;
|
||||
|
||||
namespace Content.Server.VoiceMask;
|
||||
@@ -22,6 +24,8 @@ public sealed partial class VoiceMaskSystem : EntitySystem
|
||||
[Dependency] private readonly ISharedAdminLogManager _adminLogger = default!;
|
||||
[Dependency] private readonly IPrototypeManager _proto = default!;
|
||||
[Dependency] private readonly SharedActionsSystem _actions = default!;
|
||||
[Dependency] private readonly LockSystem _lock = default!;
|
||||
[Dependency] private readonly SharedContainerSystem _container = default!;
|
||||
|
||||
// CCVar.
|
||||
private int _maxNameLength;
|
||||
@@ -30,6 +34,7 @@ public sealed partial class VoiceMaskSystem : EntitySystem
|
||||
{
|
||||
base.Initialize();
|
||||
SubscribeLocalEvent<VoiceMaskComponent, InventoryRelayedEvent<TransformSpeakerNameEvent>>(OnTransformSpeakerName);
|
||||
SubscribeLocalEvent<VoiceMaskComponent, LockToggledEvent>(OnLockToggled);
|
||||
SubscribeLocalEvent<VoiceMaskComponent, VoiceMaskChangeNameMessage>(OnChangeName);
|
||||
SubscribeLocalEvent<VoiceMaskComponent, VoiceMaskChangeVerbMessage>(OnChangeVerb);
|
||||
SubscribeLocalEvent<VoiceMaskComponent, ClothingGotEquippedEvent>(OnEquip);
|
||||
@@ -44,6 +49,14 @@ public sealed partial class VoiceMaskSystem : EntitySystem
|
||||
args.Args.SpeechVerb = entity.Comp.VoiceMaskSpeechVerb ?? args.Args.SpeechVerb;
|
||||
}
|
||||
|
||||
private void OnLockToggled(Entity<VoiceMaskComponent> ent, ref LockToggledEvent args)
|
||||
{
|
||||
if (args.Locked)
|
||||
_actions.RemoveAction(ent.Comp.ActionEntity);
|
||||
else if (_container.TryGetContainingContainer(ent.Owner, out var container))
|
||||
_actions.AddAction(container.Owner, ref ent.Comp.ActionEntity, ent.Comp.Action, ent);
|
||||
}
|
||||
|
||||
#region User inputs from UI
|
||||
private void OnChangeVerb(Entity<VoiceMaskComponent> entity, ref VoiceMaskChangeVerbMessage msg)
|
||||
{
|
||||
@@ -78,6 +91,9 @@ public sealed partial class VoiceMaskSystem : EntitySystem
|
||||
#region UI
|
||||
private void OnEquip(EntityUid uid, VoiceMaskComponent component, ClothingGotEquippedEvent args)
|
||||
{
|
||||
if (_lock.IsLocked(uid))
|
||||
return;
|
||||
|
||||
_actions.AddAction(args.Wearer, ref component.ActionEntity, component.Action, uid);
|
||||
}
|
||||
|
||||
|
||||
@@ -5,6 +5,7 @@ using Content.Shared.Contraband;
|
||||
using Content.Shared.Inventory;
|
||||
using Content.Shared.Inventory.Events;
|
||||
using Content.Shared.Item;
|
||||
using Content.Shared.Lock;
|
||||
using Content.Shared.Tag;
|
||||
using Content.Shared.Verbs;
|
||||
using Robust.Shared.Prototypes;
|
||||
@@ -23,6 +24,7 @@ public abstract class SharedChameleonClothingSystem : EntitySystem
|
||||
[Dependency] private readonly SharedAppearanceSystem _appearance = default!;
|
||||
[Dependency] private readonly TagSystem _tag = default!;
|
||||
[Dependency] protected readonly IGameTiming _timing = default!;
|
||||
[Dependency] private readonly LockSystem _lock = default!;
|
||||
|
||||
private static readonly SlotFlags[] IgnoredSlots =
|
||||
{
|
||||
@@ -122,7 +124,7 @@ public abstract class SharedChameleonClothingSystem : EntitySystem
|
||||
|
||||
private void OnVerb(Entity<ChameleonClothingComponent> ent, ref GetVerbsEvent<InteractionVerb> args)
|
||||
{
|
||||
if (!args.CanAccess || !args.CanInteract || ent.Comp.User != args.User)
|
||||
if (!args.CanAccess || !args.CanInteract || _lock.IsLocked(ent.Owner))
|
||||
return;
|
||||
|
||||
// Can't pass args from a ref event inside of lambdas
|
||||
|
||||
@@ -78,7 +78,7 @@ public sealed class ItemToggleSystem : EntitySystem
|
||||
|
||||
if (ent.Comp.Activated)
|
||||
{
|
||||
var ev = new ItemToggleActivateAttemptEvent(args.User);
|
||||
var ev = new ItemToggleDeactivateAttemptEvent(args.User);
|
||||
RaiseLocalEvent(ent.Owner, ref ev);
|
||||
|
||||
if (ev.Cancelled)
|
||||
@@ -86,7 +86,7 @@ public sealed class ItemToggleSystem : EntitySystem
|
||||
}
|
||||
else
|
||||
{
|
||||
var ev = new ItemToggleDeactivateAttemptEvent(args.User);
|
||||
var ev = new ItemToggleActivateAttemptEvent(args.User);
|
||||
RaiseLocalEvent(ent.Owner, ref ev);
|
||||
|
||||
if (ev.Cancelled)
|
||||
|
||||
@@ -5,13 +5,19 @@ namespace Content.Shared.Lock;
|
||||
/// <summary>
|
||||
/// This is used for toggleable items that require the entity to have a lock in a certain state.
|
||||
/// </summary>
|
||||
[RegisterComponent, NetworkedComponent, Access(typeof(LockSystem))]
|
||||
[RegisterComponent, NetworkedComponent, AutoGenerateComponentState, Access(typeof(LockSystem))]
|
||||
public sealed partial class ItemToggleRequiresLockComponent : Component
|
||||
{
|
||||
/// <summary>
|
||||
/// TRUE: the lock must be locked to toggle the item.
|
||||
/// FALSE: the lock must be unlocked to toggle the item.
|
||||
/// </summary>
|
||||
[DataField]
|
||||
[DataField, AutoNetworkedField]
|
||||
public bool RequireLocked;
|
||||
|
||||
/// <summary>
|
||||
/// Popup text for when someone tries to toggle the item, but it's locked. If null, no popup will be shown.
|
||||
/// </summary>
|
||||
[DataField]
|
||||
public LocId? LockedPopup = "lock-comp-generic-fail";
|
||||
}
|
||||
|
||||
@@ -21,6 +21,18 @@ public sealed partial class LockComponent : Component
|
||||
[AutoNetworkedField]
|
||||
public bool Locked = true;
|
||||
|
||||
/// <summary>
|
||||
/// If true, will show verbs to lock and unlock the item. Otherwise, it will not.
|
||||
/// </summary>
|
||||
[DataField, AutoNetworkedField]
|
||||
public bool ShowLockVerbs = true;
|
||||
|
||||
/// <summary>
|
||||
/// If true will show examine text.
|
||||
/// </summary>
|
||||
[DataField, AutoNetworkedField]
|
||||
public bool ShowExamine = true;
|
||||
|
||||
/// <summary>
|
||||
/// Whether or not the lock is locked by simply clicking.
|
||||
/// </summary>
|
||||
@@ -44,7 +56,7 @@ public sealed partial class LockComponent : Component
|
||||
/// The sound played when unlocked.
|
||||
/// </summary>
|
||||
[DataField("unlockingSound"), ViewVariables(VVAccess.ReadWrite)]
|
||||
public SoundSpecifier UnlockSound = new SoundPathSpecifier("/Audio/Machines/door_lock_off.ogg")
|
||||
public SoundSpecifier? UnlockSound = new SoundPathSpecifier("/Audio/Machines/door_lock_off.ogg")
|
||||
{
|
||||
Params = AudioParams.Default.WithVolume(-5f),
|
||||
};
|
||||
@@ -53,7 +65,7 @@ public sealed partial class LockComponent : Component
|
||||
/// The sound played when locked.
|
||||
/// </summary>
|
||||
[DataField("lockingSound"), ViewVariables(VVAccess.ReadWrite)]
|
||||
public SoundSpecifier LockSound = new SoundPathSpecifier("/Audio/Machines/door_lock_on.ogg")
|
||||
public SoundSpecifier? LockSound = new SoundPathSpecifier("/Audio/Machines/door_lock_on.ogg")
|
||||
{
|
||||
Params = AudioParams.Default.WithVolume(-5f)
|
||||
};
|
||||
|
||||
@@ -28,12 +28,12 @@ public sealed class LockSystem : EntitySystem
|
||||
{
|
||||
[Dependency] private readonly AccessReaderSystem _accessReader = default!;
|
||||
[Dependency] private readonly ActionBlockerSystem _actionBlocker = default!;
|
||||
[Dependency] private readonly ActivatableUISystem _activatableUI = default!;
|
||||
[Dependency] private readonly EmagSystem _emag = default!;
|
||||
[Dependency] private readonly SharedAppearanceSystem _appearanceSystem = default!;
|
||||
[Dependency] private readonly SharedAudioSystem _audio = default!;
|
||||
[Dependency] private readonly SharedPopupSystem _sharedPopupSystem = default!;
|
||||
[Dependency] private readonly SharedDoAfterSystem _doAfter = default!;
|
||||
[Dependency] private readonly SharedUserInterfaceSystem _ui = default!;
|
||||
|
||||
/// <inheritdoc />
|
||||
public override void Initialize()
|
||||
@@ -54,8 +54,8 @@ public sealed class LockSystem : EntitySystem
|
||||
SubscribeLocalEvent<LockedWiresPanelComponent, AttemptChangePanelEvent>(OnAttemptChangePanel);
|
||||
SubscribeLocalEvent<LockedAnchorableComponent, UnanchorAttemptEvent>(OnUnanchorAttempt);
|
||||
|
||||
SubscribeLocalEvent<ActivatableUIRequiresLockComponent, ActivatableUIOpenAttemptEvent>(OnUIOpenAttempt);
|
||||
SubscribeLocalEvent<ActivatableUIRequiresLockComponent, LockToggledEvent>(LockToggled);
|
||||
SubscribeLocalEvent<UIRequiresLockComponent, ActivatableUIOpenAttemptEvent>(OnUIOpenAttempt);
|
||||
SubscribeLocalEvent<UIRequiresLockComponent, LockToggledEvent>(LockToggled);
|
||||
|
||||
SubscribeLocalEvent<ItemToggleRequiresLockComponent, ItemToggleActivateAttemptEvent>(OnActivateAttempt);
|
||||
}
|
||||
@@ -96,6 +96,9 @@ public sealed class LockSystem : EntitySystem
|
||||
|
||||
private void OnExamined(EntityUid uid, LockComponent lockComp, ExaminedEvent args)
|
||||
{
|
||||
if (!lockComp.ShowExamine)
|
||||
return;
|
||||
|
||||
args.PushText(Loc.GetString(lockComp.Locked
|
||||
? "lock-comp-on-examined-is-locked"
|
||||
: "lock-comp-on-examined-is-unlocked",
|
||||
@@ -239,6 +242,20 @@ public sealed class LockSystem : EntitySystem
|
||||
return true;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Toggle the lock to locked if unlocked, and unlocked if locked.
|
||||
/// </summary>
|
||||
/// <param name="uid">Entity to toggle the lock state of.</param>
|
||||
/// <param name="user">The person trying to toggle the lock</param>
|
||||
/// <param name="lockComp">Entities lock comp (will be resolved)</param>
|
||||
public void ToggleLock(EntityUid uid, EntityUid? user, LockComponent? lockComp = null)
|
||||
{
|
||||
if (IsLocked((uid, lockComp)))
|
||||
Unlock(uid, user, lockComp);
|
||||
else
|
||||
Lock(uid, user, lockComp);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Returns true if the entity is locked.
|
||||
/// Entities with no lock component are considered unlocked.
|
||||
@@ -287,7 +304,7 @@ public sealed class LockSystem : EntitySystem
|
||||
|
||||
private void AddToggleLockVerb(EntityUid uid, LockComponent component, GetVerbsEvent<AlternativeVerb> args)
|
||||
{
|
||||
if (!args.CanAccess || !args.CanInteract || !args.CanComplexInteract)
|
||||
if (!args.CanAccess || !args.CanInteract || !args.CanComplexInteract || !component.ShowLockVerbs)
|
||||
return;
|
||||
|
||||
AlternativeVerb verb = new()
|
||||
@@ -394,41 +411,54 @@ public sealed class LockSystem : EntitySystem
|
||||
args.Cancel();
|
||||
}
|
||||
|
||||
private void OnUIOpenAttempt(EntityUid uid, ActivatableUIRequiresLockComponent component, ActivatableUIOpenAttemptEvent args)
|
||||
private void OnUIOpenAttempt(EntityUid uid, UIRequiresLockComponent component, ActivatableUIOpenAttemptEvent args)
|
||||
{
|
||||
if (args.Cancelled)
|
||||
return;
|
||||
|
||||
if (TryComp<LockComponent>(uid, out var lockComp) && lockComp.Locked != component.RequireLocked)
|
||||
{
|
||||
args.Cancel();
|
||||
if (lockComp.Locked)
|
||||
{
|
||||
_sharedPopupSystem.PopupClient(Loc.GetString("entity-storage-component-locked-message"), uid, args.User);
|
||||
}
|
||||
if (!TryComp<LockComponent>(uid, out var lockComp) || lockComp.Locked == component.RequireLocked)
|
||||
return;
|
||||
|
||||
_audio.PlayPredicted(component.AccessDeniedSound, uid, args.User);
|
||||
args.Cancel();
|
||||
if (lockComp.Locked && component.Popup != null)
|
||||
{
|
||||
_sharedPopupSystem.PopupClient(Loc.GetString(component.Popup), uid, args.User);
|
||||
}
|
||||
|
||||
_audio.PlayPredicted(component.AccessDeniedSound, uid, args.User);
|
||||
}
|
||||
|
||||
private void LockToggled(EntityUid uid, ActivatableUIRequiresLockComponent component, LockToggledEvent args)
|
||||
private void LockToggled(EntityUid uid, UIRequiresLockComponent component, LockToggledEvent args)
|
||||
{
|
||||
if (!TryComp<LockComponent>(uid, out var lockComp) || lockComp.Locked == component.RequireLocked)
|
||||
return;
|
||||
|
||||
_activatableUI.CloseAll(uid);
|
||||
if (component.UserInterfaceKeys == null)
|
||||
{
|
||||
_ui.CloseUis(uid);
|
||||
return;
|
||||
}
|
||||
|
||||
foreach (var key in component.UserInterfaceKeys)
|
||||
{
|
||||
_ui.CloseUi(uid, key);
|
||||
}
|
||||
}
|
||||
|
||||
private void OnActivateAttempt(EntityUid uid, ItemToggleRequiresLockComponent component, ref ItemToggleActivateAttemptEvent args)
|
||||
{
|
||||
if (args.Cancelled)
|
||||
return;
|
||||
|
||||
if (TryComp<LockComponent>(uid, out var lockComp) && lockComp.Locked != component.RequireLocked)
|
||||
if (!TryComp<LockComponent>(uid, out var lockComp) || lockComp.Locked == component.RequireLocked)
|
||||
return;
|
||||
|
||||
args.Cancelled = true;
|
||||
|
||||
if (lockComp.Locked && component.LockedPopup != null)
|
||||
{
|
||||
args.Cancelled = true;
|
||||
if (lockComp.Locked)
|
||||
_sharedPopupSystem.PopupClient(Loc.GetString("lock-comp-generic-fail",
|
||||
("target", Identity.Entity(uid, EntityManager))),
|
||||
_sharedPopupSystem.PopupClient(Loc.GetString(component.LockedPopup,
|
||||
("target", Identity.Entity(uid, EntityManager))),
|
||||
uid,
|
||||
args.User);
|
||||
}
|
||||
|
||||
@@ -7,8 +7,15 @@ namespace Content.Shared.Lock;
|
||||
/// This is used for activatable UIs that require the entity to have a lock in a certain state.
|
||||
/// </summary>
|
||||
[RegisterComponent, NetworkedComponent, Access(typeof(LockSystem))]
|
||||
public sealed partial class ActivatableUIRequiresLockComponent : Component
|
||||
public sealed partial class UIRequiresLockComponent : Component
|
||||
{
|
||||
/// <summary>
|
||||
/// UIs that are locked behind this component.
|
||||
/// If null, will close all UIs.
|
||||
/// </summary>
|
||||
[DataField]
|
||||
public List<Enum>? UserInterfaceKeys;
|
||||
|
||||
/// <summary>
|
||||
/// TRUE: the lock must be locked to access the UI.
|
||||
/// FALSE: the lock must be unlocked to access the UI.
|
||||
@@ -21,4 +28,7 @@ public sealed partial class ActivatableUIRequiresLockComponent : Component
|
||||
/// </summary>
|
||||
[DataField]
|
||||
public SoundSpecifier? AccessDeniedSound = new SoundPathSpecifier("/Audio/Machines/custom_deny.ogg");
|
||||
|
||||
[DataField]
|
||||
public LocId? Popup = "entity-storage-component-locked-message";
|
||||
}
|
||||
30
Content.Shared/SecretLocks/SharedVoiceTriggerLockSystem.cs
Normal file
30
Content.Shared/SecretLocks/SharedVoiceTriggerLockSystem.cs
Normal file
@@ -0,0 +1,30 @@
|
||||
using Content.Shared.Item.ItemToggle;
|
||||
using Content.Shared.Lock;
|
||||
using Content.Shared.Trigger.Components.Triggers;
|
||||
|
||||
namespace Content.Shared.SecretLocks;
|
||||
|
||||
public sealed partial class SharedVoiceTriggerLockSystem : EntitySystem
|
||||
{
|
||||
[Dependency] private readonly ItemToggleSystem _toggle = default!;
|
||||
|
||||
public override void Initialize()
|
||||
{
|
||||
base.Initialize();
|
||||
|
||||
SubscribeLocalEvent<VoiceTriggerLockComponent, LockToggledEvent>(OnLockToggled);
|
||||
}
|
||||
|
||||
private void OnLockToggled(Entity<VoiceTriggerLockComponent> ent, ref LockToggledEvent args)
|
||||
{
|
||||
if (!TryComp<TriggerOnVoiceComponent>(ent.Owner, out var triggerComp))
|
||||
return;
|
||||
|
||||
triggerComp.ShowVerbs = !args.Locked;
|
||||
triggerComp.ShowExamine = !args.Locked;
|
||||
|
||||
_toggle.TryDeactivate(ent.Owner, null, true, false);
|
||||
|
||||
Dirty(ent.Owner, triggerComp);
|
||||
}
|
||||
}
|
||||
10
Content.Shared/SecretLocks/VoiceTriggerLockComponent.cs
Normal file
10
Content.Shared/SecretLocks/VoiceTriggerLockComponent.cs
Normal file
@@ -0,0 +1,10 @@
|
||||
using Robust.Shared.GameStates;
|
||||
|
||||
namespace Content.Shared.SecretLocks;
|
||||
|
||||
/// <summary>
|
||||
/// "Locks" items (Doesn't actually lock them but just switches various settings) so its not possible to tell
|
||||
/// the item is triggered by a voice activation.
|
||||
/// </summary>
|
||||
[RegisterComponent, NetworkedComponent]
|
||||
public sealed partial class VoiceTriggerLockComponent : Component;
|
||||
@@ -0,0 +1,19 @@
|
||||
using Robust.Shared.GameStates;
|
||||
using Robust.Shared.Serialization;
|
||||
|
||||
namespace Content.Shared.Trigger.Components.Effects;
|
||||
|
||||
[RegisterComponent, NetworkedComponent, AutoGenerateComponentState]
|
||||
public sealed partial class LockOnTriggerComponent : BaseXOnTriggerComponent
|
||||
{
|
||||
[DataField, AutoNetworkedField]
|
||||
public LockAction LockOnTrigger = LockAction.Toggle;
|
||||
}
|
||||
|
||||
[Serializable, NetSerializable]
|
||||
public enum LockAction
|
||||
{
|
||||
Lock = 0,
|
||||
Unlock = 1,
|
||||
Toggle = 2,
|
||||
}
|
||||
@@ -44,4 +44,52 @@ public sealed partial class TriggerOnVoiceComponent : BaseTriggerOnXComponent
|
||||
/// </summary>
|
||||
[DataField, AutoNetworkedField]
|
||||
public int MaxLength = 50;
|
||||
|
||||
/// <summary>
|
||||
/// When examining the item, should it show information about what word is recorded?
|
||||
/// </summary>
|
||||
[DataField, AutoNetworkedField]
|
||||
public bool ShowExamine = true;
|
||||
|
||||
/// <summary>
|
||||
/// Should there be verbs that allow re-recording of the trigger word?
|
||||
/// </summary>
|
||||
[DataField, AutoNetworkedField]
|
||||
public bool ShowVerbs = true;
|
||||
|
||||
/// <summary>
|
||||
/// The verb text that is shown when you can start recording a message.
|
||||
/// </summary>
|
||||
[DataField]
|
||||
public LocId StartRecordingVerb = "trigger-on-voice-record";
|
||||
|
||||
/// <summary>
|
||||
/// The verb text that is shown when you can stop recording a message.
|
||||
/// </summary>
|
||||
[DataField]
|
||||
public LocId StopRecordingVerb = "trigger-on-voice-stop";
|
||||
|
||||
/// <summary>
|
||||
/// Tooltip that appears when hovering over the stop or start recording verbs.
|
||||
/// </summary>
|
||||
[DataField]
|
||||
public LocId? RecordingVerbMessage;
|
||||
|
||||
/// <summary>
|
||||
/// The verb text that is shown when you can clear a recording.
|
||||
/// </summary>
|
||||
[DataField]
|
||||
public LocId ClearRecordingVerb = "trigger-on-voice-clear";
|
||||
|
||||
/// <summary>
|
||||
/// The loc string that is shown when inspecting an uninitialized voice trigger.
|
||||
/// </summary>
|
||||
[DataField]
|
||||
public LocId? InspectUninitializedLoc = "trigger-on-voice-uninitialized";
|
||||
|
||||
/// <summary>
|
||||
/// The loc string to use when inspecting voice trigger. Will also include the triggering phrase
|
||||
/// </summary>
|
||||
[DataField]
|
||||
public LocId? InspectInitializedLoc = "trigger-on-voice-examine";
|
||||
}
|
||||
|
||||
35
Content.Shared/Trigger/Systems/LockOnTriggerSystem.cs
Normal file
35
Content.Shared/Trigger/Systems/LockOnTriggerSystem.cs
Normal file
@@ -0,0 +1,35 @@
|
||||
using Content.Shared.Lock;
|
||||
using Content.Shared.Trigger.Components.Effects;
|
||||
|
||||
namespace Content.Shared.Trigger.Systems;
|
||||
|
||||
public sealed class LockOnTriggerSystem : EntitySystem
|
||||
{
|
||||
[Dependency] private readonly LockSystem _lock = default!;
|
||||
|
||||
public override void Initialize()
|
||||
{
|
||||
base.Initialize();
|
||||
|
||||
SubscribeLocalEvent<LockOnTriggerComponent, TriggerEvent>(OnTrigger);
|
||||
}
|
||||
|
||||
private void OnTrigger(Entity<LockOnTriggerComponent> ent, ref TriggerEvent args)
|
||||
{
|
||||
if (args.Key != null && !ent.Comp.KeysIn.Contains(args.Key))
|
||||
return;
|
||||
|
||||
switch (ent.Comp.LockOnTrigger)
|
||||
{
|
||||
case LockAction.Lock:
|
||||
_lock.Lock(ent.Owner, args.User);
|
||||
break;
|
||||
case LockAction.Unlock:
|
||||
_lock.Unlock(ent, args.User);
|
||||
break;
|
||||
case LockAction.Toggle:
|
||||
_lock.ToggleLock(ent, args.User);
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -25,15 +25,21 @@ public sealed partial class TriggerSystem
|
||||
RemCompDeferred<ActiveListenerComponent>(ent);
|
||||
}
|
||||
|
||||
private void OnVoiceExamine(Entity<TriggerOnVoiceComponent> ent, ref ExaminedEvent args)
|
||||
private void OnVoiceExamine(EntityUid uid, TriggerOnVoiceComponent component, ExaminedEvent args)
|
||||
{
|
||||
if (args.IsInDetailsRange)
|
||||
if (!args.IsInDetailsRange || !component.ShowExamine)
|
||||
return;
|
||||
|
||||
if (component.InspectUninitializedLoc != null && string.IsNullOrWhiteSpace(component.KeyPhrase))
|
||||
{
|
||||
args.PushText(string.IsNullOrWhiteSpace(ent.Comp.KeyPhrase)
|
||||
? Loc.GetString("trigger-on-voice-uninitialized")
|
||||
: Loc.GetString("trigger-on-voice-examine", ("keyphrase", ent.Comp.KeyPhrase)));
|
||||
args.PushText(Loc.GetString(component.InspectUninitializedLoc));
|
||||
}
|
||||
else if (component.InspectInitializedLoc != null && !string.IsNullOrWhiteSpace(component.KeyPhrase))
|
||||
{
|
||||
args.PushText(Loc.GetString(component.InspectInitializedLoc.Value, ("keyphrase", component.KeyPhrase)));
|
||||
}
|
||||
}
|
||||
|
||||
private void OnListen(Entity<TriggerOnVoiceComponent> ent, ref ListenEvent args)
|
||||
{
|
||||
var component = ent.Comp;
|
||||
@@ -71,13 +77,13 @@ public sealed partial class TriggerSystem
|
||||
|
||||
private void OnVoiceGetAltVerbs(Entity<TriggerOnVoiceComponent> ent, ref GetVerbsEvent<AlternativeVerb> args)
|
||||
{
|
||||
if (!args.CanInteract || !args.CanAccess)
|
||||
if (!args.CanInteract || !args.CanAccess || !ent.Comp.ShowVerbs)
|
||||
return;
|
||||
|
||||
var user = args.User;
|
||||
args.Verbs.Add(new AlternativeVerb
|
||||
{
|
||||
Text = Loc.GetString(ent.Comp.IsRecording ? "trigger-on-voice-stop" : "trigger-on-voice-record"),
|
||||
Text = Loc.GetString(ent.Comp.IsRecording ? ent.Comp.StopRecordingVerb : ent.Comp.StartRecordingVerb),
|
||||
Act = () =>
|
||||
{
|
||||
if (ent.Comp.IsRecording)
|
||||
@@ -93,7 +99,7 @@ public sealed partial class TriggerSystem
|
||||
|
||||
args.Verbs.Add(new AlternativeVerb
|
||||
{
|
||||
Text = Loc.GetString("trigger-on-voice-clear"),
|
||||
Text = Loc.GetString(ent.Comp.ClearRecordingVerb),
|
||||
Act = () =>
|
||||
{
|
||||
ClearRecording(ent);
|
||||
|
||||
@@ -116,7 +116,7 @@ public sealed partial class ActivatableUISystem : EntitySystem
|
||||
}
|
||||
}
|
||||
|
||||
return args.CanInteract || HasComp<GhostComponent>(args.User) && !component.BlockSpectators;
|
||||
return (args.CanInteract || HasComp<GhostComponent>(args.User) && !component.BlockSpectators) && !RaiseCanOpenEventChecks(args.User, uid);
|
||||
}
|
||||
|
||||
private void OnUseInHand(EntityUid uid, ActivatableUIComponent component, UseInHandEvent args)
|
||||
@@ -225,11 +225,7 @@ public sealed partial class ActivatableUISystem : EntitySystem
|
||||
|
||||
// If we've gotten this far, fire a cancellable event that indicates someone is about to activate this.
|
||||
// This is so that stuff can require further conditions (like power).
|
||||
var oae = new ActivatableUIOpenAttemptEvent(user);
|
||||
var uae = new UserOpenActivatableUIAttemptEvent(user, uiEntity);
|
||||
RaiseLocalEvent(user, uae);
|
||||
RaiseLocalEvent(uiEntity, oae);
|
||||
if (oae.Cancelled || uae.Cancelled)
|
||||
if (RaiseCanOpenEventChecks(user, uiEntity))
|
||||
return false;
|
||||
|
||||
// Give the UI an opportunity to prepare itself if it needs to do anything
|
||||
@@ -286,4 +282,15 @@ public sealed partial class ActivatableUISystem : EntitySystem
|
||||
if (ent.Comp.InHandsOnly)
|
||||
CloseAll(ent, ent);
|
||||
}
|
||||
|
||||
private bool RaiseCanOpenEventChecks(EntityUid user, EntityUid uiEntity)
|
||||
{
|
||||
// If we've gotten this far, fire a cancellable event that indicates someone is about to activate this.
|
||||
// This is so that stuff can require further conditions (like power).
|
||||
var oae = new ActivatableUIOpenAttemptEvent(user);
|
||||
var uae = new UserOpenActivatableUIAttemptEvent(user, uiEntity);
|
||||
RaiseLocalEvent(user, uae);
|
||||
RaiseLocalEvent(uiEntity, oae);
|
||||
return oae.Cancelled || uae.Cancelled;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -8,3 +8,5 @@ agent-id-card-current-name = Name:
|
||||
agent-id-card-current-job = Job:
|
||||
agent-id-card-job-icon-label = Job icon:
|
||||
agent-id-menu-title = Agent ID Card
|
||||
|
||||
agent-id-open-ui-verb = Change settings
|
||||
|
||||
5
Resources/Locale/en-US/locks/voice-trigger-lock.ftl
Normal file
5
Resources/Locale/en-US/locks/voice-trigger-lock.ftl
Normal file
@@ -0,0 +1,5 @@
|
||||
voice-trigger-lock-verb-record = Record lock phrase
|
||||
voice-trigger-lock-verb-message = Locking the item will disable features that reveal its true nature!
|
||||
|
||||
voice-trigger-lock-on-uninitialized = The display is blank
|
||||
voice-trigger-lock-on-examine = The display shows the passphrase: "{$keyphrase}"
|
||||
@@ -1,5 +1,5 @@
|
||||
- type: entity
|
||||
parent: ClothingBackpack
|
||||
parent: [ClothingBackpack, BaseChameleon]
|
||||
id: ClothingBackpackChameleon
|
||||
name: backpack
|
||||
description: You wear this on your back and put items into it.
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
- type: entity
|
||||
parent: ClothingHeadsetGrey
|
||||
parent: [ClothingHeadsetGrey, BaseChameleon]
|
||||
id: ClothingHeadsetChameleon
|
||||
name: passenger headset
|
||||
description: An updated, modular intercom that fits over the head. Takes encryption keys.
|
||||
@@ -14,7 +14,3 @@
|
||||
- type: ChameleonClothing
|
||||
slot: [ears]
|
||||
default: ClothingHeadsetGrey
|
||||
- type: UserInterface
|
||||
interfaces:
|
||||
enum.ChameleonUiKey.Key:
|
||||
type: ChameleonBoundUserInterface
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
- type: entity
|
||||
parent: ClothingEyesBase
|
||||
parent: [ClothingEyesBase, BaseChameleon]
|
||||
id: ClothingEyesChameleon # no flash immunity, sorry
|
||||
name: sun glasses
|
||||
description: Useful both for security and cargonia.
|
||||
@@ -7,7 +7,7 @@
|
||||
components:
|
||||
- type: Tag
|
||||
tags: # intentionally no WhitelistChameleon tag
|
||||
- PetWearable
|
||||
- PetWearable
|
||||
- type: Sprite
|
||||
sprite: Clothing/Eyes/Glasses/sunglasses.rsi
|
||||
- type: Clothing
|
||||
@@ -15,8 +15,3 @@
|
||||
- type: ChameleonClothing
|
||||
slot: [eyes]
|
||||
default: ClothingEyesGlassesSunglasses
|
||||
- type: UserInterface
|
||||
interfaces:
|
||||
enum.ChameleonUiKey.Key:
|
||||
type: ChameleonBoundUserInterface
|
||||
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
- type: entity
|
||||
parent: ClothingHandsButcherable
|
||||
parent: [ClothingHandsButcherable, BaseChameleon]
|
||||
id: ClothingHandsChameleon # doesn't protect from electricity or heat
|
||||
name: black gloves
|
||||
description: Regular black gloves that do not keep you from frying.
|
||||
@@ -17,10 +17,6 @@
|
||||
- type: Fiber
|
||||
fiberMaterial: fibers-chameleon
|
||||
- type: FingerprintMask
|
||||
- type: UserInterface
|
||||
interfaces:
|
||||
enum.ChameleonUiKey.Key:
|
||||
type: ChameleonBoundUserInterface
|
||||
|
||||
- type: entity
|
||||
parent: ClothingHandsChameleon
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
- type: entity
|
||||
parent: ClothingHeadBase
|
||||
parent: [ClothingHeadBase, BaseChameleon]
|
||||
id: ClothingHeadHatChameleon
|
||||
name: beret
|
||||
description: A beret, an artists favorite headwear.
|
||||
@@ -14,10 +14,6 @@
|
||||
- type: ChameleonClothing
|
||||
slot: [HEAD]
|
||||
default: ClothingHeadHatBeret
|
||||
- type: UserInterface
|
||||
interfaces:
|
||||
enum.ChameleonUiKey.Key:
|
||||
type: ChameleonBoundUserInterface
|
||||
|
||||
- type: entity
|
||||
parent: ClothingHeadHatFedoraBrown
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
- type: entity
|
||||
parent: ClothingMaskBase
|
||||
parent: [ClothingMaskBase, BaseChameleon]
|
||||
id: ClothingMaskGasChameleon
|
||||
name: gas mask
|
||||
description: A face-covering mask that can be connected to an air supply.
|
||||
@@ -16,10 +16,6 @@
|
||||
default: ClothingMaskGas
|
||||
- type: BreathMask
|
||||
- type: IdentityBlocker # need that for default ClothingMaskGas
|
||||
- type: UserInterface
|
||||
interfaces:
|
||||
enum.ChameleonUiKey.Key:
|
||||
type: ChameleonBoundUserInterface
|
||||
- type: HideLayerClothing
|
||||
slots:
|
||||
- Snout
|
||||
@@ -30,6 +26,12 @@
|
||||
suffix: Voice Mask, Chameleon
|
||||
components:
|
||||
- type: VoiceMask
|
||||
- type: UIRequiresLock
|
||||
userInterfaceKeys:
|
||||
- enum.ChameleonUiKey.Key
|
||||
- enum.VoiceMaskUIKey.Key
|
||||
accessDeniedSound: null
|
||||
popup: null
|
||||
- type: HideLayerClothing
|
||||
slots:
|
||||
- Snout
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
- type: entity
|
||||
parent: ClothingNeckBase
|
||||
parent: [ClothingNeckBase, BaseChameleon]
|
||||
id: ClothingNeckChameleon
|
||||
name: striped red scarf
|
||||
description: A stylish striped red scarf. The perfect winter accessory for those with a keen fashion sense, and those who just can't handle a cold breeze on their necks.
|
||||
@@ -14,7 +14,3 @@
|
||||
- type: ChameleonClothing
|
||||
slot: [NECK]
|
||||
default: ClothingNeckScarfStripedRed
|
||||
- type: UserInterface
|
||||
interfaces:
|
||||
enum.ChameleonUiKey.Key:
|
||||
type: ChameleonBoundUserInterface
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
- type: entity
|
||||
parent: ClothingOuterBase
|
||||
parent: [ClothingOuterBase, BaseChameleon]
|
||||
id: ClothingOuterChameleon
|
||||
name: vest
|
||||
description: A thick vest with a rubbery, water-resistant shell.
|
||||
@@ -14,10 +14,6 @@
|
||||
- type: ChameleonClothing
|
||||
slot: [outerClothing]
|
||||
default: ClothingOuterVest
|
||||
- type: UserInterface
|
||||
interfaces:
|
||||
enum.ChameleonUiKey.Key:
|
||||
type: ChameleonBoundUserInterface
|
||||
- type: TemperatureProtection # Same as a basic winter coat.
|
||||
heatingCoefficient: 1.1
|
||||
coolingCoefficient: 0.1
|
||||
|
||||
@@ -163,7 +163,7 @@
|
||||
sprite: Clothing/Shoes/Specific/wizard.rsi
|
||||
|
||||
- type: entity
|
||||
parent: ClothingShoesBase
|
||||
parent: [ClothingShoesBase, BaseChameleon]
|
||||
id: ClothingShoesChameleon
|
||||
name: black shoes
|
||||
suffix: Chameleon
|
||||
@@ -197,10 +197,6 @@
|
||||
- type: ChameleonClothing
|
||||
slot: [FEET]
|
||||
default: ClothingShoesColorBlack
|
||||
- type: UserInterface
|
||||
interfaces:
|
||||
enum.ChameleonUiKey.Key:
|
||||
type: ChameleonBoundUserInterface
|
||||
|
||||
- type: entity
|
||||
parent: ClothingShoesChameleon
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
- type: entity
|
||||
parent: ClothingUniformBase
|
||||
parent: [ClothingUniformBase, BaseChameleon]
|
||||
id: ClothingUniformJumpsuitChameleon
|
||||
name: black jumpsuit
|
||||
description: A generic black jumpsuit with no rank markings.
|
||||
@@ -36,7 +36,3 @@
|
||||
- type: ChameleonClothing
|
||||
slot: [innerclothing]
|
||||
default: ClothingUniformJumpsuitColorBlack
|
||||
- type: UserInterface
|
||||
interfaces:
|
||||
enum.ChameleonUiKey.Key:
|
||||
type: ChameleonBoundUserInterface
|
||||
|
||||
@@ -167,7 +167,9 @@
|
||||
- type: Lock
|
||||
locked: true
|
||||
unlockOnClick: false
|
||||
- type: ActivatableUIRequiresLock
|
||||
- type: UIRequiresLock
|
||||
userInterfaceKeys:
|
||||
- enum.BorgUiKey.Key
|
||||
- type: LockedWiresPanel
|
||||
- type: Damageable
|
||||
damageContainer: Silicon
|
||||
|
||||
@@ -2320,6 +2320,7 @@
|
||||
types:
|
||||
Heat : 0.2 #per second, scales with temperature & other constants
|
||||
|
||||
# TODO: Make grenade penguin voice activated like the rest of the stealth items.
|
||||
- type: entity
|
||||
name: grenade penguin
|
||||
parent: [ MobPenguin, MobCombat, BaseSyndicateContraband ]
|
||||
|
||||
@@ -1467,7 +1467,7 @@
|
||||
- MedTekCartridge
|
||||
|
||||
- type: entity
|
||||
parent: BasePDA
|
||||
parent: [BasePDA, VoiceLock]
|
||||
id: ChameleonPDA
|
||||
name: passenger PDA
|
||||
description: Why isn't it gray?
|
||||
|
||||
@@ -580,7 +580,7 @@
|
||||
|
||||
- type: entity
|
||||
name: passenger ID card
|
||||
parent: IDCardStandard
|
||||
parent: [IDCardStandard, BaseChameleon]
|
||||
id: AgentIDCard
|
||||
suffix: Agent
|
||||
components:
|
||||
@@ -595,9 +595,11 @@
|
||||
- state: default
|
||||
- state: idpassenger
|
||||
- type: AgentIDCard
|
||||
- type: UIRequiresLock
|
||||
- type: ActivatableUI
|
||||
key: enum.AgentIDCardUiKey.Key
|
||||
inHandsOnly: true
|
||||
verbText: agent-id-open-ui-verb
|
||||
- type: Tag
|
||||
tags:
|
||||
- DoorBumpOpener
|
||||
|
||||
24
Resources/Prototypes/Entities/Objects/Specific/locks.yml
Normal file
24
Resources/Prototypes/Entities/Objects/Specific/locks.yml
Normal file
@@ -0,0 +1,24 @@
|
||||
- type: entity
|
||||
id: VoiceLock
|
||||
abstract: true
|
||||
components:
|
||||
- type: Lock
|
||||
locked: false
|
||||
showLockVerbs: false
|
||||
showExamine: false
|
||||
lockOnClick: false
|
||||
unlockOnClick: false
|
||||
useAccess: false
|
||||
unlockingSound: null # TODO: Maybe add sounds but just to the user?
|
||||
lockingSound: null
|
||||
lockTime: 0
|
||||
unlockTime: 0
|
||||
- type: TriggerOnVoice
|
||||
listenRange: 2 # more fun
|
||||
startRecordingVerb: voice-trigger-lock-verb-record
|
||||
recordingVerbMessage: voice-trigger-lock-verb-message
|
||||
inspectUninitializedLoc: voice-trigger-lock-on-uninitialized
|
||||
inspectInitializedLoc: voice-trigger-lock-on-examine
|
||||
- type: LockOnTrigger
|
||||
- type: ActiveListener
|
||||
- type: VoiceTriggerLock
|
||||
@@ -53,7 +53,7 @@
|
||||
- type: DisarmMalus
|
||||
|
||||
- type: entity
|
||||
parent: Cane
|
||||
parent: [Cane, VoiceLock]
|
||||
id: CaneSheath
|
||||
suffix: Empty
|
||||
components:
|
||||
@@ -69,6 +69,9 @@
|
||||
interfaces:
|
||||
enum.StorageUiKey.Key:
|
||||
type: StorageBoundUserInterface
|
||||
- type: ItemSlotsLock
|
||||
slots:
|
||||
- item
|
||||
- type: ItemSlots
|
||||
slots:
|
||||
item:
|
||||
|
||||
@@ -159,7 +159,7 @@
|
||||
|
||||
- type: entity
|
||||
name: pen
|
||||
parent: BaseMeleeWeaponEnergy
|
||||
parent: [BaseMeleeWeaponEnergy, VoiceLock]
|
||||
id: EnergyDagger
|
||||
suffix: E-Dagger
|
||||
description: 'A dark ink pen.'
|
||||
@@ -222,6 +222,16 @@
|
||||
damage:
|
||||
types:
|
||||
Blunt: 1
|
||||
- type: EmitSoundOnUse
|
||||
sound:
|
||||
path: /Audio/Items/pen_click.ogg
|
||||
params:
|
||||
volume: -4
|
||||
maxDistance: 2
|
||||
- type: UseDelay
|
||||
delay: 1.5
|
||||
- type: ItemToggleRequiresLock # TODO: FIX THIS VERB IS COOKED
|
||||
lockedPopup: null
|
||||
- type: Tag
|
||||
tags:
|
||||
- Write
|
||||
|
||||
@@ -37,7 +37,9 @@
|
||||
3: { state: can-o3, shader: "unshaded" }
|
||||
- type: ActivatableUI
|
||||
key: enum.GasCanisterUiKey.Key
|
||||
- type: ActivatableUIRequiresLock
|
||||
- type: UIRequiresLock
|
||||
userInterfaceKeys:
|
||||
- enum.GasCanisterUiKey.Key
|
||||
- type: UserInterface
|
||||
interfaces:
|
||||
enum.GasCanisterUiKey.Key:
|
||||
|
||||
15
Resources/Prototypes/chameleon.yml
Normal file
15
Resources/Prototypes/chameleon.yml
Normal file
@@ -0,0 +1,15 @@
|
||||
# for clothing that can be toggled, like magboots
|
||||
- type: entity
|
||||
parent: VoiceLock
|
||||
abstract: true
|
||||
id: BaseChameleon
|
||||
components:
|
||||
- type: UIRequiresLock
|
||||
userInterfaceKeys:
|
||||
- enum.ChameleonUiKey.Key
|
||||
accessDeniedSound: null
|
||||
popup: null
|
||||
- type: UserInterface
|
||||
interfaces:
|
||||
enum.ChameleonUiKey.Key:
|
||||
type: ChameleonBoundUserInterface
|
||||
Reference in New Issue
Block a user