Seperate EMAG into EMAG and Authentication Disruptor (#34337)

This commit is contained in:
ScarKy0
2025-01-30 05:05:47 +01:00
committed by GitHub
parent 60e83376ef
commit 253a9fbdf1
63 changed files with 536 additions and 217 deletions

View File

@@ -1,7 +1,6 @@
using Content.Server.Wires; using Content.Server.Wires;
using Content.Shared.Access; using Content.Shared.Access;
using Content.Shared.Access.Components; using Content.Shared.Access.Components;
using Content.Shared.Emag.Components;
using Content.Shared.Wires; using Content.Shared.Wires;
namespace Content.Server.Access; namespace Content.Server.Access;
@@ -30,12 +29,10 @@ public sealed partial class AccessWireAction : ComponentWireAction<AccessReaderC
} }
public override bool Mend(EntityUid user, Wire wire, AccessReaderComponent comp) public override bool Mend(EntityUid user, Wire wire, AccessReaderComponent comp)
{
if (!EntityManager.HasComponent<EmaggedComponent>(wire.Owner))
{ {
comp.Enabled = true; comp.Enabled = true;
EntityManager.Dirty(wire.Owner, comp); EntityManager.Dirty(wire.Owner, comp);
}
return true; return true;
} }
@@ -58,7 +55,7 @@ public sealed partial class AccessWireAction : ComponentWireAction<AccessReaderC
{ {
if (!wire.IsCut) if (!wire.IsCut)
{ {
if (EntityManager.TryGetComponent<AccessReaderComponent>(wire.Owner, out var access) && !EntityManager.HasComponent<EmaggedComponent>(wire.Owner)) if (EntityManager.TryGetComponent<AccessReaderComponent>(wire.Owner, out var access))
{ {
access.Enabled = true; access.Enabled = true;
EntityManager.Dirty(wire.Owner, access); EntityManager.Dirty(wire.Owner, access);

View File

@@ -245,6 +245,10 @@ public sealed class AccessOverriderSystem : SharedAccessOverriderSystem
$"{ToPrettyString(player):player} has modified {ToPrettyString(accessReaderEnt.Value):entity} with the following allowed access level holders: [{string.Join(", ", addedTags.Union(removedTags))}] [{string.Join(", ", newAccessList)}]"); $"{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); accessReaderEnt.Value.Comp.AccessLists = ConvertAccessListToHashSet(newAccessList);
var ev = new OnAccessOverriderAccessUpdatedEvent(player);
RaiseLocalEvent(component.TargetAccessReaderId, ref ev);
Dirty(accessReaderEnt.Value); Dirty(accessReaderEnt.Value);
} }

View File

@@ -116,8 +116,11 @@ public sealed class TechAnomalySystem : EntitySystem
if (_random.Prob(tech.Comp.EmagSupercritProbability)) if (_random.Prob(tech.Comp.EmagSupercritProbability))
{ {
_emag.DoEmagEffect(tech, source); var sourceEv = new GotEmaggedEvent(tech, EmagType.Access | EmagType.Interaction);
_emag.DoEmagEffect(tech, sink); RaiseLocalEvent(source, ref sourceEv);
var sinkEv = new GotEmaggedEvent(tech, EmagType.Access | EmagType.Interaction);
RaiseLocalEvent(sink, ref sinkEv);
} }
CreateNewLink(tech, source, sink); CreateNewLink(tech, source, sink);

View File

@@ -21,6 +21,7 @@ public sealed class FireAlarmSystem : EntitySystem
{ {
[Dependency] private readonly AtmosDeviceNetworkSystem _atmosDevNet = default!; [Dependency] private readonly AtmosDeviceNetworkSystem _atmosDevNet = default!;
[Dependency] private readonly AtmosAlarmableSystem _atmosAlarmable = default!; [Dependency] private readonly AtmosAlarmableSystem _atmosAlarmable = default!;
[Dependency] private readonly EmagSystem _emag = default!;
[Dependency] private readonly SharedInteractionSystem _interactionSystem = default!; [Dependency] private readonly SharedInteractionSystem _interactionSystem = default!;
[Dependency] private readonly AccessReaderSystem _access = default!; [Dependency] private readonly AccessReaderSystem _access = default!;
[Dependency] private readonly IConfigurationManager _configManager = default!; [Dependency] private readonly IConfigurationManager _configManager = default!;
@@ -77,11 +78,18 @@ public sealed class FireAlarmSystem : EntitySystem
private void OnEmagged(EntityUid uid, FireAlarmComponent component, ref GotEmaggedEvent args) private void OnEmagged(EntityUid uid, FireAlarmComponent component, ref GotEmaggedEvent args)
{ {
if (TryComp<AtmosAlarmableComponent>(uid, out var alarmable)) if (!_emag.CompareFlag(args.Type, EmagType.Interaction))
{ return;
if (_emag.CheckFlag(uid, EmagType.Interaction))
return;
if (!TryComp<AtmosAlarmableComponent>(uid, out var alarmable))
return;
// Remove the atmos alarmable component permanently from this device. // Remove the atmos alarmable component permanently from this device.
_atmosAlarmable.ForceAlert(uid, AtmosAlarmType.Emagged, alarmable); _atmosAlarmable.ForceAlert(uid, AtmosAlarmType.Emagged, alarmable);
RemCompDeferred<AtmosAlarmableComponent>(uid); RemCompDeferred<AtmosAlarmableComponent>(uid);
} args.Handled = true;
} }
} }

View File

@@ -20,6 +20,7 @@ namespace Content.Server.Bed
{ {
[Dependency] private readonly DamageableSystem _damageableSystem = default!; [Dependency] private readonly DamageableSystem _damageableSystem = default!;
[Dependency] private readonly ActionsSystem _actionsSystem = default!; [Dependency] private readonly ActionsSystem _actionsSystem = default!;
[Dependency] private readonly EmagSystem _emag = default!;
[Dependency] private readonly SleepingSystem _sleepingSystem = default!; [Dependency] private readonly SleepingSystem _sleepingSystem = default!;
[Dependency] private readonly SharedAppearanceSystem _appearance = default!; [Dependency] private readonly SharedAppearanceSystem _appearance = default!;
[Dependency] private readonly MobStateSystem _mobStateSystem = default!; [Dependency] private readonly MobStateSystem _mobStateSystem = default!;
@@ -114,7 +115,12 @@ namespace Content.Server.Bed
private void OnEmagged(EntityUid uid, StasisBedComponent component, ref GotEmaggedEvent args) 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 // Reset any metabolisms first so they receive the multiplier correctly
UpdateMetabolisms(uid, component, false); UpdateMetabolisms(uid, component, false);
component.Multiplier = 1 / component.Multiplier; component.Multiplier = 1 / component.Multiplier;

View File

@@ -8,7 +8,7 @@ using Content.Shared.Cargo.Components;
using Content.Shared.Cargo.Events; using Content.Shared.Cargo.Events;
using Content.Shared.Cargo.Prototypes; using Content.Shared.Cargo.Prototypes;
using Content.Shared.Database; using Content.Shared.Database;
using Content.Shared.Emag.Components; using Content.Shared.Emag.Systems;
using Content.Shared.IdentityManagement; using Content.Shared.IdentityManagement;
using Content.Shared.Interaction; using Content.Shared.Interaction;
using Content.Shared.Paper; using Content.Shared.Paper;
@@ -21,6 +21,7 @@ namespace Content.Server.Cargo.Systems
public sealed partial class CargoSystem public sealed partial class CargoSystem
{ {
[Dependency] private readonly SharedTransformSystem _transformSystem = default!; [Dependency] private readonly SharedTransformSystem _transformSystem = default!;
[Dependency] private readonly EmagSystem _emag = default!;
/// <summary> /// <summary>
/// How much time to wait (in seconds) before increasing bank accounts balance. /// How much time to wait (in seconds) before increasing bank accounts balance.
@@ -41,6 +42,7 @@ namespace Content.Server.Cargo.Systems
SubscribeLocalEvent<CargoOrderConsoleComponent, ComponentInit>(OnInit); SubscribeLocalEvent<CargoOrderConsoleComponent, ComponentInit>(OnInit);
SubscribeLocalEvent<CargoOrderConsoleComponent, InteractUsingEvent>(OnInteractUsing); SubscribeLocalEvent<CargoOrderConsoleComponent, InteractUsingEvent>(OnInteractUsing);
SubscribeLocalEvent<CargoOrderConsoleComponent, BankBalanceUpdatedEvent>(OnOrderBalanceUpdated); SubscribeLocalEvent<CargoOrderConsoleComponent, BankBalanceUpdatedEvent>(OnOrderBalanceUpdated);
SubscribeLocalEvent<CargoOrderConsoleComponent, GotEmaggedEvent>(OnEmagged);
Reset(); Reset();
} }
@@ -75,6 +77,17 @@ namespace Content.Server.Cargo.Systems
_timer = 0; _timer = 0;
} }
private void OnEmagged(Entity<CargoOrderConsoleComponent> 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) private void UpdateConsole(float frameTime)
{ {
_timer += frameTime; _timer += frameTime;
@@ -192,7 +205,7 @@ namespace Content.Server.Cargo.Systems
order.Approved = true; order.Approved = true;
_audio.PlayPvs(component.ConfirmSound, uid); _audio.PlayPvs(component.ConfirmSound, uid);
if (!HasComp<EmaggedComponent>(uid)) if (!_emag.CheckFlag(uid, EmagType.Interaction))
{ {
var tryGetIdentityShortInfoEvent = new TryGetIdentityShortInfoEvent(uid, player); var tryGetIdentityShortInfoEvent = new TryGetIdentityShortInfoEvent(uid, player);
RaiseLocalEvent(tryGetIdentityShortInfoEvent); RaiseLocalEvent(tryGetIdentityShortInfoEvent);

View File

@@ -60,6 +60,7 @@ namespace Content.Server.Cloning
[Dependency] private readonly SharedMindSystem _mindSystem = default!; [Dependency] private readonly SharedMindSystem _mindSystem = default!;
[Dependency] private readonly MetaDataSystem _metaSystem = default!; [Dependency] private readonly MetaDataSystem _metaSystem = default!;
[Dependency] private readonly SharedJobSystem _jobs = default!; [Dependency] private readonly SharedJobSystem _jobs = default!;
[Dependency] private readonly EmagSystem _emag = default!;
public readonly Dictionary<MindComponent, EntityUid> ClonesWaitingForMind = new(); public readonly Dictionary<MindComponent, EntityUid> ClonesWaitingForMind = new();
public const float EasyModeCloningCost = 0.7f; public const float EasyModeCloningCost = 0.7f;
@@ -276,10 +277,15 @@ namespace Content.Server.Cloning
/// </summary> /// </summary>
private void OnEmagged(EntityUid uid, CloningPodComponent clonePod, ref GotEmaggedEvent args) 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)) if (!this.IsPowered(uid, EntityManager))
return; return;
_audio.PlayPvs(clonePod.SparkSound, uid);
_popupSystem.PopupEntity(Loc.GetString("cloning-pod-component-upgrade-emag-requirement"), uid); _popupSystem.PopupEntity(Loc.GetString("cloning-pod-component-upgrade-emag-requirement"), uid);
args.Handled = true; args.Handled = true;
} }
@@ -309,7 +315,7 @@ namespace Content.Server.Cloning
var indices = _transformSystem.GetGridTilePositionOrDefault((uid, transform)); var indices = _transformSystem.GetGridTilePositionOrDefault((uid, transform));
var tileMix = _atmosphereSystem.GetTileMixture(transform.GridUid, null, indices, true); var tileMix = _atmosphereSystem.GetTileMixture(transform.GridUid, null, indices, true);
if (HasComp<EmaggedComponent>(uid)) if (_emag.CheckFlag(uid, EmagType.Interaction))
{ {
_audio.PlayPvs(clonePod.ScreamSound, uid); _audio.PlayPvs(clonePod.ScreamSound, uid);
Spawn(clonePod.MobSpawnId, transform.Coordinates); Spawn(clonePod.MobSpawnId, transform.Coordinates);
@@ -327,7 +333,7 @@ namespace Content.Server.Cloning
} }
_puddleSystem.TrySpillAt(uid, bloodSolution, out _); _puddleSystem.TrySpillAt(uid, bloodSolution, out _);
if (!HasComp<EmaggedComponent>(uid)) if (!_emag.CheckFlag(uid, EmagType.Interaction))
{ {
_material.SpawnMultipleFromMaterial(_robustRandom.Next(1, (int) (clonePod.UsedBiomass / 2.5)), clonePod.RequiredMaterial, Transform(uid).Coordinates); _material.SpawnMultipleFromMaterial(_robustRandom.Next(1, (int) (clonePod.UsedBiomass / 2.5)), clonePod.RequiredMaterial, Transform(uid).Coordinates);
} }

View File

@@ -16,7 +16,6 @@ using Content.Shared.Chat;
using Content.Shared.Communications; using Content.Shared.Communications;
using Content.Shared.Database; using Content.Shared.Database;
using Content.Shared.DeviceNetwork; using Content.Shared.DeviceNetwork;
using Content.Shared.Emag.Components;
using Content.Shared.IdentityManagement; using Content.Shared.IdentityManagement;
using Content.Shared.Popups; using Content.Shared.Popups;
using Robust.Server.GameObjects; using Robust.Server.GameObjects;
@@ -177,7 +176,7 @@ namespace Content.Server.Communications
private bool CanUse(EntityUid user, EntityUid console) private bool CanUse(EntityUid user, EntityUid console)
{ {
if (TryComp<AccessReaderComponent>(console, out var accessReaderComponent) && !HasComp<EmaggedComponent>(console)) if (TryComp<AccessReaderComponent>(console, out var accessReaderComponent))
{ {
return _accessReaderSystem.IsAllowed(user, console, accessReaderComponent); return _accessReaderSystem.IsAllowed(user, console, accessReaderComponent);
} }

View File

@@ -50,6 +50,7 @@ public sealed class FaxSystem : EntitySystem
[Dependency] private readonly ISharedAdminLogManager _adminLogger = default!; [Dependency] private readonly ISharedAdminLogManager _adminLogger = default!;
[Dependency] private readonly MetaDataSystem _metaData = default!; [Dependency] private readonly MetaDataSystem _metaData = default!;
[Dependency] private readonly FaxecuteSystem _faxecute = default!; [Dependency] private readonly FaxecuteSystem _faxecute = default!;
[Dependency] private readonly EmagSystem _emag = default!;
private const string PaperSlotId = "Paper"; private const string PaperSlotId = "Paper";
@@ -227,7 +228,7 @@ public sealed class FaxSystem : EntitySystem
return; return;
} }
if (component.KnownFaxes.ContainsValue(newName) && !HasComp<EmaggedComponent>(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); _popupSystem.PopupEntity(Loc.GetString("fax-machine-popup-name-exist"), uid);
return; return;
@@ -246,7 +247,12 @@ public sealed class FaxSystem : EntitySystem
private void OnEmagged(EntityUid uid, FaxMachineComponent component, ref GotEmaggedEvent args) 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; args.Handled = true;
} }
@@ -260,7 +266,7 @@ public sealed class FaxSystem : EntitySystem
switch (command) switch (command)
{ {
case FaxConstants.FaxPingCommand: case FaxConstants.FaxPingCommand:
var isForSyndie = HasComp<EmaggedComponent>(uid) && var isForSyndie = _emag.CheckFlag(uid, EmagType.Interaction) &&
args.Data.ContainsKey(FaxConstants.FaxSyndicateData); args.Data.ContainsKey(FaxConstants.FaxSyndicateData);
if (!isForSyndie && !component.ResponsePings) if (!isForSyndie && !component.ResponsePings)
return; return;
@@ -405,7 +411,7 @@ public sealed class FaxSystem : EntitySystem
{ DeviceNetworkConstants.Command, FaxConstants.FaxPingCommand } { DeviceNetworkConstants.Command, FaxConstants.FaxPingCommand }
}; };
if (HasComp<EmaggedComponent>(uid)) if (_emag.CheckFlag(uid, EmagType.Interaction))
payload.Add(FaxConstants.FaxSyndicateData, true); payload.Add(FaxConstants.FaxSyndicateData, true);
_deviceNetworkSystem.QueuePacket(uid, null, payload); _deviceNetworkSystem.QueuePacket(uid, null, payload);

View File

@@ -16,6 +16,7 @@ using Content.Shared.Chemistry.Reagent;
using Content.Shared.UserInterface; using Content.Shared.UserInterface;
using Content.Shared.Database; using Content.Shared.Database;
using Content.Shared.Emag.Components; using Content.Shared.Emag.Components;
using Content.Shared.Emag.Systems;
using Content.Shared.Examine; using Content.Shared.Examine;
using Content.Shared.Lathe; using Content.Shared.Lathe;
using Content.Shared.Materials; using Content.Shared.Materials;
@@ -42,6 +43,7 @@ namespace Content.Server.Lathe
[Dependency] private readonly SharedAppearanceSystem _appearance = default!; [Dependency] private readonly SharedAppearanceSystem _appearance = default!;
[Dependency] private readonly SharedAudioSystem _audio = default!; [Dependency] private readonly SharedAudioSystem _audio = default!;
[Dependency] private readonly ContainerSystem _container = default!; [Dependency] private readonly ContainerSystem _container = default!;
[Dependency] private readonly EmagSystem _emag = default!;
[Dependency] private readonly UserInterfaceSystem _uiSys = default!; [Dependency] private readonly UserInterfaceSystem _uiSys = default!;
[Dependency] private readonly MaterialStorageSystem _materialStorage = default!; [Dependency] private readonly MaterialStorageSystem _materialStorage = default!;
[Dependency] private readonly PopupSystem _popup = default!; [Dependency] private readonly PopupSystem _popup = default!;
@@ -292,7 +294,7 @@ namespace Content.Server.Lathe
{ {
if (uid != args.Lathe || !TryComp<TechnologyDatabaseComponent>(uid, out var technologyDatabase)) if (uid != args.Lathe || !TryComp<TechnologyDatabaseComponent>(uid, out var technologyDatabase))
return; return;
if (!args.getUnavailable && !HasComp<EmaggedComponent>(uid)) if (!args.getUnavailable && !_emag.CheckFlag(uid, EmagType.Interaction))
return; return;
foreach (var recipe in component.EmagDynamicRecipes) foreach (var recipe in component.EmagDynamicRecipes)
{ {

View File

@@ -21,6 +21,7 @@ namespace Content.Server.Nutrition.EntitySystems;
public sealed class FatExtractorSystem : EntitySystem public sealed class FatExtractorSystem : EntitySystem
{ {
[Dependency] private readonly IGameTiming _timing = default!; [Dependency] private readonly IGameTiming _timing = default!;
[Dependency] private readonly EmagSystem _emag = default!;
[Dependency] private readonly HungerSystem _hunger = default!; [Dependency] private readonly HungerSystem _hunger = default!;
[Dependency] private readonly SharedAppearanceSystem _appearance = default!; [Dependency] private readonly SharedAppearanceSystem _appearance = default!;
[Dependency] private readonly SharedAudioSystem _audio = default!; [Dependency] private readonly SharedAudioSystem _audio = default!;
@@ -36,8 +37,13 @@ public sealed class FatExtractorSystem : EntitySystem
private void OnGotEmagged(EntityUid uid, FatExtractorComponent component, ref GotEmaggedEvent args) 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.Handled = true;
args.Repeatable = false;
} }
private void OnClosed(EntityUid uid, FatExtractorComponent component, ref StorageAfterCloseEvent args) private void OnClosed(EntityUid uid, FatExtractorComponent component, ref StorageAfterCloseEvent args)
@@ -103,7 +109,7 @@ public sealed class FatExtractorSystem : EntitySystem
if (_hunger.GetHunger(hunger) < component.NutritionPerSecond) if (_hunger.GetHunger(hunger) < component.NutritionPerSecond)
return false; return false;
if (hunger.CurrentThreshold < component.MinHungerThreshold && !HasComp<EmaggedComponent>(uid)) if (hunger.CurrentThreshold < component.MinHungerThreshold && !_emag.CheckFlag(uid, EmagType.Interaction))
return false; return false;
return true; return true;

View File

@@ -24,6 +24,7 @@ namespace Content.Server.Nutrition.EntitySystems
{ {
[Dependency] private readonly DoAfterSystem _doAfterSystem = default!; [Dependency] private readonly DoAfterSystem _doAfterSystem = default!;
[Dependency] private readonly DamageableSystem _damageableSystem = default!; [Dependency] private readonly DamageableSystem _damageableSystem = default!;
[Dependency] private readonly EmagSystem _emag = default!;
[Dependency] private readonly FoodSystem _foodSystem = default!; [Dependency] private readonly FoodSystem _foodSystem = default!;
[Dependency] private readonly ExplosionSystem _explosionSystem = default!; [Dependency] private readonly ExplosionSystem _explosionSystem = default!;
[Dependency] private readonly PopupSystem _popupSystem = default!; [Dependency] private readonly PopupSystem _popupSystem = default!;
@@ -63,7 +64,7 @@ namespace Content.Server.Nutrition.EntitySystems
forced = false; forced = false;
} }
if (entity.Comp.ExplodeOnUse || HasComp<EmaggedComponent>(entity.Owner)) if (entity.Comp.ExplodeOnUse || _emag.CheckFlag(entity, EmagType.Interaction))
{ {
_explosionSystem.QueueExplosion(entity.Owner, "Default", entity.Comp.ExplosionIntensity, 0.5f, 3, canCreateVacuum: false); _explosionSystem.QueueExplosion(entity.Owner, "Default", entity.Comp.ExplosionIntensity, 0.5f, 3, canCreateVacuum: false);
EntityManager.DeleteEntity(entity); EntityManager.DeleteEntity(entity);
@@ -161,8 +162,15 @@ namespace Content.Server.Nutrition.EntitySystems
args.Args.Target.Value); args.Args.Target.Value);
} }
} }
private void OnEmagged(Entity<VapeComponent> entity, ref GotEmaggedEvent args) private void OnEmagged(Entity<VapeComponent> entity, ref GotEmaggedEvent args)
{ {
if (!_emag.CompareFlag(args.Type, EmagType.Interaction))
return;
if (_emag.CheckFlag(entity, EmagType.Interaction))
return;
args.Handled = true; args.Handled = true;
} }
} }

