diff --git a/Content.Client/SS220/Surgery/SurgerySystem.cs b/Content.Client/SS220/Surgery/SurgerySystem.cs new file mode 100644 index 00000000000000..64ad74d67b1eb0 --- /dev/null +++ b/Content.Client/SS220/Surgery/SurgerySystem.cs @@ -0,0 +1,10 @@ +// © 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.SS220.Surgery.Systems; + +namespace Content.Client.SS220.Surgery; + +public sealed partial class SurgerySystem : SharedSurgerySystem +{ + // exist only in to make prediction +} diff --git a/Content.Server/Implants/ImplanterSystem.cs b/Content.Server/Implants/ImplanterSystem.cs index d9f086df8d8953..a4ef868387436c 100644 --- a/Content.Server/Implants/ImplanterSystem.cs +++ b/Content.Server/Implants/ImplanterSystem.cs @@ -1,4 +1,5 @@ using System.Linq; +using Content.Server.Construction.Conditions; using Content.Server.Popups; using Content.Server.SS220.MindSlave; using Content.Shared.DoAfter; @@ -8,6 +9,7 @@ using Content.Shared.Interaction; using Content.Shared.Mindshield.Components; using Content.Shared.Popups; +using Content.Shared.Tag; // SS220-mindslave using Robust.Shared.Containers; using Robust.Shared.Prototypes; @@ -19,10 +21,14 @@ public sealed partial class ImplanterSystem : SharedImplanterSystem [Dependency] private readonly SharedDoAfterSystem _doAfter = default!; [Dependency] private readonly SharedContainerSystem _container = default!; [Dependency] private readonly MindSlaveSystem _mindslave = default!; + [Dependency] private readonly TagSystem _tag = default!; // SS220-mindslave //SS220-mindslave begin [ValidatePrototypeId] private const string MindSlaveImplantProto = "MindSlaveImplant"; + [ValidatePrototypeId] + private const string MindShieldImplantTag = "MindShield"; + private const float MindShieldRemoveTime = 40; //SS220-mindslave end public override void Initialize() @@ -46,6 +52,15 @@ private void OnImplanterAfterInteract(EntityUid uid, ImplanterComponent componen return; //SS220-mindslave begin + if (component.ImplanterSlot.ContainerSlot != null + && component.ImplanterSlot.ContainerSlot.ContainedEntity != null + && _tag.HasTag(component.ImplanterSlot.ContainerSlot.ContainedEntity.Value, MindShieldImplantTag) + && _mindslave.IsEnslaved(target)) + { + _popup.PopupEntity(Loc.GetString("mindshield-target-mindslaved"), target, args.User); + return; + } + if (component.Implant == MindSlaveImplantProto) { if (args.User == target) @@ -155,7 +170,26 @@ public void TryImplant(ImplanterComponent component, EntityUid user, EntityUid t //TODO: Remove when surgery is in public void TryDraw(ImplanterComponent component, EntityUid user, EntityUid target, EntityUid implanter) { - var args = new DoAfterArgs(EntityManager, user, component.DrawTime, new DrawEvent(), implanter, target: target, used: implanter) + //SS220-Mindshield-remove-time begin + var isMindShield = false; + + if (_container.TryGetContainer(target, ImplanterComponent.ImplantSlotId, out var implantContainer)) + { + foreach (var implant in implantContainer.ContainedEntities) + { + if (HasComp(implant) && _container.CanRemove(implant, implantContainer)) + { + if (_tag.HasTag(implant, MindShieldImplantTag)) + isMindShield = true; + break; + } + } + } + var delay = isMindShield ? MindShieldRemoveTime : component.DrawTime; + var popupPath = isMindShield ? "injector-component-drawing-mind-shield" : "injector-component-drawing-user"; + var args = new DoAfterArgs(EntityManager, user, delay, new DrawEvent(), implanter, target: target, used: implanter) + // var args = new DoAfterArgs(EntityManager, user, component.DrawTime, new DrawEvent(), implanter, target: target, used: implanter) + //SS220-Mindshield-remove-time end { BreakOnDamage = true, BreakOnMove = true, @@ -163,7 +197,9 @@ public void TryDraw(ImplanterComponent component, EntityUid user, EntityUid targ }; if (_doAfter.TryStartDoAfter(args)) - _popup.PopupEntity(Loc.GetString("injector-component-injecting-user"), target, user); + // _popup.PopupEntity(Loc.GetString("injector-component-injecting-user"), target, user); //SS220-Mindshield-remove-time + _popup.PopupEntity(Loc.GetString(popupPath), target, user); + } diff --git a/Content.Server/SS220/MindSlave/Components/MindSlaveDisfunctionComponent.cs b/Content.Server/SS220/MindSlave/Components/MindSlaveDisfunctionComponent.cs new file mode 100644 index 00000000000000..7d5a775b866157 --- /dev/null +++ b/Content.Server/SS220/MindSlave/Components/MindSlaveDisfunctionComponent.cs @@ -0,0 +1,59 @@ +// © 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.Damage; + +namespace Content.Server.SS220.MindSlave.Components; + +[RegisterComponent] +public sealed partial class MindSlaveDisfunctionComponent : Component +{ + + [ViewVariables] + public Dictionary> Disfunction => DisfunctionParameters.Disfunction; + + [ViewVariables] + public DamageSpecifier DeadlyStageDamage => DisfunctionParameters.DeadlyStageDamage; + + [DataField(required: true)] + public DisfunctionParameters DisfunctionParameters = new(); + + [ViewVariables(VVAccess.ReadOnly)] + public List DisfunctionComponents = new(); + + [ViewVariables(VVAccess.ReadWrite)] + public MindSlaveDisfunctionType DisfunctionStage = MindSlaveDisfunctionType.None; + + [ViewVariables(VVAccess.ReadWrite)] + public bool Active = true; + + [ViewVariables(VVAccess.ReadWrite)] + public bool Deadly = false; + + [ViewVariables(VVAccess.ReadWrite)] + public bool Weakened = false; + + [ViewVariables(VVAccess.ReadWrite)] + public TimeSpan NextProgressTime; + + [ViewVariables(VVAccess.ReadWrite)] + public TimeSpan NextDeadlyDamageTime; + + [ViewVariables(VVAccess.ReadWrite)] + public TimeSpan PausedTime; + + [ViewVariables(VVAccess.ReadWrite)] + public float ConstMinutesBetweenStages = 35; + + [ViewVariables(VVAccess.ReadWrite)] + public float MaxRandomMinutesBetweenStages = 7; + +} + +public enum MindSlaveDisfunctionType +{ + None = 0, + Initial, + Progressive, + Terminal, + Deadly +} diff --git a/Content.Server/SS220/MindSlave/Components/MindSlaveDisfunctionProviderComponent.cs b/Content.Server/SS220/MindSlave/Components/MindSlaveDisfunctionProviderComponent.cs new file mode 100644 index 00000000000000..82a933f5a9570e --- /dev/null +++ b/Content.Server/SS220/MindSlave/Components/MindSlaveDisfunctionProviderComponent.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.Damage; + +namespace Content.Server.SS220.MindSlave.Components; + +[RegisterComponent] +public sealed partial class MindSlaveDisfunctionProviderComponent : Component +{ + [DataField(required: true)] + public DisfunctionParameters Disfunction = new(); +} + +[DataDefinition] +public sealed partial class DisfunctionParameters +{ + [DataField(required: true)] + public Dictionary> Disfunction = new(); + + [DataField(required: true)] + public DamageSpecifier DeadlyStageDamage = new(); + + [DataField(required: true)] + public string ProgressionPopup; +} diff --git a/Content.Server/SS220/MindSlave/Components/MindSlaveStopWordContainerComponent.cs b/Content.Server/SS220/MindSlave/Components/MindSlaveStopWordContainerComponent.cs new file mode 100644 index 00000000000000..6b12004ab4d677 --- /dev/null +++ b/Content.Server/SS220/MindSlave/Components/MindSlaveStopWordContainerComponent.cs @@ -0,0 +1,23 @@ +// © SS220, An EULA/CLA with a hosting restriction, full text: https://raw.githubusercontent.com/SerbiaStrong-220/space-station-14/master/CLA.txt + +using Robust.Shared.Prototypes; + +namespace Content.Server.SS220.MindSlave.Components; + +[RegisterComponent] +public sealed partial class MindSlaveStopWordContainerComponent : Component +{ + // to pass tests add values + [DataField] + public string Collection = "nanotrasen_central_command"; + [DataField] + public string Group = "roundstart"; + [DataField] + public string Form = "hos_mindslave_briefing"; + + /// + /// This stamp will be applied to list + /// + [DataField] + public List StampList = new(); +} diff --git a/Content.Server/SS220/MindSlave/DisfunctionComponents/MindSlaveDisfunctionAccentComponent.cs b/Content.Server/SS220/MindSlave/DisfunctionComponents/MindSlaveDisfunctionAccentComponent.cs new file mode 100644 index 00000000000000..905ff2e946f99f --- /dev/null +++ b/Content.Server/SS220/MindSlave/DisfunctionComponents/MindSlaveDisfunctionAccentComponent.cs @@ -0,0 +1,10 @@ +// © SS220, An EULA/CLA with a hosting restriction, full text: https://raw.githubusercontent.com/SerbiaStrong-220/space-station-14/master/CLA.txt + +namespace Content.Server.SS220.MindSlave.DisfunctionComponents; + +[RegisterComponent] +public sealed partial class MindSlaveDisfunctionAccentComponent : Component +{ + [DataField] + public float Prob = 0.33f; +} diff --git a/Content.Server/SS220/MindSlave/DisfunctionComponents/WieldUnabilityComponent.cs b/Content.Server/SS220/MindSlave/DisfunctionComponents/WieldUnabilityComponent.cs new file mode 100644 index 00000000000000..35285977e52e40 --- /dev/null +++ b/Content.Server/SS220/MindSlave/DisfunctionComponents/WieldUnabilityComponent.cs @@ -0,0 +1,7 @@ +// © SS220, An EULA/CLA with a hosting restriction, full text: https://raw.githubusercontent.com/SerbiaStrong-220/space-station-14/master/CLA.txt + +namespace Content.Server.SS220.MindSlave.DisfunctionComponents; + +[RegisterComponent] +public sealed partial class WieldUnabilityComponent : Component +{ } diff --git a/Content.Server/SS220/MindSlave/DisfunctionSystems/MindSlaveDisfunctionAccentSystem.cs b/Content.Server/SS220/MindSlave/DisfunctionSystems/MindSlaveDisfunctionAccentSystem.cs new file mode 100644 index 00000000000000..5b8573bfe82a73 --- /dev/null +++ b/Content.Server/SS220/MindSlave/DisfunctionSystems/MindSlaveDisfunctionAccentSystem.cs @@ -0,0 +1,43 @@ +// © 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.Speech; +using Robust.Shared.Random; + +namespace Content.Server.SS220.MindSlave.DisfunctionComponents; + +public sealed class MindSlaveDisfunctionAccentSystem : EntitySystem +{ + [Dependency] private readonly IRobustRandom _random = default!; + + private readonly List _vowels = ["а", "е", "у", "о", "и", "я"]; + private readonly List _consonants = ["в", "п", "р", "к", "м", "т", "с"]; + + public override void Initialize() + { + base.Initialize(); + SubscribeLocalEvent(OnAccent); + } + + private void OnAccent(Entity entity, ref AccentGetEvent args) + { + var message = args.Message; + var vowel = _random.Pick(_vowels); + var consonant = _random.Pick(_consonants); + args.Message = TryChangeInString(TryChangeInString(message, vowel, consonant, entity.Comp.Prob), + consonant, vowel, entity.Comp.Prob); + } + + private string TryChangeInString(string value, string key, string keyAddition, float prob) + { + var result = value; + var index = value.IndexOf(key); + if (index != -1) + { + if (_random.Prob(prob)) + { + result = value.Replace(key, key + keyAddition + key); + } + } + return result; + } +} diff --git a/Content.Server/SS220/MindSlave/DisfunctionSystems/WieldUnabilitySystem.cs b/Content.Server/SS220/MindSlave/DisfunctionSystems/WieldUnabilitySystem.cs new file mode 100644 index 00000000000000..5e340b2436c4ef --- /dev/null +++ b/Content.Server/SS220/MindSlave/DisfunctionSystems/WieldUnabilitySystem.cs @@ -0,0 +1,28 @@ +// © 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.Popups; +using Content.Server.SS220.MindSlave.DisfunctionComponents; +using Content.Shared.Wieldable; + +namespace Content.Server.SS220.MindSlave.DisfunctionSystem; + +public sealed class WieldUnabilitySystem : EntitySystem +{ + [Dependency] private readonly PopupSystem _popup = default!; + + public override void Initialize() + { + base.Initialize(); + + SubscribeLocalEvent(OnWieldAttempt); + } + + private void OnWieldAttempt(Entity entity, ref BeforeWieldEvent args) + { + if (args.Cancelled) + return; + + _popup.PopupCursor(Loc.GetString("unable-to-wield", ("user", entity.Owner)), entity); + args.Cancel(); + } +} diff --git a/Content.Server/SS220/MindSlave/MindSlaveEvent.cs b/Content.Server/SS220/MindSlave/MindSlaveEvent.cs new file mode 100644 index 00000000000000..6e572517de2eea --- /dev/null +++ b/Content.Server/SS220/MindSlave/MindSlaveEvent.cs @@ -0,0 +1,24 @@ +// © SS220, An EULA/CLA with a hosting restriction, full text: https://raw.githubusercontent.com/SerbiaStrong-220/space-station-14/master/CLA.txt + +/// +/// Event raised right after Mindslave component added to Entity on slave entity +/// +public sealed class AfterEntityMindSlavedEvent(EntityUid master, EntityUid slave) : EventArgs +{ + public EntityUid Master { get; } = master; + public EntityUid Slave { get; } = slave; +} + +/// +/// Event raised right after Mindslave component added to Entity om master entity +/// +public sealed class AfterEntityMindSlavedMasterEvent(EntityUid master, EntityUid slave) : EventArgs +{ + public EntityUid Master { get; } = master; + public EntityUid Slave { get; } = slave; +} + +public sealed class StopWordGeneratedEvent(string stopWord) : EventArgs +{ + public string StopWord { get; } = stopWord; +} diff --git a/Content.Server/SS220/MindSlave/MindSlaveSystem.cs b/Content.Server/SS220/MindSlave/MindSlaveSystem.cs index 985d83dbb7780f..d93c3000f4096f 100644 --- a/Content.Server/SS220/MindSlave/MindSlaveSystem.cs +++ b/Content.Server/SS220/MindSlave/MindSlaveSystem.cs @@ -11,7 +11,11 @@ using Content.Server.Objectives.Systems; using Content.Server.Popups; using Content.Server.Roles; +using Content.Server.Speech; +using Content.Server.SS220.MindSlave.Components; +using Content.Server.SS220.MindSlave.Systems; using Content.Server.SS220.MindSlave.UI; +using Content.Server.SS220.Telepathy; using Content.Shared.Alert; using Content.Shared.Cloning; using Content.Shared.CombatMode.Pacification; @@ -22,8 +26,8 @@ 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.SS220.Telepathy; using Content.Shared.Tag; using Robust.Server.Player; using Robust.Shared.Audio; @@ -35,12 +39,15 @@ public sealed class MindSlaveSystem : EntitySystem { [Dependency] private readonly PopupSystem _popup = default!; [Dependency] private readonly MindSystem _mind = default!; + [Dependency] private readonly MindSlaveStopWordSystem _mindSlaveStopWord = default!; + [Dependency] private readonly MindSlaveDisfunctionSystem _mindSlaveDisfunction = 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 TelepathySystem _telepathy = default!; [Dependency] private readonly TraitorRuleSystem _traitorRule = default!; [Dependency] private readonly AlertsSystem _alert = default!; [Dependency] private readonly IPlayerManager _playerManager = default!; @@ -77,12 +84,38 @@ public override void Initialize() { base.Initialize(); + SubscribeLocalEvent(OnInit); + SubscribeLocalEvent(OnAccent); + SubscribeLocalEvent(OnCloned); + SubscribeLocalEvent(OnMasterDeadOrCrit); SubscribeLocalEvent(OnMasterGibbed); - SubscribeLocalEvent(OnCloned); + SubscribeLocalEvent(OnMindSlaveRemoved); } + private void OnInit(Entity entity, ref MapInitEvent args) + { + if (TryComp(entity.Owner, out var mindSlaveDisfunction)) + { + _mindSlaveDisfunction.UnpauseDisfunction((entity.Owner, mindSlaveDisfunction)); + return; + } + } + + private void OnAccent(Entity entity, ref BeforeAccentGetEvent args) + { + var stopWord = entity.Comp.StopWord.ToLower(); + var message = args.Message; + var index = message.ToLower().IndexOf(stopWord); + while (index != -1 && !string.IsNullOrEmpty(message)) + { + message = message.Remove(index, stopWord.Length).Insert(index, Loc.GetString("mindslave-stop-word-replacement")); + index = message.ToLower().IndexOf(stopWord); + } + args.Message = message; + } + private void OnMasterGibbed(Entity entity, ref BeingGibbedEvent args) { if (entity.Comp.EnslavedEntities.Count <= 0) @@ -118,7 +151,8 @@ private void OnMindSlaveRemoved(Entity mind, ref Mind if (args.Slave == null || !IsEnslaved(args.Slave.Value)) return; - TryRemoveSlave(args.Slave.Value); + if (TryRemoveSlave(args.Slave.Value)) + _mindSlaveDisfunction.PauseDisfunction(args.Slave.Value); } //I need all of this for the TraitorMinds list @@ -193,13 +227,21 @@ public bool TryMakeSlave(EntityUid slave, EntityUid master) EnsureComp(slaveRoleVal).Briefing = briefing; } - EnsureComp(slave); + // Also dont delete Master's telephaty if he have another slave. + var mindSlaveComp = EnsureComp(slave); + RaiseLocalEvent(slave, new AfterEntityMindSlavedEvent(master, slave)); // uh... I wish I made it earlier... + // we write it in comp to give more freedom to admins + mindSlaveComp.StopWord = _mindSlaveStopWord.StopWord; + _alert.ShowAlert(slave, EnslavedAlert); var masterComp = EnsureComp(master); + RaiseLocalEvent(master, new AfterEntityMindSlavedMasterEvent(master, slave)); masterComp.EnslavedEntities.Add(slave); Dirty(master, masterComp); + MakeTelepathic(master, slave); + _npcFaction.RemoveFaction(slave, NanoTrasenFactionId, false); _npcFaction.AddFaction(slave, SyndicateFactionId); @@ -236,6 +278,8 @@ public bool TryRemoveSlave(EntityUid slave) _popup.PopupEntity(briefing, slave, slave, Shared.Popups.PopupType.LargeCaution); var master = mindSlave.Value.Comp2.masterEntity; + // goes here cause we want to have slave in masters slaved list + RemoveSlaveTelepathy(master, slave); 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)) : @@ -287,7 +331,6 @@ public bool TryRemoveSlave(EntityUid slave) if (mindslaveImplant != null) _implant.ForceRemove(slave, mindslaveImplant.Value); } - return true; } @@ -312,4 +355,40 @@ public bool IsEnslaved(EntityUid entity) return _role.MindHasRole(mindId); } + + private void MakeTelepathic(EntityUid master, EntityUid slave) + { + var telepathyChannel = TryComp(master, out var oldTelepathy) + ? oldTelepathy.TelepathyChannelPrototype + : _telepathy.TakeUniqueTelepathyChannel("mindslave-telepathy-channel-name", Color.DarkViolet); + + EnsureTelepathy(slave, telepathyChannel); + EnsureTelepathy(master, telepathyChannel); + } + + private void EnsureTelepathy(EntityUid target, ProtoId channelId) + { + var slaveTelepathy = EnsureComp(target); + slaveTelepathy.CanSend = true; + slaveTelepathy.TelepathyChannelPrototype = channelId; + } + + private void RemoveSlaveTelepathy(EntityUid? master, EntityUid slave) + { + if (!TryComp(master, out var mindSlaveMaster) + || mindSlaveMaster.EnslavedEntities.Count == 1) + { + if (TryComp(slave, out var telepathyComponent)) + { + _telepathy.FreeUniqueTelepathyChannel(telepathyComponent.TelepathyChannelPrototype); + } + else + { + Log.Warning($"{ToPrettyString(slave)} was freed from mindslave but dont have a {nameof(TelepathyComponent)}"); + } + return; + } + + RemComp(slave); + } } diff --git a/Content.Server/SS220/MindSlave/Systems/MindSlaveDisfunctionSystem.cs b/Content.Server/SS220/MindSlave/Systems/MindSlaveDisfunctionSystem.cs new file mode 100644 index 00000000000000..20af55c76d351e --- /dev/null +++ b/Content.Server/SS220/MindSlave/Systems/MindSlaveDisfunctionSystem.cs @@ -0,0 +1,177 @@ +// © 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.Chat.Managers; +using Content.Server.Mind; +using Content.Server.Popups; +using Content.Server.SS220.MindSlave.Components; +using Content.Shared.Damage; +using Content.Shared.Implants; +using Content.Shared.Mobs; +using Content.Shared.Mobs.Components; +using Robust.Shared.Random; +using Robust.Shared.Timing; + +namespace Content.Server.SS220.MindSlave.Systems; + +public sealed class MindSlaveDisfunctionSystem : EntitySystem +{ + [Dependency] private readonly IComponentFactory _component = default!; + [Dependency] private readonly IChatManager _chat = default!; + [Dependency] private readonly DamageableSystem _damageable = default!; + [Dependency] private readonly IGameTiming _gameTiming = default!; + [Dependency] private readonly MindSystem _mind = default!; + [Dependency] private readonly PopupSystem _popup = default!; + [Dependency] private readonly IRobustRandom _random = default!; + + private const float SecondsBetweenStageDamage = 4f; + + public override void Initialize() + { + base.Initialize(); + + SubscribeLocalEvent(OnInit); + SubscribeLocalEvent(OnRemove); + + SubscribeLocalEvent(OnProviderImplanted); + } + + public override void Update(float frameTime) + { + base.Update(frameTime); + + var query = EntityQueryEnumerator(); + while (query.MoveNext(out var uid, out var comp)) + { + if (!comp.Active) + return; + + if (comp.DisfunctionStage == MindSlaveDisfunctionType.Deadly + && _gameTiming.CurTime > comp.NextDeadlyDamageTime + && TryComp(uid, out var stateComponent) + && stateComponent.CurrentState != MobState.Dead) + { + _damageable.TryChangeDamage(uid, comp.DeadlyStageDamage, true); + comp.NextDeadlyDamageTime = _gameTiming.CurTime + TimeSpan.FromSeconds(SecondsBetweenStageDamage); + } + + if (_gameTiming.CurTime > comp.NextProgressTime) + { + ProgressDisfunction((uid, comp)); + comp.NextProgressTime = EvaluateNextProgressTime((uid, comp)); + } + } + } + + private void OnInit(Entity entity, ref MapInitEvent _) + { + entity.Comp.NextProgressTime = EvaluateNextProgressTime(entity); + } + + private void OnRemove(Entity entity, ref ComponentShutdown _) + { + // lets help admins a little + foreach (var comp in entity.Comp.DisfunctionComponents) + { + RemComp(entity.Owner, comp); + } + } + + private void OnProviderImplanted(Entity entity, ref ImplantImplantedEvent args) + { + if (args.Implanted == null) + return; + + var disfunctionComponent = EnsureComp(args.Implanted.Value); + disfunctionComponent.DisfunctionParameters = entity.Comp.Disfunction; + } + + public void ProgressDisfunction(Entity entity) + { + var (uid, comp) = entity; + if (!Resolve(uid, ref comp)) + return; + + if (comp.DisfunctionStage == MindSlaveDisfunctionType.Deadly + || (!comp.Deadly && comp.DisfunctionStage == MindSlaveDisfunctionType.Terminal)) + return; + + if (comp.Disfunction.TryGetValue(++comp.DisfunctionStage, out var disfunctionList)) + { + foreach (var compName in disfunctionList) + { + var disfunctionComponent = _component.GetComponent(_component.GetRegistration(compName).Type); + AddComp(uid, disfunctionComponent); + comp.DisfunctionComponents.Add(disfunctionComponent); + } + } + var progressMessage = Loc.GetString(comp.DisfunctionParameters.ProgressionPopup); + _popup.PopupEntity(progressMessage, entity, entity, Shared.Popups.PopupType.SmallCaution); + comp.Weakened = false; + + if (!_mind.TryGetMind(entity, out _, out var mindComponent)) + return; + + if (mindComponent.Session == null) + return; + + _chat.ChatMessageToOne(Shared.Chat.ChatChannel.Emotes, progressMessage, progressMessage, + default, false, mindComponent.Session.Channel, colorOverride: Color.Red); + } + + public void WeakDisfunction(Entity entity, float delayMinutes, int removeAmount) + { + var (uid, comp) = entity; + if (!Resolve(uid, ref comp)) + return; + + comp.Weakened = true; + comp.NextProgressTime += TimeSpan.FromMinutes(delayMinutes); + + foreach (var disfunctionComponent in _random.GetItems(comp.DisfunctionComponents, removeAmount, false)) + { + RemComp(uid, disfunctionComponent); + } + + if (comp.DisfunctionStage == MindSlaveDisfunctionType.Terminal) + comp.Deadly = true; + } + + public void PauseDisfunction(Entity entity) + { + if (!Resolve(entity.Owner, ref entity.Comp)) + return; + + if (entity.Comp.Active == false) + { + Log.Error("Tried to pause mind slave disfunction, but it is already paused"); + return; + } + + entity.Comp.Active = false; + entity.Comp.PausedTime = _gameTiming.CurTime; + } + + public void UnpauseDisfunction(Entity entity) + { + if (!Resolve(entity.Owner, ref entity.Comp)) + return; + + if (entity.Comp.Active == true) + { + Log.Error("Tried to unpause mind slave disfunction, but it is already active"); + return; + } + + entity.Comp.Active = true; + entity.Comp.NextProgressTime += _gameTiming.CurTime - entity.Comp.PausedTime; + } + + private TimeSpan EvaluateNextProgressTime(Entity entity) + { + var (_, comp) = entity; + + return _gameTiming.CurTime + TimeSpan.FromMinutes(comp.ConstMinutesBetweenStages) + + TimeSpan.FromMinutes(_random.NextFloat(-1f, 1f) * comp.MaxRandomMinutesBetweenStages); + } + +} diff --git a/Content.Server/SS220/MindSlave/Systems/MindSlaveStopWordSystem.cs b/Content.Server/SS220/MindSlave/Systems/MindSlaveStopWordSystem.cs new file mode 100644 index 00000000000000..a8b21dc695195a --- /dev/null +++ b/Content.Server/SS220/MindSlave/Systems/MindSlaveStopWordSystem.cs @@ -0,0 +1,125 @@ +// © SS220, An EULA/CLA with a hosting restriction, full text: https://raw.githubusercontent.com/SerbiaStrong-220/space-station-14/master/CLA.txt + +using System.Linq; +using Content.Server.SS220.MindSlave.Components; +using Content.Server.SS220.Photocopier; +using Content.Server.SS220.Photocopier.Forms; +using Content.Server.SS220.Text; +using Content.Shared.Dataset; +using Content.Shared.GameTicking; +using Content.Shared.Paper; +using Content.Shared.SS220.Photocopier.Forms.FormManagerShared; +using Robust.Shared.Prototypes; +using Robust.Shared.Random; + +namespace Content.Server.SS220.MindSlave.Systems; + +public sealed partial class MindSlaveStopWordSystem : EntitySystem +{ + [Dependency] private readonly MarkovTextGenerator _markovText = default!; + [Dependency] private readonly PaperSystem _paper = default!; + [Dependency] private readonly IPrototypeManager _prototype = default!; + [Dependency] private readonly PhotocopierSystem _photocopier = default!; + [Dependency] private readonly IRobustRandom _random = default!; + + private FormManager? _specificFormManager; + + private const int KeySize = 3; + private const int StopWordMinSize = 4; // to ignore some common words + private const string StampComponentName = "Stamp"; + + [ValidatePrototypeId] + private const string TextDatasetId = "MindSlaveStopWordTexts"; + + public string StopWord + { + get + { + if (_stopWord == string.Empty) + Log.Error("Asked for mind slave stop word but it is empty"); + return _stopWord; + } + } + + public string Text + { + get + { + if (_text == string.Empty) + Log.Error("Asked for mind slave stop word but it is empty"); + return _text; + } + } + + private string _stopWord = string.Empty; + private string _text = string.Empty; + + public override void Initialize() + { + base.Initialize(); + _specificFormManager = EntityManager.System(); + + SubscribeLocalEvent(OnRoundStart); + SubscribeLocalEvent(OnRoundEnded); + + SubscribeLocalEvent(OnInit); + } + + private void OnRoundStart(RoundStartedEvent args) + { + _markovText.Initialize(_prototype.Index(TextDatasetId).Values, KeySize); + _text = _markovText.GenerateText(83); + + _stopWord = _markovText.ReplacePunctuationInEnding(_random.Pick(_text.Split().Where(x => x.Length >= StopWordMinSize).ToArray())); + RaiseLocalEvent(new StopWordGeneratedEvent(_stopWord)); + } + + private void OnRoundEnded(RoundEndedEvent args) + { + _markovText.CleatData(); + _stopWord = string.Empty; + _text = string.Empty; + } + + private void OnInit(Entity entity, ref MapInitEvent args) + { + if (_specificFormManager is null) + return; + + var form = _specificFormManager.TryGetFormFromDescriptor(new FormDescriptor(entity.Comp.Collection, entity.Comp.Group, entity.Comp.Form)); + if (form == null) + { + Log.Error($"Invalid form description for entity{ToPrettyString(entity)}"); + return; + } + + _photocopier.FormToDataToCopy(form, out var dataToCopy, out var metaData); + var spawnedForm = _photocopier.SpawnCopy(Transform(entity).Coordinates, metaData, dataToCopy); + + if (!TryComp(spawnedForm, out var paperComponent) + || !TryComp(entity, out var entityPaperComponent)) + return; + // idea was that hos documents is container of this information + _paper.SetContent((entity.Owner, entityPaperComponent), paperComponent.Content.Replace("mindslave-stop-word-text", _text)); + QueueDel(spawnedForm); + + foreach (var stampProtoId in entity.Comp.StampList) + { + var stampProto = _prototype.Index(stampProtoId); + if (!stampProto.Components.TryGetComponent(StampComponentName, out var component) + || component is not StampComponent stampComponent) + { + Log.Error("Passed entity prototype id in mind slave stop word container's stamp list, but entry dont have stampComponent"); + return; + } + + + _paper.TryStamp((entity.Owner, entityPaperComponent), new StampDisplayInfo() + { + StampedColor = stampComponent.StampedColor, + StampedName = stampComponent.StampedName + }, + stampComponent.StampState); + } + } +} diff --git a/Content.Server/SS220/Surgery/Actions/SurgeryApplyBleedingAction.cs b/Content.Server/SS220/Surgery/Actions/SurgeryApplyBleedingAction.cs new file mode 100644 index 00000000000000..18ab87e7ce6769 --- /dev/null +++ b/Content.Server/SS220/Surgery/Actions/SurgeryApplyBleedingAction.cs @@ -0,0 +1,18 @@ +// © 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.Body.Systems; +using Content.Shared.SS220.Surgery.Graph; + +namespace Content.Server.SS220.Surgery.Action; + +[DataDefinition] +public sealed partial class ApplyBleedingSurgeryAction : ISurgeryGraphAction +{ + [DataField] + public float BleedAmount = 2f; + + public void PerformAction(EntityUid uid, EntityUid? userUid, EntityUid? used, IEntityManager entityManager) + { + entityManager.System().TryModifyBleedAmount(uid, BleedAmount); + } +} diff --git a/Content.Server/SS220/Surgery/Actions/SurgeryFixMindSlaveDisfunctionAction.cs b/Content.Server/SS220/Surgery/Actions/SurgeryFixMindSlaveDisfunctionAction.cs new file mode 100644 index 00000000000000..cb5d74142effc0 --- /dev/null +++ b/Content.Server/SS220/Surgery/Actions/SurgeryFixMindSlaveDisfunctionAction.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.Server.SS220.MindSlave.Systems; +using Content.Shared.SS220.MindSlave; +using Content.Shared.SS220.Surgery.Graph; + +namespace Content.Server.SS220.Surgery.Action; + +[DataDefinition] +public sealed partial class SurgeryFixMindSlaveDisfunctionAction : ISurgeryGraphAction +{ + public void PerformAction(EntityUid uid, EntityUid? userUid, EntityUid? used, IEntityManager entityManager) + { + if (!entityManager.TryGetComponent(used, out var fixer)) + { + entityManager.System().Log.Fatal($"Surgery step with mind slave fix successfully done without used having {nameof(MindSlaveDisfunctionFixerComponent)}"); + return; + } + + entityManager.System().WeakDisfunction(uid, fixer.DelayMinutes, fixer.RemoveAmount); + } +} diff --git a/Content.Server/SS220/Surgery/Systems/SurgerySystem.cs b/Content.Server/SS220/Surgery/Systems/SurgerySystem.cs new file mode 100644 index 00000000000000..dd2eda52f4d3c0 --- /dev/null +++ b/Content.Server/SS220/Surgery/Systems/SurgerySystem.cs @@ -0,0 +1,21 @@ +// © 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.Surgery.Components; +using Content.Shared.SS220.Surgery.Graph; + +namespace Content.Server.SS220.Surgery.Systems; + +public sealed partial class SurgerySystem : SharedSurgerySystem +{ + protected override void ProceedToNextStep(Entity entity, EntityUid user, EntityUid? used, SurgeryGraphEdge chosenEdge) + { + foreach (var action in SurgeryGraph.GetActions(chosenEdge)) + { + action.PerformAction(entity.Owner, user, used, EntityManager); + } + + base.ProceedToNextStep(entity, user, used, chosenEdge); + + Dirty(entity); + } +} diff --git a/Content.Server/SS220/Telepathy/TelepathySystem.cs b/Content.Server/SS220/Telepathy/TelepathySystem.cs index 966fc9d74e94e5..5ee54e745eca5e 100644 --- a/Content.Server/SS220/Telepathy/TelepathySystem.cs +++ b/Content.Server/SS220/Telepathy/TelepathySystem.cs @@ -1,7 +1,7 @@ // © 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.Chat.Systems; using Content.Shared.Chat; +using Content.Shared.GameTicking; using Content.Shared.SS220.Telepathy; using Robust.Shared.Network; using Robust.Shared.Player; @@ -19,14 +19,30 @@ public sealed class TelepathySystem : EntitySystem [Dependency] private readonly IEntityManager _entityManager = default!; [Dependency] private readonly IPrototypeManager _prototype = default!; + /// + /// Key is a "fake" protoId. It wont indexed. + /// + private SortedDictionary, ChannelParameters> _dynamicChannels = new(); + private readonly Color _baseDynamicChannelColor = Color.Lime; + /// public override void Initialize() { base.Initialize(); + SubscribeLocalEvent(OnRoundStart); + SubscribeLocalEvent(OnTelepathySend); SubscribeLocalEvent(OnTelepathyAnnouncementSend); } + private void OnRoundStart(RoundStartedEvent args) + { + foreach (var channel in _dynamicChannels) + { + FreeUniqueTelepathyChannel(channel.Key); + } + } + private void OnTelepathyAnnouncementSend(Entity ent, ref TelepathyAnnouncementSendEvent args) { SendMessageToEveryoneWithRightChannel(args.TelepathyChannel, args.Message, null); @@ -37,21 +53,81 @@ private void OnTelepathySend(Entity ent, ref TelepathySendEv SendMessageToEveryoneWithRightChannel(ent.Comp.TelepathyChannelPrototype, args.Message, ent); } - private void SendMessageToEveryoneWithRightChannel(ProtoId rightTelepathyChanel, string message, EntityUid? senderUid) + /// + /// Tries to get free channel from already constructed and if no exists makes new one + /// + public ProtoId TakeUniqueTelepathyChannel(string? nameLocPath = null, Color? color = null) { + return MakeNewDynamicChannel(nameLocPath, color); + } + + /// + /// Returns channel with to free channels pool. + /// if than checks and delete any other TelepathyComponent left with that id. + /// + public void FreeUniqueTelepathyChannel(ProtoId protoId, bool delete = true) + { + if (_dynamicChannels.TryGetValue(protoId, out var _)) + { + Log.Error($"Tried to free unregistered channel, passed id was {protoId}"); + return; + } + + Log.Debug($"Freed channel with id {protoId}"); + _dynamicChannels.Remove(protoId); + var query = EntityQueryEnumerator(); + while (query.MoveNext(out var uid, out var telepathyComponent)) + { + if (telepathyComponent.TelepathyChannelPrototype == protoId + && !telepathyComponent.Deleted) + { + if (delete) + RemComp(uid, telepathyComponent); + else + Log.Warning($"Fried channel, but telepathy components with this id {protoId} still exists"); + } + } + } + + private ProtoId MakeNewDynamicChannel(string? nameLocPath = null, Color? color = null) + { + var id = Loc.GetString("unique-telepathy-proto-id", ("id", _dynamicChannels.Count)); + + var channelColor = color ?? _baseDynamicChannelColor; + var channelName = nameLocPath ?? "unique-telepathy-proto-name"; + + _dynamicChannels.Add(id, new ChannelParameters(channelName, channelColor)); + + Log.Debug($"The channel with ID {id} was added to dynamics ones and used."); + return id; + } + + private void SendMessageToEveryoneWithRightChannel(ProtoId rightTelepathyChannel, string message, EntityUid? senderUid) + { + ChannelParameters? channelParameters = null; + if (_dynamicChannels.TryGetValue(rightTelepathyChannel, out var dynamicParameters)) + channelParameters = dynamicParameters; + if (_prototype.TryIndex(rightTelepathyChannel, out var prototype)) + channelParameters = prototype.ChannelParameters; + if (channelParameters == null) + { + Log.Error($"Tried to send message with incorrect {nameof(TelepathyChannelPrototype)} proto id. id was: {rightTelepathyChannel}"); + return; + } + var telepathyQuery = EntityQueryEnumerator(); while (telepathyQuery.MoveNext(out var receiverUid, out var receiverTelepathy)) { - if (rightTelepathyChanel == receiverTelepathy.TelepathyChannelPrototype) - SendMessageToChat(receiverUid, message, senderUid, _prototype.Index(rightTelepathyChanel)); + if (rightTelepathyChannel == receiverTelepathy.TelepathyChannelPrototype) + SendMessageToChat(receiverUid, message, senderUid, channelParameters); } } - private void SendMessageToChat(EntityUid receiverUid, string messageString, EntityUid? senderUid, TelepathyChannelPrototype telepathyChannel) + private void SendMessageToChat(EntityUid receiverUid, string messageString, EntityUid? senderUid, ChannelParameters telepathyChannelParameters) { var netSource = _entityManager.GetNetEntity(receiverUid); - var wrappedMessage = GetWrappedTelepathyMessage(messageString, senderUid, telepathyChannel); + var wrappedMessage = GetWrappedTelepathyMessage(messageString, senderUid, telepathyChannelParameters); var message = new ChatMessage( ChatChannel.Telepathy, messageString, @@ -60,10 +136,10 @@ private void SendMessageToChat(EntityUid receiverUid, string messageString, Enti null ); if (TryComp(receiverUid, out ActorComponent? actor)) - _netMan.ServerSendMessage(new MsgChatMessage() {Message = message}, actor.PlayerSession.Channel); + _netMan.ServerSendMessage(new MsgChatMessage() { Message = message }, actor.PlayerSession.Channel); } - private string GetWrappedTelepathyMessage(string messageString, EntityUid? senderUid, TelepathyChannelPrototype telepathyChannel) + private string GetWrappedTelepathyMessage(string messageString, EntityUid? senderUid, ChannelParameters telepathyChannelParameters) { if (senderUid == null) { @@ -75,10 +151,10 @@ private string GetWrappedTelepathyMessage(string messageString, EntityUid? sende return Loc.GetString( "chat-manager-send-telepathy-message", - ("channel", $"\\[{telepathyChannel.LocalizedName}\\]"), + ("channel", $"\\[{Loc.GetString(telepathyChannelParameters.Name)}\\]"), ("message", FormattedMessage.EscapeText(messageString)), ("senderName", GetSenderName(senderUid)), - ("color", telepathyChannel.Color) + ("color", telepathyChannelParameters.Color) ); } diff --git a/Content.Server/SS220/Text/MarkovTextGenerator.cs b/Content.Server/SS220/Text/MarkovTextGenerator.cs new file mode 100644 index 00000000000000..cc02cc83648064 --- /dev/null +++ b/Content.Server/SS220/Text/MarkovTextGenerator.cs @@ -0,0 +1,124 @@ +// Code under © SS220, An EULA/CLA with a hosting restriction, full text: https://raw.githubusercontent.com/SerbiaStrong-220/space-station-14/master/CLA.txt + +using System.Linq; +using System.Text; +using Robust.Shared.Random; + +namespace Content.Server.SS220.Text; + +public sealed class MarkovTextGenerator : EntitySystem +{ + [Dependency] private readonly IRobustRandom _random = default!; + + private Dictionary> _transitionMatrix = new(); + + private readonly char[] _punctuationChars = { ',', '.', '!', '?', ';', ':' }; + private readonly char[] _endSentenceChars = { '.', '!', '?' }; + + public void CleatData() + { + _transitionMatrix.Clear(); + } + + public void Initialize(IReadOnlyList locPath, int keySize) + { + CleatData(); + + foreach (var path in locPath) + { + StringBuilder text = new(Loc.GetString(path).ToLower()); + foreach (var punctuationChar in _punctuationChars) + { + text.Replace($"{punctuationChar}", $" {punctuationChar}"); + } + + + var words = text.ToString().Split().ToArray(); + + for (int i = 0; i < words.Length - keySize; i++) + { + var key = string.Join(' ', words, i, keySize); + var value = words[i + keySize]; + AddToTransitionMatrix(key, value); + } + } + } + + /// + /// Generates text using Markov chain. For initializing used text from . + /// + /// From this text will be generated new one. + /// Text generated with using Markov chain. + public string GenerateText(int wordCount) + { + if (_transitionMatrix.Count == 0) + Log.Error("Tried to generate text, but transition matrix is clear"); + + var tokenKey = _random.Pick(_transitionMatrix.Keys.Where(x => !StartsWithAnyChar(x, _punctuationChars)).ToList()); + var previousToken = ""; + var token = tokenKey.Split().Last(); + var text = new StringBuilder(FirstToUpper(tokenKey.Split().Aggregate(TextJoin))); + for (var step = 0; step < wordCount; step++) + { + previousToken = token; + if (_transitionMatrix.TryGetValue(tokenKey, out var tokens)) + token = _random.Pick(tokens); + else + token = _random.Pick(_random.Pick(_transitionMatrix.Keys).Split()); + + tokenKey = tokenKey.Split().Skip(1).Append(token).Aggregate(Join); + text.Append(TextString(previousToken, token)); + } + + return text.Append("..").ToString(); + } + + public string ReplacePunctuationInEnding(string value) + { + return new string(value.ToCharArray().Where(x => !_punctuationChars.Contains(x)).ToArray()); + } + + private void AddToTransitionMatrix(string key, string value) + { + if (_transitionMatrix.Keys.Contains(key)) + _transitionMatrix[key].Add(value); + else + _transitionMatrix.Add(key, [value]); + } + + private string TextJoin(string a, string b) + { + return a + TextString(a, b); + } + + private string TextString(string a, string b) + { + if (a.Length == 0 || b.Length == 0) + return b; + if (_endSentenceChars.Contains(a.Last())) + return " " + FirstToUpper(b); + if (_punctuationChars.Contains(b[0])) + return b; + return " " + b; + } + + private bool StartsWithAnyChar(string value, char[] chars) + { + foreach (var item in chars) + { + if (value.StartsWith(item)) + return true; + } + return false; + } + + private string FirstToUpper(string value) + { + return char.ToUpper(value[0]) + value.Remove(0, 1); + } + + private string Join(string a, string b) + { + return a + " " + b; + } +} diff --git a/Content.Server/Speech/AccentSystem.cs b/Content.Server/Speech/AccentSystem.cs index 7e29f02caece20..6ead2a6cd7c77c 100644 --- a/Content.Server/Speech/AccentSystem.cs +++ b/Content.Server/Speech/AccentSystem.cs @@ -15,8 +15,12 @@ public override void Initialize() private void AccentHandler(TransformSpeechEvent args) { - var accentEvent = new AccentGetEvent(args.Sender, args.Message); - + // SS220 Mindslave-stop-word begin + var beforeAccentGetEvent = new BeforeAccentGetEvent(args.Sender, args.Message); + RaiseLocalEvent(args.Sender, beforeAccentGetEvent, true); + var accentEvent = new AccentGetEvent(args.Sender, beforeAccentGetEvent.Message); + // var accentEvent = new AccentGetEvent(args.Sender, args.Message); + // SS220 Mindslave-stop-word end RaiseLocalEvent(args.Sender, accentEvent, true); args.Message = accentEvent.Message; } @@ -41,4 +45,28 @@ public AccentGetEvent(EntityUid entity, string message) Message = message; } } + // SS220 Mindslave-stop-word begin + /// + /// Raised before common accent event. Used for complex replacement. + /// + public sealed class BeforeAccentGetEvent : EntityEventArgs + { + /// + /// The entity to apply the accent to. + /// + public EntityUid Entity { get; } + + /// + /// The message to apply the accent transformation to. + /// Modify this to apply the accent. + /// + public string Message { get; set; } + + public BeforeAccentGetEvent(EntityUid entity, string message) + { + Entity = entity; + Message = message; + } + } + // SS220 Mindslave-stop-word end } diff --git a/Content.Shared/Buckle/SharedBuckleSystem.Interaction.cs b/Content.Shared/Buckle/SharedBuckleSystem.Interaction.cs index 1a15e52a3c4311..c45e2888d99313 100644 --- a/Content.Shared/Buckle/SharedBuckleSystem.Interaction.cs +++ b/Content.Shared/Buckle/SharedBuckleSystem.Interaction.cs @@ -4,6 +4,7 @@ using Content.Shared.DragDrop; using Content.Shared.IdentityManagement; using Content.Shared.Interaction; +using Content.Shared.SS220.BlockBuckleVerbsInteraction; using Content.Shared.Verbs; using Robust.Shared.Utility; @@ -126,6 +127,11 @@ private void AddStrapVerbs(EntityUid uid, StrapComponent component, GetVerbsEven if (args.Hands == null || !args.CanAccess || !args.CanInteract || !component.Enabled) return; + //ss220 fix revenant verbs start + if (HasComp(args.User)) + return; + //ss220 fix revenant verbs end + // Note that for whatever bloody reason, buckle component has its own interaction range. Additionally, this // range can be set per-component, so we have to check a modified InRangeUnobstructed for every verb. @@ -201,6 +207,11 @@ private void AddUnbuckleVerb(EntityUid uid, BuckleComponent component, GetVerbsE if (!args.CanAccess || !args.CanInteract || !component.Buckled) return; + //ss220 fix revenant verbs start + if (HasComp(args.User)) + return; + //ss220 fix revenant verbs end + InteractionVerb verb = new() { Act = () => TryUnbuckle(uid, args.User, buckleComp: component), diff --git a/Content.Shared/SS220/BlockBuckleVerbsInteraction/BlockBuckleVerbsInteractionComponent.cs b/Content.Shared/SS220/BlockBuckleVerbsInteraction/BlockBuckleVerbsInteractionComponent.cs new file mode 100644 index 00000000000000..59400022e30913 --- /dev/null +++ b/Content.Shared/SS220/BlockBuckleVerbsInteraction/BlockBuckleVerbsInteractionComponent.cs @@ -0,0 +1,7 @@ +namespace Content.Shared.SS220.BlockBuckleVerbsInteraction; + +[RegisterComponent] +public sealed partial class BlockBuckleVerbsInteractionComponent : Component +{ + +} diff --git a/Content.Shared/SS220/MindSlave/MindSlaveComponent.cs b/Content.Shared/SS220/MindSlave/MindSlaveComponent.cs index f0eef6bc7ab23d..b4e073436b8506 100644 --- a/Content.Shared/SS220/MindSlave/MindSlaveComponent.cs +++ b/Content.Shared/SS220/MindSlave/MindSlaveComponent.cs @@ -1,6 +1,5 @@ // © 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; @@ -11,7 +10,12 @@ namespace Content.Shared.SS220.MindSlave; /// Used to mark an entity as a mind-slave. /// [RegisterComponent, NetworkedComponent] +[AutoGenerateComponentState] public sealed partial class MindSlaveComponent : Component { + [ViewVariables(VVAccess.ReadWrite)] + [AutoNetworkedField] + public string StopWord = string.Empty; + public ProtoId StatusIcon { get; set; } = "MindSlaveIcon"; } diff --git a/Content.Shared/SS220/MindSlave/MindSlaveDisfunctionFixerComponent.cs b/Content.Shared/SS220/MindSlave/MindSlaveDisfunctionFixerComponent.cs new file mode 100644 index 00000000000000..805bc309a09ad7 --- /dev/null +++ b/Content.Shared/SS220/MindSlave/MindSlaveDisfunctionFixerComponent.cs @@ -0,0 +1,24 @@ +// © SS220, An EULA/CLA with a hosting restriction, full text: https://raw.githubusercontent.com/SerbiaStrong-220/space-station-14/master/CLA.txt + +using Robust.Shared.GameStates; + +namespace Content.Shared.SS220.MindSlave; + +[RegisterComponent, NetworkedComponent] +[AutoGenerateComponentState] +public sealed partial class MindSlaveDisfunctionFixerComponent : Component +{ + /// + /// How much disfunction components will be removed. Can be allied on each stage of disfunction. + /// + [DataField("remove")] + [AutoNetworkedField] + public int RemoveAmount = 2; + + /// + /// How much will be added until disfunction progress. Can be allied on each stage of disfunction. + /// + [DataField] + [AutoNetworkedField] + public float DelayMinutes = 5; +} diff --git a/Content.Shared/SS220/Surgery/Components/OnSurgeryComponent.cs b/Content.Shared/SS220/Surgery/Components/OnSurgeryComponent.cs new file mode 100644 index 00000000000000..56d84305601036 --- /dev/null +++ b/Content.Shared/SS220/Surgery/Components/OnSurgeryComponent.cs @@ -0,0 +1,23 @@ +// © 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.Surgery.Graph; +using Robust.Shared.GameStates; +using Robust.Shared.Prototypes; + +namespace Content.Shared.SS220.Surgery.Components; + +/// +/// This component applies to entities on which operation is started. +/// +[RegisterComponent] +[NetworkedComponent, AutoGenerateComponentState] +public sealed partial class OnSurgeryComponent : Component +{ + [ViewVariables(VVAccess.ReadOnly)] + [AutoNetworkedField] + public ProtoId SurgeryGraphProtoId; + + [ViewVariables] + [AutoNetworkedField] + public string? CurrentNode; +} diff --git a/Content.Shared/SS220/Surgery/Components/SurgableComponent.cs b/Content.Shared/SS220/Surgery/Components/SurgableComponent.cs new file mode 100644 index 00000000000000..b1a6754a097c47 --- /dev/null +++ b/Content.Shared/SS220/Surgery/Components/SurgableComponent.cs @@ -0,0 +1,10 @@ +// © SS220, An EULA/CLA with a hosting restriction, full text: https://raw.githubusercontent.com/SerbiaStrong-220/space-station-14/master/CLA.txt + +namespace Content.Shared.SS220.Surgery.Components; + +/// +/// This component used to define which entities can be target of surgery +/// +[RegisterComponent] +public sealed partial class SurgableComponent : Component +{ } diff --git a/Content.Shared/SS220/Surgery/Components/SurgeryDrapeComponent.cs b/Content.Shared/SS220/Surgery/Components/SurgeryDrapeComponent.cs new file mode 100644 index 00000000000000..15a348a56c0f9a --- /dev/null +++ b/Content.Shared/SS220/Surgery/Components/SurgeryDrapeComponent.cs @@ -0,0 +1,10 @@ +// © SS220, An EULA/CLA with a hosting restriction, full text: https://raw.githubusercontent.com/SerbiaStrong-220/space-station-14/master/CLA.txt + +namespace Content.Shared.SS220.Surgery.Components; + +/// +/// This component used to define items which can start operations +/// +[RegisterComponent] +public sealed partial class SurgeryDrapeComponent : Component +{ } diff --git a/Content.Shared/SS220/Surgery/Components/SurgeryToolComponent.cs b/Content.Shared/SS220/Surgery/Components/SurgeryToolComponent.cs new file mode 100644 index 00000000000000..f3dee1a4db7258 --- /dev/null +++ b/Content.Shared/SS220/Surgery/Components/SurgeryToolComponent.cs @@ -0,0 +1,27 @@ +// © SS220, An EULA/CLA with a hosting restriction, full text: https://raw.githubusercontent.com/SerbiaStrong-220/space-station-14/master/CLA.txt + +using Robust.Shared.Audio; + +namespace Content.Shared.SS220.Surgery.Components; + +[RegisterComponent] +public sealed partial class SurgeryToolComponent : Component +{ + [DataField(required: true), ViewVariables(VVAccess.ReadWrite)] + public SurgeryToolType ToolType = SurgeryToolType.Invalid; + + [DataField("sound")] + public SoundSpecifier? UsingSound = null; +} + +// for now I need only this ones +public enum SurgeryToolType +{ + Invalid = -1, + Specific, + Scalpel, + Retractor, + Hemostat, + Saw, + Cautery +} diff --git a/Content.Shared/SS220/Surgery/Conditions/SurgeryHaveComponentCondition.cs b/Content.Shared/SS220/Surgery/Conditions/SurgeryHaveComponentCondition.cs new file mode 100644 index 00000000000000..2e6643bf94eacd --- /dev/null +++ b/Content.Shared/SS220/Surgery/Conditions/SurgeryHaveComponentCondition.cs @@ -0,0 +1,29 @@ +// © 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.Surgery.Graph; +using JetBrains.Annotations; + +namespace Content.Shared.SS220.Surgery.Conditions; + +[UsedImplicitly] +[Serializable] +[DataDefinition] +public sealed partial class SurgeryHaveComponentCondition : ISurgeryGraphCondition +{ + [DataField(required: true)] + public string Component = ""; + + public bool Condition(EntityUid uid, IEntityManager entityManager) + { + var compReg = entityManager.ComponentFactory.GetRegistration(Component); + if (entityManager.HasComponent(uid, compReg.Type)) + return true; + + return false; + } + + public void DoScanExamine() + { + + } +} diff --git a/Content.Shared/SS220/Surgery/Conditions/SurgeryToolCondition.cs b/Content.Shared/SS220/Surgery/Conditions/SurgeryToolCondition.cs new file mode 100644 index 00000000000000..a77054bd8db11a --- /dev/null +++ b/Content.Shared/SS220/Surgery/Conditions/SurgeryToolCondition.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.Shared.SS220.Surgery.Components; +using Content.Shared.SS220.Surgery.Graph; +using JetBrains.Annotations; +using Robust.Shared.Serialization; + +namespace Content.Shared.SS220.Surgery.Conditions; + +[UsedImplicitly] +[Serializable, NetSerializable] +[DataDefinition] +public sealed partial class SurgeryToolTypeCondition : ISurgeryGraphCondition +{ + [DataField(required: true)] + public SurgeryToolType SurgeryTool = SurgeryToolType.Invalid; + + public bool Condition(EntityUid uid, IEntityManager entityManager) + { + if (!entityManager.TryGetComponent(uid, out var surgeryTool) + || surgeryTool.ToolType != SurgeryTool) + return false; + + return true; + } + + public void DoScanExamine() + { + + } +} diff --git a/Content.Shared/SS220/Surgery/Graph/AbstractSurgeryGraphPart.cs b/Content.Shared/SS220/Surgery/Graph/AbstractSurgeryGraphPart.cs new file mode 100644 index 00000000000000..30de1d82fd70bb --- /dev/null +++ b/Content.Shared/SS220/Surgery/Graph/AbstractSurgeryGraphPart.cs @@ -0,0 +1,27 @@ +// © SS220, An EULA/CLA with a hosting restriction, full text: https://raw.githubusercontent.com/SerbiaStrong-220/space-station-14/master/CLA.txt + +using Robust.Shared.Prototypes; + +namespace Content.Shared.SS220.Surgery.Graph; + +[Prototype("abstractSurgeryNode")] +public sealed partial class AbstractSurgeryNodePrototype : IPrototype +{ + [ViewVariables] + [IdDataField] + public string ID { get; private set; } = default!; + + [DataField(priority: 0)] + public SurgeryGraphNode Node = new(); +} + +[Prototype("abstractSurgeryEdge")] +public sealed partial class AbstractSurgeryEdgePrototype : IPrototype +{ + [ViewVariables] + [IdDataField] + public string ID { get; private set; } = default!; + + [DataField(priority: 0)] + public SurgeryGraphEdge Edge = new(); +} diff --git a/Content.Shared/SS220/Surgery/Graph/SurgeryGraph.cs b/Content.Shared/SS220/Surgery/Graph/SurgeryGraph.cs new file mode 100644 index 00000000000000..6d6145e443a18a --- /dev/null +++ b/Content.Shared/SS220/Surgery/Graph/SurgeryGraph.cs @@ -0,0 +1,73 @@ +// Original code from construction graph all edits under © SS220, An EULA/CLA with a hosting restriction, full text: https://raw.githubusercontent.com/SerbiaStrong-220/space-station-14/master/CLA.txt + +using System.Diagnostics.CodeAnalysis; +using Robust.Shared.Prototypes; +using Robust.Shared.Serialization; + +namespace Content.Shared.SS220.Surgery.Graph; + +[Prototype("surgeryGraph")] +public sealed partial class SurgeryGraphPrototype : IPrototype, ISerializationHooks +{ + [ViewVariables] + [IdDataField] + public string ID { get; private set; } = default!; + + [DataField(required: true)] + public string Start { get; private set; } = default!; + + [DataField(required: true)] + public string End { get; private set; } = default!; + + [DataField("graph", priority: 0)] + private List _graph = new(); + + /// + /// For debugging purposes + /// + public IReadOnlyList Nodes => _graph; + + + public SurgeryGraphNode? GetStartNode() + { + TryGetNode(Start, out var startNode); + return startNode!; + } + + public SurgeryGraphNode GetEndNode() + { + TryGetNode(End, out var endNode); + return endNode!; + } + + public bool GetEndNode([NotNullWhen(true)] out SurgeryGraphNode? endNode) + { + if (!TryGetNode(End, out endNode)) + return false; + + return true; + } + public bool TryGetNode(string target, [NotNullWhen(true)] out SurgeryGraphNode? foundNode) + { + foundNode = null; + foreach (var node in _graph) + { + if (node.Name == target) + { + foundNode = node; + break; + } + } + + return foundNode != null; + } + + void ISerializationHooks.AfterDeserialization() + { + if (!TryGetNode(Start, out var _)) + throw new Exception($"No start node in surgery graph {ID}"); + + if (!TryGetNode(End, out var _)) + throw new Exception($"No end node in surgery graph {ID}"); + } +} diff --git a/Content.Shared/SS220/Surgery/Graph/SurgeryGraphEdge.cs b/Content.Shared/SS220/Surgery/Graph/SurgeryGraphEdge.cs new file mode 100644 index 00000000000000..91d9ba78e5eef5 --- /dev/null +++ b/Content.Shared/SS220/Surgery/Graph/SurgeryGraphEdge.cs @@ -0,0 +1,54 @@ +// Original code from construction graph all edits under © SS220, An EULA/CLA with a hosting restriction, full text: https://raw.githubusercontent.com/SerbiaStrong-220/space-station-14/master/CLA.txt + +using Robust.Shared.Audio; +using Robust.Shared.Prototypes; +using Robust.Shared.Serialization; + +namespace Content.Shared.SS220.Surgery.Graph; + +[Serializable] +[DataDefinition] +public sealed partial class SurgeryGraphEdge : ISerializationHooks +{ + [DataField("to", required: true)] + public string Target = string.Empty; + + [DataField] + public ProtoId? BaseEdge { get; private set; } + + [DataField("conditions")] + private ISurgeryGraphCondition[] _conditions = Array.Empty(); + + [DataField("actions", serverOnly: true)] + private ISurgeryGraphAction[] _actions = Array.Empty(); + + /// + /// Time which this step takes in seconds + /// + [DataField] + public float? Delay { get; private set; } + + /// + /// This sound will be played when graph gets to target node + /// + [DataField("sound")] + public SoundSpecifier? EndSound { get; private set; } = null; + + /// + /// Don't know what u are doing? -> use + /// + [ViewVariables] + public IReadOnlyList Conditions => _conditions; + + /// + /// Don't know what u are doing? -> use + /// + [ViewVariables] + public IReadOnlyList Actions => _actions; + + void ISerializationHooks.AfterDeserialization() + { + if (Delay == null && BaseEdge == null) + throw new Exception($"Null delay found in edge targeted to {Target}"); + } +} diff --git a/Content.Shared/SS220/Surgery/Graph/SurgeryGraphInterfaces.cs b/Content.Shared/SS220/Surgery/Graph/SurgeryGraphInterfaces.cs new file mode 100644 index 00000000000000..7135fba631d9de --- /dev/null +++ b/Content.Shared/SS220/Surgery/Graph/SurgeryGraphInterfaces.cs @@ -0,0 +1,17 @@ +// Original code from construction graph all edits under © SS220, An EULA/CLA with a hosting restriction, full text: https://raw.githubusercontent.com/SerbiaStrong-220/space-station-14/master/CLA.txt + +namespace Content.Shared.SS220.Surgery.Graph; + +[ImplicitDataDefinitionForInheritors] +public partial interface ISurgeryGraphCondition +{ + bool Condition(EntityUid uid, IEntityManager entityManager); + void DoScanExamine(); // surgery_TODO: make it seen in med scanner +} + +[ImplicitDataDefinitionForInheritors] +public partial interface ISurgeryGraphAction +{ + void PerformAction(EntityUid uid, EntityUid? userUid, EntityUid? used, IEntityManager entityManager); +} + diff --git a/Content.Shared/SS220/Surgery/Graph/SurgeryGraphNode.cs b/Content.Shared/SS220/Surgery/Graph/SurgeryGraphNode.cs new file mode 100644 index 00000000000000..f0f941e613a2a4 --- /dev/null +++ b/Content.Shared/SS220/Surgery/Graph/SurgeryGraphNode.cs @@ -0,0 +1,39 @@ +// Original code from construction graph all edits under © SS220, An EULA/CLA with a hosting restriction, full text: https://raw.githubusercontent.com/SerbiaStrong-220/space-station-14/master/CLA.txt + +using Robust.Shared.Prototypes; + +namespace Content.Shared.SS220.Surgery.Graph; + +[Serializable] +[DataDefinition] +public sealed partial class SurgeryGraphNode +{ + [DataField("node", required: true)] + public string Name { get; private set; } = default!; + + [DataField] + public ProtoId? BaseNode { get; private set; } + + [DataField] + public NodeTextDescription NodeText = new(); + + [DataField("edges")] + private SurgeryGraphEdge[] _edges = Array.Empty(); + + [ViewVariables] + public IReadOnlyList Edges => _edges; + +} + +[DataDefinition] +public sealed partial class NodeTextDescription +{ + [DataField] + public string? ExamineDescription; + + [DataField] + public string? Popup; + + [DataField] + public string? Description; +} diff --git a/Content.Shared/SS220/Surgery/Graph/SurgeryGraphSystem.cs b/Content.Shared/SS220/Surgery/Graph/SurgeryGraphSystem.cs new file mode 100644 index 00000000000000..def3c4e2ac2b4f --- /dev/null +++ b/Content.Shared/SS220/Surgery/Graph/SurgeryGraphSystem.cs @@ -0,0 +1,78 @@ +// © SS220, An EULA/CLA with a hosting restriction, full text: https://raw.githubusercontent.com/SerbiaStrong-220/space-station-14/master/CLA.txt + +using Robust.Shared.Audio; +using Robust.Shared.Prototypes; + +namespace Content.Shared.SS220.Surgery.Graph; + +public sealed class SurgeryGraphSystem : EntitySystem +{ + [Dependency] private readonly IPrototypeManager _prototypeManager = default!; + + public SoundSpecifier? GetSoundSpecifier(SurgeryGraphEdge edge) + { + return Get(edge, (x) => x.EndSound); + } + + public IReadOnlyList GetActions(SurgeryGraphEdge edge) + { + return GetList(edge, (x) => x.Actions); + } + + public IReadOnlyList GetConditions(SurgeryGraphEdge edge) + { + return GetList(edge, (x) => x.Conditions); + } + + public string? ExamineDescription(SurgeryGraphNode node) + { + return Get(node, (x) => x.NodeText.ExamineDescription); + } + + public string? Description(SurgeryGraphNode node) + { + return Get(node, (x) => x.NodeText.Description); + } + + public string? Popup(SurgeryGraphNode node) + { + return Get(node, (x) => x.NodeText.Popup); + } + + public float? Delay(SurgeryGraphEdge edge) + { + return Get(edge, (x) => x.Delay); + } + + public IReadOnlyList GetList(SurgeryGraphEdge edge, Func> listGetter) where T : class + { + if (edge.BaseEdge.HasValue + && listGetter(edge).Count == 0 + && _prototypeManager.TryIndex(edge.BaseEdge, out var baseEdgeProto)) + return listGetter(baseEdgeProto.Edge); + + return listGetter(edge); + } + + public T? Get(SurgeryGraphNode node, Func getter) + { + if (getter(node) != null) + return getter(node); + + if (_prototypeManager.TryIndex(node.BaseNode, out var prototype)) + return getter(prototype.Node); + + return default; + } + + public T? Get(SurgeryGraphEdge edge, Func getter) + { + if (getter(edge) != null) + return getter(edge); + + if (_prototypeManager.TryIndex(edge.BaseEdge, out var prototype)) + return getter(prototype.Edge); + + return default; + } +} diff --git a/Content.Shared/SS220/Surgery/SurgeryEvent.cs b/Content.Shared/SS220/Surgery/SurgeryEvent.cs new file mode 100644 index 00000000000000..bbc0c1d4d381e6 --- /dev/null +++ b/Content.Shared/SS220/Surgery/SurgeryEvent.cs @@ -0,0 +1,16 @@ +// © 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.Surgery.Graph; +using Content.Shared.DoAfter; +using Robust.Shared.Serialization; + +[Serializable, NetSerializable] +public sealed partial class SurgeryDoAfterEvent : SimpleDoAfterEvent +{ + public string TargetEdge; + + public SurgeryDoAfterEvent(string targetEdge) : base() + { + TargetEdge = targetEdge; + } +} diff --git a/Content.Shared/SS220/Surgery/Systems/SharedSurgerySystem.Helpers.cs b/Content.Shared/SS220/Surgery/Systems/SharedSurgerySystem.Helpers.cs new file mode 100644 index 00000000000000..f02e8ed20554af --- /dev/null +++ b/Content.Shared/SS220/Surgery/Systems/SharedSurgerySystem.Helpers.cs @@ -0,0 +1,84 @@ +// © 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.Audio; +using Content.Shared.SS220.MindSlave; +using Content.Shared.SS220.Surgery.Components; +using Content.Shared.SS220.Surgery.Graph; + +namespace Content.Server.SS220.Surgery.Systems; + +public abstract partial class SharedSurgerySystem +{ + protected bool IsValidTarget(EntityUid uid, out string? reasonLocPath) + { + reasonLocPath = null; + if (HasComp(uid) + || !HasComp(uid) // for now only for slaves + || !HasComp(uid)) + return false; + + if (!_buckleSystem.IsBuckled(uid)) + { + reasonLocPath = "surgery-invalid-target-buckle"; + return false; + } + + return true; + } + + protected bool IsValidPerformer(EntityUid uid) + { + if (!HasComp(uid)) // for now only for masters + return false; + + return true; + } + + protected bool OperationEnded(Entity entity) + { + var surgeryProto = _prototype.Index(entity.Comp.SurgeryGraphProtoId); + + if (entity.Comp.CurrentNode != surgeryProto.GetEndNode().Name) + return false; + + return true; + } + + protected virtual void ProceedToNextStep(Entity entity, EntityUid user, EntityUid? used, SurgeryGraphEdge chosenEdge) + { + ChangeSurgeryNode(entity, chosenEdge.Target, user, used); + + _audio.PlayPredicted(SurgeryGraph.GetSoundSpecifier(chosenEdge), entity.Owner, user, + AudioHelpers.WithVariation(0.125f, _random).WithVolume(1f)); + + if (OperationEnded(entity)) + RemComp(entity.Owner); + } + + protected void ChangeSurgeryNode(Entity entity, string targetNode, EntityUid performer, EntityUid? used) + { + var surgeryProto = _prototype.Index(entity.Comp.SurgeryGraphProtoId); + ChangeSurgeryNode(entity, performer, used, targetNode, surgeryProto); + } + + protected void StartSurgeryNode(Entity entity, EntityUid performer, EntityUid? used) + { + var surgeryProto = _prototype.Index(entity.Comp.SurgeryGraphProtoId); + ChangeSurgeryNode(entity, performer, used, surgeryProto.Start, surgeryProto); + } + + protected void ChangeSurgeryNode(Entity entity, EntityUid performer, EntityUid? used, string targetNode, SurgeryGraphPrototype surgeryGraph) + { + if (!surgeryGraph.TryGetNode(targetNode, out var foundNode)) + { + Log.Error($"No start node on graph {entity.Comp.SurgeryGraphProtoId} with name {targetNode}"); + return; + } + + entity.Comp.CurrentNode = foundNode.Name; + if (SurgeryGraph.Popup(foundNode) != null) + _popup.PopupPredicted(Loc.GetString(SurgeryGraph.Popup(foundNode)!, ("target", entity.Owner), + ("user", performer), ("used", used == null ? Loc.GetString("surgery-null-used") : used)), entity.Owner, performer); + // hands/pawns uh... + } +} diff --git a/Content.Shared/SS220/Surgery/Systems/SharedSurgerySystem.cs b/Content.Shared/SS220/Surgery/Systems/SharedSurgerySystem.cs new file mode 100644 index 00000000000000..c035c64a54259d --- /dev/null +++ b/Content.Shared/SS220/Surgery/Systems/SharedSurgerySystem.cs @@ -0,0 +1,206 @@ +// © 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.Audio; +using Content.Shared.DoAfter; +using Content.Shared.Interaction; +using Content.Shared.Popups; +using Content.Shared.SS220.Surgery.Graph; +using Content.Shared.SS220.Surgery.Components; +using Robust.Shared.Audio.Systems; +using Robust.Shared.Prototypes; +using Robust.Shared.Random; +using Content.Shared.Buckle; +using Content.Shared.Examine; +using Robust.Shared.Network; + +namespace Content.Server.SS220.Surgery.Systems; + +public abstract partial class SharedSurgerySystem : EntitySystem +{ + [Dependency] protected readonly SurgeryGraphSystem SurgeryGraph = default!; + [Dependency] private readonly SharedAudioSystem _audio = default!; + [Dependency] private readonly SharedBuckleSystem _buckleSystem = default!; + [Dependency] private readonly SharedDoAfterSystem _doAfter = default!; + [Dependency] private readonly INetManager _netManager = default!; + [Dependency] private readonly SharedPopupSystem _popup = default!; + [Dependency] private readonly IPrototypeManager _prototype = default!; + [Dependency] private readonly IRobustRandom _random = default!; + + private const float ErrorGettingDelayDelay = 8f; + private const float DoAfterMovementThreshold = 0.15f; + private const int SurgeryExaminePushPriority = -1; + + public override void Initialize() + { + base.Initialize(); + + SubscribeLocalEvent(OnSurgeryInteractUsing); + SubscribeLocalEvent(OnExamined); + SubscribeLocalEvent>((uid, comp, ev) => + { + BuckleDoAfterEarly((uid, comp), ev.Event, ev); + }); + SubscribeLocalEvent(OnSurgeryDoAfter); + + SubscribeLocalEvent(OnDrapeInteract); + } + + /// + /// Yes, for now surgery is forced to have something done with surgeryTool + /// + private void OnSurgeryInteractUsing(Entity entity, ref InteractUsingEvent args) + { + if (args.Handled || !TryComp(args.Used, out var surgeryTool)) + return; + + args.Handled = TryPerformOperationStep(entity, (args.Used, surgeryTool), args.User); + } + + private void OnExamined(Entity entity, ref ExaminedEvent args) + { + if (entity.Comp.CurrentNode == null) + return; + + var graphProto = _prototype.Index(entity.Comp.SurgeryGraphProtoId); + if (!graphProto.TryGetNode(entity.Comp.CurrentNode, out var currentNode)) + return; + + if (entity.Comp.CurrentNode != null + && SurgeryGraph.ExamineDescription(currentNode) != null) + args.PushMarkup(Loc.GetString(SurgeryGraph.ExamineDescription(currentNode)!), SurgeryExaminePushPriority); + } + + private void BuckleDoAfterEarly(Entity entity, SurgeryDoAfterEvent args, CancellableEntityEventArgs ev) + { + if (args.Target == null || args.Used == null) + return; + + if (!_buckleSystem.IsBuckled(args.Target.Value)) + ev.Cancel(); + } + + private void OnSurgeryDoAfter(Entity entity, ref SurgeryDoAfterEvent args) + { + if (args.Cancelled || entity.Comp.CurrentNode == null) + return; + + var operationProto = _prototype.Index(entity.Comp.SurgeryGraphProtoId); + if (!operationProto.TryGetNode(entity.Comp.CurrentNode, out var node)) + return; + + SurgeryGraphEdge? targetEdge = null; + foreach (var edge in node.Edges) + { + if (edge.Target == args.TargetEdge) + { + targetEdge = edge; + break; + } + } + + if (targetEdge == null) + { + if (_netManager.IsServer) + { + Log.Error("Got wrong target edge in surgery do after!"); + } + return; + } + + ProceedToNextStep(entity, args.User, args.Used, targetEdge); + } + + private void OnDrapeInteract(Entity entity, ref AfterInteractEvent args) + { + if (args.Handled || args.Target == null) + return; + + if (!IsValidTarget(args.Target.Value, out var reasonLocPath) || !IsValidPerformer(args.User)) + { + _popup.PopupCursor(reasonLocPath != null ? Loc.GetString(reasonLocPath) : null); + return; + } + //SS220_Surgery: here must open UI and from it you should get protoId of surgery + + args.Handled = TryStartSurgery(args.Target.Value, "MindSlaveFix", args.User, args.Used); + } + + public bool TryStartSurgery(EntityUid target, ProtoId surgery, EntityUid performer, EntityUid used) + { + if (HasComp(target)) + { + Log.Error("Patient which is already on surgery is tried for surgery again"); + return false; + } + + var onSurgery = AddComp(target); + onSurgery.SurgeryGraphProtoId = surgery; + + StartSurgeryNode((target, onSurgery), performer, used); + + return true; + } + + /// true if operation step performed successful + public bool TryPerformOperationStep(Entity entity, Entity used, EntityUid user) + { + if (entity.Comp.CurrentNode == null) + { + Log.Fatal("Tried to perform operation with null node or surgery graph proto"); + return false; + } + + var graphProto = _prototype.Index(entity.Comp.SurgeryGraphProtoId); + if (!graphProto.TryGetNode(entity.Comp.CurrentNode, out var currentNode)) + { + Log.Fatal($"Current node of {ToPrettyString(entity)} has incorrect value {entity.Comp.CurrentNode} for graph proto {entity.Comp.SurgeryGraphProtoId}"); + return false; + } + + SurgeryGraphEdge? chosenEdge = null; + bool isAbleToPerform = false; + foreach (var edge in currentNode.Edges) + { + // id any edges exist make it true + isAbleToPerform = true; + foreach (var condition in SurgeryGraph.GetConditions(edge)) + { + if (!condition.Condition(used, EntityManager)) + isAbleToPerform = false; + } + // if passed all conditions than break + if (isAbleToPerform) + { + chosenEdge = edge; + break; + } + } + // yep.. another check + if (chosenEdge == null) + return false; + + // lets be honest, I don't believe that everyone will check their's surgeryGraphPrototype mapping + var delay = SurgeryGraph.Delay(chosenEdge); + if (delay == null) + { + Log.Fatal($"Found edge with zero delay, graph id: {entity.Comp.SurgeryGraphProtoId}"); + delay = ErrorGettingDelayDelay; + } + + var performerDoAfterEventArgs = + new DoAfterArgs(EntityManager, user, TimeSpan.FromSeconds(delay.Value), + new SurgeryDoAfterEvent(chosenEdge.Target), entity.Owner, target: entity.Owner, used: used.Owner) + { + NeedHand = true, + BreakOnMove = true, + MovementThreshold = DoAfterMovementThreshold, + AttemptFrequency = AttemptFrequency.EveryTick + }; + + if (_doAfter.TryStartDoAfter(performerDoAfterEventArgs)) + _audio.PlayPredicted(used.Comp.UsingSound, entity.Owner, user, + AudioHelpers.WithVariation(0.125f, _random).WithVolume(1f)); + + return true; + } +} diff --git a/Content.Shared/SS220/Telepathy/TelepathyChannelPrototype.cs b/Content.Shared/SS220/Telepathy/TelepathyChannelPrototype.cs index 529b3c904897a2..2593f84e189187 100644 --- a/Content.Shared/SS220/Telepathy/TelepathyChannelPrototype.cs +++ b/Content.Shared/SS220/Telepathy/TelepathyChannelPrototype.cs @@ -7,19 +7,25 @@ namespace Content.Shared.SS220.Telepathy; [Prototype("telepathyChannel")] public sealed partial class TelepathyChannelPrototype : IPrototype { - /// - /// Human-readable name for the channel. - /// - [DataField("name")] - public string Name { get; private set; } = string.Empty; + [IdDataField, ViewVariables] + public string ID { get; } = default!; - [ViewVariables(VVAccess.ReadOnly)] - public string LocalizedName => Loc.GetString(Name); + [DataField] + public ChannelParameters ChannelParameters = new(); +} +[DataDefinition] +public sealed partial class ChannelParameters() +{ + [DataField("name")] + public string Name { get; private set; } = string.Empty; [DataField("color")] public Color Color { get; private set; } = Color.Lime; - [IdDataField, ViewVariables] - public string ID { get; } = default!; + public ChannelParameters(string name, Color color) : this() + { + Name = name; + Color = color; + } } diff --git a/Content.Shared/SS220/Telepathy/TelepathyComponent.cs b/Content.Shared/SS220/Telepathy/TelepathyComponent.cs index 1a155babb79e63..2b9f4322e017d7 100644 --- a/Content.Shared/SS220/Telepathy/TelepathyComponent.cs +++ b/Content.Shared/SS220/Telepathy/TelepathyComponent.cs @@ -1,6 +1,7 @@ // © 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.Actions; +using Robust.Shared.Prototypes; using Robust.Shared.Serialization.TypeSerializers.Implementations.Custom.Prototype; namespace Content.Shared.SS220.Telepathy; @@ -14,8 +15,8 @@ public sealed partial class TelepathyComponent : Component [DataField("canSend", required: true)] public bool CanSend; - [DataField("telepathyChannelPrototype", required: true, customTypeSerializer: typeof(PrototypeIdSerializer))] - public string TelepathyChannelPrototype; + [DataField("telepathyChannelPrototype", required: true)] + public ProtoId TelepathyChannelPrototype; } public sealed partial class TelepathySendEvent : InstantActionEvent diff --git a/Content.Shared/Traits/Assorted/LegsParalyzedSystem.cs b/Content.Shared/Traits/Assorted/LegsParalyzedSystem.cs index 929ee3a19fb79e..e4cc2d882220d0 100644 --- a/Content.Shared/Traits/Assorted/LegsParalyzedSystem.cs +++ b/Content.Shared/Traits/Assorted/LegsParalyzedSystem.cs @@ -1,4 +1,5 @@ using Content.Shared.Body.Systems; +using Content.Shared.Buckle; using Content.Shared.Buckle.Components; using Content.Shared.Movement.Events; using Content.Shared.Movement.Systems; @@ -9,6 +10,7 @@ namespace Content.Shared.Traits.Assorted; public sealed class LegsParalyzedSystem : EntitySystem { + [Dependency] private readonly SharedBuckleSystem _buckle = default!; // SS220-Mindslave-disfunction [Dependency] private readonly MovementSpeedModifierSystem _movementSpeedModifierSystem = default!; [Dependency] private readonly StandingStateSystem _standingSystem = default!; [Dependency] private readonly SharedBodySystem _bodySystem = default!; @@ -27,6 +29,11 @@ private void OnStartup(EntityUid uid, LegsParalyzedComponent component, Componen { // TODO: In future probably must be surgery related wound _movementSpeedModifierSystem.ChangeBaseSpeed(uid, 0, 0, 20); + + //SS220 Mindslave-disfunction begin + if (!_buckle.IsBuckled(uid)) + _standingSystem.Down(uid); + //SS220 Mindslave-disfunction end } private void OnShutdown(EntityUid uid, LegsParalyzedComponent component, ComponentShutdown args) diff --git a/Content.Shared/Wieldable/WieldableSystem.cs b/Content.Shared/Wieldable/WieldableSystem.cs index b55b66a8bbab10..7d828c3f1b8585 100644 --- a/Content.Shared/Wieldable/WieldableSystem.cs +++ b/Content.Shared/Wieldable/WieldableSystem.cs @@ -218,6 +218,7 @@ public bool TryWield(EntityUid used, WieldableComponent component, EntityUid use var ev = new BeforeWieldEvent(); RaiseLocalEvent(used, ev); + RaiseLocalEvent(user, ev); // SS220-wield-unability if (ev.Cancelled) return false; diff --git a/Resources/Audio/SS220/Surgery/attributions.yml b/Resources/Audio/SS220/Surgery/attributions.yml new file mode 100644 index 00000000000000..d64dc05655a147 --- /dev/null +++ b/Resources/Audio/SS220/Surgery/attributions.yml @@ -0,0 +1,4 @@ +- files: ["scalpel.ogg", "incision.ogg", "seal_wound.ogg", "cautery.ogg", "organ.ogg", "hemostat.ogg", "saw.ogg", "retractor.ogg", "retract_skin.ogg"] + license: "CC-BY-SA-3.0" + copyright: "Taken from tg station" + source: "https://github.com/tgstation/tgstation/commit/436ba869ebcd0b60b63973fb7562f447ee655205" diff --git a/Resources/Audio/SS220/Surgery/cautery.ogg b/Resources/Audio/SS220/Surgery/cautery.ogg new file mode 100644 index 00000000000000..4515b769d09c1b Binary files /dev/null and b/Resources/Audio/SS220/Surgery/cautery.ogg differ diff --git a/Resources/Audio/SS220/Surgery/hemostat.ogg b/Resources/Audio/SS220/Surgery/hemostat.ogg new file mode 100644 index 00000000000000..0ae7188be270aa Binary files /dev/null and b/Resources/Audio/SS220/Surgery/hemostat.ogg differ diff --git a/Resources/Audio/SS220/Surgery/incision.ogg b/Resources/Audio/SS220/Surgery/incision.ogg new file mode 100644 index 00000000000000..b11c883db79659 Binary files /dev/null and b/Resources/Audio/SS220/Surgery/incision.ogg differ diff --git a/Resources/Audio/SS220/Surgery/organ.ogg b/Resources/Audio/SS220/Surgery/organ.ogg new file mode 100644 index 00000000000000..cd7536a6d129bc Binary files /dev/null and b/Resources/Audio/SS220/Surgery/organ.ogg differ diff --git a/Resources/Audio/SS220/Surgery/retract_skin.ogg b/Resources/Audio/SS220/Surgery/retract_skin.ogg new file mode 100644 index 00000000000000..8fcbb3ea93ff81 Binary files /dev/null and b/Resources/Audio/SS220/Surgery/retract_skin.ogg differ diff --git a/Resources/Audio/SS220/Surgery/retractor.ogg b/Resources/Audio/SS220/Surgery/retractor.ogg new file mode 100644 index 00000000000000..920e94aeebe6a0 Binary files /dev/null and b/Resources/Audio/SS220/Surgery/retractor.ogg differ diff --git a/Resources/Audio/SS220/Surgery/saw.ogg b/Resources/Audio/SS220/Surgery/saw.ogg new file mode 100644 index 00000000000000..385f49ec2e4031 Binary files /dev/null and b/Resources/Audio/SS220/Surgery/saw.ogg differ diff --git a/Resources/Audio/SS220/Surgery/scalpel.ogg b/Resources/Audio/SS220/Surgery/scalpel.ogg new file mode 100644 index 00000000000000..c312d6806d8511 Binary files /dev/null and b/Resources/Audio/SS220/Surgery/scalpel.ogg differ diff --git a/Resources/Audio/SS220/Surgery/seal_wound.ogg b/Resources/Audio/SS220/Surgery/seal_wound.ogg new file mode 100644 index 00000000000000..17987712fc027d Binary files /dev/null and b/Resources/Audio/SS220/Surgery/seal_wound.ogg differ diff --git a/Resources/Changelog/Changelog220.yml b/Resources/Changelog/Changelog220.yml index ed86da5d2dcec6..55ff93ff704053 100644 --- a/Resources/Changelog/Changelog220.yml +++ b/Resources/Changelog/Changelog220.yml @@ -1,13 +1,4 @@ Entries: -- author: MiraHell - changes: - - message: "\u0421\u0443\u0449\u0435\u0441\u0442\u0432\u0430 \u0431\u0435\u0437\ - \ \u0440\u0443\u043A \u043D\u0435 \u043C\u043E\u0433\u0443\u0442 \u043A\u0440\ - \u0443\u0442\u0438\u0442\u044C \u043E\u0431\u044A\u0435\u043A\u0442\u044B" - type: Tweak - id: 177 - time: '2024-06-12T13:10:22.0000000+00:00' - url: https://github.com/SerbiaStrong-220/space-station-14/pull/1108 - author: SkaldetSkaeg changes: - message: "\u041F\u043E\u0447\u0438\u043D\u0435\u043D\u0430 \u0433\u0435\u043D\u0435\ @@ -5940,3 +5931,64 @@ id: 680 time: '2024-11-28T22:55:18.0000000+00:00' url: https://github.com/SerbiaStrong-220/space-station-14/pull/2274 +- author: AlwyAnri + changes: + - message: "\u0414\u043E\u0431\u0430\u0432\u043B\u0435\u043D\u0430 \u043F\u0440\u043E\ + \u0433\u0440\u0435\u0441\u0441\u0438\u0440\u0443\u044E\u0449\u0430\u044F \u0434\ + \u0438\u0441\u0444\u0443\u043D\u043A\u0446\u0438\u044F \u0434\u043B\u044F \u043D\ + \u043E\u0441\u0438\u0442\u0435\u043B\u0435\u0439 \u0438\u043C\u043F\u043B\u0430\ + \u043D\u0442\u0430 \u043F\u043E\u0434\u0447\u0438\u043D\u0435\u043D\u0438\u044F\ + \ \u0440\u0430\u0437\u0443\u043C\u0430!" + type: Add + - message: "\u0414\u043E\u0431\u0430\u0432\u043B\u0435\u043D \u0432\u0435\u0449\u043C\ + \u0435\u0448\u043E\u043A, \u0441\u043E\u0434\u0435\u0440\u0436\u0438\u043C\u043E\ + \u0435 \u043A\u043E\u0442\u043E\u0440\u043E\u0433\u043E \u0438\u0441\u043F\u043E\ + \u043B\u044C\u0437\u0443\u0435\u0442\u0441\u044F \u0434\u043B\u044F \u043E\u0441\ + \u043B\u0430\u0431\u043B\u0435\u043D\u0438\u044F \u0434\u0438\u0441\u0444\u0443\ + \u043D\u043A\u0446\u0438\u0438 \u043D\u043E\u0441\u0438\u0442\u0435\u043B\u044F\ + \ \u0438\u043C\u043F\u043B\u0430\u043D\u0442\u0430 \u043F\u043E\u0434\u0447\u0438\ + \u043D\u0435\u043D\u0438\u044F \u0440\u0430\u0437\u0443\u043C\u0430!" + type: Add + - message: "\u0414\u043E\u0431\u0430\u0432\u043B\u0435\u043D\u043E \u0441\u0442\u043E\ + \u043F \u0441\u043B\u043E\u0432\u043E, \u043A\u043E\u0442\u043E\u0440\u043E\u0435\ + \ \u043F\u043E\u0434\u0447\u0438\u043D\u0451\u043D\u043D\u044B\u0439 \u0433\u0443\ + \u043C\u0430\u043D\u043E\u0438\u0434 \u043F\u0440\u043E\u0438\u0437\u043D\u0435\ + \u0441\u0442\u0438 \u043D\u0435 \u043C\u043E\u0436\u0435\u0442!" + type: Add + - message: "\u0412\u043E\u0437\u0432\u0440\u0430\u0449\u0435\u043D\u044B \u0441\u0435\ + \u043A\u0440\u0435\u0442\u043D\u044B\u0435 \u0434\u043E\u043A\u0443\u043C\u0435\ + \u043D\u0442\u044B \u0413\u0421\u0411 \u0432\u043C\u0435\u0441\u0442\u0435 \u0441\ + \ \u043E\u0431\u043D\u043E\u0432\u043B\u0451\u043D\u043D\u044B\u043C \u0441\u043E\ + \u0434\u0435\u0440\u0436\u0430\u043D\u0438\u0435\u043C!" + type: Add + - message: "\u041C\u0430\u0441\u0442\u0435\u0440 \u0438 \u0441\u043B\u0435\u0439\ + \u0432 \u043C\u043E\u0433\u0443\u0442 \u0441\u0432\u044F\u0437\u044B\u0432\u0430\ + \u0442\u044C\u0441\u044F \u043F\u0440\u0438 \u043F\u043E\u043C\u043E\u0449\u0438\ + \ \u0442\u0435\u043B\u0435\u043F\u0430\u0442\u0438\u0438!" + type: Add + - message: "\u0426\u0435\u043D\u0430 \u0438\u043C\u043F\u043B\u0430\u043D\u0442\u0435\ + \u0440\u0430 \u043F\u043E\u0434\u0447\u0438\u043D\u0438\u043D\u0435\u043D\u0438\ + \u044F \u0440\u0430\u0437\u0443\u043C\u0430 \u0443\u043C\u0435\u043D\u044C\u0448\ + \u0435\u043D\u0430: 14 \u0422\u041A -> 10 \u0422\u041A" + type: Tweak + - message: "\u0412\u0440\u0435\u043C\u044F \u0432\u0432\u043E\u0434\u0430 \u0438\ + \u043C\u043F\u043B\u0430\u043D\u0442\u0430 \u043F\u043E\u0434\u0447\u0438\u043D\ + \u0435\u043D\u0438\u044F \u0440\u0430\u0437\u0443\u043C\u0430 \u0443\u043C\u0435\ + \u043D\u044C\u0448\u0435\u043D\u043E: 40 \u0441\u0435\u043A\u0443\u043D\u0434\ + \ -> 10 \u0441\u0435\u043A\u0443\u043D\u0434" + type: Tweak + - message: "\u0412\u0440\u0435\u043C\u044F \u0438\u0437\u0432\u043B\u0435\u0447\u0435\ + \u043D\u0438\u044F \u0438\u043C\u043F\u043B\u0430\u043D\u0442\u0430 \u0437\u0430\ + \u0449\u0438\u0442\u044B \u0440\u0430\u0437\u0443\u043C\u0430 \u0443\u0432\u0435\ + \u043B\u0438\u0447\u0435\u043D\u043E \u0434\u043E 40 \u0441\u0435\u043A\u0443\ + \u043D\u0434." + type: Tweak + - message: "\u0411\u043E\u043B\u044C\u0448\u0435 \u043D\u0435\u043B\u044C\u0437\u044F\ + \ \u0438\u043C\u043F\u043B\u0430\u043D\u0442\u0438\u0440\u043E\u0432\u0430\u0442\ + \u044C \u043C\u0430\u0439\u043D\u0434\u0448\u0438\u043B\u0434, \u0435\u0441\u043B\ + \u0438 \u0443\u0436\u0435 \u0435\u0441\u0442\u044C \u043C\u0430\u0439\u043D\u0434\ + \u0441\u043B\u0435\u0439\u0432!" + type: Remove + id: 681 + time: '2024-12-01T11:37:32.0000000+00:00' + url: https://github.com/SerbiaStrong-220/space-station-14/pull/2192 diff --git a/Resources/Locale/ru-RU/ss14-ru/prototypes/entities/objects/misc/secret_documents.ftl b/Resources/Locale/ru-RU/ss14-ru/prototypes/entities/objects/misc/secret_documents.ftl index b56f5d8c5b3b9e..313bccfd2878f1 100644 --- a/Resources/Locale/ru-RU/ss14-ru/prototypes/entities/objects/misc/secret_documents.ftl +++ b/Resources/Locale/ru-RU/ss14-ru/prototypes/entities/objects/misc/secret_documents.ftl @@ -1,2 +1,4 @@ -ent-BookSecretDocuments = чрезвычайные приказы по безопасности - .desc = СОВЕРШЕННО СЕКРЕТНО. В этих документах содержатся Чрезвычайные Приказы, которые ГСБ должен выполнить по требованию Центрального командования. +# SS220 Mindlsave-stop-word begin +ent-BookSecretDocuments = секретные документы + .desc = СОВЕРШЕННО СЕКРЕТНО. В этих документах содержатся указания департамента защиты активов. Не стоит вручать их каждому встречному, если Вам дорога Ваш работа. +# SS220 Mindslave-stop-word end diff --git a/Resources/Locale/ru-RU/ss220/chat/telepathy.ftl b/Resources/Locale/ru-RU/ss220/chat/telepathy.ftl index 1e897e51c8945a..3ee9746f29107e 100644 --- a/Resources/Locale/ru-RU/ss220/chat/telepathy.ftl +++ b/Resources/Locale/ru-RU/ss220/chat/telepathy.ftl @@ -1,3 +1,6 @@ chat-telepathy-yogsothothcult = Культ Йогг chat-telepathy-space-dragon = Космический дракон chat-telepathy-hive = Улей + +unique-telepathy-proto-id = unique-telepathy-{$id} +unique-telepathy-proto-name = телепатическая связь diff --git a/Resources/Locale/ru-RU/ss220/implant/injector-component.ftl b/Resources/Locale/ru-RU/ss220/implant/injector-component.ftl new file mode 100644 index 00000000000000..2381a9f709ba05 --- /dev/null +++ b/Resources/Locale/ru-RU/ss220/implant/injector-component.ftl @@ -0,0 +1 @@ +injector-component-drawing-mind-shield = Вы начинаете извлекать имплант защиты разума. diff --git a/Resources/Locale/ru-RU/ss220/implant/mindslave.ftl b/Resources/Locale/ru-RU/ss220/implant/mindslave.ftl index 16b0d8def5d76a..54bfc8e4aae01f 100644 --- a/Resources/Locale/ru-RU/ss220/implant/mindslave.ftl +++ b/Resources/Locale/ru-RU/ss220/implant/mindslave.ftl @@ -3,11 +3,13 @@ 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 = Разум цели сопротивляется! +mindshield-target-mindslaved = Разум цели сопротивляется! +mindslave-stop-word-replacement = . mindslave-master-dead = Ваш подчинитель погиб! Вам необходимо как можно быстрее вернуть его к жизни! mindslave-master-crit = Ваш подчинитель находится в критическом состоянии! Вам необходимо срочно помочь ему! @@ -30,6 +32,15 @@ ent-MindSlaveImplanter = имплантер Подчинитель разума .desc = Одноразовый имплантер, содержащий имплант, который подчиняет разум владельца тому, кто установил имплант. ent-MindSlaveImplant = Подчинитель разума .desc = Этот имплант подчиняет разум владельца тому, кто установил имлпант. При извлечении импланта контроль над разумом теряется. +ent-MindslaveFixerCerebralImplant = конфигуратор имплантов + .desc = Небольшое устройство, позволяющее произвести более тонкую настройку имплантов. На корпусе расположилось лого "Interdyne pharmaceutics". +ent-MindslaveFixSurgeryGuide = инструкция + .desc = А у кого-то есть инструкция "Как читать"?.. + +#hidden description +hidden-desc-implant-mindslave-fixer-manipulator-syndicate = Устройство позволит произвести корректировки в работу импланта подчинения разума, продлевая жизнь вашему рабу, однако вероятен смертельный исход. Саму операцию может произвести только мастер на подчинённом члене экипажа. +hidden-desc-implant-manipulator-research = Стандартный корпус конфигураторов имплантов от "Interdyne Pharmaceutics". Рассмотрев внимательно вы замечаете стёртые серийные номера на некоторых модулях. Вероятно, стоило внимательнее относится к лекциям по медицине. +hidden-desc-implant-manipulator-medical = Стандартный корпус конфигураторов имплантов от "Interdyne Pharmaceutics". Рассмотрев внимательно вы замечаете незаводские модификации стандартных модулей. Данные модификации нацелены на корректировку работы уже установленных имплантов угнетающих нервную активность и замещающих нейронные сигналы. #alert alerts-mindslave-name = Подчиненный разум @@ -41,3 +52,34 @@ roles-antag-syndicate-mindslave-objective = Ваш разум подчинен! objective-condition-mindslave-obey-master = Подчиняться воле { $targetName }, { $job }. ent-MindSlaveObeyObjective = Подчиняться воле другого. .desc = Ваш разум теперь находится под контролем другого, следуйте его воле и сохраните его жизнь. + +#telepathy +mindslave-telepathy-channel-name = подчинённый разум + +#disfunction +mindslave-disfunction-progress-popup = Вы чувствуете как ваш позвоночник вскипает! +unable-to-wield = { $user } не может удержать двумя руками! + +#guides +mindslave-fixer-surgery-guide = Поздравляем Вас с приобретением продукции "Interdyne Pharmaceutics"! + Данная инструкция поможет провести операцию по манипуляции с вживлёнными мозговыми имплантами. + Для проведения операции понадобится + Место, к которому можно надёжно зафиксировать пациента; + Хирургические простыни, + Скальпель, + Ретрактор, + Дисковая пила, + Гемостат, + Прибор для прижигания, + Устроиство для манипуляции + Сама операция происходит в несколько этапов: + 1. Надёжно зафиксируйте пациента + 2. Наложите хирургические простыни + 3. Сделайте скальпелем надрез в затылочной части головы + 4. Увеличьте ретрактором рабочую область + 5. При помощи дисковой пилы доберитесь до мозгового импланта + 6. Гемостатом зажмите кровоточащие места + 7. Произведите манипуляции с имплантам + 8. Закройте разрезы прибором для прижигания + Поздравляем! Вы закончили свою первую операцию! + diff --git a/Resources/Locale/ru-RU/ss220/objectives/steal-target-groups.ftl b/Resources/Locale/ru-RU/ss220/objectives/steal-target-groups.ftl index 775c49d9e4b95c..d72420743fa905 100644 --- a/Resources/Locale/ru-RU/ss220/objectives/steal-target-groups.ftl +++ b/Resources/Locale/ru-RU/ss220/objectives/steal-target-groups.ftl @@ -1,10 +1,11 @@ steal-target-groups-secway = { ent-VehicleSecway } steal-target-groups-engraved-knuckleduster = { ent-EngravedKnuckleduster } -steal-target-groups-multiphase-energy-gun = мультифазовый { ent-WeaponMultiPhaseEnergyGun } +steal-target-groups-multiphase-energy-gun = { ent-WeaponMultiPhaseEnergyGun } +steal-target-groups-secret-documents= { ent-BookSecretDocuments } steal-target-groups-salvage-shuttle-console-board = { ent-SalvageShuttleConsoleCircuitboard } steal-target-groups-wrist-watch-gold = наручные часы командования steal-target-groups-expensive-lighter-syndicate = { ent-ExpensiveLighterSyndicate } steal-target-groups-expensive-lighter-nanotrasen = { ent-ExpensiveLighterNanotrasen } steal-target-groups-expensive-lighter-shield = { ent-ExpensiveLighterShield } steal-target-groups-expensive-lighter-detective = { ent-ExpensiveLighterDetective } -steal-target-groups-reactive-armor = { ent-ClothingOuterReactiveArmor } \ No newline at end of file +steal-target-groups-reactive-armor = { ent-ClothingOuterReactiveArmor } diff --git a/Resources/Locale/ru-RU/ss220/prototypes/entities/misc/rubber_stamp.ftl b/Resources/Locale/ru-RU/ss220/prototypes/entities/misc/rubber_stamp.ftl index 9ce1f1cb148efe..0bb442a762a6b6 100644 --- a/Resources/Locale/ru-RU/ss220/prototypes/entities/misc/rubber_stamp.ftl +++ b/Resources/Locale/ru-RU/ss220/prototypes/entities/misc/rubber_stamp.ftl @@ -2,6 +2,10 @@ ent-RubberStampCentcomm = печать Центком .suffix = НЕ МАППИТЬ .desc = { ent-RubberStampBase.desc } +ent-RubberStampDoAP = печать Департамента защиты активов + .suffix = НЕ МАППИТЬ + .desc = { ent-RubberStampBase.desc } + ent-RubberStampMagistrate = печать магистрата .suffix = НЕ МАППИТЬ .desc = { ent-RubberStampBase.desc } diff --git a/Resources/Locale/ru-RU/ss220/prototypes/entities/objects/specific/medical.ftl b/Resources/Locale/ru-RU/ss220/prototypes/entities/objects/specific/medical.ftl new file mode 100644 index 00000000000000..66b26f30fbee0a --- /dev/null +++ b/Resources/Locale/ru-RU/ss220/prototypes/entities/objects/specific/medical.ftl @@ -0,0 +1,2 @@ +ent-SurgeryDrapes = хирургические простыни + .desc = Аккуратные простыни. Вы же не запачкаете их кровью?.. diff --git a/Resources/Locale/ru-RU/ss220/stamps.ftl b/Resources/Locale/ru-RU/ss220/stamps.ftl index cbcace1f4a021c..5b655df3cf364b 100644 --- a/Resources/Locale/ru-RU/ss220/stamps.ftl +++ b/Resources/Locale/ru-RU/ss220/stamps.ftl @@ -34,4 +34,5 @@ stamp-component-stamped-name-fake-cmo = Главсрач # Clown Fake Stamped End +stamp-component-stamped-name-DoAP = Департамент защиты активов stamp-component-stamped-name-blue-shield = Синий Щит diff --git a/Resources/Locale/ru-RU/ss220/store/uplink-catalog.ftl b/Resources/Locale/ru-RU/ss220/store/uplink-catalog.ftl index e1055c3c18208f..1054813ae19895 100644 --- a/Resources/Locale/ru-RU/ss220/store/uplink-catalog.ftl +++ b/Resources/Locale/ru-RU/ss220/store/uplink-catalog.ftl @@ -16,6 +16,9 @@ uplink-syndicate-pen-signaller-desc = Ручка, со встроенным пе uplink-mindslave-implanter-name = Имплантер Подчинитель разума uplink-mindslave-implanter-desc = { ent-MindSlaveImplant.desc } +uplink-mindslave-fix-surgery-bundle-name = Конфигуратор подчинителя разума +uplink-mindslave-fix-surgery-bundle-desc = Позволяет вашим подчинённым прожить дольше, требует вмешательства каждый раз при возникновении проблем. Идёт вместе с хирургическим набором и инструкцией! + #JobItems uplink-mime-relic-name = { ent-SacredRelicMime } uplink-mime-relic-desc = Ценная реликвия Ордена Тишины, Вашего ордена. Зажав в руке, позволяет мимам поставить сразу три невидимые стены, закрывающие широкий проход, раз в 90 секунд. diff --git a/Resources/Locale/ru-RU/ss220/surgery/base.ftl b/Resources/Locale/ru-RU/ss220/surgery/base.ftl new file mode 100644 index 00000000000000..c21fe2ff2fd92b --- /dev/null +++ b/Resources/Locale/ru-RU/ss220/surgery/base.ftl @@ -0,0 +1,23 @@ +# руки = лапы. Пока так сойдёт, душиться не буду. +surgery-null-used = руками +surgery-invalid-target-buckle = Цель должна быть пристёгнута! + +incision-examine-description = На теле заметен хирургический разрез. +incision-popup = {THE($user)} делает надрез на теле {THE($target)}, используя {$used} +incision-description = SS220 REDACTED + +retract-skin-examine-description = На теле заметен расширенный разрез. +retract-skin-popup = {THE($user)} раздвигает ткани на теле {THE($target)}, используя {$used} +retract-skin-description = SS220 REDACTED + +saw-bone-examine-description = На теле виден расширенный разрез, внутри которого убрана кость. +saw-bone-popup = {THE($user)} пилит кости в разрезе на теле {THE($target)}, используя {$used} +saw-bone-description = SS220 REDACTED + +clamp-bleeders-examine-description = На теле виден расширенный разрез, внутри которого убрана кость. Все кровоточащие области зажаты. +clamp-bleeders-popup = {THE($user)} зажимает кровотечения в разрезе на теле {THE($target)}, используя {$used} +clamp-bleeders-description = SS220 REDACTED + +seal-wound-examine-description = На теле едва заметны шрамы от недавней операции. +seal-wound-popup = {THE($user)} прижигает раны на теле {THE($target)}, используя {$used} +seal-wound-description = SS220 REDACTED diff --git a/Resources/Locale/ru-RU/ss220/surgery/specific.ftl b/Resources/Locale/ru-RU/ss220/surgery/specific.ftl new file mode 100644 index 00000000000000..4cf510f137cbce --- /dev/null +++ b/Resources/Locale/ru-RU/ss220/surgery/specific.ftl @@ -0,0 +1,7 @@ +surgery-mind-slave-fix-examine-description = На тело наложенные хирургические простыни. +surgery-mind-slave-fix-popup = {THE($user)} подготовляет {THE($target)} к операции, используя {$used} +surgery-mind-slave-fix-description = SS220 REDACTED + +mind-slave-disfunction-fix-surgery-examine-description = В открытой части мозга вы видите имплант подчинения разума. +mind-slave-disfunction-fix-surgery-popup = {THE($user)} подключает порты {THE($used)} к импланту {$used} +mind-slave-disfunction-fix-surgery-description = SS220 REDACTED diff --git a/Resources/Locale/ru-RU/ss220/text/stopWordTextTempaltes.ftl b/Resources/Locale/ru-RU/ss220/text/stopWordTextTempaltes.ftl new file mode 100644 index 00000000000000..527ac995eeb1ea --- /dev/null +++ b/Resources/Locale/ru-RU/ss220/text/stopWordTextTempaltes.ftl @@ -0,0 +1,26 @@ +# И.С. Тургенев "Записки охотника", "Бурмистр" +stop-word-text-template-0 = Делать было нечего. Вместо девяти часов утра мы выехали в два. Охотники поймут мое нетерпенье. Аркадий Павлыч любил, как он выражался, при случае побаловать себя и забрал с собою такую бездну белья, припасов, платья, духов, подушек и разных несессеров, что иному бережливому и владеющему собою немцу хватило бы всей этой благодати на год. При каждом спуске с горы Аркадий Павлыч держал краткую, но сильную речь кучеру, из чего я мог заключить, что мой знакомец порядочный трус. Впрочем, путешествие совершилось весьма благополучно; только на одном недавно починенном мостике телега с поваром завалилась, и задним колесом ему придавило желудок. +# И.С. Тургенев "Записки охотника", "Татьяна Борисовна и ее племянник" +stop-word-text-template-1 = Погода прекрасная; кротко синеет майское небо; гладкие молодые листья ракит блестят, словно вымытые; широкая, ровная дорога вся покрыта той мелкой травой с красноватым стебельком, которую так охотно щиплют овцы; направо и налево, по длинным скатам пологих холмов, тихо зыблется зеленая рожь; жидкими пятнами скользят по ней тени небольших тучек. В отдаленье темнеют леса, сверкают пруды, желтеют деревни; жаворонки сотнями поднимаются, поют, падают стремглав, вытянув шейки торчат на глыбочках; грачи на дороге останавливаются, глядят на вас, приникают к земле, дают вам проехать и, подпрыгнув раза два, тяжко отлетают в сторону. +# И.С. Тургенев "Записки охотника", "Лес и степь" +stop-word-text-template-2 = Заря запылала пожаром и обхватила полнеба. Солнце садится. Воздух вблизи как-то особенно прозрачен, словно стеклянный; вдали ложится мягкий пар, теплый на вид; вместе с росой падает алый блеск на поляны, еще недавно облитые потоками жидкого золота; от деревьев, от кустов, от высоких стогов сена побежали длинные тени... Солнце село; звезда зажглась и дрожит в огнистом море заката... Вот оно бледнеет; синеет небо; отдельные тени исчезают, воздух наливается мглою. +# Ф.М. Достоевский "Преступление и наказание" +stop-word-text-template-3 = В болезненном состоянии сны отличаются часто необыкновенною выпуклостью, яркостью и чрезвычайным сходством с действительностью. Слагается иногда картина чудовищная, но обстановка и весь процесс всего представления бывают при этом до того вероятны и с такими тонкими, неожиданными, но художественно соответствующими всей полноте картины подробностями, что их и не выдумать наяву этому же самому сновидцу, будь он такой же художник, как известные писатели. Такие сны, болезненные сны, всегда долго помнятся и производят сильное впечатление на расстроенный и уже возбужденный организм человека. +# Ф.М. Достоевский "Преступление и наказание" +stop-word-text-template-4 = В первое мгновение он думал, что с ума сойдет. Страшный холод обхватил его; но холод был и от лихорадки, которая уже давно началась с ним во сне. Теперь же вдруг ударил такой озноб, что чуть зубы не выпрыгнули и всё в нем так и заходило. Он отворил дверь и начал слушать: в доме всё совершенно спало. С изумлением оглядывал он себя и всё кругом в комнате и не понимал: как это он мог вчера, войдя, не запереть дверей на крючок и броситься на диван, не только не раздевшись, но даже в шляпе: она скатилась и тут же лежала на полу, близ подушки. +# Ф.М. Достоевский "Преступление и наказание" +stop-word-text-template-5 = Озабоченный и серьезный проснулся он на другой день в восьмом часу. Много новых и непредвиденных недоумений очутилось вдруг у него в это утро. Он и не воображал прежде, что когда-нибудь так проснется. Он помнил до последних подробностей всё вчерашнее и понимал, что с ним совершилось что-то необыденное, что он принял в себя одно, доселе совсем неизвестное ему впечатление и непохожее на все прежние. В то же время он ясно сознавал, что мечта, загоревшаяся в голове его, в высшей степени неосуществима, — до того неосуществима, что ему даже стало стыдно ее, и он поскорей перешел к другим, более насущным заботам и недоумениям. +# Ф.М. Достоевский "Преступление и наказание" +stop-word-text-template-6 = Он вошел, затворил дверь, молча посмотрел на меня и тихо прошел в угол к тому столу, который стоит почти под самою лампадкой. Я очень удивился и смотрел в ожидании; Он облокотился на столик и стал молча глядеть на меня. Так прошло минуты две-три, и я помню, что его молчание очень меня обидело и раздосадовало. Почему же он не хочет говорить? То, что он пришел так поздно, мне показалось, конечно, странным, но помню, что я не был бог знает как изумлен собственно этим. Даже напротив: я хоть утром ему и не высказал ясно моей мысли, но я знаю, что он ее понял; а эта мысль была такого свойства, что по поводу ее, конечно, можно было прийти поговорить еще раз, хотя бы даже и очень поздно. Я так и думал, что он за этим пришел. Мы утром расстались несколько враждебно, и я даже помню, он раза два поглядел на меня очень насмешливо. Вот эту-то насмешку я теперь и прочел в его взгляде, она-то меня и обидела. В том же, что это действительно сам он, а не видение, не бред, я сначала нисколько не сомневался. Даже и мысли не было. +# М. А. Шолохов «Тихий Дон» +stop-word-text-template-7 = Шла весна. Сильнее пригревало солнце. На южных склонах бугров потаял снег, и рыжая от прошлогодней травы земля в полдень уже покрывалась прозрачной сиреневой дымкой испарений. На сугревах, на курганах, из-⁠под вросших в суглинок самородных камней показались первые ярко-⁠зелёные острые ростки травы медвянки. Обнажилась зябь. С брошенных зимних дорог грачи перекочевали на гумна, на затопленную талой водой озимь. В логах и балках снег лежал синий, доверху напитанный влагой; оттуда всё ещё сурово веяло холодом, но уже тонко и певуче звенели в ярах под снегом невидимые глазу вешние ручейки, и совсем по-⁠весеннему, чуть приметно и нежно зазеленели в перелесках стволы тополей. +# И. А. Гончаров «Обломов» +stop-word-text-template-8 = Поэт и мечтатель не остались бы довольны даже общим видом этой скромной и незатейливой местности. Не удалось бы им там видеть какого-⁠нибудь вечера в швейцарском или шотландском вкусе, когда вся природа — и лес, и вода, и стены хижин, и песчаные холмы — всё горит точно багровым заревом; когда по этому багровому фону резко оттеняется едущая по песчаной извилистой дороге кавалькада мужчин, сопутствующих какой-нибудь леди в прогулках к угрюмой развалине и поспешающих в крепкий замок, где их ожидает эпизод о войне двух роз, рассказанный дедом, дикая коза на ужин да пропетая молодою мисс под звуки лютни баллада — картины, которыми так богато населило наше воображение перо. +# И. А. Гончаров «Обломов» +stop-word-text-template-9 = Ему представилось, как он сидит в летний вечер на террасе, за чайным столом, под непроницаемым для солнца навесом деревьев, с длинной трубкой и лениво втягивает в себя дым, задумчиво наслаждаясь открывающимся из-⁠за деревьев видом, прохладой, тишиной; а вдали желтеют поля, солнце опускается за знакомый березняк и румянит гладкий, как зеркало, пруд; с полей восходит пар; становится прохладно, наступают сумерки; крестьяне толпами идут домой. +# А. А. Фет, 1843 и 1855 +stop-word-text-template-10 = Я пришёл к тебе с приветом, рассказать, что солнце встало, что оно горячим светом по листам затрепетало; рассказать, что лес проснулся, весь проснулся, веткой каждой, каждой птицей встрепенулся и весенней полон жаждой; рассказать, что с той же страстью, как вчера, пришёл я снова, что душа всё так же счастью и тебе служить готова; рассказать, что отовсюду на меня весельем веет, что не знаю сам, что буду петь — но только песня зреет. На пажитях немых люблю в мороз трескучий при свете солнечном я солнца блеск колючий, леса под шапками иль в инее седом да речку звонкую под тёмно-⁠синим льдом. Как любят находить задумчивые взоры завеянные рвы, навеянные горы, былинки сонные среди нагих полей, где холм причудливый, как некий мавзолей, изваян полночью,  — иль тучи вихрей дальных на белых берегах и полыньях зеркальных. +# И. А. Бунин "Лёгкое дыхание" +stop-word-text-template-11= Я утром гуляла в саду, в поле, была в лесу, мне казалось, что я одна во всем мире, и я думала так хорошо, как никогда в жизни. Я и обедала одна, потом целый час играла, под музыку у меня было такое чувство, что я буду жить без конца и буду так счастлива, как никто. Потом заснула у него в кабинете, а в четыре часа меня разбудила онп, сказала, что приехал он. Я ему очень обрадовалась, мне было так приятно принять его и занимать. Он приехал на паре своих вяток, очень красивых, и они все время стояли у крыльца, он остался, потому что был дождь, и ему хотелось, чтобы к вечеру просохло. Он жалел, что не застал его, был очень оживлен и держал себя со мной кавалером, много шутил, что он давно влюблен в меня. Когда мы гуляли перед чаем по саду, была опять прелестная погода, солнце блестело через весь мокрый сад, хотя стало совсем холодно, и он вел меня под руку и говорил. Ему пятьдесят шесть лет, но он еще очень красив и всегда хорошо одет +# М. Ю. Лермонтов "Герой нашего времени" +stop-word-text-template-12 = Так прошло около часа. Месяц светил в окно, и луч его играл по земляному полу хаты. Вдруг на яркой полосе, пересекающей пол, промелькнула тень. Я привстал и взглянул в окно: кто-то вторично пробежал мимо его и скрылся Бог знает куда. Я не мог полагать, чтоб это существо сбежало по отвесу берега; однако иначе ему некуда было деваться. Навстречу мне слепой мальчик. Я притаился у забора, и он верной, но осторожной поступью прошел мимо меня. Под мышкой он нес какой-то узел, и повернув к пристани, стал спускаться по узкой и крутой тропинке. «В тот день немые возопиют и слепые прозрят», — подумал я, следуя за ним в таком расстоянии, чтоб не терять его из вида. Между тем луна начала одеваться тучами и на море поднялся туман; Я, с трудом спускаясь, пробирался по крутизне, и вот вижу: слепой приостановился, потом повернул низом направо; он шел так близко от воды, что казалось, сейчас волна его схватит и унесет, но видно, это была не первая его прогулка, судя по уверенности, с которой он ступал с камня на камень и избегал рытвин. Наконец он остановился, будто прислушиваясь к чему-то, присел на землю и положил возле себя узел. Я наблюдал за его движениями. Спустя несколько минут с противоположной стороны показалась белая фигура; она подошла к слепому и села возле него. diff --git a/Resources/PhotocopierForms/FormIndex.yml b/Resources/PhotocopierForms/FormIndex.yml index f273bc2678b593..7e9c5f91745d51 100644 --- a/Resources/PhotocopierForms/FormIndex.yml +++ b/Resources/PhotocopierForms/FormIndex.yml @@ -26,6 +26,11 @@ - /PhotocopierForms/nanotrasen_central_command/blank/blank_registered_letter_cc.xml - /PhotocopierForms/nanotrasen_central_command/blank/blank_regular_cc.xml - /PhotocopierForms/nanotrasen_central_command/blank/blank_secure_cc.xml + - Id: roundstart + Name: Особые документы + IconPath: /Textures/SS220/DepartmentIcons/Centcom_dep.png + Forms: + - /PhotocopierForms/nanotrasen_central_command/roundstart/hos_mindslave_briefing.xml - Id: nanotrasen_station Groups: - Id: goal diff --git a/Resources/PhotocopierForms/nanotrasen_central_command/roundstart/hos_mindslave_briefing.xml b/Resources/PhotocopierForms/nanotrasen_central_command/roundstart/hos_mindslave_briefing.xml new file mode 100644 index 00000000000000..80af72d5b65a6d --- /dev/null +++ b/Resources/PhotocopierForms/nanotrasen_central_command/roundstart/hos_mindslave_briefing.xml @@ -0,0 +1,23 @@ + +
+ hos_mindslave_briefing + PaperNtFormCcSecure + бумага + Свободный бланк ЦентКома 02 + + +
diff --git a/Resources/Prototypes/Catalog/Fills/Lockers/heads.yml b/Resources/Prototypes/Catalog/Fills/Lockers/heads.yml index f3a1065fce2531..a22c89a83629fe 100644 --- a/Resources/Prototypes/Catalog/Fills/Lockers/heads.yml +++ b/Resources/Prototypes/Catalog/Fills/Lockers/heads.yml @@ -337,6 +337,7 @@ table: !type:AllSelector children: #- id: WeaponEnergyShotgun #SS220 Energy shotgun removal + - id: BookSecretDocuments # SS220 mindslave-stop-word - id: BookSpaceLaw - id: BoxEncryptionKeySecurity - id: CigarGoldCase diff --git a/Resources/Prototypes/Entities/Mobs/NPCs/revenant.yml b/Resources/Prototypes/Entities/Mobs/NPCs/revenant.yml index 921020aa6667d9..1c762b185a3067 100644 --- a/Resources/Prototypes/Entities/Mobs/NPCs/revenant.yml +++ b/Resources/Prototypes/Entities/Mobs/NPCs/revenant.yml @@ -100,4 +100,4 @@ requirePower: false ignoreInsulation: true #ss220 revenant buff end - + - type: BlockBuckleVerbsInteraction #ss220 revenant unbuckle verbs interaction fix diff --git a/Resources/Prototypes/Entities/Mobs/Species/base.yml b/Resources/Prototypes/Entities/Mobs/Species/base.yml index 7f55d42d4b482b..9d94f420020c89 100644 --- a/Resources/Prototypes/Entities/Mobs/Species/base.yml +++ b/Resources/Prototypes/Entities/Mobs/Species/base.yml @@ -282,6 +282,7 @@ Asphyxiation: -1.0 - type: FireVisuals alternateState: Standing + - type: Surgable #SS220 Surgery - type: entity save: false diff --git a/Resources/Prototypes/Entities/Objects/Specific/Medical/surgery.yml b/Resources/Prototypes/Entities/Objects/Specific/Medical/surgery.yml index 9f92f4fdab5cd5..904852eb28bc23 100644 --- a/Resources/Prototypes/Entities/Objects/Specific/Medical/surgery.yml +++ b/Resources/Prototypes/Entities/Objects/Specific/Medical/surgery.yml @@ -32,6 +32,11 @@ Heat: 5 soundHit: path: /Audio/Effects/lightburn.ogg + # SS220 Surgery begin + - type: SurgeryTool + toolType: Cautery + sound: /Audio/SS220/Surgery/cautery.ogg + # SS220 Surgery end # Drill @@ -95,6 +100,11 @@ tags: - Scalpel # SS220 Edit biomass craft end + # SS220 Surgery begin + - type: SurgeryTool + toolType: Scalpel + sound: /Audio/SS220/Surgery/scalpel.ogg + # SS220 Surgery end - type: entity name: shiv @@ -147,6 +157,11 @@ - type: Item sprite: Objects/Specific/Medical/Surgery/scissors.rsi storedRotation: 90 + # SS220 Surgery begin + - type: SurgeryTool + toolType: Retractor + sound: /Audio/SS220/Surgery/retractor.ogg + # SS220 Surgery end - type: entity name: hemostat @@ -159,6 +174,11 @@ - type: Item heldPrefix: hemostat storedRotation: 90 + # SS220 Surgery begin + - type: SurgeryTool + toolType: Hemostat + sound: /Audio/SS220/Surgery/hemostat.ogg + # SS220 Surgery end # - type: entity # name: bone setter @@ -228,6 +248,11 @@ path: /Audio/Items/drill_hit.ogg - type: Tool speedModifier: 1.5 + # SS220 Surgery begin + - type: SurgeryTool + toolType: Saw + sound: /Audio/SS220/Surgery/saw.ogg + # SS220 Surgery end - type: entity name: advanced circular saw diff --git a/Resources/Prototypes/SS220/Catalog/uplink_catalog.yml b/Resources/Prototypes/SS220/Catalog/uplink_catalog.yml index f2d95af12d0bb7..8e5120296f80bb 100644 --- a/Resources/Prototypes/SS220/Catalog/uplink_catalog.yml +++ b/Resources/Prototypes/SS220/Catalog/uplink_catalog.yml @@ -32,16 +32,34 @@ discountDownTo: Telecrystal: 8 cost: - Telecrystal: 14 + Telecrystal: 10 categories: - - UplinkImplants + - UplinkImplants conditions: - - !type:StoreWhitelistCondition + - !type:StoreWhitelistCondition blacklist: tags: - - NukeOpsUplink - - LoneOpsUplink # SS220 mindslave fix + - NukeOpsUplink + - LoneOpsUplink +- type: listing + id: UplinkMindslaveSurgeryFixBundle + name: uplink-mindslave-fix-surgery-bundle-name + description: uplink-mindslave-fix-surgery-bundle-desc + icon: { sprite: SS220/Objects/Specific/Medical/implants.rsi, state: mindslave_fixer } + productEntity: ClothingBackpackDuffelSyndicateMindslaveFixerSurgery + discountCategory: veryRareDiscounts + discountDownTo: + Telecrystal: 2 + cost: + Telecrystal: 4 + categories: + - UplinkImplants + conditions: + - !type:BuyerWhitelistCondition + blacklist: + components: + - SurplusBundle # JobItems - type: listing diff --git a/Resources/Prototypes/SS220/Datasets/TextGenerator.yml b/Resources/Prototypes/SS220/Datasets/TextGenerator.yml new file mode 100644 index 00000000000000..3655f64640c0ff --- /dev/null +++ b/Resources/Prototypes/SS220/Datasets/TextGenerator.yml @@ -0,0 +1,16 @@ +- type: dataset + id: MindSlaveStopWordTexts + values: + - stop-word-text-template-0 + - stop-word-text-template-1 + - stop-word-text-template-2 + - stop-word-text-template-3 + - stop-word-text-template-4 + - stop-word-text-template-5 + - stop-word-text-template-6 + - stop-word-text-template-7 + - stop-word-text-template-8 + - stop-word-text-template-9 + - stop-word-text-template-10 + - stop-word-text-template-11 + - stop-word-text-template-12 diff --git a/Resources/Prototypes/SS220/Entities/Objects/Misc/implanters.yml b/Resources/Prototypes/SS220/Entities/Objects/Misc/implanters.yml index 9c4417359e0c2d..86fa2f0fc9dca5 100644 --- a/Resources/Prototypes/SS220/Entities/Objects/Misc/implanters.yml +++ b/Resources/Prototypes/SS220/Entities/Objects/Misc/implanters.yml @@ -1,3 +1,5 @@ +# © SS220, An EULA/CLA with a hosting restriction, full text: https://raw.githubusercontent.com/SerbiaStrong-220/space-station-14/master/CLA.txt + - type: entity id: MindSlaveImplanter name: mind-slave implanter @@ -5,4 +7,4 @@ components: - type: Implanter implant: MindSlaveImplant - implantTime: 40 + implantTime: 10 diff --git a/Resources/Prototypes/SS220/Entities/Objects/Misc/rubber_stamp.yml b/Resources/Prototypes/SS220/Entities/Objects/Misc/rubber_stamp.yml index cc5c4e2242a28d..60af044e69a1b5 100644 --- a/Resources/Prototypes/SS220/Entities/Objects/Misc/rubber_stamp.yml +++ b/Resources/Prototypes/SS220/Entities/Objects/Misc/rubber_stamp.yml @@ -107,6 +107,20 @@ sprite: SS220/Objects/Misc/stamps.rsi state: stamp-centcom +- type: entity + name: CentCom rubber stamp + parent: RubberStampBaseAlt + id: RubberStampDoAP + suffix: DO NOT MAP + components: + - type: Stamp + stampedName: stamp-component-stamped-name-DoAP + stampedColor: "#e23e29" + stampState: "paper_stamp-centcom" + - type: Sprite + sprite: SS220/Objects/Misc/stamps.rsi + state: stamp-centcom + - type: entity parent: RubberStampBase id: RubberStampMagistrate diff --git a/Resources/Prototypes/SS220/Entities/Objects/Misc/secret_documents.yml b/Resources/Prototypes/SS220/Entities/Objects/Misc/secret_documents.yml new file mode 100644 index 00000000000000..8930ba74845665 --- /dev/null +++ b/Resources/Prototypes/SS220/Entities/Objects/Misc/secret_documents.yml @@ -0,0 +1,35 @@ +- type: entity + parent: [BaseItem] + id: BookSecretDocuments + name: "emergency security orders" + description: TOP SECRET. These documents specify the Emergency Orders that the HoS must carry out when ordered by Central Command. + components: + - type: MindSlaveStopWordContainer + collection: nanotrasen_central_command + group: roundstart + form: hos_mindslave_briefing + stampList: + - RubberStampDoAP + - RubberStampCentcomm + - type: Paper + - type: ActivatableUI + key: enum.PaperUiKey.Key + requiresComplex: false + - type: UserInterface + interfaces: + enum.PaperUiKey.Key: + type: PaperBoundUserInterface + - type: PaperVisuals + backgroundImagePath: "/Textures/Interface/Paper/paper_background_book.svg.96dpi.png" + backgroundPatchMargin: 23.0, 16.0, 14.0, 15.0 + contentMargin: 20.0, 20.0, 20.0, 20.0 + - type: Sprite + sprite: Objects/Misc/bureaucracy.rsi + layers: + - state: folder-sec-doc + - type: Tag + tags: + - Book + - HighRiskItem + - type: StealTarget + stealGroup: BookSecretDocuments diff --git a/Resources/Prototypes/SS220/Entities/Objects/Misc/subdermal_implants.yml b/Resources/Prototypes/SS220/Entities/Objects/Misc/subdermal_implants.yml index 472bbb1e103946..ee123e75c7c581 100644 --- a/Resources/Prototypes/SS220/Entities/Objects/Misc/subdermal_implants.yml +++ b/Resources/Prototypes/SS220/Entities/Objects/Misc/subdermal_implants.yml @@ -5,6 +5,21 @@ description: lmaoooo categories: [ HideSpawnMenu ] components: - - type: Tag - tags: - - MindSlave + - type: Tag + tags: + - MindSlave + - type: MindSlaveDisfunctionProvider + disfunction: + disfunction: + Initial: + - MindSlaveDisfunctionAccent + Progressive: + - WieldUnability + Terminal: + - LegsParalyzed + deadlyStageDamage: + types: + Poison: 4 + Blunt: 6 + Airloss: 4 + progressionPopup: "mindslave-disfunction-progress-popup" diff --git a/Resources/Prototypes/SS220/Entities/Objects/Specific/Medical/surgery.yml b/Resources/Prototypes/SS220/Entities/Objects/Specific/Medical/surgery.yml new file mode 100644 index 00000000000000..e82b42302e1343 --- /dev/null +++ b/Resources/Prototypes/SS220/Entities/Objects/Specific/Medical/surgery.yml @@ -0,0 +1,49 @@ +# © SS220, An EULA/CLA with a hosting restriction, full text: https://raw.githubusercontent.com/SerbiaStrong-220/space-station-14/master/CLA.txt + +- type: entity + parent: BaseToolSurgery + id: SurgeryDrapes + name: surgery drapes + description: drapes which used in surgery + components: + - type: Sprite + sprite: SS220/Objects/Specific/Medical/surgery_tools.rsi + state: drape + - type: StaticPrice + price: 20 + - type: Tag + tags: + - SurgeryTool + - type: SurgeryDrape + +- type: entity + parent: BaseItem + id: MindslaveFixerCerebralImplant + name : cerebral implant + description: weird implant, why it even exists... + components: + - type: Sprite + sprite: SS220/Objects/Specific/Medical/implants.rsi + state: mindslave_fixer + - type: MindSlaveDisfunctionFixer + - type: SurgeryTool + toolType: Specific + - type: HiddenDescription + entries: + - label: hidden-desc-implant-manipulator-medical + jobRequired: + - Paramedic + - MedicalDoctor + - ChiefMedicalOfficer + - Brigmedic + - label: hidden-desc-implant-manipulator-research + jobRequired: + - ResearchAssistant + - ResearchDirector + - Scientist + - Borg + - label: hidden-desc-implant-mindslave-fixer-manipulator-syndicate + whitelistMind: + components: + - TraitorRole + - NukeOperative diff --git a/Resources/Prototypes/SS220/Entities/Objects/Storage/medical.yml b/Resources/Prototypes/SS220/Entities/Objects/Storage/medical.yml new file mode 100644 index 00000000000000..da7646c7d90acd --- /dev/null +++ b/Resources/Prototypes/SS220/Entities/Objects/Storage/medical.yml @@ -0,0 +1,29 @@ +# © SS220, An EULA/CLA with a hosting restriction, full text: https://raw.githubusercontent.com/SerbiaStrong-220/space-station-14/master/CLA.txt + +- type: entity + parent: Paper + id: MindslaveFixSurgeryGuide + name: Mindslave fixer surgery guide + description: Anyone has "How to read guide"?.. + components: + - type: Paper + content: mindslave-fixer-surgery-guide + writable: false + +- type: entity + parent: ClothingBackpackDuffelSyndicateMedicalBundle + id: ClothingBackpackDuffelSyndicateMindslaveFixerSurgery + name: syndicate surgical duffel bag with surgery + description: A large duffel bag containing a full suite of surgical tools and mindslave fixer. + components: + - type: StorageFill + contents: + - id: Hemostat + - id: SawElectric + - id: Cautery + - id: Retractor + - id: Scalpel + - id: ClothingHandsGlovesNitrile + - id: SurgeryDrapes + - id: MindslaveFixerCerebralImplant + - id: MindslaveFixSurgeryGuide diff --git a/Resources/Prototypes/SS220/Objectives/stealTargetGroups.yml b/Resources/Prototypes/SS220/Objectives/stealTargetGroups.yml index 9b666fe77c65ce..2387c1089949fb 100644 --- a/Resources/Prototypes/SS220/Objectives/stealTargetGroups.yml +++ b/Resources/Prototypes/SS220/Objectives/stealTargetGroups.yml @@ -19,6 +19,13 @@ sprite: SS220/Objects/Weapons/Guns/multiphase_energy_gun.rsi state: icon +- type: stealTargetGroup + id: BookSecretDocuments + name: steal-target-groups-secret-documents + sprite: + sprite: Objects/Misc/bureaucracy.rsi + state: folder-sec-doc + - type: stealTargetGroup id: ClothingOuterReactiveArmor name: steal-target-groups-reactive-armor @@ -26,7 +33,7 @@ sprite: SS220/Clothing/OuterClothing/Armor/reactive_armor.rsi state: icon -- type: stealTargetGroup +- type: stealTargetGroup id: WristWatchGold name: steal-target-groups-wrist-watch-gold sprite: diff --git a/Resources/Prototypes/SS220/Objectives/traitor.yml b/Resources/Prototypes/SS220/Objectives/traitor.yml index f2b1567da8b351..4166d2ac6222e9 100644 --- a/Resources/Prototypes/SS220/Objectives/traitor.yml +++ b/Resources/Prototypes/SS220/Objectives/traitor.yml @@ -22,6 +22,19 @@ stealGroup: WeaponMultiPhaseEnergyGun owner: job-name-hos +- type: entity + categories: [ HideSpawnMenu ] + parent: BaseTraitorStealObjective + id: SecretDocumentsStealObjective + components: + - type: Objective + difficulty: 3 + - type: NotJobRequirement + job: HeadOfSecurity + - type: StealCondition + stealGroup: BookSecretDocuments + owner: job-name-hos + - type: entity parent: BaseTraitorStealObjective id: ClothingOuterReactiveArmorObjective diff --git a/Resources/Prototypes/SS220/Recipes/Surgery/BaseSurgery.yml b/Resources/Prototypes/SS220/Recipes/Surgery/BaseSurgery.yml new file mode 100644 index 00000000000000..926c4ac5a1cad0 --- /dev/null +++ b/Resources/Prototypes/SS220/Recipes/Surgery/BaseSurgery.yml @@ -0,0 +1,113 @@ +# Nodes + +- type: abstractSurgeryNode + id: incision + node: + node: abstract + nodeText: + examineDescription: incision-examine-description + popup: incision-popup + description: incision-description + +- type: abstractSurgeryNode + id: retract skin + node: + node: abstract + nodeText: + examineDescription: retract-skin-examine-description + popup: retract-skin-popup + description: retract-skin-description + +- type: abstractSurgeryNode + id: saw bone + node: + node: abstract + nodeText: + examineDescription: saw-bone-examine-description + popup: saw-bone-popup + description: saw-bone-description + +- type: abstractSurgeryNode + id: clamp bleeders + node: + node: abstract + nodeText: + examineDescription: clamp-bleeders-examine-description + popup: clamp-bleeders-popup + description: clamp-bleeders-description + +- type: abstractSurgeryNode + id: seal wound + node: + node: abstract + nodeText: + examineDescription: seal-wound-examine-description + popup: seal-wound-popup + description: seal-wound-description + +#Edges + +- type: abstractSurgeryEdge + id: incision + edge: + to: abstract + delay: 4 + sound: /Audio/SS220/Surgery/incision.ogg + conditions: + - !type:SurgeryToolTypeCondition + surgeryTool: scalpel + actions: + - !type:ApplyBleedingSurgeryAction + bleedAmount: 2 + +- type: abstractSurgeryEdge + id: retract skin + edge: + to: abstract + delay: 4 + sound: /Audio/SS220/Surgery/retract_skin.ogg + conditions: + - !type:SurgeryToolTypeCondition + surgeryTool: retractor + actions: + - !type:ApplyBleedingSurgeryAction + bleedAmount: -2 + +- type: abstractSurgeryEdge + id: saw bone + edge: + to: abstract + delay: 12 + sound: + conditions: + - !type:SurgeryToolTypeCondition + surgeryTool: saw + actions: + - !type:ApplyBleedingSurgeryAction + bleedAmount: 2 + +- type: abstractSurgeryEdge + id: clamp bleeders + edge: + to: abstract + delay: 4 + sound: /Audio/SS220/Surgery/organ.ogg + conditions: + - !type:SurgeryToolTypeCondition + surgeryTool: hemostat + actions: + - !type:ApplyBleedingSurgeryAction + bleedAmount: -2 + +- type: abstractSurgeryEdge + id: seal wound + edge: + to: abstract + delay: 4 + sound: /Audio/SS220/Surgery/seal_wound.ogg + conditions: + - !type:SurgeryToolTypeCondition + surgeryTool: cautery + actions: + - !type:ApplyBleedingSurgeryAction + bleedAmount: -2 diff --git a/Resources/Prototypes/SS220/Recipes/Surgery/MindSlaveSurgery.yml b/Resources/Prototypes/SS220/Recipes/Surgery/MindSlaveSurgery.yml new file mode 100644 index 00000000000000..fcecbb16cae1e3 --- /dev/null +++ b/Resources/Prototypes/SS220/Recipes/Surgery/MindSlaveSurgery.yml @@ -0,0 +1,49 @@ +- type: surgeryGraph + id: MindSlaveFix + start: start + end: seal wound + graph: + - node: start + nodeText: + examineDescription: surgery-mind-slave-fix-examine-description + popup: surgery-mind-slave-fix-popup + description: surgery-mind-slave-fix-description + edges: + - to: incision + baseEdge: incision + - node: incision + baseNode: incision + edges: + - to: retract skin + baseEdge: retract skin + - node: retract skin + baseNode: retract skin + edges: + - to: saw bone + baseEdge: saw bone + - node: saw bone + baseNode: saw bone + edges: + - to: clamp bleeders + baseEdge: clamp bleeders + - node: clamp bleeders + baseNode: clamp bleeders + edges: + - to: fix disfunction + delay: 6 + sound: /Audio/SS220/Surgery/organ.ogg + conditions: + - !type:SurgeryHaveComponentCondition + component: MindSlaveDisfunctionFixer + actions: + - !type:SurgeryFixMindSlaveDisfunctionAction + - node: fix disfunction + nodeText: + examineDescription: mind-slave-disfunction-fix-surgery-examine-description + popup: mind-slave-disfunction-fix-surgery-popup + description: mind-slave-disfunction-fix-surgery-description + edges: + - to: seal wound + baseEdge: seal wound + - node: seal wound + baseNode: seal wound diff --git a/Resources/Prototypes/SS220/telepahy_channels.yml b/Resources/Prototypes/SS220/telepahy_channels.yml index b7329e3eb665e9..d9adb078495df9 100644 --- a/Resources/Prototypes/SS220/telepahy_channels.yml +++ b/Resources/Prototypes/SS220/telepahy_channels.yml @@ -1,14 +1,18 @@ - type: telepathyChannel id: TelepathyChannelYogSothothCult - name: chat-telepathy-yogsothothcult - color: Brown + channelParameters: + name: chat-telepathy-yogsothothcult + color: Brown - type: telepathyChannel id: TelepathyChannelSpaceDragon - name: chat-telepathy-space-dragon - color: "#7567b6" + channelParameters: + name: chat-telepathy-space-dragon + color: "#7567b6" + - type: telepathyChannel id: TelepathyChannelHive - name: chat-telepathy-hive - color: "#cc80ff" + channelParameters: + name: chat-telepathy-hive + color: "#cc80ff" diff --git a/Resources/Textures/SS220/Objects/Specific/Medical/implants.rsi/meta.json b/Resources/Textures/SS220/Objects/Specific/Medical/implants.rsi/meta.json new file mode 100644 index 00000000000000..0f7d8146f10f16 --- /dev/null +++ b/Resources/Textures/SS220/Objects/Specific/Medical/implants.rsi/meta.json @@ -0,0 +1,14 @@ +{ + "version": 1, + "license": "CC-BY-SA-3.0", + "copyright": "Taken from Paradise station, repo https://github.com/ParadiseSS13/Paradise", + "size": { + "x": 32, + "y": 32 + }, + "states": [ + { + "name": "mindslave_fixer" + } + ] +} diff --git a/Resources/Textures/SS220/Objects/Specific/Medical/implants.rsi/mindslave_fixer.png b/Resources/Textures/SS220/Objects/Specific/Medical/implants.rsi/mindslave_fixer.png new file mode 100644 index 00000000000000..22eef8e45b6cc1 Binary files /dev/null and b/Resources/Textures/SS220/Objects/Specific/Medical/implants.rsi/mindslave_fixer.png differ diff --git a/Resources/Textures/SS220/Objects/Specific/Medical/surgery_tools.rsi/drape.png b/Resources/Textures/SS220/Objects/Specific/Medical/surgery_tools.rsi/drape.png new file mode 100644 index 00000000000000..76fc0b21e6dba6 Binary files /dev/null and b/Resources/Textures/SS220/Objects/Specific/Medical/surgery_tools.rsi/drape.png differ diff --git a/Resources/Textures/SS220/Objects/Specific/Medical/surgery_tools.rsi/meta.json b/Resources/Textures/SS220/Objects/Specific/Medical/surgery_tools.rsi/meta.json new file mode 100644 index 00000000000000..debd4a67f2cd5f --- /dev/null +++ b/Resources/Textures/SS220/Objects/Specific/Medical/surgery_tools.rsi/meta.json @@ -0,0 +1,14 @@ +{ + "version": 1, + "license": "CC-BY-SA-3.0", + "copyright": "Taken from tg station, repo https://github.com/tgstation/tgstation", + "size": { + "x": 32, + "y": 32 + }, + "states": [ + { + "name": "drape" + } + ] +}