diff --git a/Content.Client/_White/Cult/Concealable/ConcealableVisualizer.cs b/Content.Client/_White/Cult/Concealable/ConcealableVisualizer.cs new file mode 100644 index 0000000000..fd5283af1e --- /dev/null +++ b/Content.Client/_White/Cult/Concealable/ConcealableVisualizer.cs @@ -0,0 +1,67 @@ +using System.Linq; +using Content.Client.IconSmoothing; +using Content.Client.Interactable.Components; +using Content.Shared._White.Cult.Components; +using Robust.Client.GameObjects; + +namespace Content.Client._White.Cult.Concealable; + +public sealed class ConcealableVisualizer : VisualizerSystem +{ + [Dependency] private readonly IconSmoothSystem _smooth = default!; + + protected override void OnAppearanceChange(EntityUid uid, ConcealableComponent component, ref AppearanceChangeEvent args) + { + base.OnAppearanceChange(uid, component, ref args); + + if (args.Sprite == null) + return; + + if (!AppearanceSystem.TryGetData(uid, ConcealableAppearance.Concealed, out var concealed, args.Component)) + return; + + if (component.IconSmooth) + _smooth.SetEnabled(uid, concealed); + + if (component.InteractionOutline) + { + if (concealed) + { + if (TryComp(uid, out InteractionOutlineComponent? outline)) + { + outline.OnMouseLeave(uid); + RemComp(uid); + } + } + else + EnsureComp(uid); + } + + if (concealed) + { + if (component.ConcealedSprite != null) + { + for (var i = 0; i < args.Sprite.AllLayers.Count(); i++) + { + args.Sprite.LayerSetRSI(i, component.ConcealedSprite.Value); + } + return; + } + + args.Sprite.Color = args.Sprite.Color.WithAlpha(0f); + } + else + { + if (component.RevealedSprite != null) + { + for (var i = 0; i < args.Sprite.AllLayers.Count(); i++) + { + args.Sprite.LayerSetRSI(i, component.RevealedSprite.Value); + } + return; + } + + args.Sprite.Color = args.Sprite.Color.WithAlpha(1f); + } + } +} diff --git a/Content.Client/_White/Cult/ShowCultHudSystem.cs b/Content.Client/_White/Cult/ShowCultHudSystem.cs index c5faa8f274..6c845f468d 100644 --- a/Content.Client/_White/Cult/ShowCultHudSystem.cs +++ b/Content.Client/_White/Cult/ShowCultHudSystem.cs @@ -1,4 +1,5 @@ using Content.Shared._White.Cult; +using Content.Shared._White.Cult.Components; using Robust.Client.Graphics; using Robust.Client.Player; using Robust.Shared.Player; @@ -20,10 +21,15 @@ public sealed class ShowCultHudSystem : EntitySystem SubscribeLocalEvent(OnPlayerAttached); SubscribeLocalEvent(OnPlayerDetached); + SubscribeLocalEvent(OnComponentInit); + SubscribeLocalEvent(OnComponentRemoved); + SubscribeLocalEvent(OnPlayerAttached); + SubscribeLocalEvent(OnPlayerDetached); + _overlay = new CultHudOverlay(EntityManager); } - private void OnComponentInit(EntityUid uid, CultistComponent component, ComponentInit args) + private void OnComponentInit(EntityUid uid, ShowCultHudComponent component, ComponentInit args) { if (_player.LocalSession?.AttachedEntity != uid) return; @@ -32,7 +38,7 @@ public sealed class ShowCultHudSystem : EntitySystem } - private void OnComponentRemoved(EntityUid uid, CultistComponent component, ComponentRemove args) + private void OnComponentRemoved(EntityUid uid, ShowCultHudComponent component, ComponentRemove args) { if (_player.LocalSession?.AttachedEntity != uid) return; @@ -41,7 +47,7 @@ public sealed class ShowCultHudSystem : EntitySystem } - private void OnPlayerAttached(EntityUid uid, CultistComponent component, PlayerAttachedEvent args) + private void OnPlayerAttached(EntityUid uid, ShowCultHudComponent component, PlayerAttachedEvent args) { if (_player.LocalSession != args.Player) return; @@ -49,7 +55,7 @@ public sealed class ShowCultHudSystem : EntitySystem _overlayManager.AddOverlay(_overlay); } - private void OnPlayerDetached(EntityUid uid, CultistComponent component, PlayerDetachedEvent args) + private void OnPlayerDetached(EntityUid uid, ShowCultHudComponent component, PlayerDetachedEvent args) { if (_player.LocalSession != args.Player) return; diff --git a/Content.Client/_White/Cult/UI/ApocalypseRuneEui/ApocalypseRuneEui.cs b/Content.Client/_White/Cult/UI/ApocalypseRuneEui/ApocalypseRuneEui.cs new file mode 100644 index 0000000000..81f10759bb --- /dev/null +++ b/Content.Client/_White/Cult/UI/ApocalypseRuneEui/ApocalypseRuneEui.cs @@ -0,0 +1,45 @@ +using Content.Client.Eui; +using Content.Client.Ghost.UI; +using Content.Shared._White.Cult.UI; +using JetBrains.Annotations; +using Robust.Client.Graphics; + +namespace Content.Client._White.Cult.UI.ApocalypseRuneEui; + +[UsedImplicitly] +public sealed class ApocalypseRuneEui : BaseEui +{ + private readonly ApocalypseRuneMenu _menu; + + public ApocalypseRuneEui() + { + _menu = new ApocalypseRuneMenu(); + + _menu.DenyButton.OnPressed += _ => + { + SendMessage(new ApocalypseRuneDrawMessage(false)); + _menu.Close(); + }; + + _menu.AcceptButton.OnPressed += _ => + { + SendMessage(new ApocalypseRuneDrawMessage(true)); + _menu.Close(); + }; + } + + public override void Opened() + { + IoCManager.Resolve().RequestWindowAttention(); + _menu.OpenCentered(); + } + + public override void Closed() + { + base.Closed(); + + SendMessage(new ApocalypseRuneDrawMessage(false)); + _menu.Close(); + } + +} diff --git a/Content.Client/_White/Cult/UI/ApocalypseRuneEui/ApocalypseRuneMenu.cs b/Content.Client/_White/Cult/UI/ApocalypseRuneEui/ApocalypseRuneMenu.cs new file mode 100644 index 0000000000..57ad078780 --- /dev/null +++ b/Content.Client/_White/Cult/UI/ApocalypseRuneEui/ApocalypseRuneMenu.cs @@ -0,0 +1,60 @@ +using System.Numerics; +using Robust.Client.UserInterface; +using Robust.Client.UserInterface.Controls; +using Robust.Client.UserInterface.CustomControls; +using static Robust.Client.UserInterface.Controls.BoxContainer; + +namespace Content.Client.Ghost.UI; + +public sealed class ApocalypseRuneMenu : DefaultWindow +{ + public readonly Button DenyButton; + public readonly Button AcceptButton; + + public ApocalypseRuneMenu() + { + Title = Loc.GetString("apocalypse-rune-title"); + + Contents.AddChild(new BoxContainer + { + Orientation = LayoutOrientation.Vertical, + Children = + { + new BoxContainer + { + Orientation = LayoutOrientation.Vertical, + Children = + { + (new Label() + { + Text = Loc.GetString("apocalypse-rune-text") + }), + new BoxContainer + { + Orientation = LayoutOrientation.Horizontal, + Align = AlignMode.Center, + Children = + { + (AcceptButton = new Button + { + Text = Loc.GetString("apocalypse-rune-accept-button"), + }), + + (new Control() + { + MinSize = new Vector2(20, 0) + }), + + (DenyButton = new Button + { + Text = Loc.GetString("apocalypse-rune-deny-button"), + }) + } + }, + } + }, + } + }); + } +} + diff --git a/Content.Client/_White/Cult/UI/CultistFactory/CultistFactoryBUI.cs b/Content.Client/_White/Cult/UI/CultistFactory/CultistFactoryBUI.cs index 8f9b0c90b8..74516d4ade 100644 --- a/Content.Client/_White/Cult/UI/CultistFactory/CultistFactoryBUI.cs +++ b/Content.Client/_White/Cult/UI/CultistFactory/CultistFactoryBUI.cs @@ -32,6 +32,8 @@ public sealed class CultistFactoryBUI : BoundUserInterface _radialContainer = new RadialContainer(); + _radialContainer.Closed += Close; + if (State != null) UpdateState(State); } diff --git a/Content.Client/_White/Cult/UI/ListViewSelector/ListViewSelectorWindow.xaml.cs b/Content.Client/_White/Cult/UI/ListViewSelector/ListViewSelectorWindow.xaml.cs index f440a56b7d..15e151b14a 100644 --- a/Content.Client/_White/Cult/UI/ListViewSelector/ListViewSelectorWindow.xaml.cs +++ b/Content.Client/_White/Cult/UI/ListViewSelector/ListViewSelectorWindow.xaml.cs @@ -1,4 +1,5 @@ -using Robust.Client.AutoGenerated; +using Content.Client.Lathe.UI; +using Robust.Client.AutoGenerated; using Robust.Client.UserInterface.Controls; using Robust.Client.UserInterface.CustomControls; using Robust.Client.UserInterface.XAML; @@ -7,19 +8,21 @@ using Robust.Shared.Prototypes; namespace Content.Client._White.Cult.UI.ListViewSelector; [GenerateTypedNameReferences] -public partial class ListViewSelectorWindow : DefaultWindow +public sealed partial class ListViewSelectorWindow : DefaultWindow { public Action? ItemSelected; private readonly IPrototypeManager _prototypeManager; + public string TooltipText = string.Empty; + public ListViewSelectorWindow(IPrototypeManager prototypeManager) { RobustXamlLoader.Load(this); _prototypeManager = prototypeManager; } - public void PopulateList(List items, bool isPrototypes) + public void PopulateList(List items, bool isPrototypes) { ItemsContainer.RemoveAllChildren(); @@ -27,16 +30,16 @@ public partial class ListViewSelectorWindow : DefaultWindow { var button = new Button(); var itemName = Loc.GetString($"ent-{item}"); + var itemDesc = string.Empty; - if (isPrototypes) + if (isPrototypes && _prototypeManager.TryIndex(item, out var itemPrototype)) { - if (_prototypeManager.TryIndex(item, out var itemPrototype)) - { - itemName = itemPrototype.Name; - } + itemName = itemPrototype.Name; + itemDesc = itemPrototype.Description; } button.Text = itemName; + button.TooltipSupplier = _ => new RecipeTooltip(itemDesc); button.OnPressed += _ => ItemSelected?.Invoke(item, items.IndexOf(item)); diff --git a/Content.Client/_White/Cult/UI/StructureRadial/CultPylonPlacementHijack.cs b/Content.Client/_White/Cult/UI/StructureRadial/CultPylonPlacementHijack.cs new file mode 100644 index 0000000000..bf3f349560 --- /dev/null +++ b/Content.Client/_White/Cult/UI/StructureRadial/CultPylonPlacementHijack.cs @@ -0,0 +1,82 @@ +using System.Linq; +using Content.Client.Construction; +using Content.Shared.Construction.Prototypes; +using Content.Shared.Popups; +using Robust.Client.Placement; +using Robust.Client.Utility; +using Robust.Shared.Map; + +namespace Content.Client._White.Cult.UI.StructureRadial; + +public sealed class CultPylonPlacementHijack : PlacementHijack +{ + private readonly ConstructionSystem _constructionSystem; + private readonly IEntityManager _entMan; + private readonly ConstructionPrototype? _prototype; + private readonly EntityUid _player; + + public override bool CanRotate { get; } + + public CultPylonPlacementHijack(ConstructionPrototype? prototype, IEntityManager entMan, EntityUid player) + { + _prototype = prototype; + _entMan = entMan; + _player = player; + _constructionSystem = entMan.System(); + CanRotate = prototype?.CanRotate ?? true; + } + + /// + public override bool HijackPlacementRequest(EntityCoordinates coordinates) + { + if (_prototype == null) + return true; + + if (CheckForStructure(coordinates)) + { + var popup = _entMan.System(); + popup.PopupClient(Loc.GetString("cult-structure-craft-another-structure-nearby"), _player, _player); + return true; + } + + _constructionSystem.ClearAllGhosts(); + var dir = Manager.Direction; + _constructionSystem.SpawnGhost(_prototype, coordinates, dir); + + return true; + } + + private bool CheckForStructure(EntityCoordinates coordinates) + { + var lookupSystem = _entMan.System(); + var entities = lookupSystem.GetEntitiesInRange(coordinates, 10f); + foreach (var ent in entities) + { + if (!_entMan.TryGetComponent(ent, out var metadata)) + continue; + + if (metadata.EntityPrototype?.ID is "CultPylon") + return true; + } + + return false; + } + + /// + public override bool HijackDeletion(EntityUid entity) + { + if (IoCManager.Resolve().HasComponent(entity)) + { + _constructionSystem.ClearGhost(entity.GetHashCode()); + } + + return true; + } + + /// + public override void StartHijack(PlacementManager manager) + { + base.StartHijack(manager); + manager.CurrentTextures = _prototype?.Layers.Select(sprite => sprite.DirFrame0()).ToList(); + } +} diff --git a/Content.Client/_White/Cult/UI/StructureRadial/StructureCraftBoundUserInterface.cs b/Content.Client/_White/Cult/UI/StructureRadial/StructureCraftBoundUserInterface.cs index 78f070b843..d04ff37054 100644 --- a/Content.Client/_White/Cult/UI/StructureRadial/StructureCraftBoundUserInterface.cs +++ b/Content.Client/_White/Cult/UI/StructureRadial/StructureCraftBoundUserInterface.cs @@ -86,43 +86,23 @@ public sealed class StructureCraftBoundUserInterface : BoundUserInterface if (construct == null) return; - var player = _player.LocalPlayer?.ControlledEntity; + var player = _player.LocalEntity; if (player == null) return; - if (construct.ID == "CultPylon" && CheckForStructure(player, id)) - { - var popup = _entMan.System(); - popup.PopupClient(Loc.GetString("cult-structure-craft-another-structure-nearby"), player.Value, player.Value); - return; - } + PlacementHijack hijack; - var constructSystem = _systemManager.GetEntitySystem(); - var hijack = new ConstructionPlacementHijack(constructSystem, construct); + if (construct.ID == "CultPylon") + { + hijack = new CultPylonPlacementHijack(construct, _entMan, player.Value); + } + else + { + var constructSystem = _systemManager.GetEntitySystem(); + hijack = new ConstructionPlacementHijack(constructSystem, construct); + } _placement.BeginPlacing(newObj, hijack); } - - private bool CheckForStructure(EntityUid? uid, string id) - { - if (uid == null) - return false; - - if (!_entMan.TryGetComponent(uid, out var transform)) - return false; - - var lookupSystem = _entMan.System(); - var entities = lookupSystem.GetEntitiesInRange(transform.Coordinates, 15f); - foreach (var ent in entities) - { - if (!_entMan.TryGetComponent(ent, out var metadata)) - continue; - - if (metadata.EntityPrototype?.ID == id) - return true; - } - - return false; - } } diff --git a/Content.Server/Changeling/ChangelingRuleSystem.cs b/Content.Server/Changeling/ChangelingRuleSystem.cs index 32a68b8da2..ff97fe04a1 100644 --- a/Content.Server/Changeling/ChangelingRuleSystem.cs +++ b/Content.Server/Changeling/ChangelingRuleSystem.cs @@ -55,6 +55,13 @@ public sealed class ChangelingRuleSystem : GameRuleSystem(ClearUsedNames); SubscribeLocalEvent(OnObjectivesTextGetInfo); + + SubscribeLocalEvent(OnGetBriefing); + } + + private void OnGetBriefing(Entity ent, ref GetBriefingEvent args) + { + args.Append(Loc.GetString("changeling-role-briefing-short")); } protected override void ActiveTick( @@ -176,13 +183,6 @@ public sealed class ChangelingRuleSystem : GameRuleSystem).Any()) _roundEnd.DoRoundEndBehavior(RoundEndBehavior.ShuttleCall, component.ShuttleCallTime); else diff --git a/Content.Server/Ghost/ReturnToBodyEui.cs b/Content.Server/Ghost/ReturnToBodyEui.cs index 77b143f7df..d600fb9f37 100644 --- a/Content.Server/Ghost/ReturnToBodyEui.cs +++ b/Content.Server/Ghost/ReturnToBodyEui.cs @@ -11,12 +11,26 @@ public sealed class ReturnToBodyEui : BaseEui private readonly MindComponent _mind; + private readonly EntityUid _mindId; + + private readonly EntityUid? _transferTo; + public ReturnToBodyEui(MindComponent mind, SharedMindSystem mindSystem) { _mind = mind; _mindSystem = mindSystem; } + // WD START + public ReturnToBodyEui(MindComponent mind, SharedMindSystem mindSystem, EntityUid mindId, EntityUid? transferTo) + { + _mind = mind; + _mindSystem = mindSystem; + _mindId = mindId; + _transferTo = transferTo; + } + // WD END + public override void HandleMessage(EuiMessageBase msg) { base.HandleMessage(msg); @@ -28,6 +42,9 @@ public sealed class ReturnToBodyEui : BaseEui return; } + if (_transferTo != null) // WD + _mindSystem.TransferTo(_mindId, _transferTo, mind: _mind); + _mindSystem.UnVisit(_mind.Session); Close(); diff --git a/Content.Server/Hands/Systems/HandsSystem.cs b/Content.Server/Hands/Systems/HandsSystem.cs index d59291c362..11e70fcdb0 100644 --- a/Content.Server/Hands/Systems/HandsSystem.cs +++ b/Content.Server/Hands/Systems/HandsSystem.cs @@ -1,9 +1,11 @@ using System.Numerics; using Content.Server._White.Other.ChangeThrowForceSystem; +using Content.Server.Damage.Components; using Content.Server.Inventory; using Content.Server.Pulling; using Content.Server.Stack; using Content.Server.Stunnable; +using Content.Shared._White.Cult.Systems; using Content.Shared._White.MagGloves; using Content.Shared.ActionBlocker; using Content.Shared.Body.Part; @@ -40,6 +42,7 @@ namespace Content.Server.Hands.Systems [Dependency] private readonly SharedHandsSystem _handsSystem = default!; [Dependency] private readonly PullingSystem _pullingSystem = default!; [Dependency] private readonly ThrowingSystem _throwingSystem = default!; + [Dependency] private readonly CultItemSystem _cultItem = default!; public override void Initialize() { @@ -194,6 +197,11 @@ namespace Content.Server.Hands.Systems !_actionBlockerSystem.CanThrow(player, throwEnt)) return false; + // WD EDIT START + if (HasComp(throwEnt) && !_cultItem.CanThrow(player, throwEnt)) + return false; + // WD EDIT END + if (_timing.CurTime < hands.NextThrowTime) return false; hands.NextThrowTime = _timing.CurTime + hands.ThrowCooldown; diff --git a/Content.Server/_White/Cult/GameRule/CultRuleComponent.cs b/Content.Server/_White/Cult/GameRule/CultRuleComponent.cs index 1e1e5a8d89..2c441c7c16 100644 --- a/Content.Server/_White/Cult/GameRule/CultRuleComponent.cs +++ b/Content.Server/_White/Cult/GameRule/CultRuleComponent.cs @@ -62,13 +62,14 @@ public sealed partial class CultRuleComponent : Component public List Constructs = new(); - public CultWinCondition WinCondition; + public CultWinCondition WinCondition = CultWinCondition.CultDraw; } public enum CultWinCondition : byte { + CultDraw, CultWin, - CultFailure + CultFailure, } public sealed class CultNarsieSummoned : EntityEventArgs diff --git a/Content.Server/_White/Cult/GameRule/CultRuleSystem.cs b/Content.Server/_White/Cult/GameRule/CultRuleSystem.cs index 2736fc7f54..5c0618ff9f 100644 --- a/Content.Server/_White/Cult/GameRule/CultRuleSystem.cs +++ b/Content.Server/_White/Cult/GameRule/CultRuleSystem.cs @@ -1,14 +1,15 @@ using System.Linq; -using Content.Server._Miracle.Components; using Content.Server._Miracle.GulagSystem; using Content.Server.Actions; using Content.Server.Chat.Managers; using Content.Server.GameTicking; using Content.Server.GameTicking.Rules; using Content.Server.NPC.Systems; +using Content.Server.Roles; using Content.Server.Roles.Jobs; using Content.Server.RoundEnd; using Content.Server.Shuttles.Components; +using Content.Server.StationEvents.Components; using Content.Server.Storage.EntitySystems; using Content.Shared.Body.Systems; using Content.Shared.Humanoid; @@ -25,6 +26,7 @@ using Robust.Shared.Random; using Content.Shared._White; using Content.Shared._White.Chaplain; using Content.Shared._White.Cult.Components; +using Content.Shared._White.Cult.Systems; using Content.Shared.Mind; using Robust.Shared.Audio.Systems; @@ -47,6 +49,7 @@ public sealed class CultRuleSystem : GameRuleSystem [Dependency] private readonly SharedMindSystem _mindSystem = default!; [Dependency] private readonly ActionsSystem _actions = default!; [Dependency] private readonly GulagSystem _gulag = default!; + [Dependency] private readonly BloodSpearSystem _bloodSpear = default!; private ISawmill _sawmill = default!; @@ -69,6 +72,13 @@ public sealed class CultRuleSystem : GameRuleSystem SubscribeLocalEvent(OnCultistComponentInit); SubscribeLocalEvent(OnCultistComponentRemoved); SubscribeLocalEvent(OnCultistsStateChanged); + + SubscribeLocalEvent(OnGetBriefing); + } + + private void OnGetBriefing(Entity ent, ref GetBriefingEvent args) + { + args.Append(Loc.GetString("cult-role-briefing-short")); } private void OnCultistsStateChanged(EntityUid uid, CultistComponent component, MobStateChangedEvent ev) @@ -130,7 +140,7 @@ public sealed class CultRuleSystem : GameRuleSystem if (!TryComp(owner, out var mobState)) continue; - if (_mobStateSystem.IsAlive(owner, mobState)) + if (!_mobStateSystem.IsDead(owner, mobState)) { aliveCultists++; } @@ -140,7 +150,10 @@ public sealed class CultRuleSystem : GameRuleSystem return; cultistsRule.WinCondition = CultWinCondition.CultFailure; - _roundEndSystem.EndRound(); + + // Check for all in once gamemode + if (!GameTicker.GetActiveGameRules().Where(HasComp).Any()) + _roundEndSystem.EndRound(); } private void OnCultistComponentInit(EntityUid uid, CultistComponent component, ComponentInit args) @@ -165,6 +178,8 @@ public sealed class CultRuleSystem : GameRuleSystem } UpdateCultistsAppearance(cultistsRule); + + component.OriginalMind = (mindComponent.Mind.Value, Comp(mindComponent.Mind.Value)); } private void OnCultistComponentRemoved(EntityUid uid, CultistComponent component, ComponentRemove args) @@ -177,6 +192,8 @@ public sealed class CultRuleSystem : GameRuleSystem cultistsRule.CurrentCultists.Remove(component); + _bloodSpear.DetachSpearFromUser((uid, component)); + foreach (var empower in component.SelectedEmpowers) { _actions.RemoveAction(uid, GetEntity(empower)); @@ -438,7 +455,7 @@ public sealed class CultRuleSystem : GameRuleSystem PrototypeId = cultistRule.CultistRolePrototype }; - _roleSystem.MindAddRole(mindId, cultistComponent); + _roleSystem.MindAddRole(mindId, cultistComponent, mind); EnsureComp(playerEntity); _factionSystem.RemoveFaction(playerEntity, "NanoTrasen", false); @@ -478,19 +495,27 @@ public sealed class CultRuleSystem : GameRuleSystem _roundEndSystem.EndRound(); - var query = EntityQueryEnumerator(); + var query = + EntityQueryEnumerator(); - while (query.MoveNext(out var uid, out _, out var mindContainer, out _)) + List> cultists = new(); + + while (query.MoveNext(out var uid, out _, out var mindContainer, out _, out var transform)) { - if (!mindContainer.HasMind || mindContainer.Mind is null) + cultists.Add((uid, mindContainer, transform)); + } + + foreach (var ent in cultists) + { + if (ent.Comp1.Mind is null) { continue; } - var reaper = Spawn(CultRuleComponent.ReaperPrototype, Transform(uid).Coordinates); - _mindSystem.TransferTo(mindContainer.Mind.Value, reaper); + var reaper = Spawn(CultRuleComponent.ReaperPrototype, ent.Comp2.Coordinates); + _mindSystem.TransferTo(ent.Comp1.Mind.Value, reaper); - _bodySystem.GibBody(uid); + _bodySystem.GibBody(ent); } } diff --git a/Content.Server/_White/Cult/Pylon/PylonSystem.cs b/Content.Server/_White/Cult/Pylon/PylonSystem.cs index dc070a6231..1dc61c8b55 100644 --- a/Content.Server/_White/Cult/Pylon/PylonSystem.cs +++ b/Content.Server/_White/Cult/Pylon/PylonSystem.cs @@ -13,6 +13,7 @@ using Content.Shared.Popups; using Content.Shared.Tag; using Content.Shared._White.Cult; using Content.Shared._White.Cult.Pylon; +using Content.Shared._White.Cult.Systems; using Robust.Server.GameObjects; using Robust.Server.Player; using Robust.Shared.Audio; @@ -40,6 +41,8 @@ public sealed class PylonSystem : EntitySystem [Dependency] private readonly BloodstreamSystem _blood = default!; [Dependency] private readonly TurfSystem _turf = default!; [Dependency] private readonly EntityLookupSystem _lookup = default!; + [Dependency] private readonly PointLightSystem _pointLight = default!; + [Dependency] private readonly PhysicsSystem _physics = default!; public override void Initialize() { @@ -47,6 +50,15 @@ public sealed class PylonSystem : EntitySystem SubscribeLocalEvent(OnInteract); SubscribeLocalEvent(OnInit); + SubscribeLocalEvent(OnConceal); + } + + private void OnConceal(Entity ent, ref ConcealEvent args) + { + ent.Comp.Activated = !args.Conceal; + UpdateAppearance(ent, ent.Comp); + _pointLight.SetEnabled(ent, !args.Conceal); + _physics.SetCanCollide(ent, !args.Conceal); } private void OnInit(EntityUid uid, SharedPylonComponent component, ComponentInit args) @@ -172,7 +184,7 @@ public sealed class PylonSystem : EntitySystem if (player.AttachedEntity is not { Valid: true } playerEntity) continue; - if (!EntityManager.TryGetComponent(playerEntity, out _)) + if (!HasComp(playerEntity) && !HasComp(playerEntity)) continue; if (_mobStateSystem.IsDead(playerEntity)) @@ -221,12 +233,7 @@ public sealed class PylonSystem : EntitySystem UpdateAppearance(uid, comp); - if (!TryComp(uid, out var light)) - return; - -#pragma warning disable RA0002 - light.Enabled = comp.Activated; -#pragma warning restore RA0002 + _pointLight.SetEnabled(uid, comp.Activated); var toggleMsg = Loc.GetString(comp.Activated ? "pylon-toggle-on" : "pylon-toggle-off"); _popupSystem.PopupEntity(toggleMsg, uid); diff --git a/Content.Server/_White/Cult/Runes/Comps/RuneDrawerProviderComponent.cs b/Content.Server/_White/Cult/Runes/Comps/RuneDrawerProviderComponent.cs index 8a03b32ef8..eb143905b3 100644 --- a/Content.Server/_White/Cult/Runes/Comps/RuneDrawerProviderComponent.cs +++ b/Content.Server/_White/Cult/Runes/Comps/RuneDrawerProviderComponent.cs @@ -1,6 +1,7 @@ using Content.Server.UserInterface; using Content.Shared._White.Cult.UI; using Robust.Server.GameObjects; +using Robust.Shared.Prototypes; namespace Content.Shared._White.Cult; @@ -11,5 +12,5 @@ public sealed partial class RuneDrawerProviderComponent : Component public Enum UserInterfaceKey = ListViewSelectorUiKey.Key; [DataField("runePrototypes")] - public List RunePrototypes = new(); + public List RunePrototypes = new(); } diff --git a/Content.Server/_White/Cult/Runes/Systems/CultSystem.Actions.cs b/Content.Server/_White/Cult/Runes/Systems/CultSystem.Actions.cs index 5cddd043be..134af2bfc6 100644 --- a/Content.Server/_White/Cult/Runes/Systems/CultSystem.Actions.cs +++ b/Content.Server/_White/Cult/Runes/Systems/CultSystem.Actions.cs @@ -1,10 +1,12 @@ using System.Linq; +using System.Numerics; using Content.Server.Body.Components; using Content.Server.Chemistry.Containers.EntitySystems; using Content.Server.Emp; using Content.Server.EUI; using Content.Server._White.Cult.UI; using Content.Shared._White.Chaplain; +using Content.Shared._White.Cult; using Content.Shared.Chemistry.Components; using Content.Shared.Damage; using Content.Shared.Damage.Prototypes; @@ -17,12 +19,19 @@ using Content.Shared.Stacks; using Content.Shared.StatusEffect; using Content.Shared.Stunnable; using Content.Shared._White.Cult.Actions; +using Content.Shared._White.Cult.Components; +using Content.Shared._White.Cult.Systems; +using Content.Shared._White.Cult.UI; using Content.Shared.Actions; using Content.Shared.Cuffs.Components; using Content.Shared.DoAfter; +using Content.Shared.Maps; using Content.Shared.Mindshield.Components; using Robust.Server.GameObjects; using Robust.Shared.Audio; +using Robust.Shared.Map.Components; +using Robust.Shared.Physics; +using Robust.Shared.Physics.Components; using Robust.Shared.Player; using CultistComponent = Content.Shared._White.Cult.Components.CultistComponent; @@ -36,6 +45,13 @@ public partial class CultSystem [Dependency] private readonly EuiManager _euiManager = default!; [Dependency] private readonly InventorySystem _inventorySystem = default!; [Dependency] private readonly TransformSystem _transform = default!; + [Dependency] private readonly MapSystem _mapSystem = default!; + [Dependency] private readonly TileSystem _tile = default!; + [Dependency] private readonly BloodSpearSystem _bloodSpear = default!; + [Dependency] private readonly PhysicsSystem _physics = default!; + + private const string TileId = "CultFloor"; + private const string ConcealedTileId = "CultFloorConcealed"; public void InitializeActions() { @@ -44,8 +60,10 @@ public partial class CultSystem SubscribeLocalEvent(OnShadowShackles); SubscribeLocalEvent(OnElectromagneticPulse); SubscribeLocalEvent(OnSummonCombatEquipment); - SubscribeLocalEvent(OnConcealPresence); + SubscribeLocalEvent(OnConcealPresence); + SubscribeLocalEvent(OnConcealPresence); SubscribeLocalEvent(OnBloodRites); + SubscribeLocalEvent(OnBloodSpearRecall); SubscribeLocalEvent(OnTeleport); SubscribeLocalEvent(OnStunTarget); SubscribeLocalEvent(OnActionRemoved); @@ -74,9 +92,15 @@ public partial class CultSystem private void OnStunTarget(EntityUid uid, CultistComponent component, CultStunTargetActionEvent args) { if (args.Target == uid || !TryComp(args.Performer, out var bloodstream) || - HasComp(args.Target) || !TryComp(args.Target, out var status)) + !TryComp(args.Target, out var status)) return; + if (HasComp(args.Target)) + { + _popupSystem.PopupEntity("Священная сила препятствует магии.", args.Performer, args.Performer); + return; + } + if (HasComp(args.Target)) { _popupSystem.PopupEntity("Он имплантирован чипом защиты разума.", args.Performer, args.Performer); @@ -94,14 +118,23 @@ public partial class CultSystem private void OnTeleport(EntityUid uid, CultistComponent component, CultTeleportTargetActionEvent args) { - if (!TryComp(args.Performer, out var bloodstream) || !TryComp(uid, out var actor)) + if (!TryComp(args.Performer, out var bloodstream) || + !TryComp(uid, out var actor)) return; - if (!TryComp(args.Target, out _) && - !(TryComp(args.Target, out var mobStateComponent) && - mobStateComponent.CurrentState is not MobState.Alive)) + if (HasComp(args.Target)) { - _popupSystem.PopupEntity("Цель должна быть культистом или лежать.", args.Performer, args.Performer); + _popupSystem.PopupEntity("Священная сила препятствует магии.", args.Performer, args.Performer); + return; + } + + if (!HasComp(args.Target) && !HasComp(args.Target) && + (!TryComp(args.Target, out var mobStateComponent) || + mobStateComponent.CurrentState is MobState.Alive) && + (!TryComp(args.Target, out var cuffable) || cuffable.Container.Count == 0)) + { + _popupSystem.PopupEntity("Цель должна быть культистом, быть связанной или лежать.", args.Performer, + args.Performer); return; } @@ -147,25 +180,28 @@ public partial class CultSystem totalBloodAmount += solutionContent.Quantity; - _bloodstreamSystem.TryModifyBloodLevel(uid, solutionContent.Quantity / 6f); + _bloodstreamSystem.TryModifyBloodLevel(uid, solutionContent.Quantity / 6f, bloodstreamComponent); _solutionSystem.RemoveReagent((Entity) solution, "Blood", FixedPoint2.MaxValue); - if (GetMissingBloodValue(bloodstreamComponent) == 0) + /*if (GetMissingBloodValue(bloodstreamComponent) == 0) { breakLoop = true; - } + }*/ } } if (totalBloodAmount == 0f) - { return; - } + + component.RitesBloodAmount += totalBloodAmount; _audio.PlayPvs("/Audio/White/Cult/enter_blood.ogg", uid, AudioParams.Default); _damageableSystem.TryChangeDamage(uid, new DamageSpecifier(bruteDamageGroup, -20)); _damageableSystem.TryChangeDamage(uid, new DamageSpecifier(burnDamageGroup, -20)); + _popupSystem.PopupEntity(Loc.GetString("verb-blood-rites-message", ("blood", component.RitesBloodAmount)), uid, + uid); + args.Handled = true; } @@ -174,10 +210,163 @@ public partial class CultSystem return bloodstreamComponent.BloodMaxVolume - bloodstreamComponent.BloodSolution!.Value.Comp.Solution.Volume; } - private void OnConcealPresence(EntityUid uid, CultistComponent component, CultConcealPresenceWorldActionEvent args) + private void OnBloodSpearRecall(Entity ent, ref CultBloodSpearRecallInstantActionEvent args) { - if (!TryComp(args.Performer, out _)) + if (ent.Comp.BloodSpear == null) + { + _bloodSpear.DetachSpearFromUser(ent); return; + } + + var spear = ent.Comp.BloodSpear.Value; + var xform = Transform(spear); + var coords = _transform.GetWorldPosition(xform); + var userCoords = _transform.GetWorldPosition(ent); + var distance = (userCoords - coords).Length(); + if (distance > 10f) + { + _popupSystem.PopupEntity("Копьё слишком далеко!", ent, ent); + return; + } + + TryComp(spear, out var physics); + _physics.SetBodyType(spear, BodyType.Dynamic, body: physics, xform: xform); + _transform.AttachToGridOrMap(spear, xform); + _transform.SetWorldPosition(xform, userCoords); + _handsSystem.TryPickupAnyHand(ent, spear, animate: false); + + args.Handled = true; + } + + private void OnConcealPresence(EntityUid uid, CultistComponent component, CultConcealPresenceInstantActionEvent args) + { + if (!TryComp(args.Performer, out var bloodstream) || + !TryComp(args.Performer, out var xform)) + return; + + var conceal = args is CultConcealInstantActionEvent; + + var concealableQuery = GetEntityQuery(); + var appearanceQuery = GetEntityQuery(); + + const float radius = 5f; + + var entitiesInRange = _lookup.GetEntitiesInRange(_transform.GetMapCoordinates(xform), radius); + + var success = false; + + // Conceal/Reveal runes and structures + foreach (var ent in entitiesInRange) + { + if (!concealableQuery.TryGetComponent(ent, out var concealable) || + !appearanceQuery.TryGetComponent(ent, out var appearance) || + !EntityManager.MetaQuery.TryGetComponent(ent, out var meta)) + continue; + + if (concealable.Concealed == conceal) + continue; + + _appearanceSystem.SetData(ent, ConcealableAppearance.Concealed, conceal, appearance); + + concealable.Concealed = conceal; + + RaiseLocalEvent(ent, new ConcealEvent(conceal)); + + if (concealable.ChangeMeta) + { + if (conceal) + { + _metaDataSystem.SetEntityName(ent, concealable.ConcealedName, meta); + _metaDataSystem.SetEntityDescription(ent, concealable.ConcealedDesc, meta); + } + else + { + _metaDataSystem.SetEntityName(ent, concealable.RevealedName, meta); + _metaDataSystem.SetEntityDescription(ent, concealable.RevealedDesc, meta); + } + } + + Dirty(ent, concealable, meta); + + success = true; + } + + var gridUid = xform.GridUid; + var pos = xform.Coordinates; + + var cultTileDef = (ContentTileDefinition) _tileDefinition[TileId]; + var concealedTileDef = (ContentTileDefinition) _tileDefinition[ConcealedTileId]; + + // Conceal/Reveal tiles + if (TryComp(gridUid, out MapGridComponent? mapGrid)) + { + var tileRefs = _mapSystem.GetLocalTilesIntersecting(gridUid.Value, mapGrid, + new Box2(pos.Position + new Vector2(-radius, -radius), pos.Position + new Vector2(radius, radius))); + + foreach (var tile in tileRefs) + { + var tilePos = _turf.GetTileCenter(tile); + + if (!pos.InRange(EntityManager, _transform, tilePos, radius)) + continue; + + if (conceal) + { + if (tile.Tile.TypeId != cultTileDef.TileId) + continue; + + _tile.ReplaceTile(tile, concealedTileDef); + success = true; + } + else + { + if (tile.Tile.TypeId != concealedTileDef.TileId) + continue; + + _tile.ReplaceTile(tile, cultTileDef); + success = true; + } + } + } + + if (success) + { + _audio.PlayPvs(conceal ? "/Audio/White/Cult/smoke.ogg" : "/Audio/White/Cult/enter_blood.ogg", uid, + AudioParams.Default.WithMaxDistance(2f)); + _bloodstreamSystem.TryModifyBloodLevel(uid, -2, bloodstream, createPuddle: false); + args.Handled = true; + } + + var spellQuery = GetEntityQuery(); + var actionQuery = GetEntityQuery(); + + // Alter spell concealing/revealing state + foreach (var empower in component.SelectedEmpowers) + { + if (empower == null) + continue; + + var ent = GetEntity(empower.Value); + + if (!spellQuery.TryGetComponent(ent, out var spell) || + !actionQuery.TryGetComponent(ent, out var action)) + continue; + + if (conceal) + { + spell.Revealing = true; + action.Icon = spell.RevealIcon; + action.Event = spell.RevealEvent; + } + else + { + spell.Revealing = false; + action.Icon = spell.ConcealIcon; + action.Event = spell.ConcealEvent; + } + + Dirty(ent, action); + } } private void OnSummonCombatEquipment( @@ -300,7 +489,7 @@ public partial class CultSystem var xform = Transform(args.Performer).Coordinates; var dagger = _entityManager.SpawnEntity(RitualDaggerPrototypeId, xform); - _bloodstreamSystem.TryModifyBloodLevel(args.Performer, -20, bloodstreamComponent, false); + _bloodstreamSystem.TryModifyBloodLevel(args.Performer, -10, bloodstreamComponent, false); _handsSystem.TryPickupAnyHand(args.Performer, dagger); args.Handled = true; } diff --git a/Content.Server/_White/Cult/Runes/Systems/CultSystem.Rune.cs b/Content.Server/_White/Cult/Runes/Systems/CultSystem.Rune.cs index 740e086ff4..d1275eb638 100644 --- a/Content.Server/_White/Cult/Runes/Systems/CultSystem.Rune.cs +++ b/Content.Server/_White/Cult/Runes/Systems/CultSystem.Rune.cs @@ -10,10 +10,12 @@ using Content.Server.Hands.Systems; using Content.Server.Weapons.Ranged.Systems; using Content.Server._White.Cult.GameRule; using Content.Server._White.Cult.Runes.Comps; +using Content.Server._White.Cult.UI; using Content.Server.Bible.Components; using Content.Server.Chemistry.Components; using Content.Server.Chemistry.Containers.EntitySystems; using Content.Server.Fluids.Components; +using Content.Server.Ghost; using Content.Shared._White.Chaplain; using Content.Shared.Chemistry.Components.SolutionManager; using Content.Shared.Cuffs.Components; @@ -36,10 +38,13 @@ using Content.Shared._White.Cult.Components; using Content.Shared._White.Cult.Runes; using Content.Shared._White.Cult.UI; using Content.Shared.Cuffs; +using Content.Shared.GameTicking; using Content.Shared.Mindshield.Components; using Content.Shared.Pulling; +using Content.Shared.UserInterface; using Robust.Server.GameObjects; using Robust.Shared.Audio; +using Robust.Shared.Audio.Components; using Robust.Shared.Audio.Systems; using Robust.Shared.Map; using Robust.Shared.Physics.Events; @@ -92,7 +97,8 @@ public sealed partial class CultSystem : EntitySystem SubscribeLocalEvent(OnActiveInWorld); // UI - SubscribeLocalEvent(OnRuneDrawerUseInHand); + SubscribeLocalEvent(OnRuneDrawAttempt); + SubscribeLocalEvent(BeforeRuneDraw); SubscribeLocalEvent(OnRuneSelected); SubscribeLocalEvent( OnTeleportRuneSelected); @@ -107,6 +113,8 @@ public sealed partial class CultSystem : EntitySystem SubscribeLocalEvent(OnErase); SubscribeLocalEvent(HandleCollision); + SubscribeLocalEvent(OnRoundRestart); + InitializeBuffSystem(); InitializeNarsie(); InitializeSoulShard(); @@ -134,30 +142,30 @@ public sealed partial class CultSystem : EntitySystem private readonly SoundPathSpecifier _magic = new("/Audio/White/Cult/magic.ogg"); - private readonly SoundPathSpecifier _apocRuneStartDrawing = new("/Audio/White/Cult/startdraw.ogg"); private readonly SoundPathSpecifier _apocRuneEndDrawing = new("/Audio/White/Cult/finisheddraw.ogg"); private readonly SoundPathSpecifier _narsie40Sec = new("/Audio/White/Cult/40sec.ogg"); + private Entity? _narsieSummonningAudio = null; + + private void OnRoundRestart(RoundRestartCleanupEvent ev) + { + CultRuneReviveComponent.ChargesLeft = 3; + } + /* * Rune draw start ---- */ - private void OnRuneDrawerUseInHand(EntityUid uid, RuneDrawerProviderComponent component, UseInHandEvent args) + private void OnRuneDrawAttempt(Entity ent, ref ActivatableUIOpenAttemptEvent args) { - if (!_ui.TryGetUi(uid, component.UserInterfaceKey, out _)) - return; - - if (!TryComp(args.User, out var actorComponent)) - return; - if (!HasComp(args.User)) - return; + args.Cancel(); + } - if (_ui.TryGetUi(uid, ListViewSelectorUiKey.Key, out var bui)) - { - _ui.SetUiState(bui, new ListViewBUIState(component.RunePrototypes, false)); - _ui.OpenUi(bui, actorComponent.PlayerSession); - } + private void BeforeRuneDraw(Entity ent, ref BeforeActivatableUIOpenEvent args) + { + if (_ui.TryGetUi(ent, ListViewSelectorUiKey.Key, out var bui)) + _ui.SetUiState(bui, new ListViewBUIState(ent.Comp.RunePrototypes, true)); } private void OnRuneSelected(EntityUid uid, RuneDrawerProviderComponent component, ListViewItemSelectedMessage args) @@ -185,16 +193,20 @@ public sealed partial class CultSystem : EntitySystem if (HasComp(whoCalled)) _timeToDraw /= 2; - if (runePrototype == ApocalypseRunePrototypeId) - { - _timeToDraw = 120.0f; - _chat.DispatchGlobalAnnouncement(Loc.GetString("cult-started-drawing-rune-end"), "CULT", true, - _apocRuneStartDrawing, colorOverride: Color.DarkRed); - } - if (!IsAllowedToDraw(whoCalled)) return false; + if (runePrototype == ApocalypseRunePrototypeId) + { + if (!_mindSystem.TryGetMind(whoCalled, out _, out var mind) || + mind.Session is not { } playerSession) + return false; + + _euiManager.OpenEui(new ApocalypseRuneEui(whoCalled, _entityManager), playerSession); + + return true; + } + var ev = new CultDrawEvent { Rune = runePrototype @@ -769,7 +781,7 @@ public sealed partial class CultSystem : EntitySystem _chat.DispatchGlobalAnnouncement(Loc.GetString("cult-ritual-started"), "CULT", false, colorOverride: Color.DarkRed); - _audio.PlayGlobal(_narsie40Sec, Filter.Broadcast(), false, AudioParams.Default.WithLoop(true).WithVolume(0.15f)); + _narsieSummonningAudio = _audio.PlayGlobal(_narsie40Sec, Filter.Broadcast(), false, AudioParams.Default.WithLoop(true).WithVolume(0.15f)); return true; } @@ -778,6 +790,8 @@ public sealed partial class CultSystem : EntitySystem { _doAfterAlreadyStarted = false; + _audio.Stop(_narsieSummonningAudio?.Owner, _narsieSummonningAudio?.Comp); + if (args.Cancelled) { _chat.DispatchGlobalAnnouncement(Loc.GetString("cult-ritual-prevented"), "CULT", false, @@ -790,13 +804,13 @@ public sealed partial class CultSystem : EntitySystem if (transform == null) return; - _entityManager.SpawnEntity(NarsiePrototypeId, transform.Value); - - _chat.DispatchGlobalAnnouncement(Loc.GetString("cult-narsie-summoned"), "CULT", true, _apocRuneEndDrawing, - colorOverride: Color.DarkRed); - var ev = new CultNarsieSummoned(); RaiseLocalEvent(ev); + + _entityManager.SpawnEntity(NarsiePrototypeId, transform.Value); + + //_chat.DispatchGlobalAnnouncement(Loc.GetString("cult-narsie-summoned"), "CULT", true, _apocRuneEndDrawing, + // colorOverride: Color.DarkRed); } /* @@ -852,6 +866,27 @@ public sealed partial class CultSystem : EntitySystem CultRuneReviveComponent.ChargesLeft--; _entityManager.EventBus.RaiseLocalEvent(target, new RejuvenateEvent()); + + EntityUid? transferTo = null; + + if (!_mindSystem.TryGetMind(target, out var mindId, out var mind)) + { + if (!TryComp(target, out var cultist) || cultist.OriginalMind == null) + return true; + + (mindId, mind) = cultist.OriginalMind.Value; + + transferTo = target; + } + + if (mind.Session is not { } playerSession) + return true; + + // notify them they're being revived. + if (mind.CurrentEntity != target) + { + _euiManager.OpenEui(new ReturnToBodyEui(mind, _mindSystem, mindId, transferTo), playerSession); + } return true; } @@ -1258,6 +1293,8 @@ public sealed partial class CultSystem : EntitySystem return; } + var damage = 10; + if (rune == ApocalypseRunePrototypeId) { if (!_entityManager.TryGetComponent(uid, out TransformComponent? transComp)) @@ -1265,6 +1302,7 @@ public sealed partial class CultSystem : EntitySystem return; } + damage = 40; var pos = transComp.MapPosition; var x = (int) pos.X; var y = (int) pos.Y; @@ -1273,7 +1311,7 @@ public sealed partial class CultSystem : EntitySystem "CULT", true, _apocRuneEndDrawing, colorOverride: Color.DarkRed); } - var damageSpecifier = new DamageSpecifier(_prototypeManager.Index("Slash"), 10); + var damageSpecifier = new DamageSpecifier(_prototypeManager.Index("Slash"), damage); _damageableSystem.TryChangeDamage(uid, damageSpecifier, true, false); _xform.AttachToGridOrMap(_entityManager.SpawnEntity(rune, transform.Value)); diff --git a/Content.Server/_White/Cult/Runes/Systems/CultSystem.Verb.cs b/Content.Server/_White/Cult/Runes/Systems/CultSystem.Verb.cs index 059fa77710..1f45cfaa9f 100644 --- a/Content.Server/_White/Cult/Runes/Systems/CultSystem.Verb.cs +++ b/Content.Server/_White/Cult/Runes/Systems/CultSystem.Verb.cs @@ -2,6 +2,7 @@ using System.Linq; using Content.Server.Body.Components; using Content.Shared._White.Cult; using Content.Shared._White.Cult.Components; +using Content.Shared._White.Cult.UI; using Content.Shared.DoAfter; using Content.Shared.Verbs; using Robust.Shared.Player; @@ -16,6 +17,7 @@ public sealed partial class CultSystem SubscribeLocalEvent(OnCultistEmpowerSelected); SubscribeLocalEvent(OnCultistEmpowerRemove); SubscribeLocalEvent(OnSpellCreated); + SubscribeLocalEvent(OnBloodRitesSelected); } private void OnCultistEmpowerRemove(Entity ent, ref CultEmpowerRemoveBuiMessage args) @@ -28,7 +30,7 @@ public sealed partial class CultSystem private void OnSpellCreated(EntityUid ent, CultistComponent comp, SpellCreatedEvent args) { - if (args.Cancelled || comp.SelectedEmpowers.Count >= 1) + if (args.Cancelled || comp.SelectedEmpowers.Count >= 2) return; var action = CultistComponent.CultistActions.FirstOrDefault(x => x.Equals(args.Spell)); @@ -55,7 +57,7 @@ public sealed partial class CultSystem if (action == null) return; - if (comp.SelectedEmpowers.Count >= 1) + if (comp.SelectedEmpowers.Count >= 2) { _popupSystem.PopupEntity(Loc.GetString("verb-spell-create-too-much"), ent, ent); return; @@ -98,8 +100,24 @@ public sealed partial class CultSystem } }; + var bloodRitesVerb = new Verb + { + Text = Loc.GetString("verb-blood-rites-text"), + Message = Loc.GetString("verb-blood-rites-message", ("blood", ent.Comp.RitesBloodAmount)), + Category = VerbCategory.Cult, + Act = () => + { + if (!_ui.TryGetUi(ent, BloodRitesUi.Key, out var bui)) + return; + + _ui.SetUiState(bui, new CultistFactoryBUIState(ent.Comp.BloodRites)); + _ui.OpenUi(bui, actor.PlayerSession); + } + }; + args.Verbs.Add(createSpellVerb); args.Verbs.Add(removeSpellVerb); + args.Verbs.Add(bloodRitesVerb); } private void RemoveSpell(Entity ent, ICommonSession session) @@ -112,4 +130,36 @@ public sealed partial class CultSystem _ui.TryOpen(ent, CultEmpowerRemoveUiKey.Key, session); } + + private void OnBloodRitesSelected(Entity ent, ref CultistFactoryItemSelectedMessage args) + { + if (!_prototypeManager.TryIndex(args.Item, out var prototype)) + return; + + if (ent.Comp.RitesBloodAmount < prototype.BloodCost) + { + var message = Loc.GetString("verb-blood-rites-no-blood", ("required", prototype.BloodCost), + ("blood", ent.Comp.RitesBloodAmount)); + _popupSystem.PopupEntity(message, ent, ent); + return; + } + + var success = false; + foreach (var item in prototype.Item) + { + var entity = Spawn(item, Transform(ent).Coordinates); + if (_handsSystem.TryPickupAnyHand(ent, entity)) + { + success = true; + continue; + } + + _popupSystem.PopupEntity(Loc.GetString("verb-blood-rites-no-hand"), ent, ent); + QueueDel(entity); + break; + } + + if (success) + ent.Comp.RitesBloodAmount -= prototype.BloodCost; + } } diff --git a/Content.Server/_White/Cult/Structures/RunicDoorSystem.cs b/Content.Server/_White/Cult/Structures/RunicDoorSystem.cs index 01e31df343..b92c799152 100644 --- a/Content.Server/_White/Cult/Structures/RunicDoorSystem.cs +++ b/Content.Server/_White/Cult/Structures/RunicDoorSystem.cs @@ -1,13 +1,19 @@ -using Content.Server.Doors.Systems; +using Content.Server.Cuffs; +using Content.Server.Doors.Systems; using Content.Shared._White.Chaplain; using Content.Shared.Doors; using Content.Shared.Humanoid; using Content.Shared.Stunnable; -using Content.Shared._White.Cult; +using Content.Shared._White.Cult.Components; +using Content.Shared._White.Cult.Systems; +using Content.Shared.Cuffs.Components; using Content.Shared.Doors.Components; +using Content.Shared.Mobs.Systems; +using Content.Shared.Weapons.Melee.Components; using Content.Shared.Weapons.Melee.Events; using Robust.Shared.Audio; using Robust.Shared.Audio.Systems; +using Robust.Shared.Physics.Components; using Robust.Shared.Physics.Systems; using CultistComponent = Content.Shared._White.Cult.Components.CultistComponent; @@ -19,6 +25,9 @@ public sealed class RunicDoorSystem : EntitySystem [Dependency] private readonly SharedPhysicsSystem _physics = default!; [Dependency] private readonly SharedStunSystem _stunSystem = default!; [Dependency] private readonly SharedAudioSystem _audio = default!; + [Dependency] private readonly OccluderSystem _occluder = default!; + [Dependency] private readonly MobStateSystem _mobState = default!; + [Dependency] private readonly CuffableSystem _cuffable = default!; public override void Initialize() { @@ -27,6 +36,31 @@ public sealed class RunicDoorSystem : EntitySystem SubscribeLocalEvent(OnBeforeDoorOpened); SubscribeLocalEvent(OnBeforeDoorClosed); SubscribeLocalEvent(OnGetAttacked); + SubscribeLocalEvent(OnConceal); + } + + private void OnConceal(Entity ent, ref ConcealEvent args) + { + if (!TryComp(ent, out MetaDataComponent? meta)) + return; + + if (TryComp(ent, out PhysicsComponent? physics)) + _occluder.SetEnabled(ent, args.Conceal && physics.CanCollide, meta: meta); + + if (TryComp(ent, out DoorComponent? door)) + { + door.Occludes = args.Conceal; + Dirty(ent, door, meta); + } + + if (!TryComp(ent, out MeleeSoundComponent? meleeSound) || meleeSound.SoundGroups == null) + return; + + meleeSound.SoundGroups["Brute"] = args.Conceal + ? new SoundPathSpecifier("/Audio/Weapons/smash.ogg") + : new SoundCollectionSpecifier("GlassSmash"); + + Dirty(ent, meleeSound, meta); } private void OnGetAttacked(Entity ent, ref AttackedEvent args) @@ -79,7 +113,8 @@ public sealed class RunicDoorSystem : EntitySystem _doorSystem.Deny(airlock); - if (!HasComp(user) || HasComp(user)) + if (!HasComp(user) || HasComp(user) || + TryComp(airlock, out ConcealableComponent? concealable) && concealable.Concealed) return false; var direction = Transform(user).MapPosition.Position - Transform(airlock).MapPosition.Position; @@ -90,4 +125,11 @@ public sealed class RunicDoorSystem : EntitySystem _stunSystem.TryParalyze(user, TimeSpan.FromSeconds(3), true); return false; } + + public bool CanBumpOpen(EntityUid uid, EntityUid otherUid) + { + return !HasComp(uid) || !HasComp(otherUid) && + (!HasComp(otherUid) || !_mobState.IsAlive(otherUid) || + TryComp(otherUid, out CuffableComponent? cuffable) && _cuffable.GetAllCuffs(cuffable).Count > 0); + } } diff --git a/Content.Server/_White/Cult/TimedProduction/CultistFactorySystem.cs b/Content.Server/_White/Cult/TimedProduction/CultistFactorySystem.cs index d3dff143eb..31763ee9fd 100644 --- a/Content.Server/_White/Cult/TimedProduction/CultistFactorySystem.cs +++ b/Content.Server/_White/Cult/TimedProduction/CultistFactorySystem.cs @@ -5,6 +5,7 @@ using Content.Shared.Interaction; using Content.Shared.Popups; using Content.Shared._White.Cult; using Content.Shared._White.Cult.Structures; +using Content.Shared._White.Cult.Systems; using Content.Shared._White.Cult.UI; using Robust.Server.GameObjects; using Robust.Shared.Audio.Systems; @@ -28,6 +29,7 @@ public sealed class CultistFactorySystem : EntitySystem [Dependency] private readonly SharedDoAfterSystem _doAfterSystem = default!; [Dependency] private readonly SharedPopupSystem _popup = default!; [Dependency] private readonly SharedAudioSystem _audio = default!; + [Dependency] private readonly PhysicsSystem _physics = default!; private const string RitualDaggerPrototypeId = "RitualDagger"; @@ -42,6 +44,12 @@ public sealed class CultistFactorySystem : EntitySystem SubscribeLocalEvent(TryToggleAnchor); SubscribeLocalEvent(OnAnchorDoAfter); SubscribeLocalEvent(OnExamine); + SubscribeLocalEvent(OnConceal); + } + + private void OnConceal(Entity ent, ref ConcealEvent args) + { + _physics.SetCanCollide(ent, !args.Conceal); } public override void Update(float frameTime) diff --git a/Content.Server/_White/Cult/UI/ApocalypseRuneEui.cs b/Content.Server/_White/Cult/UI/ApocalypseRuneEui.cs new file mode 100644 index 0000000000..1dee78d18a --- /dev/null +++ b/Content.Server/_White/Cult/UI/ApocalypseRuneEui.cs @@ -0,0 +1,61 @@ +using Content.Server.Chat.Systems; +using Content.Server.DoAfter; +using Content.Server.EUI; +using Content.Shared._White.Cult.Runes; +using Content.Shared._White.Cult.UI; +using Content.Shared.DoAfter; +using Content.Shared.Eui; +using Robust.Server.Audio; +using Robust.Shared.Audio; + +namespace Content.Server._White.Cult.UI; + +public sealed class ApocalypseRuneEui : BaseEui +{ + private readonly SoundPathSpecifier _apocRuneStartDrawing = new("/Audio/White/Cult/startdraw.ogg"); + private const string ApocalypseRunePrototypeId = "ApocalypseRune"; + + private readonly EntityUid _whoCalled; + private readonly IEntityManager _entityManager; + + public ApocalypseRuneEui(EntityUid whoCalled, IEntityManager entityManager) + { + _whoCalled = whoCalled; + _entityManager = entityManager; + } + + public override void HandleMessage(EuiMessageBase msg) + { + base.HandleMessage(msg); + + if (msg is not ApocalypseRuneDrawMessage {Accepted: true}) + { + Close(); + return; + } + + var ev = new CultDrawEvent + { + Rune = ApocalypseRunePrototypeId + }; + + var argsDoAfterEvent = new DoAfterArgs(_entityManager, _whoCalled, 120f, ev, _whoCalled) + { + BreakOnUserMove = true, + NeedHand = true + }; + + if (!_entityManager.System().TryStartDoAfter(argsDoAfterEvent)) + { + Close(); + return; + } + + _entityManager.System().DispatchGlobalAnnouncement(Loc.GetString("cult-started-drawing-rune-end"), + "CULT", true, _apocRuneStartDrawing, colorOverride: Color.DarkRed); + + _entityManager.System().PlayPvs("/Audio/White/Cult/butcher.ogg", _whoCalled, + AudioParams.Default.WithMaxDistance(2f)); + Close(); + } +} diff --git a/Content.Shared/Changeling/SharedTentacleGun.cs b/Content.Shared/Changeling/SharedTentacleGun.cs index 358eed81b4..eba52ec191 100644 --- a/Content.Shared/Changeling/SharedTentacleGun.cs +++ b/Content.Shared/Changeling/SharedTentacleGun.cs @@ -123,10 +123,10 @@ public abstract class SharedTentacleGun : EntitySystem foreach (var activeItem in _handsSystem.EnumerateHeld(args.Embedded)) { if(!TryComp(activeItem, out var physicsComponent)) - return; + continue; var coords = Transform(args.Embedded).Coordinates; - _handsSystem.TryDrop(args.Embedded, coords); + _handsSystem.TryDrop(args.Embedded, activeItem, coords); var force = physicsComponent.Mass * 2.5f / 2; diff --git a/Content.Shared/Hands/EntitySystems/SharedHandsSystem.Interactions.cs b/Content.Shared/Hands/EntitySystems/SharedHandsSystem.Interactions.cs index 32339eb03a..3a8f086ba2 100644 --- a/Content.Shared/Hands/EntitySystems/SharedHandsSystem.Interactions.cs +++ b/Content.Shared/Hands/EntitySystems/SharedHandsSystem.Interactions.cs @@ -1,4 +1,5 @@ using System.Linq; +using Content.Shared._White.Cult.Components; using Content.Shared.Examine; using Content.Shared.Hands.Components; using Content.Shared.IdentityManagement; @@ -7,12 +8,15 @@ using Content.Shared.Inventory.VirtualItem; using Content.Shared.Localizations; using Robust.Shared.Input.Binding; using Robust.Shared.Map; +using Robust.Shared.Network; using Robust.Shared.Player; namespace Content.Shared.Hands.EntitySystems; public abstract partial class SharedHandsSystem : EntitySystem { + [Dependency] private readonly INetManager _net = default!; // WD + private void InitializeInteractions() { SubscribeAllEvent(HandleSetHand); @@ -95,7 +99,17 @@ public abstract partial class SharedHandsSystem : EntitySystem private bool DropPressed(ICommonSession? session, EntityCoordinates coords, EntityUid netEntity) { if (TryComp(session?.AttachedEntity, out HandsComponent? hands) && hands.ActiveHand != null) + // WD EDIT START + { + if (HasComp(hands.ActiveHandEntity)) + { + if (_net.IsServer) + QueueDel(hands.ActiveHandEntity.Value); + return false; + } TryDrop(session.AttachedEntity.Value, hands.ActiveHand, coords, handsComp: hands); + } + // WD EDIT END // always send to server. return false; diff --git a/Content.Shared/Weapons/Melee/Components/MeleeSoundComponent.cs b/Content.Shared/Weapons/Melee/Components/MeleeSoundComponent.cs index ae467dc4ad..879a398655 100644 --- a/Content.Shared/Weapons/Melee/Components/MeleeSoundComponent.cs +++ b/Content.Shared/Weapons/Melee/Components/MeleeSoundComponent.cs @@ -1,5 +1,6 @@ using Content.Shared.Damage.Prototypes; using Robust.Shared.Audio; +using Robust.Shared.GameStates; using Robust.Shared.Serialization.TypeSerializers.Implementations.Custom.Prototype.Dictionary; namespace Content.Shared.Weapons.Melee.Components; @@ -8,6 +9,7 @@ namespace Content.Shared.Weapons.Melee.Components; /// Plays the specified sound upon receiving damage of the specified type. /// [RegisterComponent] +[NetworkedComponent, AutoGenerateComponentState] // WD public sealed partial class MeleeSoundComponent : Component { /// @@ -16,6 +18,7 @@ public sealed partial class MeleeSoundComponent : Component /// [DataField("soundGroups", customTypeSerializer: typeof(PrototypeIdDictionarySerializer))] + [AutoNetworkedField] // WD public Dictionary? SoundGroups; /// @@ -24,10 +27,12 @@ public sealed partial class MeleeSoundComponent : Component /// [DataField("soundTypes", customTypeSerializer: typeof(PrototypeIdDictionarySerializer))] + [AutoNetworkedField] // WD public Dictionary? SoundTypes; /// /// Sound that plays if no damage is done. /// + [AutoNetworkedField] // WD [DataField("noDamageSound")] public SoundSpecifier? NoDamageSound; } diff --git a/Content.Shared/_White/Body/BodyRejuvenateSystem.cs b/Content.Shared/_White/Body/BodyRejuvenateSystem.cs new file mode 100644 index 0000000000..055855e8dc --- /dev/null +++ b/Content.Shared/_White/Body/BodyRejuvenateSystem.cs @@ -0,0 +1,48 @@ +using System.Linq; +using Content.Shared.Body.Components; +using Content.Shared.Body.Systems; +using Content.Shared.Rejuvenate; +using Robust.Shared.Containers; +using Robust.Shared.Prototypes; + +namespace Content.Shared._White.Body; + +public sealed class BodyRejuvenateSystem : EntitySystem +{ + [Dependency] private readonly SharedBodySystem _body = default!; + [Dependency] private readonly SharedContainerSystem _container = default!; + [Dependency] private readonly IPrototypeManager _prototypeManager = default!; + + public override void Initialize() + { + base.Initialize(); + SubscribeLocalEvent(OnRejuvenate); + } + + private void OnRejuvenate(Entity ent, ref RejuvenateEvent args) + { + if (ent.Comp.Prototype == null) + return; + + var prototype = _prototypeManager.Index(ent.Comp.Prototype.Value); + var protoSlots = prototype.Slots.Values.ToList(); + + foreach (var (id, component) in _body.GetBodyChildren(ent.Owner, ent.Comp)) + { + foreach (var organSlot in component.Organs.Values) + { + if (!_container.TryGetContainer(id, SharedBodySystem.GetOrganContainerId(organSlot.Id), + out var container)) + continue; + + if (container.Count > 0) + continue; + + var organ = protoSlots.Where(x => x.Organs.ContainsKey(organSlot.Id)) + .Select(x => x.Organs[organSlot.Id]).FirstOrDefault(); + + TrySpawnInContainer(organ, id, SharedBodySystem.GetOrganContainerId(organSlot.Id), out _); + } + } + } +} diff --git a/Content.Shared/_White/Cult/Actions/CultActions.cs b/Content.Shared/_White/Cult/Actions/CultActions.cs index 4abc4b31f1..b3924e0046 100644 --- a/Content.Shared/_White/Cult/Actions/CultActions.cs +++ b/Content.Shared/_White/Cult/Actions/CultActions.cs @@ -30,10 +30,23 @@ public sealed partial class CultSummonCombatEquipmentTargetActionEvent : EntityT { } -public sealed partial class CultConcealPresenceWorldActionEvent : WorldTargetActionEvent +[Virtual] +public partial class CultConcealPresenceInstantActionEvent : InstantActionEvent +{ +} + +public sealed partial class CultConcealInstantActionEvent : CultConcealPresenceInstantActionEvent +{ +} + +public sealed partial class CultRevealInstantActionEvent : CultConcealPresenceInstantActionEvent { } public sealed partial class CultBloodRitesInstantActionEvent : InstantActionEvent { } + +public sealed partial class CultBloodSpearRecallInstantActionEvent : InstantActionEvent +{ +} diff --git a/Content.Shared/_White/Cult/Components/BloodSpearComponent.cs b/Content.Shared/_White/Cult/Components/BloodSpearComponent.cs new file mode 100644 index 0000000000..c4f93b605a --- /dev/null +++ b/Content.Shared/_White/Cult/Components/BloodSpearComponent.cs @@ -0,0 +1,17 @@ +using Robust.Shared.Audio; +using Robust.Shared.Prototypes; + +namespace Content.Shared._White.Cult.Components; + +[RegisterComponent] +public sealed partial class BloodSpearComponent : Component +{ + [ViewVariables] + public Entity? User; + + [DataField] + public EntProtoId Action; + + [DataField] + public SoundSpecifier ShatterSound = new SoundCollectionSpecifier("GlassBreak"); +} diff --git a/Content.Shared/_White/Cult/Components/BoltBarrageComponent.cs b/Content.Shared/_White/Cult/Components/BoltBarrageComponent.cs new file mode 100644 index 0000000000..6070e62e73 --- /dev/null +++ b/Content.Shared/_White/Cult/Components/BoltBarrageComponent.cs @@ -0,0 +1,7 @@ +namespace Content.Shared._White.Cult.Components; + +[RegisterComponent] +public sealed partial class BoltBarrageComponent : Component +{ + public bool Unremoveable = true; +} diff --git a/Content.Shared/_White/Cult/Components/ConcealPresenceSpellComponent.cs b/Content.Shared/_White/Cult/Components/ConcealPresenceSpellComponent.cs new file mode 100644 index 0000000000..6b59c4ca44 --- /dev/null +++ b/Content.Shared/_White/Cult/Components/ConcealPresenceSpellComponent.cs @@ -0,0 +1,23 @@ +using Content.Shared.Actions; +using Robust.Shared.Utility; + +namespace Content.Shared._White.Cult.Components; + +[RegisterComponent] +public sealed partial class ConcealPresenceSpellComponent : Component +{ + [ViewVariables] + public bool Revealing; + + [DataField(required: true), NonSerialized] + public InstantActionEvent? ConcealEvent; + + [DataField(required: true), NonSerialized] + public InstantActionEvent? RevealEvent; + + [DataField] + public SpriteSpecifier? ConcealIcon; + + [DataField] + public SpriteSpecifier? RevealIcon; +} diff --git a/Content.Shared/_White/Cult/Components/ConcealableComponent.cs b/Content.Shared/_White/Cult/Components/ConcealableComponent.cs new file mode 100644 index 0000000000..23807de31c --- /dev/null +++ b/Content.Shared/_White/Cult/Components/ConcealableComponent.cs @@ -0,0 +1,48 @@ +using Robust.Shared.GameStates; +using Robust.Shared.Serialization; +using Robust.Shared.Utility; + +namespace Content.Shared._White.Cult.Components; + +[RegisterComponent, NetworkedComponent, AutoGenerateComponentState] +public sealed partial class ConcealableComponent : Component +{ + [ViewVariables, AutoNetworkedField] + public bool Concealed; + + [DataField] + public bool ExaminableWhileConcealed; + + [DataField] + public bool IconSmooth; + + [DataField] + public bool InteractionOutline; + + [DataField] + public ResPath? ConcealedSprite; + + [DataField] + public ResPath? RevealedSprite; + + [DataField] + public bool ChangeMeta; + + [DataField] + public string ConcealedName = string.Empty; + + [DataField] + public string ConcealedDesc = string.Empty; + + [DataField] + public string RevealedName = string.Empty; + + [DataField] + public string RevealedDesc = string.Empty; +} + +[Serializable, NetSerializable] +public enum ConcealableAppearance +{ + Concealed +} diff --git a/Content.Shared/_White/Cult/Components/CultEmpowerComponent.cs b/Content.Shared/_White/Cult/Components/CultEmpowerComponent.cs index aa2a378d21..970c2a8eda 100644 --- a/Content.Shared/_White/Cult/Components/CultEmpowerComponent.cs +++ b/Content.Shared/_White/Cult/Components/CultEmpowerComponent.cs @@ -9,8 +9,8 @@ public sealed partial class CultEmpowerComponent : Component [DataField("isRune")] public bool IsRune; - public int MaxAllowedCultistActions = 4; - public int MinRequiredCultistActions = 1; + public int MaxAllowedCultistActions = 5; + public int MinRequiredCultistActions = 2; } [Serializable, NetSerializable] diff --git a/Content.Shared/_White/Cult/Components/CultistComponent.cs b/Content.Shared/_White/Cult/Components/CultistComponent.cs index 0c822b41e6..758c61cd1c 100644 --- a/Content.Shared/_White/Cult/Components/CultistComponent.cs +++ b/Content.Shared/_White/Cult/Components/CultistComponent.cs @@ -1,6 +1,9 @@ using System.Threading; +using Content.Shared.FixedPoint; +using Content.Shared.Mind; using Robust.Shared.Audio; using Robust.Shared.GameStates; +using Robust.Shared.Serialization.TypeSerializers.Implementations.Custom.Prototype.List; namespace Content.Shared._White.Cult.Components; @@ -8,7 +11,7 @@ namespace Content.Shared._White.Cult.Components; /// This is used for tagging a mob as a cultist. /// [RegisterComponent, NetworkedComponent, AutoGenerateComponentState] -public sealed partial class CultistComponent : Component +public sealed partial class CultistComponent : ShowCultHudComponent { [DataField("greetSound", customTypeSerializer: typeof(SoundSpecifierTypeSerializer))] public SoundSpecifier? CultistGreetSound = new SoundPathSpecifier("/Audio/CultSounds/fart.ogg"); @@ -21,12 +24,17 @@ public sealed partial class CultistComponent : Component [AutoNetworkedField] public List SelectedEmpowers = new(); + [ViewVariables(VVAccess.ReadWrite)] + public FixedPoint2 RitesBloodAmount = FixedPoint2.Zero; + public static string SummonCultDaggerAction = "InstantActionSummonCultDagger"; public static string BloodRitesAction = "InstantActionBloodRites"; public static string EmpPulseAction = "InstantActionEmpPulse"; + public static string ConcealPresenceAction = "InstantActionConcealPresence"; + public static string CultTwistedConstructionAction = "ActionCultTwistedConstruction"; public static string CultTeleportAction = "ActionCultTeleport"; @@ -40,6 +48,22 @@ public sealed partial class CultistComponent : Component public static List CultistActions = new() { SummonCultDaggerAction, BloodRitesAction, CultTwistedConstructionAction, CultTeleportAction, - CultSummonCombatEquipmentAction, CultStunAction, EmpPulseAction, CultShadowShacklesAction + CultSummonCombatEquipmentAction, CultStunAction, EmpPulseAction, ConcealPresenceAction, CultShadowShacklesAction }; + + [DataField("bloodRites", customTypeSerializer: typeof(PrototypeIdListSerializer))] + public List BloodRites = new () + { + "FactoryCultBloodSpear", + "FactoryCultBloodBarrage" + }; + + [ViewVariables, NonSerialized] + public Entity? BloodSpear; + + [ViewVariables, NonSerialized] + public EntityUid? BloodSpearActionEntity; + + [ViewVariables, NonSerialized] + public Entity? OriginalMind; } diff --git a/Content.Shared/_White/Cult/Components/ShowCultHudComponent.cs b/Content.Shared/_White/Cult/Components/ShowCultHudComponent.cs new file mode 100644 index 0000000000..c03470bd80 --- /dev/null +++ b/Content.Shared/_White/Cult/Components/ShowCultHudComponent.cs @@ -0,0 +1,8 @@ +using Robust.Shared.GameStates; + +namespace Content.Shared._White.Cult.Components; + +[Virtual, RegisterComponent, NetworkedComponent] +public partial class ShowCultHudComponent : Component +{ +} diff --git a/Content.Shared/_White/Cult/CultistFactoryProductionPrototype.cs b/Content.Shared/_White/Cult/CultistFactoryProductionPrototype.cs index 8b000335c0..6066023d51 100644 --- a/Content.Shared/_White/Cult/CultistFactoryProductionPrototype.cs +++ b/Content.Shared/_White/Cult/CultistFactoryProductionPrototype.cs @@ -17,6 +17,9 @@ public sealed class CultistFactoryProductionPrototype : IPrototype [DataField("name", required: true)] public string Name = default!; + + [DataField] + public int BloodCost; } [Serializable, NetSerializable] diff --git a/Content.Shared/_White/Cult/Systems/BloodSpearSystem.cs b/Content.Shared/_White/Cult/Systems/BloodSpearSystem.cs new file mode 100644 index 0000000000..308be30e70 --- /dev/null +++ b/Content.Shared/_White/Cult/Systems/BloodSpearSystem.cs @@ -0,0 +1,80 @@ +using Content.Shared._White.Cult.Components; +using Content.Shared.Actions; +using Content.Shared.Hands; +using Content.Shared.StatusEffect; +using Content.Shared.Stunnable; +using Content.Shared.Throwing; +using Robust.Shared.Audio.Systems; +using Robust.Shared.Network; + +namespace Content.Shared._White.Cult.Systems; + +public sealed class BloodSpearSystem : EntitySystem +{ + [Dependency] private readonly SharedActionsSystem _actionsSystem = default!; + [Dependency] private readonly SharedStunSystem _stunSystem = default!; + [Dependency] private readonly SharedAudioSystem _audio = default!; + [Dependency] private readonly INetManager _net = default!; + + public override void Initialize() + { + base.Initialize(); + + SubscribeLocalEvent(OnRemove); + SubscribeLocalEvent(OnEquip); + SubscribeLocalEvent(OnThrowDoHit); + } + + private void OnThrowDoHit(Entity ent, ref ThrowDoHitEvent args) + { + if (!TryComp(args.Target, out StatusEffectsComponent? status)) + return; + + if(!_stunSystem.TryParalyze(args.Target, TimeSpan.FromSeconds(6), true, status)) + return; + + if (_net.IsClient) + return; + + _audio.PlayPvs(ent.Comp.ShatterSound, Transform(ent).Coordinates); + QueueDel(ent); + } + + private void OnEquip(Entity ent, ref GotEquippedHandEvent args) + { + if (!TryComp(args.User, out CultistComponent? cultist)) + return; + + Entity user = (args.User, cultist); + + if (cultist.BloodSpear == ent && ent.Comp.User == user) + return; + + if (ent.Comp.User != null) + DetachSpearFromUser(ent.Comp.User.Value); + DetachSpearFromUser(user); + AttachSpearToUser(user, ent); + } + + public void DetachSpearFromUser(Entity user) + { + _actionsSystem.RemoveAction(user, user.Comp.BloodSpearActionEntity); + user.Comp.BloodSpearActionEntity = null; + if (user.Comp.BloodSpear != null) + user.Comp.BloodSpear.Value.Comp.User = null; + user.Comp.BloodSpear = null; + } + + public void AttachSpearToUser(Entity user, Entity spear) + { + _actionsSystem.AddAction(user, ref user.Comp.BloodSpearActionEntity, spear.Comp.Action); + user.Comp.BloodSpear = spear; + spear.Comp.User = user; + } + + private void OnRemove(Entity ent, ref ComponentRemove args) + { + if (ent.Comp.User != null) + DetachSpearFromUser(ent.Comp.User.Value); + } +} diff --git a/Content.Shared/_White/Cult/Systems/BoltBarrageSystem.cs b/Content.Shared/_White/Cult/Systems/BoltBarrageSystem.cs new file mode 100644 index 0000000000..e2fb4947a3 --- /dev/null +++ b/Content.Shared/_White/Cult/Systems/BoltBarrageSystem.cs @@ -0,0 +1,91 @@ +using System.Linq; +using Content.Shared._White.Cult.Components; +using Content.Shared.Ghost; +using Content.Shared.Hands; +using Content.Shared.Hands.Components; +using Content.Shared.Hands.EntitySystems; +using Content.Shared.Interaction.Events; +using Content.Shared.Weapons.Ranged.Events; +using Content.Shared.Weapons.Ranged.Systems; +using Robust.Shared.Containers; +using Robust.Shared.Network; +using Robust.Shared.Timing; + +namespace Content.Shared._White.Cult.Systems; + +public sealed class BoltBarrageSystem : EntitySystem +{ + [Dependency] private readonly SharedHandsSystem _hands = default!; + [Dependency] private readonly INetManager _net = default!; + [Dependency] private readonly IGameTiming _timing = default!; + + public override void Initialize() + { + base.Initialize(); + + SubscribeLocalEvent(OnShootAttempt); + SubscribeLocalEvent(OnGunShot); + SubscribeLocalEvent(OnDrop); + SubscribeLocalEvent(OnUnequipHand); + SubscribeLocalEvent(OnRemoveAttempt); + SubscribeLocalEvent(OnEmptyShot); + } + + private void OnUnequipHand(Entity ent, ref UnequippedHandEvent args) + { + if (_net.IsServer && ent.Comp.Unremoveable) + QueueDel(ent); + } + + private void OnRemoveAttempt(Entity ent, ref ContainerGettingRemovedAttemptEvent args) + { + if (!_timing.ApplyingState && ent.Comp.Unremoveable) + args.Cancel(); + } + + private void OnEmptyShot(Entity ent, ref OnEmptyGunShotEvent args) + { + if (_net.IsServer) + QueueDel(ent); + } + + private void OnDrop(Entity ent, ref DroppedEvent args) + { + if (_net.IsServer) + QueueDel(ent); + } + + private void OnGunShot(Entity ent, ref GunShotEvent args) + { + if (!TryComp(args.User, out HandsComponent? hands)) + return; + + foreach (var hand in _hands.EnumerateHands(args.User, hands)) + { + if (!hand.IsEmpty) + continue; + + ent.Comp.Unremoveable = false; + _hands.SetActiveHand(args.User, hand, hands); + _hands.TryPickup(args.User, ent, hand, false, false, hands); + ent.Comp.Unremoveable = true; + return; + } + } + + private void OnShootAttempt(Entity ent, ref AttemptShootEvent args) + { + if (!HasComp(args.User) && !HasComp(args.User)) + { + args.Cancelled = true; + args.Message = Loc.GetString("bolt-barrage-component-not-cultist"); + return; + } + + if (_hands.EnumerateHands(args.User).Any(hand => hand.IsEmpty)) + return; + + args.Cancelled = true; + args.Message = Loc.GetString("bolt-barrage-component-no-empty-hand"); + } +} diff --git a/Content.Shared/_White/Cult/Systems/ConcealableSystem.cs b/Content.Shared/_White/Cult/Systems/ConcealableSystem.cs new file mode 100644 index 0000000000..68d4122178 --- /dev/null +++ b/Content.Shared/_White/Cult/Systems/ConcealableSystem.cs @@ -0,0 +1,38 @@ +using Content.Shared._White.Cult.Components; +using Content.Shared.Examine; +using Content.Shared.Interaction.Events; + +namespace Content.Shared._White.Cult.Systems; + +public sealed class ConcealableSystem : EntitySystem +{ + public override void Initialize() + { + base.Initialize(); + + SubscribeLocalEvent(OnExamine); + SubscribeLocalEvent(OnInteract); + } + + private void OnInteract(Entity ent, ref InteractionAttemptEvent args) + { + if (ent.Comp is {Concealed: true, ExaminableWhileConcealed: false}) + args.Cancel(); + } + + private void OnExamine(Entity ent, ref ExamineAttemptEvent args) + { + if (ent.Comp is {Concealed: true, ExaminableWhileConcealed: false}) + args.Cancel(); + } +} + +public sealed class ConcealEvent : EntityEventArgs +{ + public bool Conceal; + + public ConcealEvent(bool conceal) + { + Conceal = conceal; + } +} diff --git a/Content.Shared/_White/Cult/Systems/CultItemSystem.cs b/Content.Shared/_White/Cult/Systems/CultItemSystem.cs index 67b75846f9..bc29cf60ed 100644 --- a/Content.Shared/_White/Cult/Systems/CultItemSystem.cs +++ b/Content.Shared/_White/Cult/Systems/CultItemSystem.cs @@ -49,6 +49,15 @@ public sealed class CultItemSystem : EntitySystem _popupSystem.PopupClient(Loc.GetString("cult-item-component-pickup-fail", ("name", Name(uid))), uid, args.User); } + public bool CanThrow(EntityUid player, EntityUid throwEnt) + { + if (!HasComp(throwEnt) || CanUse(player)) + return true; + + _popupSystem.PopupEntity(Loc.GetString("cult-item-component-throw-fail"), player, player); + return false; + } + private bool CanUse(EntityUid? uid) { return HasComp(uid) || HasComp(uid); diff --git a/Content.Shared/_White/Cult/UI/ApocalypseRuneDrawMessage.cs b/Content.Shared/_White/Cult/UI/ApocalypseRuneDrawMessage.cs new file mode 100644 index 0000000000..c91abcfac8 --- /dev/null +++ b/Content.Shared/_White/Cult/UI/ApocalypseRuneDrawMessage.cs @@ -0,0 +1,15 @@ +using Content.Shared.Eui; +using Robust.Shared.Serialization; + +namespace Content.Shared._White.Cult.UI; + +[Serializable, NetSerializable] +public sealed class ApocalypseRuneDrawMessage : EuiMessageBase +{ + public readonly bool Accepted; + + public ApocalypseRuneDrawMessage(bool accepted) + { + Accepted = accepted; + } +} diff --git a/Content.Shared/_White/Cult/UI/BloodRitesUi.cs b/Content.Shared/_White/Cult/UI/BloodRitesUi.cs new file mode 100644 index 0000000000..d43f80be9b --- /dev/null +++ b/Content.Shared/_White/Cult/UI/BloodRitesUi.cs @@ -0,0 +1,9 @@ +using Robust.Shared.Serialization; + +namespace Content.Shared._White.Cult.UI; + +[Serializable, NetSerializable] +public enum BloodRitesUi +{ + Key +} diff --git a/Content.Shared/_White/Cult/UI/ListViewSelectorBUIState.cs b/Content.Shared/_White/Cult/UI/ListViewSelectorBUIState.cs index f57ec01a85..17120b3c58 100644 --- a/Content.Shared/_White/Cult/UI/ListViewSelectorBUIState.cs +++ b/Content.Shared/_White/Cult/UI/ListViewSelectorBUIState.cs @@ -12,10 +12,10 @@ public enum ListViewSelectorUiKey [Serializable, NetSerializable] public class ListViewBUIState : BoundUserInterfaceState { - public List Items { get; set; } + public List Items { get; set; } public bool IsUsingPrototypes { get; set; } - public ListViewBUIState(List items, bool isUsingPrototypes) + public ListViewBUIState(List items, bool isUsingPrototypes) { Items = items; IsUsingPrototypes = isUsingPrototypes; diff --git a/Resources/Audio/White/Cult/40sec.ogg b/Resources/Audio/White/Cult/40sec.ogg index 00416855f6..44572f8ed1 100644 Binary files a/Resources/Audio/White/Cult/40sec.ogg and b/Resources/Audio/White/Cult/40sec.ogg differ diff --git a/Resources/Audio/White/Cult/smoke.ogg b/Resources/Audio/White/Cult/smoke.ogg new file mode 100644 index 0000000000..0109e1947c Binary files /dev/null and b/Resources/Audio/White/Cult/smoke.ogg differ diff --git a/Resources/Audio/White/Cult/wand_teleport.ogg b/Resources/Audio/White/Cult/wand_teleport.ogg new file mode 100644 index 0000000000..e16d231ddd Binary files /dev/null and b/Resources/Audio/White/Cult/wand_teleport.ogg differ diff --git a/Resources/Locale/ru-RU/White/cult.ftl b/Resources/Locale/ru-RU/White/cult.ftl index e0229b6cd4..e6d8326e9a 100644 --- a/Resources/Locale/ru-RU/White/cult.ftl +++ b/Resources/Locale/ru-RU/White/cult.ftl @@ -1,12 +1,15 @@ -cult-role-greeting = +cult-role-greeting = Вы - член культа! Ваши цели перечислены в меню персонажа. В ваш рюкзак были добавлены предметы, которые помогут вам. И помните - вы не единственный. Слава Нар`си! +cult-role-briefing-short = Используйте '^' для связи с другими членами культа. + cult-cond-cultwin = Культ одержал победу cult-cond-cultfailure = Экипаж уничтожил культ +cult-cond-cultdraw = Ничейный исход cultists-list-start = Культистами были: cultists-list-name = - [color=White]{ $name }[/color] ([color=gray]{ $user }[/color]) diff --git a/Resources/Locale/ru-RU/White/stuff.ftl/pylon.ftl b/Resources/Locale/ru-RU/White/stuff.ftl/pylon.ftl index b00cdb1f00..cc7dfd57fa 100644 --- a/Resources/Locale/ru-RU/White/stuff.ftl/pylon.ftl +++ b/Resources/Locale/ru-RU/White/stuff.ftl/pylon.ftl @@ -1,4 +1,4 @@ pylon-toggle-on = Кристалл воспаряет над пьедесталом, начиная пульсировать pylon-toggle-off = Кристалл перестаёт пульсировать, опускаясь на пьедестал ent-CultPylon = пилон - .desc = Мистический конструкция. + .desc = Мистическая конструкция. diff --git a/Resources/Locale/ru-RU/White/stuff.ftl/runes-entities.ftl b/Resources/Locale/ru-RU/White/stuff.ftl/runes-entities.ftl index 35a97e75af..cdea17ab9e 100644 --- a/Resources/Locale/ru-RU/White/stuff.ftl/runes-entities.ftl +++ b/Resources/Locale/ru-RU/White/stuff.ftl/runes-entities.ftl @@ -1,11 +1,28 @@ -ent-OfferingRune = руна предпонесения +ent-OfferingRune = руна предпонесения + .desc = Мгновенно превращает обычного члена экипажа в культиста, для чего требуется 2 культиста вокруг руны. Члена экипажа с имплантом защиты разума нельзя перевоплотить, можно только принести в жертву, для чего нужно 3 культиста, которые встанут вокруг руны. Если цель мертва, то она будет принесена в жертву, для чего требуется 1 культист. + ent-BuffRune = руна усиления + .desc = При активации усиливает вас, уменьшая затраты и ускоряя процесс подготовки заклинаний крови и черчения рун. + ent-EmpoweringRune = руна могущества + .desc = Позволяет культистам приготовить до 5 заклинаний крови. + ent-TeleportRune = руна телепортации + .desc = Эта руна при использовании переносит всё, что находится на ней, к другой руне телепортации. + ent-SummoningRune = руна призыва + .desc = Эта руна позволяет мгновенно призвать к руне любого несвязанного культиста. Для использования требуются 2 культиста, стоящих вокруг руны. + ent-ReviveRune = руна воскрешения + .desc = Каждый раз, когда кого-то приносят в жертву на Руне Преподнесения, этой руне добавляется один глобальный заряд. Размещение трупа культиста на руне и её активация вернет его к жизни, расходуя при этом заряд. + ent-BarrierRune = руна барьера + .desc = При активации на создаёт барьер, блокирующую проход. Может быть повторно активирована, чтобы отменить заклинание. + ent-BloodBoilRune = руна кипящей крови + .desc = При активации отнимает у призывателей некоторое количество крови, чтобы отправить несколько разрушительных импульсов любому не-культисту поблизости. Когда эффект закончится, руна ненадолго подожжет все, что находится вокруг нее. Для активации требуются 3 культиста, стоящих вокруг руны. + ent-ApocalypseRune = ритуал пространственного разрыва + .desc = Руна призыва Нар'Си. runes-window-title = Руны diff --git a/Resources/Locale/ru-RU/cult/abilities.ftl b/Resources/Locale/ru-RU/cult/abilities.ftl index 0575e85db3..1fd0c0b66f 100644 --- a/Resources/Locale/ru-RU/cult/abilities.ftl +++ b/Resources/Locale/ru-RU/cult/abilities.ftl @@ -35,3 +35,9 @@ ent-ActionCultShadowShackles = Теневые Узы ent-InstantActionEmpPulse = Электромагнитный Импульс .desc = Большое заклинание, которое позволяет пользователю направлять темную энергию в ЭМИ. + +ent-InstantActionConcealPresence = Сокрытие Присутствия + .desc = Многофункциональное заклинание, чередующееся между скрытием и обнаружением ближайших рун и структур культа. + +ent-InstantActionRecallBloodSpear = Призвать Кровавое Копьё + .desc = Призывает кровавое копье в вашу руку. diff --git a/Resources/Locale/ru-RU/cult/bolt-barrage.ftl b/Resources/Locale/ru-RU/cult/bolt-barrage.ftl new file mode 100644 index 0000000000..b6e870093d --- /dev/null +++ b/Resources/Locale/ru-RU/cult/bolt-barrage.ftl @@ -0,0 +1,2 @@ +bolt-barrage-component-no-empty-hand = Вам нужно иметь свободную руку, чтобы стрелять. +bolt-barrage-component-not-cultist = Вы не умеете пользоваться магией. diff --git a/Resources/Locale/ru-RU/cult/cult-item.ftl b/Resources/Locale/ru-RU/cult/cult-item.ftl index 21ee937641..593e64950a 100644 --- a/Resources/Locale/ru-RU/cult/cult-item.ftl +++ b/Resources/Locale/ru-RU/cult/cult-item.ftl @@ -1,3 +1,4 @@ cult-item-component-pickup-fail = Вы не можете подобрать {$name} cult-item-component-attack-fail = Оружие не позволяет вам атаковать cult-item-component-equip-fail = Вы не можете это надеть +cult-item-component-throw-fail = Оружие не позволяет вам его бросить diff --git a/Resources/Locale/ru-RU/cult/cultist-factory.ftl b/Resources/Locale/ru-RU/cult/cultist-factory.ftl index 7be764e835..ed06221a46 100644 --- a/Resources/Locale/ru-RU/cult/cultist-factory.ftl +++ b/Resources/Locale/ru-RU/cult/cultist-factory.ftl @@ -9,3 +9,9 @@ ent-CultMirrorShield = зеркальный щит .desc = Щит с зеркалом на лицевой стороне, на котором изображен какой-то религиозный знак. ent-CultOuterArmor = бронированная мантия .desc = С первого взгляда кажется, что это простая мантия, но на ней имеется элементы брони. +ent-ShadowShackles = теневые оковы + .desc = Оковы, сковывающие запястья с помощью зловещей магии. +ent-BloodSpear = кровавое копьё + .desc = Ужасающее копьё, полностью состоящее из кристаллизованной крови. +ent-BloodBarrage = залп кровавых снарядов + .desc = Кровь за кровь. diff --git a/Resources/Locale/ru-RU/cult/gui.ftl b/Resources/Locale/ru-RU/cult/gui.ftl new file mode 100644 index 0000000000..92bd1fa53e --- /dev/null +++ b/Resources/Locale/ru-RU/cult/gui.ftl @@ -0,0 +1,4 @@ +apocalypse-rune-title = Вы готовы к финальной битве? +apocalypse-rune-text = Это последний шаг к вызову Нар'Си; это долгий, болезненный ритуал, и экипаж будет предупрежден о вашем присутствии. +apocalypse-rune-accept-button = Жизнь за Нар'Си! +apocalypse-rune-deny-button = Нет diff --git a/Resources/Locale/ru-RU/cult/pylon.ftl b/Resources/Locale/ru-RU/cult/pylon.ftl index b00cdb1f00..cc7dfd57fa 100644 --- a/Resources/Locale/ru-RU/cult/pylon.ftl +++ b/Resources/Locale/ru-RU/cult/pylon.ftl @@ -1,4 +1,4 @@ pylon-toggle-on = Кристалл воспаряет над пьедесталом, начиная пульсировать pylon-toggle-off = Кристалл перестаёт пульсировать, опускаясь на пьедестал ent-CultPylon = пилон - .desc = Мистический конструкция. + .desc = Мистическая конструкция. diff --git a/Resources/Locale/ru-RU/cult/verb.ftl b/Resources/Locale/ru-RU/cult/verb.ftl index 0721351b41..08f35fca69 100644 --- a/Resources/Locale/ru-RU/cult/verb.ftl +++ b/Resources/Locale/ru-RU/cult/verb.ftl @@ -1,9 +1,14 @@ verb-categories-cult = Культ verb-spell-create-text = Создать заклинание крови -verb-spell-create-message = Вы можете создать одно заклинание крови без руны могущества. -verb-spell-create-too-much = Начертите руну могущества, чтобы создать больше одного заклинания крови. +verb-spell-create-message = Вы можете создать два заклинания крови без руны могущества. +verb-spell-create-too-much = Начертите руну могущества, чтобы создать больше двух заклинаний крови. verb-spell-remove-text = Удалить заклинание крови verb-spell-remove-message = Убрать любое из созданных заклинаний крови. verb-spell-remove-no-spells = Заклинания крови отсутствуют. + +verb-blood-rites-text = Кровавые обряды +verb-blood-rites-message = Всего высосано крови: { $blood }. +verb-blood-rites-no-blood = Для обряда необходимо высосать { $required } крови с помощью заклинания Кровавые Обряды. Всего высосано: { $blood }. +verb-blood-rites-no-hand = Вам нужна свободная рука для обряда. diff --git a/Resources/Locale/ru-RU/white/gamemodes/cult.ftl b/Resources/Locale/ru-RU/white/gamemodes/cult.ftl deleted file mode 100644 index 642b777e87..0000000000 --- a/Resources/Locale/ru-RU/white/gamemodes/cult.ftl +++ /dev/null @@ -1,7 +0,0 @@ -cult-cond-cultwin = Экипаж пососал -cult-cond-cultfailure = Экипаж не пососал -cultists-list-start = Культистами были: -cultists-list-name = - [color=White]{ $name }[/color] ([color=gray]{ $user }[/color]) -soul-shard-name = Душа { $soul } -soul-shard-description = В этом камне заключена душа { $soul } -cult-too-much-empowers = Слишком много способностей diff --git a/Resources/Prototypes/Entities/Debugging/debug_sweps.yml b/Resources/Prototypes/Entities/Debugging/debug_sweps.yml index e3289569a2..b446b6a3d3 100644 --- a/Resources/Prototypes/Entities/Debugging/debug_sweps.yml +++ b/Resources/Prototypes/Entities/Debugging/debug_sweps.yml @@ -40,6 +40,7 @@ whitelist: tags: - CartridgePistol + - type: GiftIgnore - type: entity id: MagazinePistolDebug @@ -55,6 +56,7 @@ capacity: 1000 - type: Sprite sprite: Objects/Weapons/Guns/Ammunition/Magazine/Pistol/pistol_mag.rsi + - type: GiftIgnore - type: entity id: BulletDebug @@ -103,6 +105,7 @@ - type: Item size: Tiny sprite: Objects/Weapons/Melee/debug.rsi + - type: GiftIgnore - type: entity name: bang stick 100dmg @@ -116,6 +119,7 @@ damage: types: Blunt: 100 + - type: GiftIgnore - type: entity name: bang stick 200dmg @@ -129,3 +133,4 @@ damage: types: Blunt: 200 + - type: GiftIgnore diff --git a/Resources/Prototypes/Entities/Mobs/Player/admin_ghost.yml b/Resources/Prototypes/Entities/Mobs/Player/admin_ghost.yml index ae553682fc..af6ff0b10c 100644 --- a/Resources/Prototypes/Entities/Mobs/Player/admin_ghost.yml +++ b/Resources/Prototypes/Entities/Mobs/Player/admin_ghost.yml @@ -87,6 +87,7 @@ - type: IgnoreUIRange - type: ShowRevIcons - type: ShowZombieIcons + - type: ShowCultHud - type: Inventory templateId: aghost - type: InventorySlots diff --git a/Resources/Prototypes/Entities/Mobs/Species/base.yml b/Resources/Prototypes/Entities/Mobs/Species/base.yml index 34cba6da71..e1d7ae0b0e 100644 --- a/Resources/Prototypes/Entities/Mobs/Species/base.yml +++ b/Resources/Prototypes/Entities/Mobs/Species/base.yml @@ -217,6 +217,8 @@ type: SpellSelectorBUI - key: enum.CultEmpowerRemoveUiKey.Key type: SpellRemoverBUI + - key: enum.BloodRitesUi.Key + type: CultistFactoryBUI # WD-EDIT END - type: Puller - type: Butcherable diff --git a/Resources/Prototypes/Entities/Objects/Specific/syndicate.yml b/Resources/Prototypes/Entities/Objects/Specific/syndicate.yml index 4cab76a6cb..316080501b 100644 --- a/Resources/Prototypes/Entities/Objects/Specific/syndicate.yml +++ b/Resources/Prototypes/Entities/Objects/Specific/syndicate.yml @@ -85,7 +85,6 @@ preset: StorePresetUplink balance: Telecrystal: 20 - - type: GiftIgnore - type: entity parent: BaseUplinkRadio @@ -96,7 +95,6 @@ preset: StorePresetUplink balance: Telecrystal: 25 - - type: GiftIgnore #this uplink MUST be used for nukeops, as it has the tag for filtering the listing. - type: entity @@ -111,7 +109,6 @@ - type: Tag tags: - NukeOpsUplink - - type: GiftIgnore - type: entity parent: BaseUplinkRadio diff --git a/Resources/Prototypes/Entities/Objects/Weapons/Guns/Launchers/launchers.yml b/Resources/Prototypes/Entities/Objects/Weapons/Guns/Launchers/launchers.yml index 9970bec869..e6828f8c75 100644 --- a/Resources/Prototypes/Entities/Objects/Weapons/Guns/Launchers/launchers.yml +++ b/Resources/Prototypes/Entities/Objects/Weapons/Guns/Launchers/launchers.yml @@ -340,6 +340,7 @@ unshaded: True: { visible: true } False: { visible: false } + - type: GiftIgnore - type: entity name: force gun @@ -382,6 +383,7 @@ unshaded: True: { visible: true } False: { visible: false } + - type: GiftIgnore - type: entity name: meteor launcher @@ -395,6 +397,7 @@ tags: - CartridgeRocket proto: MeteorLarge + - type: GiftIgnore - type: entity name: immovable rod launcher @@ -408,3 +411,4 @@ tags: - CartridgeRocket proto: ImmovableRodSlow + - type: GiftIgnore diff --git a/Resources/Prototypes/Entities/Objects/Weapons/Guns/Projectiles/projectiles.yml b/Resources/Prototypes/Entities/Objects/Weapons/Guns/Projectiles/projectiles.yml index 48568e0b00..87d8d2a3a5 100644 --- a/Resources/Prototypes/Entities/Objects/Weapons/Guns/Projectiles/projectiles.yml +++ b/Resources/Prototypes/Entities/Objects/Weapons/Guns/Projectiles/projectiles.yml @@ -1379,3 +1379,19 @@ shader: unshaded - type: ChangeTemperatureOnCollide temperature: -20 + +- type: entity + name: blood bolt + id: BloodBolt + parent: BaseBullet + noSpawn: true + components: + - type: Sprite + sprite: White/Cult/Entities/arcane_barrage.rsi + state: bullet + - type: Ammo + muzzleFlash: null + - type: Projectile + damage: + types: + Piercing: 20 diff --git a/Resources/Prototypes/Entities/Objects/Weapons/Melee/cult.yml b/Resources/Prototypes/Entities/Objects/Weapons/Melee/cult.yml index 9a92b88590..0abfaae309 100644 --- a/Resources/Prototypes/Entities/Objects/Weapons/Melee/cult.yml +++ b/Resources/Prototypes/Entities/Objects/Weapons/Melee/cult.yml @@ -20,8 +20,10 @@ size: Normal - type: Clothing sprite: Objects/Weapons/Melee/cult_dagger.rsi + quickEquip: false slots: - back + - suitStorage - type: DisarmMalus - type: RuneDrawerProvider runePrototypes: [ OfferingRune, @@ -33,6 +35,10 @@ SummoningRune, BloodBoilRune, ApocalypseRune ] + - type: ActivatableUI + key: enum.ListViewSelectorUiKey.Key + inHandsOnly: true + closeOnHandDeselect: true - type: UserInterface interfaces: - key: enum.ListViewSelectorUiKey.Key @@ -64,8 +70,10 @@ size: Huge - type: Clothing sprite: Objects/Weapons/Melee/cult_blade.rsi + quickEquip: false slots: - back + - suitStorage - type: DisarmMalus - type: CultItem diff --git a/Resources/Prototypes/Entities/Structures/Walls/walls.yml b/Resources/Prototypes/Entities/Structures/Walls/walls.yml index ece31ec918..9aca56144b 100644 --- a/Resources/Prototypes/Entities/Structures/Walls/walls.yml +++ b/Resources/Prototypes/Entities/Structures/Walls/walls.yml @@ -197,13 +197,13 @@ thresholds: - trigger: !type:DamageTrigger - damage: 1200 + damage: 600 behaviors: - !type:DoActsBehavior acts: ["Destruction"] - trigger: !type:DamageTrigger - damage: 600 + damage: 300 behaviors: - !type:PlaySoundBehavior sound: @@ -220,6 +220,16 @@ - type: Construction graph: CultGirder node: wall + - type: Appearance + - type: Concealable + examinableWhileConcealed: true + concealedSprite: White/Cult/Structures/Concealed/cult.rsi + revealedSprite: Structures/Walls/cult.rsi + changeMeta: true + concealedName: обычная стена + concealedDesc: Удерживает воздух внутри, а ассистентов снаружи. + revealedName: стена культа + revealedDesc: Удерживает воздух внутри, а ассистентов снаружи. - type: entity parent: BaseWall diff --git a/Resources/Prototypes/White/Mobs/Species/harpy.yml b/Resources/Prototypes/White/Mobs/Species/harpy.yml index 351a45d5ae..1f16f43833 100644 --- a/Resources/Prototypes/White/Mobs/Species/harpy.yml +++ b/Resources/Prototypes/White/Mobs/Species/harpy.yml @@ -45,6 +45,8 @@ type: SpellSelectorBUI - key: enum.CultEmpowerRemoveUiKey.Key type: SpellRemoverBUI + - key: enum.BloodRitesUi.Key + type: CultistFactoryBUI # WD-EDIT END - type: Sprite scale: 0.9, 0.9 diff --git a/Resources/Prototypes/_White/Actions/cult_actions.yml b/Resources/Prototypes/_White/Actions/cult_actions.yml index 099a61f336..fd5098ee21 100644 --- a/Resources/Prototypes/_White/Actions/cult_actions.yml +++ b/Resources/Prototypes/_White/Actions/cult_actions.yml @@ -27,8 +27,6 @@ - HumanoidAppearance - Cultist canTargetSelf: true - deselectOnMiss: true - repeat: false icon: sprite: /Textures/White/Cult/actions_cult.rsi state: teleport @@ -50,8 +48,6 @@ - HumanoidAppearance - Cultist canTargetSelf: true - deselectOnMiss: true - repeat: false icon: sprite: /Textures/White/Cult/actions_cult.rsi state: armor @@ -72,8 +68,6 @@ components: - HumanoidAppearance canTargetSelf: false - deselectOnMiss: true - repeat: false icon: sprite: /Textures/White/Cult/actions_cult.rsi state: stun @@ -94,8 +88,6 @@ components: - HumanoidAppearance canTargetSelf: false - deselectOnMiss: true - repeat: false icon: sprite: /Textures/White/Cult/actions_cult.rsi state: shackles @@ -131,7 +123,7 @@ sprite: /Textures/White/Cult/actions_cult.rsi state: blood_rites event: !type:CultBloodRitesInstantActionEvent - charges: 4 + charges: 5 temporary: true removeOnNoCharges: true @@ -149,3 +141,27 @@ charges: 1 temporary: true removeOnNoCharges: true + +- type: entity + id: InstantActionConcealPresence + name: Conceal Presence + description: A multi-function spell that alternates between hiding and revealing nearby runes and cult structures. + noSpawn: true + components: + - type: InstantAction + icon: + sprite: /Textures/White/Cult/actions_cult.rsi + state: gone + event: !type:CultConcealInstantActionEvent + charges: 10 + temporary: true + removeOnNoCharges: true + - type: ConcealPresenceSpell + concealEvent: !type:CultConcealInstantActionEvent + revealEvent: !type:CultRevealInstantActionEvent + concealIcon: + sprite: /Textures/White/Cult/actions_cult.rsi + state: gone + revealIcon: + sprite: /Textures/White/Cult/actions_cult.rsi + state: telerune diff --git a/Resources/Prototypes/_White/Construction/Cult/graphs.yml b/Resources/Prototypes/_White/Construction/Cult/graphs.yml index 73a6574d4e..fafb578917 100644 --- a/Resources/Prototypes/_White/Construction/Cult/graphs.yml +++ b/Resources/Prototypes/_White/Construction/Cult/graphs.yml @@ -64,6 +64,18 @@ doAfter: 1 - node: airlock entity: AirlockGlassCult + edges: + - to: start + completed: + - !type:SpawnPrototype + prototype: CultRunicMetal1 + amount: 1 + - !type:DeleteEntity { } + conditions: + - !type:EntityAnchored + steps: + - tool: Dagger + cultistOnly: true - type: constructionGraph id: CultPylon diff --git a/Resources/Prototypes/_White/Construction/Cult/structures.yml b/Resources/Prototypes/_White/Construction/Cult/structures.yml index 212fa632ee..96c575d359 100644 --- a/Resources/Prototypes/_White/Construction/Cult/structures.yml +++ b/Resources/Prototypes/_White/Construction/Cult/structures.yml @@ -1,4 +1,4 @@ -- type: construction +- type: construction id: CultGirder name: руническая балка description: Большой конструктивный элемент, изготовленный из металла. На этом есть руна. @@ -37,7 +37,7 @@ - type: construction id: CultPylon name: пилон - description: Мистический конструкция. + description: Мистическая конструкция. graph: CultPylon startNode: start targetNode: pylon diff --git a/Resources/Prototypes/_White/Entities/Cult/Altars/cult_altars.yml b/Resources/Prototypes/_White/Entities/Cult/Altars/cult_altars.yml index ff772fa561..7498ec6ef5 100644 --- a/Resources/Prototypes/_White/Entities/Cult/Altars/cult_altars.yml +++ b/Resources/Prototypes/_White/Entities/Cult/Altars/cult_altars.yml @@ -68,6 +68,7 @@ - type: Construction graph: CultPylon node: pylon + - type: Concealable - type: entity id: AltarTome @@ -127,6 +128,7 @@ - type: Construction graph: AltarTome node: tome + - type: Concealable - type: entity id: CultBloodAltar @@ -185,6 +187,7 @@ - type: Construction graph: CultBloodAltar node: altar + - type: Concealable - type: entity id: CultForge @@ -247,6 +250,7 @@ - type: Construction graph: CultForge node: forge + - type: Concealable - type: cultStructure id: CultStructureAltarTome diff --git a/Resources/Prototypes/_White/Entities/Cult/Items/blood_barrage.yml b/Resources/Prototypes/_White/Entities/Cult/Items/blood_barrage.yml new file mode 100644 index 0000000000..2e0c5d4452 --- /dev/null +++ b/Resources/Prototypes/_White/Entities/Cult/Items/blood_barrage.yml @@ -0,0 +1,34 @@ +- type: entity + name: blood bolt barrage + parent: BaseItem + id: BloodBarrage + description: Blood for blood. + components: + - type: Sprite + sprite: White/Cult/Entities/arcane_barrage.rsi + state: icon + - type: Item + size: Ginormous + sprite: White/Cult/Entities/arcane_barrage.rsi + - type: AmmoCounter + - type: Gun + fireRate: 4 + selectedMode: SemiAuto + availableModes: + - SemiAuto + soundEmpty: null + soundGunshot: + path: /Audio/White/Cult/wand_teleport.ogg + - type: BasicEntityAmmoProvider + proto: BloodBolt + capacity: 25 + - type: BoltBarrage + - type: GiftIgnore + +- type: cultistFactoryProduction + id: FactoryCultBloodBarrage + name: Залп Кровавых Снарядов (300 крови) + icon: "/Textures/White/Cult/Entities/arcane_barrage.rsi/icon.png" + bloodCost: 300 + item: + - BloodBarrage diff --git a/Resources/Prototypes/_White/Entities/Cult/Items/soul_shard.yml b/Resources/Prototypes/_White/Entities/Cult/Items/soul_shard.yml index 58a4d115d7..1c95a849dd 100644 --- a/Resources/Prototypes/_White/Entities/Cult/Items/soul_shard.yml +++ b/Resources/Prototypes/_White/Entities/Cult/Items/soul_shard.yml @@ -1,4 +1,4 @@ -- type: entity +- type: entity name: soul shard description: Mysterious glowing shard. parent: BaseItem @@ -25,6 +25,7 @@ True: { state: "soulstone2" } False: { state: "soulstone" } - type: Speech + - type: IsDeadIC - type: entity parent: SoulShard diff --git a/Resources/Prototypes/_White/Entities/Cult/Items/tome_craft.yml b/Resources/Prototypes/_White/Entities/Cult/Items/tome_craft.yml index bd532120f0..3816ff0208 100644 --- a/Resources/Prototypes/_White/Entities/Cult/Items/tome_craft.yml +++ b/Resources/Prototypes/_White/Entities/Cult/Items/tome_craft.yml @@ -49,6 +49,7 @@ - type: ShowHealthIcons damageContainers: - Biological + - type: ShowSecurityIcons - type: entity name: veil shifter diff --git a/Resources/Prototypes/_White/Entities/Cult/Runes/cult_runes.yml b/Resources/Prototypes/_White/Entities/Cult/Runes/cult_runes.yml index 587fad5cab..0ab3d8e9ed 100644 --- a/Resources/Prototypes/_White/Entities/Cult/Runes/cult_runes.yml +++ b/Resources/Prototypes/_White/Entities/Cult/Runes/cult_runes.yml @@ -14,6 +14,7 @@ invokePhrase: "Mah'weyh pleggh at e'ntrath!" - type: CultRuneOffering - type: CultRune + - type: Concealable - type: entity parent: CollideRune @@ -31,6 +32,7 @@ invokePhrase: "Qu'laris ver'don, thal'sorin mik'thar!" - type: CultRuneBuff - type: CultRune + - type: Concealable - type: entity parent: CollideRune @@ -52,6 +54,8 @@ - key: enum.CultEmpowerUiKey.Key type: SpellSelectorBUI - type: CultRune + - type: Concealable + examinableWhileConcealed: true - type: entity parent: CollideRune @@ -71,6 +75,7 @@ - type: CultRuneTeleport label: "безымянная метка" - type: CultRune + - type: Concealable - type: entity parent: CollideRune @@ -89,6 +94,7 @@ gatherInvokers: true - type: CultRuneSummoning - type: CultRune + - type: Concealable - type: entity parent: CollideRune @@ -106,6 +112,7 @@ invokePhrase: "Pasnar val'keriam usinar. Savrae ines amutan. Yam'toth remium il'tarat!" - type: CultRuneRevive - type: CultRune + - type: Concealable - type: entity parent: CollideRune @@ -123,6 +130,7 @@ invokePhrase: "Khari'd! Eske'te tannin!" - type: CultRuneBarrier - type: CultRune + - type: Concealable - type: entity parent: CollideRune @@ -141,6 +149,7 @@ - type: CultRuneBloodBoil projectilePrototype: ProjectileCult - type: CultRune + - type: Concealable - type: entity parent: BaseRune @@ -155,4 +164,5 @@ - type: Appearance - type: CultRuneBase invokePhrase: "TOK-LYR RQA-NAP G'OLT-ULOFT!" + cultistGatheringRange: 1.5 - type: CultRuneApocalypse diff --git a/Resources/Prototypes/_White/Entities/Cult/Weapons/blood_spear.yml b/Resources/Prototypes/_White/Entities/Cult/Weapons/blood_spear.yml new file mode 100644 index 0000000000..dfcdfc7c9b --- /dev/null +++ b/Resources/Prototypes/_White/Entities/Cult/Weapons/blood_spear.yml @@ -0,0 +1,82 @@ +- type: entity + name: blood spear + parent: BaseItem + id: BloodSpear + description: A sickening spear composed entirely of crystallized blood. + components: + - 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: + - ItemMask + restitution: 0.3 + friction: 0.2 + - type: Sharp + - type: Sprite + sprite: White/Cult/Entities/blood_spear.rsi + state: icon + - type: MeleeWeapon + wideAnimationRotation: -135 + damage: + types: + Piercing: 36 + angle: 0 + animation: WeaponArcThrust + soundHit: + path: /Audio/Weapons/bladeslice.ogg + range: 2 + - type: DamageOtherOnHit + damage: + types: + Piercing: 40 + - type: Item + sprite: White/Cult/Entities/blood_spear.rsi + size: Ginormous + - type: Clothing + slots: + - back + - type: Wieldable + - type: IncreaseDamageOnWield + damage: + types: + Piercing: 8 + - type: UseDelay + - type: DisarmMalus + - type: CultItem + - type: BloodSpear + action: InstantActionRecallBloodSpear + - type: ChangeThrowForce + throwForce: 20 + +- type: cultistFactoryProduction + id: FactoryCultBloodSpear + name: Кровавое Копьё (150 крови) + icon: "/Textures/White/Cult/Entities/blood_spear.rsi/icon.png" + bloodCost: 150 + item: + - BloodSpear + +- type: entity + id: InstantActionRecallBloodSpear + name: Recall Blood Spear + description: Recalls blood spear to your hand. + noSpawn: true + components: + - type: InstantAction + icon: + sprite: /Textures/White/Cult/actions_cult.rsi + state: bloodspear + event: !type:CultBloodSpearRecallInstantActionEvent + useDelay: 1 + temporary: true diff --git a/Resources/Prototypes/_White/Entities/Cult/constructs.yml b/Resources/Prototypes/_White/Entities/Cult/constructs.yml index e3793c1cc4..25704d1d7a 100644 --- a/Resources/Prototypes/_White/Entities/Cult/constructs.yml +++ b/Resources/Prototypes/_White/Entities/Cult/constructs.yml @@ -60,6 +60,18 @@ - type: Pullable - type: ContentEye - type: Actions + - type: Hands + - type: ShowCultHud + - type: IsDeadIC + - type: NightVision + toggleSound: null + color: White + - type: ShowWhiteHealthBars + damageContainers: + - Biological + - type: ShowHealthIcons + damageContainers: + - Biological - type: Tag tags: - CannotSuicide @@ -77,17 +89,15 @@ thresholds: 0: Alive 150: Dead - - type: MovementSpeedModifier - baseWalkSpeed: 2 - baseSprintSpeed: 2 - type: Construct actions: - InstantActionJuggernautCreateWall - type: MeleeWeapon + canHeavyAttack: false hidden: true angle: 30 - animation: WeaponArcFist - attackRate: 0.25 + animation: WeaponArcSmash + attackRate: 1 damage: types: Structural: 90 @@ -114,13 +124,18 @@ InstantActionArtificerCreateCultistWall, InstantActionArtificerCreateCultistAirlock, ] + - type: MovementSpeedModifier + baseWalkSpeed: 3 + baseSprintSpeed: 3 + - type: MovementIgnoreGravity - type: Puller needsHands: false - type: MeleeWeapon + canHeavyAttack: false hidden: true angle: 30 - animation: WeaponArcFist - attackRate: 0.85 + animation: WeaponArcPunch + attackRate: 1 damage: types: Blunt: 5 @@ -133,8 +148,8 @@ description: run... components: - type: MovementSpeedModifier - baseWalkSpeed: 3.0 - baseSprintSpeed: 3.0 + baseWalkSpeed: 4.0 + baseSprintSpeed: 4.0 - type: MobThresholds thresholds: 0: Alive @@ -148,14 +163,16 @@ actions: [InstantActionWraithPhase] - type: MovementIgnoreGravity - type: MeleeWeapon + canHeavyAttack: false hidden: true angle: 30 - animation: WeaponArcFist - attackRate: 0.5 + animation: WeaponArcPunch + attackRate: 1 damage: types: Blunt: 10 Slash: 10 + Structural: 40 - type: entity id: ReaperConstruct @@ -165,8 +182,8 @@ components: - type: MovementIgnoreGravity - type: MovementSpeedModifier - baseWalkSpeed: 3.0 - baseSprintSpeed: 3.0 + baseWalkSpeed: 5.0 + baseSprintSpeed: 5.0 - type: MobThresholds thresholds: 0: Alive @@ -176,10 +193,11 @@ - type: Puller needsHands: false - type: MeleeWeapon + canHeavyAttack: false hidden: true angle: 30 - animation: WeaponArcFist - attackRate: 0.9 + animation: WeaponArcPunch + attackRate: 1 damage: types: Blunt: 20 diff --git a/Resources/Prototypes/_White/Entities/Cult/other_structures.yml b/Resources/Prototypes/_White/Entities/Cult/other_structures.yml index 85244ec319..78a1a35f5d 100644 --- a/Resources/Prototypes/_White/Entities/Cult/other_structures.yml +++ b/Resources/Prototypes/_White/Entities/Cult/other_structures.yml @@ -9,7 +9,7 @@ Brute: collection: GlassSmash - type: Sprite - sprite: /Textures/White/Cult/Structures/cult_airlock.rsi + sprite: White/Cult/Structures/cult_airlock.rsi layers: - state: closed map: ["enum.DoorVisualLayers.Base"] @@ -18,8 +18,8 @@ - type: Occluder enabled: false - type: Door - occludes: false bumpOpen: true + occludes: false crushDamage: types: Blunt: 15 @@ -27,8 +27,6 @@ path: /Audio/Effects/stonedoor_openclose.ogg closeSound: path: /Audio/Effects/stonedoor_openclose.ogg - denySound: - path: /Audio/Machines/airlock_deny.ogg - type: Airtight fixVacuum: true noAirWhenFullyAirBlocked: false @@ -38,7 +36,7 @@ thresholds: - trigger: !type:DamageTrigger - damage: 500 + damage: 200 behaviors: - !type:DoActsBehavior acts: ["Destruction"] @@ -48,6 +46,22 @@ node: airlock - type: PlacementReplacement key: walls + - type: Appearance + - type: Concealable + examinableWhileConcealed: true + iconSmooth: true + interactionOutline: true + concealedSprite: White/Cult/Structures/Concealed/cult_airlock.rsi + revealedSprite: White/Cult/Structures/cult_airlock.rsi + changeMeta: true + concealedName: обычная стена + concealedDesc: Удерживает воздух внутри, а ассистентов снаружи. + revealedName: рунический шлюз + revealedDesc: Странный стеклянный шлюз с руной. + - type: IconSmooth + enabled: false + key: walls + mode: NoSprite placement: mode: SnapgridCenter @@ -75,7 +89,7 @@ - GlassAirlockLayer - type: InteractionOutline - type: Sprite - sprite: /Textures/White/Cult/Structures/cult_girder.rsi + sprite: White/Cult/Structures/cult_girder.rsi state: cultgirder - type: Damageable damageContainer: StructuralInorganic @@ -104,3 +118,13 @@ - type: Construction graph: CultGirder node: girder + - type: Appearance + - type: Concealable + examinableWhileConcealed: true + concealedSprite: White/Cult/Structures/Concealed/cult_girder.rsi + revealedSprite: White/Cult/Structures/cult_girder.rsi + changeMeta: true + concealedName: каркас + concealedDesc: Большой металлический каркас; Необходимо покрыть листами металла, чтобы он считался стеной. + revealedName: руническая балка + revealedDesc: Большой конструктивный элемент, изготовленный из металла. На этом есть руна. diff --git a/Resources/Prototypes/_White/Tiles/cult.yml b/Resources/Prototypes/_White/Tiles/cult.yml index 8e735e076d..ae49e33345 100644 --- a/Resources/Prototypes/_White/Tiles/cult.yml +++ b/Resources/Prototypes/_White/Tiles/cult.yml @@ -1,4 +1,4 @@ -- type: tile +- type: tile id: CultFloor name: tiles-cult-floor sprite: /Textures/White/Cult/Tiles/cult_tile.rsi/cult.png @@ -10,3 +10,20 @@ collection: FootstepFloor itemDrop: FloorTileItemSteel heatCapacity: 10000 + +- type: tile + id: CultFloorConcealed + name: tiles-steel-floor + sprite: /Textures/White/Cult/Tiles/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/_White/narsie.yml b/Resources/Prototypes/_White/narsie.yml index f25956f309..221ed2fa13 100644 --- a/Resources/Prototypes/_White/narsie.yml +++ b/Resources/Prototypes/_White/narsie.yml @@ -1,7 +1,20 @@ -- type: entity +- type: entity id: Narsie + parent: BaseMob name: Nar'si components: + - type: AnnounceOnSpawn + message: narsie-has-risen + sender: narsie-has-risen-sender + sound: + path: /Audio/Misc/narsie_rises.ogg + color: darkred + - type: CargoSellBlacklist + - type: ContentEye + maxZoom: 2.0,2.0 + - type: WarpPoint + follow: true + location: Nar'Sie - type: Sprite layers: - map: [ "enum.NarsieLayer.Default" ] @@ -15,6 +28,8 @@ - type: Narsie - type: Physics bodyType: Dynamic + bodyStatus: InAir + - type: CanMoveInAir - type: Fixtures fixtures: Penis: diff --git a/Resources/Textures/White/Cult/Entities/arcane_barrage.rsi/bullet.png b/Resources/Textures/White/Cult/Entities/arcane_barrage.rsi/bullet.png new file mode 100644 index 0000000000..98351139f2 Binary files /dev/null and b/Resources/Textures/White/Cult/Entities/arcane_barrage.rsi/bullet.png differ diff --git a/Resources/Textures/White/Cult/Entities/arcane_barrage.rsi/icon.png b/Resources/Textures/White/Cult/Entities/arcane_barrage.rsi/icon.png new file mode 100644 index 0000000000..678418769a Binary files /dev/null and b/Resources/Textures/White/Cult/Entities/arcane_barrage.rsi/icon.png differ diff --git a/Resources/Textures/White/Cult/Entities/arcane_barrage.rsi/inhand-left.png b/Resources/Textures/White/Cult/Entities/arcane_barrage.rsi/inhand-left.png new file mode 100644 index 0000000000..5aa69c6f0c Binary files /dev/null and b/Resources/Textures/White/Cult/Entities/arcane_barrage.rsi/inhand-left.png differ diff --git a/Resources/Textures/White/Cult/Entities/arcane_barrage.rsi/inhand-right.png b/Resources/Textures/White/Cult/Entities/arcane_barrage.rsi/inhand-right.png new file mode 100644 index 0000000000..72d7a44ff7 Binary files /dev/null and b/Resources/Textures/White/Cult/Entities/arcane_barrage.rsi/inhand-right.png differ diff --git a/Resources/Textures/White/Cult/Entities/arcane_barrage.rsi/meta.json b/Resources/Textures/White/Cult/Entities/arcane_barrage.rsi/meta.json new file mode 100644 index 0000000000..39fae95edd --- /dev/null +++ b/Resources/Textures/White/Cult/Entities/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/White/Cult/Entities/blood_spear.rsi/icon.png b/Resources/Textures/White/Cult/Entities/blood_spear.rsi/icon.png new file mode 100644 index 0000000000..8a1814a9bb Binary files /dev/null and b/Resources/Textures/White/Cult/Entities/blood_spear.rsi/icon.png differ diff --git a/Resources/Textures/White/Cult/Entities/blood_spear.rsi/inhand-left.png b/Resources/Textures/White/Cult/Entities/blood_spear.rsi/inhand-left.png new file mode 100644 index 0000000000..6dbe881610 Binary files /dev/null and b/Resources/Textures/White/Cult/Entities/blood_spear.rsi/inhand-left.png differ diff --git a/Resources/Textures/White/Cult/Entities/blood_spear.rsi/inhand-right.png b/Resources/Textures/White/Cult/Entities/blood_spear.rsi/inhand-right.png new file mode 100644 index 0000000000..ff278aaf9b Binary files /dev/null and b/Resources/Textures/White/Cult/Entities/blood_spear.rsi/inhand-right.png differ diff --git a/Resources/Textures/White/Cult/Entities/blood_spear.rsi/meta.json b/Resources/Textures/White/Cult/Entities/blood_spear.rsi/meta.json new file mode 100644 index 0000000000..caacbe7781 --- /dev/null +++ b/Resources/Textures/White/Cult/Entities/blood_spear.rsi/meta.json @@ -0,0 +1,30 @@ +{ + "version": 1, + "license": "CC-BY-SA-3.0", + "copyright": "Taken from yogstation", + "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/White/Cult/Entities/blood_spear.rsi/wielded-inhand-left.png b/Resources/Textures/White/Cult/Entities/blood_spear.rsi/wielded-inhand-left.png new file mode 100644 index 0000000000..def85c257f Binary files /dev/null and b/Resources/Textures/White/Cult/Entities/blood_spear.rsi/wielded-inhand-left.png differ diff --git a/Resources/Textures/White/Cult/Entities/blood_spear.rsi/wielded-inhand-right.png b/Resources/Textures/White/Cult/Entities/blood_spear.rsi/wielded-inhand-right.png new file mode 100644 index 0000000000..def85c257f Binary files /dev/null and b/Resources/Textures/White/Cult/Entities/blood_spear.rsi/wielded-inhand-right.png differ diff --git a/Resources/Textures/White/Cult/Structures/Concealed/cult.rsi/cult0.png b/Resources/Textures/White/Cult/Structures/Concealed/cult.rsi/cult0.png new file mode 100644 index 0000000000..404b6ac1c6 Binary files /dev/null and b/Resources/Textures/White/Cult/Structures/Concealed/cult.rsi/cult0.png differ diff --git a/Resources/Textures/White/Cult/Structures/Concealed/cult.rsi/cult1.png b/Resources/Textures/White/Cult/Structures/Concealed/cult.rsi/cult1.png new file mode 100644 index 0000000000..258b734263 Binary files /dev/null and b/Resources/Textures/White/Cult/Structures/Concealed/cult.rsi/cult1.png differ diff --git a/Resources/Textures/White/Cult/Structures/Concealed/cult.rsi/cult2.png b/Resources/Textures/White/Cult/Structures/Concealed/cult.rsi/cult2.png new file mode 100644 index 0000000000..404b6ac1c6 Binary files /dev/null and b/Resources/Textures/White/Cult/Structures/Concealed/cult.rsi/cult2.png differ diff --git a/Resources/Textures/White/Cult/Structures/Concealed/cult.rsi/cult3.png b/Resources/Textures/White/Cult/Structures/Concealed/cult.rsi/cult3.png new file mode 100644 index 0000000000..258b734263 Binary files /dev/null and b/Resources/Textures/White/Cult/Structures/Concealed/cult.rsi/cult3.png differ diff --git a/Resources/Textures/White/Cult/Structures/Concealed/cult.rsi/cult4.png b/Resources/Textures/White/Cult/Structures/Concealed/cult.rsi/cult4.png new file mode 100644 index 0000000000..5af1322d5e Binary files /dev/null and b/Resources/Textures/White/Cult/Structures/Concealed/cult.rsi/cult4.png differ diff --git a/Resources/Textures/White/Cult/Structures/Concealed/cult.rsi/cult5.png b/Resources/Textures/White/Cult/Structures/Concealed/cult.rsi/cult5.png new file mode 100644 index 0000000000..773e85c48a Binary files /dev/null and b/Resources/Textures/White/Cult/Structures/Concealed/cult.rsi/cult5.png differ diff --git a/Resources/Textures/White/Cult/Structures/Concealed/cult.rsi/cult6.png b/Resources/Textures/White/Cult/Structures/Concealed/cult.rsi/cult6.png new file mode 100644 index 0000000000..5af1322d5e Binary files /dev/null and b/Resources/Textures/White/Cult/Structures/Concealed/cult.rsi/cult6.png differ diff --git a/Resources/Textures/White/Cult/Structures/Concealed/cult.rsi/cult7.png b/Resources/Textures/White/Cult/Structures/Concealed/cult.rsi/cult7.png new file mode 100644 index 0000000000..47900fe305 Binary files /dev/null and b/Resources/Textures/White/Cult/Structures/Concealed/cult.rsi/cult7.png differ diff --git a/Resources/Textures/White/Cult/Structures/Concealed/cult.rsi/full.png b/Resources/Textures/White/Cult/Structures/Concealed/cult.rsi/full.png new file mode 100644 index 0000000000..c3f6fbc79f Binary files /dev/null and b/Resources/Textures/White/Cult/Structures/Concealed/cult.rsi/full.png differ diff --git a/Resources/Textures/White/Cult/Structures/Concealed/cult.rsi/meta.json b/Resources/Textures/White/Cult/Structures/Concealed/cult.rsi/meta.json new file mode 100644 index 0000000000..c289aa5878 --- /dev/null +++ b/Resources/Textures/White/Cult/Structures/Concealed/cult.rsi/meta.json @@ -0,0 +1 @@ +{"version": 1, "size": {"x": 32, "y": 32}, "license": "CC-BY-SA-3.0", "copyright": "https://github.com/vgstation-coders/vgstation13/raw/99cc2ab62d65a3a7b554dc7b21ff5f57c835f973/icons/turf/walls.dmi", "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"}]} \ No newline at end of file diff --git a/Resources/Textures/White/Cult/Structures/Concealed/cult_airlock.rsi/assembly.png b/Resources/Textures/White/Cult/Structures/Concealed/cult_airlock.rsi/assembly.png new file mode 100644 index 0000000000..e872d82c5d Binary files /dev/null and b/Resources/Textures/White/Cult/Structures/Concealed/cult_airlock.rsi/assembly.png differ diff --git a/Resources/Textures/White/Cult/Structures/Concealed/cult_airlock.rsi/closed.png b/Resources/Textures/White/Cult/Structures/Concealed/cult_airlock.rsi/closed.png new file mode 100644 index 0000000000..198151d903 Binary files /dev/null and b/Resources/Textures/White/Cult/Structures/Concealed/cult_airlock.rsi/closed.png differ diff --git a/Resources/Textures/White/Cult/Structures/Concealed/cult_airlock.rsi/closing.png b/Resources/Textures/White/Cult/Structures/Concealed/cult_airlock.rsi/closing.png new file mode 100644 index 0000000000..96bba25801 Binary files /dev/null and b/Resources/Textures/White/Cult/Structures/Concealed/cult_airlock.rsi/closing.png differ diff --git a/Resources/Textures/White/Cult/Structures/Concealed/cult_airlock.rsi/meta.json b/Resources/Textures/White/Cult/Structures/Concealed/cult_airlock.rsi/meta.json new file mode 100644 index 0000000000..7e8135f216 --- /dev/null +++ b/Resources/Textures/White/Cult/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/White/Cult/Structures/Concealed/cult_airlock.rsi/open.png b/Resources/Textures/White/Cult/Structures/Concealed/cult_airlock.rsi/open.png new file mode 100644 index 0000000000..7ca06de1ed Binary files /dev/null and b/Resources/Textures/White/Cult/Structures/Concealed/cult_airlock.rsi/open.png differ diff --git a/Resources/Textures/White/Cult/Structures/Concealed/cult_airlock.rsi/opening.png b/Resources/Textures/White/Cult/Structures/Concealed/cult_airlock.rsi/opening.png new file mode 100644 index 0000000000..861f6fbbc3 Binary files /dev/null and b/Resources/Textures/White/Cult/Structures/Concealed/cult_airlock.rsi/opening.png differ diff --git a/Resources/Textures/White/Cult/Structures/Concealed/cult_girder.rsi/cultgirder.png b/Resources/Textures/White/Cult/Structures/Concealed/cult_girder.rsi/cultgirder.png new file mode 100644 index 0000000000..2453b42338 Binary files /dev/null and b/Resources/Textures/White/Cult/Structures/Concealed/cult_girder.rsi/cultgirder.png differ diff --git a/Resources/Textures/White/Cult/Structures/Concealed/cult_girder.rsi/meta.json b/Resources/Textures/White/Cult/Structures/Concealed/cult_girder.rsi/meta.json new file mode 100644 index 0000000000..7ea2c16749 --- /dev/null +++ b/Resources/Textures/White/Cult/Structures/Concealed/cult_girder.rsi/meta.json @@ -0,0 +1,14 @@ +{ + "version": 1, + "license": "CC-BY-SA-3.0", + "copyright": "https://github.com/frosty-dev/white", + "size": { + "x": 32, + "y": 32 + }, + "states": [ + { + "name": "cultgirder" + } + ] +} \ No newline at end of file diff --git a/Resources/Textures/White/Cult/Tiles/concealed.png b/Resources/Textures/White/Cult/Tiles/concealed.png new file mode 100644 index 0000000000..1c9c4588d1 Binary files /dev/null and b/Resources/Textures/White/Cult/Tiles/concealed.png differ