diff --git a/Content.Server/Chat/Systems/ChatSystem.cs b/Content.Server/Chat/Systems/ChatSystem.cs index f8e088dfe20..5ef411a7217 100644 --- a/Content.Server/Chat/Systems/ChatSystem.cs +++ b/Content.Server/Chat/Systems/ChatSystem.cs @@ -8,6 +8,7 @@ using Content.Server.GameTicking; using Content.Server.Speech.EntitySystems; using Content.Shared.Speech.Hushing; // DeltaV using Content.Server.Nyanotrasen.Chat; +using Content.Server.Speech.Components; // DeltaV using Content.Server.Speech.Prototypes; using Content.Server.Station.Systems; using Content.Shared.ActionBlocker; @@ -963,6 +964,7 @@ public sealed partial class ChatSystem : SharedChatSystem var recipients = new Dictionary(); var ghostHearing = GetEntityQuery(); var xforms = GetEntityQuery(); + var blockListening = GetEntityQuery(); // DeltaV - block listening var transformSource = xforms.GetComponent(source); var sourceMapId = transformSource.MapID; @@ -978,6 +980,11 @@ public sealed partial class ChatSystem : SharedChatSystem if (transformEntity.MapID != sourceMapId) continue; + // Begin DeltaV - block listening + if (blockListening.HasComponent(playerEntity)) + continue; + // End DeltaV - block listening + var observer = ghostHearing.HasComponent(playerEntity); // even if they are a ghost hearer, in some situations we still need the range diff --git a/Content.Server/Silicons/Borgs/BorgSystem.Transponder.cs b/Content.Server/Silicons/Borgs/BorgSystem.Transponder.cs index a6d3cf1dffb..fe10d138d63 100644 --- a/Content.Server/Silicons/Borgs/BorgSystem.Transponder.cs +++ b/Content.Server/Silicons/Borgs/BorgSystem.Transponder.cs @@ -27,6 +27,11 @@ public sealed partial class BorgSystem var query = EntityQueryEnumerator(); while (query.MoveNext(out var uid, out var comp, out var chassis, out var device, out var meta)) { + // DeltaV Begin + if (!comp.Active) + return; + // DeltaV End + if (comp.NextDisable is { } nextDisable && now >= nextDisable) DoDisable((uid, comp, chassis, meta)); @@ -83,6 +88,11 @@ public sealed partial class BorgSystem private void OnPacketReceived(Entity ent, ref DeviceNetworkPacketEvent args) { + // DeltaV Begin + if (!ent.Comp.Active) + return; + // DeltaV End + var payload = args.Data; if (!payload.TryGetValue(DeviceNetworkConstants.Command, out string? command)) return; @@ -197,4 +207,17 @@ public sealed partial class BorgSystem return true; } + + // DeltaV Begin + /// + /// DeltaV - sets if the borg transponder is active + /// + public void SetTransponderActive(Entity ent, bool active) + { + if (!Resolve(ent, ref ent.Comp)) + return; + + ent.Comp.Active = active; + } + // DeltaV End } diff --git a/Content.Server/Speech/Components/SpeechWireAction.cs b/Content.Server/Speech/Components/SpeechWireAction.cs index dd95f2051b8..a084566707c 100644 --- a/Content.Server/Speech/Components/SpeechWireAction.cs +++ b/Content.Server/Speech/Components/SpeechWireAction.cs @@ -29,12 +29,14 @@ public sealed partial class SpeechWireAction : ComponentWireAction(user)?.Active = false; // DeltaV - we need this to override unblockable speech return true; } public override bool Mend(EntityUid user, Wire wire, SpeechComponent component) { _speech.SetSpeech(wire.Owner, true, component); + EntityManager.GetComponentOrNull(user)?.Active = true; // DeltaV - we need this to override unblockable speech return true; } diff --git a/Content.Server/Speech/Components/UnblockableSpeechComponent.cs b/Content.Server/Speech/Components/UnblockableSpeechComponent.cs index 48e2b9863af..96342aa81f7 100644 --- a/Content.Server/Speech/Components/UnblockableSpeechComponent.cs +++ b/Content.Server/Speech/Components/UnblockableSpeechComponent.cs @@ -3,5 +3,9 @@ namespace Content.Server.Speech.Components [RegisterComponent] public sealed partial class UnblockableSpeechComponent : Component { + // Begin DeltaV + [DataField] + public bool Active = true; + // End DeltaV } } diff --git a/Content.Server/Speech/EntitySystems/UnblockableSpeechSystem.cs b/Content.Server/Speech/EntitySystems/UnblockableSpeechSystem.cs index 6d5f165d6c4..a8b1fb67abc 100644 --- a/Content.Server/Speech/EntitySystems/UnblockableSpeechSystem.cs +++ b/Content.Server/Speech/EntitySystems/UnblockableSpeechSystem.cs @@ -12,7 +12,7 @@ namespace Content.Server.Speech.EntitySystems private void OnCheck(EntityUid uid, UnblockableSpeechComponent component, CheckIgnoreSpeechBlockerEvent args) { - args.IgnoreBlocker = true; + args.IgnoreBlocker = component.Active; // DeltaV } } } diff --git a/Content.Server/_DV/Silicons/BorgIdChipWireAction.cs b/Content.Server/_DV/Silicons/BorgIdChipWireAction.cs new file mode 100644 index 00000000000..ce77c03c4f5 --- /dev/null +++ b/Content.Server/_DV/Silicons/BorgIdChipWireAction.cs @@ -0,0 +1,39 @@ +using Content.Server.Wires; +using Content.Shared._DV.Silicons.Borgs; +using Content.Shared._DV.Silicons; +using Content.Shared.Wires; + +namespace Content.Server._DV.Silicons; + +/// +/// Controls whether the cyborg's ID chip slot is active +/// +public sealed partial class BorgIdChipWireAction : ComponentWireAction +{ + public override string Name { get; set; } = "wire-name-borg-id-chip"; + public override Color Color { get; set; } = Color.Thistle; + public override object StatusKey => BorgWireActionKey.ChipKey; + + public override StatusLightState? GetLightState(Wire wire, IdChipSlotComponent component) + { + return component.Active ? StatusLightState.On : StatusLightState.Off; + } + + public override bool Cut(EntityUid user, Wire wire, IdChipSlotComponent component) + { + EntityManager.System() + .SetActive((wire.Owner, component), false); + return true; + } + + public override bool Mend(EntityUid user, Wire wire, IdChipSlotComponent component) + { + EntityManager.System() + .SetActive((wire.Owner, component), true); + return true; + } + + public override void Pulse(EntityUid user, Wire wire, IdChipSlotComponent component) + { + } +} diff --git a/Content.Server/_DV/Silicons/BorgTransponderWireAction.cs b/Content.Server/_DV/Silicons/BorgTransponderWireAction.cs new file mode 100644 index 00000000000..c5b63b2e1ee --- /dev/null +++ b/Content.Server/_DV/Silicons/BorgTransponderWireAction.cs @@ -0,0 +1,40 @@ +using Content.Server.Silicons.Borgs; +using Content.Server.Wires; +using Content.Shared._DV.Silicons; +using Content.Shared.Silicons.Borgs.Components; +using Content.Shared.Wires; + +namespace Content.Server._DV.Silicons; + +/// +/// Controls whether the cyborg's transponder is active +/// +public sealed partial class BorgTransponderWireAction : ComponentWireAction +{ + public override string Name { get; set; } = "wire-name-borg-transponder"; + public override Color Color { get; set; } = Color.YellowGreen; + public override object StatusKey => BorgWireActionKey.TransponderKey; + + public override StatusLightState? GetLightState(Wire wire, BorgTransponderComponent component) + { + return component.Active ? StatusLightState.On : StatusLightState.Off; + } + + public override bool Cut(EntityUid user, Wire wire, BorgTransponderComponent component) + { + EntityManager.System() + .SetTransponderActive((wire.Owner, component), false); + return true; + } + + public override bool Mend(EntityUid user, Wire wire, BorgTransponderComponent component) + { + EntityManager.System() + .SetTransponderActive((wire.Owner, component), true); + return true; + } + + public override void Pulse(EntityUid user, Wire wire, BorgTransponderComponent component) + { + } +} diff --git a/Content.Server/_DV/Silicons/Laws/SlavedBorgSystem.cs b/Content.Server/_DV/Silicons/Laws/SlavedBorgSystem.cs index 35b1df39180..5f3721acf49 100644 --- a/Content.Server/_DV/Silicons/Laws/SlavedBorgSystem.cs +++ b/Content.Server/_DV/Silicons/Laws/SlavedBorgSystem.cs @@ -13,6 +13,7 @@ namespace Content.Server._DV.Silicons.Laws; public sealed class SlavedBorgSystem : SharedSlavedBorgSystem { [Dependency] private readonly IPrototypeManager _proto = default!; + [Dependency] private readonly SharedSiliconLawSystem _siliconLaws = default!; public override void Initialize() { @@ -24,12 +25,12 @@ public sealed class SlavedBorgSystem : SharedSlavedBorgSystem private void OnGetSiliconLaws(Entity ent, ref GetSiliconLawsEvent args) { - if (ent.Comp.Added || !TryComp(ent, out var provider)) + if (ent.Comp.HasBeenAdded || !TryComp(ent, out var provider)) return; - if (provider.Lawset is {} lawset) + if (provider.Lawset is {} lawset && ent.Comp.ShouldBeAdded) AddLaw(lawset, ent.Comp.Law); - ent.Comp.Added = true; // prevent opening the ui adding more law 0's + ent.Comp.HasBeenAdded = true; // prevent opening the ui adding more law 0's } /// @@ -39,4 +40,30 @@ public sealed class SlavedBorgSystem : SharedSlavedBorgSystem { lawset.Laws.Insert(0, _proto.Index(law).ShallowClone()); } + + /// + /// Sets whether the slaving is active. + /// + public void SetShouldBeAdded(Entity ent, bool active) + { + if (!Resolve(ent, ref ent.Comp)) + return; + + if (!TryComp(ent, out var provider) || provider.Lawset is not { } lawset) + return; + + if (!ent.Comp.ShouldBeAdded && active) + { + AddLaw(lawset, ent.Comp.Law); + ent.Comp.HasBeenAdded = true; + _siliconLaws.NotifyLawsChanged(ent); + } + else if (ent.Comp.ShouldBeAdded && !active) + { + lawset.Laws.Remove(_proto.Index(ent.Comp.Law)); + ent.Comp.HasBeenAdded = false; + _siliconLaws.NotifyLawsChanged(ent); + } + ent.Comp.ShouldBeAdded = active; + } } diff --git a/Content.Server/_DV/Silicons/PowerCellSlotWireAction.cs b/Content.Server/_DV/Silicons/PowerCellSlotWireAction.cs new file mode 100644 index 00000000000..ee2696e6260 --- /dev/null +++ b/Content.Server/_DV/Silicons/PowerCellSlotWireAction.cs @@ -0,0 +1,40 @@ +using Content.Server.Wires; +using Content.Shared._DV.Silicons; +using Content.Shared.PowerCell; +using Content.Shared.PowerCell.Components; +using Content.Shared.Wires; + +namespace Content.Server._DV.Silicons; + +/// +/// Controls whether the power cell slot is active +/// +public sealed partial class PowerCellSlotWireAction : ComponentWireAction +{ + public override string Name { get; set; } = "wire-name-power-cell"; + public override Color Color { get; set; } = Color.BurlyWood; + public override object StatusKey => BorgWireActionKey.CellKey; + + public override StatusLightState? GetLightState(Wire wire, PowerCellSlotComponent component) + { + return component.Active ? StatusLightState.On : StatusLightState.Off; + } + + public override bool Cut(EntityUid user, Wire wire, PowerCellSlotComponent component) + { + EntityManager.System() + .SetSlotActive((wire.Owner, component), false); + return true; + } + + public override bool Mend(EntityUid user, Wire wire, PowerCellSlotComponent component) + { + EntityManager.System() + .SetSlotActive((wire.Owner, component), true); + return true; + } + + public override void Pulse(EntityUid user, Wire wire, PowerCellSlotComponent component) + { + } +} diff --git a/Content.Server/_DV/Silicons/SlavedBorgWireAction.cs b/Content.Server/_DV/Silicons/SlavedBorgWireAction.cs new file mode 100644 index 00000000000..961ad07b48f --- /dev/null +++ b/Content.Server/_DV/Silicons/SlavedBorgWireAction.cs @@ -0,0 +1,41 @@ +using Content.Server._DV.Silicons.Laws; +using Content.Server.Wires; +using Content.Shared.Doors; +using Content.Shared._DV.Silicons.Laws; +using Content.Shared._DV.Silicons; +using Content.Shared.Wires; + +namespace Content.Server._DV.Silicons; + +/// +/// Controls whether the cyborg's transponder is active +/// +public sealed partial class SlavedBorgWireAction : ComponentWireAction +{ + public override string Name { get; set; } = "wire-name-slaved-borg"; + public override Color Color { get; set; } = Color.Coral; + public override object StatusKey => BorgWireActionKey.SlavedKey; + + public override StatusLightState? GetLightState(Wire wire, SlavedBorgComponent component) + { + return component.ShouldBeAdded ? StatusLightState.On : StatusLightState.Off; + } + + public override bool Cut(EntityUid user, Wire wire, SlavedBorgComponent component) + { + EntityManager.System() + .SetShouldBeAdded((wire.Owner, component), false); + return true; + } + + public override bool Mend(EntityUid user, Wire wire, SlavedBorgComponent component) + { + EntityManager.System() + .SetShouldBeAdded((wire.Owner, component), true); + return true; + } + + public override void Pulse(EntityUid user, Wire wire, SlavedBorgComponent component) + { + } +} diff --git a/Content.Shared/PowerCell/Components/PowerCellSlotComponent.cs b/Content.Shared/PowerCell/Components/PowerCellSlotComponent.cs index b0aad895696..288ba12d862 100644 --- a/Content.Shared/PowerCell/Components/PowerCellSlotComponent.cs +++ b/Content.Shared/PowerCell/Components/PowerCellSlotComponent.cs @@ -23,5 +23,12 @@ public sealed partial class PowerCellSlotComponent : Component [DataField, AutoNetworkedField] public bool FitsInCharger = true; + // Begin DeltaV + /// + /// DeltaV - power cell slots + /// + [DataField, AutoNetworkedField] + public bool Active = true; + // End DeltaV } diff --git a/Content.Shared/PowerCell/PowerCellSystem.API.cs b/Content.Shared/PowerCell/PowerCellSystem.API.cs index 9848000edd4..b060e941670 100644 --- a/Content.Shared/PowerCell/PowerCellSystem.API.cs +++ b/Content.Shared/PowerCell/PowerCellSystem.API.cs @@ -39,6 +39,14 @@ public sealed partial class PowerCellSystem return false; } + // Begin DeltaV + if (!ent.Comp.Active) + { + battery = null; + return false; + } + // End DeltaV + if (!_itemSlots.TryGetSlot(ent.Owner, ent.Comp.CellSlotId, out var slot)) { battery = null; @@ -153,6 +161,21 @@ public sealed partial class PowerCellSystem [PublicAPI] public bool HasCharge(Entity ent, float charge, EntityUid? user = null, bool predicted = false) { + // Begin DeltaV + if (Resolve(ent, ref ent.Comp, false) && !ent.Comp.Active) + { + if (user == null) + return false; + + if (predicted) + _popup.PopupClient(Loc.GetString("power-cell-disabled"), ent.Owner, user.Value); + else + _popup.PopupEntity(Loc.GetString("power-cell-disabled"), ent.Owner, user.Value); + + return false; + } + // End DeltaV + if (!TryGetBatteryFromSlot(ent, out var battery)) { if (user == null) @@ -192,6 +215,21 @@ public sealed partial class PowerCellSystem [PublicAPI] public bool TryUseCharge(Entity ent, float charge, EntityUid? user = null, bool predicted = false) { + // Begin DeltaV + if (Resolve(ent, ref ent.Comp, false) && !ent.Comp.Active) + { + if (user == null) + return false; + + if (predicted) + _popup.PopupClient(Loc.GetString("power-cell-disabled"), ent.Owner, user.Value); + else + _popup.PopupEntity(Loc.GetString("power-cell-disabled"), ent.Owner, user.Value); + + return false; + } + // End DeltaV + if (!TryGetBatteryFromSlot(ent, out var battery)) { if (user == null) @@ -247,6 +285,29 @@ public sealed partial class PowerCellSystem return _battery.GetMaxUses(battery.Value.AsNullable(), cost); } + + // Begin DeltaV + /// + /// Sets whether a power cell slot is capable of drawing from a battery + /// + [PublicAPI] + public void SetSlotActive(Entity ent, bool active) + { + if (!Resolve(ent, ref ent.Comp)) + return; + + ent.Comp.Active = active; + Dirty(ent, ent.Comp); + + if (!active) + { + var emptyEv = new PowerCellSlotEmptyEvent(); + RaiseLocalEvent(ent, ref emptyEv); + } + + _battery.RefreshChargeRate(ent.Owner); + } + // End DeltaV } // Begin DeltaV - event-based search for battery diff --git a/Content.Shared/Silicons/Borgs/Components/BorgTransponderComponent.cs b/Content.Shared/Silicons/Borgs/Components/BorgTransponderComponent.cs index 577056bb46d..4e20382b3b9 100644 --- a/Content.Shared/Silicons/Borgs/Components/BorgTransponderComponent.cs +++ b/Content.Shared/Silicons/Borgs/Components/BorgTransponderComponent.cs @@ -77,4 +77,10 @@ public sealed partial class BorgTransponderComponent : Component /// [DataField] public bool FakeDisabled; + + /// + /// DeltaV - whether the transponder is active + /// + [DataField] + public bool Active = true; } diff --git a/Content.Shared/_DV/Silicon/BorgWireActionKey.cs b/Content.Shared/_DV/Silicon/BorgWireActionKey.cs new file mode 100644 index 00000000000..5b96230e448 --- /dev/null +++ b/Content.Shared/_DV/Silicon/BorgWireActionKey.cs @@ -0,0 +1,12 @@ +using Robust.Shared.Serialization; + +namespace Content.Shared._DV.Silicons; + +[Serializable, NetSerializable] +public enum BorgWireActionKey : byte +{ + SlavedKey, + TransponderKey, + ChipKey, + CellKey, +} diff --git a/Content.Shared/_DV/Silicons/Borgs/IdChipSlotComponent.cs b/Content.Shared/_DV/Silicons/Borgs/IdChipSlotComponent.cs index 305501d081a..d0bbb87b13c 100644 --- a/Content.Shared/_DV/Silicons/Borgs/IdChipSlotComponent.cs +++ b/Content.Shared/_DV/Silicons/Borgs/IdChipSlotComponent.cs @@ -9,7 +9,7 @@ namespace Content.Shared._DV.Silicons.Borgs; /// Adds an id chip slot for a borg which will control its access. /// Enables an id chip's access when inserted to an id chip slot. /// -[RegisterComponent, NetworkedComponent, Access(typeof(IdChipSlotSystem))] +[RegisterComponent, NetworkedComponent, Access(typeof(IdChipSlotSystem)), AutoGenerateComponentState] public sealed partial class IdChipSlotComponent : Component { /// @@ -35,6 +35,12 @@ public sealed partial class IdChipSlotComponent : Component /// [DataField(required: true)] public EntityWhitelist Whitelist = new(); + + /// + /// Whether the ID chip slot is active + /// + [DataField, AutoNetworkedField] + public bool Active = true; } /// diff --git a/Content.Shared/_DV/Silicons/Borgs/IdChipSlotSystem.cs b/Content.Shared/_DV/Silicons/Borgs/IdChipSlotSystem.cs index de4a135692d..1d2fdffa6f1 100644 --- a/Content.Shared/_DV/Silicons/Borgs/IdChipSlotSystem.cs +++ b/Content.Shared/_DV/Silicons/Borgs/IdChipSlotSystem.cs @@ -73,7 +73,7 @@ public sealed class IdChipSlotSystem : EntitySystem private void OnGetAdditionalAccess(Entity ent, ref GetAdditionalAccessEvent args) { - if (ent.Comp.Chip is {} chip) + if (ent.Comp is { Active: true, Chip: {} chip }) args.Entities.Add(chip); } @@ -83,7 +83,7 @@ public sealed class IdChipSlotSystem : EntitySystem return; // enable its access so the borg can use it - _access.SetAccessEnabled(args.Entity, true); + _access.SetAccessEnabled(args.Entity, ent.Comp.Active); } private void OnChipRemoved(Entity ent, ref EntRemovedFromContainerMessage args) @@ -105,4 +105,14 @@ public sealed class IdChipSlotSystem : EntitySystem _container.Remove(chip, ent.Comp.Container); _hands.TryPickupAnyHand(args.Actor, chip); } + + public void SetActive(Entity ent, bool active) + { + if (!Resolve(ent, ref ent.Comp)) + return; + + ent.Comp.Active = active; + _access.SetAccessEnabled(ent.Owner, active && ent.Comp.Chip is not null); + Dirty(ent); + } } diff --git a/Content.Shared/_DV/Silicons/Laws/SlavedBorgComponent.cs b/Content.Shared/_DV/Silicons/Laws/SlavedBorgComponent.cs index b27e9e9a06e..31b6e0e0d3b 100644 --- a/Content.Shared/_DV/Silicons/Laws/SlavedBorgComponent.cs +++ b/Content.Shared/_DV/Silicons/Laws/SlavedBorgComponent.cs @@ -19,8 +19,14 @@ public sealed partial class SlavedBorgComponent : Component public ProtoId Law; /// - /// Prevents adding the same law twice. + /// Prevents adding the same law 0 mutliple times during law updates. /// [DataField] - public bool Added; + public bool HasBeenAdded; + + /// + /// Whether the law 0 should be added to the lawset. + /// + [DataField] + public bool ShouldBeAdded = true; } diff --git a/Resources/Locale/en-US/_DV/silicons/wires.ftl b/Resources/Locale/en-US/_DV/silicons/wires.ftl new file mode 100644 index 00000000000..a596fb33526 --- /dev/null +++ b/Resources/Locale/en-US/_DV/silicons/wires.ftl @@ -0,0 +1,4 @@ +wire-name-borg-transponder = TRNS +wire-name-slaved-borg = SYNC +wire-name-borg-id-chip = CHIP +wire-name-power-cell = CELL diff --git a/Resources/Locale/en-US/_DV/ui/power-cell.ftl b/Resources/Locale/en-US/_DV/ui/power-cell.ftl new file mode 100644 index 00000000000..6a4b2736f1d --- /dev/null +++ b/Resources/Locale/en-US/_DV/ui/power-cell.ftl @@ -0,0 +1 @@ +power-cell-disabled = Power cell disabled diff --git a/Resources/Prototypes/Entities/Mobs/Cyborgs/base_borg_chassis.yml b/Resources/Prototypes/Entities/Mobs/Cyborgs/base_borg_chassis.yml index d35596cda44..c5d7b8ace1f 100644 --- a/Resources/Prototypes/Entities/Mobs/Cyborgs/base_borg_chassis.yml +++ b/Resources/Prototypes/Entities/Mobs/Cyborgs/base_borg_chassis.yml @@ -81,6 +81,10 @@ enum.RingerUiKey.Key: type: RingerBoundUserInterface # End DeltaV - borg PDAs + # Begin DeltaV - borg wires + enum.WiresUiKey.Key: + type: WiresBoundUserInterface + # End DeltaV - type: ActivatableUI key: enum.BorgUiKey.Key - type: SiliconLawBound @@ -364,6 +368,8 @@ breakOnAccessBreaker: false # DeltaV - prevent emag metachecking by unlocking without an id - type: SlavedBorg # DeltaV: NT borgs are enslaved to the AI by default law: ObeyAI + - type: Wires # DeltaV: NT borgs have a circuit panel + layoutId: Cyborg - type: ShowJobIcons - type: InteractionPopup interactSuccessSound: diff --git a/Resources/Prototypes/_DV/Wires/layouts.yml b/Resources/Prototypes/_DV/Wires/layouts.yml index 71e45e2840d..b3c1ad10471 100644 --- a/Resources/Prototypes/_DV/Wires/layouts.yml +++ b/Resources/Prototypes/_DV/Wires/layouts.yml @@ -50,3 +50,15 @@ wires: - !type:PowerWireAction - !type:AiInteractWireAction + +- type: wireLayout + id: Cyborg + dummyWires: 3 + wires: + - !type:BorgTransponderWireAction + - !type:SlavedBorgWireAction + - !type:AiInteractWireAction + - !type:ListenWireAction + - !type:SpeechWireAction + - !type:BorgIdChipWireAction + - !type:PowerCellSlotWireAction