mirror of
https://github.com/space-syndicate/space-station-14.git
synced 2026-02-14 23:14:45 +01:00
Add voice mask implant (#41551)
* Add voice mask implant * Remove voice mask * Voice mask implant now overrides your identity * voice mask implant can now be extracted, when taking out the voice mask implant it now updates your name proplery * Simplify logic
This commit is contained in:
@@ -26,6 +26,12 @@ public sealed partial class VoiceMaskComponent : Component
|
||||
[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>
|
||||
@@ -38,3 +44,4 @@ public sealed partial class VoiceMaskComponent : Component
|
||||
[DataField]
|
||||
public EntityUid? ActionEntity;
|
||||
}
|
||||
|
||||
|
||||
@@ -4,6 +4,9 @@ using Content.Shared.CCVar;
|
||||
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;
|
||||
@@ -26,6 +29,7 @@ public sealed partial class VoiceMaskSystem : EntitySystem
|
||||
[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;
|
||||
@@ -33,7 +37,11 @@ public sealed partial class VoiceMaskSystem : EntitySystem
|
||||
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);
|
||||
@@ -43,10 +51,30 @@ public sealed partial class VoiceMaskSystem : EntitySystem
|
||||
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)
|
||||
@@ -79,6 +107,9 @@ public sealed partial class VoiceMaskSystem : EntitySystem
|
||||
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}");
|
||||
|
||||
@@ -123,5 +154,11 @@ public sealed partial class VoiceMaskSystem : EntitySystem
|
||||
{
|
||||
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
|
||||
}
|
||||
|
||||
@@ -37,4 +37,9 @@ public sealed class SeeIdentityAttemptEvent : CancellableEntityEventArgs, IInven
|
||||
|
||||
// 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;
|
||||
}
|
||||
|
||||
@@ -47,10 +47,16 @@ public sealed class IdentityRepresentation
|
||||
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();
|
||||
}
|
||||
|
||||
|
||||
@@ -8,6 +8,7 @@ using Content.Shared.Humanoid;
|
||||
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;
|
||||
@@ -53,6 +54,7 @@ public sealed class IdentitySystem : EntitySystem
|
||||
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>
|
||||
@@ -197,7 +199,7 @@ public sealed class IdentitySystem : EntitySystem
|
||||
var ev = new SeeIdentityAttemptEvent();
|
||||
|
||||
RaiseLocalEvent(target, ev);
|
||||
return representation.ToStringKnown(!ev.Cancelled);
|
||||
return representation.ToStringKnown(!ev.Cancelled, ev.NameOverride);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
|
||||
@@ -1,3 +1,5 @@
|
||||
using Content.Shared.Chat;
|
||||
using Content.Shared.IdentityManagement.Components;
|
||||
using Content.Shared.Implants.Components;
|
||||
using Content.Shared.Interaction;
|
||||
using Content.Shared.Interaction.Events;
|
||||
@@ -12,6 +14,8 @@ public abstract partial class SharedSubdermalImplantSystem
|
||||
SubscribeLocalEvent<ImplantedComponent, MobStateChangedEvent>(RelayToImplantEvent);
|
||||
SubscribeLocalEvent<ImplantedComponent, AfterInteractUsingEvent>(RelayToImplantEvent);
|
||||
SubscribeLocalEvent<ImplantedComponent, SuicideEvent>(RelayToImplantEvent);
|
||||
SubscribeLocalEvent<ImplantedComponent, TransformSpeakerNameEvent>(RelayToImplantEvent);
|
||||
SubscribeLocalEvent<ImplantedComponent, SeeIdentityAttemptEvent>(RelayToImplantEvent);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
|
||||
@@ -169,7 +169,14 @@ public abstract partial class SharedSubdermalImplantSystem : EntitySystem
|
||||
[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)
|
||||
|
||||
@@ -6,3 +6,11 @@ public sealed partial class VoiceMaskSetNameEvent : InstantActionEvent
|
||||
{
|
||||
}
|
||||
|
||||
/// <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);
|
||||
|
||||
@@ -171,9 +171,6 @@ uplink-binary-translator-key-desc = Lets you tap into the silicons' binary chann
|
||||
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.
|
||||
|
||||
@@ -226,6 +223,9 @@ uplink-micro-bomb-implanter-desc = Explode on death or manual activation with th
|
||||
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.
|
||||
|
||||
@@ -488,3 +488,11 @@
|
||||
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
|
||||
|
||||
@@ -83,7 +83,7 @@
|
||||
- EncryptionKeyStationMaster
|
||||
- CyberPen
|
||||
- BriefcaseThiefBribingBundleFilled
|
||||
- ClothingMaskGasVoiceChameleon
|
||||
- VoiceMaskImplanter
|
||||
#- todo Chameleon Stamp
|
||||
|
||||
- type: thiefBackpackSet
|
||||
|
||||
@@ -1567,6 +1567,19 @@
|
||||
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
|
||||
|
||||
@@ -1583,19 +1596,6 @@
|
||||
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
|
||||
|
||||
@@ -47,6 +47,7 @@
|
||||
- FakeMindShieldImplant
|
||||
- RadioImplant
|
||||
- ChameleonControllerImplant
|
||||
- VoiceMaskImplant
|
||||
deimplantFailureDamage:
|
||||
types:
|
||||
Cellular: 50
|
||||
@@ -256,6 +257,14 @@
|
||||
- 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
|
||||
|
||||
@@ -239,6 +239,25 @@
|
||||
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
|
||||
|
||||
Reference in New Issue
Block a user