diff --git a/Content.Client/Administration/UI/Bwoink/BwoinkControl.xaml.cs b/Content.Client/Administration/UI/Bwoink/BwoinkControl.xaml.cs index af977f763c..5ab3a71bb3 100644 --- a/Content.Client/Administration/UI/Bwoink/BwoinkControl.xaml.cs +++ b/Content.Client/Administration/UI/Bwoink/BwoinkControl.xaml.cs @@ -12,7 +12,6 @@ using Robust.Client.UserInterface.Controls; using Robust.Client.UserInterface.XAML; using Robust.Shared.Network; using Robust.Shared.Utility; -using Robust.Shared.Timing; using Robust.Shared.Configuration; namespace Content.Client.Administration.UI.Bwoink @@ -49,39 +48,11 @@ namespace Content.Client.Administration.UI.Bwoink ChannelSelector.OnSelectionChanged += sel => { _currentPlayer = sel; - SwitchToChannel(sel?.SessionId); + SwitchToChannel(sel.SessionId); ChannelSelector.PlayerListContainer.DirtyList(); }; - ChannelSelector.OverrideText += (info, text) => - { - var sb = new StringBuilder(); - - if (info.Connected) - sb.Append('●'); - else - sb.Append(info.ActiveThisRound ? '○' : '·'); - - sb.Append(' '); - if (AHelpHelper.TryGetChannel(info.SessionId, out var panel) && panel.Unread > 0) - { - if (panel.Unread < 11) - sb.Append(new Rune('➀' + (panel.Unread-1))); - else - sb.Append(new Rune(0x2639)); // ☹ - sb.Append(' '); - } - - if (info.Antag && info.ActiveThisRound) - sb.Append(new Rune(0x1F5E1)); // 🗡 - - if (info.OverallPlaytime <= TimeSpan.FromSeconds(_cfg.GetCVar(CCVars.NewPlayerThreshold))) - sb.Append(new Rune(0x23F2)); // ⏲ - - sb.AppendFormat("\"{0}\"", text); - - return sb.ToString(); - }; + ChannelSelector.OverrideText += (info, _) => FormatTabTitle(info); ChannelSelector.Comparison = (a, b) => { @@ -166,11 +137,10 @@ namespace Content.Client.Administration.UI.Bwoink ChannelSelector.PopulateList(); } - public void SelectChannel(NetUserId channel) { if (!ChannelSelector.PlayerInfo.TryFirstOrDefault( - i => i.SessionId == channel, out var info)) + i => i.SessionId == channel, out var info)) return; // clear filter if we're trying to select a channel for a player that isn't currently filtered @@ -208,33 +178,33 @@ namespace Content.Client.Administration.UI.Bwoink Follow.Disabled = !Follow.Visible || disabled; } - private string FormatTabTitle(ItemList.Item li, PlayerInfo? pl = default) + private string FormatTabTitle(PlayerInfo info) { - pl ??= (PlayerInfo) li.Metadata!; var sb = new StringBuilder(); - sb.Append(pl.Connected ? '●' : '○'); + sb.Append(info.Connected ? '●' : + info.ActiveThisRound ? '○' : '·' + ); + sb.Append(' '); - if (AHelpHelper.TryGetChannel(pl.SessionId, out var panel) && panel.Unread > 0) + if (AHelpHelper.TryGetChannel(info.SessionId, out var panel) && panel.Unread > 0) { - if (panel.Unread < 11) - sb.Append(new Rune('➀' + (panel.Unread-1))); - else - sb.Append(new Rune(0x2639)); // ☹ + sb.Append(panel.Unread < 11 ? new Rune('➀' + (panel.Unread - 1)) : new Rune(0x2639)); // ☹ + sb.Append(' '); } - if (pl.Antag) + if (info.Antag) sb.Append(new Rune(0x1F5E1)); // 🗡 - if (pl.OverallPlaytime <= TimeSpan.FromSeconds(_cfg.GetCVar(CCVars.NewPlayerThreshold))) + if (info.OverallPlaytime <= TimeSpan.FromSeconds(_cfg.GetCVar(CCVars.NewPlayerThreshold))) sb.Append(new Rune(0x23F2)); // ⏲ - sb.AppendFormat("\"{0}\"", pl.CharacterName); + sb.Append($"\"{info.CharacterName}\""); - if (pl.IdentityName != pl.CharacterName && pl.IdentityName != string.Empty) - sb.Append(' ').AppendFormat("[{0}]", pl.IdentityName); + if (info.IdentityName != info.CharacterName && info.IdentityName != string.Empty) + sb.Append(' ').Append($"[{info.IdentityName}]"); - sb.Append(' ').Append(pl.Username); + sb.Append(' ').Append(info.Username); return sb.ToString(); } @@ -257,4 +227,4 @@ namespace Content.Client.Administration.UI.Bwoink UpdateButtons(); } } -} +} \ No newline at end of file diff --git a/Content.Client/Administration/UI/CustomControls/PlayerListControl.xaml.cs b/Content.Client/Administration/UI/CustomControls/PlayerListControl.xaml.cs index e02b22f4a2..7366fc5633 100644 --- a/Content.Client/Administration/UI/CustomControls/PlayerListControl.xaml.cs +++ b/Content.Client/Administration/UI/CustomControls/PlayerListControl.xaml.cs @@ -1,10 +1,12 @@ using System.Linq; using Content.Client.Administration.Systems; +using Content.Client.Stylesheets; using Content.Client.UserInterface.Controls; using Content.Client.Verbs.UI; using Content.Shared.Administration; using Robust.Client.AutoGenerated; using Robust.Client.Graphics; +using Robust.Client.ResourceManagement; using Robust.Client.UserInterface; using Robust.Client.UserInterface.Controls; using Robust.Client.UserInterface.XAML; @@ -26,15 +28,18 @@ namespace Content.Client.Administration.UI.CustomControls public Func? OverrideText; public Comparison? Comparison; - private IEntityManager _entManager; - private IUserInterfaceManager _uiManager; + private readonly IEntityManager _entManager; + private readonly IUserInterfaceManager _uiManager; private PlayerInfo? _selectedPlayer; + private readonly Font _fontOverride; + public PlayerListControl() { _entManager = IoCManager.Resolve(); _uiManager = IoCManager.Resolve(); + _fontOverride = IoCManager.Resolve().NotoStack(size: 12); _adminSystem = _entManager.System(); RobustXamlLoader.Load(this); // Fill the Option data @@ -137,6 +142,7 @@ namespace Content.Client.Administration.UI.CustomControls new Label { ClipText = true, + FontOverride = _fontOverride, Text = GetText(info) } } diff --git a/Content.Client/Popups/PopupSystem.cs b/Content.Client/Popups/PopupSystem.cs index 821e4a8993..77197aa8ea 100644 --- a/Content.Client/Popups/PopupSystem.cs +++ b/Content.Client/Popups/PopupSystem.cs @@ -11,6 +11,7 @@ using Robust.Client.ResourceManagement; using Robust.Client.UserInterface; using Robust.Shared.Configuration; using Robust.Shared.Map; +using Robust.Shared.Network; using Robust.Shared.Player; using Robust.Shared.Prototypes; using Robust.Shared.Replays; @@ -30,6 +31,7 @@ namespace Content.Client.Popups [Dependency] private readonly IUserInterfaceManager _uiManager = default!; [Dependency] private readonly IReplayRecordingManager _replayRecording = default!; [Dependency] private readonly IChatManager _chatManager = default!; + [Dependency] private readonly IClientNetManager _clientNet = default!; public IReadOnlyList WorldLabels => _aliveWorldLabels; public IReadOnlyList CursorLabels => _aliveCursorLabels; @@ -99,12 +101,14 @@ namespace Content.Client.Popups { PopupType.LargeCaution, "15" } }; - var fontsize = fontSizeDict.ContainsKey(type) ? fontSizeDict[type] : "10"; - var fontcolor = (type == PopupType.LargeCaution || type == PopupType.MediumCaution || type == PopupType.SmallCaution) ? "c62828" : "aeabc4"; + var fontsize = fontSizeDict.GetValueOrDefault(type, "10"); + var fontcolor = type is PopupType.LargeCaution or PopupType.MediumCaution or PopupType.SmallCaution ? "c62828" : "aeabc4"; if (isLogging) { - _chatManager.SendMessage($"notice [font size={fontsize}][color=#{fontcolor}]{message}[/color][/font]", ChatSelectChannel.Console); + var wrappedMEssage = $"[font size={fontsize}][color=#{fontcolor}]{message}[/color][/font]"; + var chatMsg = new ChatMessage(ChatChannel.Emotes, message, wrappedMEssage, GetNetEntity(EntityUid.Invalid), null); + _clientNet.DispatchLocalNetMessage(new MsgChatMessage { Message = chatMsg }); } } diff --git a/Content.Client/_White/Overlays/ShowMindslaveIconsSystem.cs b/Content.Client/_White/Overlays/ShowMindslaveIconsSystem.cs new file mode 100644 index 0000000000..90c044e0d3 --- /dev/null +++ b/Content.Client/_White/Overlays/ShowMindslaveIconsSystem.cs @@ -0,0 +1,67 @@ +using Content.Client.Overlays; +using Content.Shared._White.Implants.Mindslave.Components; +using Content.Shared.StatusIcon; +using Content.Shared.StatusIcon.Components; +using Robust.Client.Player; +using Robust.Shared.Prototypes; + +namespace Content.Client._White.Overlays; + +public sealed class ShowMindslaveIconsSystem : EquipmentHudSystem +{ + [Dependency] private readonly IPrototypeManager _prototype = default!; + [Dependency] private readonly IPlayerManager _player = default!; + + public override void Initialize() + { + base.Initialize(); + + SubscribeLocalEvent(OnGetStatusIconsEvent); + } + + private void OnGetStatusIconsEvent( + EntityUid uid, + MindSlaveComponent mindSlaveComponent, + ref GetStatusIconsEvent args) + { + if (!IsActive || args.InContainer) + { + return; + } + var localEnt = _player.LocalEntity; + if (!TryComp(localEnt, out MindSlaveComponent? ownerMindSlave)) + { + return; + } + + var mindSlaveIcon = MindslaveIcon(uid, ownerMindSlave); + + args.StatusIcons.AddRange(mindSlaveIcon); + } + + private IEnumerable MindslaveIcon(EntityUid uid, MindSlaveComponent mindSlave) + { + var result = new List(); + + string? iconType; + if (GetEntity(mindSlave.Master) == uid) + { + iconType = mindSlave.MasterStatusIcon; + } + else if (mindSlave.Slaves.Contains(GetNetEntity(uid))) + { + iconType = mindSlave.SlaveStatusIcon; + } + else + { + return result; + } + + if (_prototype.TryIndex(iconType, out var mindslaveIcon)) + { + result.Add(mindslaveIcon); + } + + return result; + } +} diff --git a/Content.Server/_White/Implants/Mindslave/MindslaveSystem.cs b/Content.Server/_White/Implants/Mindslave/MindslaveSystem.cs new file mode 100644 index 0000000000..d2e1f7e733 --- /dev/null +++ b/Content.Server/_White/Implants/Mindslave/MindslaveSystem.cs @@ -0,0 +1,85 @@ +using Content.Server.Chat.Managers; +using Content.Server.Roles; +using Content.Server.Roles.Jobs; +using Content.Shared._White.Implants.Mindslave; +using Content.Shared._White.Implants.Mindslave.Components; +using Content.Shared.Chat; +using Content.Shared.Implants; +using Content.Shared.Implants.Components; + +namespace Content.Server._White.Implants.Mindslave; + +public sealed class MindslaveSystem : SharedMindslaveSystem +{ + [Dependency] private readonly RoleSystem _role = default!; + [Dependency] private readonly IChatManager _chatManager = default!; + [Dependency] private readonly JobSystem _job = default!; + + public override void Initialize() + { + base.Initialize(); + SubscribeLocalEvent(OnMindslaveInserted); + SubscribeLocalEvent(OnMindslaveRemoved); + } + + private void OnMindslaveInserted(Entity ent, ref SubdermalImplantInserted args) + { + if (!Tag.HasTag(ent.Owner, MindslaveTag)) + { + return; + } + + var slaveComponent = EnsureComp(args.Target); + slaveComponent.Slaves.Add(GetNetEntity(args.Target)); + slaveComponent.Master = GetNetEntity(args.User); + + var masterComponent = EnsureComp(args.User); + masterComponent.Slaves.Add(GetNetEntity(args.Target)); + masterComponent.Master = GetNetEntity(args.User); + + Dirty(args.Target, masterComponent); + + if (!Mind.TryGetMind(args.Target, out var targetMindId, out var targetMind) || targetMind.Session is null) + { + return; + } + + var jobName = _job.MindTryGetJobName(args.User); + + // send message to chat + var message = Loc.GetString("mindslave-chat-message", ("player", args.User), ("role", jobName)); + var wrappedMessage = Loc.GetString("chat-manager-server-wrap-message", ("message", message)); + _chatManager.ChatMessageToOne(ChatChannel.Server, message, wrappedMessage, default, false, + targetMind.Session.Channel, Color.FromHex("#5e9cff")); + + // add briefing in character menu + if (TryComp(targetMindId, out var roleBriefing)) + { + roleBriefing.Briefing += Loc.GetString("mindslave-briefing", ("player", args.User), ("role", jobName)); + Dirty(targetMindId, roleBriefing); + } + else + { + _role.MindAddRole(targetMindId, new RoleBriefingComponent + { + Briefing = Loc.GetString("mindslave-briefing", ("player", args.User), ("role", jobName)) + }, targetMind); + } + } + + private void OnMindslaveRemoved(Entity ent, ref SubdermalImplantRemoved args) + { + if (!TryComp(args.Target, out MindSlaveComponent? mindslave)) + { + return; + } + + if (Mind.TryGetMind(args.Target, out var mindId, out _)) + { + _role.MindTryRemoveRole(mindId); + Popup.PopupEntity(Loc.GetString("mindslave-freed", ("player", mindslave.Master)), args.Target, args.Target); + } + + RemComp(args.Target); + } +} \ No newline at end of file diff --git a/Content.Server/_White/SelfHeal/SelfHealSystem.cs b/Content.Server/_White/SelfHeal/SelfHealSystem.cs index 14c0bd9132..853d01c4b6 100644 --- a/Content.Server/_White/SelfHeal/SelfHealSystem.cs +++ b/Content.Server/_White/SelfHeal/SelfHealSystem.cs @@ -72,7 +72,7 @@ public sealed class SelfHealSystem: EntitySystem // Logic to determine the whether or not to repeat the healing action args.Repeat = (HasDamage(component, healing) && !dontRepeat); if (!args.Repeat && !dontRepeat) - _popupSystem.PopupEntity(Loc.GetString("self-heal-finished-using", ("verb", Loc.GetString("self-heal-lick")), ("name", uid)), uid, args.User); + _popupSystem.PopupEntity(Loc.GetString("self-heal-finished-using", ("name", uid)), uid, args.User); args.Handled = true; @@ -90,8 +90,8 @@ public sealed class SelfHealSystem: EntitySystem var targetString = EntityManager.ToPrettyString(uid); var healMessage = uid != args.User - ? $"{userString:user} healed {targetString:target} for {total:damage} with {Loc.GetString("self-heal-lick")}" - : $"{userString:user} healed themselves for {total:damage} with {Loc.GetString("self-heal-lick")}"; + ? $"{userString:user} healed {targetString:target} for {total:damage} by licking" + : $"{userString:user} healed themselves for {total:damage} by licking"; _adminLogger.Add(LogType.Healed, $"{healMessage}"); if (TryComp(args.User, out var selfHealComponent)) @@ -100,7 +100,7 @@ public sealed class SelfHealSystem: EntitySystem var audioParams = new AudioParams().WithVariation(2f).WithVolume(-5f); _audio.PlayPvs(audio, args.User, audioParams); - _popupSystem.PopupEntity(Loc.GetString("self-heal-using-other", ("name", uid), ("verb", Loc.GetString("self-heal-lick"))), uid); + _popupSystem.PopupEntity(Loc.GetString("self-heal-using-other", ("user", args.Args.User), ("target", uid)), uid); } } @@ -114,8 +114,7 @@ public sealed class SelfHealSystem: EntitySystem if (!HasDamage(targetDamage, component)) { - var popup = Loc.GetString("self-heal-cant-use", ("verb", Loc.GetString("self-heal-lick")), - ("name", target)); + var popup = Loc.GetString("self-heal-cant-use", ("name", target)); _popupSystem.PopupEntity(popup, user, user); return false; } @@ -128,7 +127,7 @@ public sealed class SelfHealSystem: EntitySystem EntityManager.TryGetComponent(blockedClothing, out var blocker) && blocker.Enabled) { - var popup = Loc.GetString("self-heal-cant-use-clothing", ("verb", Loc.GetString("self-heal-lick")), ("clothing", blockedClothing)); + var popup = Loc.GetString("self-heal-cant-use-clothing", ("clothing", blockedClothing)); _popupSystem.PopupEntity(popup, user, user); return false; } @@ -141,7 +140,7 @@ public sealed class SelfHealSystem: EntitySystem { if (_inventorySystem.TryGetSlotEntity(target, clothing, out var blockedClothing)) { - var popup = Loc.GetString("self-heal-cant-use-clothing-other", ("verb", Loc.GetString("self-heal-lick")), ("name", target), ("clothing", blockedClothing)); + var popup = Loc.GetString("self-heal-cant-use-clothing-other", ("name", target), ("clothing", blockedClothing)); _popupSystem.PopupEntity(popup, user, user); return false; } diff --git a/Content.Shared/Implants/SharedImplanterSystem.cs b/Content.Shared/Implants/SharedImplanterSystem.cs index 31cc40b3a0..2728709606 100644 --- a/Content.Shared/Implants/SharedImplanterSystem.cs +++ b/Content.Shared/Implants/SharedImplanterSystem.cs @@ -1,5 +1,4 @@ using System.Diagnostics.CodeAnalysis; -using System.Linq; using Content.Shared.Containers.ItemSlots; using Content.Shared.DoAfter; using Content.Shared.Examine; @@ -15,24 +14,46 @@ using Robust.Shared.Utility; namespace Content.Shared.Implants; //WD EDIT START -public class SubdermalImplantInserted +public sealed class SubdermalImplantInserted { - public EntityUid Entity; + /// + /// Entity who implants + /// + public EntityUid User; + + /// + /// Entity being implanted + /// + public EntityUid Target; + public SubdermalImplantComponent Component; - public SubdermalImplantInserted(EntityUid entity, SubdermalImplantComponent component) + + public SubdermalImplantInserted(EntityUid user, EntityUid target, SubdermalImplantComponent component) { - Entity = entity; + User = user; + Target = target; Component = component; } } -public class SubdermalImplantRemoved +public sealed class SubdermalImplantRemoved { - public EntityUid Entity; + /// + /// Entity who removes implant + /// + public EntityUid User; + + /// + /// Entity which implant is removing + /// + public EntityUid Target; + public SubdermalImplantComponent Component; - public SubdermalImplantRemoved(EntityUid entity, SubdermalImplantComponent component) + + public SubdermalImplantRemoved(EntityUid user, EntityUid target, SubdermalImplantComponent component) { - Entity = entity; + User = user; + Target = target; Component = component; } } @@ -90,10 +111,11 @@ public abstract class SharedImplanterSystem : EntitySystem if (component.ImplanterSlot.ContainerSlot != null) _container.Remove(implant.Value, component.ImplanterSlot.ContainerSlot); + implantComp.ImplantedEntity = target; implantContainer.OccludesLight = false; _container.Insert(implant.Value, implantContainer); - RaiseLocalEvent(new SubdermalImplantInserted(implantComp.ImplantedEntity!.Value, implantComp)); //WD EDIT + RaiseLocalEvent(implant.Value, new SubdermalImplantInserted(user, target, implantComp)); //WD EDIT if (component.CurrentMode == ImplanterToggleMode.Inject && !component.ImplantOnly) DrawMode(implanter, component); @@ -103,7 +125,7 @@ public abstract class SharedImplanterSystem : EntitySystem var ev = new TransferDnaEvent { Donor = target, Recipient = implanter }; RaiseLocalEvent(target, ref ev); - Dirty(component); + Dirty(implanter, component); } public bool CanImplant( @@ -146,45 +168,47 @@ public abstract class SharedImplanterSystem : EntitySystem var permanentFound = false; - if (_container.TryGetContainer(target, ImplanterComponent.ImplantSlotId, out var implantContainer)) + if (!_container.TryGetContainer(target, ImplanterComponent.ImplantSlotId, out var implantContainer)) + return; + + var implantCompQuery = GetEntityQuery(); + + foreach (var implant in implantContainer.ContainedEntities) { - var implantCompQuery = GetEntityQuery(); + if (!implantCompQuery.TryGetComponent(implant, out var implantComp)) + continue; - foreach (var implant in implantContainer.ContainedEntities) + //Don't remove a permanent implant and look for the next that can be drawn + if (!_container.CanRemove(implant, implantContainer)) { - if (!implantCompQuery.TryGetComponent(implant, out var implantComp)) - continue; + var implantName = Identity.Entity(implant, EntityManager); + var targetName = Identity.Entity(target, EntityManager); + var failedPermanentMessage = Loc.GetString("implanter-draw-failed-permanent", + ("implant", implantName), ("target", targetName)); - //Don't remove a permanent implant and look for the next that can be drawn - if (!_container.CanRemove(implant, implantContainer)) - { - var implantName = Identity.Entity(implant, EntityManager); - var targetName = Identity.Entity(target, EntityManager); - var failedPermanentMessage = Loc.GetString("implanter-draw-failed-permanent", - ("implant", implantName), ("target", targetName)); - _popup.PopupEntity(failedPermanentMessage, target, user); - permanentFound = implantComp.Permanent; - continue; - } - - _container.Remove(implant, implantContainer); - RaiseLocalEvent(new SubdermalImplantRemoved(implantComp.ImplantedEntity!.Value, implantComp)); // WD EDIT - implantComp.ImplantedEntity = null; - _container.Insert(implant, implanterContainer); + _popup.PopupEntity(failedPermanentMessage, target, user); permanentFound = implantComp.Permanent; - - var ev = new TransferDnaEvent { Donor = target, Recipient = implanter }; - RaiseLocalEvent(target, ref ev); - - //Break so only one implant is drawn - break; + continue; } - if (component.CurrentMode == ImplanterToggleMode.Draw && !component.ImplantOnly && !permanentFound) - ImplantMode(implanter, component); + RaiseLocalEvent(implant, new SubdermalImplantRemoved(user, target, implantComp)); // WD EDIT + _container.Remove(implant, implantContainer); - Dirty(component); + implantComp.ImplantedEntity = null; + _container.Insert(implant, implanterContainer); + permanentFound = implantComp.Permanent; + + var ev = new TransferDnaEvent { Donor = target, Recipient = implanter }; + RaiseLocalEvent(target, ref ev); + + //Break so only one implant is drawn + break; } + + if (component.CurrentMode == ImplanterToggleMode.Draw && !component.ImplantOnly && !permanentFound) + ImplantMode(implanter, component); + + Dirty(implanter, component); } private void ImplantMode(EntityUid uid, ImplanterComponent component) @@ -204,26 +228,24 @@ public abstract class SharedImplanterSystem : EntitySystem if (!TryComp(uid, out var appearance)) return; - bool implantFound; + var implantFound = component.ImplanterSlot.HasItem; - if (component.ImplanterSlot.HasItem) - implantFound = true; - - else - implantFound = false; - - if (component.CurrentMode == ImplanterToggleMode.Inject && !component.ImplantOnly) - _appearance.SetData(uid, ImplanterVisuals.Full, implantFound, appearance); - - else if (component.CurrentMode == ImplanterToggleMode.Inject && component.ImplantOnly) + switch (component.CurrentMode) { - _appearance.SetData(uid, ImplanterVisuals.Full, implantFound, appearance); - _appearance.SetData(uid, ImplanterImplantOnlyVisuals.ImplantOnly, component.ImplantOnly, - appearance); - } + case ImplanterToggleMode.Inject when !component.ImplantOnly: + _appearance.SetData(uid, ImplanterVisuals.Full, implantFound, appearance); + break; + case ImplanterToggleMode.Inject when component.ImplantOnly: + _appearance.SetData(uid, ImplanterVisuals.Full, implantFound, appearance); + _appearance.SetData(uid, ImplanterImplantOnlyVisuals.ImplantOnly, component.ImplantOnly, + appearance); - else - _appearance.SetData(uid, ImplanterVisuals.Full, implantFound, appearance); + break; + case ImplanterToggleMode.Draw: + default: + _appearance.SetData(uid, ImplanterVisuals.Full, implantFound, appearance); + break; + } } } diff --git a/Content.Shared/Implants/SharedSubdermalImplantSystem.cs b/Content.Shared/Implants/SharedSubdermalImplantSystem.cs index ebe810546c..e5ba0a1961 100644 --- a/Content.Shared/Implants/SharedSubdermalImplantSystem.cs +++ b/Content.Shared/Implants/SharedSubdermalImplantSystem.cs @@ -136,7 +136,7 @@ public abstract class SharedSubdermalImplantSystem : EntitySystem var implantContainer = implantedComp.ImplantContainer; component.ImplantedEntity = target; - RaiseLocalEvent(new SubdermalImplantInserted(target, component)); + RaiseLocalEvent(implant, new SubdermalImplantInserted(target, target, component)); _container.Insert(implant, implantContainer); } diff --git a/Content.Shared/_White/Implants/Mindslave/Components/MindSlaveComponent.cs b/Content.Shared/_White/Implants/Mindslave/Components/MindSlaveComponent.cs new file mode 100644 index 0000000000..48ec51b909 --- /dev/null +++ b/Content.Shared/_White/Implants/Mindslave/Components/MindSlaveComponent.cs @@ -0,0 +1,21 @@ +using Content.Shared.StatusIcon; +using Robust.Shared.GameStates; +using Robust.Shared.Serialization.TypeSerializers.Implementations.Custom.Prototype; + +namespace Content.Shared._White.Implants.Mindslave.Components; + +[RegisterComponent, AutoGenerateComponentState, NetworkedComponent] +public sealed partial class MindSlaveComponent : Component +{ + [ViewVariables(VVAccess.ReadOnly), AutoNetworkedField] + public List Slaves = new(); + + [ViewVariables(VVAccess.ReadOnly), AutoNetworkedField] + public NetEntity Master; + + [DataField("slaveStatusIcon", customTypeSerializer: typeof(PrototypeIdSerializer))] + public string SlaveStatusIcon = "SlaveMindslaveIcon"; + + [DataField("masterStatusIcon", customTypeSerializer: typeof(PrototypeIdSerializer))] + public string MasterStatusIcon = "MasterMindslaveIcon"; +} diff --git a/Content.Shared/_White/Implants/Mindslave/SharedMindslaveSystem.cs b/Content.Shared/_White/Implants/Mindslave/SharedMindslaveSystem.cs new file mode 100644 index 0000000000..fe1e266cf3 --- /dev/null +++ b/Content.Shared/_White/Implants/Mindslave/SharedMindslaveSystem.cs @@ -0,0 +1,54 @@ +using Content.Shared._White.Cult.Components; +using Content.Shared._White.Implants.Mindslave.Components; +using Content.Shared.Implants; +using Content.Shared.Mind; +using Content.Shared.Mind.Components; +using Content.Shared.Mindshield.Components; +using Content.Shared.Popups; +using Content.Shared.Tag; + +namespace Content.Shared._White.Implants.Mindslave; + +public abstract class SharedMindslaveSystem : EntitySystem +{ + [Dependency] protected readonly TagSystem Tag = default!; + [Dependency] protected readonly SharedMindSystem Mind = default!; + [Dependency] protected readonly SharedPopupSystem Popup = default!; + + protected const string MindslaveTag = "MindSlave"; + + public override void Initialize() + { + SubscribeLocalEvent(OnTryInsertMindslave); + } + + private void OnTryInsertMindslave(Entity ent, ref AddImplantAttemptEvent args) + { + if (!Tag.HasTag(args.Implant, MindslaveTag)) + { + return; + } + + string message; + string wrappedMessage; + if (args.Target == args.User) + { + message = Loc.GetString("mindslave-target-self"); + wrappedMessage = Loc.GetString("chat-manager-server-wrap-message", ("message", message)); + Popup.PopupClient(wrappedMessage, args.Implanter, args.User); + args.Cancel(); + return; + } + + if (HasComp(args.Target) || + HasComp(args.Target) || + HasComp(args.Target) || + HasComp(args.Target)) + { + message = Loc.GetString("mindslave-cant-insert"); + wrappedMessage = Loc.GetString("chat-manager-server-wrap-message", ("message", message)); + Popup.PopupClient(wrappedMessage, args.Implanter, args.User); + args.Cancel(); + } + } +} diff --git a/Resources/Changelog/ChangelogWhite.yml b/Resources/Changelog/ChangelogWhite.yml index a91b493b02..59a39228da 100644 --- a/Resources/Changelog/ChangelogWhite.yml +++ b/Resources/Changelog/ChangelogWhite.yml @@ -1871,3 +1871,46 @@ id: 175 time: '2024-03-02T09:10:00.0000000+00:00' url: https://api.github.com/repos/frosty-dev/ss14-core/pulls/160 +- author: Remuchi + changes: + - message: "\u0438\u0441\u043F\u0440\u0430\u0432\u043B\u0435\u043D\u044B \u043E\u0442\ + \u043E\u0431\u0440\u0430\u0436\u0435\u043D\u0438\u044F \u0430\u043D\u0442\u0430\ + \u0436\u043A\u0438, \u0441\u0442\u0430\u0442\u0443\u0441\u0430 \u0438 \u043F\ + \u043E\u0434\u043E\u0431\u043D\u043E\u0433\u043E \u0432 \u0430\u0445\u0435\u043B\ + \u043F\u0435." + type: Fix + - message: "\u043D\u043E\u0442\u0435\u0441\u044B, \u043E\u0442\u043F\u0440\u0430\ + \u0432\u043B\u044F\u0435\u043C\u044B\u0435 \u0432 \u0447\u0430\u0442, \u0431\ + \u043E\u043B\u044C\u0448\u0435 \u043D\u0435 \u043E\u0442\u043F\u0440\u0430\u0432\ + \u043B\u044F\u044E\u0442\u0441\u044F \u0435\u0449\u0451 \u0438 \u0432 \u043A\ + \u043E\u043D\u0441\u043E\u043B\u044C." + type: Fix + - message: "\u0442\u0435\u043A\u0441\u0442\u0443\u0440\u0430 \u0441\u0435\u043A\u0440\ + \u0435\u0442\u043D\u043E\u0439 \u0434\u0432\u0435\u0440\u0438 \u0431\u044B\u043B\ + \u0430 \u043F\u0435\u0440\u0435\u0440\u0438\u0441\u043E\u0432\u0430\u043D\u0430\ + \ \u0434\u043B\u044F \u0441\u043E\u043E\u0442\u0432\u0435\u0442\u0441\u0442\u0432\ + \u0438\u044F \u0441 \u0430\u043A\u0442\u0443\u0430\u043B\u044C\u043D\u044B\u043C\ + \u0438 \u0442\u0435\u043A\u0441\u0442\u0443\u0440\u0430\u043C\u0438 \u0441\u0442\ + \u0435\u043D" + type: Fix + - message: "\u0438\u0441\u043F\u0440\u0430\u0432\u043B\u0435\u043D\u044B \u043E\u0448\ + \u0438\u0431\u043A\u0438 \u043F\u0435\u0440\u0435\u0432\u043E\u0434\u0430 \u043F\ + \u0440\u0438 \u0432\u044B\u043B\u0438\u0437\u044B\u0432\u0430\u043D\u0438\u0438\ + \ \u0440\u0430\u043D \u0437\u0430 \u0444\u0435\u043B\u0438\u043D\u0438\u0434\ + \u0430" + type: Fix + - message: "\u043F\u0435\u0440\u0435\u0432\u043E\u0434\u044B \u0434\u043B\u044F\ + \ \u0441\u0435\u043A\u0440\u0435\u0442\u043D\u043E\u0439 \u0434\u0432\u0435\u0440\ + \u0438" + type: Add + id: 176 + time: '2024-03-02T14:16:27.0000000+00:00' + url: https://api.github.com/repos/frosty-dev/ss14-core/pulls/161 +- author: Remuchi + changes: + - message: "\u0418\u043C\u043F\u043B\u0430\u043D\u0442 \u043F\u043E\u0434\u0447\u0438\ + \u043D\u0435\u043D\u0438\u044F \u0432 \u0430\u043F\u043B\u0438\u043D\u043A." + type: Add + id: 177 + time: '2024-03-02T14:17:52.0000000+00:00' + url: https://api.github.com/repos/frosty-dev/ss14-core/pulls/152 diff --git a/Resources/Locale/en-US/medical/components/self-healing-component.ftl b/Resources/Locale/en-US/medical/components/self-healing-component.ftl index e654d44cbf..751f084b8f 100644 --- a/Resources/Locale/en-US/medical/components/self-healing-component.ftl +++ b/Resources/Locale/en-US/medical/components/self-healing-component.ftl @@ -1,8 +1,6 @@ -self-heal-finished-using = You have finished {$verb}ing all {$name}`s wounds -self-heal-cant-use = There is no damage you can heal by {$verb}ing {$name} -self-heal-stop-bleeding = They have stopped bleeding -self-heal-lick = lick -self-heal-cant-use-clothing = You cant {$verb}ing yourself while wearing a {$clothing} -self-heal-cant-use-clothing-other = You cant {$verb}ing {$name} while {$name} is wearing a {$clothing} +self-heal-finished-using = You have finished licking all {$name}`s wounds +self-heal-cant-use = There is no damage you can heal by licking {$name} +self-heal-cant-use-clothing = You cant lick yourself while wearing a {$clothing} +self-heal-cant-use-clothing-other = You cant lick {$name} while {$name} is wearing a {$clothing} self-heal-action = Lick the wounds -self-heal-using-other = {$name} have {$verb}ed some of {$name} wounds +self-heal-using-other = {$user} have licked some of {$target} wounds diff --git a/Resources/Locale/ru-RU/_white/implants/mindslave.ftl b/Resources/Locale/ru-RU/_white/implants/mindslave.ftl new file mode 100644 index 0000000000..8d98357654 --- /dev/null +++ b/Resources/Locale/ru-RU/_white/implants/mindslave.ftl @@ -0,0 +1,16 @@ +mindslave-briefing = Служите и защищайте {$player}, {$role}. Выполняйте каждый их приказ. Они для вас - абсолютная власть. +mindslave-chat-message = Перед вашим глазами в мгновение пролетают осколки ваших воспоминаний, после чего сознание застилает белая пелена. Во вспышке ярко-красного света вы вспоминаете свое предназначение - служить {$player}, {$role}. +mindslave-freed = Вы больше не служите {$player}! + +mindslave-target-self = Вы не можете сделать себя своим же рабом +mindslave-cant-insert = Разум данного существа уже на чем-то зациклен + +uplink-mind-slave = Имплант подчинения +uplink-mind-slave-desc = Захватите разум живого существа и прикажите ему закидать капитана взрывными пирогами. + +ent-MindSlaveImplanter = { ent-BaseImplanter } + .desc = { "" } + .suffix = майдслейв + +ent-MindslaveImplant = Имплант подчинения + .desc = Сделайте из кого-то свою личную куклу. diff --git a/Resources/Locale/ru-RU/locales-new/autotranslate-27.ftl b/Resources/Locale/ru-RU/locales-new/autotranslate-27.ftl index eb9eff115c..bdac67fb6c 100644 --- a/Resources/Locale/ru-RU/locales-new/autotranslate-27.ftl +++ b/Resources/Locale/ru-RU/locales-new/autotranslate-27.ftl @@ -1,38 +1,28 @@ ent-EggSpider = яичный паук - .desc = Это драгоценный камень? Это яйцо? это выглядит дорого. + .desc = Это драгоценный камень? Это яйцо? это выглядит дорого. ent-LampInterrogator = лампа следователя - .desc = Ультраяркая лампа для плохого полицейского + .desc = Ультраяркая лампа для плохого полицейского ent-CultistCuffs = самодельные стяжки - .desc = Самодельные наручники из запасных тросов. + .desc = Самодельные наручники из запасных тросов. ent-ParamedicIDCard = ID карта парамедика - .desc = { ent-IDCardStandard.desc } + .desc = { ent-IDCardStandard.desc } ent-BrigmedicIDCard = ID карта бригмедика - .desc = { ent-IDCardStandard.desc } + .desc = { ent-IDCardStandard.desc } ent-SeniorEngineerIDCard = ID карта бригадира - .desc = { ent-IDCardStandard.desc } + .desc = { ent-IDCardStandard.desc } ent-SeniorResearcherIDCard = ID карта ведущего исследователя - .desc = { ent-IDCardStandard.desc } + .desc = { ent-IDCardStandard.desc } ent-SeniorPhysicianIDCard = ID карта медицинского офицера - .desc = { ent-IDCardStandard.desc } + .desc = { ent-IDCardStandard.desc } ent-SeniorOfficerIDCard = ID карта ветерана СБ - .desc = { ent-IDCardStandard.desc } + .desc = { ent-IDCardStandard.desc } ent-SeniorSalvageSpecialistIDCard = ID карта охотника карго - .desc = { ent-IDCardStandard.desc } -ent-BikeHornImplanter = имплантат велосипедного гудка - .desc = "" -ent-UplinkImplanter = имплантат аплинка - .desc = "" -ent-EmpImplanter = ЭМИ-имплантат - .desc = "" -ent-DnaScramblerImplanter = имплантат скремблера ДНК - .desc = "" -ent-DeathRattleImplanter = имплантат предсмертных хрипов - .desc = "" + .desc = { ent-IDCardStandard.desc } ent-ModularReceiver = модульный приемник - .desc = Жизненно важная часть, используемая в создании огнестрельного оружия. + .desc = Жизненно важная часть, используемая в создании огнестрельного оружия. ent-RifleStock = приклад - .desc = Прочный деревянный приклад, используемый при создании огнестрельного оружия. + .desc = Прочный деревянный приклад, используемый при создании огнестрельного оружия. ent-MedalCase = футляр для медали - .desc = Футляр с медалями. + .desc = Футляр с медалями. ent-SyndicateSpongeBox = коробка куба обезьяны - .desc = Обезьяньи кубики марки Drymate. Просто добавь воды! + .desc = Обезьяньи кубики марки Drymate. Просто добавь воды! diff --git a/Resources/Locale/ru-RU/locales-new/autotranslate-28.ftl b/Resources/Locale/ru-RU/locales-new/autotranslate-28.ftl index 60c39f6356..e2a4dbb090 100644 --- a/Resources/Locale/ru-RU/locales-new/autotranslate-28.ftl +++ b/Resources/Locale/ru-RU/locales-new/autotranslate-28.ftl @@ -20,16 +20,6 @@ ent-SpiderWeb = паутина .desc = Она тягучая и липкая. ent-SpiderWebClown = клоунская паутина .desc = Она тягучая и липкая. -ent-BikeHornImplant = имплантат велосипедного гудка - .desc = Этот имплантат позволяет пользователю сигналить в любом месте в любое время. -ent-UplinkImplant = имплантат аплинка - .desc = Этот имплант позволяет пользователю по желанию получить доступ к скрытому восходящему каналу Синдиката. -ent-EmpImplant = ЭМИ-имплантат - .desc = Этот имплантат создает электромагнитный импульс при активации. -ent-DnaScramblerImplant = имплантат скремблера ДНК - .desc = Этот имплантат позволяет пользователю один раз случайным образом изменить свой внешний вид и имя. -ent-DeathRattleImplant = имплантат предсмертных хрипов - .desc = Этот имплантат сообщит по радиоканалу Синдиката, если пользователь попадет в критическое состояние или умрет. ent-FloorTileItemFlesh = пол из плоти .desc = { ent-FloorTileItemBase.desc } ent-AmeJar = топливный бак ДАМ'а diff --git a/Resources/Locale/ru-RU/medical/components/self-healing-component.ftl b/Resources/Locale/ru-RU/medical/components/self-healing-component.ftl index ec2db16b58..db06a99529 100644 --- a/Resources/Locale/ru-RU/medical/components/self-healing-component.ftl +++ b/Resources/Locale/ru-RU/medical/components/self-healing-component.ftl @@ -1,9 +1,7 @@ -self-heal-finished-using = Вы закончили {$verb} все раны {$name}! -self-heal-cant-use = {$name} не имеет ран, которые вы могли бы {$verb} -self-heal-stop-bleeding = Оно перестало кровоточить -self-heal-lick = вылизывать -self-heal-cant-use-clothing = Вы не можете {$verb}, пока на вас {$clothing} -self-heal-cant-use-clothing-other = Вы не можете {$verb} {$name}, пока {$name} носит {$clothing} +self-heal-finished-using = Вы закончили вылизывать все раны {$name}! +self-heal-cant-use = {$name} не имеет ран, которые вы могли бы вылизать +self-heal-cant-use-clothing = Вы не можете вылизываться, пока на вас {$clothing} +self-heal-cant-use-clothing-other = Вы не можете вылизать {$name}, пока {$name} носит {$clothing} self-heal-action = Зализать раны -self-heal-using-other = {$name} закончил {$verb} часть {$name} ран +self-heal-using-other = {$user} вылизывает часть ран {$target} ent-SelfHealAction = Зализать раны diff --git a/Resources/Locale/ru-RU/ss14-ru/prototypes/entities/objects/misc/implanters.ftl b/Resources/Locale/ru-RU/ss14-ru/prototypes/entities/objects/misc/implanters.ftl index 894d4165fb..07672cfe3b 100644 --- a/Resources/Locale/ru-RU/ss14-ru/prototypes/entities/objects/misc/implanters.ftl +++ b/Resources/Locale/ru-RU/ss14-ru/prototypes/entities/objects/misc/implanters.ftl @@ -1,33 +1,54 @@ ent-BaseImplanter = имплантер .desc = Специальный шприц, используемый только для имплантов. - .suffix = { "" } ent-Implanter = { ent-BaseImplanter } .desc = { ent-BaseImplanter.desc } - .suffix = { "" } ent-BaseImplantOnlyImplanter = { ent-Implanter } .desc = Одноразовый имплантер. - .suffix = { "" } -ent-SadTromboneImplanter = имплантер Грустный тромбон - .desc = Одноразовый имплантер, содержащий имплант, который проигрывает грустную мелодию при смерти владельца. - .suffix = { "" } -ent-LightImplanter = имплантер Свет - .desc = Одноразовый имплантер, содержащий имплант, который излучает свет при активации. - .suffix = { "" } -ent-TrackingImplanter = имплантер Отслеживание - .desc = Одноразовый имплантер, содержащий имплант, постоянно передающий координаты владельца. - .suffix = { "" } -ent-StorageImplanter = имплантер Хранилище - .desc = Одноразовый имплантер, содержащий имплант, создающий хранилище в теле владельца. - .suffix = { "" } -ent-FreedomImplanter = имплантер Свобода - .desc = Одноразовый имплантер, содержащий имплант, позволяющий владельцу три раза освободиться от наручников и других ограничителей. - .suffix = { "" } -ent-MicroBombImplanter = имплантер Микробомба - .desc = Одноразовый имплантер, содержащий неизвлекаемый имплант, вызывающий небольшой взрыв при смерти владельца. - .suffix = { "" } -ent-MacroBombImplanter = имплантер Макробомба - .desc = Одноразовый имплантер, содержащий имплант, вызывающий мощный взрыв при смерти владельца по истечении заданного времени. - .suffix = { "" } -ent-MindshieldImplanter = имплантер Защиты Разума - .desc = Одноразовый имплантер, содержащий имплант защиты разума. - .suffix = { "" } +ent-SadTromboneImplanter = { ent-BaseImplanter } + .desc = {""} + .suffix = грустный тромбон +ent-LightImplanter = { ent-BaseImplanter } + .desc = {""} + .suffix = свет +ent-TrackingImplanter = { ent-BaseImplanter } + .desc = {""} + .suffix = отслеживание +ent-StorageImplanter = { ent-BaseImplanter } + .desc = {""} + .suffix = хранилище +ent-FreedomImplanter = { ent-BaseImplanter } + .desc = {""} + .suffix = свобода +ent-MicroBombImplanter = { ent-BaseImplanter } + .desc = {""} + .suffix = микробомба +ent-MacroBombImplanter = { ent-BaseImplanter } + .desc = {""} + .suffix = макробомба +ent-MindShieldImplanter = { ent-BaseImplanter } + .desc = {""} + .suffix = защита разума +ent-BikeHornImplanter = { ent-BaseImplanter } + .desc = {""} + .suffix = велосипедный гудок +ent-UplinkImplanter = { ent-BaseImplanter } + .desc = {""} + .suffix = аплинк +ent-EmpImplanter = { ent-BaseImplanter } + .desc = {""} + .suffix = ЭМИ +ent-DnaScramblerImplanter = { ent-BaseImplanter } + .desc = {""} + .suffix = скремблер ДНК +ent-DeathRattleImplanter = { ent-BaseImplanter } + .desc = {""} + .suffix = предсмертный хрип +ent-ScramImplanter = { ent-BaseImplanter } + .desc = {""} + .suffix = переброска +ent-DeathAcidifierImplanter = { ent-BaseImplanter } + .desc = {""} + .suffix = посмертный растворитель +ent-ImplanterAdmeme = { ent-BaseImplanter } + .desc = {""} + .suffix = адмемы diff --git a/Resources/Locale/ru-RU/ss14-ru/prototypes/entities/objects/misc/subdermal_implants.ftl b/Resources/Locale/ru-RU/ss14-ru/prototypes/entities/objects/misc/subdermal_implants.ftl index 1931cab724..69213ef1f3 100644 --- a/Resources/Locale/ru-RU/ss14-ru/prototypes/entities/objects/misc/subdermal_implants.ftl +++ b/Resources/Locale/ru-RU/ss14-ru/prototypes/entities/objects/misc/subdermal_implants.ftl @@ -25,3 +25,13 @@ ent-MacroBombImplant = имплант Макробомба ent-MindShieldImplant = имплант Защиты Разума .desc = Маленький имплант, который защищает мозги носителя от промывки. .suffix = { "" } +ent-BikeHornImplant = имплант велосипедного гудка + .desc = Этот имплантат позволяет пользователю сигналить в любом месте в любое время. +ent-UplinkImplant = имплант аплинка + .desc = Этот имплант позволяет пользователю по желанию получить доступ к скрытому восходящему каналу Синдиката. +ent-EmpImplant = ЭМИ-имплант + .desc = Этот имплантат создает электромагнитный импульс при активации. +ent-DnaScramblerImplant = имплант скремблера ДНК + .desc = Этот имплантат позволяет пользователю один раз случайным образом изменить свой внешний вид и имя. +ent-DeathRattleImplant = имплант предсмертных хрипов + .desc = Этот имплантат сообщит по радиоканалу Синдиката, если пользователь попадет в критическое состояние или умрет. diff --git a/Resources/Locale/ru-RU/ss14-ru/prototypes/entities/structures/doors/secretdoor/secretdoor.ftl b/Resources/Locale/ru-RU/ss14-ru/prototypes/entities/structures/doors/secretdoor/secretdoor.ftl new file mode 100644 index 0000000000..fdf5e1336b --- /dev/null +++ b/Resources/Locale/ru-RU/ss14-ru/prototypes/entities/structures/doors/secretdoor/secretdoor.ftl @@ -0,0 +1,10 @@ +ent-BaseSecretDoor = { ent-WallSolid } + .desc = { ent-WallSolid.desc } + .suffix = секретная дверь + +ent-BaseSecretDoorAssembly = каркас секретной двери + .desc = Она открывается, закрывается, и даже может вас раздавить! + +ent-SolidSecretDoor = { ent-BaseSecretDoor } + .desc = { ent-WallSolid.desc } + \ No newline at end of file diff --git a/Resources/Locale/ru-RU/store/uplink-catalog.ftl b/Resources/Locale/ru-RU/store/uplink-catalog.ftl index cd81a8a648..dcbed400e5 100644 --- a/Resources/Locale/ru-RU/store/uplink-catalog.ftl +++ b/Resources/Locale/ru-RU/store/uplink-catalog.ftl @@ -181,7 +181,7 @@ uplink-freedom-implanter-name = Имплант свободы uplink-freedom-implanter-desc = Убирайся подальше от этих мерзких офицеров с этим имплантатом! uplink-scram-implanter-name = Имплант переброски -uplink-scram-implanter-desc = Имплантат 2-го использования, который телепортирует вас в большом радиусе. Пытается телепортировать вас в пустое пространство. Иногда может не справиться. Страхование жизни не предусмотрено. +uplink-scram-implanter-desc = Имплант 2-го использования, который телепортирует вас в большом радиусе. Пытается телепортировать вас в пустое пространство. Иногда может не справиться. Страхование жизни не предусмотрено. uplink-dna-scrambler-implanter-name = Имплант скремблера ДНК uplink-dna-scrambler-implanter-desc = Одноразовый имплантат, который можно активировать, чтобы изменить вашу ДНК и придать вам совершенно новый вид, также имеет функцию отмены изменений. Невозможно зашифровать уже зашифрованную ДНК. @@ -198,10 +198,10 @@ uplink-uplink-implanter-desc = Незаметно заказывайте обо uplink-deathrattle-implant-name = Коробка имплантатов предсмертного хрипа uplink-deathrattle-implant-desc = Коробка с достаточным количеством предсмертных хрипов для всего отряда. Передает сообщение, содержащее вашу позицию, на канал синдиката, когда вы переходите в критическое состояние или умираете. -uplink-death-acidifier-implant-name = Имплантат расплавления +uplink-death-acidifier-implant-name = Имплантер расплавления uplink-death-acidifier-implant-desc = Полностью расплавляет пользователя и его снаряжение при использовании или смерти. -uplink-micro-bomb-implanter-name = Имплантатор микро-бомбы +uplink-micro-bomb-implanter-name = Имплантер микро-бомбы uplink-micro-bomb-implanter-desc = Взорвитесь при смерти или ручной активации с помощью этого имплантата. Уничтожает тело со всем снаряжением. # Bundles diff --git a/Resources/Locale/ru-RU/white/items/hardlight_spear.ftl b/Resources/Locale/ru-RU/white/items/hardlight_spear.ftl index 3e6d783a7a..421650fba6 100644 --- a/Resources/Locale/ru-RU/white/items/hardlight_spear.ftl +++ b/Resources/Locale/ru-RU/white/items/hardlight_spear.ftl @@ -1,13 +1,19 @@ hardlight-spear-pickup-failed = Вы не можете подобрать световое копьё. use-hardlight-spear-implant-action-name = Создать световое копьё. use-hardlight-spear-implant-action-description = Создает световое копьё в ваших руках. -uplink-hardlight-spear-implant-name = Имплантатор световое копьё +uplink-hardlight-spear-implant-name = Имплантер световое копьё uplink-hardlight-spear-implant-desc = Имплант, вводимый в тело и активируемый по желанию пользователя. Он вызывает копье из твердого света, с помощью которого пользователь может сеять хаос. ent-SpearHardlight = световое копьё .desc = Копьё из твердого света. -ent-HardlightSpearImplanter = имплантатор световое копьё +ent-HardlightSpearImplanter = { ent-BaseImplanter } + .desc = {""} + .suffix = световое копьё ent-HardlightSpearImplant = имплант световое копьё .desc = Этот имплант создаёт световое копьё в ваших руках. + +ent-SmokeImplanter = { ent-BaseImplanter } + .desc = {""} + .suffix = дым diff --git a/Resources/Prototypes/Entities/Objects/Misc/implanters.yml b/Resources/Prototypes/Entities/Objects/Misc/implanters.yml index 4a7115c2d7..4d0e877290 100644 --- a/Resources/Prototypes/Entities/Objects/Misc/implanters.yml +++ b/Resources/Prototypes/Entities/Objects/Misc/implanters.yml @@ -124,24 +124,24 @@ - type: entity id: SadTromboneImplanter - name: sad trombone implanter parent: BaseImplantOnlyImplanter + suffix: sad trombone components: - type: Implanter implant: SadTromboneImplant - type: entity id: LightImplanter - name: light implanter parent: BaseImplantOnlyImplanter + suffix: light components: - type: Implanter implant: LightImplant - type: entity id: BikeHornImplanter - name: bike horn implanter parent: BaseImplantOnlyImplanter + suffix: bike horn components: - type: Implanter implant: BikeHornImplant @@ -150,8 +150,8 @@ - type: entity id: TrackingImplanter - name: tracking implanter parent: BaseImplantOnlyImplanter + suffix: tracking components: - type: Implanter implant: TrackingImplant @@ -160,24 +160,24 @@ - type: entity id: StorageImplanter - name: storage implanter parent: BaseImplantOnlyImplanterSyndi + suffix: storage components: - type: Implanter implant: StorageImplant - type: entity id: FreedomImplanter - name: freedom implanter parent: BaseImplantOnlyImplanterSyndi + suffix: freedom components: - type: Implanter implant: FreedomImplant - type: entity id: UplinkImplanter - name: uplink implanter parent: BaseImplantOnlyImplanterSyndi + suffix: uplink components: - type: Implanter implant: UplinkImplant @@ -185,24 +185,24 @@ - type: entity id: EmpImplanter - name: EMP implanter parent: BaseImplantOnlyImplanterSyndi + suffix: EMP components: - type: Implanter implant: EmpImplant - type: entity id: ScramImplanter - name: scram implanter parent: BaseImplantOnlyImplanterSyndi + suffix: scram components: - type: Implanter implant: ScramImplant - type: entity id: DnaScramblerImplanter - name: DNA scrambler implanter parent: BaseImplantOnlyImplanterSyndi + suffix: DNA scrambler components: - type: Implanter implant: DnaScramblerImplant @@ -211,32 +211,32 @@ - type: entity id: MicroBombImplanter - name: micro-bomb implanter parent: BaseImplantOnlyImplanterSyndi + suffix: micro-bomb components: - type: Implanter implant: MicroBombImplant - type: entity id: MacroBombImplanter - name: macro-bomb implanter parent: BaseImplantOnlyImplanterSyndi + suffix: macro-bomb components: - type: Implanter implant: MacroBombImplant - type: entity id: DeathRattleImplanter - name: death rattle implanter parent: BaseImplantOnlyImplanterSyndi + suffix: death rattle components: - type: Implanter implant: DeathRattleImplant - type: entity id: DeathAcidifierImplanter - name: death acidifier implanter parent: BaseImplantOnlyImplanterSyndi + suffix: death acidifier components: - type: Implanter implant: DeathAcidifierImplant @@ -245,8 +245,8 @@ - type: entity id: MindShieldImplanter - name: mind-shield implanter parent: BaseImplantOnlyImplanter + suffix: mind-shield components: - type: Implanter implant: MindShieldImplant diff --git a/Resources/Prototypes/White/Catalog/uplink.yml b/Resources/Prototypes/White/Catalog/uplink.yml index 9c6da52d4c..9bb495ac41 100644 --- a/Resources/Prototypes/White/Catalog/uplink.yml +++ b/Resources/Prototypes/White/Catalog/uplink.yml @@ -204,3 +204,13 @@ Telecrystal: 1 categories: - UplinkAmmo + +- type: listing + id: UplinkMindSlaveImplanter + name: uplink-mind-slave + description: uplink-mind-slave-desc + productEntity: MindSlaveImplanter + cost: + Telecrystal: 6 + categories: + - UplinkImplants diff --git a/Resources/Prototypes/White/Entities/Objects/Misc/implanters.yml b/Resources/Prototypes/White/Entities/Objects/Misc/implanters.yml index 4970be67b5..8bbbcf60e7 100644 --- a/Resources/Prototypes/White/Entities/Objects/Misc/implanters.yml +++ b/Resources/Prototypes/White/Entities/Objects/Misc/implanters.yml @@ -1,15 +1,23 @@ - type: entity id: SmokeImplanter - name: имплант дыма parent: BaseImplantOnlyImplanterSyndi + suffix: smoke components: - - type: Implanter - implant: SmokeImplant + - type: Implanter + implant: SmokeImplant - type: entity id: HardlightSpearImplanter - name: hardlight spear implanter parent: BaseImplantOnlyImplanterSyndi + suffix: hardlight spear components: - - type: Implanter - implant: HardlightSpearImplant + - type: Implanter + implant: HardlightSpearImplant + +- type: entity + id: MindSlaveImplanter + parent: BaseImplantOnlyImplanterSyndi + suffix: mindslave + components: + - type: Implanter + implant: MindslaveImplant diff --git a/Resources/Prototypes/White/Entities/Objects/Misc/subdermal_implants.yml b/Resources/Prototypes/White/Entities/Objects/Misc/subdermal_implants.yml index 686d3b4283..b916ec534f 100644 --- a/Resources/Prototypes/White/Entities/Objects/Misc/subdermal_implants.yml +++ b/Resources/Prototypes/White/Entities/Objects/Misc/subdermal_implants.yml @@ -23,3 +23,16 @@ components: - type: SubdermalImplant implantAction: ActivateHardlightSpearImplant + +- type: entity + parent: BaseSubdermalImplant + id: MindslaveImplant + name: mindslave implant + description: Make someone a proper doll for your use. + noSpawn: true + components: + - type: SubdermalImplant + - type: Tag + tags: + - MindSlave + diff --git a/Resources/Prototypes/White/tags.yml b/Resources/Prototypes/White/tags.yml index fc6f00c9af..466a1ddb17 100644 --- a/Resources/Prototypes/White/tags.yml +++ b/Resources/Prototypes/White/tags.yml @@ -51,3 +51,6 @@ - type: Tag id: WeldedKnife + +- type: Tag + id: MindSlave diff --git a/Resources/Prototypes/_White/StatusIcon/mindslave.yml b/Resources/Prototypes/_White/StatusIcon/mindslave.yml new file mode 100644 index 0000000000..9af545c195 --- /dev/null +++ b/Resources/Prototypes/_White/StatusIcon/mindslave.yml @@ -0,0 +1,15 @@ +- type: statusIcon + id: MasterMindslaveIcon + priority: 3 + locationPreference: Left + icon: + sprite: /Textures/White/Overlays/mindslave.rsi + state: master + +- type: statusIcon + id: SlaveMindslaveIcon + priority: 3 + locationPreference: Left + icon: + sprite: /Textures/White/Overlays/mindslave.rsi + state: slave diff --git a/Resources/Textures/Structures/Doors/secret_door.rsi/assembly.png b/Resources/Textures/Structures/Doors/secret_door.rsi/assembly.png index 6518b7245c..e872d82c5d 100644 Binary files a/Resources/Textures/Structures/Doors/secret_door.rsi/assembly.png and b/Resources/Textures/Structures/Doors/secret_door.rsi/assembly.png differ diff --git a/Resources/Textures/Structures/Doors/secret_door.rsi/closed.png b/Resources/Textures/Structures/Doors/secret_door.rsi/closed.png index b68b06f10d..198151d903 100644 Binary files a/Resources/Textures/Structures/Doors/secret_door.rsi/closed.png and b/Resources/Textures/Structures/Doors/secret_door.rsi/closed.png differ diff --git a/Resources/Textures/Structures/Doors/secret_door.rsi/closing.png b/Resources/Textures/Structures/Doors/secret_door.rsi/closing.png index 0bb895b050..96bba25801 100644 Binary files a/Resources/Textures/Structures/Doors/secret_door.rsi/closing.png and b/Resources/Textures/Structures/Doors/secret_door.rsi/closing.png differ diff --git a/Resources/Textures/Structures/Doors/secret_door.rsi/open.png b/Resources/Textures/Structures/Doors/secret_door.rsi/open.png index 81862e1ead..7ca06de1ed 100644 Binary files a/Resources/Textures/Structures/Doors/secret_door.rsi/open.png and b/Resources/Textures/Structures/Doors/secret_door.rsi/open.png differ diff --git a/Resources/Textures/Structures/Doors/secret_door.rsi/opening.png b/Resources/Textures/Structures/Doors/secret_door.rsi/opening.png index 9d14324686..861f6fbbc3 100644 Binary files a/Resources/Textures/Structures/Doors/secret_door.rsi/opening.png and b/Resources/Textures/Structures/Doors/secret_door.rsi/opening.png differ diff --git a/Resources/Textures/White/Overlays/mindslave.rsi/master.png b/Resources/Textures/White/Overlays/mindslave.rsi/master.png new file mode 100644 index 0000000000..56416298ef Binary files /dev/null and b/Resources/Textures/White/Overlays/mindslave.rsi/master.png differ diff --git a/Resources/Textures/White/Overlays/mindslave.rsi/meta.json b/Resources/Textures/White/Overlays/mindslave.rsi/meta.json new file mode 100644 index 0000000000..3786e26e8d --- /dev/null +++ b/Resources/Textures/White/Overlays/mindslave.rsi/meta.json @@ -0,0 +1,17 @@ +{ + "version": 1, + "size": { + "x": 32, + "y": 32 + }, + "license": "CC-BY-SA-3.0", + "copyright": "@JustNemo", + "states": [ + { + "name": "slave" + }, + { + "name": "master" + } + ] +} diff --git a/Resources/Textures/White/Overlays/mindslave.rsi/slave.png b/Resources/Textures/White/Overlays/mindslave.rsi/slave.png new file mode 100644 index 0000000000..3739880d22 Binary files /dev/null and b/Resources/Textures/White/Overlays/mindslave.rsi/slave.png differ