diff --git a/Content.Client/Silicons/StationAi/StationAiSystem.Airlock.cs b/Content.Client/Silicons/StationAi/StationAiSystem.Airlock.cs index bf6b65a969..d5bc764b34 100644 --- a/Content.Client/Silicons/StationAi/StationAiSystem.Airlock.cs +++ b/Content.Client/Silicons/StationAi/StationAiSystem.Airlock.cs @@ -1,4 +1,5 @@ using Content.Shared.Doors.Components; +using Content.Shared.Electrocution; using Content.Shared.Silicons.StationAi; using Robust.Shared.Utility; @@ -6,25 +7,69 @@ namespace Content.Client.Silicons.StationAi; public sealed partial class StationAiSystem { + private readonly ResPath _aiActionsRsi = new ResPath("/Textures/Interface/Actions/actions_ai.rsi"); + private void InitializeAirlock() { SubscribeLocalEvent(OnDoorBoltGetRadial); + SubscribeLocalEvent(OnEmergencyAccessGetRadial); + SubscribeLocalEvent(OnDoorElectrifiedGetRadial); } private void OnDoorBoltGetRadial(Entity ent, ref GetStationAiRadialEvent args) { - args.Actions.Add(new StationAiRadial() - { - Sprite = ent.Comp.BoltsDown ? - new SpriteSpecifier.Rsi( - new ResPath("/Textures/Structures/Doors/Airlocks/Standard/basic.rsi"), "open") : - new SpriteSpecifier.Rsi( - new ResPath("/Textures/Structures/Doors/Airlocks/Standard/basic.rsi"), "closed"), - Tooltip = ent.Comp.BoltsDown ? Loc.GetString("bolt-open") : Loc.GetString("bolt-close"), - Event = new StationAiBoltEvent() + args.Actions.Add( + new StationAiRadial + { + Sprite = ent.Comp.BoltsDown + ? new SpriteSpecifier.Rsi(_aiActionsRsi, "unbolt_door") + : new SpriteSpecifier.Rsi(_aiActionsRsi, "bolt_door"), + Tooltip = ent.Comp.BoltsDown + ? Loc.GetString("bolt-open") + : Loc.GetString("bolt-close"), + Event = new StationAiBoltEvent + { + Bolted = !ent.Comp.BoltsDown, + } + } + ); + } + + private void OnEmergencyAccessGetRadial(Entity ent, ref GetStationAiRadialEvent args) + { + args.Actions.Add( + new StationAiRadial + { + Sprite = ent.Comp.EmergencyAccess + ? new SpriteSpecifier.Rsi(_aiActionsRsi, "emergency_off") + : new SpriteSpecifier.Rsi(_aiActionsRsi, "emergency_on"), + Tooltip = ent.Comp.EmergencyAccess + ? Loc.GetString("emergency-access-off") + : Loc.GetString("emergency-access-on"), + Event = new StationAiEmergencyAccessEvent + { + EmergencyAccess = !ent.Comp.EmergencyAccess, + } + } + ); + } + + private void OnDoorElectrifiedGetRadial(Entity ent, ref GetStationAiRadialEvent args) + { + args.Actions.Add( + new StationAiRadial { - Bolted = !ent.Comp.BoltsDown, + Sprite = ent.Comp.Enabled + ? new SpriteSpecifier.Rsi(_aiActionsRsi, "door_overcharge_off") + : new SpriteSpecifier.Rsi(_aiActionsRsi, "door_overcharge_on"), + Tooltip = ent.Comp.Enabled + ? Loc.GetString("electrify-door-off") + : Loc.GetString("electrify-door-on"), + Event = new StationAiElectrifiedEvent + { + Electrified = !ent.Comp.Enabled, + } } - }); + ); } } diff --git a/Content.Server/Access/LogWireAction.cs b/Content.Server/Access/LogWireAction.cs new file mode 100644 index 0000000000..1e97d5c9d6 --- /dev/null +++ b/Content.Server/Access/LogWireAction.cs @@ -0,0 +1,74 @@ +using Content.Server.Wires; +using Content.Shared.Access; +using Content.Shared.Access.Components; +using Content.Shared.Access.Systems; +using Content.Shared.Emag.Components; +using Content.Shared.Wires; + +namespace Content.Server.Access; + +public sealed partial class LogWireAction : ComponentWireAction +{ + public override Color Color { get; set; } = Color.Blue; + public override string Name { get; set; } = "wire-name-log"; + + [DataField] + public int PulseTimeout = 30; + + [DataField] + public LocId PulseLog = "log-wire-pulse-access-log"; + + private AccessReaderSystem _access = default!; + + public override StatusLightState? GetLightState(Wire wire, AccessReaderComponent comp) + { + return comp.LoggingDisabled ? StatusLightState.Off : StatusLightState.On; + } + + public override object StatusKey => AccessWireActionKey.Status; + + public override void Initialize() + { + base.Initialize(); + + _access = EntityManager.System(); + } + + public override bool Cut(EntityUid user, Wire wire, AccessReaderComponent comp) + { + WiresSystem.TryCancelWireAction(wire.Owner, PulseTimeoutKey.Key); + comp.LoggingDisabled = true; + EntityManager.Dirty(wire.Owner, comp); + return true; + } + + public override bool Mend(EntityUid user, Wire wire, AccessReaderComponent comp) + { + comp.LoggingDisabled = false; + return true; + } + + public override void Pulse(EntityUid user, Wire wire, AccessReaderComponent comp) + { + _access.LogAccess((wire.Owner, comp), Loc.GetString(PulseLog)); + comp.LoggingDisabled = true; + WiresSystem.StartWireAction(wire.Owner, PulseTimeout, PulseTimeoutKey.Key, new TimedWireEvent(AwaitPulseCancel, wire)); + } + + public override void Update(Wire wire) + { + if (!IsPowered(wire.Owner)) + WiresSystem.TryCancelWireAction(wire.Owner, PulseTimeoutKey.Key); + } + + private void AwaitPulseCancel(Wire wire) + { + if (!wire.IsCut && EntityManager.TryGetComponent(wire.Owner, out var comp)) + comp.LoggingDisabled = false; + } + + private enum PulseTimeoutKey : byte + { + Key + } +} diff --git a/Content.Server/Administration/Systems/AdminVerbSystem.Tools.cs b/Content.Server/Administration/Systems/AdminVerbSystem.Tools.cs index 3aa1c20cb0..de4f7910c7 100644 --- a/Content.Server/Administration/Systems/AdminVerbSystem.Tools.cs +++ b/Content.Server/Administration/Systems/AdminVerbSystem.Tools.cs @@ -90,22 +90,22 @@ private void AddTricksVerbs(GetVerbsEvent args) args.Verbs.Add(bolt); } - if (TryComp(args.Target, out var airlock)) + if (TryComp(args.Target, out var airlockComp)) { Verb emergencyAccess = new() { - Text = airlock.EmergencyAccess ? "Emergency Access Off" : "Emergency Access On", + Text = airlockComp.EmergencyAccess ? "Emergency Access Off" : "Emergency Access On", Category = VerbCategory.Tricks, Icon = new SpriteSpecifier.Texture(new("/Textures/Interface/AdminActions/emergency_access.png")), Act = () => { - _airlockSystem.ToggleEmergencyAccess(args.Target, airlock); + _airlockSystem.SetEmergencyAccess((args.Target, airlockComp), !airlockComp.EmergencyAccess); }, Impact = LogImpact.Medium, - Message = Loc.GetString(airlock.EmergencyAccess + Message = Loc.GetString(airlockComp.EmergencyAccess ? "admin-trick-emergency-access-off-description" : "admin-trick-emergency-access-on-description"), - Priority = (int) (airlock.EmergencyAccess ? TricksVerbPriorities.EmergencyAccessOff : TricksVerbPriorities.EmergencyAccessOn), + Priority = (int) (airlockComp.EmergencyAccess ? TricksVerbPriorities.EmergencyAccessOff : TricksVerbPriorities.EmergencyAccessOn), }; args.Verbs.Add(emergencyAccess); } diff --git a/Content.Server/Construction/Completions/AttemptElectrocute.cs b/Content.Server/Construction/Completions/AttemptElectrocute.cs index 05f0977b66..5c97d5e90f 100644 --- a/Content.Server/Construction/Completions/AttemptElectrocute.cs +++ b/Content.Server/Construction/Completions/AttemptElectrocute.cs @@ -1,4 +1,5 @@ using Content.Server.Electrocution; +using Content.Shared.Electrocution; using Content.Shared.Construction; namespace Content.Server.Construction.Completions; diff --git a/Content.Server/Doors/WireActions/DoorBoltWireAction.cs b/Content.Server/Doors/WireActions/DoorBoltWireAction.cs index fc1cf50cd8..80555f68f9 100644 --- a/Content.Server/Doors/WireActions/DoorBoltWireAction.cs +++ b/Content.Server/Doors/WireActions/DoorBoltWireAction.cs @@ -2,7 +2,6 @@ using Content.Server.Wires; using Content.Shared.Doors; using Content.Shared.Doors.Components; -using Content.Shared.Doors.Systems; using Content.Shared.Wires; namespace Content.Server.Doors; diff --git a/Content.Server/Electrocution/Components/ElectrifiedComponent.cs b/Content.Server/Electrocution/Components/ElectrifiedComponent.cs deleted file mode 100644 index 4ef07a0cca..0000000000 --- a/Content.Server/Electrocution/Components/ElectrifiedComponent.cs +++ /dev/null @@ -1,91 +0,0 @@ -using Robust.Shared.Audio; - -namespace Content.Server.Electrocution; - -/// -/// Component for things that shock users on touch. -/// -[RegisterComponent] -public sealed partial class ElectrifiedComponent : Component -{ - [DataField("enabled")] - public bool Enabled = true; - - [DataField("onBump")] - public bool OnBump = true; - - [DataField("onAttacked")] - public bool OnAttacked = true; - - [DataField("noWindowInTile")] - public bool NoWindowInTile = false; - - [DataField("onHandInteract")] - public bool OnHandInteract = true; - - [DataField("onInteractUsing")] - public bool OnInteractUsing = true; - - [DataField("requirePower")] - public bool RequirePower = true; - - [DataField("usesApcPower")] - public bool UsesApcPower = false; - - [DataField("highVoltageNode")] - public string? HighVoltageNode; - - [DataField("mediumVoltageNode")] - public string? MediumVoltageNode; - - [DataField("lowVoltageNode")] - public string? LowVoltageNode; - - /// - /// Damage multiplier for HV electrocution - /// - [DataField] - public float HighVoltageDamageMultiplier = 3f; - - /// - /// Shock time multiplier for HV electrocution - /// - [DataField] - public float HighVoltageTimeMultiplier = 1.5f; - - /// - /// Damage multiplier for MV electrocution - /// - [DataField] - public float MediumVoltageDamageMultiplier = 2f; - - /// - /// Shock time multiplier for MV electrocution - /// - [DataField] - public float MediumVoltageTimeMultiplier = 1.25f; - - [DataField("shockDamage")] - public float ShockDamage = 7.5f; - - /// - /// Shock time, in seconds. - /// - [DataField("shockTime")] - public float ShockTime = 8f; - - [DataField("siemensCoefficient")] - public float SiemensCoefficient = 1f; - - [DataField("shockNoises")] - public SoundSpecifier ShockNoises = new SoundCollectionSpecifier("sparks"); - - [DataField("playSoundOnShock")] - public bool PlaySoundOnShock = true; - - [DataField("shockVolume")] - public float ShockVolume = 20; - - [DataField] - public float Probability = 1f; -} diff --git a/Content.Server/Electrocution/ElectrocutionSystem.cs b/Content.Server/Electrocution/ElectrocutionSystem.cs index ee1965a91b..e8dc64e185 100644 --- a/Content.Server/Electrocution/ElectrocutionSystem.cs +++ b/Content.Server/Electrocution/ElectrocutionSystem.cs @@ -490,4 +490,15 @@ private void PlayElectrocutionSound(EntityUid targetUid, EntityUid sourceUid, El } _audio.PlayPvs(electrified.ShockNoises, targetUid, AudioParams.Default.WithVolume(electrified.ShockVolume)); } + + public void SetElectrifiedWireCut(Entity ent, bool value) + { + if (ent.Comp.IsWireCut == value) + { + return; + } + + ent.Comp.IsWireCut = value; + Dirty(ent); + } } diff --git a/Content.Server/Power/PowerWireAction.cs b/Content.Server/Power/PowerWireAction.cs index 374c1c41ac..43d1de6e4d 100644 --- a/Content.Server/Power/PowerWireAction.cs +++ b/Content.Server/Power/PowerWireAction.cs @@ -1,4 +1,5 @@ using Content.Server.Electrocution; +using Content.Shared.Electrocution; using Content.Server.Power.Components; using Content.Server.Wires; using Content.Shared.Emp; @@ -108,6 +109,7 @@ private void SetElectrified(EntityUid used, bool setting, ElectrifiedComponent? && !EntityManager.TryGetComponent(used, out electrified)) return; + _electrocutionSystem.SetElectrifiedWireCut((used, electrified), setting); electrified.Enabled = setting; } diff --git a/Content.Server/Remotes/DoorRemoteSystem.cs b/Content.Server/Remotes/DoorRemoteSystem.cs index 31fcacdaf8..986fb1071f 100644 --- a/Content.Server/Remotes/DoorRemoteSystem.cs +++ b/Content.Server/Remotes/DoorRemoteSystem.cs @@ -82,7 +82,7 @@ private void OnBeforeInteract(Entity entity, ref BeforeRang case OperatingMode.ToggleEmergencyAccess: if (airlockComp != null) { - _airlock.ToggleEmergencyAccess(args.Target.Value, airlockComp); + _airlock.SetEmergencyAccess((args.Target.Value, airlockComp), !airlockComp.EmergencyAccess); _adminLogger.Add(LogType.Action, LogImpact.Medium, $"{ToPrettyString(args.User):player} used {ToPrettyString(args.Used)} on {ToPrettyString(args.Target.Value)} to set emergency access {(airlockComp.EmergencyAccess ? "on" : "off")}"); } diff --git a/Content.Shared/Access/Components/AccessReaderComponent.cs b/Content.Shared/Access/Components/AccessReaderComponent.cs index cbd3f5acd6..903ceab186 100644 --- a/Content.Shared/Access/Components/AccessReaderComponent.cs +++ b/Content.Shared/Access/Components/AccessReaderComponent.cs @@ -65,6 +65,13 @@ public sealed partial class AccessReaderComponent : Component [DataField, ViewVariables(VVAccess.ReadWrite)] public int AccessLogLimit = 20; + /// + /// If true logging on successful access uses will be disabled. + /// Can be set by LOG wire. + /// + [DataField] + public bool LoggingDisabled; + /// /// Whether or not emag interactions have an effect on this. /// diff --git a/Content.Shared/Access/Systems/AccessReaderSystem.cs b/Content.Shared/Access/Systems/AccessReaderSystem.cs index dd0bf7bde2..181af53893 100644 --- a/Content.Shared/Access/Systems/AccessReaderSystem.cs +++ b/Content.Shared/Access/Systems/AccessReaderSystem.cs @@ -6,6 +6,7 @@ using Content.Shared.Emag.Systems; using Content.Shared.Hands.EntitySystems; using Content.Shared.Inventory; +using Content.Shared.NameIdentifier; using Content.Shared.PDA; using Content.Shared.StationRecords; using Robust.Shared.Containers; @@ -380,19 +381,19 @@ private bool FindStationRecordKeyItem(EntityUid uid, [NotNullWhen(true)] out Sta } /// - /// Logs an access + /// Logs an access for a specific entity. /// /// The reader to log the access on /// The accessor to log - private void LogAccess(Entity ent, EntityUid accessor) + public void LogAccess(Entity ent, EntityUid accessor) { - if (IsPaused(ent)) + if (IsPaused(ent) || ent.Comp.LoggingDisabled) return; - if (ent.Comp.AccessLog.Count >= ent.Comp.AccessLogLimit) - ent.Comp.AccessLog.Dequeue(); - string? name = null; + if (TryComp(accessor, out var nameIdentifier)) + name = nameIdentifier.FullIdentifier; + // TODO pass the ID card on IsAllowed() instead of using this expensive method // Set name if the accessor has a card and that card has a name and allows itself to be recorded var getIdentityShortInfoEvent = new TryGetIdentityShortInfoEvent(ent, accessor, true); @@ -402,7 +403,21 @@ private void LogAccess(Entity ent, EntityUid accessor) name = getIdentityShortInfoEvent.Title; } - name ??= Loc.GetString("access-reader-unknown-id"); + LogAccess(ent, name ?? Loc.GetString("access-reader-unknown-id")); + } + + /// + /// Logs an access with a predetermined name + /// + /// The reader to log the access on + /// The name to log as + public void LogAccess(Entity ent, string name) + { + if (IsPaused(ent) || ent.Comp.LoggingDisabled) + return; + + if (ent.Comp.AccessLog.Count >= ent.Comp.AccessLogLimit) + ent.Comp.AccessLog.Dequeue(); var stationTime = _gameTiming.CurTime.Subtract(_gameTicker.RoundStartTimeSpan); ent.Comp.AccessLog.Enqueue(new AccessRecord(stationTime, name)); diff --git a/Content.Shared/Access/Systems/SharedIdCardSystem.cs b/Content.Shared/Access/Systems/SharedIdCardSystem.cs index c031902b6e..7a79070bd8 100644 --- a/Content.Shared/Access/Systems/SharedIdCardSystem.cs +++ b/Content.Shared/Access/Systems/SharedIdCardSystem.cs @@ -239,7 +239,7 @@ private void UpdateEntityName(EntityUid uid, IdCardComponent? id = null) private static string ExtractFullTitle(IdCardComponent idCardComponent) { - return $"{idCardComponent.FullName} ({CultureInfo.CurrentCulture.TextInfo.ToTitleCase(idCardComponent.LocalizedJobTitle ?? string.Empty)})" + return $"{idCardComponent.FullName} ({CultureInfo.CurrentCulture.TextInfo.ToTitleCase(idCardComponent.JobTitle ?? string.Empty)})" .Trim(); } } diff --git a/Content.Shared/Doors/Components/AirlockComponent.cs b/Content.Shared/Doors/Components/AirlockComponent.cs index b2fa7574f7..e5794f186f 100644 --- a/Content.Shared/Doors/Components/AirlockComponent.cs +++ b/Content.Shared/Doors/Components/AirlockComponent.cs @@ -1,5 +1,6 @@ using Content.Shared.DeviceLinking; using Content.Shared.Doors.Systems; +using Robust.Shared.Audio; using Robust.Shared.GameStates; using Robust.Shared.Serialization.TypeSerializers.Implementations.Custom.Prototype; @@ -23,6 +24,18 @@ public sealed partial class AirlockComponent : Component [ViewVariables(VVAccess.ReadWrite)] [DataField, AutoNetworkedField] public bool EmergencyAccess = false; + + /// + /// Sound to play when the airlock emergency access is turned on. + /// + [DataField] + public SoundSpecifier EmergencyOnSound = new SoundPathSpecifier("/Audio/Machines/airlock_emergencyon.ogg"); + + /// + /// Sound to play when the airlock emergency access is turned off. + /// + [DataField] + public SoundSpecifier EmergencyOffSound = new SoundPathSpecifier("/Audio/Machines/airlock_emergencyoff.ogg"); /// /// Pry modifier for a powered airlock. diff --git a/Content.Shared/Doors/Systems/SharedAirlockSystem.cs b/Content.Shared/Doors/Systems/SharedAirlockSystem.cs index 5a6d45d9ec..72e9428986 100644 --- a/Content.Shared/Doors/Systems/SharedAirlockSystem.cs +++ b/Content.Shared/Doors/Systems/SharedAirlockSystem.cs @@ -1,4 +1,5 @@ using Content.Shared.Doors.Components; +using Robust.Shared.Audio.Systems; using Content.Shared.Popups; using Content.Shared.Prying.Components; using Content.Shared.Wires; @@ -8,6 +9,7 @@ namespace Content.Shared.Doors.Systems; public abstract class SharedAirlockSystem : EntitySystem { [Dependency] protected readonly SharedAppearanceSystem Appearance = default!; + [Dependency] protected readonly SharedAudioSystem Audio = default!; [Dependency] protected readonly SharedDoorSystem DoorSystem = default!; [Dependency] protected readonly SharedPopupSystem Popup = default!; [Dependency] private readonly SharedWiresSystem _wiresSystem = default!; @@ -123,11 +125,23 @@ public void UpdateEmergencyLightStatus(EntityUid uid, AirlockComponent component Appearance.SetData(uid, DoorVisuals.EmergencyLights, component.EmergencyAccess); } - public void ToggleEmergencyAccess(EntityUid uid, AirlockComponent component) + public void SetEmergencyAccess(Entity ent, bool value, EntityUid? user = null, bool predicted = false) { - component.EmergencyAccess = !component.EmergencyAccess; - Dirty(uid, component); // This only runs on the server apparently so we need this. - UpdateEmergencyLightStatus(uid, component); + if(!ent.Comp.Powered) + return; + + if (ent.Comp.EmergencyAccess == value) + return; + + ent.Comp.EmergencyAccess = value; + Dirty(ent, ent.Comp); // This only runs on the server apparently so we need this. + UpdateEmergencyLightStatus(ent, ent.Comp); + + var sound = ent.Comp.EmergencyAccess ? ent.Comp.EmergencyOnSound : ent.Comp.EmergencyOffSound; + if (predicted) + Audio.PlayPredicted(sound, ent, user: user); + else + Audio.PlayPvs(sound, ent); } public void SetAutoCloseDelayModifier(AirlockComponent component, float value) diff --git a/Content.Shared/Doors/Systems/SharedDoorSystem.Bolts.cs b/Content.Shared/Doors/Systems/SharedDoorSystem.Bolts.cs index 35681bfd82..13050616e1 100644 --- a/Content.Shared/Doors/Systems/SharedDoorSystem.Bolts.cs +++ b/Content.Shared/Doors/Systems/SharedDoorSystem.Bolts.cs @@ -77,8 +77,20 @@ public void SetBoltLightsEnabled(Entity ent, bool value) public void SetBoltsDown(Entity ent, bool value, EntityUid? user = null, bool predicted = false) { + TrySetBoltDown(ent, value, user, predicted); + } + + public bool TrySetBoltDown( + Entity ent, + bool value, + EntityUid? user = null, + bool predicted = false + ) + { + if (!_powerReceiver.IsPowered(ent.Owner)) + return false; if (ent.Comp.BoltsDown == value) - return; + return false; ent.Comp.BoltsDown = value; Dirty(ent, ent.Comp); @@ -89,6 +101,7 @@ public void SetBoltsDown(Entity ent, bool value, EntityUid? u Audio.PlayPredicted(sound, ent, user: user); else Audio.PlayPvs(sound, ent); + return true; } private void OnStateChanged(Entity entity, ref DoorStateChangedEvent args) diff --git a/Content.Shared/Doors/Systems/SharedDoorSystem.cs b/Content.Shared/Doors/Systems/SharedDoorSystem.cs index 4e493f158b..c62d64889f 100644 --- a/Content.Shared/Doors/Systems/SharedDoorSystem.cs +++ b/Content.Shared/Doors/Systems/SharedDoorSystem.cs @@ -10,6 +10,7 @@ using Content.Shared.Interaction; using Content.Shared.Physics; using Content.Shared.Popups; +using Content.Shared.Power.EntitySystems; using Content.Shared.Prying.Components; using Content.Shared.Prying.Systems; using Content.Shared.Stunnable; @@ -41,6 +42,9 @@ public abstract partial class SharedDoorSystem : EntitySystem [Dependency] private readonly AccessReaderSystem _accessReaderSystem = default!; [Dependency] private readonly PryingSystem _pryingSystem = default!; [Dependency] protected readonly SharedPopupSystem Popup = default!; + [Dependency] private readonly SharedMapSystem _mapSystem = default!; + [Dependency] private readonly SharedPowerReceiverSystem _powerReceiver = default!; + [ValidatePrototypeId] public const string DoorBumpTag = "DoorBumpOpener"; diff --git a/Content.Shared/Electrocution/Components/ElectrifiedComponent.cs b/Content.Shared/Electrocution/Components/ElectrifiedComponent.cs new file mode 100644 index 0000000000..f7fe92b826 --- /dev/null +++ b/Content.Shared/Electrocution/Components/ElectrifiedComponent.cs @@ -0,0 +1,131 @@ +using Robust.Shared.GameStates; +using Robust.Shared.Audio; + +namespace Content.Shared.Electrocution; + +/// +/// Component for things that shock users on touch. +/// +[RegisterComponent, NetworkedComponent, AutoGenerateComponentState] +public sealed partial class ElectrifiedComponent : Component +{ + [DataField, AutoNetworkedField] + public bool Enabled = true; + + /// + /// Should player get damage on collide + /// + [DataField, AutoNetworkedField] + public bool OnBump = true; + + /// + /// Should player get damage on attack + /// + [DataField, AutoNetworkedField] + public bool OnAttacked = true; + + /// + /// When true - disables power if a window is present in the same tile + /// + [DataField, AutoNetworkedField] + public bool NoWindowInTile = false; + + /// + /// Should player get damage on interact with empty hand + /// + [DataField, AutoNetworkedField] + public bool OnHandInteract = true; + + /// + /// Should player get damage on interact while holding an object in their hand + /// + [DataField, AutoNetworkedField] + public bool OnInteractUsing = true; + + /// + /// Indicates if the entity requires power to function + /// + [DataField, AutoNetworkedField] + public bool RequirePower = true; + + /// + /// Indicates if the entity uses APC power + /// + [DataField, AutoNetworkedField] + public bool UsesApcPower = false; + + /// + /// Identifier for the high voltage node. + /// + [DataField, AutoNetworkedField] + public string? HighVoltageNode; + + /// + /// Identifier for the medium voltage node. + /// + [DataField, AutoNetworkedField] + public string? MediumVoltageNode; + + /// + /// Identifier for the low voltage node. + /// + [DataField, AutoNetworkedField] + public string? LowVoltageNode; + + /// + /// Damage multiplier for HV electrocution + /// + [DataField, AutoNetworkedField] + public float HighVoltageDamageMultiplier = 3f; + + /// + /// Shock time multiplier for HV electrocution + /// + [DataField, AutoNetworkedField] + public float HighVoltageTimeMultiplier = 1.5f; + + /// + /// Damage multiplier for MV electrocution + /// + [DataField, AutoNetworkedField] + public float MediumVoltageDamageMultiplier = 2f; + + /// + /// Shock time multiplier for MV electrocution + /// + [DataField, AutoNetworkedField] + public float MediumVoltageTimeMultiplier = 1.25f; + + [DataField, AutoNetworkedField] + public float ShockDamage = 7.5f; + + /// + /// Shock time, in seconds. + /// + [DataField, AutoNetworkedField] + public float ShockTime = 8f; + + [DataField, AutoNetworkedField] + public float SiemensCoefficient = 1f; + + [DataField, AutoNetworkedField] + public SoundSpecifier ShockNoises = new SoundCollectionSpecifier("sparks"); + + [DataField, AutoNetworkedField] + public SoundPathSpecifier AirlockElectrifyDisabled = new("/Audio/Machines/airlock_electrify_on.ogg"); + + [DataField, AutoNetworkedField] + public SoundPathSpecifier AirlockElectrifyEnabled = new("/Audio/Machines/airlock_electrify_off.ogg"); + + [DataField, AutoNetworkedField] + public bool PlaySoundOnShock = true; + + [DataField, AutoNetworkedField] + public float ShockVolume = 20; + + [DataField, AutoNetworkedField] + public float Probability = 1f; + + [DataField, AutoNetworkedField] + public bool IsWireCut = false; +} diff --git a/Content.Shared/Electrocution/SharedElectrocutionSystem.cs b/Content.Shared/Electrocution/SharedElectrocutionSystem.cs index b228a987af..e36e4a804b 100644 --- a/Content.Shared/Electrocution/SharedElectrocutionSystem.cs +++ b/Content.Shared/Electrocution/SharedElectrocutionSystem.cs @@ -23,6 +23,20 @@ public void SetInsulatedSiemensCoefficient(EntityUid uid, float siemensCoefficie Dirty(uid, insulated); } + /// + /// Sets electrified value of component and marks dirty if required. + /// + public void SetElectrified(Entity ent, bool value) + { + if (ent.Comp.Enabled == value) + { + return; + } + + ent.Comp.Enabled = value; + Dirty(ent, ent.Comp); + } + /// Entity being electrocuted. /// Source entity of the electrocution. /// How much shock damage the entity takes. diff --git a/Content.Shared/Power/EntitySystems/SharedPowerReceiverSystem.cs b/Content.Shared/Power/EntitySystems/SharedPowerReceiverSystem.cs index 2bc2af7831..b7ba2a31c5 100644 --- a/Content.Shared/Power/EntitySystems/SharedPowerReceiverSystem.cs +++ b/Content.Shared/Power/EntitySystems/SharedPowerReceiverSystem.cs @@ -1,5 +1,4 @@ using System.Diagnostics.CodeAnalysis; -using Content.Shared.Examine; using Content.Shared.Power.Components; namespace Content.Shared.Power.EntitySystems; @@ -8,6 +7,9 @@ public abstract class SharedPowerReceiverSystem : EntitySystem { public abstract bool ResolveApc(EntityUid entity, [NotNullWhen(true)] ref SharedApcPowerReceiverComponent? component); + /// + /// Checks if entity is APC-powered device, and if it have power. + /// public bool IsPowered(Entity entity) { if (!ResolveApc(entity.Owner, ref entity.Comp)) diff --git a/Content.Shared/Silicons/StationAi/SharedStationAiSystem.Airlock.cs b/Content.Shared/Silicons/StationAi/SharedStationAiSystem.Airlock.cs index ff6fc1ece0..37e5cd6e6a 100644 --- a/Content.Shared/Silicons/StationAi/SharedStationAiSystem.Airlock.cs +++ b/Content.Shared/Silicons/StationAi/SharedStationAiSystem.Airlock.cs @@ -1,5 +1,6 @@ using Content.Shared.Doors.Components; using Robust.Shared.Serialization; +using Content.Shared.Electrocution; namespace Content.Shared.Silicons.StationAi; @@ -10,16 +11,84 @@ public abstract partial class SharedStationAiSystem private void InitializeAirlock() { SubscribeLocalEvent(OnAirlockBolt); + SubscribeLocalEvent(OnAirlockEmergencyAccess); + SubscribeLocalEvent(OnElectrified); } + /// + /// Attempts to bolt door. If wire was cut (AI or for bolts) or its not powered - notifies AI and does nothing. + /// private void OnAirlockBolt(EntityUid ent, DoorBoltComponent component, StationAiBoltEvent args) { - _doors.SetBoltsDown((ent, component), args.Bolted, args.User, predicted: true); + if (component.BoltWireCut) + { + ShowDeviceNotRespondingPopup(args.User); + return; + } + + var setResult = _doors.TrySetBoltDown((ent, component), args.Bolted, args.User, predicted: true); + if (!setResult) + { + ShowDeviceNotRespondingPopup(args.User); + } + } + + /// + /// Attempts to bolt door. If wire was cut (AI) or its not powered - notifies AI and does nothing. + /// + private void OnAirlockEmergencyAccess(EntityUid ent, AirlockComponent component, StationAiEmergencyAccessEvent args) + { + if (!PowerReceiver.IsPowered(ent)) + { + ShowDeviceNotRespondingPopup(args.User); + return; + } + + _airlocks.SetEmergencyAccess((ent, component), args.EmergencyAccess, args.User, predicted: true); + } + + /// + /// Attempts to bolt door. If wire was cut (AI or for one of power-wires) or its not powered - notifies AI and does nothing. + /// + private void OnElectrified(EntityUid ent, ElectrifiedComponent component, StationAiElectrifiedEvent args) + { + if ( + component.IsWireCut + || !PowerReceiver.IsPowered(ent) + ) + { + ShowDeviceNotRespondingPopup(args.User); + return; + } + + _electrify.SetElectrified((ent, component), args.Electrified); + var soundToPlay = component.Enabled + ? component.AirlockElectrifyDisabled + : component.AirlockElectrifyEnabled; + _audio.PlayLocal(soundToPlay, ent, args.User); } } +/// Event for StationAI attempt at bolting/unbolting door. [Serializable, NetSerializable] public sealed class StationAiBoltEvent : BaseStationAiAction { + /// Marker, should be door bolted or unbolted. public bool Bolted; } + +/// Event for StationAI attempt at setting emergency access for door on/off. +[Serializable, NetSerializable] +public sealed class StationAiEmergencyAccessEvent : BaseStationAiAction +{ + /// Marker, should door have emergency access on or off. + public bool EmergencyAccess; +} + +/// Event for StationAI attempt at electrifying/de-electrifying door. +[Serializable, NetSerializable] +public sealed class StationAiElectrifiedEvent : BaseStationAiAction +{ + /// Marker, should door be electrified or no. + public bool Electrified; +} diff --git a/Content.Shared/Silicons/StationAi/SharedStationAiSystem.Held.cs b/Content.Shared/Silicons/StationAi/SharedStationAiSystem.Held.cs index a6c57f5940..33afb5673f 100644 --- a/Content.Shared/Silicons/StationAi/SharedStationAiSystem.Held.cs +++ b/Content.Shared/Silicons/StationAi/SharedStationAiSystem.Held.cs @@ -1,6 +1,7 @@ -using System.Diagnostics.CodeAnalysis; using Content.Shared.Actions.Events; +using Content.Shared.IdentityManagement; using Content.Shared.Interaction.Events; +using Content.Shared.Popups; using Content.Shared.Verbs; using Robust.Shared.Serialization; using Robust.Shared.Utility; @@ -13,6 +14,9 @@ public abstract partial class SharedStationAiSystem * Added when an entity is inserted into a StationAiCore. */ + //TODO: Fix this, please + private const string JobNameLocId = "job-name-station-ai"; + private void InitializeHeld() { SubscribeLocalEvent(OnRadialMessage); @@ -22,6 +26,22 @@ private void InitializeHeld() SubscribeLocalEvent(OnHeldInteraction); SubscribeLocalEvent(OnHeldRelay); SubscribeLocalEvent(OnCoreJump); + SubscribeLocalEvent(OnTryGetIdentityShortInfo); + } + + private void OnTryGetIdentityShortInfo(TryGetIdentityShortInfoEvent args) + { + if (args.Handled) + { + return; + } + + if (!HasComp(args.ForActor)) + { + return; + } + args.Title = $"{Name(args.ForActor)} ({Loc.GetString(JobNameLocId)})"; + args.Handled = true; } private void OnCoreJump(Entity ent, ref JumpToCoreEvent args) @@ -88,41 +108,56 @@ private void OnMessageAttempt(BoundUserInterfaceMessageAttempt ev) return; if (TryComp(ev.Actor, out StationAiHeldComponent? aiComp) && - (!ValidateAi((ev.Actor, aiComp)) || - !HasComp(ev.Target))) + (!TryComp(ev.Target, out StationAiWhitelistComponent? whitelistComponent) || + !ValidateAi((ev.Actor, aiComp)))) { + if (whitelistComponent is { Enabled: false }) + { + ShowDeviceNotRespondingPopup(ev.Actor); + } ev.Cancel(); } } private void OnHeldInteraction(Entity ent, ref InteractionAttemptEvent args) { - // Cancel if it's not us or something with a whitelist. - args.Cancelled = ent.Owner != args.Target && - args.Target != null && - (!TryComp(args.Target, out StationAiWhitelistComponent? whitelist) || !whitelist.Enabled); + // Cancel if it's not us or something with a whitelist, or whitelist is disabled. + args.Cancelled = (!TryComp(args.Target, out StationAiWhitelistComponent? whitelistComponent) + || !whitelistComponent.Enabled) + && ent.Owner != args.Target + && args.Target != null; + if (whitelistComponent is { Enabled: false }) + { + ShowDeviceNotRespondingPopup(ent.Owner); + } } private void OnTargetVerbs(Entity ent, ref GetVerbsEvent args) { - if (!args.CanComplexInteract || - !ent.Comp.Enabled || - !HasComp(args.User) || - !HasComp(args.Target)) + if (!args.CanComplexInteract + || !HasComp(args.User)) { return; } var user = args.User; + var target = args.Target; var isOpen = _uiSystem.IsUiOpen(target, AiUi.Key, user); - args.Verbs.Add(new AlternativeVerb() + var verb = new AlternativeVerb { Text = isOpen ? Loc.GetString("ai-close") : Loc.GetString("ai-open"), Act = () => { + // no need to show menu if device is not powered. + if (!PowerReceiver.IsPowered(ent.Owner)) + { + ShowDeviceNotRespondingPopup(user); + return; + } + if (isOpen) { _uiSystem.CloseUi(ent.Owner, AiUi.Key, user); @@ -132,7 +167,13 @@ private void OnTargetVerbs(Entity ent, ref GetVerbs _uiSystem.OpenUi(ent.Owner, AiUi.Key, user); } } - }); + }; + args.Verbs.Add(verb); + } + + private void ShowDeviceNotRespondingPopup(EntityUid toEntity) + { + _popup.PopupClient(Loc.GetString("ai-device-not-responding"), toEntity, PopupType.MediumCaution); } } diff --git a/Content.Shared/Silicons/StationAi/SharedStationAiSystem.cs b/Content.Shared/Silicons/StationAi/SharedStationAiSystem.cs index 348b0b0465..7fb00d9ade 100644 --- a/Content.Shared/Silicons/StationAi/SharedStationAiSystem.cs +++ b/Content.Shared/Silicons/StationAi/SharedStationAiSystem.cs @@ -4,14 +4,18 @@ using Content.Shared.Containers.ItemSlots; using Content.Shared.Database; using Content.Shared.Doors.Systems; +using Content.Shared.Electrocution; using Content.Shared.Interaction; using Content.Shared.Item.ItemToggle; using Content.Shared.Mind; using Content.Shared.Movement.Components; using Content.Shared.Movement.Systems; +using Content.Shared.Popups; using Content.Shared.Power; +using Content.Shared.Power.EntitySystems; using Content.Shared.StationAi; using Content.Shared.Verbs; +using Robust.Shared.Audio.Systems; using Robust.Shared.Containers; using Robust.Shared.Map.Components; using Robust.Shared.Network; @@ -24,23 +28,28 @@ namespace Content.Shared.Silicons.StationAi; public abstract partial class SharedStationAiSystem : EntitySystem { - [Dependency] private readonly ISharedAdminManager _admin = default!; - [Dependency] private readonly IGameTiming _timing = default!; - [Dependency] private readonly INetManager _net = default!; - [Dependency] private readonly ItemSlotsSystem _slots = default!; - [Dependency] private readonly ItemToggleSystem _toggles = default!; - [Dependency] private readonly ActionBlockerSystem _blocker = default!; - [Dependency] private readonly MetaDataSystem _metadata = default!; - [Dependency] private readonly SharedAppearanceSystem _appearance = default!; - [Dependency] private readonly SharedContainerSystem _containers = default!; - [Dependency] private readonly SharedDoorSystem _doors = default!; - [Dependency] private readonly SharedEyeSystem _eye = default!; + [Dependency] private readonly ISharedAdminManager _admin = default!; + [Dependency] private readonly IGameTiming _timing = default!; + [Dependency] private readonly INetManager _net = default!; + [Dependency] private readonly ItemSlotsSystem _slots = default!; + [Dependency] private readonly ItemToggleSystem _toggles = default!; + [Dependency] private readonly ActionBlockerSystem _blocker = default!; + [Dependency] private readonly MetaDataSystem _metadata = default!; + [Dependency] private readonly SharedAirlockSystem _airlocks = default!; + [Dependency] private readonly SharedAppearanceSystem _appearance = default!; + [Dependency] private readonly SharedAudioSystem _audio = default!; + [Dependency] private readonly SharedContainerSystem _containers = default!; + [Dependency] private readonly SharedDoorSystem _doors = default!; + [Dependency] private readonly SharedElectrocutionSystem _electrify = default!; + [Dependency] private readonly SharedEyeSystem _eye = default!; [Dependency] protected readonly SharedMapSystem Maps = default!; - [Dependency] private readonly SharedMindSystem _mind = default!; - [Dependency] private readonly SharedMoverController _mover = default!; - [Dependency] private readonly SharedTransformSystem _xforms = default!; - [Dependency] private readonly SharedUserInterfaceSystem _uiSystem = default!; - [Dependency] private readonly StationAiVisionSystem _vision = default!; + [Dependency] private readonly SharedMindSystem _mind = default!; + [Dependency] private readonly SharedMoverController _mover = default!; + [Dependency] private readonly SharedPopupSystem _popup = default!; + [Dependency] private readonly SharedPowerReceiverSystem PowerReceiver = default!; + [Dependency] private readonly SharedTransformSystem _xforms = default!; + [Dependency] private readonly SharedUserInterfaceSystem _uiSystem = default!; + [Dependency] private readonly StationAiVisionSystem _vision = default!; // StationAiHeld is added to anything inside of an AI core. // StationAiHolder indicates it can hold an AI positronic brain (e.g. holocard / core). diff --git a/Resources/Audio/Machines/airlock_electrify_off.ogg b/Resources/Audio/Machines/airlock_electrify_off.ogg new file mode 100644 index 0000000000..c88d00299e Binary files /dev/null and b/Resources/Audio/Machines/airlock_electrify_off.ogg differ diff --git a/Resources/Audio/Machines/airlock_electrify_on.ogg b/Resources/Audio/Machines/airlock_electrify_on.ogg new file mode 100644 index 0000000000..c87b7d3851 Binary files /dev/null and b/Resources/Audio/Machines/airlock_electrify_on.ogg differ diff --git a/Resources/Audio/Machines/airlock_emergencyoff.ogg b/Resources/Audio/Machines/airlock_emergencyoff.ogg new file mode 100644 index 0000000000..5046a75ec2 Binary files /dev/null and b/Resources/Audio/Machines/airlock_emergencyoff.ogg differ diff --git a/Resources/Audio/Machines/airlock_emergencyon.ogg b/Resources/Audio/Machines/airlock_emergencyon.ogg new file mode 100644 index 0000000000..6c8b54a284 Binary files /dev/null and b/Resources/Audio/Machines/airlock_emergencyon.ogg differ diff --git a/Resources/Audio/Machines/attributions.yml b/Resources/Audio/Machines/attributions.yml index 38267ebd69..6a0d5b4969 100644 --- a/Resources/Audio/Machines/attributions.yml +++ b/Resources/Audio/Machines/attributions.yml @@ -161,3 +161,17 @@ license: "CC-BY-SA-3.0" copyright: "Taken from TG station." source: "https://github.com/tgstation/tgstation/tree/d4f678a1772007ff8d7eddd21cf7218c8e07bfc0" + +- files: ["cutter.ogg"] + license: "CC0-1.0" + copyright: "by Ko4erga" + source: "https://github.com/space-wizards/space-station-14/pull/30431" + +- files: + - airlock_emergencyoff.ogg + - airlock_emergencyon.ogg + - airlock_electrify_off.ogg + - airlock_electrify_on.ogg + license: "CC0-1.0" + copyright: "by ScarKy0" + source: "https://github.com/space-wizards/space-station-14/pull/32012" diff --git a/Resources/Locale/en-US/silicons/station-ai.ftl b/Resources/Locale/en-US/silicons/station-ai.ftl index d51a99ebb0..7d9db3f6dc 100644 --- a/Resources/Locale/en-US/silicons/station-ai.ftl +++ b/Resources/Locale/en-US/silicons/station-ai.ftl @@ -11,4 +11,12 @@ ai-close = Close actions bolt-close = Close bolt bolt-open = Open bolt +emergency-access-on = Enable emergency access +emergency-access-off = Disable emergency access + +electrify-door-on = Enable overcharge +electrify-door-off = Disable overcharge + toggle-light = Toggle light + +ai-device-not-responding = Device is not responding diff --git a/Resources/Locale/en-US/wires/log-wire.ftl b/Resources/Locale/en-US/wires/log-wire.ftl new file mode 100644 index 0000000000..735816a52d --- /dev/null +++ b/Resources/Locale/en-US/wires/log-wire.ftl @@ -0,0 +1 @@ +log-wire-pulse-access-log = ERROR: Electromagnetic spike detected diff --git a/Resources/Locale/en-US/wires/wire-names.ftl b/Resources/Locale/en-US/wires/wire-names.ftl index 851241f85c..a1ebec9e7d 100644 --- a/Resources/Locale/en-US/wires/wire-names.ftl +++ b/Resources/Locale/en-US/wires/wire-names.ftl @@ -66,3 +66,4 @@ wire-name-bomb-boom = BOOM wire-name-bomb-bolt = BOLT wire-name-speech = SPKR wire-name-listen = MIC +wire-name-log = LOG diff --git a/Resources/Prototypes/Entities/Mobs/Player/silicon.yml b/Resources/Prototypes/Entities/Mobs/Player/silicon.yml index 6c169821ab..6685e47520 100644 --- a/Resources/Prototypes/Entities/Mobs/Player/silicon.yml +++ b/Resources/Prototypes/Entities/Mobs/Player/silicon.yml @@ -289,10 +289,29 @@ drawFov: false - type: Examiner - type: InputMover + - type: Speech + speechVerb: Robotic + speechSounds: Borg - type: Tag tags: - HideContextMenu - StationAi + - type: LanguageKnowledge + speaks: + - TauCetiBasic + - SolCommon + - Tradeband + - Freespeak + - Elyran + - RobotTalk + understands: + - TauCetiBasic + - SolCommon + - Tradeband + - Freespeak + - Elyran + - RobotTalk + - Sign # It's intentional that they don't "Speak" sign language. # Hologram projection that the AI's eye tracks. - type: entity diff --git a/Resources/Prototypes/Wires/layouts.yml b/Resources/Prototypes/Wires/layouts.yml index 20d5d33603..7ef30d116b 100644 --- a/Resources/Prototypes/Wires/layouts.yml +++ b/Resources/Prototypes/Wires/layouts.yml @@ -5,6 +5,7 @@ - !type:PowerWireAction - !type:PowerWireAction pulseTimeout: 15 + - !type:LogWireAction - !type:DoorBoltWireAction - !type:DoorBoltLightWireAction - !type:DoorTimingWireAction @@ -60,6 +61,7 @@ wires: - !type:PowerWireAction - !type:AccessWireAction + - !type:LogWireAction - !type:VendingMachineContrabandWireAction - !type:VendingMachineEjectItemWireAction - !type:SpeechWireAction @@ -69,6 +71,7 @@ wires: - !type:PowerWireAction - !type:AccessWireAction + - !type:LogWireAction - !type:AirAlarmPanicWire - !type:AtmosMonitorDeviceNetWire @@ -125,6 +128,7 @@ - !type:PowerWireAction - !type:PowerWireAction pulseTimeout: 15 + - !type:LogWireAction - !type:DoorBoltWireAction - !type:DoorBoltLightWireAction - !type:DoorTimingWireAction diff --git a/Resources/Textures/Interface/Actions/actions_ai.rsi/ai_core.png b/Resources/Textures/Interface/Actions/actions_ai.rsi/ai_core.png index 8dd3031f9f..89cc7aa165 100644 Binary files a/Resources/Textures/Interface/Actions/actions_ai.rsi/ai_core.png and b/Resources/Textures/Interface/Actions/actions_ai.rsi/ai_core.png differ diff --git a/Resources/Textures/Interface/Actions/actions_ai.rsi/bolt_door.png b/Resources/Textures/Interface/Actions/actions_ai.rsi/bolt_door.png new file mode 100644 index 0000000000..f794248980 Binary files /dev/null and b/Resources/Textures/Interface/Actions/actions_ai.rsi/bolt_door.png differ diff --git a/Resources/Textures/Interface/Actions/actions_ai.rsi/camera_light.png b/Resources/Textures/Interface/Actions/actions_ai.rsi/camera_light.png index 041b9b9bf7..53ebb32c59 100644 Binary files a/Resources/Textures/Interface/Actions/actions_ai.rsi/camera_light.png and b/Resources/Textures/Interface/Actions/actions_ai.rsi/camera_light.png differ diff --git a/Resources/Textures/Interface/Actions/actions_ai.rsi/comms_console.png b/Resources/Textures/Interface/Actions/actions_ai.rsi/comms_console.png new file mode 100644 index 0000000000..1958e5fc48 Binary files /dev/null and b/Resources/Textures/Interface/Actions/actions_ai.rsi/comms_console.png differ diff --git a/Resources/Textures/Interface/Actions/actions_ai.rsi/crew_monitor.png b/Resources/Textures/Interface/Actions/actions_ai.rsi/crew_monitor.png index 78fad17a76..73abb66ac1 100644 Binary files a/Resources/Textures/Interface/Actions/actions_ai.rsi/crew_monitor.png and b/Resources/Textures/Interface/Actions/actions_ai.rsi/crew_monitor.png differ diff --git a/Resources/Textures/Interface/Actions/actions_ai.rsi/door_overcharge_off.png b/Resources/Textures/Interface/Actions/actions_ai.rsi/door_overcharge_off.png new file mode 100644 index 0000000000..d5301ccba0 Binary files /dev/null and b/Resources/Textures/Interface/Actions/actions_ai.rsi/door_overcharge_off.png differ diff --git a/Resources/Textures/Interface/Actions/actions_ai.rsi/door_overcharge_on.png b/Resources/Textures/Interface/Actions/actions_ai.rsi/door_overcharge_on.png new file mode 100644 index 0000000000..ea654d8634 Binary files /dev/null and b/Resources/Textures/Interface/Actions/actions_ai.rsi/door_overcharge_on.png differ diff --git a/Resources/Textures/Interface/Actions/actions_ai.rsi/emergency_off.png b/Resources/Textures/Interface/Actions/actions_ai.rsi/emergency_off.png new file mode 100644 index 0000000000..86328da46b Binary files /dev/null and b/Resources/Textures/Interface/Actions/actions_ai.rsi/emergency_off.png differ diff --git a/Resources/Textures/Interface/Actions/actions_ai.rsi/emergency_on.png b/Resources/Textures/Interface/Actions/actions_ai.rsi/emergency_on.png new file mode 100644 index 0000000000..14034429f4 Binary files /dev/null and b/Resources/Textures/Interface/Actions/actions_ai.rsi/emergency_on.png differ diff --git a/Resources/Textures/Interface/Actions/actions_ai.rsi/mass_scanner.png b/Resources/Textures/Interface/Actions/actions_ai.rsi/mass_scanner.png new file mode 100644 index 0000000000..f7542550b5 Binary files /dev/null and b/Resources/Textures/Interface/Actions/actions_ai.rsi/mass_scanner.png differ diff --git a/Resources/Textures/Interface/Actions/actions_ai.rsi/meta.json b/Resources/Textures/Interface/Actions/actions_ai.rsi/meta.json index a7c00f7793..434b9052e0 100644 --- a/Resources/Textures/Interface/Actions/actions_ai.rsi/meta.json +++ b/Resources/Textures/Interface/Actions/actions_ai.rsi/meta.json @@ -1,26 +1,53 @@ { - "version": 1, - "license": "CC-BY-SA-3.0", - "copyright": "Taken from tgstation at commit https://github.com/tgstation/tgstation/blob/c473a8bcc28fbd80827dfca5660d81ca6e833e2c/icons/hud/screen_ai.dmi", - "size": { - "x": 32, - "y": 32 - }, - "states": [ - { - "name": "ai_core" - }, - { - "name": "camera_light" - }, - { - "name": "crew_monitor" - }, - { - "name": "manifest" - }, - { - "name": "state_laws" - } - ] + "version": 1, + "license": "CC-BY-SA-3.0", + "copyright": "Taken from tgstation at commit https://github.com/tgstation/tgstation/blob/c473a8bcc28fbd80827dfca5660d81ca6e833e2c/icons/hud/screen_ai.dmi , some sprites updated by ScarKy0(Discord), door actions by ScarKy0(Discord) and @Max_tanuki(Twitter)", + "size": { + "x": 32, + "y": 32 + }, + "states": [ + { + "name": "ai_core" + }, + { + "name": "camera_light" + }, + { + "name": "crew_monitor" + }, + { + "name": "manifest" + }, + { + "name": "state_laws" + }, + { + "name": "station_records" + }, + { + "name": "mass_scanner" + }, + { + "name": "comms_console" + }, + { + "name": "emergency_off" + }, + { + "name": "emergency_on" + }, + { + "name": "bolt_door" + }, + { + "name": "unbolt_door" + }, + { + "name": "door_overcharge_on" + }, + { + "name": "door_overcharge_off" + } + ] } diff --git a/Resources/Textures/Interface/Actions/actions_ai.rsi/state_laws.png b/Resources/Textures/Interface/Actions/actions_ai.rsi/state_laws.png index e30e891745..ff546e13b1 100644 Binary files a/Resources/Textures/Interface/Actions/actions_ai.rsi/state_laws.png and b/Resources/Textures/Interface/Actions/actions_ai.rsi/state_laws.png differ diff --git a/Resources/Textures/Interface/Actions/actions_ai.rsi/station_records.png b/Resources/Textures/Interface/Actions/actions_ai.rsi/station_records.png new file mode 100644 index 0000000000..c1f1420445 Binary files /dev/null and b/Resources/Textures/Interface/Actions/actions_ai.rsi/station_records.png differ diff --git a/Resources/Textures/Interface/Actions/actions_ai.rsi/unbolt_door.png b/Resources/Textures/Interface/Actions/actions_ai.rsi/unbolt_door.png new file mode 100644 index 0000000000..dfbb102f97 Binary files /dev/null and b/Resources/Textures/Interface/Actions/actions_ai.rsi/unbolt_door.png differ