View File

@@ -4,7 +4,6 @@ using Content.Server.Power.Components;
using Content.Server.Power.Pow3r; using Content.Server.Power.Pow3r;
using Content.Shared.Access.Systems; using Content.Shared.Access.Systems;
using Content.Shared.APC; using Content.Shared.APC;
using Content.Shared.Emag.Components;
using Content.Shared.Emag.Systems; using Content.Shared.Emag.Systems;
using Content.Shared.Popups; using Content.Shared.Popups;
using Content.Shared.Rounding; using Content.Shared.Rounding;
@@ -19,6 +18,7 @@ public sealed class ApcSystem : EntitySystem
{ {
[Dependency] private readonly AccessReaderSystem _accessReader = default!; [Dependency] private readonly AccessReaderSystem _accessReader = default!;
[Dependency] private readonly IGameTiming _gameTiming = default!; [Dependency] private readonly IGameTiming _gameTiming = default!;
[Dependency] private readonly EmagSystem _emag = default!;
[Dependency] private readonly PopupSystem _popup = default!; [Dependency] private readonly PopupSystem _popup = default!;
[Dependency] private readonly SharedAppearanceSystem _appearance = default!; [Dependency] private readonly SharedAppearanceSystem _appearance = default!;
[Dependency] private readonly SharedAudioSystem _audio = default!; [Dependency] private readonly SharedAudioSystem _audio = default!;
@@ -111,7 +111,12 @@ public sealed class ApcSystem : EntitySystem
private void OnEmagged(EntityUid uid, ApcComponent comp, ref GotEmaggedEvent args) 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; args.Handled = true;
} }
@@ -170,7 +175,7 @@ public sealed class ApcSystem : EntitySystem
private ApcChargeState CalcChargeState(EntityUid uid, PowerState.Battery battery) private ApcChargeState CalcChargeState(EntityUid uid, PowerState.Battery battery)
{ {
if (HasComp<EmaggedComponent>(uid)) if (_emag.CheckFlag(uid, EmagType.Interaction))
return ApcChargeState.Emag; return ApcChargeState.Emag;
if (battery.CurrentStorage / battery.Capacity > ApcComponent.HighPowerThreshold) if (battery.CurrentStorage / battery.Capacity > ApcComponent.HighPowerThreshold)

View File

@@ -3,6 +3,7 @@ using Content.Server.Research.Components;
using Content.Shared.UserInterface; using Content.Shared.UserInterface;
using Content.Shared.Access.Components; using Content.Shared.Access.Components;
using Content.Shared.Emag.Components; using Content.Shared.Emag.Components;
using Content.Shared.Emag.Systems;
using Content.Shared.IdentityManagement; using Content.Shared.IdentityManagement;
using Content.Shared.Research.Components; using Content.Shared.Research.Components;
using Content.Shared.Research.Prototypes; using Content.Shared.Research.Prototypes;
@@ -11,6 +12,8 @@ namespace Content.Server.Research.Systems;
public sealed partial class ResearchSystem public sealed partial class ResearchSystem
{ {
[Dependency] private readonly EmagSystem _emag = default!;
private void InitializeConsole() private void InitializeConsole()
{ {
SubscribeLocalEvent<ResearchConsoleComponent, ConsoleUnlockTechnologyMessage>(OnConsoleUnlock); SubscribeLocalEvent<ResearchConsoleComponent, ConsoleUnlockTechnologyMessage>(OnConsoleUnlock);
@@ -18,6 +21,7 @@ public sealed partial class ResearchSystem
SubscribeLocalEvent<ResearchConsoleComponent, ResearchServerPointsChangedEvent>(OnPointsChanged); SubscribeLocalEvent<ResearchConsoleComponent, ResearchServerPointsChangedEvent>(OnPointsChanged);
SubscribeLocalEvent<ResearchConsoleComponent, ResearchRegistrationChangedEvent>(OnConsoleRegistrationChanged); SubscribeLocalEvent<ResearchConsoleComponent, ResearchRegistrationChangedEvent>(OnConsoleRegistrationChanged);
SubscribeLocalEvent<ResearchConsoleComponent, TechnologyDatabaseModifiedEvent>(OnConsoleDatabaseModified); SubscribeLocalEvent<ResearchConsoleComponent, TechnologyDatabaseModifiedEvent>(OnConsoleDatabaseModified);
SubscribeLocalEvent<ResearchConsoleComponent, GotEmaggedEvent>(OnEmagged);
} }
private void OnConsoleUnlock(EntityUid uid, ResearchConsoleComponent component, ConsoleUnlockTechnologyMessage args) private void OnConsoleUnlock(EntityUid uid, ResearchConsoleComponent component, ConsoleUnlockTechnologyMessage args)
@@ -39,7 +43,7 @@ public sealed partial class ResearchSystem
if (!UnlockTechnology(uid, args.Id, act)) if (!UnlockTechnology(uid, args.Id, act))
return; return;
if (!HasComp<EmaggedComponent>(uid)) if (!_emag.CheckFlag(uid, EmagType.Interaction))
{ {
var getIdentityEvent = new TryGetIdentityShortInfoEvent(uid, act); var getIdentityEvent = new TryGetIdentityShortInfoEvent(uid, act);
RaiseLocalEvent(getIdentityEvent); RaiseLocalEvent(getIdentityEvent);
@@ -100,4 +104,15 @@ public sealed partial class ResearchSystem
UpdateConsoleInterface(uid, component); UpdateConsoleInterface(uid, component);
} }
private void OnEmagged(Entity<ResearchConsoleComponent> ent, ref GotEmaggedEvent args)
{
if (!_emag.CompareFlag(args.Type, EmagType.Interaction))
return;
if (_emag.CheckFlag(ent, EmagType.Interaction))
return;
args.Handled = true;
}
} }

View File

@@ -36,7 +36,6 @@ public sealed partial class RevenantSystem
{ {
[Dependency] private readonly ThrowingSystem _throwing = default!; [Dependency] private readonly ThrowingSystem _throwing = default!;
[Dependency] private readonly EntityStorageSystem _entityStorage = default!; [Dependency] private readonly EntityStorageSystem _entityStorage = default!;
[Dependency] private readonly EmagSystem _emag = default!;
[Dependency] private readonly SharedAppearanceSystem _appearance = default!; [Dependency] private readonly SharedAppearanceSystem _appearance = default!;
[Dependency] private readonly MobThresholdSystem _mobThresholdSystem = default!; [Dependency] private readonly MobThresholdSystem _mobThresholdSystem = default!;
[Dependency] private readonly GhostSystem _ghost = default!; [Dependency] private readonly GhostSystem _ghost = default!;
@@ -343,7 +342,8 @@ public sealed partial class RevenantSystem
_whitelistSystem.IsBlacklistPass(component.MalfunctionBlacklist, ent)) _whitelistSystem.IsBlacklistPass(component.MalfunctionBlacklist, ent))
continue; continue;
_emag.DoEmagEffect(uid, ent); //it does not emag itself. adorable. var ev = new GotEmaggedEvent(uid, EmagType.Interaction | EmagType.Access);
RaiseLocalEvent(ent, ref ev);
} }
} }
} }

