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
[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()
{
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))
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;
[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;
{
base.Initialize();
SubscribeLocalEvent<VoiceMaskComponent, InventoryRelayedEvent<TransformSpeakerNameEvent>>(OnTransformSpeakerName);
+ SubscribeLocalEvent<VoiceMaskComponent, LockToggledEvent>(OnLockToggled);
SubscribeLocalEvent<VoiceMaskComponent, VoiceMaskChangeNameMessage>(OnChangeName);
SubscribeLocalEvent<VoiceMaskComponent, VoiceMaskChangeVerbMessage>(OnChangeVerb);
SubscribeLocalEvent<VoiceMaskComponent, ClothingGotEquippedEvent>(OnEquip);
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)
{
#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);
}
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;
[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 =
{
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
if (ent.Comp.Activated)
{
- var ev = new ItemToggleActivateAttemptEvent(args.User);
+ var ev = new ItemToggleDeactivateAttemptEvent(args.User);
RaiseLocalEvent(ent.Owner, ref ev);
if (ev.Cancelled)
}
else
{
- var ev = new ItemToggleDeactivateAttemptEvent(args.User);
+ var ev = new ItemToggleActivateAttemptEvent(args.User);
RaiseLocalEvent(ent.Owner, ref ev);
if (ev.Cancelled)
/// <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";
}
[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>
/// 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),
};
/// 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)
};
{
[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()
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);
}
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",
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.
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()
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);
}
/// 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.
/// </summary>
[DataField]
public SoundSpecifier? AccessDeniedSound = new SoundPathSpecifier("/Audio/Machines/custom_deny.ogg");
+
+ [DataField]
+ public LocId? Popup = "entity-storage-component-locked-message";
}
--- /dev/null
+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);
+ }
+}
--- /dev/null
+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;
--- /dev/null
+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,
+}
/// </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";
}
--- /dev/null
+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;
+ }
+ }
+}
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;
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)
args.Verbs.Add(new AlternativeVerb
{
- Text = Loc.GetString("trigger-on-voice-clear"),
+ Text = Loc.GetString(ent.Comp.ClearRecordingVerb),
Act = () =>
{
ClearRecording(ent);
}
}
- 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)
// 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
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;
+ }
}
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
--- /dev/null
+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}"
- type: entity
- parent: ClothingBackpack
+ parent: [ClothingBackpack, BaseChameleon]
id: ClothingBackpackChameleon
name: backpack
description: You wear this on your back and put items into it.
- 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.
- type: ChameleonClothing
slot: [ears]
default: ClothingHeadsetGrey
- - type: UserInterface
- interfaces:
- enum.ChameleonUiKey.Key:
- type: ChameleonBoundUserInterface
- type: entity
- parent: ClothingEyesBase
+ parent: [ClothingEyesBase, BaseChameleon]
id: ClothingEyesChameleon # no flash immunity, sorry
name: sun glasses
description: Useful both for security and cargonia.
components:
- type: Tag
tags: # intentionally no WhitelistChameleon tag
- - PetWearable
+ - PetWearable
- type: Sprite
sprite: Clothing/Eyes/Glasses/sunglasses.rsi
- type: Clothing
- type: ChameleonClothing
slot: [eyes]
default: ClothingEyesGlassesSunglasses
- - type: UserInterface
- interfaces:
- enum.ChameleonUiKey.Key:
- type: ChameleonBoundUserInterface
-
- 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.
- type: Fiber
fiberMaterial: fibers-chameleon
- type: FingerprintMask
- - type: UserInterface
- interfaces:
- enum.ChameleonUiKey.Key:
- type: ChameleonBoundUserInterface
- type: entity
parent: ClothingHandsChameleon
- type: entity
- parent: ClothingHeadBase
+ parent: [ClothingHeadBase, BaseChameleon]
id: ClothingHeadHatChameleon
name: beret
description: A beret, an artists favorite headwear.
- type: ChameleonClothing
slot: [HEAD]
default: ClothingHeadHatBeret
- - type: UserInterface
- interfaces:
- enum.ChameleonUiKey.Key:
- type: ChameleonBoundUserInterface
- type: entity
parent: ClothingHeadHatFedoraBrown
- 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.
default: ClothingMaskGas
- type: BreathMask
- type: IdentityBlocker # need that for default ClothingMaskGas
- - type: UserInterface
- interfaces:
- enum.ChameleonUiKey.Key:
- type: ChameleonBoundUserInterface
- type: HideLayerClothing
slots:
- Snout
suffix: Voice Mask, Chameleon
components:
- type: VoiceMask
+ - type: UIRequiresLock
+ userInterfaceKeys:
+ - enum.ChameleonUiKey.Key
+ - enum.VoiceMaskUIKey.Key
+ accessDeniedSound: null
+ popup: null
- type: HideLayerClothing
slots:
- Snout
- 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.
- type: ChameleonClothing
slot: [NECK]
default: ClothingNeckScarfStripedRed
- - type: UserInterface
- interfaces:
- enum.ChameleonUiKey.Key:
- type: ChameleonBoundUserInterface
- type: entity
- parent: ClothingOuterBase
+ parent: [ClothingOuterBase, BaseChameleon]
id: ClothingOuterChameleon
name: vest
description: A thick vest with a rubbery, water-resistant shell.
- 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
sprite: Clothing/Shoes/Specific/wizard.rsi
- type: entity
- parent: ClothingShoesBase
+ parent: [ClothingShoesBase, BaseChameleon]
id: ClothingShoesChameleon
name: black shoes
suffix: Chameleon
- type: ChameleonClothing
slot: [FEET]
default: ClothingShoesColorBlack
- - type: UserInterface
- interfaces:
- enum.ChameleonUiKey.Key:
- type: ChameleonBoundUserInterface
- type: entity
parent: ClothingShoesChameleon
- type: entity
- parent: ClothingUniformBase
+ parent: [ClothingUniformBase, BaseChameleon]
id: ClothingUniformJumpsuitChameleon
name: black jumpsuit
description: A generic black jumpsuit with no rank markings.
- type: ChameleonClothing
slot: [innerclothing]
default: ClothingUniformJumpsuitColorBlack
- - type: UserInterface
- interfaces:
- enum.ChameleonUiKey.Key:
- type: ChameleonBoundUserInterface
- type: Lock
locked: true
unlockOnClick: false
- - type: ActivatableUIRequiresLock
+ - type: UIRequiresLock
+ userInterfaceKeys:
+ - enum.BorgUiKey.Key
- type: LockedWiresPanel
- type: Damageable
damageContainer: Silicon
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 ]
- MedTekCartridge
- type: entity
- parent: BasePDA
+ parent: [BasePDA, VoiceLock]
id: ChameleonPDA
name: passenger PDA
description: Why isn't it gray?
- type: entity
name: passenger ID card
- parent: IDCardStandard
+ parent: [IDCardStandard, BaseChameleon]
id: AgentIDCard
suffix: Agent
components:
- 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
--- /dev/null
+- 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
- type: DisarmMalus
- type: entity
- parent: Cane
+ parent: [Cane, VoiceLock]
id: CaneSheath
suffix: Empty
components:
interfaces:
enum.StorageUiKey.Key:
type: StorageBoundUserInterface
+ - type: ItemSlotsLock
+ slots:
+ - item
- type: ItemSlots
slots:
item:
- type: entity
name: pen
- parent: BaseMeleeWeaponEnergy
+ parent: [BaseMeleeWeaponEnergy, VoiceLock]
id: EnergyDagger
suffix: E-Dagger
description: 'A dark ink pen.'
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
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:
--- /dev/null
+# 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