diff --git a/Content.Client/Chat/Managers/ChatManager.cs b/Content.Client/Chat/Managers/ChatManager.cs index 1b66bf8732..3f594dee53 100644 --- a/Content.Client/Chat/Managers/ChatManager.cs +++ b/Content.Client/Chat/Managers/ChatManager.cs @@ -82,6 +82,12 @@ internal sealed class ChatManager : IChatManager _consoleHost.ExecuteCommand($"whisper \"{CommandParsing.Escape(str)}\""); break; + // Corvax-Wega-MindChat-start + case ChatSelectChannel.Mind: + _consoleHost.ExecuteCommand($"mindsay \"{CommandParsing.Escape(str)}\""); + break; + // Corvax-Wega-MindChat-end + default: throw new ArgumentOutOfRangeException(nameof(channel), channel, null); } diff --git a/Content.Client/UserInterface/Systems/Chat/ChatUIController.cs b/Content.Client/UserInterface/Systems/Chat/ChatUIController.cs index f1fdb51aef..d130d41ea2 100644 --- a/Content.Client/UserInterface/Systems/Chat/ChatUIController.cs +++ b/Content.Client/UserInterface/Systems/Chat/ChatUIController.cs @@ -21,6 +21,7 @@ using Content.Shared.Chat; using Content.Shared.Damage.ForceSay; using Content.Shared.Decals; using Content.Shared.Input; +using Content.Shared.Mind; // Corvax-Wega-MindChat using Content.Shared.Radio; using Content.Shared.Roles.RoleCodeword; using Robust.Client.GameObjects; @@ -84,7 +85,8 @@ public sealed partial class ChatUIController : UIController {SharedChatSystem.EmotesAltPrefix, ChatSelectChannel.Emotes}, {SharedChatSystem.AdminPrefix, ChatSelectChannel.Admin}, {SharedChatSystem.RadioCommonPrefix, ChatSelectChannel.Radio}, - {SharedChatSystem.DeadPrefix, ChatSelectChannel.Dead} + {SharedChatSystem.DeadPrefix, ChatSelectChannel.Dead}, + {SharedChatSystem.MindPrefix, ChatSelectChannel.Mind} // Corvax-Wega-MindChat }; public static readonly Dictionary ChannelPrefixes = new() @@ -97,7 +99,8 @@ public sealed partial class ChatUIController : UIController {ChatSelectChannel.Emotes, SharedChatSystem.EmotesPrefix}, {ChatSelectChannel.Admin, SharedChatSystem.AdminPrefix}, {ChatSelectChannel.Radio, SharedChatSystem.RadioCommonPrefix}, - {ChatSelectChannel.Dead, SharedChatSystem.DeadPrefix} + {ChatSelectChannel.Dead, SharedChatSystem.DeadPrefix}, + {ChatSelectChannel.Mind, SharedChatSystem.MindPrefix} // Corvax-Wega-MindChat }; /// @@ -534,6 +537,7 @@ public sealed partial class ChatUIController : UIController FilterableChannels |= ChatChannel.Whisper; FilterableChannels |= ChatChannel.Radio; FilterableChannels |= ChatChannel.Emotes; + FilterableChannels |= ChatChannel.Mind; // Corvax-Wega-MindChat FilterableChannels |= ChatChannel.Notifications; // Can only send local / radio / emote when attached to a non-ghost entity. @@ -545,6 +549,8 @@ public sealed partial class ChatUIController : UIController CanSendChannels |= ChatSelectChannel.Radio; CanSendChannels |= ChatSelectChannel.Emotes; } + + CanSendChannels |= ChatSelectChannel.Mind; // Corvax-Wega-MindChat } // Only ghosts and admins can send / see deadchat. @@ -692,46 +698,66 @@ public sealed partial class ChatUIController : UIController && _chatSys.TryProcessRadioMessage(uid, text, out _, out radioChannel, quiet: true); } + // Corvax-Wega-MindChat-start + private bool TryGetMindChannel(string text, out MindChannelPrototype? mindChannel) + { + mindChannel = null; + return _player.LocalEntity is EntityUid { Valid: true } uid + && _chatSys != null + && _chatSys.TryProcessMindMessage(uid, text, out _, out mindChannel, quiet: true); + } + // Corvax-Wega-MindChat-end + public void UpdateSelectedChannel(ChatBox box) { - var (prefixChannel, _, radioChannel) = SplitInputContents(box.ChatInput.Input.Text.ToLower()); + var (prefixChannel, text, radioChannel, mindChannel) = SplitInputContents(box.ChatInput.Input.Text.ToLower()); // Corvax-Wega-MindChat-Edit if (prefixChannel == ChatSelectChannel.None) - box.ChatInput.ChannelSelector.UpdateChannelSelectButton(box.SelectedChannel, null); + box.ChatInput.ChannelSelector.UpdateChannelSelectButton(box.SelectedChannel, null, null); // Corvax-Wega-MindChat-Edit else - box.ChatInput.ChannelSelector.UpdateChannelSelectButton(prefixChannel, radioChannel); + box.ChatInput.ChannelSelector.UpdateChannelSelectButton(prefixChannel, radioChannel, mindChannel); // Corvax-Wega-MindChat-Edit } - public (ChatSelectChannel chatChannel, string text, RadioChannelPrototype? radioChannel) SplitInputContents(string text) + public (ChatSelectChannel chatChannel, string text, RadioChannelPrototype? radioChannel, MindChannelPrototype? mindChannel) SplitInputContents(string text) // Corvax-Wega-MindChat-Edit { text = text.Trim(); if (text.Length == 0) - return (ChatSelectChannel.None, text, null); + return (ChatSelectChannel.None, text, null, null); // Corvax-Wega-MindChat-Edit // We only cut off prefix only if it is not a radio or local channel, which both map to the same /say command // because ???????? + // Corvax-Wega-MindChat-Edit-start ChatSelectChannel chatChannel; - if (TryGetRadioChannel(text, out var radioChannel)) + RadioChannelPrototype? radioChannel = null; + MindChannelPrototype? mindChannel = null; + + if (TryGetRadioChannel(text, out radioChannel)) chatChannel = ChatSelectChannel.Radio; + else if (TryGetMindChannel(text, out mindChannel)) + chatChannel = ChatSelectChannel.Mind; else chatChannel = PrefixToChannel.GetValueOrDefault(text[0]); if ((CanSendChannels & chatChannel) == 0) - return (ChatSelectChannel.None, text, null); + return (ChatSelectChannel.None, text, null, null); if (chatChannel == ChatSelectChannel.Radio) - return (chatChannel, text, radioChannel); + return (chatChannel, text, radioChannel, null); + + if (chatChannel == ChatSelectChannel.Mind) + return (chatChannel, text, null, mindChannel); if (chatChannel == ChatSelectChannel.Local) { if (_ghost?.IsGhost != true) - return (chatChannel, text, null); + return (chatChannel, text, null, null); else chatChannel = ChatSelectChannel.Dead; } - return (chatChannel, text[1..].TrimStart(), null); + return (chatChannel, text[1..].TrimStart(), null, null); + // Corvax-Wega-MindChat-Edit-end } public void SendMessage(ChatBox box, ChatSelectChannel channel) @@ -746,7 +772,7 @@ public sealed partial class ChatUIController : UIController if (string.IsNullOrWhiteSpace(text)) return; - (var prefixChannel, text, var _) = SplitInputContents(text); + (var prefixChannel, text, var _, var _) = SplitInputContents(text); // Corvax-Wega-MindChat-Edit // Check if message is longer than the character limit if (text.Length > MaxMessageLength) @@ -764,6 +790,12 @@ public sealed partial class ChatUIController : UIController // radio must have prefix as it goes through the say command. text = $";{text}"; } + // Corvax-Wega-MindChat-start + else if (channel == ChatSelectChannel.Mind) + { + text = $"+{text}"; + } + // Corvax-Wega-MindChat-end _manager.SendMessage(text, prefixChannel == 0 ? channel : prefixChannel); } diff --git a/Content.Client/UserInterface/Systems/Chat/Controls/ChannelFilterPopup.xaml.cs b/Content.Client/UserInterface/Systems/Chat/Controls/ChannelFilterPopup.xaml.cs index 100607d36e..0072e4f321 100644 --- a/Content.Client/UserInterface/Systems/Chat/Controls/ChannelFilterPopup.xaml.cs +++ b/Content.Client/UserInterface/Systems/Chat/Controls/ChannelFilterPopup.xaml.cs @@ -23,6 +23,7 @@ public sealed partial class ChannelFilterPopup : Popup ChatChannel.LOOC, ChatChannel.OOC, ChatChannel.Dead, + ChatChannel.Mind, // Corvax-Wega-MindChat ChatChannel.Admin, ChatChannel.AdminAlert, ChatChannel.AdminChat, diff --git a/Content.Client/UserInterface/Systems/Chat/Controls/ChannelSelectorButton.cs b/Content.Client/UserInterface/Systems/Chat/Controls/ChannelSelectorButton.cs index 25cf851c7b..70fde3f075 100644 --- a/Content.Client/UserInterface/Systems/Chat/Controls/ChannelSelectorButton.cs +++ b/Content.Client/UserInterface/Systems/Chat/Controls/ChannelSelectorButton.cs @@ -63,14 +63,30 @@ public sealed class ChannelSelectorButton : ChatPopupButton Color.MediumTurquoise, ChatSelectChannel.OOC => Color.LightSkyBlue, ChatSelectChannel.Dead => Color.MediumPurple, + ChatSelectChannel.Mind => Color.Peru, // Corvax-Wega-MindChat ChatSelectChannel.Admin => Color.HotPink, _ => Color.DarkGray }; } - public void UpdateChannelSelectButton(ChatSelectChannel channel, Shared.Radio.RadioChannelPrototype? radio) + // Corvax-Wega-MindChat-Edit-start + public void UpdateChannelSelectButton(ChatSelectChannel channel, Shared.Radio.RadioChannelPrototype? radio, Shared.Mind.MindChannelPrototype? mind) { - Text = radio != null ? Loc.GetString(radio.Name) : ChannelSelectorName(channel); - Modulate = radio?.Color ?? ChannelSelectColor(channel); + if (radio != null) + { + Text = Loc.GetString(radio.Name); + Modulate = radio.Color; + } + else if (mind != null) + { + Text = Loc.GetString(mind.Name); + Modulate = mind.Color; + } + else + { + Text = ChannelSelectorName(channel); + Modulate = ChannelSelectColor(channel); + } } + // Corvax-Wega-MindChat-Edit-end } diff --git a/Content.Client/UserInterface/Systems/Chat/Controls/ChannelSelectorPopup.cs b/Content.Client/UserInterface/Systems/Chat/Controls/ChannelSelectorPopup.cs index 0852c10bb9..13d99b1546 100644 --- a/Content.Client/UserInterface/Systems/Chat/Controls/ChannelSelectorPopup.cs +++ b/Content.Client/UserInterface/Systems/Chat/Controls/ChannelSelectorPopup.cs @@ -16,6 +16,7 @@ public sealed class ChannelSelectorPopup : Popup ChatSelectChannel.LOOC, ChatSelectChannel.OOC, ChatSelectChannel.Dead, + ChatSelectChannel.Mind, // Corvax-Wega-MindChat ChatSelectChannel.Admin // NOTE: Console is not in there and it can never be permanently selected. // You can, however, still submit commands as console by prefixing with /. diff --git a/Content.Client/_Wega/BloodCult/BloodCultSystem.cs b/Content.Client/_Wega/BloodCult/BloodCultSystem.cs index 1ce2472b15..b2c94f9f37 100644 --- a/Content.Client/_Wega/BloodCult/BloodCultSystem.cs +++ b/Content.Client/_Wega/BloodCult/BloodCultSystem.cs @@ -6,7 +6,6 @@ using Content.Shared.StatusIcon.Components; using Robust.Client.GameObjects; using Robust.Shared.Prototypes; using Robust.Shared.Random; -using Robust.Shared.Utility; namespace Content.Client.Blood.Cult { @@ -24,9 +23,8 @@ namespace Content.Client.Blood.Cult SubscribeLocalEvent(OnRuneAppearanceChanged); SubscribeLocalEvent(OnRuneAppearanceChanged); SubscribeLocalEvent(GetCultistIcons); - SubscribeLocalEvent(GetHalo); - SubscribeLocalEvent(RemoveHalo); - SubscribeLocalEvent(OnSoulStoneAppearanceChanged); + SubscribeLocalEvent(GetHalo); + SubscribeLocalEvent(RemoveHalo); } private void OnRuneAppearanceChanged(Entity entity, ref AppearanceChangeEvent args) @@ -51,7 +49,7 @@ namespace Content.Client.Blood.Cult args.StatusIcons.Add(iconPrototype); } - private void GetHalo(EntityUid uid, PentagramDisplayComponent component, ComponentStartup args) + private void GetHalo(EntityUid uid, BloodPentagramDisplayComponent component, ComponentStartup args) { if (!TryComp(uid, out var sprite)) return; @@ -77,28 +75,10 @@ namespace Content.Client.Blood.Cult _sprite.LayerMapSet(uid, PentagramKey.Halo, layer); } - private void RemoveHalo(EntityUid uid, PentagramDisplayComponent component, ComponentShutdown args) + private void RemoveHalo(EntityUid uid, BloodPentagramDisplayComponent component, ComponentShutdown args) { if (_sprite.LayerMapTryGet(uid, PentagramKey.Halo, out var layer, true)) - { _sprite.RemoveLayer(uid, layer); - } - } - - private void OnSoulStoneAppearanceChanged(EntityUid uid, StoneSoulComponent component, ref AppearanceChangeEvent args) - { - if (!_appearance.TryGetData(uid, StoneSoulVisuals.HasSoul, out bool hasSoul)) - hasSoul = false; - - _sprite.LayerSetVisible(uid, StoneSoulVisualLayers.Soul, hasSoul); - if (!hasSoul) - { - _sprite.LayerSetVisible(uid, StoneSoulVisualLayers.Base, true); - } - else - { - _sprite.LayerSetVisible(uid, StoneSoulVisualLayers.Base, false); - } } private enum PentagramKey diff --git a/Content.Client/_Wega/BloodCult/Ui/BloodConstructBoundUserInterface.cs b/Content.Client/_Wega/BloodCult/Ui/BloodConstructBoundUserInterface.cs new file mode 100644 index 0000000000..e636f4fbfe --- /dev/null +++ b/Content.Client/_Wega/BloodCult/Ui/BloodConstructBoundUserInterface.cs @@ -0,0 +1,30 @@ +using Content.Shared.Blood.Cult; +using JetBrains.Annotations; +using Robust.Client.UserInterface; + +namespace Content.Client._Wega.BloodCult.Ui; + +[UsedImplicitly] +public sealed class BloodConstructBoundUserInterface : BoundUserInterface +{ + [ViewVariables] + private BloodConstructMenu? _menu; + + public BloodConstructBoundUserInterface(EntityUid owner, Enum uiKey) : base(owner, uiKey) + { + } + + protected override void Open() + { + base.Open(); + + _menu = this.CreateWindow(); + _menu.OnSelectConstruct += construct => + { + SendMessage(new BloodConstructSelectMessage(construct)); + Close(); + }; + + _menu.OpenCentered(); + } +} diff --git a/Content.Client/_Wega/BloodCult/Ui/BloodConstructMenu.xaml b/Content.Client/_Wega/BloodCult/Ui/BloodConstructMenu.xaml index 5acb1720ab..0d16e57796 100644 --- a/Content.Client/_Wega/BloodCult/Ui/BloodConstructMenu.xaml +++ b/Content.Client/_Wega/BloodCult/Ui/BloodConstructMenu.xaml @@ -1,32 +1,27 @@ + VerticalExpand="True" HorizontalExpand="True"> - + - + - + - + - + diff --git a/Content.Client/_Wega/BloodCult/Ui/BloodConstructMenu.xaml.cs b/Content.Client/_Wega/BloodCult/Ui/BloodConstructMenu.xaml.cs index 81c261e10f..67ff443e87 100644 --- a/Content.Client/_Wega/BloodCult/Ui/BloodConstructMenu.xaml.cs +++ b/Content.Client/_Wega/BloodCult/Ui/BloodConstructMenu.xaml.cs @@ -1,49 +1,31 @@ using Content.Client.UserInterface.Controls; -using Content.Shared.Blood.Cult; using Robust.Client.AutoGenerated; using Robust.Client.UserInterface.XAML; -using Robust.Shared.Player; -namespace Content.Client.Select.Construct.UI; +namespace Content.Client._Wega.BloodCult.Ui; [GenerateTypedNameReferences] public sealed partial class BloodConstructMenu : RadialMenu { - [Dependency] private readonly IEntityManager _entityManager = default!; - [Dependency] private readonly IEntityNetworkManager _entityNetworkManager = default!; - [Dependency] private readonly ISharedPlayerManager _playerManager = default!; - public event Action? OnSelectConstruct; - private NetEntity _constructUid; - private NetEntity _mindUid; public BloodConstructMenu() { RobustXamlLoader.Load(this); - IoCManager.InjectDependencies(this); - InitializeButtons(); } private void InitializeButtons() { - BloodJuggernautButton.OnButtonUp += _ => HandleRitesSelection("MobConstructJuggernaut"); - BloodWraithButton.OnButtonUp += _ => HandleRitesSelection("MobConstructWraith"); - BloodArtificerButton.OnButtonUp += _ => HandleRitesSelection("MobConstructArtificer"); - BloodProteonButton.OnButtonUp += _ => HandleRitesSelection("MobConstructProteon"); + BloodJuggernautButton.OnButtonUp += _ => HandleConstructSelection("MobConstructJuggernaut"); + BloodWraithButton.OnButtonUp += _ => HandleConstructSelection("MobConstructWraith"); + BloodArtificerButton.OnButtonUp += _ => HandleConstructSelection("MobConstructArtificer"); + BloodProteonButton.OnButtonUp += _ => HandleConstructSelection("MobConstructProteon"); } - public void SetData(NetEntity constructUid, NetEntity mindUid) - { - _constructUid = constructUid; - _mindUid = mindUid; - } - - private void HandleRitesSelection(string constructName) + private void HandleConstructSelection(string constructName) { OnSelectConstruct?.Invoke(constructName); - var netEntity = _entityManager.GetNetEntity(_playerManager.LocalSession?.AttachedEntity ?? EntityUid.Invalid); - _entityNetworkManager.SendSystemNetworkMessage(new BloodConstructMenuClosedEvent(netEntity, _constructUid, _mindUid, constructName)); Close(); } } diff --git a/Content.Client/_Wega/BloodCult/Ui/BloodConstructUIController.cs b/Content.Client/_Wega/BloodCult/Ui/BloodConstructUIController.cs deleted file mode 100644 index 94dad53fe3..0000000000 --- a/Content.Client/_Wega/BloodCult/Ui/BloodConstructUIController.cs +++ /dev/null @@ -1,52 +0,0 @@ -using Content.Shared.Blood.Cult; -using Robust.Client.Player; -using Robust.Client.UserInterface; -using Robust.Client.UserInterface.Controllers; -using Timer = Robust.Shared.Timing.Timer; - -namespace Content.Client.Select.Construct.UI -{ - public sealed class BloodConstructMenuUIController : UIController - { - [Dependency] private readonly IUserInterfaceManager _uiManager = default!; - [Dependency] private readonly IEntityManager _entityManager = default!; - - private BloodConstructMenu? _menu; - - public override void Initialize() - { - base.Initialize(); - SubscribeNetworkEvent(OnConstructMenuReceived); - } - - private void OnConstructMenuReceived(OpenConstructMenuEvent args, EntitySessionEventArgs eventArgs) - { - var session = IoCManager.Resolve().LocalSession; - var userEntity = _entityManager.GetEntity(args.Uid); - - if (session?.AttachedEntity.HasValue == true && session.AttachedEntity.Value == userEntity) - { - if (_menu is null) - { - _menu = _uiManager.CreateWindow(); - - _menu.SetData(args.ConstructUid, args.Mind); - - _menu.OpenCentered(); - } - else - { - _menu.OpenCentered(); - } - - Timer.Spawn(30000, () => - { - if (_menu != null) - { - _menu.Close(); - } - }); - } - } - } -} diff --git a/Content.Client/_Wega/BloodCult/Ui/BloodMagicEui.cs b/Content.Client/_Wega/BloodCult/Ui/BloodMagicEui.cs new file mode 100644 index 0000000000..bb3f9ef162 --- /dev/null +++ b/Content.Client/_Wega/BloodCult/Ui/BloodMagicEui.cs @@ -0,0 +1,31 @@ +using Content.Client.Eui; +using Content.Shared.Blood.Cult; +using JetBrains.Annotations; + +namespace Content.Client._Wega.BloodCult.Ui; + +[UsedImplicitly] +public sealed class BloodMagicEui : BaseEui +{ + private readonly BloodMagicMenu _menu; + + public BloodMagicEui() + { + _menu = new BloodMagicMenu(); + _menu.OnSelectedSpell += spell => + { + SendMessage(new BloodMagicSelectSpellMessage(spell)); + _menu.Close(); + }; + } + + public override void Opened() + { + _menu.OpenCentered(); + } + + public override void Closed() + { + _menu.Close(); + } +} diff --git a/Content.Client/_Wega/BloodCult/Ui/BloodMagicMenu.xaml b/Content.Client/_Wega/BloodCult/Ui/BloodMagicMenu.xaml index 05f96f511d..18b06a8129 100644 --- a/Content.Client/_Wega/BloodCult/Ui/BloodMagicMenu.xaml +++ b/Content.Client/_Wega/BloodCult/Ui/BloodMagicMenu.xaml @@ -1,62 +1,57 @@ + VerticalExpand="True" HorizontalExpand="True"> - + - + - + - + - + - + - + - + - + - + - + diff --git a/Content.Client/_Wega/BloodCult/Ui/BloodMagicMenu.xaml.cs b/Content.Client/_Wega/BloodCult/Ui/BloodMagicMenu.xaml.cs index c18c4038fc..96584af689 100644 --- a/Content.Client/_Wega/BloodCult/Ui/BloodMagicMenu.xaml.cs +++ b/Content.Client/_Wega/BloodCult/Ui/BloodMagicMenu.xaml.cs @@ -1,25 +1,18 @@ using Content.Client.UserInterface.Controls; -using Content.Shared.Blood.Cult; using Robust.Client.AutoGenerated; using Robust.Client.UserInterface.XAML; -using Robust.Shared.Player; +using Robust.Shared.Prototypes; -namespace Content.Client.Blood.Magic.UI; +namespace Content.Client._Wega.BloodCult.Ui; [GenerateTypedNameReferences] public sealed partial class BloodMagicMenu : RadialMenu { - [Dependency] private readonly IEntityManager _entityManager = default!; - [Dependency] private readonly IEntityNetworkManager _entityNetworkManager = default!; - [Dependency] private readonly ISharedPlayerManager _playerManager = default!; - - public event Action? OnSelectSpell; + public event Action? OnSelectedSpell; public BloodMagicMenu() { RobustXamlLoader.Load(this); - IoCManager.InjectDependencies(this); - InitializeButtons(); } @@ -37,12 +30,9 @@ public sealed partial class BloodMagicMenu : RadialMenu BloodRitesButton.OnButtonUp += _ => HandleSpellSelection("ActionBloodCultBloodRites"); } - private void HandleSpellSelection(string spellName) + private void HandleSpellSelection(EntProtoId spell) { - OnSelectSpell?.Invoke(spellName); - var netEntity = _entityManager.GetNetEntity(_playerManager.LocalSession?.AttachedEntity ?? EntityUid.Invalid); - _entityNetworkManager.SendSystemNetworkMessage(new BloodMagicMenuClosedEvent(netEntity, spellName)); - Close(); + OnSelectedSpell?.Invoke(spell); } } diff --git a/Content.Client/_Wega/BloodCult/Ui/BloodMagicUIController.cs b/Content.Client/_Wega/BloodCult/Ui/BloodMagicUIController.cs deleted file mode 100644 index 49721973ff..0000000000 --- a/Content.Client/_Wega/BloodCult/Ui/BloodMagicUIController.cs +++ /dev/null @@ -1,45 +0,0 @@ -using Content.Shared.Blood.Cult; -using Robust.Client.Player; -using Robust.Client.UserInterface; -using Robust.Client.UserInterface.Controllers; - -namespace Content.Client.Blood.Magic.UI -{ - public sealed class BloodMagicMenuUIController : UIController - { - [Dependency] private readonly IUserInterfaceManager _uiManager = default!; - [Dependency] private readonly IEntityManager _entityManager = default!; - - private BloodMagicMenu? _menu; - - public override void Initialize() - { - base.Initialize(); - SubscribeNetworkEvent(OnBloodMagicMenuReceived); - } - - private void OnBloodMagicMenuReceived(BloodMagicPressedEvent args, EntitySessionEventArgs eventArgs) - { - var session = IoCManager.Resolve().LocalSession; - var userEntity = _entityManager.GetEntity(args.Uid); - if (session?.AttachedEntity.HasValue == true && session.AttachedEntity.Value == userEntity) - { - if (_menu is null) - { - _menu = _uiManager.CreateWindow(); - _menu.OnClose += OnMenuClosed; - _menu.OpenCentered(); - } - else - { - _menu.OpenCentered(); - } - } - } - - private void OnMenuClosed() - { - _menu = null; - } - } -} diff --git a/Content.Client/_Wega/BloodCult/Ui/BloodRitesBoundUserInterface.cs b/Content.Client/_Wega/BloodCult/Ui/BloodRitesBoundUserInterface.cs new file mode 100644 index 0000000000..a88c1ffc08 --- /dev/null +++ b/Content.Client/_Wega/BloodCult/Ui/BloodRitesBoundUserInterface.cs @@ -0,0 +1,28 @@ +using Content.Shared.Blood.Cult; +using Robust.Client.UserInterface; + +namespace Content.Client._Wega.BloodCult.Ui; + +public sealed class BloodRitesBoundUserInterface : BoundUserInterface +{ + [ViewVariables] + private BloodRitesMenu? _menu; + + public BloodRitesBoundUserInterface(EntityUid owner, Enum uiKey) : base(owner, uiKey) + { + } + + protected override void Open() + { + base.Open(); + + _menu = this.CreateWindow(); + _menu.OnSelectRites += rites => + { + SendMessage(new BloodRitesSelectRitesMessage(rites)); + Close(); + }; + + _menu.OpenCentered(); + } +} diff --git a/Content.Client/_Wega/BloodCult/Ui/BloodRitesMenu.xaml b/Content.Client/_Wega/BloodCult/Ui/BloodRitesMenu.xaml index f943415e73..a44ef51a5a 100644 --- a/Content.Client/_Wega/BloodCult/Ui/BloodRitesMenu.xaml +++ b/Content.Client/_Wega/BloodCult/Ui/BloodRitesMenu.xaml @@ -1,32 +1,27 @@ + VerticalExpand="True" HorizontalExpand="True"> - + - + - + - + - + diff --git a/Content.Client/_Wega/BloodCult/Ui/BloodRitesMenu.xaml.cs b/Content.Client/_Wega/BloodCult/Ui/BloodRitesMenu.xaml.cs index 1d6f22ead8..cb422c6138 100644 --- a/Content.Client/_Wega/BloodCult/Ui/BloodRitesMenu.xaml.cs +++ b/Content.Client/_Wega/BloodCult/Ui/BloodRitesMenu.xaml.cs @@ -1,25 +1,18 @@ using Content.Client.UserInterface.Controls; -using Content.Shared.Blood.Cult; using Robust.Client.AutoGenerated; using Robust.Client.UserInterface.XAML; -using Robust.Shared.Player; +using Robust.Shared.Prototypes; -namespace Content.Client.Blood.Rites.UI; +namespace Content.Client._Wega.BloodCult.Ui; [GenerateTypedNameReferences] public sealed partial class BloodRitesMenu : RadialMenu { - [Dependency] private readonly IEntityManager _entityManager = default!; - [Dependency] private readonly IEntityNetworkManager _entityNetworkManager = default!; - [Dependency] private readonly ISharedPlayerManager _playerManager = default!; - - public event Action? OnSelectRites; + public event Action? OnSelectRites; public BloodRitesMenu() { RobustXamlLoader.Load(this); - IoCManager.InjectDependencies(this); - InitializeButtons(); } @@ -31,12 +24,9 @@ public sealed partial class BloodRitesMenu : RadialMenu BloodBoltBarrageButton.OnButtonUp += _ => HandleRitesSelection("ActionBloodCultBoltBarrage"); } - private void HandleRitesSelection(string ritesName) + private void HandleRitesSelection(EntProtoId rites) { - OnSelectRites?.Invoke(ritesName); - var netEntity = _entityManager.GetNetEntity(_playerManager.LocalSession?.AttachedEntity ?? EntityUid.Invalid); - _entityNetworkManager.SendSystemNetworkMessage(new BloodRitesMenuClosedEvent(netEntity, ritesName)); + OnSelectRites?.Invoke(rites); Close(); } } - diff --git a/Content.Client/_Wega/BloodCult/Ui/BloodRitesUIController.cs b/Content.Client/_Wega/BloodCult/Ui/BloodRitesUIController.cs deleted file mode 100644 index 104175b056..0000000000 --- a/Content.Client/_Wega/BloodCult/Ui/BloodRitesUIController.cs +++ /dev/null @@ -1,45 +0,0 @@ -using Content.Shared.Blood.Cult; -using Robust.Client.Player; -using Robust.Client.UserInterface; -using Robust.Client.UserInterface.Controllers; - -namespace Content.Client.Blood.Rites.UI -{ - public sealed class BloodRitesMenuUIController : UIController - { - [Dependency] private readonly IUserInterfaceManager _uiManager = default!; - [Dependency] private readonly IEntityManager _entityManager = default!; - - private BloodRitesMenu? _menu; - - public override void Initialize() - { - base.Initialize(); - SubscribeNetworkEvent(OnBloodMagicMenuReceived); - } - - private void OnBloodMagicMenuReceived(BloodRitesPressedEvent args, EntitySessionEventArgs eventArgs) - { - var session = IoCManager.Resolve().LocalSession; - var userEntity = _entityManager.GetEntity(args.Uid); - if (session?.AttachedEntity.HasValue == true && session.AttachedEntity.Value == userEntity) - { - if (_menu is null) - { - _menu = _uiManager.CreateWindow(); - _menu.OnClose += OnMenuClosed; - _menu.OpenCentered(); - } - else - { - _menu.OpenCentered(); - } - } - } - - private void OnMenuClosed() - { - _menu = null; - } - } -} diff --git a/Content.Client/_Wega/BloodCult/Ui/BloodStructureBoundUserInterface.cs b/Content.Client/_Wega/BloodCult/Ui/BloodStructureBoundUserInterface.cs new file mode 100644 index 0000000000..596519b6f6 --- /dev/null +++ b/Content.Client/_Wega/BloodCult/Ui/BloodStructureBoundUserInterface.cs @@ -0,0 +1,38 @@ +using Content.Shared.Blood.Cult; +using JetBrains.Annotations; +using Robust.Client.UserInterface; + +namespace Content.Client._Wega.BloodCult.Ui; + +[UsedImplicitly] +public sealed class BloodStructureBoundUserInterface : BoundUserInterface +{ + [ViewVariables] + private BloodStructureMenu? _menu; + + public BloodStructureBoundUserInterface(EntityUid owner, Enum uiKey) : base(owner, uiKey) + { + } + + protected override void Open() + { + base.Open(); + + _menu = this.CreateWindow(); + _menu.OnSelectItem += item => + { + SendMessage(new BloodStructureSelectMessage(item)); + Close(); + }; + + _menu.OpenCentered(); + } + + protected override void UpdateState(BoundUserInterfaceState state) + { + base.UpdateState(state); + + if (state is BloodStructureBoundUserInterfaceState structureState) + _menu?.InitializeButtons(structureState); + } +} diff --git a/Content.Client/_Wega/BloodCult/Ui/BloodStructureMenu.xaml b/Content.Client/_Wega/BloodCult/Ui/BloodStructureMenu.xaml index ab3b82e370..a76de06fce 100644 --- a/Content.Client/_Wega/BloodCult/Ui/BloodStructureMenu.xaml +++ b/Content.Client/_Wega/BloodCult/Ui/BloodStructureMenu.xaml @@ -1,14 +1,9 @@ + VerticalExpand="True" HorizontalExpand="True"> - + diff --git a/Content.Client/_Wega/BloodCult/Ui/BloodStructureMenu.xaml.cs b/Content.Client/_Wega/BloodCult/Ui/BloodStructureMenu.xaml.cs index 6ed14c0591..1b152b068c 100644 --- a/Content.Client/_Wega/BloodCult/Ui/BloodStructureMenu.xaml.cs +++ b/Content.Client/_Wega/BloodCult/Ui/BloodStructureMenu.xaml.cs @@ -1,48 +1,33 @@ using System.Numerics; using Content.Client.UserInterface.Controls; using Content.Shared.Blood.Cult; -using Content.Shared.Blood.Cult.Components; using Robust.Client.AutoGenerated; using Robust.Client.UserInterface.Controls; using Robust.Client.UserInterface.XAML; -using Robust.Shared.Player; using Robust.Shared.Prototypes; -namespace Content.Client.Structure.UI; +namespace Content.Client._Wega.BloodCult.Ui; [GenerateTypedNameReferences] public sealed partial class BloodStructureMenu : RadialMenu { - [Dependency] private readonly IEntityManager _entityManager = default!; - [Dependency] private readonly IEntityNetworkManager _entityNetworkManager = default!; - [Dependency] private readonly IPrototypeManager _prototypeManager = default!; - [Dependency] private readonly ISharedPlayerManager _playerManager = default!; + [Dependency] private readonly IPrototypeManager _proto = default!; public event Action? OnSelectItem; - private NetEntity _structure; public BloodStructureMenu() { RobustXamlLoader.Load(this); - IoCManager.InjectDependencies(this); + IoCManager.Instance!.InjectDependencies(this); } - public void SetData(NetEntity structure) + public void InitializeButtons(BloodStructureBoundUserInterfaceState state) { - _structure = structure; - InitializeButtons(); - } + Main.RemoveAllChildren(); - private void InitializeButtons() - { - var structure = _entityManager.GetEntity(_structure); - if (!_entityManager.TryGetComponent(structure, out var structureComp) - || structureComp.StructureGear.Count == 0) - return; - - foreach (var prototypeId in structureComp.StructureGear) + foreach (var prototypeId in state.Items) { - if (!_prototypeManager.TryIndex(prototypeId, out var prototype)) + if (!_proto.TryIndex(prototypeId, out var prototype)) continue; var button = new RadialMenuButton @@ -75,8 +60,6 @@ public sealed partial class BloodStructureMenu : RadialMenu private void HandleItemSelection(string name) { OnSelectItem?.Invoke(name); - var netEntity = _entityManager.GetNetEntity(_playerManager.LocalSession?.AttachedEntity ?? EntityUid.Invalid); - _entityNetworkManager.SendSystemNetworkMessage(new BloodStructureMenuClosedEvent(netEntity, name, _structure)); Close(); } } diff --git a/Content.Client/_Wega/BloodCult/Ui/BloodStructureUIController.cs b/Content.Client/_Wega/BloodCult/Ui/BloodStructureUIController.cs deleted file mode 100644 index 7807859b33..0000000000 --- a/Content.Client/_Wega/BloodCult/Ui/BloodStructureUIController.cs +++ /dev/null @@ -1,57 +0,0 @@ -using Content.Shared.Blood.Cult; -using Robust.Client.Player; -using Robust.Client.UserInterface; -using Robust.Client.UserInterface.Controllers; -using Timer = Robust.Shared.Timing.Timer; - -namespace Content.Client.Structure.UI -{ - public sealed class BloodStructureMenuUIController : UIController - { - [Dependency] private readonly IUserInterfaceManager _uiManager = default!; - [Dependency] private readonly IEntityManager _entityManager = default!; - - private BloodStructureMenu? _menu; - - public override void Initialize() - { - base.Initialize(); - SubscribeNetworkEvent(OnStructureMenuReceived); - } - - private void OnStructureMenuReceived(OpenStructureMenuEvent args, EntitySessionEventArgs eventArgs) - { - var session = IoCManager.Resolve().LocalSession; - var userEntity = _entityManager.GetEntity(args.Uid); - if (session?.AttachedEntity.HasValue == true && session.AttachedEntity.Value == userEntity) - { - if (_menu is null) - { - _menu = _uiManager.CreateWindow(); - _menu.OnClose += OnMenuClosed; - - _menu.SetData(args.Structure); - - _menu.OpenCentered(); - } - else - { - _menu.OpenCentered(); - } - - Timer.Spawn(30000, () => - { - if (_menu != null) - { - _menu.Close(); - } - }); - } - } - - private void OnMenuClosed() - { - _menu = null; - } - } -} diff --git a/Content.Client/_Wega/BloodCult/Ui/EmpoweringRuneBoundUserInterface.cs b/Content.Client/_Wega/BloodCult/Ui/EmpoweringRuneBoundUserInterface.cs new file mode 100644 index 0000000000..3e9f5dfbe1 --- /dev/null +++ b/Content.Client/_Wega/BloodCult/Ui/EmpoweringRuneBoundUserInterface.cs @@ -0,0 +1,28 @@ +using Content.Shared.Blood.Cult; +using Robust.Client.UserInterface; + +namespace Content.Client._Wega.BloodCult.Ui; + +public sealed class EmpoweringRuneBoundUserInterface : BoundUserInterface +{ + [ViewVariables] + private EmpoweringRuneMenu? _menu; + + public EmpoweringRuneBoundUserInterface(EntityUid owner, Enum uiKey) : base(owner, uiKey) + { + } + + protected override void Open() + { + base.Open(); + + _menu = this.CreateWindow(); + _menu.OnSelectSpell += spell => + { + SendMessage(new EmpoweringRuneSelectSpellMessage(spell)); + Close(); + }; + + _menu.OpenCentered(); + } +} diff --git a/Content.Client/_Wega/BloodCult/Ui/EmpoweringRuneMenu.xaml b/Content.Client/_Wega/BloodCult/Ui/EmpoweringRuneMenu.xaml index 4c49f3253a..18b06a8129 100644 --- a/Content.Client/_Wega/BloodCult/Ui/EmpoweringRuneMenu.xaml +++ b/Content.Client/_Wega/BloodCult/Ui/EmpoweringRuneMenu.xaml @@ -1,62 +1,57 @@ + VerticalExpand="True" HorizontalExpand="True"> - + - + - + - + - + - + - + - + - + - + - + diff --git a/Content.Client/_Wega/BloodCult/Ui/EmpoweringRuneMenu.xaml.cs b/Content.Client/_Wega/BloodCult/Ui/EmpoweringRuneMenu.xaml.cs new file mode 100644 index 0000000000..7db9818b57 --- /dev/null +++ b/Content.Client/_Wega/BloodCult/Ui/EmpoweringRuneMenu.xaml.cs @@ -0,0 +1,38 @@ +using Content.Client.UserInterface.Controls; +using Robust.Client.AutoGenerated; +using Robust.Client.UserInterface.XAML; +using Robust.Shared.Prototypes; + +namespace Content.Client._Wega.BloodCult.Ui; + +[GenerateTypedNameReferences] +public sealed partial class EmpoweringRuneMenu : RadialMenu +{ + public event Action? OnSelectSpell; + + public EmpoweringRuneMenu() + { + RobustXamlLoader.Load(this); + InitializeButtons(); + } + + private void InitializeButtons() + { + StunButton.OnButtonUp += _ => HandleSpellSelection("ActionBloodCultStun"); + TeleportButton.OnButtonUp += _ => HandleSpellSelection("ActionBloodCultTeleport"); + ElectromagneticPulseButton.OnButtonUp += _ => HandleSpellSelection("ActionBloodCultElectromagneticPulse"); + ShadowShacklesButton.OnButtonUp += _ => HandleSpellSelection("ActionBloodCultShadowShackles"); + TwistedConstructionButton.OnButtonUp += _ => HandleSpellSelection("ActionBloodCultTwistedConstruction"); + SummonEquipmentButton.OnButtonUp += _ => HandleSpellSelection("ActionBloodCultSummonEquipment"); + SummonDaggerButton.OnButtonUp += _ => HandleSpellSelection("ActionBloodCultSummonDagger"); + HallucinationsButton.OnButtonUp += _ => HandleSpellSelection("ActionBloodCultHallucinations"); + ConcealPresenceButton.OnButtonUp += _ => HandleSpellSelection("ActionBloodCultConcealPresence"); + BloodRitesButton.OnButtonUp += _ => HandleSpellSelection("ActionBloodCultBloodRites"); + } + + private void HandleSpellSelection(EntProtoId spell) + { + OnSelectSpell?.Invoke(spell); + Close(); + } +} diff --git a/Content.Client/_Wega/BloodCult/Ui/RunesMenu.xaml b/Content.Client/_Wega/BloodCult/Ui/RunesMenu.xaml index dd85b9490a..a76de06fce 100644 --- a/Content.Client/_Wega/BloodCult/Ui/RunesMenu.xaml +++ b/Content.Client/_Wega/BloodCult/Ui/RunesMenu.xaml @@ -1,22 +1,9 @@ - + - - - - - + + - - - - - - - + diff --git a/Content.Client/_Wega/BloodCult/Ui/RunesMenu.xaml.cs b/Content.Client/_Wega/BloodCult/Ui/RunesMenu.xaml.cs index 33213acbcd..4b2dbc4567 100644 --- a/Content.Client/_Wega/BloodCult/Ui/RunesMenu.xaml.cs +++ b/Content.Client/_Wega/BloodCult/Ui/RunesMenu.xaml.cs @@ -1,162 +1,74 @@ using System.Numerics; using Content.Client.UserInterface.Controls; -using Content.Shared.Blood.Cult; -using Content.Shared.Blood.Cult.Components; using Robust.Client.AutoGenerated; using Robust.Client.UserInterface.Controls; -using Robust.Client.UserInterface.CustomControls; using Robust.Client.UserInterface.XAML; -using Robust.Shared.Player; +using Robust.Shared.Prototypes; -namespace Content.Client.Runes.Panel.Ui; +namespace Content.Client._Wega.BloodCult.Ui; -public sealed partial class RunesPanelMenu : DefaultWindow +[GenerateTypedNameReferences] +public sealed partial class RunesMenu : RadialMenu { - [Dependency] private readonly IEntityManager _entityManager = default!; - [Dependency] private readonly IEntityNetworkManager _entityNetworkManager = default!; - [Dependency] private readonly ISharedPlayerManager _playerManager = default!; + [Dependency] private readonly IPrototypeManager _proto = default!; - public event Action? OnRuneSelected; - public BoxContainer RunesContainer => this.FindControl("RunesContainer"); + public event Action? OnRuneSelected; - public RunesPanelMenu() + public RunesMenu() { RobustXamlLoader.Load(this); - IoCManager.InjectDependencies(this); - + IoCManager.Instance!.InjectDependencies(this); InitializeRunes(); } private void InitializeRunes() { - AddRuneButton(Loc.GetString("offering-rune"), "BloodRuneOffering"); - AddRuneButton(Loc.GetString("teleport-rune"), "BloodRuneTeleport"); - AddRuneButton(Loc.GetString("empowering-rune"), "BloodRuneEmpowering"); - AddRuneButton(Loc.GetString("revive-rune"), "BloodRuneRevive"); - AddRuneButton(Loc.GetString("barrier-rune"), "BloodRuneBarrier"); - AddRuneButton(Loc.GetString("summoning-rune"), "BloodRuneSummoning"); - AddRuneButton(Loc.GetString("bloodboil-rune"), "BloodRuneBloodBoil"); - AddRuneButton(Loc.GetString("spiritrealm-rune"), "BloodRuneSpiritealm"); - AddRuneButton(Loc.GetString("ritual-dimensional-rending-rune"), "BloodRuneRitualDimensionalRending"); + AddRuneButton("offering-rune", "BloodRuneOffering"); + AddRuneButton("teleport-rune", "BloodRuneTeleport"); + AddRuneButton("empowering-rune", "BloodRuneEmpowering"); + AddRuneButton("revive-rune", "BloodRuneRevive"); + AddRuneButton("barrier-rune", "BloodRuneBarrier"); + AddRuneButton("summoning-rune", "BloodRuneSummoning"); + AddRuneButton("bloodboil-rune", "BloodRuneBloodBoil"); + AddRuneButton("spiritrealm-rune", "BloodRuneSpiritealm"); + } + + public void AddRitualButton() + { + AddRuneButton("ritual-dimensional-rending-rune", "BloodRuneRitualDimensionalRending"); } private void AddRuneButton(string runeName, string protoId) { - var button = new Button + if (!_proto.TryIndex(protoId, out var prototype)) + return; + + var button = new RadialMenuButton { - Text = runeName, - MinSize = new Vector2(300, 32), - MaxSize = new Vector2(300, 32), - HorizontalAlignment = HAlignment.Center, - VerticalAlignment = VAlignment.Center, + ToolTip = Loc.GetString(runeName) + $"\n{Loc.GetString(runeName + "-desc")}", + SetSize = new Vector2(64, 64), }; + button.StyleClasses.Add("RadialMenuButton"); + + var entityView = new EntityPrototypeView + { + Scale = new Vector2(2, 2), + SetSize = new Vector2(64, 64), + Margin = new Thickness(4) + }; + entityView.SetPrototype(prototype.ID); + + button.AddChild(entityView); + button.OnPressed += _ => HandleRuneSelection(protoId); - RunesContainer.AddChild(button); + Main.AddChild(button); } - private void HandleRuneSelection(string protoId) + private void HandleRuneSelection(EntProtoId protoId) { OnRuneSelected?.Invoke(protoId); - var netEntity = _entityManager.GetNetEntity(_playerManager.LocalSession?.AttachedEntity ?? EntityUid.Invalid); - _entityNetworkManager.SendSystemNetworkMessage(new RuneSelectEvent(netEntity, protoId)); - Close(); - } - - public new void Close() - { - base.Close(); - } -} - -[GenerateTypedNameReferences] -public sealed partial class EmpoweringRuneMenu : RadialMenu -{ - [Dependency] private readonly IEntityManager _entityManager = default!; - [Dependency] private readonly IEntityNetworkManager _entityNetworkManager = default!; - [Dependency] private readonly ISharedPlayerManager _playerManager = default!; - - public event Action? OnSelectSpell; - - public EmpoweringRuneMenu() - { - RobustXamlLoader.Load(this); - IoCManager.InjectDependencies(this); - - InitializeButtons(); - } - - private void InitializeButtons() - { - StunButton.OnButtonUp += _ => HandleSpellSelection("ActionBloodCultStun"); - TeleportButton.OnButtonUp += _ => HandleSpellSelection("ActionBloodCultTeleport"); - ElectromagneticPulseButton.OnButtonUp += _ => HandleSpellSelection("ActionBloodCultElectromagneticPulse"); - ShadowShacklesButton.OnButtonUp += _ => HandleSpellSelection("ActionBloodCultShadowShackles"); - TwistedConstructionButton.OnButtonUp += _ => HandleSpellSelection("ActionBloodCultTwistedConstruction"); - SummonEquipmentButton.OnButtonUp += _ => HandleSpellSelection("ActionBloodCultSummonEquipment"); - SummonDaggerButton.OnButtonUp += _ => HandleSpellSelection("ActionBloodCultSummonDagger"); - HallucinationsButton.OnButtonUp += _ => HandleSpellSelection("ActionBloodCultHallucinations"); - ConcealPresenceButton.OnButtonUp += _ => HandleSpellSelection("ActionBloodCultConcealPresence"); - BloodRitesButton.OnButtonUp += _ => HandleSpellSelection("ActionBloodCultBloodRites"); - } - - private void HandleSpellSelection(string spellName) - { - OnSelectSpell?.Invoke(spellName); - var netEntity = _entityManager.GetNetEntity(_playerManager.LocalSession?.AttachedEntity ?? EntityUid.Invalid); - _entityNetworkManager.SendSystemNetworkMessage(new EmpoweringRuneMenuClosedEvent(netEntity, spellName)); - Close(); - } -} - -public sealed partial class SummoningRunePanelMenu : DefaultWindow -{ - [Dependency] private readonly IEntityManager _entityManager = default!; - [Dependency] private readonly IEntityNetworkManager _entityNetworkManager = default!; - [Dependency] private readonly ISharedPlayerManager _playerManager = default!; - - public BoxContainer CultistsContainer => this.FindControl("CultistsContainer"); - - public SummoningRunePanelMenu() - { - RobustXamlLoader.Load(this); - IoCManager.InjectDependencies(this); - - InitializeButtons(); - } - - private void InitializeButtons() - { - var cultistQuery = _entityManager.EntityQueryEnumerator(); - while (cultistQuery.MoveNext(out var uid, out _, out var metaData)) - { - var entityName = metaData.EntityName; - AddCultistButton(entityName, uid); - } - } - - private void AddCultistButton(string cultistName, EntityUid cultistUid) - { - var button = new Button - { - Text = cultistName, - HorizontalAlignment = HAlignment.Center, - VerticalAlignment = VAlignment.Center, - MinSize = new Vector2(300, 32), - MaxSize = new Vector2(300, 32) - }; - - button.OnPressed += _ => HandleCultistSelection(cultistUid); - - CultistsContainer.AddChild(button); - } - - private void HandleCultistSelection(EntityUid cultistUid) - { - var netTargerEntity = _entityManager.GetNetEntity(cultistUid); - var netEntity = _entityManager.GetNetEntity(_playerManager.LocalSession?.AttachedEntity ?? EntityUid.Invalid); - _entityNetworkManager.SendSystemNetworkMessage(new SummoningSelectedEvent(netEntity, netTargerEntity)); Close(); } } diff --git a/Content.Client/_Wega/BloodCult/Ui/RunesMenuBoundUserInterface.cs b/Content.Client/_Wega/BloodCult/Ui/RunesMenuBoundUserInterface.cs new file mode 100644 index 0000000000..b81faf6bd3 --- /dev/null +++ b/Content.Client/_Wega/BloodCult/Ui/RunesMenuBoundUserInterface.cs @@ -0,0 +1,38 @@ +using Content.Shared.Blood.Cult; +using JetBrains.Annotations; +using Robust.Client.UserInterface; + +namespace Content.Client._Wega.BloodCult.Ui; + +[UsedImplicitly] +public sealed class RunesMenuBoundUserInterface : BoundUserInterface +{ + [ViewVariables] + private RunesMenu? _menu; + + public RunesMenuBoundUserInterface(EntityUid owner, Enum uiKey) : base(owner, uiKey) + { + } + + protected override void Open() + { + base.Open(); + + _menu = this.CreateWindow(); + _menu.OnRuneSelected += rune => + { + SendMessage(new SelectBloodRuneMessage(rune)); + Close(); + }; + + _menu.OpenCentered(); + } + + protected override void UpdateState(BoundUserInterfaceState state) + { + base.UpdateState(state); + + if (state is BloodRitualBoundUserInterfaceState _) + _menu?.AddRitualButton(); + } +} diff --git a/Content.Client/_Wega/BloodCult/Ui/RunesMenuUIController.cs b/Content.Client/_Wega/BloodCult/Ui/RunesMenuUIController.cs deleted file mode 100644 index 3d57eea8c1..0000000000 --- a/Content.Client/_Wega/BloodCult/Ui/RunesMenuUIController.cs +++ /dev/null @@ -1,144 +0,0 @@ -using Content.Shared.Blood.Cult; -using Robust.Client.Player; -using Robust.Client.UserInterface; -using Robust.Client.UserInterface.Controllers; -using Timer = Robust.Shared.Timing.Timer; - -namespace Content.Client.Runes.Panel.Ui -{ - public sealed class RunesMenuUIController : UIController - { - [Dependency] private readonly IUserInterfaceManager _uiManager = default!; - [Dependency] private readonly IEntityManager _entityManager = default!; - - private RunesPanelMenu? _panel; - - public override void Initialize() - { - base.Initialize(); - - SubscribeNetworkEvent(OnRunesMenuReceived); - } - - private void OnRunesMenuReceived(RunesMenuOpenedEvent args, EntitySessionEventArgs eventArgs) - { - var session = IoCManager.Resolve().LocalSession; - var userEntity = _entityManager.GetEntity(args.Uid); - if (session?.AttachedEntity.HasValue == true && session.AttachedEntity.Value == userEntity) - { - if (_panel is null) - { - _panel = _uiManager.CreateWindow(); - _panel.OnClose += OnMenuClosed; - _panel.OpenCentered(); - } - else - { - _panel.OpenCentered(); - } - } - } - - private void OnMenuClosed() - { - _panel = null; - } - } - - public sealed class EmpoweringRuneMenuUIController : UIController - { - [Dependency] private readonly IUserInterfaceManager _uiManager = default!; - [Dependency] private readonly IEntityManager _entityManager = default!; - - private EmpoweringRuneMenu? _menu; - private bool _menuDisposed = false; - - public override void Initialize() - { - base.Initialize(); - - SubscribeNetworkEvent(OnRuneMenuReceived); - } - - private void OnRuneMenuReceived(EmpoweringRuneMenuOpenedEvent args, EntitySessionEventArgs eventArgs) - { - var session = IoCManager.Resolve().LocalSession; - var userEntity = _entityManager.GetEntity(args.Uid); - if (session?.AttachedEntity.HasValue == true && session.AttachedEntity.Value == userEntity) - { - if (_menu is null) - { - _menu = _uiManager.CreateWindow(); - _menu.OnClose += OnMenuClosed; - _menu.OpenCentered(); - } - else - { - _menu.OpenCentered(); - } - } - - Timer.Spawn(30000, () => - { - if (_menu != null && !_menuDisposed) - { - _menu.Close(); - } - }); - } - - private void OnMenuClosed() - { - _menuDisposed = true; - _menu = null; - } - - } - - public sealed class SummoningRuneMenuUIController : UIController - { - [Dependency] private readonly IUserInterfaceManager _uiManager = default!; - [Dependency] private readonly IEntityManager _entityManager = default!; - - private SummoningRunePanelMenu? _panel; - - public override void Initialize() - { - base.Initialize(); - - SubscribeNetworkEvent(OnRuneMenuReceived); - } - - private void OnRuneMenuReceived(SummoningRuneMenuOpenedEvent args, EntitySessionEventArgs eventArgs) - { - var session = IoCManager.Resolve().LocalSession; - var userEntity = _entityManager.GetEntity(args.Uid); - if (session?.AttachedEntity.HasValue == true && session.AttachedEntity.Value == userEntity) - { - if (_panel is null) - { - _panel = _uiManager.CreateWindow(); - _panel.OnClose += OnMenuClosed; - _panel.OpenCentered(); - } - else - { - _panel.OpenCentered(); - } - - Timer.Spawn(30000, () => - { - if (_panel != null) - { - _panel.Close(); - } - }); - } - } - - private void OnMenuClosed() - { - _panel = null; - } - } -} diff --git a/Content.Client/_Wega/BloodCult/Ui/SummoningRuneBoundUserInterface.cs b/Content.Client/_Wega/BloodCult/Ui/SummoningRuneBoundUserInterface.cs new file mode 100644 index 0000000000..d0fce7e8d3 --- /dev/null +++ b/Content.Client/_Wega/BloodCult/Ui/SummoningRuneBoundUserInterface.cs @@ -0,0 +1,28 @@ +using Content.Shared.Blood.Cult; +using Robust.Client.UserInterface; + +namespace Content.Client._Wega.BloodCult.Ui; + +public sealed class SummoningRuneBoundUserInterface : BoundUserInterface +{ + [ViewVariables] + private SummoningRuneMenu? _menu; + + public SummoningRuneBoundUserInterface(EntityUid owner, Enum uiKey) : base(owner, uiKey) + { + } + + protected override void Open() + { + base.Open(); + + _menu = this.CreateWindow(); + _menu.OnCultistSelected += cultist => + { + SendMessage(new SummoningRuneSelectCultistMessage(cultist)); + Close(); + }; + + _menu.OpenCentered(); + } +} diff --git a/Content.Client/_Wega/BloodCult/Ui/SummoningRuneMenu.xaml b/Content.Client/_Wega/BloodCult/Ui/SummoningRuneMenu.xaml index da5f6502ef..b87c20fdcf 100644 --- a/Content.Client/_Wega/BloodCult/Ui/SummoningRuneMenu.xaml +++ b/Content.Client/_Wega/BloodCult/Ui/SummoningRuneMenu.xaml @@ -1,8 +1,7 @@ - @@ -19,4 +18,4 @@ - + diff --git a/Content.Client/_Wega/BloodCult/Ui/SummoningRuneMenu.xaml.cs b/Content.Client/_Wega/BloodCult/Ui/SummoningRuneMenu.xaml.cs new file mode 100644 index 0000000000..b036a7b36c --- /dev/null +++ b/Content.Client/_Wega/BloodCult/Ui/SummoningRuneMenu.xaml.cs @@ -0,0 +1,56 @@ +using System.Numerics; +using Content.Client.UserInterface.Controls; +using Content.Shared.Blood.Cult.Components; +using Robust.Client.AutoGenerated; +using Robust.Client.UserInterface.Controls; +using Robust.Client.UserInterface.XAML; + +namespace Content.Client._Wega.BloodCult.Ui; + +[GenerateTypedNameReferences] +public sealed partial class SummoningRuneMenu : FancyWindow +{ + [Dependency] private readonly IEntityManager _entityManager = default!; + + public event Action? OnCultistSelected; + + public SummoningRuneMenu() + { + RobustXamlLoader.Load(this); + IoCManager.Instance!.InjectDependencies(this); + InitializeButtons(); + } + + private void InitializeButtons() + { + var cultistQuery = _entityManager.EntityQueryEnumerator(); + while (cultistQuery.MoveNext(out var uid, out _, out var metaData)) + { + var entityName = metaData.EntityName; + AddCultistButton(entityName, uid); + } + } + + private void AddCultistButton(string cultistName, EntityUid cultistUid) + { + var button = new Button + { + Text = cultistName, + HorizontalAlignment = HAlignment.Center, + VerticalAlignment = VAlignment.Center, + MinSize = new Vector2(300, 32), + MaxSize = new Vector2(300, 32) + }; + + button.OnPressed += _ => HandleCultistSelection(cultistUid); + + CultistsContainer.AddChild(button); + } + + private void HandleCultistSelection(EntityUid cultistUid) + { + var netCultist = _entityManager.GetNetEntity(cultistUid); + OnCultistSelected?.Invoke(netCultist); + Close(); + } +} diff --git a/Content.Client/_Wega/CardTarot/CardTarotSystem.cs b/Content.Client/_Wega/CardTarot/CardTarotSystem.cs new file mode 100644 index 0000000000..98966034aa --- /dev/null +++ b/Content.Client/_Wega/CardTarot/CardTarotSystem.cs @@ -0,0 +1,29 @@ +using Content.Shared.Card.Tarot; +using Content.Shared.Card.Tarot.Components; +using Robust.Client.GameObjects; + +namespace Content.Client.Card.Tarot; + +public sealed class CardTarotSystem : EntitySystem +{ + [Dependency] private readonly AppearanceSystem _appearance = default!; + [Dependency] private readonly SpriteSystem _sprite = default!; + + public override void Initialize() + { + base.Initialize(); + SubscribeLocalEvent(OnAppearanceChanged); + } + + private void OnAppearanceChanged(Entity entity, ref AppearanceChangeEvent args) + { + if (!_appearance.TryGetData(entity, CardTarotVisuals.State, out CardTarot card) + || !_appearance.TryGetData(entity, CardTarotVisuals.Reversed, out bool reversed)) + return; + + var state = card.ToString().ToLower(); + if (reversed) state += "-reversed"; + + _sprite.LayerSetRsiState(entity.Owner, 0, state); + } +} diff --git a/Content.Client/_Wega/Vampire/Ui/SelectClassMenu.xaml b/Content.Client/_Wega/Vampire/Ui/SelectClassMenu.xaml index 83d0a2289e..902913d1cc 100644 --- a/Content.Client/_Wega/Vampire/Ui/SelectClassMenu.xaml +++ b/Content.Client/_Wega/Vampire/Ui/SelectClassMenu.xaml @@ -13,27 +13,27 @@ - + - + - + - + diff --git a/Content.Server/Chat/Systems/ChatSystem.cs b/Content.Server/Chat/Systems/ChatSystem.cs index 73731d2a51..393a43a3eb 100644 --- a/Content.Server/Chat/Systems/ChatSystem.cs +++ b/Content.Server/Chat/Systems/ChatSystem.cs @@ -36,6 +36,8 @@ using Robust.Shared.Replays; using Robust.Shared.Utility; using Content.Shared.Strangulation; // Corvax-Wega-Strangulation using Content.Shared.SoundInsolation; // Corvax-Wega-SoundInsolation +using Content.Shared.Mind; // Corvax-Wega-MindChat +using Content.Shared.Blood.Cult.Components; // Corvax-Wega-Blood-Cult namespace Content.Server.Chat.Systems; @@ -235,6 +237,14 @@ public sealed partial class ChatSystem : SharedChatSystem SendEntityWhisper(source, modMessage, range, channel, nameOverride, hideLog, ignoreActionBlocker); return; } + + // Corvax-Wega-MindChat-start + if (TryProcessMindMessage(source, message, out var mindMessage, out var mindChannel) && mindChannel != null) + { + SendMindMessage(source, mindMessage, mindChannel, ignoreActionBlocker); + return; + } + // Corvax-Wega-MindChat-end } // Otherwise, send whatever type. @@ -384,6 +394,69 @@ public sealed partial class ChatSystem : SharedChatSystem _adminLogger.Add(LogType.Chat, LogImpact.Low, $"Station Announcement on {station} from {sender}: {message}"); } + // Corvax-Wega-MindChat-start + /// + public override void SendMindMessage( + EntityUid source, + string message, + MindChannelPrototype channel, + bool ignoreActionBlocker = false) + { + if (string.IsNullOrWhiteSpace(message)) + return; + + if (!ignoreActionBlocker && !_actionBlocker.CanSpeak(source)) + return; + + var name = MetaData(source).EntityName; + name = FormattedMessage.EscapeText(name); + + var wrappedMessage = Loc.GetString("chat-mind-message-wrap", + ("color", channel.Color), + ("channel", $"\\[{channel.LocalizedName}\\]"), + ("name", name), + ("message", message)); + + // Send to all entities with the same mind channel + foreach (var (session, _) in GetRecipients(source, MindChatRange)) + { + if ((!TryComp(session.AttachedEntity, out var mindLink) || !mindLink.Channels.Contains(channel.ID)) + && !HasComp(session.AttachedEntity)) + continue; + + _chatManager.ChatMessageToOne( + ChatChannel.Mind, + message, + wrappedMessage, + source, + false, + session.Channel); + } + + // Also send a whisper + TrySendInGameICMessage( + source, + message, + InGameICChatType.Whisper, + ChatTransmitRange.Normal, + nameOverride: name, + ignoreActionBlocker: true); + + // Log to admin logs + _adminLogger.Add(LogType.Chat, LogImpact.Low, + $"Mind message from {ToPrettyString(source):user} on {channel.LocalizedName}: {message}"); + + // Record for replay + var chat = new ChatMessage( + ChatChannel.Mind, + message, + wrappedMessage, + GetNetEntity(source), + null); + _replay.RecordServerMessage(chat); + } + // Corvax-Wega-MindChat-end + #endregion #region Private API @@ -634,6 +707,9 @@ public sealed partial class ChatSystem : SharedChatSystem private void SendDeadChat(EntityUid source, ICommonSession player, string message, bool hideChat) { + if (HasComp(source)) // Corvax-Wega-Blood-Cult-Add + return; // Corvax-Wega-Blood-Cult-Add + var clients = GetDeadChatClients(); var playerName = Name(source); string wrappedMessage; @@ -814,6 +890,7 @@ public sealed partial class ChatSystem : SharedChatSystem .AddWhereAttachedEntity(HasComp) .Recipients .Union(_adminManager.ActiveAdmins) + .Where(d => !HasComp(d.AttachedEntity)) // Corvax-Wega-Blood-Cult-Add .Select(p => p.Channel); } diff --git a/Content.Server/_Wega/BloodCult/BloodCultSystem.Abilities.cs b/Content.Server/_Wega/BloodCult/BloodCultSystem.Abilities.cs index d041b75b69..aedbafccfc 100644 --- a/Content.Server/_Wega/BloodCult/BloodCultSystem.Abilities.cs +++ b/Content.Server/_Wega/BloodCult/BloodCultSystem.Abilities.cs @@ -1,51 +1,51 @@ using System.Linq; using Content.Server.Administration; -using Content.Server.Administration.Logs; +using Content.Server.Blood.Cult.UI; using Content.Server.Body.Systems; using Content.Server.Chat.Systems; using Content.Server.Emp; +using Content.Server.EUI; using Content.Server.Flash; using Content.Server.Hallucinations; using Content.Shared.Bed.Sleep; using Content.Shared.Blood.Cult; using Content.Shared.Blood.Cult.Components; +using Content.Shared.Body.Components; +using Content.Shared.Card.Tarot; +using Content.Shared.Card.Tarot.Components; using Content.Shared.Chemistry.Components; using Content.Shared.Clothing; using Content.Shared.Cuffs; using Content.Shared.Cuffs.Components; using Content.Shared.Damage; using Content.Shared.Damage.Components; +using Content.Shared.Damage.Systems; using Content.Shared.DoAfter; using Content.Shared.Doors.Components; -using Content.Shared.Database; +using Content.Shared.EnergyShield; using Content.Shared.FixedPoint; using Content.Shared.Fluids.Components; -using Content.Shared.Hands.EntitySystems; using Content.Shared.Humanoid; using Content.Shared.Interaction; using Content.Shared.Interaction.Events; using Content.Shared.Inventory; -using Content.Shared.Mobs; -using Content.Shared.Mobs.Components; +using Content.Shared.NullRod.Components; using Content.Shared.Popups; using Content.Shared.Roles; using Content.Shared.Stacks; using Content.Shared.Standing; -using Content.Shared.Speech.Muting; +using Content.Shared.StatusEffectNew; using Content.Shared.Stunnable; using Content.Shared.Timing; using Robust.Server.GameObjects; +using Robust.Shared.Audio; using Robust.Shared.Containers; +using Robust.Shared.Map; using Robust.Shared.Physics.Components; using Robust.Shared.Physics.Systems; -using Robust.Shared.Prototypes; using Robust.Shared.Player; -using Robust.Shared.Timing; -using Content.Shared.Flash.Components; -using Content.Shared.Body.Components; -using Content.Shared.NullRod.Components; -using Content.Shared.Chat; -using Content.Shared.Damage.Systems; +using Robust.Shared.Prototypes; +using Robust.Shared.Random; namespace Content.Server.Blood.Cult; @@ -55,34 +55,33 @@ public sealed partial class BloodCultSystem [Dependency] private readonly ChatSystem _chat = default!; [Dependency] private readonly DamageableSystem _damage = default!; [Dependency] private readonly EmpSystem _emp = default!; - [Dependency] private readonly IEntityManager _entityManager = default!; [Dependency] private readonly EntityLookupSystem _entityLookup = default!; + [Dependency] private readonly EuiManager _euiMan = default!; [Dependency] private readonly FixtureSystem _fixtures = default!; [Dependency] private readonly FlashSystem _flash = default!; [Dependency] private readonly HallucinationsSystem _hallucinations = default!; - [Dependency] private readonly IAdminLogManager _admin = default!; - [Dependency] private readonly IPrototypeManager _prototypeManager = default!; - [Dependency] private readonly InventorySystem _inventorySystem = default!; + [Dependency] private readonly InventorySystem _inventory = default!; + [Dependency] private readonly ISharedPlayerManager _player = default!; + [Dependency] private readonly LoadoutSystem _loadout = default!; [Dependency] private readonly QuickDialogSystem _quickDialog = default!; [Dependency] private readonly SharedCuffableSystem _cuff = default!; - [Dependency] private readonly SharedHandsSystem _hands = default!; [Dependency] private readonly SharedPhysicsSystem _physics = default!; [Dependency] private readonly SharedStackSystem _stack = default!; [Dependency] private readonly SharedStunSystem _stun = default!; [Dependency] private readonly SharedTransformSystem _transform = default!; + [Dependency] private readonly StatusEffectsSystem _statusEffect = default!; [Dependency] private readonly UseDelaySystem _useDelay = default!; [Dependency] private readonly VisibilitySystem _visibility = default!; - [Dependency] private readonly LoadoutSystem _loadout = default!; + + private static readonly SoundPathSpecifier CultSpell = new SoundPathSpecifier("/Audio/_Wega/Effects/cult_spell.ogg"); private void InitializeBloodAbilities() { // Blood Magic SubscribeLocalEvent(OnBloodMagic); - SubscribeNetworkEvent(AfterSpellSelect); SubscribeLocalEvent(DoAfterSpellSelect); // Abilities - SubscribeLocalEvent(OnCultCommune); SubscribeLocalEvent(OnInteract); SubscribeLocalEvent(OnRecallDagger); @@ -99,7 +98,7 @@ public sealed partial class BloodCultSystem SubscribeLocalEvent(OnBloodRites); SubscribeLocalEvent(BloodRites); - SubscribeNetworkEvent(BloodRitesSelect); + SubscribeLocalEvent(BloodRitesSelect); SubscribeLocalEvent(OnBloodOrb); SubscribeLocalEvent(OnBloodOrbAbsorbed); SubscribeLocalEvent(OnBloodRecharge); @@ -111,20 +110,24 @@ public sealed partial class BloodCultSystem #region Blood Magic private void OnBloodMagic(EntityUid uid, BloodCultistComponent component, BloodCultBloodMagicActionEvent args) { - var netEntity = _entityManager.GetNetEntity(uid); - RaiseNetworkEvent(new BloodMagicPressedEvent(netEntity)); - args.Handled = true; + if (_mind.TryGetMind(uid, out _, out var mind) && + mind is { UserId: not null } && _player.TryGetSessionById(mind.UserId, out var session)) + { + var menu = new BloodMagicEui(uid, this); + _euiMan.OpenEui(menu, session); + + args.Handled = true; + } } - private void AfterSpellSelect(BloodMagicMenuClosedEvent args, EntitySessionEventArgs eventArgs) + public void AfterSpellSelect(EntityUid cultist, EntProtoId selectedSpell) { - var uid = _entityManager.GetEntity(args.Uid); - if (!TryComp(uid, out var cult)) + if (!TryComp(cultist, out var cult)) return; if (!cult.BloodMagicActive) { - _doAfterSystem.TryStartDoAfter(new DoAfterArgs(EntityManager, uid, TimeSpan.FromSeconds(10f), new BloodMagicDoAfterEvent(args.SelectedSpell), uid) + _doAfterSystem.TryStartDoAfter(new DoAfterArgs(EntityManager, cultist, TimeSpan.FromSeconds(10f), new BloodMagicDoAfterEvent(selectedSpell), cultist) { BreakOnMove = true, BreakOnDamage = true, @@ -135,12 +138,11 @@ public sealed partial class BloodCultSystem else { var remSpell = cult.SelectedSpell; - if (remSpell != null) - _action.RemoveAction(uid, remSpell); + _action.RemoveAction(cultist, remSpell); cult.SelectedSpell = null; cult.BloodMagicActive = false; - _doAfterSystem.TryStartDoAfter(new DoAfterArgs(EntityManager, uid, TimeSpan.FromSeconds(10f), new BloodMagicDoAfterEvent(args.SelectedSpell), uid) + _doAfterSystem.TryStartDoAfter(new DoAfterArgs(EntityManager, cultist, TimeSpan.FromSeconds(10f), new BloodMagicDoAfterEvent(selectedSpell), cultist) { BreakOnMove = true, BreakOnDamage = true, @@ -152,13 +154,10 @@ public sealed partial class BloodCultSystem private void DoAfterSpellSelect(EntityUid cultist, BloodCultistComponent component, BloodMagicDoAfterEvent args) { - if (args.Cancelled) return; + if (args.Cancelled) + return; - var actionEntityUid = _action.AddAction(cultist, args.SelectedSpell); - if (actionEntityUid.HasValue) - component.SelectedSpell = actionEntityUid.Value; - else - component.SelectedSpell = null; + component.SelectedSpell = _action.AddAction(cultist, args.SelectedSpell); ExtractBlood(cultist, -20, 10); component.BloodMagicActive = true; @@ -166,46 +165,6 @@ public sealed partial class BloodCultSystem #endregion #region Abilities - private void OnCultCommune(BloodCultCommuneActionEvent args) - { - var uid = args.Performer; - if (!TryComp(uid, out var playerActor)) - return; - - // ะะดะผะธะฝ ะปะพะณะธะบะฐ, ะทะฐั‚ะพ ะบะฐะบ ะฟั€ะพัั‚ะพ - var playerSession = playerActor.PlayerSession; - _quickDialog.OpenDialog(playerSession, Loc.GetString("cult-commune-title"), "", - (string message) => - { - var finalMessage = string.IsNullOrWhiteSpace(message) - ? "" - : message; - - var senderName = Name(uid) ?? "Unknown"; - var popupMessage = Loc.GetString("cult-commune-massage", ("name", senderName), ("massage", finalMessage)); - - var cultistQuery = EntityQueryEnumerator(); - while (cultistQuery.MoveNext(out var cultistUid, out var actorComp, out var cultistComp)) - { - if (actorComp == playerActor) continue; - - _prayerSystem.SendSubtleMessage(actorComp.PlayerSession, actorComp.PlayerSession, string.Empty, popupMessage); - } - - var constructQuery = EntityQueryEnumerator(); - while (constructQuery.MoveNext(out var constructUid, out var actorComp, out var constructComp)) - { - if (actorComp == playerActor) continue; - - _prayerSystem.SendSubtleMessage(actorComp.PlayerSession, actorComp.PlayerSession, string.Empty, popupMessage); - } - - _admin.Add(LogType.Chat, LogImpact.Low, $"{ToPrettyString(uid):user} saying the: {finalMessage} in cult commune"); - _chat.TrySendInGameICMessage(uid, finalMessage, InGameICChatType.Whisper, ChatTransmitRange.Normal, checkRadioPrefix: false); - }); - args.Handled = true; - } - private void OnRecallDagger(EntityUid cultist, BloodCultistComponent component, RecallBloodDaggerEvent args) { if (component.RecallDaggerActionEntity is not { } dagger || !HasComp(dagger)) @@ -219,6 +178,7 @@ public sealed partial class BloodCultSystem _transform.SetWorldPosition(dagger, cultistPosition); _popup.PopupEntity(Loc.GetString("blood-cult-dagger-recalled"), cultist, cultist); _hands.TryPickupAnyHand(cultist, dagger); + _audio.PlayPvs(CultSpell, dagger); args.Handled = true; } @@ -251,12 +211,32 @@ public sealed partial class BloodCultSystem private void OnElectromagneticPulse(EntityUid cultist, BloodCultistComponent component, BloodCultElectromagneticPulseActionEvent args) { var coords = _transform.GetMapCoordinates(cultist); + var exclusions = new List(); var entitiesInRange = _entityLookup.GetEntitiesInRange(coords, 5f); foreach (var uid in entitiesInRange) { if (HasComp(uid)) + { exclusions.Add(uid); + continue; + } + + if (HasComp(Transform(uid).ParentUid)) + { + exclusions.Add(uid); + if (!TryComp(uid, out var containerManager)) + continue; + + var containers = _container.GetAllContainers(uid, containerManager) + .Where(c => c.ContainedEntities.Count > 0).ToList(); + + foreach (var container in containers) + { + foreach (var ent in container.ContainedEntities) + exclusions.Add(ent); + } + } } _emp.EmpPulseExclusions(coords, 5f, 100000f, 60f, exclusions); @@ -305,7 +285,7 @@ public sealed partial class BloodCultSystem private void OnSummonDagger(EntityUid cultist, BloodCultistComponent component, BloodCultSummonDaggerActionEvent args) { - if (_entityManager.EntityExists(component.RecallDaggerActionEntity)) + if (Exists(component.RecallDaggerActionEntity)) { _popup.PopupEntity(Loc.GetString("blood-cult-blood-dagger-exists"), cultist, cultist, PopupType.SmallCaution); args.Handled = true; @@ -313,15 +293,15 @@ public sealed partial class BloodCultSystem } var cultistCoords = Transform(cultist).Coordinates; - string selectedDagger = GetCurrentGod() switch + EntProtoId selectedDagger = GetCurrentGod() switch { - "Narsie" => "WeaponBloodDagger", - "Reaper" => "WeaponDeathDagger", - "Kharin" => "WeaponHellDagger", + BloodCultGod.NarSi => "WeaponBloodDagger", + BloodCultGod.Reaper => "WeaponDeathDagger", + BloodCultGod.Kharin => "WeaponHellDagger", _ => "WeaponBloodDagger" }; - var dagger = _entityManager.SpawnEntity(selectedDagger, cultistCoords); + var dagger = Spawn(selectedDagger, cultistCoords); component.RecallDaggerActionEntity = dagger; _hands.TryPickupAnyHand(cultist, dagger); @@ -340,7 +320,7 @@ public sealed partial class BloodCultSystem private void OnConcealPresence(EntityUid cultist, BloodCultistComponent component, BloodCultConcealPresenceActionEvent args) { - var transform = _entityManager.GetComponent(cultist); + var transform = Transform(cultist); var runes = _entityLookup.GetEntitiesInRange(transform.Coordinates, 4f); var structures = _entityLookup.GetEntitiesInRange(transform.Coordinates, 4f); @@ -348,9 +328,9 @@ public sealed partial class BloodCultSystem { foreach (var rune in runes) { - if (EntityManager.TryGetComponent(rune.Owner, out BloodRuneComponent? bloodRuneComp)) + if (TryComp(rune.Owner, out BloodRuneComponent? bloodRuneComp)) { - if (EntityManager.TryGetComponent(rune.Owner, out VisibilityComponent? visibilityComp)) + if (TryComp(rune.Owner, out VisibilityComponent? visibilityComp)) { var entity = new Entity(rune.Owner, visibilityComp); if (bloodRuneComp.IsActive) @@ -360,7 +340,7 @@ public sealed partial class BloodCultSystem } else { - var newVisibilityComp = EntityManager.AddComponent(rune.Owner); + var newVisibilityComp = AddComp(rune.Owner); var entity = new Entity(rune.Owner, newVisibilityComp); if (bloodRuneComp.IsActive) _visibility.SetLayer(entity, 6); @@ -377,9 +357,9 @@ public sealed partial class BloodCultSystem { foreach (var structure in structures) { - if (EntityManager.TryGetComponent(structure.Owner, out BloodStructureComponent? bloodStructureComp)) + if (TryComp(structure.Owner, out BloodStructureComponent? bloodStructureComp)) { - if (EntityManager.TryGetComponent(structure.Owner, out VisibilityComponent? visibilityComp)) + if (TryComp(structure.Owner, out VisibilityComponent? visibilityComp)) { var entity = new Entity(structure.Owner, visibilityComp); if (bloodStructureComp.IsActive) @@ -389,7 +369,7 @@ public sealed partial class BloodCultSystem } else { - var newVisibilityComp = EntityManager.AddComponent(structure.Owner); + var newVisibilityComp = AddComp(structure.Owner); var entity = new Entity(structure.Owner, newVisibilityComp); if (bloodStructureComp.IsActive) _visibility.SetLayer(entity, 6); @@ -397,7 +377,7 @@ public sealed partial class BloodCultSystem _visibility.SetLayer(entity, 1); } - if (EntityManager.TryGetComponent(structure.Owner, out PhysicsComponent? physicsComp)) + if (HasComp(structure.Owner)) { var fixture = _fixtures.GetFixtureOrNull(structure.Owner, bloodStructureComp.FixtureId); if (fixture != null) @@ -429,22 +409,20 @@ public sealed partial class BloodCultSystem private void BloodRites(Entity ent, ref UseInHandEvent args) { - if (!TryComp(ent, out var comp) || comp.Prototype.FirstOrDefault() != "bloodrites") + if (!HasComp(args.User) || ent.Comp.SpellType != BloodCultSpell.BloodRites) return; args.Handled = true; - _entityManager.DeleteEntity(ent); - var netEntity = _entityManager.GetNetEntity(args.User); - RaiseNetworkEvent(new BloodRitesPressedEvent(netEntity)); + _ui.OpenUi(ent.Owner, BloodRitesUiKey.Key, args.User); } - private void BloodRitesSelect(BloodRitesMenuClosedEvent args, EntitySessionEventArgs eventArgs) + private void BloodRitesSelect(Entity ent, ref BloodRitesSelectRitesMessage args) { - var uid = _entityManager.GetEntity(args.Uid); - if (!HasComp(uid)) + if (!HasComp(args.Actor) || ent.Comp.SpellType != BloodCultSpell.BloodRites) return; - _action.AddAction(uid, args.SelectedRites); + _action.AddAction(args.Actor, args.Rites); + QueueDel(ent); } private void OnBloodOrb(EntityUid cultist, BloodCultistComponent component, BloodCultBloodOrbActionEvent args) @@ -470,7 +448,7 @@ public sealed partial class BloodCultSystem { component.BloodCount -= inputValue; - var bloodOrb = _entityManager.SpawnEntity("BloodCultOrb", Transform(cultist).Coordinates); + var bloodOrb = Spawn("BloodCultOrb", Transform(cultist).Coordinates); EnsureComp(bloodOrb, out var orb); orb.Blood = inputValue; @@ -489,28 +467,66 @@ public sealed partial class BloodCultSystem || !TryComp(ent, out var component)) return; - var addedBlood = component.Blood; - cultistcomp.BloodCount += addedBlood; + cultistcomp.BloodCount += component.Blood; _popup.PopupEntity(Loc.GetString("blood-orb-absorbed"), cultist, cultist, PopupType.Small); - _entityManager.DeleteEntity(ent); + QueueDel(ent); } private void OnBloodRecharge(EntityUid cultist, BloodCultistComponent component, BloodCultBloodRechargeActionEvent args) { - var target = args.Target; - if (TryComp(target, out var veilShifterComponent)) + if (component.BloodCount < 75) { - var totalActivations = veilShifterComponent.ActivationsCount; - veilShifterComponent.ActivationsCount = Math.Min(totalActivations + 4, 4); + _popup.PopupEntity(Loc.GetString("blood-cult-recharge-failed"), cultist, cultist, PopupType.SmallCaution); + return; } - _action.RemoveAction(cultist, args.Action!); + var target = args.Target; + if (TryComp(target, out var veilShifter)) + { + var totalActivations = veilShifter.ActivationsCount; + veilShifter.ActivationsCount = Math.Min(totalActivations + 4, 4); + + _appearance.SetData(target, VeilShifterVisuals.Charged, veilShifter.ActivationsCount > 0); + + component.BloodCount -= 75; + _audio.PlayPvs(CultSpell, target); + _action.RemoveAction(cultist, args.Action!); + } + else if (TryComp(target, out var bloodShield) && !HasComp(cultist)) + { + _inventory.TryUnequip(cultist, bloodShield.CurrentSlot, force: true); + if (_inventory.TryEquip(cultist, target, bloodShield.CurrentSlot, force: true)) + { + var shield = EnsureComp(cultist); + shield.ShieldEntity = Spawn("BloodCultShieldEffect", Transform(cultist).Coordinates); + _transform.SetParent(shield.ShieldEntity.Value, cultist); + + _audio.PlayPvs(CultSpell, target); + } + + component.BloodCount -= 75; + _action.RemoveAction(cultist, args.Action!); + } + else if (TryComp(target, out var tarot) && tarot.Card == CardTarot.NotEnchanted + && component.BloodCount >= 100) + { + var allCards = Enum.GetValues(); + tarot.Card = (CardTarot)_random.Next(1, allCards.Length); + + bool reversed = _random.Prob(0.5f); + if (reversed) tarot.CardType = CardTarotType.Reversed; + + _appearance.SetData(target, CardTarotVisuals.State, tarot.Card); + _appearance.SetData(target, CardTarotVisuals.Reversed, reversed); + + component.BloodCount -= 100; + _action.RemoveAction(cultist, args.Action!); + } } private void OnBloodSpear(EntityUid cultist, BloodCultistComponent component, BloodCultBloodSpearActionEvent args) { - var totalBlood = component.BloodCount; - if (totalBlood < 150) + if (component.BloodCount < 150) { _popup.PopupEntity(Loc.GetString("blood-cult-spear-failed"), cultist, cultist, PopupType.SmallCaution); return; @@ -518,29 +534,28 @@ public sealed partial class BloodCultSystem if (component.RecallSpearActionEntity != null) { - _entityManager.DeleteEntity(component.RecallSpearActionEntity); + QueueDel(component.RecallSpearActionEntity); component.RecallSpearActionEntity = null; _action.RemoveAction(cultist, component.RecallSpearAction); component.RecallSpearAction = null; } - var spear = _entityManager.SpawnEntity("BloodCultSpear", Transform(cultist).Coordinates); + var spear = Spawn("BloodCultSpear", Transform(cultist).Coordinates); component.RecallSpearActionEntity = spear; _hands.TryPickupAnyHand(cultist, spear); var action = _action.AddAction(cultist, BloodCultistComponent.RecallBloodSpear); component.RecallSpearAction = action; - totalBlood -= 150; - component.BloodCount = totalBlood; + component.BloodCount -= 150; _action.RemoveAction(cultist, args.Action!); args.Handled = true; } private void OnRecallSpear(EntityUid cultist, BloodCultistComponent component, RecallBloodSpearEvent args) { - if (component.RecallSpearActionEntity is not { } spear || !_entityManager.EntityExists(spear)) + if (component.RecallSpearActionEntity is not { } spear || !Exists(spear)) { _popup.PopupEntity(Loc.GetString("cult-spear-not-found"), cultist, cultist); component.RecallSpearActionEntity = null; @@ -567,8 +582,7 @@ public sealed partial class BloodCultSystem private void OnBloodBoltBarrage(EntityUid cultist, BloodCultistComponent component, BloodCultBloodBoltBarrageActionEvent args) { - var totalBlood = component.BloodCount; - if (totalBlood < 300) + if (component.BloodCount < 300) { _popup.PopupEntity(Loc.GetString("blood-cult-bolt-barrage-failed"), cultist, cultist, PopupType.SmallCaution); return; @@ -580,8 +594,7 @@ public sealed partial class BloodCultSystem List> gear = new() { boltBarrageGear }; _loadout.Equip(cultist, gear, null); - totalBlood -= 300; - component.BloodCount = totalBlood; + component.BloodCount -= 300; _action.RemoveAction(cultist, args.Action!); args.Handled = true; } @@ -589,6 +602,7 @@ public sealed partial class BloodCultSystem #endregion Abilities #region Other + private void OnInteract(Entity entity, ref AfterInteractEvent args) { if (args.Handled || !args.CanReach || args.Target is not { Valid: true } target @@ -596,268 +610,25 @@ public sealed partial class BloodCultSystem return; var user = args.User; - switch (spellComp.Prototype.FirstOrDefault()) + switch (spellComp.SpellType) { - case "stun": - if (!HasComp(target) && !HasComp(target)) - { - ExtractBlood(user, -10, 6); - if (!HasComp(target)) - { - EnsureComp(target); - Timer.Spawn(10000, () => { RemComp(target); }); - } - - _stun.TryUpdateParalyzeDuration(target, TimeSpan.FromSeconds(4f)); - if (!TryComp(target, out var flash)) - _flash.Flash(target, user, entity, TimeSpan.FromSeconds(2f), 1f); - _entityManager.DeleteEntity(entity); - } + case BloodCultSpell.Stun: + HandleStunSpell(entity, user, target); break; - case "teleport": - ExtractBlood(user, -7, 5); - if (HasComp(target)) - break; - - _doAfterSystem.TryStartDoAfter(new DoAfterArgs(EntityManager, user, TimeSpan.FromSeconds(3f), new TeleportSpellDoAfterEvent(), user, target, entity) - { - BreakOnMove = true, - BreakOnDamage = true, - MovementThreshold = 0.01f, - NeedHand = true - }); + case BloodCultSpell.Teleport: + HandleTeleportSpell(entity, user, target); break; - case "shadowshackles": - if (!HasComp(target) && !HasComp(target)) - { - if (TryComp(target, out var mobstate) && mobstate.CurrentState != MobState.Alive && mobstate.CurrentState != MobState.Invalid - || HasComp(target) || TryComp(target, out var stamina) && stamina.StaminaDamage >= stamina.CritThreshold * 0.9f) - { - if (TryComp(target, out var cuffable) && cuffable.CanStillInteract) - { - var handcuffs = _entityManager.SpawnEntity("Handcuffs", Transform(target).Coordinates); - if (TryComp(handcuffs, out var handcuffsComp)) - { - if (_cuff.TryAddNewCuffs(target, user, handcuffs, cuffable, handcuffsComp)) - { - _cuff.CuffUsed(handcuffsComp); - EnsureComp(target); - Timer.Spawn(12000, () => { RemComp(target); }); - _entityManager.DeleteEntity(entity); - } - else - { - _popup.PopupEntity(Loc.GetString("blood-cult-shadow-shackles-failed"), user, user, PopupType.SmallCaution); - _entityManager.DeleteEntity(handcuffs); - } - } - } - else - { - _popup.PopupEntity(Loc.GetString("blood-cult-shadow-shackles-failed"), user, user, PopupType.SmallCaution); - } - } - } + case BloodCultSpell.ShadowShackles: + HandleShadowShacklesSpell(entity, user, target); break; - case "twistedconstruction": - if (HasComp(target)) - { - ExtractBlood(user, -12, 8); - _entityManager.DeleteEntity(entity); - - var airlockTransform = Transform(target).Coordinates; - _entityManager.DeleteEntity(target); - _entityManager.SpawnEntity("AirlockBloodCult", airlockTransform); - } - else if (TryComp(target, out var stack)) - { - if (_prototypeManager.TryIndex(stack.StackTypeId, out var stackPrototype)) - { - if (stackPrototype.ID is "Steel" || stackPrototype.ID is "Plasteel") - { - ExtractBlood(user, -12, 8); - var coords = Transform(target).Coordinates; - if (stackPrototype.ID is "Steel" && stack.Count >= 30) - { - _stack.ReduceCount(target, 30); - if (stack.Count > 0) - { - _entityManager.SpawnEntity("BloodCultConstruct", coords); - } - else - { - _entityManager.DeleteEntity(target); - _entityManager.SpawnEntity("BloodCultConstruct", coords); - } - } - if (stackPrototype.ID is "Plasteel") - { - var count = stack.Count; - var runeSteel = _entityManager.SpawnEntity("SheetRuneMetal1", coords); - _entityManager.DeleteEntity(target); - if (TryComp(runeSteel, out var newStack)) - { - _stack.SetCount((runeSteel, newStack), count); - } - } - - _entityManager.DeleteEntity(entity); - } - } - } - else - { - _popup.PopupEntity(Loc.GetString("blood-cult-twisted-failed"), user, user, PopupType.SmallCaution); - _entityManager.DeleteEntity(entity); - } + case BloodCultSpell.TwistedConstruction: + HandleTwistedConstructionSpell(entity, user, target); break; - case "summonequipment": - _entityManager.DeleteEntity(entity); - var dropEvent = new DropHandItemsEvent(); - RaiseLocalEvent(target, ref dropEvent); - ProtoId selectedGear = GetCurrentGod() switch - { - "Narsie" => new ProtoId("BloodCultWeaponBloodGear"), - "Reaper" => new ProtoId("BloodCultWeaponDeathGear"), - "Kharin" => new ProtoId("BloodCultWeaponHellGear"), - _ => new ProtoId("BloodCultWeaponBloodGear") - }; - - List> gear = new() { selectedGear }; - _loadout.Equip(target, gear, null); - if (TryComp(target, out var targetInventory)) - { - var specificSlots = new[] { "outerClothing", "jumpsuit", "back", "shoes" }; - foreach (var slot in specificSlots) - { - if (!_inventorySystem.TryGetSlotEntity(target, slot, out var slotEntity, targetInventory)) - { - switch (slot) - { - case "outerClothing": - var outerClothingGear = new ProtoId("BloodCultOuterGear"); - List> outerClothing = new() { outerClothingGear }; - _loadout.Equip(target, outerClothing, null); - break; - case "jumpsuit": - var jumpsuitGear = new ProtoId("BloodCultJumpsuitGear"); - List> jumpsuit = new() { jumpsuitGear }; - _loadout.Equip(target, jumpsuit, null); - break; - case "back": - var backGear = new ProtoId("BloodCultBackpackGear"); - List> back = new() { backGear }; - _loadout.Equip(target, back, null); - break; - case "shoes": - var shoesGear = new ProtoId("BloodCultShoesGear"); - List> shoes = new() { shoesGear }; - _loadout.Equip(target, shoes, null); - break; - default: - break; - } - } - } - _entityManager.DeleteEntity(entity); - } + case BloodCultSpell.SummonEquipment: + HandleSummonEquipmentSpell(entity, user, target); break; - case "bloodrites": - if (!TryComp(user, out var cultist)) - { - _entityManager.DeleteEntity(entity); - return; - } - - if (!TryComp(entity, out var useDelay) || _useDelay.IsDelayed((entity, useDelay))) - return; - - if (HasComp(target)) - { - if (!TryComp(target, out var damage)) - return; - - var totalBlood = cultist.BloodCount; - var prioritizedDamageTypes = new[] { "Blunt", "Piercing", "Heat", "Slash", "Caustic" }; - foreach (var damageType in prioritizedDamageTypes) - { - if (totalBlood <= 0) - break; - - if (damage.Damage.DamageDict.TryGetValue(damageType, out var currentDamage) && currentDamage > 0) - { - var healAmount = FixedPoint2.Min(currentDamage, totalBlood); - var healSpecifier = new DamageSpecifier { DamageDict = { { damageType, -healAmount } } }; - _damage.TryChangeDamage(target, healSpecifier, true); - totalBlood -= healAmount.Int(); - } - } - cultist.BloodCount = totalBlood; - args.Handled = true; - } - else if (HasComp(target) && !HasComp(target)) - { - if (!TryComp(target, out var blood) || HasComp(target)) - return; - - if (_blood.GetBloodLevel(target) > 0.6) - { - _blood.TryModifyBloodLevel(target, -50); - cultist.BloodCount += 50; - } - else - { - _popup.PopupEntity(Loc.GetString("blood-cult-blood-rites-failed"), user, user, PopupType.SmallCaution); - } - args.Handled = true; - } - else if (TryComp(target, out var puddle)) - { - var puddlesInRange = _entityLookup - .GetEntitiesInRange(Transform(user).Coordinates, 4f) - .Where(puddle => TryComp(puddle.Owner, out ContainerManagerComponent? containerManager) && - containerManager.Containers.TryGetValue("solution@puddle", out var container) && - container.ContainedEntities.Any(containedEntity => - TryComp(containedEntity, out SolutionComponent? solutionComponent) && - solutionComponent.Solution.Contents.Any(r => - r.Reagent.Prototype == "Blood" || r.Reagent.Prototype == "CopperBlood"))) - .ToList(); - - var absorbedBlood = 0; - foreach (var bloodPuddle in puddlesInRange) - { - if (TryComp(bloodPuddle.Owner, out ContainerManagerComponent? containerManager) && - containerManager.Containers.TryGetValue("solution@puddle", out var container)) - { - foreach (var containedEntity in container.ContainedEntities.ToList()) - { - if (TryComp(containedEntity, out SolutionComponent? solutionComponent)) - { - foreach (var reagent in solutionComponent.Solution.Contents.ToList()) - { - if (reagent.Reagent.Prototype == "Blood" || reagent.Reagent.Prototype == "CopperBlood") - { - absorbedBlood += reagent.Quantity.Int(); - solutionComponent.Solution.RemoveReagent(reagent.Reagent, reagent.Quantity); - } - } - - _entityManager.SpawnEntity("BloodCultFloorGlowEffect", Transform(bloodPuddle.Owner).Coordinates); - if (solutionComponent.Solution.Contents.Count == 0) - _entityManager.DeleteEntity(bloodPuddle.Owner); - } - } - } - } - cultist.BloodCount += absorbedBlood; - args.Handled = true; - } - else - { - _popup.PopupEntity(Loc.GetString("blood-cult-blood-rites-failed"), user, user, PopupType.SmallCaution); - args.Handled = true; - } - _useDelay.TryResetDelay((entity, useDelay)); + case BloodCultSpell.BloodRites: + HandleBloodRitesSpell(entity, user, target, ref args); break; default: _popup.PopupEntity(Loc.GetString("blood-cult-spell-failed"), user, user, PopupType.SmallCaution); @@ -865,9 +636,364 @@ public sealed partial class BloodCultSystem } } + #region Spell Handlers + + private void HandleStunSpell(Entity spell, EntityUid user, EntityUid target) + { + if (HasComp(target) || HasComp(target)) + return; + + ExtractBlood(user, -10, 6); + + _stun.TryKnockdown(target, TimeSpan.FromSeconds(4f)); + _statusEffect.TryAddStatusEffectDuration(target, "Muted", TimeSpan.FromSeconds(10f)); + _flash.Flash(target, user, spell, TimeSpan.FromSeconds(2f), 1f); + + QueueDel(spell); + } + + private void HandleTeleportSpell(Entity spell, EntityUid user, EntityUid target) + { + if (HasComp(target)) + return; + + ExtractBlood(user, -7, 5); + + _doAfterSystem.TryStartDoAfter(new DoAfterArgs(EntityManager, user, TimeSpan.FromSeconds(2f), + new TeleportSpellDoAfterEvent(), user, target, spell) + { + BreakOnMove = true, + BreakOnDamage = true, + MovementThreshold = 0.01f, + NeedHand = true + }); + } + + private void HandleShadowShacklesSpell(Entity spell, EntityUid user, EntityUid target) + { + if (HasComp(target) || HasComp(target)) + return; + + if (!IsValidForCuffing(target)) + { + _popup.PopupEntity(Loc.GetString("blood-cult-shadow-shackles-failed"), user, user, PopupType.SmallCaution); + return; + } + + TryApplyHandcuffs(spell, user, target); + } + + private bool IsValidForCuffing(EntityUid target) + { + if (!_mobState.IsAlive(target)) + return true; + + if (HasComp(target)) + return true; + + if (TryComp(target, out var stamina) && + stamina.StaminaDamage >= stamina.CritThreshold * 0.9f) + return true; + + return false; + } + + private void TryApplyHandcuffs(Entity spell, EntityUid user, EntityUid target) + { + if (!TryComp(target, out var cuffable) || !cuffable.CanStillInteract) + { + _popup.PopupEntity(Loc.GetString("blood-cult-shadow-shackles-failed"), user, user, PopupType.SmallCaution); + return; + } + + var handcuffs = Spawn("Handcuffs", Transform(target).Coordinates); + if (!TryComp(handcuffs, out var handcuffsComp) || + !_cuff.TryAddNewCuffs(target, user, handcuffs, cuffable, handcuffsComp)) + { + _popup.PopupEntity(Loc.GetString("blood-cult-shadow-shackles-failed"), user, user, PopupType.SmallCaution); + QueueDel(handcuffs); + return; + } + + _cuff.CuffUsed(handcuffsComp); + _statusEffect.TryAddStatusEffectDuration(target, "Muted", TimeSpan.FromSeconds(12f)); + + QueueDel(spell); + } + + private void HandleTwistedConstructionSpell(Entity spell, EntityUid user, EntityUid target) + { + if (HasComp(target)) + { + TransformAirlock(spell, user, target); + return; + } + + if (TryComp(target, out var stack)) + { + TransformMaterial(spell, user, target, stack); + return; + } + + _popup.PopupEntity(Loc.GetString("blood-cult-twisted-failed"), user, user, PopupType.SmallCaution); + } + + private void TransformAirlock(Entity spell, EntityUid user, EntityUid airlock) + { + ExtractBlood(user, -12, 8); + + string spawnProto = "AirlockBloodCult"; + if (TryComp(airlock, out var door) && !door.Occludes) + spawnProto = "AirlockBloodCultGlass"; + + var airlockTransform = Transform(airlock).Coordinates; + QueueDel(airlock); + + var ent = Spawn(spawnProto, airlockTransform); + _audio.PlayPvs(CultSpell, ent); + QueueDel(spell); + } + + private void TransformMaterial(Entity spell, EntityUid user, EntityUid material, StackComponent stack) + { + if (!_prototypeManager.TryIndex(stack.StackTypeId, out var stackPrototype)) + return; + + if (stackPrototype.ID is not ("Steel" or "Plasteel")) + return; + + ExtractBlood(user, -12, 8); + var coords = Transform(material).Coordinates; + + if (stackPrototype.ID == "Steel" && stack.Count >= 30) + { + TransformSteelToConstruct(material, coords, stack); + } + else if (stackPrototype.ID == "Plasteel") + { + TransformPlasteelToRuneMetal(material, coords, stack.Count); + } + + _audio.PlayPvs(CultSpell, user); + QueueDel(spell); + } + + private void TransformSteelToConstruct(EntityUid steelStack, EntityCoordinates coords, StackComponent stack) + { + _stack.ReduceCount(steelStack, 30); + if (stack.Count > 0) + { + Spawn("BloodCultConstruct", coords); + } + else + { + QueueDel(steelStack); + Spawn("BloodCultConstruct", coords); + } + } + + private void TransformPlasteelToRuneMetal(EntityUid plasteelStack, EntityCoordinates coords, int count) + { + var runeSteel = Spawn("SheetRuneMetal1", coords); + QueueDel(plasteelStack); + + if (TryComp(runeSteel, out var newStack)) + _stack.SetCount((runeSteel, newStack), count); + } + + private void HandleSummonEquipmentSpell(Entity spell, EntityUid user, EntityUid target) + { + QueueDel(spell); + + var dropEvent = new DropHandItemsEvent(); + RaiseLocalEvent(target, ref dropEvent); + + var selectedGear = GetGodWeaponGear(); + var gear = new List> { selectedGear }; + _loadout.Equip(target, gear, null); + + FillMissingEquipmentSlots(target); + + QueueDel(spell); + } + + private ProtoId GetGodWeaponGear() + { + return GetCurrentGod() switch + { + BloodCultGod.NarSi => new ProtoId("BloodCultWeaponBloodGear"), + BloodCultGod.Reaper => new ProtoId("BloodCultWeaponDeathGear"), + BloodCultGod.Kharin => new ProtoId("BloodCultWeaponHellGear"), + _ => new ProtoId("BloodCultWeaponBloodGear") + }; + } + + private void FillMissingEquipmentSlots(EntityUid target) + { + if (!TryComp(target, out var targetInventory)) + return; + + var slotGearMap = new Dictionary> + { + ["outerClothing"] = new ProtoId("BloodCultOuterGear"), + ["jumpsuit"] = new ProtoId("BloodCultJumpsuitGear"), + ["back"] = new ProtoId("BloodCultBackpackGear"), + ["shoes"] = new ProtoId("BloodCultShoesGear") + }; + + foreach (var (slot, gearPrototype) in slotGearMap) + { + if (!_inventory.TryGetSlotEntity(target, slot, out _, targetInventory)) + { + var gear = new List> { gearPrototype }; + _loadout.Equip(target, gear, null); + } + } + } + + private void HandleBloodRitesSpell(Entity spell, EntityUid user, EntityUid target, ref AfterInteractEvent args) + { + if (!TryComp(user, out var cultist)) + { + QueueDel(spell); + return; + } + + if (!TryComp(spell, out var useDelay) || _useDelay.IsDelayed((spell, useDelay))) + return; + + var handled = false; + if (HasComp(target)) + { + handled = HealCultist(cultist, target); + } + else if (HasComp(target) && !HasComp(target)) + { + handled = StealBloodFromHumanoid(cultist, user, target); + } + else if (TryComp(target, out _)) + { + handled = AbsorbBloodFromPuddles(cultist, user); + } + else + { + _popup.PopupEntity(Loc.GetString("blood-cult-blood-rites-failed"), user, user, PopupType.SmallCaution); + handled = true; + } + + if (handled) + { + args.Handled = true; + _useDelay.TryResetDelay((spell, useDelay)); + } + } + + private bool HealCultist(BloodCultistComponent cultist, EntityUid target) + { + if (!TryComp(target, out var damage)) + return false; + + var totalBlood = cultist.BloodCount; + var prioritizedDamageTypes = new[] { "Blunt", "Piercing", "Heat", "Slash", "Caustic" }; + + foreach (var damageType in prioritizedDamageTypes) + { + if (totalBlood <= 0) + break; + + if (damage.Damage.DamageDict.TryGetValue(damageType, out var currentDamage) && currentDamage > 0) + { + var healAmount = FixedPoint2.Min(currentDamage, totalBlood); + var healSpecifier = new DamageSpecifier { DamageDict = { { damageType, -healAmount } } }; + _damage.TryChangeDamage(target, healSpecifier, true); + totalBlood -= healAmount.Int(); + } + } + + cultist.BloodCount = totalBlood; + return true; + } + + private bool StealBloodFromHumanoid(BloodCultistComponent cultist, EntityUid user, EntityUid target) + { + if (!HasComp(target) || HasComp(target)) + return false; + + if (_blood.GetBloodLevel(target) > 0.6) + { + _blood.TryModifyBloodLevel(target, -50); + cultist.BloodCount += 50; + return true; + } + + _popup.PopupEntity(Loc.GetString("blood-cult-blood-rites-failed"), user, user, PopupType.SmallCaution); + return false; + } + + private bool AbsorbBloodFromPuddles(BloodCultistComponent cultist, EntityUid user) + { + var puddlesInRange = _entityLookup + .GetEntitiesInRange(Transform(user).Coordinates, 4f) + .Where(puddle => IsBloodPuddle(puddle.Owner)) + .ToList(); + + var absorbedBlood = 0; + foreach (var bloodPuddle in puddlesInRange) + { + absorbedBlood += ExtractBloodFromPuddle(bloodPuddle.Owner); + } + + cultist.BloodCount += absorbedBlood; + return true; + } + + private bool IsBloodPuddle(EntityUid puddle) + { + if (!TryComp(puddle, out ContainerManagerComponent? containerManager) || + !containerManager.Containers.TryGetValue("solution@puddle", out var container)) + return false; + + return container.ContainedEntities.Any(containedEntity => + TryComp(containedEntity, out SolutionComponent? solutionComponent) && + solutionComponent.Solution.Contents.Any(r => + r.Reagent.Prototype == "Blood" || r.Reagent.Prototype == "CopperBlood")); + } + + private int ExtractBloodFromPuddle(EntityUid puddle) + { + if (!TryComp(puddle, out ContainerManagerComponent? containerManager) || + !containerManager.Containers.TryGetValue("solution@puddle", out var container)) + return 0; + + var absorbedBlood = 0; + foreach (var containedEntity in container.ContainedEntities.ToList()) + { + if (!TryComp(containedEntity, out SolutionComponent? solutionComponent)) + continue; + + foreach (var reagent in solutionComponent.Solution.Contents.ToList()) + { + if (reagent.Reagent.Prototype == "Blood" || reagent.Reagent.Prototype == "CopperBlood") + { + absorbedBlood += reagent.Quantity.Int(); + solutionComponent.Solution.RemoveReagent(reagent.Reagent, reagent.Quantity); + } + } + + Spawn("BloodCultFloorGlowEffect", Transform(puddle).Coordinates); + + if (solutionComponent.Solution.Contents.Count == 0) + QueueDel(puddle); + } + + return absorbedBlood; + } + + #endregion + private void ExtractBlood(EntityUid cultist, int extractBlood, FixedPoint2 bloodDamage) { - if (TryComp(cultist, out var blood) && _blood.GetBloodLevel(cultist) > 0) + if (HasComp(cultist) && _blood.GetBloodLevel(cultist) > 0) _blood.TryModifyBloodLevel(cultist, extractBlood); else { @@ -881,26 +1007,24 @@ public sealed partial class BloodCultSystem if (args.Cancelled || args.Target == null || args.Used == null) return; - _entityManager.DeleteEntity(args.Used); + QueueDel(args.Used); var runes = new List(); var runeQuery = EntityQueryEnumerator(); while (runeQuery.MoveNext(out var runeUid, out var runeComp)) { - if (runeComp.Prototype == "teleport") + if (runeComp.RuneType == BloodCultRune.Teleport) runes.Add(runeUid); } if (runes.Count > 0) { var randomRune = runes[_random.Next(runes.Count)]; - var runeTransform = _entityManager.GetComponent(randomRune); - var targetCoords = Transform(args.Target.Value).Coordinates; - _entityManager.SpawnEntity("BloodCultOutEffect", targetCoords); - _transform.SetCoordinates(args.Target.Value, runeTransform.Coordinates); - _entityManager.SpawnEntity("BloodCultInEffect", runeTransform.Coordinates); - _entityManager.DeleteEntity(randomRune); + Spawn("BloodCultOutEffect", Transform(args.Target.Value).Coordinates); + _transform.SetCoordinates(args.Target.Value, Transform(randomRune).Coordinates); + Spawn("BloodCultInEffect", Transform(randomRune).Coordinates); + QueueDel(randomRune); } } @@ -908,9 +1032,7 @@ public sealed partial class BloodCultSystem { if (component.SelectedEmpoweringSpells.Contains(spell)) { - component.Empowering--; component.SelectedEmpoweringSpells.Remove(spell); - _action.RemoveAction(spell); } } diff --git a/Content.Server/_Wega/BloodCult/BloodCultSystem.Runes.cs b/Content.Server/_Wega/BloodCult/BloodCultSystem.Runes.cs new file mode 100644 index 0000000000..8e1094a7d9 --- /dev/null +++ b/Content.Server/_Wega/BloodCult/BloodCultSystem.Runes.cs @@ -0,0 +1,1002 @@ +using System.Linq; +using System.Threading.Tasks; +using Content.Server.Atmos.EntitySystems; +using Content.Server.Bible.Components; +using Content.Server.Ghost.Roles.Components; +using Content.Server.Pinpointer; +using Content.Server.Station.Components; +using Content.Shared.Administration.Systems; +using Content.Shared.Atmos.Components; +using Content.Shared.Blood.Cult; +using Content.Shared.Blood.Cult.Components; +using Content.Shared.Body.Components; +using Content.Shared.Chat; +using Content.Shared.Chemistry.Reagent; +using Content.Shared.Damage; +using Content.Shared.DoAfter; +using Content.Shared.Examine; +using Content.Shared.Ghost; +using Content.Shared.Humanoid; +using Content.Shared.Interaction; +using Content.Shared.Interaction.Events; +using Content.Shared.Mind; +using Content.Shared.Mind.Components; +using Content.Shared.Mindshield.Components; +using Content.Shared.NullRod.Components; +using Content.Shared.Popups; +using Content.Shared.Silicons.Borgs.Components; +using Content.Shared.Standing; +using Content.Shared.Surgery.Components; +using Content.Shared.Timing; +using Robust.Shared.Audio; +using Robust.Shared.Console; +using Robust.Shared.Map; +using Robust.Shared.Map.Components; +using Robust.Shared.Physics.Components; +using Robust.Shared.Player; +using Robust.Shared.Prototypes; +using Robust.Shared.Timing; +using Robust.Shared.Utility; + +namespace Content.Server.Blood.Cult; + +public sealed partial class BloodCultSystem +{ + [Dependency] private readonly FlammableSystem _flammable = default!; + [Dependency] private readonly IConsoleHost _consoleHost = default!; + [Dependency] private readonly IMapManager _mapMan = default!; + [Dependency] private readonly NavMapSystem _navMap = default!; + [Dependency] private readonly RejuvenateSystem _rejuvenate = default!; + [Dependency] private readonly SharedGhostSystem _ghost = default!; + + private static readonly EntProtoId BloodCultObserver = "MobObserverIfrit"; + + private void InitializeRunes() + { + base.Initialize(); + + SubscribeLocalEvent(AfterRuneSelect); + SubscribeLocalEvent(DoAfterRuneSelect); + SubscribeLocalEvent(OnDaggerInteract); + SubscribeLocalEvent(OnRuneInteract); + SubscribeLocalEvent(OnRuneExamined); + SubscribeLocalEvent(OnRitualInteract); + + SubscribeLocalEvent(OnEmpoweringSelected); + SubscribeLocalEvent(OnEmpoweringDoAfter); + SubscribeLocalEvent(OnSummoningSelected); + + SubscribeLocalEvent(DoAfterInteractRune); + SubscribeLocalEvent(DoAfterInteractRune); + } + + #region Runes + + private void AfterRuneSelect(Entity rune, ref SelectBloodRuneMessage args) + { + if (!HasComp(args.Actor) || IsInSpace(args.Actor)) + return; + + var selectedRune = args.RuneProtoId; + if (!ValidateRuneSelection(args.Actor, selectedRune, out _)) + return; + + var effectRune = SpawnRuneEffect(args.Actor, selectedRune); + + StartRuneCreationDoAfter(args.Actor, selectedRune, effectRune, + selectedRune == "BloodRuneRitualDimensionalRending" ? 9.75f : 4f); + } + + private bool ValidateRuneSelection(EntityUid cultist, string selectedRune, out bool isValidSurface) + { + isValidSurface = true; + var cult = _bloodCult.GetActiveRule(); + if (cult != null && selectedRune == "BloodRuneRitualDimensionalRending" && !cult.RitualStage) + { + _popup.PopupEntity(Loc.GetString("rune-ritual-failed"), cultist, cultist, PopupType.MediumCaution); + return false; + } + + if (cult != null && selectedRune == "BloodRuneRitualDimensionalRending" && cult.RitualStage) + { + var xform = Transform(cultist); + if (!HasComp(xform.GridUid) || !HasComp(xform.GridUid)) + { + _popup.PopupEntity(Loc.GetString("rune-ritual-failed"), cultist, cultist, PopupType.MediumCaution); + return false; + } + + var cultistPosition = _transform.GetMapCoordinates(Transform(cultist)); + isValidSurface = _mapMan.TryFindGridAt(cultistPosition, out _, out _); + + var ritual = EntityQuery().FirstOrDefault(); + if (!isValidSurface || ritual != default) + { + _popup.PopupEntity(Loc.GetString("rune-ritual-failed"), cultist, cultist, PopupType.MediumCaution); + return false; + } + } + + return true; + } + + private EntityUid SpawnRuneEffect(EntityUid cultist, string runeProto) + { + var rune = Spawn(runeProto + "Effect", Transform(cultist).Coordinates); + _appearance.SetData(rune, RuneColorVisuals.Color, TryFindColor(cultist)); + return rune; + } + + private void StartRuneCreationDoAfter(EntityUid cultist, string selectedRune, EntityUid effectRune, float duration) + { + _doAfterSystem.TryStartDoAfter(new DoAfterArgs(EntityManager, cultist, TimeSpan.FromSeconds(duration), + new BloodRuneDoAfterEvent(selectedRune, GetNetEntity(effectRune)), cultist) + { + BreakOnMove = true, + BreakOnDamage = true, + MovementThreshold = 0.01f, + NeedHand = false + }); + } + + private void DoAfterRuneSelect(EntityUid cultist, BloodCultistComponent component, BloodRuneDoAfterEvent args) + { + if (args.Cancelled) + { + QueueDel(GetEntity(args.Rune)); + return; + } + + var rune = SpawnFinalRune(cultist, args.SelectedRune); + if (args.SelectedRune == "BloodRuneRitualDimensionalRending") + AnnounceRitualRune(rune); + + ExtractBloodCost(cultist, 5); + _popup.PopupEntity(Loc.GetString("rune-select-complete"), cultist, cultist, PopupType.SmallCaution); + args.Handled = true; + } + + private EntityUid SpawnFinalRune(EntityUid cultist, string runeProto) + { + var rune = Spawn(runeProto, Transform(cultist).Coordinates); + _appearance.SetData(rune, RuneColorVisuals.Color, TryFindColor(cultist)); + return rune; + } + + private void AnnounceRitualRune(EntityUid rune) + { + var xform = Transform(rune); + var msg = Loc.GetString("blood-ritual-warning", + ("location", FormattedMessage.RemoveMarkupOrThrow(_navMap.GetNearestBeaconString((rune, xform))))); + _chat.DispatchGlobalAnnouncement(msg, colorOverride: Color.Red); + } + + private void OnRuneInteract(EntityUid rune, BloodRuneComponent component, InteractHandEvent args) + { + if (args.Handled || !HasComp(args.User)) + return; + + if (rune is not { Valid: true } target) + return; + + OnRuneAfterInteract(target, component, args.User); + args.Handled = true; + } + + private void OnRuneExamined(EntityUid uid, BloodRuneComponent component, ExaminedEvent args) + { + if (!args.IsInDetailsRange || !HasComp(args.Examiner)) + return; + + args.PushMarkup(component.LocDesc); + } + + private void OnRitualInteract(EntityUid rune, BloodRitualDimensionalRendingComponent component, InteractHandEvent args) + { + if (args.Handled || !HasComp(args.User)) + return; + + if (!ValidateRitualActivation(rune, component, args.User)) + return; + + ActivateRitual(rune, component, args.User); + args.Handled = true; + } + + private bool ValidateRitualActivation(EntityUid rune, BloodRitualDimensionalRendingComponent component, EntityUid user) + { + if (component.Activate) + return false; + + var currentTime = _gameTiming.CurTime; + if (currentTime < component.ActivateTime + TimeSpan.FromSeconds(120)) + { + var remainingTime = component.ActivateTime + TimeSpan.FromSeconds(120) - currentTime; + _popup.PopupEntity(Loc.GetString("ritual-activate-too-soon", + ("time", remainingTime.TotalSeconds)), user, user, PopupType.LargeCaution); + return false; + } + + if (rune is not { Valid: true } target || !CheckRitual(_transform.GetMapCoordinates(target), 9)) + { + _popup.PopupEntity(Loc.GetString("ritual-activate-failed"), user, user, PopupType.LargeCaution); + return false; + } + + return true; + } + + private void ActivateRitual(EntityUid rune, BloodRitualDimensionalRendingComponent component, EntityUid user) + { + component.ActivateTime = _gameTiming.CurTime; + component.Activate = true; + + OnRitualAfterInteract(rune, component); + + var cultistEntities = _entityLookup.GetEntitiesInRange( + _transform.GetMapCoordinates(rune), 6f); + + foreach (var cultistEntity in cultistEntities) + { + SendCultistMessage(cultistEntity.Owner, BloodCultRune.Ritual); + } + } + + private void OnRuneAfterInteract(EntityUid rune, BloodRuneComponent runeComp, EntityUid cultist) + { + var coords = _transform.GetMapCoordinates(rune); + if (!TryComp(rune, out var useDelay) || _useDelay.IsDelayed((rune, useDelay))) + { + _popup.PopupEntity(Loc.GetString("rune-activate-failed"), cultist, cultist, PopupType.MediumCaution); + return; + } + + HandleRuneActivation(rune, runeComp, cultist, coords); + + if (Exists(rune)) _useDelay.TryResetDelay((rune, useDelay)); + } + + private void HandleRuneActivation(EntityUid rune, BloodRuneComponent runeComp, EntityUid cultist, MapCoordinates coords) + { + switch (runeComp.RuneType) + { + case BloodCultRune.Offering: + HandleOfferingRune(rune, runeComp, cultist, coords); + break; + case BloodCultRune.Teleport: + HandleTeleportRune(rune, runeComp, cultist, coords); + break; + case BloodCultRune.Empowering: + HandleEmpoweringRune(rune, runeComp, cultist, coords); + break; + case BloodCultRune.Revive: + HandleReviveRune(rune, runeComp, cultist, coords); + break; + case BloodCultRune.Barrier: + HandleBarrierRune(rune, runeComp, cultist, coords); + break; + case BloodCultRune.Summoning: + HandleSummoningRune(rune, runeComp, cultist, coords); + break; + case BloodCultRune.Bloodboil: + HandleBloodboilRune(rune, runeComp, cultist, coords); + break; + case BloodCultRune.Spiritrealm: + HandleSpiritrealmRune(rune, runeComp, cultist, coords); + break; + default: + _popup.PopupEntity(Loc.GetString("rune-activate-failed"), cultist, cultist, PopupType.MediumCaution); + break; + } + } + + #region Rune Type Handlers + + private void HandleOfferingRune(EntityUid rune, BloodRuneComponent runeComp, EntityUid cultist, MapCoordinates coords) + { + var targets = _entityLookup.GetEntitiesInRange(coords, 1f); + foreach (var targetEntity in targets) + { + var target = targetEntity.Owner; + if (HasComp(target) || HasComp(target) || + HasComp(target)) + continue; + + if (_mobState.IsDead(target) && IsSpecialTarget(target) && !HasComp(target)) + { + if (CheckRuneActivate(coords, 3)) + HandleSpecialSacrifice(target, cultist, coords, runeComp); + else + ShowActivationFailed(cultist); + break; + } + else if (!_mobState.IsDead(target) && IsConvertibleTarget(target)) + { + if (CheckRuneActivate(coords, 2)) + ConvertToCultist(target, cultist, coords, runeComp); + else + ShowActivationFailed(cultist); + break; + } + else if (_mobState.IsDead(target) && IsRegularTarget(target)) + { + if (CheckRuneActivate(coords, 1)) + HandleRegularSacrifice(target, cultist, coords, runeComp); + else + ShowActivationFailed(cultist); + break; + } + else + { + ShowActivationFailed(cultist); + } + } + } + + private bool IsSpecialTarget(EntityUid target) + { + return HasComp(target) + || HasComp(target) + || HasComp(target); + } + + private bool IsConvertibleTarget(EntityUid target) + { + return !HasComp(target) + && !HasComp(target) + && !HasComp(target); + } + + private bool IsRegularTarget(EntityUid target) + { + return !HasComp(target) + && !HasComp(target) + && !HasComp(target); + } + + private void HandleSpecialSacrifice(EntityUid target, EntityUid cultist, MapCoordinates coords, BloodRuneComponent runeComp) + { + SendRuneMessageToCultists(coords, 2f, runeComp.RuneType); + + var cult = _bloodCult.GetActiveRule(); + if (cult == null) + return; + + cult.Offerings++; + CreateSoulStone(target); + if (HasComp(target)) + { + cult.SelectedTargets.Remove(target); + RemComp(target); + } + + _body.GibBody(target); + } + + private void ConvertToCultist(EntityUid target, EntityUid cultist, MapCoordinates coords, BloodRuneComponent runeComp) + { + SendRuneMessageToCultists(coords, 2f, runeComp.RuneType); + _rejuvenate.PerformRejuvenate(target); + EnsureComp(target); + } + + private void HandleRegularSacrifice(EntityUid target, EntityUid cultist, MapCoordinates coords, BloodRuneComponent runeComp) + { + SendRuneMessageToCultists(coords, 2f, runeComp.RuneType); + + CreateSoulStone(target); + _body.GibBody(target); + + var cult = _bloodCult.GetActiveRule(); + if (cult != null) cult.Offerings++; + } + + private void CreateSoulStone(EntityUid target) + { + var soulStone = Spawn("BloodCultSoulStone", Transform(target).Coordinates); + if (TryComp(target, out var mindContainer) && mindContainer.Mind != null) + _mind.TransferTo(mindContainer.Mind.Value, soulStone); + } + + private void HandleTeleportRune(EntityUid rune, BloodRuneComponent runeComp, EntityUid cultist, MapCoordinates coords) + { + var teleportRunes = FindTeleportRunes(rune); + + if (teleportRunes.Any() && CheckRuneActivate(coords, 1)) + { + TeleportToRandomRune(cultist, teleportRunes); + SendCultistMessage(cultist, runeComp.RuneType); + } + else + { + ShowActivationFailed(cultist); + } + } + + private List FindTeleportRunes(EntityUid excludeRune) + { + var runes = new List(); + var runeQuery = EntityQueryEnumerator(); + + while (runeQuery.MoveNext(out var runeUid, out var runeCompQ)) + { + if (runeCompQ.RuneType == BloodCultRune.Teleport && runeUid != excludeRune) + runes.Add(runeUid); + } + + return runes; + } + + private void TeleportToRandomRune(EntityUid cultist, List teleportRunes) + { + var randomRuneEntity = teleportRunes[_random.Next(teleportRunes.Count)]; + var runeCoords = Transform(randomRuneEntity).Coordinates; + + Spawn("BloodCultOutEffect", Transform(cultist).Coordinates); + _transform.SetCoordinates(cultist, runeCoords); + Spawn("BloodCultInEffect", runeCoords); + QueueDel(randomRuneEntity); + } + + private void HandleEmpoweringRune(EntityUid rune, BloodRuneComponent runeComp, EntityUid cultist, MapCoordinates coords) + { + if (!CheckRuneActivate(coords, 1)) + return; + + if (TryComp(cultist, out var comp) && comp.SelectedEmpoweringSpells.Count < 4) + { + SendCultistMessage(cultist, runeComp.RuneType); + OpenEmpoweringMenu(rune, cultist); + } + else + { + ShowActivationFailed(cultist); + } + } + + private void OpenEmpoweringMenu(EntityUid rune, EntityUid cultist) + { + _ui.OpenUi(rune, EmpoweringRuneUiKey.Key, cultist); + } + + private void HandleReviveRune(EntityUid rune, BloodRuneComponent runeComp, EntityUid cultist, MapCoordinates coords) + { + if (!CheckRuneActivate(coords, 1)) + { + ShowActivationFailed(cultist); + return; + } + + var reviveTargets = _entityLookup.GetEntitiesInRange(coords, 1f); + + foreach (var targetEntity in reviveTargets) + { + var target = targetEntity.Owner; + if (target == cultist) + continue; + + if (TryReviveDeadCultist(target, cultist, runeComp)) + break; + + if (TryCreateGhostRoleForCultist(target, cultist, runeComp)) + break; + + if (TrySacrificeNonHumanoidBody(target, cultist, runeComp)) + break; + + ShowActivationFailed(cultist); + } + } + + private bool TryReviveDeadCultist(EntityUid target, EntityUid cultist, BloodRuneComponent runeComp) + { + if (!HasComp(target) || !HasComp(target) || + _mobState.IsDead(target)) + return false; + + var cult = _bloodCult.GetActiveRule(); + if (cult == null || cult.Offerings < 3) + { + ShowActivationFailed(cultist); + return true; + } + + SendCultistMessage(cultist, runeComp.RuneType); + _rejuvenate.PerformRejuvenate(target); + cult.Offerings -= 3; + + if (TryComp(target, out var mind) && mind.Mind is null && + !HasComp(target)) + { + CreateGhostRole(target); + } + + return true; + } + + private bool TryCreateGhostRoleForCultist(EntityUid target, EntityUid cultist, BloodRuneComponent runeComp) + { + if (!HasComp(target) || !HasComp(target)) + return false; + + if (!TryComp(target, out var mind) || mind.Mind is not null || + HasComp(target)) + return false; + + SendCultistMessage(cultist, runeComp.RuneType); + CreateGhostRole(target); + return true; + } + + private bool TrySacrificeNonHumanoidBody(EntityUid target, EntityUid cultist, BloodRuneComponent runeComp) + { + if (!HasComp(target) || HasComp(target) || + !_mobState.IsDead(target) || HasComp(target) || + HasComp(target) || HasComp(target)) + return false; + + SendCultistMessage(cultist, runeComp.RuneType); + _body.GibBody(target); + + var cult = _bloodCult.GetActiveRule(); + if (cult != null) cult.Offerings++; + + return true; + } + + private void CreateGhostRole(EntityUid target) + { + var formattedCommand = string.Format( + "makeghostrole {0} {1} {2} {3}", + target, + Loc.GetString("ghost-role-information-cultist"), + Loc.GetString("ghost-role-information-cultist-desc"), + Loc.GetString("ghost-role-information-cultist-rules")); + _consoleHost.ExecuteCommand(formattedCommand); + } + + private void HandleBarrierRune(EntityUid rune, BloodRuneComponent runeComp, EntityUid cultist, MapCoordinates coords) + { + if (!CheckRuneActivate(coords, 1)) + { + ShowActivationFailed(cultist); + return; + } + + if (!runeComp.BarrierActive) + { + ActivateBarrier(rune, runeComp, cultist, coords); + } + else + { + DeactivateBarrier(rune, runeComp, cultist); + } + } + + private void ActivateBarrier(EntityUid rune, BloodRuneComponent runeComp, EntityUid cultist, MapCoordinates coords) + { + runeComp.BarrierActive = true; + SendCultistMessage(cultist, runeComp.RuneType); + + ActivateNearbyBarrierRunes(coords, rune); + SetBarrierPhysics(rune, true); + + ApplyBarrierDamage(cultist); + } + + private void ActivateNearbyBarrierRunes(MapCoordinates coords, EntityUid excludeRune) + { + var nearbyRunes = _entityLookup.GetEntitiesInRange(coords, 1f) + .Where(r => TryComp(r, out BloodRuneComponent? nearbyRuneComp) && + nearbyRuneComp.RuneType == BloodCultRune.Barrier && r.Owner != excludeRune) + .ToList(); + + if (!nearbyRunes.Any()) + return; + + var randomRune = nearbyRunes[new Random().Next(nearbyRunes.Count)]; + if (TryComp(randomRune, out var randomRuneComp) && !randomRuneComp.BarrierActive) + { + randomRuneComp.BarrierActive = true; + SetBarrierPhysics(randomRune, true); + } + } + + private void SetBarrierPhysics(EntityUid rune, bool active) + { + if (!TryComp(rune, out PhysicsComponent? physicsComp)) + return; + + var fixture = _fixtures.GetFixtureOrNull(rune, "barrier"); + if (fixture != null) + { + _physics.SetHard(rune, fixture, active); + } + } + + private void ApplyBarrierDamage(EntityUid cultist) + { + var barrierRunes = EntityQuery() + .Count(r => r.RuneType == BloodCultRune.Barrier); + + var damageFormula = 2 * barrierRunes; + var damage = new DamageSpecifier { DamageDict = { { "Slash", damageFormula } } }; + _damage.TryChangeDamage(cultist, damage, true); + } + + private void DeactivateBarrier(EntityUid rune, BloodRuneComponent runeComp, EntityUid cultist) + { + runeComp.BarrierActive = false; + SendCultistMessage(cultist, runeComp.RuneType); + SetBarrierPhysics(rune, false); + } + + private void HandleSummoningRune(EntityUid rune, BloodRuneComponent runeComp, EntityUid cultist, MapCoordinates coords) + { + if (CheckRuneActivate(coords, 2)) + { + SendRuneMessageToCultists(coords, 2f, runeComp.RuneType); + OpenSummoningMenu(rune, cultist); + } + else + { + ShowActivationFailed(cultist); + } + } + + private void SendRuneMessageToCultists(MapCoordinates coords, float range, BloodCultRune runeType) + { + var cultistEntities = _entityLookup.GetEntitiesInRange(coords, range); + foreach (var cultistEntity in cultistEntities) + { + SendCultistMessage(cultistEntity.Owner, runeType); + } + } + + private void OpenSummoningMenu(EntityUid rune, EntityUid cultist) + { + _ui.OpenUi(rune, SummoningRuneUiKey.Key, cultist); + } + + private void HandleBloodboilRune(EntityUid rune, BloodRuneComponent runeComp, EntityUid cultist, MapCoordinates coords) + { + if (CheckRuneActivate(coords, 2)) + { + RemComp(rune); + SendRuneMessageToCultists(coords, 2f, runeComp.RuneType); + StartBloodboilEffect(rune, coords, cultist); + } + else + { + ShowActivationFailed(cultist); + } + } + + private void StartBloodboilEffect(EntityUid rune, MapCoordinates coords, EntityUid cultist) + { + Task.Run(async () => + { + var damageValues = new[] { 5, 10, 10 }; + for (int i = 0; i < 3; i++) + { + ApplyBloodboilDamage(coords, cultist, damageValues[i]); + + await Task.Delay(5000); + } + + QueueDel(rune); + }); + } + + private void ApplyBloodboilDamage(MapCoordinates coords, EntityUid cultist, int damageValue) + { + var targetsFlammable = _entityLookup.GetEntitiesInRange(coords, 10f) + .Where(flammableEntity => !HasComp(flammableEntity.Owner) + && HasComp(flammableEntity.Owner)) + .ToList(); + + foreach (var targetFlammable in targetsFlammable) + { + if (HasComp(targetFlammable.Owner)) + continue; + + targetFlammable.Comp.FireStacks = 3f; + _flammable.Ignite(targetFlammable.Owner, targetFlammable.Owner); + + var damage = new DamageSpecifier { DamageDict = { { "Heat", damageValue } } }; + _damage.TryChangeDamage(cultist, damage, false); + } + } + + private void HandleSpiritrealmRune(EntityUid rune, BloodRuneComponent runeComp, EntityUid cultist, MapCoordinates coords) + { + if (!CheckRuneActivate(coords, 1)) + return; + + SendCultistMessage(cultist, runeComp.RuneType); + + if (!TryComp(cultist, out var mindContainer) || mindContainer.Mind == null) + { + ShowActivationFailed(cultist); + return; + } + + EnterSpiritRealm(cultist, coords, mindContainer.Mind.Value); + } + + private void EnterSpiritRealm(EntityUid cultist, MapCoordinates coords, EntityUid mindId) + { + if (!_mind.TryGetMind(cultist, out _, out var mind)) + return; + + CleanupExistingGhost(mindId, mind); + + var canReturn = mind.CurrentEntity != null && !HasComp(mind.CurrentEntity); + var ghost = Spawn(BloodCultObserver, coords); + + _transform.AttachToGridOrMap(ghost, Transform(ghost)); + + if (canReturn) + { + if (!string.IsNullOrWhiteSpace(mind.CharacterName)) + _meta.SetEntityName(ghost, mind.CharacterName); + + _mind.Visit(mindId, ghost, mind); + } + else + { + _meta.SetEntityName(ghost, Name(cultist)); + _mind.TransferTo(mindId, ghost, mind: mind); + } + + if (TryComp(ghost, out var ghostComp)) + _action.RemoveAction(ghost, ghostComp.ToggleGhostBarActionEntity); + _ghost.SetCanReturnToBody((ghost, ghostComp), canReturn); + } + + private void CleanupExistingGhost(EntityUid mindId, MindComponent mind) + { + if (mind.VisitingEntity != default && + TryComp(mind.VisitingEntity, out var oldGhostComponent)) + { + _mind.UnVisit(mindId, mind); + if (oldGhostComponent.CanGhostInteract) + return; + } + } + + private void ShowActivationFailed(EntityUid cultist) + { + _popup.PopupEntity(Loc.GetString("rune-activate-failed"), cultist, cultist, PopupType.MediumCaution); + } + + #endregion + + private void OnRitualAfterInteract(EntityUid rune, BloodRitualDimensionalRendingComponent runeComp) + { + AnnounceRitualActivation(rune); + + _audio.PlayGlobal(new SoundPathSpecifier("/Audio/_Wega/Ambience/Antag/bloodcult_scribe.ogg"), + Filter.Broadcast(), true); + + Timer.Spawn(TimeSpan.FromSeconds(90), () => CompleteRitual(rune, runeComp)); + } + + private void AnnounceRitualActivation(EntityUid rune) + { + var xform = Transform(rune); + var msg = Loc.GetString("blood-ritual-activate-warning", + ("location", FormattedMessage.RemoveMarkupOrThrow(_navMap.GetNearestBeaconString((rune, xform))))); + _chat.DispatchGlobalAnnouncement(msg, playSound: false, colorOverride: Color.Red); + } + + private void CompleteRitual(EntityUid rune, BloodRitualDimensionalRendingComponent runeComp) + { + if (!runeComp.Activate) + { + NotifyRitualFailed(); + return; + } + + SpawnGodAndTransformCultists(rune); + } + + private void NotifyRitualFailed() + { + var cultists = EntityQueryEnumerator(); + while (cultists.MoveNext(out var cultist, out _)) + { + _popup.PopupEntity(Loc.GetString("ritual-failed"), cultist, cultist, PopupType.LargeCaution); + } + } + + private void SpawnGodAndTransformCultists(EntityUid rune) + { + var coords = Transform(rune).Coordinates; + + QueueDel(rune); + Spawn("BloodCultDistortedEffect", coords); + + Spawn(GetGodPrototype(), coords); + + RaiseLocalEvent(new GodCalledEvent()); + TransformNearbyCultists(coords); + } + + private EntProtoId GetGodPrototype() + { + return GetCurrentGod() switch + { + BloodCultGod.NarSi => "MobNarsieSpawn", + BloodCultGod.Reaper => "MobReaperSpawn", + BloodCultGod.Kharin => "MobKharinSpawn", + _ => "MobNarsieSpawn" + }; + } + + private void TransformNearbyCultists(EntityCoordinates coords) + { + var nearbyCultists = _entityLookup.GetEntitiesInRange(coords, 6f).ToList(); + + foreach (var target in nearbyCultists) + { + var harvester = Spawn("MobConstructHarvester", Transform(target).Coordinates); + if (TryComp(target, out var mindContainer) && mindContainer.Mind != null) + _mind.TransferTo(mindContainer.Mind.Value, harvester); + + _body.GibBody(target); + } + } + + #endregion + + private void OnEmpoweringSelected(Entity rune, ref EmpoweringRuneSelectSpellMessage args) + { + if (!HasComp(args.Actor)) + return; + + _doAfterSystem.TryStartDoAfter(new DoAfterArgs(EntityManager, args.Actor, TimeSpan.FromSeconds(4f), new EmpoweringDoAfterEvent(args.Spell), args.Actor) + { + BreakOnMove = true, + BreakOnDamage = true, + MovementThreshold = 0.01f, + NeedHand = true + }); + } + + private void OnEmpoweringDoAfter(EntityUid cultist, BloodCultistComponent component, EmpoweringDoAfterEvent args) + { + if (args.Cancelled) return; + + var actionEntityUid = _action.AddAction(cultist, args.SelectedSpell); + component.SelectedEmpoweringSpells.Add(actionEntityUid); + + ExtractBloodCost(cultist, 5); + } + + private void OnSummoningSelected(Entity rune, ref SummoningRuneSelectCultistMessage args) + { + var target = GetEntity(args.CultistUid); + Spawn("BloodCultOutEffect", Transform(target).Coordinates); + _transform.SetCoordinates(target, Transform(rune).Coordinates); + Spawn("BloodCultInEffect", Transform(rune).Coordinates); + + QueueDel(rune); + } + + private void ExtractBloodCost(EntityUid cultist, int amount) + { + if (HasComp(cultist) && _blood.GetBloodLevel(cultist) > 0) + _blood.TryModifyBloodLevel(cultist, -amount); + else + { + var damage = new DamageSpecifier { DamageDict = { { "Slash", amount } } }; + _damage.TryChangeDamage(cultist, damage, true); + } + } + + private bool CheckRuneActivate(MapCoordinates coords, int needCount) + { + var constructsCount = _entityLookup.GetEntitiesInRange(coords, 2f).Count(); + var aliveCultistsCount = _entityLookup.GetEntitiesInRange(coords, 2f) + .Count(cultist => !_mobState.IsDead(cultist)); + return aliveCultistsCount + constructsCount >= needCount; + } + + private bool CheckRitual(MapCoordinates coords, int needCount) + { + var aliveCultistsCount = _entityLookup.GetEntitiesInRange(coords, 6f) + .Count(cultist => !_mobState.IsDead(cultist)); + return aliveCultistsCount >= needCount; + } + + private void SendCultistMessage(EntityUid cultist, BloodCultRune type) + { + string message = type switch + { + BloodCultRune.Offering => Loc.GetString("blood-cultist-offering-message"), + BloodCultRune.Teleport => Loc.GetString("blood-cultist-teleport-message"), + BloodCultRune.Empowering => Loc.GetString("blood-cultist-empowering-message"), + BloodCultRune.Revive => Loc.GetString("blood-cultist-revive-message"), + BloodCultRune.Barrier => Loc.GetString("blood-cultist-barrier-message"), + BloodCultRune.Summoning => Loc.GetString("blood-cultist-summoning-message"), + BloodCultRune.Bloodboil => Loc.GetString("blood-cultist-bloodboil-message"), + BloodCultRune.Spiritrealm => Loc.GetString("blood-cultist-spiritrealm-message"), + BloodCultRune.Ritual => Loc.GetString("blood-cultist-ritual-message"), + _ => Loc.GetString("blood-cultist-default-message") + }; + + _chat.TrySendInGameICMessage(cultist, message, InGameICChatType.Whisper, ChatTransmitRange.Normal, checkRadioPrefix: false); + } + + private void OnDaggerInteract(Entity ent, ref UseInHandEvent args) + { + var user = args.User; + if (!HasComp(user)) + { + var dropEvent = new DropHandItemsEvent(); + RaiseLocalEvent(user, ref dropEvent); + var damage = new DamageSpecifier { DamageDict = { { "Slash", 5 } } }; + _damage.TryChangeDamage(user, damage, true); + _popup.PopupEntity(Loc.GetString("blood-dagger-failed-interact"), user, user, PopupType.SmallCaution); + return; + } + + _ui.OpenUi(ent.Owner, BloodRunesUiKey.Key, user); + + var cult = _bloodCult.GetActiveRule(); + if (cult != null && cult.RitualStage) + { + var state = new BloodRitualBoundUserInterfaceState(); + _ui.SetUiState(ent.Owner, BloodRunesUiKey.Key, state); + } + + args.Handled = true; + } + + private bool IsInSpace(EntityUid cultist) + { + var cultistPosition = _transform.GetMapCoordinates(Transform(cultist)); + if (!_mapMan.TryFindGridAt(cultistPosition, out _, out _)) + return true; + + return false; + } + + private Color TryFindColor(EntityUid cultist) + { + if (!TryComp(cultist, out var bloodStreamComponent)) + return Color.FromHex("#880000"); + + string? bloodReagentPrototypeId = null; + if (bloodStreamComponent.BloodReferenceSolution.Contents.Count > 0) + { + var reagentQuantity = bloodStreamComponent.BloodReferenceSolution.Contents[0]; + bloodReagentPrototypeId = reagentQuantity.Reagent.Prototype; + } + + if (bloodReagentPrototypeId == null) + return Color.FromHex("#880000"); + + if (!_prototypeManager.TryIndex(bloodReagentPrototypeId, out ReagentPrototype? reagentPrototype)) + return Color.FromHex("#880000"); + + return reagentPrototype.SubstanceColor; + } + + private void DoAfterInteractRune(BloodRuneCleaningDoAfterEvent args) + { + if (args.Cancelled) + return; + + QueueDel(args.Target); + } + + private void DoAfterInteractRune(EntityUid cultist, BloodCultistComponent component, BloodRuneCleaningDoAfterEvent args) + { + if (args.Cancelled) + return; + + QueueDel(args.Target); + } +} diff --git a/Content.Server/_Wega/BloodCult/BloodCultSystem.cs b/Content.Server/_Wega/BloodCult/BloodCultSystem.cs index cf4db51fea..deb64b9351 100644 --- a/Content.Server/_Wega/BloodCult/BloodCultSystem.cs +++ b/Content.Server/_Wega/BloodCult/BloodCultSystem.cs @@ -1,41 +1,39 @@ using System.Linq; using System.Numerics; -using Content.Server.Bed.Cryostorage; -using Content.Server.Body.Components; +using Content.Server.Audio; using Content.Server.Body.Systems; -using Content.Server.GameTicking.Rules.Components; -using Content.Server.Prayer; +using Content.Server.GameTicking.Rules; using Content.Server.RoundEnd; using Content.Shared.Actions; using Content.Shared.Blood.Cult; using Content.Shared.Blood.Cult.Components; using Content.Shared.Body.Components; -using Content.Shared.Chemistry.Components.SolutionManager; using Content.Shared.Chemistry.EntitySystems; using Content.Shared.Chemistry.Reagent; using Content.Shared.Containers.ItemSlots; using Content.Shared.Damage; using Content.Shared.DoAfter; +using Content.Shared.EnergyShield; +using Content.Shared.Examine; using Content.Shared.FixedPoint; -using Content.Shared.Humanoid; +using Content.Shared.Hands.EntitySystems; using Content.Shared.IdentityManagement; using Content.Shared.Interaction; using Content.Shared.Interaction.Components; using Content.Shared.Interaction.Events; +using Content.Shared.Inventory.Events; using Content.Shared.Mind; using Content.Shared.Mind.Components; -using Content.Shared.Mindshield.Components; -using Content.Shared.Mobs; -using Content.Shared.Mobs.Components; +using Content.Shared.Mobs.Systems; using Content.Shared.Popups; using Content.Shared.Standing; using Content.Shared.Weapons.Melee; +using Content.Shared.Weapons.Melee.Events; using Content.Shared.Weapons.Ranged.Events; using Robust.Server.Audio; using Robust.Server.GameObjects; -using Robust.Shared.Audio; using Robust.Shared.Containers; -using Robust.Shared.Player; +using Robust.Shared.Prototypes; using Robust.Shared.Random; using Robust.Shared.Timing; @@ -44,37 +42,37 @@ namespace Content.Server.Blood.Cult; public sealed partial class BloodCultSystem : SharedBloodCultSystem { [Dependency] private readonly AudioSystem _audio = default!; + [Dependency] private readonly BloodCultRuleSystem _bloodCult = default!; [Dependency] private readonly BodySystem _body = default!; [Dependency] private readonly IGameTiming _gameTiming = default!; + [Dependency] private readonly IPrototypeManager _prototypeManager = default!; [Dependency] private readonly IRobustRandom _random = default!; + [Dependency] private readonly MetaDataSystem _meta = default!; + [Dependency] private readonly MobStateSystem _mobState = default!; + [Dependency] private readonly RoundEndSystem _roundEndSystem = default!; + [Dependency] private readonly ServerGlobalSoundSystem _sound = default!; [Dependency] private readonly SharedActionsSystem _action = default!; [Dependency] private readonly SharedAppearanceSystem _appearance = default!; [Dependency] private readonly SharedContainerSystem _container = default!; [Dependency] private readonly SharedDoAfterSystem _doAfterSystem = default!; + [Dependency] private readonly SharedHandsSystem _hands = default!; [Dependency] private readonly SharedMindSystem _mind = default!; [Dependency] private readonly SharedPopupSystem _popup = default!; [Dependency] private readonly SharedSolutionContainerSystem _solution = default!; - [Dependency] private readonly PrayerSystem _prayerSystem = default!; - [Dependency] private readonly RoundEndSystem _roundEndSystem = default!; - - private readonly List _selectedTargets = new(); - private bool _firstTriggered = false; - private bool _secondTriggered = false; - private bool _conductedComplete = false; - private bool _ritualStage = false; - private int _curses = 2; + [Dependency] private readonly UserInterfaceSystem _ui = default!; public override void Initialize() { - SubscribeLocalEvent(OnRuleShutdown); - SubscribeLocalEvent(OnCheckObjective); - SubscribeLocalEvent(OnComponentStartup); + base.Initialize(); + + InitializeRunes(); + InitializeBloodAbilities(); + + SubscribeLocalEvent(OnCultistEyesExamined); + SubscribeLocalEvent(OnShotAttempted); // Corvax-Wega-Testing - SubscribeLocalEvent(OnComponentStartup); - SubscribeLocalEvent(OnComponentShutdown); - SubscribeLocalEvent(OnCryostorageEnter); + SubscribeLocalEvent(OnAttemptMelee); SubscribeLocalEvent(OnInteract); - SubscribeLocalEvent(OnAttackAttempt); SubscribeLocalEvent(OnComponentInit); SubscribeLocalEvent(OnShutdown); @@ -84,16 +82,17 @@ public sealed partial class BloodCultSystem : SharedBloodCultSystem SubscribeLocalEvent(OnShuttleCurse); + SubscribeLocalEvent(OnVeilShifterExamined); SubscribeLocalEvent(OnVeilShifter); + SubscribeLocalEvent(OnShieldGotUnequipped); + SubscribeLocalEvent(OnConstructInteract); - SubscribeNetworkEvent(OnConstructSelect); + SubscribeLocalEvent(OnConstructSelect); + SubscribeLocalEvent(OnStructureMapInit); SubscribeLocalEvent(OnStructureInteract); - SubscribeNetworkEvent(OnStructureItemSelect); - - InitializeRunes(); - InitializeBloodAbilities(); + SubscribeLocalEvent(OnStructureItemSelect); } public override void Update(float frameTime) @@ -107,8 +106,7 @@ public sealed partial class BloodCultSystem : SharedBloodCultSystem { pylonQueryComponent.NextTimeTick = 3; var nearbyCultists = _entityLookup.GetEntitiesInRange(Transform(pylon).Coordinates, 11f) - .Where(cultist => !_entityManager.TryGetComponent(cultist.Owner, out var thresholds) - || thresholds.CurrentThresholdState != MobState.Dead) + .Where(cultist => !_mobState.IsDead(cultist)) .ToList(); foreach (var target in nearbyCultists) @@ -116,35 +114,44 @@ public sealed partial class BloodCultSystem : SharedBloodCultSystem var heal = new DamageSpecifier { DamageDict = { { "Blunt", -1 }, { "Slash", -1 } } }; _damage.TryChangeDamage(target.Owner, heal, true); - if (TryComp(target, out var blood)) - _blood.TryModifyBloodLevel(target.Owner, +1); + _blood.TryModifyBloodLevel(target.Owner, +1); } } pylonQueryComponent.NextTimeTick -= frameTime; } var ritualQuery = EntityQueryEnumerator(); - while (ritualQuery.MoveNext(out var rune, out var ritualQueryComponent)) + while (ritualQuery.MoveNext(out var rune, out var ritualDimensional)) { - if (ritualQueryComponent.Activate) + if (ritualDimensional.Activate) { - if (!_ritualStage) + if (ritualDimensional.NextTimeTick <= 0) { - _ritualStage = true; - CheckStage(); - } - - if (ritualQueryComponent.NextTimeTick <= 0) - { - ritualQueryComponent.NextTimeTick = 1; + ritualDimensional.NextTimeTick = 1; if (!CheckRitual(_transform.GetMapCoordinates(rune), 9)) - ritualQueryComponent.Activate = false; + ritualDimensional.Activate = false; + + if (!ritualDimensional.SoundPlayed && _gameTiming.CurTime > ritualDimensional.ActivateTime + TimeSpan.FromSeconds(30)) + { + _sound.PlayGlobalOnStation(rune, _audio.ResolveSound(ritualDimensional.RitualMusic)); + ritualDimensional.SoundPlayed = true; + } } - ritualQueryComponent.NextTimeTick -= frameTime; + ritualDimensional.NextTimeTick -= frameTime; } } } + private void OnCultistEyesExamined(EntityUid uid, BloodCultistEyesComponent component, ExaminedEvent args) + { + if (!args.IsInDetailsRange) + return; + + var name = Identity.Name(uid, EntityManager, args.Examiner); + if (Name(uid) == name) + args.PushMarkup(Loc.GetString("blood-cultist-eyes-glow-examined", ("name", name))); + } + // Corvax-Wega-Testing-start // ะ”ะฐ ั ะฟะพะผะตั‚ะธะป ั‚ะตะณะฐะผะธ ั‡ั‚ะพะฑั‹ ะฑะฐะฝะฐะปัŒะฝะพ ะฝะต ะทะฐะฑั‹ั‚ัŒ ะฟั€ะพ ัั‚ะพ ะธ ั‡ะพ? private void OnShotAttempted(Entity ent, ref ShotAttemptedEvent args) @@ -157,308 +164,20 @@ public sealed partial class BloodCultSystem : SharedBloodCultSystem } // Corvax-Wega-Testing-end - #region Stages Update - private void OnRuleShutdown(EntityUid uid, BloodCultRuleComponent component, ComponentShutdown args) + #region Dagger & Weapon + private void OnAttemptMelee(Entity entity, ref AttemptMeleeEvent args) { - _selectedTargets.Clear(); - _firstTriggered = false; - _secondTriggered = false; - _conductedComplete = false; - _curses = 2; - - _offerings = 3; - _isRitualRuneUnlocked = false; - } - - public void SelectRandomTargets() - { - _selectedTargets.Clear(); - - var candidates = new List(); - var enumerator = EntityQueryEnumerator(); - while (enumerator.MoveNext(out var uid, out _)) + var user = Transform(entity.Owner).ParentUid; + if (!HasComp(user)) { - candidates.Add(uid); - } + _popup.PopupEntity(Loc.GetString("blood-cult-failed-attack"), user, user, PopupType.SmallCaution); - if (candidates.Count >= 2) - { - var selectedIndices = new HashSet(); - while (selectedIndices.Count < 2) - { - var index = _random.Next(0, candidates.Count); - selectedIndices.Add(index); - } - - foreach (var index in selectedIndices) - { - var target = candidates[index]; - _selectedTargets.Add(target); - EnsureComp(target); - } - return; - } - - _selectedTargets.AddRange(candidates); - foreach (var target in candidates) - { - EnsureComp(target); - } - - var globalCandidates = new List(); - var globalEnumerator = EntityQueryEnumerator(); - while (globalEnumerator.MoveNext(out var uid, out _, out _, out _)) - { - if (_selectedTargets.Contains(uid) || HasComp(uid)) - { - continue; - } - globalCandidates.Add(uid); - } - - while (_selectedTargets.Count < 2 && globalCandidates.Count > 0) - { - var index = _random.Next(0, globalCandidates.Count); - var target = globalCandidates[index]; - _selectedTargets.Add(target); - EnsureComp(target); - globalCandidates.RemoveAt(index); + var dropEvent = new DropHandItemsEvent(); + RaiseLocalEvent(user, ref dropEvent); + args.Cancelled = true; } } - private EntityUid? FindNewRandomTarget(Entity excludedEntity) - { - var candidates = new List(); - var query = EntityQueryEnumerator(); - while (query.MoveNext(out var uid, out _, out _, out _)) - { - if (uid == excludedEntity.Owner || HasComp(uid) - || HasComp(uid)) - { - continue; - } - candidates.Add(uid); - } - - if (candidates.Count == 0) - return null; - - var index = _random.Next(0, candidates.Count); - return candidates[index]; - } - - private void CheckTargetsConducted(EntityUid eliminatedTarget) - { - if (_selectedTargets.Contains(eliminatedTarget)) - _selectedTargets.Remove(eliminatedTarget); - - if (_selectedTargets.Count == 0 || !_selectedTargets.Any(IsTargetValid)) - { - _conductedComplete = true; - RaiseLocalEvent(new RitualConductedEvent()); - } - } - - private bool IsTargetValid(EntityUid target) - { - return _entityManager.EntityExists(target); - } - - private void OnCheckObjective(EntityUid uid, BloodCultistComponent component, BloodCultObjectiveActionEvent args) - { - if (!TryComp(uid, out var playerActor)) - return; - - string msg; - if (_selectedTargets.Count == 0 && !_conductedComplete || !_selectedTargets.Any(IsTargetValid) && !_conductedComplete) - { - msg = Loc.GetString("blood-cult-targets-no-select"); - } - else if (_selectedTargets.Count == 0 && IsRitualConducted()) - { - msg = Loc.GetString("blood-cult-ritual-completed-next-objective"); - } - else if (IsGodCalled()) - { - msg = Loc.GetString("blood-cult-objective-complete"); - } - else - { - var targetNames = _selectedTargets - .Where(IsTargetValid) - .Select(target => Name(target)) - .ToList(); - - if (targetNames.Count > 0) - { - msg = Loc.GetString("blood-cult-current-targets", ("targets", string.Join(", ", targetNames))); - } - else - { - msg = Loc.GetString("blood-cult-no-valid-targets"); - } - } - - _prayerSystem.SendSubtleMessage(playerActor.PlayerSession, playerActor.PlayerSession, string.Empty, msg); - args.Handled = true; - } - - private bool IsRitualConducted() - { - var query = EntityManager.EntityQuery(); - foreach (var cult in query) - { - var winConditions = cult.BloodCultWinCondition.ToList(); - if (winConditions.Contains(BloodCultWinType.RitualConducted)) - return true; - } - return false; - } - - private bool IsGodCalled() - { - var query = EntityManager.EntityQuery(); - foreach (var cult in query) - { - var winConditions = cult.BloodCultWinCondition.ToList(); - if (winConditions.Contains(BloodCultWinType.GodCalled)) - return true; - } - return false; - } - - private void OnComponentStartup(Entity entity, ref ComponentStartup args) - { - CheckStage(); - } - - private void OnComponentStartup(Entity entity, ref ComponentStartup args) - { - CheckStage(); - } - - private void OnComponentShutdown(Entity entity, ref ComponentShutdown args) - { - CheckStage(); - } - - private void OnCryostorageEnter(Entity entity, ref CryostorageEnterEvent args) - { - if (!TryComp(args.Uid, out var objectComponent)) - return; - - var newTarget = FindNewRandomTarget((args.Uid, objectComponent)); - if (newTarget != null) - { - _selectedTargets.Add(newTarget.Value); - EnsureComp(newTarget.Value); - } - - _selectedTargets.Remove(args.Uid); - RemComp(args.Uid); - } - - private void CheckStage() - { - var totalCultEntities = GetCultEntities(); - var playerCount = GetPlayerCount(); - - // Second - if (playerCount >= 100 && totalCultEntities >= playerCount * 0.1f || playerCount < 100 && totalCultEntities >= playerCount * 0.2f || _ritualStage) - { - foreach (var cultist in GetAllCultists()) - { - if (!HasComp(cultist)) - { - UpdateCultistEyes(cultist); - AddComp(cultist); - } - } - if (!_firstTriggered) - { - var actorFilter = Filter.Empty(); - var actorQuery = EntityQueryEnumerator(); - while (actorQuery.MoveNext(out var actorUid, out var actor, out _)) - { - if (actorUid != EntityUid.Invalid) - { - actorFilter.AddPlayer(actor.PlayerSession); - _popup.PopupEntity(Loc.GetString("blood-cult-first-warning"), actorUid, actorUid, PopupType.SmallCaution); - } - } - _audio.PlayGlobal(new SoundPathSpecifier("/Audio/_Wega/Ambience/Antag/bloodcult_eyes.ogg"), actorFilter, true); - _firstTriggered = true; - } - } - - // Third - if (playerCount >= 100 && totalCultEntities >= playerCount * 0.2f || playerCount < 100 && totalCultEntities >= playerCount * 0.3f || _ritualStage) - { - foreach (var cultist in GetAllCultists()) - { - if (!HasComp(cultist)) - { - AddComp(cultist); - } - } - if (!_secondTriggered) - { - var actorFilter = Filter.Empty(); - var actorQuery = EntityQueryEnumerator(); - while (actorQuery.MoveNext(out var actorUid, out var actor, out _)) - { - if (actorUid != EntityUid.Invalid) - { - actorFilter.AddPlayer(actor.PlayerSession); - _popup.PopupEntity(Loc.GetString("blood-cult-second-warning"), actorUid, actorUid, PopupType.SmallCaution); - } - } - _audio.PlayGlobal(new SoundPathSpecifier("/Audio/_Wega/Ambience/Antag/bloodcult_halos.ogg"), actorFilter, true); - _secondTriggered = true; - } - } - } - - private void UpdateCultistEyes(EntityUid cultist) - { - if (TryComp(cultist, out var appearanceComponent)) - { - appearanceComponent.EyeColor = Color.FromHex("#E22218FF"); - Dirty(cultist, appearanceComponent); - } - } - - private int GetCultEntities() - { - var totalCultists = GetAllCultists().Count; - var totalConstructs = EntityQuery().Count(); - return totalCultists + totalConstructs; - } - - private int GetPlayerCount() - { - var players = AllEntityQuery(); - int count = 0; - while (players.MoveNext(out _, out _, out _, out _, out _)) - { - count++; - } - return count; - } - - private List GetAllCultists() - { - var cultists = new List(); - var enumerator = EntityQueryEnumerator(); - while (enumerator.MoveNext(out var uid, out _)) - { - cultists.Add(uid); - } - return cultists; - } - #endregion - - #region Dagger private void OnInteract(EntityUid uid, BloodDaggerComponent component, AfterInteractEvent args) { if (args.Handled || !args.CanReach || args.Target is not { Valid: true } target) @@ -472,6 +191,7 @@ public sealed partial class BloodCultSystem : SharedBloodCultSystem var damage = new DamageSpecifier { DamageDict = { { "Slash", 5 } } }; _damage.TryChangeDamage(user, damage, true); _popup.PopupEntity(Loc.GetString("blood-dagger-failed-interact"), user, user, PopupType.SmallCaution); + args.Handled = true; return; } @@ -496,30 +216,25 @@ public sealed partial class BloodCultSystem : SharedBloodCultSystem private void HandleCultistInteraction(AfterInteractEvent args) { - if (!TryComp(args.Target, out var bodyComponent)) + if (!HasComp(args.Target) || !TryComp(args.Target, out var bloodstream)) return; - foreach (var organ in _body.GetBodyOrgans(args.Target.Value, bodyComponent)) - { - if (!HasComp(organ.Id) - || !TryComp(organ.Id, out var stomachComponent) || stomachComponent.Solution == null - || !TryComp(stomachComponent.Solution.Value, out var solutionContainer) - || !_solution.TryGetSolution((stomachComponent.Solution.Value, solutionContainer), null, out var solutionEntity, out var solution)) - continue; + var solution = bloodstream.BloodSolution; + if (solution == null) + return; - var holywaterReagentId = new ReagentId("Holywater", new List()); - var holywater = solution.GetReagentQuantity(holywaterReagentId); + var holywaterReagentId = new ReagentId("Holywater", new List()); + var holywater = solution.Value.Comp.Solution.GetReagentQuantity(holywaterReagentId); - if (holywater <= 0) - continue; + if (holywater <= 0) + return; - solution.RemoveReagent(holywaterReagentId, holywater); + solution.Value.Comp.Solution.RemoveReagent(holywaterReagentId, holywater); - var unholywaterReagentId = new ReagentId("Unholywater", new List()); - var unholywaterQuantity = new ReagentQuantity(unholywaterReagentId, holywater); - if (solutionEntity != null && _solution.TryAddReagent(solutionEntity.Value, unholywaterQuantity, out var addedQuantity) && addedQuantity > 0) - args.Handled = true; - } + var unholywaterReagentId = new ReagentId("Unholywater", new List()); + var unholywaterQuantity = new ReagentQuantity(unholywaterReagentId, holywater); + + args.Handled = _solution.TryAddReagent(solution.Value, unholywaterQuantity, out var addedQuantity) && addedQuantity > 0; } private void HandleRuneInteraction(AfterInteractEvent args) @@ -548,8 +263,8 @@ public sealed partial class BloodCultSystem : SharedBloodCultSystem meleeWeaponComponent.Damage.DamageDict["Slash"] = FixedPoint2.New(4); component.IsSharpered = true; - _entityManager.DeleteEntity(args.Target); - _entityManager.SpawnEntity("Ash", Transform(user).Coordinates); + QueueDel(args.Target); + Spawn("Ash", Transform(user).Coordinates); _popup.PopupEntity(Loc.GetString("blood-sharpener-success"), user, user, PopupType.Small); } else @@ -557,19 +272,6 @@ public sealed partial class BloodCultSystem : SharedBloodCultSystem _popup.PopupEntity(Loc.GetString("blood-sharpener-failed"), user, user, PopupType.Small); } } - - private void OnAttackAttempt(AttackAttemptEvent args) - { - if (args.Weapon == null || !HasComp(args.Weapon)) - return; - - var user = args.Uid; - if (!HasComp(user)) - { - _popup.PopupEntity(Loc.GetString("blood-cult-failed-attack"), user, user, PopupType.SmallCaution); - args.Cancel(); - } - } #endregion #region Soul Stone @@ -580,10 +282,8 @@ public sealed partial class BloodCultSystem : SharedBloodCultSystem private void OnShutdown(EntityUid uid, StoneSoulComponent component, ComponentShutdown args) { - if (component.SoulEntity != null && _entityManager.EntityExists(component.SoulEntity.Value)) - { + if (component.SoulEntity != null && Exists(component.SoulEntity.Value)) QueueDel(component.SoulEntity.Value); - } } private void OnUseInHand(EntityUid uid, StoneSoulComponent component, UseInHandEvent args) @@ -612,9 +312,6 @@ public sealed partial class BloodCultSystem : SharedBloodCultSystem return; } - var transformSystem = _entityManager.System(); - var metaDataSystem = _entityManager.System(); - if (!_mind.TryGetMind(stone, out var mindId, out var mind)) { _popup.PopupEntity(Loc.GetString("stone-soul-empty"), user, user); @@ -629,10 +326,10 @@ public sealed partial class BloodCultSystem : SharedBloodCultSystem var stoneTransform = Transform(stone).Coordinates; var soul = Spawn(component.SoulProto, stoneTransform); - transformSystem.AttachToGridOrMap(soul, Transform(soul)); + _transform.AttachToGridOrMap(soul, Transform(soul)); if (!string.IsNullOrWhiteSpace(mind.CharacterName)) - metaDataSystem.SetEntityName(soul, mind.CharacterName); + _meta.SetEntityName(soul, mind.CharacterName); _mind.Visit(mindId, soul, mind); component.SoulEntity = soul; @@ -643,7 +340,7 @@ public sealed partial class BloodCultSystem : SharedBloodCultSystem private void RetractSoul(EntityUid stone, StoneSoulComponent component, EntityUid user) { - if (component.SoulEntity == null || !_entityManager.EntityExists(component.SoulEntity.Value)) + if (component.SoulEntity == null || !Exists(component.SoulEntity.Value)) { _popup.PopupEntity(Loc.GetString("stone-soul-empty"), user, user); return; @@ -681,11 +378,12 @@ public sealed partial class BloodCultSystem : SharedBloodCultSystem if (args.Handled || !HasComp(user)) return; - if (_curses > 0) + var cult = _bloodCult.GetActiveRule(); + if (cult != null && cult.Curses > 0) { - _roundEndSystem.CancelRoundEndCountdown(user); - _entityManager.DeleteEntity(entity); - _curses--; + _roundEndSystem.CancelRoundEndCountdown(user, true); + QueueDel(entity); + cult.Curses--; } else { @@ -696,6 +394,14 @@ public sealed partial class BloodCultSystem : SharedBloodCultSystem #endregion #region Veil Shifter + private void OnVeilShifterExamined(EntityUid uid, VeilShifterComponent component, ExaminedEvent args) + { + if (!args.IsInDetailsRange || !HasComp(args.Examiner)) + return; + + args.PushMarkup(Loc.GetString("veil-shifter-examined", ("count", component.ActivationsCount))); + } + private void OnVeilShifter(EntityUid uid, VeilShifterComponent component, UseInHandEvent args) { var user = args.User; @@ -715,11 +421,14 @@ public sealed partial class BloodCultSystem : SharedBloodCultSystem var transform = Transform(user); var targetPosition = transform.Coordinates.Offset(alignedDirection * randomDistance); _transform.SetCoordinates(user, targetPosition); + + _appearance.SetData(uid, VeilShifterVisuals.Charged, component.ActivationsCount > 0); } else { _popup.PopupEntity(Loc.GetString("blood-veil-shifter-failed"), user, user, PopupType.SmallCaution); } + args.Handled = true; } @@ -738,131 +447,123 @@ public sealed partial class BloodCultSystem : SharedBloodCultSystem } #endregion + #region Shield + private void OnShieldGotUnequipped(Entity ent, ref GotUnequippedEvent args) + { + if (!TryComp(args.Equipee, out var energyShield)) + return; + + QueueDel(energyShield.ShieldEntity); + RemComp(args.Equipee, energyShield); + } + #endregion + #region Construct - private void OnConstructInteract(Entity cosntruct, ref InteractHandEvent args) + private void OnConstructInteract(Entity construct, ref InteractHandEvent args) { var user = args.User; if (args.Handled || !HasComp(user)) return; - if (TryComp(cosntruct, out var itemSlotsComponent)) + if (TryComp(construct, out var itemSlotsComponent)) { - foreach (var slot in itemSlotsComponent.Slots.Values) + EntityUid? item = itemSlotsComponent.Slots.First().Value.Item; + + if (item != null) { - if (slot.HasItem) + if (_mind.TryGetMind(item.Value, out _, out _)) { - var containedEntity = slot.Item; - if (containedEntity != null) - { - if (TryComp(containedEntity.Value, out var mindContainer) && mindContainer.Mind != null) - { - var netEntity = _entityManager.GetNetEntity(user); - var netCosntruct = _entityManager.GetNetEntity(cosntruct); - var mind = _entityManager.GetNetEntity(mindContainer.Mind.Value); - RaiseNetworkEvent(new OpenConstructMenuEvent(netEntity, netCosntruct, mind)); - } - else - { - _popup.PopupEntity(Loc.GetString("blood-construct-no-mind"), user, user, PopupType.SmallCaution); - } - } + _ui.OpenUi(construct.Owner, BloodConstructUiKey.Key, user); } else { - _popup.PopupEntity(Loc.GetString("blood-construct-failed"), user, user, PopupType.SmallCaution); + _popup.PopupEntity(Loc.GetString("blood-construct-no-mind"), user, user, PopupType.SmallCaution); } } - } - else - { - _popup.PopupEntity(Loc.GetString("blood-construct-failed"), user, user, PopupType.SmallCaution); + else + { + _popup.PopupEntity(Loc.GetString("blood-construct-failed"), user, user, PopupType.SmallCaution); + } } } - private void OnConstructSelect(BloodConstructMenuClosedEvent args) + private void OnConstructSelect(Entity construct, ref BloodConstructSelectMessage args) { - var user = _entityManager.GetEntity(args.Uid); - var construct = _entityManager.GetEntity(args.ConstructUid); - var mind = _entityManager.GetEntity(args.Mind); + EntityUid? mindUid = null; + if (TryComp(construct, out var itemSlotsComponent)) + mindUid = itemSlotsComponent.Slots.First().Value.Item; - if (mind == EntityUid.Invalid) + if (mindUid == null || !_mind.TryGetMind(mindUid.Value, out var mind, out _)) { - _popup.PopupEntity(Loc.GetString("blood-construct-no-mind"), user, user, PopupType.SmallCaution); + _popup.PopupEntity(Loc.GetString("blood-construct-no-mind"), args.Actor, args.Actor, PopupType.SmallCaution); return; } - var constructMobe = _entityManager.SpawnEntity(args.ConstructProto, Transform(construct).Coordinates); + var constructMobe = Spawn(args.Construct, Transform(construct).Coordinates); _mind.TransferTo(mind, constructMobe); - _entityManager.DeleteEntity(construct); + QueueDel(construct); - _popup.PopupEntity(Loc.GetString("blood-construct-succses"), user, user); + _popup.PopupEntity(Loc.GetString("blood-construct-succses"), args.Actor, args.Actor); } #endregion #region Structures + private void OnStructureMapInit(EntityUid structure, BloodStructureComponent component, MapInitEvent args) + { + component.ActivateTime = _gameTiming.CurTime + TimeSpan.FromMinutes(4); + } + private void OnStructureInteract(EntityUid structure, BloodStructureComponent component, InteractHandEvent args) { var user = args.User; if (args.Handled || !HasComp(user)) return; - if (structure is not { Valid: true } target || !component.CanInteract) - return; - - var currentTime = _gameTiming.RealTime; - if (currentTime < component.ActivateTime) + var currentTime = _gameTiming.CurTime; + var nextActivateTime = component.ActivateTime + TimeSpan.FromMinutes(4); + if (currentTime < nextActivateTime) { - var remainingTime = (component.ActivateTime - currentTime).TotalSeconds; + var remainingTime = (nextActivateTime - currentTime).TotalSeconds; _popup.PopupEntity(Loc.GetString("blood-structure-failed", ("time", Math.Ceiling(remainingTime))), user, user, PopupType.Small); return; } - var netEntity = _entityManager.GetNetEntity(user); - var netStructureEntity = _entityManager.GetNetEntity(target); - RaiseNetworkEvent(new OpenStructureMenuEvent(netEntity, netStructureEntity)); + _ui.OpenUi(structure, BloodStructureUiKey.Key, user); + var state = new BloodStructureBoundUserInterfaceState(component.StructureGear); + _ui.SetUiState(structure, BloodStructureUiKey.Key, state); } - private void OnStructureItemSelect(BloodStructureMenuClosedEvent args) + private void OnStructureItemSelect(Entity structure, ref BloodStructureSelectMessage args) { - var user = _entityManager.GetEntity(args.Uid); - var structure = _entityManager.GetEntity(args.Structure); - if (!TryComp(structure, out var structureComp)) - return; - - var currentTime = _gameTiming.RealTime; - if (currentTime < structureComp.ActivateTime) + var currentTime = _gameTiming.CurTime; + var nextActivateTime = structure.Comp.ActivateTime + TimeSpan.FromMinutes(4); + if (currentTime < nextActivateTime) { - var remainingTime = (structureComp.ActivateTime - currentTime).TotalSeconds; - _popup.PopupEntity(Loc.GetString("blood-structure-failed", ("time", Math.Ceiling(remainingTime))), user, user, PopupType.Small); + var remainingTime = (nextActivateTime - currentTime).TotalSeconds; + _popup.PopupEntity(Loc.GetString("blood-structure-failed", ("time", Math.Ceiling(remainingTime))), args.Actor, args.Actor, PopupType.Small); return; } - structureComp.ActivateTime = currentTime + TimeSpan.FromMinutes(4); + structure.Comp.ActivateTime = currentTime; - var item = _entityManager.SpawnEntity(args.Item, Transform(structure).Coordinates); - _audio.PlayPvs(structureComp.Sound, structure); + var item = Spawn(args.Item, Transform(structure).Coordinates); + _audio.PlayPvs(structure.Comp.Sound, structure); - var cultistPosition = _transform.GetWorldPosition(user); + var cultistPosition = _transform.GetWorldPosition(args.Actor); var structurePosition = _transform.GetWorldPosition(structure); var distance = (structurePosition - cultistPosition).Length(); - if (distance < 3f) - _hands.TryPickupAnyHand(user, item); + if (distance < 3f) _hands.TryPickupAnyHand(args.Actor, item); } #endregion #region God Check - private string GetCurrentGod() + private BloodCultGod GetCurrentGod() { - var query = EntityQueryEnumerator(); - while (query.MoveNext(out var cult)) - { - if (cult.SelectedGod == null) - { - return "Narsie"; - } - return cult.SelectedGod; - } - return "Narsie"; + var cult = _bloodCult.GetActiveRule(); + if (cult != null && cult.SelectedGod != null) + return cult.SelectedGod.Value; + + return BloodCultGod.NarSi; } #endregion } diff --git a/Content.Server/_Wega/BloodCult/BloodMagicEui.cs b/Content.Server/_Wega/BloodCult/BloodMagicEui.cs new file mode 100644 index 0000000000..8c8d9aac38 --- /dev/null +++ b/Content.Server/_Wega/BloodCult/BloodMagicEui.cs @@ -0,0 +1,24 @@ +using Content.Server.EUI; +using Content.Shared.Blood.Cult; +using Content.Shared.Eui; + +namespace Content.Server.Blood.Cult.UI; + +/// +/// Logic for the blood magic window +/// +public sealed class BloodMagicEui(EntityUid cultist, BloodCultSystem bloodCult) : BaseEui +{ + public override EuiStateBase GetNewState() + => new BloodMagicState(); + + public override void HandleMessage(EuiMessageBase msg) + { + base.HandleMessage(msg); + + if (msg is BloodMagicSelectSpellMessage msgSpell) + bloodCult.AfterSpellSelect(cultist, msgSpell.Spell); + + Close(); + } +} diff --git a/Content.Server/_Wega/BloodCult/RuneSystem.cs b/Content.Server/_Wega/BloodCult/RuneSystem.cs deleted file mode 100644 index cd683fba99..0000000000 --- a/Content.Server/_Wega/BloodCult/RuneSystem.cs +++ /dev/null @@ -1,880 +0,0 @@ -using System.Linq; -using System.Threading.Tasks; -using Content.Server.Atmos.EntitySystems; -using Content.Server.Bible.Components; -using Content.Server.GameTicking; -using Content.Server.Ghost.Roles.Components; -using Content.Server.Pinpointer; -using Content.Server.Station.Components; -using Content.Shared.Administration.Systems; -using Content.Shared.Atmos.Components; -using Content.Shared.Blood.Cult; -using Content.Shared.Blood.Cult.Components; -using Content.Shared.Body.Components; -using Content.Shared.Chat; -using Content.Shared.Chemistry.Reagent; -using Content.Shared.Damage; -using Content.Shared.DoAfter; -using Content.Shared.Ghost; -using Content.Shared.Humanoid; -using Content.Shared.Interaction; -using Content.Shared.Interaction.Events; -using Content.Shared.Mind; -using Content.Shared.Mind.Components; -using Content.Shared.Mindshield.Components; -using Content.Shared.Mobs; -using Content.Shared.Mobs.Components; -using Content.Shared.NullRod.Components; -using Content.Shared.Popups; -using Content.Shared.Silicons.Borgs.Components; -using Content.Shared.Standing; -using Content.Shared.Surgery.Components; -using Content.Shared.Timing; -using Robust.Server.GameObjects; -using Robust.Shared.Audio; -using Robust.Shared.Console; -using Robust.Shared.Map; -using Robust.Shared.Map.Components; -using Robust.Shared.Physics.Components; -using Robust.Shared.Player; -using Robust.Shared.Prototypes; -using Robust.Shared.Timing; -using Robust.Shared.Utility; - -namespace Content.Server.Blood.Cult; - -public sealed partial class BloodCultSystem -{ - [Dependency] private readonly BloodCultSystem _bloodCult = default!; - [Dependency] private readonly IConsoleHost _consoleHost = default!; - [Dependency] private readonly FlammableSystem _flammable = default!; - [Dependency] private readonly NavMapSystem _navMap = default!; - [Dependency] private readonly IMapManager _mapMan = default!; - [Dependency] private readonly RejuvenateSystem _rejuvenate = default!; - [Dependency] private readonly SharedGhostSystem _ghost = default!; - - private static readonly EntProtoId ActionComms = "ActionBloodCultComms"; - private const string BloodCultObserver = "MobObserverIfrit"; - private static int _offerings = 3; - private bool _isRitualRuneUnlocked = false; - - private void InitializeRunes() - { - base.Initialize(); - - SubscribeLocalEvent(UnlockRitual); - - SubscribeNetworkEvent(AfterRuneSelect); - SubscribeLocalEvent(DoAfterRuneSelect); - SubscribeLocalEvent(OnDaggerInteract); - SubscribeLocalEvent(OnRuneInteract); - SubscribeLocalEvent(OnRitualInteract); - SubscribeLocalEvent(OnComponentShutdown); - - SubscribeNetworkEvent(OnEmpoweringSelected); - SubscribeLocalEvent(OnEmpoweringDoAfter); - SubscribeNetworkEvent(OnSummoningSelected); - - SubscribeLocalEvent(DoAfterInteractRune); - SubscribeLocalEvent(DoAfterInteractRune); - } - - #region Runes - private void UnlockRitual(RitualConductedEvent ev) - { - _isRitualRuneUnlocked = true; - } - - private void OnComponentShutdown(EntityUid uid, BloodRitualDimensionalRendingComponent component, ComponentShutdown args) - { - _isRitualRuneUnlocked = false; - } - - private void AfterRuneSelect(RuneSelectEvent args, EntitySessionEventArgs eventArgs) - { - var uid = _entityManager.GetEntity(args.Uid); - if (!HasComp(uid) || IsInSpace(uid)) - return; - - var selectedRune = args.RuneProto; - if (selectedRune == "BloodRuneRitualDimensionalRending" && !_isRitualRuneUnlocked) - { - _popup.PopupEntity(Loc.GetString("rune-ritual-failed"), uid, uid, PopupType.MediumCaution); - return; - } - else if (selectedRune == "BloodRuneRitualDimensionalRending" && _isRitualRuneUnlocked) - { - var xform = Transform(uid); - if (!TryComp(xform.GridUid, out var grid) || !HasComp(xform.GridUid.Value)) - { - _popup.PopupEntity(Loc.GetString("rune-ritual-failed"), uid, uid, PopupType.MediumCaution); - return; - } - - bool isValidSurface = true; - var cultistPosition = _transform.GetMapCoordinates(Transform(uid)); - if (!_mapMan.TryFindGridAt(cultistPosition, out _, out _)) - isValidSurface = false; - - if (isValidSurface) - { - var ritualRune = _entityManager.SpawnEntity(TrySelectRuneEffect(selectedRune), Transform(uid).Coordinates); - _appearance.SetData(ritualRune, RuneColorVisuals.Color, TryFindColor(uid)); - - _doAfterSystem.TryStartDoAfter(new DoAfterArgs(EntityManager, uid, TimeSpan.FromSeconds(9.75f), - new BloodRuneDoAfterEvent(selectedRune, GetNetEntity(ritualRune)), uid) - { - BreakOnMove = true, - BreakOnDamage = true, - MovementThreshold = 0.01f, - NeedHand = false - }); - } - else - { - _popup.PopupEntity(Loc.GetString("rune-ritual-failed"), uid, uid, PopupType.MediumCaution); - } - return; - } - - var rune = _entityManager.SpawnEntity(TrySelectRuneEffect(selectedRune), Transform(uid).Coordinates); - _appearance.SetData(rune, RuneColorVisuals.Color, TryFindColor(uid)); - - _doAfterSystem.TryStartDoAfter(new DoAfterArgs(EntityManager, uid, TimeSpan.FromSeconds(4f), - new BloodRuneDoAfterEvent(selectedRune, GetNetEntity(rune)), uid) - { - BreakOnMove = true, - BreakOnDamage = true, - MovementThreshold = 0.01f, - NeedHand = false - }); - } - - private void DoAfterRuneSelect(EntityUid cultist, BloodCultistComponent component, BloodRuneDoAfterEvent args) - { - if (args.Cancelled) - { - _entityManager.DeleteEntity(GetEntity(args.Rune)); - return; - } - - var rune = _entityManager.SpawnEntity(args.SelectedRune, Transform(cultist).Coordinates); - _appearance.SetData(rune, RuneColorVisuals.Color, TryFindColor(cultist)); - - if (args.SelectedRune == "BloodRuneRitualDimensionalRending") - { - var xform = _entityManager.GetComponent(rune); - var msg = Loc.GetString("blood-ritual-warning", - ("location", FormattedMessage.RemoveMarkupOrThrow(_navMap.GetNearestBeaconString((rune, xform))))); - _chat.DispatchGlobalAnnouncement(msg, colorOverride: Color.Red); - - _isRitualRuneUnlocked = false; - } - - if (TryComp(cultist, out var blood) && _blood.GetBloodLevel(cultist) > 0) - _blood.TryModifyBloodLevel(cultist, -5); - else - { - var damage = new DamageSpecifier { DamageDict = { { "Slash", 5 } } }; - _damage.TryChangeDamage(cultist, damage, true); - } - _popup.PopupEntity(Loc.GetString("rune-select-complete"), cultist, cultist, PopupType.SmallCaution); - args.Handled = true; - } - - private void OnRuneInteract(EntityUid rune, BloodRuneComponent component, InteractHandEvent args) - { - if (args.Handled || !HasComp(args.User)) - return; - - if (rune is not { Valid: true } target) - return; - - if (component.Prototype is null) - return; - - OnRuneAfterInteract(target, component, args.User); - args.Handled = true; - } - - private void OnRitualInteract(EntityUid rune, BloodRitualDimensionalRendingComponent component, InteractHandEvent args) - { - if (args.Handled || !HasComp(args.User)) - return; - - var currentTime = _gameTiming.RealTime; - if (currentTime < component.ActivateTime) - { - var remainingTime = component.ActivateTime - currentTime; - _popup.PopupEntity(Loc.GetString("ritual-activate-too-soon", ("time", remainingTime.TotalSeconds)), args.User, args.User, PopupType.LargeCaution); - return; - } - - if (rune is not { Valid: true } target || !CheckRitual(_transform.GetMapCoordinates(target), 9)) - { - _popup.PopupEntity(Loc.GetString("ritual-activate-failed"), args.User, args.User, PopupType.LargeCaution); - return; - } - - component.ActivateTime = currentTime + TimeSpan.FromSeconds(120); - component.Activate = true; - - OnRitualAfterInteract(target, component); - var cultistEntities = _entityLookup.GetEntitiesInRange(_transform.GetMapCoordinates(target), 6f); - foreach (var cultistEntity in cultistEntities) - { - SendCultistMessage(cultistEntity.Owner, "ritual"); - } - args.Handled = true; - } - - private void OnRuneAfterInteract(EntityUid rune, BloodRuneComponent runeComp, EntityUid cultist) - { - var coords = _transform.GetMapCoordinates(rune); - if (!TryComp(rune, out var useDelay) || _useDelay.IsDelayed((rune, useDelay))) - { - _popup.PopupEntity(Loc.GetString("rune-activate-failed"), cultist, cultist, PopupType.MediumCaution); - return; - } - - switch (runeComp.Prototype) - { - case "offering": - var targets = _entityLookup.GetEntitiesInRange(coords, 1f); - foreach (var targetEntity in targets) - { - var target = targetEntity.Owner; - if (HasComp(target) || HasComp(target) - || HasComp(target)) - continue; - - if (!_entityManager.TryGetComponent(target, out var targetThresholds)) - continue; - - var currentState = targetThresholds.CurrentThresholdState; - if (currentState is MobState.Dead && (HasComp(target) || HasComp(target) - || HasComp(target)) && !HasComp(target)) - { - if (CheckRuneActivate(coords, 3)) - { - var cultistEntities = _entityLookup.GetEntitiesInRange(coords, 2f); - foreach (var cultistEntity in cultistEntities) - { - SendCultistMessage(cultistEntity.Owner, "offering"); - } - - var soulStone = _entityManager.SpawnEntity("BloodCultSoulStone", Transform(target).Coordinates); - if (TryComp(target, out var mindContainer) && mindContainer.Mind != null) - _mind.TransferTo(mindContainer.Mind.Value, soulStone); - - // Gib - if (HasComp(target)) - { - _bloodCult.CheckTargetsConducted(target); - RemComp(target); - } - - var damage = new DamageSpecifier { DamageDict = { { "Blunt", 1000 } } }; - _damage.TryChangeDamage(target, damage, true); - IncrementOfferingsCount(); - } - else - { - _popup.PopupEntity(Loc.GetString("rune-activate-failed"), cultist, cultist, PopupType.MediumCaution); - } - break; - } - else if (currentState != MobState.Dead && !HasComp(target) && !HasComp(target) - && !HasComp(target)) - { - if (CheckRuneActivate(coords, 2)) - { - var cultistEntities = _entityLookup.GetEntitiesInRange(coords, 2f); - foreach (var cultistEntity in cultistEntities) - { - SendCultistMessage(cultistEntity.Owner, "offering"); - } - - _rejuvenate.PerformRejuvenate(target); - EnsureComp(target); - } - else - { - _popup.PopupEntity(Loc.GetString("rune-activate-failed"), cultist, cultist, PopupType.MediumCaution); - } - break; - } - else if (currentState is MobState.Dead && !HasComp(target) && !HasComp(target) - && !HasComp(target)) - { - if (CheckRuneActivate(coords, 1)) - { - var cultistEntities = _entityLookup.GetEntitiesInRange(coords, 2f); - foreach (var cultistEntity in cultistEntities) - { - SendCultistMessage(cultistEntity.Owner, "offering"); - } - - var soulStone = _entityManager.SpawnEntity("BloodCultSoulStone", Transform(target).Coordinates); - if (TryComp(target, out var mindContainer) && mindContainer.Mind != null) - _mind.TransferTo(mindContainer.Mind.Value, soulStone); - - // Gib - var damage = new DamageSpecifier { DamageDict = { { "Blunt", 1000 } } }; - _damage.TryChangeDamage(target, damage, true); - IncrementOfferingsCount(); - } - else - { - _popup.PopupEntity(Loc.GetString("rune-activate-failed"), cultist, cultist, PopupType.MediumCaution); - } - break; - } - else - { - _popup.PopupEntity(Loc.GetString("rune-activate-failed"), cultist, cultist, PopupType.MediumCaution); - } - } - break; - case "teleport": - var runes = new List(); - var runeQuery = EntityQueryEnumerator(); - while (runeQuery.MoveNext(out var runeUid, out var runeCompQ)) - { - if (runeCompQ.Prototype == "teleport" && runeUid != rune) - runes.Add(runeUid); - } - - if (runes.Any() && CheckRuneActivate(coords, 1)) - { - var randomRuneEntity = runes[_random.Next(runes.Count)]; - var runeTransform = _entityManager.GetComponent(randomRuneEntity); - var runeCoords = runeTransform.Coordinates; - SendCultistMessage(cultist, "teleport"); - - _entityManager.SpawnEntity("BloodCultOutEffect", Transform(cultist).Coordinates); - _transform.SetCoordinates(cultist, runeCoords); - _entityManager.SpawnEntity("BloodCultInEffect", runeCoords); - _entityManager.DeleteEntity(randomRuneEntity); - } - else - { - _popup.PopupEntity(Loc.GetString("rune-activate-failed"), cultist, cultist, PopupType.MediumCaution); - } - break; - case "empowering": - if (CheckRuneActivate(coords, 1)) - { - if (TryComp(cultist, out var comp) && comp.Empowering < 4) - { - SendCultistMessage(cultist, "empowering"); - - var netEntity = _entityManager.GetNetEntity(cultist); - RaiseNetworkEvent(new EmpoweringRuneMenuOpenedEvent(netEntity)); - } - else - { - _popup.PopupEntity(Loc.GetString("rune-activate-failed"), cultist, cultist, PopupType.MediumCaution); - } - } - break; - case "revive": - if (CheckRuneActivate(coords, 1)) - { - var revivetarget = _entityLookup.GetEntitiesInRange(coords, 1f); - foreach (var targetEntity in revivetarget) - { - var target = targetEntity.Owner; - if (!_entityManager.TryGetComponent(target, out var targetThresholds) || target == cultist) - continue; - - var currentState = targetThresholds.CurrentThresholdState; - if (HasComp(target) && HasComp(target) - && currentState is MobState.Dead) - { - if (GetOfferingsCount() >= 3) - { - SendCultistMessage(cultist, "revive"); - _rejuvenate.PerformRejuvenate(target); - SubtractOfferingsCount(); - - if (TryComp(target, out var mind) && mind.Mind is null - && !HasComp(target)) - { - var formattedCommand = string.Format( - "makeghostrole {0} {1} {2} {3}", - target, - Loc.GetString("ghost-role-information-cultist"), - Loc.GetString("ghost-role-information-cultist-desc"), - Loc.GetString("ghost-role-information-cultist-rules") - ); - _consoleHost.ExecuteCommand(formattedCommand); - } - } - else - { - _popup.PopupEntity(Loc.GetString("rune-activate-failed"), cultist, cultist, PopupType.MediumCaution); - } - break; - } - else if (HasComp(target) && HasComp(target) - && TryComp(target, out var mind) && mind.Mind is null && !HasComp(target)) - { - SendCultistMessage(cultist, "revive"); - var formattedCommand = string.Format( - "makeghostrole {0} {1} {2} {3}", - target, - Loc.GetString("ghost-role-information-cultist"), - Loc.GetString("ghost-role-information-cultist-desc"), - Loc.GetString("ghost-role-information-cultist-rules") - ); - _consoleHost.ExecuteCommand(formattedCommand); - } - else if (HasComp(target) && !HasComp(target) && currentState is MobState.Dead - && !HasComp(target) && !HasComp(target) - && !HasComp(target)/*Stop killing humanoid this way*/) - { - SendCultistMessage(cultist, "revive"); - - // Gib - var damage = new DamageSpecifier { DamageDict = { { "Blunt", 1000 } } }; - _damage.TryChangeDamage(target, damage, true); - IncrementOfferingsCount(); - break; - } - else - { - _popup.PopupEntity(Loc.GetString("rune-activate-failed"), cultist, cultist, PopupType.MediumCaution); - } - } - } - else - { - _popup.PopupEntity(Loc.GetString("rune-activate-failed"), cultist, cultist, PopupType.MediumCaution); - } - break; - case "barrier": - if (CheckRuneActivate(coords, 1)) - { - if (!runeComp.BarrierActive) - { - runeComp.BarrierActive = true; - SendCultistMessage(cultist, "barrier"); - var nearbyRunes = _entityLookup.GetEntitiesInRange(coords, 1f) - .Where(r => EntityManager.TryGetComponent(r, out BloodRuneComponent? nearbyRuneComp) - && nearbyRuneComp.Prototype == "barrier" && r.Owner != rune) - .ToList(); - - Entity? randomRune = nearbyRunes.Any() - ? nearbyRunes[new Random().Next(nearbyRunes.Count)] - : null; - - if (randomRune != null) - { - var randomRuneUid = randomRune.Value; - if (TryComp(randomRuneUid, out var randomRuneComp) && !randomRuneComp.BarrierActive) - { - randomRuneComp.BarrierActive = true; - if (EntityManager.TryGetComponent(randomRuneUid, out PhysicsComponent? randomPhysicsComp)) - { - var fixture = _fixtures.GetFixtureOrNull(randomRuneUid, "barrier"); - if (fixture != null) - { - _physics.SetHard(randomRuneUid, fixture, randomRuneComp.BarrierActive); - } - } - } - } - - if (EntityManager.TryGetComponent(rune, out PhysicsComponent? physicsComp)) - { - var fixture = _fixtures.GetFixtureOrNull(rune, "barrier"); - if (fixture != null) - { - _physics.SetHard(rune, fixture, runeComp.BarrierActive); - } - } - - var barrierRunes = new List(); - var barrierRuneQuery = EntityQueryEnumerator(); - - while (barrierRuneQuery.MoveNext(out var runeUid, out var runeCompQ)) - { - if (runeCompQ.Prototype == "barrier") - barrierRunes.Add(runeUid); - } - - var damageFormula = 2 * barrierRunes.Count; - var damage = new DamageSpecifier { DamageDict = { { "Slash", damageFormula } } }; - _damage.TryChangeDamage(cultist, damage, true); - } - else - { - runeComp.BarrierActive = false; - SendCultistMessage(cultist, "barrier"); - if (EntityManager.TryGetComponent(rune, out PhysicsComponent? physicsComp)) - { - var fixture = _fixtures.GetFixtureOrNull(rune, "barrier"); - if (fixture != null) - { - _physics.SetHard(rune, fixture, runeComp.BarrierActive); - } - } - } - } - else - { - _popup.PopupEntity(Loc.GetString("rune-activate-failed"), cultist, cultist, PopupType.MediumCaution); - } - break; - case "summoning": - if (CheckRuneActivate(coords, 2)) - { - var cultistEntities = _entityLookup.GetEntitiesInRange(coords, 2f); - foreach (var cultistEntity in cultistEntities) - { - SendCultistMessage(cultist, "summoning"); - } - - var netEntity = _entityManager.GetNetEntity(cultist); - RaiseNetworkEvent(new SummoningRuneMenuOpenedEvent(netEntity)); - - _entityManager.DeleteEntity(rune); - } - else - { - _popup.PopupEntity(Loc.GetString("rune-activate-failed"), cultist, cultist, PopupType.MediumCaution); - } - break; - case "bloodboil": - if (CheckRuneActivate(coords, 2)) - { - RemComp(rune); - var cultistEntities = _entityLookup.GetEntitiesInRange(coords, 2f); - foreach (var cultistEntity in cultistEntities) - { - SendCultistMessage(cultistEntity.Owner, "bloodboil"); - } - - Task.Run(async () => - { - var damageValues = new[] { 5, 10, 10 }; - for (int i = 0; i < 3; i++) - { - var targetsFlammable = _entityLookup.GetEntitiesInRange(coords, 10f) - .Where(flammableEntity => - !HasComp(flammableEntity.Owner)) - .ToList(); - - foreach (var targetFlammable in targetsFlammable) - { - if (HasComp(targetFlammable.Owner)) - continue; - - if (TryComp(targetFlammable.Owner, out var flammable)) - { - flammable.FireStacks = 3f; - _flammable.Ignite(targetFlammable.Owner, rune); - - var damage = new DamageSpecifier { DamageDict = { { "Heat", damageValues[i] } } }; - _damage.TryChangeDamage(cultist, damage, false); - } - } - - if (i < 2) - { - await Task.Delay(5000); - } - } - - _entityManager.DeleteEntity(rune); - }); - } - else - { - _popup.PopupEntity(Loc.GetString("rune-activate-failed"), cultist, cultist, PopupType.MediumCaution); - } - break; - case "spiritrealm": - if (CheckRuneActivate(coords, 1)) - { - SendCultistMessage(cultist, "spiritrealm"); - if (TryComp(cultist, out var mindContainer) && mindContainer.Mind != null) - { - var mindSystem = _entityManager.System(); - var metaDataSystem = _entityManager.System(); - var transformSystem = _entityManager.System(); - var gameTicker = _entityManager.System(); - - if (!mindSystem.TryGetMind(cultist, out var mindId, out var mind)) - return; - - if (mind.VisitingEntity != default && _entityManager.TryGetComponent(mind.VisitingEntity, out var oldGhostComponent)) - { - mindSystem.UnVisit(mindId, mind); - if (oldGhostComponent.CanGhostInteract) - return; - } - - var canReturn = mind.CurrentEntity != null - && !_entityManager.HasComponent(mind.CurrentEntity); - var ghost = _entityManager.SpawnEntity(BloodCultObserver, coords); - transformSystem.AttachToGridOrMap(ghost, _entityManager.GetComponent(ghost)); - - if (canReturn) - { - if (!string.IsNullOrWhiteSpace(mind.CharacterName)) - metaDataSystem.SetEntityName(ghost, mind.CharacterName); - - mindSystem.Visit(mindId, ghost, mind); - } - else - { - metaDataSystem.SetEntityName(ghost, Name(cultist)); - mindSystem.TransferTo(mindId, ghost, mind: mind); - } - - var comp = _entityManager.GetComponent(ghost); - _action.RemoveAction(ghost, comp.ToggleGhostBarActionEntity); // Ghost-Bar-Block - _action.AddAction(ghost, ActionComms); - _ghost.SetCanReturnToBody((ghost, comp), canReturn); - break; - } - else - { - _popup.PopupEntity(Loc.GetString("rune-activate-failed"), cultist, cultist, PopupType.MediumCaution); - } - } - break; - default: - _popup.PopupEntity(Loc.GetString("rune-activate-failed"), cultist, cultist, PopupType.MediumCaution); - break; - } - - if (_entityManager.EntityExists(rune)) - _useDelay.TryResetDelay((rune, useDelay)); - } - - private void OnRitualAfterInteract(EntityUid rune, BloodRitualDimensionalRendingComponent runeComp) - { - var xform = _entityManager.GetComponent(rune); - var msg = Loc.GetString("blood-ritual-activate-warning", - ("location", FormattedMessage.RemoveMarkupOrThrow(_navMap.GetNearestBeaconString((rune, xform))))); - _chat.DispatchGlobalAnnouncement(msg, playSound: false, colorOverride: Color.Red); - _audio.PlayGlobal(new SoundPathSpecifier("/Audio/_Wega/Ambience/Antag/bloodcult_scribe.ogg"), Filter.Broadcast(), true); - Timer.Spawn(TimeSpan.FromSeconds(45), () => - { - if (runeComp.Activate) - { - var coords = Transform(rune).Coordinates; - _entityManager.DeleteEntity(rune); - _entityManager.SpawnEntity("BloodCultDistortedEffect", coords); - string currentGod = GetCurrentGod() switch - { - "Narsie" => "MobNarsieSpawn", - "Reaper" => "MobReaperSpawn", - "Kharin" => "MobKharinSpawn", - _ => "MobNarsieSpawn" - }; - _entityManager.SpawnEntity(currentGod, coords); - RaiseLocalEvent(new GodCalledEvent()); - - var nearbyCultists = _entityLookup.GetEntitiesInRange(coords, 6f) - .ToList(); - - foreach (var target in nearbyCultists) - { - var harvester = _entityManager.SpawnEntity("MobConstructHarvester", Transform(target).Coordinates); - if (TryComp(target, out var mindContainer) && mindContainer.Mind != null) - _mind.TransferTo(mindContainer.Mind.Value, harvester); - - var damage = new DamageSpecifier { DamageDict = { { "Blunt", 1000 } } }; - _damage.TryChangeDamage(target.Owner, damage, true); - } - } - else - { - var cultists = EntityQueryEnumerator(); - while (cultists.MoveNext(out var cultist, out _)) - { - _popup.PopupEntity(Loc.GetString("ritual-failed"), cultist, cultist, PopupType.LargeCaution); - } - } - }); - } - #endregion - - #region Other - private void OnEmpoweringSelected(EmpoweringRuneMenuClosedEvent args) - { - var cultist = _entityManager.GetEntity(args.Uid); - if (!HasComp(cultist)) - return; - - _doAfterSystem.TryStartDoAfter(new DoAfterArgs(EntityManager, cultist, TimeSpan.FromSeconds(4f), new EmpoweringDoAfterEvent(args.SelectedSpell), cultist) - { - BreakOnMove = true, - BreakOnDamage = true, - MovementThreshold = 0.01f, - NeedHand = true - }); - } - - private void OnEmpoweringDoAfter(EntityUid cultist, BloodCultistComponent component, EmpoweringDoAfterEvent args) - { - if (args.Cancelled) return; - - var actionEntityUid = _action.AddAction(cultist, args.SelectedSpell); - component.SelectedEmpoweringSpells.Add(actionEntityUid); - component.Empowering++; - - if (TryComp(cultist, out var blood) && _blood.GetBloodLevel(cultist) > 0) - _blood.TryModifyBloodLevel(cultist, -5); - else - { - var damage = new DamageSpecifier { DamageDict = { { "Slash", 2 } } }; - _damage.TryChangeDamage(cultist, damage, true); - } - } - - private void OnSummoningSelected(SummoningSelectedEvent args) - { - var user = _entityManager.GetEntity(args.User); - var target = _entityManager.GetEntity(args.Target); - _entityManager.SpawnEntity("BloodCultOutEffect", Transform(target).Coordinates); - _transform.SetCoordinates(target, Transform(user).Coordinates); - _entityManager.SpawnEntity("BloodCultInEffect", Transform(user).Coordinates); - } - - private bool CheckRuneActivate(MapCoordinates coords, int needCount) - { - var constructsCount = _entityLookup.GetEntitiesInRange(coords, 2f).Count(); - var aliveCultistsCount = _entityLookup.GetEntitiesInRange(coords, 2f) - .Count(cultist => !_entityManager.TryGetComponent(cultist.Owner, out var thresholds) - || thresholds.CurrentThresholdState != MobState.Dead); - return aliveCultistsCount + constructsCount >= needCount; - } - - private bool CheckRitual(MapCoordinates coords, int needCount) - { - var aliveCultistsCount = _entityLookup.GetEntitiesInRange(coords, 6f) - .Count(cultist => !_entityManager.TryGetComponent(cultist.Owner, out var thresholds) - || thresholds.CurrentThresholdState != MobState.Dead); - return aliveCultistsCount >= needCount; - } - - private void SendCultistMessage(EntityUid cultist, string messageType) - { - string message = messageType switch - { - "offering" => Loc.GetString("blood-cultist-offering-message"), - "teleport" => Loc.GetString("blood-cultist-teleport-message"), - "empowering" => Loc.GetString("blood-cultist-empowering-message"), - "revive" => Loc.GetString("blood-cultist-revive-message"), - "barrier" => Loc.GetString("blood-cultist-barrier-message"), - "summoning" => Loc.GetString("blood-cultist-summoning-message"), - "bloodboil" => Loc.GetString("blood-cultist-bloodboil-message"), - "spiritrealm" => Loc.GetString("blood-cultist-spiritrealm-message"), - "ritual" => Loc.GetString("blood-cultist-ritual-message"), - _ => Loc.GetString("blood-cultist-default-message") - }; - - _chat.TrySendInGameICMessage(cultist, message, InGameICChatType.Whisper, ChatTransmitRange.Normal, checkRadioPrefix: false); - } - - private string TrySelectRuneEffect(string messageType) - { - string message = messageType switch - { - "BloodRuneOffering" => "BloodRuneOfferingEffect", - "BloodRuneTeleport" => "BloodRuneTeleportEffect", - "BloodRuneEmpowering" => "BloodRuneEmpoweringEffect", - "BloodRuneRevive" => "BloodRuneReviveEffect", - "BloodRuneBarrier" => "BloodRuneBarrierEffect", - "BloodRuneSummoning" => "BloodRuneSummoningEffect", - "BloodRuneBloodBoil" => "BloodRuneBloodBoilEffect", - "BloodRuneSpiritealm" => "BloodRuneSpiritealmEffect", - "BloodRuneRitualDimensionalRending" => "BloodRuneRitualDimensionalRendingEffect", - _ => "BloodRuneOfferingEffect" - }; - return message; - } - - private void OnDaggerInteract(Entity ent, ref UseInHandEvent args) - { - var user = args.User; - if (!HasComp(user)) - { - var dropEvent = new DropHandItemsEvent(); - RaiseLocalEvent(user, ref dropEvent); - var damage = new DamageSpecifier { DamageDict = { { "Slash", 5 } } }; - _damage.TryChangeDamage(user, damage, true); - _popup.PopupEntity(Loc.GetString("blood-dagger-failed-interact"), user, user, PopupType.SmallCaution); - return; - } - - var netEntity = _entityManager.GetNetEntity(args.User); - RaiseNetworkEvent(new RunesMenuOpenedEvent(netEntity)); - args.Handled = true; - } - - private bool IsInSpace(EntityUid cultist) - { - var cultistPosition = _transform.GetMapCoordinates(Transform(cultist)); - if (!_mapMan.TryFindGridAt(cultistPosition, out _, out _)) - return true; - - return false; - } - - private Color TryFindColor(EntityUid cultist) - { - if (!TryComp(cultist, out var bloodStreamComponent)) - return Color.White; - - string? bloodReagentPrototypeId = null; - if (bloodStreamComponent.BloodReferenceSolution.Contents.Count > 0) - { - var reagentQuantity = bloodStreamComponent.BloodReferenceSolution.Contents[0]; - bloodReagentPrototypeId = reagentQuantity.Reagent.Prototype; - } - - if (bloodReagentPrototypeId == null) - return Color.White; - - if (!_prototypeManager.TryIndex(bloodReagentPrototypeId, out ReagentPrototype? reagentPrototype)) - return Color.White; - - return reagentPrototype.SubstanceColor; - } - - private void DoAfterInteractRune(BloodRuneCleaningDoAfterEvent args) - { - if (args.Cancelled) return; - - _entityManager.DeleteEntity(args.Target); - } - - private void DoAfterInteractRune(EntityUid cultist, BloodCultistComponent component, BloodRuneCleaningDoAfterEvent args) - { - if (args.Cancelled) return; - - _entityManager.DeleteEntity(args.Target); - } - - private static void IncrementOfferingsCount() - { - _offerings++; - } - - private static void SubtractOfferingsCount() - { - _offerings -= 3; - } - - private static int GetOfferingsCount() - { - return _offerings; - } - #endregion -} diff --git a/Content.Server/_Wega/CardTarot/CardTarotSystem.cs b/Content.Server/_Wega/CardTarot/CardTarotSystem.cs new file mode 100644 index 0000000000..d0827c5697 --- /dev/null +++ b/Content.Server/_Wega/CardTarot/CardTarotSystem.cs @@ -0,0 +1,920 @@ +using System.Linq; +using System.Numerics; +using Content.Server.Body.Systems; +using Content.Server.Cargo.Components; +using Content.Server.Chat.Systems; +using Content.Server.Dice; +using Content.Server.Economy.SlotMachine; +using Content.Server.Guardian; +using Content.Server.Hallucinations; +using Content.Server.Polymorph.Systems; +using Content.Server.Revolutionary.Components; +using Content.Server.Shuttles.Components; +using Content.Server.Stack; +using Content.Server.Station.Components; +using Content.Server.Surgery; +using Content.Shared.Administration.Systems; +using Content.Shared.Armor; +using Content.Shared.Blood.Cult.Components; +using Content.Shared.Body.Components; +using Content.Shared.Card.Tarot; +using Content.Shared.Card.Tarot.Components; +using Content.Shared.Chat; +using Content.Shared.Chemistry.EntitySystems; +using Content.Shared.Chemistry.Reagent; +using Content.Shared.CombatMode.Pacification; +using Content.Shared.Damage; +using Content.Shared.Damage.Components; +using Content.Shared.Damage.Prototypes; +using Content.Shared.Damage.Systems; +using Content.Shared.Disease.Components; +using Content.Shared.EnergyShield; +using Content.Shared.FixedPoint; +using Content.Shared.Ghost; +using Content.Shared.Gravity; +using Content.Shared.Humanoid; +using Content.Shared.IdentityManagement; +using Content.Shared.Interaction; +using Content.Shared.Interaction.Events; +using Content.Shared.Inventory; +using Content.Shared.Lock; +using Content.Shared.Mobs.Components; +using Content.Shared.Nutrition.EntitySystems; +using Content.Shared.Pinpointer; +using Content.Shared.Polymorph; +using Content.Shared.Popups; +using Content.Shared.Stacks; +using Content.Shared.StatusEffectNew; +using Content.Shared.Storage.Components; +using Content.Shared.Stunnable; +using Content.Shared.Tag; +using Content.Shared.Throwing; +using Content.Shared.Tiles; +using Content.Shared.Traits.Assorted; +using Content.Shared.Trigger.Components.Triggers; +using Content.Shared.Trigger.Systems; +using Content.Shared.VendingMachines; +using Content.Shared.Weapons.Melee; +using Content.Shared.Weapons.Ranged.Components; +using Robust.Server.GameObjects; +using Robust.Shared.Audio.Systems; +using Robust.Shared.Containers; +using Robust.Shared.Enums; +using Robust.Shared.Map; +using Robust.Shared.Physics.Systems; +using Robust.Shared.Prototypes; +using Robust.Shared.Random; +using Robust.Shared.Timing; + +namespace Content.Server.Card.Tarot; + +public sealed class CardTarotSystem : EntitySystem +{ + [Dependency] private readonly AppearanceSystem _appearance = default!; + [Dependency] private readonly BloodstreamSystem _blood = default!; + [Dependency] private readonly ChatSystem _chat = default!; + [Dependency] private readonly SharedContainerSystem _container = default!; + [Dependency] private readonly DamageableSystem _damage = default!; + [Dependency] private readonly DiceOfFateSystem _dice = default!; + [Dependency] private readonly EntityLookupSystem _entityLookup = default!; + [Dependency] private readonly SharedGravitySystem _gravity = default!; + [Dependency] private readonly HallucinationsSystem _hallucinations = default!; + [Dependency] private readonly IngestionSystem _ingestion = default!; + [Dependency] private readonly InventorySystem _inventory = default!; + [Dependency] private readonly LockSystem _lock = default!; + [Dependency] private readonly SharedPhysicsSystem _physics = default!; + [Dependency] private readonly PolymorphSystem _polymorph = default!; + [Dependency] private readonly SharedPopupSystem _popup = default!; + [Dependency] private readonly IMapManager _mapManager = default!; + [Dependency] private readonly IRobustRandom _random = default!; + [Dependency] private readonly RejuvenateSystem _rejuvenate = default!; + [Dependency] private readonly SlotMachineSystem _slotMachine = default!; + [Dependency] private readonly SharedSolutionContainerSystem _solution = default!; + [Dependency] private readonly StackSystem _stack = default!; + [Dependency] private readonly StatusEffectsSystem _statusEffects = default!; + [Dependency] private readonly SharedStunSystem _stun = default!; + [Dependency] private readonly SurgerySystem _surgery = default!; + [Dependency] private readonly TagSystem _tag = default!; + [Dependency] private readonly ThrowingSystem _throwing = default!; + [Dependency] private readonly TriggerSystem _trigger = default!; + [Dependency] private readonly SharedTransformSystem _transform = default!; + [Dependency] private readonly SharedAudioSystem _audio = default!; + + // 200,000 static variables are ready, and another million is on the way + private static readonly EntProtoId Ash = "Ash"; + private static readonly EntProtoId ClusterBang = "ClusterBangFull"; + private static readonly EntProtoId ClusterGrenade = "ClusterGrenade"; + private static readonly EntProtoId CursedSlotMachine = "CursedSlotMachine"; + private static readonly EntProtoId Drunk = "StatusEffectDrunk"; + private static readonly EntProtoId EmptyCardTarot = "CardTarotNotEnchanted"; + private static readonly EntProtoId Pill = "StrangePill"; + private static readonly EntProtoId RandomContainer = "RandomContainerBlank"; + private static readonly EntProtoId RandomVending = "RandomVending"; + private static readonly EntProtoId Rock = "WallRock"; + private static readonly EntProtoId Smoke = "AdminInstantEffectSmoke30"; + private static readonly EntProtoId SpaceCash = "SpaceCash"; + private static readonly EntProtoId Stand = "MobHoloparasiteGuardian"; + + private static readonly List DeathEnt = new() { + "BloodCultConstruct", "BloodCultSoulStone" + }; + + private static readonly List JusticeEnt = new() { + "MedkitFilled", "TearGasGrenade", "WeaponWandPolymorphDoor", "SpaceCash100" + }; + + private static readonly ProtoId BluntDamage = "Blunt"; + private static readonly ProtoId CellularDamage = "Cellular"; + private static readonly ProtoId HeatDamage = "Heat"; + private static readonly ProtoId PoisonDamage = "Poison"; + private static readonly ProtoId RadiationDamage = "Blunt"; + + private static readonly ProtoId ChariotStatue = "ChariotStatue"; + + private static readonly ProtoId Grenade = "HandGrenade"; + private static readonly ProtoId SlowImmune = "SlowImmune"; + private static readonly ProtoId StunImmune = "StunImmune"; + + private static readonly string Drug = "Desoxyephedrine"; + private static readonly string NotHeal = "Puncturase"; // LoL + + public override void Initialize() + { + base.Initialize(); + + SubscribeLocalEvent(OnTarotInteract); + SubscribeLocalEvent(OnUseTarot); + } + + #region Card Tarot + private void OnTarotInteract(Entity ent, ref AfterInteractEvent args) + { + if (args.Handled || !args.CanReach || args.Target is not { Valid: true } target) + return; + + if (ent.Comp.Card == CardTarot.NotEnchanted) + { + _popup.PopupEntity("tarot-card-not-enchanted", args.User, args.User); + return; + } + + PerformTarotEffect(target, args.User, ent); + args.Handled = true; + } + + private void OnUseTarot(Entity ent, ref UseInHandEvent args) + { + if (args.Handled) + return; + + if (ent.Comp.Card == CardTarot.NotEnchanted) + { + _popup.PopupEntity("tarot-card-not-enchanted", args.User, args.User); + return; + } + + PerformTarotEffect(args.User, args.User, ent); + args.Handled = true; + } + + private void PerformTarotEffect(EntityUid target, EntityUid user, Entity card) + { + var isReversed = card.Comp.CardType == CardTarotType.Reversed; + + switch (card.Comp.Card) + { + case CardTarot.NotEnchanted: + _popup.PopupEntity("tarot-card-not-enchanted", user, user); + return; + case CardTarot.Fool: + PerformFool(target, isReversed); + break; + case CardTarot.Magician: + PerformMagician(target, isReversed); + break; + case CardTarot.HighPriestess: + PerformHighPriestess(target, isReversed); + break; + case CardTarot.Empress: + PerformEmpress(target, isReversed); + break; + case CardTarot.Emperor: + PerformEmperor(target, isReversed); + break; + case CardTarot.Hierophant: + PerformHierophant(target, isReversed); + break; + case CardTarot.Lovers: + PerformLovers(target, isReversed); + break; + case CardTarot.Chariot: + PerformChariot(target, isReversed); + break; + case CardTarot.Justice: + PerformJustice(target, isReversed); + break; + case CardTarot.Hermit: + PerformHermit(target, isReversed); + break; + case CardTarot.WheelOfFortune: + PerformWheelOfFortune(target, isReversed); + break; + case CardTarot.Strength: + PerformStrength(target, isReversed); + break; + case CardTarot.HangedMan: + PerformHangedMan(target, isReversed); + break; + case CardTarot.Death: + PerformDeath(user, target, isReversed); + break; + case CardTarot.Temperance: + PerformTemperance(target, isReversed); + break; + case CardTarot.Devil: + PerformDevil(target, isReversed); + break; + case CardTarot.Tower: + PerformTower(target, isReversed); + break; + case CardTarot.Stars: + PerformStars(target, isReversed); + break; + case CardTarot.Moon: + PerformMoon(target, isReversed); + break; + case CardTarot.Sun: + PerformSun(target, isReversed); + break; + case CardTarot.Judgement: + PerformJudgement(target, isReversed); + break; + case CardTarot.TheWorld: + PerformTheWorld(target, isReversed); + break; + default: break; + } + + var coords = Transform(user).Coordinates; + + var ash = Spawn(Ash, coords); + _throwing.TryThrow(ash, _random.NextVector2()); + _audio.PlayPredicted(card.Comp.UseSound, coords, user); + _popup.PopupEntity(Loc.GetString("tarot-used", ("name", Identity.Name(user, EntityManager)), + ("type", Loc.GetString($"tarot-card-{card.Comp.Card.ToString().ToLower()}"))), + user, PopupType.Medium); + + QueueDel(card); + } + + #region Card Effects + private void PerformFool(EntityUid target, bool reversed) + { + if (reversed) + { + if (_inventory.TryGetSlots(target, out var slots)) + { + foreach (var slot in slots) + { + _inventory.TryUnequip(target, slot.Name, force: true); + } + } + } + else + { + EntityUid? shuttle = null; + var shuttles = EntityQueryEnumerator(); + while (shuttles.MoveNext(out var uid, out _)) + { + shuttle = uid; + break; + } + + if (shuttle == null) + return; + + _transform.SetCoordinates(target, Transform(shuttle.Value).Coordinates); + } + } + + private void PerformMagician(EntityUid target, bool reversed) + { + if (reversed) + { + var nearbyEntity = _entityLookup.GetEntitiesInRange(Transform(target).Coordinates, 6f, LookupFlags.Dynamic) + .Where(e => !e.Comp.Anchored).ToList(); + + foreach (var entity in nearbyEntity) + { + var entityUid = entity.Owner; + if (entityUid == target) + continue; + + var targetPosition = _transform.GetWorldPosition(target); + var entityPosition = _transform.GetWorldPosition(entityUid); + var direction = (entityPosition - targetPosition).Normalized(); + + _physics.ApplyLinearImpulse(entityUid, direction * 2000f); + } + } + else + { + _statusEffects.TrySetStatusEffectDuration(target, "StatusEffectPainNumbness", TimeSpan.FromMinutes(2)); + } + } + + private void PerformHighPriestess(EntityUid target, bool reversed) + { + if (reversed) + { + /// Temporarily empty + } + else + { + var time = TimeSpan.FromSeconds(20); + _stun.TryAddParalyzeDuration(target, time); + Timer.Spawn(time, () => + { + var damage = new DamageSpecifier { DamageDict = { { BluntDamage, 60 } } }; + _damage.TryChangeDamage(target, damage); + }); + } + } + + private void PerformEmpress(EntityUid target, bool reversed) + { + if (reversed) + { + var nearbyEntity = _entityLookup.GetEntitiesInRange(Transform(target).Coordinates, 6f) + .Where(e => e.Owner != target).ToList(); + + foreach (var entity in nearbyEntity) + { + var entityUid = entity.Owner; + if (HasComp(entityUid)) + return; + + EnsureComp(entityUid); + Timer.Spawn(TimeSpan.FromSeconds(40), () => { RemComp(entityUid); }); + } + } + else + { + if (!TryComp(target, out var bloodstream) || bloodstream.BloodSolution == null) + return; + + var drugQuantity = new ReagentQuantity(Drug, FixedPoint2.New(4.5)); + var notHealQuantity = new ReagentQuantity(NotHeal, FixedPoint2.New(12)); + + _solution.TryAddReagent(bloodstream.BloodSolution.Value, drugQuantity, out _); + _solution.TryAddReagent(bloodstream.BloodSolution.Value, notHealQuantity, out _); + } + } + + private void PerformEmperor(EntityUid target, bool reversed) + { + if (reversed) + { + var selected = new List(); + var commandQuery = EntityQueryEnumerator(); + while (commandQuery.MoveNext(out var uid, out _)) + selected.Add(uid); + + if (selected.Count == 0) + return; + + _transform.SetCoordinates(target, Transform(_random.Pick(selected)).Coordinates); + } + else + { + EntityUid? bridgeUid = null; + var beaconQuery = EntityQueryEnumerator(); + while (beaconQuery.MoveNext(out var uid, out var beacon)) + { + if (beacon.DefaultText == "station-beacon-bridge") + { + bridgeUid = uid; + break; + } + } + + if (bridgeUid == null) + return; + + _transform.SetCoordinates(target, Transform(bridgeUid.Value).Coordinates); + } + } + + private void PerformHierophant(EntityUid target, bool reversed) + { + if (reversed) + { + /// Temporarily empty + } + else + { + var slot = "jumpsuit"; + if (!_inventory.TryGetSlotEntity(target, slot, out var clothing)) + return; + + if (!HasComp(clothing)) + { + EnsureComp(clothing.Value).CurrentSlot = slot; + + var shield = EnsureComp(target); + shield.ShieldEntity = Spawn("BloodCultShieldEffect", Transform(target).Coordinates); + _transform.SetParent(shield.ShieldEntity.Value, target); + } + } + } + + private void PerformLovers(EntityUid target, bool reversed) + { + if (reversed) + { + var damage = new DamageSpecifier { DamageDict = { { BluntDamage, 20 }, { HeatDamage, 20 } } }; + _damage.TryChangeDamage(target, damage, true); + _blood.TryModifyBloodLevel(target, -120); + } + else + { + var damage = new DamageSpecifier { DamageDict = { { BluntDamage, -40 }, { HeatDamage, -40 }, { PoisonDamage, -40 } } }; + _damage.TryChangeDamage(target, damage, true); + _blood.TryModifyBloodLevel(target, 100); + } + } + + private void PerformChariot(EntityUid target, bool reversed) + { + if (reversed) + { + _polymorph.PolymorphEntity(target, ChariotStatue); + } + else + { + var time = TimeSpan.FromSeconds(10); + _statusEffects.TrySetStatusEffectDuration(target, "StatusEffectDesoxyStamina", time); + + bool isStunImmuned = false; + bool isSlowImmuned = false; + bool isPacified = false; + if (!_tag.HasTag(target, StunImmune)) + { + isStunImmuned = _tag.TryAddTag(target, StunImmune); + } + + if (!_tag.HasTag(target, SlowImmune)) + { + isSlowImmuned = _tag.TryAddTag(target, SlowImmune); + } + + if (!HasComp(target)) + { + EnsureComp(target); + isPacified = true; + } + + Timer.Spawn(time, () => + { + if (isStunImmuned) _tag.RemoveTag(target, StunImmune); + if (isSlowImmuned) _tag.RemoveTag(target, SlowImmune); + if (isPacified) RemComp(target); + }); + } + } + + private void PerformJustice(EntityUid target, bool reversed) + { + if (reversed) + { + Spawn(RandomContainer, Transform(target).Coordinates); + } + else + { + var coords = Transform(target).Coordinates; + foreach (var ent in JusticeEnt) + Spawn(ent, coords); + } + } + + private void PerformHermit(EntityUid target, bool reversed) + { + if (reversed) + { + var allEnt = new HashSet(); + var nearbyGuns = _entityLookup.GetEntitiesInRange(Transform(target).Coordinates, 6f); + var nearbyMelees = _entityLookup.GetEntitiesInRange(Transform(target).Coordinates, 6f) + .Where(e => !HasComp(e) && !Transform(e).Anchored); + var nearbyArmor = _entityLookup.GetEntitiesInRange(Transform(target).Coordinates, 6f); + var nearbyGrenades = _entityLookup.GetEntitiesInRange(Transform(target).Coordinates, 6f) + .Where(e => _tag.HasTag(e, Grenade)); + + allEnt.UnionWith(nearbyGuns.Select(e => e.Owner)); + allEnt.UnionWith(nearbyMelees.Select(e => e.Owner)); + allEnt.UnionWith(nearbyArmor.Select(e => e.Owner)); + allEnt.UnionWith(nearbyGrenades.Select(e => e.Owner)); + + foreach (var ent in allEnt) + { + var cash = Spawn(SpaceCash, Transform(ent).Coordinates); + if (TryComp(cash, out var stack) && TryComp(ent, out var price)) + _stack.SetCount((cash, stack), (int)price.Price); + + QueueDel(ent); + } + } + else + { + var map = Transform(target).MapID; + + var selected = new List(); + var vendigsQuery = EntityQueryEnumerator(); + while (vendigsQuery.MoveNext(out var uid, out _)) + { + if (map == Transform(uid).MapID) + selected.Add(uid); + } + + if (selected.Count == 0) + return; + + _transform.SetCoordinates(target, Transform(_random.Pick(selected)).Coordinates); + } + } + + private void PerformWheelOfFortune(EntityUid target, bool reversed) + { + if (reversed) + { + var luck = _random.Next(1, 21); + _dice.RollFate(target, luck); + } + else + { + Spawn(RandomVending, Transform(target).Coordinates); + } + } + + private void PerformStrength(EntityUid target, bool reversed) + { + if (reversed) + { + var nearbyEntity = _entityLookup.GetEntitiesInRange(Transform(target).Coordinates, 6f); + + foreach (var entity in nearbyEntity) + { + var entityUid = entity.Owner; + _hallucinations.StartHallucinations(entityUid, "Hallucinations", TimeSpan.FromMinutes(2), true, "MindBreaker"); + } + } + else + { + if (!TryComp(target, out var damageable)) + return; + + var oldMod = damageable.DamageModifierSetId; + _damage.SetDamageModifierSetId(target, "VampireBloodSwell"); + + Timer.Spawn(TimeSpan.FromSeconds(30), () => { _damage.SetDamageModifierSetId(target, oldMod); }); + } + } + + private void PerformHangedMan(EntityUid target, bool reversed) + { + if (reversed) + { + var slotMachine = Spawn(CursedSlotMachine, Transform(target).Coordinates); + _slotMachine.FreeSpeen(slotMachine, target); + } + else + { + _gravity.RefreshWeightless(target, false); + Timer.Spawn(TimeSpan.FromMinutes(1), () => { _gravity.RefreshWeightless(target, true); }); + } + } + + private void PerformDeath(EntityUid user, EntityUid target, bool reversed) + { + if (reversed) + { + var coords = Transform(target).Coordinates; + foreach (var ent in DeathEnt) + Spawn(ent, coords); + } + else + { + var nearbyEntity = _entityLookup.GetEntitiesInRange(Transform(target).Coordinates, 6f) + .Where(e => e.Owner != user); + + var damage = new DamageSpecifier { DamageDict = { { BluntDamage, 20 }, { HeatDamage, 20 } } }; + foreach (var entity in nearbyEntity) + { + _damage.TryChangeDamage(entity.Owner, damage, true); + } + } + } + + private void PerformTemperance(EntityUid target, bool reversed) + { + if (reversed) + { + for (var i = 0; i < 5; i++) + { + var pill = Spawn(Pill, Transform(target).Coordinates); + if (!_ingestion.TryIngest(target, pill)) + break; + } + } + else + { + _statusEffects.TryRemoveStatusEffect(target, Drunk); + if (TryComp(target, out var disease)) + disease.Diseases.Clear(); + + if (TryComp(target, out var damageable)) + { + var damageTypes = new[] { RadiationDamage, PoisonDamage }; + foreach (var damageType in damageTypes) + { + if (damageable.Damage.DamageDict.TryGetValue(damageType, out var currentDamage) && currentDamage > 0) + { + var healSpecifier = new DamageSpecifier { DamageDict = { { damageType, -currentDamage } } }; + _damage.TryChangeDamage(target, healSpecifier, true); + } + } + } + } + } + + private void PerformDevil(EntityUid target, bool reversed) + { + if (reversed) + { + var grenade = Spawn(ClusterBang, Transform(target).Coordinates); + _trigger.Trigger(grenade); + } + else + { + var nearbyEntity = _entityLookup.GetEntitiesInRange(Transform(target).Coordinates, 6f) + .Where(e => e.Owner != target && HasComp(e)).Select(e => e.Owner).ToList(); + + var heal = new DamageSpecifier { DamageDict = { { BluntDamage, -45 }, { HeatDamage, -45 } } }; + _damage.TryChangeDamage(target, heal, true); + _popup.PopupEntity(Loc.GetString("tarot-devil-healed"), target, target); + + Timer.Spawn(TimeSpan.FromSeconds(3), () => + { + if (!Exists(target)) + return; + + StartDamagePhase(nearbyEntity); + }); + } + } + + private void StartDamagePhase(List initialNearby) + { + var validTargets = initialNearby.Where(Exists).ToList(); + if (validTargets.Count == 0) + return; + + var damageTicks = 30; + var currentDamageTick = 0; + + var totalBluntPerTarget = 22; + var totalHeatPerTarget = 23; + + var bluntPerSecondPerTarget = totalBluntPerTarget / (float)damageTicks; + var heatPerSecondPerTarget = totalHeatPerTarget / (float)damageTicks; + + var damageDealt = new Dictionary(); + foreach (var target in validTargets) + { + damageDealt[target] = (0, 0); + } + + void DamageTick() + { + validTargets = validTargets.Where(Exists).ToList(); + if (validTargets.Count == 0) + return; + + foreach (var target in validTargets) + { + var expectedBlunt = bluntPerSecondPerTarget * (currentDamageTick + 1); + var expectedHeat = heatPerSecondPerTarget * (currentDamageTick + 1); + + var (dealtBlunt, dealtHeat) = damageDealt[target]; + var bluntThisTick = (int)(expectedBlunt - dealtBlunt); + var heatThisTick = (int)(expectedHeat - dealtHeat); + + if (bluntThisTick > 0 || heatThisTick > 0) + { + var damageSpec = new DamageSpecifier(); + + if (bluntThisTick > 0) + damageSpec.DamageDict[BluntDamage] = bluntThisTick; + + if (heatThisTick > 0) + damageSpec.DamageDict[HeatDamage] = heatThisTick; + + _damage.TryChangeDamage(target, damageSpec, true); + + if (_random.Prob(0.2f)) + { + _popup.PopupEntity(Loc.GetString("tarot-devil-damaged"), target, target); + } + } + } + + currentDamageTick++; + if (currentDamageTick < damageTicks) + { + Timer.Spawn(TimeSpan.FromSeconds(1), DamageTick); + } + } + + DamageTick(); + } + + private void PerformTower(EntityUid target, bool reversed) + { + if (reversed) + { + var radius = 6; + var center = Transform(target).Coordinates; + for (int x = -radius; x <= radius; x++) + { + for (int y = -radius; y <= radius; y++) + { + if (x * x + y * y <= radius * radius) + { + if (_random.Prob(0.25f)) + { + var spawnCoords = center.Offset(new Vector2(x, y)); + Spawn(Rock, spawnCoords); + } + } + } + } + } + else + { + var grenade = Spawn(ClusterGrenade, Transform(target).Coordinates); + _trigger.Trigger(grenade); + } + } + + private void PerformStars(EntityUid target, bool reversed) + { + if (reversed) + { + var damage = new DamageSpecifier { DamageDict = { { CellularDamage, 50 } } }; + _damage.TryChangeDamage(target, damage, true); + + var damageTypes = new[] { "ClosedFracture", "ArterialBleeding", "MildBurns" }; + _surgery.TryAddInternalDamage(target, _random.Pick(damageTypes)); + + for (var i = 0; i < 2; i++) + { + var card = Spawn(EmptyCardTarot, Transform(target).Coordinates); + var tarot = EnsureComp(card); + + var allCards = Enum.GetValues(); + tarot.Card = (CardTarot)_random.Next(1, allCards.Length); + + bool reversedCard = _random.Prob(0.5f); + if (reversed) tarot.CardType = CardTarotType.Reversed; + + _appearance.SetData(card, CardTarotVisuals.State, tarot.Card); + _appearance.SetData(card, CardTarotVisuals.Reversed, reversedCard); + + _throwing.TryThrow(card, _random.NextVector2()); + } + } + else + { + var lockers = new List(); + var lockersQuery = EntityQueryEnumerator(); + while (lockersQuery.MoveNext(out var uid, out _, out _, out var meta)) + { + if (meta.EntityPrototype != null && meta.EntityPrototype.ID == "LockerEvidence") + lockers.Add(uid); + } + + if (lockers.Count == 0) + return; + + var locker = _random.Pick(lockers); + var lockerCoords = Transform(locker).Coordinates; + var centerTile = Transform(locker).LocalPosition; + + for (int radiusStep = 1; radiusStep <= 3; radiusStep++) + { + for (int i = 0; i < 8; i++) + { + var angle = (float)i / 8 * MathHelper.TwoPi; + var offset = new Vector2( + (float)Math.Cos(angle) * radiusStep, + (float)Math.Sin(angle) * radiusStep); + + var testPos = centerTile + offset; + var testCoords = new EntityCoordinates(lockerCoords.EntityId, testPos); + + var mapCoords = _transform.ToMapCoordinates(testCoords); + var intersecting = _entityLookup.GetEntitiesIntersecting(mapCoords, LookupFlags.Dynamic) + .Where(e => e != target).ToList(); + + if (intersecting.Count == 0) + { + _transform.SetCoordinates(target, testCoords); + _lock.Unlock(locker, null); + return; + } + } + } + } + } + + private void PerformMoon(EntityUid target, bool reversed) + { + if (reversed) + { + // Well, like i couldn't think of anything smarter + var message = Loc.GetString("tarot-moon-m-message"); + if (TryComp(target, out var humanoid) && humanoid.Gender == Gender.Female) + message = Loc.GetString("tarot-moon-f-message"); + + _chat.TrySendInGameICMessage(target, message, InGameICChatType.Speak, false); + + } + else + { + var grids = _mapManager.GetAllGrids(Transform(target).MapID) + .Where(g => !HasComp(g) && !HasComp(g)).ToList(); + + if (grids.Count == 0) + return; + + var randomGrid = _random.Pick(grids); + _transform.SetCoordinates(target, Transform(randomGrid).Coordinates); + } + } + + private void PerformSun(EntityUid target, bool reversed) + { + if (reversed) + { + if (HasComp(target)) + return; + + EnsureComp(target).Blindness = 4; + Timer.Spawn(TimeSpan.FromMinutes(1), () => { RemComp(target); }); + } + else + { + _rejuvenate.PerformRejuvenate(target); + } + } + + private void PerformJudgement(EntityUid target, bool reversed) + { + if (reversed) + { + /// Temporarily empty + } + else + { + // ALL GHOSTS BE MINE!!! + var ghosts = new List(); + var ghostsQuery = EntityQueryEnumerator(); + while (ghostsQuery.MoveNext(out var uid, out _)) + ghosts.Add(uid); + + foreach (var ghost in ghosts) + _transform.SetCoordinates(ghost, Transform(target).Coordinates); + } + } + + private void PerformTheWorld(EntityUid target, bool reversed) + { + if (reversed) + { + // He should be doing something else, but that means "Temporarily empty." So it's a reference to JoJo + var host = EnsureComp(target); + var guardian = Spawn(Stand, Transform(target).Coordinates); + + _container.Insert(guardian, host.GuardianContainer); + host.HostedGuardian = guardian; + + if (TryComp(guardian, out var guardianComp)) + guardianComp.Host = target; + } + else + { + Spawn(Smoke, Transform(target).Coordinates); + } + } + #endregion + #endregion +} diff --git a/Content.Server/_Wega/Chat/Commands/MindSayCommand.cs b/Content.Server/_Wega/Chat/Commands/MindSayCommand.cs new file mode 100644 index 0000000000..a708a8859c --- /dev/null +++ b/Content.Server/_Wega/Chat/Commands/MindSayCommand.cs @@ -0,0 +1,63 @@ +using Content.Server.Chat.Systems; +using Content.Shared.Administration; +using Content.Shared.Mind; +using Robust.Shared.Console; +using Robust.Shared.Enums; + +namespace Content.Server.Chat.Commands +{ + [AnyCommand] + internal sealed class MindSayCommand : LocalizedEntityCommands + { + [Dependency] private readonly ChatSystem _chatSystem = default!; + + public override string Command => "mindsay"; + + public override void Execute(IConsoleShell shell, string argStr, string[] args) + { + if (shell.Player is not { } player) + { + shell.WriteError(Loc.GetString("shell-cannot-run-command-from-server")); + return; + } + + if (player.Status != SessionStatus.InGame) + return; + + if (player.AttachedEntity is not { } playerEntity) + { + shell.WriteError(Loc.GetString($"shell-must-be-attached-to-entity")); + return; + } + + if (args.Length < 1) + return; + + var message = string.Join(" ", args).Trim(); + if (string.IsNullOrEmpty(message)) + return; + + // Process the mind message + if (_chatSystem.TryProcessMindMessage(playerEntity, message, out var modifiedMessage, out var channel)) + { + if (channel != null) + { + // Check if entity has access to the channel + if (EntityManager.TryGetComponent(playerEntity, out var mindLink) && + mindLink.Channels.Contains(channel.ID)) + { + _chatSystem.SendMindMessage(playerEntity, modifiedMessage, channel); + } + else + { + shell.WriteError(Loc.GetString("chat-manager-no-access-mind-channel")); + } + } + else + { + shell.WriteError(Loc.GetString("chat-manager-no-mind-channel")); + } + } + } + } +} diff --git a/Content.Server/_Wega/Dice/DiceOfFateComponent.cs b/Content.Server/_Wega/Dice/DiceOfFateComponent.cs new file mode 100644 index 0000000000..646386b2dd --- /dev/null +++ b/Content.Server/_Wega/Dice/DiceOfFateComponent.cs @@ -0,0 +1,7 @@ +namespace Content.Server.Dice; + +[RegisterComponent] +public sealed partial class DiceOfFateComponent : Component +{ + public bool Used; +} diff --git a/Content.Server/_Wega/Dice/DiceOfFateSystem.cs b/Content.Server/_Wega/Dice/DiceOfFateSystem.cs new file mode 100644 index 0000000000..02f3fe65a1 --- /dev/null +++ b/Content.Server/_Wega/Dice/DiceOfFateSystem.cs @@ -0,0 +1,333 @@ +using System.Linq; +using Content.Server.Administration.Logs; +using Content.Server.Antag; +using Content.Server.Body.Systems; +using Content.Server.Explosion.EntitySystems; +using Content.Server.Polymorph.Systems; +using Content.Shared.Access; +using Content.Shared.Access.Components; +using Content.Shared.Access.Systems; +using Content.Shared.Administration.Systems; +using Content.Shared.Damage; +using Content.Shared.Damage.Prototypes; +using Content.Shared.Damage.Systems; +using Content.Shared.Database; +using Content.Shared.Dice; +using Content.Shared.Disease; +using Content.Shared.Explosion; +using Content.Shared.Hands.Components; +using Content.Shared.Hands.EntitySystems; +using Content.Shared.Interaction.Events; +using Content.Shared.Inventory; +using Content.Shared.Movement.Components; +using Content.Shared.Movement.Systems; +using Content.Shared.PDA; +using Content.Shared.Polymorph; +using Content.Shared.Popups; +using Content.Shared.Roles.Components; +using Content.Shared.Stunnable; +using Content.Shared.Throwing; +using Robust.Shared.Player; +using Robust.Shared.Prototypes; +using Robust.Shared.Random; +using Robust.Shared.Timing; + +namespace Content.Server.Dice; + +public sealed class DiceOfFateSystem : EntitySystem +{ + [Dependency] private readonly SharedAccessSystem _access = default!; + [Dependency] private readonly IAdminLogManager _admin = default!; + [Dependency] private readonly AntagSelectionSystem _antag = default!; + [Dependency] private readonly BodySystem _body = default!; + [Dependency] private readonly DamageableSystem _damage = default!; + [Dependency] private readonly SharedDiseaseSystem _disease = default!; + [Dependency] private readonly ExplosionSystem _explosion = default!; + [Dependency] private readonly SharedHandsSystem _hands = default!; + [Dependency] private readonly InventorySystem _inventory = default!; + [Dependency] private readonly MovementSpeedModifierSystem _speed = default!; + [Dependency] private readonly SharedPopupSystem _popup = default!; + [Dependency] private readonly PolymorphSystem _polymorph = default!; + [Dependency] private readonly IPrototypeManager _prototype = default!; + [Dependency] private readonly IRobustRandom _random = default!; + [Dependency] private readonly RejuvenateSystem _rejuvenate = default!; + [Dependency] private readonly SharedStunSystem _stun = default!; + + public override void Initialize() + { + base.Initialize(); + + SubscribeLocalEvent(OnUseInHand, after: [typeof(SharedDiceSystem)]); + SubscribeLocalEvent(OnLand, after: [typeof(SharedDiceSystem)]); + } + + private static readonly ProtoId DamageMod = "DiceOfFateMod"; + private static readonly ProtoId Monkey = "Monkey"; + private static readonly ProtoId Cold = "SpaceCold"; + private static readonly ProtoId Damage = "Asphyxiation"; + private static readonly EntProtoId RandomAgressive = "RandomAgressiveAnimal"; + private static readonly EntProtoId RandomSpellbook = "RandomSpellbook"; + private static readonly EntProtoId Revolver = "WeaponRevolverInspector"; + private static readonly EntProtoId DefaultWizardRule = "Wizard"; + private static readonly EntProtoId Cookie = "FoodBakedCookie"; + private static readonly EntProtoId Servant = "PlushieLizard"; + private static readonly EntProtoId Toolbox = "ToolboxThief"; + private static readonly EntProtoId Cash = "SpaceCash10000"; + + private void OnUseInHand(Entity entity, ref UseInHandEvent args) + { + if (!TryComp(entity, out var dice) || entity.Comp.Used) + return; + + entity.Comp.Used = true; + RollFate(args.User, dice.CurrentValue); + Timer.Spawn(TimeSpan.FromSeconds(1), () => { QueueDel(entity); }); // So that you can see the number + } + + private void OnLand(Entity entity, ref LandEvent args) + { + if (args.User == null || !TryComp(entity, out var dice) + || entity.Comp.Used) + return; + + entity.Comp.Used = true; + RollFate(args.User.Value, dice.CurrentValue); + Timer.Spawn(TimeSpan.FromSeconds(1), () => { QueueDel(entity); }); // So that you can see the number + } + + public void RollFate(EntityUid user, int value) + { + var success = value switch + { + 1 => CompleteAnnihilation(user), + 2 => InstantDeath(user), + 3 => SummonAggressiveCreatures(user), + 4 => DestroyAllEquippedItems(user), + 5 => TransformIntoMonkey(user), + 6 => PermanentMovementSpeedReduction(user), + 7 => StunAndDamage(user), + 8 => ExplosionUser(user), + 9 => CommonCold(user), + 10 => NothingHappens(user), + 11 => SpawnCookie(user), + 12 => FullHealthRestoration(user), + 13 => SpawnMoney(user), + 14 => SpawnRevolver(user), + 15 => SpawnSpellbook(user), + 16 => SummonServant(user), + 17 => SuspiciousBeacon(user), + 18 => FullAccess(user), + 19 => PermanentDamageReduction(user), + 20 => BecomeWizard(user), + _ => NothingHappens(user) + }; + + _admin.Add(LogType.Action, LogImpact.Extreme, $"{ToPrettyString(user):user} rools fade is '{success}', got nimber {value}."); + } + + private bool CompleteAnnihilation(EntityUid user) + { + _body.GibBody(user, true, splatModifier: 10f); + return true; + } + + private bool InstantDeath(EntityUid user) + { + var damage = new DamageSpecifier { DamageDict = { { Damage, 400 } } }; + _damage.ChangeDamage(user, damage, true); + return true; + } + + private bool SummonAggressiveCreatures(EntityUid user) + { + var count = _random.Next(3, 6); + for (var i = 0; i < count; i++) + { + Spawn(RandomAgressive, Transform(user).Coordinates); + } + + return true; + } + + private bool DestroyAllEquippedItems(EntityUid user) + { + if (_inventory.TryGetSlots(user, out var slots)) + { + foreach (var slot in slots) + { + if (_inventory.TryGetSlotEntity(user, slot.Name, out var ent)) + QueueDel(ent); + } + } + + return true; + } + + private bool TransformIntoMonkey(EntityUid user) + { + _polymorph.PolymorphEntity(user, Monkey); + return true; + } + + private bool PermanentMovementSpeedReduction(EntityUid user) + { + if (TryComp(user, out MovementSpeedModifierComponent? speedmod)) + { + var originalWalkSpeed = speedmod.BaseWalkSpeed; + var originalSprintSpeed = speedmod.BaseSprintSpeed; + + var multiplier = _random.NextFloat(0.3f, 0.95f); + _speed.ChangeBaseSpeed(user, originalWalkSpeed * multiplier, originalSprintSpeed * multiplier, speedmod.Acceleration, speedmod); + } + + return true; + } + + private bool StunAndDamage(EntityUid user) + { + _stun.TryKnockdown(user, TimeSpan.FromSeconds(30)); + var damage = new DamageSpecifier { DamageDict = { { Damage, 50 } } }; + _damage.ChangeDamage(user, damage, true); + + return true; + } + + private bool ExplosionUser(EntityUid user) + { + if (!_prototype.TryIndex(ExplosionSystem.DefaultExplosionPrototypeId, out ExplosionPrototype? type)) + return false; + + _explosion.QueueExplosion(user, type.ID, 5000f, 3f, 10f); + return true; + } + + private bool CommonCold(EntityUid user) + { + _disease.TryAddDisease(user, Cold); + return true; + } + + private bool NothingHappens(EntityUid user) + { + // I'm Lazy + _popup.PopupEntity(Loc.GetString("reagent-desc-nothing"), user, user); + return true; + } + + private bool SpawnCookie(EntityUid user) + { + var cookie = Spawn(Cookie, Transform(user).Coordinates); + _hands.TryForcePickupAnyHand(user, cookie); + + return true; + } + + private bool FullHealthRestoration(EntityUid user) + { + _rejuvenate.PerformRejuvenate(user); + return true; + } + + private bool SpawnMoney(EntityUid user) + { + var cash = Spawn(Cash, Transform(user).Coordinates); + _hands.TryForcePickupAnyHand(user, cash); + + return true; + } + + private bool SpawnRevolver(EntityUid user) + { + var revolver = Spawn(Revolver, Transform(user).Coordinates); + _hands.TryForcePickupAnyHand(user, revolver); + + return true; + } + + private bool SpawnSpellbook(EntityUid user) + { + var spellbook = Spawn(RandomSpellbook, Transform(user).Coordinates); + _hands.TryForcePickupAnyHand(user, spellbook); + + return true; + } + + // Very goodluck + private bool SummonServant(EntityUid user) + { + var servant = Spawn(Servant, Transform(user).Coordinates); + _hands.TryForcePickupAnyHand(user, servant); + + return true; + } + + private bool SuspiciousBeacon(EntityUid user) + { + var toolbox = Spawn(Toolbox, Transform(user).Coordinates); + _hands.TryForcePickupAnyHand(user, toolbox); + + return true; + } + + private bool FullAccess(EntityUid user) + { + var ent = FindActiveId(user); + if (ent == null) + return false; + + GiveAllAccess(ent.Value); + return true; + } + + private EntityUid? FindActiveId(EntityUid target) + { + if (_inventory.TryGetSlotEntity(target, "id", out var slotEntity)) + { + if (HasComp(slotEntity)) + { + return slotEntity.Value; + } + else if (TryComp(slotEntity, out var pda) + && HasComp(pda.ContainedId)) + { + return pda.ContainedId; + } + } + else if (TryComp(target, out var hands)) + { + foreach (var held in _hands.EnumerateHeld((target, hands))) + { + if (HasComp(held)) + { + return held; + } + } + } + + return null; + } + + private void GiveAllAccess(EntityUid entity) + { + var allAccess = _prototype + .EnumeratePrototypes() + .Select(p => new ProtoId(p.ID)).ToArray(); + + _access.TrySetTags(entity, allAccess); + } + + private bool PermanentDamageReduction(EntityUid user) + { + _damage.SetDamageModifierSetId(user, DamageMod); + return true; + } + + private bool BecomeWizard(EntityUid user) + { + if (!TryComp(user, out var actor)) + return false; + + _antag.ForceMakeAntag(actor.PlayerSession, DefaultWizardRule); + return true; + } +} diff --git a/Content.Server/_Wega/GameTicking/Rules/BloodCultRuleSystem.cs b/Content.Server/_Wega/GameTicking/Rules/BloodCultRuleSystem.cs index 0955dab1c7..309f543009 100644 --- a/Content.Server/_Wega/GameTicking/Rules/BloodCultRuleSystem.cs +++ b/Content.Server/_Wega/GameTicking/Rules/BloodCultRuleSystem.cs @@ -2,11 +2,14 @@ using System.Linq; using Content.Server.Actions; using Content.Server.Administration.Logs; using Content.Server.Antag; -using Content.Server.Blood.Cult; +using Content.Server.Bed.Cryostorage; using Content.Server.Body.Components; using Content.Server.Body.Systems; using Content.Server.GameTicking.Rules.Components; using Content.Server.Mind; +using Content.Server.Objectives; +using Content.Server.Objectives.Components; +using Content.Server.Objectives.Systems; using Content.Server.Roles; using Content.Server.RoundEnd; using Content.Shared.Blood.Cult; @@ -18,14 +21,17 @@ using Content.Shared.Database; using Content.Shared.GameTicking.Components; using Content.Shared.Hands.EntitySystems; using Content.Shared.Humanoid; +using Content.Shared.Mindshield.Components; using Content.Shared.Mobs; using Content.Shared.NPC.Prototypes; using Content.Shared.NPC.Systems; +using Content.Shared.Popups; using Content.Shared.Zombies; using Robust.Shared.Audio; +using Robust.Shared.Audio.Systems; using Robust.Shared.Player; using Robust.Shared.Prototypes; -using Robust.Shared.Timing; +using Robust.Shared.Random; namespace Content.Server.GameTicking.Rules { @@ -34,7 +40,6 @@ namespace Content.Server.GameTicking.Rules [Dependency] private readonly ActionsSystem _action = default!; [Dependency] private readonly AntagSelectionSystem _antag = default!; [Dependency] private readonly BodySystem _body = default!; - [Dependency] private readonly BloodCultSystem _cult = default!; [Dependency] private readonly IEntityManager _entityManager = default!; [Dependency] private readonly ISharedPlayerManager _player = default!; [Dependency] private readonly IAdminLogManager _adminLogManager = default!; @@ -44,6 +49,12 @@ namespace Content.Server.GameTicking.Rules [Dependency] private readonly RoleSystem _role = default!; [Dependency] private readonly SharedHandsSystem _hands = default!; [Dependency] private readonly RoundEndSystem _roundEndSystem = default!; + [Dependency] private readonly IRobustRandom _random = default!; + [Dependency] private readonly SharedAudioSystem _audio = default!; + [Dependency] private readonly SharedPopupSystem _popup = default!; + [Dependency] private readonly ObjectivesSystem _objectives = default!; + [Dependency] private readonly TargetObjectiveSystem _target = default!; + [Dependency] private readonly MetaDataSystem _meta = default!; public readonly ProtoId BloodCultNpcFaction = "BloodCult"; @@ -54,44 +65,164 @@ namespace Content.Server.GameTicking.Rules SubscribeLocalEvent(OnRuleStartup); SubscribeLocalEvent(OnCultistSelected); + SubscribeLocalEvent((_, _, _) => CheckStage()); + SubscribeLocalEvent((_, _, _) => CheckStage()); + SubscribeLocalEvent(OnBloodCultObjectShutdown); + SubscribeLocalEvent(OnCryostorageEnter); + SubscribeLocalEvent(OnGodCalled); SubscribeLocalEvent(OnRitualConducted); SubscribeLocalEvent(OnAutoCultistAdded); SubscribeLocalEvent(OnComponentRemove); SubscribeLocalEvent(OnMobStateChanged); - SubscribeLocalEvent(OnOperativeZombified); + SubscribeLocalEvent(OnCultistZombified); } private void OnRuleStartup(EntityUid uid, BloodCultRuleComponent component, ComponentStartup args) { - List gods = new List { "Narsie", "Reaper", "Kharin" }; - component.SelectedGod = gods[new Random().Next(gods.Count)]; - Timer.Spawn(TimeSpan.FromMinutes(1), _cult.SelectRandomTargets); + component.SelectedGod = (BloodCultGod)_random.Next(0, 3); } + private void OnCryostorageEnter(EntityUid uid, BloodCultObjectComponent component, CryostorageEnterEvent args) + { + var cult = GetActiveRule(); + if (cult == null) + return; + + if (!cult.SelectedTargets.Contains(uid)) + return; + + var newTarget = FindNewRandomTarget(cult, uid); + if (newTarget == null) + return; + + ReplaceTargetForAllCultists(uid, newTarget.Value); + + cult.SelectedTargets.Remove(uid); + cult.SelectedTargets.Add(newTarget.Value); + + RemComp(uid); + EnsureComp(newTarget.Value); + } + + #region Cultist Processing + private void OnCultistSelected(Entity mindId, ref AfterAntagEntitySelectedEvent args) { var ent = args.EntityUid; + if (mindId.Comp.SelectedTargets.Count == 0) + SelectRandomTargets(mindId.Comp); + MakeCultist(ent); _antag.SendBriefing(ent, MakeBriefing(ent), Color.Red, null); } + private void SelectRandomTargets(BloodCultRuleComponent cult) + { + cult.SelectedTargets.Clear(); + + var mindShieldCandidates = new List(); + var enumerator = EntityQueryEnumerator(); + while (enumerator.MoveNext(out var uid, out _)) + mindShieldCandidates.Add(uid); + + if (mindShieldCandidates.Count >= 2) + { + var selectedIndices = new HashSet(); + while (selectedIndices.Count < 2) + { + var index = _random.Next(0, mindShieldCandidates.Count); + selectedIndices.Add(index); + } + + foreach (var index in selectedIndices) + { + var target = mindShieldCandidates[index]; + cult.SelectedTargets.Add(target); + EnsureComp(target); + } + return; + } + + foreach (var target in mindShieldCandidates) + { + cult.SelectedTargets.Add(target); + EnsureComp(target); + } + + var globalCandidates = new List(); + var globalEnumerator = EntityQueryEnumerator(); + while (globalEnumerator.MoveNext(out var uid, out _, out _)) + { + if (cult.SelectedTargets.Contains(uid) || HasComp(uid)) + continue; + + globalCandidates.Add(uid); + } + + while (cult.SelectedTargets.Count < 2 && globalCandidates.Count > 0) + { + var index = _random.Next(0, globalCandidates.Count); + var target = globalCandidates[index]; + cult.SelectedTargets.Add(target); + EnsureComp(target); + globalCandidates.RemoveAt(index); + } + } + + private EntityUid? FindNewRandomTarget(BloodCultRuleComponent cult, EntityUid excludedTarget) + { + var candidates = new List(); + var query = EntityQueryEnumerator(); + while (query.MoveNext(out var uid, out _, out _)) + { + if (uid == excludedTarget || cult.SelectedTargets.Contains(uid) + || HasComp(uid) + || HasComp(uid)) + continue; + + candidates.Add(uid); + } + + if (candidates.Count == 0) + return null; + + var index = _random.Next(0, candidates.Count); + return candidates[index]; + } + + private void ReplaceTargetForAllCultists(EntityUid oldTarget, EntityUid newTarget) + { + var replacedObjectives = new List(); + var query = EntityQueryEnumerator(); + while (query.MoveNext(out var objectiveUid, out var targetComp, out _)) + { + if (targetComp.Target == oldTarget) + { + replacedObjectives.Add(objectiveUid); + } + } + + foreach (var objectiveUid in replacedObjectives) + { + _target.SetTarget(objectiveUid, newTarget); + _meta.SetEntityName(objectiveUid, Loc.GetString("objective-condition-blood-ritual-person-title", + ("targetName", Name(newTarget)))); + } + } + private void MakeCultist(EntityUid ent) { var actionPrototypes = new[] { - BloodCultistComponent.CultObjective, - BloodCultistComponent.CultCommunication, BloodCultistComponent.BloodMagic, BloodCultistComponent.RecallBloodDagger }; foreach (var actionPrototype in actionPrototypes) - { _action.AddAction(ent, actionPrototype); - } var componentsToRemove = new[] { @@ -100,25 +231,65 @@ namespace Content.Server.GameTicking.Rules }; foreach (var compType in componentsToRemove) - { - if (HasComp(ent, compType)) - RemComp(ent, compType); - } + RemComp(ent, compType); HandleMetabolism(ent); + CreateObjectivesForCultist(ent); + } + + private void CreateObjectivesForCultist(EntityUid cultist) + { + var cult = GetActiveRule(); + if (cult == null || cult.SelectedTargets.Count == 0) + return; + + if (!_mind.TryGetMind(cultist, out var mindId, out var mind)) + return; + + foreach (var target in cult.SelectedTargets) + { + if (!Exists(target)) + continue; + + var objective = _objectives.TryCreateObjective(mindId, mind, cult.ObjectivePrototype); + if (objective == null) + continue; + + _target.SetTarget(objective.Value, target); + _meta.SetEntityName(objective.Value, Loc.GetString("objective-condition-blood-ritual-person-title", + ("targetName", Name(target)))); // here doesn't worked, or i'm stupid + _mind.AddObjective(mindId, mind, objective.Value); + } + } + + private void HandleMetabolism(EntityUid cultist) + { + if (TryComp(cultist, out var bodyComponent)) + { + foreach (var organ in _body.GetBodyOrgans(cultist, bodyComponent)) + { + if (TryComp(organ.Id, out var metabolizer)) + { + if (TryComp(organ.Id, out _)) + _metabolism.ClearMetabolizerTypes(metabolizer); + + _metabolism.TryAddMetabolizerType(metabolizer, "BloodCultist"); + } + } + } } private string MakeBriefing(EntityUid ent) { - string selectedGod = Loc.GetString("current-god-narsie"); + string selectedGod = ""; var query = QueryActiveRules(); while (query.MoveNext(out _, out _, out var cult, out _)) { selectedGod = cult.SelectedGod switch { - "Narsie" => Loc.GetString("current-god-narsie"), - "Reaper" => Loc.GetString("current-god-reaper"), - "Kharin" => Loc.GetString("current-god-kharin"), + BloodCultGod.NarSi => Loc.GetString("current-god-narsie"), + BloodCultGod.Reaper => Loc.GetString("current-god-reaper"), + BloodCultGod.Kharin => Loc.GetString("current-god-kharin"), _ => Loc.GetString("current-god-narsie") }; break; @@ -153,11 +324,11 @@ namespace Content.Server.GameTicking.Rules var query = QueryActiveRules(); while (query.MoveNext(out _, out _, out var cult, out _)) { - string selectedDagger = cult.SelectedGod switch + EntProtoId selectedDagger = cult.SelectedGod switch { - "Narsie" => "WeaponBloodDagger", - "Reaper" => "WeaponDeathDagger", - "Kharin" => "WeaponHellDagger", + BloodCultGod.NarSi => "WeaponBloodDagger", + BloodCultGod.Reaper => "WeaponDeathDagger", + BloodCultGod.Kharin => "WeaponHellDagger", _ => "WeaponBloodDagger" }; @@ -168,23 +339,134 @@ namespace Content.Server.GameTicking.Rules } } - private void HandleMetabolism(EntityUid cultist) - { - if (TryComp(cultist, out var bodyComponent)) - { - foreach (var organ in _body.GetBodyOrgans(cultist, bodyComponent)) - { - if (TryComp(organ.Id, out var metabolizer)) - { - if (TryComp(organ.Id, out _)) - _metabolism.ClearMetabolizerTypes(metabolizer); + #endregion - _metabolism.TryAddMetabolizerType(metabolizer, "Cultist"); + #region Stages + + private void OnBloodCultObjectShutdown(EntityUid uid, BloodCultObjectComponent component, ComponentShutdown args) + { + var cult = GetActiveRule(); + if (cult == null) + return; + + if (!cult.SelectedTargets.Contains(uid)) + { + CheckStage(); + if (cult.SelectedTargets.Count == 0) + RaiseLocalEvent(new RitualConductedEvent()); + return; + } + + var newTarget = FindNewRandomTarget(cult, uid); + if (newTarget == null) + return; + + ReplaceTargetForAllCultists(uid, newTarget.Value); + + cult.SelectedTargets.Remove(uid); + cult.SelectedTargets.Add(newTarget.Value); + + EnsureComp(newTarget.Value); + } + + private void CheckStage() + { + var cult = GetActiveRule(); + if (cult == null) + return; + + var totalCultEntities = GetCultEntities(); + var playerCount = GetPlayerCount(); + + // Second + if (playerCount >= 100 && totalCultEntities >= playerCount * 0.1f || playerCount < 100 && totalCultEntities >= playerCount * 0.2f || cult.RitualStage) + { + foreach (var cultist in GetAllCultists()) + { + if (!HasComp(cultist)) + { + UpdateCultistEyes(cultist); + AddComp(cultist); } } + + if (!cult.FirstTriggered) + { + var actorFilter = Filter.Empty(); + var actorQuery = EntityQueryEnumerator(); + while (actorQuery.MoveNext(out var actorUid, out var actor, out _)) + { + actorFilter.AddPlayer(actor.PlayerSession); + _popup.PopupEntity(Loc.GetString("blood-cult-first-warning"), actorUid, actorUid, PopupType.SmallCaution); + } + + _audio.PlayGlobal(new SoundPathSpecifier("/Audio/_Wega/Ambience/Antag/bloodcult_eyes.ogg"), actorFilter, true); + cult.FirstTriggered = true; + } + } + + // Third + if (playerCount >= 100 && totalCultEntities >= playerCount * 0.2f || playerCount < 100 && totalCultEntities >= playerCount * 0.3f || cult.RitualStage) + { + foreach (var cultist in GetAllCultists()) + { + EnsureComp(cultist); + } + + if (!cult.SecondTriggered) + { + var actorFilter = Filter.Empty(); + var actorQuery = EntityQueryEnumerator(); + while (actorQuery.MoveNext(out var actorUid, out var actor, out _)) + { + actorFilter.AddPlayer(actor.PlayerSession); + _popup.PopupEntity(Loc.GetString("blood-cult-second-warning"), actorUid, actorUid, PopupType.SmallCaution); + } + + _audio.PlayGlobal(new SoundPathSpecifier("/Audio/_Wega/Ambience/Antag/bloodcult_halos.ogg"), actorFilter, true); + cult.SecondTriggered = true; + } } } + private void UpdateCultistEyes(EntityUid cultist) + { + if (TryComp(cultist, out var appearanceComponent)) + { + appearanceComponent.EyeColor = Color.FromHex("#E22218FF"); + Dirty(cultist, appearanceComponent); + } + } + + private int GetCultEntities() + { + var totalCultists = GetAllCultists().Count; + var totalConstructs = EntityQuery().Count(); + return totalCultists + totalConstructs; + } + + private int GetPlayerCount() + { + int count = 0; + var players = AllEntityQuery(); + while (players.MoveNext(out _, out _, out _, out _)) + count++; + + return count; + } + + private List GetAllCultists() + { + var cultists = new List(); + var enumerator = EntityQueryEnumerator(); + while (enumerator.MoveNext(out var uid, out _)) + cultists.Add(uid); + + return cultists; + } + + #endregion + protected override void AppendRoundEndText(EntityUid uid, BloodCultRuleComponent component, GameRuleComponent gameRule, @@ -208,69 +490,71 @@ namespace Content.Server.GameTicking.Rules } } - private void OnGodCalled(GodCalledEvent ev) + public BloodCultRuleComponent? GetActiveRule() { var query = QueryActiveRules(); while (query.MoveNext(out _, out _, out var cult, out _)) { - if (cult.BloodCultWinCondition.Contains(BloodCultWinType.RitualConducted)) - cult.BloodCultWinCondition.Remove(BloodCultWinType.RitualConducted); + return cult; + } + return null; + } - cult.WinType = BloodCultWinType.GodCalled; + private void OnGodCalled(GodCalledEvent ev) + { + var cult = GetActiveRule(); + if (cult == null) + return; - if (!cult.BloodCultWinCondition.Contains(BloodCultWinType.GodCalled)) - { - cult.BloodCultWinCondition.Add(BloodCultWinType.GodCalled); - _roundEndSystem.DoRoundEndBehavior(RoundEndBehavior.ShuttleCall, TimeSpan.FromMinutes(1f)); - } + if (cult.BloodCultWinCondition.Contains(BloodCultWinType.RitualConducted)) + cult.BloodCultWinCondition.Remove(BloodCultWinType.RitualConducted); + + cult.WinType = BloodCultWinType.GodCalled; + + if (!cult.BloodCultWinCondition.Contains(BloodCultWinType.GodCalled)) + { + cult.BloodCultWinCondition.Add(BloodCultWinType.GodCalled); + _roundEndSystem.DoRoundEndBehavior(RoundEndBehavior.ShuttleCall, TimeSpan.FromMinutes(1f)); } } private void OnRitualConducted(RitualConductedEvent ev) { - var query = QueryActiveRules(); - while (query.MoveNext(out _, out _, out var cult, out _)) - { - cult.WinType = BloodCultWinType.RitualConducted; + var cult = GetActiveRule(); + if (cult == null) + return; - if (!cult.BloodCultWinCondition.Contains(BloodCultWinType.RitualConducted)) - cult.BloodCultWinCondition.Add(BloodCultWinType.RitualConducted); - } + cult.RitualStage = true; + cult.WinType = BloodCultWinType.RitualConducted; + + CheckStage(); + + if (!cult.BloodCultWinCondition.Contains(BloodCultWinType.RitualConducted)) + cult.BloodCultWinCondition.Add(BloodCultWinType.RitualConducted); } private void OnMobStateChanged(EntityUid uid, BloodCultistComponent component, MobStateChangedEvent ev) { if (ev.NewMobState == MobState.Dead) - { - var query = QueryActiveRules(); - while (query.MoveNext(out var ruleUid, out _, out var cult, out _)) - { - CheckCultLose(ruleUid, cult); - } - } + CheckCultLose(GetActiveRule()); } private void OnComponentRemove(EntityUid uid, BloodCultistComponent component, ComponentRemove args) { - var query = QueryActiveRules(); - while (query.MoveNext(out var ruleUid, out _, out var cult, out _)) - { - CheckCultLose(ruleUid, cult); - } + CheckCultLose(GetActiveRule()); } - private void OnOperativeZombified(EntityUid uid, BloodCultistComponent component, EntityZombifiedEvent args) + private void OnCultistZombified(EntityUid uid, BloodCultistComponent component, EntityZombifiedEvent args) { - var query = QueryActiveRules(); - while (query.MoveNext(out var ruleUid, out _, out var cult, out _)) - { - CheckCultLose(ruleUid, cult); - } + CheckCultLose(GetActiveRule()); } - private void CheckCultLose(EntityUid uid, BloodCultRuleComponent cult) + private void CheckCultLose(BloodCultRuleComponent? cult) { - var hasLivingCultists = EntityManager.EntityQuery().Any(); + if (cult == null) + return; + + var hasLivingCultists = EntityQuery().Any(); if (!hasLivingCultists && !cult.BloodCultWinCondition.Contains(BloodCultWinType.GodCalled) && !cult.BloodCultWinCondition.Contains(BloodCultWinType.RitualConducted)) { diff --git a/Content.Server/_Wega/GameTicking/Rules/Components/BloodCultRuleComponent.cs b/Content.Server/_Wega/GameTicking/Rules/Components/BloodCultRuleComponent.cs index 5d07c74931..a3c50c7b20 100644 --- a/Content.Server/_Wega/GameTicking/Rules/Components/BloodCultRuleComponent.cs +++ b/Content.Server/_Wega/GameTicking/Rules/Components/BloodCultRuleComponent.cs @@ -1,19 +1,38 @@ +using Content.Server.Blood.Cult; +using Content.Shared.Blood.Cult; +using Robust.Shared.Prototypes; + namespace Content.Server.GameTicking.Rules.Components; /// -/// Stores data for . +/// Stores data for and . /// -[RegisterComponent, Access(typeof(BloodCultRuleSystem))] +[RegisterComponent, Access(typeof(BloodCultRuleSystem), typeof(BloodCultSystem))] public sealed partial class BloodCultRuleComponent : Component { [DataField] - public string? SelectedGod; + public BloodCultGod? SelectedGod; [DataField] public BloodCultWinType WinType = BloodCultWinType.Neutral; [DataField] public List BloodCultWinCondition = new(); + + [DataField] + public HashSet SelectedTargets = new(); + + public EntProtoId ObjectivePrototype = "BloodCultTargetObjective"; + + [DataField] + public int Curses = 2; + + [DataField] + public int Offerings = 3; + + [DataField] public bool FirstTriggered; + [DataField] public bool SecondTriggered; + [DataField] public bool RitualStage; } public enum BloodCultWinType : byte diff --git a/Content.Server/_Wega/Objectives/Components/BloodCultRitualObjectiveComponent.cs b/Content.Server/_Wega/Objectives/Components/BloodCultRitualObjectiveComponent.cs new file mode 100644 index 0000000000..a9172a2ca4 --- /dev/null +++ b/Content.Server/_Wega/Objectives/Components/BloodCultRitualObjectiveComponent.cs @@ -0,0 +1,9 @@ +using Content.Server.Objectives.Systems; + +namespace Content.Server.Objectives.Components; + +/// +/// A goal that requires completion of the ritual. +/// +[RegisterComponent, Access(typeof(BloodCultRitualObjectiveSystem))] +public sealed partial class BloodCultRitualObjectiveComponent : Component; diff --git a/Content.Server/_Wega/Objectives/Components/BloodCultTargetObjectiveComponent.cs b/Content.Server/_Wega/Objectives/Components/BloodCultTargetObjectiveComponent.cs new file mode 100644 index 0000000000..0e251e2be2 --- /dev/null +++ b/Content.Server/_Wega/Objectives/Components/BloodCultTargetObjectiveComponent.cs @@ -0,0 +1,9 @@ +using Content.Server.Objectives.Systems; + +namespace Content.Server.Objectives.Components; + +/// +/// A common elimination task. +/// +[RegisterComponent, Access(typeof(BloodCultTargetObjectiveSystem))] +public sealed partial class BloodCultTargetObjectiveComponent : Component; diff --git a/Content.Server/_Wega/Objectives/Systems/BloodCultRitualObjectiveSystem.cs b/Content.Server/_Wega/Objectives/Systems/BloodCultRitualObjectiveSystem.cs new file mode 100644 index 0000000000..6abd3dc6c1 --- /dev/null +++ b/Content.Server/_Wega/Objectives/Systems/BloodCultRitualObjectiveSystem.cs @@ -0,0 +1,39 @@ +using System.Linq; +using Content.Server.GameTicking.Rules; +using Content.Server.GameTicking.Rules.Components; +using Content.Server.Objectives.Components; +using Content.Shared.Objectives.Components; + +namespace Content.Server.Objectives.Systems; + +public sealed class BloodCultRitualObjectiveSystem : EntitySystem +{ + [Dependency] private readonly BloodCultRuleSystem _bloodCult = default!; + + public override void Initialize() + { + base.Initialize(); + + SubscribeLocalEvent(OnGetProgress); + } + + private void OnGetProgress(EntityUid uid, BloodCultRitualObjectiveComponent comp, ref ObjectiveGetProgressEvent args) + { + var cult = _bloodCult.GetActiveRule(); + if (cult == null || !cult.RitualStage) + { + args.Progress = 0f; + return; + } + + var condition = cult.BloodCultWinCondition.ToList(); + if (condition.Contains(BloodCultWinType.GodCalled)) + { + args.Progress = 1f; + } + else + { + args.Progress = 0.5f; + } + } +} diff --git a/Content.Server/_Wega/Objectives/Systems/BloodCultTargetObjectiveSystem.cs b/Content.Server/_Wega/Objectives/Systems/BloodCultTargetObjectiveSystem.cs new file mode 100644 index 0000000000..4d068442da --- /dev/null +++ b/Content.Server/_Wega/Objectives/Systems/BloodCultTargetObjectiveSystem.cs @@ -0,0 +1,30 @@ +using Content.Server.Objectives.Components; +using Content.Shared.Objectives.Components; + +namespace Content.Server.Objectives.Systems; + +public sealed class BloodCultTargetObjectiveSystem : EntitySystem +{ + [Dependency] private readonly TargetObjectiveSystem _target = default!; + + public override void Initialize() + { + base.Initialize(); + + SubscribeLocalEvent(OnGetProgress); + } + + private void OnGetProgress(EntityUid uid, BloodCultTargetObjectiveComponent comp, ref ObjectiveGetProgressEvent args) + { + if (!_target.GetTarget(uid, out var target)) + return; + + if (!Exists(target)) + { + args.Progress = 1f; + return; + } + + args.Progress = 0f; + } +} diff --git a/Content.Server/_Wega/SlotMachine/SlotMachineSystem.cs b/Content.Server/_Wega/SlotMachine/SlotMachineSystem.cs new file mode 100644 index 0000000000..00fa5bcd51 --- /dev/null +++ b/Content.Server/_Wega/SlotMachine/SlotMachineSystem.cs @@ -0,0 +1,469 @@ +using System.Linq; +using Content.Server.Chat.Systems; +using Content.Server.Destructible; +using Content.Server.Hands.Systems; +using Content.Server.Power.EntitySystems; +using Content.Server.Stack; +using Content.Shared.Damage.Systems; +using Content.Shared.Economy.SlotMachine; +using Content.Shared.Examine; +using Content.Shared.IdentityManagement; +using Content.Shared.Interaction; +using Content.Shared.Popups; +using Content.Shared.Stacks; +using Robust.Shared.Audio.Systems; +using Robust.Shared.Prototypes; +using Robust.Shared.Random; +using Robust.Shared.Timing; + +namespace Content.Server.Economy.SlotMachine; + +public sealed class SlotMachineSystem : EntitySystem +{ + [Dependency] private readonly SharedAppearanceSystem _appearance = default!; + [Dependency] private readonly SharedAudioSystem _audio = default!; + [Dependency] private readonly ChatSystem _chat = default!; + [Dependency] private readonly DamageableSystem _damage = default!; + [Dependency] private readonly DestructibleSystem _destructible = default!; + [Dependency] private readonly HandsSystem _hands = default!; + [Dependency] private readonly SharedPopupSystem _popup = default!; + [Dependency] private readonly IRobustRandom _random = default!; + [Dependency] private readonly StackSystem _stack = default!; + [Dependency] private readonly IGameTiming _timing = default!; + + private static readonly string[] AllSymbols = { "โ™ฅ", "โ˜…", "โ™ ", "โ™ฆ", "โ™ฃ", "โ™ก" }; + private static readonly string[] CursedSymbols = { "โ˜ ", "๐Ÿฉธ", "โ˜ข", "โ˜ฃ" }; + private static readonly string[] CursedWinSymbols = { "๐Ÿ’ฐ", "โ™”", "๐ŸŽฎ" }; + private static readonly ProtoId Credit = "Credit"; + private static readonly EntProtoId SpaceCash = "SpaceCash"; + private static readonly EntProtoId Reward = "DiceOfFate"; + + private const float JackpotChance = 0.0002f; + private const float BigWinChance = 0.004f; + private const float MediumWinChance = 0.016f; + private const float SmallWinChance = 0.08f; + private const float TinyWinChance = 0.1f; + private const float CursedWinChance = 0.05f; + + public override void Initialize() + { + base.Initialize(); + + SubscribeLocalEvent(OnMapInit); + SubscribeLocalEvent(OnExamined); + SubscribeLocalEvent(OnInteractUsing); + } + + public override void Update(float frameTime) + { + base.Update(frameTime); + + var query = EntityQueryEnumerator(); + while (query.MoveNext(out var uid, out var comp)) + { + if (comp.Working && comp.SpinFinishTime.HasValue) + { + if (_timing.CurTime >= comp.SpinFinishTime.Value) + FinishSpin(uid, comp); + else + UpdateSlotsAnimation(uid, comp); + } + } + } + + private void OnMapInit(EntityUid uid, SlotMachineComponent comp, MapInitEvent args) + => UpdateAppearance(uid); + + private void OnExamined(Entity entity, ref ExaminedEvent args) + { + if (!args.IsInDetailsRange) + return; + + string slots = string.Empty; + foreach (var slot in entity.Comp.Slots) + slots += $"{slot} "; + + args.PushMarkup(Loc.GetString("slot-machine-examine", ("slots", slots.Trim()), ("spins", entity.Comp.Plays))); + + if (TryComp(entity, out var cursedComp)) + { + args.PushMarkup(Loc.GetString("cursed-slot-machine-uses", + ("uses", cursedComp.Uses), ("max", cursedComp.MaxUses))); + } + } + + private void OnInteractUsing(Entity entity, ref InteractUsingEvent args) + { + if (args.Handled) + return; + + args.Handled = TrySpin(entity, args.User, args.Used); + } + + public bool TrySpin(Entity entity, EntityUid user, EntityUid used) + { + if (!TryComp(used, out var stack)) + return false; + + if (entity.Comp.Working) + { + _popup.PopupEntity(Loc.GetString("slot-machine-busy"), user, user); + return false; + } + + bool isCursed = HasComp(entity); + if (!this.IsPowered(entity.Owner, EntityManager) && !isCursed) + { + _popup.PopupEntity(Loc.GetString("slot-machine-unpowered"), user, user); + return false; + } + + if (stack.StackTypeId != Credit) + return false; + + if (isCursed) + { + var cursedComp = Comp(entity); + if (cursedComp.Uses >= cursedComp.MaxUses) + { + _popup.PopupEntity(Loc.GetString("cursed-slot-machine-deny"), user, user, PopupType.SmallCaution); + return false; + } + } + + if (stack.Count < entity.Comp.SpinCost) + { + _popup.PopupEntity(Loc.GetString("slot-machine-no-money"), user, user); + return false; + } + + StartSpin(entity, user, isCursed); + _stack.ReduceCount(used, entity.Comp.SpinCost); + return true; + } + + private void StartSpin(Entity entity, EntityUid user, bool isCursed) + { + entity.Comp.User = user; + + var spinTime = isCursed ? 5 : 2.5; + entity.Comp.SpinFinishTime = _timing.CurTime + TimeSpan.FromSeconds(spinTime); + entity.Comp.Working = true; + entity.Comp.Plays++; + + entity.Comp.Slots = new[] { "?", "?", "?" }; + + UpdateAppearance(entity.Owner); + + if (isCursed && TryComp(entity, out var cursedComp)) + { + _audio.PlayPvs(entity.Comp.CoinSound, entity); + _audio.PlayPvs(cursedComp.RollSound, entity); + } + else + { + _audio.PlayPvs(entity.Comp.CoinSound, entity); + _audio.PlayPvs(entity.Comp.RollSound, entity); + } + + _popup.PopupEntity(Loc.GetString("slot-machine-spinning"), user, user); + + if (isCursed) + { + _popup.PopupEntity(Loc.GetString("cursed-slot-machine-spin", ("name", Identity.Name(user, EntityManager))), + entity.Owner, PopupType.Medium); + } + } + + private void UpdateSlotsAnimation(EntityUid uid, SlotMachineComponent comp) + { + var symbols = HasComp(uid) ? CursedSymbols : AllSymbols; + + for (int i = 0; i < comp.Slots.Length; i++) + { + if (_random.Prob(0.3f)) + { + comp.Slots[i] = _random.Pick(symbols); + } + } + } + + private void FinishSpin(EntityUid machineUid, SlotMachineComponent comp) + { + comp.Working = false; + comp.SpinFinishTime = null; + + if (TryComp(machineUid, out var cursed)) + { + DetermineCursedResult(machineUid, comp, cursed); + } + else + { + DetermineNormalResult(machineUid, comp); + } + + UpdateAppearance(machineUid); + + _audio.PlayPvs(comp.EndSound, machineUid); + } + + private void DetermineNormalResult(EntityUid machineUid, SlotMachineComponent comp) + { + var user = comp.User; + if (user == null) + return; + + var rand = _random.NextFloat(); + + if (rand < JackpotChance) + { + GenerateJackpotSlots(comp); + AwardJackpot(machineUid, comp, user.Value); + } + else if (rand < JackpotChance + BigWinChance) + { + GenerateBigWinSlots(comp); + AwardBigWin(machineUid, comp, user.Value); + } + else if (rand < JackpotChance + BigWinChance + MediumWinChance) + { + GenerateMediumWinSlots(comp); + AwardMediumWin(machineUid, comp, user.Value); + } + else if (rand < JackpotChance + BigWinChance + MediumWinChance + SmallWinChance) + { + GenerateSmallWinSlots(comp); + AwardSmallWin(machineUid, comp, user.Value); + } + else if (rand < JackpotChance + BigWinChance + MediumWinChance + SmallWinChance + TinyWinChance) + { + GenerateTinyWinSlots(comp); + AwardTinyWin(machineUid, comp, user.Value); + } + else + { + GenerateLoseSlots(comp); + _popup.PopupEntity(Loc.GetString("slot-machine-lose"), user.Value, user.Value); + _audio.PlayPvs(comp.FailedSound, machineUid); + } + + comp.User = null; + } + + private void DetermineCursedResult(EntityUid machineUid, SlotMachineComponent comp, CursedSlotMachineComponent cursed) + { + var user = comp.User; + if (user == null) + return; + + var rand = _random.NextFloat(); + + if (rand < CursedWinChance) + { + GenerateCursedWinSlots(comp); + AwardCursedJackpot(machineUid, user.Value, cursed); + } + else + { + GenerateCursedLoseSlots(comp); + AwardCursedLoss(machineUid, comp, user.Value, cursed); + } + + comp.User = null; + } + + #region Slots Vis Generation + + private void GenerateJackpotSlots(SlotMachineComponent comp) + { + comp.Slots = new[] { "โ˜…", "โ˜…", "โ˜…" }; + } + + private void GenerateBigWinSlots(SlotMachineComponent comp) + { + var symbol = _random.Pick(AllSymbols.Where(s => s != "โ˜…").ToArray()); + comp.Slots = new[] { symbol, symbol, symbol }; + } + + private void GenerateMediumWinSlots(SlotMachineComponent comp) + { + var symbols = new[] { "โ™ฅ", "โ™ฆ", "โ™ก" }; + var symbol = _random.Pick(symbols); + comp.Slots = new[] { symbol, symbol, symbol }; + } + + private void GenerateSmallWinSlots(SlotMachineComponent comp) + { + var symbol = _random.Pick(AllSymbols); + var otherSymbols = AllSymbols.Where(s => s != symbol).ToArray(); + + var pattern = _random.Next(3); + switch (pattern) + { + case 0: + comp.Slots = new[] { symbol, symbol, _random.Pick(otherSymbols) }; + break; + case 1: + comp.Slots = new[] { _random.Pick(otherSymbols), symbol, symbol }; + break; + default: + comp.Slots = new[] { symbol, _random.Pick(otherSymbols), symbol }; + break; + } + } + + private void GenerateTinyWinSlots(SlotMachineComponent comp) + { + var symbols = new[] { "โ™ ", "โ™ฃ" }; + var symbol = _random.Pick(symbols); + var otherSymbols = AllSymbols.Where(s => s != symbol).ToArray(); + + var pattern = _random.Next(3); + switch (pattern) + { + case 0: + comp.Slots = new[] { symbol, symbol, _random.Pick(otherSymbols) }; + break; + case 1: + comp.Slots = new[] { _random.Pick(otherSymbols), symbol, symbol }; + break; + default: + comp.Slots = new[] { symbol, _random.Pick(otherSymbols), symbol }; + break; + } + } + + private void GenerateLoseSlots(SlotMachineComponent comp) + { + while (true) + { + comp.Slots = new[] + { + _random.Pick(AllSymbols), + _random.Pick(AllSymbols), + _random.Pick(AllSymbols) + }; + + if (IsLosingCombination(comp.Slots)) + break; + } + } + + private void GenerateCursedWinSlots(SlotMachineComponent comp) + { + var symbol = _random.Pick(CursedWinSymbols); + comp.Slots = new[] { symbol, symbol, symbol }; + } + + private void GenerateCursedLoseSlots(SlotMachineComponent comp) + { + comp.Slots = new[] + { + _random.Pick(CursedSymbols), + _random.Pick(CursedSymbols), + _random.Pick(CursedSymbols) + }; + } + + private bool IsLosingCombination(string[] slots) + { + if (slots[0] == slots[1] && slots[1] == slots[2]) + return false; + + if (slots[0] == slots[1] || slots[1] == slots[2] || slots[0] == slots[2]) + return false; + + var luckySymbols = new[] { "โ™ฅ", "โ™ฆ", "โ™ก" }; + if (luckySymbols.Contains(slots[0]) && luckySymbols.Contains(slots[1]) && luckySymbols.Contains(slots[2])) + return false; + + return true; + } + + #endregion + + #region Awards + + private void AwardJackpot(EntityUid machineUid, SlotMachineComponent comp, EntityUid user) + { + SpawnAward(machineUid, user, comp.JackpotPrize); + _audio.PlayPvs(comp.JackpotSound, machineUid); + _popup.PopupEntity(Loc.GetString("slot-machine-jackpot", ("prize", comp.JackpotPrize)), user, user); + + var name = Identity.Name(user, EntityManager); + _chat.DispatchGlobalAnnouncement(Loc.GetString("auto-announcements-jackpot", ("winner", name)), + Loc.GetString("auto-announcements-title"), true, colorOverride: Color.Turquoise); + } + + private void AwardBigWin(EntityUid machineUid, SlotMachineComponent comp, EntityUid user) + { + SpawnAward(machineUid, user, comp.BigWinPrize); + _popup.PopupEntity(Loc.GetString("slot-machine-bigwin", ("prize", comp.BigWinPrize)), user, user); + } + + private void AwardMediumWin(EntityUid machineUid, SlotMachineComponent comp, EntityUid user) + { + SpawnAward(machineUid, user, comp.MediumWinPrize); + _popup.PopupEntity(Loc.GetString("slot-medium-win", ("prize", comp.MediumWinPrize)), user, user); + } + + private void AwardSmallWin(EntityUid machineUid, SlotMachineComponent comp, EntityUid user) + { + SpawnAward(machineUid, user, comp.SmallWinPrize); + _popup.PopupEntity(Loc.GetString("slot-small-win", ("prize", comp.SmallWinPrize)), user, user); + } + + private void AwardTinyWin(EntityUid machineUid, SlotMachineComponent comp, EntityUid user) + { + SpawnAward(machineUid, user, comp.TinyWinPrize); + _popup.PopupEntity(Loc.GetString("slot-tiny-win", ("prize", comp.TinyWinPrize)), user, user); + } + + private void AwardCursedJackpot(EntityUid machineUid, EntityUid user, CursedSlotMachineComponent cursedComp) + { + var die = Spawn(Reward, Transform(machineUid).Coordinates); + _hands.TryPickupAnyHand(user, die); + + _audio.PlayPvs(cursedComp.JackpotSound, machineUid); + _popup.PopupEntity(Loc.GetString("cursed-slot-machine-jackpot", ("name", Name(user))), // He know who are you + machineUid, PopupType.LargeCaution); + + cursedComp.Uses = 5; // Win. Stop + Timer.Spawn(TimeSpan.FromSeconds(5), () => { _destructible.DestroyEntity(machineUid); }); + } + + private void AwardCursedLoss(EntityUid machineUid, SlotMachineComponent comp, EntityUid user, CursedSlotMachineComponent cursedComp) + { + cursedComp.Uses++; + _damage.TryChangeDamage(user, cursedComp.Damage, true); + + _audio.PlayPvs(comp.FailedSound, machineUid); + _popup.PopupEntity(Loc.GetString("cursed-slot-machine-lose"), user, user, PopupType.SmallCaution); + } + + private void SpawnAward(EntityUid machineUid, EntityUid user, int award) + { + var cash = Spawn(SpaceCash, Transform(machineUid).Coordinates); + _stack.SetCount((cash, null), award); + + _hands.TryPickupAnyHand(user, cash); + } + + #endregion + + public void FreeSpeen(Entity entity, EntityUid user) + { + if (!Resolve(entity.Owner, ref entity.Comp)) + return; + + StartSpin((entity.Owner, entity.Comp), user, HasComp(entity)); + } + + private void UpdateAppearance(Entity entity) + { + if (!Resolve(entity.Owner, ref entity.Comp)) + return; + + _appearance.SetData(entity, SlotMachineVisuals.Working, entity.Comp.Working); + } +} diff --git a/Content.Shared/Chat/ChatChannel.cs b/Content.Shared/Chat/ChatChannel.cs index faed544b82..7dfee5b4b0 100644 --- a/Content.Shared/Chat/ChatChannel.cs +++ b/Content.Shared/Chat/ChatChannel.cs @@ -65,25 +65,32 @@ namespace Content.Shared.Chat /// Dead = 1 << 10, + // Corvax-Wega-MindChat-start + /// + /// Mind chat + /// + Mind = 1 << 11, + // Corvax-Wega-MindChat-end + /// /// Misc admin messages /// - Admin = 1 << 11, + Admin = 1 << 12, // Corvax-Wega-Edit /// /// Admin alerts, messages likely of elevated importance to admins /// - AdminAlert = 1 << 12, + AdminAlert = 1 << 13, // Corvax-Wega-Edit /// /// Admin chat /// - AdminChat = 1 << 13, + AdminChat = 1 << 14, // Corvax-Wega-Edit /// /// Unspecified. /// - Unspecified = 1 << 14, + Unspecified = 1 << 15, // Corvax-Wega-Edit /// /// Channels considered to be IC. diff --git a/Content.Shared/Chat/ChatChannelExtensions.cs b/Content.Shared/Chat/ChatChannelExtensions.cs index 9b707a53a0..07b941ee79 100644 --- a/Content.Shared/Chat/ChatChannelExtensions.cs +++ b/Content.Shared/Chat/ChatChannelExtensions.cs @@ -11,6 +11,7 @@ public static class ChatChannelExtensions ChatChannel.LOOC => Color.MediumTurquoise, ChatChannel.OOC => Color.LightSkyBlue, ChatChannel.Dead => Color.MediumPurple, + ChatChannel.Mind => Color.Peru, // Corvax-Wega-MindChat ChatChannel.Admin => Color.Red, ChatChannel.AdminAlert => Color.Red, ChatChannel.AdminChat => Color.HotPink, diff --git a/Content.Shared/Chat/ChatSelectChannel.cs b/Content.Shared/Chat/ChatSelectChannel.cs index c18bb9b8ee..f8a5bdea3e 100644 --- a/Content.Shared/Chat/ChatSelectChannel.cs +++ b/Content.Shared/Chat/ChatSelectChannel.cs @@ -46,6 +46,13 @@ /// Dead = ChatChannel.Dead, + // Corvax-Wega-MindChat-start + /// + /// Mind chat + /// + Mind = ChatChannel.Mind, + // Corvax-Wega-MindChat-end + /// /// Admin chat /// diff --git a/Content.Shared/Chat/SharedChatSystem.cs b/Content.Shared/Chat/SharedChatSystem.cs index d458cc8236..1111656262 100644 --- a/Content.Shared/Chat/SharedChatSystem.cs +++ b/Content.Shared/Chat/SharedChatSystem.cs @@ -2,6 +2,7 @@ using System.Collections.Frozen; using System.Text.RegularExpressions; using Content.Shared.ActionBlocker; using Content.Shared.Chat.Prototypes; +using Content.Shared.Mind; // Corvax-Wega-MindChat using Content.Shared.Popups; using Content.Shared.Radio; using Content.Shared.Speech; @@ -31,11 +32,13 @@ public abstract partial class SharedChatSystem : EntitySystem public const char EmotesAltPrefix = '*'; public const char AdminPrefix = ']'; public const char WhisperPrefix = ','; + public const char MindPrefix = '+'; // Corvax-Wega-MindChat public const char DefaultChannelKey = 'ะฐ'; // Corvax-Wega-Edit // Corvax-TTS-Start: Moved from Server to Shared public const int VoiceRange = 10; // how far voice goes in world units public const int WhisperClearRange = 2; // how far whisper goes while still being understandable, in world units public const int WhisperMuffledRange = 5; // how far whisper goes at all, in world units + public const int MindChatRange = 1000; // Corvax-Wega-MindChat public static readonly SoundSpecifier DefaultAnnouncementSound = new SoundPathSpecifier("/Audio/Announcements/announce.ogg"); @@ -57,6 +60,8 @@ public abstract partial class SharedChatSystem : EntitySystem /// private FrozenDictionary _keyCodes = default!; + private FrozenDictionary _mindKeyCodes = default!; // Corvax-Wega-MindChat + public override void Initialize() { base.Initialize(); @@ -66,6 +71,7 @@ public abstract partial class SharedChatSystem : EntitySystem SubscribeLocalEvent(OnPrototypeReload); CacheRadios(); CacheEmotes(); + CacheMindChannels(); // Corvax-Wega-MindChat } protected virtual void OnPrototypeReload(PrototypesReloadedEventArgs obj) @@ -75,6 +81,11 @@ public abstract partial class SharedChatSystem : EntitySystem if (obj.WasModified()) CacheEmotes(); + + // Corvax-Wega-MindChat-start + if (obj.WasModified()) + CacheMindChannels(); + // Corvax-Wega-MindChat-end } private void CacheRadios() @@ -83,6 +94,14 @@ public abstract partial class SharedChatSystem : EntitySystem .ToFrozenDictionary(x => x.KeyCode); } + // Corvax-Wega-MindChat-start + private void CacheMindChannels() + { + _mindKeyCodes = _prototypeManager.EnumeratePrototypes() + .ToFrozenDictionary(x => x.KeyCode); + } + // Corvax-Wega-MindChat-end + /// /// Attempts to find an applicable for a speaking entity's message. /// If one is not found, returns . @@ -200,6 +219,42 @@ public abstract partial class SharedChatSystem : EntitySystem return true; } + // Corvax-Wega-MindChat-start + public bool TryProcessMindMessage( + EntityUid source, + string input, + out string output, + out MindChannelPrototype? channel, + bool quiet = false) + { + output = input.Trim(); + channel = null; + + if (input.Length == 0 || !input.StartsWith(MindPrefix)) + return false; + + if (input.Length < 2 || char.IsWhiteSpace(input[1])) + { + output = SanitizeMessageCapital(input[1..].TrimStart()); + if (!quiet) + _popup.PopupEntity(Loc.GetString("chat-manager-no-mind-key"), source, source); + return true; + } + + var channelKey = input[1]; + channelKey = char.ToLower(channelKey); + output = SanitizeMessageCapital(input[2..].TrimStart()); + + if (!_mindKeyCodes.TryGetValue(channelKey, out channel) && !quiet) + { + var msg = Loc.GetString("chat-manager-no-such-mind-channel", ("key", channelKey)); + _popup.PopupEntity(msg, source, source); + } + + return true; + } + // Corvax-Wega-MindChat-end + public string SanitizeMessageCapital(string message) { if (string.IsNullOrEmpty(message)) @@ -450,6 +505,15 @@ public abstract partial class SharedChatSystem : EntitySystem SoundSpecifier? announcementSound = null, Color? colorOverride = null) { } + + // Corvax-Wega-MindChat-start + public virtual void SendMindMessage( + EntityUid source, + string message, + MindChannelPrototype channel, + bool ignoreActionBlocker = false) + { } + // Corvax-Wega-MindChat-end } /// diff --git a/Content.Shared/Emp/SharedEmpSystem.cs b/Content.Shared/Emp/SharedEmpSystem.cs index 67cbfc42b1..f8f10e02ce 100644 --- a/Content.Shared/Emp/SharedEmpSystem.cs +++ b/Content.Shared/Emp/SharedEmpSystem.cs @@ -130,6 +130,7 @@ public abstract class SharedEmpSystem : EntitySystem { if (exclusionsSet.Contains(uid)) continue; + TryEmpEffects(uid, energyConsumption, TimeSpan.FromSeconds(duration)); } Spawn(EmpPulseEffectPrototype, coordinates); diff --git a/Content.Shared/_Wega/BloodCult/BloodCultComponents.cs b/Content.Shared/_Wega/BloodCult/BloodCultComponents.cs index 2e7f6b7401..70cc79cd7a 100644 --- a/Content.Shared/_Wega/BloodCult/BloodCultComponents.cs +++ b/Content.Shared/_Wega/BloodCult/BloodCultComponents.cs @@ -3,35 +3,27 @@ using Robust.Shared.Audio; using Robust.Shared.Containers; using Robust.Shared.GameStates; using Robust.Shared.Prototypes; -using Robust.Shared.Serialization; namespace Content.Shared.Blood.Cult.Components; -[RegisterComponent, NetworkedComponent, AutoGenerateComponentState] +[RegisterComponent, NetworkedComponent] public sealed partial class BloodCultistComponent : Component { public bool BloodMagicActive = false; - public EntityUid? SelectedSpell { get; set; } + [DataField] public EntityUid? SelectedSpell { get; set; } - public List SelectedEmpoweringSpells = new(); + [DataField] public List SelectedEmpoweringSpells = new(); - [DataField, AutoNetworkedField] - public EntityUid? RecallDaggerActionEntity; + [DataField] public EntityUid? RecallDaggerActionEntity; - public EntityUid? RecallSpearAction { get; set; } + [DataField] public EntityUid? RecallSpearAction { get; set; } - [DataField, AutoNetworkedField] - public EntityUid? RecallSpearActionEntity; + [DataField] public EntityUid? RecallSpearActionEntity; [DataField] public int BloodCount = 5; - [DataField] - public int Empowering = 0; - - public static readonly EntProtoId CultObjective = "ActionBloodCultObjective"; - public static readonly EntProtoId CultCommunication = "ActionBloodCultComms"; public static readonly EntProtoId BloodMagic = "ActionBloodMagic"; public static readonly EntProtoId RecallBloodDagger = "ActionRecallBloodDagger"; public static readonly EntProtoId RecallBloodSpear = "RecallBloodCultSpear"; @@ -40,14 +32,17 @@ public sealed partial class BloodCultistComponent : Component public ProtoId StatusIcon { get; set; } = "BloodCultistFaction"; } +[RegisterComponent] +public sealed partial class AutoCultistComponent : Component; + [RegisterComponent, NetworkedComponent] public sealed partial class ShowCultistIconsComponent : Component; [RegisterComponent] -public sealed partial class AutoCultistComponent : Component; +public sealed partial class BloodCultObjectComponent : Component; [RegisterComponent] -public sealed partial class BloodCultObjectComponent : Component; +public sealed partial class BloodCultWeaponComponent : Component; [RegisterComponent] public sealed partial class BloodDaggerComponent : Component @@ -59,15 +54,21 @@ public sealed partial class BloodDaggerComponent : Component [RegisterComponent] public sealed partial class BloodSpellComponent : Component { - [DataField] - public List Prototype = new(); + [DataField(required: true)] + public BloodCultSpell SpellType = default!; } [RegisterComponent] public sealed partial class BloodRuneComponent : Component { + [DataField(required: true)] + public BloodCultRune RuneType = default!; + [DataField] - public string Prototype = default!; + public string Desc { get; private set; } = string.Empty; + + [ViewVariables(VVAccess.ReadOnly)] + public string LocDesc => Loc.GetString(Desc); public bool IsActive = true; @@ -83,13 +84,18 @@ public sealed partial class BloodRitualDimensionalRendingComponent : Component public bool Activate = false; public float NextTimeTick { get; set; } + + [DataField("ritualMusic")] + public SoundSpecifier RitualMusic = new SoundCollectionSpecifier("BloodCultMusic"); + + public bool SoundPlayed; } [RegisterComponent, NetworkedComponent] public sealed partial class BloodStructureComponent : Component { [DataField("structureGear")] - public List StructureGear { get; private set; } = new(); + public List StructureGear { get; private set; } = new(); [ViewVariables(VVAccess.ReadOnly), DataField] public TimeSpan ActivateTime = TimeSpan.Zero; @@ -100,9 +106,6 @@ public sealed partial class BloodStructureComponent : Component [DataField] public SoundSpecifier? Sound { get; private set; } - [DataField] - public bool CanInteract = true; - public bool IsActive = true; } @@ -112,6 +115,12 @@ public sealed partial class BloodPylonComponent : Component public float NextTimeTick { get; set; } } +[RegisterComponent] +public sealed partial class BloodShieldActivaebleComponent : Component +{ + public string CurrentSlot = "outerClothing"; +} + [RegisterComponent] public sealed partial class BloodOrbComponent : Component { @@ -122,7 +131,7 @@ public sealed partial class BloodOrbComponent : Component public sealed partial class StoneSoulComponent : Component { [DataField("soulProto", required: true)] - public string SoulProto { get; set; } = default!; + public EntProtoId SoulProto { get; set; } = default!; public EntityUid? SoulEntity; @@ -138,6 +147,9 @@ public sealed partial class ConstructComponent : Component; [RegisterComponent, NetworkedComponent] public sealed partial class BloodCultConstructComponent : Component; +[RegisterComponent, NetworkedComponent] +public sealed partial class BloodCultGhostComponent : Component; + [RegisterComponent, NetworkedComponent] public sealed partial class BloodShuttleCurseComponent : Component; @@ -155,26 +167,7 @@ public sealed partial class BloodSharpenerComponent : Component; /// ะ—ะฐะณะปัƒัˆะบะฐ ะดะปั ะปะพะณะธะบะธ /// [RegisterComponent] -public sealed partial class CultistEyesComponent : Component; +public sealed partial class BloodCultistEyesComponent : Component; [RegisterComponent, NetworkedComponent] -public sealed partial class PentagramDisplayComponent : Component; - -[Serializable, NetSerializable] -public enum RuneColorVisuals -{ - Color -} - -[Serializable, NetSerializable] -public enum StoneSoulVisualLayers : byte -{ - Base, - Soul -} - -[Serializable, NetSerializable] -public enum StoneSoulVisuals : byte -{ - HasSoul -} +public sealed partial class BloodPentagramDisplayComponent : Component; diff --git a/Content.Shared/_Wega/BloodCult/BloodCultEnums.cs b/Content.Shared/_Wega/BloodCult/BloodCultEnums.cs new file mode 100644 index 0000000000..a64ad810e3 --- /dev/null +++ b/Content.Shared/_Wega/BloodCult/BloodCultEnums.cs @@ -0,0 +1,113 @@ +using Content.Shared.Eui; +using Robust.Shared.Prototypes; +using Robust.Shared.Serialization; + +namespace Content.Shared.Blood.Cult; + +[Serializable, NetSerializable] +public enum BloodCultGod : byte +{ + NarSi, + Reaper, + Kharin +} + +[Serializable, NetSerializable] +public enum BloodCultSpell : byte +{ + Stun, + Teleport, + ShadowShackles, + TwistedConstruction, + SummonEquipment, + BloodRites +} + +[Serializable, NetSerializable] +public enum BloodCultRune : byte +{ + Offering, + Teleport, + Empowering, + Revive, + Barrier, + Summoning, + Bloodboil, + Spiritrealm, + Ritual, + Default +} + +[Serializable, NetSerializable] +public enum RuneColorVisuals : byte +{ + Color +} + +[Serializable, NetSerializable] +public enum StoneSoulVisuals : byte +{ + HasSoul +} + +[Serializable, NetSerializable] +public enum VeilShifterVisuals : byte +{ + Charged +} + +[Serializable, NetSerializable] +public sealed class BloodMagicState : EuiStateBase +{ +} + +[Serializable, NetSerializable] +public enum BloodRitesUiKey : byte +{ + Key +} + +[Serializable, NetSerializable] +public enum BloodConstructUiKey : byte +{ + Key +} + +[Serializable, NetSerializable] +public enum BloodStructureUiKey : byte +{ + Key +} + +[Serializable, NetSerializable] +public sealed class BloodStructureBoundUserInterfaceState : BoundUserInterfaceState +{ + public readonly List Items; + public BloodStructureBoundUserInterfaceState(List items) + { + Items = items; + } +} + +[Serializable, NetSerializable] +public enum BloodRunesUiKey : byte +{ + Key +} + +[Serializable, NetSerializable] +public sealed class BloodRitualBoundUserInterfaceState : BoundUserInterfaceState +{ +} + +[Serializable, NetSerializable] +public enum EmpoweringRuneUiKey : byte +{ + Key +} + +[Serializable, NetSerializable] +public enum SummoningRuneUiKey : byte +{ + Key +} diff --git a/Content.Shared/_Wega/BloodCult/BloodCultEvents.cs b/Content.Shared/_Wega/BloodCult/BloodCultEvents.cs index 69646fe750..8a744fe916 100644 --- a/Content.Shared/_Wega/BloodCult/BloodCultEvents.cs +++ b/Content.Shared/_Wega/BloodCult/BloodCultEvents.cs @@ -1,5 +1,7 @@ using Content.Shared.Actions; using Content.Shared.DoAfter; +using Content.Shared.Eui; +using Robust.Shared.Prototypes; using Robust.Shared.Serialization; namespace Content.Shared.Blood.Cult; @@ -14,35 +16,83 @@ public sealed class RitualConductedEvent : EntityEventArgs } [Serializable, NetSerializable] -public sealed class BloodMagicPressedEvent : EntityEventArgs +public sealed class BloodMagicSelectSpellMessage(EntProtoId spell) : EuiMessageBase { - public NetEntity Uid { get; } + public readonly EntProtoId Spell = spell; +} - public BloodMagicPressedEvent(NetEntity uid) +[Serializable, NetSerializable] +public sealed class BloodRitesSelectRitesMessage : BoundUserInterfaceMessage +{ + public EntProtoId Rites { get; } + + public BloodRitesSelectRitesMessage(EntProtoId rites) { - Uid = uid; + Rites = rites; } } [Serializable, NetSerializable] -public sealed class BloodMagicMenuClosedEvent : EntityEventArgs +public sealed class BloodConstructSelectMessage : BoundUserInterfaceMessage { - public NetEntity Uid { get; } - public string SelectedSpell { get; } + public EntProtoId Construct { get; } - public BloodMagicMenuClosedEvent(NetEntity uid, string selectedSpell) + public BloodConstructSelectMessage(EntProtoId construct) { - Uid = uid; - SelectedSpell = selectedSpell; + Construct = construct; + } +} + +[Serializable, NetSerializable] +public sealed class BloodStructureSelectMessage : BoundUserInterfaceMessage +{ + public EntProtoId Item { get; } + + public BloodStructureSelectMessage(EntProtoId item) + { + Item = item; + } +} + +[Serializable, NetSerializable] +public sealed class SelectBloodRuneMessage : BoundUserInterfaceMessage +{ + public EntProtoId RuneProtoId { get; } + + public SelectBloodRuneMessage(EntProtoId runeProtoId) + { + RuneProtoId = runeProtoId; + } +} + +[Serializable, NetSerializable] +public sealed class EmpoweringRuneSelectSpellMessage : BoundUserInterfaceMessage +{ + public EntProtoId Spell { get; } + + public EmpoweringRuneSelectSpellMessage(EntProtoId spell) + { + Spell = spell; + } +} + +[Serializable, NetSerializable] +public sealed class SummoningRuneSelectCultistMessage : BoundUserInterfaceMessage +{ + public NetEntity CultistUid { get; } + + public SummoningRuneSelectCultistMessage(NetEntity cultistUid) + { + CultistUid = cultistUid; } } [Serializable, NetSerializable] public sealed partial class BloodMagicDoAfterEvent : SimpleDoAfterEvent { - public string SelectedSpell { get; } + public EntProtoId SelectedSpell { get; } - public BloodMagicDoAfterEvent(string selectedSpell) + public BloodMagicDoAfterEvent(EntProtoId selectedSpell) { SelectedSpell = selectedSpell; } @@ -53,89 +103,17 @@ public sealed partial class TeleportSpellDoAfterEvent : SimpleDoAfterEvent { } -[Serializable, NetSerializable] -public sealed class EmpoweringRuneMenuOpenedEvent : EntityEventArgs -{ - public NetEntity Uid { get; } - - public EmpoweringRuneMenuOpenedEvent(NetEntity uid) - { - Uid = uid; - } -} - -[Serializable, NetSerializable] -public sealed class EmpoweringRuneMenuClosedEvent : EntityEventArgs -{ - public NetEntity Uid { get; } - public string SelectedSpell { get; } - - public EmpoweringRuneMenuClosedEvent(NetEntity uid, string selectedSpell) - { - Uid = uid; - SelectedSpell = selectedSpell; - } -} - [Serializable, NetSerializable] public sealed partial class EmpoweringDoAfterEvent : SimpleDoAfterEvent { - public string SelectedSpell { get; } + public EntProtoId SelectedSpell { get; } - public EmpoweringDoAfterEvent(string selectedSpell) + public EmpoweringDoAfterEvent(EntProtoId selectedSpell) { SelectedSpell = selectedSpell; } } -[Serializable, NetSerializable] -public sealed class BloodRitesPressedEvent : EntityEventArgs -{ - public NetEntity Uid { get; } - - public BloodRitesPressedEvent(NetEntity uid) - { - Uid = uid; - } -} - -[Serializable, NetSerializable] -public sealed class BloodRitesMenuClosedEvent : EntityEventArgs -{ - public NetEntity Uid { get; } - public string SelectedRites { get; } - - public BloodRitesMenuClosedEvent(NetEntity uid, string selectedRites) - { - Uid = uid; - SelectedRites = selectedRites; - } -} - -[Serializable, NetSerializable] -public sealed class RunesMenuOpenedEvent : EntityEventArgs -{ - public NetEntity Uid { get; } - - public RunesMenuOpenedEvent(NetEntity uid) - { - Uid = uid; - } -} - -[Serializable, NetSerializable] -public sealed class RuneSelectEvent : EntityEventArgs -{ - public NetEntity Uid { get; } - public string RuneProto { get; } - - public RuneSelectEvent(NetEntity uid, string runeProto) - { - Uid = uid; - RuneProto = runeProto; - } -} - [Serializable, NetSerializable] public sealed partial class BloodRuneDoAfterEvent : SimpleDoAfterEvent { @@ -154,99 +132,7 @@ public sealed partial class BloodRuneCleaningDoAfterEvent : SimpleDoAfterEvent { } -[Serializable, NetSerializable] -public sealed class SummoningRuneMenuOpenedEvent : EntityEventArgs -{ - public NetEntity Uid { get; } - - public SummoningRuneMenuOpenedEvent(NetEntity uid) - { - Uid = uid; - } -} - -[Serializable, NetSerializable] -public sealed class SummoningSelectedEvent : EntityEventArgs -{ - public NetEntity User { get; } - public NetEntity Target { get; } - - public SummoningSelectedEvent(NetEntity user, NetEntity target) - { - User = user; - Target = target; - } -} - -[Serializable, NetSerializable] -public sealed class OpenConstructMenuEvent : EntityEventArgs -{ - public NetEntity Uid { get; } - public NetEntity ConstructUid { get; } - public NetEntity Mind { get; } - - public OpenConstructMenuEvent(NetEntity uid, NetEntity constructUid, NetEntity mind) - { - Uid = uid; - ConstructUid = constructUid; - Mind = mind; - } -} - -[Serializable, NetSerializable] -public sealed class BloodConstructMenuClosedEvent : EntityEventArgs -{ - public NetEntity Uid { get; } - public NetEntity ConstructUid { get; } - public NetEntity Mind { get; } - public string ConstructProto { get; } - - public BloodConstructMenuClosedEvent(NetEntity uid, NetEntity constructUid, NetEntity mind, string constructProto) - { - Uid = uid; - ConstructUid = constructUid; - Mind = mind; - ConstructProto = constructProto; - } -} - -[Serializable, NetSerializable] -public sealed class OpenStructureMenuEvent : EntityEventArgs -{ - public NetEntity Uid { get; } - public NetEntity Structure { get; } - - public OpenStructureMenuEvent(NetEntity uid, NetEntity structure) - { - Uid = uid; - Structure = structure; - } -} - -[Serializable, NetSerializable] -public sealed class BloodStructureMenuClosedEvent : EntityEventArgs -{ - public NetEntity Uid { get; } - public string Item { get; } - public NetEntity Structure { get; } - - public BloodStructureMenuClosedEvent(NetEntity uid, string item, NetEntity structure) - { - Uid = uid; - Item = item; - Structure = structure; - } -} - // Abilities -public sealed partial class BloodCultObjectiveActionEvent : InstantActionEvent -{ -} - -public sealed partial class BloodCultCommuneActionEvent : InstantActionEvent -{ -} - public sealed partial class BloodCultBloodMagicActionEvent : InstantActionEvent { } @@ -282,7 +168,6 @@ public sealed partial class RecallBloodDaggerEvent : InstantActionEvent { } - public sealed partial class BloodCultHallucinationsActionEvent : EntityTargetActionEvent { } diff --git a/Content.Shared/_Wega/BloodCult/SharedBloodCultSystem.cs b/Content.Shared/_Wega/BloodCult/SharedBloodCultSystem.cs index 55aab5bddc..bbf8f7b10a 100644 --- a/Content.Shared/_Wega/BloodCult/SharedBloodCultSystem.cs +++ b/Content.Shared/_Wega/BloodCult/SharedBloodCultSystem.cs @@ -28,9 +28,7 @@ public abstract class SharedBloodCultSystem : EntitySystem continue; var protoId = meta.EntityPrototype?.ID; - if (protoId == BloodCultistComponent.CultObjective.Id - || protoId == BloodCultistComponent.CultCommunication.Id - || protoId == BloodCultistComponent.BloodMagic.Id + if (protoId == BloodCultistComponent.BloodMagic.Id || protoId == BloodCultistComponent.RecallBloodDagger.Id) { _action.RemoveAction(cultist, actionId); @@ -38,29 +36,18 @@ public abstract class SharedBloodCultSystem : EntitySystem } } - if (bloodCultist.RecallSpearActionEntity != null) - _action.RemoveAction(cultist, bloodCultist.RecallSpearActionEntity); - - if (bloodCultist.SelectedSpell != null) - _action.RemoveAction(cultist, bloodCultist.SelectedSpell.Value); + _action.RemoveAction(cultist, bloodCultist.RecallSpearActionEntity); + _action.RemoveAction(cultist, bloodCultist.SelectedSpell); foreach (var spell in bloodCultist.SelectedEmpoweringSpells) - { - if (spell != null) - { - _action.RemoveAction(cultist, spell.Value); - } - } + _action.RemoveAction(cultist, spell); - var stunTime = TimeSpan.FromSeconds(4); - var name = Identity.Entity(cultist, EntityManager); - - _stun.TryKnockdown(cultist, stunTime, true); - _popup.PopupEntity(Loc.GetString("blood-cult-break-control", ("name", name)), cultist); + _stun.TryKnockdown(cultist, TimeSpan.FromSeconds(4), true); + _popup.PopupEntity(Loc.GetString("blood-cult-break-control", ("name", Identity.Entity(cultist, EntityManager))), cultist); RemComp(cultist); - if (HasComp(cultist)) RemComp(cultist); - if (HasComp(cultist)) RemComp(cultist); + RemComp(cultist); + RemComp(cultist); } #endregion -} \ No newline at end of file +} diff --git a/Content.Shared/_Wega/CardTarot/CardTarotComponent.cs b/Content.Shared/_Wega/CardTarot/CardTarotComponent.cs new file mode 100644 index 0000000000..54fa90c3ac --- /dev/null +++ b/Content.Shared/_Wega/CardTarot/CardTarotComponent.cs @@ -0,0 +1,16 @@ +using Robust.Shared.Audio; +using Robust.Shared.GameStates; + +namespace Content.Shared.Card.Tarot.Components; + +[RegisterComponent, NetworkedComponent] +public sealed partial class CardTarotComponent : Component +{ + [DataField(required: true)] + public CardTarot Card = CardTarot.NotEnchanted; + + [DataField] + public CardTarotType CardType = CardTarotType.Normal; + + public SoundSpecifier UseSound = new SoundPathSpecifier("/Audio/Effects/lightburn.ogg"); +} diff --git a/Content.Shared/_Wega/CardTarot/CardTarotEnums.cs b/Content.Shared/_Wega/CardTarot/CardTarotEnums.cs new file mode 100644 index 0000000000..f003576ff2 --- /dev/null +++ b/Content.Shared/_Wega/CardTarot/CardTarotEnums.cs @@ -0,0 +1,45 @@ +using Robust.Shared.Serialization; + +namespace Content.Shared.Card.Tarot; + +[Serializable, NetSerializable] +public enum CardTarotType : byte +{ + Normal, + Reversed +} + +[Serializable, NetSerializable] +public enum CardTarot : byte +{ + NotEnchanted, + Fool, + Magician, + HighPriestess, + Empress, + Emperor, + Hierophant, + Lovers, + Chariot, + Justice, + Hermit, + WheelOfFortune, + Strength, + HangedMan, + Death, + Temperance, + Devil, + Tower, + Stars, + Moon, + Sun, + Judgement, + TheWorld // !!!! +} + +[Serializable, NetSerializable] +public enum CardTarotVisuals : byte +{ + State, + Reversed +} diff --git a/Content.Shared/_Wega/EnergyShield/EnergyShieldComponent.cs b/Content.Shared/_Wega/EnergyShield/EnergyShieldComponent.cs new file mode 100644 index 0000000000..405bfdd88d --- /dev/null +++ b/Content.Shared/_Wega/EnergyShield/EnergyShieldComponent.cs @@ -0,0 +1,11 @@ +namespace Content.Shared.EnergyShield; + +[RegisterComponent] +public sealed partial class EnergyShieldOwnerComponent : Component +{ + [DataField] + public EntityUid? ShieldEntity = null; + + [DataField] + public int SustainingCount = 3; +} diff --git a/Content.Shared/_Wega/EnergyShield/EnergyShieldSystem.cs b/Content.Shared/_Wega/EnergyShield/EnergyShieldSystem.cs new file mode 100644 index 0000000000..8e14827680 --- /dev/null +++ b/Content.Shared/_Wega/EnergyShield/EnergyShieldSystem.cs @@ -0,0 +1,58 @@ +using Content.Shared.Projectiles; +using Content.Shared.Weapons.Melee; +using Content.Shared.Weapons.Melee.Events; + +namespace Content.Shared.EnergyShield; + +public sealed partial class EnergyShieldSystem : EntitySystem +{ + [Dependency] private readonly SharedMeleeWeaponSystem _meleeWeapon = default!; + + public override void Initialize() + { + base.Initialize(); + + SubscribeLocalEvent(OnAttacked); + SubscribeLocalEvent(OnProjectileAttemptE); + } + + private void OnAttacked(Entity ent, ref AttackedEvent args) + { + if (ent.Comp.ShieldEntity == null || ent.Comp.SustainingCount <= 0) + { + RemCompDeferred(ent.Owner, ent.Comp); + QueueDel(ent.Comp.ShieldEntity); + return; + } + + ent.Comp.SustainingCount--; + var damage = _meleeWeapon.GetDamage(args.Used, args.User); + args.BonusDamage = -damage; + + if (ent.Comp.SustainingCount <= 0) + { + QueueDel(ent.Comp.ShieldEntity); + RemCompDeferred(ent.Owner, ent.Comp); + } + } + + private void OnProjectileAttemptE(Entity ent, ref ProjectileReflectAttemptEvent args) + { + if (ent.Comp.ShieldEntity == null || ent.Comp.SustainingCount <= 0) + { + RemCompDeferred(ent.Owner, ent.Comp); + QueueDel(ent.Comp.ShieldEntity); + return; + } + + ent.Comp.SustainingCount--; + args.Cancelled = true; + QueueDel(args.ProjUid); + + if (ent.Comp.SustainingCount <= 0) + { + QueueDel(ent.Comp.ShieldEntity); + RemCompDeferred(ent.Owner, ent.Comp); + } + } +} diff --git a/Content.Shared/_Wega/Mind/MindChannelPrototype.cs b/Content.Shared/_Wega/Mind/MindChannelPrototype.cs new file mode 100644 index 0000000000..9e5637dc31 --- /dev/null +++ b/Content.Shared/_Wega/Mind/MindChannelPrototype.cs @@ -0,0 +1,28 @@ +using Robust.Shared.Prototypes; + +namespace Content.Shared.Mind; + +[Prototype("mindChannel")] +public sealed partial class MindChannelPrototype : IPrototype +{ + /// + /// Human-readable name for the channel. + /// + [DataField("name")] + public LocId Name { get; private set; } = string.Empty; + + [ViewVariables(VVAccess.ReadOnly)] + public string LocalizedName => Loc.GetString(Name); + + /// + /// Single-character prefix to determine what channel a message should be sent to. + /// + [DataField("keycode")] + public char KeyCode { get; private set; } = '\0'; + + [DataField("color")] + public Color Color { get; private set; } = Color.Peru; + + [IdDataField, ViewVariables] + public string ID { get; private set; } = default!; +} diff --git a/Content.Shared/_Wega/Mind/MindLinkComponents.cs b/Content.Shared/_Wega/Mind/MindLinkComponents.cs new file mode 100644 index 0000000000..89430b0805 --- /dev/null +++ b/Content.Shared/_Wega/Mind/MindLinkComponents.cs @@ -0,0 +1,17 @@ +using Robust.Shared.GameStates; +using Robust.Shared.Prototypes; + +namespace Content.Shared.Mind; + +[RegisterComponent, NetworkedComponent, AutoGenerateComponentState] +public sealed partial class MindLinkComponent : Component +{ + /// + /// Available mind channels for this entity + /// + [DataField, AutoNetworkedField] + public HashSet> Channels = new(); +} + +[RegisterComponent] +public sealed partial class AdminMindLinkListenerComponent : Component; diff --git a/Content.Shared/_Wega/SlotMachine/SlotMachineComponents.cs b/Content.Shared/_Wega/SlotMachine/SlotMachineComponents.cs new file mode 100644 index 0000000000..ce7ff8387f --- /dev/null +++ b/Content.Shared/_Wega/SlotMachine/SlotMachineComponents.cs @@ -0,0 +1,62 @@ +using Content.Shared.Damage; +using Robust.Shared.Audio; +using Robust.Shared.Serialization; + +namespace Content.Shared.Economy.SlotMachine; + +[RegisterComponent] +public sealed partial class SlotMachineComponent : Component +{ + [DataField, ViewVariables(VVAccess.ReadOnly)] + public bool Working = false; + + [DataField, ViewVariables(VVAccess.ReadOnly)] + public int Plays = 0; + + [DataField] + public string[] Slots = { "?", "?", "?" }; + + [DataField] + public int SpinCost = 10; + + [DataField] + public EntityUid? User; + + public TimeSpan? SpinFinishTime; + + [ViewVariables(VVAccess.ReadOnly)] public int JackpotPrize = 50000; + [ViewVariables(VVAccess.ReadOnly)] public int BigWinPrize = 2500; + [ViewVariables(VVAccess.ReadOnly)] public int MediumWinPrize = 1250; + [ViewVariables(VVAccess.ReadOnly)] public int SmallWinPrize = 50; + [ViewVariables(VVAccess.ReadOnly)] public int TinyWinPrize = 10; + + // Sounds + public SoundSpecifier CoinSound = new SoundCollectionSpecifier("CoinDrop"); + public SoundSpecifier RollSound = new SoundPathSpecifier("/Audio/_Wega/Machines/Roulette/roulettewheel.ogg"); + public SoundSpecifier EndSound = new SoundPathSpecifier("/Audio/_Wega/Machines/Roulette/ding_short.ogg"); + public SoundSpecifier JackpotSound = new SoundPathSpecifier("/Audio/_Wega/Machines/Roulette/roulettejackpot.ogg"); + public SoundSpecifier FailedSound = new SoundPathSpecifier("/Audio/Effects/Cargo/buzz_sigh.ogg"); +} + +[RegisterComponent] +public sealed partial class CursedSlotMachineComponent : Component +{ + [DataField] public int Uses = 0; + [DataField] public int MaxUses = 5; + + [ViewVariables(VVAccess.ReadOnly)] + public DamageSpecifier Damage = new DamageSpecifier() + { + DamageDict = { ["Blunt"] = 10, ["Heat"] = 10 } + }; + + // Sounds + public SoundSpecifier RollSound = new SoundPathSpecifier("/Audio/_Wega/Machines/Roulette/cursed.ogg"); + public SoundSpecifier JackpotSound = new SoundPathSpecifier("/Audio/_Wega/Machines/Roulette/cursed_jackpot.ogg"); +} + +[Serializable, NetSerializable] +public enum SlotMachineVisuals : byte +{ + Working +} diff --git a/Resources/Audio/_Wega/Effects/cult_spell.ogg b/Resources/Audio/_Wega/Effects/cult_spell.ogg new file mode 100644 index 0000000000..47e08be0e8 Binary files /dev/null and b/Resources/Audio/_Wega/Effects/cult_spell.ogg differ diff --git a/Resources/Audio/_Wega/Machines/Roulette/attributions.yml b/Resources/Audio/_Wega/Machines/Roulette/attributions.yml new file mode 100644 index 0000000000..73c12f0055 --- /dev/null +++ b/Resources/Audio/_Wega/Machines/Roulette/attributions.yml @@ -0,0 +1,4 @@ +- files: ["coindrop.ogg", "coindrop2.ogg", "cursed_jackpot.ogg", "cursed.ogg", "ding_short.ogg", "roulettejackpot.ogg", "roulettewheel.ogg"] + license: "CC-BY-SA-3.0" + copyright: "Taken from TG Station" + source: "https://github.com/tgstation/tgstation" diff --git a/Resources/Audio/_Wega/Machines/Roulette/coindrop.ogg b/Resources/Audio/_Wega/Machines/Roulette/coindrop.ogg new file mode 100644 index 0000000000..dd79f980d3 Binary files /dev/null and b/Resources/Audio/_Wega/Machines/Roulette/coindrop.ogg differ diff --git a/Resources/Audio/_Wega/Machines/Roulette/coindrop2.ogg b/Resources/Audio/_Wega/Machines/Roulette/coindrop2.ogg new file mode 100644 index 0000000000..451b97079f Binary files /dev/null and b/Resources/Audio/_Wega/Machines/Roulette/coindrop2.ogg differ diff --git a/Resources/Audio/_Wega/Machines/Roulette/cursed.ogg b/Resources/Audio/_Wega/Machines/Roulette/cursed.ogg new file mode 100644 index 0000000000..a54666d338 Binary files /dev/null and b/Resources/Audio/_Wega/Machines/Roulette/cursed.ogg differ diff --git a/Resources/Audio/_Wega/Machines/Roulette/cursed_jackpot.ogg b/Resources/Audio/_Wega/Machines/Roulette/cursed_jackpot.ogg new file mode 100644 index 0000000000..ba4eb98455 Binary files /dev/null and b/Resources/Audio/_Wega/Machines/Roulette/cursed_jackpot.ogg differ diff --git a/Resources/Audio/_Wega/Machines/Roulette/ding_short.ogg b/Resources/Audio/_Wega/Machines/Roulette/ding_short.ogg new file mode 100644 index 0000000000..50987592b6 Binary files /dev/null and b/Resources/Audio/_Wega/Machines/Roulette/ding_short.ogg differ diff --git a/Resources/Audio/_Wega/Machines/Roulette/roulettejackpot.ogg b/Resources/Audio/_Wega/Machines/Roulette/roulettejackpot.ogg new file mode 100644 index 0000000000..5d7caab30d Binary files /dev/null and b/Resources/Audio/_Wega/Machines/Roulette/roulettejackpot.ogg differ diff --git a/Resources/Audio/_Wega/Machines/Roulette/roulettewheel.ogg b/Resources/Audio/_Wega/Machines/Roulette/roulettewheel.ogg new file mode 100644 index 0000000000..f0fe0053ac Binary files /dev/null and b/Resources/Audio/_Wega/Machines/Roulette/roulettewheel.ogg differ diff --git a/Resources/Audio/_Wega/StationEvents/attributions.yml b/Resources/Audio/_Wega/StationEvents/attributions.yml new file mode 100644 index 0000000000..fe41fe1717 --- /dev/null +++ b/Resources/Audio/_Wega/StationEvents/attributions.yml @@ -0,0 +1,4 @@ +- files: ["tear_of_veil.ogg"] + license: "CC-BY-3.0" + copyright: "Created by Bolgarich" + source: "https://www.youtube.com/watch?v=NqNHKfTAvcw" diff --git a/Resources/Audio/_Wega/StationEvents/tear_of_veil.ogg b/Resources/Audio/_Wega/StationEvents/tear_of_veil.ogg new file mode 100644 index 0000000000..3005c560f4 Binary files /dev/null and b/Resources/Audio/_Wega/StationEvents/tear_of_veil.ogg differ diff --git a/Resources/Locale/ru-RU/_wega/announcements/auto-announcements.ftl b/Resources/Locale/ru-RU/_wega/announcements/auto-announcements.ftl index ae4f7b8cb2..fb978af622 100644 --- a/Resources/Locale/ru-RU/_wega/announcements/auto-announcements.ftl +++ b/Resources/Locale/ru-RU/_wega/announcements/auto-announcements.ftl @@ -20,3 +20,4 @@ auto-announcements-holiday-mode = ะœั‹ ะฝะฐะดะตะตะผัั, ั‡ั‚ะพ ัั‚ะพ ะดะพะฑะฐะฒะธั‚ ะฝะฐัั‚ั€ะพะตะฝะธั ะธ ัะดะตะปะฐะตั‚ ะฒะฐัˆะต ะฟั€ะตะฑั‹ะฒะฐะฝะธะต ะฝะฐ ัั‚ะฐะฝั†ะธะธ ะตั‰ะต ะฑะพะปะตะต ะบะพะผั„ะพั€ั‚ะฝั‹ะผ. ะ•ัะปะธ ัƒ ะฒะฐั ะฒะพะทะฝะธะบะฝัƒั‚ ะฒะพะฟั€ะพัั‹ ะธะปะธ ะฟั€ะตะดะปะพะถะตะฝะธั, ะฟะพะถะฐะปัƒะนัั‚ะฐ, ัะพะพะฑั‰ะธั‚ะต ะพะฑ ัั‚ะพะผ ั‡ะตั€ะตะท ัะปัƒะถะฑัƒ ะฟะพะดะดะตั€ะถะบะธ. ะก ัƒะฒะฐะถะตะฝะธะตะผ, ะะฒั‚ะพะผะฐั‚ะธั‡ะตัะบะฐั ัะธัั‚ะตะผะฐ "ะขะตะบะฝะฐ" +auto-announcements-jackpot = ะŸะžะ—ะ”ะ ะะ’ะ›ะฏะ•ะœ! {$winner} ะฒั‹ะธะณั€ะฐะป ะ”ะ–ะ•ะšะŸะžะข ะฒ ะธะณั€ะพะฒะพะผ ะฐะฒั‚ะพะผะฐั‚ะต! diff --git a/Resources/Locale/ru-RU/_wega/bloodcult/bloodcult.ftl b/Resources/Locale/ru-RU/_wega/bloodcult/bloodcult.ftl index 1e50c36dc8..68b5eefe3e 100644 --- a/Resources/Locale/ru-RU/_wega/bloodcult/bloodcult.ftl +++ b/Resources/Locale/ru-RU/_wega/bloodcult/bloodcult.ftl @@ -20,26 +20,30 @@ select-blood-recharge = ะšั€ะพะฒะฐะฒะฐั ะŸะตั€ะตะทะฐั€ัะดะบะฐ select-blood-spear = ะšั€ะพะฒะฐะฒะพะต ะšะพะฟัŒะต select-blood-bolt-barrage = ะšั€ะพะฒะฐะฒั‹ะน ะจะบะฒะฐะป ะ‘ะพะปั‚ะพะฒ -runes-ui-default-title = ะ ัƒะฝั‹ offering-rune = ะ ัƒะฝะฐ ะŸั€ะตะดะปะพะถะตะฝะธั +offering-rune-desc = ะšะพะฝะฒะตั€ั‚ะธั€ัƒะตั‚ ะฝะพั€ะผะฐะปัŒะฝะพะณะพ ั‡ะปะตะฝะฐ ัะบะธะฟะฐะถะฐ ะฒ ะบัƒะปัŒั‚ะธัั‚ะฐ, ะธัั†ะตะปัั ะตะณะพ ะพั‚ ั„ะธะทะธั‡ะตัะบะธั… ะธ ะพะถะพะณะพะฒั‹ั… ะฟะพะฒั€ะตะถะดะตะฝะธะน. ะขะฐะบะถะต ัะพะทะดะฐั‘ั‚ ั€ะธั‚ัƒะฐะปัŒะฝั‹ะน ะบะธะฝะถะฐะป. ะ•ัะปะธ ั†ะตะปัŒ ะผะตั€ั‚ะฒะฐ, ะฝะตะฟั€ะธะณะพะดะฝะฐ ะดะปั ะบะพะฝะฒะตั€ั‚ะฐั†ะธะธ ะธะปะธ ะธะผะตะตั‚ ะธะผะฟะปะฐะฝั‚ "ะ—ะฐั‰ะธั‚ะฐ ั€ะฐะทัƒะผะฐ", ะพะฝะฐ ะณะธะฑะฝะตั‚, ะพัั‚ะฐะฒะปัั ะบะฐะผะตะฝัŒ ะดัƒัˆ. teleport-rune = ะ ัƒะฝะฐ ะขะตะปะตะฟะพั€ั‚ะฐ +teleport-rune-desc = ะขะตะปะตะฟะพั€ั‚ะธั€ัƒะตั‚ ะพะฑัŠะตะบั‚ั‹ ะธ ะปัŽะดะตะน ะฝะฐ ะดั€ัƒะณัƒัŽ ัะปัƒั‡ะฐะนะฝัƒัŽ ั€ัƒะฝัƒ ั‚ะตะปะตะฟะพั€ั‚ะฐั†ะธะธ. ะœะพะถะตั‚ ะฑั‹ั‚ัŒ ะธัะฟะพะปัŒะทะพะฒะฐะฝะฐ ะดะปั ะดะพัั‚ะฐั‚ะพั‡ะฝะพ ะฑั‹ัั‚ั€ะพะน ั‚ั€ะฐะฝัะฟะพั€ั‚ะธั€ะพะฒะบะธ. empowering-rune = ะ ัƒะฝะฐ ะฃัะธะปะตะฝะธั +empowering-rune-desc = ะŸะพะทะฒะพะปัะตั‚ ะดะพะฟะพะปะฝะธั‚ะตะปัŒะฝะพ ะฟะพะดะณะพั‚ะพะฒะธั‚ัŒ 4 ะดะพัั‚ัƒะฟะฝั‹ั… ะทะฐะบะปะธะฝะฐะฝะธั ะธ ัะฝะธะถะฐะตั‚ ะฒั€ะตะผั ะธั… ะฟะพะดะณะพั‚ะพะฒะบะธ. revive-rune = ะ ัƒะฝะฐ ะ’ะพะทั€ะพะถะดะตะฝะธั +revive-rune-desc = ะŸะพะทะฒะพะปัะตั‚ ะฒะพัะบั€ะตัˆะฐั‚ัŒ ะฟะฐะฒัˆะธั… ะบัƒะปัŒั‚ะธัั‚ะพะฒ, ะธัะฟะพะปัŒะทัƒั ะณะปะพะฑะฐะปัŒะฝั‹ะต ะทะฐั€ัะดั‹, ะฝะฐะบะพะฟะปะตะฝะฝั‹ะต ะฟั€ะธ ะถะตั€ั‚ะฒะพะฟั€ะธะฝะพัˆะตะฝะธัั… ะฝะฐ ั€ัƒะฝะต ะฟั€ะตะดะปะพะถะตะฝะธั. ะขะฐะบะถะต ะผะพะถะฝะพ ะฟั€ะพะฑัƒะถะดะฐั‚ัŒ ะบะฐั‚ะฐั‚ะพะฝะธะบะพะฒ(ะกะกะ”) ะฝะพะฒะพะน ะดัƒัˆะพะน. barrier-rune = ะ ัƒะฝะฐ ะ‘ะฐั€ัŒะตั€ะฐ +barrier-rune-desc = ะกะพะทะดะฐั‘ั‚ ะทะฐั‰ะธั‚ะฝั‹ะน ะฑะฐั€ัŒะตั€ ั 100 ะทะดะพั€ะพะฒัŒั, ะบะพั‚ะพั€ั‹ะน ะผะพะถะฝะพ ะฐะบั‚ะธะฒะธั€ะพะฒะฐั‚ัŒ ะธะปะธ ะดะตะฐะบั‚ะธะฒะธั€ะพะฒะฐั‚ัŒ, ะฝะพ ะบะฐะถะดะพะต ะธัะฟะพะปัŒะทะพะฒะฐะฝะธะต ะฝะฐะฝะพัะธั‚ ัƒั€ะพะฝ ะทะฐะบะปะธะฝะฐั‚ะตะปัŽ. summoning-rune = ะ ัƒะฝะฐ ะŸั€ะธะทั‹ะฒะฐ +summoning-rune-desc = ะ’ั‹ะทั‹ะฒะฐะตั‚ ะฝะฐ ั€ัƒะฝัƒ ะถะธะฒะพะณะพ ะบัƒะปัŒั‚ะธัั‚ะฐ, ะฟะพัะปะต ั‡ะตะณะพ ั€ัƒะฝะฐ ัั‚ะธั€ะฐะตั‚ัั. ะ ะฐะฑะพั‚ะฐะตั‚ ั‚ะพะปัŒะบะพ ะฝะฐ ัั‚ะฐะฝั†ะธะธ. bloodboil-rune = ะ ัƒะฝะฐ ะ’ัะบะธะฟะฐะฝะธั ะšั€ะพะฒะธ +bloodboil-rune-desc = ะ’ั‹ัะฐัั‹ะฒะฐะตั‚ ะทะดะพั€ะพะฒัŒะต ัƒ ะทะฐะบะปะธะฝะฐั‚ะตะปะตะน ะธ ะฝะฐะฝะพัะธั‚ ะผะพั‰ะฝั‹ะน ัƒั€ะพะฝ ะฒัะตะผ, ะบั‚ะพ ะฒะธะดะธั‚ ั€ัƒะฝัƒ. ะะต ะดะตะนัั‚ะฒัƒะตั‚ ะฝะฐ ััƒั‰ะตัั‚ะฒะฐ ะฑะตะท ะบั€ะพะฒะธ. spiritrealm-rune = ะ ัƒะฝะฐ ะฆะฐั€ัั‚ะฒะฐ ะ”ัƒั…ะพะฒ +spiritrealm-rune-desc = ะŸะพะทะฒะพะปัะตั‚ ัะฐะผะพะผัƒ ัั‚ะฐั‚ัŒ ะดัƒั…ะพะผ, ั‡ั‚ะพะฑั‹ ะบะพะพั€ะดะธะฝะธั€ะพะฒะฐั‚ัŒ ะบัƒะปัŒั‚. ritual-dimensional-rending-rune = ะ ะธั‚ัƒะฐะป ะ ะฐะทั€ั‹ะฒะฐ ะ˜ะทะผะตั€ะตะฝะธะน +ritual-dimensional-rending-rune-desc = ะ’ั‹ะทั‹ะฒะฐะตั‚ ะžะดะฝะพ ะธะท ะฑะพะถะตัั‚ะฒ ะบั€ะพะฒะธ ั‡ะตั€ะตะท ะฟั€ะพัั‚ั€ะฐะฝัั‚ะฒะตะฝะฝั‹ะน ั€ะฐะทั€ั‹ะฒ. # System +blood-cultist-eyes-glow-examined = [color=red]ะ“ะปะฐะทะฐ {$name} ัะพะทะตั€ั†ะฐัŽั‚ ะฝะตะตัั‚ะตัั‚ะฒะตะฝะฝั‹ะผ ั†ะฒะตั‚ะพะผ, ัั‚ะพ ะฝะต ะบ ะดะพะฑั€ัƒ...[/color] blood-cult-first-warning = ะ’ะฐัˆะธ ะณะปะฐะทะฐ ะฝะฐั‡ะฐะปะธ ะฟะพะปั‹ั…ะฐั‚ัŒ ะทะฐั€ะตะฒะพะผ ะบั€ะพะฒะธ blood-cult-second-warning = ะšั€ะพะฒะฐะฒะฐั ะฟะตะฝั‚ะฐะณั€ะฐะผะผะฐ ะพะฑั€ะฐะทัƒะตั‚ัั ะฝะฐะด ะฒะฐะผะธ, ัั‚ะพ ะทะฝะฐะบ ะบ ะดะตะนัั‚ะฒะธัŽ -blood-cult-targets-no-select = ะฆะตะปะธ ะตั‰ั‘ ะฝะต ะพะฟั€ะตะดะตะปะตะฝั‹... -blood-cult-ritual-completed-next-objective = ะ ะธั‚ัƒะฐะป ะทะฐะฒะตั€ัˆั‘ะฝ. ะŸั€ะธัั‚ัƒะฟะฐะนั‚ะต ะบ ัะปะตะดัƒัŽั‰ะตะน ั†ะตะปะธ โ€” ะฟั€ะธะทั‹ะฒ ะะฐั€-ะกะธ -blood-cult-objective-complete = ะฆะตะปัŒ ะฒั‹ะฟะพะปะฝะตะฝะฐ! ะกะปะฐะฒะฐ ะะฐั€-ะกะธ! -blood-cult-current-targets = ะขะตะบัƒั‰ะธะต ะถะตั€ั‚ะฒั‹: { $targets }. ะŸั€ะธะฝะตัะธั‚ะต ะธั… ะฒ ะถะตั€ั‚ะฒัƒ, ั‡ั‚ะพะฑั‹ ะดะพัั‚ะธั‡ัŒ ัะฒะพะตะน ั†ะตะปะธ. -blood-cult-no-valid-targets = ะะตั‚ ะดะพัั‚ัƒะฟะฝั‹ั… ั†ะตะปะตะน ะดะปั ะฒั‹ะฟะพะปะฝะตะฝะธั ะทะฐะดะฐั‡ะธ... -blood-dagger-failed-interact = ะบะธะฝะถะฐะป ะฒั‹ัะบะฐะปัŒะทั‹ะฒะฐะตั‚ ะธะท ะฒะฐัˆะตะน ั€ัƒะบะธ ะฟะพั€ะตะทะฐะฒ ะตะต +blood-dagger-failed-interact = ะบะธะฝะถะฐะป ะฒั‹ัะบะฐะปัŒะทั‹ะฒะฐะตั‚ ะธะท ะฒะฐัˆะตะน ั€ัƒะบะธ ะปะตะทะฒะธะตะผ ะฝะฐะฝะพัั ัƒะฒะตั‡ัŒั blood-sharpener-success = ั‚ะพั‡ะธะปะพ ะพะฑั€ะฐั‚ะธะปะพััŒ ะฒ ะฟั€ะฐั… blood-sharpener-failed = ะบะธะฝะถะฐะป ัƒะถะต ะฑั‹ะป ะทะฐั‚ะพั‡ะตะฝ blood-cult-failed-attack = ะฒั‹ ะฝะต ะผะพะถะตั‚ะต ะฝะฐะฒั€ะตะดะธั‚ัŒ ั‡ะปะตะฝะฐะผ ะบัƒะปัŒั‚ะฐ @@ -47,6 +51,7 @@ stone-soul-empty = ะบะฐะผะตะฝัŒ ะดัƒัˆะธ ะฟัƒัั‚ะพะน stone-soul-already-summoned = ะดัƒัˆะฐ ัƒะถะต ะฑั‹ะปะฐ ะฟั€ะธะทะฒะฐะฝะฐ stone-soul-summoned = ะดัƒัˆะฐ ะฟั€ะธะทะฒะฐะฝะฐ stone-soul-retracted = ะดัƒัˆะฐ ะฒะพะทะฒั€ะฐั‚ะธะปะพััŒ ะฒ ะบะฐะผะตะฝัŒ +veil-shifter-examined = ะžัั‚ะฐะปะพััŒ {$count} ะทะฐั€ัะดะพะฒ blood-curse-failed = ะบั€ะพะฒะฐะฒะพะต ะฟั€ะพะบะปัั‚ะธะต ะฝะต ัƒะดะฐะปะพััŒ blood-veil-shifter-failed = ะฝะธั‡ะตะณะพ ะฝะต ะฟั€ะพะธะทะพัˆะปะพ blood-construct-no-mind = ะบะฐะผะตะฝัŒ ะดัƒัˆะธ ะฟัƒัั‚ะพะน @@ -54,8 +59,6 @@ blood-construct-failed = ะบะพะฝัั‚ั€ัƒะบั‚ ะฟัƒัั‚ะพะน blood-construct-succses = ะบะพะฝัั‚ั€ัƒะบั‚ ะฟั€ะธะทะฒะฐะฝ blood-structure-failed = ะฒะทะฐะธะผะพะดะตะนัั‚ะฒะธะต ะฑัƒะดะตั‚ ะฒะพะทะผะพะถะฝะพ ั‡ะตั€ะตะท { $time } ัะตะบัƒะฝะด -cult-commune-title = ะžะฑั‰ะตะฝะธะต -cult-commune-massage = { $name }: { $massage } blood-cult-dagger-not-found = ะบะธะฝะถะฐะปะฐ ะฝะต ััƒั‰ะตัั‚ะฒัƒะตั‚ blood-cult-dagger-recalled = ะบะธะฝะถะฐะป ะพะฑั€ะฐะทัƒะตั‚ัั ะฟะตั€ะตะด ะฒะฐะผะธ blood-cult-blood-dagger-exists = ะฒั‹ ัƒะถะต ะธะผะตะตั‚ะต ะบะธะฝะถะฐะป @@ -65,6 +68,7 @@ blood-orb-invalid-input = ะะตะฒะพะทะผะพะถะฝะพะต ะทะฝะฐั‡ะตะฝะธะต, ัƒะบะฐะถะธั‚ blood-orb-not-enough-blood = ะฃ ะฒะฐั ะฝะต ะดะพัั‚ะฐั‚ะพั‡ะฝะพ ัะพะฑั€ะฐะฝะฝะพะน ะบั€ะพะฒะธ blood-orb-success = ะ’ั‹ ะฒั‹ะดะตะปะธะปะธ { $amount } ะตะดะธะฝะธั† ะบั€ะพะฒะธ blood-orb-absorbed = ัั„ะตั€ะฐ ั€ะฐัั‚ะตะบะฐะตั‚ัั ะฒ ะปัƒะถัƒ ะบั€ะพะฒะธ ะธ ะฟะพะณะปะฐั‰ะฐะตั‚ัั +blood-cult-recharge-failed = ะฝะตะดะพัั‚ะฐั‚ะพั‡ะฝะพ ะบั€ะพะฒะธ ะดะปั ะฟะตั€ะตะทะฐั€ัะดะบะธ... blood-cult-spear-failed = ะฝะตะดะพัั‚ะฐั‚ะพั‡ะฝะพ ะบั€ะพะฒะธ ะดะปั ะฟั€ะธะทั‹ะฒะฐ ะบะพะฟัŒั cult-spear-not-found = ะบั€ะพะฒะฐะฒะพะณะพ ะบะพะฟัŒั ะฝะต ััƒั‰ะตัั‚ะฒัƒะตั‚ cult-spear-too-far = ั€ะฐััั‚ะพัะฝะธะต ะดะพ ะบะพะฟัŒั ัะปะธัˆะบะพะผ ะฒะตะปะธะบะพ @@ -81,7 +85,7 @@ ritual-activate-too-soon = ะ ะ˜ะขะฃะะ› ะœะžะ–ะะž ะ‘ะฃะ”ะ•ะข ะะะงะะขะฌ ะŸะž ritual-activate-failed = ะะ•ะ’ะžะ—ะœะžะ–ะะž ะะะงะะขะฌ ะ ะ˜ะขะฃะะ› rune-activate-failed = ะฝะตะฒะพะทะผะพะถะฝะพ ะฐะบั‚ะธะฒะธั€ะพะฒะฐั‚ัŒ ั€ัƒะฝัƒ blood-ritual-warning = ะžะฑั€ะฐะทั‹ ะดั€ะตะฒะฝะตะณะพ ะฑะพะณะพะฟะพะดะพะฑะฝะพะณะพ ััƒั‰ะตัั‚ะฒะฐ ัะพะตะดะธะฝััŽัั‚ั ะฒะพะตะดะธะฝะพ { $location }. ะŸั€ะตั€ะฒะธั‚ะต ั€ะธั‚ัƒะฐะป ะปัŽะฑะพะน ั†ะตะปะพะน, ะฟะพะบะฐ ัั‚ะฐะฝั†ะธั ะฝะต ะฑั‹ะปะฐ ัƒะฝะธั‡ั‚ะพะถะตะฝะฐ! -blood-ritual-activate-warning = ะ‘ั‹ะป ะพะฑะฝะฐั€ัƒะถะตะฝ ัะดะฒะธะณ ะฟั€ะพัั‚ั€ะฐะฝัั‚ะฒะฐ { $location }. ะŸั€ะตะบั€ะฐั‚ะธั‚ะต ั€ะฐัะฟั€ะพัั‚ั€ะฐะฝะตะฝะธะต ะฒัะตะผะธ ะดะพัั‚ัƒะฟะฝั‹ะผะธ ัั€ะตะดัั‚ะฒะฐะผะธ. ะžะถะธะดะฐะตะผะพะต ั€ะฐััˆะธั€ะตะฝะธะต ั‡ะตั€ะตะท 45 ัะตะบัƒะฝะด. +blood-ritual-activate-warning = ะ‘ั‹ะป ะพะฑะฝะฐั€ัƒะถะตะฝ ัะดะฒะธะณ ะฟั€ะพัั‚ั€ะฐะฝัั‚ะฒะฐ { $location }. ะŸั€ะตะบั€ะฐั‚ะธั‚ะต ั€ะฐัะฟั€ะพัั‚ั€ะฐะฝะตะฝะธะต ะฒัะตะผะธ ะดะพัั‚ัƒะฟะฝั‹ะผะธ ัั€ะตะดัั‚ะฒะฐะผะธ. ะžะถะธะดะฐะตะผะพะต ั€ะฐััˆะธั€ะตะฝะธะต ั‡ะตั€ะตะท 90 ัะตะบัƒะฝะด. ritual-failed = ะ ะ˜ะขะฃะะ› ะŸะ ะžะ’ะะ›ะ˜ blood-cultist-offering-message = Mah'weyh pleggh at e'ntrath! diff --git a/Resources/Locale/ru-RU/_wega/cardtarot/cardtarot.ftl b/Resources/Locale/ru-RU/_wega/cardtarot/cardtarot.ftl new file mode 100644 index 0000000000..553c0eead0 --- /dev/null +++ b/Resources/Locale/ru-RU/_wega/cardtarot/cardtarot.ftl @@ -0,0 +1,29 @@ +tarot-card-not-enchanted = ะšะฐั€ั‚ะฐ ะขะฐั€ะพ ะฝะต ะทะฐั‡ะฐั€ะพะฒะฐะฝะฐ +tarot-used = {$name} ะธัะฟะพะปัŒะทัƒะตั‚ ะบะฐั€ั‚ัƒ {$type} +tarot-card-fool = ะ”ัƒั€ะฐะบะฐ +tarot-card-magician = ะœะฐะณะฐ +tarot-card-highpriestess = ะ’ะตั€ั…ะพะฒะฝะพะณะพ ะถั€ะตั†ะฐ +tarot-card-empress = ะ˜ะผะฟะตั€ะฐั‚ั€ะธั†ั‹ +tarot-card-emperor = ะ˜ะผะฟะตั€ะฐั‚ะพั€ะฐ +tarot-card-hierophant = ะ˜ะตั€ะพั„ะฐะฝั‚ะฐ +tarot-card-lovers = ะ›ัŽะฑะพะฒะฝะธะบะพะฒ +tarot-card-chariot = ะšะพะปะตัะฝะธั†ั‹ +tarot-card-justice = ะŸั€ะฐะฒะพััƒะดะธั +tarot-card-hermit = ะžั‚ัˆะตะปัŒะฝะธะบะฐ +tarot-card-wheeloffortune = ะšะพะปะตัะพ ัƒะดะฐั‡ะธ +tarot-card-strength = ะกะธะปั‹ +tarot-card-hangedman = ะŸะพะฒะตัˆะตะฝะฝะพะณะพ +tarot-card-death = ะกะผะตั€ั‚ะธ +tarot-card-temperance = ะฃะผะตั€ะตะฝะฝะพัั‚ะธ +tarot-card-devil = ะ”ะตะผะพะฝะฐ +tarot-card-tower = ะ‘ะฐัˆะฝะธ +tarot-card-stars = ะ—ะฒะตะทะด +tarot-card-moon = ะ›ัƒะฝั‹ +tarot-card-sun = ะกะพะปะฝั†ะฐ +tarot-card-judgement = ะกัƒะดะฐ +tarot-card-theworld = ะ—ะะะะะะะ ะ’ะะ ะ”ะžะžะžะžะžะžะžะžะž + +tarot-devil-healed = ะขะตะผะฝะฐั ัะฝะตั€ะณะธั ะธัั†ะตะปัะตั‚ ะฒะฐัˆะธ ั€ะฐะฝั‹... +tarot-devil-damaged = ะขะตะผะฝะฐั ัะฝะตั€ะณะธั ะฒั‹ัะฐัั‹ะฒะฐะตั‚ ะฒะฐัˆัƒ ะถะธะทะฝะตะฝะฝัƒัŽ ัะธะปัƒ... +tarot-moon-m-message = ะฏ ะดะพะปะฑะพะตะฑ +tarot-moon-f-message = ะฏ ะดัƒั€ะฐ diff --git a/Resources/Locale/ru-RU/_wega/chat/managers/chat-manager.ftl b/Resources/Locale/ru-RU/_wega/chat/managers/chat-manager.ftl new file mode 100644 index 0000000000..cffc6c7aba --- /dev/null +++ b/Resources/Locale/ru-RU/_wega/chat/managers/chat-manager.ftl @@ -0,0 +1 @@ +chat-mind-message-wrap = [color={$color}]{$channel} [bold]{$name}[/bold] ั€ะฐะทะผั‹ัˆะปัะตั‚, "{$message}"[/color] diff --git a/Resources/Locale/ru-RU/_wega/chat/ui/chat-box.ftl b/Resources/Locale/ru-RU/_wega/chat/ui/chat-box.ftl new file mode 100644 index 0000000000..05200171c2 --- /dev/null +++ b/Resources/Locale/ru-RU/_wega/chat/ui/chat-box.ftl @@ -0,0 +1,2 @@ +hud-chatbox-select-Mind = ะ ะฐะทัƒะผ +hud-chatbox-select-channel-Mind = ะ ะฐะทัƒะผ diff --git a/Resources/Locale/ru-RU/_wega/commands/mindsay-command.ftl b/Resources/Locale/ru-RU/_wega/commands/mindsay-command.ftl new file mode 100644 index 0000000000..212af5fdb3 --- /dev/null +++ b/Resources/Locale/ru-RU/_wega/commands/mindsay-command.ftl @@ -0,0 +1,2 @@ +chat-manager-no-access-mind-channel = ะ’ั‹ ะฝะต ะผะพะถะตั‚ะต ะฟะพะปัŒะทะพะฒะฐั‚ัŒัั ะดะฐะฝะฝั‹ะผ ะบะฐะฝะฐะปะพะผ +chat-manager-no-mind-channel = ะšะฐะฝะฐะปะฐ ะฝะต ััƒั‰ะตัั‚ะฒัƒะตั‚ diff --git a/Resources/Locale/ru-RU/_wega/game-ticking/game-presets/preset-blood-cult.ftl b/Resources/Locale/ru-RU/_wega/game-ticking/game-presets/preset-blood-cult.ftl index a0366b6940..c1cfbdd424 100644 --- a/Resources/Locale/ru-RU/_wega/game-ticking/game-presets/preset-blood-cult.ftl +++ b/Resources/Locale/ru-RU/_wega/game-ticking/game-presets/preset-blood-cult.ftl @@ -1,14 +1,16 @@ blood-cult-title = ะšัƒะปัŒั‚ ะบั€ะพะฒะธ blood-cult-description = ะ’ะตั€ะฝั‹ะต ะฟะพัะปะตะดะพะฒะฐั‚ะตะปะธ ะบั€ะพะฒะธ ัั€ะตะดะธ ะฝะฐั... +objective-issuer-blood-god = [color=red]ะ“ะตะพะผะตั‚ั€ะธะฒะธ ะšั€ะพะฒะธ[/color] + blood-cult-role-greeting-human = - ะ’ั‹ โ€” ะšัƒะปัŒั‚ะธัั‚ ะšั€ะพะฒะธ! - ะ’ั‹ โ€” ั‡ะฐัั‚ัŒ ั‚ั‘ะผะฝะพะณะพ ะบัƒะปัŒั‚ะฐ, ั‡ั‚ะพ ัะปัƒะถะธั‚ ะ“ะตะพะผะตั‚ั€ะธะฒะธ ะšั€ะพะฒะธ, { $god }. - ะ’ะฐัˆะฐ ั†ะตะปัŒ โ€” ะฟั€ะธะทะฒะฐั‚ัŒ ะฐะฒะฐั‚ะฐั€ ะธ ะฟั€ะธะฒะตัั‚ะธ ะบัƒะปัŒั‚ ะบ ะฟะพะฑะตะดะต. + ะ’ั‹ - ะšัƒะปัŒั‚ะธัั‚ ะšั€ะพะฒะธ! + ะ’ั‹ ั‡ะฐัั‚ัŒ ั‚ั‘ะผะฝะพะณะพ ะบัƒะปัŒั‚ะฐ, ั‡ั‚ะพ ัะปัƒะถะธั‚ ะ“ะตะพะผะตั‚ั€ะธะฒะธ ะšั€ะพะฒะธ, { $god }. + ะ’ะฐัˆะฐ ั†ะตะปัŒ - ะฟั€ะธะทะฒะฐั‚ัŒ ะฐะฒะฐั‚ะฐั€ ะธ ะฟั€ะธะฒะตัั‚ะธ ะบัƒะปัŒั‚ ะบ ะฟะพะฑะตะดะต. ะ’ ะฒะฐัˆะธั… ั€ัƒะบะฐั… ั€ะธั‚ัƒะฐะปัŒะฝั‹ะน ะบะธะฝะถะฐะป ะธ ั€ัƒะฝั‹ ะดะปั ัะพะทะดะฐะฝะธั ัั‚ั€ะฐัˆะฝั‹ั… ัั‚ั€ัƒะบั‚ัƒั€. ะ–ะตั€ั‚ะฒั‹ ะดะพะปะถะฝั‹ ะฑั‹ั‚ัŒ ะฟั€ะธะฝะตัะตะฝั‹ ะฒ ะดะฐั€ ะฒั‹ัะพะบะพะฟะพัั‚ะฐะฒะปะตะฝะฝั‹ะต ั‡ะปะตะฝั‹ ัะบะธะฟะฐะถะฐ ะธะปะธ ะพั…ั€ะฐะฝั‹. ะŸั€ะธะทะพะฒะธั‚ะต ะฑะพะถะตัั‚ะฒะพ ะฒ ัƒะบั€ะพะผะฝะพะผ ะผะตัั‚ะต ั ะฟะพะผะพั‰ัŒัŽ 9 ะบัƒะปัŒั‚ะธัั‚ะพะฒ ะธ ั€ะธั‚ัƒะฐะปะฐ. ะขะพะปัŒะบะพ ั‚ะฐะบ ะฒั‹ ะพะฑะตัะฟะตั‡ะธั‚ะต ะฟะพะฑะตะดัƒ! -blood-cult-role-greeting-animal = ะ’ั‹ โ€” ะกัƒั‰ะตัั‚ะฒะพ ะšั€ะพะฒะธ! ะ’ั‹ โ€” ั‡ะฐัั‚ัŒ ั‚ะตะฝะธ, ัะปัƒะถะธั‚ะต ะ“ะตะพะผะตั‚ั€ะธะธ ะšั€ะพะฒะธ, { $god }. ะŸะพะผะพะณะธั‚ะต ัะฒะพะธะผ ะฑั€ะฐั‚ัŒัะผ ะฒ ะฟั€ะธะทั‹ะฒะต ะธ ะฟั€ะธะฝะตัะตะฝะธะธ ะถะตั€ั‚ะฒ. +blood-cult-role-greeting-animal = ะ’ั‹ - ะกัƒั‰ะตัั‚ะฒะพ ะšั€ะพะฒะธ! ะ’ั‹ - ั‡ะฐัั‚ัŒ ั‚ะตะฝะธ, ัะปัƒะถะธั‚ะต ะ“ะตะพะผะตั‚ั€ะธะธ ะšั€ะพะฒะธ, { $god }. ะŸะพะผะพะณะธั‚ะต ัะฒะพะธะผ ะฑั€ะฐั‚ัŒัะผ ะฒ ะฟั€ะธะทั‹ะฒะต ะธ ะฟั€ะธะฝะตัะตะฝะธะธ ะถะตั€ั‚ะฒ. current-god-narsie = ะะฐั€'ะกะธ current-god-reaper = ะ–ะฝะตั†ัƒ diff --git a/Resources/Locale/ru-RU/_wega/metabolism/metabolizer-types.ftl b/Resources/Locale/ru-RU/_wega/metabolism/metabolizer-types.ftl index 5fd721edbb..ffe79f6a81 100644 --- a/Resources/Locale/ru-RU/_wega/metabolism/metabolizer-types.ftl +++ b/Resources/Locale/ru-RU/_wega/metabolism/metabolizer-types.ftl @@ -1,3 +1,3 @@ metabolizer-type-vampire = ะ’ะฐะผะฟะธั€ -metabolizer-type-cultist = ะšัƒะปัŒั‚ะธัั‚ +metabolizer-type-blood-cultist = ะšัƒะปัŒั‚ะธัั‚ ะบั€ะพะฒะธ metabolizer-type-ariral = ะั€ะธั€ะฐะป diff --git a/Resources/Locale/ru-RU/_wega/mind/mindlink.ftl b/Resources/Locale/ru-RU/_wega/mind/mindlink.ftl new file mode 100644 index 0000000000..4250365fd2 --- /dev/null +++ b/Resources/Locale/ru-RU/_wega/mind/mindlink.ftl @@ -0,0 +1 @@ +blood-cult-mind-channel = ะšัƒะปัŒั‚ ะบั€ะพะฒะธ diff --git a/Resources/Locale/ru-RU/_wega/objectives/conditions/bloodcult.ftl b/Resources/Locale/ru-RU/_wega/objectives/conditions/bloodcult.ftl new file mode 100644 index 0000000000..2409f4221b --- /dev/null +++ b/Resources/Locale/ru-RU/_wega/objectives/conditions/bloodcult.ftl @@ -0,0 +1 @@ +objective-condition-blood-ritual-person-title = ะŸั€ะตะฝะธัะธั‚ะต ะฒ ะถะตั€ั‚ะฒัƒ { $targetName } ะฒะพ ัะปะฐะฒัƒ ะ“ะตะพะผะตั‚ั€ะธะฒะธ ะšั€ะพะฒะธ. diff --git a/Resources/Locale/ru-RU/_wega/slotmachine/slotmachine.ftl b/Resources/Locale/ru-RU/_wega/slotmachine/slotmachine.ftl new file mode 100644 index 0000000000..30f2302bd9 --- /dev/null +++ b/Resources/Locale/ru-RU/_wega/slotmachine/slotmachine.ftl @@ -0,0 +1,18 @@ +slot-machine-examine = [color=green]{$slots}[/color] | ะ’ัะตะณะพ ัั‹ะณั€ะฐะฝะพ {$spins} ั€ะฐะท +slot-machine-busy = ะะฒั‚ะพะผะฐั‚ ัƒะถะต ะบั€ัƒั‚ะธั‚ัั! +slot-machine-unpowered = ะœะฐัˆะธะฝะฐ ะฑะตะท ะฟะธั‚ะฐะฝะธั +slot-machine-no-money = ะะตะดะพัั‚ะฐั‚ะพั‡ะฝะพ ะดะตะฝะตะณ ะดะปั ะธะณั€ั‹! +slot-machine-spinning = ะะฒั‚ะพะผะฐั‚ ะฝะฐั‡ะธะฝะฐะตั‚ ะฒั€ะฐั‰ะตะฝะธะต... +slot-machine-lose = ะ’ั‹ ะฝะธั‡ะตะณะพ ะฝะต ะฒั‹ะธะณั€ะฐะปะธ. + +slot-machine-jackpot = ะ”ะ–ะ•ะšะŸะžะข! ะ’ั‹ ะฒั‹ะธะณั€ะฐะปะธ {$prize} ะบั€ะตะดะธั‚ะพะฒ! +slot-machine-bigwin = ะšั€ัƒะฟะฝั‹ะน ะฒั‹ะธะณั€ั‹ัˆ! ะ’ั‹ ะฒั‹ะธะณั€ะฐะปะธ {$prize} ะบั€ะตะดะธั‚ะพะฒ! +slot-medium-win = ะ’ั‹ะธะณั€ั‹ัˆ! ะ’ั‹ ะฟะพะปัƒั‡ะธะปะธ {$prize} ะบั€ะตะดะธั‚ะพะฒ. +slot-small-win = ะะตะฟะปะพั…ะพ! ะ’ั‹ ะฒั‹ะธะณั€ะฐะปะธ {$prize} ะบั€ะตะดะธั‚ะพะฒ. +slot-tiny-win = ะกะบั€ะพะผะฝั‹ะน ะฟั€ะธะท, ะฒั‹ ะฒั‹ะธะณั€ะฐะปะธ {$prize} ะบั€ะตะดะธั‚ะพะฒ! + +cursed-slot-machine-uses = ะ˜ัะฟะพะปัŒะทะพะฒะฐะฝะธะน [color=red]{$uses}/{$max}[/color] +cursed-slot-machine-deny = ะ’ั‹ ะธะณั€ะฐะปะธ ัะปะธัˆะบะพะผ ะผะฝะพะณะพ ั€ะฐะท... +cursed-slot-machine-lose = ะ’ั‹ ั‡ัƒะฒัั‚ะฒัƒะตั‚ะต ะฑะพะปัŒ, ะบะพะณะดะฐ ัะฝะตั€ะณะธั ะฟะพะบะธะดะฐะตั‚ ะฒะฐัˆะต ั‚ะตะปะพ! +cursed-slot-machine-jackpot = ะกะœะ•ะฅ ะ ะะ—ะ”ะะ•ะขะกะฏ ะ’ะžะšะ ะฃะ“! {$name} ะ’ะซะ˜ะ“ะ ะะ› ะ”ะ–ะ•ะšะŸะžะข! +cursed-slot-machine-spin = {$name} ะดะตั€ะณะฐะตั‚ ั€ั‹ั‡ะฐะณ ั ะฑะปะตัะบะพะผ ะฒ ะณะปะฐะทะฐั…! diff --git a/Resources/Locale/ru-RU/_wega/station-events/events/cargo-gifts.ftl b/Resources/Locale/ru-RU/_wega/station-events/events/cargo-gifts.ftl new file mode 100644 index 0000000000..ec0c23bf72 --- /dev/null +++ b/Resources/Locale/ru-RU/_wega/station-events/events/cargo-gifts.ftl @@ -0,0 +1 @@ +cargo-gift-gambling = ัะปะพั‚ ะผะฐัˆะธะฝั‹ diff --git a/Resources/Locale/ru-RU/_wega/store/uplink-catalog.ftl b/Resources/Locale/ru-RU/_wega/store/uplink-catalog.ftl index 0cf9d7103f..648a762f6e 100644 --- a/Resources/Locale/ru-RU/_wega/store/uplink-catalog.ftl +++ b/Resources/Locale/ru-RU/_wega/store/uplink-catalog.ftl @@ -38,3 +38,5 @@ uplink-syndie-player-name = ะŸะปะตะตั€ ัะธะฝะดะธะบะฐั‚ะฐ uplink-syndie-player-desc = ะŸะพะทะฒะพะปัะตั‚ ั€ะฐะทะฑะฐะฒะธั‚ัŒ ั€ัƒั‚ะธะฝะฝัƒัŽ ั€ะตะทะฝัŽ ัะฒะพะตะน ะปัŽะฑะธะผะพะน ะผัƒะทั‹ะบะพะน. uplink-syndie-pouch-name = ะฃัะธะปะตะฝะฝั‹ะน ะบะฐั€ะผะฐะฝะฝั‹ะน ะฟะพะดััƒะผะพะบ ัะธะฝะดะธะบะฐั‚ะฐ uplink-syndie-pouch-desc = ะ’ะผะตัั‚ะธั‚ะตะปัŒะฝั‹ะน ะฟะพะดััƒะผะพะบ ั ัƒัะธะปะตะฝะฝะพะน ะทะฐั‰ะธั‚ะพะน ะพั‚ ะฒะทั€ั‹ะฒะพะฒ. ะ’ัั‘, ั‡ั‚ะพ ะฝัƒะถะฝะพ ะฐะณะตะฝั‚ัƒ, ะพัั‚ะฐะฝะตั‚ัั ะฟั€ะธ ะฝั‘ะผ. +uplink-syndie-dice-of-fate-name = ะšะพัั‚ัŒ ััƒะดัŒะฑั‹ +uplink-syndie-dice-of-fate-desc = ะะตะธะทะฒะตัั‚ะฝะพะน ั‚ะฐะธะฝัั‚ะฒะตะฝะฝะพัั‚ะธ ะธ ัะธะปั‹ ะฐั€ั‚ะตั„ะฐะบั‚, ะบะพั‚ะพั€ั‹ะน ัะฟะพัะพะฑะตะฝ ะบะฐั€ะดะธะฝะฐะปัŒะฝะพ ะธะทะผะตะฝะธั‚ัŒ ััƒะดัŒะฑัƒ ะบะธะฝัƒะฒัˆะตะณะพ ะตะณะพ. ะžะดะฝะฐะบะพ ัั‚ะพ ะฝะตะฒะตั€ะพัั‚ะฝั‹ะน ั€ะธัะบ! diff --git a/Resources/Locale/ru-RU/ss14-ru/prototypes/_wega/entities/objects/misc/magic.ftl b/Resources/Locale/ru-RU/ss14-ru/prototypes/_wega/entities/objects/misc/magic.ftl new file mode 100644 index 0000000000..44f1f44aa4 --- /dev/null +++ b/Resources/Locale/ru-RU/ss14-ru/prototypes/_wega/entities/objects/misc/magic.ftl @@ -0,0 +1,125 @@ +ent-DiceOfFate = { ent-d20Dice } + .desc = { ent-d20Dice.desc } + .suffix = ะšะพัั‚ัŒ ััƒะดัŒะฑั‹, ะะ• ะœะะŸะŸะ˜ะขะฌ +ent-CardTarotBoxEmpty = ะบะพั€ะพะฑะบะฐ ั ะบะฐั€ั‚ะฐะผะธ ะขะฐั€ะพ + .desc = ะ—ะฐั‡ะฐั€ะพะฒะฐะฝะฝะฐั ะบะพะปะพะดะฐ ะบะฐั€ั‚ ะขะฐั€ะพ. + .suffix = ะŸัƒัั‚ะฐั +ent-CardTarotBoxFilled = { ent-CardTarotBoxEmpty } + .desc = { ent-CardTarotBoxEmpty.desc } + .suffix = ะŸะพะปะฝะฐั, ะะ• ะœะะŸะŸะ˜ะขะฌ + +ent-BaseCardTarot = ะบะฐั€ั‚ะฐ ะขะฐั€ะพ + .desc = ะญั‚ะพ ะพะดะฝะฐ ะธะท ะบะฐั€ั‚ ะธะท ะบะพะปะพะดั‹ ะขะฐั€ะพ. +ent-CardTarotNotEnchanted = ะฟัƒัั‚ะฐั ะบะฐั€ั‚ะฐ ะขะฐั€ะพ + .desc = ะญั‚ะพ ะพะดะฝะฐ ะธะท ะบะฐั€ั‚ ะฒ ะบะพะปะพะดะต ะขะฐั€ะพ, ะฝะพ ะพะฝะฐ, ะบะฐะถะตั‚ัั, ะฟัƒัั‚ะฐั. +ent-CardTarotFool = { ent-BaseCardTarot } + .desc = { ent-BaseCardTarot.desc } +ent-CardTarotMagician = { ent-BaseCardTarot } + .desc = { ent-BaseCardTarot.desc } +ent-CardTarotHighPriestess = { ent-BaseCardTarot } + .desc = { ent-BaseCardTarot.desc } +ent-CardTarotEmpress = { ent-BaseCardTarot } + .desc = { ent-BaseCardTarot.desc } +ent-CardTarotEmperor = { ent-BaseCardTarot } + .desc = { ent-BaseCardTarot.desc } +ent-CardTarotHierophant = { ent-BaseCardTarot } + .desc = { ent-BaseCardTarot.desc } +ent-CardTarotLovers = { ent-BaseCardTarot } + .desc = { ent-BaseCardTarot.desc } +ent-CardTarotChariot = { ent-BaseCardTarot } + .desc = { ent-BaseCardTarot.desc } +ent-CardTarotJustice = { ent-BaseCardTarot } + .desc = { ent-BaseCardTarot.desc } +ent-CardTarotHermit = { ent-BaseCardTarot } + .desc = { ent-BaseCardTarot.desc } +ent-CardTarotWheelOfFortune = { ent-BaseCardTarot } + .desc = { ent-BaseCardTarot.desc } +ent-CardTarotStrength = { ent-BaseCardTarot } + .desc = { ent-BaseCardTarot.desc } +ent-CardTarotHangedMan = { ent-BaseCardTarot } + .desc = { ent-BaseCardTarot.desc } +ent-CardTarotDeath = { ent-BaseCardTarot } + .desc = { ent-BaseCardTarot.desc } +ent-CardTarotTemperance = { ent-BaseCardTarot } + .desc = { ent-BaseCardTarot.desc } +ent-CardTarotDevil = { ent-BaseCardTarot } + .desc = { ent-BaseCardTarot.desc } +ent-CardTarotTower = { ent-BaseCardTarot } + .desc = { ent-BaseCardTarot.desc } +ent-CardTarotStars = { ent-BaseCardTarot } + .desc = { ent-BaseCardTarot.desc } +ent-CardTarotMoon = { ent-BaseCardTarot } + .desc = { ent-BaseCardTarot.desc } +ent-CardTarotSun = { ent-BaseCardTarot } + .desc = { ent-BaseCardTarot.desc } +ent-CardTarotJudgement = { ent-BaseCardTarot } + .desc = { ent-BaseCardTarot.desc } +ent-CardTarotTheWorld = { ent-BaseCardTarot } + .desc = { ent-BaseCardTarot.desc } + +ent-CardTarotFoolReversed = { ent-BaseCardTarot } + .desc = { ent-BaseCardTarot.desc } + .suffix = ะŸะตั€ะตะฒะตั€ะฝัƒั‚ะฐั +ent-CardTarotMagicianReversed = { ent-BaseCardTarot } + .desc = { ent-BaseCardTarot.desc } + .suffix = ะŸะตั€ะตะฒะตั€ะฝัƒั‚ะฐั +ent-CardTarotHighPriestessReversed = { ent-BaseCardTarot } + .desc = { ent-BaseCardTarot.desc } + .suffix = ะŸะตั€ะตะฒะตั€ะฝัƒั‚ะฐั +ent-CardTarotEmpressReversed = { ent-BaseCardTarot } + .desc = { ent-BaseCardTarot.desc } + .suffix = ะŸะตั€ะตะฒะตั€ะฝัƒั‚ะฐั +ent-CardTarotEmperorReversed = { ent-BaseCardTarot } + .desc = { ent-BaseCardTarot.desc } + .suffix = ะŸะตั€ะตะฒะตั€ะฝัƒั‚ะฐั +ent-CardTarotHierophantReversed = { ent-BaseCardTarot } + .desc = { ent-BaseCardTarot.desc } + .suffix = ะŸะตั€ะตะฒะตั€ะฝัƒั‚ะฐั +ent-CardTarotLoversReversed = { ent-BaseCardTarot } + .desc = { ent-BaseCardTarot.desc } + .suffix = ะŸะตั€ะตะฒะตั€ะฝัƒั‚ะฐั +ent-CardTarotChariotReversed = { ent-BaseCardTarot } + .desc = { ent-BaseCardTarot.desc } + .suffix = ะŸะตั€ะตะฒะตั€ะฝัƒั‚ะฐั +ent-CardTarotJusticeReversed = { ent-BaseCardTarot } + .desc = { ent-BaseCardTarot.desc } + .suffix = ะŸะตั€ะตะฒะตั€ะฝัƒั‚ะฐั +ent-CardTarotHermitReversed = { ent-BaseCardTarot } + .desc = { ent-BaseCardTarot.desc } + .suffix = ะŸะตั€ะตะฒะตั€ะฝัƒั‚ะฐั +ent-CardTarotWheelOfFortuneReversed = { ent-BaseCardTarot } + .desc = { ent-BaseCardTarot.desc } + .suffix = ะŸะตั€ะตะฒะตั€ะฝัƒั‚ะฐั +ent-CardTarotStrengthReversed = { ent-BaseCardTarot } + .desc = { ent-BaseCardTarot.desc } + .suffix = ะŸะตั€ะตะฒะตั€ะฝัƒั‚ะฐั +ent-CardTarotHangedManReversed = { ent-BaseCardTarot } + .desc = { ent-BaseCardTarot.desc } + .suffix = ะŸะตั€ะตะฒะตั€ะฝัƒั‚ะฐั +ent-CardTarotDeathReversed = { ent-BaseCardTarot } + .desc = { ent-BaseCardTarot.desc } + .suffix = ะŸะตั€ะตะฒะตั€ะฝัƒั‚ะฐั +ent-CardTarotTemperanceReversed = { ent-BaseCardTarot } + .desc = { ent-BaseCardTarot.desc } + .suffix = ะŸะตั€ะตะฒะตั€ะฝัƒั‚ะฐั +ent-CardTarotDevilReversed = { ent-BaseCardTarot } + .desc = { ent-BaseCardTarot.desc } + .suffix = ะŸะตั€ะตะฒะตั€ะฝัƒั‚ะฐั +ent-CardTarotTowerReversed = { ent-BaseCardTarot } + .desc = { ent-BaseCardTarot.desc } + .suffix = ะŸะตั€ะตะฒะตั€ะฝัƒั‚ะฐั +ent-CardTarotStarsReversed = { ent-BaseCardTarot } + .desc = { ent-BaseCardTarot.desc } + .suffix = ะŸะตั€ะตะฒะตั€ะฝัƒั‚ะฐั +ent-CardTarotMoonReversed = { ent-BaseCardTarot } + .desc = { ent-BaseCardTarot.desc } + .suffix = ะŸะตั€ะตะฒะตั€ะฝัƒั‚ะฐั +ent-CardTarotSunReversed = { ent-BaseCardTarot } + .desc = { ent-BaseCardTarot.desc } + .suffix = ะŸะตั€ะตะฒะตั€ะฝัƒั‚ะฐั +ent-CardTarotJudgementReversed = { ent-BaseCardTarot } + .desc = { ent-BaseCardTarot.desc } + .suffix = ะŸะตั€ะตะฒะตั€ะฝัƒั‚ะฐั +ent-CardTarotTheWorldReversed = { ent-BaseCardTarot } + .desc = { ent-BaseCardTarot.desc } + .suffix = ะŸะตั€ะตะฒะตั€ะฝัƒั‚ะฐั diff --git a/Resources/Locale/ru-RU/ss14-ru/prototypes/_wega/entities/structures/machines/slot_machine.ftl b/Resources/Locale/ru-RU/ss14-ru/prototypes/_wega/entities/structures/machines/slot_machine.ftl new file mode 100644 index 0000000000..9bb8198d9a --- /dev/null +++ b/Resources/Locale/ru-RU/ss14-ru/prototypes/_wega/entities/structures/machines/slot_machine.ftl @@ -0,0 +1,8 @@ +ent-SlotMachine = ัะปะพั‚ ะผะฐัˆะธะฝะฐ + .desc = ะะทะฐั€ั‚ะฝั‹ะต ะธะณั€ั‹ ะดะปั ะฝะตะพะฑั‰ะธั‚ะตะปัŒะฝั‹ั… ะปัŽะดะตะน. ะ’ะฝะตัะธั‚ะต 10 ะบั€ะตะดะธั‚ะพะฒ ะธ ะธัะฟั‹ั‚ะฐะนั‚ะต ัะฒะพัŽ ัƒะดะฐั‡ัƒ. +ent-BrokenSlotMachine = ัะปะพะผะฐะฝะฝะฐั ัะปะพั‚ ะผะฐัˆะธะฝะฐ + .desc = ะ’ะธะดะธะผะพ, ะบะพะผัƒ-ั‚ะพ ะพั‡ะตะฝัŒ ะฝะต ะฟะพะฒะตะทะปะพ ะธ ะพะฝ ะฑั‹ะป ะพั‡ะตะฝัŒ ะทะพะป. + .suffix = ะกะปะพะผะฐะฝะฝะฐั +ent-CursedSlotMachine = ัะปะพั‚ ะผะฐัˆะธะฝะฐ + .desc = ะ’ั‹ัะพะบะธะต ัั‚ะฐะฒะบะธ, ะฒั‹ัะพะบะธะต ะฝะฐะณั€ะฐะดั‹. ะžั‚ ัั‚ะพะณะพ ะฐะฒั‚ะพะผะฐั‚ะฐ ะธัั…ะพะดะธั‚ ะถัƒั‚ะบะพะฒะฐั‚ะฐั ะฐัƒั€ะฐ. + .suffix = ะŸั€ะพะบะปัั‚ะฐั, ะะ• ะœะะŸะŸะ˜ะขะฌ diff --git a/Resources/Locale/ru-RU/ss14-ru/prototypes/_wega/objectives/bloodcult.ftl b/Resources/Locale/ru-RU/ss14-ru/prototypes/_wega/objectives/bloodcult.ftl new file mode 100644 index 0000000000..5e529790d6 --- /dev/null +++ b/Resources/Locale/ru-RU/ss14-ru/prototypes/_wega/objectives/bloodcult.ftl @@ -0,0 +1,3 @@ +ent-BloodCultTargetObjective = { ent-BaseObjective } +ent-BloodCultRitualObjective = ะŸั€ะพะฒะตะดะธั‚ะต ะบั€ะพะฒะฐะฒั‹ะน ั€ะธั‚ัƒะฐะป + .desc = ะ“ะตะพะผะตั‚ั€ะธะฒะธ ะšั€ะพะฒะธ ะถะฐะถะดะตั‚ ะฒะตั€ะฝัƒั‚ัŒัั ะฒ ัั‚ะพั‚ ะผะธั€, ะธ ะฒั‹ ะดะพะปะถะฝั‹ ัะพะฒะตั€ัˆะธั‚ัŒ ั€ะธั‚ัƒะฐะป, ั‡ั‚ะพะฑั‹ ะฒะตั€ะฝัƒั‚ัŒ ะตะต ะพะฑั€ะฐั‚ะฝะพ. diff --git a/Resources/Prototypes/Entities/Clothing/OuterClothing/armor.yml b/Resources/Prototypes/Entities/Clothing/OuterClothing/armor.yml index d625ba4380..53f4adf432 100644 --- a/Resources/Prototypes/Entities/Clothing/OuterClothing/armor.yml +++ b/Resources/Prototypes/Entities/Clothing/OuterClothing/armor.yml @@ -359,6 +359,7 @@ Piercing: 0.5 Heat: 0.5 - type: GroupExamine + - type: BloodShieldActivaeble # Corvax-Wega-Blood-Cult - type: entity parent: [ClothingOuterBaseLarge, AllowSuitStorageClothing] diff --git a/Resources/Prototypes/Entities/Mobs/Player/admin_ghost.yml b/Resources/Prototypes/Entities/Mobs/Player/admin_ghost.yml index df3f79ae0c..d6be7ad7fd 100644 --- a/Resources/Prototypes/Entities/Mobs/Player/admin_ghost.yml +++ b/Resources/Prototypes/Entities/Mobs/Player/admin_ghost.yml @@ -114,6 +114,7 @@ - type: MovementSpeedModifier baseWalkSpeed: 30 # Corvax-End + - type: AdminMindLinkListener # Corvax-Wega-MindChat - type: entity abstract: true diff --git a/Resources/Prototypes/Entities/Objects/Fun/dice_bag.yml b/Resources/Prototypes/Entities/Objects/Fun/dice_bag.yml index 41cea1412c..7fb83341b9 100644 --- a/Resources/Prototypes/Entities/Objects/Fun/dice_bag.yml +++ b/Resources/Prototypes/Entities/Objects/Fun/dice_bag.yml @@ -15,6 +15,11 @@ - id: d12Dice - id: d20Dice - id: PercentileDie + # Corvax-Wega-DiceOfFate-start + # For fun lol + - id: DiceOfFate + prob: 0.00001 + # Corvax-Wega-DiceOfFate-end - type: Sprite sprite: Objects/Fun/dice.rsi state: dicebag diff --git a/Resources/Prototypes/Roles/Jobs/Fun/wizard_startinggear.yml b/Resources/Prototypes/Roles/Jobs/Fun/wizard_startinggear.yml index e59e08bf79..9a6376d42f 100644 --- a/Resources/Prototypes/Roles/Jobs/Fun/wizard_startinggear.yml +++ b/Resources/Prototypes/Roles/Jobs/Fun/wizard_startinggear.yml @@ -12,6 +12,11 @@ belt: ClothingBeltWand pocket1: WizardTeleportScroll pocket2: WizardsGrimoire + # Corvax-Wega-CardTarot-start + storage: + back: + - CardTarotBoxFilled + # Corvax-Wega-CardTarot-end - type: startingGear id: WizardRedGear diff --git a/Resources/Prototypes/_Wega/Actions/bloodcult.yml b/Resources/Prototypes/_Wega/Actions/bloodcult.yml index f82ce8fa09..c532b4e70d 100644 --- a/Resources/Prototypes/_Wega/Actions/bloodcult.yml +++ b/Resources/Prototypes/_Wega/Actions/bloodcult.yml @@ -1,31 +1,4 @@ # Basic -- type: entity - id: ActionBloodCultObjective - parent: BaseAction - name: "[color=red]Objective[/color]" - description: "Shows the current purpose of the cult." - categories: [ HideSpawnMenu ] - components: - - type: Action - icon: { sprite: _Wega/Interface/Actions/actions_bloodcult.rsi, state: "vote" } - useDelay: 5 - - type: InstantAction - event: !type:BloodCultObjectiveActionEvent - -- type: entity - id: ActionBloodCultComms - parent: BaseAction - name: "[color=red]Communication[/color]" - description: "Allows you to talk to your fellow humans." - categories: [ HideSpawnMenu ] - components: - - type: Action - icon: { sprite: _Wega/Interface/Actions/actions_bloodcult.rsi, state: "comms" } - checkCanInteract: false - useDelay: 5 - - type: InstantAction - event: !type:BloodCultCommuneActionEvent - - type: entity id: ActionBloodMagic parent: BaseAction diff --git a/Resources/Prototypes/_Wega/Catalog/Cargo/cargo_fun.yml b/Resources/Prototypes/_Wega/Catalog/Cargo/cargo_fun.yml index ff97b397b0..f5b2fc4c8e 100644 --- a/Resources/Prototypes/_Wega/Catalog/Cargo/cargo_fun.yml +++ b/Resources/Prototypes/_Wega/Catalog/Cargo/cargo_fun.yml @@ -26,4 +26,14 @@ product: SpaceVillainArcadeFilled cost: 1500 category: cargoproduct-category-name-fun - group: market \ No newline at end of file + group: market + +- type: cargoProduct + id: SlotMachine + icon: + sprite: _Wega/Structures/Machines/slot_machine.rsi + state: base + product: SlotMachine + cost: 5000 + category: cargoproduct-category-name-fun + group: market diff --git a/Resources/Prototypes/_Wega/Catalog/uplink_catalog.yml b/Resources/Prototypes/_Wega/Catalog/uplink_catalog.yml index d369729a66..bc3adf2391 100644 --- a/Resources/Prototypes/_Wega/Catalog/uplink_catalog.yml +++ b/Resources/Prototypes/_Wega/Catalog/uplink_catalog.yml @@ -90,7 +90,7 @@ Telecrystal: 3 categories: - UplinkDisruption - + - type: listing id: UplinkClothingHandsKnuckleDustersStun name: uplink-clothing-hands-knuckle-dusters-stun-name @@ -245,7 +245,7 @@ conditions: - !type:ListingLimitedStockCondition stock: 1 - + - type: listing id: UplinkC20RBundleNuke name: uplink-c20r-bundle-name @@ -350,7 +350,7 @@ conditions: - !type:ListingLimitedStockCondition stock: 1 - + - type: listing id: UplinkSyndiePouch name: uplink-syndie-pouch-name @@ -366,4 +366,22 @@ Telecrystal: 1 conditions: - !type:ListingLimitedStockCondition - stock: 2 \ No newline at end of file + stock: 2 + +- type: listing + id: UplinkDiceOfFate + name: uplink-syndie-dice-of-fate-name + description: uplink-syndie-dice-of-fate-desc + icon: { sprite: Objects/Fun/dice.rsi, state: d20_20 } + productEntity: DiceOfFate + categories: + - UplinkJob + discountCategory: veryRareDiscounts + cost: + Telecrystal: 20 + conditions: + - !type:BuyerJobCondition + whitelist: + - Librarian + - !type:ListingLimitedStockCondition + stock: 1 diff --git a/Resources/Prototypes/_Wega/Chemistry/metabolizer_types.yml b/Resources/Prototypes/_Wega/Chemistry/metabolizer_types.yml index c3f7a6161f..76539f31d2 100644 --- a/Resources/Prototypes/_Wega/Chemistry/metabolizer_types.yml +++ b/Resources/Prototypes/_Wega/Chemistry/metabolizer_types.yml @@ -3,8 +3,8 @@ name: metabolizer-type-vampire - type: metabolizerType - id: Cultist - name: metabolizer-type-cultist + id: BloodCultist + name: metabolizer-type-blood-cultist - type: metabolizerType id: Ariral diff --git a/Resources/Prototypes/_Wega/Damage/modifier_sets.yml b/Resources/Prototypes/_Wega/Damage/modifier_sets.yml index 59b56686f8..013106480c 100644 --- a/Resources/Prototypes/_Wega/Damage/modifier_sets.yml +++ b/Resources/Prototypes/_Wega/Damage/modifier_sets.yml @@ -15,8 +15,8 @@ Blunt: 0.4 Piercing: 0.4 Cold: 0.1 - Heat: 1 - Shock: 1 + Heat: 0.5 + Shock: 0.5 Poison: 0.0 Radiation: 0.0 @@ -63,3 +63,21 @@ Cold: 1.25 Heat: 1.25 Poison: 0.8 + +- type: damageModifierSet + id: DiceOfFateMod + coefficients: + Asphyxiation: 0.5 + Bloodloss: 0.5 + Blunt: 0.5 + Cellular: 0.5 + Caustic: 0.5 + Cold: 0.5 + Heat: 0.5 + Piercing: 0.5 + Poison: 0.5 + Radiation: 0.5 + Shock: 0.5 + Slash: 0.5 + Structural: 0.5 + Holy: 0.5 diff --git a/Resources/Prototypes/_Wega/Entities/Clothing/OuterClothing/armor.yml b/Resources/Prototypes/_Wega/Entities/Clothing/OuterClothing/armor.yml index 5ddea893c4..5e99b1a23a 100644 --- a/Resources/Prototypes/_Wega/Entities/Clothing/OuterClothing/armor.yml +++ b/Resources/Prototypes/_Wega/Entities/Clothing/OuterClothing/armor.yml @@ -50,7 +50,7 @@ sprite: _Wega/Clothing/OuterClothing/Armor/captain_carapace_green.rsi - type: Clothing sprite: _Wega/Clothing/OuterClothing/Armor/captain_carapace_green.rsi - + - type: entity parent: ClothingOuterArmorCaptainCarapace id: ClothingOuterArmorCaptainCarapaceWhite @@ -60,4 +60,4 @@ - type: Sprite sprite: _Wega/Clothing/OuterClothing/Armor/captain_carapace_white.rsi - type: Clothing - sprite: _Wega/Clothing/OuterClothing/Armor/captain_carapace_white.rsi \ No newline at end of file + sprite: _Wega/Clothing/OuterClothing/Armor/captain_carapace_white.rsi diff --git a/Resources/Prototypes/_Wega/Entities/Clothing/OuterClothing/coats.yml b/Resources/Prototypes/_Wega/Entities/Clothing/OuterClothing/coats.yml index cb1b1e7a96..b073363faa 100644 --- a/Resources/Prototypes/_Wega/Entities/Clothing/OuterClothing/coats.yml +++ b/Resources/Prototypes/_Wega/Entities/Clothing/OuterClothing/coats.yml @@ -57,12 +57,12 @@ - type: Armor modifiers: coefficients: - Blunt: 2 - Slash: 2 - Piercing: 2 - Heat: 2 + Blunt: 1.75 + Slash: 1.75 + Piercing: 1.75 + Heat: 1.75 - type: ExplosionResistance - damageCoefficient: 2 + damageCoefficient: 1.75 - type: ClothingSpeedModifier walkModifier: 1.6 sprintModifier: 1.6 @@ -90,7 +90,7 @@ sprite: _Wega/Clothing/OuterClothing/Coats/captain_green.rsi - type: Clothing sprite: _Wega/Clothing/OuterClothing/Coats/captain_green.rsi - + - type: entity parent: ClothingOuterCoatCaptain id: ClothingOuterCoatWhite @@ -100,4 +100,4 @@ - type: Sprite sprite: _Wega/Clothing/OuterClothing/Coats/captain_white.rsi - type: Clothing - sprite: _Wega/Clothing/OuterClothing/Coats/captain_white.rsi \ No newline at end of file + sprite: _Wega/Clothing/OuterClothing/Coats/captain_white.rsi diff --git a/Resources/Prototypes/_Wega/Entities/Effects/bloodcult.yml b/Resources/Prototypes/_Wega/Entities/Effects/bloodcult.yml index 362352ac58..a3693ca6bd 100644 --- a/Resources/Prototypes/_Wega/Entities/Effects/bloodcult.yml +++ b/Resources/Prototypes/_Wega/Entities/Effects/bloodcult.yml @@ -20,6 +20,16 @@ state: blood_orb - type: BloodOrb +- type: entity + id: BloodCultShieldEffect + categories: [ HideSpawnMenu ] + components: + - type: Sprite + noRot: true + drawdepth: OverMobs + sprite: _Wega/Effects/shield-bloodcult.rsi + state: shield + - type: entity id: BloodCultOutEffect categories: [ HideSpawnMenu ] @@ -52,7 +62,7 @@ !type:String "#ff0000" - type: BloodRune - prototype: default + runeType: Default - type: TimedDespawn lifetime: 4.0 diff --git a/Resources/Prototypes/_Wega/Entities/Markers/Spawners/Random/containers.yml b/Resources/Prototypes/_Wega/Entities/Markers/Spawners/Random/containers.yml new file mode 100644 index 0000000000..8555f2c735 --- /dev/null +++ b/Resources/Prototypes/_Wega/Entities/Markers/Spawners/Random/containers.yml @@ -0,0 +1,19 @@ +- type: entity + id: RandomContainerBlank + name: random container spawner + components: + - type: RandomSpawner + prototypes: + - ShippingContainerBlank + - ShippingContainerConarex + - ShippingContainerCybersun + - ShippingContainerDeforest + - ShippingContainerDonkCo + - ShippingContainerGorlexRed + - ShippingContainerGorlex + - ShippingContainerInterdyne + - ShippingContainerKahraman + - ShippingContainerKosmologistika + - ShippingContainerNakamura + - ShippingContainerNanotrasen + - ShippingContainerVitezstvi diff --git a/Resources/Prototypes/_Wega/Entities/Markers/Spawners/Random/dice.yml b/Resources/Prototypes/_Wega/Entities/Markers/Spawners/Random/dice.yml new file mode 100644 index 0000000000..c496f34f23 --- /dev/null +++ b/Resources/Prototypes/_Wega/Entities/Markers/Spawners/Random/dice.yml @@ -0,0 +1,11 @@ +- type: entity + id: RandomSpellbook + categories: [ HideSpawnMenu ] + components: + - type: RandomSpawner + prototypes: + - FireballSpellbook + - SpawnSpellbook + - BlinkBook + - ForceWallSpellbook + - KnockSpellbook diff --git a/Resources/Prototypes/_Wega/Entities/Mobs/Player/bloodcult.yml b/Resources/Prototypes/_Wega/Entities/Mobs/Player/bloodcult.yml index ac4b77ad6a..4b92d39618 100644 --- a/Resources/Prototypes/_Wega/Entities/Mobs/Player/bloodcult.yml +++ b/Resources/Prototypes/_Wega/Entities/Mobs/Player/bloodcult.yml @@ -18,7 +18,10 @@ - type: Examiner skipChecks: true - type: Ghost + - type: BloodCultGhost - type: Spectral + - type: MindLink + channels: ["MindBloodCult"] - type: entity id: MobBanshee @@ -76,6 +79,10 @@ - type: MovementSpeedModifier baseWalkSpeed: 5 baseSprintSpeed: 5 + - type: FriendlyFaction + faction: BloodCult + - type: MindLink + channels: ["MindBloodCult"] - type: entity abstract: true @@ -160,6 +167,10 @@ - type: NpcFactionMember factions: - BloodCult + - type: FriendlyFaction + faction: BloodCult + - type: MindLink + channels: ["MindBloodCult"] - type: ShowCultistIcons - type: BloodCultConstruct - type: Barotrauma diff --git a/Resources/Prototypes/_Wega/Entities/Objects/Misc/magic.yml b/Resources/Prototypes/_Wega/Entities/Objects/Misc/magic.yml new file mode 100644 index 0000000000..e47d06ebf6 --- /dev/null +++ b/Resources/Prototypes/_Wega/Entities/Objects/Misc/magic.yml @@ -0,0 +1,633 @@ +- type: entity + parent: d20Dice + id: DiceOfFate + name: d20 + description: A die with twenty sides. The preferred die to throw at the GM. + suffix: Dice Of Fate, DO NOT MAP + components: + - type: DiceOfFate + +- type: entity + parent: BaseStorageItem + id: CardTarotBoxEmpty + name: tarot card box + description: "An enchanted deck of Tarot cards." + suffix: Empty + components: + - type: Sprite + sprite: _Wega/Objects/Misc/cardtarot.rsi + state: box + - type: SpaceGarbage + - type: Tag + tags: + - Trash + - type: Storage + maxItemSize: Tiny + grid: + - 0,0,11,3 + whitelist: + tags: + - CardTarot + +- type: entity + parent: CardTarotBoxEmpty + id: CardTarotBoxFilled + name: tarot card box + description: "An enchanted deck of Tarot cards." + suffix: Filled, DO NOT MAP + components: + - type: EntityTableContainerFill + containers: + storagebase: !type:AllSelector + children: + - id: CardTarotFool + - id: CardTarotMagician + - id: CardTarotHighPriestess + - id: CardTarotEmpress + - id: CardTarotEmperor + - id: CardTarotHierophant + - id: CardTarotLovers + - id: CardTarotChariot + - id: CardTarotJustice + - id: CardTarotHermit + - id: CardTarotWheelOfFortune + - id: CardTarotStrength + - id: CardTarotHangedMan + - id: CardTarotDeath + - id: CardTarotTemperance + - id: CardTarotDevil + - id: CardTarotTower + - id: CardTarotStars + - id: CardTarotMoon + - id: CardTarotSun + - id: CardTarotJudgement + - id: CardTarotTheWorld + # -------------------------------- # + - id: CardTarotFoolReversed + - id: CardTarotMagicianReversed + - id: CardTarotHighPriestessReversed + - id: CardTarotEmpressReversed + - id: CardTarotEmperorReversed + - id: CardTarotHierophantReversed + - id: CardTarotLoversReversed + - id: CardTarotChariotReversed + - id: CardTarotJusticeReversed + - id: CardTarotHermitReversed + - id: CardTarotWheelOfFortuneReversed + - id: CardTarotStrengthReversed + - id: CardTarotHangedManReversed + - id: CardTarotDeathReversed + - id: CardTarotTemperanceReversed + - id: CardTarotDevilReversed + - id: CardTarotTowerReversed + - id: CardTarotStarsReversed + - id: CardTarotMoonReversed + - id: CardTarotSunReversed + - id: CardTarotJudgementReversed + - id: CardTarotTheWorldReversed + +- type: entity + parent: BaseItem + id: BaseCardTarot + abstract: true + name: card tarot + description: "This is one of the cards from the Tarot deck." + components: + - type: Sprite + sprite: _Wega/Objects/Misc/cardtarot.rsi + - type: CardTarot + card: NotEnchanted + - type: Item + size: Tiny + - type: Tag + tags: + - CardTarot + - type: Appearance + - type: StaticPrice + price: 5 + +- type: entity + parent: BaseCardTarot + id: BaseCardTarotReversed + abstract: true + components: + - type: CardTarot + card: NotEnchanted + cardType: Reversed + +- type: entity + parent: BaseCardTarot + id: CardTarotNotEnchanted + name: empty card tarot + description: "This is one of the cards in the Tarot deck, but it seems to be empty." + components: + - type: Sprite + state: notenchanted + +- type: entity + parent: BaseCardTarot + id: CardTarotFool + name: card tarot + description: "This is one of the cards from the Tarot deck." + components: + - type: Sprite + state: fool + - type: CardTarot + card: Fool + +- type: entity + parent: BaseCardTarot + id: CardTarotMagician + name: card tarot + description: "This is one of the cards from the Tarot deck." + components: + - type: Sprite + state: magician + - type: CardTarot + card: Magician + +- type: entity + parent: BaseCardTarot + id: CardTarotHighPriestess + name: card tarot + description: "This is one of the cards from the Tarot deck." + components: + - type: Sprite + state: highpriestess + - type: CardTarot + card: HighPriestess + +- type: entity + parent: BaseCardTarot + id: CardTarotEmpress + name: card tarot + description: "This is one of the cards from the Tarot deck." + components: + - type: Sprite + state: empress + - type: CardTarot + card: Empress + +- type: entity + parent: BaseCardTarot + id: CardTarotEmperor + name: card tarot + description: "This is one of the cards from the Tarot deck." + components: + - type: Sprite + state: emperor + - type: CardTarot + card: Emperor + +- type: entity + parent: BaseCardTarot + id: CardTarotHierophant + name: card tarot + description: "This is one of the cards from the Tarot deck." + components: + - type: Sprite + state: hierophant + - type: CardTarot + card: Hierophant + +- type: entity + parent: BaseCardTarot + id: CardTarotLovers + name: card tarot + description: "This is one of the cards from the Tarot deck." + components: + - type: Sprite + state: lovers + - type: CardTarot + card: Lovers + +- type: entity + parent: BaseCardTarot + id: CardTarotChariot + name: card tarot + description: "This is one of the cards from the Tarot deck." + components: + - type: Sprite + state: chariot + - type: CardTarot + card: Chariot + +- type: entity + parent: BaseCardTarot + id: CardTarotJustice + name: card tarot + description: "This is one of the cards from the Tarot deck." + components: + - type: Sprite + state: justice + - type: CardTarot + card: Justice + +- type: entity + parent: BaseCardTarot + id: CardTarotHermit + name: card tarot + description: "This is one of the cards from the Tarot deck." + components: + - type: Sprite + state: hermit + - type: CardTarot + card: Hermit + +- type: entity + parent: BaseCardTarot + id: CardTarotWheelOfFortune + name: card tarot + description: "This is one of the cards from the Tarot deck." + components: + - type: Sprite + state: wheeloffortune + - type: CardTarot + card: WheelOfFortune + +- type: entity + parent: BaseCardTarot + id: CardTarotStrength + name: card tarot + description: "This is one of the cards from the Tarot deck." + components: + - type: Sprite + state: strength + - type: CardTarot + card: Strength + +- type: entity + parent: BaseCardTarot + id: CardTarotHangedMan + name: card tarot + description: "This is one of the cards from the Tarot deck." + components: + - type: Sprite + state: hangedman + - type: CardTarot + card: HangedMan + +- type: entity + parent: BaseCardTarot + id: CardTarotDeath + name: card tarot + description: "This is one of the cards from the Tarot deck." + components: + - type: Sprite + state: death + - type: CardTarot + card: Death + +- type: entity + parent: BaseCardTarot + id: CardTarotTemperance + name: card tarot + description: "This is one of the cards from the Tarot deck." + components: + - type: Sprite + state: temperance + - type: CardTarot + card: Temperance + +- type: entity + parent: BaseCardTarot + id: CardTarotDevil + name: card tarot + description: "This is one of the cards from the Tarot deck." + components: + - type: Sprite + state: devil + - type: CardTarot + card: Devil + +- type: entity + parent: BaseCardTarot + id: CardTarotTower + name: card tarot + description: "This is one of the cards from the Tarot deck." + components: + - type: Sprite + state: tower + - type: CardTarot + card: Tower + +- type: entity + parent: BaseCardTarot + id: CardTarotStars + name: card tarot + description: "This is one of the cards from the Tarot deck." + components: + - type: Sprite + state: stars + - type: CardTarot + card: Stars + +- type: entity + parent: BaseCardTarot + id: CardTarotMoon + name: card tarot + description: "This is one of the cards from the Tarot deck." + components: + - type: Sprite + state: moon + - type: CardTarot + card: Moon + +- type: entity + parent: BaseCardTarot + id: CardTarotSun + name: card tarot + description: "This is one of the cards from the Tarot deck." + components: + - type: Sprite + state: sun + - type: CardTarot + card: Sun + +- type: entity + parent: BaseCardTarot + id: CardTarotJudgement + name: card tarot + description: "This is one of the cards from the Tarot deck." + components: + - type: Sprite + state: judgement + - type: CardTarot + card: Judgement + +- type: entity + parent: BaseCardTarot + id: CardTarotTheWorld + name: card tarot + description: "This is one of the cards from the Tarot deck." + components: + - type: Sprite + state: theworld + - type: CardTarot + card: TheWorld + +# ------------------------------------ # + +- type: entity + parent: BaseCardTarotReversed + id: CardTarotFoolReversed + name: card tarot + description: "This is one of the cards from the Tarot deck." + suffix: Reversed + components: + - type: Sprite + state: fool-reversed + - type: CardTarot + card: Fool + +- type: entity + parent: BaseCardTarotReversed + id: CardTarotMagicianReversed + name: card tarot + description: "This is one of the cards from the Tarot deck." + suffix: Reversed + components: + - type: Sprite + state: magician-reversed + - type: CardTarot + card: Magician + +- type: entity + parent: BaseCardTarotReversed + id: CardTarotHighPriestessReversed + name: card tarot + description: "This is one of the cards from the Tarot deck." + suffix: Reversed + components: + - type: Sprite + state: highpriestess-reversed + - type: CardTarot + card: HighPriestess + +- type: entity + parent: BaseCardTarotReversed + id: CardTarotEmpressReversed + name: card tarot + description: "This is one of the cards from the Tarot deck." + suffix: Reversed + components: + - type: Sprite + state: empress-reversed + - type: CardTarot + card: Empress + +- type: entity + parent: BaseCardTarotReversed + id: CardTarotEmperorReversed + name: card tarot + description: "This is one of the cards from the Tarot deck." + suffix: Reversed + components: + - type: Sprite + state: emperor-reversed + - type: CardTarot + card: Emperor + +- type: entity + parent: BaseCardTarotReversed + id: CardTarotHierophantReversed + name: card tarot + description: "This is one of the cards from the Tarot deck." + suffix: Reversed + components: + - type: Sprite + state: hierophant-reversed + - type: CardTarot + card: Hierophant + +- type: entity + parent: BaseCardTarotReversed + id: CardTarotLoversReversed + name: card tarot + description: "This is one of the cards from the Tarot deck." + suffix: Reversed + components: + - type: Sprite + state: lovers-reversed + - type: CardTarot + card: Lovers + +- type: entity + parent: BaseCardTarotReversed + id: CardTarotChariotReversed + name: card tarot + description: "This is one of the cards from the Tarot deck." + suffix: Reversed + components: + - type: Sprite + state: chariot-reversed + - type: CardTarot + card: Chariot + +- type: entity + parent: BaseCardTarotReversed + id: CardTarotJusticeReversed + name: card tarot + description: "This is one of the cards from the Tarot deck." + suffix: Reversed + components: + - type: Sprite + state: justice-reversed + - type: CardTarot + card: Justice + +- type: entity + parent: BaseCardTarotReversed + id: CardTarotHermitReversed + name: card tarot + description: "This is one of the cards from the Tarot deck." + suffix: Reversed + components: + - type: Sprite + state: hermit-reversed + - type: CardTarot + card: Hermit + +- type: entity + parent: BaseCardTarotReversed + id: CardTarotWheelOfFortuneReversed + name: card tarot + description: "This is one of the cards from the Tarot deck." + suffix: Reversed + components: + - type: Sprite + state: wheeloffortune-reversed + - type: CardTarot + card: WheelOfFortune + +- type: entity + parent: BaseCardTarotReversed + id: CardTarotStrengthReversed + name: card tarot + description: "This is one of the cards from the Tarot deck." + suffix: Reversed + components: + - type: Sprite + state: strength-reversed + - type: CardTarot + card: Strength + +- type: entity + parent: BaseCardTarotReversed + id: CardTarotHangedManReversed + name: card tarot + description: "This is one of the cards from the Tarot deck." + suffix: Reversed + components: + - type: Sprite + state: hangedman-reversed + - type: CardTarot + card: HangedMan + +- type: entity + parent: BaseCardTarotReversed + id: CardTarotDeathReversed + name: card tarot + description: "This is one of the cards from the Tarot deck." + suffix: Reversed + components: + - type: Sprite + state: death-reversed + - type: CardTarot + card: Death + +- type: entity + parent: BaseCardTarotReversed + id: CardTarotTemperanceReversed + name: card tarot + description: "This is one of the cards from the Tarot deck." + suffix: Reversed + components: + - type: Sprite + state: temperance-reversed + - type: CardTarot + card: Temperance + +- type: entity + parent: BaseCardTarotReversed + id: CardTarotDevilReversed + name: card tarot + description: "This is one of the cards from the Tarot deck." + suffix: Reversed + components: + - type: Sprite + state: devil-reversed + - type: CardTarot + card: Devil + +- type: entity + parent: BaseCardTarotReversed + id: CardTarotTowerReversed + name: card tarot + description: "This is one of the cards from the Tarot deck." + suffix: Reversed + components: + - type: Sprite + state: tower-reversed + - type: CardTarot + card: Tower + +- type: entity + parent: BaseCardTarotReversed + id: CardTarotStarsReversed + name: card tarot + description: "This is one of the cards from the Tarot deck." + suffix: Reversed + components: + - type: Sprite + state: stars-reversed + - type: CardTarot + card: Stars + +- type: entity + parent: BaseCardTarotReversed + id: CardTarotMoonReversed + name: card tarot + description: "This is one of the cards from the Tarot deck." + suffix: Reversed + components: + - type: Sprite + state: moon-reversed + - type: CardTarot + card: Moon + +- type: entity + parent: BaseCardTarotReversed + id: CardTarotSunReversed + name: card tarot + description: "This is one of the cards from the Tarot deck." + suffix: Reversed + components: + - type: Sprite + state: sun-reversed + - type: CardTarot + card: Sun + +- type: entity + parent: BaseCardTarotReversed + id: CardTarotJudgementReversed + name: card tarot + description: "This is one of the cards from the Tarot deck." + suffix: Reversed + components: + - type: Sprite + state: judgement-reversed + - type: CardTarot + card: Judgement + +- type: entity + parent: BaseCardTarotReversed + id: CardTarotTheWorldReversed + name: card tarot + description: "This is one of the cards from the Tarot deck." + suffix: Reversed + components: + - type: Sprite + state: theworld-reversed + - type: CardTarot + card: TheWorld diff --git a/Resources/Prototypes/_Wega/Entities/Objects/Specific/BloodCult/stone.yml b/Resources/Prototypes/_Wega/Entities/Objects/Specific/BloodCult/stone.yml index cd0863b3f8..49b05f1fe0 100644 --- a/Resources/Prototypes/_Wega/Entities/Objects/Specific/BloodCult/stone.yml +++ b/Resources/Prototypes/_Wega/Entities/Objects/Specific/BloodCult/stone.yml @@ -7,11 +7,8 @@ - type: Sprite sprite: _Wega/Objects/Specific/BloodCult/stone.rsi layers: - - state: stone_soul - map: ["enum.StoneSoulVisualLayers.Soul"] - visible: false - state: stone - map: ["enum.StoneSoulVisualLayers.Base"] + map: ["soul"] - type: MindContainer - type: BlockMovement - type: GhostRole @@ -24,6 +21,12 @@ settings: default - type: GhostTakeoverAvailable - type: Appearance + - type: GenericVisualizer + visuals: + enum.StoneSoulVisuals.HasSoul: + soul: + True: { state: stone_soul } + False: { state: stone } - type: StoneSoul soulProto: MobBanshee - type: Tag diff --git a/Resources/Prototypes/_Wega/Entities/Objects/Weapons/Melee/magic.yml b/Resources/Prototypes/_Wega/Entities/Objects/Weapons/Melee/magic.yml index e198478178..0a0de9e4cf 100644 --- a/Resources/Prototypes/_Wega/Entities/Objects/Weapons/Melee/magic.yml +++ b/Resources/Prototypes/_Wega/Entities/Objects/Weapons/Melee/magic.yml @@ -127,19 +127,24 @@ slots: - belt - type: BloodDagger + - type: BloodCultWeapon - type: Tool qualities: - Slicing - BloodDagger - type: MeleeWeapon wideAnimationRotation: -135 - attackRate: 0.75 + attackRate: 1.25 damage: types: Slash: 12 + - type: UserInterface + interfaces: + enum.BloodRunesUiKey.Key: + type: RunesMenuBoundUserInterface - type: entity - parent: BaseKnife + parent: WeaponBloodDagger id: WeaponDeathDagger name: ritual dagger description: An old, worn dagger. @@ -147,24 +152,9 @@ - type: Sprite sprite: _Wega/Objects/Weapons/Melee/death_dagger.rsi state: icon - - type: Clothing - quickEquip: false - slots: - - belt - - type: BloodDagger - - type: Tool - qualities: - - Slicing - - BloodDagger - - type: MeleeWeapon - wideAnimationRotation: -135 - attackRate: 0.75 - damage: - types: - Slash: 12 - type: entity - parent: BaseKnife + parent: WeaponBloodDagger id: WeaponHellDagger name: ritual dagger description: An old, worn dagger. @@ -172,21 +162,6 @@ - type: Sprite sprite: _Wega/Objects/Weapons/Melee/hell_dagger.rsi state: icon - - type: Clothing - quickEquip: false - slots: - - belt - - type: BloodDagger - - type: Tool - qualities: - - Slicing - - BloodDagger - - type: MeleeWeapon - wideAnimationRotation: -135 - attackRate: 0.75 - damage: - types: - Slash: 12 - type: entity id: WeaponBloodBlade @@ -204,10 +179,14 @@ - back - suitStorage - type: Item + storedRotation: -45 size: Normal + shape: + - 0,0,0,2 + - type: BloodCultWeapon - type: MeleeWeapon wideAnimationRotation: -135 - attackRate: 0.75 + attackRate: 1 damage: types: Slash: 25 @@ -231,13 +210,18 @@ - back - suitStorage - type: Item + storedRotation: -45 size: Normal + shape: + - 0,0,1,0 + - 0,2,0,1 + - type: BloodCultWeapon - type: MeleeWeapon wideAnimationRotation: -135 - attackRate: 0.75 + attackRate: 2 damage: types: - Slash: 25 + Slash: 10 soundHit: path: /Audio/Weapons/bladeslice.ogg - type: DisarmMalus @@ -258,13 +242,18 @@ - back - suitStorage - type: Item + storedRotation: -45 size: Normal + shape: + - 0,0,2,1 + - 1,2,1,1 + - type: BloodCultWeapon - type: MeleeWeapon wideAnimationRotation: -135 - attackRate: 0.75 + attackRate: 0.35 damage: types: - Slash: 25 + Slash: 40 soundHit: path: /Audio/Weapons/bladeslice.ogg - type: DisarmMalus @@ -325,6 +314,7 @@ transferForensics: true - !type:DoActsBehavior acts: [ "Destruction" ] + - type: BloodCultWeapon - type: MeleeWeapon wideAnimationRotation: -135 damage: @@ -370,8 +360,7 @@ - type: Sprite color: "#e20205" - type: BloodSpell - prototype: - - stun + spellType: Stun - type: Item inhandVisuals: left: @@ -388,8 +377,7 @@ - type: Sprite color: "#4e1459" - type: BloodSpell - prototype: - - teleport + spellType: Teleport - type: Item inhandVisuals: left: @@ -406,8 +394,7 @@ - type: Sprite color: "#000002" - type: BloodSpell - prototype: - - shadowshackles + spellType: ShadowShackles - type: Item inhandVisuals: left: @@ -424,8 +411,7 @@ - type: Sprite color: "#000002" - type: BloodSpell - prototype: - - twistedconstruction + spellType: TwistedConstruction - type: Item inhandVisuals: left: @@ -442,8 +428,7 @@ - type: Sprite color: "#448d33" - type: BloodSpell - prototype: - - summonequipment + spellType: SummonEquipment - type: Item inhandVisuals: left: @@ -460,8 +445,7 @@ - type: Sprite color: "#6f0f13" - type: BloodSpell - prototype: - - bloodrites + spellType: BloodRites - type: UseDelay delay: 5 - type: Item @@ -472,6 +456,10 @@ right: - state: inhand-right color: "#6f0f13" + - type: UserInterface + interfaces: + enum.BloodRitesUiKey.Key: + type: BloodRitesBoundUserInterface - type: entity parent: BaseItem @@ -481,10 +469,19 @@ components: - type: Sprite sprite: _Wega/Objects/Weapons/Melee/blood_shifter.rsi - state: icon + layers: + - state: activated + map: ["active"] - type: VeilShifter - type: UseDelay delay: 4 + - type: Appearance + - type: GenericVisualizer + visuals: + enum.VeilShifterVisuals.Charged: + active: + True: { state: activated } + False: { state: base } - type: entity parent: BaseBloodCultSpell diff --git a/Resources/Prototypes/_Wega/Entities/Structures/Machines/slot_machine.yml b/Resources/Prototypes/_Wega/Entities/Structures/Machines/slot_machine.yml new file mode 100644 index 0000000000..9d958c014b --- /dev/null +++ b/Resources/Prototypes/_Wega/Entities/Structures/Machines/slot_machine.yml @@ -0,0 +1,124 @@ +- type: entity + parent: BaseMachinePowered + id: SlotMachine + name: slot machine + description: "Gambling for the antisocial. Insert 10 credits and test your luck." + components: + - type: Sprite + sprite: _Wega/Structures/Machines/slot_machine.rsi + noRot: true + layers: + - state: base + - state: light + map: ["working"] + shader: unshaded + - type: Fixtures + fixtures: + fix1: + shape: + !type:PhysShapeAabb + bounds: "-0.25, -0.4, 0.25, 0.4" + density: 190 + mask: + - MachineMask + layer: + - MachineLayer + - type: Physics + bodyType: Static + - type: SlotMachine + - type: Appearance + - type: GenericVisualizer + visuals: + enum.SlotMachineVisuals.Working: + working: + True: { state: spinning } + False: { state: light } + - type: Pullable + - type: Damageable + damageContainer: StructuralInorganic + damageModifierSet: Metallic + - type: Destructible + thresholds: + - trigger: + !type:DamageTrigger + damage: 400 + behaviors: + - !type:DoActsBehavior + acts: [ "Destruction" ] + - trigger: + !type:DamageTrigger + damage: 200 + behaviors: + - !type:PlaySoundBehavior + sound: + collection: MetalBreak + - !type:SpawnEntitiesBehavior + spawn: + BrokenSlotMachine: + min: 1 + max: 1 + SpaceCash: + min: 0 + max: 10 + transferForensics: true + - !type:DoActsBehavior + acts: [ "Destruction" ] + - type: PointLight + radius: 1.8 + energy: 1.6 + color: "#882028" + +- type: entity + parent: BaseMachine + id: BrokenSlotMachine + name: broken slot machine + description: "Apparently, someone was very unlucky and very angry." + suffix: Broken + components: + - type: Sprite + sprite: _Wega/Structures/Machines/slot_machine.rsi + state: broken + - type: Transform + noRot: false + anchored: false + - type: Physics + bodyType: Dynamic + - type: Anchorable + - type: Pullable + - type: Damageable + damageContainer: StructuralInorganic + damageModifierSet: Metallic + - type: Destructible + thresholds: + - trigger: + !type:DamageTrigger + damage: 50 + behaviors: + - !type:PlaySoundBehavior + sound: + collection: MetalBreak + - !type:SpawnEntitiesBehavior + spawn: + SheetSteel: + min: 1 + max: 3 + SheetGlass: + min: 1 + max: 1 + CableApcStack1: + min: 0 + max: 1 + transferForensics: true + - !type:DoActsBehavior + acts: [ "Destruction" ] + +- type: entity + parent: SlotMachine + id: CursedSlotMachine + name: slot machine + description: "High stakes, high rewards. An eerie aura emanates from this machine." + suffix: Cursed, DO NOT MAP + components: + - type: SlotMachine + spinCost: 50 + - type: CursedSlotMachine diff --git a/Resources/Prototypes/_Wega/Entities/Structures/Specific/bloodcult.yml b/Resources/Prototypes/_Wega/Entities/Structures/Specific/bloodcult.yml index e97b0ff969..a0c34aa87a 100644 --- a/Resources/Prototypes/_Wega/Entities/Structures/Specific/bloodcult.yml +++ b/Resources/Prototypes/_Wega/Entities/Structures/Specific/bloodcult.yml @@ -28,7 +28,8 @@ - type: Sprite state: offering - type: BloodRune - prototype: offering + runeType: Offering + desc: "offering-rune-desc" - type: entity id: BloodRuneTeleport @@ -39,7 +40,8 @@ - type: Sprite state: teleport - type: BloodRune - prototype: teleport + runeType: Teleport + desc: "teleport-rune-desc" - type: entity id: BloodRuneEmpowering @@ -50,7 +52,12 @@ - type: Sprite state: empowering - type: BloodRune - prototype: empowering + runeType: Empowering + desc: "empowering-rune-desc" + - type: UserInterface + interfaces: + enum.EmpoweringRuneUiKey.Key: + type: EmpoweringRuneBoundUserInterface - type: entity id: BloodRuneRevive @@ -61,7 +68,8 @@ - type: Sprite state: revive - type: BloodRune - prototype: revive + runeType: Revive + desc: "revive-rune-desc" - type: entity id: BloodRuneBarrier @@ -95,7 +103,8 @@ - !type:DoActsBehavior acts: [ "Destruction" ] - type: BloodRune - prototype: barrier + runeType: Barrier + desc: "barrier-rune-desc" - type: entity id: BloodRuneSummoning @@ -106,7 +115,12 @@ - type: Sprite state: summoning - type: BloodRune - prototype: summoning + runeType: Summoning + desc: "summoning-rune-desc" + - type: UserInterface + interfaces: + enum.SummoningRuneUiKey.Key: + type: SummoningRuneBoundUserInterface - type: entity id: BloodRuneBloodBoil @@ -117,7 +131,8 @@ - type: Sprite state: bloodboil - type: BloodRune - prototype: bloodboil + runeType: BloodBoil + desc: "bloodboil-rune-desc" - type: entity id: BloodRuneSpiritealm @@ -128,7 +143,8 @@ - type: Sprite state: spiritrealm - type: BloodRune - prototype: spiritrealm + runeType: SpiritRealm + desc: "spiritrealm-rune-desc" - type: entity id: BloodRuneRitualDimensionalRending @@ -193,6 +209,10 @@ whitelist: tags: - SoulStone + - type: UserInterface + interfaces: + enum.BloodConstructUiKey.Key: + type: BloodConstructBoundUserInterface - type: entity id: BaseBloodCultStructure @@ -206,6 +226,10 @@ - type: InteractionOutline - type: BloodStructure fixture: fix1 + - type: UserInterface + interfaces: + enum.BloodStructureUiKey.Key: + type: BloodStructureBoundUserInterface - type: entity parent: BaseBloodCultStructure @@ -254,6 +278,7 @@ - ClothingEyesZealotBlindfold - BloodCultVeilShifter - BloodCultShuttleCurse + - CardTarotNotEnchanted - type: Construction graph: BloodCultStructureArchivesGraph node: BloodCultStructureArchives @@ -379,7 +404,6 @@ - type: BloodPylon - type: BloodStructure fixture: fix1 - canInteract: false - type: Damageable damageContainer: Inorganic damageModifierSet: Metallic diff --git a/Resources/Prototypes/_Wega/GameRules/cargo_gifts.yml b/Resources/Prototypes/_Wega/GameRules/cargo_gifts.yml new file mode 100644 index 0000000000..0c0517648e --- /dev/null +++ b/Resources/Prototypes/_Wega/GameRules/cargo_gifts.yml @@ -0,0 +1,14 @@ +- type: entity + id: GiftsGambling + parent: CargoGiftsBase + components: + - type: StationEvent + weight: 3 + duration: 120 + earliestStart: 20 + minimumPlayers: 10 + - type: CargoGiftsRule + description: cargo-gift-gambling + dest: cargo-gift-default-dest + gifts: + SlotMachine: 3 diff --git a/Resources/Prototypes/_Wega/GameRules/roundstart.yml b/Resources/Prototypes/_Wega/GameRules/roundstart.yml index b672927575..4937a361f7 100644 --- a/Resources/Prototypes/_Wega/GameRules/roundstart.yml +++ b/Resources/Prototypes/_Wega/GameRules/roundstart.yml @@ -52,6 +52,9 @@ - type: GameRule minPlayers: 30 - type: BloodCultRule + - type: AntagObjectives + objectives: + - BloodCultRitualObjective - type: AntagSelection definitions: - prefRoles: [ BloodCultist ] @@ -66,9 +69,13 @@ lateJoinAdditional: true components: - type: BloodCultist + - type: MindLink + channels: ["MindBloodCult"] - type: NpcFactionMember factions: - BloodCult + - type: FriendlyFaction + faction: BloodCult mindRoles: - MindRoleBloodCultist briefing: diff --git a/Resources/Prototypes/_Wega/Mind/mindChannels.yml b/Resources/Prototypes/_Wega/Mind/mindChannels.yml new file mode 100644 index 0000000000..5b1807bafd --- /dev/null +++ b/Resources/Prototypes/_Wega/Mind/mindChannels.yml @@ -0,0 +1,5 @@ +- type: mindChannel + id: MindBloodCult + name: blood-cult-mind-channel + keycode: 'ะบ' + color: "#880000" diff --git a/Resources/Prototypes/_Wega/Objectives/bloodcult.yml b/Resources/Prototypes/_Wega/Objectives/bloodcult.yml new file mode 100644 index 0000000000..cf36201631 --- /dev/null +++ b/Resources/Prototypes/_Wega/Objectives/bloodcult.yml @@ -0,0 +1,39 @@ +- type: entity + abstract: true + parent: BaseObjective + id: BaseBloodCultObjective + components: + - type: Objective + issuer: objective-issuer-blood-god + - type: RoleRequirement + roles: + - BloodCultistRole + +# Kill +- type: entity + parent: BaseBloodCultObjective + id: BloodCultTargetObjective + components: + - type: Objective + unique: false + difficulty: 1 + icon: + sprite: _Wega/Objects/Weapons/Melee/blood_dagger.rsi + state: icon + - type: TargetObjective + title: objective-condition-blood-ritual-person-title + - type: BloodCultTargetObjective + +# Complete Ritual +- type: entity + parent: BaseBloodCultObjective + id: BloodCultRitualObjective + name: Complete blood ritual + description: "Geometric Blood is eager to return to this world, and you must perform a ritual to bring it back." + components: + - type: Objective + difficulty: 2 + icon: + sprite: Objects/Fun/Plushies/narsie.rsi + state: icon + - type: BloodCultRitualObjective diff --git a/Resources/Prototypes/_Wega/Polymorphs/polymorph.yml b/Resources/Prototypes/_Wega/Polymorphs/polymorph.yml index 106adb9ed9..11d9a19f42 100644 --- a/Resources/Prototypes/_Wega/Polymorphs/polymorph.yml +++ b/Resources/Prototypes/_Wega/Polymorphs/polymorph.yml @@ -30,3 +30,12 @@ entity: MobZilla forced: true duration: 600 + +#Other +- type: polymorph + id: ChariotStatue + configuration: + entity: StatueVenusRed + transferName: true + forced: true + duration: 120 diff --git a/Resources/Prototypes/_Wega/Reagents/medicine.yml b/Resources/Prototypes/_Wega/Reagents/medicine.yml index 947817fae2..5d471d061b 100644 --- a/Resources/Prototypes/_Wega/Reagents/medicine.yml +++ b/Resources/Prototypes/_Wega/Reagents/medicine.yml @@ -50,7 +50,7 @@ - !type:HealthChange conditions: - !type:MetabolizerTypeCondition - type: [ Cultist ] + type: [ BloodCultist ] damage: types: Blunt: -0.4 diff --git a/Resources/Prototypes/_Wega/Roles/Antags/bloodcultist.yml b/Resources/Prototypes/_Wega/Roles/Antags/bloodcultist.yml index bb0678233a..094e462f35 100644 --- a/Resources/Prototypes/_Wega/Roles/Antags/bloodcultist.yml +++ b/Resources/Prototypes/_Wega/Roles/Antags/bloodcultist.yml @@ -7,7 +7,10 @@ guides: [ BloodCult ] requirements: - !type:OverallPlaytimeRequirement - time: 54000 # 15h # Corvax-RoleTime + time: 50h + - !type:DepartmentTimeRequirement + department: Security + time: 25h # Ability gears - type: startingGear diff --git a/Resources/Prototypes/_Wega/SoundCollections/bloodcultmusic.yml b/Resources/Prototypes/_Wega/SoundCollections/bloodcultmusic.yml new file mode 100644 index 0000000000..0b8e4443c1 --- /dev/null +++ b/Resources/Prototypes/_Wega/SoundCollections/bloodcultmusic.yml @@ -0,0 +1,4 @@ +- type: soundCollection + id: BloodCultMusic + files: + - /Audio/_Wega/StationEvents/tear_of_veil.ogg diff --git a/Resources/Prototypes/_Wega/SoundCollections/roulette.yml b/Resources/Prototypes/_Wega/SoundCollections/roulette.yml new file mode 100644 index 0000000000..51e88d1701 --- /dev/null +++ b/Resources/Prototypes/_Wega/SoundCollections/roulette.yml @@ -0,0 +1,5 @@ +- type: soundCollection + id: CoinDrop + files: + - /Audio/_Wega/Machines/Roulette/coindrop.ogg + - /Audio/_Wega/Machines/Roulette/coindrop2.ogg diff --git a/Resources/Prototypes/_Wega/secret_weights.yml b/Resources/Prototypes/_Wega/secret_weights.yml index 69fb9575d5..0b7ec09247 100644 --- a/Resources/Prototypes/_Wega/secret_weights.yml +++ b/Resources/Prototypes/_Wega/secret_weights.yml @@ -1,10 +1,10 @@ - type: weightedRandom id: WegaSecretDefault weights: - Traitor: 0.375 - Vampire: 0.375 + Traitor: 0.3 + Vampire: 0.3 Nukeops: 0.15 - # BloodCult: 0.15 + BloodCult: 0.15 Revolutionary: 0.15 Survival: 0.05 Zombie: 0.05 diff --git a/Resources/Prototypes/_Wega/status_effects.yml b/Resources/Prototypes/_Wega/status_effects.yml index f647f83572..07b0dd332f 100644 --- a/Resources/Prototypes/_Wega/status_effects.yml +++ b/Resources/Prototypes/_Wega/status_effects.yml @@ -8,4 +8,4 @@ id: Stealth - type: statusEffect - id: StealthOnMove \ No newline at end of file + id: StealthOnMove diff --git a/Resources/Prototypes/_Wega/tags.yml b/Resources/Prototypes/_Wega/tags.yml index 0806bef9d9..532cf61ef5 100644 --- a/Resources/Prototypes/_Wega/tags.yml +++ b/Resources/Prototypes/_Wega/tags.yml @@ -274,3 +274,6 @@ - type: Tag id: Lollipops + +- type: Tag + id: CardTarot diff --git a/Resources/Textures/_Wega/Effects/shield-bloodcult.rsi/meta.json b/Resources/Textures/_Wega/Effects/shield-bloodcult.rsi/meta.json new file mode 100644 index 0000000000..a10dc7a493 --- /dev/null +++ b/Resources/Textures/_Wega/Effects/shield-bloodcult.rsi/meta.json @@ -0,0 +1,15 @@ +{ + "version": 1, + "license": "CC-BY-NC-SA-3.0", + "copyright": "Taken from https://github.com/ss220-space/Paradise", + "size": { + "x": 32, + "y": 32 + }, + "states": [ + { + "name": "shield", + "delays": [ [ 0.1, 0.1, 0.1, 0.1, 0.1, 0.1, 0.1 ] ] + } + ] +} diff --git a/Resources/Textures/_Wega/Effects/shield-bloodcult.rsi/shield.png b/Resources/Textures/_Wega/Effects/shield-bloodcult.rsi/shield.png new file mode 100644 index 0000000000..3bdbd6217e Binary files /dev/null and b/Resources/Textures/_Wega/Effects/shield-bloodcult.rsi/shield.png differ diff --git a/Resources/Textures/_Wega/Interface/Actions/actions_bloodcult.rsi/barrage.png b/Resources/Textures/_Wega/Interface/Actions/actions_bloodcult.rsi/barrage.png index a624914f7b..c9dc4574f3 100644 Binary files a/Resources/Textures/_Wega/Interface/Actions/actions_bloodcult.rsi/barrage.png and b/Resources/Textures/_Wega/Interface/Actions/actions_bloodcult.rsi/barrage.png differ diff --git a/Resources/Textures/_Wega/Interface/Actions/actions_bloodcult.rsi/bg_bloodcult.png b/Resources/Textures/_Wega/Interface/Actions/actions_bloodcult.rsi/bg_bloodcult.png deleted file mode 100644 index 49a4137916..0000000000 Binary files a/Resources/Textures/_Wega/Interface/Actions/actions_bloodcult.rsi/bg_bloodcult.png and /dev/null differ diff --git a/Resources/Textures/_Wega/Interface/Actions/actions_bloodcult.rsi/blood_magic.png b/Resources/Textures/_Wega/Interface/Actions/actions_bloodcult.rsi/blood_magic.png index 4007313fa9..1f8c83d3c0 100644 Binary files a/Resources/Textures/_Wega/Interface/Actions/actions_bloodcult.rsi/blood_magic.png and b/Resources/Textures/_Wega/Interface/Actions/actions_bloodcult.rsi/blood_magic.png differ diff --git a/Resources/Textures/_Wega/Interface/Actions/actions_bloodcult.rsi/blood_rites.png b/Resources/Textures/_Wega/Interface/Actions/actions_bloodcult.rsi/blood_rites.png index 3c0cc79746..a9472c07fb 100644 Binary files a/Resources/Textures/_Wega/Interface/Actions/actions_bloodcult.rsi/blood_rites.png and b/Resources/Textures/_Wega/Interface/Actions/actions_bloodcult.rsi/blood_rites.png differ diff --git a/Resources/Textures/_Wega/Interface/Actions/actions_bloodcult.rsi/comms.png b/Resources/Textures/_Wega/Interface/Actions/actions_bloodcult.rsi/comms.png deleted file mode 100644 index aa614770d2..0000000000 Binary files a/Resources/Textures/_Wega/Interface/Actions/actions_bloodcult.rsi/comms.png and /dev/null differ diff --git a/Resources/Textures/_Wega/Interface/Actions/actions_bloodcult.rsi/concealpresence.png b/Resources/Textures/_Wega/Interface/Actions/actions_bloodcult.rsi/concealpresence.png index 04b9e078ab..1fd520dbfb 100644 Binary files a/Resources/Textures/_Wega/Interface/Actions/actions_bloodcult.rsi/concealpresence.png and b/Resources/Textures/_Wega/Interface/Actions/actions_bloodcult.rsi/concealpresence.png differ diff --git a/Resources/Textures/_Wega/Interface/Actions/actions_bloodcult.rsi/dagger.png b/Resources/Textures/_Wega/Interface/Actions/actions_bloodcult.rsi/dagger.png index 315ac9d46c..eeda007697 100644 Binary files a/Resources/Textures/_Wega/Interface/Actions/actions_bloodcult.rsi/dagger.png and b/Resources/Textures/_Wega/Interface/Actions/actions_bloodcult.rsi/dagger.png differ diff --git a/Resources/Textures/_Wega/Interface/Actions/actions_bloodcult.rsi/electromagneticpulse.png b/Resources/Textures/_Wega/Interface/Actions/actions_bloodcult.rsi/electromagneticpulse.png index 6ae213ec21..8cf6b9014b 100644 Binary files a/Resources/Textures/_Wega/Interface/Actions/actions_bloodcult.rsi/electromagneticpulse.png and b/Resources/Textures/_Wega/Interface/Actions/actions_bloodcult.rsi/electromagneticpulse.png differ diff --git a/Resources/Textures/_Wega/Interface/Actions/actions_bloodcult.rsi/hallucinations.png b/Resources/Textures/_Wega/Interface/Actions/actions_bloodcult.rsi/hallucinations.png index 848e837f78..2a65d2a1dd 100644 Binary files a/Resources/Textures/_Wega/Interface/Actions/actions_bloodcult.rsi/hallucinations.png and b/Resources/Textures/_Wega/Interface/Actions/actions_bloodcult.rsi/hallucinations.png differ diff --git a/Resources/Textures/_Wega/Interface/Actions/actions_bloodcult.rsi/meta.json b/Resources/Textures/_Wega/Interface/Actions/actions_bloodcult.rsi/meta.json index ebc305a473..d51f67cc69 100644 --- a/Resources/Textures/_Wega/Interface/Actions/actions_bloodcult.rsi/meta.json +++ b/Resources/Textures/_Wega/Interface/Actions/actions_bloodcult.rsi/meta.json @@ -7,9 +7,6 @@ "y": 32 }, "states": [ - { - "name": "bg_bloodcult" - }, { "name": "barrage" }, @@ -19,9 +16,6 @@ { "name": "blood_rites" }, - { - "name": "comms" - }, { "name": "concealpresence" }, @@ -67,9 +61,6 @@ { "name": "twistedconstruction" }, - { - "name": "vote" - }, { "name": "wraith_teleport" } diff --git a/Resources/Textures/_Wega/Interface/Actions/actions_bloodcult.rsi/orb.png b/Resources/Textures/_Wega/Interface/Actions/actions_bloodcult.rsi/orb.png index 6134f1c4c2..0b7d9db48b 100644 Binary files a/Resources/Textures/_Wega/Interface/Actions/actions_bloodcult.rsi/orb.png and b/Resources/Textures/_Wega/Interface/Actions/actions_bloodcult.rsi/orb.png differ diff --git a/Resources/Textures/_Wega/Interface/Actions/actions_bloodcult.rsi/recall_dagger.png b/Resources/Textures/_Wega/Interface/Actions/actions_bloodcult.rsi/recall_dagger.png index a23f6b8047..1ad14724ac 100644 Binary files a/Resources/Textures/_Wega/Interface/Actions/actions_bloodcult.rsi/recall_dagger.png and b/Resources/Textures/_Wega/Interface/Actions/actions_bloodcult.rsi/recall_dagger.png differ diff --git a/Resources/Textures/_Wega/Interface/Actions/actions_bloodcult.rsi/recall_spear.png b/Resources/Textures/_Wega/Interface/Actions/actions_bloodcult.rsi/recall_spear.png index ff1b5d1e23..67f82823dd 100644 Binary files a/Resources/Textures/_Wega/Interface/Actions/actions_bloodcult.rsi/recall_spear.png and b/Resources/Textures/_Wega/Interface/Actions/actions_bloodcult.rsi/recall_spear.png differ diff --git a/Resources/Textures/_Wega/Interface/Actions/actions_bloodcult.rsi/recharge.png b/Resources/Textures/_Wega/Interface/Actions/actions_bloodcult.rsi/recharge.png index 3e70305680..5b32dbf2be 100644 Binary files a/Resources/Textures/_Wega/Interface/Actions/actions_bloodcult.rsi/recharge.png and b/Resources/Textures/_Wega/Interface/Actions/actions_bloodcult.rsi/recharge.png differ diff --git a/Resources/Textures/_Wega/Interface/Actions/actions_bloodcult.rsi/shadowshackles.png b/Resources/Textures/_Wega/Interface/Actions/actions_bloodcult.rsi/shadowshackles.png index 90ab470dc9..738a9a183f 100644 Binary files a/Resources/Textures/_Wega/Interface/Actions/actions_bloodcult.rsi/shadowshackles.png and b/Resources/Textures/_Wega/Interface/Actions/actions_bloodcult.rsi/shadowshackles.png differ diff --git a/Resources/Textures/_Wega/Interface/Actions/actions_bloodcult.rsi/spear.png b/Resources/Textures/_Wega/Interface/Actions/actions_bloodcult.rsi/spear.png index e2514f00fc..7dc97d513b 100644 Binary files a/Resources/Textures/_Wega/Interface/Actions/actions_bloodcult.rsi/spear.png and b/Resources/Textures/_Wega/Interface/Actions/actions_bloodcult.rsi/spear.png differ diff --git a/Resources/Textures/_Wega/Interface/Actions/actions_bloodcult.rsi/stun.png b/Resources/Textures/_Wega/Interface/Actions/actions_bloodcult.rsi/stun.png index 9b6e2c5d75..55efde7560 100644 Binary files a/Resources/Textures/_Wega/Interface/Actions/actions_bloodcult.rsi/stun.png and b/Resources/Textures/_Wega/Interface/Actions/actions_bloodcult.rsi/stun.png differ diff --git a/Resources/Textures/_Wega/Interface/Actions/actions_bloodcult.rsi/summonequipment.png b/Resources/Textures/_Wega/Interface/Actions/actions_bloodcult.rsi/summonequipment.png index af7211b4f7..2ae617eeab 100644 Binary files a/Resources/Textures/_Wega/Interface/Actions/actions_bloodcult.rsi/summonequipment.png and b/Resources/Textures/_Wega/Interface/Actions/actions_bloodcult.rsi/summonequipment.png differ diff --git a/Resources/Textures/_Wega/Interface/Actions/actions_bloodcult.rsi/teleport.png b/Resources/Textures/_Wega/Interface/Actions/actions_bloodcult.rsi/teleport.png index f5e1372250..740a03ca5a 100644 Binary files a/Resources/Textures/_Wega/Interface/Actions/actions_bloodcult.rsi/teleport.png and b/Resources/Textures/_Wega/Interface/Actions/actions_bloodcult.rsi/teleport.png differ diff --git a/Resources/Textures/_Wega/Interface/Actions/actions_bloodcult.rsi/twistedconstruction.png b/Resources/Textures/_Wega/Interface/Actions/actions_bloodcult.rsi/twistedconstruction.png index 48da465d11..ff9f53ecb0 100644 Binary files a/Resources/Textures/_Wega/Interface/Actions/actions_bloodcult.rsi/twistedconstruction.png and b/Resources/Textures/_Wega/Interface/Actions/actions_bloodcult.rsi/twistedconstruction.png differ diff --git a/Resources/Textures/_Wega/Interface/Actions/actions_bloodcult.rsi/vote.png b/Resources/Textures/_Wega/Interface/Actions/actions_bloodcult.rsi/vote.png deleted file mode 100644 index 435661dd37..0000000000 Binary files a/Resources/Textures/_Wega/Interface/Actions/actions_bloodcult.rsi/vote.png and /dev/null differ diff --git a/Resources/Textures/_Wega/artificer.png b/Resources/Textures/_Wega/Interface/Actions/artificer.png similarity index 100% rename from Resources/Textures/_Wega/artificer.png rename to Resources/Textures/_Wega/Interface/Actions/artificer.png diff --git a/Resources/Textures/_Wega/claws.png b/Resources/Textures/_Wega/Interface/Actions/claws.png similarity index 100% rename from Resources/Textures/_Wega/claws.png rename to Resources/Textures/_Wega/Interface/Actions/claws.png diff --git a/Resources/Textures/_Wega/cloak.png b/Resources/Textures/_Wega/Interface/Actions/cloak.png similarity index 100% rename from Resources/Textures/_Wega/cloak.png rename to Resources/Textures/_Wega/Interface/Actions/cloak.png diff --git a/Resources/Textures/_Wega/enthrall.png b/Resources/Textures/_Wega/Interface/Actions/enthrall.png similarity index 100% rename from Resources/Textures/_Wega/enthrall.png rename to Resources/Textures/_Wega/Interface/Actions/enthrall.png diff --git a/Resources/Textures/_Wega/harvester.png b/Resources/Textures/_Wega/Interface/Actions/harvester.png similarity index 100% rename from Resources/Textures/_Wega/harvester.png rename to Resources/Textures/_Wega/Interface/Actions/harvester.png diff --git a/Resources/Textures/_Wega/juggernaut.png b/Resources/Textures/_Wega/Interface/Actions/juggernaut.png similarity index 100% rename from Resources/Textures/_Wega/juggernaut.png rename to Resources/Textures/_Wega/Interface/Actions/juggernaut.png diff --git a/Resources/Textures/_Wega/proteon.png b/Resources/Textures/_Wega/Interface/Actions/proteon.png similarity index 100% rename from Resources/Textures/_Wega/proteon.png rename to Resources/Textures/_Wega/Interface/Actions/proteon.png diff --git a/Resources/Textures/_Wega/rush.png b/Resources/Textures/_Wega/Interface/Actions/rush.png similarity index 100% rename from Resources/Textures/_Wega/rush.png rename to Resources/Textures/_Wega/Interface/Actions/rush.png diff --git a/Resources/Textures/_Wega/swell.png b/Resources/Textures/_Wega/Interface/Actions/swell.png similarity index 100% rename from Resources/Textures/_Wega/swell.png rename to Resources/Textures/_Wega/Interface/Actions/swell.png diff --git a/Resources/Textures/_Wega/wraith.png b/Resources/Textures/_Wega/Interface/Actions/wraith.png similarity index 100% rename from Resources/Textures/_Wega/wraith.png rename to Resources/Textures/_Wega/Interface/Actions/wraith.png diff --git a/Resources/Textures/_Wega/Mobs/Demons/bloodcultmobs.rsi/meta.json b/Resources/Textures/_Wega/Mobs/Demons/bloodcultmobs.rsi/meta.json index 43f275777a..d0e4b68ef1 100644 --- a/Resources/Textures/_Wega/Mobs/Demons/bloodcultmobs.rsi/meta.json +++ b/Resources/Textures/_Wega/Mobs/Demons/bloodcultmobs.rsi/meta.json @@ -22,7 +22,7 @@ { "name": "ifrit", "directions": 4, - "delays": [ [ 0.1, 0.1, 0.1, 0.1, 0.1 ], [ 0.1, 0.1, 0.1, 0.1, 0.1 ], [ 0.1, 0.1, 0.1, 0.1, 0.1 ], [ 0.1, 0.1, 0.1, 0.1, 0.1 ] ] + "delays": [ [ 0.1, 0.2, 0.2, 0.2, 0.1 ], [ 0.1, 0.2, 0.2, 0.2, 0.1 ], [ 0.1, 0.2, 0.2, 0.2, 0.1 ], [ 0.1, 0.2, 0.2, 0.2, 0.1 ] ] }, { "name": "juggernaut", diff --git a/Resources/Textures/_Wega/Objects/Misc/cardtarot.rsi/box.png b/Resources/Textures/_Wega/Objects/Misc/cardtarot.rsi/box.png new file mode 100644 index 0000000000..ed7afd596d Binary files /dev/null and b/Resources/Textures/_Wega/Objects/Misc/cardtarot.rsi/box.png differ diff --git a/Resources/Textures/_Wega/Objects/Misc/cardtarot.rsi/chariot-reversed.png b/Resources/Textures/_Wega/Objects/Misc/cardtarot.rsi/chariot-reversed.png new file mode 100644 index 0000000000..f903e8a47d Binary files /dev/null and b/Resources/Textures/_Wega/Objects/Misc/cardtarot.rsi/chariot-reversed.png differ diff --git a/Resources/Textures/_Wega/Objects/Misc/cardtarot.rsi/chariot.png b/Resources/Textures/_Wega/Objects/Misc/cardtarot.rsi/chariot.png new file mode 100644 index 0000000000..eb76e816a9 Binary files /dev/null and b/Resources/Textures/_Wega/Objects/Misc/cardtarot.rsi/chariot.png differ diff --git a/Resources/Textures/_Wega/Objects/Misc/cardtarot.rsi/death-reversed.png b/Resources/Textures/_Wega/Objects/Misc/cardtarot.rsi/death-reversed.png new file mode 100644 index 0000000000..620472119c Binary files /dev/null and b/Resources/Textures/_Wega/Objects/Misc/cardtarot.rsi/death-reversed.png differ diff --git a/Resources/Textures/_Wega/Objects/Misc/cardtarot.rsi/death.png b/Resources/Textures/_Wega/Objects/Misc/cardtarot.rsi/death.png new file mode 100644 index 0000000000..d7396b4e70 Binary files /dev/null and b/Resources/Textures/_Wega/Objects/Misc/cardtarot.rsi/death.png differ diff --git a/Resources/Textures/_Wega/Objects/Misc/cardtarot.rsi/deck.png b/Resources/Textures/_Wega/Objects/Misc/cardtarot.rsi/deck.png new file mode 100644 index 0000000000..a6a7fb226c Binary files /dev/null and b/Resources/Textures/_Wega/Objects/Misc/cardtarot.rsi/deck.png differ diff --git a/Resources/Textures/_Wega/Objects/Misc/cardtarot.rsi/devil-reversed.png b/Resources/Textures/_Wega/Objects/Misc/cardtarot.rsi/devil-reversed.png new file mode 100644 index 0000000000..f1c103b618 Binary files /dev/null and b/Resources/Textures/_Wega/Objects/Misc/cardtarot.rsi/devil-reversed.png differ diff --git a/Resources/Textures/_Wega/Objects/Misc/cardtarot.rsi/devil.png b/Resources/Textures/_Wega/Objects/Misc/cardtarot.rsi/devil.png new file mode 100644 index 0000000000..033af89f1c Binary files /dev/null and b/Resources/Textures/_Wega/Objects/Misc/cardtarot.rsi/devil.png differ diff --git a/Resources/Textures/_Wega/Objects/Misc/cardtarot.rsi/emperor-reversed.png b/Resources/Textures/_Wega/Objects/Misc/cardtarot.rsi/emperor-reversed.png new file mode 100644 index 0000000000..13aeff9bc2 Binary files /dev/null and b/Resources/Textures/_Wega/Objects/Misc/cardtarot.rsi/emperor-reversed.png differ diff --git a/Resources/Textures/_Wega/Objects/Misc/cardtarot.rsi/emperor.png b/Resources/Textures/_Wega/Objects/Misc/cardtarot.rsi/emperor.png new file mode 100644 index 0000000000..964fcf3487 Binary files /dev/null and b/Resources/Textures/_Wega/Objects/Misc/cardtarot.rsi/emperor.png differ diff --git a/Resources/Textures/_Wega/Objects/Misc/cardtarot.rsi/empress-reversed.png b/Resources/Textures/_Wega/Objects/Misc/cardtarot.rsi/empress-reversed.png new file mode 100644 index 0000000000..fc0daf67da Binary files /dev/null and b/Resources/Textures/_Wega/Objects/Misc/cardtarot.rsi/empress-reversed.png differ diff --git a/Resources/Textures/_Wega/Objects/Misc/cardtarot.rsi/empress.png b/Resources/Textures/_Wega/Objects/Misc/cardtarot.rsi/empress.png new file mode 100644 index 0000000000..cc2c88ad89 Binary files /dev/null and b/Resources/Textures/_Wega/Objects/Misc/cardtarot.rsi/empress.png differ diff --git a/Resources/Textures/_Wega/Objects/Misc/cardtarot.rsi/fool-reversed.png b/Resources/Textures/_Wega/Objects/Misc/cardtarot.rsi/fool-reversed.png new file mode 100644 index 0000000000..691d1b13bb Binary files /dev/null and b/Resources/Textures/_Wega/Objects/Misc/cardtarot.rsi/fool-reversed.png differ diff --git a/Resources/Textures/_Wega/Objects/Misc/cardtarot.rsi/fool.png b/Resources/Textures/_Wega/Objects/Misc/cardtarot.rsi/fool.png new file mode 100644 index 0000000000..415ec7c703 Binary files /dev/null and b/Resources/Textures/_Wega/Objects/Misc/cardtarot.rsi/fool.png differ diff --git a/Resources/Textures/_Wega/Objects/Misc/cardtarot.rsi/hangedman-reversed.png b/Resources/Textures/_Wega/Objects/Misc/cardtarot.rsi/hangedman-reversed.png new file mode 100644 index 0000000000..469b086324 Binary files /dev/null and b/Resources/Textures/_Wega/Objects/Misc/cardtarot.rsi/hangedman-reversed.png differ diff --git a/Resources/Textures/_Wega/Objects/Misc/cardtarot.rsi/hangedman.png b/Resources/Textures/_Wega/Objects/Misc/cardtarot.rsi/hangedman.png new file mode 100644 index 0000000000..ab2bb2ccc7 Binary files /dev/null and b/Resources/Textures/_Wega/Objects/Misc/cardtarot.rsi/hangedman.png differ diff --git a/Resources/Textures/_Wega/Objects/Misc/cardtarot.rsi/hermit-reversed.png b/Resources/Textures/_Wega/Objects/Misc/cardtarot.rsi/hermit-reversed.png new file mode 100644 index 0000000000..de81d6f3c0 Binary files /dev/null and b/Resources/Textures/_Wega/Objects/Misc/cardtarot.rsi/hermit-reversed.png differ diff --git a/Resources/Textures/_Wega/Objects/Misc/cardtarot.rsi/hermit.png b/Resources/Textures/_Wega/Objects/Misc/cardtarot.rsi/hermit.png new file mode 100644 index 0000000000..1366d87ee4 Binary files /dev/null and b/Resources/Textures/_Wega/Objects/Misc/cardtarot.rsi/hermit.png differ diff --git a/Resources/Textures/_Wega/Objects/Misc/cardtarot.rsi/hierophant-reversed.png b/Resources/Textures/_Wega/Objects/Misc/cardtarot.rsi/hierophant-reversed.png new file mode 100644 index 0000000000..a1590956e3 Binary files /dev/null and b/Resources/Textures/_Wega/Objects/Misc/cardtarot.rsi/hierophant-reversed.png differ diff --git a/Resources/Textures/_Wega/Objects/Misc/cardtarot.rsi/hierophant.png b/Resources/Textures/_Wega/Objects/Misc/cardtarot.rsi/hierophant.png new file mode 100644 index 0000000000..bf7b236f1b Binary files /dev/null and b/Resources/Textures/_Wega/Objects/Misc/cardtarot.rsi/hierophant.png differ diff --git a/Resources/Textures/_Wega/Objects/Misc/cardtarot.rsi/highpriestess-reversed.png b/Resources/Textures/_Wega/Objects/Misc/cardtarot.rsi/highpriestess-reversed.png new file mode 100644 index 0000000000..b95ef7553e Binary files /dev/null and b/Resources/Textures/_Wega/Objects/Misc/cardtarot.rsi/highpriestess-reversed.png differ diff --git a/Resources/Textures/_Wega/Objects/Misc/cardtarot.rsi/highpriestess.png b/Resources/Textures/_Wega/Objects/Misc/cardtarot.rsi/highpriestess.png new file mode 100644 index 0000000000..b2af398eb0 Binary files /dev/null and b/Resources/Textures/_Wega/Objects/Misc/cardtarot.rsi/highpriestess.png differ diff --git a/Resources/Textures/_Wega/Objects/Misc/cardtarot.rsi/judgement-reversed.png b/Resources/Textures/_Wega/Objects/Misc/cardtarot.rsi/judgement-reversed.png new file mode 100644 index 0000000000..07074ca9ac Binary files /dev/null and b/Resources/Textures/_Wega/Objects/Misc/cardtarot.rsi/judgement-reversed.png differ diff --git a/Resources/Textures/_Wega/Objects/Misc/cardtarot.rsi/judgement.png b/Resources/Textures/_Wega/Objects/Misc/cardtarot.rsi/judgement.png new file mode 100644 index 0000000000..d4e858da16 Binary files /dev/null and b/Resources/Textures/_Wega/Objects/Misc/cardtarot.rsi/judgement.png differ diff --git a/Resources/Textures/_Wega/Objects/Misc/cardtarot.rsi/justice-reversed.png b/Resources/Textures/_Wega/Objects/Misc/cardtarot.rsi/justice-reversed.png new file mode 100644 index 0000000000..f7f6bc337b Binary files /dev/null and b/Resources/Textures/_Wega/Objects/Misc/cardtarot.rsi/justice-reversed.png differ diff --git a/Resources/Textures/_Wega/Objects/Misc/cardtarot.rsi/justice.png b/Resources/Textures/_Wega/Objects/Misc/cardtarot.rsi/justice.png new file mode 100644 index 0000000000..3fc21791e4 Binary files /dev/null and b/Resources/Textures/_Wega/Objects/Misc/cardtarot.rsi/justice.png differ diff --git a/Resources/Textures/_Wega/Objects/Misc/cardtarot.rsi/lovers-reversed.png b/Resources/Textures/_Wega/Objects/Misc/cardtarot.rsi/lovers-reversed.png new file mode 100644 index 0000000000..a24d9dc033 Binary files /dev/null and b/Resources/Textures/_Wega/Objects/Misc/cardtarot.rsi/lovers-reversed.png differ diff --git a/Resources/Textures/_Wega/Objects/Misc/cardtarot.rsi/lovers.png b/Resources/Textures/_Wega/Objects/Misc/cardtarot.rsi/lovers.png new file mode 100644 index 0000000000..8b54705204 Binary files /dev/null and b/Resources/Textures/_Wega/Objects/Misc/cardtarot.rsi/lovers.png differ diff --git a/Resources/Textures/_Wega/Objects/Misc/cardtarot.rsi/magician-reversed.png b/Resources/Textures/_Wega/Objects/Misc/cardtarot.rsi/magician-reversed.png new file mode 100644 index 0000000000..30bb7f8d0f Binary files /dev/null and b/Resources/Textures/_Wega/Objects/Misc/cardtarot.rsi/magician-reversed.png differ diff --git a/Resources/Textures/_Wega/Objects/Misc/cardtarot.rsi/magician.png b/Resources/Textures/_Wega/Objects/Misc/cardtarot.rsi/magician.png new file mode 100644 index 0000000000..6c42808aac Binary files /dev/null and b/Resources/Textures/_Wega/Objects/Misc/cardtarot.rsi/magician.png differ diff --git a/Resources/Textures/_Wega/Objects/Misc/cardtarot.rsi/meta.json b/Resources/Textures/_Wega/Objects/Misc/cardtarot.rsi/meta.json new file mode 100644 index 0000000000..665f104f08 --- /dev/null +++ b/Resources/Textures/_Wega/Objects/Misc/cardtarot.rsi/meta.json @@ -0,0 +1,155 @@ +{ + "version": 1, + "license": "CC-BY-SA-3.0", + "copyright": "Taken from https://github.com/ParadiseSS13/Paradise/blob/HEAD/icons/obj/playing_cards.dmi", + "size": { + "x": 32, + "y": 32 + }, + "states": [ + { + "name": "box" + }, + { + "name": "deck" + }, + { + "name": "notenchanted" + }, + { + "name": "chariot" + }, + { + "name": "chariot-reversed" + }, + { + "name": "death" + }, + { + "name": "death-reversed" + }, + { + "name": "devil" + }, + { + "name": "devil-reversed" + }, + { + "name": "emperor" + }, + { + "name": "emperor-reversed" + }, + { + "name": "empress" + }, + { + "name": "empress-reversed" + }, + { + "name": "fool" + }, + { + "name": "fool-reversed" + }, + { + "name": "hangedman" + }, + { + "name": "hangedman-reversed" + }, + { + "name": "hermit" + }, + { + "name": "hermit-reversed" + }, + { + "name": "hierophant" + }, + { + "name": "hierophant-reversed" + }, + { + "name": "highpriestess" + }, + { + "name": "highpriestess-reversed" + }, + { + "name": "judgement" + }, + { + "name": "judgement-reversed" + }, + { + "name": "justice" + }, + { + "name": "justice-reversed" + }, + { + "name": "lovers" + }, + { + "name": "lovers-reversed" + }, + { + "name": "magician" + }, + { + "name": "magician-reversed" + }, + { + "name": "moon" + }, + { + "name": "moon-reversed" + }, + { + "name": "stars" + }, + { + "name": "stars-reversed" + }, + { + "name": "strength" + }, + { + "name": "strength-reversed" + }, + { + "name": "sun" + }, + { + "name": "sun-reversed" + }, + { + "name": "temperance" + }, + { + "name": "temperance-reversed" + }, + { + "name": "theworld" + }, + { + "name": "theworld-reversed" + }, + { + "name": "tower" + }, + { + "name": "tower-reversed" + }, + { + "name": "unknown" + }, + { + "name": "wheeloffortune" + }, + { + "name": "wheeloffortune-reversed" + } + ] +} diff --git a/Resources/Textures/_Wega/Objects/Misc/cardtarot.rsi/moon-reversed.png b/Resources/Textures/_Wega/Objects/Misc/cardtarot.rsi/moon-reversed.png new file mode 100644 index 0000000000..72c6db787a Binary files /dev/null and b/Resources/Textures/_Wega/Objects/Misc/cardtarot.rsi/moon-reversed.png differ diff --git a/Resources/Textures/_Wega/Objects/Misc/cardtarot.rsi/moon.png b/Resources/Textures/_Wega/Objects/Misc/cardtarot.rsi/moon.png new file mode 100644 index 0000000000..8a0adc8f6f Binary files /dev/null and b/Resources/Textures/_Wega/Objects/Misc/cardtarot.rsi/moon.png differ diff --git a/Resources/Textures/_Wega/Objects/Misc/cardtarot.rsi/notenchanted.png b/Resources/Textures/_Wega/Objects/Misc/cardtarot.rsi/notenchanted.png new file mode 100644 index 0000000000..f20c45ba98 Binary files /dev/null and b/Resources/Textures/_Wega/Objects/Misc/cardtarot.rsi/notenchanted.png differ diff --git a/Resources/Textures/_Wega/Objects/Misc/cardtarot.rsi/stars-reversed.png b/Resources/Textures/_Wega/Objects/Misc/cardtarot.rsi/stars-reversed.png new file mode 100644 index 0000000000..82cdf25e66 Binary files /dev/null and b/Resources/Textures/_Wega/Objects/Misc/cardtarot.rsi/stars-reversed.png differ diff --git a/Resources/Textures/_Wega/Objects/Misc/cardtarot.rsi/stars.png b/Resources/Textures/_Wega/Objects/Misc/cardtarot.rsi/stars.png new file mode 100644 index 0000000000..6585bf8e6b Binary files /dev/null and b/Resources/Textures/_Wega/Objects/Misc/cardtarot.rsi/stars.png differ diff --git a/Resources/Textures/_Wega/Objects/Misc/cardtarot.rsi/strength-reversed.png b/Resources/Textures/_Wega/Objects/Misc/cardtarot.rsi/strength-reversed.png new file mode 100644 index 0000000000..d7fdcbea57 Binary files /dev/null and b/Resources/Textures/_Wega/Objects/Misc/cardtarot.rsi/strength-reversed.png differ diff --git a/Resources/Textures/_Wega/Objects/Misc/cardtarot.rsi/strength.png b/Resources/Textures/_Wega/Objects/Misc/cardtarot.rsi/strength.png new file mode 100644 index 0000000000..cf66949ca9 Binary files /dev/null and b/Resources/Textures/_Wega/Objects/Misc/cardtarot.rsi/strength.png differ diff --git a/Resources/Textures/_Wega/Objects/Misc/cardtarot.rsi/sun-reversed.png b/Resources/Textures/_Wega/Objects/Misc/cardtarot.rsi/sun-reversed.png new file mode 100644 index 0000000000..3781462b35 Binary files /dev/null and b/Resources/Textures/_Wega/Objects/Misc/cardtarot.rsi/sun-reversed.png differ diff --git a/Resources/Textures/_Wega/Objects/Misc/cardtarot.rsi/sun.png b/Resources/Textures/_Wega/Objects/Misc/cardtarot.rsi/sun.png new file mode 100644 index 0000000000..3a7e8771de Binary files /dev/null and b/Resources/Textures/_Wega/Objects/Misc/cardtarot.rsi/sun.png differ diff --git a/Resources/Textures/_Wega/Objects/Misc/cardtarot.rsi/temperance-reversed.png b/Resources/Textures/_Wega/Objects/Misc/cardtarot.rsi/temperance-reversed.png new file mode 100644 index 0000000000..30d56b3eff Binary files /dev/null and b/Resources/Textures/_Wega/Objects/Misc/cardtarot.rsi/temperance-reversed.png differ diff --git a/Resources/Textures/_Wega/Objects/Misc/cardtarot.rsi/temperance.png b/Resources/Textures/_Wega/Objects/Misc/cardtarot.rsi/temperance.png new file mode 100644 index 0000000000..631b6aa3ef Binary files /dev/null and b/Resources/Textures/_Wega/Objects/Misc/cardtarot.rsi/temperance.png differ diff --git a/Resources/Textures/_Wega/Objects/Misc/cardtarot.rsi/theworld-reversed.png b/Resources/Textures/_Wega/Objects/Misc/cardtarot.rsi/theworld-reversed.png new file mode 100644 index 0000000000..c3c94b2bab Binary files /dev/null and b/Resources/Textures/_Wega/Objects/Misc/cardtarot.rsi/theworld-reversed.png differ diff --git a/Resources/Textures/_Wega/Objects/Misc/cardtarot.rsi/theworld.png b/Resources/Textures/_Wega/Objects/Misc/cardtarot.rsi/theworld.png new file mode 100644 index 0000000000..8ea29471a8 Binary files /dev/null and b/Resources/Textures/_Wega/Objects/Misc/cardtarot.rsi/theworld.png differ diff --git a/Resources/Textures/_Wega/Objects/Misc/cardtarot.rsi/tower-reversed.png b/Resources/Textures/_Wega/Objects/Misc/cardtarot.rsi/tower-reversed.png new file mode 100644 index 0000000000..4442cfa467 Binary files /dev/null and b/Resources/Textures/_Wega/Objects/Misc/cardtarot.rsi/tower-reversed.png differ diff --git a/Resources/Textures/_Wega/Objects/Misc/cardtarot.rsi/tower.png b/Resources/Textures/_Wega/Objects/Misc/cardtarot.rsi/tower.png new file mode 100644 index 0000000000..2db125fe42 Binary files /dev/null and b/Resources/Textures/_Wega/Objects/Misc/cardtarot.rsi/tower.png differ diff --git a/Resources/Textures/_Wega/Objects/Misc/cardtarot.rsi/unknown.png b/Resources/Textures/_Wega/Objects/Misc/cardtarot.rsi/unknown.png new file mode 100644 index 0000000000..0f53a132f6 Binary files /dev/null and b/Resources/Textures/_Wega/Objects/Misc/cardtarot.rsi/unknown.png differ diff --git a/Resources/Textures/_Wega/Objects/Misc/cardtarot.rsi/wheeloffortune-reversed.png b/Resources/Textures/_Wega/Objects/Misc/cardtarot.rsi/wheeloffortune-reversed.png new file mode 100644 index 0000000000..352e22b505 Binary files /dev/null and b/Resources/Textures/_Wega/Objects/Misc/cardtarot.rsi/wheeloffortune-reversed.png differ diff --git a/Resources/Textures/_Wega/Objects/Misc/cardtarot.rsi/wheeloffortune.png b/Resources/Textures/_Wega/Objects/Misc/cardtarot.rsi/wheeloffortune.png new file mode 100644 index 0000000000..385d0b2899 Binary files /dev/null and b/Resources/Textures/_Wega/Objects/Misc/cardtarot.rsi/wheeloffortune.png differ diff --git a/Resources/Textures/_Wega/Objects/Weapons/Melee/blood_shifter.rsi/icon.png b/Resources/Textures/_Wega/Objects/Weapons/Melee/blood_shifter.rsi/activated.png similarity index 100% rename from Resources/Textures/_Wega/Objects/Weapons/Melee/blood_shifter.rsi/icon.png rename to Resources/Textures/_Wega/Objects/Weapons/Melee/blood_shifter.rsi/activated.png diff --git a/Resources/Textures/_Wega/Objects/Weapons/Melee/blood_shifter.rsi/base.png b/Resources/Textures/_Wega/Objects/Weapons/Melee/blood_shifter.rsi/base.png new file mode 100644 index 0000000000..3a1be5f5df Binary files /dev/null and b/Resources/Textures/_Wega/Objects/Weapons/Melee/blood_shifter.rsi/base.png differ diff --git a/Resources/Textures/_Wega/Objects/Weapons/Melee/blood_shifter.rsi/meta.json b/Resources/Textures/_Wega/Objects/Weapons/Melee/blood_shifter.rsi/meta.json index ee9ed46651..1c6812065f 100644 --- a/Resources/Textures/_Wega/Objects/Weapons/Melee/blood_shifter.rsi/meta.json +++ b/Resources/Textures/_Wega/Objects/Weapons/Melee/blood_shifter.rsi/meta.json @@ -8,8 +8,11 @@ }, "states": [ { - "name": "icon", - "delays": [ [ 0.1, 0.1, 0.1, 0.1 ] ] + "name": "base" + }, + { + "name": "activated", + "delays": [ [ 0.45, 0.45, 0.45, 0.45 ] ] } ] } diff --git a/Resources/Textures/_Wega/Structures/Machines/slot_machine.rsi/base.png b/Resources/Textures/_Wega/Structures/Machines/slot_machine.rsi/base.png new file mode 100644 index 0000000000..77e23be711 Binary files /dev/null and b/Resources/Textures/_Wega/Structures/Machines/slot_machine.rsi/base.png differ diff --git a/Resources/Textures/_Wega/Structures/Machines/slot_machine.rsi/broken.png b/Resources/Textures/_Wega/Structures/Machines/slot_machine.rsi/broken.png new file mode 100644 index 0000000000..89cd295361 Binary files /dev/null and b/Resources/Textures/_Wega/Structures/Machines/slot_machine.rsi/broken.png differ diff --git a/Resources/Textures/_Wega/Structures/Machines/slot_machine.rsi/light.png b/Resources/Textures/_Wega/Structures/Machines/slot_machine.rsi/light.png new file mode 100644 index 0000000000..e8feb87399 Binary files /dev/null and b/Resources/Textures/_Wega/Structures/Machines/slot_machine.rsi/light.png differ diff --git a/Resources/Textures/_Wega/Structures/Machines/slot_machine.rsi/meta.json b/Resources/Textures/_Wega/Structures/Machines/slot_machine.rsi/meta.json new file mode 100644 index 0000000000..edaa9891a4 --- /dev/null +++ b/Resources/Textures/_Wega/Structures/Machines/slot_machine.rsi/meta.json @@ -0,0 +1,25 @@ +{ + "version": 1, + "license": "CC-BY-SA-3.0", + "copyright": "Taken from https://github.com/tgstation/tgstation", + "size": { + "x": 32, + "y": 32 + }, + "states": [ + { + "name": "base" + }, + { + "name": "broken" + }, + { + "name": "light", + "delays": [ [ 0.1, 0.1, 0.1, 0.1, 0.1, 0.1, 0.1, 0.1, 0.1, 0.1, 0.1, 0.1, 0.1, 0.1, 0.1, 0.1 ] ] + }, + { + "name": "spinning", + "delays": [ [ 0.1, 0.1, 0.1 ] ] + } + ] +} diff --git a/Resources/Textures/_Wega/Structures/Machines/slot_machine.rsi/spinning.png b/Resources/Textures/_Wega/Structures/Machines/slot_machine.rsi/spinning.png new file mode 100644 index 0000000000..d646c19f0f Binary files /dev/null and b/Resources/Textures/_Wega/Structures/Machines/slot_machine.rsi/spinning.png differ diff --git a/Resources/Textures/_Wega/Structures/Specific/bloodcult_structures.rsi/meta.json b/Resources/Textures/_Wega/Structures/Specific/bloodcult_structures.rsi/meta.json index a4c1b21f36..32efcb451c 100644 --- a/Resources/Textures/_Wega/Structures/Specific/bloodcult_structures.rsi/meta.json +++ b/Resources/Textures/_Wega/Structures/Specific/bloodcult_structures.rsi/meta.json @@ -40,7 +40,7 @@ }, { "name": "forge", - "delays": [ [ 0.1, 0.1, 0.1, 0.1 ] ] + "delays": [ [ 0.5, 0.2, 0.2, 0.2 ] ] }, { "name": "offering" diff --git a/Resources/Textures/_Wega/barrage.png b/Resources/Textures/_Wega/barrage.png deleted file mode 100644 index c9dc4574f3..0000000000 Binary files a/Resources/Textures/_Wega/barrage.png and /dev/null differ diff --git a/Resources/Textures/_Wega/blood_rites.png b/Resources/Textures/_Wega/blood_rites.png deleted file mode 100644 index a9472c07fb..0000000000 Binary files a/Resources/Textures/_Wega/blood_rites.png and /dev/null differ diff --git a/Resources/Textures/_Wega/concealpresence.png b/Resources/Textures/_Wega/concealpresence.png deleted file mode 100644 index 1fd520dbfb..0000000000 Binary files a/Resources/Textures/_Wega/concealpresence.png and /dev/null differ diff --git a/Resources/Textures/_Wega/dagger.png b/Resources/Textures/_Wega/dagger.png deleted file mode 100644 index eeda007697..0000000000 Binary files a/Resources/Textures/_Wega/dagger.png and /dev/null differ diff --git a/Resources/Textures/_Wega/electromagneticpulse.png b/Resources/Textures/_Wega/electromagneticpulse.png deleted file mode 100644 index 8cf6b9014b..0000000000 Binary files a/Resources/Textures/_Wega/electromagneticpulse.png and /dev/null differ diff --git a/Resources/Textures/_Wega/hallucinations.png b/Resources/Textures/_Wega/hallucinations.png deleted file mode 100644 index 2a65d2a1dd..0000000000 Binary files a/Resources/Textures/_Wega/hallucinations.png and /dev/null differ diff --git a/Resources/Textures/_Wega/orb.png b/Resources/Textures/_Wega/orb.png deleted file mode 100644 index 0b7d9db48b..0000000000 Binary files a/Resources/Textures/_Wega/orb.png and /dev/null differ diff --git a/Resources/Textures/_Wega/recharge.png b/Resources/Textures/_Wega/recharge.png deleted file mode 100644 index 5b32dbf2be..0000000000 Binary files a/Resources/Textures/_Wega/recharge.png and /dev/null differ diff --git a/Resources/Textures/_Wega/shadowshackles.png b/Resources/Textures/_Wega/shadowshackles.png deleted file mode 100644 index 738a9a183f..0000000000 Binary files a/Resources/Textures/_Wega/shadowshackles.png and /dev/null differ diff --git a/Resources/Textures/_Wega/spear.png b/Resources/Textures/_Wega/spear.png deleted file mode 100644 index 7dc97d513b..0000000000 Binary files a/Resources/Textures/_Wega/spear.png and /dev/null differ diff --git a/Resources/Textures/_Wega/stun.png b/Resources/Textures/_Wega/stun.png deleted file mode 100644 index 55efde7560..0000000000 Binary files a/Resources/Textures/_Wega/stun.png and /dev/null differ diff --git a/Resources/Textures/_Wega/summonequipment.png b/Resources/Textures/_Wega/summonequipment.png deleted file mode 100644 index 2ae617eeab..0000000000 Binary files a/Resources/Textures/_Wega/summonequipment.png and /dev/null differ diff --git a/Resources/Textures/_Wega/teleport.png b/Resources/Textures/_Wega/teleport.png deleted file mode 100644 index 740a03ca5a..0000000000 Binary files a/Resources/Textures/_Wega/teleport.png and /dev/null differ diff --git a/Resources/Textures/_Wega/twistedconstruction.png b/Resources/Textures/_Wega/twistedconstruction.png deleted file mode 100644 index ff9f53ecb0..0000000000 Binary files a/Resources/Textures/_Wega/twistedconstruction.png and /dev/null differ