View File

@@ -8,6 +8,7 @@ using Content.Server.DeviceNetwork;
using Content.Server.DeviceNetwork.Components; using Content.Server.DeviceNetwork.Components;
using Content.Server.DeviceNetwork.Systems; using Content.Server.DeviceNetwork.Systems;
using Content.Server.Explosion.Components; using Content.Server.Explosion.Components;
using Content.Shared.Emag.Systems;
using Robust.Shared.Utility; using Robust.Shared.Utility;
namespace Content.Server.Silicons.Borgs; namespace Content.Server.Silicons.Borgs;
@@ -15,6 +16,8 @@ namespace Content.Server.Silicons.Borgs;
/// <inheritdoc/> /// <inheritdoc/>
public sealed partial class BorgSystem public sealed partial class BorgSystem
{ {
[Dependency] private readonly EmagSystem _emag = default!;
private void InitializeTransponder() private void InitializeTransponder()
{ {
SubscribeLocalEvent<BorgTransponderComponent, DeviceNetworkPacketEvent>(OnPacketReceived); SubscribeLocalEvent<BorgTransponderComponent, DeviceNetworkPacketEvent>(OnPacketReceived);
@@ -127,7 +130,7 @@ public sealed partial class BorgSystem
private bool CheckEmagged(EntityUid uid, string name) private bool CheckEmagged(EntityUid uid, string name)
{ {
if (HasComp<EmaggedComponent>(uid)) if (_emag.CheckFlag(uid, EmagType.Interaction))
{ {
Popup.PopupEntity(Loc.GetString($"borg-transponder-emagged-{name}-popup"), uid, uid, PopupType.LargeCaution); Popup.PopupEntity(Loc.GetString($"borg-transponder-emagged-{name}-popup"), uid, uid, PopupType.LargeCaution);
return true; return true;

View File

@@ -6,7 +6,6 @@ using Content.Server.Roles;
using Content.Server.Station.Systems; using Content.Server.Station.Systems;
using Content.Shared.Administration; using Content.Shared.Administration;
using Content.Shared.Chat; using Content.Shared.Chat;
using Content.Shared.Emag.Components;
using Content.Shared.Emag.Systems; using Content.Shared.Emag.Systems;
using Content.Shared.GameTicking; using Content.Shared.GameTicking;
using Content.Shared.Mind; using Content.Shared.Mind;
@@ -14,7 +13,6 @@ using Content.Shared.Mind.Components;
using Content.Shared.Roles; using Content.Shared.Roles;
using Content.Shared.Silicons.Laws; using Content.Shared.Silicons.Laws;
using Content.Shared.Silicons.Laws.Components; using Content.Shared.Silicons.Laws.Components;
using Content.Shared.Stunnable;
using Content.Shared.Wires; using Content.Shared.Wires;
using Robust.Server.GameObjects; using Robust.Server.GameObjects;
using Robust.Shared.Audio; using Robust.Shared.Audio;
@@ -22,7 +20,6 @@ using Robust.Shared.Containers;
using Robust.Shared.Player; using Robust.Shared.Player;
using Robust.Shared.Prototypes; using Robust.Shared.Prototypes;
using Robust.Shared.Toolshed; using Robust.Shared.Toolshed;
using Robust.Shared.Audio;
namespace Content.Server.Silicons.Laws; namespace Content.Server.Silicons.Laws;
@@ -34,8 +31,8 @@ public sealed class SiliconLawSystem : SharedSiliconLawSystem
[Dependency] private readonly IPrototypeManager _prototype = default!; [Dependency] private readonly IPrototypeManager _prototype = default!;
[Dependency] private readonly SharedRoleSystem _roles = default!; [Dependency] private readonly SharedRoleSystem _roles = default!;
[Dependency] private readonly StationSystem _station = default!; [Dependency] private readonly StationSystem _station = default!;
[Dependency] private readonly SharedStunSystem _stunSystem = default!;
[Dependency] private readonly UserInterfaceSystem _userInterface = default!; [Dependency] private readonly UserInterfaceSystem _userInterface = default!;
[Dependency] private readonly EmagSystem _emag = default!;
/// <inheritdoc/> /// <inheritdoc/>
public override void Initialize() public override void Initialize()
@@ -52,7 +49,7 @@ public sealed class SiliconLawSystem : SharedSiliconLawSystem
SubscribeLocalEvent<SiliconLawProviderComponent, IonStormLawsEvent>(OnIonStormLaws); SubscribeLocalEvent<SiliconLawProviderComponent, IonStormLawsEvent>(OnIonStormLaws);
SubscribeLocalEvent<SiliconLawProviderComponent, MindAddedMessage>(OnLawProviderMindAdded); SubscribeLocalEvent<SiliconLawProviderComponent, MindAddedMessage>(OnLawProviderMindAdded);
SubscribeLocalEvent<SiliconLawProviderComponent, MindRemovedMessage>(OnLawProviderMindRemoved); SubscribeLocalEvent<SiliconLawProviderComponent, MindRemovedMessage>(OnLawProviderMindRemoved);
SubscribeLocalEvent<SiliconLawProviderComponent, GotEmaggedEvent>(OnEmagLawsAdded); SubscribeLocalEvent<SiliconLawProviderComponent, SiliconEmaggedEvent>(OnEmagLawsAdded);
} }
private void OnMapInit(EntityUid uid, SiliconLawBoundComponent component, MapInitEvent args) private void OnMapInit(EntityUid uid, SiliconLawBoundComponent component, MapInitEvent args)
@@ -135,7 +132,7 @@ public sealed class SiliconLawSystem : SharedSiliconLawSystem
private void OnIonStormLaws(EntityUid uid, SiliconLawProviderComponent component, ref IonStormLawsEvent args) private void OnIonStormLaws(EntityUid uid, SiliconLawProviderComponent component, ref IonStormLawsEvent args)
{ {
// Emagged borgs are immune to ion storm // Emagged borgs are immune to ion storm
if (!HasComp<EmaggedComponent>(uid)) if (!_emag.CheckFlag(uid, EmagType.Interaction))
{ {
component.Lawset = args.Lawset; component.Lawset = args.Lawset;
@@ -152,9 +149,8 @@ public sealed class SiliconLawSystem : SharedSiliconLawSystem
} }
} }
private void OnEmagLawsAdded(EntityUid uid, SiliconLawProviderComponent component, ref GotEmaggedEvent args) private void OnEmagLawsAdded(EntityUid uid, SiliconLawProviderComponent component, ref SiliconEmaggedEvent args)
{ {
if (component.Lawset == null) if (component.Lawset == null)
component.Lawset = GetLawset(component.Laws); component.Lawset = GetLawset(component.Laws);
@@ -164,7 +160,7 @@ public sealed class SiliconLawSystem : SharedSiliconLawSystem
// Add the first emag law before the others // Add the first emag law before the others
component.Lawset?.Laws.Insert(0, new SiliconLaw 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 Order = 0
}); });
@@ -176,20 +172,6 @@ public sealed class SiliconLawSystem : SharedSiliconLawSystem
}); });
} }
protected override void OnGotEmagged(EntityUid uid, EmagSiliconLawComponent component, ref GotEmaggedEvent args)
{
if (component.RequireOpenPanel && TryComp<WiresPanelComponent>(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) private void EnsureSubvertedSiliconRole(EntityUid mindId)
{ {
if (!_roles.MindHasRole<SubvertedSiliconRoleComponent>(mindId)) if (!_roles.MindHasRole<SubvertedSiliconRoleComponent>(mindId))

View File

@@ -39,6 +39,7 @@ namespace Content.Server.VendingMachines
[Dependency] private readonly IGameTiming _timing = default!; [Dependency] private readonly IGameTiming _timing = default!;
[Dependency] private readonly SpeakOnUIClosedSystem _speakOnUIClosed = default!; [Dependency] private readonly SpeakOnUIClosedSystem _speakOnUIClosed = default!;
[Dependency] private readonly SharedPointLightSystem _light = default!; [Dependency] private readonly SharedPointLightSystem _light = default!;
[Dependency] private readonly EmagSystem _emag = default!;
private const float WallVendEjectDistanceFromWall = 1f; private const float WallVendEjectDistanceFromWall = 1f;
@@ -48,7 +49,6 @@ namespace Content.Server.VendingMachines
SubscribeLocalEvent<VendingMachineComponent, PowerChangedEvent>(OnPowerChanged); SubscribeLocalEvent<VendingMachineComponent, PowerChangedEvent>(OnPowerChanged);
SubscribeLocalEvent<VendingMachineComponent, BreakageEventArgs>(OnBreak); SubscribeLocalEvent<VendingMachineComponent, BreakageEventArgs>(OnBreak);
SubscribeLocalEvent<VendingMachineComponent, GotEmaggedEvent>(OnEmagged);
SubscribeLocalEvent<VendingMachineComponent, DamageChangedEvent>(OnDamageChanged); SubscribeLocalEvent<VendingMachineComponent, DamageChangedEvent>(OnDamageChanged);
SubscribeLocalEvent<VendingMachineComponent, PriceCalculationEvent>(OnVendingPrice); SubscribeLocalEvent<VendingMachineComponent, PriceCalculationEvent>(OnVendingPrice);
SubscribeLocalEvent<VendingMachineComponent, EmpPulseEvent>(OnEmpPulse); SubscribeLocalEvent<VendingMachineComponent, EmpPulseEvent>(OnEmpPulse);
@@ -123,12 +123,6 @@ namespace Content.Server.VendingMachines
TryUpdateVisualState(uid, vendComponent); 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) private void OnDamageChanged(EntityUid uid, VendingMachineComponent component, DamageChangedEvent args)
{ {
if (!args.DamageIncreased && component.Broken) if (!args.DamageIncreased && component.Broken)
@@ -232,7 +226,7 @@ namespace Content.Server.VendingMachines
if (!TryComp<AccessReaderComponent>(uid, out var accessReader)) if (!TryComp<AccessReaderComponent>(uid, out var accessReader))
return true; return true;
if (_accessReader.IsAllowed(sender, uid, accessReader) || HasComp<EmaggedComponent>(uid)) if (_accessReader.IsAllowed(sender, uid, accessReader))
return true; return true;
Popup.PopupEntity(Loc.GetString("vending-machine-component-try-eject-access-denied"), uid); Popup.PopupEntity(Loc.GetString("vending-machine-component-try-eject-access-denied"), uid);
@@ -422,7 +416,7 @@ namespace Content.Server.VendingMachines
if (!Resolve(uid, ref component)) if (!Resolve(uid, ref component))
return null; return null;
if (type == InventoryType.Emagged && HasComp<EmaggedComponent>(uid)) if (type == InventoryType.Emagged && _emag.CheckFlag(uid, EmagType.Interaction))
return component.EmaggedInventory.GetValueOrDefault(entryId); return component.EmaggedInventory.GetValueOrDefault(entryId);
if (type == InventoryType.Contraband && component.Contraband) if (type == InventoryType.Contraband && component.Contraband)

View File

@@ -76,7 +76,7 @@ public sealed partial class AccessReaderComponent : Component
/// Whether or not emag interactions have an effect on this. /// Whether or not emag interactions have an effect on this.
/// </summary> /// </summary>
[DataField] [DataField]
public bool BreakOnEmag = true; public bool BreakOnAccessBreaker = true;
} }
[DataDefinition, Serializable, NetSerializable] [DataDefinition, Serializable, NetSerializable]

View File

@@ -2,7 +2,6 @@ using System.Diagnostics.CodeAnalysis;
using System.Linq; using System.Linq;
using Content.Shared.Access.Components; using Content.Shared.Access.Components;
using Content.Shared.DeviceLinking.Events; using Content.Shared.DeviceLinking.Events;
using Content.Shared.Emag.Components;
using Content.Shared.Emag.Systems; using Content.Shared.Emag.Systems;
using Content.Shared.Hands.EntitySystems; using Content.Shared.Hands.EntitySystems;
using Content.Shared.Inventory; using Content.Shared.Inventory;
@@ -24,6 +23,7 @@ public sealed class AccessReaderSystem : EntitySystem
[Dependency] private readonly IPrototypeManager _prototype = default!; [Dependency] private readonly IPrototypeManager _prototype = default!;
[Dependency] private readonly InventorySystem _inventorySystem = default!; [Dependency] private readonly InventorySystem _inventorySystem = default!;
[Dependency] private readonly IGameTiming _gameTiming = default!; [Dependency] private readonly IGameTiming _gameTiming = default!;
[Dependency] private readonly EmagSystem _emag = default!;
[Dependency] private readonly SharedGameTicker _gameTicker = default!; [Dependency] private readonly SharedGameTicker _gameTicker = default!;
[Dependency] private readonly SharedHandsSystem _handsSystem = default!; [Dependency] private readonly SharedHandsSystem _handsSystem = default!;
[Dependency] private readonly SharedContainerSystem _containerSystem = default!; [Dependency] private readonly SharedContainerSystem _containerSystem = default!;
@@ -71,17 +71,28 @@ public sealed class AccessReaderSystem : EntitySystem
{ {
if (args.User == null) // AutoLink (and presumably future external linkers) have no user. if (args.User == null) // AutoLink (and presumably future external linkers) have no user.
return; return;
if (!HasComp<EmaggedComponent>(uid) && !IsAllowed(args.User.Value, uid, component)) if (!IsAllowed(args.User.Value, uid, component))
args.Cancel(); args.Cancel();
} }
private void OnEmagged(EntityUid uid, AccessReaderComponent reader, ref GotEmaggedEvent args) private void OnEmagged(EntityUid uid, AccessReaderComponent reader, ref GotEmaggedEvent args)
{ {
if (!reader.BreakOnEmag) if (!_emag.CompareFlag(args.Type, EmagType.Access))
return; 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; args.Handled = true;
reader.Enabled = false; accessReader.Value.Comp.AccessLists.Clear();
reader.AccessLog.Clear(); accessReader.Value.Comp.AccessLog.Clear();
Dirty(uid, reader); Dirty(uid, reader);
} }
@@ -135,6 +146,7 @@ public sealed class AccessReaderSystem : EntitySystem
return true; return true;
} }
} }
return true; return true;
} }

View File

@@ -45,3 +45,6 @@ namespace Content.Shared.Access.Systems
} }
} }
} }
[ByRefEvent]
public record struct OnAccessOverriderAccessUpdatedEvent(EntityUid UserUid, bool Handled = false);

View File

@@ -46,15 +46,6 @@ public sealed partial class CloningPodComponent : Component
[DataField("mobSpawnId"), ViewVariables(VVAccess.ReadWrite)] [DataField("mobSpawnId"), ViewVariables(VVAccess.ReadWrite)]
public EntProtoId MobSpawnId = "MobAbomination"; public EntProtoId MobSpawnId = "MobAbomination";
/// <summary>
/// Emag sound effects.
/// </summary>
[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 // TODO: Remove this from here when cloning and/or zombies are refactored
[DataField("screamSound")] [DataField("screamSound")]
public SoundSpecifier ScreamSound = new SoundCollectionSpecifier("ZombieScreams") public SoundSpecifier ScreamSound = new SoundCollectionSpecifier("ZombieScreams")

View File

@@ -24,6 +24,7 @@ public sealed partial class DisposalDoAfterEvent : SimpleDoAfterEvent
public abstract class SharedDisposalUnitSystem : EntitySystem public abstract class SharedDisposalUnitSystem : EntitySystem
{ {
[Dependency] protected readonly IGameTiming GameTiming = default!; [Dependency] protected readonly IGameTiming GameTiming = default!;
[Dependency] protected readonly EmagSystem _emag = default!;
[Dependency] protected readonly MetaDataSystem Metadata = default!; [Dependency] protected readonly MetaDataSystem Metadata = default!;
[Dependency] protected readonly SharedJointSystem Joints = default!; [Dependency] protected readonly SharedJointSystem Joints = default!;
[Dependency] private readonly EntityWhitelistSystem _whitelistSystem = default!; [Dependency] private readonly EntityWhitelistSystem _whitelistSystem = default!;
@@ -102,6 +103,12 @@ public abstract class SharedDisposalUnitSystem : EntitySystem
protected void OnEmagged(EntityUid uid, SharedDisposalUnitComponent component, ref GotEmaggedEvent args) 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; component.DisablePressure = true;
args.Handled = true; args.Handled = true;
} }

View File

@@ -34,6 +34,7 @@ public abstract partial class SharedDoorSystem : EntitySystem
[Dependency] private readonly INetManager _net = default!; [Dependency] private readonly INetManager _net = default!;
[Dependency] protected readonly SharedPhysicsSystem PhysicsSystem = default!; [Dependency] protected readonly SharedPhysicsSystem PhysicsSystem = default!;
[Dependency] private readonly DamageableSystem _damageableSystem = default!; [Dependency] private readonly DamageableSystem _damageableSystem = default!;
[Dependency] private readonly EmagSystem _emag = default!;
[Dependency] private readonly SharedStunSystem _stunSystem = default!; [Dependency] private readonly SharedStunSystem _stunSystem = default!;
[Dependency] protected readonly TagSystem Tags = default!; [Dependency] protected readonly TagSystem Tags = default!;
[Dependency] protected readonly SharedAudioSystem Audio = default!; [Dependency] protected readonly SharedAudioSystem Audio = default!;
@@ -77,8 +78,6 @@ public abstract partial class SharedDoorSystem : EntitySystem
SubscribeLocalEvent<DoorComponent, WeldableAttemptEvent>(OnWeldAttempt); SubscribeLocalEvent<DoorComponent, WeldableAttemptEvent>(OnWeldAttempt);
SubscribeLocalEvent<DoorComponent, WeldableChangedEvent>(OnWeldChanged); SubscribeLocalEvent<DoorComponent, WeldableChangedEvent>(OnWeldChanged);
SubscribeLocalEvent<DoorComponent, GetPryTimeModifierEvent>(OnPryTimeModifier); SubscribeLocalEvent<DoorComponent, GetPryTimeModifierEvent>(OnPryTimeModifier);
SubscribeLocalEvent<DoorComponent, OnAttemptEmagEvent>(OnAttemptEmag);
SubscribeLocalEvent<DoorComponent, GotEmaggedEvent>(OnEmagged); SubscribeLocalEvent<DoorComponent, GotEmaggedEvent>(OnEmagged);
} }
@@ -118,31 +117,24 @@ public abstract partial class SharedDoorSystem : EntitySystem
_activeDoors.Remove(door); _activeDoors.Remove(door);
} }
private void OnAttemptEmag(EntityUid uid, DoorComponent door, ref OnAttemptEmagEvent args)
{
if (!TryComp<AirlockComponent>(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;
}
}
private void OnEmagged(EntityUid uid, DoorComponent door, ref GotEmaggedEvent args) private void OnEmagged(EntityUid uid, DoorComponent door, ref GotEmaggedEvent args)
{ {
if (!_emag.CompareFlag(args.Type, EmagType.Access))
return;
if (!TryComp<AirlockComponent>(uid, out var airlock))
return;
if (IsBolted(uid) || !airlock.Powered)
return;
if (door.State != DoorState.Closed)
return;
if (!SetState(uid, DoorState.Emagging, door)) if (!SetState(uid, DoorState.Emagging, door))
return; return;
Audio.PlayPredicted(door.SparkSound, uid, args.UserUid, AudioParams.Default.WithVolume(8));
args.Repeatable = true;
args.Handled = true; args.Handled = true;
} }

View File

@@ -1,6 +1,8 @@
using Content.Shared.Emag.Systems; using Content.Shared.Emag.Systems;
using Content.Shared.Tag; using Content.Shared.Tag;
using Robust.Shared.Audio;
using Robust.Shared.GameStates; using Robust.Shared.GameStates;
using Robust.Shared.Prototypes;
using Robust.Shared.Serialization.TypeSerializers.Implementations.Custom.Prototype; using Robust.Shared.Serialization.TypeSerializers.Implementations.Custom.Prototype;
using Robust.Shared.Serialization; using Robust.Shared.Serialization;
@@ -14,7 +16,21 @@ public sealed partial class EmagComponent : Component
/// <summary> /// <summary>
/// The tag that marks an entity as immune to emags /// The tag that marks an entity as immune to emags
/// </summary> /// </summary>
[DataField("emagImmuneTag", customTypeSerializer: typeof(PrototypeIdSerializer<TagPrototype>)), ViewVariables(VVAccess.ReadWrite)] [DataField]
[AutoNetworkedField] [AutoNetworkedField]
public string EmagImmuneTag = "EmagImmune"; public ProtoId<TagPrototype> EmagImmuneTag = "EmagImmune";
/// <summary>
/// What type of emag effect this device will do
/// </summary>
[DataField]
[AutoNetworkedField]
public EmagType EmagType = EmagType.Interaction;
/// <summary>
/// What sound should the emag play when used
/// </summary>
[DataField]
[AutoNetworkedField]
public SoundSpecifier EmagSound = new SoundCollectionSpecifier("sparks");
} }

