diff --git a/Content.Server/Access/AccessWireAction.cs b/Content.Server/Access/AccessWireAction.cs index 7676ce489f43..b3beb3967b9b 100644 --- a/Content.Server/Access/AccessWireAction.cs +++ b/Content.Server/Access/AccessWireAction.cs @@ -1,7 +1,6 @@ using Content.Server.Wires; using Content.Shared.Access; using Content.Shared.Access.Components; -using Content.Shared.Emag.Components; using Content.Shared.Wires; namespace Content.Server.Access; @@ -31,11 +30,9 @@ public override bool Cut(EntityUid user, Wire wire, AccessReaderComponent comp) public override bool Mend(EntityUid user, Wire wire, AccessReaderComponent comp) { - if (!EntityManager.HasComponent(wire.Owner)) - { - comp.Enabled = true; - EntityManager.Dirty(wire.Owner, comp); - } + comp.Enabled = true; + EntityManager.Dirty(wire.Owner, comp); + return true; } @@ -58,7 +55,7 @@ private void AwaitPulseCancel(Wire wire) { if (!wire.IsCut) { - if (EntityManager.TryGetComponent(wire.Owner, out var access) && !EntityManager.HasComponent(wire.Owner)) + if (EntityManager.TryGetComponent(wire.Owner, out var access)) { access.Enabled = true; EntityManager.Dirty(wire.Owner, access); diff --git a/Content.Server/Access/Systems/AccessOverriderSystem.cs b/Content.Server/Access/Systems/AccessOverriderSystem.cs index a882209b2d17..a0dbc96677b5 100644 --- a/Content.Server/Access/Systems/AccessOverriderSystem.cs +++ b/Content.Server/Access/Systems/AccessOverriderSystem.cs @@ -245,6 +245,10 @@ private void TryWriteToTargetAccessReaderId(EntityUid uid, $"{ToPrettyString(player):player} has modified {ToPrettyString(accessReaderEnt.Value):entity} with the following allowed access level holders: [{string.Join(", ", addedTags.Union(removedTags))}] [{string.Join(", ", newAccessList)}]"); accessReaderEnt.Value.Comp.AccessLists = ConvertAccessListToHashSet(newAccessList); + + var ev = new OnAccessOverriderAccessUpdatedEvent(player); + RaiseLocalEvent(component.TargetAccessReaderId, ref ev); + Dirty(accessReaderEnt.Value); } diff --git a/Content.Server/Anomaly/Effects/TechAnomalySystem.cs b/Content.Server/Anomaly/Effects/TechAnomalySystem.cs index 9f81c64dbc10..983cf2c8f465 100644 --- a/Content.Server/Anomaly/Effects/TechAnomalySystem.cs +++ b/Content.Server/Anomaly/Effects/TechAnomalySystem.cs @@ -116,8 +116,11 @@ private void OnSupercritical(Entity tech, ref AnomalySuper if (_random.Prob(tech.Comp.EmagSupercritProbability)) { - _emag.DoEmagEffect(tech, source); - _emag.DoEmagEffect(tech, sink); + var sourceEv = new GotEmaggedEvent(tech, EmagType.Access | EmagType.Interaction); + RaiseLocalEvent(source, ref sourceEv); + + var sinkEv = new GotEmaggedEvent(tech, EmagType.Access | EmagType.Interaction); + RaiseLocalEvent(sink, ref sinkEv); } CreateNewLink(tech, source, sink); diff --git a/Content.Server/Atmos/Monitor/Systems/FireAlarmSystem.cs b/Content.Server/Atmos/Monitor/Systems/FireAlarmSystem.cs index 0a3ee4d7f7dc..6d768c129261 100644 --- a/Content.Server/Atmos/Monitor/Systems/FireAlarmSystem.cs +++ b/Content.Server/Atmos/Monitor/Systems/FireAlarmSystem.cs @@ -21,6 +21,7 @@ public sealed class FireAlarmSystem : EntitySystem { [Dependency] private readonly AtmosDeviceNetworkSystem _atmosDevNet = default!; [Dependency] private readonly AtmosAlarmableSystem _atmosAlarmable = default!; + [Dependency] private readonly EmagSystem _emag = default!; [Dependency] private readonly SharedInteractionSystem _interactionSystem = default!; [Dependency] private readonly AccessReaderSystem _access = default!; [Dependency] private readonly IConfigurationManager _configManager = default!; @@ -77,11 +78,18 @@ private void OnInteractHand(EntityUid uid, FireAlarmComponent component, Interac private void OnEmagged(EntityUid uid, FireAlarmComponent component, ref GotEmaggedEvent args) { - if (TryComp(uid, out var alarmable)) - { - // Remove the atmos alarmable component permanently from this device. - _atmosAlarmable.ForceAlert(uid, AtmosAlarmType.Emagged, alarmable); - RemCompDeferred(uid); - } + if (!_emag.CompareFlag(args.Type, EmagType.Interaction)) + return; + + if (_emag.CheckFlag(uid, EmagType.Interaction)) + return; + + if (!TryComp(uid, out var alarmable)) + return; + + // Remove the atmos alarmable component permanently from this device. + _atmosAlarmable.ForceAlert(uid, AtmosAlarmType.Emagged, alarmable); + RemCompDeferred(uid); + args.Handled = true; } } diff --git a/Content.Server/Bed/BedSystem.cs b/Content.Server/Bed/BedSystem.cs index 2cc8085e725c..b2d688940861 100644 --- a/Content.Server/Bed/BedSystem.cs +++ b/Content.Server/Bed/BedSystem.cs @@ -20,6 +20,7 @@ public sealed class BedSystem : EntitySystem { [Dependency] private readonly DamageableSystem _damageableSystem = default!; [Dependency] private readonly ActionsSystem _actionsSystem = default!; + [Dependency] private readonly EmagSystem _emag = default!; [Dependency] private readonly SleepingSystem _sleepingSystem = default!; [Dependency] private readonly SharedAppearanceSystem _appearance = default!; [Dependency] private readonly MobStateSystem _mobStateSystem = default!; @@ -114,7 +115,12 @@ private void OnPowerChanged(EntityUid uid, StasisBedComponent component, ref Pow private void OnEmagged(EntityUid uid, StasisBedComponent component, ref GotEmaggedEvent args) { - args.Repeatable = true; + if (!_emag.CompareFlag(args.Type, EmagType.Interaction)) + return; + + if (_emag.CheckFlag(uid, EmagType.Interaction)) + return; + // Reset any metabolisms first so they receive the multiplier correctly UpdateMetabolisms(uid, component, false); component.Multiplier = 1 / component.Multiplier; diff --git a/Content.Server/Cargo/Systems/CargoSystem.Orders.cs b/Content.Server/Cargo/Systems/CargoSystem.Orders.cs index e4bd9515136b..e05b33e975be 100644 --- a/Content.Server/Cargo/Systems/CargoSystem.Orders.cs +++ b/Content.Server/Cargo/Systems/CargoSystem.Orders.cs @@ -8,7 +8,7 @@ using Content.Shared.Cargo.Events; using Content.Shared.Cargo.Prototypes; using Content.Shared.Database; -using Content.Shared.Emag.Components; +using Content.Shared.Emag.Systems; using Content.Shared.IdentityManagement; using Content.Shared.Interaction; using Content.Shared.Paper; @@ -21,6 +21,7 @@ namespace Content.Server.Cargo.Systems public sealed partial class CargoSystem { [Dependency] private readonly SharedTransformSystem _transformSystem = default!; + [Dependency] private readonly EmagSystem _emag = default!; /// /// How much time to wait (in seconds) before increasing bank accounts balance. @@ -41,6 +42,7 @@ private void InitializeConsole() SubscribeLocalEvent(OnInit); SubscribeLocalEvent(OnInteractUsing); SubscribeLocalEvent(OnOrderBalanceUpdated); + SubscribeLocalEvent(OnEmagged); Reset(); } @@ -75,6 +77,17 @@ private void Reset() _timer = 0; } + private void OnEmagged(Entity ent, ref GotEmaggedEvent args) + { + if (!_emag.CompareFlag(args.Type, EmagType.Interaction)) + return; + + if (_emag.CheckFlag(ent, EmagType.Interaction)) + return; + + args.Handled = true; + } + private void UpdateConsole(float frameTime) { _timer += frameTime; @@ -192,7 +205,7 @@ private void OnApproveOrderMessage(EntityUid uid, CargoOrderConsoleComponent com order.Approved = true; _audio.PlayPvs(component.ConfirmSound, uid); - if (!HasComp(uid)) + if (!_emag.CheckFlag(uid, EmagType.Interaction)) { var tryGetIdentityShortInfoEvent = new TryGetIdentityShortInfoEvent(uid, player); RaiseLocalEvent(tryGetIdentityShortInfoEvent); diff --git a/Content.Server/Cloning/CloningSystem.cs b/Content.Server/Cloning/CloningSystem.cs index 65f927400162..d8aac5651599 100644 --- a/Content.Server/Cloning/CloningSystem.cs +++ b/Content.Server/Cloning/CloningSystem.cs @@ -60,6 +60,7 @@ public sealed class CloningSystem : EntitySystem [Dependency] private readonly SharedMindSystem _mindSystem = default!; [Dependency] private readonly MetaDataSystem _metaSystem = default!; [Dependency] private readonly SharedJobSystem _jobs = default!; + [Dependency] private readonly EmagSystem _emag = default!; public readonly Dictionary ClonesWaitingForMind = new(); public const float EasyModeCloningCost = 0.7f; @@ -276,10 +277,15 @@ public override void Update(float frameTime) /// private void OnEmagged(EntityUid uid, CloningPodComponent clonePod, ref GotEmaggedEvent args) { + if (!_emag.CompareFlag(args.Type, EmagType.Interaction)) + return; + + if (_emag.CheckFlag(uid, EmagType.Interaction)) + return; + if (!this.IsPowered(uid, EntityManager)) return; - _audio.PlayPvs(clonePod.SparkSound, uid); _popupSystem.PopupEntity(Loc.GetString("cloning-pod-component-upgrade-emag-requirement"), uid); args.Handled = true; } @@ -309,7 +315,7 @@ private void EndFailedCloning(EntityUid uid, CloningPodComponent clonePod) var indices = _transformSystem.GetGridTilePositionOrDefault((uid, transform)); var tileMix = _atmosphereSystem.GetTileMixture(transform.GridUid, null, indices, true); - if (HasComp(uid)) + if (_emag.CheckFlag(uid, EmagType.Interaction)) { _audio.PlayPvs(clonePod.ScreamSound, uid); Spawn(clonePod.MobSpawnId, transform.Coordinates); @@ -327,7 +333,7 @@ private void EndFailedCloning(EntityUid uid, CloningPodComponent clonePod) } _puddleSystem.TrySpillAt(uid, bloodSolution, out _); - if (!HasComp(uid)) + if (!_emag.CheckFlag(uid, EmagType.Interaction)) { _material.SpawnMultipleFromMaterial(_robustRandom.Next(1, (int) (clonePod.UsedBiomass / 2.5)), clonePod.RequiredMaterial, Transform(uid).Coordinates); } diff --git a/Content.Server/Communications/CommunicationsConsoleSystem.cs b/Content.Server/Communications/CommunicationsConsoleSystem.cs index 6c320edb23c1..abcd93f32806 100644 --- a/Content.Server/Communications/CommunicationsConsoleSystem.cs +++ b/Content.Server/Communications/CommunicationsConsoleSystem.cs @@ -16,7 +16,6 @@ using Content.Shared.Communications; using Content.Shared.Database; using Content.Shared.DeviceNetwork; -using Content.Shared.Emag.Components; using Content.Shared.IdentityManagement; using Content.Shared.Popups; using Robust.Server.GameObjects; @@ -177,7 +176,7 @@ private static bool CanAnnounce(CommunicationsConsoleComponent comp) private bool CanUse(EntityUid user, EntityUid console) { - if (TryComp(console, out var accessReaderComponent) && !HasComp(console)) + if (TryComp(console, out var accessReaderComponent)) { return _accessReaderSystem.IsAllowed(user, console, accessReaderComponent); } diff --git a/Content.Server/Fax/FaxSystem.cs b/Content.Server/Fax/FaxSystem.cs index a43d0171e604..b73c101e3fea 100644 --- a/Content.Server/Fax/FaxSystem.cs +++ b/Content.Server/Fax/FaxSystem.cs @@ -50,6 +50,7 @@ public sealed class FaxSystem : EntitySystem [Dependency] private readonly ISharedAdminLogManager _adminLogger = default!; [Dependency] private readonly MetaDataSystem _metaData = default!; [Dependency] private readonly FaxecuteSystem _faxecute = default!; + [Dependency] private readonly EmagSystem _emag = default!; private const string PaperSlotId = "Paper"; @@ -227,7 +228,7 @@ private void OnInteractUsing(EntityUid uid, FaxMachineComponent component, Inter return; } - if (component.KnownFaxes.ContainsValue(newName) && !HasComp(uid)) // Allow existing names if emagged for fun + if (component.KnownFaxes.ContainsValue(newName) && !_emag.CheckFlag(uid, EmagType.Interaction)) // Allow existing names if emagged for fun { _popupSystem.PopupEntity(Loc.GetString("fax-machine-popup-name-exist"), uid); return; @@ -246,7 +247,12 @@ private void OnInteractUsing(EntityUid uid, FaxMachineComponent component, Inter private void OnEmagged(EntityUid uid, FaxMachineComponent component, ref GotEmaggedEvent args) { - _audioSystem.PlayPvs(component.EmagSound, uid); + if (!_emag.CompareFlag(args.Type, EmagType.Interaction)) + return; + + if (_emag.CheckFlag(uid, EmagType.Interaction)) + return; + args.Handled = true; } @@ -260,7 +266,7 @@ private void OnPacketReceived(EntityUid uid, FaxMachineComponent component, Devi switch (command) { case FaxConstants.FaxPingCommand: - var isForSyndie = HasComp(uid) && + var isForSyndie = _emag.CheckFlag(uid, EmagType.Interaction) && args.Data.ContainsKey(FaxConstants.FaxSyndicateData); if (!isForSyndie && !component.ResponsePings) return; @@ -405,7 +411,7 @@ public void Refresh(EntityUid uid, FaxMachineComponent? component = null) { DeviceNetworkConstants.Command, FaxConstants.FaxPingCommand } }; - if (HasComp(uid)) + if (_emag.CheckFlag(uid, EmagType.Interaction)) payload.Add(FaxConstants.FaxSyndicateData, true); _deviceNetworkSystem.QueuePacket(uid, null, payload); diff --git a/Content.Server/Lathe/LatheSystem.cs b/Content.Server/Lathe/LatheSystem.cs index 18f246dcef49..ba82e65aa79a 100644 --- a/Content.Server/Lathe/LatheSystem.cs +++ b/Content.Server/Lathe/LatheSystem.cs @@ -16,6 +16,7 @@ using Content.Shared.UserInterface; using Content.Shared.Database; using Content.Shared.Emag.Components; +using Content.Shared.Emag.Systems; using Content.Shared.Examine; using Content.Shared.Lathe; using Content.Shared.Materials; @@ -42,6 +43,7 @@ public sealed class LatheSystem : SharedLatheSystem [Dependency] private readonly SharedAppearanceSystem _appearance = default!; [Dependency] private readonly SharedAudioSystem _audio = default!; [Dependency] private readonly ContainerSystem _container = default!; + [Dependency] private readonly EmagSystem _emag = default!; [Dependency] private readonly UserInterfaceSystem _uiSys = default!; [Dependency] private readonly MaterialStorageSystem _materialStorage = default!; [Dependency] private readonly PopupSystem _popup = default!; @@ -292,7 +294,7 @@ private void GetEmagLatheRecipes(EntityUid uid, EmagLatheRecipesComponent compon { if (uid != args.Lathe || !TryComp(uid, out var technologyDatabase)) return; - if (!args.getUnavailable && !HasComp(uid)) + if (!args.getUnavailable && !_emag.CheckFlag(uid, EmagType.Interaction)) return; foreach (var recipe in component.EmagDynamicRecipes) { diff --git a/Content.Server/Nutrition/EntitySystems/FatExtractorSystem.cs b/Content.Server/Nutrition/EntitySystems/FatExtractorSystem.cs index 6e9856a61dc0..8f4c5f415003 100644 --- a/Content.Server/Nutrition/EntitySystems/FatExtractorSystem.cs +++ b/Content.Server/Nutrition/EntitySystems/FatExtractorSystem.cs @@ -21,6 +21,7 @@ namespace Content.Server.Nutrition.EntitySystems; public sealed class FatExtractorSystem : EntitySystem { [Dependency] private readonly IGameTiming _timing = default!; + [Dependency] private readonly EmagSystem _emag = default!; [Dependency] private readonly HungerSystem _hunger = default!; [Dependency] private readonly SharedAppearanceSystem _appearance = default!; [Dependency] private readonly SharedAudioSystem _audio = default!; @@ -36,8 +37,13 @@ public override void Initialize() private void OnGotEmagged(EntityUid uid, FatExtractorComponent component, ref GotEmaggedEvent args) { + if (!_emag.CompareFlag(args.Type, EmagType.Interaction)) + return; + + if (_emag.CheckFlag(uid, EmagType.Interaction)) + return; + args.Handled = true; - args.Repeatable = false; } private void OnClosed(EntityUid uid, FatExtractorComponent component, ref StorageAfterCloseEvent args) @@ -103,7 +109,7 @@ public bool TryGetValidOccupant(EntityUid uid, [NotNullWhen(true)] out EntityUid if (_hunger.GetHunger(hunger) < component.NutritionPerSecond) return false; - if (hunger.CurrentThreshold < component.MinHungerThreshold && !HasComp(uid)) + if (hunger.CurrentThreshold < component.MinHungerThreshold && !_emag.CheckFlag(uid, EmagType.Interaction)) return false; return true; diff --git a/Content.Server/Nutrition/EntitySystems/SmokingSystem.Vape.cs b/Content.Server/Nutrition/EntitySystems/SmokingSystem.Vape.cs index 26fa5ca3cc8f..48fb946135e7 100644 --- a/Content.Server/Nutrition/EntitySystems/SmokingSystem.Vape.cs +++ b/Content.Server/Nutrition/EntitySystems/SmokingSystem.Vape.cs @@ -24,6 +24,7 @@ public sealed partial class SmokingSystem { [Dependency] private readonly DoAfterSystem _doAfterSystem = default!; [Dependency] private readonly DamageableSystem _damageableSystem = default!; + [Dependency] private readonly EmagSystem _emag = default!; [Dependency] private readonly FoodSystem _foodSystem = default!; [Dependency] private readonly ExplosionSystem _explosionSystem = default!; [Dependency] private readonly PopupSystem _popupSystem = default!; @@ -63,7 +64,7 @@ private void OnVapeInteraction(Entity entity, ref AfterInteractEv forced = false; } - if (entity.Comp.ExplodeOnUse || HasComp(entity.Owner)) + if (entity.Comp.ExplodeOnUse || _emag.CheckFlag(entity, EmagType.Interaction)) { _explosionSystem.QueueExplosion(entity.Owner, "Default", entity.Comp.ExplosionIntensity, 0.5f, 3, canCreateVacuum: false); EntityManager.DeleteEntity(entity); @@ -161,8 +162,15 @@ private void OnVapeDoAfter(Entity entity, ref VapeDoAfterEvent ar args.Args.Target.Value); } } + private void OnEmagged(Entity entity, ref GotEmaggedEvent args) { + if (!_emag.CompareFlag(args.Type, EmagType.Interaction)) + return; + + if (_emag.CheckFlag(entity, EmagType.Interaction)) + return; + args.Handled = true; } } diff --git a/Content.Server/Power/EntitySystems/ApcSystem.cs b/Content.Server/Power/EntitySystems/ApcSystem.cs index 14dddbb43e32..29c1431179da 100644 --- a/Content.Server/Power/EntitySystems/ApcSystem.cs +++ b/Content.Server/Power/EntitySystems/ApcSystem.cs @@ -4,7 +4,6 @@ using Content.Server.Power.Pow3r; using Content.Shared.Access.Systems; using Content.Shared.APC; -using Content.Shared.Emag.Components; using Content.Shared.Emag.Systems; using Content.Shared.Popups; using Content.Shared.Rounding; @@ -19,6 +18,7 @@ public sealed class ApcSystem : EntitySystem { [Dependency] private readonly AccessReaderSystem _accessReader = default!; [Dependency] private readonly IGameTiming _gameTiming = default!; + [Dependency] private readonly EmagSystem _emag = default!; [Dependency] private readonly PopupSystem _popup = default!; [Dependency] private readonly SharedAppearanceSystem _appearance = default!; [Dependency] private readonly SharedAudioSystem _audio = default!; @@ -111,7 +111,12 @@ public void ApcToggleBreaker(EntityUid uid, ApcComponent? apc = null, PowerNetwo private void OnEmagged(EntityUid uid, ApcComponent comp, ref GotEmaggedEvent args) { - // no fancy conditions + if (!_emag.CompareFlag(args.Type, EmagType.Interaction)) + return; + + if (_emag.CheckFlag(uid, EmagType.Interaction)) + return; + args.Handled = true; } @@ -170,7 +175,7 @@ public void UpdateUIState(EntityUid uid, private ApcChargeState CalcChargeState(EntityUid uid, PowerState.Battery battery) { - if (HasComp(uid)) + if (_emag.CheckFlag(uid, EmagType.Interaction)) return ApcChargeState.Emag; if (battery.CurrentStorage / battery.Capacity > ApcComponent.HighPowerThreshold) diff --git a/Content.Server/Research/Systems/ResearchSystem.Console.cs b/Content.Server/Research/Systems/ResearchSystem.Console.cs index 127b80a631e6..c227aee7f075 100644 --- a/Content.Server/Research/Systems/ResearchSystem.Console.cs +++ b/Content.Server/Research/Systems/ResearchSystem.Console.cs @@ -3,6 +3,7 @@ using Content.Shared.UserInterface; using Content.Shared.Access.Components; using Content.Shared.Emag.Components; +using Content.Shared.Emag.Systems; using Content.Shared.IdentityManagement; using Content.Shared.Research.Components; using Content.Shared.Research.Prototypes; @@ -11,6 +12,8 @@ namespace Content.Server.Research.Systems; public sealed partial class ResearchSystem { + [Dependency] private readonly EmagSystem _emag = default!; + private void InitializeConsole() { SubscribeLocalEvent(OnConsoleUnlock); @@ -18,6 +21,7 @@ private void InitializeConsole() SubscribeLocalEvent(OnPointsChanged); SubscribeLocalEvent(OnConsoleRegistrationChanged); SubscribeLocalEvent(OnConsoleDatabaseModified); + SubscribeLocalEvent(OnEmagged); } private void OnConsoleUnlock(EntityUid uid, ResearchConsoleComponent component, ConsoleUnlockTechnologyMessage args) @@ -39,7 +43,7 @@ private void OnConsoleUnlock(EntityUid uid, ResearchConsoleComponent component, if (!UnlockTechnology(uid, args.Id, act)) return; - if (!HasComp(uid)) + if (!_emag.CheckFlag(uid, EmagType.Interaction)) { var getIdentityEvent = new TryGetIdentityShortInfoEvent(uid, act); RaiseLocalEvent(getIdentityEvent); @@ -52,7 +56,7 @@ private void OnConsoleUnlock(EntityUid uid, ResearchConsoleComponent component, ); _radio.SendRadioMessage(uid, message, component.AnnouncementChannel, uid, escapeMarkup: false); } - + SyncClientWithServer(uid); UpdateConsoleInterface(uid, component); } @@ -100,4 +104,15 @@ private void OnConsoleDatabaseModified(EntityUid uid, ResearchConsoleComponent c UpdateConsoleInterface(uid, component); } + private void OnEmagged(Entity ent, ref GotEmaggedEvent args) + { + if (!_emag.CompareFlag(args.Type, EmagType.Interaction)) + return; + + if (_emag.CheckFlag(ent, EmagType.Interaction)) + return; + + args.Handled = true; + } + } diff --git a/Content.Server/Revenant/EntitySystems/RevenantSystem.Abilities.cs b/Content.Server/Revenant/EntitySystems/RevenantSystem.Abilities.cs index 6b83a61d2860..1eb9aabed6a1 100644 --- a/Content.Server/Revenant/EntitySystems/RevenantSystem.Abilities.cs +++ b/Content.Server/Revenant/EntitySystems/RevenantSystem.Abilities.cs @@ -36,7 +36,6 @@ public sealed partial class RevenantSystem { [Dependency] private readonly ThrowingSystem _throwing = default!; [Dependency] private readonly EntityStorageSystem _entityStorage = default!; - [Dependency] private readonly EmagSystem _emag = default!; [Dependency] private readonly SharedAppearanceSystem _appearance = default!; [Dependency] private readonly MobThresholdSystem _mobThresholdSystem = default!; [Dependency] private readonly GhostSystem _ghost = default!; @@ -343,7 +342,8 @@ private void OnMalfunctionAction(EntityUid uid, RevenantComponent component, Rev _whitelistSystem.IsBlacklistPass(component.MalfunctionBlacklist, ent)) continue; - _emag.DoEmagEffect(uid, ent); //it does not emag itself. adorable. + var ev = new GotEmaggedEvent(uid, EmagType.Interaction | EmagType.Access); + RaiseLocalEvent(ent, ref ev); } } } diff --git a/Content.Server/Silicons/Borgs/BorgSystem.Transponder.cs b/Content.Server/Silicons/Borgs/BorgSystem.Transponder.cs index b4ba14004415..4d2a8912e830 100644 --- a/Content.Server/Silicons/Borgs/BorgSystem.Transponder.cs +++ b/Content.Server/Silicons/Borgs/BorgSystem.Transponder.cs @@ -8,6 +8,7 @@ using Content.Server.DeviceNetwork.Components; using Content.Server.DeviceNetwork.Systems; using Content.Server.Explosion.Components; +using Content.Shared.Emag.Systems; using Robust.Shared.Utility; namespace Content.Server.Silicons.Borgs; @@ -15,6 +16,8 @@ namespace Content.Server.Silicons.Borgs; /// public sealed partial class BorgSystem { + [Dependency] private readonly EmagSystem _emag = default!; + private void InitializeTransponder() { SubscribeLocalEvent(OnPacketReceived); @@ -127,7 +130,7 @@ private void Destroy(Entity ent) private bool CheckEmagged(EntityUid uid, string name) { - if (HasComp(uid)) + if (_emag.CheckFlag(uid, EmagType.Interaction)) { Popup.PopupEntity(Loc.GetString($"borg-transponder-emagged-{name}-popup"), uid, uid, PopupType.LargeCaution); return true; diff --git a/Content.Server/Silicons/Laws/SiliconLawSystem.cs b/Content.Server/Silicons/Laws/SiliconLawSystem.cs index 109c183f0401..f5ead80bddbf 100644 --- a/Content.Server/Silicons/Laws/SiliconLawSystem.cs +++ b/Content.Server/Silicons/Laws/SiliconLawSystem.cs @@ -6,7 +6,6 @@ using Content.Server.Station.Systems; using Content.Shared.Administration; using Content.Shared.Chat; -using Content.Shared.Emag.Components; using Content.Shared.Emag.Systems; using Content.Shared.GameTicking; using Content.Shared.Mind; @@ -14,7 +13,6 @@ using Content.Shared.Roles; using Content.Shared.Silicons.Laws; using Content.Shared.Silicons.Laws.Components; -using Content.Shared.Stunnable; using Content.Shared.Wires; using Robust.Server.GameObjects; using Robust.Shared.Audio; @@ -22,7 +20,6 @@ using Robust.Shared.Player; using Robust.Shared.Prototypes; using Robust.Shared.Toolshed; -using Robust.Shared.Audio; namespace Content.Server.Silicons.Laws; @@ -34,8 +31,8 @@ public sealed class SiliconLawSystem : SharedSiliconLawSystem [Dependency] private readonly IPrototypeManager _prototype = default!; [Dependency] private readonly SharedRoleSystem _roles = default!; [Dependency] private readonly StationSystem _station = default!; - [Dependency] private readonly SharedStunSystem _stunSystem = default!; [Dependency] private readonly UserInterfaceSystem _userInterface = default!; + [Dependency] private readonly EmagSystem _emag = default!; /// public override void Initialize() @@ -52,7 +49,7 @@ public override void Initialize() SubscribeLocalEvent(OnIonStormLaws); SubscribeLocalEvent(OnLawProviderMindAdded); SubscribeLocalEvent(OnLawProviderMindRemoved); - SubscribeLocalEvent(OnEmagLawsAdded); + SubscribeLocalEvent(OnEmagLawsAdded); } private void OnMapInit(EntityUid uid, SiliconLawBoundComponent component, MapInitEvent args) @@ -135,7 +132,7 @@ private void OnDirectedGetLaws(EntityUid uid, SiliconLawProviderComponent compon private void OnIonStormLaws(EntityUid uid, SiliconLawProviderComponent component, ref IonStormLawsEvent args) { // Emagged borgs are immune to ion storm - if (!HasComp(uid)) + if (!_emag.CheckFlag(uid, EmagType.Interaction)) { component.Lawset = args.Lawset; @@ -152,9 +149,8 @@ private void OnIonStormLaws(EntityUid uid, SiliconLawProviderComponent component } } - private void OnEmagLawsAdded(EntityUid uid, SiliconLawProviderComponent component, ref GotEmaggedEvent args) + private void OnEmagLawsAdded(EntityUid uid, SiliconLawProviderComponent component, ref SiliconEmaggedEvent args) { - if (component.Lawset == null) component.Lawset = GetLawset(component.Laws); @@ -164,7 +160,7 @@ private void OnEmagLawsAdded(EntityUid uid, SiliconLawProviderComponent componen // Add the first emag law before the others component.Lawset?.Laws.Insert(0, new SiliconLaw { - LawString = Loc.GetString("law-emag-custom", ("name", Name(args.UserUid)), ("title", Loc.GetString(component.Lawset.ObeysTo))), + LawString = Loc.GetString("law-emag-custom", ("name", Name(args.user)), ("title", Loc.GetString(component.Lawset.ObeysTo))), Order = 0 }); @@ -176,20 +172,6 @@ private void OnEmagLawsAdded(EntityUid uid, SiliconLawProviderComponent componen }); } - protected override void OnGotEmagged(EntityUid uid, EmagSiliconLawComponent component, ref GotEmaggedEvent args) - { - if (component.RequireOpenPanel && TryComp(uid, out var panel) && !panel.Open) - return; - - base.OnGotEmagged(uid, component, ref args); - NotifyLawsChanged(uid, component.EmaggedSound); - if(_mind.TryGetMind(uid, out var mindId, out _)) - EnsureSubvertedSiliconRole(mindId); - - _stunSystem.TryParalyze(uid, component.StunTime, true); - - } - private void EnsureSubvertedSiliconRole(EntityUid mindId) { if (!_roles.MindHasRole(mindId)) diff --git a/Content.Server/VendingMachines/VendingMachineSystem.cs b/Content.Server/VendingMachines/VendingMachineSystem.cs index c20a6a46446c..c2ea6dd1ac00 100644 --- a/Content.Server/VendingMachines/VendingMachineSystem.cs +++ b/Content.Server/VendingMachines/VendingMachineSystem.cs @@ -39,6 +39,7 @@ public sealed class VendingMachineSystem : SharedVendingMachineSystem [Dependency] private readonly IGameTiming _timing = default!; [Dependency] private readonly SpeakOnUIClosedSystem _speakOnUIClosed = default!; [Dependency] private readonly SharedPointLightSystem _light = default!; + [Dependency] private readonly EmagSystem _emag = default!; private const float WallVendEjectDistanceFromWall = 1f; @@ -48,7 +49,6 @@ public override void Initialize() SubscribeLocalEvent(OnPowerChanged); SubscribeLocalEvent(OnBreak); - SubscribeLocalEvent(OnEmagged); SubscribeLocalEvent(OnDamageChanged); SubscribeLocalEvent(OnVendingPrice); SubscribeLocalEvent(OnEmpPulse); @@ -123,12 +123,6 @@ private void OnBreak(EntityUid uid, VendingMachineComponent vendComponent, Break TryUpdateVisualState(uid, vendComponent); } - private void OnEmagged(EntityUid uid, VendingMachineComponent component, ref GotEmaggedEvent args) - { - // only emag if there are emag-only items - args.Handled = component.EmaggedInventory.Count > 0; - } - private void OnDamageChanged(EntityUid uid, VendingMachineComponent component, DamageChangedEvent args) { if (!args.DamageIncreased && component.Broken) @@ -232,7 +226,7 @@ public bool IsAuthorized(EntityUid uid, EntityUid sender, VendingMachineComponen if (!TryComp(uid, out var accessReader)) return true; - if (_accessReader.IsAllowed(sender, uid, accessReader) || HasComp(uid)) + if (_accessReader.IsAllowed(sender, uid, accessReader)) return true; Popup.PopupEntity(Loc.GetString("vending-machine-component-try-eject-access-denied"), uid); @@ -422,7 +416,7 @@ private void EjectItem(EntityUid uid, VendingMachineComponent? vendComponent = n if (!Resolve(uid, ref component)) return null; - if (type == InventoryType.Emagged && HasComp(uid)) + if (type == InventoryType.Emagged && _emag.CheckFlag(uid, EmagType.Interaction)) return component.EmaggedInventory.GetValueOrDefault(entryId); if (type == InventoryType.Contraband && component.Contraband) diff --git a/Content.Shared/Access/Components/AccessReaderComponent.cs b/Content.Shared/Access/Components/AccessReaderComponent.cs index 903ceab186d1..0219fd2b1ad6 100644 --- a/Content.Shared/Access/Components/AccessReaderComponent.cs +++ b/Content.Shared/Access/Components/AccessReaderComponent.cs @@ -76,7 +76,7 @@ public sealed partial class AccessReaderComponent : Component /// Whether or not emag interactions have an effect on this. /// [DataField] - public bool BreakOnEmag = true; + public bool BreakOnAccessBreaker = true; } [DataDefinition, Serializable, NetSerializable] diff --git a/Content.Shared/Access/Systems/AccessReaderSystem.cs b/Content.Shared/Access/Systems/AccessReaderSystem.cs index 0b1d508cc241..84de549b6636 100644 --- a/Content.Shared/Access/Systems/AccessReaderSystem.cs +++ b/Content.Shared/Access/Systems/AccessReaderSystem.cs @@ -2,7 +2,6 @@ using System.Linq; using Content.Shared.Access.Components; using Content.Shared.DeviceLinking.Events; -using Content.Shared.Emag.Components; using Content.Shared.Emag.Systems; using Content.Shared.Hands.EntitySystems; using Content.Shared.Inventory; @@ -24,6 +23,7 @@ public sealed class AccessReaderSystem : EntitySystem [Dependency] private readonly IPrototypeManager _prototype = default!; [Dependency] private readonly InventorySystem _inventorySystem = default!; [Dependency] private readonly IGameTiming _gameTiming = default!; + [Dependency] private readonly EmagSystem _emag = default!; [Dependency] private readonly SharedGameTicker _gameTicker = default!; [Dependency] private readonly SharedHandsSystem _handsSystem = default!; [Dependency] private readonly SharedContainerSystem _containerSystem = default!; @@ -71,17 +71,28 @@ private void OnLinkAttempt(EntityUid uid, AccessReaderComponent component, LinkA { if (args.User == null) // AutoLink (and presumably future external linkers) have no user. return; - if (!HasComp(uid) && !IsAllowed(args.User.Value, uid, component)) + if (!IsAllowed(args.User.Value, uid, component)) args.Cancel(); } private void OnEmagged(EntityUid uid, AccessReaderComponent reader, ref GotEmaggedEvent args) { - if (!reader.BreakOnEmag) + if (!_emag.CompareFlag(args.Type, EmagType.Access)) return; + + if (!reader.BreakOnAccessBreaker) + return; + + if (!GetMainAccessReader(uid, out var accessReader)) + return; + + if (accessReader.Value.Comp.AccessLists.Count < 1) + return; + + args.Repeatable = true; args.Handled = true; - reader.Enabled = false; - reader.AccessLog.Clear(); + accessReader.Value.Comp.AccessLists.Clear(); + accessReader.Value.Comp.AccessLog.Clear(); Dirty(uid, reader); } @@ -135,6 +146,7 @@ public bool GetMainAccessReader(EntityUid uid, [NotNullWhen(true)] out Entity - /// Emag sound effects. - /// - [DataField("sparkSound")] - public SoundSpecifier SparkSound = new SoundCollectionSpecifier("sparks") - { - Params = AudioParams.Default.WithVolume(8), - }; - // TODO: Remove this from here when cloning and/or zombies are refactored [DataField("screamSound")] public SoundSpecifier ScreamSound = new SoundCollectionSpecifier("ZombieScreams") diff --git a/Content.Shared/Disposal/SharedDisposalUnitSystem.cs b/Content.Shared/Disposal/SharedDisposalUnitSystem.cs index 9fdb4a6a804d..a650ef72f803 100644 --- a/Content.Shared/Disposal/SharedDisposalUnitSystem.cs +++ b/Content.Shared/Disposal/SharedDisposalUnitSystem.cs @@ -24,6 +24,7 @@ public sealed partial class DisposalDoAfterEvent : SimpleDoAfterEvent public abstract class SharedDisposalUnitSystem : EntitySystem { [Dependency] protected readonly IGameTiming GameTiming = default!; + [Dependency] protected readonly EmagSystem _emag = default!; [Dependency] protected readonly MetaDataSystem Metadata = default!; [Dependency] protected readonly SharedJointSystem Joints = default!; [Dependency] private readonly EntityWhitelistSystem _whitelistSystem = default!; @@ -102,6 +103,12 @@ protected void OnCanDragDropOn(EntityUid uid, SharedDisposalUnitComponent compon protected void OnEmagged(EntityUid uid, SharedDisposalUnitComponent component, ref GotEmaggedEvent args) { + if (!_emag.CompareFlag(args.Type, EmagType.Interaction)) + return; + + if (component.DisablePressure == true) + return; + component.DisablePressure = true; args.Handled = true; } diff --git a/Content.Shared/Doors/Systems/SharedDoorSystem.cs b/Content.Shared/Doors/Systems/SharedDoorSystem.cs index ef9d28e4ef7a..f2dcd443e918 100644 --- a/Content.Shared/Doors/Systems/SharedDoorSystem.cs +++ b/Content.Shared/Doors/Systems/SharedDoorSystem.cs @@ -34,6 +34,7 @@ public abstract partial class SharedDoorSystem : EntitySystem [Dependency] private readonly INetManager _net = default!; [Dependency] protected readonly SharedPhysicsSystem PhysicsSystem = default!; [Dependency] private readonly DamageableSystem _damageableSystem = default!; + [Dependency] private readonly EmagSystem _emag = default!; [Dependency] private readonly SharedStunSystem _stunSystem = default!; [Dependency] protected readonly TagSystem Tags = default!; [Dependency] protected readonly SharedAudioSystem Audio = default!; @@ -77,8 +78,6 @@ public override void Initialize() SubscribeLocalEvent(OnWeldAttempt); SubscribeLocalEvent(OnWeldChanged); SubscribeLocalEvent(OnPryTimeModifier); - - SubscribeLocalEvent(OnAttemptEmag); SubscribeLocalEvent(OnEmagged); } @@ -118,31 +117,24 @@ private void OnRemove(Entity door, ref ComponentRemove args) _activeDoors.Remove(door); } - private void OnAttemptEmag(EntityUid uid, DoorComponent door, ref OnAttemptEmagEvent args) + private void OnEmagged(EntityUid uid, DoorComponent door, ref GotEmaggedEvent args) { + if (!_emag.CompareFlag(args.Type, EmagType.Access)) + return; + if (!TryComp(uid, out var airlock)) - { - args.Handled = true; return; - } if (IsBolted(uid) || !airlock.Powered) - { - args.Handled = true; return; - } if (door.State != DoorState.Closed) - { - args.Handled = true; - } - } + return; - private void OnEmagged(EntityUid uid, DoorComponent door, ref GotEmaggedEvent args) - { if (!SetState(uid, DoorState.Emagging, door)) return; - Audio.PlayPredicted(door.SparkSound, uid, args.UserUid, AudioParams.Default.WithVolume(8)); + + args.Repeatable = true; args.Handled = true; } diff --git a/Content.Shared/Emag/Components/EmagComponent.cs b/Content.Shared/Emag/Components/EmagComponent.cs index 235cf0c74448..23d3b90eeda9 100644 --- a/Content.Shared/Emag/Components/EmagComponent.cs +++ b/Content.Shared/Emag/Components/EmagComponent.cs @@ -1,6 +1,8 @@ using Content.Shared.Emag.Systems; using Content.Shared.Tag; +using Robust.Shared.Audio; using Robust.Shared.GameStates; +using Robust.Shared.Prototypes; using Robust.Shared.Serialization.TypeSerializers.Implementations.Custom.Prototype; using Robust.Shared.Serialization; @@ -14,7 +16,21 @@ public sealed partial class EmagComponent : Component /// /// The tag that marks an entity as immune to emags /// - [DataField("emagImmuneTag", customTypeSerializer: typeof(PrototypeIdSerializer)), ViewVariables(VVAccess.ReadWrite)] + [DataField] [AutoNetworkedField] - public string EmagImmuneTag = "EmagImmune"; + public ProtoId EmagImmuneTag = "EmagImmune"; + + /// + /// What type of emag effect this device will do + /// + [DataField] + [AutoNetworkedField] + public EmagType EmagType = EmagType.Interaction; + + /// + /// What sound should the emag play when used + /// + [DataField] + [AutoNetworkedField] + public SoundSpecifier EmagSound = new SoundCollectionSpecifier("sparks"); } diff --git a/Content.Shared/Emag/Components/EmaggedComponent.cs b/Content.Shared/Emag/Components/EmaggedComponent.cs index 337f1a8e5611..152fdca8e368 100644 --- a/Content.Shared/Emag/Components/EmaggedComponent.cs +++ b/Content.Shared/Emag/Components/EmaggedComponent.cs @@ -1,3 +1,4 @@ +using Content.Shared.Emag.Systems; using Robust.Shared.GameStates; namespace Content.Shared.Emag.Components; @@ -5,7 +6,12 @@ namespace Content.Shared.Emag.Components; /// /// Marker component for emagged entities /// -[RegisterComponent, NetworkedComponent] +[RegisterComponent, NetworkedComponent, AutoGenerateComponentState] public sealed partial class EmaggedComponent : Component { + /// + /// The EmagType flags that were used to emag this device + /// + [DataField, AutoNetworkedField] + public EmagType EmagType = EmagType.None; } diff --git a/Content.Shared/Emag/Systems/EmagSystem.cs b/Content.Shared/Emag/Systems/EmagSystem.cs index 3a556b47063b..e6d0e1e653cb 100644 --- a/Content.Shared/Emag/Systems/EmagSystem.cs +++ b/Content.Shared/Emag/Systems/EmagSystem.cs @@ -6,8 +6,8 @@ using Content.Shared.IdentityManagement; using Content.Shared.Interaction; using Content.Shared.Popups; -using Content.Shared.Silicons.Laws.Components; using Content.Shared.Tag; +using Robust.Shared.Audio.Systems; namespace Content.Shared.Emag.Systems; @@ -23,88 +23,123 @@ public sealed class EmagSystem : EntitySystem [Dependency] private readonly SharedChargesSystem _charges = default!; [Dependency] private readonly SharedPopupSystem _popup = default!; [Dependency] private readonly TagSystem _tag = default!; + [Dependency] private readonly SharedAudioSystem _audio = default!; public override void Initialize() { base.Initialize(); SubscribeLocalEvent(OnAfterInteract); + SubscribeLocalEvent(OnAccessOverriderAccessUpdated); } + private void OnAccessOverriderAccessUpdated(Entity entity, ref OnAccessOverriderAccessUpdatedEvent args) + { + if (!CompareFlag(entity.Comp.EmagType, EmagType.Access)) + return; + + entity.Comp.EmagType &= ~EmagType.Access; + Dirty(entity); + } private void OnAfterInteract(EntityUid uid, EmagComponent comp, AfterInteractEvent args) { if (!args.CanReach || args.Target is not { } target) return; - args.Handled = TryUseEmag(uid, args.User, target, comp); + args.Handled = TryEmagEffect((uid, comp), args.User, target); } /// - /// Tries to use the emag on a target entity + /// Does the emag effect on a specified entity /// - public bool TryUseEmag(EntityUid uid, EntityUid user, EntityUid target, EmagComponent? comp = null) + public bool TryEmagEffect(Entity ent, EntityUid user, EntityUid target) { - if (!Resolve(uid, ref comp, false)) + if (!Resolve(ent, ref ent.Comp, false)) return false; - if (_tag.HasTag(target, comp.EmagImmuneTag)) + if (_tag.HasTag(target, ent.Comp.EmagImmuneTag)) return false; - TryComp(uid, out var charges); - if (_charges.IsEmpty(uid, charges)) + TryComp(ent, out var charges); + if (_charges.IsEmpty(ent, charges)) { _popup.PopupClient(Loc.GetString("emag-no-charges"), user, user); return false; } - var handled = DoEmagEffect(user, target); - if (!handled) + var emaggedEvent = new GotEmaggedEvent(user, ent.Comp.EmagType); + RaiseLocalEvent(target, ref emaggedEvent); + + if (!emaggedEvent.Handled) return false; - _popup.PopupClient(Loc.GetString("emag-success", ("target", Identity.Entity(target, EntityManager))), user, - user, PopupType.Medium); + _popup.PopupPredicted(Loc.GetString("emag-success", ("target", Identity.Entity(target, EntityManager))), user, user, PopupType.Medium); - _adminLogger.Add(LogType.Emag, LogImpact.High, $"{ToPrettyString(user):player} emagged {ToPrettyString(target):target}"); + _audio.PlayPredicted(ent.Comp.EmagSound, ent, ent); - if (charges != null) - _charges.UseCharge(uid, charges); - return true; + _adminLogger.Add(LogType.Emag, LogImpact.High, $"{ToPrettyString(user):player} emagged {ToPrettyString(target):target} with flag(s): {ent.Comp.EmagType}"); + + if (charges != null && emaggedEvent.Handled) + _charges.UseCharge(ent, charges); + + if (!emaggedEvent.Repeatable) + { + EnsureComp(target, out var emaggedComp); + + emaggedComp.EmagType |= ent.Comp.EmagType; + Dirty(target, emaggedComp); + } + + return emaggedEvent.Handled; } /// - /// Does the emag effect on a specified entity + /// Checks whether an entity has the EmaggedComponent with a set flag. /// - public bool DoEmagEffect(EntityUid user, EntityUid target) + /// The target entity to check for the flag. + /// The EmagType flag to check for. + /// True if entity has EmaggedComponent and the provided flag. False if the entity lacks EmaggedComponent or provided flag. + public bool CheckFlag(EntityUid target, EmagType flag) { - // prevent emagging twice - if (HasComp(target)) + if (!TryComp(target, out var comp)) return false; - var onAttemptEmagEvent = new OnAttemptEmagEvent(user); - RaiseLocalEvent(target, ref onAttemptEmagEvent); + if ((comp.EmagType & flag) == flag) + return true; - // prevent emagging if attempt fails - if (onAttemptEmagEvent.Handled) - return false; + return false; + } - var emaggedEvent = new GotEmaggedEvent(user); - RaiseLocalEvent(target, ref emaggedEvent); + /// + /// Compares a flag to the target. + /// + /// The target flag to check. + /// The flag to check for within the target. + /// True if target contains flag. Otherwise false. + public bool CompareFlag(EmagType target, EmagType flag) + { + if ((target & flag) == flag) + return true; - if (emaggedEvent.Handled && !emaggedEvent.Repeatable) - EnsureComp(target); - return emaggedEvent.Handled; + return false; } } + +[Flags] +public enum EmagType : byte +{ + None = 0, + Interaction = 1 << 1, + Access = 1 << 2 +} /// /// Shows a popup to emag user (client side only!) and adds to the entity when handled /// /// Emag user +/// The emag type to use /// Did the emagging succeed? Causes a user-only popup to show on client side /// Can the entity be emagged more than once? Prevents adding of /// Needs to be handled in shared/client, not just the server, to actually show the emagging popup [ByRefEvent] -public record struct GotEmaggedEvent(EntityUid UserUid, bool Handled = false, bool Repeatable = false); - -[ByRefEvent] -public record struct OnAttemptEmagEvent(EntityUid UserUid, bool Handled = false); +public record struct GotEmaggedEvent(EntityUid UserUid, EmagType Type, bool Handled = false, bool Repeatable = false); diff --git a/Content.Shared/Fax/Components/FaxMachineComponent.cs b/Content.Shared/Fax/Components/FaxMachineComponent.cs index 161c878e2752..2407ba594995 100644 --- a/Content.Shared/Fax/Components/FaxMachineComponent.cs +++ b/Content.Shared/Fax/Components/FaxMachineComponent.cs @@ -59,12 +59,6 @@ public sealed partial class FaxMachineComponent : Component [DataField] public bool ReceiveNukeCodes { get; set; } = false; - /// - /// Sound to play when fax has been emagged - /// - [DataField] - public SoundSpecifier EmagSound = new SoundCollectionSpecifier("sparks"); - /// /// Sound to play when fax printing new message /// diff --git a/Content.Shared/Lathe/SharedLatheSystem.cs b/Content.Shared/Lathe/SharedLatheSystem.cs index dd251ed18b3f..7328787f25ef 100644 --- a/Content.Shared/Lathe/SharedLatheSystem.cs +++ b/Content.Shared/Lathe/SharedLatheSystem.cs @@ -18,6 +18,7 @@ public abstract class SharedLatheSystem : EntitySystem { [Dependency] private readonly IPrototypeManager _proto = default!; [Dependency] private readonly SharedMaterialStorageSystem _materialStorage = default!; + [Dependency] private readonly EmagSystem _emag = default!; public readonly Dictionary> InverseRecipes = new(); @@ -66,6 +67,12 @@ public bool CanProduce(EntityUid uid, LatheRecipePrototype recipe, int amount = private void OnEmagged(EntityUid uid, EmagLatheRecipesComponent component, ref GotEmaggedEvent args) { + if (!_emag.CompareFlag(args.Type, EmagType.Interaction)) + return; + + if (_emag.CheckFlag(uid, EmagType.Interaction)) + return; + args.Handled = true; } diff --git a/Content.Shared/Light/EntitySystems/UnpoweredFlashlightSystem.cs b/Content.Shared/Light/EntitySystems/UnpoweredFlashlightSystem.cs index 8754de50583e..6dc6cbfe0b32 100644 --- a/Content.Shared/Light/EntitySystems/UnpoweredFlashlightSystem.cs +++ b/Content.Shared/Light/EntitySystems/UnpoweredFlashlightSystem.cs @@ -2,6 +2,7 @@ using Content.Shared.Emag.Systems; using Content.Shared.Light.Components; using Content.Shared.Mind.Components; +using Content.Shared.Storage.Components; using Content.Shared.Toggleable; using Content.Shared.Verbs; using Robust.Shared.Audio.Systems; @@ -22,6 +23,7 @@ public sealed class UnpoweredFlashlightSystem : EntitySystem [Dependency] private readonly SharedAppearanceSystem _appearance = default!; [Dependency] private readonly SharedAudioSystem _audioSystem = default!; [Dependency] private readonly SharedPointLightSystem _light = default!; + [Dependency] private readonly EmagSystem _emag = default!; public override void Initialize() { @@ -78,6 +80,9 @@ private void OnMindAdded(EntityUid uid, UnpoweredFlashlightComponent component, private void OnGotEmagged(EntityUid uid, UnpoweredFlashlightComponent component, ref GotEmaggedEvent args) { + if (!_emag.CompareFlag(args.Type, EmagType.Interaction)) + return; + if (!_light.TryGetLight(uid, out var light)) return; diff --git a/Content.Shared/Lock/LockComponent.cs b/Content.Shared/Lock/LockComponent.cs index 070d5801c1c8..2689602ae8c9 100644 --- a/Content.Shared/Lock/LockComponent.cs +++ b/Content.Shared/Lock/LockComponent.cs @@ -54,9 +54,9 @@ public sealed partial class LockComponent : Component /// /// Whether or not an emag disables it. /// - [DataField("breakOnEmag")] + [DataField] [AutoNetworkedField] - public bool BreakOnEmag = true; + public bool BreakOnAccessBreaker = true; /// /// Amount of do-after time needed to lock the entity. diff --git a/Content.Shared/Lock/LockSystem.cs b/Content.Shared/Lock/LockSystem.cs index 106528009533..0b24bc672216 100644 --- a/Content.Shared/Lock/LockSystem.cs +++ b/Content.Shared/Lock/LockSystem.cs @@ -28,6 +28,7 @@ public sealed class LockSystem : EntitySystem [Dependency] private readonly AccessReaderSystem _accessReader = default!; [Dependency] private readonly ActionBlockerSystem _actionBlocker = default!; [Dependency] private readonly ActivatableUISystem _activatableUI = default!; + [Dependency] private readonly EmagSystem _emag = default!; [Dependency] private readonly SharedAppearanceSystem _appearanceSystem = default!; [Dependency] private readonly SharedAudioSystem _audio = default!; [Dependency] private readonly SharedPopupSystem _sharedPopupSystem = default!; @@ -295,7 +296,10 @@ private void AddToggleLockVerb(EntityUid uid, LockComponent component, GetVerbsE private void OnEmagged(EntityUid uid, LockComponent component, ref GotEmaggedEvent args) { - if (!component.Locked || !component.BreakOnEmag) + if (!_emag.CompareFlag(args.Type, EmagType.Access)) + return; + + if (!component.Locked || !component.BreakOnAccessBreaker) return; _audio.PlayPredicted(component.UnlockSound, uid, args.UserUid); @@ -307,7 +311,7 @@ private void OnEmagged(EntityUid uid, LockComponent component, ref GotEmaggedEve var ev = new LockToggledEvent(false); RaiseLocalEvent(uid, ref ev, true); - RemComp(uid); //Literally destroys the lock as a tell it was emagged + args.Repeatable = true; args.Handled = true; } diff --git a/Content.Shared/Materials/SharedMaterialReclaimerSystem.cs b/Content.Shared/Materials/SharedMaterialReclaimerSystem.cs index d143f509485c..8eb541ee5982 100644 --- a/Content.Shared/Materials/SharedMaterialReclaimerSystem.cs +++ b/Content.Shared/Materials/SharedMaterialReclaimerSystem.cs @@ -29,6 +29,7 @@ public abstract class SharedMaterialReclaimerSystem : EntitySystem [Dependency] private readonly SharedAudioSystem _audio = default!; [Dependency] protected readonly SharedContainerSystem Container = default!; [Dependency] private readonly EntityWhitelistSystem _whitelistSystem = default!; + [Dependency] private readonly EmagSystem _emag = default!; public const string ActiveReclaimerContainerId = "active-material-reclaimer-container"; @@ -60,6 +61,12 @@ private void OnExamined(EntityUid uid, MaterialReclaimerComponent component, Exa private void OnEmagged(EntityUid uid, MaterialReclaimerComponent component, ref GotEmaggedEvent args) { + if (!_emag.CompareFlag(args.Type, EmagType.Interaction)) + return; + + if (_emag.CheckFlag(uid, EmagType.Interaction)) + return; + args.Handled = true; } @@ -207,7 +214,7 @@ public bool CanGib(EntityUid uid, EntityUid victim, MaterialReclaimerComponent c component.Enabled && !component.Broken && HasComp(victim) && - HasComp(uid); + _emag.CheckFlag(uid, EmagType.Interaction); } /// diff --git a/Content.Shared/Medical/Cryogenics/SharedCryoPodSystem.cs b/Content.Shared/Medical/Cryogenics/SharedCryoPodSystem.cs index f6ce235ff749..891129a2ddc0 100644 --- a/Content.Shared/Medical/Cryogenics/SharedCryoPodSystem.cs +++ b/Content.Shared/Medical/Cryogenics/SharedCryoPodSystem.cs @@ -20,6 +20,7 @@ public abstract partial class SharedCryoPodSystem: EntitySystem { [Dependency] private readonly SharedAppearanceSystem _appearanceSystem = default!; [Dependency] private readonly StandingStateSystem _standingStateSystem = default!; + [Dependency] private readonly EmagSystem _emag = default!; [Dependency] private readonly MobStateSystem _mobStateSystem = default!; [Dependency] private readonly SharedPopupSystem _popupSystem = default!; [Dependency] private readonly SharedContainerSystem _containerSystem = default!; @@ -156,9 +157,13 @@ protected void AddAlternativeVerbs(EntityUid uid, CryoPodComponent cryoPodCompon protected void OnEmagged(EntityUid uid, CryoPodComponent? cryoPodComponent, ref GotEmaggedEvent args) { if (!Resolve(uid, ref cryoPodComponent)) - { return; - } + + if (!_emag.CompareFlag(args.Type, EmagType.Interaction)) + return; + + if (cryoPodComponent.PermaLocked && cryoPodComponent.Locked) + return; cryoPodComponent.PermaLocked = true; cryoPodComponent.Locked = true; diff --git a/Content.Shared/Ninja/Components/EmagProviderComponent.cs b/Content.Shared/Ninja/Components/EmagProviderComponent.cs index ae3e85cbe42a..630ce12d53ce 100644 --- a/Content.Shared/Ninja/Components/EmagProviderComponent.cs +++ b/Content.Shared/Ninja/Components/EmagProviderComponent.cs @@ -1,6 +1,8 @@ +using Content.Shared.Emag.Systems; using Content.Shared.Ninja.Systems; using Content.Shared.Tag; using Content.Shared.Whitelist; +using Robust.Shared.Audio; using Robust.Shared.GameStates; using Robust.Shared.Prototypes; @@ -17,11 +19,23 @@ public sealed partial class EmagProviderComponent : Component /// The tag that marks an entity as immune to emagging. /// [DataField] - public ProtoId EmagImmuneTag = "EmagImmune"; + public ProtoId AccessBreakerImmuneTag = "AccessBreakerImmune"; /// /// Whitelist that entities must be on to work. /// [DataField] public EntityWhitelist? Whitelist; + + /// + /// What type of emag this will provide. + /// + [DataField] + public EmagType EmagType = EmagType.Access; + + /// + /// What sound should the emag play when used + /// + [DataField] + public SoundSpecifier EmagSound = new SoundCollectionSpecifier("sparks"); } diff --git a/Content.Shared/Ninja/Systems/EmagProviderSystem.cs b/Content.Shared/Ninja/Systems/EmagProviderSystem.cs index ae0bacaf5f65..3be2ae52e130 100644 --- a/Content.Shared/Ninja/Systems/EmagProviderSystem.cs +++ b/Content.Shared/Ninja/Systems/EmagProviderSystem.cs @@ -5,6 +5,7 @@ using Content.Shared.Ninja.Components; using Content.Shared.Tag; using Content.Shared.Whitelist; +using Robust.Shared.Audio.Systems; namespace Content.Shared.Ninja.Systems; @@ -13,6 +14,7 @@ namespace Content.Shared.Ninja.Systems; /// public sealed class EmagProviderSystem : EntitySystem { + [Dependency] private readonly SharedAudioSystem _audio = default!; [Dependency] private readonly EmagSystem _emag = default!; [Dependency] private readonly EntityWhitelistSystem _whitelist = default!; [Dependency] private readonly ISharedAdminLogManager _adminLogger = default!; @@ -42,14 +44,18 @@ private void OnBeforeInteractHand(Entity ent, ref BeforeI return; // only allowed to emag non-immune entities - if (_tag.HasTag(target, comp.EmagImmuneTag)) + if (_tag.HasTag(target, comp.AccessBreakerImmuneTag)) return; - var handled = _emag.DoEmagEffect(uid, target); - if (!handled) + var emagEv = new GotEmaggedEvent(uid, EmagType.Access); + RaiseLocalEvent(args.Target, ref emagEv); + + if (!emagEv.Handled) return; - _adminLogger.Add(LogType.Emag, LogImpact.High, $"{ToPrettyString(uid):player} emagged {ToPrettyString(target):target}"); + _audio.PlayPredicted(comp.EmagSound, uid, uid); + + _adminLogger.Add(LogType.Emag, LogImpact.High, $"{ToPrettyString(uid):player} emagged {ToPrettyString(target):target} with flag(s): {ent.Comp.EmagType}"); var ev = new EmaggedSomethingEvent(target); RaiseLocalEvent(uid, ref ev); args.Handled = true; @@ -57,7 +63,7 @@ private void OnBeforeInteractHand(Entity ent, ref BeforeI } /// -/// Raised on the player when emagging something. +/// Raised on the player when access breaking something. /// [ByRefEvent] public record struct EmaggedSomethingEvent(EntityUid Target); diff --git a/Content.Shared/Pinpointer/SharedPinpointerSystem.cs b/Content.Shared/Pinpointer/SharedPinpointerSystem.cs index 7f6b88912559..471096018356 100644 --- a/Content.Shared/Pinpointer/SharedPinpointerSystem.cs +++ b/Content.Shared/Pinpointer/SharedPinpointerSystem.cs @@ -10,6 +10,7 @@ namespace Content.Shared.Pinpointer; public abstract class SharedPinpointerSystem : EntitySystem { [Dependency] private readonly ISharedAdminLogManager _adminLogger = default!; + [Dependency] private readonly EmagSystem _emag = default!; public override void Initialize() { @@ -137,6 +138,15 @@ public virtual bool TogglePinpointer(EntityUid uid, PinpointerComponent? pinpoin private void OnEmagged(EntityUid uid, PinpointerComponent component, ref GotEmaggedEvent args) { + if (!_emag.CompareFlag(args.Type, EmagType.Interaction)) + return; + + if (_emag.CheckFlag(uid, EmagType.Interaction)) + return; + + if (component.CanRetarget) + return; + args.Handled = true; component.CanRetarget = true; } diff --git a/Content.Shared/Silicons/Bots/EmaggableMedibotComponent.cs b/Content.Shared/Silicons/Bots/EmaggableMedibotComponent.cs index 73775aaf91ac..0253cf12bc06 100644 --- a/Content.Shared/Silicons/Bots/EmaggableMedibotComponent.cs +++ b/Content.Shared/Silicons/Bots/EmaggableMedibotComponent.cs @@ -16,13 +16,4 @@ public sealed partial class EmaggableMedibotComponent : Component /// [DataField(required: true), ViewVariables(VVAccess.ReadWrite)] public Dictionary Replacements = new(); - - /// - /// Sound to play when the bot has been emagged - /// - [DataField] - public SoundSpecifier SparkSound = new SoundCollectionSpecifier("sparks") - { - Params = AudioParams.Default.WithVolume(8f) - }; } diff --git a/Content.Shared/Silicons/Bots/MedibotSystem.cs b/Content.Shared/Silicons/Bots/MedibotSystem.cs index 68f930931c2e..407e586eb1c8 100644 --- a/Content.Shared/Silicons/Bots/MedibotSystem.cs +++ b/Content.Shared/Silicons/Bots/MedibotSystem.cs @@ -20,6 +20,7 @@ namespace Content.Shared.Silicons.Bots; public sealed class MedibotSystem : EntitySystem { [Dependency] private readonly SharedAudioSystem _audio = default!; + [Dependency] private readonly EmagSystem _emag = default!; [Dependency] private SharedInteractionSystem _interaction = default!; [Dependency] private SharedSolutionContainerSystem _solutionContainer = default!; [Dependency] private SharedPopupSystem _popup = default!; @@ -36,10 +37,14 @@ public override void Initialize() private void OnEmagged(EntityUid uid, EmaggableMedibotComponent comp, ref GotEmaggedEvent args) { - if (!TryComp(uid, out var medibot)) + if (!_emag.CompareFlag(args.Type, EmagType.Interaction)) + return; + + if (_emag.CheckFlag(uid, EmagType.Interaction)) return; - _audio.PlayPredicted(comp.SparkSound, uid, args.UserUid); + if (!TryComp(uid, out var medibot)) + return; foreach (var (state, treatment) in comp.Replacements) { diff --git a/Content.Shared/Silicons/Laws/SharedSiliconLawSystem.cs b/Content.Shared/Silicons/Laws/SharedSiliconLawSystem.cs index a30e7c8980f9..c3a8c228084a 100644 --- a/Content.Shared/Silicons/Laws/SharedSiliconLawSystem.cs +++ b/Content.Shared/Silicons/Laws/SharedSiliconLawSystem.cs @@ -1,7 +1,10 @@ using Content.Shared.Emag.Systems; +using Content.Shared.Mind; using Content.Shared.Popups; using Content.Shared.Silicons.Laws.Components; +using Content.Shared.Stunnable; using Content.Shared.Wires; +using Robust.Shared.Audio; namespace Content.Shared.Silicons.Laws; @@ -11,22 +14,29 @@ namespace Content.Shared.Silicons.Laws; public abstract partial class SharedSiliconLawSystem : EntitySystem { [Dependency] private readonly SharedPopupSystem _popup = default!; + [Dependency] private readonly SharedStunSystem _stunSystem = default!; + [Dependency] private readonly EmagSystem _emag = default!; + [Dependency] private readonly SharedMindSystem _mind = default!; /// public override void Initialize() { InitializeUpdater(); SubscribeLocalEvent(OnGotEmagged); - SubscribeLocalEvent(OnAttemptEmag); } - protected virtual void OnAttemptEmag(EntityUid uid, EmagSiliconLawComponent component, ref OnAttemptEmagEvent args) + private void OnGotEmagged(EntityUid uid, EmagSiliconLawComponent component, ref GotEmaggedEvent args) { - //prevent self emagging + if (!_emag.CompareFlag(args.Type, EmagType.Interaction)) + return; + + if (_emag.CheckFlag(uid, EmagType.Interaction)) + return; + + // prevent self-emagging if (uid == args.UserUid) { _popup.PopupClient(Loc.GetString("law-emag-cannot-emag-self"), uid, args.UserUid); - args.Handled = true; return; } @@ -35,14 +45,33 @@ protected virtual void OnAttemptEmag(EntityUid uid, EmagSiliconLawComponent comp !panel.Open) { _popup.PopupClient(Loc.GetString("law-emag-require-panel"), uid, args.UserUid); - args.Handled = true; + return; } - } + var ev = new SiliconEmaggedEvent(args.UserUid); + RaiseLocalEvent(uid, ref ev); - protected virtual void OnGotEmagged(EntityUid uid, EmagSiliconLawComponent component, ref GotEmaggedEvent args) - { component.OwnerName = Name(args.UserUid); + + NotifyLawsChanged(uid, component.EmaggedSound); + if(_mind.TryGetMind(uid, out var mindId, out _)) + EnsureSubvertedSiliconRole(mindId); + + _stunSystem.TryParalyze(uid, component.StunTime, true); + args.Handled = true; } + + protected virtual void NotifyLawsChanged(EntityUid uid, SoundSpecifier? cue = null) + { + + } + + protected virtual void EnsureSubvertedSiliconRole(EntityUid mindId) + { + + } } + +[ByRefEvent] +public record struct SiliconEmaggedEvent(EntityUid user); diff --git a/Content.Shared/Singularity/EntitySystems/SharedSingularityGeneratorSystem.cs b/Content.Shared/Singularity/EntitySystems/SharedSingularityGeneratorSystem.cs index ee6dc89bb847..331c1fa4ddb6 100644 --- a/Content.Shared/Singularity/EntitySystems/SharedSingularityGeneratorSystem.cs +++ b/Content.Shared/Singularity/EntitySystems/SharedSingularityGeneratorSystem.cs @@ -11,6 +11,7 @@ public abstract class SharedSingularityGeneratorSystem : EntitySystem { #region Dependencies [Dependency] protected readonly SharedPopupSystem PopupSystem = default!; + [Dependency] private readonly EmagSystem _emag = default!; #endregion Dependencies public override void Initialize() @@ -22,7 +23,16 @@ public override void Initialize() private void OnEmagged(EntityUid uid, SingularityGeneratorComponent component, ref GotEmaggedEvent args) { + if (!_emag.CompareFlag(args.Type, EmagType.Interaction)) + return; + + if (_emag.CheckFlag(uid, EmagType.Interaction)) + return; + + if (component.FailsafeDisabled) + return; + component.FailsafeDisabled = true; args.Handled = true; } -} \ No newline at end of file +} diff --git a/Content.Shared/VendingMachines/SharedVendingMachineSystem.cs b/Content.Shared/VendingMachines/SharedVendingMachineSystem.cs index 94562ce8d1bf..c4f3eede2d29 100644 --- a/Content.Shared/VendingMachines/SharedVendingMachineSystem.cs +++ b/Content.Shared/VendingMachines/SharedVendingMachineSystem.cs @@ -2,6 +2,7 @@ using Robust.Shared.Prototypes; using System.Linq; using Content.Shared.DoAfter; +using Content.Shared.Emag.Systems; using Content.Shared.Interaction; using Content.Shared.Popups; using Robust.Shared.Audio; @@ -19,11 +20,14 @@ public abstract partial class SharedVendingMachineSystem : EntitySystem [Dependency] private readonly SharedDoAfterSystem _doAfter = default!; [Dependency] protected readonly SharedPopupSystem Popup = default!; [Dependency] protected readonly IRobustRandom Randomizer = default!; + [Dependency] private readonly EmagSystem _emag = default!; public override void Initialize() { base.Initialize(); SubscribeLocalEvent(OnMapInit); + SubscribeLocalEvent(OnEmagged); + SubscribeLocalEvent(OnAfterInteract); } @@ -49,9 +53,21 @@ public void RestockInventoryFromPrototype(EntityUid uid, Dirty(uid, component); } + private void OnEmagged(EntityUid uid, VendingMachineComponent component, ref GotEmaggedEvent args) + { + if (!_emag.CompareFlag(args.Type, EmagType.Interaction)) + return; + + if (_emag.CheckFlag(uid, EmagType.Interaction)) + return; + + // only emag if there are emag-only items + args.Handled = component.EmaggedInventory.Count > 0; + } + /// /// Returns all of the vending machine's inventory. Only includes emagged and contraband inventories if - /// exists and is true + /// with the EmagType.Interaction flag exists and is true /// are true respectively. /// /// @@ -64,7 +80,7 @@ public List GetAllInventory(EntityUid uid, Vending var inventory = new List(component.Inventory.Values); - if (HasComp(uid)) + if (_emag.CheckFlag(uid, EmagType.Interaction)) inventory.AddRange(component.EmaggedInventory.Values); if (component.Contraband) diff --git a/Content.Shared/Xenoarchaeology/Equipment/SharedArtifactCrusherSystem.cs b/Content.Shared/Xenoarchaeology/Equipment/SharedArtifactCrusherSystem.cs index da253ba80afc..2d7d86822f9e 100644 --- a/Content.Shared/Xenoarchaeology/Equipment/SharedArtifactCrusherSystem.cs +++ b/Content.Shared/Xenoarchaeology/Equipment/SharedArtifactCrusherSystem.cs @@ -14,6 +14,7 @@ public abstract class SharedArtifactCrusherSystem : EntitySystem [Dependency] protected readonly SharedAppearanceSystem Appearance = default!; [Dependency] protected readonly SharedAudioSystem AudioSystem = default!; [Dependency] protected readonly SharedContainerSystem ContainerSystem = default!; + [Dependency] private readonly EmagSystem _emag = default!; /// public override void Initialize() @@ -40,6 +41,15 @@ private void OnStorageAfterOpen(Entity ent, ref Storag private void OnEmagged(Entity ent, ref GotEmaggedEvent args) { + if (!_emag.CompareFlag(args.Type, EmagType.Interaction)) + return; + + if (_emag.CheckFlag(ent, EmagType.Interaction)) + return; + + if (ent.Comp.AutoLock) + return; + ent.Comp.AutoLock = true; args.Handled = true; } diff --git a/Resources/Locale/en-US/emag/emag.ftl b/Resources/Locale/en-US/emag/emag.ftl index b4679870b522..e91bacbc657d 100644 --- a/Resources/Locale/en-US/emag/emag.ftl +++ b/Resources/Locale/en-US/emag/emag.ftl @@ -1,2 +1,2 @@ -emag-success = The card zaps something in {THE($target)}. +emag-success = The device zaps something in {THE($target)}. emag-no-charges = No charges left! diff --git a/Resources/Locale/en-US/store/uplink-catalog.ftl b/Resources/Locale/en-US/store/uplink-catalog.ftl index 655c620e3073..5e7b393fcc6e 100644 --- a/Resources/Locale/en-US/store/uplink-catalog.ftl +++ b/Resources/Locale/en-US/store/uplink-catalog.ftl @@ -113,7 +113,10 @@ uplink-chest-rig-name = Chest Rig uplink-chest-rig-desc = Explosion-resistant tactical webbing used for holding traitor goods. uplink-emag-name = Emag -uplink-emag-desc = The business card of the syndicate, this sequencer is able to break open airlocks and tamper with a variety of station devices. Recharges automatically. +uplink-emag-desc = The business card of the syndicate, this sequencer is able to tamper with a variety of station devices. Recharges automatically. + +uplink-access-breaker-name = Access Breaker +uplink-access-breaker-desc = A hacked access configurator and a good friend of the emag. This device is able to force airlocks open as well as erase access requirements from station equipment. Recharges automatically. uplink-agent-id-card-name = Agent ID Card uplink-agent-id-card-desc = A modified ID card that can copy accesses from other cards and change its name and job title at-will. diff --git a/Resources/Locale/en-US/thief/backpack.ftl b/Resources/Locale/en-US/thief/backpack.ftl index 6a69ab8bc6f3..6d3baa2c0d8a 100644 --- a/Resources/Locale/en-US/thief/backpack.ftl +++ b/Resources/Locale/en-US/thief/backpack.ftl @@ -39,7 +39,7 @@ thief-backpack-category-syndie-name = syndie kit thief-backpack-category-syndie-description = Trinkets from a disavowed past, or stolen from a careless agent? You've made some connections. Whiskey, echo... - Includes: An Emag, Interdyne cigs, a Syndicate codeword, + Includes: An Emag, Access Breaker, Interdyne cigs, a Syndicate codeword, a Radio Jammer, a lighter and some strange red crystals. thief-backpack-category-sleeper-name = sleeper kit diff --git a/Resources/Prototypes/Catalog/thief_toolbox_sets.yml b/Resources/Prototypes/Catalog/thief_toolbox_sets.yml index 06328870fc8a..62e9a01edc06 100644 --- a/Resources/Prototypes/Catalog/thief_toolbox_sets.yml +++ b/Resources/Prototypes/Catalog/thief_toolbox_sets.yml @@ -63,6 +63,7 @@ - RadioJammer - TraitorCodePaper - Emag + - AccessBreaker - Lighter - CigPackSyndicate - Telecrystal10 #The thief cannot use them, but it may induce communication with traitors diff --git a/Resources/Prototypes/Catalog/uplink_catalog.yml b/Resources/Prototypes/Catalog/uplink_catalog.yml index f3ace5b0bb2b..3e86b7f0e997 100644 --- a/Resources/Prototypes/Catalog/uplink_catalog.yml +++ b/Resources/Prototypes/Catalog/uplink_catalog.yml @@ -865,16 +865,29 @@ # Disruption +- type: listing + id: UplinkAccessBreaker + name: uplink-access-breaker-name + description: uplink-access-breaker-desc + productEntity: AccessBreaker + discountCategory: veryRareDiscounts + discountDownTo: + Telecrystal: 3 + cost: + Telecrystal: 5 + categories: + - UplinkDisruption + - type: listing id: UplinkEmag name: uplink-emag-name description: uplink-emag-desc productEntity: Emag - discountCategory: veryRareDiscounts + discountCategory: rareDiscounts discountDownTo: - Telecrystal: 5 + Telecrystal: 2 cost: - Telecrystal: 8 + Telecrystal: 4 categories: - UplinkDisruption diff --git a/Resources/Prototypes/Entities/Mobs/Cyborgs/base_borg_chassis.yml b/Resources/Prototypes/Entities/Mobs/Cyborgs/base_borg_chassis.yml index 28e4042afb4f..fdc6574c9495 100644 --- a/Resources/Prototypes/Entities/Mobs/Cyborgs/base_borg_chassis.yml +++ b/Resources/Prototypes/Entities/Mobs/Cyborgs/base_borg_chassis.yml @@ -167,7 +167,6 @@ - cell_slot - type: Lock locked: true - breakOnEmag: false unlockOnClick: false - type: ActivatableUIRequiresLock - type: LockedWiresPanel diff --git a/Resources/Prototypes/Entities/Objects/Misc/arabianlamp.yml b/Resources/Prototypes/Entities/Objects/Misc/arabianlamp.yml index 791a0d09453e..ee40316e62d8 100644 --- a/Resources/Prototypes/Entities/Objects/Misc/arabianlamp.yml +++ b/Resources/Prototypes/Entities/Objects/Misc/arabianlamp.yml @@ -7,10 +7,10 @@ - type: Appearance - type: AccessReader access: [ [ "CentralCommand" ] ] - breakOnEmag: false + breakOnAccessBreaker: false - type: Lock lockOnClick: false - breakOnEmag: false + breakOnAccessBreaker: false - type: EntityStorage capacity: 1 # Its smol. itemCanStoreMobs: false # just leaving this here explicitly since I know at some point someone will want to use this to hold a mob. This also prevents it from becoming His Grace. diff --git a/Resources/Prototypes/Entities/Objects/Specific/Robotics/borg_modules.yml b/Resources/Prototypes/Entities/Objects/Specific/Robotics/borg_modules.yml index 5cc3adef900d..ff8e98339956 100644 --- a/Resources/Prototypes/Entities/Objects/Specific/Robotics/borg_modules.yml +++ b/Resources/Prototypes/Entities/Objects/Specific/Robotics/borg_modules.yml @@ -570,7 +570,7 @@ id: BorgModuleOperative parent: [ BaseBorgModuleSyndicate, BaseProviderBorgModule, BaseSyndicateContraband ] name: operative cyborg module - description: A module that comes with a crowbar, an Emag and a syndicate pinpointer. + description: A module that comes with a crowbar, an Emag, an Access Breaker and a syndicate pinpointer. components: - type: Sprite layers: @@ -580,6 +580,7 @@ items: - Crowbar - Emag + - AccessBreaker - PinpointerSyndicateNuclear - type: BorgModuleIcon icon: { sprite: Interface/Actions/actions_borg.rsi, state: syndicate-operative-module } diff --git a/Resources/Prototypes/Entities/Objects/Tools/access_breaker.yml b/Resources/Prototypes/Entities/Objects/Tools/access_breaker.yml new file mode 100644 index 000000000000..15c1beba8ed2 --- /dev/null +++ b/Resources/Prototypes/Entities/Objects/Tools/access_breaker.yml @@ -0,0 +1,22 @@ +- type: entity + parent: [BaseItem, BaseSyndicateContraband] + id: AccessBreakerUnlimited + suffix: Unlimited + name: authentication disruptor + description: A hacked access configurator, specialized to unlock and erase access from digital locks. + components: + - type: Emag + emagType: Access + - type: Sprite + sprite: Objects/Tools/access_breaker.rsi + state: icon + - type: Item + sprite: Objects/Tools/access_breaker.rsi + +- type: entity + parent: AccessBreakerUnlimited + id: AccessBreaker + suffix: Limited + components: + - type: LimitedCharges + - type: AutoRecharge diff --git a/Resources/Prototypes/Entities/Objects/Weapons/Guns/Projectiles/magic.yml b/Resources/Prototypes/Entities/Objects/Weapons/Guns/Projectiles/magic.yml index cd736a33d082..4c3fd937e03f 100644 --- a/Resources/Prototypes/Entities/Objects/Weapons/Guns/Projectiles/magic.yml +++ b/Resources/Prototypes/Entities/Objects/Weapons/Guns/Projectiles/magic.yml @@ -98,10 +98,10 @@ stateLocked: cursed_door stateUnlocked: decursed_door - type: Lock - breakOnEmag: false + breakOnAccessBreaker: false - type: AccessReader access: [["Wizard"]] - breakOnEmag: false + breakOnAccessBreaker: false - type: Projectile deleteOnCollide: false onlyCollideWhenShot: true diff --git a/Resources/Prototypes/Entities/Structures/Machines/Computers/computers.yml b/Resources/Prototypes/Entities/Structures/Machines/Computers/computers.yml index 1180b370967f..2e58679e7cad 100644 --- a/Resources/Prototypes/Entities/Structures/Machines/Computers/computers.yml +++ b/Resources/Prototypes/Entities/Structures/Machines/Computers/computers.yml @@ -621,7 +621,7 @@ color: "#3c5eb5" - type: Tag tags: - - EmagImmune + - AccessBreakerImmune - type: ItemSlots - type: ContainerContainer containers: diff --git a/Resources/Prototypes/Entities/Structures/Machines/fatextractor.yml b/Resources/Prototypes/Entities/Structures/Machines/fatextractor.yml index 96de462b79c6..770a17a8ff7c 100644 --- a/Resources/Prototypes/Entities/Structures/Machines/fatextractor.yml +++ b/Resources/Prototypes/Entities/Structures/Machines/fatextractor.yml @@ -35,7 +35,7 @@ visible: false map: ["enum.FatExtractorVisualLayers.Smoke"] - type: Lock - breakOnEmag: false + breakOnAccessBreaker: false - type: GenericVisualizer visuals: enum.StorageVisuals.Open: diff --git a/Resources/Prototypes/Entities/Structures/Machines/fax_machine.yml b/Resources/Prototypes/Entities/Structures/Machines/fax_machine.yml index 402c408ed844..9efa7ce6f2bd 100644 --- a/Resources/Prototypes/Entities/Structures/Machines/fax_machine.yml +++ b/Resources/Prototypes/Entities/Structures/Machines/fax_machine.yml @@ -101,6 +101,7 @@ responsePings: false notifyAdmins: true - type: Emagged + emagType: Interaction - type: entity parent: FaxMachineBase diff --git a/Resources/Prototypes/Entities/Structures/cryogenic_sleep_unit.yml b/Resources/Prototypes/Entities/Structures/cryogenic_sleep_unit.yml index bdb8862a8293..e9e9294e6364 100644 --- a/Resources/Prototypes/Entities/Structures/cryogenic_sleep_unit.yml +++ b/Resources/Prototypes/Entities/Structures/cryogenic_sleep_unit.yml @@ -17,7 +17,7 @@ - type: ActivatableUI key: enum.CryostorageUIKey.Key - type: AccessReader - breakOnEmag: false + breakOnAccessBreaker: false access: [["Cryogenics"]] - type: InteractionOutline - type: Cryostorage diff --git a/Resources/Prototypes/tags.yml b/Resources/Prototypes/tags.yml index 478e86b8afd7..7847e8da0da1 100644 --- a/Resources/Prototypes/tags.yml +++ b/Resources/Prototypes/tags.yml @@ -1,5 +1,8 @@ # PUT YOUR TAGS IN ALPHABETICAL ORDER +- type: Tag + id: AccessBreakerImmune + - type: Tag id: AirAlarm diff --git a/Resources/Textures/Objects/Tools/access_breaker.rsi/equipped-BELT.png b/Resources/Textures/Objects/Tools/access_breaker.rsi/equipped-BELT.png new file mode 100644 index 000000000000..b8903ccb4f7b Binary files /dev/null and b/Resources/Textures/Objects/Tools/access_breaker.rsi/equipped-BELT.png differ diff --git a/Resources/Textures/Objects/Tools/access_breaker.rsi/icon.png b/Resources/Textures/Objects/Tools/access_breaker.rsi/icon.png new file mode 100644 index 000000000000..fab24db72c81 Binary files /dev/null and b/Resources/Textures/Objects/Tools/access_breaker.rsi/icon.png differ diff --git a/Resources/Textures/Objects/Tools/access_breaker.rsi/inhand-left.png b/Resources/Textures/Objects/Tools/access_breaker.rsi/inhand-left.png new file mode 100644 index 000000000000..8ea6bbb23f26 Binary files /dev/null and b/Resources/Textures/Objects/Tools/access_breaker.rsi/inhand-left.png differ diff --git a/Resources/Textures/Objects/Tools/access_breaker.rsi/inhand-right.png b/Resources/Textures/Objects/Tools/access_breaker.rsi/inhand-right.png new file mode 100644 index 000000000000..32070ed47a97 Binary files /dev/null and b/Resources/Textures/Objects/Tools/access_breaker.rsi/inhand-right.png differ diff --git a/Resources/Textures/Objects/Tools/access_breaker.rsi/meta.json b/Resources/Textures/Objects/Tools/access_breaker.rsi/meta.json new file mode 100644 index 000000000000..1e1baabbba21 --- /dev/null +++ b/Resources/Textures/Objects/Tools/access_breaker.rsi/meta.json @@ -0,0 +1,44 @@ +{ + "version": 1, + "license": "CC-BY-SA-3.0", + "copyright": "Made by 20kdc (GitHub), edited by DieselMohawk (GitHub)", + "size": { + "x": 32, + "y": 32 + }, + "states": [ + { + "name": "inhand-left", + "directions": 4 + }, + { + "name": "inhand-right", + "directions": 4 + }, + { + "name": "icon", + "delays": [ + [ + 0.1, + 0.1, + 0.1, + 0.1, + 0.1, + 0.1, + 0.5, + 0.5, + 0.5, + 0.1, + 0.1, + 0.1, + 0.1, + 0.1, + 0.1 + ] + ] + }, + { + "name": "equipped-BELT" + } + ] +}