From: beck-thompson <107373427+beck-thompson@users.noreply.github.com> Date: Tue, 26 Aug 2025 14:18:10 +0000 (-0700) Subject: Syndicate locks are now selectable (#39532) X-Git-Url: https://git.smokeofanarchy.ru/gitweb.cgi?a=commitdiff_plain;h=688c0b5884003b2f2cf81bcbb1842915851356b3;p=space-station-14.git Syndicate locks are now selectable (#39532) * Syndicate locks are now selectable * Minor tweaks * Make not syndicate themed * Address refview * review --------- Co-authored-by: slarticodefast <161409025+slarticodefast@users.noreply.github.com> --- diff --git a/Content.Shared/SelectableComponentAdder/SelectableComponentAdderComponent.cs b/Content.Shared/SelectableComponentAdder/SelectableComponentAdderComponent.cs new file mode 100644 index 0000000000..5b7ccffafb --- /dev/null +++ b/Content.Shared/SelectableComponentAdder/SelectableComponentAdderComponent.cs @@ -0,0 +1,77 @@ +using Robust.Shared.GameStates; +using Robust.Shared.Prototypes; +using Robust.Shared.Serialization; + +namespace Content.Shared.SelectableComponentAdder; + +/// +/// Brings up a verb menu that allows players to select components that will get added to the item with this component. +/// +[RegisterComponent, NetworkedComponent, AutoGenerateComponentState] +public sealed partial class SelectableComponentAdderComponent : Component +{ + /// + /// List of verb -> components to add for that verb when selected basically! + /// + [DataField(required: true)] + public List Entries = new(); + + /// + /// The amount of times players can make a selection and add a component. If null, there is no limit. + /// + [DataField, AutoNetworkedField] + public int? Selections; + + /// + /// The verb category name that will be used. + /// + [DataField, AutoNetworkedField] + public LocId VerbCategoryName = "selectable-component-adder-category-name"; +} + +[DataDefinition] +public sealed partial class ComponentAdderEntry +{ + /// + /// Name of the verb that will add the components in . + /// + [DataField(required: true)] + public LocId VerbName; + + /// + /// Popup to show when this option is selected. + /// + [DataField(required: true)] + public LocId? Popup; + + /// + /// List of all the components that will get added when the verb is selected. + /// + [DataField(required: true)] + public ComponentRegistry? ComponentsToAdd; + + /// + /// The type of behavior that occurs when the component(s) already exist on the entity. + /// + [DataField] + public ComponentExistsSetting ComponentExistsBehavior = ComponentExistsSetting.Skip; + + /// + /// The priorty of the verb in the list + /// + [DataField] + public int Priority; +} + +[Serializable, NetSerializable] +public enum ComponentExistsSetting : byte +{ + // If one of the components exist, skip adding it and continue adding the rest. + // If all components already exist, disable the verb. + Skip = 0, + // If a component already exists, replace it with the new one. + // The verb is always enabled. + Replace = 1, + // Disable the verb if any one of the components already exists. + Block = 2, +} diff --git a/Content.Shared/SelectableComponentAdder/SelectableComponentAdderSystem.cs b/Content.Shared/SelectableComponentAdder/SelectableComponentAdderSystem.cs new file mode 100644 index 0000000000..29619904d7 --- /dev/null +++ b/Content.Shared/SelectableComponentAdder/SelectableComponentAdderSystem.cs @@ -0,0 +1,95 @@ +using Content.Shared.Popups; +using Content.Shared.Verbs; +using Robust.Shared.Prototypes; + +namespace Content.Shared.SelectableComponentAdder; + +public sealed partial class SelectableComponentAdderSystem : EntitySystem +{ + [Dependency] private readonly SharedPopupSystem _popup = default!; + + public override void Initialize() + { + base.Initialize(); + + SubscribeLocalEvent>(OnGetVerb); + } + + private void OnGetVerb(Entity ent, ref GetVerbsEvent args) + { + if (!args.CanAccess || !args.CanInteract || ent.Comp.Selections <= 0) + return; + + var target = args.Target; + var user = args.User; + var verbCategory = new VerbCategory(ent.Comp.VerbCategoryName, null); + + foreach (var entry in ent.Comp.Entries) + { + var verb = new Verb + { + Priority = entry.Priority, + Category = verbCategory, + Disabled = CheckDisabled(target, entry.ComponentsToAdd, entry.ComponentExistsBehavior), + Act = () => + { + AddComponents(target, entry.ComponentsToAdd, entry.ComponentExistsBehavior); + ent.Comp.Selections--; + Dirty(ent); + if (entry.Popup == null) + return; + var message = Loc.GetString(entry.Popup.Value, ("target", target)); + _popup.PopupClient(message, target, user); + }, + Text = Loc.GetString(entry.VerbName), + }; + args.Verbs.Add(verb); + } + } + + private bool CheckDisabled(EntityUid target, ComponentRegistry? registry, ComponentExistsSetting setting) + { + if (registry == null) + return false; + + switch (setting) + { + case ComponentExistsSetting.Skip: + // disable the verb if all components already exist + foreach (var component in registry) + { + if (!EntityManager.HasComponent(target, Factory.GetComponent(component.Key).GetType())) + return false; + } + return true; + case ComponentExistsSetting.Replace: + // always allow the verb + return false; + case ComponentExistsSetting.Block: + // disable the verb if any component already exists. + foreach (var component in registry) + { + if (EntityManager.HasComponent(target, Factory.GetComponent(component.Key).GetType())) + return true; + } + return false; + default: + throw new NotImplementedException(); + } + } + + private void AddComponents(EntityUid target, ComponentRegistry? registry, ComponentExistsSetting setting) + { + if (registry == null || CheckDisabled(target, registry, setting)) + return; + + foreach (var component in registry) + { + if (EntityManager.HasComponent(target, Factory.GetComponent(component.Key).GetType()) && + setting is ComponentExistsSetting.Skip or ComponentExistsSetting.Block) + continue; + + EntityManager.AddComponent(target, component.Value, true); + } + } +} diff --git a/Content.Shared/Trigger/Components/Triggers/TriggerOnVoiceComponent.cs b/Content.Shared/Trigger/Components/Triggers/TriggerOnVoiceComponent.cs index 1fc3c1b966..974d5322e0 100644 --- a/Content.Shared/Trigger/Components/Triggers/TriggerOnVoiceComponent.cs +++ b/Content.Shared/Trigger/Components/Triggers/TriggerOnVoiceComponent.cs @@ -60,36 +60,36 @@ public sealed partial class TriggerOnVoiceComponent : BaseTriggerOnXComponent /// /// The verb text that is shown when you can start recording a message. /// - [DataField] + [DataField, AutoNetworkedField] public LocId StartRecordingVerb = "trigger-on-voice-record"; /// /// The verb text that is shown when you can stop recording a message. /// - [DataField] + [DataField, AutoNetworkedField] public LocId StopRecordingVerb = "trigger-on-voice-stop"; /// /// Tooltip that appears when hovering over the stop or start recording verbs. /// - [DataField] + [DataField, AutoNetworkedField] public LocId? RecordingVerbMessage; /// /// The verb text that is shown when you can clear a recording. /// - [DataField] + [DataField, AutoNetworkedField] public LocId ClearRecordingVerb = "trigger-on-voice-clear"; /// /// The loc string that is shown when inspecting an uninitialized voice trigger. /// - [DataField] + [DataField, AutoNetworkedField] public LocId? InspectUninitializedLoc = "trigger-on-voice-uninitialized"; /// /// The loc string to use when inspecting voice trigger. Will also include the triggering phrase /// - [DataField] + [DataField, AutoNetworkedField] public LocId? InspectInitializedLoc = "trigger-on-voice-examine"; } diff --git a/Resources/Locale/en-US/locks/selectable-locks.ftl b/Resources/Locale/en-US/locks/selectable-locks.ftl new file mode 100644 index 0000000000..3dbf088505 --- /dev/null +++ b/Resources/Locale/en-US/locks/selectable-locks.ftl @@ -0,0 +1,3 @@ +selectable-lock-verb-category-name = Add lock +selectable-lock-verb-no-lock = No lock +selectable-lock-verb-no-lock-popup = No lock has been added to {THE($target)}. diff --git a/Resources/Locale/en-US/locks/voice-trigger-lock.ftl b/Resources/Locale/en-US/locks/voice-trigger-lock.ftl index fd2dc38d23..a7069378f8 100644 --- a/Resources/Locale/en-US/locks/voice-trigger-lock.ftl +++ b/Resources/Locale/en-US/locks/voice-trigger-lock.ftl @@ -1,3 +1,6 @@ +voice-trigger-lock-add-verb = Voice Lock +voice-trigger-lock-add-verb-popup = A voice lock has been added to {THE($target)}. + voice-trigger-lock-verb-record = Record lock phrase voice-trigger-lock-verb-message = Locking the item will disable features that reveal its true nature! diff --git a/Resources/Locale/en-US/selectable-component/selectable-component.ftl b/Resources/Locale/en-US/selectable-component/selectable-component.ftl new file mode 100644 index 0000000000..f30c499f76 --- /dev/null +++ b/Resources/Locale/en-US/selectable-component/selectable-component.ftl @@ -0,0 +1 @@ +selectable-component-adder-category-name = Add feature diff --git a/Resources/Prototypes/Entities/Objects/Devices/pda.yml b/Resources/Prototypes/Entities/Objects/Devices/pda.yml index 92bd4297ad..1153047e7d 100644 --- a/Resources/Prototypes/Entities/Objects/Devices/pda.yml +++ b/Resources/Prototypes/Entities/Objects/Devices/pda.yml @@ -1467,7 +1467,7 @@ - MedTekCartridge - type: entity - parent: [BasePDA, VoiceLock] + parent: [BasePDA, SelectableLock] id: ChameleonPDA name: passenger PDA description: Why isn't it gray? diff --git a/Resources/Prototypes/Entities/Objects/Specific/locks.yml b/Resources/Prototypes/Entities/Objects/Specific/locks.yml index 296b9fd7d9..c280c4a60e 100644 --- a/Resources/Prototypes/Entities/Objects/Specific/locks.yml +++ b/Resources/Prototypes/Entities/Objects/Specific/locks.yml @@ -1,5 +1,5 @@ - type: entity - id: VoiceLock + id: SelectableLock abstract: true components: - type: Lock @@ -11,14 +11,27 @@ useAccess: false unlockingSound: null # TODO: Maybe add sounds but just to the user? lockingSound: null + breakOnAccessBreaker: true # more fun 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: SelectableComponentAdder + selections: 1 + entries: + - verbName: selectable-lock-verb-no-lock + popup: selectable-lock-verb-no-lock-popup + priority: 0 + componentsToAdd: null + - verbName: voice-trigger-lock-add-verb + popup: voice-trigger-lock-add-verb-popup + priority: 1 + componentsToAdd: + - 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: ActiveListener + - type: VoiceTriggerLock + verbCategoryName: selectable-lock-verb-category-name diff --git a/Resources/Prototypes/Entities/Objects/Weapons/Melee/cane.yml b/Resources/Prototypes/Entities/Objects/Weapons/Melee/cane.yml index 8b891a7533..fcf0b91f8e 100644 --- a/Resources/Prototypes/Entities/Objects/Weapons/Melee/cane.yml +++ b/Resources/Prototypes/Entities/Objects/Weapons/Melee/cane.yml @@ -54,7 +54,7 @@ - type: DisarmMalus - type: entity - parent: [Cane, VoiceLock] + parent: [Cane, SelectableLock] id: CaneSheath suffix: Empty components: diff --git a/Resources/Prototypes/Entities/Objects/Weapons/Melee/e_sword.yml b/Resources/Prototypes/Entities/Objects/Weapons/Melee/e_sword.yml index 5f431416e7..04b65ddb6b 100644 --- a/Resources/Prototypes/Entities/Objects/Weapons/Melee/e_sword.yml +++ b/Resources/Prototypes/Entities/Objects/Weapons/Melee/e_sword.yml @@ -159,7 +159,7 @@ - type: entity name: pen - parent: [BaseMeleeWeaponEnergy, VoiceLock] + parent: [BaseMeleeWeaponEnergy, SelectableLock] id: EnergyDagger suffix: E-Dagger description: 'A dark ink pen.' diff --git a/Resources/Prototypes/chameleon.yml b/Resources/Prototypes/chameleon.yml index 6969380621..ed08c979b5 100644 --- a/Resources/Prototypes/chameleon.yml +++ b/Resources/Prototypes/chameleon.yml @@ -1,6 +1,6 @@ # for clothing that can be toggled, like magboots - type: entity - parent: VoiceLock + parent: SelectableLock abstract: true id: BaseChameleon components: