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>
This commit is contained in:
@@ -0,0 +1,77 @@
|
|||||||
|
using Robust.Shared.GameStates;
|
||||||
|
using Robust.Shared.Prototypes;
|
||||||
|
using Robust.Shared.Serialization;
|
||||||
|
|
||||||
|
namespace Content.Shared.SelectableComponentAdder;
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Brings up a verb menu that allows players to select components that will get added to the item with this component.
|
||||||
|
/// </summary>
|
||||||
|
[RegisterComponent, NetworkedComponent, AutoGenerateComponentState]
|
||||||
|
public sealed partial class SelectableComponentAdderComponent : Component
|
||||||
|
{
|
||||||
|
/// <summary>
|
||||||
|
/// List of verb -> components to add for that verb when selected basically!
|
||||||
|
/// </summary>
|
||||||
|
[DataField(required: true)]
|
||||||
|
public List<ComponentAdderEntry> Entries = new();
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// The amount of times players can make a selection and add a component. If null, there is no limit.
|
||||||
|
/// </summary>
|
||||||
|
[DataField, AutoNetworkedField]
|
||||||
|
public int? Selections;
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// The verb category name that will be used.
|
||||||
|
/// </summary>
|
||||||
|
[DataField, AutoNetworkedField]
|
||||||
|
public LocId VerbCategoryName = "selectable-component-adder-category-name";
|
||||||
|
}
|
||||||
|
|
||||||
|
[DataDefinition]
|
||||||
|
public sealed partial class ComponentAdderEntry
|
||||||
|
{
|
||||||
|
/// <summary>
|
||||||
|
/// Name of the verb that will add the components in <see cref="ComponentsToAdd"/>.
|
||||||
|
/// </summary>
|
||||||
|
[DataField(required: true)]
|
||||||
|
public LocId VerbName;
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Popup to show when this option is selected.
|
||||||
|
/// </summary>
|
||||||
|
[DataField(required: true)]
|
||||||
|
public LocId? Popup;
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// List of all the components that will get added when the verb is selected.
|
||||||
|
/// </summary>
|
||||||
|
[DataField(required: true)]
|
||||||
|
public ComponentRegistry? ComponentsToAdd;
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// The type of behavior that occurs when the component(s) already exist on the entity.
|
||||||
|
/// </summary>
|
||||||
|
[DataField]
|
||||||
|
public ComponentExistsSetting ComponentExistsBehavior = ComponentExistsSetting.Skip;
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// The priorty of the verb in the list
|
||||||
|
/// </summary>
|
||||||
|
[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,
|
||||||
|
}
|
||||||
@@ -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<SelectableComponentAdderComponent, GetVerbsEvent<Verb>>(OnGetVerb);
|
||||||
|
}
|
||||||
|
|
||||||
|
private void OnGetVerb(Entity<SelectableComponentAdderComponent> ent, ref GetVerbsEvent<Verb> 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);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -60,36 +60,36 @@ public sealed partial class TriggerOnVoiceComponent : BaseTriggerOnXComponent
|
|||||||
/// <summary>
|
/// <summary>
|
||||||
/// The verb text that is shown when you can start recording a message.
|
/// The verb text that is shown when you can start recording a message.
|
||||||
/// </summary>
|
/// </summary>
|
||||||
[DataField]
|
[DataField, AutoNetworkedField]
|
||||||
public LocId StartRecordingVerb = "trigger-on-voice-record";
|
public LocId StartRecordingVerb = "trigger-on-voice-record";
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// The verb text that is shown when you can stop recording a message.
|
/// The verb text that is shown when you can stop recording a message.
|
||||||
/// </summary>
|
/// </summary>
|
||||||
[DataField]
|
[DataField, AutoNetworkedField]
|
||||||
public LocId StopRecordingVerb = "trigger-on-voice-stop";
|
public LocId StopRecordingVerb = "trigger-on-voice-stop";
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// Tooltip that appears when hovering over the stop or start recording verbs.
|
/// Tooltip that appears when hovering over the stop or start recording verbs.
|
||||||
/// </summary>
|
/// </summary>
|
||||||
[DataField]
|
[DataField, AutoNetworkedField]
|
||||||
public LocId? RecordingVerbMessage;
|
public LocId? RecordingVerbMessage;
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// The verb text that is shown when you can clear a recording.
|
/// The verb text that is shown when you can clear a recording.
|
||||||
/// </summary>
|
/// </summary>
|
||||||
[DataField]
|
[DataField, AutoNetworkedField]
|
||||||
public LocId ClearRecordingVerb = "trigger-on-voice-clear";
|
public LocId ClearRecordingVerb = "trigger-on-voice-clear";
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// The loc string that is shown when inspecting an uninitialized voice trigger.
|
/// The loc string that is shown when inspecting an uninitialized voice trigger.
|
||||||
/// </summary>
|
/// </summary>
|
||||||
[DataField]
|
[DataField, AutoNetworkedField]
|
||||||
public LocId? InspectUninitializedLoc = "trigger-on-voice-uninitialized";
|
public LocId? InspectUninitializedLoc = "trigger-on-voice-uninitialized";
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// The loc string to use when inspecting voice trigger. Will also include the triggering phrase
|
/// The loc string to use when inspecting voice trigger. Will also include the triggering phrase
|
||||||
/// </summary>
|
/// </summary>
|
||||||
[DataField]
|
[DataField, AutoNetworkedField]
|
||||||
public LocId? InspectInitializedLoc = "trigger-on-voice-examine";
|
public LocId? InspectInitializedLoc = "trigger-on-voice-examine";
|
||||||
}
|
}
|
||||||
|
|||||||
3
Resources/Locale/en-US/locks/selectable-locks.ftl
Normal file
3
Resources/Locale/en-US/locks/selectable-locks.ftl
Normal file
@@ -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)}.
|
||||||
@@ -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-record = Record lock phrase
|
||||||
voice-trigger-lock-verb-message = Locking the item will disable features that reveal its true nature!
|
voice-trigger-lock-verb-message = Locking the item will disable features that reveal its true nature!
|
||||||
|
|
||||||
|
|||||||
@@ -0,0 +1 @@
|
|||||||
|
selectable-component-adder-category-name = Add feature
|
||||||
@@ -1467,7 +1467,7 @@
|
|||||||
- MedTekCartridge
|
- MedTekCartridge
|
||||||
|
|
||||||
- type: entity
|
- type: entity
|
||||||
parent: [BasePDA, VoiceLock]
|
parent: [BasePDA, SelectableLock]
|
||||||
id: ChameleonPDA
|
id: ChameleonPDA
|
||||||
name: passenger PDA
|
name: passenger PDA
|
||||||
description: Why isn't it gray?
|
description: Why isn't it gray?
|
||||||
|
|||||||
@@ -1,5 +1,5 @@
|
|||||||
- type: entity
|
- type: entity
|
||||||
id: VoiceLock
|
id: SelectableLock
|
||||||
abstract: true
|
abstract: true
|
||||||
components:
|
components:
|
||||||
- type: Lock
|
- type: Lock
|
||||||
@@ -11,14 +11,27 @@
|
|||||||
useAccess: false
|
useAccess: false
|
||||||
unlockingSound: null # TODO: Maybe add sounds but just to the user?
|
unlockingSound: null # TODO: Maybe add sounds but just to the user?
|
||||||
lockingSound: null
|
lockingSound: null
|
||||||
|
breakOnAccessBreaker: true # more fun
|
||||||
lockTime: 0
|
lockTime: 0
|
||||||
unlockTime: 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: LockOnTrigger
|
||||||
- type: ActiveListener
|
- type: SelectableComponentAdder
|
||||||
- type: VoiceTriggerLock
|
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
|
||||||
|
|||||||
@@ -54,7 +54,7 @@
|
|||||||
- type: DisarmMalus
|
- type: DisarmMalus
|
||||||
|
|
||||||
- type: entity
|
- type: entity
|
||||||
parent: [Cane, VoiceLock]
|
parent: [Cane, SelectableLock]
|
||||||
id: CaneSheath
|
id: CaneSheath
|
||||||
suffix: Empty
|
suffix: Empty
|
||||||
components:
|
components:
|
||||||
|
|||||||
@@ -159,7 +159,7 @@
|
|||||||
|
|
||||||
- type: entity
|
- type: entity
|
||||||
name: pen
|
name: pen
|
||||||
parent: [BaseMeleeWeaponEnergy, VoiceLock]
|
parent: [BaseMeleeWeaponEnergy, SelectableLock]
|
||||||
id: EnergyDagger
|
id: EnergyDagger
|
||||||
suffix: E-Dagger
|
suffix: E-Dagger
|
||||||
description: 'A dark ink pen.'
|
description: 'A dark ink pen.'
|
||||||
|
|||||||
@@ -1,6 +1,6 @@
|
|||||||
# for clothing that can be toggled, like magboots
|
# for clothing that can be toggled, like magboots
|
||||||
- type: entity
|
- type: entity
|
||||||
parent: VoiceLock
|
parent: SelectableLock
|
||||||
abstract: true
|
abstract: true
|
||||||
id: BaseChameleon
|
id: BaseChameleon
|
||||||
components:
|
components:
|
||||||
|
|||||||
Reference in New Issue
Block a user