[DataField]
public ProtoId<SpeechVerbPrototype>? VoiceMaskSpeechVerb;
+ /// <summary>
+ /// If true will override the users identity with whatever <see cref="VoiceMaskName"/> is.
+ /// </summary>
+ [DataField]
+ public bool OverrideIdentity;
+
/// <summary>
/// The action that gets displayed when the voice mask is equipped.
/// </summary>
[DataField]
public EntityUid? ActionEntity;
}
+
using Content.Shared.Chat;
using Content.Shared.Clothing;
using Content.Shared.Database;
+using Content.Shared.IdentityManagement;
+using Content.Shared.IdentityManagement.Components;
+using Content.Shared.Implants;
using Content.Shared.Inventory;
using Content.Shared.Lock;
using Content.Shared.Popups;
[Dependency] private readonly SharedActionsSystem _actions = default!;
[Dependency] private readonly LockSystem _lock = default!;
[Dependency] private readonly SharedContainerSystem _container = default!;
+ [Dependency] private readonly IdentitySystem _identity = default!;
// CCVar.
private int _maxNameLength;
public override void Initialize()
{
base.Initialize();
- SubscribeLocalEvent<VoiceMaskComponent, InventoryRelayedEvent<TransformSpeakerNameEvent>>(OnTransformSpeakerName);
+ SubscribeLocalEvent<VoiceMaskComponent, InventoryRelayedEvent<TransformSpeakerNameEvent>>(OnTransformSpeakerNameInventory);
+ SubscribeLocalEvent<VoiceMaskComponent, ImplantRelayEvent<TransformSpeakerNameEvent>>(OnTransformSpeakerNameImplant);
+ SubscribeLocalEvent<VoiceMaskComponent, ImplantRelayEvent<SeeIdentityAttemptEvent>>(OnSeeIdentityAttemptEvent);
+ SubscribeLocalEvent<VoiceMaskComponent, ImplantImplantedEvent>(OnImplantImplantedEvent);
+ SubscribeLocalEvent<VoiceMaskComponent, ImplantRemovedEvent>(OnImplantRemovedEventEvent);
SubscribeLocalEvent<VoiceMaskComponent, LockToggledEvent>(OnLockToggled);
SubscribeLocalEvent<VoiceMaskComponent, VoiceMaskChangeNameMessage>(OnChangeName);
SubscribeLocalEvent<VoiceMaskComponent, VoiceMaskChangeVerbMessage>(OnChangeVerb);
Subs.CVar(_cfgManager, CCVars.MaxNameLength, value => _maxNameLength = value, true);
}
- private void OnTransformSpeakerName(Entity<VoiceMaskComponent> entity, ref InventoryRelayedEvent<TransformSpeakerNameEvent> args)
+ private void OnTransformSpeakerNameInventory(Entity<VoiceMaskComponent> entity, ref InventoryRelayedEvent<TransformSpeakerNameEvent> args)
{
- args.Args.VoiceName = GetCurrentVoiceName(entity);
- args.Args.SpeechVerb = entity.Comp.VoiceMaskSpeechVerb ?? args.Args.SpeechVerb;
+ TransformVoice(entity, args.Args);
+ }
+
+ private void OnTransformSpeakerNameImplant(Entity<VoiceMaskComponent> entity, ref ImplantRelayEvent<TransformSpeakerNameEvent> args)
+ {
+ TransformVoice(entity, args.Event);
+ }
+
+ private void OnSeeIdentityAttemptEvent(Entity<VoiceMaskComponent> entity, ref ImplantRelayEvent<SeeIdentityAttemptEvent> args)
+ {
+ if (entity.Comp.OverrideIdentity)
+ args.Event.NameOverride = GetCurrentVoiceName(entity);
+ }
+
+ private void OnImplantImplantedEvent(Entity<VoiceMaskComponent> entity, ref ImplantImplantedEvent ev)
+ {
+ _identity.QueueIdentityUpdate(ev.Implanted);
+ }
+
+ private void OnImplantRemovedEventEvent(Entity<VoiceMaskComponent> entity, ref ImplantRemovedEvent ev)
+ {
+ _identity.QueueIdentityUpdate(ev.Implanted);
}
private void OnLockToggled(Entity<VoiceMaskComponent> ent, ref LockToggledEvent args)
return;
}
+ var nameUpdatedEvent = new VoiceMaskNameUpdatedEvent(entity, entity.Comp.VoiceMaskName, message.Name);
+ RaiseLocalEvent(message.Actor, ref nameUpdatedEvent);
+
entity.Comp.VoiceMaskName = message.Name;
_adminLogger.Add(LogType.Action, LogImpact.Medium, $"{ToPrettyString(message.Actor):player} set voice of {ToPrettyString(entity):mask}: {entity.Comp.VoiceMaskName}");
{
return entity.Comp.VoiceMaskName ?? Loc.GetString("voice-mask-default-name-override");
}
+
+ private void TransformVoice(Entity<VoiceMaskComponent> entity, TransformSpeakerNameEvent args)
+ {
+ args.VoiceName = GetCurrentVoiceName(entity);
+ args.SpeechVerb = entity.Comp.VoiceMaskSpeechVerb ?? args.SpeechVerb;
+ }
#endregion
}
// cumulative coverage from each relayed slot
public IdentityBlockerCoverage TotalCoverage = IdentityBlockerCoverage.NONE;
+
+ /// <summary>
+ /// A specific name to override your identiy with.
+ /// </summary>
+ public string? NameOverride = null;
}
PresumedName = presumedName;
}
- public string ToStringKnown(bool trueName)
+ /// <summary>
+ /// Get this identity as a string
+ /// </summary>
+ /// <param name="trueName">Should we show their "true" name or hide it?</param>
+ /// <param name="nameOverride">A "true name" override</param>
+ /// <returns></returns>
+ public string ToStringKnown(bool trueName, string? nameOverride)
{
return trueName
- ? TrueName
+ ? nameOverride ?? TrueName
: PresumedName ?? ToStringUnknown();
}
using Content.Shared.IdentityManagement.Components;
using Content.Shared.Inventory;
using Content.Shared.Inventory.Events;
+using Content.Shared.VoiceMask;
using Robust.Shared.Containers;
using Robust.Shared.Enums;
using Robust.Shared.GameObjects.Components.Localization;
SubscribeLocalEvent<IdentityComponent, DidUnequipHandEvent>((uid, _, _) => QueueIdentityUpdate(uid));
SubscribeLocalEvent<IdentityComponent, WearerMaskToggledEvent>((uid, _, _) => QueueIdentityUpdate(uid));
SubscribeLocalEvent<IdentityComponent, EntityRenamedEvent>((uid, _, _) => QueueIdentityUpdate(uid));
+ SubscribeLocalEvent<IdentityComponent, VoiceMaskNameUpdatedEvent>((uid, _, _) => QueueIdentityUpdate(uid));
}
/// <summary>
var ev = new SeeIdentityAttemptEvent();
RaiseLocalEvent(target, ev);
- return representation.ToStringKnown(!ev.Cancelled);
+ return representation.ToStringKnown(!ev.Cancelled, ev.NameOverride);
}
/// <summary>
+using Content.Shared.Chat;
+using Content.Shared.IdentityManagement.Components;
using Content.Shared.Implants.Components;
using Content.Shared.Interaction;
using Content.Shared.Interaction.Events;
SubscribeLocalEvent<ImplantedComponent, MobStateChangedEvent>(RelayToImplantEvent);
SubscribeLocalEvent<ImplantedComponent, AfterInteractUsingEvent>(RelayToImplantEvent);
SubscribeLocalEvent<ImplantedComponent, SuicideEvent>(RelayToImplantEvent);
+ SubscribeLocalEvent<ImplantedComponent, TransformSpeakerNameEvent>(RelayToImplantEvent);
+ SubscribeLocalEvent<ImplantedComponent, SeeIdentityAttemptEvent>(RelayToImplantEvent);
}
/// <summary>
[ByRefEvent]
public readonly record struct ImplantImplantedEvent
{
+ /// <summary>
+ /// The implant itself
+ /// </summary>
public readonly EntityUid Implant;
+
+ /// <summary>
+ /// The entity getting implanted
+ /// </summary>
public readonly EntityUid Implanted;
public ImplantImplantedEvent(EntityUid implant, EntityUid implanted)
{
}
+/// <summary>
+/// Raised on an entity when their voice masks name is updated
+/// </summary>
+/// <param name="VoiceMaskUid">Uid of the voice mask</param>
+/// <param name="OldName">The old name</param>
+/// <param name="NewName">The new name</param>
+[ByRefEvent]
+public readonly record struct VoiceMaskNameUpdatedEvent(EntityUid VoiceMaskUid, string? OldName, string NewName);
uplink-hypopen-name = Hypopen
uplink-hypopen-desc = A chemical hypospray disguised as a pen, capable of instantly injecting up to 10u of reagents. Starts empty.
-uplink-voice-mask-name = Voice Mask
-uplink-voice-mask-desc = A gas mask that lets you adjust your voice to whoever you can think of. Also utilizes cutting-edge chameleon technology.
-
uplink-clothing-eyes-hud-syndicate-name = Syndicate Visor
uplink-clothing-eyes-hud-syndicate-desc = The syndicate's professional head-up display, designed for better detection of humanoids and their subsequent elimination.
uplink-radio-implanter-name = Radio Implanter
uplink-radio-implanter-desc = Implants a Syndicate radio, allowing covert communication without a headset.
+uplink-voice-mask-implanter-name = Voice Mask Implanter
+uplink-voice-mask-implanter-desc = Modifies your vocal cords to be able to sound like anyone you could imagine.
+
# Bundles
uplink-observation-kit-name = Observation Kit
uplink-observation-kit-desc = Includes surveillance camera monitor board and security hud disguised as sunglasses.
icon: Interface/Actions/shop.png
- type: InstantAction
event: !type:IntrinsicStoreActionEvent
+
+- type: entity
+ parent: ActionChangeVoiceMask
+ id: ActionChangeVoiceMaskImplant
+ components:
+ - type: Action
+ icon: { sprite: Interface/Actions/voice-mask.rsi, state: icon }
+ itemIconStyle: BigAction
- EncryptionKeyStationMaster
- CyberPen
- BriefcaseThiefBribingBundleFilled
- - ClothingMaskGasVoiceChameleon
+ - VoiceMaskImplanter
#- todo Chameleon Stamp
- type: thiefBackpackSet
categories:
- UplinkImplants
+- type: listing
+ id: UplinkVoiceMaskImplant
+ name: uplink-voice-mask-implanter-name
+ description: uplink-voice-mask-implanter-desc
+ icon: { sprite: Interface/Actions/voice-mask.rsi, state: icon }
+ productEntity: VoiceMaskImplanter
+ discountCategory: usualDiscounts
+ discountDownTo:
+ Telecrystal: 1
+ cost:
+ Telecrystal: 2
+ categories:
+ - UplinkImplants
# Wearables
categories:
- UplinkWearables
-- type: listing
- id: UplinkVoiceMask
- name: uplink-voice-mask-name
- description: uplink-voice-mask-desc
- productEntity: ClothingMaskGasVoiceChameleon
- discountCategory: usualDiscounts
- discountDownTo:
- Telecrystal: 1
- cost:
- Telecrystal: 2
- categories:
- - UplinkWearables
-
- type: listing
id: UplinkHolster
name: uplink-holster-name
- FakeMindShieldImplant
- RadioImplant
- ChameleonControllerImplant
+ - VoiceMaskImplant
deimplantFailureDamage:
types:
Cellular: 50
- type: Implanter
implant: ChameleonControllerImplant
+- type: entity
+ id: VoiceMaskImplanter
+ name: voice mask implanter
+ parent: BaseImplantOnlyImplanterSyndi
+ components:
+ - type: Implanter
+ implant: VoiceMaskImplant
+
#Nuclear Operative/Special implanters
- type: entity
enum.ChameleonControllerKey.Key:
type: ChameleonControllerBoundUserInterface
+- type: entity
+ parent: BaseSubdermalImplant
+ id: VoiceMaskImplant
+ name: voice mask implant
+ description: This implant allows you to change your voice at will.
+ categories: [ HideSpawnMenu ]
+ components:
+ - type: SubdermalImplant
+ implantAction: ActionChangeVoiceMaskImplant
+ - type: VoiceMask
+ overrideIdentity: true
+ - type: UserInterface
+ interfaces:
+ enum.VoiceMaskUIKey.Key:
+ type: VoiceMaskBoundUserInterface
+ - type: Tag
+ tags:
+ - SubdermalImplant
+
#Nuclear Operative/Special Exclusive implants
- type: entity