View File

@@ -1,3 +1,4 @@
using Content.Shared.Emag.Systems;
using Robust.Shared.GameStates; using Robust.Shared.GameStates;
namespace Content.Shared.Emag.Components; namespace Content.Shared.Emag.Components;
@@ -5,7 +6,12 @@ namespace Content.Shared.Emag.Components;
/// <summary> /// <summary>
/// Marker component for emagged entities /// Marker component for emagged entities
/// </summary> /// </summary>
[RegisterComponent, NetworkedComponent] [RegisterComponent, NetworkedComponent, AutoGenerateComponentState]
public sealed partial class EmaggedComponent : Component public sealed partial class EmaggedComponent : Component
{ {
/// <summary>
/// The EmagType flags that were used to emag this device
/// </summary>
[DataField, AutoNetworkedField]
public EmagType EmagType = EmagType.None;
} }

View File

@@ -6,8 +6,8 @@ using Content.Shared.Emag.Components;
using Content.Shared.IdentityManagement; using Content.Shared.IdentityManagement;
using Content.Shared.Interaction; using Content.Shared.Interaction;
using Content.Shared.Popups; using Content.Shared.Popups;
using Content.Shared.Silicons.Laws.Components;
using Content.Shared.Tag; using Content.Shared.Tag;
using Robust.Shared.Audio.Systems;
namespace Content.Shared.Emag.Systems; namespace Content.Shared.Emag.Systems;
@@ -23,88 +23,123 @@ public sealed class EmagSystem : EntitySystem
[Dependency] private readonly SharedChargesSystem _charges = default!; [Dependency] private readonly SharedChargesSystem _charges = default!;
[Dependency] private readonly SharedPopupSystem _popup = default!; [Dependency] private readonly SharedPopupSystem _popup = default!;
[Dependency] private readonly TagSystem _tag = default!; [Dependency] private readonly TagSystem _tag = default!;
[Dependency] private readonly SharedAudioSystem _audio = default!;
public override void Initialize() public override void Initialize()
{ {
base.Initialize(); base.Initialize();
SubscribeLocalEvent<EmagComponent, AfterInteractEvent>(OnAfterInteract); SubscribeLocalEvent<EmagComponent, AfterInteractEvent>(OnAfterInteract);
SubscribeLocalEvent<EmaggedComponent, OnAccessOverriderAccessUpdatedEvent>(OnAccessOverriderAccessUpdated);
} }
private void OnAccessOverriderAccessUpdated(Entity<EmaggedComponent> 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) private void OnAfterInteract(EntityUid uid, EmagComponent comp, AfterInteractEvent args)
{ {
if (!args.CanReach || args.Target is not { } target) if (!args.CanReach || args.Target is not { } target)
return; return;
args.Handled = TryUseEmag(uid, args.User, target, comp); args.Handled = TryEmagEffect((uid, comp), args.User, target);
}
/// <summary>
/// Tries to use the emag on a target entity
/// </summary>
public bool TryUseEmag(EntityUid uid, EntityUid user, EntityUid target, EmagComponent? comp = null)
{
if (!Resolve(uid, ref comp, false))
return false;
if (_tag.HasTag(target, comp.EmagImmuneTag))
return false;
TryComp<LimitedChargesComponent>(uid, out var charges);
if (_charges.IsEmpty(uid, charges))
{
_popup.PopupClient(Loc.GetString("emag-no-charges"), user, user);
return false;
}
var handled = DoEmagEffect(user, target);
if (!handled)
return false;
_popup.PopupClient(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}");
if (charges != null)
_charges.UseCharge(uid, charges);
return true;
} }
/// <summary> /// <summary>
/// Does the emag effect on a specified entity /// Does the emag effect on a specified entity
/// </summary> /// </summary>
public bool DoEmagEffect(EntityUid user, EntityUid target) public bool TryEmagEffect(Entity<EmagComponent?> ent, EntityUid user, EntityUid target)
{ {
// prevent emagging twice if (!Resolve(ent, ref ent.Comp, false))
if (HasComp<EmaggedComponent>(target))
return false; return false;
var onAttemptEmagEvent = new OnAttemptEmagEvent(user); if (_tag.HasTag(target, ent.Comp.EmagImmuneTag))
RaiseLocalEvent(target, ref onAttemptEmagEvent);
// prevent emagging if attempt fails
if (onAttemptEmagEvent.Handled)
return false; return false;
var emaggedEvent = new GotEmaggedEvent(user); TryComp<LimitedChargesComponent>(ent, out var charges);
if (_charges.IsEmpty(ent, charges))
{
_popup.PopupClient(Loc.GetString("emag-no-charges"), user, user);
return false;
}
var emaggedEvent = new GotEmaggedEvent(user, ent.Comp.EmagType);
RaiseLocalEvent(target, ref emaggedEvent); RaiseLocalEvent(target, ref emaggedEvent);
if (emaggedEvent.Handled && !emaggedEvent.Repeatable) if (!emaggedEvent.Handled)
EnsureComp<EmaggedComponent>(target); return false;
_popup.PopupPredicted(Loc.GetString("emag-success", ("target", Identity.Entity(target, EntityManager))), user, user, PopupType.Medium);
_audio.PlayPredicted(ent.Comp.EmagSound, ent, ent);
_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<EmaggedComponent>(target, out var emaggedComp);
emaggedComp.EmagType |= ent.Comp.EmagType;
Dirty(target, emaggedComp);
}
return emaggedEvent.Handled; return emaggedEvent.Handled;
} }
/// <summary>
/// Checks whether an entity has the EmaggedComponent with a set flag.
/// </summary>
/// <param name="target">The target entity to check for the flag.</param>
/// <param name="flag">The EmagType flag to check for.</param>
/// <returns>True if entity has EmaggedComponent and the provided flag. False if the entity lacks EmaggedComponent or provided flag.</returns>
public bool CheckFlag(EntityUid target, EmagType flag)
{
if (!TryComp<EmaggedComponent>(target, out var comp))
return false;
if ((comp.EmagType & flag) == flag)
return true;
return false;
}
/// <summary>
/// Compares a flag to the target.
/// </summary>
/// <param name="target">The target flag to check.</param>
/// <param name="flag">The flag to check for within the target.</param>
/// <returns>True if target contains flag. Otherwise false.</returns>
public bool CompareFlag(EmagType target, EmagType flag)
{
if ((target & flag) == flag)
return true;
return false;
}
} }
[Flags]
public enum EmagType : byte
{
None = 0,
Interaction = 1 << 1,
Access = 1 << 2
}
/// <summary> /// <summary>
/// Shows a popup to emag user (client side only!) and adds <see cref="EmaggedComponent"/> to the entity when handled /// Shows a popup to emag user (client side only!) and adds <see cref="EmaggedComponent"/> to the entity when handled
/// </summary> /// </summary>
/// <param name="UserUid">Emag user</param> /// <param name="UserUid">Emag user</param>
/// <param name="Type">The emag type to use</param>
/// <param name="Handled">Did the emagging succeed? Causes a user-only popup to show on client side</param> /// <param name="Handled">Did the emagging succeed? Causes a user-only popup to show on client side</param>
/// <param name="Repeatable">Can the entity be emagged more than once? Prevents adding of <see cref="EmaggedComponent"/></param> /// <param name="Repeatable">Can the entity be emagged more than once? Prevents adding of <see cref="EmaggedComponent"/></param>
/// <remarks>Needs to be handled in shared/client, not just the server, to actually show the emagging popup</remarks> /// <remarks>Needs to be handled in shared/client, not just the server, to actually show the emagging popup</remarks>
[ByRefEvent] [ByRefEvent]
public record struct GotEmaggedEvent(EntityUid UserUid, bool Handled = false, bool Repeatable = false); public record struct GotEmaggedEvent(EntityUid UserUid, EmagType Type, bool Handled = false, bool Repeatable = false);
[ByRefEvent]
public record struct OnAttemptEmagEvent(EntityUid UserUid, bool Handled = false);

