From c6f32292f738db1950ffe07bc6117e438c36a4ce Mon Sep 17 00:00:00 2001 From: Dexler <69513582+DexlerXD@users.noreply.github.com> Date: Sat, 11 May 2024 00:17:16 +0300 Subject: [PATCH] MindSlave (#983) * basic implementation of mindslave * mindslave implementation 2 (broken) * mindslave implementation 3 (fixed) * mindslave implementation 4 * Fix NT issue * Added notification eui and minor fixes --- Content.Client/Antag/AntagStatusIconSystem.cs | 3 + .../SS220/MindSlave/MindSlaveSystem.cs | 34 ++ .../MindSlave/UI/MindSlaveNotificationEui.cs | 48 +++ .../UI/MindSlaveNotificationWindow.cs | 54 +++ .../GameTicking/Rules/TraitorRuleSystem.cs | 29 ++ Content.Server/Implants/ImplanterSystem.cs | 32 ++ Content.Server/Mindshield/MindShieldSystem.cs | 16 + .../Systems/TargetObjectiveSystem.cs | 13 + Content.Server/Roles/RoleSystem.cs | 3 + .../SS220/MindSlave/MindSlaveSystem.cs | 327 ++++++++++++++++++ .../MindSlave/UI/MindSlaveNotificationEui.cs | 31 ++ Content.Shared/Alert/AlertType.cs | 3 +- .../Components/SubdermalImplantComponent.cs | 5 + .../Implants/SharedImplanterSystem.cs | 3 + .../Implants/SharedSubdermalImplantSystem.cs | 32 ++ .../SS220/MindSlave/MindSlaveComponent.cs | 19 + .../MindSlave/MindSlaveMasterComponent.cs | 25 ++ .../MindSlave/MindSlaveNotificationMessage.cs | 19 + .../SS220/MindSlave/MindSlaveRoleComponent.cs | 22 ++ .../Locale/ru-RU/ss220/implant/mindslave.ftl | 43 +++ .../ru-RU/ss220/store/uplink-catalog.ftl | 4 + .../Prototypes/SS220/Alerts/mindslave.yml | 7 + .../Prototypes/SS220/Antags/mindslave.yml | 6 + .../SS220/Catalog/uplink_catalog.yml | 18 + .../Entities/Objects/Misc/implanters.yml | 7 + .../Objects/Misc/subdermal_implants.yml | 10 + .../Prototypes/SS220/Objectives/mindslave.yml | 14 + .../Prototypes/SS220/StatusIcons/antag.yml | 16 + Resources/Prototypes/SS220/tags.yml | 3 + .../SS220/Alerts/MindSlave.rsi/alert.png | Bin 0 -> 2326 bytes .../SS220/Alerts/MindSlave.rsi/icon.png | Bin 0 -> 730 bytes .../SS220/Alerts/MindSlave.rsi/meta.json | 26 ++ .../Misc/mindslave-job-icon.rsi/master.png | Bin 0 -> 191 bytes .../Misc/mindslave-job-icon.rsi/meta.json | 17 + .../Misc/mindslave-job-icon.rsi/slave.png | Bin 0 -> 206 bytes 35 files changed, 888 insertions(+), 1 deletion(-) create mode 100644 Content.Client/SS220/MindSlave/MindSlaveSystem.cs create mode 100644 Content.Client/SS220/MindSlave/UI/MindSlaveNotificationEui.cs create mode 100644 Content.Client/SS220/MindSlave/UI/MindSlaveNotificationWindow.cs create mode 100644 Content.Server/SS220/MindSlave/MindSlaveSystem.cs create mode 100644 Content.Server/SS220/MindSlave/UI/MindSlaveNotificationEui.cs create mode 100644 Content.Shared/SS220/MindSlave/MindSlaveComponent.cs create mode 100644 Content.Shared/SS220/MindSlave/MindSlaveMasterComponent.cs create mode 100644 Content.Shared/SS220/MindSlave/MindSlaveNotificationMessage.cs create mode 100644 Content.Shared/SS220/MindSlave/MindSlaveRoleComponent.cs create mode 100644 Resources/Locale/ru-RU/ss220/implant/mindslave.ftl create mode 100644 Resources/Prototypes/SS220/Alerts/mindslave.yml create mode 100644 Resources/Prototypes/SS220/Antags/mindslave.yml create mode 100644 Resources/Prototypes/SS220/Entities/Objects/Misc/implanters.yml create mode 100644 Resources/Prototypes/SS220/Entities/Objects/Misc/subdermal_implants.yml create mode 100644 Resources/Prototypes/SS220/Objectives/mindslave.yml create mode 100644 Resources/Textures/SS220/Alerts/MindSlave.rsi/alert.png create mode 100644 Resources/Textures/SS220/Alerts/MindSlave.rsi/icon.png create mode 100644 Resources/Textures/SS220/Alerts/MindSlave.rsi/meta.json create mode 100644 Resources/Textures/SS220/Interface/Misc/mindslave-job-icon.rsi/master.png create mode 100644 Resources/Textures/SS220/Interface/Misc/mindslave-job-icon.rsi/meta.json create mode 100644 Resources/Textures/SS220/Interface/Misc/mindslave-job-icon.rsi/slave.png diff --git a/Content.Client/Antag/AntagStatusIconSystem.cs b/Content.Client/Antag/AntagStatusIconSystem.cs index 907f6357e35a..4f4f84c9c3d1 100644 --- a/Content.Client/Antag/AntagStatusIconSystem.cs +++ b/Content.Client/Antag/AntagStatusIconSystem.cs @@ -1,6 +1,7 @@ using Content.Shared.Antag; using Content.Shared.Revolutionary.Components; using Content.Shared.SS220.AdmemeEvents; +using Content.Shared.SS220.MindSlave; using Content.Shared.StatusIcon; using Content.Shared.StatusIcon.Components; using Content.Shared.Zombies; @@ -25,6 +26,8 @@ public override void Initialize() SubscribeLocalEvent(GetIcon); SubscribeLocalEvent(GetIcon); SubscribeLocalEvent(GetIcon); //SS220-admeme-ebents + SubscribeLocalEvent(GetIcon); //SS220-mindslave + SubscribeLocalEvent(GetIcon); //SS220-mindslave } /// diff --git a/Content.Client/SS220/MindSlave/MindSlaveSystem.cs b/Content.Client/SS220/MindSlave/MindSlaveSystem.cs new file mode 100644 index 000000000000..ace2db4fac6e --- /dev/null +++ b/Content.Client/SS220/MindSlave/MindSlaveSystem.cs @@ -0,0 +1,34 @@ +// © SS220, An EULA/CLA with a hosting restriction, full text: https://raw.githubusercontent.com/SerbiaStrong-220/space-station-14/master/CLA.txt + +using Content.Shared.SS220.MindSlave; +using Content.Shared.StatusIcon.Components; + +namespace Content.Client.SS220.MindSlave; + +public sealed class MindSlaveSystem : EntitySystem +{ + public override void Initialize() + { + base.Initialize(); + + SubscribeLocalEvent(OnSlaveGetIcons); + SubscribeLocalEvent(OnMasterGetIcons); + } + + private void OnSlaveGetIcons(Entity entity, ref CanDisplayStatusIconsEvent args) + { + if (TryComp(args.User, out var masterComp) && masterComp.EnslavedEntities.Contains(entity)) + return; + + args.Cancelled = true; + } + + private void OnMasterGetIcons(Entity entity, ref CanDisplayStatusIconsEvent args) + { + if (HasComp(args.User) && entity.Comp.EnslavedEntities.Contains(args.User.Value)) + return; + + args.Cancelled = true; + } +} + diff --git a/Content.Client/SS220/MindSlave/UI/MindSlaveNotificationEui.cs b/Content.Client/SS220/MindSlave/UI/MindSlaveNotificationEui.cs new file mode 100644 index 000000000000..7c64c6e2b6dd --- /dev/null +++ b/Content.Client/SS220/MindSlave/UI/MindSlaveNotificationEui.cs @@ -0,0 +1,48 @@ +// © SS220, An EULA/CLA with a hosting restriction, full text: https://raw.githubusercontent.com/SerbiaStrong-220/space-station-14/master/CLA.txt + +using Content.Client.Eui; +using Content.Shared.Eui; +using Content.Shared.SS220.MindSlave; +using JetBrains.Annotations; +using Robust.Client.Graphics; + +namespace Content.Client.SS220.MindSlave.UI; + +//Stolen from DeathReminder +[UsedImplicitly] +public sealed class MindSlaveNotificationEui : BaseEui +{ + private readonly MindSlaveNotificationWindow _window; + + public MindSlaveNotificationEui() + { + _window = new MindSlaveNotificationWindow(); + + _window.AcceptButton.OnPressed += _ => + { + _window.Close(); + }; + } + + public override void HandleState(EuiStateBase state) + { + if (state is not MindSlaveNotificationEuiState newState) + return; + + _window.TextLabel.Text = newState.IsEnslaved ? + Loc.GetString("mindslave-notification-window-text-enslaved", ("name", newState.MasterName)) : + Loc.GetString("mindslave-notification-window-text-freed"); + } + + public override void Opened() + { + IoCManager.Resolve().RequestWindowAttention(); + _window.OpenCentered(); + } + + public override void Closed() + { + _window.Close(); + } +} + diff --git a/Content.Client/SS220/MindSlave/UI/MindSlaveNotificationWindow.cs b/Content.Client/SS220/MindSlave/UI/MindSlaveNotificationWindow.cs new file mode 100644 index 000000000000..0f90695be5cb --- /dev/null +++ b/Content.Client/SS220/MindSlave/UI/MindSlaveNotificationWindow.cs @@ -0,0 +1,54 @@ +// © SS220, An EULA/CLA with a hosting restriction, full text: https://raw.githubusercontent.com/SerbiaStrong-220/space-station-14/master/CLA.txt + +using Robust.Client.UserInterface; +using Robust.Client.UserInterface.Controls; +using Robust.Client.UserInterface.CustomControls; +using System.Numerics; + +namespace Content.Client.SS220.MindSlave.UI; + +//Stolen from DeathReminder +public sealed class MindSlaveNotificationWindow : DefaultWindow +{ + public readonly Button AcceptButton; + public readonly Label TextLabel; + + public MindSlaveNotificationWindow() + { + Title = Loc.GetString("mindslave-notification-window-title"); + + Contents.AddChild(new BoxContainer + { + Orientation = BoxContainer.LayoutOrientation.Vertical, + Children = + { + new BoxContainer + { + Orientation = BoxContainer.LayoutOrientation.Vertical, + Children = + { + (TextLabel = new Label() + { + }), + new BoxContainer + { + Orientation = BoxContainer.LayoutOrientation.Horizontal, + Align = BoxContainer.AlignMode.Center, + Children = + { + (AcceptButton = new Button + { + Text = Loc.GetString("mindslave-notification-window-accept"), + }), + (new Control() + { + MinSize = new Vector2(20, 0) + }), + } + }, + } + }, + } + }); + } +} diff --git a/Content.Server/GameTicking/Rules/TraitorRuleSystem.cs b/Content.Server/GameTicking/Rules/TraitorRuleSystem.cs index b6bcd5ee1e89..08a816a6382e 100644 --- a/Content.Server/GameTicking/Rules/TraitorRuleSystem.cs +++ b/Content.Server/GameTicking/Rules/TraitorRuleSystem.cs @@ -16,6 +16,7 @@ using System.Linq; using System.Text; using Content.Server.GameTicking.Components; +using Content.Server.SS220.MindSlave; namespace Content.Server.GameTicking.Rules; @@ -30,6 +31,7 @@ public sealed class TraitorRuleSystem : GameRuleSystem [Dependency] private readonly SharedRoleSystem _roleSystem = default!; [Dependency] private readonly SharedJobSystem _jobs = default!; [Dependency] private readonly ObjectivesSystem _objectives = default!; + [Dependency] private readonly MindSlaveSystem _mindSlave = default!; public const int MaxPicks = 20; @@ -67,6 +69,24 @@ private void MakeCodewords(TraitorRuleComponent component) } } + //SS220-mindslave begin + public void AddToTraitorList(EntityUid mind, EntityUid gameRuleEntity, TraitorRuleComponent? component = null) + { + if (!Resolve(gameRuleEntity, ref component)) + return; + + component.TraitorMinds.Add(mind); + } + + public void RemoveFromTraitorList(EntityUid mind, EntityUid gameRuleEntity, TraitorRuleComponent? component = null) + { + if (!Resolve(gameRuleEntity, ref component)) + return; + + component.TraitorMinds.Remove(mind); + } + //SS220-mindslave end + public bool MakeTraitor(EntityUid traitor, TraitorRuleComponent component, bool giveUplink = true, bool giveObjectives = true) { //Grab the mind if it wasnt provided @@ -134,6 +154,15 @@ public bool MakeTraitor(EntityUid traitor, TraitorRuleComponent component, bool private void OnObjectivesTextGetInfo(EntityUid uid, TraitorRuleComponent comp, ref ObjectivesTextGetInfoEvent args) { args.Minds = _antag.GetAntagMindEntityUids(uid); + + //SS220-mindslave begin + if (_mindSlave.EnslavedMinds.Count > 0) + { + foreach (var slave in _mindSlave.EnslavedMinds) + args.Minds.Add(slave.Key); + } + //SS220-mindslave end + args.AgentName = Loc.GetString("traitor-round-end-agent-name"); } diff --git a/Content.Server/Implants/ImplanterSystem.cs b/Content.Server/Implants/ImplanterSystem.cs index e441574213e9..d9f086df8d89 100644 --- a/Content.Server/Implants/ImplanterSystem.cs +++ b/Content.Server/Implants/ImplanterSystem.cs @@ -1,12 +1,15 @@ using System.Linq; using Content.Server.Popups; +using Content.Server.SS220.MindSlave; using Content.Shared.DoAfter; using Content.Shared.IdentityManagement; using Content.Shared.Implants; using Content.Shared.Implants.Components; using Content.Shared.Interaction; +using Content.Shared.Mindshield.Components; using Content.Shared.Popups; using Robust.Shared.Containers; +using Robust.Shared.Prototypes; namespace Content.Server.Implants; @@ -15,6 +18,12 @@ public sealed partial class ImplanterSystem : SharedImplanterSystem [Dependency] private readonly PopupSystem _popup = default!; [Dependency] private readonly SharedDoAfterSystem _doAfter = default!; [Dependency] private readonly SharedContainerSystem _container = default!; + [Dependency] private readonly MindSlaveSystem _mindslave = default!; + + //SS220-mindslave begin + [ValidatePrototypeId] + private const string MindSlaveImplantProto = "MindSlaveImplant"; + //SS220-mindslave end public override void Initialize() { @@ -36,6 +45,29 @@ private void OnImplanterAfterInteract(EntityUid uid, ImplanterComponent componen if (!CheckTarget(target, component.Whitelist, component.Blacklist)) return; + //SS220-mindslave begin + if (component.Implant == MindSlaveImplantProto) + { + if (args.User == target) + { + _popup.PopupEntity(Loc.GetString("mindslave-enslaving-yourself-attempt"), target, args.User); + return; + } + + if (_mindslave.IsEnslaved(target)) + { + _popup.PopupEntity(Loc.GetString("mindslave-target-already-enslaved"), target, args.User); + return; + } + + if (HasComp(target)) + { + _popup.PopupEntity(Loc.GetString("mindslave-target-mindshielded"), target, args.User); + return; + } + } + //SS220-mindslave end + //TODO: Rework when surgery is in for implant cases if (component.CurrentMode == ImplanterToggleMode.Draw && !component.ImplantOnly) { diff --git a/Content.Server/Mindshield/MindShieldSystem.cs b/Content.Server/Mindshield/MindShieldSystem.cs index bfca6c008ea5..7d962da2ac9e 100644 --- a/Content.Server/Mindshield/MindShieldSystem.cs +++ b/Content.Server/Mindshield/MindShieldSystem.cs @@ -2,6 +2,7 @@ using Content.Server.Mind; using Content.Server.Popups; using Content.Server.Roles; +using Content.Server.SS220.MindSlave; using Content.Shared.Database; using Content.Shared.Implants; using Content.Shared.Implants.Components; @@ -21,10 +22,17 @@ public sealed class MindShieldSystem : EntitySystem [Dependency] private readonly MindSystem _mindSystem = default!; [Dependency] private readonly TagSystem _tag = default!; [Dependency] private readonly PopupSystem _popupSystem = default!; + [Dependency] private readonly MindSlaveSystem _mindSlave = default!; + [Dependency] private readonly SharedSubdermalImplantSystem _sharedSubdermalImplant = default!; [ValidatePrototypeId] public const string MindShieldTag = "MindShield"; + //SS220-mindslave begin + [ValidatePrototypeId] + public const string MindSlaveTag = "MindSlave"; + //SS220-mindslave end + public override void Initialize() { base.Initialize(); @@ -41,6 +49,14 @@ public void ImplantCheck(EntityUid uid, SubdermalImplantComponent comp, ref Impl EnsureComp(ev.Implanted.Value); MindShieldRemovalCheck(ev.Implanted.Value, ev.Implant); } + + //SS220-mindslave begin + if (_tag.HasTag(ev.Implant, MindSlaveTag) && ev.Implanted != null && comp.user != null) + { + if (!_mindSlave.TryMakeSlave(ev.Implanted.Value, comp.user.Value)) + _sharedSubdermalImplant.ForceRemove(ev.Implanted.Value, ev.Implant); + } + //SS220-mindslave end } /// diff --git a/Content.Server/Objectives/Systems/TargetObjectiveSystem.cs b/Content.Server/Objectives/Systems/TargetObjectiveSystem.cs index 82ebd28ca9e0..d9cfa27c59c5 100644 --- a/Content.Server/Objectives/Systems/TargetObjectiveSystem.cs +++ b/Content.Server/Objectives/Systems/TargetObjectiveSystem.cs @@ -30,6 +30,19 @@ private void OnAfterAssign(EntityUid uid, TargetObjectiveComponent comp, ref Obj _metaData.SetEntityName(uid, GetTitle(target.Value, comp.Title), args.Meta); } + //SS220-mindslave begin + public void ResetEntityName(EntityUid uid, TargetObjectiveComponent? comp = null) + { + if (!Resolve(uid, ref comp)) + return; + + if (!GetTarget(uid, out var target, comp)) + return; + + _metaData.SetEntityName(uid, GetTitle(target.Value, comp.Title), MetaData(uid)); + } + //SS220-mindslave end + /// /// Sets the Target field for the title and other components to use. /// diff --git a/Content.Server/Roles/RoleSystem.cs b/Content.Server/Roles/RoleSystem.cs index c53fa1cf9ebc..6294a268ed0c 100644 --- a/Content.Server/Roles/RoleSystem.cs +++ b/Content.Server/Roles/RoleSystem.cs @@ -1,4 +1,5 @@ using Content.Shared.Roles; +using Content.Shared.SS220.MindSlave; namespace Content.Server.Roles; @@ -18,6 +19,8 @@ public override void Initialize() SubscribeAntagEvents(); SubscribeAntagEvents(); SubscribeAntagEvents(); + //SS220-mindslave + SubscribeAntagEvents(); } public string? MindGetBriefing(EntityUid? mindId) diff --git a/Content.Server/SS220/MindSlave/MindSlaveSystem.cs b/Content.Server/SS220/MindSlave/MindSlaveSystem.cs new file mode 100644 index 000000000000..e11e31febf4a --- /dev/null +++ b/Content.Server/SS220/MindSlave/MindSlaveSystem.cs @@ -0,0 +1,327 @@ +// © SS220, An EULA/CLA with a hosting restriction, full text: https://raw.githubusercontent.com/SerbiaStrong-220/space-station-14/master/CLA.txt + +using Content.Server.Antag; +using Content.Server.Body.Components; +using Content.Server.EUI; +using Content.Server.GameTicking; +using Content.Server.GameTicking.Rules; +using Content.Server.GameTicking.Rules.Components; +using Content.Server.Mind; +using Content.Server.Objectives.Components; +using Content.Server.Objectives.Systems; +using Content.Server.Popups; +using Content.Server.Roles; +using Content.Server.SS220.MindSlave.UI; +using Content.Shared.Alert; +using Content.Shared.Cloning; +using Content.Shared.Implants; +using Content.Shared.Implants.Components; +using Content.Shared.Mindshield.Components; +using Content.Shared.Mobs; +using Content.Shared.NPC.Prototypes; +using Content.Shared.NPC.Systems; +using Content.Shared.Objectives.Systems; +using Content.Shared.Roles; +using Content.Shared.SS220.MindSlave; +using Content.Shared.Tag; +using Robust.Server.Player; +using Robust.Shared.Audio; +using Robust.Shared.Prototypes; + +namespace Content.Server.SS220.MindSlave; + +public sealed class MindSlaveSystem : EntitySystem +{ + [Dependency] private readonly PopupSystem _popup = default!; + [Dependency] private readonly MindSystem _mind = default!; + [Dependency] private readonly RoleSystem _role = default!; + [Dependency] private readonly NpcFactionSystem _npcFaction = default!; + [Dependency] private readonly AntagSelectionSystem _antagSelection = default!; + [Dependency] private readonly SharedObjectivesSystem _objectives = default!; + [Dependency] private readonly GameTicker _gameTicker = default!; + [Dependency] private readonly TargetObjectiveSystem _targetObjective = default!; + [Dependency] private readonly TraitorRuleSystem _traitorRule = default!; + [Dependency] private readonly AlertsSystem _alert = default!; + [Dependency] private readonly IPlayerManager _playerManager = default!; + [Dependency] private readonly EuiManager _eui = default!; + [Dependency] private readonly SharedSubdermalImplantSystem _implant = default!; + [Dependency] private readonly TagSystem _tag = default!; + + [ValidatePrototypeId] + private const string MindSlaveAntagId = "MindSlave"; + + [ValidatePrototypeId] + private const string MindSlaveObjectiveId = "MindSlaveObeyObjective"; + + [ValidatePrototypeId] + private const string NanoTrasenFactionId = "NanoTrasen"; + + [ValidatePrototypeId] + private const string SyndicateFactionId = "Syndicate"; + + [ValidatePrototypeId] + private const string MindSlaveImplantTag = "MindSlave"; + + private readonly SoundSpecifier GreetSoundNotification = new SoundPathSpecifier("/Audio/Ambience/Antag/traitor_start.ogg"); + + private const AlertType EnslavedAlert = AlertType.MindSlaved; + + /// + /// Dictionary, containing list of all enslaved minds (as a key), and their master (as a value). + /// + public Dictionary EnslavedMinds { get; private set; } = new(); + + public override void Initialize() + { + base.Initialize(); + + SubscribeLocalEvent(OnMasterDeadOrCrit); + SubscribeLocalEvent(OnMasterGibbed); + SubscribeLocalEvent(OnDead); + SubscribeLocalEvent(OnCloned); + SubscribeLocalEvent(OnMindSlaveRemoved); + } + + private void OnMasterGibbed(Entity entity, ref BeingGibbedEvent args) + { + if (entity.Comp.EnslavedEntities.Count <= 0) + return; + + // This is disgusting, but I need to get rid of a reference + RemoveSlaves(new List(entity.Comp.EnslavedEntities)); + } + + private void OnMasterDeadOrCrit(Entity entity, ref MobStateChangedEvent args) + { + if (args.NewMobState != MobState.Dead && args.NewMobState != MobState.Critical) + return; + + if (entity.Comp.EnslavedEntities.Count <= 0) + return; + + var message = args.NewMobState == MobState.Dead ? Loc.GetString("mindslave-master-dead") : Loc.GetString("mindslave-master-crit"); + foreach (var slave in entity.Comp.EnslavedEntities) + { + _popup.PopupEntity(message, slave, slave, Shared.Popups.PopupType.LargeCaution); + _antagSelection.SendBriefing(slave, message, Color.Red, null); + } + } + + private void OnDead(Entity entity, ref MobStateChangedEvent args) + { + if (args.NewMobState != MobState.Dead) + return; + + TryRemoveSlave(entity); + } + + private void OnCloned(Entity entity, ref CloningEvent args) + { + TryRemoveSlave(entity); + } + + private void OnMindSlaveRemoved(Entity mind, ref MindSlaveRemoved args) + { + if (args.Slave == null || !IsEnslaved(args.Slave.Value)) + return; + + TryRemoveSlave(args.Slave.Value); + } + + //I need all of this for the TraitorMinds list + private void GetTraitorGamerule(out EntityUid? ruleEntity, out TraitorRuleComponent? component) + { + var gameRules = _gameTicker.GetActiveGameRules().GetEnumerator(); + ruleEntity = null; + while (gameRules.MoveNext()) + { + if (!HasComp(gameRules.Current)) + continue; + + ruleEntity = gameRules.Current; + break; + } + + TryComp(ruleEntity, out component); + } + + /// + /// Makes entity a slave, converting it into an antag. + /// + /// Entity to be enslaved. + /// Master of the given entity. + /// Whether enslaving were succesfull. + public bool TryMakeSlave(EntityUid slave, EntityUid master) + { + if (IsEnslaved(slave)) + return false; + + if (!_mind.TryGetMind(slave, out var mindId, out var mindComp)) + return false; + + if (!_mind.TryGetMind(master, out var masterMindId, out var masterMindComp)) + return false; + + if (HasComp(slave)) + { + _popup.PopupEntity(Loc.GetString("mindslave-target-mindshielded"), slave, master); + return false; + } + + var objective = _objectives.TryCreateObjective(mindId, mindComp, MindSlaveObjectiveId); + _role.MindAddRole(mindId, new MindSlaveRoleComponent + { + PrototypeId = MindSlaveAntagId, + masterEntity = master, + objectiveEntity = objective + }, mindComp, true); + + var masterName = masterMindComp.CharacterName ?? Loc.GetString("mindslave-unknown-master"); + var briefing = Loc.GetString("mindslave-briefing-slave", ("master", masterName)); + _antagSelection.SendBriefing(slave, briefing, Color.Red, GreetSoundNotification); + _popup.PopupEntity(briefing, slave, slave, Shared.Popups.PopupType.LargeCaution); + + if (mindComp.CharacterName != null) + { + var briefingMaster = Loc.GetString("mindslave-briefing-slave-master", ("name", mindComp.CharacterName), ("ent", slave)); + _popup.PopupEntity(briefingMaster, slave, master); + _antagSelection.SendBriefing(master, briefingMaster, null, null); + } + + //If we fail to add objective - try to use briefing for that... + if (objective != null && TryComp(objective.Value, out var targetObjective)) + { + _targetObjective.SetTarget(objective.Value, masterMindId, targetObjective); + _targetObjective.ResetEntityName(objective.Value, targetObjective); + _mind.AddObjective(mindId, mindComp, objective.Value); + } + else + { + _role.MindAddRole(mindId, new RoleBriefingComponent + { + Briefing = briefing.ToString() + }, mindComp, true); + } + + EnsureComp(slave); + _alert.ShowAlert(slave, EnslavedAlert); + + var masterComp = EnsureComp(master); + masterComp.EnslavedEntities.Add(slave); + Dirty(master, masterComp); + + _npcFaction.RemoveFaction(slave, NanoTrasenFactionId, false); + _npcFaction.AddFaction(slave, SyndicateFactionId); + + EnslavedMinds.Add(mindId, masterMindId); + GetTraitorGamerule(out var gameRuleEntity, out var gameRule); + if (gameRule != null && gameRuleEntity != null) + _traitorRule.AddToTraitorList(mindId, gameRuleEntity.Value, gameRule); + + if (mindComp.UserId != null && _playerManager.TryGetSessionById(mindComp.UserId.Value, out var session)) + _eui.OpenEui(new MindSlaveNotificationEui(masterName, true), session); + + return true; + } + + /// + /// Removes MindSlave from enslaved entity. + /// + /// Enslaved entity. + /// Whether removing MindSlave were succesfull. + public bool TryRemoveSlave(EntityUid slave) + { + if (!IsEnslaved(slave)) + return false; + + if (!_mind.TryGetMind(slave, out var mindId, out var mindComp)) + return false; + + if (!TryComp(mindId, out var mindSlave)) + return false; + + var briefing = Loc.GetString("mindslave-removed-slave"); + _antagSelection.SendBriefing(slave, briefing, Color.Red, null); + _popup.PopupEntity(briefing, slave, slave, Shared.Popups.PopupType.LargeCaution); + + var master = mindSlave.masterEntity; + if (master != null && TryComp(master.Value, out var masterComponent)) + { + var briefingMaster = mindComp.CharacterName != null ? Loc.GetString("mindslave-removed-slave-master", ("name", mindComp.CharacterName), ("ent", slave)) : + "mindslave-removed-slave-master-unknown"; + + _antagSelection.SendBriefing(master.Value, briefingMaster, Color.Red, null); + _popup.PopupEntity(briefingMaster, master.Value, master.Value, Shared.Popups.PopupType.MediumCaution); + + masterComponent.EnslavedEntities.Remove(slave); + Dirty(master.Value, masterComponent); + } + var masterName = master != null ? Name(master.Value) : string.Empty; + + _role.MindRemoveRole(mindId); + + //If slave had a valid objective - remove it, otherwise - remove briefing + var objective = mindSlave.objectiveEntity; + if (objective != null) + _mind.TryRemoveObjective(mindId, mindComp, objective.Value); + else + _role.MindTryRemoveRole(mindId); + + RemComp(slave); + _alert.ClearAlert(slave, EnslavedAlert); + + _npcFaction.RemoveFaction(slave, SyndicateFactionId, false); + _npcFaction.AddFaction(slave, NanoTrasenFactionId); + + EnslavedMinds.Remove(mindId); + GetTraitorGamerule(out var gameRuleEntity, out var gameRule); + if (gameRule != null && gameRuleEntity != null) + _traitorRule.RemoveFromTraitorList(mindId, gameRuleEntity.Value, gameRule); + + if (mindComp.UserId != null && master != null && _playerManager.TryGetSessionById(mindComp.UserId.Value, out var session)) + _eui.OpenEui(new MindSlaveNotificationEui(masterName, false), session); + + // Remove implant if has one + // Recursion's not happening because all the other stuff is already removed, so it will not proceed with checks. + if (TryComp(slave, out var implantComp)) + { + var implants = implantComp.ImplantContainer.ContainedEntities; + + EntityUid? mindslaveImplant = null; + foreach (var implant in implants) + { + if (!_tag.HasTag(implant, MindSlaveImplantTag)) + continue; + + mindslaveImplant = implant; + } + + if (mindslaveImplant != null) + _implant.ForceRemove(slave, mindslaveImplant.Value); + } + + return true; + } + + public void RemoveSlaves(List slaves) + { + if (slaves.Count <= 0) + return; + + foreach (var slave in slaves) + TryRemoveSlave(slave); + } + + /// + /// Returns whether the given entity is mind-enslaved by someone. + /// + /// Entity to be checked. + /// Whether the entity is mind-enslaved. + public bool IsEnslaved(EntityUid entity) + { + if (!_mind.TryGetMind(entity, out var mindId, out var mindComp)) + return false; + + return HasComp(mindId); + } +} diff --git a/Content.Server/SS220/MindSlave/UI/MindSlaveNotificationEui.cs b/Content.Server/SS220/MindSlave/UI/MindSlaveNotificationEui.cs new file mode 100644 index 000000000000..6ac765cb515c --- /dev/null +++ b/Content.Server/SS220/MindSlave/UI/MindSlaveNotificationEui.cs @@ -0,0 +1,31 @@ +// © SS220, An EULA/CLA with a hosting restriction, full text: https://raw.githubusercontent.com/SerbiaStrong-220/space-station-14/master/CLA.txt + +using Content.Server.EUI; +using Content.Shared.Eui; +using Content.Shared.SS220.MindSlave; + +namespace Content.Server.SS220.MindSlave.UI; + +public sealed class MindSlaveNotificationEui : BaseEui +{ + public readonly string MasterName = string.Empty; + public readonly bool IsEnslaved; + + public MindSlaveNotificationEui(string masterName, bool isEnslaved) + { + MasterName = masterName; + IsEnslaved = isEnslaved; + } + + public override void Opened() + { + StateDirty(); + } + + public override EuiStateBase GetNewState() + { + var state = new MindSlaveNotificationEuiState(MasterName, IsEnslaved); + return state; + } +} + diff --git a/Content.Shared/Alert/AlertType.cs b/Content.Shared/Alert/AlertType.cs index fe391761747d..84636d8b06a8 100644 --- a/Content.Shared/Alert/AlertType.cs +++ b/Content.Shared/Alert/AlertType.cs @@ -55,7 +55,8 @@ public enum AlertType : byte BorgDead, DeadscoreStage1, //SS220 DarkReaper DeadscoreStage2, //SS220 DarkReaper - ItemOffer, // SS220 ItemOfferVerb + ItemOffer, // SS220 ItemOfferVerb + MindSlaved, //SS220-mindslave } } diff --git a/Content.Shared/Implants/Components/SubdermalImplantComponent.cs b/Content.Shared/Implants/Components/SubdermalImplantComponent.cs index 2b710b5fade3..c93922e310a1 100644 --- a/Content.Shared/Implants/Components/SubdermalImplantComponent.cs +++ b/Content.Shared/Implants/Components/SubdermalImplantComponent.cs @@ -49,6 +49,11 @@ public sealed partial class SubdermalImplantComponent : Component /// [DataField] public EntityWhitelist? Blacklist; + + //SS220-mindslave begin + //I really need to pass user's uid... + public EntityUid? user; + //SS220-mindslave end } public sealed partial class UseChemicalImplantEvent : InstantActionEvent diff --git a/Content.Shared/Implants/SharedImplanterSystem.cs b/Content.Shared/Implants/SharedImplanterSystem.cs index 595d0534364a..8e253509a4bb 100644 --- a/Content.Shared/Implants/SharedImplanterSystem.cs +++ b/Content.Shared/Implants/SharedImplanterSystem.cs @@ -61,6 +61,9 @@ public void Implant(EntityUid user, EntityUid target, EntityUid implanter, Impla if (!CanImplant(user, target, implanter, component, out var implant, out var implantComp)) return; + //SS220-mindslave + implantComp.user = user; + //If the target doesn't have the implanted component, add it. var implantedComp = EnsureComp(target); var implantContainer = implantedComp.ImplantContainer; diff --git a/Content.Shared/Implants/SharedSubdermalImplantSystem.cs b/Content.Shared/Implants/SharedSubdermalImplantSystem.cs index 830d2270aa45..a08bc132abcf 100644 --- a/Content.Shared/Implants/SharedSubdermalImplantSystem.cs +++ b/Content.Shared/Implants/SharedSubdermalImplantSystem.cs @@ -20,6 +20,11 @@ public abstract class SharedSubdermalImplantSystem : EntitySystem public const string BaseStorageId = "storagebase"; + //SS220-mindslave begin + [ValidatePrototypeId] + private const string MindSlaveTag = "MindSlave"; + //SS220-mindslave end + public override void Initialize() { SubscribeLocalEvent(OnInsert); @@ -72,6 +77,14 @@ private void OnRemove(EntityUid uid, SubdermalImplantComponent component, EntGot if (component.ImplantAction != null) _actionsSystem.RemoveProvidedActions(component.ImplantedEntity.Value, uid); + //SS220-mindslave start + if (_tag.HasTag(uid, MindSlaveTag)) + { + var mindSlaveRemoved = new MindSlaveRemoved(uid, component.ImplantedEntity); + RaiseLocalEvent(uid, ref mindSlaveRemoved); + } + //SS220-mindslave end + if (!_container.TryGetContainer(uid, BaseStorageId, out var storageImplant)) return; @@ -205,3 +218,22 @@ public ImplantImplantedEvent(EntityUid implant, EntityUid? implanted) Implanted = implanted; } } + +//SS220-mindslave start +/// +/// Event raised whenever MindSlave implant is removed. +/// Raied on the implant itself. +/// +[ByRefEvent] +public readonly struct MindSlaveRemoved +{ + public readonly EntityUid Implant; + public readonly EntityUid? Slave; + + public MindSlaveRemoved(EntityUid implant, EntityUid? slave) + { + Implant = implant; + Slave = slave; + } +} +//SS220-mindslave end diff --git a/Content.Shared/SS220/MindSlave/MindSlaveComponent.cs b/Content.Shared/SS220/MindSlave/MindSlaveComponent.cs new file mode 100644 index 000000000000..1f4191ad86a0 --- /dev/null +++ b/Content.Shared/SS220/MindSlave/MindSlaveComponent.cs @@ -0,0 +1,19 @@ +// © SS220, An EULA/CLA with a hosting restriction, full text: https://raw.githubusercontent.com/SerbiaStrong-220/space-station-14/master/CLA.txt + +using Content.Shared.Antag; +using Content.Shared.StatusIcon; +using Robust.Shared.GameStates; +using Robust.Shared.Prototypes; + +namespace Content.Shared.SS220.MindSlave; + +/// +/// Used to mark an entity as a mind-slave. +/// +[RegisterComponent, NetworkedComponent] +public sealed partial class MindSlaveComponent : Component, IAntagStatusIconComponent +{ + public ProtoId StatusIcon { get; set; } = "MindSlaveIcon"; + + public bool IconVisibleToGhost { get; set; } = false; +} diff --git a/Content.Shared/SS220/MindSlave/MindSlaveMasterComponent.cs b/Content.Shared/SS220/MindSlave/MindSlaveMasterComponent.cs new file mode 100644 index 000000000000..9d4ff013faed --- /dev/null +++ b/Content.Shared/SS220/MindSlave/MindSlaveMasterComponent.cs @@ -0,0 +1,25 @@ +// © SS220, An EULA/CLA with a hosting restriction, full text: https://raw.githubusercontent.com/SerbiaStrong-220/space-station-14/master/CLA.txt + +using Content.Shared.Antag; +using Content.Shared.StatusIcon; +using Robust.Shared.GameStates; +using Robust.Shared.Prototypes; + +namespace Content.Shared.SS220.MindSlave; + +/// +/// Component, used to mark the master of some enslaved minds. +/// +[RegisterComponent, NetworkedComponent, AutoGenerateComponentState] +public sealed partial class MindSlaveMasterComponent : Component, IAntagStatusIconComponent +{ + /// + /// List of all enslaved entities, which were enslaved by the owner. + /// + [ViewVariables(VVAccess.ReadOnly), AutoNetworkedField] + public List EnslavedEntities = new(); + + public ProtoId StatusIcon { get; set; } = "MindSlaveMasterIcon"; + + public bool IconVisibleToGhost { get; set; } = false; +} diff --git a/Content.Shared/SS220/MindSlave/MindSlaveNotificationMessage.cs b/Content.Shared/SS220/MindSlave/MindSlaveNotificationMessage.cs new file mode 100644 index 000000000000..cc2be2b073f0 --- /dev/null +++ b/Content.Shared/SS220/MindSlave/MindSlaveNotificationMessage.cs @@ -0,0 +1,19 @@ +// © SS220, An EULA/CLA with a hosting restriction, full text: https://raw.githubusercontent.com/SerbiaStrong-220/space-station-14/master/CLA.txt + +using Content.Shared.Eui; +using Robust.Shared.Serialization; + +namespace Content.Shared.SS220.MindSlave; + +[Serializable, NetSerializable] +public sealed class MindSlaveNotificationEuiState : EuiStateBase +{ + public readonly string MasterName; + public readonly bool IsEnslaved; + + public MindSlaveNotificationEuiState(string masterName, bool isEnslaved) + { + MasterName = masterName; + IsEnslaved = isEnslaved; + } +} diff --git a/Content.Shared/SS220/MindSlave/MindSlaveRoleComponent.cs b/Content.Shared/SS220/MindSlave/MindSlaveRoleComponent.cs new file mode 100644 index 000000000000..abfe7a7e44f4 --- /dev/null +++ b/Content.Shared/SS220/MindSlave/MindSlaveRoleComponent.cs @@ -0,0 +1,22 @@ +// © SS220, An EULA/CLA with a hosting restriction, full text: https://raw.githubusercontent.com/SerbiaStrong-220/space-station-14/master/CLA.txt + +using Content.Shared.Roles; +using Robust.Shared.GameStates; + +namespace Content.Shared.SS220.MindSlave; + +[RegisterComponent, NetworkedComponent, AutoGenerateComponentState] +public sealed partial class MindSlaveRoleComponent : AntagonistRoleComponent +{ + /// + /// Enslaved person's master, which he obeys to. + /// + [ViewVariables(VVAccess.ReadOnly), AutoNetworkedField] + public EntityUid? masterEntity; + + /// + /// Enslaved person's objective. + /// + [ViewVariables(VVAccess.ReadOnly), AutoNetworkedField] + public EntityUid? objectiveEntity; +} diff --git a/Resources/Locale/ru-RU/ss220/implant/mindslave.ftl b/Resources/Locale/ru-RU/ss220/implant/mindslave.ftl new file mode 100644 index 000000000000..16b0d8def5d7 --- /dev/null +++ b/Resources/Locale/ru-RU/ss220/implant/mindslave.ftl @@ -0,0 +1,43 @@ +mindslave-briefing-slave = Ваша воля сломлена инородным ИИ, теперь вы подчиняетесь { $master }. +mindslave-removed-slave = Ваш разум вновь помутнен... Вы забираете контроль над своей волей, однако забываете всё, что произошло после потери контроля! +mindslave-briefing-slave-master = Вы получили контроль над { $name }! Теперь {SUBJECT($ent)} подчиняется вашей воле. +mindslave-removed-slave-master = Вы потеряли контроль над { $name }! {CAPITALIZE(SUBJECT($ent))} забывает всё, что произошло после потери контроля. +mindslave-removed-slave-master-unknown = Вы потеряли контроль над одним из своих подчиненных разумов! +# хз че еще тут написать можно было +mindslave-unknown-master = ЛИЧНОСТЬ СКРЫТА +mindslave-enslaving-yourself-attempt = Нельзя подчинить разум самому себе! +mindslave-target-already-enslaved = Цель уже подчинена! +mindslave-target-mindshielded = Разум цели сопротивляется! +mindslave-master-dead = Ваш подчинитель погиб! Вам необходимо как можно быстрее вернуть его к жизни! +mindslave-master-crit = Ваш подчинитель находится в критическом состоянии! Вам необходимо срочно помочь ему! + +#ui +mindslave-notification-window-title = Подчиненный разум +mindslave-notification-window-text-enslaved = + Ваш разум был подчинён! Отныне, вы обязаны исполнять ВСЕ приказы { $name }. + Однако, вы не можете: + навредить себе (инстинкт самосохранения выше), + навредить своему подчинителю, + раскрывать личность своего подчинителя. +mindslave-notification-window-text-freed = + Вы освободились от оков подчинения! + Вы забываете всё, что произошло после того, как вы стали подчиненным. + Как имя подчинителя, так и сами действия. +mindslave-notification-window-accept = Понятно + +#entities +ent-MindSlaveImplanter = имплантер Подчинитель разума + .desc = Одноразовый имплантер, содержащий имплант, который подчиняет разум владельца тому, кто установил имплант. +ent-MindSlaveImplant = Подчинитель разума + .desc = Этот имплант подчиняет разум владельца тому, кто установил имлпант. При извлечении импланта контроль над разумом теряется. + +#alert +alerts-mindslave-name = Подчиненный разум +alerts-mindslave-desc = Ваш разум был подчинен + +#role +roles-antag-syndicate-mindslave-name = Подчиненный разум +roles-antag-syndicate-mindslave-objective = Ваш разум подчинен! Вы обязаны исполнять волю своего подчинителя. +objective-condition-mindslave-obey-master = Подчиняться воле { $targetName }, { $job }. +ent-MindSlaveObeyObjective = Подчиняться воле другого. + .desc = Ваш разум теперь находится под контролем другого, следуйте его воле и сохраните его жизнь. diff --git a/Resources/Locale/ru-RU/ss220/store/uplink-catalog.ftl b/Resources/Locale/ru-RU/ss220/store/uplink-catalog.ftl index 1bff8fc30fa7..05e5cbab88a1 100644 --- a/Resources/Locale/ru-RU/ss220/store/uplink-catalog.ftl +++ b/Resources/Locale/ru-RU/ss220/store/uplink-catalog.ftl @@ -2,6 +2,10 @@ uplink-ecrossbow-name = { ent-WeaponMiniEnergyCrossbow } uplink-ecrossbow-desc = { ent-WeaponMiniEnergyCrossbow.desc } +# Implants +uplink-mindslave-implanter-name = Имплантер Подчинитель разума +uplink-mindslave-implanter-desc = { ent-MindSlaveImplant.desc } + # Pointless uplink-expensive-lighter-name = { ent-ExpensiveLighterSyndicate } uplink-expensive-lighter-desc = { ent-ExpensiveLighterSyndicate.desc } diff --git a/Resources/Prototypes/SS220/Alerts/mindslave.yml b/Resources/Prototypes/SS220/Alerts/mindslave.yml new file mode 100644 index 000000000000..0f50d6ce11db --- /dev/null +++ b/Resources/Prototypes/SS220/Alerts/mindslave.yml @@ -0,0 +1,7 @@ +- type: alert + id: MindSlaved + icons: + - sprite: /Textures/SS220/Alerts/MindSlave.rsi + state: alert + name: alerts-mindslave-name + description: alerts-mindslave-desc \ No newline at end of file diff --git a/Resources/Prototypes/SS220/Antags/mindslave.yml b/Resources/Prototypes/SS220/Antags/mindslave.yml new file mode 100644 index 000000000000..c1baf386fa7f --- /dev/null +++ b/Resources/Prototypes/SS220/Antags/mindslave.yml @@ -0,0 +1,6 @@ +- type: antag + id: MindSlave + name: roles-antag-syndicate-mindslave-name + antagonist: true + setPreference: false + objective: roles-antag-syndicate-mindslave-objective \ No newline at end of file diff --git a/Resources/Prototypes/SS220/Catalog/uplink_catalog.yml b/Resources/Prototypes/SS220/Catalog/uplink_catalog.yml index 6372ec235b73..83c34130e68a 100644 --- a/Resources/Prototypes/SS220/Catalog/uplink_catalog.yml +++ b/Resources/Prototypes/SS220/Catalog/uplink_catalog.yml @@ -16,6 +16,24 @@ components: - NukeOpsUplink +# Implants + +- type: listing + id: UplinkMindSlaveImplanter + name: uplink-mindslave-implanter-name + description: uplink-mindslave-implanter-desc + icon: { sprite: /Textures/SS220/Alerts/MindSlave.rsi, state: icon } + productEntity: MindSlaveImplanter + cost: + Telecrystal: 8 + categories: + - UplinkImplants + conditions: + - !type:StoreWhitelistCondition + blacklist: + tags: + - NukeOpsUplink + # Pointless - type: listing diff --git a/Resources/Prototypes/SS220/Entities/Objects/Misc/implanters.yml b/Resources/Prototypes/SS220/Entities/Objects/Misc/implanters.yml new file mode 100644 index 000000000000..cef0afe6a7cf --- /dev/null +++ b/Resources/Prototypes/SS220/Entities/Objects/Misc/implanters.yml @@ -0,0 +1,7 @@ +- type: entity + id: MindSlaveImplanter + name: mind-slave implanter + parent: BaseImplantOnlyImplanterSyndi + components: + - type: Implanter + implant: MindSlaveImplant \ No newline at end of file diff --git a/Resources/Prototypes/SS220/Entities/Objects/Misc/subdermal_implants.yml b/Resources/Prototypes/SS220/Entities/Objects/Misc/subdermal_implants.yml new file mode 100644 index 000000000000..f8285a5e461d --- /dev/null +++ b/Resources/Prototypes/SS220/Entities/Objects/Misc/subdermal_implants.yml @@ -0,0 +1,10 @@ +- type: entity + parent: BaseSubdermalImplant + id: MindSlaveImplant + name: mind-slave implant + description: lmaoooo + noSpawn: true + components: + - type: Tag + tags: + - MindSlave \ No newline at end of file diff --git a/Resources/Prototypes/SS220/Objectives/mindslave.yml b/Resources/Prototypes/SS220/Objectives/mindslave.yml new file mode 100644 index 000000000000..121cdf9286a5 --- /dev/null +++ b/Resources/Prototypes/SS220/Objectives/mindslave.yml @@ -0,0 +1,14 @@ +- type: entity + noSpawn: true + parent: BaseSocialObjective + id: MindSlaveObeyObjective + components: + - type: Objective + issuer: syndicate + difficulty: 1 + icon: + sprite: SS220/Alerts/MindSlave.rsi + state: icon + - type: TargetObjective + title: objective-condition-mindslave-obey-master + - type: KeepAliveCondition \ No newline at end of file diff --git a/Resources/Prototypes/SS220/StatusIcons/antag.yml b/Resources/Prototypes/SS220/StatusIcons/antag.yml index f4212d173fcc..58d7949d99d5 100644 --- a/Resources/Prototypes/SS220/StatusIcons/antag.yml +++ b/Resources/Prototypes/SS220/StatusIcons/antag.yml @@ -109,3 +109,19 @@ icon: sprite: /Textures/SS220/Interface/Misc/event-job-icons.rsi state: USSPMedicalOfficer + +- type: statusIcon + id: MindSlaveIcon + priority: 11 + locationPreference: Right + icon: + sprite: /Textures/SS220/Interface/Misc/mindslave-job-icon.rsi + state: slave + +- type: statusIcon + id: MindSlaveMasterIcon + priority: 11 + locationPreference: Right + icon: + sprite: /Textures/SS220/Interface/Misc/mindslave-job-icon.rsi + state: master \ No newline at end of file diff --git a/Resources/Prototypes/SS220/tags.yml b/Resources/Prototypes/SS220/tags.yml index fefd6306374d..4d07a68adace 100644 --- a/Resources/Prototypes/SS220/tags.yml +++ b/Resources/Prototypes/SS220/tags.yml @@ -37,3 +37,6 @@ # ss220 detective-camera - type: Tag id: DetectiveCamera + +- type: Tag + id: MindSlave diff --git a/Resources/Textures/SS220/Alerts/MindSlave.rsi/alert.png b/Resources/Textures/SS220/Alerts/MindSlave.rsi/alert.png new file mode 100644 index 0000000000000000000000000000000000000000..914622dec63999fbcbc44df2c4ff7541d040fe3d GIT binary patch literal 2326 zcmV+x3F-EUP)eB9LK-A6!u^x%epzVVKFYoR8m4N3bKtFJnTlvO*EJ7p_ZgArNlx=59=wtq(V1@ z5=cmLNKcAa?Rbz>H$j+OLRwK*Sw|!l*=Up{B!cAiVBTbQ_Pv=mJ2TE|{9bl;d%R7x^t@7ncBlb~6HeXblZJJGeI~qqD6U=UUDnx4gnM=H~q&jAZ70 z01yj>OosqbJ^+ka`BwRHSrN9M#b-KT*IRc9 z{oZ}{B>CXo53~N9vWkMN0D$WK&bDSW)YqvkhkgRPJtn%jY1;j*@G+AY<^vEKe%!L3 z#pkYqMl$oh*i0VjP>76V=6!R@s(Qm{Z3J`5DmvSmRd}MK73W&c6v63lVAuO1m9OO& zWCig+z!82r6e1#(?-oBE2r&7E%TPL5rjqUCi{qaab;rS9E~z(<)aPueif6~NM(M@QV z)OJnUI<&1u)Nn07dNqRUmv3O;&NW;7M!)$pXF2>DoA#?+gFdxsCk?v9YN)Tn^~*O< z-FzGX)Uj`DQbs{mFmUG@vWrXV{{g6DVMBc#q=_j!9iB#0PYZzE6@tE7n(?3h{0E+# zI_eppXMb!akEY*$H09TfKF503=@P!$EasF|96Ndh4fS=%Ew8Aa8=I7oU0gzS^Ko^? zrvM70*J;mDII!D|d$0BY*j-O|wl$YLlAH0LoH~lw`xTEDe4hP{e!nTdX7o9Z{LsbZ z7Y#v=rk~+l%NbS+AzvK-3{PbRSNeMCS{QUm_GtQ9Q314tpoY+Kk}_Cb(~RFZyN)Ig z`7HZC-`O$c#|-$Y1KtA(?N%e2n~h}Vecb?Q>S-w%4}sEg?&kd=bhb5PY*Gd-IGvov zCS~v#l&@`RT0YKyiEpml!VArOR4$*zFypiA|NhMzRGaV{XV-C$!{;uK(xDL1@7Z*p zJxL0(f~KApbwyB+6}5Y-s{x(#`yc7|+Yv^4LacSOQ+)bFYV=&dVm*6 zJ^&yW3=-b{2|aGMzmjHbzU6{Jp*@*+AV65xTj8_Li|z@<0|Bo6v|MiTg>PbXL;X3+ zaIl?NE}!R6BeCwU-B`3^zt9`8@$GG&{zBF}7cQiDAV40Zk|dkWl594sx4Wbe@j!rxzK*oQ&tx*p zGGO=Jt?C4$<)jmQGy8MFAa^-kmROY!jnUcyWHK2?ji-EG%`m(BLo@#_wWnZOxMXLxaPP@DEi< zItcOkUh6PFz+)Ze2Y4)7`2hzZKHqB{<_CBzTloRy#K-TK{D8fZt^5GMdVGAL!s7G2 zmaY5%tMTztI4ra!yqEF$UTaVD1JZ>;#l`1)Ez|q}x8viD_+I7*?3GOO1Ny70D>^?w zlyB#Cq?^SzR#{D4Y~&llm> zj6OHzdnHTx0g|j>pzp_uiq99}f9v<-u>ju-l|=pm?CpZ?vnO{eVR*V|Jf^8bG#+06 zNw3jH5F0T7_I3e}PlqO}{7Tu9u$>U%&7&tO4M1e7@H*3TUFo%>s`0bACX@$jk^MC+N?WV>~OWZZGo_ z+>~&B04qM9R<6-wz7tyjtXOp_>j_Q_e{Rdt_#nN0Z7V+C3X3ORWt&A`S7POtCDzJ_W>eB&R8~~9lL;EE zmYoX*nZdG7^D9E>P>4K8CArbaS|!r;L zpmHNW!BeF}p^}!D3kI2S@zeos#OI63>HGwzWjQ}Un|Mc6_v#fhE}rT{{8Cv7(kwpx zZWbD@=U1u|EI%bR^|ave!|R3>K+nM}Lzg4*@mse?)%*Z6Ei5b)m7x+Nss9H_ wf;J83)17Pp000hUSV?A0O#mtY000O800000007cclK=n!07*qoM6N<$f{P-AH2?qr literal 0 HcmV?d00001 diff --git a/Resources/Textures/SS220/Alerts/MindSlave.rsi/icon.png b/Resources/Textures/SS220/Alerts/MindSlave.rsi/icon.png new file mode 100644 index 0000000000000000000000000000000000000000..f51f07487b510ffe3e417e2a380ec75d5559e187 GIT binary patch literal 730 zcmV<00ww*4P)aSOx_P8T}o132~rWG-at6zD82Vzh%` zhg!NA5&s~;Ayd9UP6zXz+^g41Y@kr^S+4hxNUoKi8R!odIlmT7Q)aS2SFbJp;5KwiGNy`$S30Eo>B9^H5-rgF(B>DV#- z(b#h_=NFfA#J&PL!wGgFPp#ReTyN>;eyOiD+hj8dI>U*Q8sT67%QRdyo+MzZ_KT9= zC-EcoxVp#GcZL&k$tZwceJ&ZrG7asVTo>WVdsq4`DA!vqa&gDmwsh>6o7+3z7QA>0 zd8wFEZ+BkN{B_Oy&>;YyK7L?hbBi~xce%g6=f}_A?C-y$+Z)gyjoIDVW@B^9mmGXT z=)BKn5_Ee5!odLLdW$pr)V0$7XiR4~fofxVT>>rvLY&Pcs8k!sHafq!(8;}JPP5_{KyYM0PAz9>q|Xv6DJH%58kaG7W|@Xdu!xqntUDhb?rme42K~`k zla=eOx#B_~`(NI3jtRLqDsI|^Jh^04AIYv3)S7M5v16CQ2dGHs0Dv?5RL%mx&{=Q_ zaEcJ&k`v*O1OV literal 0 HcmV?d00001 diff --git a/Resources/Textures/SS220/Alerts/MindSlave.rsi/meta.json b/Resources/Textures/SS220/Alerts/MindSlave.rsi/meta.json new file mode 100644 index 000000000000..fcb4c8e9c4ba --- /dev/null +++ b/Resources/Textures/SS220/Alerts/MindSlave.rsi/meta.json @@ -0,0 +1,26 @@ +{ + "version": 1, + "license": "EULA/CLA with a hosting restriction, full text: https://raw.githubusercontent.com/SerbiaStrong-220/space-station-14/master/CLA.txt", + "copyright": "Made by ell_good (Discord) For SS220", + "size": { + "x": 32, + "y": 32 + }, + "states": [ + { + "name": "alert", + "delays": [ + [ + 0.3, + 0.3, + 0.3, + 0.3, + 0.3 + ] + ] + }, + { + "name": "icon" + } + ] +} diff --git a/Resources/Textures/SS220/Interface/Misc/mindslave-job-icon.rsi/master.png b/Resources/Textures/SS220/Interface/Misc/mindslave-job-icon.rsi/master.png new file mode 100644 index 0000000000000000000000000000000000000000..8a16d1892ac581880e45af9260847cea3b6e9f10 GIT binary patch literal 191 zcmeAS@N?(olHy`uVBq!ia0vp^93afW3?x5a^xFxf7>k44ofy`glX(f`u%tWsIx;Y9 z?C1WI$O`212l#}zUeMBVXJ>!+?p=R>KTzz1>TZxhtR+Ey!T*7P;rBMaV4whJfk$L9 z0|Vb-5N14{zaj-FsNw127$Om#oRFB1!k{Fkz{r*`tCRCoQWD!m2R7pXmQXGhi3PVL aBN(_87~DgyWtsu?FnGH9xvXk44ofy`glX(f`u%tWsIx;Y9 z?C1WI$O`0(2Ka=ysxvUSv$M~ui9NEQt|r#u{?=Zg%*PAi%0P;}B*-uLKNv7bc{95K zg*Xd5B8wRq_zr_G