diff --git a/Content.Client/Antag/AntagStatusIconSystem.cs b/Content.Client/Antag/AntagStatusIconSystem.cs index 804ae21ad4a..3f0f391e48e 100644 --- a/Content.Client/Antag/AntagStatusIconSystem.cs +++ b/Content.Client/Antag/AntagStatusIconSystem.cs @@ -2,6 +2,9 @@ using Content.Shared.Revolutionary.Components; using Content.Shared.StatusIcon; using Content.Shared.StatusIcon.Components; +using Content.Shared.WhiteDream.BloodCult.BloodCultist; +using Content.Shared.WhiteDream.BloodCult.Components; +using Content.Shared.WhiteDream.BloodCult.Constructs; using Content.Shared.Zombies; using Robust.Client.Player; using Robust.Shared.Prototypes; @@ -23,6 +26,10 @@ public override void Initialize() SubscribeLocalEvent(GetIcon); SubscribeLocalEvent(GetIcon); SubscribeLocalEvent(GetIcon); + + SubscribeLocalEvent(GetIcon); + SubscribeLocalEvent(GetBloodCultIcon); + SubscribeLocalEvent(GetIcon); } /// @@ -39,7 +46,6 @@ private void GetIcon(EntityUid uid, T comp, ref GetStatusIconsEvent ev) where ev.StatusIcons.Add(_prototype.Index(comp.StatusIcon)); } - /// /// Adds the Rev Icon on an entity if the player is supposed to see it. This additional function is needed to deal /// with a special case where if someone is a head rev we only want to display the headrev icon. @@ -50,6 +56,13 @@ private void GetRevIcon(EntityUid uid, RevolutionaryComponent comp, ref GetStatu return; GetIcon(uid, comp, ref ev); + } + private void GetBloodCultIcon(EntityUid uid, BloodCultistComponent comp, ref GetStatusIconsEvent ev) + { + if (HasComp(uid)) + return; + + GetIcon(uid, comp, ref ev); } } diff --git a/Content.Client/ListViewSelector/ListViewSelectorBUI.cs b/Content.Client/ListViewSelector/ListViewSelectorBUI.cs new file mode 100644 index 00000000000..f02bab512e8 --- /dev/null +++ b/Content.Client/ListViewSelector/ListViewSelectorBUI.cs @@ -0,0 +1,111 @@ +using Content.Client.Lathe.UI; +using Content.Client.UserInterface.Controls; +using Content.Shared.ListViewSelector; +using JetBrains.Annotations; +using Robust.Client.UserInterface.Controls; +using Robust.Shared.Prototypes; + +// ReSharper disable InconsistentNaming + +namespace Content.Client.ListViewSelector; + +[UsedImplicitly] +public sealed class ListViewSelectorBUI(EntityUid owner, Enum uiKey) : BoundUserInterface(owner, uiKey) +{ + [Dependency] private readonly IPrototypeManager _prototypeManager = default!; + + private FancyWindow _window = new(); + private BoxContainer? _itemsContainer; + private Dictionary _metaData = new(); + + protected override void Open() + { + _window = FormWindow(); + _window.OnClose += Close; + _window.OpenCentered(); + } + + protected override void UpdateState(BoundUserInterfaceState state) + { + base.UpdateState(state); + if (state is not ListViewSelectorState listViewSelectorState) + return; + + PopulateWindow(listViewSelectorState.Items); + _metaData = listViewSelectorState.MetaData; + } + + protected override void Dispose(bool disposing) + { + base.Dispose(disposing); + + if (disposing) + _window.Close(); + } + + private FancyWindow FormWindow() + { + var window = new FancyWindow + { + HorizontalExpand = true, + VerticalExpand = true, + MinWidth = 350, + MinHeight = 400, + Title = Loc.GetString("list-view-window-default-title") + }; + + var scrollContainer = new ScrollContainer + { + HorizontalExpand = true, + VerticalExpand = true + }; + + var itemsContainer = new BoxContainer + { + Orientation = BoxContainer.LayoutOrientation.Vertical + }; + + scrollContainer.AddChild(itemsContainer); + window.AddChild(scrollContainer); + + _itemsContainer = itemsContainer; + + return window; + } + + private void PopulateWindow(List items) + { + if (_itemsContainer is null) + return; + + _itemsContainer.Children.Clear(); + + foreach (var item in items) + { + var itemName = item.Name; + var itemDesc = item.Description; + if (_prototypeManager.TryIndex(item.Id, out var itemPrototype)) + { + itemName = itemPrototype.Name; + itemDesc = itemPrototype.Description; + } + + var button = new Button + { + Text = itemName, + }; + + if (!string.IsNullOrEmpty(itemDesc)) + button.TooltipSupplier = _ => new RecipeTooltip(itemDesc); + + button.OnButtonUp += _ => + { + var msg = new ListViewItemSelectedMessage(item, items.IndexOf(item), _metaData); + SendMessage(msg); + Close(); + }; + + _itemsContainer.AddChild(button); + } + } +} diff --git a/Content.Client/ShortConstruction/ShortConstructionSystem.cs b/Content.Client/ShortConstruction/ShortConstructionSystem.cs index 492411977b7..1b4b324add4 100644 --- a/Content.Client/ShortConstruction/ShortConstructionSystem.cs +++ b/Content.Client/ShortConstruction/ShortConstructionSystem.cs @@ -1,4 +1,5 @@ using Content.Client.Construction; +// using Content.Client.WhiteDream.BloodCult.UI; using Content.Shared.Construction.Prototypes; using Content.Shared.RadialSelector; using Content.Shared.ShortConstruction; @@ -36,11 +37,13 @@ private void OnItemRecieved(Entity ent, ref RadialSe return; } + var hijack = new ConstructionPlacementHijack(_construction, prototype); + _placement.BeginPlacing(new PlacementInformation { IsTile = false, PlacementOption = prototype.PlacementMode }, - new ConstructionPlacementHijack(_construction, prototype)); + hijack); } } diff --git a/Content.Client/WhiteDream/BloodCult/BloodCultistSystem.cs b/Content.Client/WhiteDream/BloodCult/BloodCultistSystem.cs new file mode 100644 index 00000000000..b732df9801c --- /dev/null +++ b/Content.Client/WhiteDream/BloodCult/BloodCultistSystem.cs @@ -0,0 +1,73 @@ +using System.Numerics; +using Content.Shared.Antag; +using Content.Shared.Ghost; +using Content.Shared.StatusIcon.Components; +using Content.Shared.WhiteDream.BloodCult; +using Content.Shared.WhiteDream.BloodCult.BloodCultist; +using Content.Shared.WhiteDream.BloodCult.Components; +using Content.Shared.WhiteDream.BloodCult.Constructs; +using Robust.Client.GameObjects; +using Robust.Shared.Random; +using Robust.Shared.Utility; + +namespace Content.Client.WhiteDream.BloodCult; + +public sealed class BloodCultistSystem : EntitySystem +{ + [Dependency] private readonly IRobustRandom _random = default!; + + public override void Initialize() + { + SubscribeLocalEvent(OnPentagramAdded); + SubscribeLocalEvent(OnPentagramRemoved); + + SubscribeLocalEvent(OnCanShowCultIcon); + SubscribeLocalEvent(OnCanShowCultIcon); + SubscribeLocalEvent(OnCanShowCultIcon); + } + + private void OnPentagramAdded(EntityUid uid, PentagramComponent component, ComponentStartup args) + { + if (!TryComp(uid, out var sprite) || sprite.LayerMapTryGet(PentagramKey.Key, out _)) + return; + + var adj = sprite.Bounds.Height / 2 + 1.0f / 32 * 10.0f; + + var randomState = _random.Pick(component.States); + + var layer = sprite.AddLayer(new SpriteSpecifier.Rsi(component.RsiPath, randomState)); + + sprite.LayerMapSet(PentagramKey.Key, layer); + sprite.LayerSetOffset(layer, new Vector2(0.0f, adj)); + } + + private void OnPentagramRemoved(EntityUid uid, PentagramComponent component, ComponentShutdown args) + { + if (!TryComp(uid, out var sprite) || !sprite.LayerMapTryGet(PentagramKey.Key, out var layer)) + return; + + sprite.RemoveLayer(layer); + } + + /// + /// Determine whether a client should display the cult icon. + /// + private void OnCanShowCultIcon(EntityUid uid, T comp, ref CanDisplayStatusIconsEvent args) + where T : IAntagStatusIconComponent + { + if (!CanDisplayIcon(args.User, comp.IconVisibleToGhost)) + args.Cancelled = true; + } + + /// + /// The criteria that determine whether a client should see Cult/Cult leader icons. + /// + private bool CanDisplayIcon(EntityUid? uid, bool visibleToGhost) + { + if (HasComp(uid) || HasComp(uid) || + HasComp(uid)) + return true; + + return visibleToGhost && HasComp(uid); + } +} diff --git a/Content.Client/WhiteDream/BloodCult/Items/VoidTorch/VoidTorchSystem.cs b/Content.Client/WhiteDream/BloodCult/Items/VoidTorch/VoidTorchSystem.cs new file mode 100644 index 00000000000..8b92fb7da89 --- /dev/null +++ b/Content.Client/WhiteDream/BloodCult/Items/VoidTorch/VoidTorchSystem.cs @@ -0,0 +1,23 @@ +using Content.Client.Light.Components; +using Content.Shared.WhiteDream.BloodCult; +using Content.Shared.WhiteDream.BloodCult.Items.VoidTorch; +using Robust.Client.GameObjects; + +namespace Content.Client.WhiteDream.BloodCult.Items.VoidTorch; + +public sealed class VoidTorchSystem : VisualizerSystem +{ + protected override void OnAppearanceChange(EntityUid uid, + VoidTorchComponent component, + ref AppearanceChangeEvent args) + { + if (args.Sprite == null) + return; + if (!AppearanceSystem.TryGetData(uid, GenericCultVisuals.State, out var state) + || !TryComp(uid, out var lightBehaviour)) + return; + + lightBehaviour.StopLightBehaviour(); + lightBehaviour.StartLightBehaviour(state ? component.TurnOnLightBehaviour : component.TurnOffLightBehaviour); + } +} diff --git a/Content.Client/WhiteDream/BloodCult/NameSelector/NameSelectorBUI.cs b/Content.Client/WhiteDream/BloodCult/NameSelector/NameSelectorBUI.cs new file mode 100644 index 00000000000..6d3788f775c --- /dev/null +++ b/Content.Client/WhiteDream/BloodCult/NameSelector/NameSelectorBUI.cs @@ -0,0 +1,67 @@ +using Content.Client.UserInterface.Controls; +using Content.Shared.WhiteDream.BloodCult.UI; +using JetBrains.Annotations; +using Robust.Client.UserInterface.Controls; + +// ReSharper disable InconsistentNaming + +namespace Content.Client.WhiteDream.BloodCult.NameSelector; + +[UsedImplicitly] +public sealed class NameSelectorBUI(EntityUid owner, Enum uiKey) : BoundUserInterface(owner, uiKey) +{ + private readonly FancyWindow _window = new(); + + protected override void Open() + { + base.Open(); + + FormWindow(); + _window.OpenCentered(); + _window.OnClose += Close; + } + + protected override void Dispose(bool disposing) + { + base.Dispose(disposing); + + if (disposing) + _window.Close(); + } + + private void FormWindow() + { + var container = new BoxContainer + { + Orientation = BoxContainer.LayoutOrientation.Vertical + }; + + var label = new Label + { + Text = Loc.GetString("name-selector-title") + }; + + var lineEdit = new LineEdit + { + HorizontalExpand = true + }; + + var button = new Button + { + Text = Loc.GetString("name-selector-accept-button") + }; + + button.OnButtonUp += _ => + { + var msg = new NameSelectedMessage(lineEdit.Text); + SendMessage(msg); + Close(); + }; + + container.AddChild(label); + container.AddChild(lineEdit); + container.AddChild(button); + + _window.AddChild(container); + } +} diff --git a/Content.Client/WhiteDream/BloodCult/Runes/UI/RuneDrawerBUI.cs b/Content.Client/WhiteDream/BloodCult/Runes/UI/RuneDrawerBUI.cs new file mode 100644 index 00000000000..ed0f27bc5da --- /dev/null +++ b/Content.Client/WhiteDream/BloodCult/Runes/UI/RuneDrawerBUI.cs @@ -0,0 +1,108 @@ +using System.Linq; +using System.Numerics; +using Content.Client.UserInterface.Controls; +using Content.Shared.WhiteDream.BloodCult.Runes; +using JetBrains.Annotations; +using Robust.Client.GameObjects; +using Robust.Client.Graphics; +using Robust.Client.Input; +using Robust.Client.UserInterface; +using Robust.Client.UserInterface.Controls; +using Robust.Shared.Prototypes; + +// ReSharper disable InconsistentNaming + +namespace Content.Client.WhiteDream.BloodCult.Runes.UI; + +[UsedImplicitly] +public sealed class RuneDrawerBUI : BoundUserInterface +{ + [Dependency] private readonly EntityManager _entManager = default!; + [Dependency] private readonly IClyde _displayManager = default!; + [Dependency] private readonly IPrototypeManager _protoManager = default!; + [Dependency] private readonly IInputManager _inputManager = default!; + + private readonly SpriteSystem _spriteSystem; + + private RadialMenu? _menu; + + public RuneDrawerBUI(EntityUid owner, Enum uiKey) : base(owner, uiKey) + { + _spriteSystem = _entManager.System(); + } + + protected override void Open() + { + _menu = FormMenu(); + _menu.OnClose += Close; + _menu.OpenCenteredAt(_inputManager.MouseScreenPosition.Position / _displayManager.ScreenSize); + } + + protected override void Dispose(bool disposing) + { + base.Dispose(disposing); + if (disposing) + _menu?.Dispose(); + } + + private RadialMenu FormMenu() + { + var menu = new RadialMenu + { + HorizontalExpand = true, + VerticalExpand = true, + BackButtonStyleClass = "RadialMenuBackButton", + CloseButtonStyleClass = "RadialMenuCloseButton" + }; + + if (!_entManager.HasComponent(Owner)) + return menu; + + var runeSelectorArray = _protoManager.EnumeratePrototypes().OrderBy(r => r.ID).ToArray(); + + var mainContainer = new RadialContainer + { + Radius = 36f / (runeSelectorArray.Length == 1 + ? 1 + : MathF.Sin(MathF.PI / runeSelectorArray.Length)) + }; + + foreach (var runeSelector in runeSelectorArray) + { + if (!_protoManager.TryIndex(runeSelector.Prototype, out var proto)) + continue; + + var itemSize = new Vector2(64f, 64f); + var button = new RadialMenuTextureButton + { + ToolTip = Loc.GetString(proto.Name), + StyleClasses = { "RadialMenuButton" }, + SetSize = itemSize + }; + + var runeIcon = _spriteSystem.Frame0(proto); + var runeScale = itemSize / runeIcon.Size; + + var texture = new TextureRect + { + VerticalAlignment = Control.VAlignment.Center, + HorizontalAlignment = Control.HAlignment.Center, + Texture = _spriteSystem.Frame0(proto), + TextureScale = runeScale + }; + + button.AddChild(texture); + + button.OnButtonUp += _ => + { + SendMessage(new RuneDrawerSelectedMessage(runeSelector)); + Close(); + }; + + mainContainer.AddChild(button); + } + + menu.AddChild(mainContainer); + return menu; + } +} diff --git a/Content.Client/WhiteDream/BloodCult/UI/AlignPylonConstruction.cs b/Content.Client/WhiteDream/BloodCult/UI/AlignPylonConstruction.cs new file mode 100644 index 00000000000..6749b3f0952 --- /dev/null +++ b/Content.Client/WhiteDream/BloodCult/UI/AlignPylonConstruction.cs @@ -0,0 +1,33 @@ +using System.Linq; +using Content.Shared.WhiteDream.BloodCult.Components; +using Robust.Client.Placement; +using Robust.Client.Placement.Modes; +using Robust.Shared.Map; + +namespace Content.Client.WhiteDream.BloodCult.UI; + +public sealed class AlignPylonConstruction : SnapgridCenter +{ + [Dependency] private readonly IEntityManager _entityManager = default!; + + private readonly EntityLookupSystem _lookup; + + private const float PylonLookupRange = 10; + + public AlignPylonConstruction(PlacementManager pMan) : base(pMan) + { + IoCManager.InjectDependencies(this); + _lookup = _entityManager.System(); + } + + public override bool IsValidPosition(EntityCoordinates position) + { + return base.IsValidPosition(position) && !CheckForOtherPylons(position, PylonLookupRange); + } + + private bool CheckForOtherPylons(EntityCoordinates coordinates, float range) + { + var entities = _lookup.GetEntitiesInRange(coordinates, range); + return entities.Any(_entityManager.HasComponent); + } +} diff --git a/Content.Client/WhiteDream/BloodCult/UI/BloodRitesUi.cs b/Content.Client/WhiteDream/BloodCult/UI/BloodRitesUi.cs new file mode 100644 index 00000000000..9a221b025a5 --- /dev/null +++ b/Content.Client/WhiteDream/BloodCult/UI/BloodRitesUi.cs @@ -0,0 +1,133 @@ +using System.Numerics; +using Content.Client.Popups; +using Content.Client.UserInterface.Controls; +using Content.Shared.FixedPoint; +using Content.Shared.WhiteDream.BloodCult.UI; +using JetBrains.Annotations; +using Robust.Client.GameObjects; +using Robust.Client.Graphics; +using Robust.Client.Input; +using Robust.Client.UserInterface; +using Robust.Client.UserInterface.Controls; +using Robust.Shared.Prototypes; + +namespace Content.Client.WhiteDream.BloodCult.UI; + +[UsedImplicitly] +public sealed class BloodRitesUi : BoundUserInterface +{ + [Dependency] private readonly IClyde _displayManager = default!; + [Dependency] private readonly IInputManager _inputManager = default!; + [Dependency] private readonly IEntityManager _entManager = default!; + [Dependency] private readonly IPrototypeManager _protoManager = default!; + + private readonly PopupSystem _popup; + private readonly SpriteSystem _sprite; + private readonly Vector2 _itemSize = Vector2.One * 64; + + private RadialMenu? _menu; + private FixedPoint2 _storedBlood; + + public BloodRitesUi(EntityUid owner, Enum uiKey) : base(owner, uiKey) + { + _sprite = _entManager.System(); + _popup = _entManager.System(); + } + + protected override void Open() + { + base.Open(); + _menu = new RadialMenu + { + HorizontalExpand = true, + VerticalExpand = true, + BackButtonStyleClass = "RadialMenuBackButton", + CloseButtonStyleClass = "RadialMenuCloseButton" + }; + + _menu.OpenCenteredAt(_inputManager.MouseScreenPosition.Position / _displayManager.ScreenSize); + _menu.OnClose += Close; + } + + protected override void UpdateState(BoundUserInterfaceState state) + { + base.UpdateState(state); + + if (state is not BloodRitesUiState ritesState) + return; + + CreateMenu(ritesState.Crafts); + _storedBlood = ritesState.StoredBlood; + } + + protected override void Dispose(bool disposing) + { + base.Dispose(disposing); + if (disposing && _menu is not null) + _menu.Dispose(); + } + + private void CreateMenu(Dictionary crafts) + { + if (_menu is null) + return; + + var container = new RadialContainer + { + Name = "Blood Rites", + Radius = 64f + 32f * MathF.Log(crafts.Count), + }; + + _menu.AddChild(container); + + foreach (var (protoId, cost) in crafts) + { + if (!_protoManager.TryIndex(protoId, out var proto)) + return; + + var name = $"{cost}: {proto.Name}"; + var button = CreateButton(name, _sprite.Frame0(proto)); + button.OnButtonUp += _ => + { + TryCraft(protoId, cost); + }; + + container.AddChild(button); + } + } + + private RadialMenuTextureButton CreateButton(string name, Texture icon) + { + var button = new RadialMenuTextureButton + { + ToolTip = Loc.GetString(name), + StyleClasses = { "RadialMenuButton" }, + SetSize = _itemSize + }; + + var iconScale = _itemSize / icon.Size; + var texture = new TextureRect + { + VerticalAlignment = Control.VAlignment.Center, + HorizontalAlignment = Control.HAlignment.Center, + Texture = icon, + TextureScale = iconScale + }; + + button.AddChild(texture); + return button; + } + + private void TryCraft(EntProtoId protId, FixedPoint2 cost) + { + if (cost > _storedBlood) + { + _popup.PopupEntity(Loc.GetString("blood-rites-not-enough-blood"), Owner); + return; + } + + _storedBlood -= cost; + var msg = new BloodRitesMessage(protId); + SendPredictedMessage(msg); + } +} diff --git a/Content.Server/Administration/Systems/AdminVerbSystem.Antags.cs b/Content.Server/Administration/Systems/AdminVerbSystem.Antags.cs index 4103b8a8aa7..d9d7943c45c 100644 --- a/Content.Server/Administration/Systems/AdminVerbSystem.Antags.cs +++ b/Content.Server/Administration/Systems/AdminVerbSystem.Antags.cs @@ -1,6 +1,7 @@ using Content.Server.Administration.Commands; using Content.Server.Antag; using Content.Server.GameTicking.Rules.Components; +using Content.Server.WhiteDream.BloodCult.Gamerule; using Content.Server.Zombies; using Content.Shared.Administration; using Content.Shared.Database; @@ -30,6 +31,9 @@ public sealed partial class AdminVerbSystem [ValidatePrototypeId] private const string DefaultThiefRule = "Thief"; + [ValidatePrototypeId] + private const string DefaultBloodCultRule = "BloodCult"; + [ValidatePrototypeId] private const string PirateGearId = "PirateGear"; @@ -134,5 +138,19 @@ private void AddAntagVerbs(GetVerbsEvent args) Message = Loc.GetString("admin-verb-make-thief"), }; args.Verbs.Add(thief); + + Verb cultist = new() + { + Text = Loc.GetString("admin-verb-text-make-blood-cultist"), + Category = VerbCategory.Antag, + Icon = new SpriteSpecifier.Rsi(new("/Textures/Objects/Weapons/Melee/cult_dagger.rsi"), "icon"), + Act = () => + { + _antag.ForceMakeAntag(player, DefaultBloodCultRule); + }, + Impact = LogImpact.High, + Message = Loc.GetString("admin-verb-make-blood-cultist"), + }; + args.Verbs.Add(cultist); } } diff --git a/Content.Server/Body/Systems/BloodstreamSystem.cs b/Content.Server/Body/Systems/BloodstreamSystem.cs index 095018f9b9a..54e51d7e35e 100644 --- a/Content.Server/Body/Systems/BloodstreamSystem.cs +++ b/Content.Server/Body/Systems/BloodstreamSystem.cs @@ -360,7 +360,8 @@ public void SetBloodMaxVolume(EntityUid uid, FixedPoint2 volume, BloodstreamComp /// /// Attempts to modify the blood level of this entity directly. /// - public bool TryModifyBloodLevel(EntityUid uid, FixedPoint2 amount, BloodstreamComponent? component = null) + public bool TryModifyBloodLevel(EntityUid uid, FixedPoint2 amount, BloodstreamComponent? component = null, + bool createPuddle = true) { if (!Resolve(uid, ref component, logMissing: false) || !_solutionContainerSystem.ResolveSolution(uid, component.BloodSolutionName, ref component.BloodSolution)) @@ -381,7 +382,7 @@ public bool TryModifyBloodLevel(EntityUid uid, FixedPoint2 amount, BloodstreamCo tempSolution.AddSolution(newSol, _prototypeManager); - if (tempSolution.Volume > component.BleedPuddleThreshold) + if (tempSolution.Volume > component.BleedPuddleThreshold && createPuddle) { // Pass some of the chemstream into the spilled blood. if (_solutionContainerSystem.ResolveSolution(uid, component.ChemicalSolutionName, ref component.ChemicalSolution)) diff --git a/Content.Server/Chemistry/ReagentEffects/PurifyEvil.cs b/Content.Server/Chemistry/ReagentEffects/PurifyEvil.cs new file mode 100644 index 00000000000..896ecf2eac5 --- /dev/null +++ b/Content.Server/Chemistry/ReagentEffects/PurifyEvil.cs @@ -0,0 +1,49 @@ +using System.Threading; +using Content.Shared.Chemistry.Reagent; +using Content.Shared.Jittering; +using Content.Shared.WhiteDream.BloodCult.BloodCultist; +using JetBrains.Annotations; +using Robust.Shared.Prototypes; + +namespace Content.Server.Chemistry.ReagentEffects; + +[UsedImplicitly] +public sealed partial class PurifyEvil : ReagentEffect +{ + [DataField] + public float Amplitude = 10.0f; + + [DataField] + public float Frequency = 4.0f; + + [DataField] + public TimeSpan Time = TimeSpan.FromSeconds(30.0f); + + protected override string ReagentEffectGuidebookText(IPrototypeManager prototype, IEntitySystemManager entSys) + { + return Loc.GetString("reagent-effect-guidebook-purify-evil"); + } + + public override void Effect(ReagentEffectArgs args) + { + var entityManager = args.EntityManager; + var uid = args.SolutionEntity; + if (!entityManager.TryGetComponent(uid, out BloodCultistComponent? bloodCultist) || + bloodCultist.DeconvertToken is not null) + { + return; + } + + entityManager.System().DoJitter(uid, Time, true, Amplitude, Frequency); + + bloodCultist.DeconvertToken = new CancellationTokenSource(); + Robust.Shared.Timing.Timer.Spawn(Time, () => DeconvertCultist(uid, entityManager), + bloodCultist.DeconvertToken.Token); + } + + private void DeconvertCultist(EntityUid uid, IEntityManager entityManager) + { + if (entityManager.HasComponent(uid)) + entityManager.RemoveComponent(uid); + } +} diff --git a/Content.Server/Ensnaring/EnsnareableSystem.Ensnaring.cs b/Content.Server/Ensnaring/EnsnareableSystem.Ensnaring.cs index 5117d67e944..8dd3d56a1ee 100644 --- a/Content.Server/Ensnaring/EnsnareableSystem.Ensnaring.cs +++ b/Content.Server/Ensnaring/EnsnareableSystem.Ensnaring.cs @@ -11,6 +11,7 @@ using Content.Shared.IdentityManagement; using Content.Shared.StepTrigger.Systems; using Content.Shared.Throwing; +using Content.Shared.Whitelist; namespace Content.Server.Ensnaring; @@ -20,6 +21,7 @@ public sealed partial class EnsnareableSystem [Dependency] private readonly AlertsSystem _alerts = default!; [Dependency] private readonly BodySystem _body = default!; [Dependency] private readonly StaminaSystem _stamina = default!; + [Dependency] private readonly EntityWhitelistSystem _entityWhitelist = default!; public void InitializeEnsnaring() { @@ -70,8 +72,9 @@ private void OnThrowHit(EntityUid uid, EnsnaringComponent component, ThrowDoHitE /// The ensnaring component public void TryEnsnare(EntityUid target, EntityUid ensnare, EnsnaringComponent component) { - //Don't do anything if they don't have the ensnareable component. - if (!TryComp(target, out var ensnareable)) + //Don't do anything if they don't have the ensnareable component or should be ignored. + if (!TryComp(target, out var ensnareable) || + component.IgnoredTargets is not null && _entityWhitelist.IsValid(component.IgnoredTargets, target)) return; var legs = _body.GetBodyChildrenOfType(target, BodyPartType.Leg).Count(); diff --git a/Content.Server/Magic/MagicSystem.cs b/Content.Server/Magic/MagicSystem.cs index c25aada3a07..5997c56e240 100644 --- a/Content.Server/Magic/MagicSystem.cs +++ b/Content.Server/Magic/MagicSystem.cs @@ -18,6 +18,6 @@ public override void Initialize() private void OnSpellSpoken(ref SpeakSpellEvent args) { - _chat.TrySendInGameICMessage(args.Performer, Loc.GetString(args.Speech), InGameICChatType.Speak, false); + _chat.TrySendInGameICMessage(args.Performer, Loc.GetString(args.Speech), args.ChatType, false); } } diff --git a/Content.Server/RoundEnd/RoundEndSystem.cs b/Content.Server/RoundEnd/RoundEndSystem.cs index 0866b975c2b..fe05f69d4ab 100644 --- a/Content.Server/RoundEnd/RoundEndSystem.cs +++ b/Content.Server/RoundEnd/RoundEndSystem.cs @@ -359,6 +359,22 @@ private void ActivateCooldown() }, _cooldownTokenSource.Token); } + public void DelayShuttle(TimeSpan delay) + { + if (_countdownTokenSource == null || !ExpectedCountdownEnd.HasValue) + return; + + var countdown = ExpectedCountdownEnd.Value - _gameTiming.CurTime + delay; + if (countdown.TotalSeconds < 0) + return; + + ExpectedCountdownEnd = _gameTiming.CurTime + countdown; + _countdownTokenSource.Cancel(); + _countdownTokenSource = new CancellationTokenSource(); + + Timer.Spawn(countdown, _shuttle.CallEmergencyShuttle, _countdownTokenSource.Token); + } + public override void Update(float frameTime) { // Check if we should auto-call. diff --git a/Content.Server/Stunnable/Components/StunOnCollideComponent.cs b/Content.Server/Stunnable/Components/StunOnCollideComponent.cs index 1ce1cbea575..cec99f0df72 100644 --- a/Content.Server/Stunnable/Components/StunOnCollideComponent.cs +++ b/Content.Server/Stunnable/Components/StunOnCollideComponent.cs @@ -1,32 +1,40 @@ -namespace Content.Server.Stunnable.Components +using Content.Server.Stunnable.Systems; +using Content.Shared.Whitelist; + +namespace Content.Server.Stunnable.Components; + +/// +/// Adds stun when it collides with an entity +/// +[RegisterComponent, Access(typeof(StunOnCollideSystem))] +public sealed partial class StunOnCollideComponent : Component { - /// - /// Adds stun when it collides with an entity - /// - [RegisterComponent, Access(typeof(StunOnCollideSystem))] - public sealed partial class StunOnCollideComponent : Component - { - // TODO: Can probably predict this. + // TODO: Can probably predict this. - // See stunsystem for what these do - [DataField("stunAmount")] - public int StunAmount; + [DataField] + public TimeSpan StunAmount = TimeSpan.FromSeconds(5); - [DataField("knockdownAmount")] - public int KnockdownAmount; + [DataField] + public TimeSpan KnockdownAmount = TimeSpan.FromSeconds(5); - [DataField("slowdownAmount")] - public int SlowdownAmount; + [DataField] + public TimeSpan SlowdownAmount = TimeSpan.FromSeconds(10); - [DataField("walkSpeedMultiplier")] - public float WalkSpeedMultiplier = 1f; + [DataField] + public float WalkSpeedMultiplier = 1f; - [DataField("runSpeedMultiplier")] - public float RunSpeedMultiplier = 1f; + [DataField] + public float RunSpeedMultiplier = 1f; - /// - /// Fixture we track for the collision. - /// - [DataField("fixture")] public string FixtureID = "projectile"; - } + /// + /// Fixture we track for the collision. + /// + [DataField] + public string FixtureId = "projectile"; + + /// + /// Entities excluded from collision check. + /// + [DataField] + public EntityWhitelist? Blacklist; } diff --git a/Content.Server/Stunnable/Systems/StunOnCollideSystem.cs b/Content.Server/Stunnable/Systems/StunOnCollideSystem.cs index 52e3cab79c5..b7c23477074 100644 --- a/Content.Server/Stunnable/Systems/StunOnCollideSystem.cs +++ b/Content.Server/Stunnable/Systems/StunOnCollideSystem.cs @@ -1,50 +1,51 @@ using Content.Server.Stunnable.Components; -using Content.Shared.Standing; using Content.Shared.StatusEffect; using JetBrains.Annotations; -using Robust.Shared.Physics.Dynamics; using Content.Shared.Throwing; +using Content.Shared.Whitelist; using Robust.Shared.Physics.Events; -namespace Content.Server.Stunnable +namespace Content.Server.Stunnable.Systems; + +[UsedImplicitly] +internal sealed class StunOnCollideSystem : EntitySystem { - [UsedImplicitly] - internal sealed class StunOnCollideSystem : EntitySystem + [Dependency] private readonly StunSystem _stunSystem = default!; + [Dependency] private readonly EntityWhitelistSystem _entityWhitelist = default!; + + public override void Initialize() + { + base.Initialize(); + SubscribeLocalEvent(HandleCollide); + SubscribeLocalEvent(HandleThrow); + } + + private void TryDoCollideStun(Entity ent, EntityUid target) { - [Dependency] private readonly StunSystem _stunSystem = default!; - - public override void Initialize() - { - base.Initialize(); - SubscribeLocalEvent(HandleCollide); - SubscribeLocalEvent(HandleThrow); - } - - private void TryDoCollideStun(EntityUid uid, StunOnCollideComponent component, EntityUid target) - { - - if (EntityManager.TryGetComponent(target, out var status)) - { - _stunSystem.TryStun(target, TimeSpan.FromSeconds(component.StunAmount), true, status); - - _stunSystem.TryKnockdown(target, TimeSpan.FromSeconds(component.KnockdownAmount), true, - status); - - _stunSystem.TrySlowdown(target, TimeSpan.FromSeconds(component.SlowdownAmount), true, - component.WalkSpeedMultiplier, component.RunSpeedMultiplier, status); - } - } - private void HandleCollide(EntityUid uid, StunOnCollideComponent component, ref StartCollideEvent args) - { - if (args.OurFixtureId != component.FixtureID) - return; - - TryDoCollideStun(uid, component, args.OtherEntity); - } - - private void HandleThrow(EntityUid uid, StunOnCollideComponent component, ThrowDoHitEvent args) - { - TryDoCollideStun(uid, component, args.Target); - } + if (!EntityManager.TryGetComponent(target, out var status) || + ent.Comp.Blacklist is { } blacklist && _entityWhitelist.IsValid(blacklist, target)) + return; + + _stunSystem.TryStun(target, ent.Comp.StunAmount, true, status); + _stunSystem.TryKnockdown(target, ent.Comp.KnockdownAmount, true, status); + + _stunSystem.TrySlowdown( + target, + ent.Comp.SlowdownAmount, + true, + ent.Comp.WalkSpeedMultiplier, + ent.Comp.RunSpeedMultiplier, + status); } + + private void HandleCollide(Entity ent, ref StartCollideEvent args) + { + if (args.OurFixtureId != ent.Comp.FixtureId) + return; + + TryDoCollideStun(ent, args.OtherEntity); + } + + private void HandleThrow(Entity ent, ref ThrowDoHitEvent args) => + TryDoCollideStun(ent, args.Target); } diff --git a/Content.Server/Whetstone/WhetstoneComponent.cs b/Content.Server/Whetstone/WhetstoneComponent.cs new file mode 100644 index 00000000000..59fab2e04bf --- /dev/null +++ b/Content.Server/Whetstone/WhetstoneComponent.cs @@ -0,0 +1,34 @@ +using Content.Shared.Damage; +using Content.Shared.FixedPoint; +using Content.Shared.Whitelist; +using Robust.Shared.Audio; + +namespace Content.Server.Whetstone; + +[RegisterComponent] +public sealed partial class WhetstoneComponent : Component +{ + [DataField] + public int Uses = 1; + + [DataField] + public DamageSpecifier DamageIncrease = new() + { + DamageDict = new Dictionary + { + ["Slash"] = 4 + } + }; + + [DataField] + public float MaximumIncrease = 25; + + [DataField] + public EntityWhitelist Whitelist = new(); + + [DataField] + public EntityWhitelist Blacklist = new(); + + [DataField] + public SoundSpecifier SharpenAudio = new SoundPathSpecifier("/Audio/SimpleStation14/Items/Handling/sword_sheath.ogg"); +} diff --git a/Content.Server/Whetstone/WhetstoneSystem.cs b/Content.Server/Whetstone/WhetstoneSystem.cs new file mode 100644 index 00000000000..e0d55a8a6a9 --- /dev/null +++ b/Content.Server/Whetstone/WhetstoneSystem.cs @@ -0,0 +1,52 @@ +using Content.Shared.Interaction; +using Content.Shared.Item; +using Content.Shared.Weapons.Melee; +using Content.Shared.WhiteDream.BloodCult; +using Content.Shared.Whitelist; +using Robust.Server.Audio; +using Robust.Server.GameObjects; +using Robust.Shared.Player; + +namespace Content.Server.Whetstone; + +public sealed class WhetstoneSystem : EntitySystem +{ + [Dependency] private readonly AppearanceSystem _appearance = default!; + [Dependency] private readonly AudioSystem _audio = default!; + [Dependency] private readonly EntityWhitelistSystem _entityWhitelist = default!; + + public override void Initialize() + { + base.Initialize(); + + SubscribeLocalEvent(OnAfterInteract); + } + + private void OnAfterInteract(Entity stone, ref AfterInteractEvent args) + { + if (args.Handled || args.Target is not { } target || stone.Comp.Uses <= 0 || + !TryComp(target, out MeleeWeaponComponent? meleeWeapon) || + !HasComp(target) || // We don't want to sharpen felinids or vulps + _entityWhitelist.IsValid(stone.Comp.Blacklist, target) || + !_entityWhitelist.IsValid(stone.Comp.Whitelist, target)) + return; + + foreach (var (damageTypeId, value) in stone.Comp.DamageIncrease.DamageDict) + { + if (!meleeWeapon.Damage.DamageDict.TryGetValue(damageTypeId, out var defaultDamage) || + defaultDamage > stone.Comp.MaximumIncrease) + continue; + + var newDamage = defaultDamage + value; + if (newDamage > stone.Comp.MaximumIncrease) + newDamage = stone.Comp.MaximumIncrease; + + meleeWeapon.Damage.DamageDict[damageTypeId] = newDamage; + } + + _audio.PlayEntity(stone.Comp.SharpenAudio, Filter.Pvs(target), target, true); + stone.Comp.Uses--; + if (stone.Comp.Uses <= 0) + _appearance.SetData(stone, GenericCultVisuals.State, false); + } +} diff --git a/Content.Server/WhiteDream/BloodCult/BloodBoilProjectile/BloodBoilProjectileComponent.cs b/Content.Server/WhiteDream/BloodCult/BloodBoilProjectile/BloodBoilProjectileComponent.cs new file mode 100644 index 00000000000..931eb247f2e --- /dev/null +++ b/Content.Server/WhiteDream/BloodCult/BloodBoilProjectile/BloodBoilProjectileComponent.cs @@ -0,0 +1,7 @@ +namespace Content.Server.WhiteDream.BloodCult.BloodBoilProjectile; + +[RegisterComponent] +public sealed partial class BloodBoilProjectileComponent : Component +{ + public EntityUid Target; +} diff --git a/Content.Server/WhiteDream/BloodCult/BloodBoilProjectile/BloodBoilProjectileSystem.cs b/Content.Server/WhiteDream/BloodCult/BloodBoilProjectile/BloodBoilProjectileSystem.cs new file mode 100644 index 00000000000..7054416253b --- /dev/null +++ b/Content.Server/WhiteDream/BloodCult/BloodBoilProjectile/BloodBoilProjectileSystem.cs @@ -0,0 +1,19 @@ +using Robust.Shared.Physics.Events; + +namespace Content.Server.WhiteDream.BloodCult.BloodBoilProjectile; + +public sealed class BloodBoilProjectileSystem : EntitySystem +{ + public override void Initialize() + { + base.Initialize(); + + SubscribeLocalEvent(CheckCollision); + } + + private void CheckCollision(Entity ent, ref PreventCollideEvent args) + { + if (args.OtherEntity != ent.Comp.Target) + args.Cancelled = true; + } +} diff --git a/Content.Server/WhiteDream/BloodCult/BloodCultChatSystem.cs b/Content.Server/WhiteDream/BloodCult/BloodCultChatSystem.cs new file mode 100644 index 00000000000..5a9e2055c1f --- /dev/null +++ b/Content.Server/WhiteDream/BloodCult/BloodCultChatSystem.cs @@ -0,0 +1,70 @@ +using System.Linq; +using Content.Server.Administration.Managers; +using Content.Server.Chat.Managers; +using Content.Server.Chat.Systems; +using Content.Server.Language; +using Content.Shared.Chat; +using Content.Shared.Language; +using Content.Shared.WhiteDream.BloodCult.BloodCultist; +using Robust.Shared.Network; +using Robust.Shared.Player; +using Robust.Shared.Utility; + +namespace Content.Server.WhiteDream.BloodCult; + +public sealed class BloodCultChatSystem : EntitySystem +{ + [Dependency] private readonly IAdminManager _adminManager = default!; + [Dependency] private readonly IChatManager _chatManager = default!; + + [Dependency] private readonly LanguageSystem _language = default!; + + public override void Initialize() + { + base.Initialize(); + + SubscribeLocalEvent(OnSpeak); + } + + private void OnSpeak(EntityUid uid, BloodCultistComponent component, EntitySpokeEvent args) + { + if (args.Source != uid || args.Language.ID != component.CultLanguageId || args.IsWhisper) + return; + + SendMessage(args.Source, args.Message, false, args.Language); + } + + private void SendMessage(EntityUid source, string message, bool hideChat, LanguagePrototype language) + { + var clients = GetClients(language.ID); + var playerName = Name(source); + var wrappedMessage = Loc.GetString("chat-manager-send-cult-chat-wrap-message", + ("channelName", Loc.GetString("chat-manager-cult-channel-name")), + ("player", playerName), + ("message", FormattedMessage.EscapeText(message))); + + _chatManager.ChatMessageToMany(ChatChannel.Telepathic, + message, + wrappedMessage, + source, + hideChat, + true, + clients.ToList(), + language.SpeechOverride.Color); + } + + private IEnumerable GetClients(string languageId) + { + return Filter.Empty() + .AddWhereAttachedEntity(entity => CanHearBloodCult(entity, languageId)) + .Recipients + .Union(_adminManager.ActiveAdmins) + .Select(p => p.Channel); + } + + private bool CanHearBloodCult(EntityUid entity, string languageId) + { + var understood = _language.GetUnderstoodLanguages(entity); + return understood.Any(language => language.Id == languageId); + } +} diff --git a/Content.Server/WhiteDream/BloodCult/BloodRites/BloodRitesAuraComponent.cs b/Content.Server/WhiteDream/BloodCult/BloodRites/BloodRitesAuraComponent.cs new file mode 100644 index 00000000000..09e92b31c5a --- /dev/null +++ b/Content.Server/WhiteDream/BloodCult/BloodRites/BloodRitesAuraComponent.cs @@ -0,0 +1,66 @@ +using Content.Shared.DoAfter; +using Content.Shared.FixedPoint; +using Robust.Shared.Audio; +using Robust.Shared.Prototypes; +using Robust.Shared.Utility; + +namespace Content.Server.WhiteDream.BloodCult.BloodRites; + +[RegisterComponent] +public sealed partial class BloodRitesAuraComponent : Component +{ + /// + /// Total blood stored in the Aura. + /// + [DataField] + public FixedPoint2 StoredBlood; + + /// + /// Ratio which is applied to calculate the amount to regenerate blood in someone. + /// + [DataField] + public float BloodRegenerationRatio = 0.1f; + + /// + /// Ratio which is applied to calculate the amount to heal yourself. + /// + [DataField] + public float SelfHealRatio = 2f; + + /// + /// The amount of blood that is extracted from a person on using it on them. + /// + [DataField] + public FixedPoint2 BloodExtractionAmount = 30f; + + /// + /// Time required to extract blood of something with bloodstream. + /// + [DataField] + public TimeSpan BloodExtractionTime = TimeSpan.FromSeconds(5); + + /// + /// How much is consumed on healing. + /// + [DataField] + public FixedPoint2 HealingCost = 40; + + /// + /// How much damage each use of the hand will heal. Will heal literally anything. Nar'sien magic, you know. + /// + [DataField] + public FixedPoint2 TotalHealing = 20; + + [DataField] + public SoundSpecifier BloodRitesAudio = new SoundPathSpecifier( + new ResPath("/Audio/WhiteDream/BloodCult/rites.ogg"), + AudioParams.Default.WithVolume(-3)); + + [DataField] + public Dictionary Crafts = new() + { + ["BloodSpear"] = 300 + }; + + public DoAfterId? ExtractDoAfterId; +} diff --git a/Content.Server/WhiteDream/BloodCult/BloodRites/BloodRitesSystem.cs b/Content.Server/WhiteDream/BloodCult/BloodRites/BloodRitesSystem.cs new file mode 100644 index 00000000000..76adc9bde89 --- /dev/null +++ b/Content.Server/WhiteDream/BloodCult/BloodRites/BloodRitesSystem.cs @@ -0,0 +1,237 @@ +using Content.Server.Body.Components; +using Content.Server.Body.Systems; +using Content.Server.DoAfter; +using Content.Server.Hands.Systems; +using Content.Server.Popups; +using Content.Shared.Chemistry.Components.SolutionManager; +using Content.Shared.Chemistry.EntitySystems; +using Content.Shared.Chemistry.Reagent; +using Content.Shared.Damage; +using Content.Shared.Damage.Prototypes; +using Content.Shared.DoAfter; +using Content.Shared.Examine; +using Content.Shared.Interaction; +using Content.Shared.Mobs; +using Content.Shared.Mobs.Components; +using Content.Shared.UserInterface; +using Content.Shared.Weapons.Melee.Events; +using Content.Shared.WhiteDream.BloodCult.BloodCultist; +using Content.Shared.WhiteDream.BloodCult.Constructs; +using Content.Shared.WhiteDream.BloodCult.Spells; +using Content.Shared.WhiteDream.BloodCult.UI; +using Robust.Server.Audio; +using Robust.Server.GameObjects; +using Robust.Shared.Prototypes; + +namespace Content.Server.WhiteDream.BloodCult.BloodRites; + +public sealed class BloodRitesSystem : EntitySystem +{ + [Dependency] private readonly IPrototypeManager _protoManager = default!; + + [Dependency] private readonly AudioSystem _audio = default!; + [Dependency] private readonly BloodstreamSystem _bloodstream = default!; + [Dependency] private readonly DamageableSystem _damageable = default!; + [Dependency] private readonly DoAfterSystem _doAfter = default!; + [Dependency] private readonly HandsSystem _handsSystem = default!; + [Dependency] private readonly TransformSystem _transform = default!; + [Dependency] private readonly PopupSystem _popup = default!; + [Dependency] private readonly SharedSolutionContainerSystem _solutionContainer = default!; + [Dependency] private readonly UserInterfaceSystem _ui = default!; + + private readonly ProtoId _bloodProto = "Blood"; + + public override void Initialize() + { + SubscribeLocalEvent(OnExamining); + + SubscribeLocalEvent(OnAfterInteract); + SubscribeLocalEvent(OnDoAfter); + + SubscribeLocalEvent(OnCultistHit); + + SubscribeLocalEvent(BeforeUiOpen); + SubscribeLocalEvent(OnRitesMessage); + } + + private void OnExamining(Entity rites, ref ExaminedEvent args) => + args.PushMarkup(Loc.GetString("blood-rites-stored-blood", ("amount", rites.Comp.StoredBlood.ToString()))); + + private void OnAfterInteract(Entity rites, ref AfterInteractEvent args) + { + if (!args.Target.HasValue || args.Handled || args.Target == args.User || + HasComp(args.Target)) + return; + + if (HasComp(args.Target)) + { + if (rites.Comp.ExtractDoAfterId.HasValue) + return; + + var ev = new BloodRitesExtractDoAfterEvent(); + var time = rites.Comp.BloodExtractionTime; + var doAfterArgs = new DoAfterArgs(EntityManager, args.User, time, ev, rites, args.Target) + { + BreakOnUserMove = true, + BreakOnTargetMove = true, + BreakOnDamage = true + }; + if (_doAfter.TryStartDoAfter(doAfterArgs, out var doAfterId)) + rites.Comp.ExtractDoAfterId = doAfterId; + + args.Handled = true; + return; + } + + if (!TryComp(args.Target, out SolutionContainerManagerComponent? solutionContainer)) // please send help + return; + + foreach (var (_, solution) in _solutionContainer.EnumerateSolutions((args.Target.Value, solutionContainer))) + { + // I don't think something will ever have more than 1000 blood units in it's solution... + rites.Comp.StoredBlood += solution.Comp.Solution.RemoveReagent(_bloodProto, 1000); + _solutionContainer.UpdateChemicals(solution); + break; + } + + _audio.PlayPvs(rites.Comp.BloodRitesAudio, rites); + args.Handled = true; + } + + private void OnDoAfter(Entity rites, ref BloodRitesExtractDoAfterEvent args) + { + rites.Comp.ExtractDoAfterId = null; + if (args.Cancelled || args.Handled || args.Target is not { } target || + !TryComp(target, out BloodstreamComponent? bloodstream) || bloodstream.BloodSolution is not { } solution) + return; + + var extracted = solution.Comp.Solution.RemoveReagent(_bloodProto, rites.Comp.BloodExtractionAmount); + rites.Comp.StoredBlood += extracted; + _audio.PlayPvs(rites.Comp.BloodRitesAudio, rites); + args.Handled = true; + } + + private void OnCultistHit(Entity rites, ref MeleeHitEvent args) + { + if (args.HitEntities.Count == 0) + return; + + var playSound = false; + + foreach (var target in args.HitEntities) + { + if (!HasComp(target) && !HasComp(target)) + return; + + if (TryComp(target, out BloodstreamComponent? bloodstream)) + { + if (RestoreBloodLevel(rites, args.User, (target, bloodstream))) + playSound = true; + } + + if (TryComp(target, out DamageableComponent? damageable)) + { + if (Heal(rites, args.User, (target, damageable))) + playSound = true; + } + } + + if (playSound) + _audio.PlayPvs(rites.Comp.BloodRitesAudio, rites); + } + + private void BeforeUiOpen(Entity rites, ref BeforeActivatableUIOpenEvent args) + { + var state = new BloodRitesUiState(rites.Comp.Crafts, rites.Comp.StoredBlood); + _ui.SetUiState(rites.Owner, BloodRitesUiKey.Key, state); + } + + private void OnRitesMessage(Entity rites, ref BloodRitesMessage args) + { + Del(rites); + + var ent = Spawn(args.SelectedProto, _transform.GetMapCoordinates(args.Actor)); + _handsSystem.TryPickup(args.Actor, ent); + } + + private bool Heal(Entity rites, EntityUid user, Entity target) + { + if (target.Comp.TotalDamage == 0) + return false; + + if (TryComp(target, out MobStateComponent? mobState) && mobState.CurrentState == MobState.Dead) + { + _popup.PopupEntity(Loc.GetString("blood-rites-heal-dead"), target, user); + return false; + } + + if (!HasComp(target)) + { + _popup.PopupEntity(Loc.GetString("blood-rites-heal-no-bloodstream"), target, user); + return false; + } + + var bloodCost = rites.Comp.HealingCost; + if (target.Owner == user) + bloodCost *= rites.Comp.SelfHealRatio; + + if (bloodCost >= rites.Comp.StoredBlood) + { + _popup.PopupEntity(Loc.GetString("blood-rites-not-enough-blood"), rites, user); + return false; + } + + var healingLeft = rites.Comp.TotalHealing; + + foreach (var (type, value) in target.Comp.Damage.DamageDict) + { + // somehow? + if (!_protoManager.TryIndex(type, out DamageTypePrototype? damageType)) + continue; + + var toHeal = value; + if (toHeal > healingLeft) + toHeal = healingLeft; + + _damageable.TryChangeDamage(target, new DamageSpecifier(damageType, -toHeal)); + + healingLeft -= toHeal; + if (healingLeft == 0) + break; + } + + rites.Comp.StoredBlood -= bloodCost; + return true; + } + + private bool RestoreBloodLevel( + Entity rites, + EntityUid user, + Entity target + ) + { + if (target.Comp.BloodSolution is null) + return false; + + _bloodstream.FlushChemicals(target, "", 10); + var missingBlood = target.Comp.BloodSolution.Value.Comp.Solution.AvailableVolume; + if (missingBlood == 0) + return false; + + var bloodCost = missingBlood * rites.Comp.BloodRegenerationRatio; + if (target.Owner == user) + bloodCost *= rites.Comp.SelfHealRatio; + + if (bloodCost > rites.Comp.StoredBlood) + { + _popup.PopupEntity("blood-rites-no-blood-left", rites, user); + bloodCost = rites.Comp.StoredBlood; + } + + _bloodstream.TryModifyBleedAmount(target, -3); + _bloodstream.TryModifyBloodLevel(target, bloodCost / rites.Comp.BloodRegenerationRatio); + + rites.Comp.StoredBlood -= bloodCost; + return true; + } +} diff --git a/Content.Server/WhiteDream/BloodCult/Constructs/ConstructSystem.cs b/Content.Server/WhiteDream/BloodCult/Constructs/ConstructSystem.cs new file mode 100644 index 00000000000..fbb800eb0ba --- /dev/null +++ b/Content.Server/WhiteDream/BloodCult/Constructs/ConstructSystem.cs @@ -0,0 +1,59 @@ +using Content.Server.WhiteDream.BloodCult.Gamerule; +using Content.Shared.WhiteDream.BloodCult; +using Content.Shared.WhiteDream.BloodCult.Constructs; +using Robust.Server.GameObjects; + +namespace Content.Server.WhiteDream.BloodCult.Constructs; + +public sealed class ConstructSystem : EntitySystem +{ + [Dependency] private readonly AppearanceSystem _appearanceSystem = default!; + + public override void Initialize() + { + base.Initialize(); + + SubscribeLocalEvent(OnComponentStartup); + SubscribeLocalEvent(OnComponentShutdown); + } + + public override void Update(float frameTime) + { + base.Update(frameTime); + + var query = EntityQueryEnumerator(); + while (query.MoveNext(out var uid, out var construct)) + { + if (!construct.Transforming) + continue; + + construct.TransformAccumulator += frameTime; + if (construct.TransformAccumulator < construct.TransformDelay) + continue; + + construct.TransformAccumulator = 0f; + construct.Transforming = false; + _appearanceSystem.SetData(uid, ConstructVisualsState.Transforming, false); + } + } + + private void OnComponentStartup(Entity ent, ref ComponentStartup args) + { + _appearanceSystem.SetData(ent, ConstructVisualsState.Transforming, true); + ent.Comp.Transforming = true; + var cultistRule = EntityManager.EntityQueryEnumerator(); + while (cultistRule.MoveNext(out _, out var rule)) + { + rule.Constructs.Add(ent); + } + } + + private void OnComponentShutdown(Entity ent, ref ComponentShutdown args) + { + var cultistRule = EntityManager.EntityQueryEnumerator(); + while (cultistRule.MoveNext(out _, out var rule)) + { + rule.Constructs.Remove(ent); + } + } +} diff --git a/Content.Server/WhiteDream/BloodCult/Constructs/Shell/ConstructShellComponent.cs b/Content.Server/WhiteDream/BloodCult/Constructs/Shell/ConstructShellComponent.cs new file mode 100644 index 00000000000..f004959384f --- /dev/null +++ b/Content.Server/WhiteDream/BloodCult/Constructs/Shell/ConstructShellComponent.cs @@ -0,0 +1,29 @@ +using Content.Shared.Containers.ItemSlots; +using Content.Shared.RadialSelector; + +namespace Content.Server.WhiteDream.BloodCult.Constructs.Shell; + +[RegisterComponent] +public sealed partial class ConstructShellComponent : Component +{ + [DataField(required: true)] + public ItemSlot ShardSlot = new(); + + public readonly string ShardSlotId = "Shard"; + + [DataField] + public List Constructs = new() + { + new() { Prototype = "ConstructJuggernaut", }, + new() { Prototype = "ConstructArtificer", }, + new() { Prototype = "ConstructWraith", } + }; + + [DataField] + public List PurifiedConstructs = new() + { + new() { Prototype = "ConstructJuggernautHoly", }, + new() { Prototype = "ConstructArtificerHoly", }, + new() { Prototype = "ConstructWraithHoly", } + }; +} diff --git a/Content.Server/WhiteDream/BloodCult/Constructs/Shell/ConstructShellSystem.cs b/Content.Server/WhiteDream/BloodCult/Constructs/Shell/ConstructShellSystem.cs new file mode 100644 index 00000000000..1923f21f2a6 --- /dev/null +++ b/Content.Server/WhiteDream/BloodCult/Constructs/Shell/ConstructShellSystem.cs @@ -0,0 +1,116 @@ +using Content.Server.Mind; +using Content.Server.Popups; +using Content.Server.WhiteDream.BloodCult.Constructs.SoulShard; +using Content.Shared.Containers.ItemSlots; +using Content.Shared.Mind.Components; +using Content.Shared.RadialSelector; +using Content.Shared.Verbs; +using Robust.Server.GameObjects; +using Robust.Shared.Containers; +using Robust.Shared.Utility; + +namespace Content.Server.WhiteDream.BloodCult.Constructs.Shell; + +public sealed class ConstructShellSystem : EntitySystem +{ + [Dependency] private readonly ItemSlotsSystem _slots = default!; + [Dependency] private readonly MindSystem _mind = default!; + [Dependency] private readonly PopupSystem _popup = default!; + [Dependency] private readonly TransformSystem _transform = default!; + [Dependency] private readonly UserInterfaceSystem _ui = default!; + + public override void Initialize() + { + base.Initialize(); + + SubscribeLocalEvent>(OnGetVerbs); + SubscribeLocalEvent(OnShellInit); + SubscribeLocalEvent(OnInteractUsing); + SubscribeLocalEvent(OnConstructSelected); + SubscribeLocalEvent(OnShellRemove); + } + + private void OnGetVerbs(Entity shell, ref GetVerbsEvent args) + { + var shellUid = shell.Owner; + if (_ui.IsUiOpen(shellUid, RadialSelectorUiKey.Key)) + return; + + // Holy shitcode. + Action action; + if (args.User == shellUid) + { + action = () => + { + _ui.SetUiState(shellUid, RadialSelectorUiKey.Key, new RadialSelectorState(shell.Comp.Constructs, true)); + _ui.TryToggleUi(shellUid, RadialSelectorUiKey.Key, shell); + }; + } + else if (_slots.GetItemOrNull(shell, shell.Comp.ShardSlotId) is { } shard && args.User == shard && + TryComp(shard, out SoulShardComponent? soulShard)) + { + action = () => + { + _ui.SetUiState(shellUid, + RadialSelectorUiKey.Key, + new RadialSelectorState(soulShard.IsBlessed ? shell.Comp.PurifiedConstructs : shell.Comp.Constructs, + true)); + _ui.TryToggleUi(shellUid, RadialSelectorUiKey.Key, shard); + }; + } + else + return; + + args.Verbs.Add(new ExamineVerb + { + DoContactInteraction = true, + Text = Loc.GetString("soul-shard-selector-form"), + Icon = new SpriteSpecifier.Texture( + new ResPath("/Textures/WhiteDream/BloodCult/Entities/Items/construct_shell.rsi")), + Act = action + }); + } + + private void OnShellInit(Entity shell, ref ComponentInit args) + { + _slots.AddItemSlot(shell, shell.Comp.ShardSlotId, shell.Comp.ShardSlot); + } + + private void OnInteractUsing(Entity shell, ref ContainerIsInsertingAttemptEvent args) + { + var shellUid = shell.Owner; + if (!TryComp(args.EntityUid, out SoulShardComponent? soulShard) || + _ui.IsUiOpen(shellUid, RadialSelectorUiKey.Key)) + return; + + if (!TryComp(args.EntityUid, out var mindContainer) || !mindContainer.HasMind) + { + _popup.PopupEntity(Loc.GetString("soul-shard-try-insert-no-soul"), shell); + args.Cancel(); + return; + } + + _slots.SetLock(shell, shell.Comp.ShardSlotId, true); + _ui.SetUiState(shellUid, + RadialSelectorUiKey.Key, + new RadialSelectorState(soulShard.IsBlessed ? shell.Comp.PurifiedConstructs : shell.Comp.Constructs, true)); + + _ui.TryToggleUi(shellUid, RadialSelectorUiKey.Key, args.EntityUid); + } + + private void OnConstructSelected(Entity shell, ref RadialSelectorSelectedMessage args) + { + if (!_mind.TryGetMind(args.Actor, out var mindId, out _)) + return; + + _ui.CloseUi(shell.Owner, RadialSelectorUiKey.Key); + var construct = Spawn(args.SelectedItem, _transform.GetMapCoordinates(shell)); + _mind.TransferTo(mindId, construct); + Del(shell); + } + + private void OnShellRemove(Entity shell, ref ComponentRemove args) + { + _slots.RemoveItemSlot(shell, shell.Comp.ShardSlot); + } +} diff --git a/Content.Server/WhiteDream/BloodCult/Constructs/SoulShard/SoulShardComponent.cs b/Content.Server/WhiteDream/BloodCult/Constructs/SoulShard/SoulShardComponent.cs new file mode 100644 index 00000000000..40340a9550b --- /dev/null +++ b/Content.Server/WhiteDream/BloodCult/Constructs/SoulShard/SoulShardComponent.cs @@ -0,0 +1,21 @@ +using Robust.Shared.Prototypes; + +namespace Content.Server.WhiteDream.BloodCult.Constructs.SoulShard; + +[RegisterComponent] +public sealed partial class SoulShardComponent : Component +{ + [DataField] + public bool IsBlessed; + + [DataField] + public Color BlessedLightColor = Color.LightCyan; + + [DataField] + public EntProtoId ShadeProto = "ShadeCult"; + + [DataField] + public EntProtoId PurifiedShadeProto = "ShadeHoly"; + + public EntityUid? ShadeUid; +} diff --git a/Content.Server/WhiteDream/BloodCult/Constructs/SoulShard/SoulShardSystem.cs b/Content.Server/WhiteDream/BloodCult/Constructs/SoulShard/SoulShardSystem.cs new file mode 100644 index 00000000000..50e92bf2770 --- /dev/null +++ b/Content.Server/WhiteDream/BloodCult/Constructs/SoulShard/SoulShardSystem.cs @@ -0,0 +1,108 @@ +using Content.Server.Bible.Components; +using Content.Server.Mind; +using Content.Server.Popups; +using Content.Server.Roles; +using Content.Shared.Interaction; +using Content.Shared.Mind.Components; +using Content.Shared.Roles; +using Content.Shared.WhiteDream.BloodCult; +using Content.Shared.WhiteDream.BloodCult.BloodCultist; +using Robust.Server.Audio; +using Robust.Server.GameObjects; +using Robust.Shared.Prototypes; + +namespace Content.Server.WhiteDream.BloodCult.Constructs.SoulShard; + +public sealed class SoulShardSystem : EntitySystem +{ + [Dependency] private readonly AppearanceSystem _appearanceSystem = default!; + [Dependency] private readonly AudioSystem _audio = default!; + [Dependency] private readonly SharedPointLightSystem _lightSystem = default!; + [Dependency] private readonly TransformSystem _transform = default!; + [Dependency] private readonly MindSystem _mind = default!; + [Dependency] private readonly SharedRoleSystem _roleSystem = default!; + [Dependency] private readonly PopupSystem _popup = default!; + + public override void Initialize() + { + base.Initialize(); + + SubscribeLocalEvent(OnInteractUsing); + SubscribeLocalEvent(OnActivate); + SubscribeLocalEvent(OnShardMindAdded); + SubscribeLocalEvent(OnShardMindRemoved); + } + + private void OnActivate(Entity shard, ref ActivateInWorldEvent args) + { + if (!_mind.TryGetMind(shard, out var mindId, out _)) + return; + + if (!shard.Comp.IsBlessed) + { + if (!HasComp(args.User)) + return; + if (shard.Comp.ShadeUid.HasValue) + DespawnShade(shard); + else + SpawnShade(shard, shard.Comp.ShadeProto, mindId); + return; + } + + if (shard.Comp.ShadeUid.HasValue) + DespawnShade(shard); + else + SpawnShade(shard, shard.Comp.PurifiedShadeProto, mindId); + } + + private void OnInteractUsing(Entity shard, ref InteractUsingEvent args) + { + if (shard.Comp.IsBlessed || !TryComp(args.Used, out BibleComponent? bible)) + return; + + _popup.PopupEntity(Loc.GetString("bible-sizzle"), args.User, args.User); + _audio.PlayPvs(bible.HealSoundPath, args.User); + _appearanceSystem.SetData(shard, SoulShardVisualState.Blessed, true); + _lightSystem.SetColor(shard, shard.Comp.BlessedLightColor); + shard.Comp.IsBlessed = true; + } + + private void OnShardMindAdded(Entity shard, ref MindAddedMessage args) + { + if (!TryComp(shard, out var mindContainer) || !mindContainer.HasMind) + return; + + _roleSystem.MindTryRemoveRole(mindContainer.Mind.Value); + UpdateGlowVisuals(shard, true); + } + + private void OnShardMindRemoved(Entity shard, ref MindRemovedMessage args) + { + UpdateGlowVisuals(shard, false); + } + + private void SpawnShade(Entity shard, EntProtoId proto, EntityUid mindId) + { + var position = _transform.GetMapCoordinates(shard); + var shadeUid = Spawn(proto, position); + _mind.TransferTo(mindId, shadeUid); + _mind.UnVisit(mindId); + } + + private void DespawnShade(Entity shard) + { + if (!_mind.TryGetMind(shard.Comp.ShadeUid!.Value, out var mindId, out _)) + { + _mind.TransferTo(mindId, shard); + _mind.UnVisit(mindId); + } + + Del(shard.Comp.ShadeUid); + } + + private void UpdateGlowVisuals(Entity shard, bool state) + { + _appearanceSystem.SetData(shard, SoulShardVisualState.HasMind, state); + _lightSystem.SetEnabled(shard, state); + } +} diff --git a/Content.Server/WhiteDream/BloodCult/CultBarrier/BloodCultBarrierComponent.cs b/Content.Server/WhiteDream/BloodCult/CultBarrier/BloodCultBarrierComponent.cs new file mode 100644 index 00000000000..89c8dcc0120 --- /dev/null +++ b/Content.Server/WhiteDream/BloodCult/CultBarrier/BloodCultBarrierComponent.cs @@ -0,0 +1,4 @@ +namespace Content.Server.WhiteDream.BloodCult.CultBarrier; + +[RegisterComponent] +public sealed partial class BloodCultBarrierComponent : Component; diff --git a/Content.Server/WhiteDream/BloodCult/CultBarrier/BloodCultBarrierSystem.cs b/Content.Server/WhiteDream/BloodCult/CultBarrier/BloodCultBarrierSystem.cs new file mode 100644 index 00000000000..a6ac813245d --- /dev/null +++ b/Content.Server/WhiteDream/BloodCult/CultBarrier/BloodCultBarrierSystem.cs @@ -0,0 +1,25 @@ +using Content.Server.Popups; +using Content.Shared.Interaction; +using Content.Shared.WhiteDream.BloodCult.BloodCultist; +using Content.Shared.WhiteDream.BloodCult.Runes; + +namespace Content.Server.WhiteDream.BloodCult.CultBarrier; + +public sealed class BloodCultBarrierSystem : EntitySystem +{ + [Dependency] private readonly PopupSystem _popup = default!; + + public override void Initialize() + { + SubscribeLocalEvent(OnInteract); + } + + private void OnInteract(Entity ent, ref InteractUsingEvent args) + { + if (!HasComp(args.Used) || !HasComp(args.User)) + return; + + _popup.PopupEntity("cult-barrier-destroyed", args.User, args.User); + Del(args.Target); + } +} diff --git a/Content.Server/WhiteDream/BloodCult/Empower/BloodCultEmpoweredComponent.cs b/Content.Server/WhiteDream/BloodCult/Empower/BloodCultEmpoweredComponent.cs new file mode 100644 index 00000000000..dd94ecb1b3f --- /dev/null +++ b/Content.Server/WhiteDream/BloodCult/Empower/BloodCultEmpoweredComponent.cs @@ -0,0 +1,44 @@ +using Content.Shared.Alert; +using Robust.Shared.Prototypes; + +namespace Content.Server.WhiteDream.BloodCult.Empower; + +[RegisterComponent] +public sealed partial class BloodCultEmpoweredComponent : Component +{ + /// + /// Changes the damage from drawing/using runes. + /// + [DataField] + public float RuneDamageMultiplier = 0.5f; + + /// + /// Changes the drawing time of runes. + /// + [DataField] + public float RuneTimeMultiplier = 0.5f; + + /// + /// Increases the amount of spells cultists can create at once. + /// + [DataField] + public int ExtraSpells = 3; + + /// + /// The default duration of the empowering. + /// + [DataField] + public TimeSpan DefaultTime = TimeSpan.FromSeconds(20); + + [DataField] + public float NearbyCultTileRadius = 1f; + + [DataField] + public string CultTile = "CultFloor"; + + [DataField] + public ProtoId EmpoweredAlert = "CultEmpowered"; + + [ViewVariables(VVAccess.ReadOnly)] + public TimeSpan TimeRemaining = TimeSpan.Zero; +} diff --git a/Content.Server/WhiteDream/BloodCult/Empower/BloodCultEmpoweredSystem.cs b/Content.Server/WhiteDream/BloodCult/Empower/BloodCultEmpoweredSystem.cs new file mode 100644 index 00000000000..f7fbdd045e4 --- /dev/null +++ b/Content.Server/WhiteDream/BloodCult/Empower/BloodCultEmpoweredSystem.cs @@ -0,0 +1,88 @@ +using System.Linq; +using System.Numerics; +using Content.Server.WhiteDream.BloodCult.Spells; +using Content.Shared.Alert; +using Content.Shared.WhiteDream.BloodCult.BloodCultist; +using Robust.Server.GameObjects; +using Robust.Shared.Map; +using Robust.Shared.Map.Components; + +namespace Content.Server.WhiteDream.BloodCult.Empower; + +public sealed class BloodCultEmpoweredSystem : EntitySystem +{ + [Dependency] private readonly ITileDefinitionManager _tileDefinition = default!; + + [Dependency] private readonly AlertsSystem _alerts = default!; + [Dependency] private readonly MapSystem _map = default!; + + public override void Initialize() + { + base.Initialize(); + + SubscribeLocalEvent(OnEmpowerStartup); + SubscribeLocalEvent(OnEmpowerShutdown); + } + + public override void Update(float frameTime) + { + base.Update(frameTime); + UpdateTimers(frameTime); + } + + private void OnEmpowerStartup(Entity cultist, ref ComponentStartup args) + { + _alerts.ShowAlert(cultist, cultist.Comp.EmpoweredAlert); + if (TryComp(cultist, out BloodCultSpellsHolderComponent? spellsHolder)) + spellsHolder.MaxSpells += cultist.Comp.ExtraSpells; + } + + private void OnEmpowerShutdown(Entity cultist, ref ComponentShutdown args) + { + _alerts.ClearAlert(cultist, cultist.Comp.EmpoweredAlert); + if (TryComp(cultist, out BloodCultSpellsHolderComponent? spellsHolder)) + spellsHolder.MaxSpells -= cultist.Comp.ExtraSpells; + } + + private void UpdateTimers(float frameTime) + { + var query = EntityQueryEnumerator(); + while (query.MoveNext(out var uid, out var empowered)) + { + if (!HasComp(uid)) + { + RemComp(uid, empowered); + continue; + } + + if (AnyCultTilesNearby((uid, empowered))) + { + empowered.TimeRemaining = empowered.DefaultTime; + continue; + } + + empowered.TimeRemaining -= TimeSpan.FromSeconds(frameTime); + if (empowered.TimeRemaining <= TimeSpan.Zero) + RemComp(uid, empowered); + } + } + + private bool AnyCultTilesNearby(Entity ent) + { + var transform = Transform(ent); + var localpos = transform.Coordinates.Position; + var gridUid = transform.GridUid; + if (!gridUid.HasValue || !TryComp(gridUid, out MapGridComponent? grid)) + return false; + + var cultTile = _tileDefinition[ent.Comp.CultTile]; + + var radius = ent.Comp.NearbyCultTileRadius; + var tilesRefs = _map.GetLocalTilesIntersecting( + gridUid.Value, + grid, + new Box2(localpos + new Vector2(-radius, -radius), localpos + new Vector2(radius, radius))); + + return tilesRefs.Any(tileRef => tileRef.Tile.TypeId == cultTile.TileId); + } +} diff --git a/Content.Server/WhiteDream/BloodCult/Gamerule/BloodCultRuleComponent.cs b/Content.Server/WhiteDream/BloodCult/Gamerule/BloodCultRuleComponent.cs new file mode 100644 index 00000000000..09dc2b542fb --- /dev/null +++ b/Content.Server/WhiteDream/BloodCult/Gamerule/BloodCultRuleComponent.cs @@ -0,0 +1,65 @@ +using Content.Server.NPC.Components; +using Content.Shared.WhiteDream.BloodCult.BloodCultist; +using Content.Shared.WhiteDream.BloodCult.Constructs; +using Robust.Shared.Prototypes; + +namespace Content.Server.WhiteDream.BloodCult.Gamerule; + +[RegisterComponent] +public sealed partial class BloodCultRuleComponent : Component +{ + [DataField] + public ProtoId NanoTrasenFaction = "NanoTrasen"; + + [DataField] + public ProtoId BloodCultFaction = "GeometerOfBlood"; + + [DataField] + public EntProtoId HarvesterPrototype = "ConstructHarvester"; + + [DataField] + public Color EyeColor = Color.FromHex("#f80000"); + + [DataField] + public int ReadEyeThreshold = 5; + + [DataField] + public int PentagramThreshold = 8; + + [ViewVariables(VVAccess.ReadOnly)] + public bool LeaderSelected; + + /// + /// The entityUid of body which should be sacrificed. + /// + [ViewVariables(VVAccess.ReadOnly)] + public EntityUid? OfferingTarget; + + [ViewVariables(VVAccess.ReadOnly)] + public EntityUid? CultLeader; + + [ViewVariables(VVAccess.ReadOnly)] + public CultStage Stage = CultStage.Start; + + public CultWinCondition WinCondition = CultWinCondition.Draw; + + public List> Cultists = new(); + + public List> Constructs = new(); +} + +public enum CultWinCondition : byte +{ + Draw, + Win, + Failure +} + +public enum CultStage : byte +{ + Start, + RedEyes, + Pentagram +} + +public sealed class BloodCultNarsieSummoned : EntityEventArgs; diff --git a/Content.Server/WhiteDream/BloodCult/Gamerule/BloodCultRuleSystem.cs b/Content.Server/WhiteDream/BloodCult/Gamerule/BloodCultRuleSystem.cs new file mode 100644 index 00000000000..c753d929134 --- /dev/null +++ b/Content.Server/WhiteDream/BloodCult/Gamerule/BloodCultRuleSystem.cs @@ -0,0 +1,382 @@ +using System.Diagnostics.CodeAnalysis; +using System.Linq; +using Content.Server.Actions; +using Content.Server.Antag; +using Content.Server.Antag.Components; +using Content.Server.GameTicking; +using Content.Server.GameTicking.Rules; +using Content.Server.Hands.Systems; +using Content.Server.Language; +using Content.Server.NPC.Systems; +using Content.Server.Roles; +using Content.Server.RoundEnd; +using Content.Server.StationEvents.Components; +using Content.Server.WhiteDream.BloodCult.Items.BloodSpear; +using Content.Server.WhiteDream.BloodCult.Objectives; +using Content.Server.WhiteDream.BloodCult.Spells; +using Content.Shared.Body.Systems; +using Content.Shared.Cloning; +using Content.Shared.Cuffs.Components; +using Content.Shared.GameTicking.Components; +using Content.Shared.Humanoid; +using Content.Shared.Inventory; +using Content.Shared.Mind; +using Content.Shared.Mind.Components; +using Content.Shared.Mobs; +using Content.Shared.Mobs.Systems; +using Content.Shared.Mood; +using Content.Shared.Movement.Pulling.Components; +using Content.Shared.Roles; +using Content.Shared.WhiteDream.BloodCult.Components; +using Content.Shared.WhiteDream.BloodCult.BloodCultist; +using Content.Shared.WhiteDream.BloodCult.Items; +using Robust.Server.Containers; +using Robust.Shared.Player; +using Robust.Shared.Random; + +namespace Content.Server.WhiteDream.BloodCult.Gamerule; + +public sealed class BloodCultRuleSystem : GameRuleSystem +{ + [Dependency] private readonly IRobustRandom _random = default!; + + [Dependency] private readonly ActionsSystem _actions = default!; + [Dependency] private readonly AntagSelectionSystem _antagSelection = default!; + [Dependency] private readonly BloodSpearSystem _bloodSpear = default!; + [Dependency] private readonly ContainerSystem _container = default!; + [Dependency] private readonly HandsSystem _hands = default!; + [Dependency] private readonly InventorySystem _inventorySystem = default!; + [Dependency] private readonly LanguageSystem _languageSystem = default!; + [Dependency] private readonly NpcFactionSystem _factionSystem = default!; + [Dependency] private readonly MobStateSystem _mobStateSystem = default!; + [Dependency] private readonly RoundEndSystem _roundEndSystem = default!; + [Dependency] private readonly SharedBodySystem _bodySystem = default!; + [Dependency] private readonly SharedRoleSystem _roleSystem = default!; + [Dependency] private readonly SharedMindSystem _mindSystem = default!; + + public override void Initialize() + { + base.Initialize(); + + SubscribeLocalEvent(AfterEntitySelected); + + SubscribeLocalEvent(OnNarsieSummon); + + SubscribeLocalEvent(OnCultistComponentInit); + SubscribeLocalEvent(OnCultistComponentRemoved); + SubscribeLocalEvent(OnCultistsStateChanged); + SubscribeLocalEvent(OnClone); + + SubscribeLocalEvent(OnGetBriefing); + } + + protected override void Started( + EntityUid uid, + BloodCultRuleComponent component, + GameRuleComponent gameRule, + GameRuleStartedEvent args + ) + { + base.Started(uid, component, gameRule, args); + + component.OfferingTarget = FindTarget(); + } + + protected override void AppendRoundEndText( + EntityUid uid, + BloodCultRuleComponent component, + GameRuleComponent gameRule, + ref RoundEndTextAppendEvent args + ) + { + base.AppendRoundEndText(uid, component, gameRule, ref args); + var winText = Loc.GetString($"blood-cult-condition-{component.WinCondition.ToString().ToLower()}"); + args.AddLine(winText); + + args.AddLine(Loc.GetString("blood-cultists-list-start")); + + var sessionData = _antagSelection.GetAntagIdentifiers(uid); + foreach (var (_, data, name) in sessionData) + { + var lising = Loc.GetString("blood-cultists-list-name", ("name", name), ("user", data.UserName)); + args.AddLine(lising); + } + } + + private void AfterEntitySelected(Entity ent, ref AfterAntagEntitySelectedEvent args) => + MakeCultist(args.EntityUid, ent); + + private void OnNarsieSummon(BloodCultNarsieSummoned ev) + { + var rulesQuery = QueryActiveRules(); + while (rulesQuery.MoveNext(out _, out var cult, out _)) + { + cult.WinCondition = CultWinCondition.Win; + _roundEndSystem.EndRound(); + + foreach (var ent in cult.Cultists) + { + if (Deleted(ent.Owner) || !TryComp(ent.Owner, out MindContainerComponent? mindContainer) || + !mindContainer.Mind.HasValue) + continue; + + var harvester = Spawn(cult.HarvesterPrototype, Transform(ent.Owner).Coordinates); + _mindSystem.TransferTo(mindContainer.Mind.Value, harvester); + _bodySystem.GibBody(ent); + } + + return; + } + } + + private void OnCultistComponentInit(Entity cultist, ref ComponentInit args) + { + RaiseLocalEvent(cultist, new MoodEffectEvent("CultFocused")); + _languageSystem.AddLanguage(cultist, cultist.Comp.CultLanguageId); + + var query = QueryActiveRules(); + while (query.MoveNext(out _, out var cult, out _)) + { + cult.Cultists.Add(cultist); + UpdateCultStage(cult); + } + } + + private void OnCultistComponentRemoved(Entity cultist, ref ComponentRemove args) + { + var query = QueryActiveRules(); + while (query.MoveNext(out _, out var cult, out _)) + cult.Cultists.Remove(cultist); + + CheckRoundShouldEnd(); + + if (TerminatingOrDeleted(cultist.Owner)) + return; + + RemoveAllCultItems(cultist); + RemoveCultistAppearance(cultist); + RemoveObjectiveAndRole(cultist.Owner); + RaiseLocalEvent(cultist.Owner, new MoodRemoveEffectEvent("CultFocused")); + _languageSystem.RemoveLanguage(cultist.Owner, cultist.Comp.CultLanguageId); + + if (!TryComp(cultist, out BloodCultSpellsHolderComponent? powersHolder)) + return; + + foreach (var power in powersHolder.SelectedSpells) + _actions.RemoveAction(cultist.Owner, power); + } + + private void OnCultistsStateChanged(Entity ent, ref MobStateChangedEvent args) + { + if (args.NewMobState == MobState.Dead) + CheckRoundShouldEnd(); + } + + private void OnClone(Entity ent, ref CloningEvent args) => RemoveObjectiveAndRole(ent); + + private void OnGetBriefing(Entity ent, ref GetBriefingEvent args) => + args.Append(Loc.GetString("blood-cult-role-briefing-short")); + + public void Convert(EntityUid target) + { + if (!TryComp(target, out ActorComponent? actor)) + return; + + var query = QueryActiveRules(); + while (query.MoveNext(out var ruleUid, out _, out _, out _)) + { + if (!TryComp(ruleUid, out AntagSelectionComponent? antagSelection)) + continue; + + var antagSelectionEnt = (ruleUid, antagSelection); + if (!_antagSelection.TryGetNextAvailableDefinition(antagSelectionEnt, out var def)) + continue; + + _antagSelection.MakeAntag(antagSelectionEnt, actor.PlayerSession, def.Value); + } + } + + public bool TryGetTarget([NotNullWhen(true)] out EntityUid? target) + { + target = GetTarget(); + return target is not null; + } + + public EntityUid? GetTarget() + { + var query = QueryActiveRules(); + while (query.MoveNext(out _, out var bloodCultRule, out _)) + if (bloodCultRule.OfferingTarget.HasValue) + return bloodCultRule.OfferingTarget.Value; + + return null; + } + + public bool IsTarget(EntityUid entityUid) + { + var query = QueryActiveRules(); + while (query.MoveNext(out _, out var bloodCultRule, out _)) + return entityUid == bloodCultRule.OfferingTarget; + + return false; + } + + public void RemoveObjectiveAndRole(EntityUid uid) + { + if (!_mindSystem.TryGetMind(uid, out var mindId, out var mind)) + return; + + var objectives = mind.Objectives.FindAll(HasComp); + foreach (var obj in objectives) + _mindSystem.TryRemoveObjective(mindId, mind, mind.Objectives.IndexOf(obj)); + + if (_roleSystem.MindHasRole(mindId)) + _roleSystem.MindRemoveRole(mindId); + } + + private void CheckRoundShouldEnd() + { + var query = QueryActiveRules(); + while (query.MoveNext(out _, out var cult, out _)) + { + var aliveCultists = cult.Cultists.Count(cultist => !_mobStateSystem.IsDead(cultist)); + if (aliveCultists != 0) + return; + + cult.WinCondition = CultWinCondition.Failure; + + // Check for all at once gamemode + if (!GameTicker.GetActiveGameRules().Where(HasComp).Any()) + _roundEndSystem.EndRound(); + } + } + + private void MakeCultist(EntityUid cultist, Entity rule) + { + if (!_mindSystem.TryGetMind(cultist, out var mindId, out var mind)) + return; + + EnsureComp(cultist); + + _factionSystem.RemoveFaction(cultist, rule.Comp.NanoTrasenFaction); + _factionSystem.AddFaction(cultist, rule.Comp.BloodCultFaction); + + _mindSystem.TryAddObjective(mindId, mind, "KillTargetCultObjective"); + } + + private EntityUid? FindTarget(ICollection exclude = null!) + { + var querry = EntityManager + .EntityQueryEnumerator(); + + var potentialTargets = new List(); + + while (querry.MoveNext(out var uid, out var mind, out _, out _)) + { + var entity = mind.Mind; + if (entity == default || exclude?.Contains(uid) is true || HasComp(uid)) + continue; + + potentialTargets.Add(uid); + } + + return potentialTargets.Count > 0 ? _random.Pick(potentialTargets) : null; + } + + private void RemoveAllCultItems(Entity cultist) + { + if (!_inventorySystem.TryGetContainerSlotEnumerator(cultist.Owner, out var enumerator)) + return; + + _bloodSpear.DetachSpearFromMaster(cultist); + while (enumerator.MoveNext(out var container)) + if (container.ContainedEntity != null && HasComp(container.ContainedEntity.Value)) + _container.Remove(container.ContainedEntity.Value, container, true, true); + + foreach (var item in _hands.EnumerateHeld(cultist)) + if (TryComp(item, out CultItemComponent? cultItem) && !cultItem.AllowUseToEveryone && + !_hands.TryDrop(cultist, item, null, false, false)) + QueueDel(item); + } + + private void RemoveCultistAppearance(Entity cultist) + { + if (TryComp(cultist, out var appearanceComponent)) + { + appearanceComponent.EyeColor = cultist.Comp.OriginalEyeColor; + Dirty(cultist, appearanceComponent); + } + + RemComp(cultist); + } + + private void UpdateCultStage(BloodCultRuleComponent cultRule) + { + var cultistsCount = cultRule.Cultists.Count; + var prevStage = cultRule.Stage; + + if (cultistsCount >= cultRule.PentagramThreshold) + { + cultRule.Stage = CultStage.Pentagram; + SelectRandomLeader(cultRule); + } + else if (cultistsCount >= cultRule.ReadEyeThreshold) + cultRule.Stage = CultStage.RedEyes; + else + cultRule.Stage = CultStage.Start; + + if (cultRule.Stage != prevStage) + UpdateCultistsAppearance(cultRule, prevStage); + } + + private void UpdateCultistsAppearance(BloodCultRuleComponent cultRule, CultStage prevStage) + { + switch (cultRule.Stage) + { + case CultStage.Start when prevStage == CultStage.RedEyes: + foreach (var cultist in cultRule.Cultists) + RemoveCultistAppearance(cultist); + + break; + case CultStage.RedEyes when prevStage == CultStage.Start: + foreach (var cultist in cultRule.Cultists) + { + if (!TryComp(cultist, out var appearanceComponent)) + continue; + cultist.Comp.OriginalEyeColor = appearanceComponent.EyeColor; + appearanceComponent.EyeColor = cultRule.EyeColor; + Dirty(cultist, appearanceComponent); + } + + break; + case CultStage.Pentagram: + foreach (var cultist in cultRule.Cultists) + EnsureComp(cultist); + + break; + } + } + + /// + /// A crutch while we have no NORMAL voting system. The DarkRP one fucking sucks. + /// + private void SelectRandomLeader(BloodCultRuleComponent cultRule) + { + if (cultRule.LeaderSelected) + return; + + var candidats = cultRule.Cultists; + candidats.RemoveAll( + entity => + TryComp(entity, out PullableComponent? pullable) && pullable.BeingPulled || + TryComp(entity, out CuffableComponent? cuffable) && cuffable.CuffedHandCount > 0); + + if (candidats.Count == 0) + return; + + var leader = _random.Pick(candidats); + AddComp(leader); + cultRule.LeaderSelected = true; + cultRule.CultLeader = leader; + } +} diff --git a/Content.Server/WhiteDream/BloodCult/Items/BloodSpear/BloodSpearComponent.cs b/Content.Server/WhiteDream/BloodCult/Items/BloodSpear/BloodSpearComponent.cs new file mode 100644 index 00000000000..44eb83f05c0 --- /dev/null +++ b/Content.Server/WhiteDream/BloodCult/Items/BloodSpear/BloodSpearComponent.cs @@ -0,0 +1,25 @@ +using Robust.Shared.Audio; +using Robust.Shared.Prototypes; +using Robust.Shared.Utility; + +namespace Content.Server.WhiteDream.BloodCult.Items.BloodSpear; + +[RegisterComponent] +public sealed partial class BloodSpearComponent : Component +{ + [DataField] + public EntityUid? Master; + + [DataField] + public TimeSpan ParalyzeTime = TimeSpan.FromSeconds(4); + + [DataField] + public EntProtoId RecallActionId = "ActionBloodSpearRecall"; + + public EntityUid? RecallAction; + + [DataField] + public SoundSpecifier RecallAudio = new SoundPathSpecifier( + new ResPath("/Audio/WhiteDream/BloodCult/rites.ogg"), + AudioParams.Default.WithVolume(-3)); +} diff --git a/Content.Server/WhiteDream/BloodCult/Items/BloodSpear/BloodSpearSystem.cs b/Content.Server/WhiteDream/BloodCult/Items/BloodSpear/BloodSpearSystem.cs new file mode 100644 index 00000000000..fce72241a41 --- /dev/null +++ b/Content.Server/WhiteDream/BloodCult/Items/BloodSpear/BloodSpearSystem.cs @@ -0,0 +1,84 @@ +using Content.Server.Actions; +using Content.Server.Hands.Systems; +using Content.Server.Stunnable; +using Content.Shared.Humanoid; +using Content.Shared.Item; +using Content.Shared.Projectiles; +using Content.Shared.WhiteDream.BloodCult.BloodCultist; +using Content.Shared.WhiteDream.BloodCult.Spells; +using Robust.Server.Audio; + +namespace Content.Server.WhiteDream.BloodCult.Items.BloodSpear; + +public sealed class BloodSpearSystem : EntitySystem +{ + [Dependency] private readonly ActionsSystem _actions = default!; + [Dependency] private readonly AudioSystem _audio = default!; + [Dependency] private readonly HandsSystem _hands = default!; + [Dependency] private readonly StunSystem _stun = default!; + + public override void Initialize() + { + SubscribeLocalEvent(OnEmbed); + + SubscribeLocalEvent(OnPickedUp); + SubscribeLocalEvent(OnSpearRecalled); + + SubscribeLocalEvent(OnComponentShutdown); + } + + private void OnComponentShutdown(Entity spear, ref ComponentShutdown args) + { + if (!spear.Comp.RecallAction.HasValue) + return; + + _actions.RemoveAction(spear.Comp.RecallAction); + } + + private void OnEmbed(Entity spear, ref EmbedEvent args) + { + if (!HasComp(args.Embedded)) + return; + + _stun.TryParalyze(args.Embedded, spear.Comp.ParalyzeTime, true); + QueueDel(spear); + } + + private void OnPickedUp(Entity spear, ref GettingPickedUpAttemptEvent args) + { + if (args.Cancelled || spear.Comp.Master.HasValue || + !TryComp(args.User, out BloodCultistComponent? bloodCultist)) + return; + + spear.Comp.Master = args.User; + + var action = _actions.AddAction(args.User, spear.Comp.RecallActionId); + spear.Comp.RecallAction = action; + bloodCultist.BloodSpear = spear; + } + + private void OnSpearRecalled(Entity cultist, ref BloodSpearRecalledEvent args) + { + if (args.Handled) + return; + + var spearUid = cultist.Comp.BloodSpear; + if (!spearUid.HasValue || !TryComp(spearUid, out BloodSpearComponent? spear)) + return; + + _hands.TryForcePickupAnyHand(cultist, spearUid.Value); + _audio.PlayPvs(spear.RecallAudio, spearUid.Value); + args.Handled = true; + } + + public void DetachSpearFromMaster(Entity cultist) + { + if (cultist.Comp.BloodSpear is not { } spearUid || !TryComp(spearUid, out BloodSpearComponent? spear)) + return; + + _actions.RemoveAction(spear.RecallAction); + spear.RecallAction = null; + spear.Master = null; + cultist.Comp.BloodSpear = null; + } +} diff --git a/Content.Server/WhiteDream/BloodCult/Items/ShuttleCurse/ShuttleCurseComponent.cs b/Content.Server/WhiteDream/BloodCult/Items/ShuttleCurse/ShuttleCurseComponent.cs new file mode 100644 index 00000000000..1af76de1ffb --- /dev/null +++ b/Content.Server/WhiteDream/BloodCult/Items/ShuttleCurse/ShuttleCurseComponent.cs @@ -0,0 +1,29 @@ +using Robust.Shared.Audio; + +namespace Content.Server.WhiteDream.BloodCult.Items.ShuttleCurse; + +[RegisterComponent] +public sealed partial class ShuttleCurseComponent : Component +{ + [DataField] + public TimeSpan DelayTime = TimeSpan.FromMinutes(3); + + [DataField] + public SoundSpecifier ScatterSound = new SoundCollectionSpecifier("GlassBreak"); + + [DataField] + public List CurseMessages = new() + { + "shuttle-curse-message-1", + "shuttle-curse-message-2", + "shuttle-curse-message-3", + "shuttle-curse-message-4", + "shuttle-curse-message-5", + "shuttle-curse-message-6", + "shuttle-curse-message-7", + "shuttle-curse-message-8", + "shuttle-curse-message-9", + "shuttle-curse-message-10", + "shuttle-curse-message-11", + }; +} diff --git a/Content.Server/WhiteDream/BloodCult/Items/ShuttleCurse/ShuttleCurseProviderComponent.cs b/Content.Server/WhiteDream/BloodCult/Items/ShuttleCurse/ShuttleCurseProviderComponent.cs new file mode 100644 index 00000000000..9dd4b5d0dc2 --- /dev/null +++ b/Content.Server/WhiteDream/BloodCult/Items/ShuttleCurse/ShuttleCurseProviderComponent.cs @@ -0,0 +1,11 @@ +namespace Content.Server.WhiteDream.BloodCult.Items.ShuttleCurse; + +[RegisterComponent] +public sealed partial class ShuttleCurseProviderComponent : Component +{ + [DataField] + public int MaxUses = 3; + + [ViewVariables(VVAccess.ReadOnly)] + public int CurrentUses = 0; +} diff --git a/Content.Server/WhiteDream/BloodCult/Items/ShuttleCurse/ShuttleCurseSystem.cs b/Content.Server/WhiteDream/BloodCult/Items/ShuttleCurse/ShuttleCurseSystem.cs new file mode 100644 index 00000000000..7e23649f7b8 --- /dev/null +++ b/Content.Server/WhiteDream/BloodCult/Items/ShuttleCurse/ShuttleCurseSystem.cs @@ -0,0 +1,81 @@ +using Content.Server.Chat.Systems; +using Content.Server.Popups; +using Content.Server.RoundEnd; +using Content.Server.Shuttles.Systems; +using Content.Shared.Interaction; +using Robust.Server.Audio; +using Robust.Shared.Player; +using Robust.Shared.Random; + +namespace Content.Server.WhiteDream.BloodCult.Items.ShuttleCurse; + +public sealed class ShuttleCurseSystem : EntitySystem +{ + [Dependency] private readonly IRobustRandom _random = default!; + + [Dependency] private readonly AudioSystem _audio = default!; + [Dependency] private readonly ChatSystem _chat = default!; + [Dependency] private readonly EmergencyShuttleSystem _emergencyShuttle = default!; + [Dependency] private readonly RoundEndSystem _roundEnd = default!; + [Dependency] private readonly PopupSystem _popup = default!; + + public override void Initialize() + { + base.Initialize(); + + SubscribeLocalEvent(OnActivate); + } + + private void OnActivate(Entity orb, ref ActivateInWorldEvent args) + { + if (args.Handled) + return; + + var curseProvider = EnsureShuttleCurseProvider(orb); + if (curseProvider is null) + { + _popup.PopupEntity(Loc.GetString("shuttle-curse-cant-activate"), orb, args.User); + return; + } + + if (curseProvider.CurrentUses >= curseProvider.MaxUses) + { + _popup.PopupEntity(Loc.GetString("shuttle-curse-max-charges"), orb, args.User); + return; + } + + if (_emergencyShuttle.EmergencyShuttleArrived) + { + _popup.PopupEntity(Loc.GetString("shuttle-curse-shuttle-arrived"), orb, args.User); + return; + } + + if (_roundEnd.ExpectedCountdownEnd is null) + { + _popup.PopupEntity(Loc.GetString("shuttle-curse-shuttle-not-called"), orb, args.User); + return; + } + + _roundEnd.DelayShuttle(orb.Comp.DelayTime); + + var cursedMessage = string.Concat(Loc.GetString(_random.Pick(orb.Comp.CurseMessages)), + " ", + Loc.GetString("shuttle-curse-success-global", ("time", orb.Comp.DelayTime.TotalMinutes))); + + _chat.DispatchGlobalAnnouncement(cursedMessage, + Loc.GetString("shuttle-curse-system-failure"), + colorOverride: Color.Gold); + + _popup.PopupEntity(Loc.GetString("shuttle-curse-success"), args.User, args.User); + curseProvider.CurrentUses++; + + _audio.PlayEntity(orb.Comp.ScatterSound, Filter.Pvs(orb), orb, true); + Del(orb); + } + + private ShuttleCurseProviderComponent? EnsureShuttleCurseProvider(EntityUid orb) + { + var mapUid = Transform(orb).MapUid; + return !mapUid.HasValue ? null : EnsureComp(mapUid.Value); + } +} diff --git a/Content.Server/WhiteDream/BloodCult/Items/VeilShifter/VeilShifterComponent.cs b/Content.Server/WhiteDream/BloodCult/Items/VeilShifter/VeilShifterComponent.cs new file mode 100644 index 00000000000..a2ec1c69570 --- /dev/null +++ b/Content.Server/WhiteDream/BloodCult/Items/VeilShifter/VeilShifterComponent.cs @@ -0,0 +1,35 @@ +using Robust.Shared.Audio; + +namespace Content.Server.WhiteDream.BloodCult.Items.VeilShifter; + +[RegisterComponent] +public sealed partial class VeilShifterComponent : Component +{ + [DataField] + public int Charges = 4; + + [DataField] + public int TeleportDistanceMax = 10; + + [DataField] + public int TeleportDistanceMin = 5; + + [DataField] + public Vector2i Offset = Vector2i.One * 2; + + // How many times it will try to find safe location before aborting the operation? + [DataField] + public int Attempts = 10; + + [DataField] + public SoundPathSpecifier TeleportInSound = new("/Audio/WhiteDream/BloodCult/veilin.ogg"); + + [DataField] + public SoundPathSpecifier TeleportOutSound = new("/Audio/WhiteDream/BloodCult/veilout.ogg"); + + [ViewVariables(VVAccess.ReadOnly), DataField("teleportInEffect")] + public string? TeleportInEffect = "CultTeleportInEffect"; + + [ViewVariables(VVAccess.ReadOnly), DataField("teleportOutEffect")] + public string? TeleportOutEffect = "CultTeleportOutEffect"; +} diff --git a/Content.Server/WhiteDream/BloodCult/Items/VeilShifter/VeilShifterSystem.cs b/Content.Server/WhiteDream/BloodCult/Items/VeilShifter/VeilShifterSystem.cs new file mode 100644 index 00000000000..188006e2196 --- /dev/null +++ b/Content.Server/WhiteDream/BloodCult/Items/VeilShifter/VeilShifterSystem.cs @@ -0,0 +1,91 @@ +using Content.Server.Popups; +using Content.Shared.Coordinates.Helpers; +using Content.Shared.Interaction.Events; +using Content.Shared.Maps; +using Content.Shared.Movement.Pulling.Systems; +using Content.Shared.Physics; +using Content.Shared.WhiteDream.BloodCult; +using Robust.Server.Audio; +using Robust.Server.GameObjects; +using Robust.Shared.Map; +using Robust.Shared.Random; + +namespace Content.Server.WhiteDream.BloodCult.Items.VeilShifter; + +public sealed class VeilShifterSystem : EntitySystem +{ + [Dependency] private readonly IRobustRandom _random = default!; + + [Dependency] private readonly AppearanceSystem _appearance = default!; + [Dependency] private readonly AudioSystem _audio = default!; + [Dependency] private readonly TransformSystem _transform = default!; + [Dependency] private readonly TurfSystem _turf = default!; + [Dependency] private readonly PopupSystem _popup = default!; + [Dependency] private readonly PullingSystem _pulling = default!; + + public override void Initialize() + { + base.Initialize(); + + SubscribeLocalEvent(OnUseInHand); + } + + private void OnUseInHand(Entity veil, ref UseInHandEvent args) + { + if (veil.Comp.Charges == 0) + return; + + if (!Teleport(veil, args.User)) + return; + + veil.Comp.Charges--; + if (veil.Comp.Charges == 0) + _appearance.SetData(veil, GenericCultVisuals.State, false); + } + + private bool Teleport(Entity veil, EntityUid user) + { + var userTransform = Transform(user); + + EntityCoordinates coords = default; + var oldCoords = userTransform.Coordinates; + var direction = userTransform.LocalRotation.GetDir().ToVec(); + var offset = userTransform.LocalRotation.ToWorldVec().Normalized(); + + var foundPos = false; + + for (var i = 0; i < veil.Comp.Attempts; i++) + { + var distance = _random.Next(veil.Comp.TeleportDistanceMin, veil.Comp.TeleportDistanceMax); + coords = userTransform.Coordinates.Offset(offset + direction * distance).SnapToGrid(); + + if (!coords.TryGetTileRef(out var tileRef) || _turf.IsTileBlocked(tileRef.Value, CollisionGroup.MobMask)) + continue; + foundPos = true; + break; + } + + if (!foundPos) + { + _popup.PopupClient(Loc.GetString("veil-shifter-cant-teleport"), veil, user); + return false; + } + + if (_pulling.TryGetPulledEntity(user, out var pulledEntity)) + _pulling.TryStopPull(pulledEntity.Value); + + _transform.SetCoordinates(user, coords); + if (pulledEntity.HasValue) + { + _transform.SetCoordinates(pulledEntity.Value, coords); + _pulling.TryStartPull(user, pulledEntity.Value); + } + + _audio.PlayPvs(veil.Comp.TeleportInSound, coords); + _audio.PlayPvs(veil.Comp.TeleportOutSound, oldCoords); + + Spawn(veil.Comp.TeleportInEffect, coords); + Spawn(veil.Comp.TeleportOutEffect, oldCoords); + return true; + } +} diff --git a/Content.Server/WhiteDream/BloodCult/Items/VoidTorch/VoidTorchSystem.cs b/Content.Server/WhiteDream/BloodCult/Items/VoidTorch/VoidTorchSystem.cs new file mode 100644 index 00000000000..7946999028f --- /dev/null +++ b/Content.Server/WhiteDream/BloodCult/Items/VoidTorch/VoidTorchSystem.cs @@ -0,0 +1,85 @@ +using Content.Server.Hands.Systems; +using Content.Server.Popups; +using Content.Shared.Interaction; +using Content.Shared.Item; +using Content.Shared.ListViewSelector; +using Content.Shared.WhiteDream.BloodCult; +using Content.Shared.WhiteDream.BloodCult.BloodCultist; +using Content.Shared.WhiteDream.BloodCult.Items.VoidTorch; +using Robust.Server.Audio; +using Robust.Server.GameObjects; + +namespace Content.Server.WhiteDream.BloodCult.Items.VoidTorch; + +public sealed class VoidTorchSystem : EntitySystem +{ + [Dependency] private readonly AppearanceSystem _appearance = default!; + [Dependency] private readonly AudioSystem _audio = default!; + [Dependency] private readonly HandsSystem _hands = default!; + [Dependency] private readonly TransformSystem _transform = default!; + [Dependency] private readonly PopupSystem _popup = default!; + [Dependency] private readonly UserInterfaceSystem _ui = default!; + + public override void Initialize() + { + base.Initialize(); + + SubscribeLocalEvent(OnComponentStartup); + SubscribeLocalEvent(OnAfterInteract); + SubscribeLocalEvent(OnCultistSelected); + } + + private void OnComponentStartup(Entity torch, ref ComponentStartup args) + { + _appearance.SetData(torch, GenericCultVisuals.State, true); + } + + private void OnAfterInteract(Entity torch, ref AfterInteractEvent args) + { + if (torch.Comp.Charges == 0 || args.Target is not { } target || !HasComp(target)) + return; + + var cultistsQuery = EntityQueryEnumerator(); + var cultist = new List(); + while (cultistsQuery.MoveNext(out var cultistUid, out _)) + { + if (cultistUid == args.User) + continue; + + var metaData = MetaData(cultistUid); + var entry = new ListViewSelectorEntry(cultistUid.ToString(), + metaData.EntityName, + metaData.EntityDescription); + + cultist.Add(entry); + } + + if (cultist.Count == 0) + { + _popup.PopupEntity(Loc.GetString("cult-rune-no-targets"), args.User, args.User); + args.Handled = true; + return; + } + + torch.Comp.TargetItem = target; + _ui.SetUiState(torch.Owner, ListViewSelectorUiKey.Key, new ListViewSelectorState(cultist)); + _ui.TryToggleUi(torch.Owner, ListViewSelectorUiKey.Key, args.User); + } + + private void OnCultistSelected(Entity torch, ref ListViewItemSelectedMessage args) + { + if (!EntityUid.TryParse(args.SelectedItem.Id, out var target) || torch.Comp.TargetItem is not { } item) + return; + + var targetTransform = Transform(target); + + _transform.SetCoordinates(item, targetTransform.Coordinates); + _hands.TryPickupAnyHand(target, item); + + _audio.PlayPvs(torch.Comp.TeleportSound, torch); + + torch.Comp.Charges--; + if (torch.Comp.Charges == 0) + _appearance.SetData(torch, GenericCultVisuals.State, false); + } +} diff --git a/Content.Server/WhiteDream/BloodCult/Objectives/KillTargetCultComponent.cs b/Content.Server/WhiteDream/BloodCult/Objectives/KillTargetCultComponent.cs new file mode 100644 index 00000000000..8c500a04328 --- /dev/null +++ b/Content.Server/WhiteDream/BloodCult/Objectives/KillTargetCultComponent.cs @@ -0,0 +1,11 @@ +namespace Content.Server.WhiteDream.BloodCult.Objectives; + +[RegisterComponent, Access(typeof(KillTargetCultSystem))] +public sealed partial class KillTargetCultComponent : Component +{ + [DataField(required: true), ViewVariables(VVAccess.ReadWrite)] + public string Title = string.Empty; + + [DataField, ViewVariables(VVAccess.ReadWrite)] + public EntityUid Target; +} diff --git a/Content.Server/WhiteDream/BloodCult/Objectives/KillTargetCultSystem.cs b/Content.Server/WhiteDream/BloodCult/Objectives/KillTargetCultSystem.cs new file mode 100644 index 00000000000..b2ef487790e --- /dev/null +++ b/Content.Server/WhiteDream/BloodCult/Objectives/KillTargetCultSystem.cs @@ -0,0 +1,50 @@ +using System.Linq; +using Content.Server.Roles.Jobs; +using Content.Server.WhiteDream.BloodCult.Gamerule; +using Content.Shared.Mobs.Components; +using Content.Shared.Mobs.Systems; +using Content.Shared.Objectives.Components; + +namespace Content.Server.WhiteDream.BloodCult.Objectives; + +public sealed class KillTargetCultSystem : EntitySystem +{ + [Dependency] private readonly JobSystem _job = default!; + [Dependency] private readonly MetaDataSystem _metaData = default!; + [Dependency] private readonly MobStateSystem _mobState = default!; + + public override void Initialize() + { + SubscribeLocalEvent(OnObjectiveAssigned); + SubscribeLocalEvent(OnAfterAssign); + SubscribeLocalEvent(OnGetProgress); + } + + private void OnObjectiveAssigned(Entity ent, ref ObjectiveAssignedEvent args) + { + var cultistRule = EntityManager.EntityQuery().FirstOrDefault(); + if (cultistRule?.OfferingTarget is { } target) + ent.Comp.Target = target; + } + + private void OnAfterAssign(Entity ent, ref ObjectiveAfterAssignEvent args) + { + _metaData.SetEntityName(ent, GetTitle(ent.Comp.Target, ent.Comp.Title), args.Meta); + } + + private void OnGetProgress(Entity ent, ref ObjectiveGetProgressEvent args) + { + var target = ent.Comp.Target; + + args.Progress = !HasComp(target) || _mobState.IsDead(target) + ? args.Progress = 1f + : args.Progress = 0f; + } + + private string GetTitle(EntityUid target, string title) + { + var targetName = MetaData(target).EntityName; + var jobName = _job.MindTryGetJobName(target); + return Loc.GetString(title, ("targetName", targetName), ("job", jobName)); + } +} diff --git a/Content.Server/WhiteDream/BloodCult/Pylon/PylonSystem.cs b/Content.Server/WhiteDream/BloodCult/Pylon/PylonSystem.cs new file mode 100644 index 00000000000..84717d69437 --- /dev/null +++ b/Content.Server/WhiteDream/BloodCult/Pylon/PylonSystem.cs @@ -0,0 +1,136 @@ +using System.Linq; +using System.Numerics; +using Content.Server.Popups; +using Content.Shared.Damage; +using Content.Shared.Humanoid; +using Content.Shared.Interaction; +using Content.Shared.Maps; +using Content.Shared.Mobs.Systems; +using Content.Shared.WhiteDream.BloodCult; +using Content.Shared.WhiteDream.BloodCult.BloodCultist; +using Content.Shared.WhiteDream.BloodCult.Components; +using Robust.Server.Audio; +using Robust.Server.GameObjects; +using Robust.Shared.Audio; +using Robust.Shared.Map; +using Robust.Shared.Map.Components; +using Robust.Shared.Player; +using Robust.Shared.Random; + +namespace Content.Server.WhiteDream.BloodCult.Pylon; + +public sealed class PylonSystem : EntitySystem +{ + [Dependency] private readonly ITileDefinitionManager _tileDefinition = default!; + [Dependency] private readonly IRobustRandom _random = default!; + + [Dependency] private readonly AppearanceSystem _appearance = default!; + [Dependency] private readonly AudioSystem _audio = default!; + [Dependency] private readonly DamageableSystem _damageable = default!; + [Dependency] private readonly EntityLookupSystem _lookup = default!; + [Dependency] private readonly MapSystem _map = default!; + [Dependency] private readonly MobStateSystem _mobState = default!; + [Dependency] private readonly TileSystem _tile = default!; + [Dependency] private readonly TurfSystem _turfs = default!; + [Dependency] private readonly PointLightSystem _pointLight = default!; + [Dependency] private readonly PopupSystem _popup = default!; + + public override void Initialize() + { + base.Initialize(); + + SubscribeLocalEvent(OnInteract); + } + + public override void Update(float frameTime) + { + base.Update(frameTime); + + var pylonQuery = EntityQueryEnumerator(); + while (pylonQuery.MoveNext(out var uid, out var pylon)) + { + if (!pylon.IsActive) + continue; + + pylon.CorruptionAccumulator += frameTime; + pylon.HealingAccumulator += frameTime; + + if (pylon.CorruptionAccumulator >= pylon.CorruptionCooldown) + { + pylon.CorruptionAccumulator = 0; + CorruptTilesInRange((uid, pylon)); + } + + if (pylon.HealingAccumulator >= pylon.HealingCooldown) + { + pylon.HealingAccumulator = 0; + HealInRange((uid, pylon)); + } + } + } + + private void OnInteract(Entity pylon, ref InteractHandEvent args) + { + if (!HasComp(args.User)) + { + _audio.PlayEntity(pylon.Comp.BurnHandSound, Filter.Pvs(pylon), pylon, true); + _popup.PopupEntity(Loc.GetString("powered-light-component-burn-hand"), pylon, args.User); + _damageable.TryChangeDamage(args.User, pylon.Comp.DamageOnInteract, true); + return; + } + + ToggleActive(pylon); + var toggleMsg = Loc.GetString(pylon.Comp.IsActive ? "pylon-toggle-on" : "pylon-toggle-off"); + _popup.PopupEntity(toggleMsg, pylon); + } + + private void ToggleActive(Entity pylon) + { + var state = !pylon.Comp.IsActive; + pylon.Comp.IsActive = state; + _appearance.SetData(pylon, PylonVisuals.Activated, state); + _pointLight.SetEnabled(pylon, state); + } + + private void CorruptTilesInRange(Entity pylon) + { + var pylonTrans = Transform(pylon); + if (pylonTrans.GridUid is not { } gridUid || !TryComp(pylonTrans.GridUid, out MapGridComponent? mapGrid)) + return; + + var radius = pylon.Comp.CorruptionRadius; + var tilesRefs = _map.GetLocalTilesIntersecting(gridUid, + mapGrid, + new Box2(pylonTrans.Coordinates.Position + new Vector2(-radius, -radius), + pylonTrans.Coordinates.Position + new Vector2(radius, radius))) + .ToList(); + + _random.Shuffle(tilesRefs); + + var cultTileDefinition = (ContentTileDefinition) _tileDefinition[pylon.Comp.CultTile]; + foreach (var tile in tilesRefs) + { + if (tile.Tile.TypeId == cultTileDefinition.TileId) + continue; + + var tilePos = _turfs.GetTileCenter(tile); + _audio.PlayPvs(pylon.Comp.CorruptTileSound, tilePos, AudioParams.Default.WithVolume(-5)); + _tile.ReplaceTile(tile, cultTileDefinition); + Spawn(pylon.Comp.TileCorruptEffect, tilePos); + return; + } + } + + private void HealInRange(Entity pylon) + { + var pylonPosition = Transform(pylon).Coordinates; + var targets = + _lookup.GetEntitiesInRange(pylonPosition, pylon.Comp.HealingAuraRange); + + foreach (var target in targets) + { + if (HasComp(target) && !_mobState.IsDead(target)) + _damageable.TryChangeDamage(target, pylon.Comp.Healing, true); + } + } +} diff --git a/Content.Server/WhiteDream/BloodCult/Runes/Apocalypse/CultRuneApocalypseComponent.cs b/Content.Server/WhiteDream/BloodCult/Runes/Apocalypse/CultRuneApocalypseComponent.cs new file mode 100644 index 00000000000..d7c25ae24d0 --- /dev/null +++ b/Content.Server/WhiteDream/BloodCult/Runes/Apocalypse/CultRuneApocalypseComponent.cs @@ -0,0 +1,61 @@ +using Robust.Shared.Prototypes; + +namespace Content.Server.WhiteDream.BloodCult.Runes.Apocalypse; + +[RegisterComponent] +public sealed partial class CultRuneApocalypseComponent : Component +{ + [DataField] + public float InvokeTime = 20; + + /// + /// If cult has less than this percent of current server population, + /// one of the possible events will be triggered. + /// + [DataField] + public float CultistsThreshold = 0.15f; + + [DataField] + public float EmpRange = 30f; + + [DataField] + public float EmpEnergyConsumption = 10000; + + [DataField] + public float EmpDuration = 180; + + /// + /// Was the rune already used or not. + /// + [DataField] + public bool Used; + + [DataField] + public Color UsedColor = Color.DimGray; + + /// + /// These events will be triggered on each rune activation. + /// + [DataField] + public List GuaranteedEvents = new() + { + "PowerGridCheck", + "SolarFlare" + }; + + /// + /// One of these events will be selected on each rune activation. + /// Stores the event and how many times it should be repeated. + /// + [DataField] + public Dictionary PossibleEvents = new() + { + ["ImmovableRodSpawn"] = 3, + ["MimicVendorRule"] = 2, + ["RatKingSpawn"] = 2, + ["MeteorSwarm"] = 2, + ["SpiderSpawn"] = 3, // more spiders + ["AnomalySpawn"] = 4, + ["KudzuGrowth"] = 2, + }; +} diff --git a/Content.Server/WhiteDream/BloodCult/Runes/Apocalypse/CultRuneApocalypseSystem.cs b/Content.Server/WhiteDream/BloodCult/Runes/Apocalypse/CultRuneApocalypseSystem.cs new file mode 100644 index 00000000000..60b889e7496 --- /dev/null +++ b/Content.Server/WhiteDream/BloodCult/Runes/Apocalypse/CultRuneApocalypseSystem.cs @@ -0,0 +1,78 @@ +using System.Linq; +using Content.Server.DoAfter; +using Content.Server.Emp; +using Content.Server.GameTicking; +using Content.Server.WhiteDream.BloodCult.Gamerule; +using Content.Shared.DoAfter; +using Content.Shared.WhiteDream.BloodCult.Runes; +using Robust.Server.GameObjects; +using Robust.Shared.Player; +using Robust.Shared.Random; + +namespace Content.Server.WhiteDream.BloodCult.Runes.Apocalypse; + +public sealed class CultRuneApocalypseSystem : EntitySystem +{ + [Dependency] private readonly ISharedPlayerManager _playerManager = default!; + [Dependency] private readonly IRobustRandom _random = default!; + + [Dependency] private readonly AppearanceSystem _appearance = default!; + [Dependency] private readonly DoAfterSystem _doAfter = default!; + [Dependency] private readonly EmpSystem _emp = default!; + [Dependency] private readonly GameTicker _gameTicker = default!; + [Dependency] private readonly TransformSystem _transform = default!; + + public override void Initialize() + { + base.Initialize(); + + SubscribeLocalEvent(OnApocalypseRuneInvoked); + SubscribeLocalEvent(OnDoAfter); + } + + private void OnApocalypseRuneInvoked(Entity ent, ref TryInvokeCultRuneEvent args) + { + if (ent.Comp.Used) + { + args.Cancel(); + return; + } + + var doAfter = new DoAfterArgs(EntityManager, args.User, ent.Comp.InvokeTime, new ApocalypseRuneDoAfter(), ent) + { + BreakOnUserMove = true + }; + + _doAfter.TryStartDoAfter(doAfter); + } + + private void OnDoAfter(Entity ent, ref ApocalypseRuneDoAfter args) + { + if (args.Cancelled || EntityManager.EntityQuery().FirstOrDefault() is not { } cultRule) + return; + + ent.Comp.Used = true; + _appearance.SetData(ent, ApocalypseRuneVisuals.Used, true); + + _emp.EmpPulse(_transform.GetMapCoordinates(ent), + ent.Comp.EmpRange, + ent.Comp.EmpEnergyConsumption, + ent.Comp.EmpDuration); + + foreach (var guaranteedEvent in ent.Comp.GuaranteedEvents) + { + _gameTicker.StartGameRule(guaranteedEvent); + } + + var requiredCultistsThreshold = MathF.Floor(_playerManager.PlayerCount * ent.Comp.CultistsThreshold); + var totalCultists = cultRule.Cultists.Count + cultRule.Constructs.Count; + if (totalCultists >= requiredCultistsThreshold) + return; + + var (randomEvent, repeatTimes) = _random.Pick(ent.Comp.PossibleEvents); + for (var i = 0; i < repeatTimes; i++) + { + _gameTicker.StartGameRule(randomEvent); + } + } +} diff --git a/Content.Server/WhiteDream/BloodCult/Runes/Barrier/CultRuneBarrierComponent.cs b/Content.Server/WhiteDream/BloodCult/Runes/Barrier/CultRuneBarrierComponent.cs new file mode 100644 index 00000000000..64f8fba2e78 --- /dev/null +++ b/Content.Server/WhiteDream/BloodCult/Runes/Barrier/CultRuneBarrierComponent.cs @@ -0,0 +1,10 @@ +using Robust.Shared.Prototypes; + +namespace Content.Server.WhiteDream.BloodCult.Runes.Barrier; + +[RegisterComponent] +public sealed partial class CultRuneBarrierComponent : Component +{ + [DataField] + public EntProtoId SpawnPrototype = "BloodCultBarrier"; +} diff --git a/Content.Server/WhiteDream/BloodCult/Runes/Barrier/CultRuneBarrierSystem.cs b/Content.Server/WhiteDream/BloodCult/Runes/Barrier/CultRuneBarrierSystem.cs new file mode 100644 index 00000000000..e4062dce625 --- /dev/null +++ b/Content.Server/WhiteDream/BloodCult/Runes/Barrier/CultRuneBarrierSystem.cs @@ -0,0 +1,15 @@ +namespace Content.Server.WhiteDream.BloodCult.Runes.Barrier; + +public sealed class CultRuneBarrierSystem : EntitySystem +{ + public override void Initialize() + { + SubscribeLocalEvent(OnBarrierRuneInvoked); + } + + private void OnBarrierRuneInvoked(Entity ent, ref TryInvokeCultRuneEvent args) + { + Spawn(ent.Comp.SpawnPrototype, Transform(ent).Coordinates); + Del(ent); + } +} diff --git a/Content.Server/WhiteDream/BloodCult/Runes/BloodBoil/CultRuneBloodBoilComponent.cs b/Content.Server/WhiteDream/BloodCult/Runes/BloodBoil/CultRuneBloodBoilComponent.cs new file mode 100644 index 00000000000..1ec1397545d --- /dev/null +++ b/Content.Server/WhiteDream/BloodCult/Runes/BloodBoil/CultRuneBloodBoilComponent.cs @@ -0,0 +1,26 @@ +using Robust.Shared.Audio; +using Robust.Shared.Prototypes; + +namespace Content.Server.WhiteDream.BloodCult.Runes.BloodBoil; + +[RegisterComponent] +public sealed partial class CultRuneBloodBoilComponent : Component +{ + [DataField] + public EntProtoId ProjectilePrototype = "BloodBoilProjectile"; + + [DataField] + public float ProjectileSpeed = 50; + + [DataField] + public float TargetsLookupRange = 15f; + + [DataField] + public float ProjectileCount = 3; + + [DataField] + public float FireStacksPerProjectile = 1; + + [DataField] + public SoundSpecifier ActivationSound = new SoundPathSpecifier("/Audio/WhiteDream/BloodCult/magic.ogg"); +} diff --git a/Content.Server/WhiteDream/BloodCult/Runes/BloodBoil/CultRuneBloodBoilSystem.cs b/Content.Server/WhiteDream/BloodCult/Runes/BloodBoil/CultRuneBloodBoilSystem.cs new file mode 100644 index 00000000000..1c3981a392a --- /dev/null +++ b/Content.Server/WhiteDream/BloodCult/Runes/BloodBoil/CultRuneBloodBoilSystem.cs @@ -0,0 +1,86 @@ +using System.Linq; +using System.Numerics; +using Content.Server.Atmos.Components; +using Content.Server.Atmos.EntitySystems; +using Content.Server.Body.Components; +using Content.Server.Examine; +using Content.Server.Popups; +using Content.Server.Weapons.Ranged.Systems; +using Content.Server.WhiteDream.BloodCult.BloodBoilProjectile; +using Content.Shared.Projectiles; +using Content.Shared.WhiteDream.BloodCult.BloodCultist; +using Robust.Server.Audio; +using Robust.Server.GameObjects; +using Robust.Shared.Audio; +using Robust.Shared.Random; + +namespace Content.Server.WhiteDream.BloodCult.Runes.BloodBoil; + +public sealed class CultRuneBloodBoilSystem : EntitySystem +{ + [Dependency] private readonly IRobustRandom _random = default!; + + [Dependency] private readonly AudioSystem _audio = default!; + [Dependency] private readonly CultRuneBaseSystem _cultRune = default!; + [Dependency] private readonly ExamineSystem _examine = default!; + [Dependency] private readonly FlammableSystem _flammable = default!; + [Dependency] private readonly GunSystem _gun = default!; + [Dependency] private readonly TransformSystem _transform = default!; + [Dependency] private readonly PopupSystem _popup = default!; + + public override void Initialize() + { + base.Initialize(); + + SubscribeLocalEvent(OnBloodBoilRuneInvoked); + } + + private void OnBloodBoilRuneInvoked(Entity ent, ref TryInvokeCultRuneEvent args) + { + var targets = _cultRune.GetTargetsNearRune(ent, + ent.Comp.TargetsLookupRange, + entity => + HasComp(entity) || + !HasComp(entity) || + !_examine.InRangeUnOccluded(ent, entity, ent.Comp.TargetsLookupRange)) + .ToList(); + + if (targets.Count == 0) + { + _popup.PopupEntity(Loc.GetString("cult-blood-boil-rune-no-targets"), ent, args.User); + args.Cancel(); + return; + } + + for (var i = 0; i < ent.Comp.ProjectileCount; i++) + { + var target = _random.PickAndTake(targets); + if (HasComp(target)) + { + _flammable.AdjustFireStacks(target, ent.Comp.FireStacksPerProjectile); + _flammable.Ignite(target, ent); + } + + Shoot(ent, target); + } + + _audio.PlayPvs(ent.Comp.ActivationSound, ent, AudioParams.Default.WithMaxDistance(2f)); + } + + private void Shoot(Entity ent, EntityUid target) + { + var runeMapPos = _transform.GetMapCoordinates(ent); + var targetMapPos = _transform.GetMapCoordinates(target); + + var projectileEntity = Spawn(ent.Comp.ProjectilePrototype, runeMapPos); + var direction = targetMapPos.Position - runeMapPos.Position; + + if (!HasComp(projectileEntity)) + return; + + var bloodBoilProjectile = EnsureComp(projectileEntity); + bloodBoilProjectile.Target = target; + + _gun.ShootProjectile(projectileEntity, direction, Vector2.Zero, ent, ent, ent.Comp.ProjectileSpeed); + } +} diff --git a/Content.Server/WhiteDream/BloodCult/Runes/CultRuneBaseComponent.cs b/Content.Server/WhiteDream/BloodCult/Runes/CultRuneBaseComponent.cs new file mode 100644 index 00000000000..023112a6ef4 --- /dev/null +++ b/Content.Server/WhiteDream/BloodCult/Runes/CultRuneBaseComponent.cs @@ -0,0 +1,41 @@ +using Content.Shared.Chat; +using Content.Shared.Damage; +using Content.Shared.Humanoid; +using Robust.Shared.Prototypes; + +namespace Content.Server.WhiteDream.BloodCult.Runes; + +[RegisterComponent] +public sealed partial class CultRuneBaseComponent : Component +{ + [DataField(required: true)] + public string InvokePhrase = ""; + + [DataField] + public InGameICChatType InvokeChatType = InGameICChatType.Whisper; + + [DataField] + public int RequiredInvokers = 1; + + [DataField] + public float RuneActivationRange = 1f; + + /// + /// Damage dealt on the rune activation. + /// + [DataField] + public DamageSpecifier? ActivationDamage; + + public EntProtoId HolyWaterPrototype = "HolyWater"; +} + +public sealed class TryInvokeCultRuneEvent(EntityUid user, HashSet invokers) : CancellableEntityEventArgs +{ + public EntityUid User = user; + public HashSet Invokers = invokers; +} + +public sealed class AfterRunePlaced(EntityUid user) +{ + public EntityUid User = user; +} diff --git a/Content.Server/WhiteDream/BloodCult/Runes/CultRuneBaseSystem.Helpers.cs b/Content.Server/WhiteDream/BloodCult/Runes/CultRuneBaseSystem.Helpers.cs new file mode 100644 index 00000000000..ec81af67761 --- /dev/null +++ b/Content.Server/WhiteDream/BloodCult/Runes/CultRuneBaseSystem.Helpers.cs @@ -0,0 +1,54 @@ +using Content.Shared.Humanoid; +using Content.Shared.Movement.Pulling.Components; +using Content.Shared.Movement.Pulling.Systems; +using Content.Shared.WhiteDream.BloodCult.BloodCultist; +using Content.Shared.WhiteDream.BloodCult.Constructs; + +namespace Content.Server.WhiteDream.BloodCult.Runes; + +public sealed partial class CultRuneBaseSystem +{ + [Dependency] private readonly PullingSystem _pulling = default!; + + /// + /// Gets all cultists/constructs near rune. + /// + public HashSet GatherCultists(EntityUid rune, float range) + { + var runeTransform = Transform(rune); + var entities = _lookup.GetEntitiesInRange(runeTransform.Coordinates, range); + entities.RemoveWhere(entity => !HasComp(entity) && !HasComp(entity)); + return entities; + } + + /// + /// Gets all the humanoids near rune. + /// + /// The rune itself. + /// Radius for a lookup. + /// Filter to exlude from return. + public HashSet> GetTargetsNearRune(EntityUid rune, + float range, + Predicate>? exlude = null) + { + var runeTransform = Transform(rune); + var possibleTargets = _lookup.GetEntitiesInRange(runeTransform.Coordinates, range); + if (exlude != null) + possibleTargets.RemoveWhere(exlude); + + return possibleTargets; + } + + /// + /// Is used to stop target from pulling/being pulled before teleporting them. + /// + public void StopPulling(EntityUid target) + { + if (TryComp(target, out PullableComponent? pullable) && pullable.BeingPulled) + _pulling.TryStopPull(target, pullable); + + // I wish there was a better way to do it + if (_pulling.TryGetPulledEntity(target, out var pulling)) + _pulling.TryStopPull(pulling.Value); + } +} diff --git a/Content.Server/WhiteDream/BloodCult/Runes/CultRuneBaseSystem.cs b/Content.Server/WhiteDream/BloodCult/Runes/CultRuneBaseSystem.cs new file mode 100644 index 00000000000..0ad21e63693 --- /dev/null +++ b/Content.Server/WhiteDream/BloodCult/Runes/CultRuneBaseSystem.cs @@ -0,0 +1,221 @@ +using System.Linq; +using System.Numerics; +using Content.Server.Bible.Components; +using Content.Server.Chat.Systems; +using Content.Server.Chemistry.Components; +using Content.Server.DoAfter; +using Content.Server.Fluids.Components; +using Content.Server.Popups; +using Content.Server.WhiteDream.BloodCult.Empower; +using Content.Shared.Chemistry.Components.SolutionManager; +using Content.Shared.Chemistry.EntitySystems; +using Content.Shared.Damage; +using Content.Shared.DoAfter; +using Content.Shared.Interaction; +using Content.Shared.Maps; +using Content.Shared.WhiteDream.BloodCult.BloodCultist; +using Content.Shared.WhiteDream.BloodCult.Runes; +using Robust.Server.Audio; +using Robust.Server.GameObjects; +using Robust.Shared.Audio; +using Robust.Shared.Physics.Events; +using Robust.Shared.Prototypes; + +namespace Content.Server.WhiteDream.BloodCult.Runes; + +public sealed partial class CultRuneBaseSystem : EntitySystem +{ + [Dependency] private readonly IPrototypeManager _protoManager = default!; + [Dependency] private readonly AudioSystem _audio = default!; + [Dependency] private readonly ChatSystem _chat = default!; + [Dependency] private readonly DamageableSystem _damageable = default!; + [Dependency] private readonly DoAfterSystem _doAfter = default!; + [Dependency] private readonly TransformSystem _transform = default!; + [Dependency] private readonly EntityLookupSystem _lookup = default!; + [Dependency] private readonly SharedSolutionContainerSystem _solutionContainer = default!; + [Dependency] private readonly PopupSystem _popup = default!; + + public override void Initialize() + { + base.Initialize(); + + // Drawing rune + SubscribeLocalEvent(OnRuneSelected); + SubscribeLocalEvent(OnDrawRune); + + // Erasing rune + SubscribeLocalEvent(EraseOnInteractUsing); + SubscribeLocalEvent(OnRuneErase); + SubscribeLocalEvent(EraseOnCollding); + + // Rune invoking + SubscribeLocalEvent(OnRuneActivate); + } + + private void OnRuneSelected(Entity ent, ref RuneDrawerSelectedMessage args) + { + if (!_protoManager.TryIndex(args.SelectedRune, out var runeSelector) || !CanDrawRune(args.Actor)) + return; + + var timeToDraw = runeSelector.DrawTime; + if (TryComp(args.Actor, out BloodCultEmpoweredComponent? empowered)) + timeToDraw *= empowered.RuneTimeMultiplier; + + var ev = new DrawRuneDoAfter + { + Rune = args.SelectedRune, + EndDrawingSound = ent.Comp.EndDrawingSound + }; + + var argsDoAfterEvent = new DoAfterArgs(EntityManager, args.Actor, timeToDraw, ev, args.Actor) + { + BreakOnUserMove = true, + NeedHand = true + }; + + if (_doAfter.TryStartDoAfter(argsDoAfterEvent)) + _audio.PlayPvs(ent.Comp.StartDrawingSound, args.Actor, AudioParams.Default.WithMaxDistance(2f)); + } + + private void OnDrawRune(Entity ent, ref DrawRuneDoAfter args) + { + if (args.Cancelled || !_protoManager.TryIndex(args.Rune, out var runeSelector)) + return; + + DealDamage(args.User, runeSelector.DrawDamage); + + _audio.PlayPvs(args.EndDrawingSound, args.User, AudioParams.Default.WithMaxDistance(2f)); + var rune = SpawnRune(args.User, runeSelector.Prototype); + + var ev = new AfterRunePlaced(args.User); + RaiseLocalEvent(rune, ev); + } + + private void EraseOnInteractUsing(Entity ent, ref InteractUsingEvent args) + { + // Logic for bible erasing + if (TryComp(args.Used, out var bible) && HasComp(args.User)) + { + _popup.PopupEntity(Loc.GetString("cult-rune-erased"), ent, args.User); + _audio.PlayPvs(bible.HealSoundPath, args.User); + EntityManager.DeleteEntity(args.Target); + return; + } + + if (!TryComp(args.Used, out RuneDrawerComponent? runeDrawer)) + return; + + var argsDoAfterEvent = + new DoAfterArgs(EntityManager, args.User, runeDrawer.EraseTime, new RuneEraseDoAfterEvent(), ent) + { + BreakOnUserMove = true, + BreakOnDamage = true, + NeedHand = true + }; + + if (_doAfter.TryStartDoAfter(argsDoAfterEvent)) + _popup.PopupEntity(Loc.GetString("cult-rune-started-erasing"), ent, args.User); + } + + private void OnRuneErase(Entity ent, ref RuneEraseDoAfterEvent args) + { + if (args.Cancelled) + return; + + _popup.PopupEntity(Loc.GetString("cult-rune-erased"), ent, args.User); + EntityManager.DeleteEntity(ent); + } + + private void EraseOnCollding(Entity ent, ref StartCollideEvent args) + { + if (!TryComp(args.OtherEntity, out var solutionContainer) || + !HasComp(args.OtherEntity) && !HasComp(args.OtherEntity)) + return; + + if (_solutionContainer.EnumerateSolutions((args.OtherEntity, solutionContainer)) + .Any(solution => solution.Solution.Comp.Solution.ContainsPrototype(ent.Comp.HolyWaterPrototype))) + EntityManager.DeleteEntity(ent); + } + + private void OnRuneActivate(Entity ent, ref ActivateInWorldEvent args) + { + var runeCoordinates = Transform(ent).Coordinates; + var userCoordinates = Transform(args.User).Coordinates; + if (args.Handled || !HasComp(args.User) || + !userCoordinates.TryDistance(EntityManager, runeCoordinates, out var distance) || + distance > ent.Comp.RuneActivationRange) + return; + + args.Handled = true; + + var cultists = GatherCultists(ent, ent.Comp.RuneActivationRange); + if (cultists.Count < ent.Comp.RequiredInvokers) + { + _popup.PopupEntity(Loc.GetString("cult-rune-not-enough-cultists"), ent, args.User); + return; + } + + var tryInvokeEv = new TryInvokeCultRuneEvent(args.User, cultists); + RaiseLocalEvent(ent, tryInvokeEv); + if (tryInvokeEv.Cancelled) + return; + + foreach (var cultist in cultists) + { + DealDamage(cultist, ent.Comp.ActivationDamage); + _chat.TrySendInGameICMessage(cultist, + ent.Comp.InvokePhrase, + ent.Comp.InvokeChatType, + false, + checkRadioPrefix: false); + } + } + + private EntityUid SpawnRune(EntityUid user, EntProtoId rune) + { + var transform = Transform(user); + var snappedLocalPosition = new Vector2(MathF.Floor(transform.LocalPosition.X) + 0.5f, + MathF.Floor(transform.LocalPosition.Y) + 0.5f); + var spawnPosition = _transform.GetMapCoordinates(user); + var runeEntity = EntityManager.Spawn(rune, spawnPosition); + _transform.SetLocalPosition(runeEntity, snappedLocalPosition); + + return runeEntity; + } + + private bool CanDrawRune(EntityUid uid) + { + var transform = Transform(uid); + var gridUid = transform.GridUid; + if (!gridUid.HasValue) + { + _popup.PopupEntity(Loc.GetString("cult-rune-cant-draw"), uid, uid); + return false; + } + + var tile = transform.Coordinates.GetTileRef(); + if (tile.HasValue) + return true; + + _popup.PopupEntity(Loc.GetString("cult-cant-draw-rune"), uid, uid); + return false; + } + + private void DealDamage(EntityUid user, DamageSpecifier? damage = null) + { + if (damage is null) + return; + + // So the original DamageSpecifier will not be changed. + var newDamage = new DamageSpecifier(damage); + if (TryComp(user, out BloodCultEmpoweredComponent? empowered)) + { + foreach (var (key, value) in damage.DamageDict) + { + damage.DamageDict[key] = value * empowered.RuneDamageMultiplier; + } + } + + _damageable.TryChangeDamage(user, newDamage, true); + } +} diff --git a/Content.Server/WhiteDream/BloodCult/Runes/Empower/CultRuneEmpowerComponent.cs b/Content.Server/WhiteDream/BloodCult/Runes/Empower/CultRuneEmpowerComponent.cs new file mode 100644 index 00000000000..880e3f897cf --- /dev/null +++ b/Content.Server/WhiteDream/BloodCult/Runes/Empower/CultRuneEmpowerComponent.cs @@ -0,0 +1,8 @@ +namespace Content.Server.WhiteDream.BloodCult.Runes.Empower; + +[RegisterComponent] +public sealed partial class CultRuneEmpowerComponent : Component +{ + [DataField] + public string ComponentToGive = "BloodCultEmpowered"; +} diff --git a/Content.Server/WhiteDream/BloodCult/Runes/Empower/CultRuneEmpowerSystem.cs b/Content.Server/WhiteDream/BloodCult/Runes/Empower/CultRuneEmpowerSystem.cs new file mode 100644 index 00000000000..3c015072199 --- /dev/null +++ b/Content.Server/WhiteDream/BloodCult/Runes/Empower/CultRuneEmpowerSystem.cs @@ -0,0 +1,33 @@ +using Content.Server.Popups; +using Content.Server.WhiteDream.BloodCult.Empower; + +namespace Content.Server.WhiteDream.BloodCult.Runes.Empower; + +public sealed class CultRuneEmpowerSystem : EntitySystem +{ + [Dependency] private readonly IComponentFactory _factory = default!; + + [Dependency] private readonly PopupSystem _popup = default!; + + public override void Initialize() + { + base.Initialize(); + + SubscribeLocalEvent(OnStrengthRuneInvoked); + } + + private void OnStrengthRuneInvoked(Entity ent, ref TryInvokeCultRuneEvent args) + { + var registration = _factory.GetRegistration(ent.Comp.ComponentToGive); + if (HasComp(args.User, registration.Type)) + { + _popup.PopupEntity(Loc.GetString("cult-buff-already-buffed"), args.User, args.User); + args.Cancel(); + return; + } + + var empowered = (BloodCultEmpoweredComponent) _factory.GetComponent(ent.Comp.ComponentToGive); + empowered.TimeRemaining = empowered.DefaultTime; + AddComp(args.User, empowered); + } +} diff --git a/Content.Server/WhiteDream/BloodCult/Runes/Offering/CultRuneOfferingComponent.cs b/Content.Server/WhiteDream/BloodCult/Runes/Offering/CultRuneOfferingComponent.cs new file mode 100644 index 00000000000..06d750bfc82 --- /dev/null +++ b/Content.Server/WhiteDream/BloodCult/Runes/Offering/CultRuneOfferingComponent.cs @@ -0,0 +1,42 @@ +using Content.Shared.Damage; +using Content.Shared.FixedPoint; + +namespace Content.Server.WhiteDream.BloodCult.Runes.Offering; + +[RegisterComponent] +public sealed partial class CultRuneOfferingComponent : Component +{ + /// + /// The lookup range for offering targets + /// + [DataField] + public float OfferingRange = 0.5f; + + /// + /// The amount of cultists require to convert a living target. + /// + [DataField] + public int ConvertInvokersAmount = 2; + + /// + /// The amount of cultists required to sacrifice a living target. + /// + [DataField] + public int AliveSacrificeInvokersAmount = 3; + + /// + /// The amount of charges revive rune system should recieve on sacrifice/convert. + /// + [DataField] + public int ReviveChargesPerOffering = 1; + + [DataField] + public DamageSpecifier ConvertHealing = new() + { + DamageDict = new Dictionary + { + ["Brute"] = -40, + ["Burn"] = -40 + } + }; +} diff --git a/Content.Server/WhiteDream/BloodCult/Runes/Offering/CultRuneOfferingSystem.cs b/Content.Server/WhiteDream/BloodCult/Runes/Offering/CultRuneOfferingSystem.cs new file mode 100644 index 00000000000..19614e20bd9 --- /dev/null +++ b/Content.Server/WhiteDream/BloodCult/Runes/Offering/CultRuneOfferingSystem.cs @@ -0,0 +1,125 @@ +using System.Linq; +using Content.Server.Bible.Components; +using Content.Server.Body.Systems; +using Content.Server.Cuffs; +using Content.Server.Mind; +using Content.Server.Stunnable; +using Content.Server.WhiteDream.BloodCult.Gamerule; +using Content.Server.WhiteDream.BloodCult.Runes.Revive; +using Content.Shared.Cuffs.Components; +using Content.Shared.Damage; +using Content.Shared.Humanoid; +using Content.Shared.Mindshield.Components; +using Content.Shared.Mobs.Systems; +using Content.Shared.StatusEffect; +using Content.Shared.WhiteDream.BloodCult.BloodCultist; + +namespace Content.Server.WhiteDream.BloodCult.Runes.Offering; + +public sealed class CultRuneOfferingSystem : EntitySystem +{ + [Dependency] private readonly BloodCultRuleSystem _bloodCultRule = default!; + [Dependency] private readonly BodySystem _body = default!; + [Dependency] private readonly CuffableSystem _cuffable = default!; + [Dependency] private readonly CultRuneBaseSystem _cultRune = default!; + [Dependency] private readonly CultRuneReviveSystem _cultRuneRevive = default!; + [Dependency] private readonly DamageableSystem _damageable = default!; + [Dependency] private readonly MindSystem _mind = default!; + [Dependency] private readonly MobStateSystem _mobState = default!; + [Dependency] private readonly StatusEffectsSystem _statusEffects = default!; + [Dependency] private readonly StunSystem _stun = default!; + + public override void Initialize() + { + base.Initialize(); + SubscribeLocalEvent(OnOfferingRuneInvoked); + } + + private void OnOfferingRuneInvoked(Entity ent, ref TryInvokeCultRuneEvent args) + { + var possibleTargets = _cultRune.GetTargetsNearRune(ent, + ent.Comp.OfferingRange, + entity => HasComp(entity)); + + if (possibleTargets.Count == 0) + { + args.Cancel(); + return; + } + + var target = possibleTargets.First(); + + // if the target is dead we should always sacrifice it. + if (_mobState.IsDead(target)) + { + Sacrifice(target); + return; + } + + if (!_mind.TryGetMind(target, out _, out _) || + _bloodCultRule.IsTarget(target) || + HasComp(target) || + HasComp(target)) + { + if (!TrySacrifice(target, ent, args.Invokers.Count)) + args.Cancel(); + + return; + } + + if (!TryConvert(target, ent, args.User, args.Invokers.Count)) + args.Cancel(); + } + + private bool TrySacrifice(Entity target, + Entity rune, + int invokersAmount) + { + if (invokersAmount < rune.Comp.AliveSacrificeInvokersAmount) + return false; + + _cultRuneRevive.AddCharges(rune, rune.Comp.ReviveChargesPerOffering); + Sacrifice(target); + return true; + } + + private void Sacrifice(EntityUid target) + { + var transform = Transform(target); + var shard = Spawn("SoulShard", transform.Coordinates); + _body.GibBody(target); + + if (!_mind.TryGetMind(target, out var mindId, out _)) + return; + + _mind.TransferTo(mindId, shard); + _mind.UnVisit(mindId); + } + + private bool TryConvert(EntityUid target, + Entity rune, + EntityUid user, + int invokersAmount) + { + if (invokersAmount < rune.Comp.ConvertInvokersAmount) + return false; + + _cultRuneRevive.AddCharges(rune, rune.Comp.ReviveChargesPerOffering); + Convert(rune, target, user); + return true; + } + + private void Convert(Entity rune, EntityUid target, EntityUid user) + { + _bloodCultRule.Convert(target); + _stun.TryStun(target, TimeSpan.FromSeconds(2f), false); + if (TryComp(target, out CuffableComponent? cuffs) && cuffs.Container.ContainedEntities.Count >= 1) + { + var lastAddedCuffs = cuffs.LastAddedCuffs; + _cuffable.Uncuff(target, user, lastAddedCuffs); + } + + _statusEffects.TryRemoveStatusEffect(target, "Muted"); + _damageable.TryChangeDamage(target, rune.Comp.ConvertHealing); + } +} diff --git a/Content.Server/WhiteDream/BloodCult/Runes/Rending/CultRuneRendingComponent.cs b/Content.Server/WhiteDream/BloodCult/Runes/Rending/CultRuneRendingComponent.cs new file mode 100644 index 00000000000..9c049a189d9 --- /dev/null +++ b/Content.Server/WhiteDream/BloodCult/Runes/Rending/CultRuneRendingComponent.cs @@ -0,0 +1,33 @@ +using Content.Shared.DoAfter; +using Robust.Shared.Audio; +using Robust.Shared.Audio.Components; +using Robust.Shared.Prototypes; + +namespace Content.Server.WhiteDream.BloodCult.Runes.Rending; + +[RegisterComponent] +public sealed partial class CultRuneRendingComponent : Component +{ + [DataField] + public float SummonTime = 40; + + [DataField] + public SoundSpecifier FinishedDrawingAudio = + new SoundPathSpecifier("/Audio/WhiteDream/BloodCult/rending_draw_finished.ogg"); + + [DataField] + public SoundSpecifier SummonAudio = new SoundPathSpecifier("/Audio/WhiteDream/BloodCult/rending_ritual.ogg"); + + [DataField] + public EntProtoId NarsiePrototype = "MobNarsieSpawn"; + + /// + /// Used to track if the rune is being used right now. + /// + public DoAfterId? CurrentDoAfter; + + /// + /// Used to track the summon audio entity. + /// + public Entity? AudioEntity; +} diff --git a/Content.Server/WhiteDream/BloodCult/Runes/Rending/CultRuneRendingSystem.cs b/Content.Server/WhiteDream/BloodCult/Runes/Rending/CultRuneRendingSystem.cs new file mode 100644 index 00000000000..d051d07528f --- /dev/null +++ b/Content.Server/WhiteDream/BloodCult/Runes/Rending/CultRuneRendingSystem.cs @@ -0,0 +1,109 @@ +using Content.Server.Chat.Systems; +using Content.Server.DoAfter; +using Content.Server.Pinpointer; +using Content.Server.Popups; +using Content.Server.WhiteDream.BloodCult.Gamerule; +using Content.Shared.DoAfter; +using Content.Shared.Mobs.Components; +using Content.Shared.Mobs.Systems; +using Content.Shared.WhiteDream.BloodCult.Runes; +using Robust.Server.Audio; +using Robust.Server.GameObjects; +using Robust.Shared.Audio; +using Robust.Shared.Player; +using Robust.Shared.Utility; + +namespace Content.Server.WhiteDream.BloodCult.Runes.Rending; + +public sealed class CultRuneRendingSystem : EntitySystem +{ + [Dependency] private readonly AppearanceSystem _appearance = default!; + [Dependency] private readonly AudioSystem _audio = default!; + [Dependency] private readonly ChatSystem _chat = default!; + [Dependency] private readonly BloodCultRuleSystem _cultRule = default!; + [Dependency] private readonly DoAfterSystem _doAfter = default!; + [Dependency] private readonly MobStateSystem _mobState = default!; + [Dependency] private readonly NavMapSystem _navMap = default!; + [Dependency] private readonly PopupSystem _popup = default!; + [Dependency] private readonly TransformSystem _transform = default!; + + public override void Initialize() + { + base.Initialize(); + SubscribeLocalEvent(OnRendingRunePlaced); + SubscribeLocalEvent(OnRendingRuneInvoked); + SubscribeLocalEvent(SpawnNarSie); + } + + private void OnRendingRunePlaced(Entity rune, ref AfterRunePlaced args) + { + var position = _transform.GetMapCoordinates(rune); + var message = Loc.GetString("cult-rending-drawing-finished", + ("location", FormattedMessage.RemoveMarkupPermissive(_navMap.GetNearestBeaconString(position)))); + + _chat.DispatchGlobalAnnouncement(message, + Loc.GetString("blood-cult-title"), + true, + rune.Comp.FinishedDrawingAudio, + Color.DarkRed); + } + + private void OnRendingRuneInvoked(Entity rune, ref TryInvokeCultRuneEvent args) + { + if (!_cultRule.TryGetTarget(out var target) || + !TryComp(target.Value, out MobStateComponent? _) || + _mobState.IsAlive(target.Value)) + { + _popup.PopupEntity(Loc.GetString("cult-rending-target-alive"), rune, args.User); + args.Cancel(); + return; + } + + if (rune.Comp.CurrentDoAfter.HasValue) + { + _popup.PopupEntity(Loc.GetString("cult-rending-already-summoning"), rune, args.User); + args.Cancel(); + return; + } + + var ev = new RendingRuneDoAfter(); + var argsDoAfterEvent = new DoAfterArgs(EntityManager, args.User, rune.Comp.SummonTime, ev, rune) + { + BreakOnUserMove = true + }; + + if (!_doAfter.TryStartDoAfter(argsDoAfterEvent, out rune.Comp.CurrentDoAfter)) + { + args.Cancel(); + return; + } + + _chat.DispatchGlobalAnnouncement(Loc.GetString("cult-rending-started"), + Loc.GetString("blood-cult-title"), + false, + colorOverride: Color.DarkRed); + + _appearance.SetData(rune, RendingRuneVisuals.Active, true); + rune.Comp.AudioEntity = + _audio.PlayGlobal(rune.Comp.SummonAudio, Filter.Broadcast(), false, AudioParams.Default.WithLoop(true)); + } + + private void SpawnNarSie(Entity rune, ref RendingRuneDoAfter args) + { + rune.Comp.CurrentDoAfter = null; + _audio.Stop(rune.Comp.AudioEntity); + _appearance.SetData(rune, RendingRuneVisuals.Active, false); + if (args.Cancelled) + { + _chat.DispatchGlobalAnnouncement(Loc.GetString("cult-rending-prevented"), + Loc.GetString("blood-cult-title"), + false, + colorOverride: Color.DarkRed); + return; + } + + var ev = new BloodCultNarsieSummoned(); + RaiseLocalEvent(ev); + Spawn(rune.Comp.NarsiePrototype, _transform.GetMapCoordinates(rune)); + } +} diff --git a/Content.Server/WhiteDream/BloodCult/Runes/Revive/CultRuneReviveComponent.cs b/Content.Server/WhiteDream/BloodCult/Runes/Revive/CultRuneReviveComponent.cs new file mode 100644 index 00000000000..1cddaef0154 --- /dev/null +++ b/Content.Server/WhiteDream/BloodCult/Runes/Revive/CultRuneReviveComponent.cs @@ -0,0 +1,26 @@ +using Content.Shared.Damage; +using Content.Shared.FixedPoint; + +namespace Content.Server.WhiteDream.BloodCult.Runes.Revive; + +[RegisterComponent] +public sealed partial class CultRuneReviveComponent : Component +{ + [DataField] + public float ReviveRange = 0.5f; + + [DataField] + public DamageSpecifier Healing = new() + { + DamageDict = new Dictionary + { + ["Brute"] = -100, + ["Burn"] = -100, + ["Heat"] = -100, + ["Asphyxiation"] = -100, + ["Bloodloss"] = -100, + ["Poison"] = -50, + ["Cellular"] = -50 + } + }; +} diff --git a/Content.Server/WhiteDream/BloodCult/Runes/Revive/CultRuneReviveSystem.cs b/Content.Server/WhiteDream/BloodCult/Runes/Revive/CultRuneReviveSystem.cs new file mode 100644 index 00000000000..0842422b466 --- /dev/null +++ b/Content.Server/WhiteDream/BloodCult/Runes/Revive/CultRuneReviveSystem.cs @@ -0,0 +1,113 @@ +using System.Linq; +using Content.Server.EUI; +using Content.Server.Ghost; +using Content.Server.Mind; +using Content.Server.Popups; +using Content.Shared.Damage; +using Content.Shared.Mobs; +using Content.Shared.Mobs.Components; +using Content.Shared.Mobs.Systems; +using Content.Shared.WhiteDream.BloodCult.BloodCultist; + +namespace Content.Server.WhiteDream.BloodCult.Runes.Revive; + +public sealed class CultRuneReviveSystem : EntitySystem +{ + [Dependency] private readonly EuiManager _eui = default!; + + [Dependency] private readonly CultRuneBaseSystem _cultRune = default!; + [Dependency] private readonly DamageableSystem _damageable = default!; + [Dependency] private readonly MindSystem _mind = default!; + [Dependency] private readonly MobStateSystem _mobState = default!; + [Dependency] private readonly MobThresholdSystem _threshold = default!; + [Dependency] private readonly PopupSystem _popup = default!; + + public override void Initialize() + { + base.Initialize(); + + SubscribeLocalEvent(OnReviveRuneInvoked); + } + + private void OnReviveRuneInvoked(Entity ent, ref TryInvokeCultRuneEvent args) + { + var chargesProvider = EnsureReviveRuneChargesProvider(ent); + if (chargesProvider is null) + { + _popup.PopupEntity(Loc.GetString("cult-revive-rune-no-charges"), args.User, args.User); + args.Cancel(); + return; + } + + var possibleTargets = _cultRune.GetTargetsNearRune(ent, + ent.Comp.ReviveRange, + entity => + !HasComp(entity) || + !HasComp(entity) || + !HasComp(entity) || + _mobState.IsAlive(entity) + ); + + if (possibleTargets.Count == 0) + { + _popup.PopupEntity(Loc.GetString("cult-rune-no-targets"), args.User, args.User); + args.Cancel(); + return; + } + + var victim = possibleTargets.First(); + + if (chargesProvider.Charges == 0) + { + _popup.PopupEntity(Loc.GetString("cult-revive-rune-no-charges"), args.User, args.User); + args.Cancel(); + return; + } + + Revive(victim, args.User, ent); + } + + public void AddCharges(EntityUid ent, int charges) + { + var chargesProvider = EnsureReviveRuneChargesProvider(ent); + if (chargesProvider is null) + return; + + chargesProvider.Charges += charges; + } + + private void Revive(EntityUid target, EntityUid user, Entity rune) + { + var chargesProvider = EnsureReviveRuneChargesProvider(rune); + if (chargesProvider is null) + return; + + chargesProvider.Charges--; + + var deadThreshold = _threshold.GetThresholdForState(target, MobState.Dead); + _damageable.TryChangeDamage(target, rune.Comp.Healing); + + if (!TryComp(target, out var damageable) || damageable.TotalDamage > deadThreshold) + return; + + _mobState.ChangeMobState(target, MobState.Critical, origin: user); + if (!_mind.TryGetMind(target, out _, out var mind)) + { + // if the mind is not found in the body, try to find the original cultist mind + if (TryComp(target, out var cultist) && cultist.OriginalMind != null) + mind = cultist.OriginalMind.Value; + } + + if (mind?.Session is not { } playerSession || mind.CurrentEntity == target) + return; + + // notify them they're being revived. + _eui.OpenEui(new ReturnToBodyEui(mind, _mind), playerSession); + } + + private ReviveRuneChargesProviderComponent? EnsureReviveRuneChargesProvider(EntityUid ent) + { + var mapUid = Transform(ent).MapUid; + return !mapUid.HasValue ? null : EnsureComp(mapUid.Value); + } +} diff --git a/Content.Server/WhiteDream/BloodCult/Runes/Revive/ReviveRuneChargesProviderComponent.cs b/Content.Server/WhiteDream/BloodCult/Runes/Revive/ReviveRuneChargesProviderComponent.cs new file mode 100644 index 00000000000..ab167aba889 --- /dev/null +++ b/Content.Server/WhiteDream/BloodCult/Runes/Revive/ReviveRuneChargesProviderComponent.cs @@ -0,0 +1,8 @@ +namespace Content.Server.WhiteDream.BloodCult.Runes.Revive; + +[RegisterComponent] +public sealed partial class ReviveRuneChargesProviderComponent : Component +{ + [DataField] + public int Charges = 3; +} diff --git a/Content.Server/WhiteDream/BloodCult/Runes/Summon/CultRuneSummonComponent.cs b/Content.Server/WhiteDream/BloodCult/Runes/Summon/CultRuneSummonComponent.cs new file mode 100644 index 00000000000..a5c04b54873 --- /dev/null +++ b/Content.Server/WhiteDream/BloodCult/Runes/Summon/CultRuneSummonComponent.cs @@ -0,0 +1,10 @@ +using Robust.Shared.Audio; + +namespace Content.Server.WhiteDream.BloodCult.Runes.Summon; + +[RegisterComponent] +public sealed partial class CultRuneSummonComponent : Component +{ + [DataField] + public SoundPathSpecifier TeleportSound = new("/Audio/WhiteDream/BloodCult/veilin.ogg"); +} diff --git a/Content.Server/WhiteDream/BloodCult/Runes/Summon/CultRuneSummonSystem.cs b/Content.Server/WhiteDream/BloodCult/Runes/Summon/CultRuneSummonSystem.cs new file mode 100644 index 00000000000..0c0f228dafe --- /dev/null +++ b/Content.Server/WhiteDream/BloodCult/Runes/Summon/CultRuneSummonSystem.cs @@ -0,0 +1,89 @@ +using System.Linq; +using Content.Server.Popups; +using Content.Shared.Cuffs.Components; +using Content.Shared.ListViewSelector; +using Content.Shared.Movement.Pulling.Components; +using Content.Shared.WhiteDream.BloodCult.BloodCultist; +using Robust.Server.Audio; +using Robust.Server.GameObjects; + +namespace Content.Server.WhiteDream.BloodCult.Runes.Summon; + +public sealed class CultRuneSummonSystem : EntitySystem +{ + [Dependency] private readonly AudioSystem _audio = default!; + [Dependency] private readonly CultRuneBaseSystem _cultRune = default!; + [Dependency] private readonly TransformSystem _transform = default!; + [Dependency] private readonly PopupSystem _popup = default!; + [Dependency] private readonly UserInterfaceSystem _ui = default!; + + public override void Initialize() + { + base.Initialize(); + + SubscribeLocalEvent(OnSummonRuneInvoked); + SubscribeLocalEvent(OnCultistSelected); + } + + private void OnSummonRuneInvoked(Entity rune, ref TryInvokeCultRuneEvent args) + { + var runeUid = rune.Owner; + if (_ui.IsUiOpen(runeUid, ListViewSelectorUiKey.Key)) + { + args.Cancel(); + return; + } + + var cultistsQuery = EntityQueryEnumerator(); + var cultist = new List(); + var invokers = args.Invokers.ToArray(); + while (cultistsQuery.MoveNext(out var cultistUid, out _)) + { + if (invokers.Contains(cultistUid)) + continue; + + var metaData = MetaData(cultistUid); + var entry = new ListViewSelectorEntry(cultistUid.ToString(), + metaData.EntityName, + metaData.EntityDescription); + + cultist.Add(entry); + } + + if (cultist.Count == 0) + { + _popup.PopupEntity(Loc.GetString("cult-rune-no-targets"), args.User, args.User); + args.Cancel(); + return; + } + + _ui.SetUiState(runeUid, ListViewSelectorUiKey.Key, new ListViewSelectorState(cultist)); + _ui.TryToggleUi(runeUid, ListViewSelectorUiKey.Key, args.User); + } + + private void OnCultistSelected(Entity ent, ref ListViewItemSelectedMessage args) + { + if (!EntityUid.TryParse(args.SelectedItem.Id, out var target)) + return; + + if (TryComp(target, out PullableComponent? pullable) && pullable.BeingPulled) + { + _popup.PopupEntity(Loc.GetString("blood-cult-summon-being-pulled"), ent, args.Actor); + return; + } + + if (TryComp(target, out CuffableComponent? cuffable) && cuffable.CuffedHandCount > 0) + { + _popup.PopupEntity(Loc.GetString("blood-cult-summon-cuffed"), ent, args.Actor); + return; + } + + var runeTransform = Transform(ent); + + _cultRune.StopPulling(target); + + _transform.SetCoordinates(target, runeTransform.Coordinates); + + _audio.PlayPvs(ent.Comp.TeleportSound, ent); + } +} diff --git a/Content.Server/WhiteDream/BloodCult/Runes/Teleport/CultRuneTeleportComponent.cs b/Content.Server/WhiteDream/BloodCult/Runes/Teleport/CultRuneTeleportComponent.cs new file mode 100644 index 00000000000..7acc40f727d --- /dev/null +++ b/Content.Server/WhiteDream/BloodCult/Runes/Teleport/CultRuneTeleportComponent.cs @@ -0,0 +1,19 @@ +using Robust.Shared.Audio; + +namespace Content.Server.WhiteDream.BloodCult.Runes.Teleport; + +[RegisterComponent] +public sealed partial class CultRuneTeleportComponent : Component +{ + [DataField] + public float TeleportGatherRange = 0.65f; + + [DataField] + public string Name = ""; + + [DataField] + public SoundPathSpecifier TeleportInSound = new("/Audio/WhiteDream/BloodCult/veilin.ogg"); + + [DataField] + public SoundPathSpecifier TeleportOutSound = new("/Audio/WhiteDream/BloodCult/veilout.ogg"); +} diff --git a/Content.Server/WhiteDream/BloodCult/Runes/Teleport/CultRuneTeleportSystem.cs b/Content.Server/WhiteDream/BloodCult/Runes/Teleport/CultRuneTeleportSystem.cs new file mode 100644 index 00000000000..c2f40ca0dfd --- /dev/null +++ b/Content.Server/WhiteDream/BloodCult/Runes/Teleport/CultRuneTeleportSystem.cs @@ -0,0 +1,93 @@ +using Content.Server.Popups; +using Content.Shared.ListViewSelector; +using Content.Shared.WhiteDream.BloodCult.UI; +using Robust.Server.Audio; +using Robust.Server.GameObjects; + +namespace Content.Server.WhiteDream.BloodCult.Runes.Teleport; + +public sealed class CultRuneTeleportSystem : EntitySystem +{ + [Dependency] private readonly AudioSystem _audio = default!; + [Dependency] private readonly CultRuneBaseSystem _cultRune = default!; + [Dependency] private readonly TransformSystem _transform = default!; + [Dependency] private readonly PopupSystem _popup = default!; + [Dependency] private readonly UserInterfaceSystem _ui = default!; + + public override void Initialize() + { + base.Initialize(); + + SubscribeLocalEvent(OnAfterRunePlaced); + SubscribeLocalEvent(OnNameSelected); + SubscribeLocalEvent(OnTeleportRuneInvoked); + SubscribeLocalEvent(OnTeleportRuneSelected); + } + + private void OnAfterRunePlaced(Entity rune, ref AfterRunePlaced args) + { + _ui.OpenUi(rune.Owner, NameSelectorUiKey.Key, args.User); + } + + private void OnNameSelected(Entity rune, ref NameSelectedMessage args) + { + rune.Comp.Name = args.Name; + } + + private void OnTeleportRuneInvoked(Entity rune, ref TryInvokeCultRuneEvent args) + { + var runeUid = rune.Owner; + if (_ui.IsUiOpen(runeUid, ListViewSelectorUiKey.Key)) + { + args.Cancel(); + return; + } + + if (!TryGetTeleportRunes(runeUid, out var runes, args.User)) + { + args.Cancel(); + return; + } + + _ui.SetUiState(runeUid, ListViewSelectorUiKey.Key, new ListViewSelectorState(runes)); + _ui.TryToggleUi(runeUid, ListViewSelectorUiKey.Key, args.User); + } + + private void OnTeleportRuneSelected(Entity origin, ref ListViewItemSelectedMessage args) + { + if (!EntityUid.TryParse(args.SelectedItem.Id, out var destination)) + return; + + var teleportTargets = _cultRune.GetTargetsNearRune(origin, origin.Comp.TeleportGatherRange); + var destinationTransform = Transform(destination); + + foreach (var target in teleportTargets) + { + _cultRune.StopPulling(target); + _transform.SetCoordinates(target, destinationTransform.Coordinates); + } + + _audio.PlayPvs(origin.Comp.TeleportOutSound, origin); + _audio.PlayPvs(origin.Comp.TeleportInSound, destination); + } + + public bool TryGetTeleportRunes(EntityUid user, out List runes, EntityUid? runeUid = null) + { + var runeQuery = EntityQueryEnumerator(); + runes = new List(); + while (runeQuery.MoveNext(out var targetRune, out var teleportRune)) + { + if (targetRune == runeUid) + continue; + + var entry = new ListViewSelectorEntry(targetRune.ToString(), teleportRune.Name); + runes.Add(entry); + } + + if (runes.Count != 0) + return true; + + _popup.PopupEntity(Loc.GetString("cult-teleport-not-found"), user, user); + return false; + } +} diff --git a/Content.Server/WhiteDream/BloodCult/Spells/BaseCultSpellComponent.cs b/Content.Server/WhiteDream/BloodCult/Spells/BaseCultSpellComponent.cs new file mode 100644 index 00000000000..9ace43c4896 --- /dev/null +++ b/Content.Server/WhiteDream/BloodCult/Spells/BaseCultSpellComponent.cs @@ -0,0 +1,11 @@ +namespace Content.Server.WhiteDream.BloodCult.Spells; + +[RegisterComponent] +public sealed partial class BaseCultSpellComponent : Component +{ + /// + /// If true will ignore protection like mindshield of holy magic. + /// + [DataField] + public bool BypassProtection; +} diff --git a/Content.Server/WhiteDream/BloodCult/Spells/BloodCultSpellsHolderComponent.cs b/Content.Server/WhiteDream/BloodCult/Spells/BloodCultSpellsHolderComponent.cs new file mode 100644 index 00000000000..5beb41e7dea --- /dev/null +++ b/Content.Server/WhiteDream/BloodCult/Spells/BloodCultSpellsHolderComponent.cs @@ -0,0 +1,31 @@ +using Content.Shared.DoAfter; +using Content.Shared.Psionics; +using Robust.Shared.Prototypes; + +namespace Content.Server.WhiteDream.BloodCult.Spells; + +[RegisterComponent] +public sealed partial class BloodCultSpellsHolderComponent : Component +{ + [DataField] + public int DefaultMaxSpells = 1; + + [DataField] + public TimeSpan SpellCreationTime = TimeSpan.FromSeconds(2); + + [DataField] + public ProtoId PowersPoolPrototype = "BloodCultPowers"; + + [ViewVariables(VVAccess.ReadOnly)] + public List SelectedSpells = new(); + + public int MaxSpells; + + public DoAfterId? DoAfterId; + + /// + /// Since radial selector menu doesn't have metadata, we use this to toggle between remove and + /// add spells modes. + /// + public bool AddSpellsMode = true; +} diff --git a/Content.Server/WhiteDream/BloodCult/Spells/BloodCultSpellsSystem.cs b/Content.Server/WhiteDream/BloodCult/Spells/BloodCultSpellsSystem.cs new file mode 100644 index 00000000000..76697c252a8 --- /dev/null +++ b/Content.Server/WhiteDream/BloodCult/Spells/BloodCultSpellsSystem.cs @@ -0,0 +1,294 @@ +using Content.Server.Actions; +using Content.Server.Cuffs; +using Content.Server.DoAfter; +using Content.Server.Emp; +using Content.Server.Hands.Systems; +using Content.Server.Popups; +using Content.Server.Stunnable; +using Content.Shared.Abilities.Psionics; +using Content.Shared.Actions; +using Content.Shared.Actions.Events; +using Content.Shared.Clothing.Components; +using Content.Shared.DoAfter; +using Content.Shared.Inventory; +using Content.Shared.Mindshield.Components; +using Content.Shared.Popups; +using Content.Shared.RadialSelector; +using Content.Shared.Speech.Muting; +using Content.Shared.StatusEffect; +using Content.Shared.Verbs; +using Content.Shared.WhiteDream.BloodCult.Spells; +using Robust.Server.GameObjects; +using Robust.Shared.Prototypes; +using Robust.Shared.Utility; + +namespace Content.Server.WhiteDream.BloodCult.Spells; + +public sealed class BloodCultSpellsSystem : EntitySystem +{ + [Dependency] private readonly IPrototypeManager _proto = default!; + + [Dependency] private readonly ActionsSystem _actions = default!; + [Dependency] private readonly DoAfterSystem _doAfter = default!; + [Dependency] private readonly CuffableSystem _cuffable = default!; + [Dependency] private readonly EmpSystem _empSystem = default!; + [Dependency] private readonly HandsSystem _hands = default!; + [Dependency] private readonly InventorySystem _inventory = default!; + [Dependency] private readonly TransformSystem _transform = default!; + [Dependency] private readonly PopupSystem _popup = default!; + [Dependency] private readonly StatusEffectsSystem _statusEffects = default!; + [Dependency] private readonly StunSystem _stun = default!; + [Dependency] private readonly UserInterfaceSystem _ui = default!; + + public override void Initialize() + { + base.Initialize(); + + SubscribeLocalEvent(OnCultTargetEvent); + SubscribeLocalEvent(OnActionGettingDisabled); + + SubscribeLocalEvent(OnComponentStartup); + SubscribeLocalEvent>(OnGetVerbs); + SubscribeLocalEvent(OnSpellSelected); + SubscribeLocalEvent(OnSpellCreated); + + SubscribeLocalEvent(OnStun); + SubscribeLocalEvent(OnEmp); + SubscribeLocalEvent(OnShackles); + SubscribeLocalEvent(OnSummonEquipment); + } + + #region BaseHandlers + + private void OnCultTargetEvent(Entity spell, ref EntityTargetActionEvent args) + { + if (_statusEffects.HasStatusEffect(args.Performer, "Muted")) + { + args.Handled = true; + return; + } + + if (spell.Comp.BypassProtection) + return; + + if (HasComp(args.Target) || HasComp(args.Target)) + args.Handled = true; + } + + private void OnActionGettingDisabled(Entity spell, ref ActionGettingDisabledEvent args) + { + if (TryComp(args.Performer, out BloodCultSpellsHolderComponent? spellsHolder)) + spellsHolder.SelectedSpells.Remove(spell); + + _actions.RemoveAction(args.Performer, spell); + } + + private void OnComponentStartup(Entity cultist, ref ComponentStartup args) + { + cultist.Comp.MaxSpells = cultist.Comp.DefaultMaxSpells; + } + + private void OnGetVerbs(Entity cultist, ref GetVerbsEvent args) + { + if (args.User != args.Target) + return; + + var addVerb = new ExamineVerb + { + Category = VerbCategory.BloodSpells, + Text = Loc.GetString("blood-cult-select-spells-verb"), + Priority = 1, + Act = () => SelectBloodSpells(cultist) + }; + var removeVerb = new ExamineVerb + { + Category = VerbCategory.BloodSpells, + Text = Loc.GetString("blood-cult-remove-spells-verb"), + Priority = 0, + Act = () => RemoveBloodSpells(cultist) + }; + + args.Verbs.Add(removeVerb); + args.Verbs.Add(addVerb); + } + + private void OnSpellSelected(Entity cultist, ref RadialSelectorSelectedMessage args) + { + if (!cultist.Comp.AddSpellsMode) + { + if (EntityUid.TryParse(args.SelectedItem, out var actionUid)) + { + _actions.RemoveAction(cultist, actionUid); + cultist.Comp.SelectedSpells.Remove(actionUid); + } + + return; + } + + if (cultist.Comp.SelectedSpells.Count >= cultist.Comp.MaxSpells) + { + _popup.PopupEntity(Loc.GetString("blood-cult-spells-too-many"), cultist, cultist, PopupType.Medium); + return; + } + + var createSpellEvent = new CreateSpeellDoAfterEvent + { + ActionProtoId = args.SelectedItem + }; + + var doAfter = new DoAfterArgs(EntityManager, + cultist.Owner, + cultist.Comp.SpellCreationTime, + createSpellEvent, + cultist.Owner) + { + BreakOnUserMove = true + }; + + if (_doAfter.TryStartDoAfter(doAfter, out var doAfterId)) + cultist.Comp.DoAfterId = doAfterId; + } + + private void OnSpellCreated(Entity cultist, ref CreateSpeellDoAfterEvent args) + { + cultist.Comp.DoAfterId = null; + if (args.Handled || args.Cancelled) + return; + + var action = _actions.AddAction(cultist, args.ActionProtoId); + if (action.HasValue) + cultist.Comp.SelectedSpells.Add(action.Value); + } + + #endregion + + #region SpellsHandlers + + private void OnStun(BloodCultStunEvent ev) + { + if (ev.Handled) + return; + + _statusEffects.TryAddStatusEffect(ev.Target, "Muted", ev.MuteDuration, true); + _stun.TryParalyze(ev.Target, ev.ParalyzeDuration, true); + ev.Handled = true; + } + + private void OnEmp(BloodCultEmpEvent ev) + { + if (ev.Handled) + return; + + _empSystem.EmpPulse(_transform.GetMapCoordinates(ev.Performer), ev.Range, ev.EnergyConsumption, ev.Duration); + ev.Handled = true; + } + + private void OnShackles(BloodCultShacklesEvent ev) + { + if (ev.Handled) + return; + + var shuckles = Spawn(ev.ShacklesProto); + if (!_cuffable.TryAddNewCuffs(ev.Performer, ev.Target, shuckles)) + return; + + _stun.TryKnockdown(ev.Target, ev.KnockdownDuration, true); + _statusEffects.TryAddStatusEffect(ev.Target, "Muted", ev.MuteDuration, true); + ev.Handled = true; + } + + private void OnSummonEquipment(SummonEquipmentEvent ev) + { + if (ev.Handled) + return; + + foreach (var (slot, protoId) in ev.Prototypes) + { + var entity = Spawn(protoId, _transform.GetMapCoordinates(ev.Performer)); + _hands.TryPickupAnyHand(ev.Performer, entity); + if (!TryComp(entity, out ClothingComponent? _)) + continue; + + _inventory.TryUnequip(ev.Performer, slot); + _inventory.TryEquip(ev.Performer, entity, slot, force: true); + } + + ev.Handled = true; + } + + #endregion + + #region Helpers + + private void SelectBloodSpells(Entity cultist) + { + if (!_proto.TryIndex(cultist.Comp.PowersPoolPrototype, out var pool)) + return; + + if (cultist.Comp.SelectedSpells.Count >= cultist.Comp.MaxSpells) + { + _popup.PopupEntity(Loc.GetString("blood-cult-spells-too-many"), cultist, cultist, PopupType.Medium); + return; + } + + cultist.Comp.AddSpellsMode = true; + + var radialList = new List(); + foreach (var spellId in pool.Powers) + { + var entry = new RadialSelectorEntry + { + Prototype = spellId + }; + + radialList.Add(entry); + } + + var state = new RadialSelectorState(radialList, true); + + _ui.SetUiState(cultist.Owner, RadialSelectorUiKey.Key, state); + _ui.TryToggleUi(cultist.Owner, RadialSelectorUiKey.Key, cultist.Owner); + } + + private void RemoveBloodSpells(Entity cultist) + { + if (cultist.Comp.SelectedSpells.Count == 0) + { + _popup.PopupEntity(Loc.GetString("blood-cult-no-spells"), cultist, cultist, PopupType.Medium); + return; + } + + cultist.Comp.AddSpellsMode = false; + + var radialList = new List(); + foreach (var spell in cultist.Comp.SelectedSpells) + { + var entry = new RadialSelectorEntry + { + Prototype = spell.ToString(), + Icon = GetActionIcon(spell) + }; + + radialList.Add(entry); + } + + var state = new RadialSelectorState(radialList, true); + + _ui.SetUiState(cultist.Owner, RadialSelectorUiKey.Key, state); + _ui.TryToggleUi(cultist.Owner, RadialSelectorUiKey.Key, cultist.Owner); + } + + private SpriteSpecifier? GetActionIcon(EntityUid actionUid) + { + if (TryComp(actionUid, out EntityTargetActionComponent? targetAction)) + return targetAction.Icon; + if (TryComp(actionUid, out WorldTargetActionComponent? worldTargetAction)) + return worldTargetAction.Icon; + if (TryComp(actionUid, out InstantActionComponent? instantActionComponent)) + return instantActionComponent.Icon; + + return null; + } + + #endregion +} diff --git a/Content.Server/WhiteDream/BloodCult/Spells/BloodCultTeleportSpellSystem.cs b/Content.Server/WhiteDream/BloodCult/Spells/BloodCultTeleportSpellSystem.cs new file mode 100644 index 00000000000..bd605475ede --- /dev/null +++ b/Content.Server/WhiteDream/BloodCult/Spells/BloodCultTeleportSpellSystem.cs @@ -0,0 +1,67 @@ +using Content.Server.DoAfter; +using Content.Server.WhiteDream.BloodCult.Runes; +using Content.Server.WhiteDream.BloodCult.Runes.Teleport; +using Content.Shared.DoAfter; +using Content.Shared.ListViewSelector; +using Content.Shared.WhiteDream.BloodCult.Spells; +using Robust.Server.Audio; +using Robust.Server.GameObjects; + +namespace Content.Server.WhiteDream.BloodCult.Spells; + +public sealed class BloodCultTeleportSpellSystem : EntitySystem +{ + [Dependency] private readonly AudioSystem _audio = default!; + [Dependency] private readonly CultRuneBaseSystem _cultRune = default!; + [Dependency] private readonly CultRuneTeleportSystem _runeTeleport = default!; + [Dependency] private readonly DoAfterSystem _doAfter = default!; + [Dependency] private readonly TransformSystem _transform = default!; + [Dependency] private readonly UserInterfaceSystem _ui = default!; + + public override void Initialize() + { + SubscribeLocalEvent(OnTeleport); + SubscribeLocalEvent(OnTeleportRuneSelected); + SubscribeLocalEvent(OnTeleportDoAfter); + } + + private void OnTeleport(BloodCultTeleportEvent ev) + { + if (ev.Handled || !_runeTeleport.TryGetTeleportRunes(ev.Performer, out var runes)) + return; + + _ui.SetUiState(ev.Performer, ListViewSelectorUiKey.Key, new ListViewSelectorState(runes)); + _ui.TryToggleUi(ev.Performer, ListViewSelectorUiKey.Key, ev.Performer); + ev.Handled = true; + } + + private void OnTeleportRuneSelected(Entity ent, + ref ListViewItemSelectedMessage args) + { + if (!args.MetaData.TryGetValue("target", out var rawTarget) || rawTarget is not EntityUid target || + !args.MetaData.TryGetValue("duration", out var rawDuration) || rawDuration is not TimeSpan duration) + return; + + var teleportDoAfter = new TeleportActionDoAfterEvent + { + Rune = GetNetEntity(EntityUid.Parse(args.SelectedItem.Id)), + }; + var doAfterArgs = new DoAfterArgs(EntityManager, ent.Owner, duration, teleportDoAfter, target); + + _doAfter.TryStartDoAfter(doAfterArgs); + } + + private void OnTeleportDoAfter(TeleportActionDoAfterEvent ev) + { + if (ev.Target is not { } target) + return; + + var rune = GetEntity(ev.Rune); + _audio.PlayPvs(ev.TeleportOutSound, target); + + _cultRune.StopPulling(target); + _transform.SetCoordinates(target, Transform(rune).Coordinates); + + _audio.PlayPvs(ev.TeleportInSound, rune); + } +} diff --git a/Content.Server/WhiteDream/BloodCult/Spells/TwistedConstruction/TwistedConstructionSystem.cs b/Content.Server/WhiteDream/BloodCult/Spells/TwistedConstruction/TwistedConstructionSystem.cs new file mode 100644 index 00000000000..3fe6cfa6847 --- /dev/null +++ b/Content.Server/WhiteDream/BloodCult/Spells/TwistedConstruction/TwistedConstructionSystem.cs @@ -0,0 +1,51 @@ +using Content.Server.DoAfter; +using Content.Server.Mind; +using Content.Server.Stack; +using Content.Shared.DoAfter; +using Content.Shared.Stacks; +using Content.Shared.WhiteDream.BloodCult.Components; +using Content.Shared.WhiteDream.BloodCult.Spells; +using Robust.Server.GameObjects; + +namespace Content.Server.WhiteDream.BloodCult.Spells.TwistedConstruction; + +public sealed class TwistedConstructionSystem : EntitySystem +{ + [Dependency] private readonly DoAfterSystem _doAfter = default!; + [Dependency] private readonly MindSystem _mind = default!; + [Dependency] private readonly TransformSystem _transform = default!; + [Dependency] private readonly StackSystem _stack = default!; + + public override void Initialize() + { + SubscribeLocalEvent(OnTwistedConstruction); + SubscribeLocalEvent(OnDoAfter); + } + + private void OnTwistedConstruction(BloodCultTwistedConstructionEvent ev) + { + if (ev.Handled || !TryComp(ev.Target, out TwistedConstructionTargetComponent? twistedConstruction)) + return; + + var args = new DoAfterArgs(EntityManager, + ev.Performer, + twistedConstruction.DoAfterDelay, + new TwistedConstructionDoAfterEvent(), + ev.Target); + + if (_doAfter.TryStartDoAfter(args)) + ev.Handled = true; + } + + private void OnDoAfter(Entity target, ref TwistedConstructionDoAfterEvent args) + { + var replacement = Spawn(target.Comp.ReplacementProto, _transform.GetMapCoordinates(target)); + if (TryComp(target, out StackComponent? stack) && TryComp(replacement, out StackComponent? targetStack)) + _stack.SetCount(replacement, stack.Count, targetStack); + + if (_mind.TryGetMind(target, out var mindId, out _)) + _mind.TransferTo(mindId, replacement); + + QueueDel(target); + } +} diff --git a/Content.Server/WhiteDream/BloodCult/TimedFactory/TimedFactoryComponent.cs b/Content.Server/WhiteDream/BloodCult/TimedFactory/TimedFactoryComponent.cs new file mode 100644 index 00000000000..5e41c7475a4 --- /dev/null +++ b/Content.Server/WhiteDream/BloodCult/TimedFactory/TimedFactoryComponent.cs @@ -0,0 +1,16 @@ +using Content.Shared.RadialSelector; + +namespace Content.Server.WhiteDream.BloodCult.TimedFactory; + +[RegisterComponent] +public sealed partial class TimedFactoryComponent : Component +{ + [DataField(required: true)] + public List Entries = new(); + + [DataField] + public float Cooldown = 240; + + [ViewVariables(VVAccess.ReadOnly)] + public float CooldownRemaining = 0; +} diff --git a/Content.Server/WhiteDream/BloodCult/TimedFactory/TimedFactorySystem.cs b/Content.Server/WhiteDream/BloodCult/TimedFactory/TimedFactorySystem.cs new file mode 100644 index 00000000000..5dc4ff3d653 --- /dev/null +++ b/Content.Server/WhiteDream/BloodCult/TimedFactory/TimedFactorySystem.cs @@ -0,0 +1,61 @@ +using Content.Server.Hands.Systems; +using Content.Server.Popups; +using Content.Shared.RadialSelector; +using Content.Shared.UserInterface; +using Content.Shared.WhiteDream.BloodCult; +using Robust.Server.GameObjects; + +namespace Content.Server.WhiteDream.BloodCult.TimedFactory; + +public sealed class TimedFactorySystem : EntitySystem +{ + [Dependency] private readonly AppearanceSystem _appearance = default!; + [Dependency] private readonly HandsSystem _hands = default!; + [Dependency] private readonly PopupSystem _popup = default!; + [Dependency] private readonly UserInterfaceSystem _ui = default!; + + public override void Initialize() + { + base.Initialize(); + + SubscribeLocalEvent(OnTryOpenMenu); + SubscribeLocalEvent(OnPrototypeSelected); + } + + public override void Update(float frameTime) + { + base.Update(frameTime); + + var factoryQuery = EntityQueryEnumerator(); + while (factoryQuery.MoveNext(out var uid, out var factory)) + { + if (factory.CooldownRemaining > 0) + factory.CooldownRemaining -= frameTime; + else + _appearance.SetData(uid, GenericCultVisuals.State, true); + } + } + + private void OnTryOpenMenu(Entity factory, ref ActivatableUIOpenAttemptEvent args) + { + var cooldown = MathF.Ceiling(factory.Comp.CooldownRemaining); + if (cooldown > 0) + { + args.Cancel(); + _popup.PopupEntity(Loc.GetString("timed-factory-cooldown", ("cooldown", cooldown)), factory, args.User); + } + + if (_ui.IsUiOpen(factory.Owner, RadialSelectorUiKey.Key)) + return; + + _ui.SetUiState(factory.Owner, RadialSelectorUiKey.Key, new RadialSelectorState(factory.Comp.Entries)); + } + + private void OnPrototypeSelected(Entity factory, ref RadialSelectorSelectedMessage args) + { + var product = Spawn(args.SelectedItem, Transform(args.Actor).Coordinates); + _hands.TryPickupAnyHand(args.Actor, product); + factory.Comp.CooldownRemaining = factory.Comp.Cooldown; + _appearance.SetData(factory, GenericCultVisuals.State, false); + } +} diff --git a/Content.Shared/Actions/Events/ActionGettingDisabledEvent.cs b/Content.Shared/Actions/Events/ActionGettingDisabledEvent.cs new file mode 100644 index 00000000000..0cf3dd6d39e --- /dev/null +++ b/Content.Shared/Actions/Events/ActionGettingDisabledEvent.cs @@ -0,0 +1,8 @@ +namespace Content.Shared.Actions.Events; + +/// +/// Raised on the action entity when it is getting disabled on performer. +/// +/// The entity that performed this action. +[ByRefEvent] +public readonly record struct ActionGettingDisabledEvent(EntityUid Performer); diff --git a/Content.Shared/Actions/SharedActionsSystem.cs b/Content.Shared/Actions/SharedActionsSystem.cs index 6445039b9cb..785a4478d56 100644 --- a/Content.Shared/Actions/SharedActionsSystem.cs +++ b/Content.Shared/Actions/SharedActionsSystem.cs @@ -580,7 +580,11 @@ public void PerformAction(EntityUid performer, ActionsComponent? component, Enti dirty = true; action.Charges--; if (action is { Charges: 0, RenewCharges: false }) + { + var disabledEv = new ActionGettingDisabledEvent(performer); + RaiseLocalEvent(actionId, ref disabledEv); action.Enabled = false; + } } action.Cooldown = null; diff --git a/Content.Shared/Blocking/BlockingSystem.User.cs b/Content.Shared/Blocking/BlockingSystem.User.cs index 2cd1db7f1fe..584df3a3ffc 100644 --- a/Content.Shared/Blocking/BlockingSystem.User.cs +++ b/Content.Shared/Blocking/BlockingSystem.User.cs @@ -27,7 +27,11 @@ private void OnParentChanged(EntityUid uid, BlockingUserComponent component, ref UserStopBlocking(uid, component); } - private void OnInsertAttempt(EntityUid uid, BlockingUserComponent component, ContainerGettingInsertedAttemptEvent args) + private void OnInsertAttempt( + EntityUid uid, + BlockingUserComponent component, + ContainerGettingInsertedAttemptEvent args + ) { UserStopBlocking(uid, component); } @@ -42,32 +46,27 @@ private void OnAnchorChanged(EntityUid uid, BlockingUserComponent component, ref private void OnUserDamageModified(EntityUid uid, BlockingUserComponent component, DamageModifyEvent args) { - if (TryComp(component.BlockingItem, out var blocking)) - { - if (args.Damage.GetTotal() <= 0) - return; - - // A shield should only block damage it can itself absorb. To determine that we need the Damageable component on it. - if (!TryComp(component.BlockingItem, out var dmgComp)) - return; + // A shield should only block damage it can itself absorb. To determine that we need the Damageable component on it. + if (!TryComp(component.BlockingItem, out var blocking) || args.Damage.GetTotal() <= 0 || + !TryComp(component.BlockingItem, out var dmgComp)) + return; - var blockFraction = blocking.IsBlocking ? blocking.ActiveBlockFraction : blocking.PassiveBlockFraction; - blockFraction = Math.Clamp(blockFraction, 0, 1); - _damageable.TryChangeDamage(component.BlockingItem, blockFraction * args.OriginalDamage); + var ev = new BeforeBlockingEvent(uid, args.Origin); + RaiseLocalEvent(component.BlockingItem.Value, ev); + if (ev.Cancelled) + return; - var modify = new DamageModifierSet(); - foreach (var key in dmgComp.Damage.DamageDict.Keys) - { - modify.Coefficients.TryAdd(key, 1 - blockFraction); - } + var blockFraction = blocking.IsBlocking ? blocking.ActiveBlockFraction : blocking.PassiveBlockFraction; + blockFraction = Math.Clamp(blockFraction, 0, 1); + _damageable.TryChangeDamage(component.BlockingItem, blockFraction * args.OriginalDamage); - args.Damage = DamageSpecifier.ApplyModifierSet(args.Damage, modify); + var modify = new DamageModifierSet(); + foreach (var key in dmgComp.Damage.DamageDict.Keys) + modify.Coefficients.TryAdd(key, 1 - blockFraction); - if (blocking.IsBlocking && !args.Damage.Equals(args.OriginalDamage)) - { - _audio.PlayPvs(blocking.BlockSound, uid); - } - } + args.Damage = DamageSpecifier.ApplyModifierSet(args.Damage, modify); + if (blocking.IsBlocking && !args.Damage.Equals(args.OriginalDamage)) + _audio.PlayPvs(blocking.BlockSound, uid); } private void OnDamageModified(EntityUid uid, BlockingComponent component, DamageModifyEvent args) @@ -87,7 +86,6 @@ private void OnEntityTerminating(EntityUid uid, BlockingUserComponent component, return; StopBlockingHelper(component.BlockingItem.Value, blockingComponent, uid); - } /// diff --git a/Content.Shared/Blocking/Components/BlockingComponent.cs b/Content.Shared/Blocking/Components/BlockingComponent.cs index f869c20679d..43162049b60 100644 --- a/Content.Shared/Blocking/Components/BlockingComponent.cs +++ b/Content.Shared/Blocking/Components/BlockingComponent.cs @@ -77,3 +77,12 @@ public sealed partial class BlockingComponent : Component [DataField("activeBlockFraction"), ViewVariables(VVAccess.ReadWrite)] public float ActiveBlockFraction = 1.0f; } + +/// +/// Raised directed on the blocking object when attempting to block. +/// +public sealed class BeforeBlockingEvent(EntityUid user, EntityUid? origin) : CancellableEntityEventArgs +{ + public EntityUid User = user; + public EntityUid? Origin = origin; +} diff --git a/Content.Shared/Cuffs/SharedCuffableSystem.cs b/Content.Shared/Cuffs/SharedCuffableSystem.cs index d70a1e63083..297fe095f8c 100644 --- a/Content.Shared/Cuffs/SharedCuffableSystem.cs +++ b/Content.Shared/Cuffs/SharedCuffableSystem.cs @@ -666,8 +666,11 @@ public void Uncuff(EntityUid target, EntityUid? user, EntityUid cuffsToRemove, C if (cuff.BreakOnRemove) { QueueDel(cuffsToRemove); - var trash = Spawn(cuff.BrokenPrototype, Transform(cuffsToRemove).Coordinates); - _hands.PickupOrDrop(user, trash); + if (cuff.BrokenPrototype.HasValue) + { + var trash = Spawn(cuff.BrokenPrototype, Transform(cuffsToRemove).Coordinates); + _hands.PickupOrDrop(user, trash); + } } else { diff --git a/Content.Shared/Doors/DoorEvents.cs b/Content.Shared/Doors/DoorEvents.cs index 08a2c8b18b1..e8d880f1b5d 100644 --- a/Content.Shared/Doors/DoorEvents.cs +++ b/Content.Shared/Doors/DoorEvents.cs @@ -37,6 +37,7 @@ public sealed class BeforeDoorOpenedEvent : CancellableEntityEventArgs public sealed class BeforeDoorClosedEvent : CancellableEntityEventArgs { public bool PerformCollisionCheck; + public EntityUid? User = null; public BeforeDoorClosedEvent(bool performCollisionCheck) { diff --git a/Content.Shared/Doors/Systems/SharedDoorSystem.cs b/Content.Shared/Doors/Systems/SharedDoorSystem.cs index b58b7b265e9..ac9256ba3cf 100644 --- a/Content.Shared/Doors/Systems/SharedDoorSystem.cs +++ b/Content.Shared/Doors/Systems/SharedDoorSystem.cs @@ -430,7 +430,11 @@ public bool CanClose(EntityUid uid, DoorComponent? door = null, EntityUid? user if (door.State is DoorState.Welded or DoorState.Closed) return false; - var ev = new BeforeDoorClosedEvent(door.PerformCollisionCheck); + var ev = new BeforeDoorClosedEvent(door.PerformCollisionCheck) + { + User = user + }; + RaiseLocalEvent(uid, ev); if (ev.Cancelled) return false; diff --git a/Content.Shared/Ensnaring/Components/EnsnaringComponent.cs b/Content.Shared/Ensnaring/Components/EnsnaringComponent.cs index fe415e0f4a8..98b4d387fb8 100644 --- a/Content.Shared/Ensnaring/Components/EnsnaringComponent.cs +++ b/Content.Shared/Ensnaring/Components/EnsnaringComponent.cs @@ -1,7 +1,9 @@ using System.Threading; +using Content.Shared.Whitelist; using Robust.Shared.GameStates; namespace Content.Shared.Ensnaring.Components; + /// /// Use this on something you want to use to ensnare an entity with /// @@ -61,7 +63,12 @@ public sealed partial class EnsnaringComponent : Component /// [DataField] public bool DestroyOnRemove = false; - + + /// + /// Entites which bola will pass through. + /// + [DataField] + public EntityWhitelist? IgnoredTargets; } /// diff --git a/Content.Shared/ListViewSelector/ListViewSelectorEntry.cs b/Content.Shared/ListViewSelector/ListViewSelectorEntry.cs new file mode 100644 index 00000000000..1c97108277c --- /dev/null +++ b/Content.Shared/ListViewSelector/ListViewSelectorEntry.cs @@ -0,0 +1,33 @@ +using Robust.Shared.Serialization; + +namespace Content.Shared.ListViewSelector; + +[Serializable, NetSerializable] +public record ListViewSelectorEntry(string Id, string Name = "", string Description = ""); + +[Serializable, NetSerializable] +public enum ListViewSelectorUiKey +{ + Key +} + +[Serializable, NetSerializable] +public sealed class ListViewSelectorState( + List items, + Dictionary metaData = default!) : BoundUserInterfaceState +{ + public List Items { get; } = items; + public Dictionary MetaData = metaData; +} + +[Serializable, NetSerializable] +public sealed class ListViewItemSelectedMessage( + ListViewSelectorEntry selectedItem, + int index, + Dictionary metaData = default!) + : BoundUserInterfaceMessage +{ + public ListViewSelectorEntry SelectedItem { get; private set; } = selectedItem; + public int Index { get; private set; } = index; + public Dictionary MetaData = metaData; +} diff --git a/Content.Shared/Magic/Events/ChangeComponentSpellEvent.cs b/Content.Shared/Magic/Events/ChangeComponentSpellEvent.cs index 61e75c8b1aa..1f09bfd3b2d 100644 --- a/Content.Shared/Magic/Events/ChangeComponentSpellEvent.cs +++ b/Content.Shared/Magic/Events/ChangeComponentSpellEvent.cs @@ -1,4 +1,5 @@ using Content.Shared.Actions; +using Content.Shared.Chat; using Robust.Shared.Prototypes; namespace Content.Shared.Magic.Events; @@ -21,4 +22,6 @@ public sealed partial class ChangeComponentsSpellEvent : EntityTargetActionEvent [DataField("speech")] public string? Speech { get; private set; } + + public InGameICChatType ChatType { get; } = InGameICChatType.Speak; } diff --git a/Content.Shared/Magic/Events/ChargeSpellEvent.cs b/Content.Shared/Magic/Events/ChargeSpellEvent.cs index 8898761ec2a..57e1eb880dd 100644 --- a/Content.Shared/Magic/Events/ChargeSpellEvent.cs +++ b/Content.Shared/Magic/Events/ChargeSpellEvent.cs @@ -1,4 +1,5 @@ using Content.Shared.Actions; +using Content.Shared.Chat; namespace Content.Shared.Magic.Events; @@ -15,4 +16,6 @@ public sealed partial class ChargeSpellEvent : InstantActionEvent, ISpeakSpell [DataField] public string? Speech { get; private set; } + + public InGameICChatType ChatType { get; } = InGameICChatType.Speak; } diff --git a/Content.Shared/Magic/Events/InstantSpawnSpellEvent.cs b/Content.Shared/Magic/Events/InstantSpawnSpellEvent.cs index 1405b158271..66337839596 100644 --- a/Content.Shared/Magic/Events/InstantSpawnSpellEvent.cs +++ b/Content.Shared/Magic/Events/InstantSpawnSpellEvent.cs @@ -1,4 +1,5 @@ using Content.Shared.Actions; +using Content.Shared.Chat; using Robust.Shared.Prototypes; namespace Content.Shared.Magic.Events; @@ -17,6 +18,8 @@ public sealed partial class InstantSpawnSpellEvent : InstantActionEvent, ISpeakS [DataField] public string? Speech { get; private set; } + public InGameICChatType ChatType { get; } = InGameICChatType.Speak; + /// /// Gets the targeted spawn positons; may lead to multiple entities being spawned. /// diff --git a/Content.Shared/Magic/Events/KnockSpellEvent.cs b/Content.Shared/Magic/Events/KnockSpellEvent.cs index 24a1700d21f..6775a679ab8 100644 --- a/Content.Shared/Magic/Events/KnockSpellEvent.cs +++ b/Content.Shared/Magic/Events/KnockSpellEvent.cs @@ -1,4 +1,5 @@ using Content.Shared.Actions; +using Content.Shared.Chat; namespace Content.Shared.Magic.Events; @@ -14,4 +15,6 @@ public sealed partial class KnockSpellEvent : InstantActionEvent, ISpeakSpell [DataField] public string? Speech { get; private set; } + + public InGameICChatType ChatType { get; } = InGameICChatType.Speak; } diff --git a/Content.Shared/Magic/Events/ProjectileSpellEvent.cs b/Content.Shared/Magic/Events/ProjectileSpellEvent.cs index 336ea03346b..cf338a6bb43 100644 --- a/Content.Shared/Magic/Events/ProjectileSpellEvent.cs +++ b/Content.Shared/Magic/Events/ProjectileSpellEvent.cs @@ -1,4 +1,5 @@ using Content.Shared.Actions; +using Content.Shared.Chat; using Robust.Shared.Prototypes; namespace Content.Shared.Magic.Events; @@ -13,4 +14,6 @@ public sealed partial class ProjectileSpellEvent : WorldTargetActionEvent, ISpea [DataField] public string? Speech { get; private set; } + + public InGameICChatType ChatType { get; } = InGameICChatType.Speak; } diff --git a/Content.Shared/Magic/Events/SmiteSpellEvent.cs b/Content.Shared/Magic/Events/SmiteSpellEvent.cs index 74ca116ad59..35d9a5b1cf8 100644 --- a/Content.Shared/Magic/Events/SmiteSpellEvent.cs +++ b/Content.Shared/Magic/Events/SmiteSpellEvent.cs @@ -1,4 +1,5 @@ using Content.Shared.Actions; +using Content.Shared.Chat; namespace Content.Shared.Magic.Events; @@ -13,4 +14,6 @@ public sealed partial class SmiteSpellEvent : EntityTargetActionEvent, ISpeakSpe [DataField] public string? Speech { get; private set; } + + public InGameICChatType ChatType { get; } = InGameICChatType.Speak; } diff --git a/Content.Shared/Magic/Events/SpeakSpellEvent.cs b/Content.Shared/Magic/Events/SpeakSpellEvent.cs index 1b3f7af63c3..d04daf139d8 100644 --- a/Content.Shared/Magic/Events/SpeakSpellEvent.cs +++ b/Content.Shared/Magic/Events/SpeakSpellEvent.cs @@ -1,8 +1,11 @@ -namespace Content.Shared.Magic.Events; +using Content.Shared.Chat; + +namespace Content.Shared.Magic.Events; [ByRefEvent] -public readonly struct SpeakSpellEvent(EntityUid performer, string speech) +public readonly struct SpeakSpellEvent(EntityUid performer, string speech, InGameICChatType chatType) { public readonly EntityUid Performer = performer; public readonly string Speech = speech; + public readonly InGameICChatType ChatType = chatType; } diff --git a/Content.Shared/Magic/Events/TeleportSpellEvent.cs b/Content.Shared/Magic/Events/TeleportSpellEvent.cs index 525c1e51052..2f07cab2016 100644 --- a/Content.Shared/Magic/Events/TeleportSpellEvent.cs +++ b/Content.Shared/Magic/Events/TeleportSpellEvent.cs @@ -1,4 +1,5 @@ using Content.Shared.Actions; +using Content.Shared.Chat; namespace Content.Shared.Magic.Events; @@ -8,6 +9,8 @@ public sealed partial class TeleportSpellEvent : WorldTargetActionEvent, ISpeakS [DataField] public string? Speech { get; private set; } + public InGameICChatType ChatType { get; } = InGameICChatType.Speak; + // TODO: Move to magic component // TODO: Maybe not since sound specifier is a thing // Keep here to remind what the volume was set as diff --git a/Content.Shared/Magic/Events/WorldSpawnSpellEvent.cs b/Content.Shared/Magic/Events/WorldSpawnSpellEvent.cs index 2f50c67b3e7..01d5af8fd78 100644 --- a/Content.Shared/Magic/Events/WorldSpawnSpellEvent.cs +++ b/Content.Shared/Magic/Events/WorldSpawnSpellEvent.cs @@ -1,5 +1,6 @@ using System.Numerics; using Content.Shared.Actions; +using Content.Shared.Chat; using Content.Shared.Storage; namespace Content.Shared.Magic.Events; @@ -31,4 +32,6 @@ public sealed partial class WorldSpawnSpellEvent : WorldTargetActionEvent, ISpea [DataField] public string? Speech { get; private set; } + + public InGameICChatType ChatType { get; } = InGameICChatType.Speak; } diff --git a/Content.Shared/Magic/ISpeakSpell.cs b/Content.Shared/Magic/ISpeakSpell.cs index 954b99417fc..30e7f5a2dea 100644 --- a/Content.Shared/Magic/ISpeakSpell.cs +++ b/Content.Shared/Magic/ISpeakSpell.cs @@ -1,4 +1,6 @@ -namespace Content.Shared.Magic; +using Content.Shared.Chat; + +namespace Content.Shared.Magic; public interface ISpeakSpell // The speak n spell interface { @@ -6,4 +8,6 @@ public interface ISpeakSpell // The speak n spell interface /// Localized string spoken by the caster when casting this spell. /// public string? Speech { get; } + + public InGameICChatType ChatType { get; } } diff --git a/Content.Shared/Magic/SharedMagicSystem.cs b/Content.Shared/Magic/SharedMagicSystem.cs index cc7a297aa40..b0a9fef75d0 100644 --- a/Content.Shared/Magic/SharedMagicSystem.cs +++ b/Content.Shared/Magic/SharedMagicSystem.cs @@ -513,7 +513,7 @@ private void Speak(BaseActionEvent args) if (args is not ISpeakSpell speak || string.IsNullOrWhiteSpace(speak.Speech)) return; - var ev = new SpeakSpellEvent(args.Performer, speak.Speech); + var ev = new SpeakSpellEvent(args.Performer, speak.Speech, speak.ChatType); RaiseLocalEvent(ref ev); } } diff --git a/Content.Shared/Movement/Pulling/Systems/PullingSystem.cs b/Content.Shared/Movement/Pulling/Systems/PullingSystem.cs index 11a1d94b29b..f766e55211f 100644 --- a/Content.Shared/Movement/Pulling/Systems/PullingSystem.cs +++ b/Content.Shared/Movement/Pulling/Systems/PullingSystem.cs @@ -1,3 +1,5 @@ +using System.Diagnostics.CodeAnalysis; +using System.Numerics; using Content.Shared.ActionBlocker; using Content.Shared.Administration.Logs; using Content.Shared.Alert; @@ -405,6 +407,16 @@ private bool OnRequestMovePulledObject(ICommonSession? session, EntityCoordinate return false; } + public bool TryGetPulledEntity(EntityUid puller, [NotNullWhen(true)] out EntityUid? pulling, PullerComponent? component = null) + { + pulling = null; + if (!Resolve(puller, ref component, false) || !component.Pulling.HasValue) + return false; + + pulling = component.Pulling; + return true; + } + public bool IsPulling(EntityUid puller, PullerComponent? component = null) { return Resolve(puller, ref component, false) && component.Pulling != null; @@ -584,8 +596,11 @@ public bool TryStartPull(EntityUid pullerUid, EntityUid pullableUid, return true; } - public bool TryStopPull(EntityUid pullableUid, PullableComponent pullable, EntityUid? user = null) + public bool TryStopPull(EntityUid pullableUid, PullableComponent? pullable = null, EntityUid? user = null) { + if (!Resolve(pullableUid, ref pullable, false)) + return false; + var pullerUidNull = pullable.Puller; if (pullerUidNull == null) diff --git a/Content.Shared/Psionics/PsionicPowerPoolPrototype.cs b/Content.Shared/Psionics/PsionicPowerPoolPrototype.cs new file mode 100644 index 00000000000..9c505a07775 --- /dev/null +++ b/Content.Shared/Psionics/PsionicPowerPoolPrototype.cs @@ -0,0 +1,15 @@ +using Robust.Shared.Prototypes; + +namespace Content.Shared.Psionics; + +[Prototype("psionicPowerPool")] +public sealed partial class PsionicPowerPoolPrototype : IPrototype +{ + [ViewVariables] + [IdDataField] + public string ID { get; private set; } = default!; + + [ViewVariables] + [DataField] + public List Powers = new(); +} diff --git a/Content.Shared/Repulsor/RepulseComponent.cs b/Content.Shared/Repulsor/RepulseComponent.cs new file mode 100644 index 00000000000..cef64458e27 --- /dev/null +++ b/Content.Shared/Repulsor/RepulseComponent.cs @@ -0,0 +1,19 @@ +namespace Content.Shared.Repulsor; + +[RegisterComponent] +public sealed partial class RepulseComponent : Component +{ + [DataField] + public float ForceMultiplier = 13000; + + [DataField] + public TimeSpan KnockdownDuration = TimeSpan.FromSeconds(3); + + [DataField] + public TimeSpan StunDuration = TimeSpan.FromSeconds(3); +} + +public sealed class BeforeRepulseEvent(EntityUid target) : CancellableEntityEventArgs +{ + public EntityUid Target = target; +} diff --git a/Content.Shared/Repulsor/RepulseOnTouchComponent.cs b/Content.Shared/Repulsor/RepulseOnTouchComponent.cs new file mode 100644 index 00000000000..a210f40d785 --- /dev/null +++ b/Content.Shared/Repulsor/RepulseOnTouchComponent.cs @@ -0,0 +1,4 @@ +namespace Content.Shared.Repulsor; + +[RegisterComponent] +public sealed partial class RepulseOnTouchComponent : Component; diff --git a/Content.Shared/Repulsor/RepulseSystem.cs b/Content.Shared/Repulsor/RepulseSystem.cs new file mode 100644 index 00000000000..70e39bad5ba --- /dev/null +++ b/Content.Shared/Repulsor/RepulseSystem.cs @@ -0,0 +1,50 @@ +using Content.Shared.Interaction; +using Content.Shared.Standing; +using Content.Shared.Stunnable; +using Robust.Shared.Physics.Events; +using Robust.Shared.Physics.Systems; + +namespace Content.Shared.Repulsor; + +public sealed class RepulseSystem : EntitySystem +{ + [Dependency] private readonly SharedTransformSystem _transform = default!; + [Dependency] private readonly SharedStunSystem _stunSystem = default!; + [Dependency] private readonly SharedPhysicsSystem _physics = default!; + + public override void Initialize() + { + base.Initialize(); + + SubscribeLocalEvent(HandleCollision); + SubscribeLocalEvent(OnHandInteract); + } + + private void HandleCollision(Entity touchRepulsor, ref StartCollideEvent args) + { + if (!TryComp(touchRepulsor, out RepulseComponent? repulse)) + return; + + Repulse((touchRepulsor.Owner, repulse), args.OtherEntity); + } + + private void OnHandInteract(Entity repulsor, ref InteractHandEvent args) + { + Repulse(repulsor, args.User); + } + + public void Repulse(Entity repulsor, EntityUid user) + { + var ev = new BeforeRepulseEvent(user); + RaiseLocalEvent(repulsor, ev); + if (ev.Cancelled) + return; + + var direction = _transform.GetMapCoordinates(user).Position - _transform.GetMapCoordinates(repulsor).Position; + var impulse = direction * repulsor.Comp.ForceMultiplier; + + _physics.ApplyLinearImpulse(user, impulse); + _stunSystem.TryStun(user, repulsor.Comp.StunDuration, true); + _stunSystem.TryKnockdown(user, repulsor.Comp.KnockdownDuration, true, DropHeldItemsBehavior.DropIfStanding); + } +} diff --git a/Content.Shared/Stunnable/SharedStunSystem.cs b/Content.Shared/Stunnable/SharedStunSystem.cs index ad36ba9329a..989af647dc8 100644 --- a/Content.Shared/Stunnable/SharedStunSystem.cs +++ b/Content.Shared/Stunnable/SharedStunSystem.cs @@ -172,8 +172,7 @@ public bool TryStun(EntityUid uid, TimeSpan time, bool refresh, /// /// Knocks down the entity, making it fall to the ground. /// - public bool TryKnockdown(EntityUid uid, TimeSpan time, bool refresh, - DropHeldItemsBehavior behavior = DropHeldItemsBehavior.DropIfStanding, + public bool TryKnockdown(EntityUid uid, TimeSpan time, bool refresh, DropHeldItemsBehavior behavior, StatusEffectsComponent? status = null) { if (time <= TimeSpan.Zero || !Resolve(uid, ref status, false)) diff --git a/Content.Shared/UserInterface/ActivatableUIComponent.cs b/Content.Shared/UserInterface/ActivatableUIComponent.cs index 93f05acac05..30c07637420 100644 --- a/Content.Shared/UserInterface/ActivatableUIComponent.cs +++ b/Content.Shared/UserInterface/ActivatableUIComponent.cs @@ -45,6 +45,12 @@ public sealed partial class ActivatableUIComponent : Component [DataField, ViewVariables(VVAccess.ReadWrite)] public EntityWhitelist? RequiredItems; + /// + /// Whitelist for the user who is trying to open this UI. + /// + [DataField] + public EntityWhitelist? UserWhitelist; + /// /// If true, then this UI can only be opened via verbs. I.e., normal interactions/activations will not open /// the UI. diff --git a/Content.Shared/UserInterface/ActivatableUISystem.cs b/Content.Shared/UserInterface/ActivatableUISystem.cs index 14ce4f20dce..19524c39b34 100644 --- a/Content.Shared/UserInterface/ActivatableUISystem.cs +++ b/Content.Shared/UserInterface/ActivatableUISystem.cs @@ -9,6 +9,8 @@ using Content.Shared.Popups; using Content.Shared.Verbs; using Robust.Shared.Utility; +using Content.Shared.Whitelist; +using Robust.Shared.Containers; namespace Content.Shared.UserInterface; @@ -16,6 +18,7 @@ public sealed partial class ActivatableUISystem : EntitySystem { [Dependency] private readonly ISharedAdminManager _adminManager = default!; [Dependency] private readonly ActionBlockerSystem _blockerSystem = default!; + [Dependency] private readonly EntityWhitelistSystem _entityWhitelist = default!; [Dependency] private readonly SharedUserInterfaceSystem _uiSystem = default!; [Dependency] private readonly SharedPopupSystem _popupSystem = default!; [Dependency] private readonly SharedHandsSystem _hands = default!; @@ -96,7 +99,8 @@ private bool ShouldAddVerb(EntityUid uid, ActivatableUIComponent component, G if (!args.CanAccess) return false; - if (!component.RequiredItems?.IsValid(args.Using ?? default, EntityManager) ?? false) + if (component.RequiredItems is not null && !_entityWhitelist.IsValid(component.RequiredItems, args.Using ?? default) || + component.UserWhitelist is not null && !_entityWhitelist.IsValid(component.UserWhitelist, args.User)) return false; if (component.RequireHands) @@ -153,10 +157,8 @@ private void OnInteractUsing(EntityUid uid, ActivatableUIComponent component, In if (component.VerbOnly) return; - if (component.RequiredItems == null) - return; - - if (!component.RequiredItems.IsValid(args.Used, EntityManager)) + if (component.RequiredItems is null || !_entityWhitelist.IsValid(component.RequiredItems, args.Used) || + component.UserWhitelist is not null && !_entityWhitelist.IsValid(component.UserWhitelist, args.User)) return; args.Handled = InteractUI(args.User, uid, component); diff --git a/Content.Shared/Verbs/VerbCategory.cs b/Content.Shared/Verbs/VerbCategory.cs index 3331cad30b0..38ec881a7ce 100644 --- a/Content.Shared/Verbs/VerbCategory.cs +++ b/Content.Shared/Verbs/VerbCategory.cs @@ -28,10 +28,23 @@ public sealed class VerbCategory /// public readonly bool IconsOnly; - public VerbCategory(string text, string? icon, bool iconsOnly = false) + public VerbCategory(string text, bool iconsOnly = false) { Text = Loc.GetString(text); - Icon = icon == null ? null : new SpriteSpecifier.Texture(new(icon)); + IconsOnly = iconsOnly; + } + + public VerbCategory(string text, string icon, bool iconsOnly = false) + { + Text = Loc.GetString(text); + Icon = new SpriteSpecifier.Texture(new ResPath(icon)); + IconsOnly = iconsOnly; + } + + public VerbCategory(string text, SpriteSpecifier? sprite, bool iconsOnly = false) + { + Text = Loc.GetString(text); + Icon = sprite; IconsOnly = iconsOnly; } @@ -39,7 +52,8 @@ public VerbCategory(string text, string? icon, bool iconsOnly = false) new("verb-categories-admin", "/Textures/Interface/character.svg.192dpi.png"); public static readonly VerbCategory Antag = - new("verb-categories-antag", "/Textures/Interface/VerbIcons/antag-e_sword-temp.192dpi.png", iconsOnly: true) { Columns = 5 }; + new("verb-categories-antag", "/Textures/Interface/VerbIcons/antag-e_sword-temp.192dpi.png", iconsOnly: true) + { Columns = 5 }; public static readonly VerbCategory Examine = new("verb-categories-examine", "/Textures/Interface/VerbIcons/examine.svg.192dpi.png"); @@ -60,32 +74,37 @@ public VerbCategory(string text, string? icon, bool iconsOnly = false) new("verb-categories-unbuckle", "/Textures/Interface/VerbIcons/unbuckle.svg.192dpi.png"); public static readonly VerbCategory Rotate = - new("verb-categories-rotate", "/Textures/Interface/VerbIcons/refresh.svg.192dpi.png", iconsOnly: true) { Columns = 5 }; + new("verb-categories-rotate", "/Textures/Interface/VerbIcons/refresh.svg.192dpi.png", iconsOnly: true) + { Columns = 5 }; public static readonly VerbCategory Smite = - new("verb-categories-smite", "/Textures/Interface/VerbIcons/smite.svg.192dpi.png", iconsOnly: true) { Columns = 6 }; + new("verb-categories-smite", "/Textures/Interface/VerbIcons/smite.svg.192dpi.png", iconsOnly: true) + { Columns = 6 }; + public static readonly VerbCategory Tricks = - new("verb-categories-tricks", "/Textures/Interface/AdminActions/tricks.png", iconsOnly: true) { Columns = 5 }; + new("verb-categories-tricks", "/Textures/Interface/AdminActions/tricks.png", iconsOnly: true) + { Columns = 5 }; public static readonly VerbCategory SetTransferAmount = new("verb-categories-transfer", "/Textures/Interface/VerbIcons/spill.svg.192dpi.png"); - public static readonly VerbCategory Split = - new("verb-categories-split", null); + public static readonly VerbCategory Split = new("verb-categories-split"); + + public static readonly VerbCategory InstrumentStyle = new("verb-categories-instrument-style"); - public static readonly VerbCategory InstrumentStyle = - new("verb-categories-instrument-style", null); + public static readonly VerbCategory ChannelSelect = new("verb-categories-channel-select"); - public static readonly VerbCategory ChannelSelect = new("verb-categories-channel-select", null); + public static readonly VerbCategory SetSensor = new("verb-categories-set-sensor"); - public static readonly VerbCategory SetSensor = new("verb-categories-set-sensor", null); + public static readonly VerbCategory Lever = new("verb-categories-lever"); - public static readonly VerbCategory Lever = new("verb-categories-lever", null); + public static readonly VerbCategory SelectType = new("verb-categories-select-type"); - public static readonly VerbCategory SelectType = new("verb-categories-select-type", null); + public static readonly VerbCategory PowerLevel = new("verb-categories-power-level"); - public static readonly VerbCategory PowerLevel = new("verb-categories-power-level", null); + public static readonly VerbCategory Interaction = new("verb-categories-interaction"); - public static readonly VerbCategory Interaction = new("verb-categories-interaction", null); + public static readonly VerbCategory BloodSpells = new("verb-categories-blood-cult", + new SpriteSpecifier.Rsi(new ResPath("/Textures/WhiteDream/BloodCult/actions.rsi"), "blood_spells")); } } diff --git a/Content.Shared/Weapons/Melee/Events/AttemptMeleeEvent.cs b/Content.Shared/Weapons/Melee/Events/AttemptMeleeEvent.cs index 2800e3b34da..5f96ab2db05 100644 --- a/Content.Shared/Weapons/Melee/Events/AttemptMeleeEvent.cs +++ b/Content.Shared/Weapons/Melee/Events/AttemptMeleeEvent.cs @@ -4,4 +4,4 @@ namespace Content.Shared.Weapons.Melee.Events; /// Raised directed on a weapon when attempt a melee attack. /// [ByRefEvent] -public record struct AttemptMeleeEvent(bool Cancelled, string? Message); +public record struct AttemptMeleeEvent(bool Cancelled, string? Message, EntityUid PlayerUid); // White Dream: Add PlayerUid argument diff --git a/Content.Shared/Weapons/Melee/SharedMeleeWeaponSystem.cs b/Content.Shared/Weapons/Melee/SharedMeleeWeaponSystem.cs index 8ccf7578fe7..20bf6d5c4e1 100644 --- a/Content.Shared/Weapons/Melee/SharedMeleeWeaponSystem.cs +++ b/Content.Shared/Weapons/Melee/SharedMeleeWeaponSystem.cs @@ -389,7 +389,12 @@ private bool AttemptAttack(EntityUid user, EntityUid weaponUid, MeleeWeaponCompo Dirty(weaponUid, weapon); // Do this AFTER attack so it doesn't spam every tick - var ev = new AttemptMeleeEvent(); + // White Dream: Added PlayerUid + var ev = new AttemptMeleeEvent + { + PlayerUid = user + }; + RaiseLocalEvent(weaponUid, ref ev); if (ev.Cancelled) diff --git a/Content.Shared/Weapons/Reflect/ReflectSystem.cs b/Content.Shared/Weapons/Reflect/ReflectSystem.cs index 03ad97edff2..76c98a427f5 100644 --- a/Content.Shared/Weapons/Reflect/ReflectSystem.cs +++ b/Content.Shared/Weapons/Reflect/ReflectSystem.cs @@ -15,6 +15,8 @@ using Content.Shared.Standing; using Content.Shared.Weapons.Ranged.Components; using Content.Shared.Weapons.Ranged.Events; +using Content.Shared.WhiteDream.BloodCult.BloodCultist; +using Content.Shared.WhiteDream.BloodCult.Items; using Robust.Shared.Audio.Systems; using Robust.Shared.Network; using Robust.Shared.Physics.Components; @@ -112,6 +114,10 @@ private bool TryReflectProjectile(EntityUid user, EntityUid reflector, EntityUid ) return false; + // Non cultists can't use cult items to reflect anything. + if (HasComp(reflector) && !HasComp(user)) + return false; + if (!_random.Prob(CalcReflectChance(reflector, reflect))) return false; @@ -202,20 +208,19 @@ private bool TryReflectHitscan( Vector2 direction, [NotNullWhen(true)] out Vector2? newDirection) { + newDirection = null; if (!TryComp(reflector, out var reflect) || !reflect.Enabled || TryComp(reflector, out var staminaComponent) && staminaComponent.Critical || _standing.IsDown(reflector)) - { - newDirection = null; return false; - } + + // Non cultists can't use cult items to reflect anything. + if (HasComp(reflector) && !HasComp(user)) + return false; if (!_random.Prob(CalcReflectChance(reflector, reflect))) - { - newDirection = null; return false; - } if (_netManager.IsServer) { diff --git a/Content.Shared/WhiteDream/BloodCult/BloodCultist/BloodCultLeaderComponent.cs b/Content.Shared/WhiteDream/BloodCult/BloodCultist/BloodCultLeaderComponent.cs new file mode 100644 index 00000000000..6c0a118cac5 --- /dev/null +++ b/Content.Shared/WhiteDream/BloodCult/BloodCultist/BloodCultLeaderComponent.cs @@ -0,0 +1,16 @@ +using Content.Shared.Antag; +using Content.Shared.StatusIcon; +using Robust.Shared.GameStates; +using Robust.Shared.Prototypes; + +namespace Content.Shared.WhiteDream.BloodCult.BloodCultist; + +[RegisterComponent, NetworkedComponent] +public sealed partial class BloodCultLeaderComponent : Component, IAntagStatusIconComponent +{ + [DataField] + public ProtoId StatusIcon { get; set; } = "BloodCultLeader"; + + [DataField] + public bool IconVisibleToGhost { get; set; } = true; +} diff --git a/Content.Shared/WhiteDream/BloodCult/BloodCultist/BloodCultistComponent.cs b/Content.Shared/WhiteDream/BloodCult/BloodCultist/BloodCultistComponent.cs new file mode 100644 index 00000000000..288d496ee5a --- /dev/null +++ b/Content.Shared/WhiteDream/BloodCult/BloodCultist/BloodCultistComponent.cs @@ -0,0 +1,42 @@ +using System.Threading; +using Content.Shared.Antag; +using Content.Shared.FixedPoint; +using Content.Shared.Language; +using Content.Shared.Mind; +using Content.Shared.StatusIcon; +using Robust.Shared.GameStates; +using Robust.Shared.Prototypes; + +namespace Content.Shared.WhiteDream.BloodCult.BloodCultist; + +[RegisterComponent, NetworkedComponent] +public sealed partial class BloodCultistComponent : Component, IAntagStatusIconComponent +{ + [DataField] + public float HolyConvertTime = 15f; + + [DataField] + public int MaximumAllowedEmpowers = 4; + + [DataField] + public ProtoId StatusIcon { get; set; } = "BloodCultMember"; + + [DataField] + public bool IconVisibleToGhost { get; set; } = true; + + [DataField] + public ProtoId CultLanguageId { get; set; } = "Eldritch"; + + [ViewVariables, NonSerialized] + public EntityUid? BloodSpear; + + [ViewVariables, NonSerialized] + public Entity? OriginalMind; + + [ViewVariables(VVAccess.ReadWrite)] + public FixedPoint2 RitesBloodAmount = FixedPoint2.Zero; + + public Color OriginalEyeColor = Color.White; + + public CancellationTokenSource? DeconvertToken { get; set; } +} diff --git a/Content.Shared/WhiteDream/BloodCult/BloodCultist/BloodCultistRoleComponent.cs b/Content.Shared/WhiteDream/BloodCult/BloodCultist/BloodCultistRoleComponent.cs new file mode 100644 index 00000000000..acbb92bba48 --- /dev/null +++ b/Content.Shared/WhiteDream/BloodCult/BloodCultist/BloodCultistRoleComponent.cs @@ -0,0 +1,6 @@ +using Content.Shared.Roles; + +namespace Content.Shared.WhiteDream.BloodCult.BloodCultist; + +[RegisterComponent] +public sealed partial class BloodCultistRoleComponent : AntagonistRoleComponent; diff --git a/Content.Shared/WhiteDream/BloodCult/Components/PentagramComponent.cs b/Content.Shared/WhiteDream/BloodCult/Components/PentagramComponent.cs new file mode 100644 index 00000000000..2d94c1aedb7 --- /dev/null +++ b/Content.Shared/WhiteDream/BloodCult/Components/PentagramComponent.cs @@ -0,0 +1,20 @@ +using Robust.Shared.GameStates; +using Robust.Shared.Utility; + +namespace Content.Shared.WhiteDream.BloodCult.Components; + +[NetworkedComponent, RegisterComponent] +public sealed partial class PentagramComponent : Component +{ + public ResPath RsiPath = new("/Textures/WhiteDream/BloodCult/Effects/pentagram.rsi"); + + public readonly string[] States = + [ + "halo1", + "halo2", + "halo3", + "halo4", + "halo5", + "halo6" + ]; +} diff --git a/Content.Shared/WhiteDream/BloodCult/Components/PylonComponent.cs b/Content.Shared/WhiteDream/BloodCult/Components/PylonComponent.cs new file mode 100644 index 00000000000..2827f357626 --- /dev/null +++ b/Content.Shared/WhiteDream/BloodCult/Components/PylonComponent.cs @@ -0,0 +1,58 @@ +using Content.Shared.Damage; +using Robust.Shared.Audio; +using Robust.Shared.GameStates; +using Robust.Shared.Prototypes; + +namespace Content.Shared.WhiteDream.BloodCult.Components; + +[RegisterComponent, NetworkedComponent] +public sealed partial class PylonComponent : Component +{ + [DataField] + public bool IsActive = true; + + [DataField] + public float HealingDelay = 20; + + [DataField] + public float HealingAuraRange = 5; + + [DataField] + public float CorruptionRadius = 5; + + /// + /// Length of the cooldown in between tile corruptions. + /// + [DataField] + public float CorruptionCooldown = 5; + + /// + /// Length of the cooldown in between healinng. + /// + [DataField] + public float HealingCooldown = 20; + + [DataField] + public string CultTile = "CultFloor"; + + [DataField] + public EntProtoId TileCorruptEffect = "CultTileSpawnEffect"; + + [DataField] + public SoundSpecifier BurnHandSound = new SoundPathSpecifier("/Audio/Effects/lightburn.ogg"); + + [DataField] + public SoundSpecifier CorruptTileSound = new SoundPathSpecifier("/Audio/WhiteDream/BloodCult/curse.ogg"); + + [DataField] + public DamageSpecifier Healing = new(); + + [DataField] + public DamageSpecifier DamageOnInteract = new(); + + [ViewVariables(VVAccess.ReadOnly)] + public float CorruptionAccumulator = 0; + + [ViewVariables(VVAccess.ReadOnly)] + public float HealingAccumulator = 0; +} diff --git a/Content.Shared/WhiteDream/BloodCult/Components/TwistedConstructionTargetComponent.cs b/Content.Shared/WhiteDream/BloodCult/Components/TwistedConstructionTargetComponent.cs new file mode 100644 index 00000000000..79eb7d5567a --- /dev/null +++ b/Content.Shared/WhiteDream/BloodCult/Components/TwistedConstructionTargetComponent.cs @@ -0,0 +1,14 @@ +using Robust.Shared.GameStates; +using Robust.Shared.Prototypes; + +namespace Content.Shared.WhiteDream.BloodCult.Components; + +[RegisterComponent, NetworkedComponent] +public sealed partial class TwistedConstructionTargetComponent : Component +{ + [DataField(required: true)] + public EntProtoId ReplacementProto = ""; + + [DataField] + public TimeSpan DoAfterDelay = TimeSpan.FromSeconds(2); +} diff --git a/Content.Shared/WhiteDream/BloodCult/Constructs/ConstructComponent.cs b/Content.Shared/WhiteDream/BloodCult/Constructs/ConstructComponent.cs new file mode 100644 index 00000000000..24d67ed2c8d --- /dev/null +++ b/Content.Shared/WhiteDream/BloodCult/Constructs/ConstructComponent.cs @@ -0,0 +1,29 @@ +using Content.Shared.Antag; +using Content.Shared.StatusIcon; +using Robust.Shared.Prototypes; + +namespace Content.Shared.WhiteDream.BloodCult.Constructs; + +[RegisterComponent] +public sealed partial class ConstructComponent : Component, IAntagStatusIconComponent +{ + [DataField] + public List Actions = new(); + + /// + /// Used by the client to determine how long the transform animation should be played. + /// + [DataField] + public float TransformDelay = 1; + + [DataField] + public ProtoId StatusIcon { get; set; } = "BloodCultMember"; + + [DataField] + public bool IconVisibleToGhost { get; set; } = true; + + public bool Transforming = false; + public float TransformAccumulator = 0; + + public List ActionEntities = new(); +} diff --git a/Content.Shared/WhiteDream/BloodCult/Items/CultItemComponent.cs b/Content.Shared/WhiteDream/BloodCult/Items/CultItemComponent.cs new file mode 100644 index 00000000000..b0a41ed31b8 --- /dev/null +++ b/Content.Shared/WhiteDream/BloodCult/Items/CultItemComponent.cs @@ -0,0 +1,16 @@ +using Robust.Shared.GameStates; + +namespace Content.Shared.WhiteDream.BloodCult.Items; + +[RegisterComponent, NetworkedComponent] +public sealed partial class CultItemComponent : Component +{ + /// + /// Allow non-cultists to use this item? + /// + [DataField] + public bool AllowUseToEveryone; + + [DataField] + public TimeSpan KnockdownDuration = TimeSpan.FromSeconds(2); +} diff --git a/Content.Shared/WhiteDream/BloodCult/Items/CultItemSystem.cs b/Content.Shared/WhiteDream/BloodCult/Items/CultItemSystem.cs new file mode 100644 index 00000000000..1dbbb769aad --- /dev/null +++ b/Content.Shared/WhiteDream/BloodCult/Items/CultItemSystem.cs @@ -0,0 +1,82 @@ +using Content.Shared.Blocking; +using Content.Shared.Ghost; +using Content.Shared.Hands.EntitySystems; +using Content.Shared.Interaction; +using Content.Shared.Inventory.Events; +using Content.Shared.Popups; +using Content.Shared.Stunnable; +using Content.Shared.Throwing; +using Content.Shared.Weapons.Melee.Events; +using Content.Shared.WhiteDream.BloodCult.BloodCultist; + +namespace Content.Shared.WhiteDream.BloodCult.Items; + +public sealed class CultItemSystem : EntitySystem +{ + [Dependency] private readonly SharedHandsSystem _hands = default!; + [Dependency] private readonly SharedPopupSystem _popup = default!; + [Dependency] private readonly SharedStunSystem _stun = default!; + + public override void Initialize() + { + SubscribeLocalEvent(OnActivate); + SubscribeLocalEvent(OnBeforeThrow); + SubscribeLocalEvent(OnEquipAttempt); + SubscribeLocalEvent(OnMeleeAttempt); + SubscribeLocalEvent(OnBeforeBlocking); + } + + private void OnActivate(Entity item, ref ActivateInWorldEvent args) + { + if (CanUse(args.User)) + return; + + args.Handled = true; + KnockdownAndDropItem(item, args.User, Loc.GetString("cult-item-component-generic")); + } + + private void OnBeforeThrow(Entity item, ref BeforeThrowEvent args) + { + if (CanUse(args.PlayerUid)) + return; + + args.Cancelled = true; + KnockdownAndDropItem(item, args.PlayerUid, Loc.GetString("cult-item-component-throw-fail")); + } + + private void OnEquipAttempt(Entity item, ref BeingEquippedAttemptEvent args) + { + if (CanUse(args.EquipTarget)) + return; + + args.Cancel(); + KnockdownAndDropItem(item, args.Equipee, Loc.GetString("cult-item-component-equip-fail")); + } + + private void OnMeleeAttempt(Entity item, ref AttemptMeleeEvent args) + { + if (CanUse(args.PlayerUid)) + return; + + args.Cancelled = true; + KnockdownAndDropItem(item, args.PlayerUid, Loc.GetString("cult-item-component-attack-fail")); + } + + private void OnBeforeBlocking(Entity item, ref BeforeBlockingEvent args) + { + if (CanUse(args.User)) + return; + + args.Cancel(); + KnockdownAndDropItem(item, args.User, Loc.GetString("cult-item-component-block-fail")); + } + + private void KnockdownAndDropItem(Entity item, EntityUid user, string message) + { + _popup.PopupPredicted(message, item, user); + _stun.TryKnockdown(user, item.Comp.KnockdownDuration, true); + _hands.TryDrop(user); + } + + private bool CanUse(EntityUid? uid) => HasComp(uid) || HasComp(uid); +} diff --git a/Content.Shared/WhiteDream/BloodCult/Items/VoidTorch/VoidTorchComponent.cs b/Content.Shared/WhiteDream/BloodCult/Items/VoidTorch/VoidTorchComponent.cs new file mode 100644 index 00000000000..572e41c5883 --- /dev/null +++ b/Content.Shared/WhiteDream/BloodCult/Items/VoidTorch/VoidTorchComponent.cs @@ -0,0 +1,21 @@ +using Robust.Shared.Audio; + +namespace Content.Shared.WhiteDream.BloodCult.Items.VoidTorch; + +[RegisterComponent] +public sealed partial class VoidTorchComponent : Component +{ + [DataField] + public int Charges = 5; + + [DataField] + public SoundPathSpecifier TeleportSound = new("/Audio/WhiteDream/BloodCult/veilin.ogg"); + + [DataField] + public string TurnOnLightBehaviour = "turn_on"; + + [DataField] + public string TurnOffLightBehaviour = "fade_out"; + + public EntityUid? TargetItem; +} diff --git a/Content.Shared/WhiteDream/BloodCult/RunedDoor/RunedDoorComponent.cs b/Content.Shared/WhiteDream/BloodCult/RunedDoor/RunedDoorComponent.cs new file mode 100644 index 00000000000..608e427e4cf --- /dev/null +++ b/Content.Shared/WhiteDream/BloodCult/RunedDoor/RunedDoorComponent.cs @@ -0,0 +1,4 @@ +namespace Content.Shared.WhiteDream.BloodCult.RunedDoor; + +[RegisterComponent] +public sealed partial class RunedDoorComponent : Component; diff --git a/Content.Shared/WhiteDream/BloodCult/RunedDoor/RunedDoorSystem.cs b/Content.Shared/WhiteDream/BloodCult/RunedDoor/RunedDoorSystem.cs new file mode 100644 index 00000000000..7d9dc2281cc --- /dev/null +++ b/Content.Shared/WhiteDream/BloodCult/RunedDoor/RunedDoorSystem.cs @@ -0,0 +1,54 @@ +using Content.Shared.Doors; +using Content.Shared.Prying.Components; +using Content.Shared.Repulsor; +using Content.Shared.WhiteDream.BloodCult.BloodCultist; +using Content.Shared.WhiteDream.BloodCult.Constructs; + +namespace Content.Shared.WhiteDream.BloodCult.RunedDoor; + +public sealed class RunedDoorSystem : EntitySystem +{ + public override void Initialize() + { + base.Initialize(); + + SubscribeLocalEvent(OnBeforeDoorOpened); + SubscribeLocalEvent(OnBeforeDoorClosed); + SubscribeLocalEvent(OnBeforePry); + SubscribeLocalEvent(BefoRepulse); + } + + private void OnBeforeDoorOpened(Entity door, ref BeforeDoorOpenedEvent args) + { + if (args.User is not { } user) + return; + + if (!CanInteract(user)) + args.Cancel(); + } + + private void OnBeforeDoorClosed(Entity door, ref BeforeDoorClosedEvent args) + { + if (args.User is not { } user) + return; + + if (!CanInteract(user)) + args.Cancel(); + } + + private void OnBeforePry(Entity door, ref BeforePryEvent args) + { + args.Cancelled = true; + } + + private void BefoRepulse(Entity door, ref BeforeRepulseEvent args) + { + if (HasComp(args.Target) || HasComp(args.Target)) + args.Cancel(); + } + + private bool CanInteract(EntityUid user) + { + return HasComp(user) || HasComp(user); + } +} diff --git a/Content.Shared/WhiteDream/BloodCult/Runes/ApocalypseRune.cs b/Content.Shared/WhiteDream/BloodCult/Runes/ApocalypseRune.cs new file mode 100644 index 00000000000..5488226c4d0 --- /dev/null +++ b/Content.Shared/WhiteDream/BloodCult/Runes/ApocalypseRune.cs @@ -0,0 +1,14 @@ +using Content.Shared.DoAfter; +using Robust.Shared.Serialization; + +namespace Content.Shared.WhiteDream.BloodCult.Runes; + +[Serializable, NetSerializable] +public sealed partial class ApocalypseRuneDoAfter : SimpleDoAfterEvent; + +[Serializable, NetSerializable] +public enum ApocalypseRuneVisuals +{ + Used, + Layer +} diff --git a/Content.Shared/WhiteDream/BloodCult/Runes/RendingRune.cs b/Content.Shared/WhiteDream/BloodCult/Runes/RendingRune.cs new file mode 100644 index 00000000000..431acdaec47 --- /dev/null +++ b/Content.Shared/WhiteDream/BloodCult/Runes/RendingRune.cs @@ -0,0 +1,14 @@ +using Content.Shared.DoAfter; +using Robust.Shared.Serialization; + +namespace Content.Shared.WhiteDream.BloodCult.Runes; + +[Serializable, NetSerializable] +public sealed partial class RendingRuneDoAfter : SimpleDoAfterEvent; + +[Serializable, NetSerializable] +public enum RendingRuneVisuals +{ + Active, + Layer +} diff --git a/Content.Shared/WhiteDream/BloodCult/Runes/RuneDrawerComponent.cs b/Content.Shared/WhiteDream/BloodCult/Runes/RuneDrawerComponent.cs new file mode 100644 index 00000000000..b6dec321e4e --- /dev/null +++ b/Content.Shared/WhiteDream/BloodCult/Runes/RuneDrawerComponent.cs @@ -0,0 +1,41 @@ +using Content.Shared.DoAfter; +using Robust.Shared.Audio; +using Robust.Shared.GameStates; +using Robust.Shared.Prototypes; +using Robust.Shared.Serialization; + +namespace Content.Shared.WhiteDream.BloodCult.Runes; + +[RegisterComponent, NetworkedComponent] +public sealed partial class RuneDrawerComponent : Component +{ + [DataField] + public float EraseTime = 4f; + + [DataField] + public SoundSpecifier StartDrawingSound = new SoundPathSpecifier("/Audio/WhiteDream/BloodCult/butcher.ogg"); + + public SoundSpecifier EndDrawingSound = new SoundPathSpecifier("/Audio/WhiteDream/BloodCult/blood.ogg"); +} + +[Serializable, NetSerializable] +public enum RuneDrawerBuiKey +{ + Key +} + +[Serializable, NetSerializable] +public sealed class RuneDrawerSelectedMessage(RuneSelectorPrototype selectedRune) : BoundUserInterfaceMessage +{ + public ProtoId SelectedRune { get; private set; } = selectedRune.ID; +} + +[Serializable, NetSerializable] +public sealed partial class RuneEraseDoAfterEvent : SimpleDoAfterEvent; + +[Serializable, NetSerializable] +public sealed partial class DrawRuneDoAfter : SimpleDoAfterEvent +{ + public ProtoId Rune; + public SoundSpecifier EndDrawingSound; +} diff --git a/Content.Shared/WhiteDream/BloodCult/Runes/RuneSelectorPrototype.cs b/Content.Shared/WhiteDream/BloodCult/Runes/RuneSelectorPrototype.cs new file mode 100644 index 00000000000..372ab866f07 --- /dev/null +++ b/Content.Shared/WhiteDream/BloodCult/Runes/RuneSelectorPrototype.cs @@ -0,0 +1,30 @@ +using Content.Shared.Damage; +using Content.Shared.FixedPoint; +using Robust.Shared.Prototypes; + +namespace Content.Shared.WhiteDream.BloodCult.Runes; + +[Prototype("runeSelector")] +public sealed class RuneSelectorPrototype : IPrototype +{ + [IdDataField] + public string ID { get; } = default!; + + [DataField(required: true)] + public EntProtoId Prototype { get; } + + [DataField] + public float DrawTime { get; } = 4f; + + /// + /// Damage dealt on the rune drawing. + /// + [DataField] + public DamageSpecifier DrawDamage = new() + { + DamageDict = new Dictionary + { + ["Slash"] = 15, + } + }; +} diff --git a/Content.Shared/WhiteDream/BloodCult/Spells/Events.cs b/Content.Shared/WhiteDream/BloodCult/Spells/Events.cs new file mode 100644 index 00000000000..293a32691dc --- /dev/null +++ b/Content.Shared/WhiteDream/BloodCult/Spells/Events.cs @@ -0,0 +1,112 @@ +using Content.Shared.Actions; +using Content.Shared.Chat; +using Content.Shared.DoAfter; +using Content.Shared.Magic; +using Robust.Shared.Audio; +using Robust.Shared.Prototypes; +using Robust.Shared.Serialization; + +namespace Content.Shared.WhiteDream.BloodCult.Spells; + +public sealed partial class BloodCultStunEvent : EntityTargetActionEvent, ISpeakSpell +{ + [DataField] + public TimeSpan ParalyzeDuration = TimeSpan.FromSeconds(16); + + [DataField] + public TimeSpan MuteDuration = TimeSpan.FromSeconds(12); + + [DataField] + public string? Speech { get; set; } + + public InGameICChatType ChatType => InGameICChatType.Whisper; +} + +public sealed partial class BloodCultTeleportEvent : EntityTargetActionEvent, ISpeakSpell +{ + [DataField] + public float Range = 5; + + [DataField] + public string? Speech { get; set; } + + public InGameICChatType ChatType => InGameICChatType.Whisper; +} + +public sealed partial class BloodCultEmpEvent : InstantActionEvent, ISpeakSpell +{ + [DataField] + public float Range = 4; + + [DataField] + public float EnergyConsumption = 1000; + + [DataField] + public float Duration = 20; + + [DataField] + public string? Speech { get; set; } + + public InGameICChatType ChatType => InGameICChatType.Whisper; +} + +public sealed partial class BloodCultShacklesEvent : EntityTargetActionEvent, ISpeakSpell +{ + [DataField] + public EntProtoId ShacklesProto = "ShadowShackles"; + + [DataField] + public TimeSpan MuteDuration = TimeSpan.FromSeconds(5); + + [DataField] + public TimeSpan KnockdownDuration = TimeSpan.FromSeconds(1); + + [DataField] + public string? Speech { get; set; } + + public InGameICChatType ChatType => InGameICChatType.Whisper; +} + +public sealed partial class BloodCultTwistedConstructionEvent : EntityTargetActionEvent, ISpeakSpell +{ + [DataField] + public string? Speech { get; set; } + + public InGameICChatType ChatType => InGameICChatType.Whisper; +} + +public sealed partial class SummonEquipmentEvent : InstantActionEvent, ISpeakSpell +{ + /// + /// Slot - EntProtoId + /// + [DataField] + public Dictionary Prototypes = new(); + + [DataField] + public string? Speech { get; set; } + + public InGameICChatType ChatType => InGameICChatType.Whisper; +} + +public sealed partial class BloodSpearRecalledEvent : InstantActionEvent; + +[Serializable, NetSerializable] +public sealed partial class TwistedConstructionDoAfterEvent : SimpleDoAfterEvent; + +[Serializable, NetSerializable] +public sealed partial class CreateSpeellDoAfterEvent : SimpleDoAfterEvent +{ + public EntProtoId ActionProtoId; +} + +[Serializable, NetSerializable] +public sealed partial class TeleportActionDoAfterEvent : SimpleDoAfterEvent +{ + public NetEntity Rune; + public SoundPathSpecifier TeleportInSound = new("/Audio/WhiteDream/BloodCult/veilin.ogg"); + public SoundPathSpecifier TeleportOutSound = new("/Audio/WhiteDream/BloodCult/veilout.ogg"); +} + +[Serializable, NetSerializable] +public sealed partial class BloodRitesExtractDoAfterEvent : SimpleDoAfterEvent; diff --git a/Content.Shared/WhiteDream/BloodCult/UI/BloodRites.cs b/Content.Shared/WhiteDream/BloodCult/UI/BloodRites.cs new file mode 100644 index 00000000000..961f2015a4d --- /dev/null +++ b/Content.Shared/WhiteDream/BloodCult/UI/BloodRites.cs @@ -0,0 +1,25 @@ +using Content.Shared.FixedPoint; +using Robust.Shared.Prototypes; +using Robust.Shared.Serialization; + +namespace Content.Shared.WhiteDream.BloodCult.UI; + +[NetSerializable, Serializable] +public enum BloodRitesUiKey : byte +{ + Key, +} + +[Serializable, NetSerializable] +public sealed class BloodRitesUiState(Dictionary crafts, FixedPoint2 storedBlood) + : BoundUserInterfaceState +{ + public Dictionary Crafts = crafts; + public FixedPoint2 StoredBlood = storedBlood; +} + +[Serializable, NetSerializable] +public sealed class BloodRitesMessage(EntProtoId selectedProto) : BoundUserInterfaceMessage +{ + public EntProtoId SelectedProto = selectedProto; +} diff --git a/Content.Shared/WhiteDream/BloodCult/UI/NameSelector.cs b/Content.Shared/WhiteDream/BloodCult/UI/NameSelector.cs new file mode 100644 index 00000000000..7b2b6c53896 --- /dev/null +++ b/Content.Shared/WhiteDream/BloodCult/UI/NameSelector.cs @@ -0,0 +1,16 @@ +using Robust.Shared.Serialization; + +namespace Content.Shared.WhiteDream.BloodCult.UI; + +[Serializable, NetSerializable] +public enum NameSelectorUiKey +{ + Key +} + +[Serializable, NetSerializable] +public sealed class NameSelectedMessage(string name) + : BoundUserInterfaceMessage +{ + public string Name { get; } = name; +} diff --git a/Content.Shared/WhiteDream/BloodCult/Visuals.cs b/Content.Shared/WhiteDream/BloodCult/Visuals.cs new file mode 100644 index 00000000000..2d01da48c97 --- /dev/null +++ b/Content.Shared/WhiteDream/BloodCult/Visuals.cs @@ -0,0 +1,40 @@ +using Robust.Shared.Serialization; + +namespace Content.Shared.WhiteDream.BloodCult; + +[Serializable, NetSerializable] +public enum SoulShardVisualState : byte +{ + HasMind, + Blessed, + Sprite, + Glow +} + +[Serializable, NetSerializable] +public enum ConstructVisualsState : byte +{ + Transforming, + Sprite, + Glow +} + +[Serializable, NetSerializable] +public enum GenericCultVisuals : byte +{ + State, // True or False + Layer +} + +[Serializable, NetSerializable] +public enum PylonVisuals : byte +{ + Activated, + Layer +} + +[Serializable, NetSerializable] +public enum PentagramKey +{ + Key +} diff --git a/Resources/Audio/WhiteDream/BloodCult/blood.ogg b/Resources/Audio/WhiteDream/BloodCult/blood.ogg new file mode 100644 index 00000000000..f6024a5ff6a Binary files /dev/null and b/Resources/Audio/WhiteDream/BloodCult/blood.ogg differ diff --git a/Resources/Audio/WhiteDream/BloodCult/blood_cult_greeting.ogg b/Resources/Audio/WhiteDream/BloodCult/blood_cult_greeting.ogg new file mode 100644 index 00000000000..9fa22df51d3 Binary files /dev/null and b/Resources/Audio/WhiteDream/BloodCult/blood_cult_greeting.ogg differ diff --git a/Resources/Audio/WhiteDream/BloodCult/butcher.ogg b/Resources/Audio/WhiteDream/BloodCult/butcher.ogg new file mode 100644 index 00000000000..2e4a0d2ddc7 Binary files /dev/null and b/Resources/Audio/WhiteDream/BloodCult/butcher.ogg differ diff --git a/Resources/Audio/WhiteDream/BloodCult/curse.ogg b/Resources/Audio/WhiteDream/BloodCult/curse.ogg new file mode 100644 index 00000000000..c495f0e4398 Binary files /dev/null and b/Resources/Audio/WhiteDream/BloodCult/curse.ogg differ diff --git a/Resources/Audio/WhiteDream/BloodCult/enter_blood.ogg b/Resources/Audio/WhiteDream/BloodCult/enter_blood.ogg new file mode 100644 index 00000000000..fb1666e48ca Binary files /dev/null and b/Resources/Audio/WhiteDream/BloodCult/enter_blood.ogg differ diff --git a/Resources/Audio/WhiteDream/BloodCult/magic.ogg b/Resources/Audio/WhiteDream/BloodCult/magic.ogg new file mode 100644 index 00000000000..c107743d805 Binary files /dev/null and b/Resources/Audio/WhiteDream/BloodCult/magic.ogg differ diff --git a/Resources/Audio/WhiteDream/BloodCult/narsie_summoned.ogg b/Resources/Audio/WhiteDream/BloodCult/narsie_summoned.ogg new file mode 100644 index 00000000000..fb35af598ed Binary files /dev/null and b/Resources/Audio/WhiteDream/BloodCult/narsie_summoned.ogg differ diff --git a/Resources/Audio/WhiteDream/BloodCult/rending_draw_finished.ogg b/Resources/Audio/WhiteDream/BloodCult/rending_draw_finished.ogg new file mode 100644 index 00000000000..bfca2abf2ca Binary files /dev/null and b/Resources/Audio/WhiteDream/BloodCult/rending_draw_finished.ogg differ diff --git a/Resources/Audio/WhiteDream/BloodCult/rending_ritual.ogg b/Resources/Audio/WhiteDream/BloodCult/rending_ritual.ogg new file mode 100644 index 00000000000..44572f8ed15 Binary files /dev/null and b/Resources/Audio/WhiteDream/BloodCult/rending_ritual.ogg differ diff --git a/Resources/Audio/WhiteDream/BloodCult/rites.ogg b/Resources/Audio/WhiteDream/BloodCult/rites.ogg new file mode 100644 index 00000000000..486940a4147 Binary files /dev/null and b/Resources/Audio/WhiteDream/BloodCult/rites.ogg differ diff --git a/Resources/Audio/WhiteDream/BloodCult/smoke.ogg b/Resources/Audio/WhiteDream/BloodCult/smoke.ogg new file mode 100644 index 00000000000..0109e1947c6 Binary files /dev/null and b/Resources/Audio/WhiteDream/BloodCult/smoke.ogg differ diff --git a/Resources/Audio/WhiteDream/BloodCult/startdraw.ogg b/Resources/Audio/WhiteDream/BloodCult/startdraw.ogg new file mode 100644 index 00000000000..e9054093579 Binary files /dev/null and b/Resources/Audio/WhiteDream/BloodCult/startdraw.ogg differ diff --git a/Resources/Audio/WhiteDream/BloodCult/veilin.ogg b/Resources/Audio/WhiteDream/BloodCult/veilin.ogg new file mode 100644 index 00000000000..d14cf85c5d6 Binary files /dev/null and b/Resources/Audio/WhiteDream/BloodCult/veilin.ogg differ diff --git a/Resources/Audio/WhiteDream/BloodCult/veilout.ogg b/Resources/Audio/WhiteDream/BloodCult/veilout.ogg new file mode 100644 index 00000000000..bf69f3c4082 Binary files /dev/null and b/Resources/Audio/WhiteDream/BloodCult/veilout.ogg differ diff --git a/Resources/Audio/WhiteDream/BloodCult/wand_teleport.ogg b/Resources/Audio/WhiteDream/BloodCult/wand_teleport.ogg new file mode 100644 index 00000000000..e16d231dddb Binary files /dev/null and b/Resources/Audio/WhiteDream/BloodCult/wand_teleport.ogg differ diff --git a/Resources/Audio/WhiteDream/BloodCult/wraith_phase.ogg b/Resources/Audio/WhiteDream/BloodCult/wraith_phase.ogg new file mode 100644 index 00000000000..a5672ed7cc9 Binary files /dev/null and b/Resources/Audio/WhiteDream/BloodCult/wraith_phase.ogg differ diff --git a/Resources/Locale/en-US/chat/managers/chat-manager.ftl b/Resources/Locale/en-US/chat/managers/chat-manager.ftl index f2b70e72a83..8a61f8d5da5 100644 --- a/Resources/Locale/en-US/chat/managers/chat-manager.ftl +++ b/Resources/Locale/en-US/chat/managers/chat-manager.ftl @@ -52,6 +52,9 @@ chat-manager-rate-limit-admin-announcement = Player { $player } breached chat ra chat-manager-send-empathy-chat-wrap-message = {$source}: {$message} +chat-manager-send-cult-chat-wrap-message = [bold]\[{ $channelName }\] [BubbleHeader]{ $player }[/BubbleHeader]:[/bold] [BubbleContent]{ $message }[/BubbleContent] +chat-manager-cult-channel-name = Blood Cult + ## Speech verbs for chat chat-speech-verb-suffix-exclamation = ! diff --git a/Resources/Locale/en-US/guidebook/chemistry/effects.ftl b/Resources/Locale/en-US/guidebook/chemistry/effects.ftl index 9da6c0c0aec..705473c2eca 100644 --- a/Resources/Locale/en-US/guidebook/chemistry/effects.ftl +++ b/Resources/Locale/en-US/guidebook/chemistry/effects.ftl @@ -374,4 +374,6 @@ reagent-effect-guidebook-add-moodlet = { $timeout -> [0] indefinitely *[other] for {$timeout} seconds - } \ No newline at end of file + } + +reagent-effect-guidebook-purify-evil = Purifies evil powers diff --git a/Resources/Locale/en-US/language/languages.ftl b/Resources/Locale/en-US/language/languages.ftl index 935eef896a0..8cb420a0204 100644 --- a/Resources/Locale/en-US/language/languages.ftl +++ b/Resources/Locale/en-US/language/languages.ftl @@ -114,3 +114,8 @@ language-Kobold-description = Hiss! language-Hissing-name = Hissing language-Hissing-description = Hiss! + +language-Eldritch-name = Eldritch +language-Eldritch-description = + A language that is considered to be long forgotten - now the only speakers of this profaned tongue of screeches and + mumbles are the followers of an ancient God of Blood. diff --git a/Resources/Locale/en-US/verbs/verb-system.ftl b/Resources/Locale/en-US/verbs/verb-system.ftl index dfb4e621dca..c99f9987cbf 100644 --- a/Resources/Locale/en-US/verbs/verb-system.ftl +++ b/Resources/Locale/en-US/verbs/verb-system.ftl @@ -29,6 +29,7 @@ verb-categories-select-type = Select Type verb-categories-fax = Set Destination verb-categories-power-level = Power Level verb-categories-interaction = Interact +verb-categories-blood-cult = Blood Cult verb-common-toggle-light = Toggle light verb-common-close = Close diff --git a/Resources/Locale/en-US/white-dream/administration/antag.ftl b/Resources/Locale/en-US/white-dream/administration/antag.ftl new file mode 100644 index 00000000000..8e8a8b2ac07 --- /dev/null +++ b/Resources/Locale/en-US/white-dream/administration/antag.ftl @@ -0,0 +1,2 @@ +admin-verb-text-make-blood-cultist = Make Blood Cultist +admin-verb-make-blood-cultist = Make the target into a blood cultist. diff --git a/Resources/Locale/en-US/white-dream/alerts.ftl b/Resources/Locale/en-US/white-dream/alerts.ftl new file mode 100644 index 00000000000..5156b31ddb0 --- /dev/null +++ b/Resources/Locale/en-US/white-dream/alerts.ftl @@ -0,0 +1,5 @@ +alerts-blood-spells-name = Blood spells +alerts-blood-spells-desc = Click to create or remove blood spells. + +alerts-blood-cult-buff-name = Empowered +alerts-blood-cult-buff-desc = Blood magic requires much less time to cast and you lose less blood from it. You're also immune to pressure damage. diff --git a/Resources/Locale/en-US/white-dream/cult/gamerule.ftl b/Resources/Locale/en-US/white-dream/cult/gamerule.ftl new file mode 100644 index 00000000000..0f7872985f0 --- /dev/null +++ b/Resources/Locale/en-US/white-dream/cult/gamerule.ftl @@ -0,0 +1,18 @@ +blood-cult-title = The Blood Cult +blood-cult-description = The oldest and strongest emotion of mankind is fear, and the oldest and strongest kind of fear is fear of the unknown. + +roles-antag-blood-cultist-name = Blood cultist +roles-antag-blood-cultist-objective = Summon the Old God Nar'Sie. + +blood-cult-role-greeting = The Geometer of Blood, Nar-Sie, has sent a number of her followers to Space Station. + As a cultist, you have an abundance of cult magics at your disposal, something for all situations. + You must work with your brethren to summon an avatar of your eldritch goddess! + +blood-cult-role-briefing-short = Use '^' to contact other members of your brethren. + +blood-cult-condition-win = The Geometer of Blood has successfully summoned their Eldritch Goddess! +blood-cult-condition-draw = Both parties were destroyed. +blood-cult-condition-failure = The crew have managed to stop the rending of reality! + +blood-cultists-list-start = Members of Geometer of Blood were: +blood-cultists-list-name = [color=White]{ $name }[/color] ([color=gray]{ $user }[/color]) diff --git a/Resources/Locale/en-US/white-dream/cult/items/blood-rites.ftl b/Resources/Locale/en-US/white-dream/cult/items/blood-rites.ftl new file mode 100644 index 00000000000..a33c8db8474 --- /dev/null +++ b/Resources/Locale/en-US/white-dream/cult/items/blood-rites.ftl @@ -0,0 +1,5 @@ +blood-rites-stored-blood = It has [color=darkred]{$amount}u. of blood[/color] stored. +blood-rites-not-enough-blood = You don't have enough blood stored. +blood-rites-heal-dead = Only a revive rune can bring back the dead. +blood-rites-no-blood-left = You use the last of drop of blood stored in your blood rites. +blood-rites-heal-no-bloodstream = You can't heal this bloodless creature. diff --git a/Resources/Locale/en-US/white-dream/cult/items/general.ftl b/Resources/Locale/en-US/white-dream/cult/items/general.ftl new file mode 100644 index 00000000000..6ad4938adea --- /dev/null +++ b/Resources/Locale/en-US/white-dream/cult/items/general.ftl @@ -0,0 +1,22 @@ +cult-item-component-generic = The item refuses to obey your will. You can't use it. +cult-item-component-attack-fail = The weapon refuses to obey your will. You can't attack with it. +cult-item-component-equip-fail = The armor refuses to obey your will. You can't equip it. +cult-item-component-throw-fail = The weapon refuses to obey your will. You can't throw it. +cult-item-component-block-fail = The shield betrays you! + +soul-shard-try-insert-no-soul = The shard has no soul. +soul-shard-selector-form = Select form. + +ghost-role-information-soul-shard-name = Soul Shard +ghost-role-information-soul-shard-description = Become the servant of The Blood Cult. +ghost-role-information-soul-shard-rules = Take the form of one of the constructs and help your Masters bring their Old Goddess back to the world! + +shuttle-curse-cant-activate = Nar'Sien power doesn't seem to work. +shuttle-curse-max-charges = You try to shatter the orb, but it remains as solid as a rock! +shuttle-curse-shuttle-arrived = The shuttle has already arived! You can't delay it anymore. +shuttle-curse-shuttle-not-called = The shuttle has not yet been called. + +shuttle-curse-system-failure = SYSTEM FAILURE +shuttle-curse-success-global = The shuttle will be delayed by {$time} minutes. + +veil-shifter-cant-teleport = Couldn't find a place to teleport you. Try again! diff --git a/Resources/Locale/en-US/white-dream/cult/materials.ftl b/Resources/Locale/en-US/white-dream/cult/materials.ftl new file mode 100644 index 00000000000..6b796265a22 --- /dev/null +++ b/Resources/Locale/en-US/white-dream/cult/materials.ftl @@ -0,0 +1 @@ +materials-runed-metal = runed metal diff --git a/Resources/Locale/en-US/white-dream/cult/runes.ftl b/Resources/Locale/en-US/white-dream/cult/runes.ftl new file mode 100644 index 00000000000..f13e72a3726 --- /dev/null +++ b/Resources/Locale/en-US/white-dream/cult/runes.ftl @@ -0,0 +1,18 @@ +cult-rune-cant-draw = You can not draw rune here! +cult-rune-started-erasing = Started erasing... +cult-rune-erased = Rune has been erased. +cult-rune-not-enough-cultists = Not enough cultists to perform the ritual! +cult-rune-no-targets = No targets have been found! + +cult-teleport-not-found = No runes found. + +cult-revive-rune-no-charges = Can not perform the revive ritual: no charges left. +cult-revive-rune-already-alive = The target is already alive. + +cult-buff-already-buffed = You are already empowered. + +cult-rending-drawing-finished = The Geometer Of Blood has finished drawing the rune of end! Nearby location: {$location}. +cult-rending-target-alive = Can not start the ritual: the target is alive. +cult-rending-already-summoning = Can not start the ritual: it's already in progress. +cult-rending-started = The Geometer Of Blood has started the ritual of Dimensional Rending! +cult-rending-prevented = Someone has stopped the ritual. diff --git a/Resources/Locale/en-US/white-dream/cult/shuttle-curse.ftl b/Resources/Locale/en-US/white-dream/cult/shuttle-curse.ftl new file mode 100644 index 00000000000..5a31be9c914 --- /dev/null +++ b/Resources/Locale/en-US/white-dream/cult/shuttle-curse.ftl @@ -0,0 +1,11 @@ +shuttle-curse-message-1 = A fuel technician just slit his own throat and begged for death. +shuttle-curse-message-2 = A scan of the shuttle's fuel tank has revealed tainting by a mixture of humanoid innards and teeth. +shuttle-curse-message-3 = A security incident involving a frenzied shuttle worker attacking coworkers with a laser cutter has just been reported as resolved by on-site security. +shuttle-curse-message-4 = A shuttle engineer began screaming 'DEATH IS NOT THE END' and ripped out wires until an arc flash seared off her flesh. +shuttle-curse-message-5 = A shuttle engineer was spotted inside the cockpit, feverishly arranging her innards on the floor in the shape of a rune before expiring. +shuttle-curse-message-6 = A shuttle inspector started laughing madly over the radio and then threw herself into an engine turbine. +shuttle-curse-message-7 = The corpse of an unidentified shuttle worker was found mutilated beyond recognition in the shuttle's main hold, with at least five unique sources of blood on the scene. +shuttle-curse-message-8 = The shuttle dispatcher was found dead with bloody symbols carved into their flesh. +shuttle-curse-message-9 = The shuttle's custodian was found washing the windows with their own blood. +shuttle-curse-message-10 = The shuttle's navigation programming was replaced by a file containing just two words: IT COMES. +shuttle-curse-message-11 = The shuttle's transponder is emitting the encoded message 'FEAR THE OLD BLOOD' in lieu of its assigned identification signal. diff --git a/Resources/Locale/en-US/white-dream/cult/spells.ftl b/Resources/Locale/en-US/white-dream/cult/spells.ftl new file mode 100644 index 00000000000..f0934d74cb9 --- /dev/null +++ b/Resources/Locale/en-US/white-dream/cult/spells.ftl @@ -0,0 +1,5 @@ +blood-cult-spells-too-many = Too many spells already selected. +blood-cult-no-spells = You have no spells selected. + +blood-cult-select-spells-verb = Select blood spells +blood-cult-remove-spells-verb = Remove blood spells diff --git a/Resources/Locale/en-US/white-dream/cult/structures/pylon.ftl b/Resources/Locale/en-US/white-dream/cult/structures/pylon.ftl new file mode 100644 index 00000000000..de81f3a1b8a --- /dev/null +++ b/Resources/Locale/en-US/white-dream/cult/structures/pylon.ftl @@ -0,0 +1 @@ +pylon-placement-another-pylon-nearby = Can't place pylon here as another pylon is nearby. diff --git a/Resources/Locale/en-US/white-dream/cult/structures/timed-factory.ftl b/Resources/Locale/en-US/white-dream/cult/structures/timed-factory.ftl new file mode 100644 index 00000000000..e8728c0285c --- /dev/null +++ b/Resources/Locale/en-US/white-dream/cult/structures/timed-factory.ftl @@ -0,0 +1 @@ +timed-factory-cooldown = The factory is recharging. Time left: {$cooldown} diff --git a/Resources/Locale/en-US/white-dream/name-selector.ftl b/Resources/Locale/en-US/white-dream/name-selector.ftl new file mode 100644 index 00000000000..c7f71045626 --- /dev/null +++ b/Resources/Locale/en-US/white-dream/name-selector.ftl @@ -0,0 +1,2 @@ +name-selector-title = Select a name +name-selector-accept-button = Accept diff --git a/Resources/Prototypes/Entities/Mobs/Cyborgs/base_borg_chassis.yml b/Resources/Prototypes/Entities/Mobs/Cyborgs/base_borg_chassis.yml index 7974b06870e..5e3652d93d3 100644 --- a/Resources/Prototypes/Entities/Mobs/Cyborgs/base_borg_chassis.yml +++ b/Resources/Prototypes/Entities/Mobs/Cyborgs/base_borg_chassis.yml @@ -228,6 +228,8 @@ - TauCetiBasic - RobotTalk - type: PsionicInsulation + - type: TwistedConstructionTarget + replacementProto: ConstructShell - type: entity abstract: true diff --git a/Resources/Prototypes/Entities/Mobs/Player/ipc.yml b/Resources/Prototypes/Entities/Mobs/Player/ipc.yml index d80eb4699d1..c3ae577120d 100644 --- a/Resources/Prototypes/Entities/Mobs/Player/ipc.yml +++ b/Resources/Prototypes/Entities/Mobs/Player/ipc.yml @@ -106,6 +106,8 @@ - type: OfferItem - type: LayingDown - type: Carriable + - type: StatusIcon + bounds: -0.5,-0.5,0.5,0.5 - type: StepTriggerImmune whitelist: types: diff --git a/Resources/Prototypes/Entities/Mobs/Player/narsie.yml b/Resources/Prototypes/Entities/Mobs/Player/narsie.yml index 7030572cf47..9fa58d35f8e 100644 --- a/Resources/Prototypes/Entities/Mobs/Player/narsie.yml +++ b/Resources/Prototypes/Entities/Mobs/Player/narsie.yml @@ -87,13 +87,6 @@ bodyType: Dynamic bodyStatus: InAir - type: CanMoveInAir - # singulose components - - type: EventHorizon - radius: 5 - canBreachContainment: true - - type: GravityWell - baseRadialAcceleration: 6 - maxRange: 8 - type: WarpPoint follow: true location: Nar'Sie diff --git a/Resources/Prototypes/Entities/Mobs/Player/silicon_base.yml b/Resources/Prototypes/Entities/Mobs/Player/silicon_base.yml index 0163f6727e7..6ca0435b35e 100644 --- a/Resources/Prototypes/Entities/Mobs/Player/silicon_base.yml +++ b/Resources/Prototypes/Entities/Mobs/Player/silicon_base.yml @@ -207,6 +207,10 @@ type: HumanoidMarkingModifierBoundUserInterface enum.StrippingUiKey.Key: type: StrippableBoundUserInterface + enum.RadialSelectorUiKey.Key: + type: RadialSelectorMenuBUI + enum.ListViewSelectorUiKey.Key: + type: ListViewSelectorBUI enum.SurgeryUIKey.Key: type: SurgeryBui - type: Emoting diff --git a/Resources/Prototypes/Entities/Mobs/Species/base.yml b/Resources/Prototypes/Entities/Mobs/Species/base.yml index 24ee2a964a5..5c6a0fcaa16 100644 --- a/Resources/Prototypes/Entities/Mobs/Species/base.yml +++ b/Resources/Prototypes/Entities/Mobs/Species/base.yml @@ -197,6 +197,10 @@ enum.InstrumentUiKey.Key: type: InstrumentBoundUserInterface requireInputValidation: false + enum.RadialSelectorUiKey.Key: + type: RadialSelectorMenuBUI + enum.ListViewSelectorUiKey.Key: + type: ListViewSelectorBUI enum.SurgeryUIKey.Key: type: SurgeryBui - type: Puller diff --git a/Resources/Prototypes/Entities/Objects/Materials/Sheets/metal.yml b/Resources/Prototypes/Entities/Objects/Materials/Sheets/metal.yml index 95ecd3fe324..072c46626f3 100644 --- a/Resources/Prototypes/Entities/Objects/Materials/Sheets/metal.yml +++ b/Resources/Prototypes/Entities/Objects/Materials/Sheets/metal.yml @@ -295,6 +295,8 @@ - type: ShortConstruction entries: - prototype: SecureWindoor + - type: TwistedConstructionTarget + replacementProto: RunedMetal1 - type: entity parent: SheetPlasteel diff --git a/Resources/Prototypes/Entities/Objects/Shields/shields.yml b/Resources/Prototypes/Entities/Objects/Shields/shields.yml index e7ebb1b98d4..f0d9a7c0b1b 100644 --- a/Resources/Prototypes/Entities/Objects/Shields/shields.yml +++ b/Resources/Prototypes/Entities/Objects/Shields/shields.yml @@ -309,55 +309,6 @@ Piercing: 1 #Have it break into brass when clock cult is in -- type: entity - name: mirror shield - parent: BaseShield - id: MirrorShield - description: Glows an eerie red. You hear the Geometer whispering... - components: - - type: Sprite - state: mirror-icon - - type: Item - heldPrefix: mirror - - type: Reflect - reflectProb: 0.95 - innate: true - reflects: - - Energy - - type: Blocking #Mirror shield reflects heat/laser, but is relatively weak to everything else. - passiveBlockModifier: - coefficients: - Blunt: 1.2 - Slash: 1.2 - Piercing: 1.2 - Heat: .7 - activeBlockModifier: - coefficients: - Blunt: 1.2 - Slash: 1.2 - Piercing: 1.2 - Heat: .6 - flatReductions: - Heat: 1 - blockSound: !type:SoundPathSpecifier - path: /Audio/Effects/glass_step.ogg - - type: Destructible - thresholds: - - trigger: - !type:DamageTrigger - damage: 40 - behaviors: - - !type:DoActsBehavior - acts: [ "Destruction" ] - - !type:PlaySoundBehavior - sound: - collection: GlassBreak - - !type:SpawnEntitiesBehavior - spawn: - SheetGlass: - min: 5 - max: 5 - - type: entity name: energy shield parent: BaseItem diff --git a/Resources/Prototypes/Entities/Objects/Specific/Robotics/endoskeleton.yml b/Resources/Prototypes/Entities/Objects/Specific/Robotics/endoskeleton.yml index 98f953f6824..e1a2ff18476 100644 --- a/Resources/Prototypes/Entities/Objects/Specific/Robotics/endoskeleton.yml +++ b/Resources/Prototypes/Entities/Objects/Specific/Robotics/endoskeleton.yml @@ -223,3 +223,6 @@ - type: GuideHelp guides: - Cyborgs + - type: TwistedConstructionTarget + replacementProto: ConstructShell + doAfterDelay: 5 diff --git a/Resources/Prototypes/Entities/Objects/Weapons/Melee/cult.yml b/Resources/Prototypes/Entities/Objects/Weapons/Melee/cult.yml index a681ef52ebf..3fee36a58af 100644 --- a/Resources/Prototypes/Entities/Objects/Weapons/Melee/cult.yml +++ b/Resources/Prototypes/Entities/Objects/Weapons/Melee/cult.yml @@ -1,60 +1,3 @@ -- type: entity - name: ritual dagger - parent: BaseItem - id: RitualDagger - description: A strange dagger used by sinister groups for rituals and sacrifices. - components: - - type: Sharp - - type: Sprite - sprite: Objects/Weapons/Melee/cult_dagger.rsi - state: icon - - type: MeleeWeapon - wideAnimationRotation: -135 - attackRate: 1.25 - range: 1.5 - damage: - types: - Slash: 8 - heavyRateModifier: 0.9 - heavyDamageBaseModifier: 1.2 - heavyStaminaCost: 5 - - type: Item - size: Normal - - type: Clothing - sprite: Objects/Weapons/Melee/cult_dagger.rsi - slots: - - back - - type: DisarmMalus - -- type: entity - name: eldritch blade - parent: BaseItem - id: EldritchBlade - description: A sword humming with unholy energy. - components: - - type: Sharp - - type: Sprite - sprite: Objects/Weapons/Melee/cult_blade.rsi - state: icon - - type: MeleeWeapon - wideAnimationRotation: -135 - attackRate: 0.75 - range: 1.65 - damage: - types: - Slash: 12 - heavyDamageBaseModifier: 1.2 - heavyStaminaCost: 7.5 - maxTargets: 6 - angle: 90 - - type: Item - size: Normal - - type: Clothing - sprite: Objects/Weapons/Melee/cult_blade.rsi - slots: - - back - - type: DisarmMalus - - type: entity name: unholy halberd parent: BaseItem diff --git a/Resources/Prototypes/Entities/Structures/Doors/Airlocks/base_structureairlocks.yml b/Resources/Prototypes/Entities/Structures/Doors/Airlocks/base_structureairlocks.yml index 586902bd776..97a6d8e076e 100644 --- a/Resources/Prototypes/Entities/Structures/Doors/Airlocks/base_structureairlocks.yml +++ b/Resources/Prototypes/Entities/Structures/Doors/Airlocks/base_structureairlocks.yml @@ -153,6 +153,9 @@ - type: InteractionVerbs allowedVerbs: - KnockOn + - type: TwistedConstructionTarget + replacementProto: CultDoor + doAfterDelay: 10 placement: mode: SnapgridCenter diff --git a/Resources/Prototypes/Entities/Structures/Storage/Tanks/base_structuretanks.yml b/Resources/Prototypes/Entities/Structures/Storage/Tanks/base_structuretanks.yml index 7b56e6d36b5..243f51ca6fa 100644 --- a/Resources/Prototypes/Entities/Structures/Storage/Tanks/base_structuretanks.yml +++ b/Resources/Prototypes/Entities/Structures/Storage/Tanks/base_structuretanks.yml @@ -82,4 +82,4 @@ mask: - MachineMask layer: - - WallLayer \ No newline at end of file + - WallLayer diff --git a/Resources/Prototypes/Entities/Structures/Walls/walls.yml b/Resources/Prototypes/Entities/Structures/Walls/walls.yml index 675de3dab55..b3ea345681a 100644 --- a/Resources/Prototypes/Entities/Structures/Walls/walls.yml +++ b/Resources/Prototypes/Entities/Structures/Walls/walls.yml @@ -191,6 +191,7 @@ parent: BaseWall id: WallCult name: cult wall + description: Keeps the cult in and the crew out. components: - type: Tag tags: @@ -215,6 +216,9 @@ - type: IconSmooth key: walls base: cult + - type: Construction + graph: CultGirder + node: wall - type: entity parent: BaseWall diff --git a/Resources/Prototypes/Language/FactionSpecific/blood_cult.yml b/Resources/Prototypes/Language/FactionSpecific/blood_cult.yml new file mode 100644 index 00000000000..6d336743520 --- /dev/null +++ b/Resources/Prototypes/Language/FactionSpecific/blood_cult.yml @@ -0,0 +1,159 @@ +# Used by followers of Nar'Sie +- type: language + id: Eldritch + speech: + color: "#dc143c" + obfuscation: + !type:SyllableObfuscation + minSyllables: 2 # Replacements are really short + maxSyllables: 7 + replacement: + - "'ra" + - so + - at + - il + - "'ta" + - gh + - sh + - ya + - "'te" + - sh + - ol + - ma + - om + - ig + - ni + - in + - sha + - mir + - sas + - mah + - zar + - tok + - lyr + - nqa + - nap + - olt + - val + - qha + - fwe + - ath + - yro + - eth + - gal + - gib + - bar + - jin + - kla + - atu + - kal + - lig + - yoka + - drak + - loso + - arta + - weyh + - ines + - toth + - fara + - amar + - nyag + - eske + - reth + - dedo + - btoh + - nikt + - neth + - kanas + - garis + - uloft + - tarat + - khari + - thnor + - rekka + - ragga + - rfikk + - harfr + - andid + - ethra + - dedol + - totum + - ntrath + - keriam + - sha + - mir + - sas + - mah + - hra + - zar + - "'tok" + - lyr + - nqa + - nap + - olt + - val + - yam + - qha + - fel + - det + - fwe + - mah + - erl + - ath + - yro + - eth + - gal + - mud + - gib + - bar + - tea + - fuu + - jin + - kla + - atu + - kal + - lig + - yoka + - drak + - loso + - arta + - weyh + - ines + - toth + - fara + - amar + - nyag + - eske + - reth + - dedo + - btoh + - nikt + - neth + - abis + - kanas + - garis + - uloft + - tarat + - khari + - thnor + - rekka + - ragga + - rfikk + - harfr + - andid + - ethra + - dedol + - totum + - verbot + - pleggh + - ntrath + - barhah + - pasnar + - keriam + - usinar + - savrae + - amutan + - tannin + - remium + - barada + - forbici diff --git a/Resources/Prototypes/Nyanotrasen/Reagents/Consumable/Drink/drinks.yml b/Resources/Prototypes/Nyanotrasen/Reagents/Consumable/Drink/drinks.yml index 24d83be9431..8ba31c15d1b 100644 --- a/Resources/Prototypes/Nyanotrasen/Reagents/Consumable/Drink/drinks.yml +++ b/Resources/Prototypes/Nyanotrasen/Reagents/Consumable/Drink/drinks.yml @@ -67,42 +67,3 @@ - !type:AdjustReagent reagent: Nutriment amount: 0.1 - -- type: reagent - id: HolyWater - name: reagent-name-holywater - parent: BaseDrink - desc: reagent-name-holywater - physicalDesc: reagent-physical-desc-translucent - flavor: holy - color: "#75b1f0" - boilingPoint: 100.0 - meltingPoint: 0.0 - reactiveEffects: - Acidic: - methods: [ Touch ] - effects: - - !type:HealthChange - scaleByQuantity: true - ignoreResistances: false - damage: - types: - Holy: 0.5 - metabolisms: #Could nullify debuffs of feeding. - Drink: - effects: - - !type:SatiateThirst - factor: 3 - Medicine: - effects: - - !type:ModifyBloodLevel - amount: 0.1 - - !type:HealthChange - damage: - groups: - Burn: -0.5 - types: - Holy: 1 - plantMetabolism: #Heals plants a little with the holy power within it. - - !type:PlantAdjustHealth - amount: 0.1 diff --git a/Resources/Prototypes/Nyanotrasen/Recipes/Reactions/drink.yml b/Resources/Prototypes/Nyanotrasen/Recipes/Reactions/drink.yml index 5645b17a67e..66192e9ace8 100644 --- a/Resources/Prototypes/Nyanotrasen/Recipes/Reactions/drink.yml +++ b/Resources/Prototypes/Nyanotrasen/Recipes/Reactions/drink.yml @@ -143,15 +143,3 @@ amount: 1 products: GrapeSoda: 3 - -- type: reaction - id: HolyWater - reactants: - Water: - amount: 1 - Wine: - amount: 1 - Mercury: - amount: 1 - products: - HolyWater: 3 diff --git a/Resources/Prototypes/Nyanotrasen/Recipes/Reactions/single_reagent.yml b/Resources/Prototypes/Nyanotrasen/Recipes/Reactions/single_reagent.yml index 189ce2bb8d3..6cb548a51ac 100644 --- a/Resources/Prototypes/Nyanotrasen/Recipes/Reactions/single_reagent.yml +++ b/Resources/Prototypes/Nyanotrasen/Recipes/Reactions/single_reagent.yml @@ -1,5 +1,6 @@ - type: reaction id: BlessHolyWater + sound: /Audio/Effects/holy.ogg impact: Low requiredMixerCategories: - Holy diff --git a/Resources/Prototypes/Reagents/medicine.yml b/Resources/Prototypes/Reagents/medicine.yml index f5edc342166..0c3731aea8e 100644 --- a/Resources/Prototypes/Reagents/medicine.yml +++ b/Resources/Prototypes/Reagents/medicine.yml @@ -1260,3 +1260,47 @@ - "psicodine-effect-anxieties-wash-away" - "psicodine-effect-at-peace" probability: 0.2 + +- type: reagent + id: HolyWater + name: reagent-name-holywater + group: Medicine + desc: reagent-desc-holywater + physicalDesc: reagent-physical-desc-translucent + flavor: holy + color: "#75b1f0" + boilingPoint: 100.0 + meltingPoint: 0.0 + reactiveEffects: + Acidic: + methods: [ Touch ] + effects: + - !type:HealthChange + scaleByQuantity: true + ignoreResistances: false + damage: + types: + Holy: 0.5 + metabolisms: #Could nullify debuffs of feeding. + Drink: + effects: + - !type:SatiateThirst + factor: 3 + Medicine: + effects: + - !type:ModifyBloodLevel + amount: 0.1 + - !type:HealthChange + damage: + groups: + Brute: -0.5 + Burn: -0.5 + types: + Holy: 1 + - !type:PurifyEvil + conditions: + - !type:ReagentThreshold + min: 15 + plantMetabolism: #Heals plants a little with the holy power within it. + - !type:PlantAdjustHealth + amount: 0.1 diff --git a/Resources/Prototypes/WhiteDream/Entities/Actions/cult_constructs.yml b/Resources/Prototypes/WhiteDream/Entities/Actions/cult_constructs.yml new file mode 100644 index 00000000000..e69de29bb2d diff --git a/Resources/Prototypes/WhiteDream/Entities/Actions/cult_items.yml b/Resources/Prototypes/WhiteDream/Entities/Actions/cult_items.yml new file mode 100644 index 00000000000..a134bf0486c --- /dev/null +++ b/Resources/Prototypes/WhiteDream/Entities/Actions/cult_items.yml @@ -0,0 +1,16 @@ +- type: entity + id: ActionBloodSpearRecall + name: Recall spear + description: Recalls your blood spear back to your hand. + categories: [ HideSpawnMenu ] + components: + - type: Sprite + sprite: WhiteDream/BloodCult/actions.rsi + state: stun + - type: InstantAction + itemIconStyle: BigAction + useDelay: 30 + icon: + sprite: WhiteDream/BloodCult/Entities/Items/Weapons/cult_spear.rsi + state: icon + event: !type:BloodSpearRecalledEvent diff --git a/Resources/Prototypes/WhiteDream/Entities/Actions/cult_leader.yml b/Resources/Prototypes/WhiteDream/Entities/Actions/cult_leader.yml new file mode 100644 index 00000000000..e69de29bb2d diff --git a/Resources/Prototypes/WhiteDream/Entities/Actions/cultists.yml b/Resources/Prototypes/WhiteDream/Entities/Actions/cultists.yml new file mode 100644 index 00000000000..814b950ead1 --- /dev/null +++ b/Resources/Prototypes/WhiteDream/Entities/Actions/cultists.yml @@ -0,0 +1,195 @@ +- type: entity + id: ActionBloodCultStun + name: Stun + description: Empowers your hand to stun and mute a victim on contact. + categories: [ HideSpawnMenu ] + components: + - type: Sprite + sprite: WhiteDream/BloodCult/actions.rsi + state: stun + - type: EntityTargetAction + checkCanAccess: false + range: 3 + itemIconStyle: BigAction + charges: 1 + temporary: true + canTargetSelf: false + interactOnMiss: false + whitelist: + components: + - Body + icon: + sprite: WhiteDream/BloodCult/actions.rsi + state: stun + event: !type:BloodCultStunEvent + speech: "Fuu ma'jin!" + - type: BaseCultSpell + +- type: entity + id: ActionBloodCultTeleport + name: Teleport + description: Empowers your hand to teleport yourself or another cultist to a teleport rune on contact. + categories: [ HideSpawnMenu ] + components: + - type: Sprite + sprite: WhiteDream/BloodCult/actions.rsi + state: teleport + - type: EntityTargetAction + checkCanAccess: false + range: 3 + itemIconStyle: BigAction + charges: 1 + temporary: true + interactOnMiss: false + whitelist: + components: + - BloodCultist + icon: + sprite: WhiteDream/BloodCult/actions.rsi + state: teleport + event: !type:BloodCultTeleportEvent + speech: "Sas'so c'arta forbici" + - type: BaseCultSpell + +- type: entity + id: ActionBloodCultEmp + name: Electromagnetic Pulse + description: Emits a large electromagnetic pulse. + categories: [ HideSpawnMenu ] + components: + - type: Sprite + sprite: WhiteDream/BloodCult/actions.rsi + state: create_emp + - type: InstantAction + itemIconStyle: BigAction + charges: 1 + temporary: true + icon: + sprite: WhiteDream/BloodCult/actions.rsi + state: teleport + event: !type:BloodCultEmpEvent + speech: "Ta'gh fara'qha fel d'amar det!" + - type: BaseCultSpell + +- type: entity + id: ActionBloodCultShadowShackles + name: shadow shackles + description: Empowers your hand to handcuff a victim on contact, and mute them if successful. + categories: [ HideSpawnMenu ] + components: + - type: Sprite + sprite: WhiteDream/BloodCult/actions.rsi + state: shackles + - type: EntityTargetAction + checkCanAccess: false + range: 3 + itemIconStyle: BigAction + charges: 4 + temporary: true + canTargetSelf: false + interactOnMiss: false + whitelist: + components: + - Body + icon: + sprite: WhiteDream/BloodCult/actions.rsi + state: shackles + event: !type:BloodCultShacklesEvent + speech: "In'totum Lig'abis!" + - type: BaseCultSpell + +- type: entity + id: ActionBloodCultTwistedConstruction + name: twisted construction + description: Empowers your hand to corrupt certain metallic objects. + categories: [ HideSpawnMenu ] + components: + - type: Sprite + sprite: WhiteDream/BloodCult/actions.rsi + state: transmute + - type: EntityTargetAction + checkCanAccess: false + range: 3 + itemIconStyle: BigAction + charges: 1 + temporary: true + canTargetSelf: false + interactOnMiss: false + whitelist: + components: + - TwistedConstructionTarget + icon: + sprite: WhiteDream/BloodCult/actions.rsi + state: transmute + event: !type:BloodCultTwistedConstructionEvent + speech: "Ethra p'ni dedol!" + - type: BaseCultSpell + +- type: entity + id: ActionBloodCultSummonCombatEquipment + name: summon combat equipment + description: Allows you to summon combat cult gear, including cult armor, a cult bola and a cult sword. + categories: [ HideSpawnMenu ] + components: + - type: Sprite + sprite: WhiteDream/BloodCult/actions.rsi + state: summon_combat_equipment + - type: InstantAction + itemIconStyle: BigAction + charges: 1 + temporary: true + icon: + sprite: WhiteDream/BloodCult/actions.rsi + state: summon_combat_equipment + event: !type:SummonEquipmentEvent + speech: "Wur d'dai leev'mai k'sagan!" + prototypes: + outerClothing: ClothingOuterCultArmor + hand1: EldritchLongsword + hand2: CultBola + - type: BaseCultSpell + +- type: entity + id: ActionBloodCultSummonRitualDagger + name: summon ritual dagger + description: Allows you to summon a ritual dagger, in case you've lost the dagger that was given to you. + categories: [ HideSpawnMenu ] + components: + - type: Sprite + sprite: WhiteDream/BloodCult/actions.rsi + state: summon_dagger + - type: InstantAction + itemIconStyle: BigAction + charges: 1 + temporary: true + icon: + sprite: WhiteDream/BloodCult/actions.rsi + state: summon_dagger + event: !type:SummonEquipmentEvent + speech: "Wur d'dai leev'mai k'sagan!" + prototypes: + hand: RitualDagger + - type: BaseCultSpell + +- type: entity + id: ActionBloodCultBloodRites + name: blood rites + description: Empowers your hand to absorb blood to be used for advanced rites, or heal a cultist on contact. Use the spell in-hand to cast advanced rites + categories: [ HideSpawnMenu ] + components: + - type: Sprite + sprite: WhiteDream/BloodCult/actions.rsi + state: blood_rites + - type: InstantAction + itemIconStyle: BigAction + charges: 1 + useDelay: 3 + temporary: true + icon: + sprite: WhiteDream/BloodCult/actions.rsi + state: blood_rites + event: !type:SummonEquipmentEvent + speech: "Fel'th Dol Ab'orod!" + prototypes: + hand1: BloodRitesAura + - type: BaseCultSpell diff --git a/Resources/Prototypes/WhiteDream/Entities/Clothing/Cult/armor.yml b/Resources/Prototypes/WhiteDream/Entities/Clothing/Cult/armor.yml new file mode 100644 index 00000000000..ea8e22b80d9 --- /dev/null +++ b/Resources/Prototypes/WhiteDream/Entities/Clothing/Cult/armor.yml @@ -0,0 +1,123 @@ +- type: entity + parent: ClothingOuterHardsuitBase + id: ClothingOuterCultArmor + name: true nar'sien hardened armor + description: heavily-armored exosuit worn by warriors of the Nar'Sien cult. It can withstand hard vacuum. + components: + - type: Sprite + sprite: WhiteDream/BloodCult/Entities/Clothes/Outer/cult_armor.rsi + - type: Clothing + sprite: WhiteDream/BloodCult/Entities/Clothes/Outer/cult_armor.rsi + - type: PressureProtection + highPressureMultiplier: 0.02 + lowPressureMultiplier: 1000 + - type: ClothingSpeedModifier + walkModifier: 1.0 + sprintModifier: 1.0 + - type: Armor + modifiers: + coefficients: + Blunt: 0.5 + Slash: 0.6 + Piercing: 0.6 + Heat: 0.7 + Caustic: 0.2 + Radiation: 0.2 + - type: ExplosionResistance + damageCoefficient: 0.5 + - type: TemperatureProtection + coefficient: 0.001 + - type: ToggleableClothing + clothingPrototype: ClothingHeadHelmetCultArmor + - type: CultItem + +- type: entity + parent: ClothingHeadHardsuitBase + id: ClothingHeadHelmetCultArmor + categories: [ HideSpawnMenu ] + name: true nar'sien hardened helmet + description: A heavily-armored helmet worn by warriors of the Nar'Sien cult. It can withstand hard vacuum. + components: + - type: BreathMask + - type: Sprite + sprite: WhiteDream/BloodCult/Entities/Clothes/Helmet/cult_helmet.rsi + - type: Clothing + sprite: WhiteDream/BloodCult/Entities/Clothes/Helmet/cult_helmet.rsi + equipSound: /Audio/Effects/rustle1.ogg + unequipSound: /Audio/Effects/rustle2.ogg + - type: PressureProtection + highPressureMultiplier: 0.08 + lowPressureMultiplier: 1000 + +- type: entity + parent: ClothingOuterRobesCult + id: ClothingOuterRobesCultTrue + name: flagellant's robe + description: Blood-soaked robes infused with dark magic; allows the user to move at inhuman speeds, but at the cost of increased damage. + components: + - type: Sprite + sprite: WhiteDream/BloodCult/Entities/Clothes/Outer/cult_robe.rsi + - type: Clothing + sprite: WhiteDream/BloodCult/Entities/Clothes/Outer/cult_robe.rsi + - type: CultItem + - type: ClothingSpeedModifier + walkModifier: 1.25 + sprintModifier: 1.25 + - type: Armor + modifiers: + coefficients: + Blunt: 1.4 + Slash: 1.4 + Piercing: 1.4 + Heat: 1.4 + - type: ToggleableClothing + clothingPrototype: ClothingHeadHatHoodCultHoodTrue + - type: ContainerContainer + containers: + toggleable-clothing: !type:ContainerSlot { } + +- type: entity + parent: ClothingHeadHatHoodCulthood + id: ClothingHeadHatHoodCultHoodTrue + name: flagellant's hood + description: A blood-soaked hood infused with dark magic. + categories: [ HideSpawnMenu ] + components: + - type: Sprite + sprite: WhiteDream/BloodCult/Entities/Clothes/Helmet/cult_hood.rsi + - type: Clothing + sprite: WhiteDream/BloodCult/Entities/Clothes/Helmet/cult_hood.rsi + equipSound: /Audio/Effects/rustle1.ogg + unequipSound: /Audio/Effects/rustle2.ogg + - type: ClothingSpeedModifier + walkModifier: 1.25 + sprintModifier: 1.25 + - type: Armor + modifiers: + coefficients: + Blunt: 1.4 + Slash: 1.4 + Piercing: 1.4 + Heat: 1.4 + +- type: entity + parent: ClothingEyesBase + id: ClothingEyeCultBlindfold + name: zealot's blindfold + description: May Nar'Sie guide you through the darkness and shield you from the light + components: + - type: Sprite + sprite: Clothing/Eyes/Misc/blindfold.rsi + state: icon + - type: Clothing + sprite: Clothing/Eyes/Misc/blindfold.rsi + - type: FlashImmunity + - type: ShowHealthBars + damageContainers: + - Biological + - Inorganic + - type: ShowHealthIcons + damageContainers: + - Biological + # TODO: ADD NIGHT VISION + diff --git a/Resources/Prototypes/WhiteDream/Entities/Effects/cult.yml b/Resources/Prototypes/WhiteDream/Entities/Effects/cult.yml new file mode 100644 index 00000000000..cba21c51c38 --- /dev/null +++ b/Resources/Prototypes/WhiteDream/Entities/Effects/cult.yml @@ -0,0 +1,58 @@ +- type: entity + id: CultTileSpawnEffect + name: cult tile effect + placement: + mode: SnapgridCenter + components: + - type: TimedDespawn + lifetime: 0.5 + - type: Transform + noRot: true + anchored: true + - type: Sprite + layers: + - sprite: WhiteDream/BloodCult/Effects/tiles_spawn.rsi + state: floorglow + shader: unshaded + netsync: false + drawdepth: FloorObjects + - type: PointLight + color: "#FF0000" + +- type: entity + id: CultTeleportInEffect + name: Teleport in + components: + - type: TimedDespawn + lifetime: 0.8 + - type: Transform + noRot: true + anchored: true + - type: Sprite + layers: + - sprite: WhiteDream/BloodCult/Effects/cult_in_out.rsi + state: cult_in + shader: unshaded + netsync: false + drawdepth: Effects + - type: PointLight + color: "#FF0000" + +- type: entity + id: CultTeleportOutEffect + name: Teleport out + components: + - type: TimedDespawn + lifetime: 0.8 + - type: Transform + noRot: true + anchored: true + - type: Sprite + layers: + - sprite: WhiteDream/BloodCult/Effects/cult_in_out.rsi + state: cult_out + shader: unshaded + netsync: false + drawdepth: Effects + - type: PointLight + color: "#FF0000" diff --git a/Resources/Prototypes/WhiteDream/Entities/Objects/Cult/constructs.yml b/Resources/Prototypes/WhiteDream/Entities/Objects/Cult/constructs.yml new file mode 100644 index 00000000000..93806feb0a9 --- /dev/null +++ b/Resources/Prototypes/WhiteDream/Entities/Objects/Cult/constructs.yml @@ -0,0 +1,343 @@ +- type: entity + id: ConstructShell + name: construct shell + description: empty construct shell + parent: BaseItem + components: + - type: Sprite + sprite: WhiteDream/BloodCult/Entities/Items/construct_shell.rsi + state: icon + - type: ItemSlots + - type: ConstructShell + shardSlot: + ejectOnBreak: true + whitelist: + components: + - SoulShard + - type: UserInterface + interfaces: + enum.RadialSelectorUiKey.Key: + type: RadialSelectorMenuBUI + - type: ContainerContainer + containers: + Shard: !type:ContainerSlot + - type: CultItem + - type: Examiner + skipChecks: true + +- type: entity + id: ConstructBase + abstract: true + categories: [ HideSpawnMenu ] + components: + - type: LagCompensation + - type: Input + context: "human" + - type: MobMover + - type: InputMover + - type: MovementSpeedModifier + baseWalkSpeed: 3 + baseSprintSpeed: 3 + - type: DamageOnHighSpeedImpact + damage: + types: + Blunt: 5 + soundHit: + path: /Audio/Effects/hit_kick.ogg + - type: Sprite + drawdepth: Mobs + sprite: WhiteDream/BloodCult/mobs.rsi + noRot: true + - type: Clickable + - type: InteractionOutline + - type: Physics + bodyType: KinematicController + - type: Construct + - type: Fixtures + fixtures: + fix1: + shape: !type:PhysShapeCircle + radius: 0.35 + density: 300 + mask: + - FlyingMobMask + layer: + - FlyingMobLayer + - type: Damageable + damageContainer: CorporealSpirit + damageModifierSet: CorporealSpirit + - type: MobThresholds + thresholds: + 0: Alive + 60: Dead + - type: MobState + allowedStates: + - Alive + - Dead + - type: CombatMode + canDisarm: false + - type: Internals + - type: Examiner + - type: Speech + - type: TypingIndicator + proto: guardian + - type: Pullable + - type: Puller + needsHands: false + - type: ActionsContainer + - type: ContainerContainer + containers: + actions: !type:Container + - type: Visibility + - type: ContentEye + - type: Actions + - type: Hands + - type: IsDeadIC + - type: ShowHealthBars + damageContainers: + - Biological + - Inorganic + - type: ShowHealthIcons + damageContainers: + - Biological + - type: Tag + tags: + - CannotSuicide + - DoorBumpOpener + - type: Appearance + +- type: entity + id: ConstructJuggernaut + parent: ConstructBase + name: juggernaut + description: A massive, armored construct built to spearhead attacks and soak up enemy fire. + components: + - type: Sprite + layers: + - state: juggernaut + map: [ "enum.ConstructVisualsState.Sprite" ] + - state: glow_juggernaut_cult + map: [ "enum.ConstructVisualsState.Glow" ] + - type: MobThresholds + thresholds: + 0: Alive + 100: Dead + - type: Construct + - type: MeleeWeapon + hidden: true + angle: 30 + animation: WeaponArcSmash + attackRate: 1 + damage: + types: + Structural: 90 + Blunt: 25 + soundHit: + path: /Audio/Nyanotrasen/Weapons/club.ogg + - type: GenericVisualizer + visuals: + enum.ConstructVisualsState.Transforming: + enum.ConstructVisualsState.Sprite: + True: { state: make_juggernaut_cult } + False: { state: juggernaut } + enum.ConstructVisualsState.Glow: + True: { visible: false } + False: { visible: true } + +- type: entity + parent: ConstructBase + id: ConstructArtificer + name: artificer + description: A bulbous construct dedicated to building and maintaining the Cult of Nar'Sie's armies. + components: + - type: Sprite + layers: + - state: artificer + map: [ "enum.ConstructVisualsState.Sprite" ] + - state: glow_artificer_cult + map: [ "enum.ConstructVisualsState.Glow" ] + - type: Construct + - type: MovementIgnoreGravity + - type: MeleeWeapon + hidden: true + angle: 30 + animation: WeaponArcPunch + attackRate: 1 + damage: + types: + Blunt: 5 + Structural: 60 + - type: GenericVisualizer + visuals: + enum.ConstructVisualsState.Transforming: + enum.ConstructVisualsState.Sprite: + True: { state: make_artificer_cult } + False: { state: artificer } + enum.ConstructVisualsState.Glow: + True: { visible: false } + False: { visible: true } + +- type: entity + parent: ConstructBase + id: ConstructWraith + name: wraith + description: A wicked, clawed shell constructed to assassinate enemies and sow chaos behind enemy lines. + components: + - type: Sprite + layers: + - state: wraith + map: [ "enum.ConstructVisualsState.Sprite" ] + - state: glow_wraith_cult + map: [ "enum.ConstructVisualsState.Glow" ] + - type: MobThresholds + thresholds: + 0: Alive + 65: Dead + - type: Construct + - type: MovementSpeedModifier + baseWalkSpeed: 4 + baseSprintSpeed: 4 + - type: MeleeWeapon + hidden: true + angle: 30 + animation: WeaponArcSmash + attackRate: 1 + damage: + types: + Structural: 40 + Blunt: 20 + - type: GenericVisualizer + visuals: + enum.ConstructVisualsState.Transforming: + enum.ConstructVisualsState.Sprite: + True: { state: make_wraith_cult } + False: { state: wraith } + enum.ConstructVisualsState.Glow: + True: { visible: false } + False: { visible: true } + +- type: entity + id: ConstructHarvester + parent: ConstructBase + name: harvester + description: A long, thin construct built to herald Nar'Sie's rise. It'll be all over soon. + components: + - type: Sprite + layers: + - state: harvester + map: [ "enum.ConstructVisualsState.Sprite" ] + - state: glow_harvester_cult + map: [ "enum.ConstructVisualsState.Glow" ] + - type: MobThresholds + thresholds: + 0: Alive + 65: Dead + - type: Construct + - type: MovementSpeedModifier + baseWalkSpeed: 5 + baseSprintSpeed: 5 + - type: MeleeWeapon + hidden: true + angle: 30 + animation: WeaponArcSmash + attackRate: 1 + damage: + types: + Structural: 40 + Blunt: 50 + +- type: entity + id: ShadeCult + parent: ConstructBase # It's not technically a construct but it code wise? it is. + name: shade + description: A bound spirit. + components: + - type: Sprite + state: shade_cult + - type: MobThresholds + thresholds: + 0: Alive + 40: Dead + - type: MeleeWeapon + hidden: true + angle: 30 + animation: WeaponArcSmash + attackRate: 1 + damage: + types: + Blunt: 10 + - type: MovementSpeedModifier + baseWalkSpeed: 5.5 + baseSprintSpeed: 5.5 + +- type: entity + id: ConstructJuggernautHoly + parent: ConstructJuggernaut + name: purified juggernaut + components: + - type: Sprite + layers: + - state: juggernaut + map: [ "enum.ConstructVisualsState.Sprite" ] + - state: glow_juggernaut_holy + map: [ "enum.ConstructVisualsState.Glow" ] + - type: GenericVisualizer + visuals: + enum.ConstructVisualsState.Transforming: + enum.ConstructVisualsState.Sprite: + True: { state: make_juggernaut_holy } + False: { state: juggernaut } + enum.ConstructVisualsState.Glow: + True: { visible: false } + False: { visible: true } + +- type: entity + id: ConstructArtificerHoly + parent: ConstructArtificer + name: purified artificer + description: A bulbous construct dedicated to building and maintaining the holy armies. + components: + - type: Sprite + layers: + - state: artificer + map: [ "enum.ConstructVisualsState.Sprite" ] + - state: glow_artificer_holy + map: [ "enum.ConstructVisualsState.Glow" ] + - type: GenericVisualizer + visuals: + enum.ConstructVisualsState.Transforming: + enum.ConstructVisualsState.Sprite: + True: { state: make_artificer_holy } + False: { state: artificer } + enum.ConstructVisualsState.Glow: + True: { visible: false } + False: { visible: true } + +- type: entity + id: ConstructWraithHoly + parent: ConstructWraith + name: purified wraith + components: + - type: Sprite + layers: + - state: wraith + map: [ "enum.ConstructVisualsState.Sprite" ] + - state: glow_wraith_holy + map: [ "enum.ConstructVisualsState.Glow" ] + - type: GenericVisualizer + visuals: + enum.ConstructVisualsState.Transforming: + enum.ConstructVisualsState.Sprite: + True: { state: make_wraith_holy } + False: { state: wraith } + enum.ConstructVisualsState.Glow: + True: { visible: false } + False: { visible: true } + +- type: entity + id: ShadeHoly + parent: ShadeCult + name: purified shade + components: + - type: Sprite + state: shade_holy diff --git a/Resources/Prototypes/WhiteDream/Entities/Objects/Cult/souls_shards.yml b/Resources/Prototypes/WhiteDream/Entities/Objects/Cult/souls_shards.yml new file mode 100644 index 00000000000..f872f634034 --- /dev/null +++ b/Resources/Prototypes/WhiteDream/Entities/Objects/Cult/souls_shards.yml @@ -0,0 +1,49 @@ +- type: entity + name: soul shard + description: Mysterious glowing shard. + parent: BaseItem + id: SoulShard + components: + - type: LagCompensation + - type: Sprite + sprite: WhiteDream/BloodCult/Entities/Items/soul_stone.rsi + layers: + - state: soul_stone + map: [ "enum.SoulShardVisualState.Sprite" ] + - state: soul_stone_glow + map: [ "enum.SoulShardVisualState.Glow" ] + visible: false + - type: MindContainer + - type: Examiner + skipChecks: true + - type: PointLight + color: Red + radius: 2 + softness: 1 + enabled: false + - type: Appearance + - type: GenericVisualizer + visuals: + enum.SoulShardVisualState.HasMind: + enum.SoulShardVisualState.Glow: + True: { visible: true } + False: { visible: false } + enum.SoulShardVisualState.Blessed: + enum.SoulShardVisualState.Sprite: + True: { state: "soul_stone_blessed" } + False: { state: "soul_stone" } + - type: Speech + - type: IsDeadIC + - type: SoulShard + +- type: entity + parent: SoulShard + id: SoulShardGhost + suffix: Ghost Role + components: + - type: GhostRole + allowMovement: true + name: ghost-role-information-soul-shard-name + description: ghost-role-information-soul-shard-description + rules: ghost-role-information-soul-shard-rules + - type: GhostTakeoverAvailable diff --git a/Resources/Prototypes/WhiteDream/Entities/Objects/Items/cult.yml b/Resources/Prototypes/WhiteDream/Entities/Objects/Items/cult.yml new file mode 100644 index 00000000000..e85ee49f20e --- /dev/null +++ b/Resources/Prototypes/WhiteDream/Entities/Objects/Items/cult.yml @@ -0,0 +1,200 @@ +- type: entity + parent: BaseItem + id: ShuttleCurse + name: cursed orb + description: You peer within this smokey orb and glimpse terrible fates befalling the emergency escape shuttle. + components: + - type: Sprite + sprite: WhiteDream/BloodCult/Entities/Items/shuttle_curse.rsi + state: icon + - type: CultItem + - type: ShuttleCurse + +- type: entity + parent: BaseItem + id: WhetstoneCult + name: eldritch whetstone + description: A block, empowered by dark magic. Sharp weapons will be enhanced when used on the stone. + components: + - type: Sprite + sprite: WhiteDream/BloodCult/Entities/Items/whetstone_cult.rsi + layers: + - state: icon + map: [ "enum.GenericCultVisuals.Layer" ] + - type: CultItem + - type: Whetstone + whitelist: + components: + - Sharp + blacklist: + components: + - EnergySword + - type: Appearance + - type: GenericVisualizer + visuals: + enum.GenericCultVisuals.State: + enum.GenericCultVisuals.Layer: + True: { state: icon } + False: { state: icon_off } + +- type: entity + parent: BaseItem + id: VeilShifter + name: veil shifter + description: This relic instantly teleports you, and anything you're pulling, forward by a moderate distance. + components: + - type: Sprite + sprite: WhiteDream/BloodCult/Entities/Items/veil_shifter.rsi + layers: + - state: icon + map: [ "enum.GenericCultVisuals.Layer" ] + - type: CultItem + - type: VeilShifter + - type: Appearance + - type: GenericVisualizer + visuals: + enum.GenericCultVisuals.State: + enum.GenericCultVisuals.Layer: + True: { state: icon } + False: { state: icon_off } + +- type: entity + parent: BaseItem + id: VoidTorch + name: void torch + description: Used by veteran cultists to instantly transport items to their needful brethren + components: + - type: Sprite + sprite: WhiteDream/BloodCult/Entities/Items/void_torch.rsi + layers: + - state: icon + map: [ "enum.GenericCultVisuals.Layer" ] + - type: CultItem + - type: VoidTorch + - type: IgnitionSource + temperature: 400 + ignited: false + - type: PointLight + enabled: false + color: "#e33119" + radius: 1.0 + energy: 5.0 + netsync: false + - type: LightBehaviour + behaviours: + - !type:RandomizeBehaviour # immediately make it bright and flickery + id: turn_on + interpolate: Nearest + minDuration: 0.02 + maxDuration: 0.06 + startValue: 6.0 + endValue: 9.0 + property: Energy + isLooped: true + - !type:FadeBehaviour # have the radius start small and get larger as it starts to burn + id: turn_on + maxDuration: 8.0 + startValue: 1.0 + endValue: 6.0 + - !type:RandomizeBehaviour # weaker flicker as it fades out + id: fade_out + interpolate: Nearest + minDuration: 0.02 + maxDuration: 0.06 + startValue: 4.0 + endValue: 8.0 + property: Energy + isLooped: true + - !type:FadeBehaviour # fade out radius as it burns out + id: fade_out + maxDuration: 4.0 + startValue: 6.0 + endValue: 1.0 + - type: UserInterface + interfaces: + enum.ListViewSelectorUiKey.Key: + type: ListViewSelectorBUI + - type: Appearance + - type: GenericVisualizer + visuals: + enum.GenericCultVisuals.State: + enum.GenericCultVisuals.Layer: + True: { state: icon } + False: { state: icon_off } + - type: Tag + tags: + - Torch + +- type: entity + parent: BaseItem + id: ShadowShackles + name: shadow shackles + description: Shackles that bind the wrists with sinister magic. + components: + - type: Item + size: Small + storedRotation: 90 + - type: Handcuff + breakoutTime: 5 + breakOnRemove: true + cuffedRSI: Objects/Misc/cablecuffs.rsi + bodyIconState: body-overlay + color: black + - type: Sprite + sprite: WhiteDream/BloodCult/actions.rsi + state: cuff + +- type: entity + parent: BaseItem + id: MirrorShieldCult + name: mirror shield + description: An infamous shield used by Nar'Sien sects to confuse and disorient their enemies. Its edges are weighted for use as a throwing weapon - capable of disabling multiple foes with preternatural accuracy. + components: + - type: Sprite + sprite: WhiteDream/BloodCult/Entities/Items/mirror_shield.rsi + state: icon + - type: Item + size: Ginormous + - type: CultItem + - type: StunOnCollide + blacklist: + components: + - BloodCultist + - type: Reflect + reflectProb: 0.75 + innate: true + reflects: + - Energy + - type: Blocking + passiveBlockModifier: + coefficients: + Blunt: 0.8 + Slash: 0.8 + Piercing: 0.8 + Heat: 0.8 + activeBlockModifier: + coefficients: + Blunt: 0.8 + Slash: 0.8 + Piercing: 0.8 + Heat: 0.8 + flatReductions: + Blunt: 1 + Slash: 1 + Piercing: 1 + Heat: 1 + blockSound: !type:SoundPathSpecifier + path: /Audio/Effects/glass_step.ogg + - type: Damageable + damageContainer: Shield + - type: Destructible + thresholds: + - trigger: + !type:DamageTrigger + damage: 100 + behaviors: + - !type:DoActsBehavior + acts: [ "Destruction" ] + - !type:PlaySoundBehavior + sound: + collection: GlassBreak diff --git a/Resources/Prototypes/WhiteDream/Entities/Objects/Materials/cult.yml b/Resources/Prototypes/WhiteDream/Entities/Objects/Materials/cult.yml new file mode 100644 index 00000000000..0f80d80c9e2 --- /dev/null +++ b/Resources/Prototypes/WhiteDream/Entities/Objects/Materials/cult.yml @@ -0,0 +1,78 @@ +- type: entity + parent: SheetOtherBase + id: RunedMetal + name: runic metal + description: An unusual sheet of metal with a pulsating rune. + suffix: Full + components: + - type: Sprite + sprite: WhiteDream/BloodCult/Entities/runic_metal.rsi + layers: + - state: runic_3 + map: [ "base" ] + - type: Tag + tags: + - Sheet + - type: Material + - type: PhysicalComposition + materialComposition: + RunedMetal: 100 + - type: Stack + stackType: RunedMetalSheets + baseLayer: base + layerStates: + - runic + - runic_2 + - runic_3 + - type: Appearance + - type: Item + size: Small + - type: CultItem + - type: UserInterface + interfaces: + enum.RadialSelectorUiKey.Key: + type: RadialSelectorMenuBUI + - type: ActivatableUI + inHandsOnly: true + key: enum.RadialSelectorUiKey.Key + userWhitelist: + components: + - BloodCultist + - type: ShortConstruction + entries: + - prototype: CultPylon + - prototype: CultFactoryArchives + - prototype: CultFactoryForge + - prototype: CultFactoryAltar + - prototype: CultGirder + - prototype: CultDoor + +- type: entity + parent: RunedMetal + id: RunedMetal1 + suffix: Single + components: + - type: Sprite + state: runic + - type: Stack + count: 1 + +- type: entity + parent: RunedMetal + id: RunedMetal4 + suffix: 4 + components: + - type: Sprite + state: runic + - type: Stack + count: 4 + +- type: entity + parent: RunedMetal + id: RunedMetal20 + suffix: 20 + components: + - type: Sprite + state: runic + - type: Stack + count: 20 diff --git a/Resources/Prototypes/WhiteDream/Entities/Objects/Runes/cult.yml b/Resources/Prototypes/WhiteDream/Entities/Objects/Runes/cult.yml new file mode 100644 index 00000000000..8195f4773fd --- /dev/null +++ b/Resources/Prototypes/WhiteDream/Entities/Objects/Runes/cult.yml @@ -0,0 +1,166 @@ +- type: entity + id: CultRuneBase + name: based rune + abstract: true + placement: + mode: SnapgridCenter + components: + - type: Fixtures + fixtures: + rune: + shape: + !type:PhysShapeAabb + bounds: "-0.4,-0.4,0.4,0.4" + hard: false + mask: + - ItemMask + layer: + - SlipLayer + - type: Physics + - type: Clickable + - type: Sprite + sprite: WhiteDream/BloodCult/Entities/Runes/regular.rsi + state: "offering" + - type: CultRuneBase + - type: Appearance + +- type: entity + parent: CultRuneBase + id: CultRuneOffering + name: rune of offering + components: + - type: Sprite + state: "offering" + - type: CultRuneBase + invokePhrase: "Mah'weyh pleggh at e'ntrath!" + - type: CultRuneOffering + +- type: entity + parent: CultRuneBase + id: CultRuneEmpower + name: rune of empower + components: + - type: Sprite + state: strength + - type: CultRuneBase + invokePhrase: "Qu'laris ver'don, thal'sorin mik'thar!" + - type: CultRuneEmpower + +- type: entity + parent: CultRuneBase + id: CultRuneTeleport + name: rune of teleportation + components: + - type: Sprite + state: teleport + - type: CultRuneBase + invokePhrase: "Sas'so c'arta forbici!" + - type: CultRuneTeleport + - type: UserInterface + interfaces: + enum.ListViewSelectorUiKey.Key: + type: ListViewSelectorBUI + enum.NameSelectorUiKey.Key: + type: NameSelectorBUI + +- type: entity + parent: CultRuneBase + id: CultRuneRevive + name: rune of rejuvenation + components: + - type: Sprite + state: revive + - type: CultRuneBase + invokePhrase: "Pasnar val'keriam usinar. Savrae ines amutan. Yam'toth remium il'tarat!" + - type: CultRuneRevive + +- type: entity + parent: CultRuneBase + id: CultRuneBarrier + name: rune of barrier + components: + - type: Sprite + state: barrier + - type: CultRuneBase + invokePhrase: "Khari'd! Eske'te tannin!" + runeActivationRange: 1.5 + activationDamage: + types: + Slash: 5 + - type: CultRuneBarrier + +- type: entity + parent: CultRuneBase + id: CultRuneSummoning + name: rune of summoning + components: + - type: Sprite + state: summon + - type: CultRuneBase + requiredInvokers: 3 + invokePhrase: "N'ath reth sh'yro eth d'rekkathnor!" + - type: CultRuneSummon + - type: UserInterface + interfaces: + enum.ListViewSelectorUiKey.Key: + type: ListViewSelectorBUI + +- type: entity + parent: CultRuneBase + id: CultRuneBloodBoil + name: rune of boiling blood + components: + - type: Sprite + state: blood_boil + - type: CultRuneBase + invokePhrase: "N'Dedo ol'btoh!" + requiredInvokers: 3 + activationDamage: + types: + Slash: 35 + - type: CultRuneBloodBoil + +- type: entity + parent: CultRuneBase + id: CultRuneApocalypse + name: rune of apocalypse + components: + - type: Sprite + sprite: WhiteDream/BloodCult/Entities/Runes/apocalypse.rsi + layers: + - state: icon + map: [ "enum.ApocalypseRuneVisuals.Layer" ] + - type: CultRuneBase + requiredInvokers: 3 + invokePhrase: "Ta'gh fara'qha fel d'amar det!" + activationDamage: + types: + Slash: 35 + - type: CultRuneApocalypse + - type: GenericVisualizer + visuals: + enum.ApocalypseRuneVisuals.Used: + enum.ApocalypseRuneVisuals.Layer: + True: { color: "#696969" } + +- type: entity + parent: CultRuneBase + id: CultRuneDimensionalRending + name: rune of dimensional rending + components: + - type: Sprite + sprite: WhiteDream/BloodCult/Entities/Runes/dimensional_rending.rsi + layers: + - state: rune + map: [ "enum.RendingRuneVisuals.Layer" ] + - type: CultRuneBase + requiredInvokers: 10 + invokeChatType: Speak + invokePhrase: "TOK-LYR RQA-NAP G'OLT-ULOFT!!!" + - type: CultRuneRending + - type: GenericVisualizer + visuals: + enum.RendingRuneVisuals.Active: + enum.RendingRuneVisuals.Layer: + True: { state: "rune_animated" } + False: { state: "rune"} diff --git a/Resources/Prototypes/WhiteDream/Entities/Objects/Structures/Cult/airlock.yml b/Resources/Prototypes/WhiteDream/Entities/Objects/Structures/Cult/airlock.yml new file mode 100644 index 00000000000..188649d9aa8 --- /dev/null +++ b/Resources/Prototypes/WhiteDream/Entities/Objects/Structures/Cult/airlock.yml @@ -0,0 +1,54 @@ +- type: entity + id: CultDoor + parent: BaseMaterialDoor + name: runed door + description: It opens, it closes, and maybe crushes you. This one has a strange glowing rune on it. + placement: + mode: SnapgridCenter + components: + - type: Airtight + fixVacuum: true + noAirWhenFullyAirBlocked: false + - type: Destructible + thresholds: + - trigger: + !type:DamageTrigger + damage: 200 + behaviors: + - !type:DoActsBehavior + acts: ["Destruction"] + - type: Door + bumpOpen: true + occludes: false + crushDamage: + types: + Blunt: 15 + openSound: + path: /Audio/Effects/stonedoor_openclose.ogg + closeSound: + path: /Audio/Effects/stonedoor_openclose.ogg + - type: Sprite + sprite: WhiteDream/BloodCult/Entities/Structures/cult_airlock.rsi + layers: + - state: closed + map: ["enum.DoorVisualLayers.Base"] + - type: MeleeSound + soundGroups: + Brute: + collection: GlassSmash + - type: Physics + bodyType: Static + - type: Occluder + enabled: false + - type: RadiationBlocker + resistance: 2 + - type: RCDDeconstructable + deconstructable: false + - type: RunedDoor + - type: Repulse + - type: RepulseOnTouch + - type: PlacementReplacement + key: walls + - type: Construction + graph: CultDoor + node: door diff --git a/Resources/Prototypes/WhiteDream/Entities/Objects/Structures/Cult/barrier.yml b/Resources/Prototypes/WhiteDream/Entities/Objects/Structures/Cult/barrier.yml new file mode 100644 index 00000000000..c8ae0d6b3a3 --- /dev/null +++ b/Resources/Prototypes/WhiteDream/Entities/Objects/Structures/Cult/barrier.yml @@ -0,0 +1,71 @@ +- type: entity + name: cult barrier + description: Can be destroyed with ritual dagger. + id: BloodCultBarrier + parent: BaseStructure + components: + - type: Transform + noRot: true + - type: Sprite + layers: + - sprite: WhiteDream/BloodCult/Entities/Structures/barrier.rsi + state: barrier + shader: unshaded + - type: Appearance + - type: InteractionOutline + - type: Fixtures + fixtures: + fix1: + shape: + !type:PhysShapeCircle + radius: 0.45 + density: 75 + mask: + - MachineMask + layer: + - WallLayer + - type: Damageable + damageContainer: StructuralInorganic + damageModifierSet: Metallic + - type: Destructible + thresholds: + - trigger: + !type:DamageTrigger + damage: 600 + behaviors: + - !type:DoActsBehavior + acts: [ "Destruction" ] + - trigger: + !type:DamageTrigger + damage: 300 + behaviors: + - !type:SpawnEntitiesBehavior + spawn: + RunedMetal: + min: 5 + max: 5 + - !type:PlaySoundBehavior + sound: + collection: MetalBreak + - !type:DoActsBehavior + acts: [ "Destruction" ] + - type: PointLight + enabled: false + radius: 3 + color: red + - type: BloodCultBarrier + - type: Airtight + noAirWhenFullyAirBlocked: false + +- type: entity + id: WallForceCult + parent: WallForce + name: glowing wall + description: An unholy shield that blocks all attacks. + components: + - type: Sprite + sprite: WhiteDream/BloodCult/Entities/Structures/cult_shield.rsi + state: icon + - type: Icon + sprite: WhiteDream/BloodCult/Entities/Structures/cult_shield.rsi + state: icon diff --git a/Resources/Prototypes/WhiteDream/Entities/Objects/Structures/Cult/factories.yml b/Resources/Prototypes/WhiteDream/Entities/Objects/Structures/Cult/factories.yml new file mode 100644 index 00000000000..052d92158d2 --- /dev/null +++ b/Resources/Prototypes/WhiteDream/Entities/Objects/Structures/Cult/factories.yml @@ -0,0 +1,109 @@ +- type: entity + id: CultFactoryBase + parent: BaseStructure + name: base cult factory + description: You can make things here. + abstract: true + components: + - type: Sprite + sprite: WhiteDream/BloodCult/Entities/Structures/altar.rsi + layers: + - state: icon + map: [ "enum.GenericCultVisuals.Layer" ] + - type: Transform + noRot: true + - type: Fixtures + fixtures: + fix1: + shape: + !type:PhysShapeAabb + bounds: "-0.1,-0.3,0.1,0.3" + density: 55 + mask: + - TableMask + layer: + - TableLayer + - type: InteractionOutline + - type: Damageable + damageContainer: StructuralInorganic + damageModifierSet: Metallic + - type: Destructible + thresholds: + - trigger: + !type:DamageTrigger + damage: 100 + behaviors: + - !type:DoActsBehavior + acts: [ "Destruction" ] + - !type:PlaySoundBehavior + sound: + collection: MetalBreak + - type: Appearance + - type: TimedFactory + - type: UserInterface + interfaces: + enum.RadialSelectorUiKey.Key: + type: RadialSelectorMenuBUI + - type: ActivatableUI + key: enum.RadialSelectorUiKey.Key + userWhitelist: + components: + - BloodCultist + - type: GenericVisualizer + visuals: + enum.GenericCultVisuals.State: + enum.GenericCultVisuals.Layer: + True: { state: "icon" } + False: { state: "icon_off" } + +- type: entity + id: CultFactoryAltar + parent: CultFactoryBase + name: altar + description: A bloodstained altar dedicated to Nar'Sie. + components: + - type: Sprite + sprite: WhiteDream/BloodCult/Entities/Structures/altar.rsi + - type: TimedFactory + entries: + - prototype: ConstructShell + - prototype: WhetstoneCult + - type: Construction + graph: CultFactoryAltar + node: altar + +- type: entity + id: CultFactoryForge + parent: CultFactoryBase + name: daemon forge + description: A forge used in crafting the unholy weapons used by the armies of Nar'Sie. + components: + - type: Sprite + sprite: WhiteDream/BloodCult/Entities/Structures/forge.rsi + - type: TimedFactory + entries: + - prototype: MirrorShieldCult + - prototype: EldritchLongsword + - prototype: ClothingOuterCultArmor + - prototype: ClothingOuterRobesCultTrue + - type: Construction + graph: CultFactoryForge + node: forge + +- type: entity + id: CultFactoryArchives + parent: CultFactoryBase + name: archives + description: A desk covered in arcane manuscripts and tomes in unknown languages. Looking at the text makes your skin crawl. + components: + - type: Sprite + sprite: WhiteDream/BloodCult/Entities/Structures/archives.rsi + - type: TimedFactory + entries: + - prototype: ShuttleCurse + - prototype: ClothingEyeCultBlindfold + - prototype: VeilShifter + - prototype: VoidTorch + - type: Construction + graph: CultFactoryArchives + node: archives diff --git a/Resources/Prototypes/WhiteDream/Entities/Objects/Structures/Cult/pylon.yml b/Resources/Prototypes/WhiteDream/Entities/Objects/Structures/Cult/pylon.yml new file mode 100644 index 00000000000..8068ad1ef92 --- /dev/null +++ b/Resources/Prototypes/WhiteDream/Entities/Objects/Structures/Cult/pylon.yml @@ -0,0 +1,71 @@ +- type: entity + id: CultPylon + parent: BaseStructure + name: pylon + description: A floating crystal that slowly heals those faithful to Nar'Sie. + components: + - type: Transform + noRot: true + - type: Fixtures + fixtures: + pylonFix: + shape: + !type:PhysShapeAabb + bounds: "-0.1,-0.3,0.1,0.3" + density: 190 + mask: + - TabletopMachineMask + layer: + - TabletopMachineLayer + - type: Sprite + noRot: true + sprite: WhiteDream/BloodCult/Entities/Structures/pylon.rsi + layers: + - state: icon + map: [ "enum.PylonVisuals.Layer" ] + - type: InteractionOutline + - type: Damageable + damageContainer: StructuralInorganic + damageModifierSet: Glass + - type: Destructible + thresholds: + - trigger: + !type:DamageTrigger + damage: 50 + behaviors: + - !type:DoActsBehavior + acts: [ "Destruction" ] + - !type:PlaySoundBehavior + sound: + collection: GlassBreak + - !type:SpawnEntitiesBehavior + spawn: + ShardGlass: + min: 1 + max: 2 + - type: Appearance + - type: Pylon + healing: + groups: + Brute: -20 + Burn: -20 + Toxin: -10 + Genetic: -5 + Airloss: -20 + damageOnInteract: + groups: + Burn: 5 + - type: PointLight + color: "#FF0000" + radius: 2 + energy: 2 + enabled: true + - type: GenericVisualizer + visuals: + enum.PylonVisuals.Activated: + enum.PylonVisuals.Layer: + True: { state: "icon" } + False: { state: "icon_off" } + - type: Construction + graph: CultPylon + node: pylon diff --git a/Resources/Prototypes/WhiteDream/Entities/Objects/Structures/Cult/walls.yml b/Resources/Prototypes/WhiteDream/Entities/Objects/Structures/Cult/walls.yml new file mode 100644 index 00000000000..408eeb21fab --- /dev/null +++ b/Resources/Prototypes/WhiteDream/Entities/Objects/Structures/Cult/walls.yml @@ -0,0 +1,34 @@ +- type: entity + id: CultGirder + parent: Girder + name: runed girder + description: Framework made of a strange and shockingly cold metal. It doesn't seem to have any bolts + components: + - type: Construction + graph: CultGirder + node: girder + - type: Sprite + sprite: WhiteDream/BloodCult/Entities/Structures/cult_girder.rsi + state: icon + - type: Destructible + thresholds: + - trigger: + !type:DamageTrigger + damage: 200 + behaviors: #excess damage, don't spawn entities. + - !type:DoActsBehavior + acts: [ "Destruction" ] + - trigger: + !type:DamageTrigger + damage: 50 + behaviors: + - !type:SpawnEntitiesBehavior + spawn: + SheetSteel1: + min: 1 + max: 1 + - !type:DoActsBehavior + acts: [ "Destruction" ] + - !type:PlaySoundBehavior + sound: + collection: MetalBreak diff --git a/Resources/Prototypes/WhiteDream/Entities/Objects/Weapons/Guns/Projectiles/magic.yml b/Resources/Prototypes/WhiteDream/Entities/Objects/Weapons/Guns/Projectiles/magic.yml new file mode 100644 index 00000000000..c4271eeb95f --- /dev/null +++ b/Resources/Prototypes/WhiteDream/Entities/Objects/Weapons/Guns/Projectiles/magic.yml @@ -0,0 +1,21 @@ +- type: entity + id: BloodBoilProjectile + parent: BaseBullet + name: Concentrated Blood + description: Oh no. + categories: [ HideSpawnMenu ] + components: + - type: Sprite + sprite: WhiteDream/BloodCult/Effects/blood_boil.rsi + state: bullet + - type: Projectile + damage: + groups: + Burn: 10 + Brute: 10 + - type: PointLight + enabled: true + color: "#ff4300" + radius: 2.0 + energy: 7.0 + - type: BloodBoilProjectile diff --git a/Resources/Prototypes/WhiteDream/Entities/Objects/Weapons/Melee/cult.yml b/Resources/Prototypes/WhiteDream/Entities/Objects/Weapons/Melee/cult.yml new file mode 100644 index 00000000000..c21c383e8e6 --- /dev/null +++ b/Resources/Prototypes/WhiteDream/Entities/Objects/Weapons/Melee/cult.yml @@ -0,0 +1,158 @@ +- type: entity + name: ritual dagger + parent: BaseKnife + id: RitualDagger + description: A strange dagger used by sinister groups for rituals and sacrifices. + components: + - type: Sprite + sprite: Objects/Weapons/Melee/cult_dagger.rsi + state: icon + - type: MeleeWeapon # TODO: It should have armor piercing effect (around 50%?) but we have no such system yet. Sucks. + wideAnimationRotation: -135 + maxTargets: 1 + heavyRateModifier: 0.95 + heavyStaminaCost: 5 + damage: + types: + Piercing: 15 + - type: Clothing + sprite: Objects/Weapons/Melee/cult_dagger.rsi + slots: + - back + - type: DisarmMalus + - type: CultItem + - type: RuneDrawer + - type: ActivatableUI + key: enum.RuneDrawerBuiKey.Key + inHandsOnly: true + userWhitelist: + components: + - BloodCultist + - type: UserInterface + interfaces: + enum.RuneDrawerBuiKey.Key: + type: RuneDrawerBUI + +- type: entity + name: eldritch longsword + parent: BaseItem + id: EldritchLongsword + description: A sword humming with unholy energy. It glows with a dim red light. + components: + - type: Sharp + - type: Sprite + sprite: WhiteDream/BloodCult/Entities/Items/Weapons/cult_blade.rsi + state: icon + - type: MeleeWeapon + wideAnimationRotation: -135 + attackRate: 0.75 + range: 1.65 + damage: + types: + Slash: 24 + heavyDamageBaseModifier: 1.2 + heavyStaminaCost: 10 + maxTargets: 3 + angle: 90 + soundHit: + path: /Audio/Weapons/bladeslice.ogg + - type: Item + size: Normal + - type: Clothing + slots: + - back + - type: DisarmMalus + - type: CultItem + - type: PointLight + color: Red + radius: 2 + softness: 1 + +- type: entity + parent: BaseItem + id: BloodSpear + name: blood halberd + description: A sickening spear composed entirely of crystallized blood. + components: + - type: Sprite + sprite: WhiteDream/BloodCult/Entities/Items/Weapons/cult_spear.rsi + state: icon + - type: EmbeddableProjectile + offset: 0.15,0.15 + - type: ThrowingAngle + angle: 225 + - type: Fixtures + fixtures: + fix1: + shape: !type:PolygonShape + vertices: + - -0.20,-0.10 + - -0.10,-0.20 + - 0.40,0.30 + - 0.30,0.40 + density: 20 + mask: + - BulletImpassable + restitution: 0.3 + friction: 0.2 + - type: Sharp + - type: MeleeWeapon + wideAnimationRotation: -135 + damage: + types: + Piercing: 36 + angle: 0 + animation: WeaponArcThrust + soundHit: + path: /Audio/Weapons/bladeslice.ogg + range: 2 + attackRate: 0.7 + - type: DamageOtherOnHit + damage: + types: + Piercing: 40 + - type: Item + sprite: WhiteDream/BloodCult/Entities/Items/Weapons/cult_spear.rsi + storedRotation: 44 + size: Huge + shape: + - 0,0,5,0 + - type: Clothing + slots: + - back + - suitStorage + - type: Wieldable + - type: IncreaseDamageOnWield + damage: + types: + Piercing: 8 + - type: UseDelay + - type: DisarmMalus + - type: CultItem + - type: BloodSpear + +- type: entity + parent: BaseItem + id: BloodRitesAura + name: blood rite aura + description: Absorbs blood from anything you touch. Touching cultists and constructs can heal them. Use in-hand to cast an advanced rite. + components: + - type: Sprite + sprite: WhiteDream/BloodCult/Entities/Items/rites.rsi + state: icon + - type: MeleeWeapon + damage: + types: + Blunt: 0 + heavyStaminaCost: 0 + maxTargets: 1 + - type: Unremoveable + - type: BloodRitesAura + - type: UserInterface + interfaces: + enum.BloodRitesUiKey.Key: + type: BloodRitesUi + - type: ActivatableUI + key: enum.BloodRitesUiKey.Key + inHandsOnly: true + requireActiveHand: false diff --git a/Resources/Prototypes/WhiteDream/Entities/Objects/Weapons/Throwable/cult.yml b/Resources/Prototypes/WhiteDream/Entities/Objects/Weapons/Throwable/cult.yml new file mode 100644 index 00000000000..05f9f8506bc --- /dev/null +++ b/Resources/Prototypes/WhiteDream/Entities/Objects/Weapons/Throwable/cult.yml @@ -0,0 +1,22 @@ +- type: entity + parent: [BaseBola] + id: CultBola + name: nar'sien bola + description: A strong bola, bound with dark magic that allows it to pass harmlessly through Nar'Sien cultists. Throw it to trip and slow your victim. + components: + - type: Sprite + sprite: WhiteDream/BloodCult/Entities/Items/bola.rsi + state: icon + - type: CultItem + - type: Ensnaring + freeTime: 2.0 + breakoutTime: 3.5 + walkSpeed: 0.7 + sprintSpeed: 0.7 + staminaDamage: 55 + canThrowTrigger: true + canMoveBreakout: true + destroyOnRemove: true + ignoredTargets: + components: + - BloodCultist diff --git a/Resources/Prototypes/WhiteDream/GameRules/roundstart.yml b/Resources/Prototypes/WhiteDream/GameRules/roundstart.yml new file mode 100644 index 00000000000..0ffe0220552 --- /dev/null +++ b/Resources/Prototypes/WhiteDream/GameRules/roundstart.yml @@ -0,0 +1,25 @@ +- type: entity + id: BloodCult + parent: BaseGameRule + categories: [ HideSpawnMenu ] + components: + - type: GameRule + minPlayers: 30 + - type: BloodCultRule + - type: AntagSelection + definitions: + - prefRoles: [ BloodCultist ] + max: 4 + min: 2 + playerRatio: 15 + briefing: + text: blood-cult-role-greeting + color: Red + sound: "/Audio/WhiteDream/BloodCult/blood_cult_greeting.ogg" + startingGear: BloodCultistGear + components: + - type: BloodCultist + - type: BloodCultSpellsHolder + mindComponents: + - type: BloodCultistRole + prototype: BloodCultist diff --git a/Resources/Prototypes/WhiteDream/Objectives/cult.yml b/Resources/Prototypes/WhiteDream/Objectives/cult.yml new file mode 100644 index 00000000000..835807bfce6 --- /dev/null +++ b/Resources/Prototypes/WhiteDream/Objectives/cult.yml @@ -0,0 +1,19 @@ +- type: entity + parent: BaseObjective + id: KillTargetCultObjective + description: This fool person should be sacrificed in the glory of our Goddess. + categories: [ HideSpawnMenu ] + components: + - type: Objective + issuer: The Geometer of Blood + unique: true + difficulty: 3 + icon: + sprite: Objects/Weapons/Melee/cult_dagger.rsi + state: icon + - type: RoleRequirement + roles: + components: + - BloodCultistRole + - type: KillTargetCult + title: objective-condition-kill-person-title diff --git a/Resources/Prototypes/WhiteDream/Pool/cult_powers_pool.yml b/Resources/Prototypes/WhiteDream/Pool/cult_powers_pool.yml new file mode 100644 index 00000000000..6da36a41f0c --- /dev/null +++ b/Resources/Prototypes/WhiteDream/Pool/cult_powers_pool.yml @@ -0,0 +1,11 @@ +- type: psionicPowerPool + id: BloodCultPowers + powers: + - ActionBloodCultStun + - ActionBloodCultTeleport + - ActionBloodCultEmp + - ActionBloodCultShadowShackles + - ActionBloodCultTwistedConstruction + - ActionBloodCultSummonCombatEquipment + - ActionBloodCultSummonRitualDagger + - ActionBloodCultBloodRites diff --git a/Resources/Prototypes/WhiteDream/Reagents/Materials/cult.yml b/Resources/Prototypes/WhiteDream/Reagents/Materials/cult.yml new file mode 100644 index 00000000000..e444df178e6 --- /dev/null +++ b/Resources/Prototypes/WhiteDream/Reagents/Materials/cult.yml @@ -0,0 +1,7 @@ +- type: material + id: RunedMetal + stackEntity: RunedMetal1 + name: materials-runed-metal + icon: { sprite: /Textures/WhiteDream/BloodCult/Entities/runic_metal.rsi, state: runic} + color: "#9d2b39" + price: 0.05 diff --git a/Resources/Prototypes/WhiteDream/Recipes/Construction/cult.yml b/Resources/Prototypes/WhiteDream/Recipes/Construction/cult.yml new file mode 100644 index 00000000000..a87edd40fb7 --- /dev/null +++ b/Resources/Prototypes/WhiteDream/Recipes/Construction/cult.yml @@ -0,0 +1,107 @@ +- type: construction + id: CultPylon + name: cult pylon + description: A floating crystal that slowly heals those faithful to Nar'Sie. + category: construction-category-structures + hide: true + graph: CultPylon + startNode: start + targetNode: pylon + icon: + sprite: WhiteDream/BloodCult/Entities/Structures/pylon.rsi + state: icon_off + objectType: Structure + placementMode: AlignPylonConstruction + canBuildInImpassable: false + conditions: + - !type:TileNotBlocked + +- type: construction + id: CultFactoryAltar + name: altar + description: A bloodstained altar dedicated to Nar'Sie. + category: construction-category-structures + hide: true + graph: CultFactoryAltar + startNode: start + targetNode: altar + icon: + sprite: WhiteDream/BloodCult/Entities/Structures/altar.rsi + state: icon_off + objectType: Structure + placementMode: SnapgridCenter + canBuildInImpassable: false + conditions: + - !type:TileNotBlocked + +- type: construction + id: CultFactoryForge + name: daemon forge + description: A forge used in crafting the unholy weapons used by the armies of Nar'Sie. + category: construction-category-structures + hide: true + graph: CultFactoryForge + startNode: start + targetNode: forge + icon: + sprite: WhiteDream/BloodCult/Entities/Structures/forge.rsi + state: icon_off + objectType: Structure + placementMode: SnapgridCenter + canBuildInImpassable: false + conditions: + - !type:TileNotBlocked + +- type: construction + id: CultFactoryArchives + name: archives + description: A desk covered in arcane manuscripts and tomes in unknown languages. Looking at the text makes your skin crawl. + category: construction-category-structures + hide: true + graph: CultFactoryArchives + startNode: start + targetNode: archives + icon: + sprite: WhiteDream/BloodCult/Entities/Structures/archives.rsi + state: icon_off + objectType: Structure + placementMode: SnapgridCenter + canBuildInImpassable: false + conditions: + - !type:TileNotBlocked + +- type: construction + id: CultDoor + name: runed door + description: It opens, it closes, and maybe crushes you. This one has a strange glowing rune on it. + category: construction-category-structures + hide: true + graph: CultDoor + startNode: start + targetNode: door + icon: + sprite: WhiteDream/BloodCult/Entities/Structures/cult_airlock.rsi + state: closed + objectType: Structure + placementMode: SnapgridCenter + canBuildInImpassable: false + conditions: + - !type:TileNotBlocked + +- type: construction + id: CultGirder + name: runed girder + description: Framework made of a strange and shockingly cold metal. It doesn't seem to have any bolts + category: construction-category-structures + hide: true + graph: CultGirder + startNode: start + targetNode: girder + icon: + sprite: WhiteDream/BloodCult/Entities/Structures/cult_girder.rsi + state: icon + objectType: Structure + placementMode: SnapgridCenter + canBuildInImpassable: false + conditions: + - !type:TileNotBlocked diff --git a/Resources/Prototypes/WhiteDream/Recipes/Construction/cult_graphs.yml b/Resources/Prototypes/WhiteDream/Recipes/Construction/cult_graphs.yml new file mode 100644 index 00000000000..8bb2dd84213 --- /dev/null +++ b/Resources/Prototypes/WhiteDream/Recipes/Construction/cult_graphs.yml @@ -0,0 +1,114 @@ +- type: constructionGraph + id: CultPylon + start: start + graph: + - node: start + edges: + - to: pylon + completed: + - !type:SnapToGrid + southRotation: true + steps: + - material: RunedMetalSheets + amount: 4 + doAfter: 3 + - node: pylon + entity: CultPylon + +- type: constructionGraph + id: CultFactoryAltar + start: start + graph: + - node: start + edges: + - to: altar + completed: + - !type:SnapToGrid + southRotation: true + steps: + - material: RunedMetalSheets + amount: 3 + doAfter: 3 + - node: altar + entity: CultFactoryAltar + +- type: constructionGraph + id: CultFactoryForge + start: start + graph: + - node: start + edges: + - to: forge + completed: + - !type:SnapToGrid + southRotation: true + steps: + - material: RunedMetalSheets + amount: 3 + doAfter: 3 + - node: forge + entity: CultFactoryForge + +- type: constructionGraph + id: CultFactoryArchives + start: start + graph: + - node: start + edges: + - to: archives + completed: + - !type:SnapToGrid + southRotation: true + steps: + - material: RunedMetalSheets + amount: 3 + doAfter: 3 + - node: archives + entity: CultFactoryArchives + +- type: constructionGraph + id: CultDoor + start: start + graph: + - node: start + edges: + - to: door + completed: + - !type:SnapToGrid + southRotation: true + steps: + - material: RunedMetalSheets + amount: 1 + doAfter: 3 + - node: door + entity: CultDoor + +- type: constructionGraph + id: CultGirder + start: start + graph: + - node: start + edges: + - to: girder + completed: + - !type:SnapToGrid + southRotation: true + steps: + - material: RunedMetalSheets + amount: 1 + doAfter: 3 + - node: girder + entity: CultGirder + edges: + - to: wall + completed: + - !type:SnapToGrid + southRotation: true + conditions: + - !type:EntityAnchored { } + steps: + - material: RunedMetalSheets + amount: 1 + doAfter: 2 + - node: wall + entity: WallCult diff --git a/Resources/Prototypes/WhiteDream/Roles/Antags/blood-cultist.yml b/Resources/Prototypes/WhiteDream/Roles/Antags/blood-cultist.yml new file mode 100644 index 00000000000..0e0e9b59438 --- /dev/null +++ b/Resources/Prototypes/WhiteDream/Roles/Antags/blood-cultist.yml @@ -0,0 +1,16 @@ +- type: antag + id: BloodCultist + name: roles-antag-blood-cultist-name + antagonist: true + setPreference: true + objective: roles-antag-blood-cultist-objective + requirements: + - !type:CharacterOverallTimeRequirement + min: 43200 + +- type: startingGear + id: BloodCultistGear + storage: + back: + - RitualDagger + - RunedMetal20 diff --git a/Resources/Prototypes/WhiteDream/Stacks/Materials/cult.yml b/Resources/Prototypes/WhiteDream/Stacks/Materials/cult.yml new file mode 100644 index 00000000000..d5674f13944 --- /dev/null +++ b/Resources/Prototypes/WhiteDream/Stacks/Materials/cult.yml @@ -0,0 +1,6 @@ +- type: stack + id: RunedMetalSheets + name: materials-runed-metal + icon: { sprite: /Textures/WhiteDream/BloodCult/Entities/runic_metal.rsi, state: runic} + spawn: RunedMetal1 + maxCount: 30 diff --git a/Resources/Prototypes/WhiteDream/StatusIcon/antag.yml b/Resources/Prototypes/WhiteDream/StatusIcon/antag.yml new file mode 100644 index 00000000000..9910ed6f4c0 --- /dev/null +++ b/Resources/Prototypes/WhiteDream/StatusIcon/antag.yml @@ -0,0 +1,15 @@ +- type: statusIcon + id: BloodCultMember + priority: 11 + locationPreference: left + icon: + sprite: /Textures/WhiteDream/BloodCult/cult_hud.rsi + state: cult_member + +- type: statusIcon + id: BloodCultLeader + priority: 11 + locationPreference: left + icon: + sprite: /Textures/WhiteDream/BloodCult/cult_hud.rsi + state: cult_leader diff --git a/Resources/Prototypes/WhiteDream/ai_factions.yml b/Resources/Prototypes/WhiteDream/ai_factions.yml new file mode 100644 index 00000000000..e7a68dcbb99 --- /dev/null +++ b/Resources/Prototypes/WhiteDream/ai_factions.yml @@ -0,0 +1,8 @@ +- type: npcFaction + id: GeometerOfBlood + hostile: + - NanoTrasen + - SimpleHostile + - Xeno + - PetsNT + - Zombie diff --git a/Resources/Prototypes/WhiteDream/alerts.yml b/Resources/Prototypes/WhiteDream/alerts.yml new file mode 100644 index 00000000000..f9693664b42 --- /dev/null +++ b/Resources/Prototypes/WhiteDream/alerts.yml @@ -0,0 +1,5 @@ +- type: alert + id: CultEmpowered + icons: [ /Textures/WhiteDream/BloodCult/Alerts/empowered.png ] + name: alerts-blood-cult-empowered-name + description: alerts-blood-cult-empowered-desc diff --git a/Resources/Prototypes/WhiteDream/game_presets.yml b/Resources/Prototypes/WhiteDream/game_presets.yml new file mode 100644 index 00000000000..fca12c7ded7 --- /dev/null +++ b/Resources/Prototypes/WhiteDream/game_presets.yml @@ -0,0 +1,12 @@ +- type: gamePreset + id: BloodCult + alias: + - bloodcult + name: blood-cult-title + showInVote: true + description: blood-cult-description + rules: + - BloodCult + - SubGamemodesRule + - BasicStationEventScheduler + - BasicRoundstartVariation diff --git a/Resources/Prototypes/WhiteDream/runeSelectors.yml b/Resources/Prototypes/WhiteDream/runeSelectors.yml new file mode 100644 index 00000000000..d63c81aa8da --- /dev/null +++ b/Resources/Prototypes/WhiteDream/runeSelectors.yml @@ -0,0 +1,43 @@ +- type: runeSelector + id: CultRuneOffering + prototype: CultRuneOffering + +- type: runeSelector + id: CultRuneEmpower + prototype: CultRuneEmpower + +- type: runeSelector + id: CultRuneTeleport + prototype: CultRuneTeleport + +- type: runeSelector + id: CultRuneRevive + prototype: CultRuneRevive + +- type: runeSelector + id: CultRuneBarrier + prototype: CultRuneBarrier + +- type: runeSelector + id: CultRuneSummoning + prototype: CultRuneSummoning + +- type: runeSelector + id: CultRuneBloodBoil + prototype: CultRuneBloodBoil + +- type: runeSelector + id: CultRuneApocalypse + prototype: CultRuneApocalypse + drawTime: 40 + drawDamage: + types: + Slash: 20 + +- type: runeSelector + id: CultRuneDimensionalRending + prototype: CultRuneDimensionalRending + drawTime: 40 + drawDamage: + types: + Slash: 50 diff --git a/Resources/Prototypes/WhiteDream/tiles.yml b/Resources/Prototypes/WhiteDream/tiles.yml new file mode 100644 index 00000000000..67f8f15632e --- /dev/null +++ b/Resources/Prototypes/WhiteDream/tiles.yml @@ -0,0 +1,29 @@ +- type: tile + id: CultFloor + name: tiles-cult-floor + sprite: /Textures/WhiteDream/BloodCult/Tiles/cult_tile/cult.png + variants: 1 + baseTurf: Plating + isSubfloor: false + deconstructTools: [ Prying ] + footstepSounds: + collection: FootstepFloor + itemDrop: FloorTileItemSteel + heatCapacity: 10000 + +- type: tile + id: CultFloorConcealed + name: tiles-steel-floor + sprite: /Textures/WhiteDream/BloodCult/Tiles/cult_tile/concealed.png + variants: 3 + placementVariants: + - 1.0 + - 1.0 + - 1.0 + baseTurf: Plating + isSubfloor: false + deconstructTools: [ Prying ] + footstepSounds: + collection: FootstepFloor + itemDrop: FloorTileItemSteel + heatCapacity: 10000 diff --git a/Resources/Prototypes/ai_factions.yml b/Resources/Prototypes/ai_factions.yml index cdbbf868662..c0f7c7da6a0 100644 --- a/Resources/Prototypes/ai_factions.yml +++ b/Resources/Prototypes/ai_factions.yml @@ -44,6 +44,7 @@ - PetsNT - Zombie - Revolutionary + - GeometerOfBlood - type: npcFaction id: SimpleNeutral diff --git a/Resources/Prototypes/secret_weights.yml b/Resources/Prototypes/secret_weights.yml index 4ad31cd1940..75954d5118a 100644 --- a/Resources/Prototypes/secret_weights.yml +++ b/Resources/Prototypes/secret_weights.yml @@ -1,9 +1,10 @@ - type: weightedRandom id: Secret weights: - Survival: 0.44 + Survival: 0.34 Nukeops: 0.14 Zombie: 0.03 - Traitor: 0.39 + Traitor: 0.34 + BloodCult: 0.15 #Pirates: 0.15 #ahoy me bucko diff --git a/Resources/Textures/WhiteDream/BloodCult/Alerts/empowered.png b/Resources/Textures/WhiteDream/BloodCult/Alerts/empowered.png new file mode 100644 index 00000000000..26ddf7fb0a6 Binary files /dev/null and b/Resources/Textures/WhiteDream/BloodCult/Alerts/empowered.png differ diff --git a/Resources/Textures/WhiteDream/BloodCult/Effects/airlock_glow.rsi/doorglow.png b/Resources/Textures/WhiteDream/BloodCult/Effects/airlock_glow.rsi/doorglow.png new file mode 100644 index 00000000000..1b255cc2cb3 Binary files /dev/null and b/Resources/Textures/WhiteDream/BloodCult/Effects/airlock_glow.rsi/doorglow.png differ diff --git a/Resources/Textures/WhiteDream/BloodCult/Effects/airlock_glow.rsi/meta.json b/Resources/Textures/WhiteDream/BloodCult/Effects/airlock_glow.rsi/meta.json new file mode 100644 index 00000000000..c8899120552 --- /dev/null +++ b/Resources/Textures/WhiteDream/BloodCult/Effects/airlock_glow.rsi/meta.json @@ -0,0 +1,28 @@ +{ + "version": 1, + "license": "CC-BY-SA-3.0", + "copyright": "Taken from TG station 13", + "size": { + "x": 32, + "y": 32 + }, + "states": [ + { + "name": "doorglow", + "delays": [ + [ + 0.1, + 0.1, + 0.1, + 0.1, + 0.1, + 0.1, + 0.1, + 0.1, + 0.1, + 0.1 + ] + ] + } + ] +} diff --git a/Resources/Textures/WhiteDream/BloodCult/Effects/blood_boil.rsi/bullet.png b/Resources/Textures/WhiteDream/BloodCult/Effects/blood_boil.rsi/bullet.png new file mode 100644 index 00000000000..5ef822c73cf Binary files /dev/null and b/Resources/Textures/WhiteDream/BloodCult/Effects/blood_boil.rsi/bullet.png differ diff --git a/Resources/Textures/WhiteDream/BloodCult/Effects/blood_boil.rsi/meta.json b/Resources/Textures/WhiteDream/BloodCult/Effects/blood_boil.rsi/meta.json new file mode 100644 index 00000000000..183dbc10d17 --- /dev/null +++ b/Resources/Textures/WhiteDream/BloodCult/Effects/blood_boil.rsi/meta.json @@ -0,0 +1,21 @@ +{ + "license": "CC-BY-SA-3.0", + "copyright": "Taken from TG station 13", + "version": 1, + "size": { + "x": 32, + "y": 32 + }, + "states": [ + { + "name": "bullet", + "delays": [ + [ + 0.07, + 0.07, + 0.07 + ] + ] + } + ] +} diff --git a/Resources/Textures/WhiteDream/BloodCult/Effects/cult_in_out.rsi/cult_in.png b/Resources/Textures/WhiteDream/BloodCult/Effects/cult_in_out.rsi/cult_in.png new file mode 100644 index 00000000000..5d937d4dbd3 Binary files /dev/null and b/Resources/Textures/WhiteDream/BloodCult/Effects/cult_in_out.rsi/cult_in.png differ diff --git a/Resources/Textures/WhiteDream/BloodCult/Effects/cult_in_out.rsi/cult_out.png b/Resources/Textures/WhiteDream/BloodCult/Effects/cult_in_out.rsi/cult_out.png new file mode 100644 index 00000000000..cba5e50ddbe Binary files /dev/null and b/Resources/Textures/WhiteDream/BloodCult/Effects/cult_in_out.rsi/cult_out.png differ diff --git a/Resources/Textures/WhiteDream/BloodCult/Effects/cult_in_out.rsi/meta.json b/Resources/Textures/WhiteDream/BloodCult/Effects/cult_in_out.rsi/meta.json new file mode 100644 index 00000000000..33c2fe8fc12 --- /dev/null +++ b/Resources/Textures/WhiteDream/BloodCult/Effects/cult_in_out.rsi/meta.json @@ -0,0 +1,103 @@ +{ + "version": 1, + "license": "CC-BY-SA-3.0", + "copyright": "Taken from TG station 13", + "size": { + "x": 32, + "y": 32 + }, + "states": [ + { + "name": "cult_out", + "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, + 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": "cult_in", + "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, + 0.1, + 0.1, + 0.1, + 0.1 + ], + [ + 0.1, + 0.1, + 0.1, + 0.1, + 0.1, + 0.1, + 0.1, + 0.1 + ] + ] + } + ] +} diff --git a/Resources/Textures/WhiteDream/BloodCult/Effects/pentagram.rsi/halo1.png b/Resources/Textures/WhiteDream/BloodCult/Effects/pentagram.rsi/halo1.png new file mode 100644 index 00000000000..5d73c095cbb Binary files /dev/null and b/Resources/Textures/WhiteDream/BloodCult/Effects/pentagram.rsi/halo1.png differ diff --git a/Resources/Textures/WhiteDream/BloodCult/Effects/pentagram.rsi/halo2.png b/Resources/Textures/WhiteDream/BloodCult/Effects/pentagram.rsi/halo2.png new file mode 100644 index 00000000000..3b2860a7265 Binary files /dev/null and b/Resources/Textures/WhiteDream/BloodCult/Effects/pentagram.rsi/halo2.png differ diff --git a/Resources/Textures/WhiteDream/BloodCult/Effects/pentagram.rsi/halo3.png b/Resources/Textures/WhiteDream/BloodCult/Effects/pentagram.rsi/halo3.png new file mode 100644 index 00000000000..439ef1005ce Binary files /dev/null and b/Resources/Textures/WhiteDream/BloodCult/Effects/pentagram.rsi/halo3.png differ diff --git a/Resources/Textures/WhiteDream/BloodCult/Effects/pentagram.rsi/halo4.png b/Resources/Textures/WhiteDream/BloodCult/Effects/pentagram.rsi/halo4.png new file mode 100644 index 00000000000..9221a45b7a6 Binary files /dev/null and b/Resources/Textures/WhiteDream/BloodCult/Effects/pentagram.rsi/halo4.png differ diff --git a/Resources/Textures/WhiteDream/BloodCult/Effects/pentagram.rsi/halo5.png b/Resources/Textures/WhiteDream/BloodCult/Effects/pentagram.rsi/halo5.png new file mode 100644 index 00000000000..8ce43423217 Binary files /dev/null and b/Resources/Textures/WhiteDream/BloodCult/Effects/pentagram.rsi/halo5.png differ diff --git a/Resources/Textures/WhiteDream/BloodCult/Effects/pentagram.rsi/halo6.png b/Resources/Textures/WhiteDream/BloodCult/Effects/pentagram.rsi/halo6.png new file mode 100644 index 00000000000..1457a6e3c6e Binary files /dev/null and b/Resources/Textures/WhiteDream/BloodCult/Effects/pentagram.rsi/halo6.png differ diff --git a/Resources/Textures/WhiteDream/BloodCult/Effects/pentagram.rsi/meta.json b/Resources/Textures/WhiteDream/BloodCult/Effects/pentagram.rsi/meta.json new file mode 100644 index 00000000000..65b4bf886d8 --- /dev/null +++ b/Resources/Textures/WhiteDream/BloodCult/Effects/pentagram.rsi/meta.json @@ -0,0 +1,419 @@ +{ + "license": "CC-BY-SA-3.0", + "copyright": "Taken from TG station 13", + "version": 1, + "size": { + "x": 32, + "y": 64 + }, + "states": [ + { + "name": "halo1", + "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, + 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, + 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, + 0.1 + ] + ] + }, + { + "name": "halo2", + "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, + 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, + 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, + 0.1 + ] + ] + }, + { + "name": "halo3", + "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, + 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, + 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, + 0.1 + ] + ] + }, + { + "name": "halo4", + "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, + 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, + 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, + 0.1 + ] + ] + }, + { + "name": "halo5", + "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, + 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, + 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, + 0.1 + ] + ] + }, + { + "name": "halo6", + "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, + 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, + 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, + 0.1 + ] + ] + } + ] +} diff --git a/Resources/Textures/WhiteDream/BloodCult/Effects/tiles_spawn.rsi/floorglow.png b/Resources/Textures/WhiteDream/BloodCult/Effects/tiles_spawn.rsi/floorglow.png new file mode 100644 index 00000000000..82e1c67ded6 Binary files /dev/null and b/Resources/Textures/WhiteDream/BloodCult/Effects/tiles_spawn.rsi/floorglow.png differ diff --git a/Resources/Textures/WhiteDream/BloodCult/Effects/tiles_spawn.rsi/meta.json b/Resources/Textures/WhiteDream/BloodCult/Effects/tiles_spawn.rsi/meta.json new file mode 100644 index 00000000000..070d383593d --- /dev/null +++ b/Resources/Textures/WhiteDream/BloodCult/Effects/tiles_spawn.rsi/meta.json @@ -0,0 +1,28 @@ +{ + "version": 1, + "license": "CC-BY-SA-3.0", + "copyright": "Taken from TG station 13", + "size": { + "x": 32, + "y": 32 + }, + "states": [ + { + "name": "floorglow", + "delays": [ + [ + 0.05, + 0.05, + 0.05, + 0.05, + 0.05, + 0.05, + 0.05, + 0.05, + 0.05, + 0.05 + ] + ] + } + ] +} diff --git a/Resources/Textures/WhiteDream/BloodCult/Effects/wall_glow.rsi/meta.json b/Resources/Textures/WhiteDream/BloodCult/Effects/wall_glow.rsi/meta.json new file mode 100644 index 00000000000..430ae0f7444 --- /dev/null +++ b/Resources/Textures/WhiteDream/BloodCult/Effects/wall_glow.rsi/meta.json @@ -0,0 +1,28 @@ +{ + "version": 1, + "license": "CC-BY-SA-3.0", + "copyright": "Taken from TG station 13", + "size": { + "x": 32, + "y": 32 + }, + "states": [ + { + "name": "wallglow", + "delays": [ + [ + 0.1, + 0.1, + 0.1, + 0.1, + 0.1, + 0.1, + 0.1, + 0.1, + 0.1, + 0.1 + ] + ] + } + ] +} diff --git a/Resources/Textures/WhiteDream/BloodCult/Effects/wall_glow.rsi/wallglow.png b/Resources/Textures/WhiteDream/BloodCult/Effects/wall_glow.rsi/wallglow.png new file mode 100644 index 00000000000..120ab642d6a Binary files /dev/null and b/Resources/Textures/WhiteDream/BloodCult/Effects/wall_glow.rsi/wallglow.png differ diff --git a/Resources/Textures/WhiteDream/BloodCult/Entities/Clothes/Helmet/cult_helmet.rsi/equipped-HELMET.png b/Resources/Textures/WhiteDream/BloodCult/Entities/Clothes/Helmet/cult_helmet.rsi/equipped-HELMET.png new file mode 100644 index 00000000000..ef53285d7dd Binary files /dev/null and b/Resources/Textures/WhiteDream/BloodCult/Entities/Clothes/Helmet/cult_helmet.rsi/equipped-HELMET.png differ diff --git a/Resources/Textures/WhiteDream/BloodCult/Entities/Clothes/Helmet/cult_helmet.rsi/icon.png b/Resources/Textures/WhiteDream/BloodCult/Entities/Clothes/Helmet/cult_helmet.rsi/icon.png new file mode 100644 index 00000000000..70f0fb7c84c Binary files /dev/null and b/Resources/Textures/WhiteDream/BloodCult/Entities/Clothes/Helmet/cult_helmet.rsi/icon.png differ diff --git a/Resources/Textures/WhiteDream/BloodCult/Entities/Clothes/Helmet/cult_helmet.rsi/meta.json b/Resources/Textures/WhiteDream/BloodCult/Entities/Clothes/Helmet/cult_helmet.rsi/meta.json new file mode 100644 index 00000000000..71a3cf96dbf --- /dev/null +++ b/Resources/Textures/WhiteDream/BloodCult/Entities/Clothes/Helmet/cult_helmet.rsi/meta.json @@ -0,0 +1,18 @@ +{ + "version": 1, + "license": "CC-BY-SA-3.0", + "copyright": "Taken from Bee Station at commit https://github.com/BeeStation/BeeStation-Hornet/commit/3050f5915f4aef410643be227510b9350350f7b2", + "size": { + "x": 32, + "y": 32 + }, + "states": [ + { + "name": "icon" + }, + { + "name": "equipped-HELMET", + "directions": 4 + } + ] +} diff --git a/Resources/Textures/WhiteDream/BloodCult/Entities/Clothes/Helmet/cult_hood.rsi/equipped-HELMET.png b/Resources/Textures/WhiteDream/BloodCult/Entities/Clothes/Helmet/cult_hood.rsi/equipped-HELMET.png new file mode 100644 index 00000000000..a2f0c42d0db Binary files /dev/null and b/Resources/Textures/WhiteDream/BloodCult/Entities/Clothes/Helmet/cult_hood.rsi/equipped-HELMET.png differ diff --git a/Resources/Textures/WhiteDream/BloodCult/Entities/Clothes/Helmet/cult_hood.rsi/icon.png b/Resources/Textures/WhiteDream/BloodCult/Entities/Clothes/Helmet/cult_hood.rsi/icon.png new file mode 100644 index 00000000000..fd548943746 Binary files /dev/null and b/Resources/Textures/WhiteDream/BloodCult/Entities/Clothes/Helmet/cult_hood.rsi/icon.png differ diff --git a/Resources/Textures/WhiteDream/BloodCult/Entities/Clothes/Helmet/cult_hood.rsi/meta.json b/Resources/Textures/WhiteDream/BloodCult/Entities/Clothes/Helmet/cult_hood.rsi/meta.json new file mode 100644 index 00000000000..71a3cf96dbf --- /dev/null +++ b/Resources/Textures/WhiteDream/BloodCult/Entities/Clothes/Helmet/cult_hood.rsi/meta.json @@ -0,0 +1,18 @@ +{ + "version": 1, + "license": "CC-BY-SA-3.0", + "copyright": "Taken from Bee Station at commit https://github.com/BeeStation/BeeStation-Hornet/commit/3050f5915f4aef410643be227510b9350350f7b2", + "size": { + "x": 32, + "y": 32 + }, + "states": [ + { + "name": "icon" + }, + { + "name": "equipped-HELMET", + "directions": 4 + } + ] +} diff --git a/Resources/Textures/WhiteDream/BloodCult/Entities/Clothes/Outer/cult_armor.rsi/equipped-OUTERCLOTHING.png b/Resources/Textures/WhiteDream/BloodCult/Entities/Clothes/Outer/cult_armor.rsi/equipped-OUTERCLOTHING.png new file mode 100644 index 00000000000..11d4097fbab Binary files /dev/null and b/Resources/Textures/WhiteDream/BloodCult/Entities/Clothes/Outer/cult_armor.rsi/equipped-OUTERCLOTHING.png differ diff --git a/Resources/Textures/WhiteDream/BloodCult/Entities/Clothes/Outer/cult_armor.rsi/icon.png b/Resources/Textures/WhiteDream/BloodCult/Entities/Clothes/Outer/cult_armor.rsi/icon.png new file mode 100644 index 00000000000..e2b2886b82e Binary files /dev/null and b/Resources/Textures/WhiteDream/BloodCult/Entities/Clothes/Outer/cult_armor.rsi/icon.png differ diff --git a/Resources/Textures/WhiteDream/BloodCult/Entities/Clothes/Outer/cult_armor.rsi/meta.json b/Resources/Textures/WhiteDream/BloodCult/Entities/Clothes/Outer/cult_armor.rsi/meta.json new file mode 100644 index 00000000000..2b040938ec5 --- /dev/null +++ b/Resources/Textures/WhiteDream/BloodCult/Entities/Clothes/Outer/cult_armor.rsi/meta.json @@ -0,0 +1,18 @@ +{ + "version": 1, + "license": "CC-BY-SA-3.0", + "copyright": "Taken from Bee Station at commit https://github.com/BeeStation/BeeStation-Hornet/commit/3050f5915f4aef410643be227510b9350350f7b2", + "size": { + "x": 32, + "y": 32 + }, + "states": [ + { + "name": "icon" + }, + { + "name": "equipped-OUTERCLOTHING", + "directions": 4 + } + ] +} diff --git a/Resources/Textures/WhiteDream/BloodCult/Entities/Clothes/Outer/cult_robe.rsi/equipped-OUTERCLOTHING.png b/Resources/Textures/WhiteDream/BloodCult/Entities/Clothes/Outer/cult_robe.rsi/equipped-OUTERCLOTHING.png new file mode 100644 index 00000000000..115565d3ec5 Binary files /dev/null and b/Resources/Textures/WhiteDream/BloodCult/Entities/Clothes/Outer/cult_robe.rsi/equipped-OUTERCLOTHING.png differ diff --git a/Resources/Textures/WhiteDream/BloodCult/Entities/Clothes/Outer/cult_robe.rsi/icon.png b/Resources/Textures/WhiteDream/BloodCult/Entities/Clothes/Outer/cult_robe.rsi/icon.png new file mode 100644 index 00000000000..8fe50a3572c Binary files /dev/null and b/Resources/Textures/WhiteDream/BloodCult/Entities/Clothes/Outer/cult_robe.rsi/icon.png differ diff --git a/Resources/Textures/WhiteDream/BloodCult/Entities/Clothes/Outer/cult_robe.rsi/meta.json b/Resources/Textures/WhiteDream/BloodCult/Entities/Clothes/Outer/cult_robe.rsi/meta.json new file mode 100644 index 00000000000..2b040938ec5 --- /dev/null +++ b/Resources/Textures/WhiteDream/BloodCult/Entities/Clothes/Outer/cult_robe.rsi/meta.json @@ -0,0 +1,18 @@ +{ + "version": 1, + "license": "CC-BY-SA-3.0", + "copyright": "Taken from Bee Station at commit https://github.com/BeeStation/BeeStation-Hornet/commit/3050f5915f4aef410643be227510b9350350f7b2", + "size": { + "x": 32, + "y": 32 + }, + "states": [ + { + "name": "icon" + }, + { + "name": "equipped-OUTERCLOTHING", + "directions": 4 + } + ] +} diff --git a/Resources/Textures/WhiteDream/BloodCult/Entities/Items/Weapons/cult_blade.rsi/icon.png b/Resources/Textures/WhiteDream/BloodCult/Entities/Items/Weapons/cult_blade.rsi/icon.png new file mode 100644 index 00000000000..38408cb9987 Binary files /dev/null and b/Resources/Textures/WhiteDream/BloodCult/Entities/Items/Weapons/cult_blade.rsi/icon.png differ diff --git a/Resources/Textures/WhiteDream/BloodCult/Entities/Items/Weapons/cult_blade.rsi/inhand-left.png b/Resources/Textures/WhiteDream/BloodCult/Entities/Items/Weapons/cult_blade.rsi/inhand-left.png new file mode 100644 index 00000000000..2716516dcec Binary files /dev/null and b/Resources/Textures/WhiteDream/BloodCult/Entities/Items/Weapons/cult_blade.rsi/inhand-left.png differ diff --git a/Resources/Textures/WhiteDream/BloodCult/Entities/Items/Weapons/cult_blade.rsi/inhand-right.png b/Resources/Textures/WhiteDream/BloodCult/Entities/Items/Weapons/cult_blade.rsi/inhand-right.png new file mode 100644 index 00000000000..48435b8fa13 Binary files /dev/null and b/Resources/Textures/WhiteDream/BloodCult/Entities/Items/Weapons/cult_blade.rsi/inhand-right.png differ diff --git a/Resources/Textures/WhiteDream/BloodCult/Entities/Items/Weapons/cult_blade.rsi/meta.json b/Resources/Textures/WhiteDream/BloodCult/Entities/Items/Weapons/cult_blade.rsi/meta.json new file mode 100644 index 00000000000..9b604947e2f --- /dev/null +++ b/Resources/Textures/WhiteDream/BloodCult/Entities/Items/Weapons/cult_blade.rsi/meta.json @@ -0,0 +1,72 @@ +{ + "version": 1, + "license": "CC-BY-SA-3.0", + "copyright": "Taken from BeeStation at https://github.com/BeeStation/BeeStation-Hornet/commit/e5b645f1622f5b9186ad4c11feccbc75b3cf7e84", + "size": { + "x": 32, + "y": 32 + }, + "states": [ + { + "name": "icon", + "delays": [ + [ + 0.1, + 0.1, + 0.1, + 0.1, + 0.1, + 0.1, + 0.1, + 0.1, + 0.1, + 0.1 + ] + ] + }, + { + "name": "inhand-left", + "directions": 4, + "delays": [ + [ + 0.3, + 0.3 + ], + [ + 0.3, + 0.3 + ], + [ + 0.3, + 0.3 + ], + [ + 0.3, + 0.3 + ] + ] + }, + { + "name": "inhand-right", + "directions": 4, + "delays": [ + [ + 0.3, + 0.3 + ], + [ + 0.3, + 0.3 + ], + [ + 0.3, + 0.3 + ], + [ + 0.3, + 0.3 + ] + ] + } + ] +} diff --git a/Resources/Textures/WhiteDream/BloodCult/Entities/Items/Weapons/cult_spear.rsi/icon.png b/Resources/Textures/WhiteDream/BloodCult/Entities/Items/Weapons/cult_spear.rsi/icon.png new file mode 100644 index 00000000000..53ac6575fef Binary files /dev/null and b/Resources/Textures/WhiteDream/BloodCult/Entities/Items/Weapons/cult_spear.rsi/icon.png differ diff --git a/Resources/Textures/WhiteDream/BloodCult/Entities/Items/Weapons/cult_spear.rsi/inhand-left.png b/Resources/Textures/WhiteDream/BloodCult/Entities/Items/Weapons/cult_spear.rsi/inhand-left.png new file mode 100644 index 00000000000..f4372ccb856 Binary files /dev/null and b/Resources/Textures/WhiteDream/BloodCult/Entities/Items/Weapons/cult_spear.rsi/inhand-left.png differ diff --git a/Resources/Textures/WhiteDream/BloodCult/Entities/Items/Weapons/cult_spear.rsi/inhand-right.png b/Resources/Textures/WhiteDream/BloodCult/Entities/Items/Weapons/cult_spear.rsi/inhand-right.png new file mode 100644 index 00000000000..b44bebbb3c4 Binary files /dev/null and b/Resources/Textures/WhiteDream/BloodCult/Entities/Items/Weapons/cult_spear.rsi/inhand-right.png differ diff --git a/Resources/Textures/WhiteDream/BloodCult/Entities/Items/Weapons/cult_spear.rsi/meta.json b/Resources/Textures/WhiteDream/BloodCult/Entities/Items/Weapons/cult_spear.rsi/meta.json new file mode 100644 index 00000000000..468770e3d56 --- /dev/null +++ b/Resources/Textures/WhiteDream/BloodCult/Entities/Items/Weapons/cult_spear.rsi/meta.json @@ -0,0 +1,30 @@ +{ + "version": 1, + "license": "CC-BY-SA-3.0", + "copyright": "Taken from BeeStation at https://github.com/BeeStation/BeeStation-Hornet/commit/e5b645f1622f5b9186ad4c11feccbc75b3cf7e84", + "size": { + "x": 32, + "y": 32 + }, + "states": [ + { + "name": "icon" + }, + { + "name": "inhand-left", + "directions": 4 + }, + { + "name": "inhand-right", + "directions": 4 + }, + { + "name": "wielded-inhand-left", + "directions": 4 + }, + { + "name": "wielded-inhand-right", + "directions": 4 + } + ] +} diff --git a/Resources/Textures/WhiteDream/BloodCult/Entities/Items/Weapons/cult_spear.rsi/wielded-inhand-left.png b/Resources/Textures/WhiteDream/BloodCult/Entities/Items/Weapons/cult_spear.rsi/wielded-inhand-left.png new file mode 100644 index 00000000000..4532c1e661e Binary files /dev/null and b/Resources/Textures/WhiteDream/BloodCult/Entities/Items/Weapons/cult_spear.rsi/wielded-inhand-left.png differ diff --git a/Resources/Textures/WhiteDream/BloodCult/Entities/Items/Weapons/cult_spear.rsi/wielded-inhand-right.png b/Resources/Textures/WhiteDream/BloodCult/Entities/Items/Weapons/cult_spear.rsi/wielded-inhand-right.png new file mode 100644 index 00000000000..4532c1e661e Binary files /dev/null and b/Resources/Textures/WhiteDream/BloodCult/Entities/Items/Weapons/cult_spear.rsi/wielded-inhand-right.png differ diff --git a/Resources/Textures/WhiteDream/BloodCult/Entities/Items/arcane_barrage.rsi/bullet.png b/Resources/Textures/WhiteDream/BloodCult/Entities/Items/arcane_barrage.rsi/bullet.png new file mode 100644 index 00000000000..f1184e4e7b9 Binary files /dev/null and b/Resources/Textures/WhiteDream/BloodCult/Entities/Items/arcane_barrage.rsi/bullet.png differ diff --git a/Resources/Textures/WhiteDream/BloodCult/Entities/Items/arcane_barrage.rsi/icon.png b/Resources/Textures/WhiteDream/BloodCult/Entities/Items/arcane_barrage.rsi/icon.png new file mode 100644 index 00000000000..678418769a2 Binary files /dev/null and b/Resources/Textures/WhiteDream/BloodCult/Entities/Items/arcane_barrage.rsi/icon.png differ diff --git a/Resources/Textures/WhiteDream/BloodCult/Entities/Items/arcane_barrage.rsi/inhand-left.png b/Resources/Textures/WhiteDream/BloodCult/Entities/Items/arcane_barrage.rsi/inhand-left.png new file mode 100644 index 00000000000..5aa69c6f0ce Binary files /dev/null and b/Resources/Textures/WhiteDream/BloodCult/Entities/Items/arcane_barrage.rsi/inhand-left.png differ diff --git a/Resources/Textures/WhiteDream/BloodCult/Entities/Items/arcane_barrage.rsi/inhand-right.png b/Resources/Textures/WhiteDream/BloodCult/Entities/Items/arcane_barrage.rsi/inhand-right.png new file mode 100644 index 00000000000..72d7a44ff77 Binary files /dev/null and b/Resources/Textures/WhiteDream/BloodCult/Entities/Items/arcane_barrage.rsi/inhand-right.png differ diff --git a/Resources/Textures/WhiteDream/BloodCult/Entities/Items/arcane_barrage.rsi/meta.json b/Resources/Textures/WhiteDream/BloodCult/Entities/Items/arcane_barrage.rsi/meta.json new file mode 100644 index 00000000000..39fae95edd9 --- /dev/null +++ b/Resources/Textures/WhiteDream/BloodCult/Entities/Items/arcane_barrage.rsi/meta.json @@ -0,0 +1,77 @@ +{ + "version": 1, + "license": "CC-BY-SA-3.0", + "copyright": "Taken from tgstation", + "size": { + "x": 32, + "y": 32 + }, + "states": [ + { + "name": "icon" + }, + { + "name": "inhand-left", + "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 + ] + ] + }, + { + "name": "inhand-right", + "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 + ] + ] + }, + { + "name": "bullet", + "delays": [ + [ + 0.2, + 0.2, + 0.2, + 0.2 + ] + ] + } + ] +} diff --git a/Resources/Textures/WhiteDream/BloodCult/Entities/Items/bola.rsi/icon.png b/Resources/Textures/WhiteDream/BloodCult/Entities/Items/bola.rsi/icon.png new file mode 100644 index 00000000000..a0a1a792215 Binary files /dev/null and b/Resources/Textures/WhiteDream/BloodCult/Entities/Items/bola.rsi/icon.png differ diff --git a/Resources/Textures/WhiteDream/BloodCult/Entities/Items/bola.rsi/meta.json b/Resources/Textures/WhiteDream/BloodCult/Entities/Items/bola.rsi/meta.json new file mode 100644 index 00000000000..9efc29f5850 --- /dev/null +++ b/Resources/Textures/WhiteDream/BloodCult/Entities/Items/bola.rsi/meta.json @@ -0,0 +1,14 @@ +{ + "version": 1, + "license": "CC-BY-SA-3.0", + "copyright": "Taken from Citadel Station at https://github.com/Citadel-Station-13/Citadel-Station-13/commit/3cfea7eb92246d311de8b531347795bc76d6dab6", + "size": { + "x": 32, + "y": 32 + }, + "states": [ + { + "name": "icon" + } + ] +} diff --git a/Resources/Textures/WhiteDream/BloodCult/Entities/Items/construct_shell.rsi/icon.png b/Resources/Textures/WhiteDream/BloodCult/Entities/Items/construct_shell.rsi/icon.png new file mode 100644 index 00000000000..e215e9b9977 Binary files /dev/null and b/Resources/Textures/WhiteDream/BloodCult/Entities/Items/construct_shell.rsi/icon.png differ diff --git a/Resources/Textures/WhiteDream/BloodCult/Entities/Items/construct_shell.rsi/meta.json b/Resources/Textures/WhiteDream/BloodCult/Entities/Items/construct_shell.rsi/meta.json new file mode 100644 index 00000000000..97fb7e5f348 --- /dev/null +++ b/Resources/Textures/WhiteDream/BloodCult/Entities/Items/construct_shell.rsi/meta.json @@ -0,0 +1,22 @@ +{ + "version": 1, + "license": "CC-BY-SA-3.0", + "copyright": "Taken from TG station 13", + "size": { + "x": 32, + "y": 32 + }, + "states": [ + { + "name": "icon", + "delays": [ + [ + 0.5, + 0.5, + 0.5, + 0.5 + ] + ] + } + ] +} diff --git a/Resources/Textures/WhiteDream/BloodCult/Entities/Items/mirror_shield.rsi/icon.png b/Resources/Textures/WhiteDream/BloodCult/Entities/Items/mirror_shield.rsi/icon.png new file mode 100644 index 00000000000..22ff0de0716 Binary files /dev/null and b/Resources/Textures/WhiteDream/BloodCult/Entities/Items/mirror_shield.rsi/icon.png differ diff --git a/Resources/Textures/WhiteDream/BloodCult/Entities/Items/mirror_shield.rsi/meta.json b/Resources/Textures/WhiteDream/BloodCult/Entities/Items/mirror_shield.rsi/meta.json new file mode 100644 index 00000000000..244e0b78906 --- /dev/null +++ b/Resources/Textures/WhiteDream/BloodCult/Entities/Items/mirror_shield.rsi/meta.json @@ -0,0 +1,82 @@ +{ + "version": 1, + "license": "CC-BY-SA-3.0", + "copyright": "Taken from TG station 13", + "size": { + "x": 32, + "y": 32 + }, + "states": [ + { + "name": "icon", + "delays": [ + [ + 0.5, + 0.05, + 0.1, + 0.1 + ] + ] + }, + { + "name": "mirror-inhand-left", + "directions": 4, + "delays": [ + [ + 0.5, + 0.05, + 0.1, + 0.1 + ], + [ + 0.5, + 0.05, + 0.1, + 0.1 + ], + [ + 0.5, + 0.05, + 0.1, + 0.1 + ], + [ + 0.5, + 0.05, + 0.1, + 0.1 + ] + ] + }, + { + "name": "mirror-inhand-right", + "directions": 4, + "delays": [ + [ + 0.5, + 0.05, + 0.1, + 0.1 + ], + [ + 0.5, + 0.05, + 0.1, + 0.1 + ], + [ + 0.5, + 0.05, + 0.1, + 0.1 + ], + [ + 0.5, + 0.05, + 0.1, + 0.1 + ] + ] + } + ] +} diff --git a/Resources/Textures/WhiteDream/BloodCult/Entities/Items/mirror_shield.rsi/mirror-inhand-left.png b/Resources/Textures/WhiteDream/BloodCult/Entities/Items/mirror_shield.rsi/mirror-inhand-left.png new file mode 100644 index 00000000000..23e00c0734c Binary files /dev/null and b/Resources/Textures/WhiteDream/BloodCult/Entities/Items/mirror_shield.rsi/mirror-inhand-left.png differ diff --git a/Resources/Textures/WhiteDream/BloodCult/Entities/Items/mirror_shield.rsi/mirror-inhand-right.png b/Resources/Textures/WhiteDream/BloodCult/Entities/Items/mirror_shield.rsi/mirror-inhand-right.png new file mode 100644 index 00000000000..0ce6509226b Binary files /dev/null and b/Resources/Textures/WhiteDream/BloodCult/Entities/Items/mirror_shield.rsi/mirror-inhand-right.png differ diff --git a/Resources/Textures/WhiteDream/BloodCult/Entities/Items/rites.rsi/icon.png b/Resources/Textures/WhiteDream/BloodCult/Entities/Items/rites.rsi/icon.png new file mode 100644 index 00000000000..e7df415ac85 Binary files /dev/null and b/Resources/Textures/WhiteDream/BloodCult/Entities/Items/rites.rsi/icon.png differ diff --git a/Resources/Textures/WhiteDream/BloodCult/Entities/Items/rites.rsi/inhand-left.png b/Resources/Textures/WhiteDream/BloodCult/Entities/Items/rites.rsi/inhand-left.png new file mode 100644 index 00000000000..516c310227c Binary files /dev/null and b/Resources/Textures/WhiteDream/BloodCult/Entities/Items/rites.rsi/inhand-left.png differ diff --git a/Resources/Textures/WhiteDream/BloodCult/Entities/Items/rites.rsi/inhand-right.png b/Resources/Textures/WhiteDream/BloodCult/Entities/Items/rites.rsi/inhand-right.png new file mode 100644 index 00000000000..a159a468a0a Binary files /dev/null and b/Resources/Textures/WhiteDream/BloodCult/Entities/Items/rites.rsi/inhand-right.png differ diff --git a/Resources/Textures/WhiteDream/BloodCult/Entities/Items/rites.rsi/meta.json b/Resources/Textures/WhiteDream/BloodCult/Entities/Items/rites.rsi/meta.json new file mode 100644 index 00000000000..489466f74a8 --- /dev/null +++ b/Resources/Textures/WhiteDream/BloodCult/Entities/Items/rites.rsi/meta.json @@ -0,0 +1,66 @@ +{ + "version": 1, + "license": "CC-BY-SA-3.0", + "copyright": "Taken from tgstation at https://github.com/tgstation/tgstation/pull/49264/commits/d0dffe7ca643db2624424fdcebf45863f85c0448", + "size": { + "x": 32, + "y": 32 + }, + "states": [ + { + "name": "icon" + }, + { + "name": "inhand-left", + "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 + ] + ] + }, + { + "name": "inhand-right", + "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 + ] + ] + } + ] +} diff --git a/Resources/Textures/WhiteDream/BloodCult/Entities/Items/shuttle_curse.rsi/icon.png b/Resources/Textures/WhiteDream/BloodCult/Entities/Items/shuttle_curse.rsi/icon.png new file mode 100644 index 00000000000..526af8f2add Binary files /dev/null and b/Resources/Textures/WhiteDream/BloodCult/Entities/Items/shuttle_curse.rsi/icon.png differ diff --git a/Resources/Textures/WhiteDream/BloodCult/Entities/Items/shuttle_curse.rsi/meta.json b/Resources/Textures/WhiteDream/BloodCult/Entities/Items/shuttle_curse.rsi/meta.json new file mode 100644 index 00000000000..1568be4e9f5 --- /dev/null +++ b/Resources/Textures/WhiteDream/BloodCult/Entities/Items/shuttle_curse.rsi/meta.json @@ -0,0 +1,25 @@ +{ + "version": 1, + "license": "CC-BY-SA-3.0", + "copyright": "Taken from BeeStation at https://github.com/BeeStation/BeeStation-Hornet/commit/e5b645f1622f5b9186ad4c11feccbc75b3cf7e84", + "size": { + "x": 32, + "y": 32 + }, + "states": [ + { + "name": "icon", + "delays": [ + [ + 0.1, + 0.1, + 0.1, + 0.1, + 0.1, + 0.1, + 0.1 + ] + ] + } + ] +} diff --git a/Resources/Textures/WhiteDream/BloodCult/Entities/Items/soul_stone.rsi/meta.json b/Resources/Textures/WhiteDream/BloodCult/Entities/Items/soul_stone.rsi/meta.json new file mode 100644 index 00000000000..82b84570c31 --- /dev/null +++ b/Resources/Textures/WhiteDream/BloodCult/Entities/Items/soul_stone.rsi/meta.json @@ -0,0 +1,27 @@ +{ + "license": "CC-BY-SA-3.0", + "copyright": "Taken from TG station 13", + "version": 1, + "size": { + "x": 32, + "y": 32 + }, + "states": [ + { + "name": "soul_stone" + }, + { + "name": "soul_stone_blessed" + }, + { + "name": "soul_stone_glow", + "delays": [ + [ + 0.1, + 0.1, + 0.1 + ] + ] + } + ] +} diff --git a/Resources/Textures/WhiteDream/BloodCult/Entities/Items/soul_stone.rsi/soul_stone.png b/Resources/Textures/WhiteDream/BloodCult/Entities/Items/soul_stone.rsi/soul_stone.png new file mode 100644 index 00000000000..a765aa699c7 Binary files /dev/null and b/Resources/Textures/WhiteDream/BloodCult/Entities/Items/soul_stone.rsi/soul_stone.png differ diff --git a/Resources/Textures/WhiteDream/BloodCult/Entities/Items/soul_stone.rsi/soul_stone_blessed.png b/Resources/Textures/WhiteDream/BloodCult/Entities/Items/soul_stone.rsi/soul_stone_blessed.png new file mode 100644 index 00000000000..1dee5e3fdca Binary files /dev/null and b/Resources/Textures/WhiteDream/BloodCult/Entities/Items/soul_stone.rsi/soul_stone_blessed.png differ diff --git a/Resources/Textures/WhiteDream/BloodCult/Entities/Items/soul_stone.rsi/soul_stone_glow.png b/Resources/Textures/WhiteDream/BloodCult/Entities/Items/soul_stone.rsi/soul_stone_glow.png new file mode 100644 index 00000000000..54c53e88f42 Binary files /dev/null and b/Resources/Textures/WhiteDream/BloodCult/Entities/Items/soul_stone.rsi/soul_stone_glow.png differ diff --git a/Resources/Textures/WhiteDream/BloodCult/Entities/Items/veil_shifter.rsi/icon.png b/Resources/Textures/WhiteDream/BloodCult/Entities/Items/veil_shifter.rsi/icon.png new file mode 100644 index 00000000000..71b703ba7e6 Binary files /dev/null and b/Resources/Textures/WhiteDream/BloodCult/Entities/Items/veil_shifter.rsi/icon.png differ diff --git a/Resources/Textures/WhiteDream/BloodCult/Entities/Items/veil_shifter.rsi/icon_off.png b/Resources/Textures/WhiteDream/BloodCult/Entities/Items/veil_shifter.rsi/icon_off.png new file mode 100644 index 00000000000..371f6205a62 Binary files /dev/null and b/Resources/Textures/WhiteDream/BloodCult/Entities/Items/veil_shifter.rsi/icon_off.png differ diff --git a/Resources/Textures/WhiteDream/BloodCult/Entities/Items/veil_shifter.rsi/meta.json b/Resources/Textures/WhiteDream/BloodCult/Entities/Items/veil_shifter.rsi/meta.json new file mode 100644 index 00000000000..b446b7060c7 --- /dev/null +++ b/Resources/Textures/WhiteDream/BloodCult/Entities/Items/veil_shifter.rsi/meta.json @@ -0,0 +1,29 @@ +{ + "version": 1, + "license": "CC-BY-SA-3.0", + "copyright": "Taken from BeeStation at https://github.com/BeeStation/BeeStation-Hornet/commit/e5b645f1622f5b9186ad4c11feccbc75b3cf7e84", + "size": { + "x": 32, + "y": 32 + }, + "states": [ + { + "name": "icon", + "delays": [ + [ + 0.1, + 0.1, + 0.1, + 0.1, + 0.1, + 0.1, + 0.1, + 0.1 + ] + ] + }, + { + "name": "icon_off" + } + ] +} diff --git a/Resources/Textures/WhiteDream/BloodCult/Entities/Items/void_torch.rsi/icon.png b/Resources/Textures/WhiteDream/BloodCult/Entities/Items/void_torch.rsi/icon.png new file mode 100644 index 00000000000..3040ae6a832 Binary files /dev/null and b/Resources/Textures/WhiteDream/BloodCult/Entities/Items/void_torch.rsi/icon.png differ diff --git a/Resources/Textures/WhiteDream/BloodCult/Entities/Items/void_torch.rsi/icon_off.png b/Resources/Textures/WhiteDream/BloodCult/Entities/Items/void_torch.rsi/icon_off.png new file mode 100644 index 00000000000..6cfbbb737e4 Binary files /dev/null and b/Resources/Textures/WhiteDream/BloodCult/Entities/Items/void_torch.rsi/icon_off.png differ diff --git a/Resources/Textures/WhiteDream/BloodCult/Entities/Items/void_torch.rsi/lit-inhand-left.png b/Resources/Textures/WhiteDream/BloodCult/Entities/Items/void_torch.rsi/lit-inhand-left.png new file mode 100644 index 00000000000..882a7668e57 Binary files /dev/null and b/Resources/Textures/WhiteDream/BloodCult/Entities/Items/void_torch.rsi/lit-inhand-left.png differ diff --git a/Resources/Textures/WhiteDream/BloodCult/Entities/Items/void_torch.rsi/lit-inhand-right.png b/Resources/Textures/WhiteDream/BloodCult/Entities/Items/void_torch.rsi/lit-inhand-right.png new file mode 100644 index 00000000000..c53f54968fc Binary files /dev/null and b/Resources/Textures/WhiteDream/BloodCult/Entities/Items/void_torch.rsi/lit-inhand-right.png differ diff --git a/Resources/Textures/WhiteDream/BloodCult/Entities/Items/void_torch.rsi/meta.json b/Resources/Textures/WhiteDream/BloodCult/Entities/Items/void_torch.rsi/meta.json new file mode 100644 index 00000000000..72c4fd00be7 --- /dev/null +++ b/Resources/Textures/WhiteDream/BloodCult/Entities/Items/void_torch.rsi/meta.json @@ -0,0 +1,95 @@ +{ + "version": 1, + "license": "CC-BY-SA-3.0", + "copyright": "Taken from TG station 13", + "size": { + "x": 32, + "y": 32 + }, + "states": [ + { + "name": "icon", + "delays": [ + [ + 0.1, + 0.1, + 0.1, + 0.1, + 0.1, + 0.1 + ] + ] + }, + { + "name": "icon_off" + }, + { + "name": "lit-inhand-left", + "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 + ] + ] + }, + { + "name": "lit-inhand-right", + "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 + ] + ] + }, + { + "name": "unlit-inhand-left", + "directions": 4 + }, + { + "name": "unlit-inhand-right", + "directions": 4 + } + ] +} diff --git a/Resources/Textures/WhiteDream/BloodCult/Entities/Items/void_torch.rsi/unlit-inhand-left.png b/Resources/Textures/WhiteDream/BloodCult/Entities/Items/void_torch.rsi/unlit-inhand-left.png new file mode 100644 index 00000000000..81048fc33f0 Binary files /dev/null and b/Resources/Textures/WhiteDream/BloodCult/Entities/Items/void_torch.rsi/unlit-inhand-left.png differ diff --git a/Resources/Textures/WhiteDream/BloodCult/Entities/Items/void_torch.rsi/unlit-inhand-right.png b/Resources/Textures/WhiteDream/BloodCult/Entities/Items/void_torch.rsi/unlit-inhand-right.png new file mode 100644 index 00000000000..d8a3016072f Binary files /dev/null and b/Resources/Textures/WhiteDream/BloodCult/Entities/Items/void_torch.rsi/unlit-inhand-right.png differ diff --git a/Resources/Textures/WhiteDream/BloodCult/Entities/Items/whetstone_cult.rsi/icon.png b/Resources/Textures/WhiteDream/BloodCult/Entities/Items/whetstone_cult.rsi/icon.png new file mode 100644 index 00000000000..d7ef2e3af4d Binary files /dev/null and b/Resources/Textures/WhiteDream/BloodCult/Entities/Items/whetstone_cult.rsi/icon.png differ diff --git a/Resources/Textures/WhiteDream/BloodCult/Entities/Items/whetstone_cult.rsi/icon_off.png b/Resources/Textures/WhiteDream/BloodCult/Entities/Items/whetstone_cult.rsi/icon_off.png new file mode 100644 index 00000000000..9e5ff36441a Binary files /dev/null and b/Resources/Textures/WhiteDream/BloodCult/Entities/Items/whetstone_cult.rsi/icon_off.png differ diff --git a/Resources/Textures/WhiteDream/BloodCult/Entities/Items/whetstone_cult.rsi/meta.json b/Resources/Textures/WhiteDream/BloodCult/Entities/Items/whetstone_cult.rsi/meta.json new file mode 100644 index 00000000000..6753c5926f8 --- /dev/null +++ b/Resources/Textures/WhiteDream/BloodCult/Entities/Items/whetstone_cult.rsi/meta.json @@ -0,0 +1,17 @@ +{ + "version": 1, + "license": "CC-BY-SA-3.0", + "copyright": "Taken by TG station at commit https://github.com/tgstation/tgstation/commit/4eaa299c0b20ae8629910a6a25be4be9d58a559e", + "size": { + "x": 32, + "y": 32 + }, + "states": [ + { + "name": "icon" + }, + { + "name": "icon_off" + } + ] +} diff --git a/Resources/Textures/WhiteDream/BloodCult/Entities/Runes/apocalypse.rsi/icon.png b/Resources/Textures/WhiteDream/BloodCult/Entities/Runes/apocalypse.rsi/icon.png new file mode 100644 index 00000000000..af7ac8970ca Binary files /dev/null and b/Resources/Textures/WhiteDream/BloodCult/Entities/Runes/apocalypse.rsi/icon.png differ diff --git a/Resources/Textures/WhiteDream/BloodCult/Entities/Runes/apocalypse.rsi/meta.json b/Resources/Textures/WhiteDream/BloodCult/Entities/Runes/apocalypse.rsi/meta.json new file mode 100644 index 00000000000..129ae13cafc --- /dev/null +++ b/Resources/Textures/WhiteDream/BloodCult/Entities/Runes/apocalypse.rsi/meta.json @@ -0,0 +1,14 @@ +{ + "license": "CC-BY-SA-3.0", + "copyright": "Taken from TG station 13, edited by @kilath (discord 493110710377906196)", + "version": 1, + "size": { + "x": 96, + "y": 96 + }, + "states": [ + { + "name": "icon" + } + ] +} diff --git a/Resources/Textures/WhiteDream/BloodCult/Entities/Runes/dimensional_rending.rsi/meta.json b/Resources/Textures/WhiteDream/BloodCult/Entities/Runes/dimensional_rending.rsi/meta.json new file mode 100644 index 00000000000..18c2bb1ce40 --- /dev/null +++ b/Resources/Textures/WhiteDream/BloodCult/Entities/Runes/dimensional_rending.rsi/meta.json @@ -0,0 +1,29 @@ +{ + "version": 1, + "license": "CC-BY-SA-3.0", + "copyright": "Taken from Paradise Station 13, edited by @kilath (discord 493110710377906196)", + "size": { + "x": 96, + "y": 96 + }, + "states": [ + { + "name": "rune" + }, + { + "name": "rune_animated", + "delays": [ + [ + 0.50, + 0.07, + 0.07, + 0.07, + 0.07, + 0.07, + 0.07, + 0.07 + ] + ] + } + ] +} diff --git a/Resources/Textures/WhiteDream/BloodCult/Entities/Runes/dimensional_rending.rsi/rune.png b/Resources/Textures/WhiteDream/BloodCult/Entities/Runes/dimensional_rending.rsi/rune.png new file mode 100644 index 00000000000..db04c0731b2 Binary files /dev/null and b/Resources/Textures/WhiteDream/BloodCult/Entities/Runes/dimensional_rending.rsi/rune.png differ diff --git a/Resources/Textures/WhiteDream/BloodCult/Entities/Runes/dimensional_rending.rsi/rune_animated.png b/Resources/Textures/WhiteDream/BloodCult/Entities/Runes/dimensional_rending.rsi/rune_animated.png new file mode 100644 index 00000000000..f87055f639e Binary files /dev/null and b/Resources/Textures/WhiteDream/BloodCult/Entities/Runes/dimensional_rending.rsi/rune_animated.png differ diff --git a/Resources/Textures/WhiteDream/BloodCult/Entities/Runes/regular.rsi/barrier.png b/Resources/Textures/WhiteDream/BloodCult/Entities/Runes/regular.rsi/barrier.png new file mode 100644 index 00000000000..64d657bba05 Binary files /dev/null and b/Resources/Textures/WhiteDream/BloodCult/Entities/Runes/regular.rsi/barrier.png differ diff --git a/Resources/Textures/WhiteDream/BloodCult/Entities/Runes/regular.rsi/blood_boil.png b/Resources/Textures/WhiteDream/BloodCult/Entities/Runes/regular.rsi/blood_boil.png new file mode 100644 index 00000000000..0449c5d2f28 Binary files /dev/null and b/Resources/Textures/WhiteDream/BloodCult/Entities/Runes/regular.rsi/blood_boil.png differ diff --git a/Resources/Textures/WhiteDream/BloodCult/Entities/Runes/regular.rsi/empower.png b/Resources/Textures/WhiteDream/BloodCult/Entities/Runes/regular.rsi/empower.png new file mode 100644 index 00000000000..e0e0de69d3e Binary files /dev/null and b/Resources/Textures/WhiteDream/BloodCult/Entities/Runes/regular.rsi/empower.png differ diff --git a/Resources/Textures/WhiteDream/BloodCult/Entities/Runes/regular.rsi/meta.json b/Resources/Textures/WhiteDream/BloodCult/Entities/Runes/regular.rsi/meta.json new file mode 100644 index 00000000000..95ec518eb0b --- /dev/null +++ b/Resources/Textures/WhiteDream/BloodCult/Entities/Runes/regular.rsi/meta.json @@ -0,0 +1,38 @@ +{ + "license": "CC-BY-SA-3.0", + "copyright": "Taken from Paradise Station 13, edited by @kilath (discord 493110710377906196)", + "version": 1, + "size": { + "x": 32, + "y": 32 + }, + "states": [ + { + "name": "barrier" + }, + { + "name": "blood_boil" + }, + { + "name": "empower" + }, + { + "name": "offering" + }, + { + "name": "revive" + }, + { + "name": "spirit_realm" + }, + { + "name": "strength" + }, + { + "name": "summon" + }, + { + "name": "teleport" + } + ] +} diff --git a/Resources/Textures/WhiteDream/BloodCult/Entities/Runes/regular.rsi/offering.png b/Resources/Textures/WhiteDream/BloodCult/Entities/Runes/regular.rsi/offering.png new file mode 100644 index 00000000000..505738e31e3 Binary files /dev/null and b/Resources/Textures/WhiteDream/BloodCult/Entities/Runes/regular.rsi/offering.png differ diff --git a/Resources/Textures/WhiteDream/BloodCult/Entities/Runes/regular.rsi/revive.png b/Resources/Textures/WhiteDream/BloodCult/Entities/Runes/regular.rsi/revive.png new file mode 100644 index 00000000000..16be77759ef Binary files /dev/null and b/Resources/Textures/WhiteDream/BloodCult/Entities/Runes/regular.rsi/revive.png differ diff --git a/Resources/Textures/WhiteDream/BloodCult/Entities/Runes/regular.rsi/spirit_realm.png b/Resources/Textures/WhiteDream/BloodCult/Entities/Runes/regular.rsi/spirit_realm.png new file mode 100644 index 00000000000..e28bf1ad97d Binary files /dev/null and b/Resources/Textures/WhiteDream/BloodCult/Entities/Runes/regular.rsi/spirit_realm.png differ diff --git a/Resources/Textures/WhiteDream/BloodCult/Entities/Runes/regular.rsi/strength.png b/Resources/Textures/WhiteDream/BloodCult/Entities/Runes/regular.rsi/strength.png new file mode 100644 index 00000000000..973aa352fff Binary files /dev/null and b/Resources/Textures/WhiteDream/BloodCult/Entities/Runes/regular.rsi/strength.png differ diff --git a/Resources/Textures/WhiteDream/BloodCult/Entities/Runes/regular.rsi/summon.png b/Resources/Textures/WhiteDream/BloodCult/Entities/Runes/regular.rsi/summon.png new file mode 100644 index 00000000000..dd8a82e6f9f Binary files /dev/null and b/Resources/Textures/WhiteDream/BloodCult/Entities/Runes/regular.rsi/summon.png differ diff --git a/Resources/Textures/WhiteDream/BloodCult/Entities/Runes/regular.rsi/teleport.png b/Resources/Textures/WhiteDream/BloodCult/Entities/Runes/regular.rsi/teleport.png new file mode 100644 index 00000000000..96a9970e876 Binary files /dev/null and b/Resources/Textures/WhiteDream/BloodCult/Entities/Runes/regular.rsi/teleport.png differ diff --git a/Resources/Textures/WhiteDream/BloodCult/Entities/Structures/Concealed/cult.rsi/cult0.png b/Resources/Textures/WhiteDream/BloodCult/Entities/Structures/Concealed/cult.rsi/cult0.png new file mode 100644 index 00000000000..3849504b6ff Binary files /dev/null and b/Resources/Textures/WhiteDream/BloodCult/Entities/Structures/Concealed/cult.rsi/cult0.png differ diff --git a/Resources/Textures/WhiteDream/BloodCult/Entities/Structures/Concealed/cult.rsi/cult1.png b/Resources/Textures/WhiteDream/BloodCult/Entities/Structures/Concealed/cult.rsi/cult1.png new file mode 100644 index 00000000000..9ac2271692b Binary files /dev/null and b/Resources/Textures/WhiteDream/BloodCult/Entities/Structures/Concealed/cult.rsi/cult1.png differ diff --git a/Resources/Textures/WhiteDream/BloodCult/Entities/Structures/Concealed/cult.rsi/cult2.png b/Resources/Textures/WhiteDream/BloodCult/Entities/Structures/Concealed/cult.rsi/cult2.png new file mode 100644 index 00000000000..3849504b6ff Binary files /dev/null and b/Resources/Textures/WhiteDream/BloodCult/Entities/Structures/Concealed/cult.rsi/cult2.png differ diff --git a/Resources/Textures/WhiteDream/BloodCult/Entities/Structures/Concealed/cult.rsi/cult3.png b/Resources/Textures/WhiteDream/BloodCult/Entities/Structures/Concealed/cult.rsi/cult3.png new file mode 100644 index 00000000000..9ac2271692b Binary files /dev/null and b/Resources/Textures/WhiteDream/BloodCult/Entities/Structures/Concealed/cult.rsi/cult3.png differ diff --git a/Resources/Textures/WhiteDream/BloodCult/Entities/Structures/Concealed/cult.rsi/cult4.png b/Resources/Textures/WhiteDream/BloodCult/Entities/Structures/Concealed/cult.rsi/cult4.png new file mode 100644 index 00000000000..a5a6dd28566 Binary files /dev/null and b/Resources/Textures/WhiteDream/BloodCult/Entities/Structures/Concealed/cult.rsi/cult4.png differ diff --git a/Resources/Textures/WhiteDream/BloodCult/Entities/Structures/Concealed/cult.rsi/cult5.png b/Resources/Textures/WhiteDream/BloodCult/Entities/Structures/Concealed/cult.rsi/cult5.png new file mode 100644 index 00000000000..3c0b42ddbf7 Binary files /dev/null and b/Resources/Textures/WhiteDream/BloodCult/Entities/Structures/Concealed/cult.rsi/cult5.png differ diff --git a/Resources/Textures/WhiteDream/BloodCult/Entities/Structures/Concealed/cult.rsi/cult6.png b/Resources/Textures/WhiteDream/BloodCult/Entities/Structures/Concealed/cult.rsi/cult6.png new file mode 100644 index 00000000000..a5a6dd28566 Binary files /dev/null and b/Resources/Textures/WhiteDream/BloodCult/Entities/Structures/Concealed/cult.rsi/cult6.png differ diff --git a/Resources/Textures/WhiteDream/BloodCult/Entities/Structures/Concealed/cult.rsi/cult7.png b/Resources/Textures/WhiteDream/BloodCult/Entities/Structures/Concealed/cult.rsi/cult7.png new file mode 100644 index 00000000000..b4bc95f4f0d Binary files /dev/null and b/Resources/Textures/WhiteDream/BloodCult/Entities/Structures/Concealed/cult.rsi/cult7.png differ diff --git a/Resources/Textures/WhiteDream/BloodCult/Entities/Structures/Concealed/cult.rsi/full.png b/Resources/Textures/WhiteDream/BloodCult/Entities/Structures/Concealed/cult.rsi/full.png new file mode 100644 index 00000000000..b4e991e3457 Binary files /dev/null and b/Resources/Textures/WhiteDream/BloodCult/Entities/Structures/Concealed/cult.rsi/full.png differ diff --git a/Resources/Textures/WhiteDream/BloodCult/Entities/Structures/Concealed/cult.rsi/meta.json b/Resources/Textures/WhiteDream/BloodCult/Entities/Structures/Concealed/cult.rsi/meta.json new file mode 100644 index 00000000000..e4789013908 --- /dev/null +++ b/Resources/Textures/WhiteDream/BloodCult/Entities/Structures/Concealed/cult.rsi/meta.json @@ -0,0 +1,46 @@ +{ + "version": 1, + "license": "CC-BY-SA-3.0", + "copyright": "Taken from https://github.com/ParadiseSS13/Paradise/ and modified by FoxxoTrystan", + "size": { + "x": 32, + "y": 32 + }, + "states": [ + { + "name": "cult0", + "directions": 4 + }, + { + "name": "cult1", + "directions": 4 + }, + { + "name": "cult2", + "directions": 4 + }, + { + "name": "cult3", + "directions": 4 + }, + { + "name": "cult4", + "directions": 4 + }, + { + "name": "cult5", + "directions": 4 + }, + { + "name": "cult6", + "directions": 4 + }, + { + "name": "cult7", + "directions": 4 + }, + { + "name": "full" + } + ] +} diff --git a/Resources/Textures/WhiteDream/BloodCult/Entities/Structures/Concealed/cult_airlock.rsi/assembly.png b/Resources/Textures/WhiteDream/BloodCult/Entities/Structures/Concealed/cult_airlock.rsi/assembly.png new file mode 100644 index 00000000000..1a84e526a4a Binary files /dev/null and b/Resources/Textures/WhiteDream/BloodCult/Entities/Structures/Concealed/cult_airlock.rsi/assembly.png differ diff --git a/Resources/Textures/WhiteDream/BloodCult/Entities/Structures/Concealed/cult_airlock.rsi/closed.png b/Resources/Textures/WhiteDream/BloodCult/Entities/Structures/Concealed/cult_airlock.rsi/closed.png new file mode 100644 index 00000000000..0f60f5243cc Binary files /dev/null and b/Resources/Textures/WhiteDream/BloodCult/Entities/Structures/Concealed/cult_airlock.rsi/closed.png differ diff --git a/Resources/Textures/WhiteDream/BloodCult/Entities/Structures/Concealed/cult_airlock.rsi/closing.png b/Resources/Textures/WhiteDream/BloodCult/Entities/Structures/Concealed/cult_airlock.rsi/closing.png new file mode 100644 index 00000000000..9dd42badabf Binary files /dev/null and b/Resources/Textures/WhiteDream/BloodCult/Entities/Structures/Concealed/cult_airlock.rsi/closing.png differ diff --git a/Resources/Textures/WhiteDream/BloodCult/Entities/Structures/Concealed/cult_airlock.rsi/meta.json b/Resources/Textures/WhiteDream/BloodCult/Entities/Structures/Concealed/cult_airlock.rsi/meta.json new file mode 100644 index 00000000000..7e8135f2168 --- /dev/null +++ b/Resources/Textures/WhiteDream/BloodCult/Entities/Structures/Concealed/cult_airlock.rsi/meta.json @@ -0,0 +1,60 @@ +{ + "version": 1, + "license": "CC-BY-SA-3.0", + "copyright": "Made by Nimfar11 (GitHub) for Space Station 14", + "size": { + "x": 32, + "y": 32 + }, + "states": [ + { + "name": "assembly" + }, + { + "name": "closed", + "directions": 1, + "delays": [ + [ + 1.0 + ] + ] + }, + { + "name": "closing", + "directions": 1, + "delays": [ + [ + 0.2, + 0.2, + 0.2, + 0.2, + 0.2, + 0.2 + ] + ] + }, + { + "name": "open", + "directions": 1, + "delays": [ + [ + 1.0 + ] + ] + }, + { + "name": "opening", + "directions": 1, + "delays": [ + [ + 0.2, + 0.2, + 0.2, + 0.2, + 0.2, + 0.2 + ] + ] + } + ] +} diff --git a/Resources/Textures/WhiteDream/BloodCult/Entities/Structures/Concealed/cult_airlock.rsi/open.png b/Resources/Textures/WhiteDream/BloodCult/Entities/Structures/Concealed/cult_airlock.rsi/open.png new file mode 100644 index 00000000000..5f78166d8cf Binary files /dev/null and b/Resources/Textures/WhiteDream/BloodCult/Entities/Structures/Concealed/cult_airlock.rsi/open.png differ diff --git a/Resources/Textures/WhiteDream/BloodCult/Entities/Structures/Concealed/cult_airlock.rsi/opening.png b/Resources/Textures/WhiteDream/BloodCult/Entities/Structures/Concealed/cult_airlock.rsi/opening.png new file mode 100644 index 00000000000..127a10b53db Binary files /dev/null and b/Resources/Textures/WhiteDream/BloodCult/Entities/Structures/Concealed/cult_airlock.rsi/opening.png differ diff --git a/Resources/Textures/WhiteDream/BloodCult/Entities/Structures/Concealed/cult_girder.rsi/cultgirder.png b/Resources/Textures/WhiteDream/BloodCult/Entities/Structures/Concealed/cult_girder.rsi/cultgirder.png new file mode 100644 index 00000000000..2453b42338a Binary files /dev/null and b/Resources/Textures/WhiteDream/BloodCult/Entities/Structures/Concealed/cult_girder.rsi/cultgirder.png differ diff --git a/Resources/Textures/WhiteDream/BloodCult/Entities/Structures/Concealed/cult_girder.rsi/meta.json b/Resources/Textures/WhiteDream/BloodCult/Entities/Structures/Concealed/cult_girder.rsi/meta.json new file mode 100644 index 00000000000..4ce89d754ee --- /dev/null +++ b/Resources/Textures/WhiteDream/BloodCult/Entities/Structures/Concealed/cult_girder.rsi/meta.json @@ -0,0 +1,14 @@ +{ + "version": 1, + "license": "CC-BY-SA-3.0", + "copyright": "Taken from https://github.com/ParadiseSS13/Paradise/ and modified by FoxxoTrystan", + "size": { + "x": 32, + "y": 32 + }, + "states": [ + { + "name": "cultgirder" + } + ] +} diff --git a/Resources/Textures/WhiteDream/BloodCult/Entities/Structures/altar.rsi/icon.png b/Resources/Textures/WhiteDream/BloodCult/Entities/Structures/altar.rsi/icon.png new file mode 100644 index 00000000000..beb1a5e1f0e Binary files /dev/null and b/Resources/Textures/WhiteDream/BloodCult/Entities/Structures/altar.rsi/icon.png differ diff --git a/Resources/Textures/WhiteDream/BloodCult/Entities/Structures/altar.rsi/icon_off.png b/Resources/Textures/WhiteDream/BloodCult/Entities/Structures/altar.rsi/icon_off.png new file mode 100644 index 00000000000..86b84aac67d Binary files /dev/null and b/Resources/Textures/WhiteDream/BloodCult/Entities/Structures/altar.rsi/icon_off.png differ diff --git a/Resources/Textures/WhiteDream/BloodCult/Entities/Structures/altar.rsi/meta.json b/Resources/Textures/WhiteDream/BloodCult/Entities/Structures/altar.rsi/meta.json new file mode 100644 index 00000000000..b446b7060c7 --- /dev/null +++ b/Resources/Textures/WhiteDream/BloodCult/Entities/Structures/altar.rsi/meta.json @@ -0,0 +1,29 @@ +{ + "version": 1, + "license": "CC-BY-SA-3.0", + "copyright": "Taken from BeeStation at https://github.com/BeeStation/BeeStation-Hornet/commit/e5b645f1622f5b9186ad4c11feccbc75b3cf7e84", + "size": { + "x": 32, + "y": 32 + }, + "states": [ + { + "name": "icon", + "delays": [ + [ + 0.1, + 0.1, + 0.1, + 0.1, + 0.1, + 0.1, + 0.1, + 0.1 + ] + ] + }, + { + "name": "icon_off" + } + ] +} diff --git a/Resources/Textures/WhiteDream/BloodCult/Entities/Structures/archives.rsi/icon.png b/Resources/Textures/WhiteDream/BloodCult/Entities/Structures/archives.rsi/icon.png new file mode 100644 index 00000000000..5929d631218 Binary files /dev/null and b/Resources/Textures/WhiteDream/BloodCult/Entities/Structures/archives.rsi/icon.png differ diff --git a/Resources/Textures/WhiteDream/BloodCult/Entities/Structures/archives.rsi/icon_off.png b/Resources/Textures/WhiteDream/BloodCult/Entities/Structures/archives.rsi/icon_off.png new file mode 100644 index 00000000000..3e13421e962 Binary files /dev/null and b/Resources/Textures/WhiteDream/BloodCult/Entities/Structures/archives.rsi/icon_off.png differ diff --git a/Resources/Textures/WhiteDream/BloodCult/Entities/Structures/archives.rsi/meta.json b/Resources/Textures/WhiteDream/BloodCult/Entities/Structures/archives.rsi/meta.json new file mode 100644 index 00000000000..b446b7060c7 --- /dev/null +++ b/Resources/Textures/WhiteDream/BloodCult/Entities/Structures/archives.rsi/meta.json @@ -0,0 +1,29 @@ +{ + "version": 1, + "license": "CC-BY-SA-3.0", + "copyright": "Taken from BeeStation at https://github.com/BeeStation/BeeStation-Hornet/commit/e5b645f1622f5b9186ad4c11feccbc75b3cf7e84", + "size": { + "x": 32, + "y": 32 + }, + "states": [ + { + "name": "icon", + "delays": [ + [ + 0.1, + 0.1, + 0.1, + 0.1, + 0.1, + 0.1, + 0.1, + 0.1 + ] + ] + }, + { + "name": "icon_off" + } + ] +} diff --git a/Resources/Textures/WhiteDream/BloodCult/Entities/Structures/barrier.rsi/barrier.png b/Resources/Textures/WhiteDream/BloodCult/Entities/Structures/barrier.rsi/barrier.png new file mode 100644 index 00000000000..a4f75f45b75 Binary files /dev/null and b/Resources/Textures/WhiteDream/BloodCult/Entities/Structures/barrier.rsi/barrier.png differ diff --git a/Resources/Textures/WhiteDream/BloodCult/Entities/Structures/barrier.rsi/meta.json b/Resources/Textures/WhiteDream/BloodCult/Entities/Structures/barrier.rsi/meta.json new file mode 100644 index 00000000000..7a35e61e3d8 --- /dev/null +++ b/Resources/Textures/WhiteDream/BloodCult/Entities/Structures/barrier.rsi/meta.json @@ -0,0 +1,25 @@ +{ + "license": "CC-BY-SA-3.0", + "copyright": "Taken from TG station 13", + "version": 1, + "size": { + "x": 32, + "y": 32 + }, + "states": [ + { + "name": "barrier", + "delays": [ + [ + 0.1, + 0.1, + 0.1, + 0.1, + 0.1, + 0.1, + 0.1 + ] + ] + } + ] +} diff --git a/Resources/Textures/WhiteDream/BloodCult/Entities/Structures/cult_airlock.rsi/assembly.png b/Resources/Textures/WhiteDream/BloodCult/Entities/Structures/cult_airlock.rsi/assembly.png new file mode 100644 index 00000000000..78560839457 Binary files /dev/null and b/Resources/Textures/WhiteDream/BloodCult/Entities/Structures/cult_airlock.rsi/assembly.png differ diff --git a/Resources/Textures/WhiteDream/BloodCult/Entities/Structures/cult_airlock.rsi/closed.png b/Resources/Textures/WhiteDream/BloodCult/Entities/Structures/cult_airlock.rsi/closed.png new file mode 100644 index 00000000000..bbd196ff149 Binary files /dev/null and b/Resources/Textures/WhiteDream/BloodCult/Entities/Structures/cult_airlock.rsi/closed.png differ diff --git a/Resources/Textures/WhiteDream/BloodCult/Entities/Structures/cult_airlock.rsi/closing.png b/Resources/Textures/WhiteDream/BloodCult/Entities/Structures/cult_airlock.rsi/closing.png new file mode 100644 index 00000000000..263a6b01de0 Binary files /dev/null and b/Resources/Textures/WhiteDream/BloodCult/Entities/Structures/cult_airlock.rsi/closing.png differ diff --git a/Resources/Textures/WhiteDream/BloodCult/Entities/Structures/cult_airlock.rsi/meta.json b/Resources/Textures/WhiteDream/BloodCult/Entities/Structures/cult_airlock.rsi/meta.json new file mode 100644 index 00000000000..bec4aa2e8aa --- /dev/null +++ b/Resources/Textures/WhiteDream/BloodCult/Entities/Structures/cult_airlock.rsi/meta.json @@ -0,0 +1,46 @@ +{ + "version": 1, + "license": "CC-BY-SA-3.0", + "copyright": "Taken from TG station 13", + "size": { + "x": 32, + "y": 32 + }, + "states": [ + { + "name": "open" + }, + { + "name": "closed" + }, + { + "name": "closing", + "delays": [ + [ + 0.1, + 0.1, + 0.1, + 0.1, + 0.1, + 0.1 + ] + ] + }, + { + "name": "opening", + "delays": [ + [ + 0.1, + 0.1, + 0.1, + 0.1, + 0.1, + 0.1 + ] + ] + }, + { + "name": "assembly" + } + ] +} diff --git a/Resources/Textures/WhiteDream/BloodCult/Entities/Structures/cult_airlock.rsi/open.png b/Resources/Textures/WhiteDream/BloodCult/Entities/Structures/cult_airlock.rsi/open.png new file mode 100644 index 00000000000..92c2f27a09a Binary files /dev/null and b/Resources/Textures/WhiteDream/BloodCult/Entities/Structures/cult_airlock.rsi/open.png differ diff --git a/Resources/Textures/WhiteDream/BloodCult/Entities/Structures/cult_airlock.rsi/opening.png b/Resources/Textures/WhiteDream/BloodCult/Entities/Structures/cult_airlock.rsi/opening.png new file mode 100644 index 00000000000..997689cdd5b Binary files /dev/null and b/Resources/Textures/WhiteDream/BloodCult/Entities/Structures/cult_airlock.rsi/opening.png differ diff --git a/Resources/Textures/WhiteDream/BloodCult/Entities/Structures/cult_girder.rsi/icon.png b/Resources/Textures/WhiteDream/BloodCult/Entities/Structures/cult_girder.rsi/icon.png new file mode 100644 index 00000000000..70ec581c811 Binary files /dev/null and b/Resources/Textures/WhiteDream/BloodCult/Entities/Structures/cult_girder.rsi/icon.png differ diff --git a/Resources/Textures/WhiteDream/BloodCult/Entities/Structures/cult_girder.rsi/meta.json b/Resources/Textures/WhiteDream/BloodCult/Entities/Structures/cult_girder.rsi/meta.json new file mode 100644 index 00000000000..3619047ef13 --- /dev/null +++ b/Resources/Textures/WhiteDream/BloodCult/Entities/Structures/cult_girder.rsi/meta.json @@ -0,0 +1,14 @@ +{ + "version": 1, + "license": "CC-BY-SA-3.0", + "copyright": "Taken from TG station 13", + "size": { + "x": 32, + "y": 32 + }, + "states": [ + { + "name": "icon" + } + ] +} diff --git a/Resources/Textures/WhiteDream/BloodCult/Entities/Structures/cult_shield.rsi/icon.png b/Resources/Textures/WhiteDream/BloodCult/Entities/Structures/cult_shield.rsi/icon.png new file mode 100644 index 00000000000..a2087a18d07 Binary files /dev/null and b/Resources/Textures/WhiteDream/BloodCult/Entities/Structures/cult_shield.rsi/icon.png differ diff --git a/Resources/Textures/WhiteDream/BloodCult/Entities/Structures/cult_shield.rsi/meta.json b/Resources/Textures/WhiteDream/BloodCult/Entities/Structures/cult_shield.rsi/meta.json new file mode 100644 index 00000000000..04e6b1c0986 --- /dev/null +++ b/Resources/Textures/WhiteDream/BloodCult/Entities/Structures/cult_shield.rsi/meta.json @@ -0,0 +1,26 @@ +{ + "version": 1, + "license": "CC-BY-SA-3.0", + "copyright": "Taken from TG station 13", + "size": { + "x": 32, + "y": 32 + }, + "states": [ + { + "name": "icon", + "delays": [ + [ + 0.1, + 0.1, + 0.1, + 0.1, + 0.1, + 0.1, + 0.1, + 1 + ] + ] + } + ] +} diff --git a/Resources/Textures/WhiteDream/BloodCult/Entities/Structures/forge.rsi/icon.png b/Resources/Textures/WhiteDream/BloodCult/Entities/Structures/forge.rsi/icon.png new file mode 100644 index 00000000000..8a63affbc3c Binary files /dev/null and b/Resources/Textures/WhiteDream/BloodCult/Entities/Structures/forge.rsi/icon.png differ diff --git a/Resources/Textures/WhiteDream/BloodCult/Entities/Structures/forge.rsi/icon_off.png b/Resources/Textures/WhiteDream/BloodCult/Entities/Structures/forge.rsi/icon_off.png new file mode 100644 index 00000000000..b1f6682ac60 Binary files /dev/null and b/Resources/Textures/WhiteDream/BloodCult/Entities/Structures/forge.rsi/icon_off.png differ diff --git a/Resources/Textures/WhiteDream/BloodCult/Entities/Structures/forge.rsi/meta.json b/Resources/Textures/WhiteDream/BloodCult/Entities/Structures/forge.rsi/meta.json new file mode 100644 index 00000000000..8798cd813e8 --- /dev/null +++ b/Resources/Textures/WhiteDream/BloodCult/Entities/Structures/forge.rsi/meta.json @@ -0,0 +1,24 @@ +{ + "version": 1, + "license": "CC-BY-SA-3.0", + "copyright": "Taken from BeeStation at https://github.com/BeeStation/BeeStation-Hornet/commit/e5b645f1622f5b9186ad4c11feccbc75b3cf7e84", + "size": { + "x": 32, + "y": 32 + }, + "states": [ + { + "name": "icon", + "delays": [ + [ + 0.1, + 0.1, + 0.1 + ] + ] + }, + { + "name": "icon_off" + } + ] +} diff --git a/Resources/Textures/WhiteDream/BloodCult/Entities/Structures/pylon.rsi/icon.png b/Resources/Textures/WhiteDream/BloodCult/Entities/Structures/pylon.rsi/icon.png new file mode 100644 index 00000000000..e4c3559bbbd Binary files /dev/null and b/Resources/Textures/WhiteDream/BloodCult/Entities/Structures/pylon.rsi/icon.png differ diff --git a/Resources/Textures/WhiteDream/BloodCult/Entities/Structures/pylon.rsi/icon_off.png b/Resources/Textures/WhiteDream/BloodCult/Entities/Structures/pylon.rsi/icon_off.png new file mode 100644 index 00000000000..60e9747297a Binary files /dev/null and b/Resources/Textures/WhiteDream/BloodCult/Entities/Structures/pylon.rsi/icon_off.png differ diff --git a/Resources/Textures/WhiteDream/BloodCult/Entities/Structures/pylon.rsi/meta.json b/Resources/Textures/WhiteDream/BloodCult/Entities/Structures/pylon.rsi/meta.json new file mode 100644 index 00000000000..9077e010f87 --- /dev/null +++ b/Resources/Textures/WhiteDream/BloodCult/Entities/Structures/pylon.rsi/meta.json @@ -0,0 +1,53 @@ +{ + "version": 1, + "license": "CC-BY-SA-3.0", + "copyright": "Taken from BeeStation at https://github.com/BeeStation/BeeStation-Hornet/commit/e5b645f1622f5b9186ad4c11feccbc75b3cf7e84", + "size": { + "x": 32, + "y": 32 + }, + "states": [ + { + "name": "icon", + "delays": [ + [ + 0.2, + 0.2, + 0.2, + 0.2, + 0.2, + 0.2, + 0.2, + 0.2, + 0.2, + 0.2, + 0.2, + 0.2, + 0.2, + 0.2 + ] + ] + }, + { + "name": "icon_off", + "delays": [ + [ + 0.2, + 0.2, + 0.2, + 0.2, + 0.2, + 0.2, + 0.2, + 0.2, + 0.2, + 0.2, + 0.2, + 0.2, + 0.2, + 0.2 + ] + ] + } + ] +} diff --git a/Resources/Textures/WhiteDream/BloodCult/Entities/runic_metal.rsi/meta.json b/Resources/Textures/WhiteDream/BloodCult/Entities/runic_metal.rsi/meta.json new file mode 100644 index 00000000000..491af7e6e3e --- /dev/null +++ b/Resources/Textures/WhiteDream/BloodCult/Entities/runic_metal.rsi/meta.json @@ -0,0 +1,65 @@ +{ + "version": 1, + "license": "CC-BY-SA-3.0", + "copyright": "Taken from TG station 13", + "size": { + "x": 32, + "y": 32 + }, + "states": [ + { + "name": "runic", + "delays": [ + [ + 5, + 0.1, + 0.1, + 0.1, + 0.1, + 0.1, + 0.1, + 0.1, + 0.1, + 0.1, + 0.1 + ] + ] + }, + { + "name": "runic_2", + "delays": [ + [ + 5, + 0.1, + 0.1, + 0.1, + 0.1, + 0.1, + 0.1, + 0.1, + 0.1, + 0.1, + 0.1 + ] + ] + }, + { + "name": "runic_3", + "delays": [ + [ + 5, + 0.1, + 0.1, + 0.1, + 0.1, + 0.1, + 0.1, + 0.1, + 0.1, + 0.1, + 0.1 + ] + ] + } + ] +} diff --git a/Resources/Textures/WhiteDream/BloodCult/Entities/runic_metal.rsi/runic.png b/Resources/Textures/WhiteDream/BloodCult/Entities/runic_metal.rsi/runic.png new file mode 100644 index 00000000000..90e837d4b92 Binary files /dev/null and b/Resources/Textures/WhiteDream/BloodCult/Entities/runic_metal.rsi/runic.png differ diff --git a/Resources/Textures/WhiteDream/BloodCult/Entities/runic_metal.rsi/runic_2.png b/Resources/Textures/WhiteDream/BloodCult/Entities/runic_metal.rsi/runic_2.png new file mode 100644 index 00000000000..90e837d4b92 Binary files /dev/null and b/Resources/Textures/WhiteDream/BloodCult/Entities/runic_metal.rsi/runic_2.png differ diff --git a/Resources/Textures/WhiteDream/BloodCult/Entities/runic_metal.rsi/runic_3.png b/Resources/Textures/WhiteDream/BloodCult/Entities/runic_metal.rsi/runic_3.png new file mode 100644 index 00000000000..90e837d4b92 Binary files /dev/null and b/Resources/Textures/WhiteDream/BloodCult/Entities/runic_metal.rsi/runic_3.png differ diff --git a/Resources/Textures/WhiteDream/BloodCult/Tiles/cult_tile/attributions.yml b/Resources/Textures/WhiteDream/BloodCult/Tiles/cult_tile/attributions.yml new file mode 100644 index 00000000000..ef9673d338f --- /dev/null +++ b/Resources/Textures/WhiteDream/BloodCult/Tiles/cult_tile/attributions.yml @@ -0,0 +1,9 @@ +- files: [ "cult.png" ] + license: "CC-BY-SA-3.0" + copyright: "TG station" + source: "https://github.com/tgstation/tgstation/" + +- files: [ "concealed.png" ] + license: "CC-BY-SA-3.0" + copyright: "TG station" + source: "https://github.com/tgstation/tgstation/" diff --git a/Resources/Textures/WhiteDream/BloodCult/Tiles/cult_tile/concealed.png b/Resources/Textures/WhiteDream/BloodCult/Tiles/cult_tile/concealed.png new file mode 100644 index 00000000000..1c9c4588d1e Binary files /dev/null and b/Resources/Textures/WhiteDream/BloodCult/Tiles/cult_tile/concealed.png differ diff --git a/Resources/Textures/WhiteDream/BloodCult/Tiles/cult_tile/cult.png b/Resources/Textures/WhiteDream/BloodCult/Tiles/cult_tile/cult.png new file mode 100644 index 00000000000..da8f7982428 Binary files /dev/null and b/Resources/Textures/WhiteDream/BloodCult/Tiles/cult_tile/cult.png differ diff --git a/Resources/Textures/WhiteDream/BloodCult/actions.rsi/back.png b/Resources/Textures/WhiteDream/BloodCult/actions.rsi/back.png new file mode 100644 index 00000000000..ad552cd873e Binary files /dev/null and b/Resources/Textures/WhiteDream/BloodCult/actions.rsi/back.png differ diff --git a/Resources/Textures/WhiteDream/BloodCult/actions.rsi/barrier.png b/Resources/Textures/WhiteDream/BloodCult/actions.rsi/barrier.png new file mode 100644 index 00000000000..5371583bc98 Binary files /dev/null and b/Resources/Textures/WhiteDream/BloodCult/actions.rsi/barrier.png differ diff --git a/Resources/Textures/WhiteDream/BloodCult/actions.rsi/blood_barrage.png b/Resources/Textures/WhiteDream/BloodCult/actions.rsi/blood_barrage.png new file mode 100644 index 00000000000..50800e74905 Binary files /dev/null and b/Resources/Textures/WhiteDream/BloodCult/actions.rsi/blood_barrage.png differ diff --git a/Resources/Textures/WhiteDream/BloodCult/actions.rsi/blood_rites.png b/Resources/Textures/WhiteDream/BloodCult/actions.rsi/blood_rites.png new file mode 100644 index 00000000000..ef8df7780a7 Binary files /dev/null and b/Resources/Textures/WhiteDream/BloodCult/actions.rsi/blood_rites.png differ diff --git a/Resources/Textures/WhiteDream/BloodCult/actions.rsi/blood_spells.png b/Resources/Textures/WhiteDream/BloodCult/actions.rsi/blood_spells.png new file mode 100644 index 00000000000..35135782563 Binary files /dev/null and b/Resources/Textures/WhiteDream/BloodCult/actions.rsi/blood_spells.png differ diff --git a/Resources/Textures/WhiteDream/BloodCult/actions.rsi/create_cult_floor.png b/Resources/Textures/WhiteDream/BloodCult/actions.rsi/create_cult_floor.png new file mode 100644 index 00000000000..68d59ee95f3 Binary files /dev/null and b/Resources/Textures/WhiteDream/BloodCult/actions.rsi/create_cult_floor.png differ diff --git a/Resources/Textures/WhiteDream/BloodCult/actions.rsi/create_emp.png b/Resources/Textures/WhiteDream/BloodCult/actions.rsi/create_emp.png new file mode 100644 index 00000000000..4c3baaddfc6 Binary files /dev/null and b/Resources/Textures/WhiteDream/BloodCult/actions.rsi/create_emp.png differ diff --git a/Resources/Textures/WhiteDream/BloodCult/actions.rsi/create_soul_stone.png b/Resources/Textures/WhiteDream/BloodCult/actions.rsi/create_soul_stone.png new file mode 100644 index 00000000000..647e1928d4c Binary files /dev/null and b/Resources/Textures/WhiteDream/BloodCult/actions.rsi/create_soul_stone.png differ diff --git a/Resources/Textures/WhiteDream/BloodCult/actions.rsi/cuff.png b/Resources/Textures/WhiteDream/BloodCult/actions.rsi/cuff.png new file mode 100644 index 00000000000..149b197549d Binary files /dev/null and b/Resources/Textures/WhiteDream/BloodCult/actions.rsi/cuff.png differ diff --git a/Resources/Textures/WhiteDream/BloodCult/actions.rsi/cult_mark.png b/Resources/Textures/WhiteDream/BloodCult/actions.rsi/cult_mark.png new file mode 100644 index 00000000000..0d8d1493263 Binary files /dev/null and b/Resources/Textures/WhiteDream/BloodCult/actions.rsi/cult_mark.png differ diff --git a/Resources/Textures/WhiteDream/BloodCult/actions.rsi/final_reckoning.png b/Resources/Textures/WhiteDream/BloodCult/actions.rsi/final_reckoning.png new file mode 100644 index 00000000000..4011cefe991 Binary files /dev/null and b/Resources/Textures/WhiteDream/BloodCult/actions.rsi/final_reckoning.png differ diff --git a/Resources/Textures/WhiteDream/BloodCult/actions.rsi/gone.png b/Resources/Textures/WhiteDream/BloodCult/actions.rsi/gone.png new file mode 100644 index 00000000000..869eba7ffd0 Binary files /dev/null and b/Resources/Textures/WhiteDream/BloodCult/actions.rsi/gone.png differ diff --git a/Resources/Textures/WhiteDream/BloodCult/actions.rsi/lesser_construct.png b/Resources/Textures/WhiteDream/BloodCult/actions.rsi/lesser_construct.png new file mode 100644 index 00000000000..465b992605c Binary files /dev/null and b/Resources/Textures/WhiteDream/BloodCult/actions.rsi/lesser_construct.png differ diff --git a/Resources/Textures/WhiteDream/BloodCult/actions.rsi/meta.json b/Resources/Textures/WhiteDream/BloodCult/actions.rsi/meta.json new file mode 100644 index 00000000000..57c5432a245 --- /dev/null +++ b/Resources/Textures/WhiteDream/BloodCult/actions.rsi/meta.json @@ -0,0 +1,83 @@ +{ + "license": "CC-BY-SA-3.0", + "copyright": "Taken from Bee Station 13", + "version": 1, + "size": { + "x": 32, + "y": 32 + }, + "states": [ + { + "name": "back" + }, + { + "name": "barrier" + }, + { + "name": "blood_barrage" + }, + { + "name": "blood_rites" + }, + { + "name": "blood_spells" + }, + { + "name": "create_cult_floor" + }, + { + "name": "create_emp" + }, + { + "name": "create_soul_stone" + }, + { + "name": "cuff" + }, + { + "name": "cult_mark" + }, + { + "name": "final_reckoning" + }, + { + "name": "gone" + }, + { + "name": "lesser_construct" + }, + { + "name": "phase_shift" + }, + { + "name": "revealing" + }, + { + "name": "shackles" + }, + { + "name": "stun" + }, + { + "name": "summon_blood_spear" + }, + { + "name": "summon_combat_equipment" + }, + { + "name": "summon_dagger" + }, + { + "name": "summon_force_wall" + }, + { + "name": "teleport" + }, + { + "name": "transmute" + }, + { + "name": "veiling" + } + ] +} diff --git a/Resources/Textures/WhiteDream/BloodCult/actions.rsi/phase_shift.png b/Resources/Textures/WhiteDream/BloodCult/actions.rsi/phase_shift.png new file mode 100644 index 00000000000..b50beeb5cc3 Binary files /dev/null and b/Resources/Textures/WhiteDream/BloodCult/actions.rsi/phase_shift.png differ diff --git a/Resources/Textures/WhiteDream/BloodCult/actions.rsi/revealing.png b/Resources/Textures/WhiteDream/BloodCult/actions.rsi/revealing.png new file mode 100644 index 00000000000..c6b68d8f470 Binary files /dev/null and b/Resources/Textures/WhiteDream/BloodCult/actions.rsi/revealing.png differ diff --git a/Resources/Textures/WhiteDream/BloodCult/actions.rsi/shackles.png b/Resources/Textures/WhiteDream/BloodCult/actions.rsi/shackles.png new file mode 100644 index 00000000000..279836bd3a1 Binary files /dev/null and b/Resources/Textures/WhiteDream/BloodCult/actions.rsi/shackles.png differ diff --git a/Resources/Textures/WhiteDream/BloodCult/actions.rsi/stun.png b/Resources/Textures/WhiteDream/BloodCult/actions.rsi/stun.png new file mode 100644 index 00000000000..d19d7ff7974 Binary files /dev/null and b/Resources/Textures/WhiteDream/BloodCult/actions.rsi/stun.png differ diff --git a/Resources/Textures/WhiteDream/BloodCult/actions.rsi/summon_blood_spear.png b/Resources/Textures/WhiteDream/BloodCult/actions.rsi/summon_blood_spear.png new file mode 100644 index 00000000000..2cc9f62555e Binary files /dev/null and b/Resources/Textures/WhiteDream/BloodCult/actions.rsi/summon_blood_spear.png differ diff --git a/Resources/Textures/WhiteDream/BloodCult/actions.rsi/summon_combat_equipment.png b/Resources/Textures/WhiteDream/BloodCult/actions.rsi/summon_combat_equipment.png new file mode 100644 index 00000000000..1d2f7bfc2b0 Binary files /dev/null and b/Resources/Textures/WhiteDream/BloodCult/actions.rsi/summon_combat_equipment.png differ diff --git a/Resources/Textures/WhiteDream/BloodCult/actions.rsi/summon_dagger.png b/Resources/Textures/WhiteDream/BloodCult/actions.rsi/summon_dagger.png new file mode 100644 index 00000000000..9159067b566 Binary files /dev/null and b/Resources/Textures/WhiteDream/BloodCult/actions.rsi/summon_dagger.png differ diff --git a/Resources/Textures/WhiteDream/BloodCult/actions.rsi/summon_force_wall.png b/Resources/Textures/WhiteDream/BloodCult/actions.rsi/summon_force_wall.png new file mode 100644 index 00000000000..7a15af1b30e Binary files /dev/null and b/Resources/Textures/WhiteDream/BloodCult/actions.rsi/summon_force_wall.png differ diff --git a/Resources/Textures/WhiteDream/BloodCult/actions.rsi/teleport.png b/Resources/Textures/WhiteDream/BloodCult/actions.rsi/teleport.png new file mode 100644 index 00000000000..387eb288009 Binary files /dev/null and b/Resources/Textures/WhiteDream/BloodCult/actions.rsi/teleport.png differ diff --git a/Resources/Textures/WhiteDream/BloodCult/actions.rsi/transmute.png b/Resources/Textures/WhiteDream/BloodCult/actions.rsi/transmute.png new file mode 100644 index 00000000000..b40eb9e8f91 Binary files /dev/null and b/Resources/Textures/WhiteDream/BloodCult/actions.rsi/transmute.png differ diff --git a/Resources/Textures/WhiteDream/BloodCult/actions.rsi/veiling.png b/Resources/Textures/WhiteDream/BloodCult/actions.rsi/veiling.png new file mode 100644 index 00000000000..e551d65cb96 Binary files /dev/null and b/Resources/Textures/WhiteDream/BloodCult/actions.rsi/veiling.png differ diff --git a/Resources/Textures/WhiteDream/BloodCult/cult_hud.rsi/cult_leader.png b/Resources/Textures/WhiteDream/BloodCult/cult_hud.rsi/cult_leader.png new file mode 100644 index 00000000000..b31b12ad508 Binary files /dev/null and b/Resources/Textures/WhiteDream/BloodCult/cult_hud.rsi/cult_leader.png differ diff --git a/Resources/Textures/WhiteDream/BloodCult/cult_hud.rsi/cult_member.png b/Resources/Textures/WhiteDream/BloodCult/cult_hud.rsi/cult_member.png new file mode 100644 index 00000000000..d069a3a6528 Binary files /dev/null and b/Resources/Textures/WhiteDream/BloodCult/cult_hud.rsi/cult_member.png differ diff --git a/Resources/Textures/WhiteDream/BloodCult/cult_hud.rsi/meta.json b/Resources/Textures/WhiteDream/BloodCult/cult_hud.rsi/meta.json new file mode 100644 index 00000000000..b7a1a585963 --- /dev/null +++ b/Resources/Textures/WhiteDream/BloodCult/cult_hud.rsi/meta.json @@ -0,0 +1,23 @@ +{ + "license": "CC-BY-SA-3.0", + "copyright": "Taken from TG station 13", + "version": 1, + "size": { + "x": 16, + "y": 16 + }, + "states": [ + { + "name": "cult_member" + }, + { + "name": "cult_leader", + "delays": [ + [ + 1, + 1 + ] + ] + } + ] +} diff --git a/Resources/Textures/WhiteDream/BloodCult/mobs.rsi/artificer.png b/Resources/Textures/WhiteDream/BloodCult/mobs.rsi/artificer.png new file mode 100644 index 00000000000..b09a19ccc38 Binary files /dev/null and b/Resources/Textures/WhiteDream/BloodCult/mobs.rsi/artificer.png differ diff --git a/Resources/Textures/WhiteDream/BloodCult/mobs.rsi/glow_artificer_cult.png b/Resources/Textures/WhiteDream/BloodCult/mobs.rsi/glow_artificer_cult.png new file mode 100644 index 00000000000..48ba4a127f4 Binary files /dev/null and b/Resources/Textures/WhiteDream/BloodCult/mobs.rsi/glow_artificer_cult.png differ diff --git a/Resources/Textures/WhiteDream/BloodCult/mobs.rsi/glow_artificer_holy.png b/Resources/Textures/WhiteDream/BloodCult/mobs.rsi/glow_artificer_holy.png new file mode 100644 index 00000000000..d37abeddc4c Binary files /dev/null and b/Resources/Textures/WhiteDream/BloodCult/mobs.rsi/glow_artificer_holy.png differ diff --git a/Resources/Textures/WhiteDream/BloodCult/mobs.rsi/glow_harvester_cult.png b/Resources/Textures/WhiteDream/BloodCult/mobs.rsi/glow_harvester_cult.png new file mode 100644 index 00000000000..8701b9f2fc0 Binary files /dev/null and b/Resources/Textures/WhiteDream/BloodCult/mobs.rsi/glow_harvester_cult.png differ diff --git a/Resources/Textures/WhiteDream/BloodCult/mobs.rsi/glow_harvester_holy.png b/Resources/Textures/WhiteDream/BloodCult/mobs.rsi/glow_harvester_holy.png new file mode 100644 index 00000000000..cb2c5487ebf Binary files /dev/null and b/Resources/Textures/WhiteDream/BloodCult/mobs.rsi/glow_harvester_holy.png differ diff --git a/Resources/Textures/WhiteDream/BloodCult/mobs.rsi/glow_juggernaut_cult.png b/Resources/Textures/WhiteDream/BloodCult/mobs.rsi/glow_juggernaut_cult.png new file mode 100644 index 00000000000..15718566306 Binary files /dev/null and b/Resources/Textures/WhiteDream/BloodCult/mobs.rsi/glow_juggernaut_cult.png differ diff --git a/Resources/Textures/WhiteDream/BloodCult/mobs.rsi/glow_juggernaut_holy.png b/Resources/Textures/WhiteDream/BloodCult/mobs.rsi/glow_juggernaut_holy.png new file mode 100644 index 00000000000..15d7433c1d4 Binary files /dev/null and b/Resources/Textures/WhiteDream/BloodCult/mobs.rsi/glow_juggernaut_holy.png differ diff --git a/Resources/Textures/WhiteDream/BloodCult/mobs.rsi/glow_wraith_cult.png b/Resources/Textures/WhiteDream/BloodCult/mobs.rsi/glow_wraith_cult.png new file mode 100644 index 00000000000..1f782ca4e09 Binary files /dev/null and b/Resources/Textures/WhiteDream/BloodCult/mobs.rsi/glow_wraith_cult.png differ diff --git a/Resources/Textures/WhiteDream/BloodCult/mobs.rsi/glow_wraith_holy.png b/Resources/Textures/WhiteDream/BloodCult/mobs.rsi/glow_wraith_holy.png new file mode 100644 index 00000000000..1a3046c5561 Binary files /dev/null and b/Resources/Textures/WhiteDream/BloodCult/mobs.rsi/glow_wraith_holy.png differ diff --git a/Resources/Textures/WhiteDream/BloodCult/mobs.rsi/harvester.png b/Resources/Textures/WhiteDream/BloodCult/mobs.rsi/harvester.png new file mode 100644 index 00000000000..ce11dba2ce1 Binary files /dev/null and b/Resources/Textures/WhiteDream/BloodCult/mobs.rsi/harvester.png differ diff --git a/Resources/Textures/WhiteDream/BloodCult/mobs.rsi/juggernaut.png b/Resources/Textures/WhiteDream/BloodCult/mobs.rsi/juggernaut.png new file mode 100644 index 00000000000..e470857bafb Binary files /dev/null and b/Resources/Textures/WhiteDream/BloodCult/mobs.rsi/juggernaut.png differ diff --git a/Resources/Textures/WhiteDream/BloodCult/mobs.rsi/make_artificer_cult.png b/Resources/Textures/WhiteDream/BloodCult/mobs.rsi/make_artificer_cult.png new file mode 100644 index 00000000000..bce0f145b59 Binary files /dev/null and b/Resources/Textures/WhiteDream/BloodCult/mobs.rsi/make_artificer_cult.png differ diff --git a/Resources/Textures/WhiteDream/BloodCult/mobs.rsi/make_artificer_holy.png b/Resources/Textures/WhiteDream/BloodCult/mobs.rsi/make_artificer_holy.png new file mode 100644 index 00000000000..a3f5f7d4360 Binary files /dev/null and b/Resources/Textures/WhiteDream/BloodCult/mobs.rsi/make_artificer_holy.png differ diff --git a/Resources/Textures/WhiteDream/BloodCult/mobs.rsi/make_juggernaut_cult.png b/Resources/Textures/WhiteDream/BloodCult/mobs.rsi/make_juggernaut_cult.png new file mode 100644 index 00000000000..d4f4eb1c357 Binary files /dev/null and b/Resources/Textures/WhiteDream/BloodCult/mobs.rsi/make_juggernaut_cult.png differ diff --git a/Resources/Textures/WhiteDream/BloodCult/mobs.rsi/make_juggernaut_holy.png b/Resources/Textures/WhiteDream/BloodCult/mobs.rsi/make_juggernaut_holy.png new file mode 100644 index 00000000000..163f878f639 Binary files /dev/null and b/Resources/Textures/WhiteDream/BloodCult/mobs.rsi/make_juggernaut_holy.png differ diff --git a/Resources/Textures/WhiteDream/BloodCult/mobs.rsi/make_wraith_cult.png b/Resources/Textures/WhiteDream/BloodCult/mobs.rsi/make_wraith_cult.png new file mode 100644 index 00000000000..e954c966278 Binary files /dev/null and b/Resources/Textures/WhiteDream/BloodCult/mobs.rsi/make_wraith_cult.png differ diff --git a/Resources/Textures/WhiteDream/BloodCult/mobs.rsi/make_wraith_holy.png b/Resources/Textures/WhiteDream/BloodCult/mobs.rsi/make_wraith_holy.png new file mode 100644 index 00000000000..c6c37fa335b Binary files /dev/null and b/Resources/Textures/WhiteDream/BloodCult/mobs.rsi/make_wraith_holy.png differ diff --git a/Resources/Textures/WhiteDream/BloodCult/mobs.rsi/meta.json b/Resources/Textures/WhiteDream/BloodCult/mobs.rsi/meta.json new file mode 100644 index 00000000000..39650685b14 --- /dev/null +++ b/Resources/Textures/WhiteDream/BloodCult/mobs.rsi/meta.json @@ -0,0 +1,401 @@ +{ + "version": 1, + "license": "CC-BY-SA-3.0", + "copyright": "Taken from TG station 13, commit https://github.com/tgstation/tgstation/commit/4eaa299c0b20ae8629910a6a25be4be9d58a559e", + "size": { + "x": 32, + "y": 32 + }, + "states": [ + { + "name": "shade_cult", + "directions": 4 + }, + { + "name": "shade_holy", + "directions": 4 + }, + { + "name": "artificer", + "directions": 4 + }, + { + "name": "wraith", + "directions": 4 + }, + { + "name": "juggernaut", + "directions": 4 + }, + { + "name": "harvester", + "directions": 4 + }, + { + "name": "glow_artificer_cult", + "directions": 4 + }, + { + "name": "glow_wraith_cult", + "directions": 4 + }, + { + "name": "glow_juggernaut_cult", + "directions": 4 + }, + { + "name": "glow_harvester_cult", + "directions": 4 + }, + { + "name": "make_artificer_cult", + "delays": [ + [ + 0.075, + 0.075, + 0.075, + 0.075, + 0.075, + 0.075, + 0.075, + 0.075, + 0.075, + 0.35 + ] + ] + }, + { + "name": "make_wraith_cult", + "delays": [ + [ + 0.075, + 0.075, + 0.075, + 0.075, + 0.075, + 0.075, + 0.075, + 0.075, + 0.075, + 0.35 + ] + ] + }, + { + "name": "make_juggernaut_cult", + "delays": [ + [ + 0.075, + 0.075, + 0.075, + 0.075, + 0.075, + 0.075, + 0.075, + 0.075, + 0.075, + 0.35 + ] + ] + }, + { + "name": "phase_shift_cult", + "directions": 4, + "delays": [ + [ + 0.01, + 0.05, + 0.030000001, + 0.07, + 0.1, + 0.08, + 0.060000002, + 0.060000002, + 0.04, + 0.04, + 0.1 + ], + [ + 0.01, + 0.05, + 0.030000001, + 0.07, + 0.1, + 0.08, + 0.060000002, + 0.060000002, + 0.04, + 0.04, + 0.1 + ], + [ + 0.01, + 0.05, + 0.030000001, + 0.07, + 0.1, + 0.08, + 0.060000002, + 0.060000002, + 0.04, + 0.04, + 0.1 + ], + [ + 0.01, + 0.05, + 0.030000001, + 0.07, + 0.1, + 0.08, + 0.060000002, + 0.060000002, + 0.04, + 0.04, + 0.1 + ] + ] + }, + { + "name": "phase_shift2_cult", + "directions": 4, + "delays": [ + [ + 0.01, + 0.05, + 0.030000001, + 0.07, + 0.1, + 0.08, + 0.060000002, + 0.060000002, + 0.04, + 0.04, + 0.1 + ], + [ + 0.01, + 0.05, + 0.030000001, + 0.07, + 0.1, + 0.08, + 0.060000002, + 0.060000002, + 0.04, + 0.04, + 0.1 + ], + [ + 0.01, + 0.05, + 0.030000001, + 0.07, + 0.1, + 0.08, + 0.060000002, + 0.060000002, + 0.04, + 0.04, + 0.1 + ], + [ + 0.01, + 0.05, + 0.030000001, + 0.07, + 0.1, + 0.08, + 0.060000002, + 0.060000002, + 0.04, + 0.04, + 0.1 + ] + ] + }, + { + "name": "glow_artificer_holy", + "directions": 4 + }, + { + "name": "glow_wraith_holy", + "directions": 4 + }, + { + "name": "glow_juggernaut_holy", + "directions": 4 + }, + { + "name": "glow_harvester_holy", + "directions": 4 + }, + { + "name": "make_artificer_holy", + "delays": [ + [ + 0.075, + 0.075, + 0.075, + 0.075, + 0.075, + 0.075, + 0.075, + 0.075, + 0.075, + 0.35 + ] + ] + }, + { + "name": "make_wraith_holy", + "delays": [ + [ + 0.075, + 0.075, + 0.075, + 0.075, + 0.075, + 0.075, + 0.075, + 0.075, + 0.075, + 0.35 + ] + ] + }, + { + "name": "make_juggernaut_holy", + "delays": [ + [ + 0.075, + 0.075, + 0.075, + 0.075, + 0.075, + 0.075, + 0.075, + 0.075, + 0.075, + 0.35 + ] + ] + }, + { + "name": "phase_shift_holy", + "directions": 4, + "delays": [ + [ + 0.01, + 0.05, + 0.030000001, + 0.07, + 0.1, + 0.08, + 0.060000002, + 0.060000002, + 0.04, + 0.04, + 0.1 + ], + [ + 0.01, + 0.05, + 0.030000001, + 0.07, + 0.1, + 0.08, + 0.060000002, + 0.060000002, + 0.04, + 0.04, + 0.1 + ], + [ + 0.01, + 0.05, + 0.030000001, + 0.07, + 0.1, + 0.08, + 0.060000002, + 0.060000002, + 0.04, + 0.04, + 0.1 + ], + [ + 0.01, + 0.05, + 0.030000001, + 0.07, + 0.1, + 0.08, + 0.060000002, + 0.060000002, + 0.04, + 0.04, + 0.1 + ] + ] + }, + { + "name": "phase_shift2_holy", + "directions": 4, + "delays": [ + [ + 0.01, + 0.05, + 0.030000001, + 0.07, + 0.1, + 0.08, + 0.060000002, + 0.060000002, + 0.04, + 0.04, + 0.1 + ], + [ + 0.01, + 0.05, + 0.030000001, + 0.07, + 0.1, + 0.08, + 0.060000002, + 0.060000002, + 0.04, + 0.04, + 0.1 + ], + [ + 0.01, + 0.05, + 0.030000001, + 0.07, + 0.1, + 0.08, + 0.060000002, + 0.060000002, + 0.04, + 0.04, + 0.1 + ], + [ + 0.01, + 0.05, + 0.030000001, + 0.07, + 0.1, + 0.08, + 0.060000002, + 0.060000002, + 0.04, + 0.04, + 0.1 + ] + ] + } + ] +} diff --git a/Resources/Textures/WhiteDream/BloodCult/mobs.rsi/phase_shift2_cult.png b/Resources/Textures/WhiteDream/BloodCult/mobs.rsi/phase_shift2_cult.png new file mode 100644 index 00000000000..df2e97e72e1 Binary files /dev/null and b/Resources/Textures/WhiteDream/BloodCult/mobs.rsi/phase_shift2_cult.png differ diff --git a/Resources/Textures/WhiteDream/BloodCult/mobs.rsi/phase_shift2_holy.png b/Resources/Textures/WhiteDream/BloodCult/mobs.rsi/phase_shift2_holy.png new file mode 100644 index 00000000000..80b04eec97e Binary files /dev/null and b/Resources/Textures/WhiteDream/BloodCult/mobs.rsi/phase_shift2_holy.png differ diff --git a/Resources/Textures/WhiteDream/BloodCult/mobs.rsi/phase_shift_cult.png b/Resources/Textures/WhiteDream/BloodCult/mobs.rsi/phase_shift_cult.png new file mode 100644 index 00000000000..25e5af357be Binary files /dev/null and b/Resources/Textures/WhiteDream/BloodCult/mobs.rsi/phase_shift_cult.png differ diff --git a/Resources/Textures/WhiteDream/BloodCult/mobs.rsi/phase_shift_holy.png b/Resources/Textures/WhiteDream/BloodCult/mobs.rsi/phase_shift_holy.png new file mode 100644 index 00000000000..bc1ed67559b Binary files /dev/null and b/Resources/Textures/WhiteDream/BloodCult/mobs.rsi/phase_shift_holy.png differ diff --git a/Resources/Textures/WhiteDream/BloodCult/mobs.rsi/shade_cult.png b/Resources/Textures/WhiteDream/BloodCult/mobs.rsi/shade_cult.png new file mode 100644 index 00000000000..e7ffd23f240 Binary files /dev/null and b/Resources/Textures/WhiteDream/BloodCult/mobs.rsi/shade_cult.png differ diff --git a/Resources/Textures/WhiteDream/BloodCult/mobs.rsi/shade_holy.png b/Resources/Textures/WhiteDream/BloodCult/mobs.rsi/shade_holy.png new file mode 100644 index 00000000000..448324cbd63 Binary files /dev/null and b/Resources/Textures/WhiteDream/BloodCult/mobs.rsi/shade_holy.png differ diff --git a/Resources/Textures/WhiteDream/BloodCult/mobs.rsi/wraith.png b/Resources/Textures/WhiteDream/BloodCult/mobs.rsi/wraith.png new file mode 100644 index 00000000000..0ce519ec78f Binary files /dev/null and b/Resources/Textures/WhiteDream/BloodCult/mobs.rsi/wraith.png differ diff --git a/Resources/Textures/WhiteDream/BloodCult/narsie.rsi/meta.json b/Resources/Textures/WhiteDream/BloodCult/narsie.rsi/meta.json new file mode 100644 index 00000000000..380599c7fa7 --- /dev/null +++ b/Resources/Textures/WhiteDream/BloodCult/narsie.rsi/meta.json @@ -0,0 +1,57 @@ +{ + "license": "CC-BY-SA-3.0", + "copyright": "Taken from TG station 13", + "version": 1, + "size": { + "x": 504, + "y": 532 + }, + "states": [ + { + "name": "narsie", + "delays": [ + [ + 0.1, + 0.1, + 0.1, + 0.1, + 0.1, + 0.1, + 0.1, + 0.1, + 0.1 + ] + ] + }, + { + "name": "narsie_spawn_anim", + "delays": [ + [ + 0.3, + 0.1, + 0.1, + 0.1, + 0.1, + 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.2 + ] + ] + } + ] +} diff --git a/Resources/Textures/WhiteDream/BloodCult/narsie.rsi/narsie.png b/Resources/Textures/WhiteDream/BloodCult/narsie.rsi/narsie.png new file mode 100644 index 00000000000..e5643681cc2 Binary files /dev/null and b/Resources/Textures/WhiteDream/BloodCult/narsie.rsi/narsie.png differ diff --git a/Resources/Textures/WhiteDream/BloodCult/narsie.rsi/narsie_spawn_anim.png b/Resources/Textures/WhiteDream/BloodCult/narsie.rsi/narsie_spawn_anim.png new file mode 100644 index 00000000000..6bec5773103 Binary files /dev/null and b/Resources/Textures/WhiteDream/BloodCult/narsie.rsi/narsie_spawn_anim.png differ