View File

@@ -59,12 +59,6 @@ public sealed partial class FaxMachineComponent : Component
[DataField] [DataField]
public bool ReceiveNukeCodes { get; set; } = false; public bool ReceiveNukeCodes { get; set; } = false;
/// <summary>
/// Sound to play when fax has been emagged
/// </summary>
[DataField]
public SoundSpecifier EmagSound = new SoundCollectionSpecifier("sparks");
/// <summary> /// <summary>
/// Sound to play when fax printing new message /// Sound to play when fax printing new message
/// </summary> /// </summary>

View File

@@ -18,6 +18,7 @@ public abstract class SharedLatheSystem : EntitySystem
{ {
[Dependency] private readonly IPrototypeManager _proto = default!; [Dependency] private readonly IPrototypeManager _proto = default!;
[Dependency] private readonly SharedMaterialStorageSystem _materialStorage = default!; [Dependency] private readonly SharedMaterialStorageSystem _materialStorage = default!;
[Dependency] private readonly EmagSystem _emag = default!;
public readonly Dictionary<string, List<LatheRecipePrototype>> InverseRecipes = new(); public readonly Dictionary<string, List<LatheRecipePrototype>> InverseRecipes = new();
@@ -66,6 +67,12 @@ public abstract class SharedLatheSystem : EntitySystem
private void OnEmagged(EntityUid uid, EmagLatheRecipesComponent component, ref GotEmaggedEvent args) 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; args.Handled = true;
} }

View File

@@ -2,6 +2,7 @@ using Content.Shared.Actions;
using Content.Shared.Emag.Systems; using Content.Shared.Emag.Systems;
using Content.Shared.Light.Components; using Content.Shared.Light.Components;
using Content.Shared.Mind.Components; using Content.Shared.Mind.Components;
using Content.Shared.Storage.Components;
using Content.Shared.Toggleable; using Content.Shared.Toggleable;
using Content.Shared.Verbs; using Content.Shared.Verbs;
using Robust.Shared.Audio.Systems; using Robust.Shared.Audio.Systems;
@@ -22,6 +23,7 @@ public sealed class UnpoweredFlashlightSystem : EntitySystem
[Dependency] private readonly SharedAppearanceSystem _appearance = default!; [Dependency] private readonly SharedAppearanceSystem _appearance = default!;
[Dependency] private readonly SharedAudioSystem _audioSystem = default!; [Dependency] private readonly SharedAudioSystem _audioSystem = default!;
[Dependency] private readonly SharedPointLightSystem _light = default!; [Dependency] private readonly SharedPointLightSystem _light = default!;
[Dependency] private readonly EmagSystem _emag = default!;
public override void Initialize() public override void Initialize()
{ {
@@ -78,6 +80,9 @@ public sealed class UnpoweredFlashlightSystem : EntitySystem
private void OnGotEmagged(EntityUid uid, UnpoweredFlashlightComponent component, ref GotEmaggedEvent args) 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)) if (!_light.TryGetLight(uid, out var light))
return; return;

View File

@@ -54,9 +54,9 @@ public sealed partial class LockComponent : Component
/// <summary> /// <summary>
/// Whether or not an emag disables it. /// Whether or not an emag disables it.
/// </summary> /// </summary>
[DataField("breakOnEmag")] [DataField]
[AutoNetworkedField] [AutoNetworkedField]
public bool BreakOnEmag = true; public bool BreakOnAccessBreaker = true;
/// <summary> /// <summary>
/// Amount of do-after time needed to lock the entity. /// Amount of do-after time needed to lock the entity.

View File

@@ -28,6 +28,7 @@ public sealed class LockSystem : EntitySystem
[Dependency] private readonly AccessReaderSystem _accessReader = default!; [Dependency] private readonly AccessReaderSystem _accessReader = default!;
[Dependency] private readonly ActionBlockerSystem _actionBlocker = default!; [Dependency] private readonly ActionBlockerSystem _actionBlocker = default!;
[Dependency] private readonly ActivatableUISystem _activatableUI = default!; [Dependency] private readonly ActivatableUISystem _activatableUI = default!;
[Dependency] private readonly EmagSystem _emag = default!;
[Dependency] private readonly SharedAppearanceSystem _appearanceSystem = default!; [Dependency] private readonly SharedAppearanceSystem _appearanceSystem = default!;
[Dependency] private readonly SharedAudioSystem _audio = default!; [Dependency] private readonly SharedAudioSystem _audio = default!;
[Dependency] private readonly SharedPopupSystem _sharedPopupSystem = default!; [Dependency] private readonly SharedPopupSystem _sharedPopupSystem = default!;
@@ -295,7 +296,10 @@ public sealed class LockSystem : EntitySystem
private void OnEmagged(EntityUid uid, LockComponent component, ref GotEmaggedEvent args) 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; return;
_audio.PlayPredicted(component.UnlockSound, uid, args.UserUid); _audio.PlayPredicted(component.UnlockSound, uid, args.UserUid);
@@ -307,7 +311,7 @@ public sealed class LockSystem : EntitySystem
var ev = new LockToggledEvent(false); var ev = new LockToggledEvent(false);
RaiseLocalEvent(uid, ref ev, true); RaiseLocalEvent(uid, ref ev, true);
RemComp<LockComponent>(uid); //Literally destroys the lock as a tell it was emagged args.Repeatable = true;
args.Handled = true; args.Handled = true;
} }

View File

@@ -29,6 +29,7 @@ public abstract class SharedMaterialReclaimerSystem : EntitySystem
[Dependency] private readonly SharedAudioSystem _audio = default!; [Dependency] private readonly SharedAudioSystem _audio = default!;
[Dependency] protected readonly SharedContainerSystem Container = default!; [Dependency] protected readonly SharedContainerSystem Container = default!;
[Dependency] private readonly EntityWhitelistSystem _whitelistSystem = default!; [Dependency] private readonly EntityWhitelistSystem _whitelistSystem = default!;
[Dependency] private readonly EmagSystem _emag = default!;
public const string ActiveReclaimerContainerId = "active-material-reclaimer-container"; public const string ActiveReclaimerContainerId = "active-material-reclaimer-container";
@@ -60,6 +61,12 @@ public abstract class SharedMaterialReclaimerSystem : EntitySystem
private void OnEmagged(EntityUid uid, MaterialReclaimerComponent component, ref GotEmaggedEvent args) 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; args.Handled = true;
} }
@@ -207,7 +214,7 @@ public abstract class SharedMaterialReclaimerSystem : EntitySystem
component.Enabled && component.Enabled &&
!component.Broken && !component.Broken &&
HasComp<BodyComponent>(victim) && HasComp<BodyComponent>(victim) &&
HasComp<EmaggedComponent>(uid); _emag.CheckFlag(uid, EmagType.Interaction);
} }
/// <summary> /// <summary>

View File

@@ -20,6 +20,7 @@ public abstract partial class SharedCryoPodSystem: EntitySystem
{ {
[Dependency] private readonly SharedAppearanceSystem _appearanceSystem = default!; [Dependency] private readonly SharedAppearanceSystem _appearanceSystem = default!;
[Dependency] private readonly StandingStateSystem _standingStateSystem = default!; [Dependency] private readonly StandingStateSystem _standingStateSystem = default!;
[Dependency] private readonly EmagSystem _emag = default!;
[Dependency] private readonly MobStateSystem _mobStateSystem = default!; [Dependency] private readonly MobStateSystem _mobStateSystem = default!;
[Dependency] private readonly SharedPopupSystem _popupSystem = default!; [Dependency] private readonly SharedPopupSystem _popupSystem = default!;
[Dependency] private readonly SharedContainerSystem _containerSystem = default!; [Dependency] private readonly SharedContainerSystem _containerSystem = default!;
@@ -156,9 +157,13 @@ public abstract partial class SharedCryoPodSystem: EntitySystem
protected void OnEmagged(EntityUid uid, CryoPodComponent? cryoPodComponent, ref GotEmaggedEvent args) protected void OnEmagged(EntityUid uid, CryoPodComponent? cryoPodComponent, ref GotEmaggedEvent args)
{ {
if (!Resolve(uid, ref cryoPodComponent)) if (!Resolve(uid, ref cryoPodComponent))
{
return; return;
}
if (!_emag.CompareFlag(args.Type, EmagType.Interaction))
return;
if (cryoPodComponent.PermaLocked && cryoPodComponent.Locked)
return;
cryoPodComponent.PermaLocked = true; cryoPodComponent.PermaLocked = true;
cryoPodComponent.Locked = true; cryoPodComponent.Locked = true;

View File

@@ -1,6 +1,8 @@
using Content.Shared.Emag.Systems;
using Content.Shared.Ninja.Systems; using Content.Shared.Ninja.Systems;
using Content.Shared.Tag; using Content.Shared.Tag;
using Content.Shared.Whitelist; using Content.Shared.Whitelist;
using Robust.Shared.Audio;
using Robust.Shared.GameStates; using Robust.Shared.GameStates;
using Robust.Shared.Prototypes; using Robust.Shared.Prototypes;
@@ -17,11 +19,23 @@ public sealed partial class EmagProviderComponent : Component
/// The tag that marks an entity as immune to emagging. /// The tag that marks an entity as immune to emagging.
/// </summary> /// </summary>
[DataField] [DataField]
public ProtoId<TagPrototype> EmagImmuneTag = "EmagImmune"; public ProtoId<TagPrototype> AccessBreakerImmuneTag = "AccessBreakerImmune";
/// <summary> /// <summary>
/// Whitelist that entities must be on to work. /// Whitelist that entities must be on to work.
/// </summary> /// </summary>
[DataField] [DataField]
public EntityWhitelist? Whitelist; public EntityWhitelist? Whitelist;
/// <summary>
/// What type of emag this will provide.
/// </summary>
[DataField]
public EmagType EmagType = EmagType.Access;
/// <summary>
/// What sound should the emag play when used
/// </summary>
[DataField]
public SoundSpecifier EmagSound = new SoundCollectionSpecifier("sparks");
} }

View File

@@ -5,6 +5,7 @@ using Content.Shared.Interaction;
using Content.Shared.Ninja.Components; using Content.Shared.Ninja.Components;
using Content.Shared.Tag; using Content.Shared.Tag;
using Content.Shared.Whitelist; using Content.Shared.Whitelist;
using Robust.Shared.Audio.Systems;
namespace Content.Shared.Ninja.Systems; namespace Content.Shared.Ninja.Systems;
@@ -13,6 +14,7 @@ namespace Content.Shared.Ninja.Systems;
/// </summary> /// </summary>
public sealed class EmagProviderSystem : EntitySystem public sealed class EmagProviderSystem : EntitySystem
{ {
[Dependency] private readonly SharedAudioSystem _audio = default!;
[Dependency] private readonly EmagSystem _emag = default!; [Dependency] private readonly EmagSystem _emag = default!;
[Dependency] private readonly EntityWhitelistSystem _whitelist = default!; [Dependency] private readonly EntityWhitelistSystem _whitelist = default!;
[Dependency] private readonly ISharedAdminLogManager _adminLogger = default!; [Dependency] private readonly ISharedAdminLogManager _adminLogger = default!;
@@ -42,14 +44,18 @@ public sealed class EmagProviderSystem : EntitySystem
return; return;
// only allowed to emag non-immune entities // only allowed to emag non-immune entities
if (_tag.HasTag(target, comp.EmagImmuneTag)) if (_tag.HasTag(target, comp.AccessBreakerImmuneTag))
return; return;
var handled = _emag.DoEmagEffect(uid, target); var emagEv = new GotEmaggedEvent(uid, EmagType.Access);
if (!handled) RaiseLocalEvent(args.Target, ref emagEv);
if (!emagEv.Handled)
return; 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); var ev = new EmaggedSomethingEvent(target);
RaiseLocalEvent(uid, ref ev); RaiseLocalEvent(uid, ref ev);
args.Handled = true; args.Handled = true;
@@ -57,7 +63,7 @@ public sealed class EmagProviderSystem : EntitySystem
} }
/// <summary> /// <summary>
/// Raised on the player when emagging something. /// Raised on the player when access breaking something.
/// </summary> /// </summary>
[ByRefEvent] [ByRefEvent]
public record struct EmaggedSomethingEvent(EntityUid Target); public record struct EmaggedSomethingEvent(EntityUid Target);

View File

@@ -10,6 +10,7 @@ namespace Content.Shared.Pinpointer;
public abstract class SharedPinpointerSystem : EntitySystem public abstract class SharedPinpointerSystem : EntitySystem
{ {
[Dependency] private readonly ISharedAdminLogManager _adminLogger = default!; [Dependency] private readonly ISharedAdminLogManager _adminLogger = default!;
[Dependency] private readonly EmagSystem _emag = default!;
public override void Initialize() public override void Initialize()
{ {
@@ -137,6 +138,15 @@ public abstract class SharedPinpointerSystem : EntitySystem
private void OnEmagged(EntityUid uid, PinpointerComponent component, ref GotEmaggedEvent args) 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; args.Handled = true;
component.CanRetarget = true; component.CanRetarget = true;
} }

View File

@@ -16,13 +16,4 @@ public sealed partial class EmaggableMedibotComponent : Component
/// </summary> /// </summary>
[DataField(required: true), ViewVariables(VVAccess.ReadWrite)] [DataField(required: true), ViewVariables(VVAccess.ReadWrite)]
public Dictionary<MobState, MedibotTreatment> Replacements = new(); public Dictionary<MobState, MedibotTreatment> Replacements = new();
/// <summary>
/// Sound to play when the bot has been emagged
/// </summary>
[DataField]
public SoundSpecifier SparkSound = new SoundCollectionSpecifier("sparks")
{
Params = AudioParams.Default.WithVolume(8f)
};
} }

View File

@@ -20,6 +20,7 @@ namespace Content.Shared.Silicons.Bots;
public sealed class MedibotSystem : EntitySystem public sealed class MedibotSystem : EntitySystem
{ {
[Dependency] private readonly SharedAudioSystem _audio = default!; [Dependency] private readonly SharedAudioSystem _audio = default!;
[Dependency] private readonly EmagSystem _emag = default!;
[Dependency] private SharedInteractionSystem _interaction = default!; [Dependency] private SharedInteractionSystem _interaction = default!;
[Dependency] private SharedSolutionContainerSystem _solutionContainer = default!; [Dependency] private SharedSolutionContainerSystem _solutionContainer = default!;
[Dependency] private SharedPopupSystem _popup = default!; [Dependency] private SharedPopupSystem _popup = default!;
@@ -36,10 +37,14 @@ public sealed class MedibotSystem : EntitySystem
private void OnEmagged(EntityUid uid, EmaggableMedibotComponent comp, ref GotEmaggedEvent args) private void OnEmagged(EntityUid uid, EmaggableMedibotComponent comp, ref GotEmaggedEvent args)
{ {
if (!TryComp<MedibotComponent>(uid, out var medibot)) if (!_emag.CompareFlag(args.Type, EmagType.Interaction))
return; return;
_audio.PlayPredicted(comp.SparkSound, uid, args.UserUid); if (_emag.CheckFlag(uid, EmagType.Interaction))
return;
if (!TryComp<MedibotComponent>(uid, out var medibot))
return;
foreach (var (state, treatment) in comp.Replacements) foreach (var (state, treatment) in comp.Replacements)
{ {

View File

@@ -1,7 +1,10 @@
using Content.Shared.Emag.Systems; using Content.Shared.Emag.Systems;
using Content.Shared.Mind;
using Content.Shared.Popups; using Content.Shared.Popups;
using Content.Shared.Silicons.Laws.Components; using Content.Shared.Silicons.Laws.Components;
using Content.Shared.Stunnable;
using Content.Shared.Wires; using Content.Shared.Wires;
using Robust.Shared.Audio;
namespace Content.Shared.Silicons.Laws; namespace Content.Shared.Silicons.Laws;
@@ -11,22 +14,29 @@ namespace Content.Shared.Silicons.Laws;
public abstract partial class SharedSiliconLawSystem : EntitySystem public abstract partial class SharedSiliconLawSystem : EntitySystem
{ {
[Dependency] private readonly SharedPopupSystem _popup = default!; [Dependency] private readonly SharedPopupSystem _popup = default!;
[Dependency] private readonly SharedStunSystem _stunSystem = default!;
[Dependency] private readonly EmagSystem _emag = default!;
[Dependency] private readonly SharedMindSystem _mind = default!;
/// <inheritdoc/> /// <inheritdoc/>
public override void Initialize() public override void Initialize()
{ {
InitializeUpdater(); InitializeUpdater();
SubscribeLocalEvent<EmagSiliconLawComponent, GotEmaggedEvent>(OnGotEmagged); SubscribeLocalEvent<EmagSiliconLawComponent, GotEmaggedEvent>(OnGotEmagged);
SubscribeLocalEvent<EmagSiliconLawComponent, OnAttemptEmagEvent>(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) if (uid == args.UserUid)
{ {
_popup.PopupClient(Loc.GetString("law-emag-cannot-emag-self"), uid, args.UserUid); _popup.PopupClient(Loc.GetString("law-emag-cannot-emag-self"), uid, args.UserUid);
args.Handled = true;
return; return;
} }
@@ -35,14 +45,33 @@ public abstract partial class SharedSiliconLawSystem : EntitySystem
!panel.Open) !panel.Open)
{ {
_popup.PopupClient(Loc.GetString("law-emag-require-panel"), uid, args.UserUid); _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); 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; 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);

View File

@@ -11,6 +11,7 @@ public abstract class SharedSingularityGeneratorSystem : EntitySystem
{ {
#region Dependencies #region Dependencies
[Dependency] protected readonly SharedPopupSystem PopupSystem = default!; [Dependency] protected readonly SharedPopupSystem PopupSystem = default!;
[Dependency] private readonly EmagSystem _emag = default!;
#endregion Dependencies #endregion Dependencies
public override void Initialize() public override void Initialize()
@@ -22,6 +23,15 @@ public abstract class SharedSingularityGeneratorSystem : EntitySystem
private void OnEmagged(EntityUid uid, SingularityGeneratorComponent component, ref GotEmaggedEvent args) 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; component.FailsafeDisabled = true;
args.Handled = true; args.Handled = true;
} }

View File

@@ -2,6 +2,7 @@ using Content.Shared.Emag.Components;
using Robust.Shared.Prototypes; using Robust.Shared.Prototypes;
using System.Linq; using System.Linq;
using Content.Shared.DoAfter; using Content.Shared.DoAfter;
using Content.Shared.Emag.Systems;
using Content.Shared.Interaction; using Content.Shared.Interaction;
using Content.Shared.Popups; using Content.Shared.Popups;
using Robust.Shared.Audio; using Robust.Shared.Audio;
@@ -19,11 +20,14 @@ public abstract partial class SharedVendingMachineSystem : EntitySystem
[Dependency] private readonly SharedDoAfterSystem _doAfter = default!; [Dependency] private readonly SharedDoAfterSystem _doAfter = default!;
[Dependency] protected readonly SharedPopupSystem Popup = default!; [Dependency] protected readonly SharedPopupSystem Popup = default!;
[Dependency] protected readonly IRobustRandom Randomizer = default!; [Dependency] protected readonly IRobustRandom Randomizer = default!;
[Dependency] private readonly EmagSystem _emag = default!;
public override void Initialize() public override void Initialize()
{ {
base.Initialize(); base.Initialize();
SubscribeLocalEvent<VendingMachineComponent, MapInitEvent>(OnMapInit); SubscribeLocalEvent<VendingMachineComponent, MapInitEvent>(OnMapInit);
SubscribeLocalEvent<VendingMachineComponent, GotEmaggedEvent>(OnEmagged);
SubscribeLocalEvent<VendingMachineRestockComponent, AfterInteractEvent>(OnAfterInteract); SubscribeLocalEvent<VendingMachineRestockComponent, AfterInteractEvent>(OnAfterInteract);
} }
@@ -49,9 +53,21 @@ public abstract partial class SharedVendingMachineSystem : EntitySystem
Dirty(uid, component); 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;
}
/// <summary> /// <summary>
/// Returns all of the vending machine's inventory. Only includes emagged and contraband inventories if /// Returns all of the vending machine's inventory. Only includes emagged and contraband inventories if
/// <see cref="EmaggedComponent"/> exists and <see cref="VendingMachineComponent.Contraband"/> is true /// <see cref="EmaggedComponent"/> with the EmagType.Interaction flag exists and <see cref="VendingMachineComponent.Contraband"/> is true
/// are <c>true</c> respectively. /// are <c>true</c> respectively.
/// </summary> /// </summary>
/// <param name="uid"></param> /// <param name="uid"></param>
@@ -64,7 +80,7 @@ public abstract partial class SharedVendingMachineSystem : EntitySystem
var inventory = new List<VendingMachineInventoryEntry>(component.Inventory.Values); var inventory = new List<VendingMachineInventoryEntry>(component.Inventory.Values);
if (HasComp<EmaggedComponent>(uid)) if (_emag.CheckFlag(uid, EmagType.Interaction))
inventory.AddRange(component.EmaggedInventory.Values); inventory.AddRange(component.EmaggedInventory.Values);
if (component.Contraband) if (component.Contraband)

View File

@@ -14,6 +14,7 @@ public abstract class SharedArtifactCrusherSystem : EntitySystem
[Dependency] protected readonly SharedAppearanceSystem Appearance = default!; [Dependency] protected readonly SharedAppearanceSystem Appearance = default!;
[Dependency] protected readonly SharedAudioSystem AudioSystem = default!; [Dependency] protected readonly SharedAudioSystem AudioSystem = default!;
[Dependency] protected readonly SharedContainerSystem ContainerSystem = default!; [Dependency] protected readonly SharedContainerSystem ContainerSystem = default!;
[Dependency] private readonly EmagSystem _emag = default!;
/// <inheritdoc/> /// <inheritdoc/>
public override void Initialize() public override void Initialize()
@@ -40,6 +41,15 @@ public abstract class SharedArtifactCrusherSystem : EntitySystem
private void OnEmagged(Entity<ArtifactCrusherComponent> ent, ref GotEmaggedEvent args) private void OnEmagged(Entity<ArtifactCrusherComponent> 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; ent.Comp.AutoLock = true;
args.Handled = true; args.Handled = true;
} }

View File

@@ -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! emag-no-charges = No charges left!

View File

@@ -113,7 +113,10 @@ uplink-chest-rig-name = Chest Rig
uplink-chest-rig-desc = Explosion-resistant tactical webbing used for holding traitor goods. uplink-chest-rig-desc = Explosion-resistant tactical webbing used for holding traitor goods.
uplink-emag-name = Emag 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-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. 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.

View File

@@ -39,7 +39,7 @@ thief-backpack-category-syndie-name = syndie kit
thief-backpack-category-syndie-description = thief-backpack-category-syndie-description =
Trinkets from a disavowed past, or stolen from a careless agent? Trinkets from a disavowed past, or stolen from a careless agent?
You've made some connections. Whiskey, echo... 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. a Radio Jammer, a lighter and some strange red crystals.
thief-backpack-category-sleeper-name = sleeper kit thief-backpack-category-sleeper-name = sleeper kit

View File

@@ -63,6 +63,7 @@
- RadioJammer - RadioJammer
- TraitorCodePaper - TraitorCodePaper
- Emag - Emag
- AccessBreaker
- Lighter - Lighter
- CigPackSyndicate - CigPackSyndicate
- Telecrystal10 #The thief cannot use them, but it may induce communication with traitors - Telecrystal10 #The thief cannot use them, but it may induce communication with traitors

View File

@@ -865,16 +865,29 @@
# Disruption # 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 - type: listing
id: UplinkEmag id: UplinkEmag
name: uplink-emag-name name: uplink-emag-name
description: uplink-emag-desc description: uplink-emag-desc
productEntity: Emag productEntity: Emag
discountCategory: veryRareDiscounts discountCategory: rareDiscounts
discountDownTo: discountDownTo:
Telecrystal: 5 Telecrystal: 2
cost: cost:
Telecrystal: 8 Telecrystal: 4
categories: categories:
- UplinkDisruption - UplinkDisruption

View File

@@ -167,7 +167,6 @@
- cell_slot - cell_slot
- type: Lock - type: Lock
locked: true locked: true
breakOnEmag: false
unlockOnClick: false unlockOnClick: false
- type: ActivatableUIRequiresLock - type: ActivatableUIRequiresLock
- type: LockedWiresPanel - type: LockedWiresPanel

View File

@@ -7,10 +7,10 @@
- type: Appearance - type: Appearance
- type: AccessReader - type: AccessReader
access: [ [ "CentralCommand" ] ] access: [ [ "CentralCommand" ] ]
breakOnEmag: false breakOnAccessBreaker: false
- type: Lock - type: Lock
lockOnClick: false lockOnClick: false
breakOnEmag: false breakOnAccessBreaker: false
- type: EntityStorage - type: EntityStorage
capacity: 1 # Its smol. 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. 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.

View File

@@ -570,7 +570,7 @@
id: BorgModuleOperative id: BorgModuleOperative
parent: [ BaseBorgModuleSyndicate, BaseProviderBorgModule, BaseSyndicateContraband ] parent: [ BaseBorgModuleSyndicate, BaseProviderBorgModule, BaseSyndicateContraband ]
name: operative cyborg module 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: components:
- type: Sprite - type: Sprite
layers: layers:
@@ -580,6 +580,7 @@
items: items:
- Crowbar - Crowbar
- Emag - Emag
- AccessBreaker
- PinpointerSyndicateNuclear - PinpointerSyndicateNuclear
- type: BorgModuleIcon - type: BorgModuleIcon
icon: { sprite: Interface/Actions/actions_borg.rsi, state: syndicate-operative-module } icon: { sprite: Interface/Actions/actions_borg.rsi, state: syndicate-operative-module }

View File

@@ -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

View File

@@ -98,10 +98,10 @@
stateLocked: cursed_door stateLocked: cursed_door
stateUnlocked: decursed_door stateUnlocked: decursed_door
- type: Lock - type: Lock
breakOnEmag: false breakOnAccessBreaker: false
- type: AccessReader - type: AccessReader
access: [["Wizard"]] access: [["Wizard"]]
breakOnEmag: false breakOnAccessBreaker: false
- type: Projectile - type: Projectile
deleteOnCollide: false deleteOnCollide: false
onlyCollideWhenShot: true onlyCollideWhenShot: true

View File

@@ -621,7 +621,7 @@
color: "#3c5eb5" color: "#3c5eb5"
- type: Tag - type: Tag
tags: tags:
- EmagImmune - AccessBreakerImmune
- type: ItemSlots - type: ItemSlots
- type: ContainerContainer - type: ContainerContainer
containers: containers:

View File

@@ -35,7 +35,7 @@
visible: false visible: false
map: ["enum.FatExtractorVisualLayers.Smoke"] map: ["enum.FatExtractorVisualLayers.Smoke"]
- type: Lock - type: Lock
breakOnEmag: false breakOnAccessBreaker: false
- type: GenericVisualizer - type: GenericVisualizer
visuals: visuals:
enum.StorageVisuals.Open: enum.StorageVisuals.Open:

View File

@@ -101,6 +101,7 @@
responsePings: false responsePings: false
notifyAdmins: true notifyAdmins: true
- type: Emagged - type: Emagged
emagType: Interaction
- type: entity - type: entity
parent: FaxMachineBase parent: FaxMachineBase

View File

@@ -17,7 +17,7 @@
- type: ActivatableUI - type: ActivatableUI
key: enum.CryostorageUIKey.Key key: enum.CryostorageUIKey.Key
- type: AccessReader - type: AccessReader
breakOnEmag: false breakOnAccessBreaker: false
access: [["Cryogenics"]] access: [["Cryogenics"]]
- type: InteractionOutline - type: InteractionOutline
- type: Cryostorage - type: Cryostorage

View File

@@ -1,5 +1,8 @@
# PUT YOUR TAGS IN ALPHABETICAL ORDER # PUT YOUR TAGS IN ALPHABETICAL ORDER
- type: Tag
id: AccessBreakerImmune
- type: Tag - type: Tag
id: AirAlarm id: AirAlarm

Binary file not shown.

After

Width:  |  Height:  |  Size: 141 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.7 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 353 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 383 B

View File

@@ -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"
}
]
}