Cult update (#220)

* - tweak: Cult door not bump openable.

* - tweak: Better summoning and Narsie

* - tweak: Construct update.

* - tweak: Eldrich blade fits in suit storage.

* - tweak: More spell limit.

* - fix: Fix pylon desc.

* - tweak: Teleport works on cuffed targets.

* - tweak: More popups if target is holy.

* - fix: No rune drawing using fingers.

* - tweak: Better pylon placement & less pylon healing range.

* - tweak: More blood rites charge.

* - fix: Fix max spell amount.

* - tweak: Less cult door and wall health.

* - fix: Constructs are dead IC.

* - add: Revive rune now notifies player.

* - add: Narsie summon rune eui.

* - fix: Fix narsie summon sound not playing for reapers.

* - tweak: Whatever.

* - add: Conceal presence spell.

* - tweak: Tweakz.

* - add: Blood spear.

* - add: Blood boil barrage.

* - tweak: Artificer flies.

* - tweak: Blood bolt color tweaks.

* - tweak: Runic door is bump openable again.

* - fix: Fix concealed door outline.

* - add: Update concealable name and desc.

* - tweak: Remove the unremoveable.

* - tweak: Gift ignore.

* - add: Organs regenerate on rejuvenate.

* - tweak: Brainless cultist is fine.

* - add: Added more fun.

* - add: Add rune descriptions.

* - fix: Fixes.

* - tweak: Blood rites now uses verb.

* - tweak: Bring it back.
This commit is contained in:
Aviu00
2024-03-22 17:23:33 +09:00
committed by GitHub
parent 1337e4d26e
commit 74ef19d6a6
109 changed files with 1831 additions and 182 deletions

View File

@@ -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<ConcealableComponent>
{
[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<bool>(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<InteractionOutlineComponent>(uid);
}
}
else
EnsureComp<InteractionOutlineComponent>(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);
}
}
}

View File

@@ -1,4 +1,5 @@
using Content.Shared._White.Cult; using Content.Shared._White.Cult;
using Content.Shared._White.Cult.Components;
using Robust.Client.Graphics; using Robust.Client.Graphics;
using Robust.Client.Player; using Robust.Client.Player;
using Robust.Shared.Player; using Robust.Shared.Player;
@@ -20,10 +21,15 @@ public sealed class ShowCultHudSystem : EntitySystem
SubscribeLocalEvent<CultistComponent, PlayerAttachedEvent>(OnPlayerAttached); SubscribeLocalEvent<CultistComponent, PlayerAttachedEvent>(OnPlayerAttached);
SubscribeLocalEvent<CultistComponent, PlayerDetachedEvent>(OnPlayerDetached); SubscribeLocalEvent<CultistComponent, PlayerDetachedEvent>(OnPlayerDetached);
SubscribeLocalEvent<ShowCultHudComponent, ComponentInit>(OnComponentInit);
SubscribeLocalEvent<ShowCultHudComponent, ComponentRemove>(OnComponentRemoved);
SubscribeLocalEvent<ShowCultHudComponent, PlayerAttachedEvent>(OnPlayerAttached);
SubscribeLocalEvent<ShowCultHudComponent, PlayerDetachedEvent>(OnPlayerDetached);
_overlay = new CultHudOverlay(EntityManager); _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) if (_player.LocalSession?.AttachedEntity != uid)
return; 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) if (_player.LocalSession?.AttachedEntity != uid)
return; 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) if (_player.LocalSession != args.Player)
return; return;
@@ -49,7 +55,7 @@ public sealed class ShowCultHudSystem : EntitySystem
_overlayManager.AddOverlay(_overlay); _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) if (_player.LocalSession != args.Player)
return; return;

View File

@@ -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<IClyde>().RequestWindowAttention();
_menu.OpenCentered();
}
public override void Closed()
{
base.Closed();
SendMessage(new ApocalypseRuneDrawMessage(false));
_menu.Close();
}
}

View File

@@ -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"),
})
}
},
}
},
}
});
}
}

View File

@@ -32,6 +32,8 @@ public sealed class CultistFactoryBUI : BoundUserInterface
_radialContainer = new RadialContainer(); _radialContainer = new RadialContainer();
_radialContainer.Closed += Close;
if (State != null) if (State != null)
UpdateState(State); UpdateState(State);
} }

View File

@@ -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.Controls;
using Robust.Client.UserInterface.CustomControls; using Robust.Client.UserInterface.CustomControls;
using Robust.Client.UserInterface.XAML; using Robust.Client.UserInterface.XAML;
@@ -7,19 +8,21 @@ using Robust.Shared.Prototypes;
namespace Content.Client._White.Cult.UI.ListViewSelector; namespace Content.Client._White.Cult.UI.ListViewSelector;
[GenerateTypedNameReferences] [GenerateTypedNameReferences]
public partial class ListViewSelectorWindow : DefaultWindow public sealed partial class ListViewSelectorWindow : DefaultWindow
{ {
public Action<string, int>? ItemSelected; public Action<string, int>? ItemSelected;
private readonly IPrototypeManager _prototypeManager; private readonly IPrototypeManager _prototypeManager;
public string TooltipText = string.Empty;
public ListViewSelectorWindow(IPrototypeManager prototypeManager) public ListViewSelectorWindow(IPrototypeManager prototypeManager)
{ {
RobustXamlLoader.Load(this); RobustXamlLoader.Load(this);
_prototypeManager = prototypeManager; _prototypeManager = prototypeManager;
} }
public void PopulateList(List<string> items, bool isPrototypes) public void PopulateList(List<EntProtoId> items, bool isPrototypes)
{ {
ItemsContainer.RemoveAllChildren(); ItemsContainer.RemoveAllChildren();
@@ -27,16 +30,16 @@ public partial class ListViewSelectorWindow : DefaultWindow
{ {
var button = new Button(); var button = new Button();
var itemName = Loc.GetString($"ent-{item}"); var itemName = Loc.GetString($"ent-{item}");
var itemDesc = string.Empty;
if (isPrototypes) if (isPrototypes && _prototypeManager.TryIndex<EntityPrototype>(item, out var itemPrototype))
{ {
if (_prototypeManager.TryIndex<EntityPrototype>(item, out var itemPrototype)) itemName = itemPrototype.Name;
{ itemDesc = itemPrototype.Description;
itemName = itemPrototype.Name;
}
} }
button.Text = itemName; button.Text = itemName;
button.TooltipSupplier = _ => new RecipeTooltip(itemDesc);
button.OnPressed += _ => ItemSelected?.Invoke(item, items.IndexOf(item)); button.OnPressed += _ => ItemSelected?.Invoke(item, items.IndexOf(item));

View File

@@ -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<ConstructionSystem>();
CanRotate = prototype?.CanRotate ?? true;
}
/// <inheritdoc />
public override bool HijackPlacementRequest(EntityCoordinates coordinates)
{
if (_prototype == null)
return true;
if (CheckForStructure(coordinates))
{
var popup = _entMan.System<SharedPopupSystem>();
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<EntityLookupSystem>();
var entities = lookupSystem.GetEntitiesInRange(coordinates, 10f);
foreach (var ent in entities)
{
if (!_entMan.TryGetComponent<MetaDataComponent>(ent, out var metadata))
continue;
if (metadata.EntityPrototype?.ID is "CultPylon")
return true;
}
return false;
}
/// <inheritdoc />
public override bool HijackDeletion(EntityUid entity)
{
if (IoCManager.Resolve<IEntityManager>().HasComponent<ConstructionGhostComponent>(entity))
{
_constructionSystem.ClearGhost(entity.GetHashCode());
}
return true;
}
/// <inheritdoc />
public override void StartHijack(PlacementManager manager)
{
base.StartHijack(manager);
manager.CurrentTextures = _prototype?.Layers.Select(sprite => sprite.DirFrame0()).ToList();
}
}

View File

@@ -86,43 +86,23 @@ public sealed class StructureCraftBoundUserInterface : BoundUserInterface
if (construct == null) if (construct == null)
return; return;
var player = _player.LocalPlayer?.ControlledEntity; var player = _player.LocalEntity;
if (player == null) if (player == null)
return; return;
if (construct.ID == "CultPylon" && CheckForStructure(player, id)) PlacementHijack hijack;
{
var popup = _entMan.System<SharedPopupSystem>();
popup.PopupClient(Loc.GetString("cult-structure-craft-another-structure-nearby"), player.Value, player.Value);
return;
}
var constructSystem = _systemManager.GetEntitySystem<ConstructionSystem>(); if (construct.ID == "CultPylon")
var hijack = new ConstructionPlacementHijack(constructSystem, construct); {
hijack = new CultPylonPlacementHijack(construct, _entMan, player.Value);
}
else
{
var constructSystem = _systemManager.GetEntitySystem<ConstructionSystem>();
hijack = new ConstructionPlacementHijack(constructSystem, construct);
}
_placement.BeginPlacing(newObj, hijack); _placement.BeginPlacing(newObj, hijack);
} }
private bool CheckForStructure(EntityUid? uid, string id)
{
if (uid == null)
return false;
if (!_entMan.TryGetComponent<TransformComponent>(uid, out var transform))
return false;
var lookupSystem = _entMan.System<EntityLookupSystem>();
var entities = lookupSystem.GetEntitiesInRange(transform.Coordinates, 15f);
foreach (var ent in entities)
{
if (!_entMan.TryGetComponent<MetaDataComponent>(ent, out var metadata))
continue;
if (metadata.EntityPrototype?.ID == id)
return true;
}
return false;
}
} }

View File

@@ -55,6 +55,13 @@ public sealed class ChangelingRuleSystem : GameRuleSystem<ChangelingRuleComponen
SubscribeLocalEvent<RoundRestartCleanupEvent>(ClearUsedNames); SubscribeLocalEvent<RoundRestartCleanupEvent>(ClearUsedNames);
SubscribeLocalEvent<ChangelingRuleComponent, ObjectivesTextGetInfoEvent>(OnObjectivesTextGetInfo); SubscribeLocalEvent<ChangelingRuleComponent, ObjectivesTextGetInfoEvent>(OnObjectivesTextGetInfo);
SubscribeLocalEvent<ChangelingRoleComponent, GetBriefingEvent>(OnGetBriefing);
}
private void OnGetBriefing(Entity<ChangelingRoleComponent> ent, ref GetBriefingEvent args)
{
args.Append(Loc.GetString("changeling-role-briefing-short"));
} }
protected override void ActiveTick( protected override void ActiveTick(
@@ -176,13 +183,6 @@ public sealed class ChangelingRuleSystem : GameRuleSystem<ChangelingRuleComponen
PrototypeId = changelingRule.ChangelingPrototypeId PrototypeId = changelingRule.ChangelingPrototypeId
}, mind); }, mind);
var briefing = Loc.GetString("changeling-role-briefing-short");
_roleSystem.MindAddRole(mindId, new RoleBriefingComponent
{
Briefing = briefing
}, mind, true);
_roleSystem.MindPlaySound(mindId, changelingRule.GreetSoundNotification, mind); _roleSystem.MindPlaySound(mindId, changelingRule.GreetSoundNotification, mind);
SendChangelingBriefing(mindId); SendChangelingBriefing(mindId);
changelingRule.ChangelingMinds.Add(mindId); changelingRule.ChangelingMinds.Add(mindId);

View File

@@ -24,6 +24,7 @@ public sealed class DoorSystem : SharedDoorSystem
[Dependency] private readonly DoorBoltSystem _bolts = default!; [Dependency] private readonly DoorBoltSystem _bolts = default!;
[Dependency] private readonly AirtightSystem _airtightSystem = default!; [Dependency] private readonly AirtightSystem _airtightSystem = default!;
[Dependency] private readonly PryingSystem _pryingSystem = default!; [Dependency] private readonly PryingSystem _pryingSystem = default!;
[Dependency] private readonly RunicDoorSystem _runicDoor = default!; // WD
public override void Initialize() public override void Initialize()
{ {
@@ -142,6 +143,9 @@ public sealed class DoorSystem : SharedDoorSystem
var otherUid = args.OtherEntity; var otherUid = args.OtherEntity;
if (!_runicDoor.CanBumpOpen(uid, otherUid)) // WD
return;
if (Tags.HasTag(otherUid, "DoorBumpOpener")) if (Tags.HasTag(otherUid, "DoorBumpOpener"))
TryOpen(uid, door, otherUid, quiet: door.State == DoorState.Denying); TryOpen(uid, door, otherUid, quiet: door.State == DoorState.Denying);
} }
@@ -201,6 +205,9 @@ public sealed class DoorSystem : SharedDoorSystem
{ {
foreach (var other in PhysicsSystem.GetContactingEntities(uid, physics, approximate: true)) foreach (var other in PhysicsSystem.GetContactingEntities(uid, physics, approximate: true))
{ {
if (!_runicDoor.CanBumpOpen(uid, other)) // WD
continue;
if (Tags.HasTag(other, "DoorBumpOpener") && TryOpen(uid, door, other, quiet: true)) if (Tags.HasTag(other, "DoorBumpOpener") && TryOpen(uid, door, other, quiet: true))
break; break;
} }

View File

@@ -90,7 +90,7 @@ public sealed class RevolutionaryRuleSystem : GameRuleSystem<RevolutionaryRuleCo
if (CheckCommandLose()) if (CheckCommandLose())
{ {
// WD EDIT START // WD EDIT START
// Basically check for all in once gamemode // Check for all in once gamemode
if (_gameTicker.GetActiveGameRules().Where(HasComp<RampingStationEventSchedulerComponent>).Any()) if (_gameTicker.GetActiveGameRules().Where(HasComp<RampingStationEventSchedulerComponent>).Any())
_roundEnd.DoRoundEndBehavior(RoundEndBehavior.ShuttleCall, component.ShuttleCallTime); _roundEnd.DoRoundEndBehavior(RoundEndBehavior.ShuttleCall, component.ShuttleCallTime);
else else

View File

@@ -11,12 +11,26 @@ public sealed class ReturnToBodyEui : BaseEui
private readonly MindComponent _mind; private readonly MindComponent _mind;
private readonly EntityUid _mindId;
private readonly EntityUid? _transferTo;
public ReturnToBodyEui(MindComponent mind, SharedMindSystem mindSystem) public ReturnToBodyEui(MindComponent mind, SharedMindSystem mindSystem)
{ {
_mind = mind; _mind = mind;
_mindSystem = mindSystem; _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) public override void HandleMessage(EuiMessageBase msg)
{ {
base.HandleMessage(msg); base.HandleMessage(msg);
@@ -28,6 +42,9 @@ public sealed class ReturnToBodyEui : BaseEui
return; return;
} }
if (_transferTo != null) // WD
_mindSystem.TransferTo(_mindId, _transferTo, mind: _mind);
_mindSystem.UnVisit(_mind.Session); _mindSystem.UnVisit(_mind.Session);
Close(); Close();

View File

@@ -1,9 +1,11 @@
using System.Numerics; using System.Numerics;
using Content.Server._White.Other.ChangeThrowForceSystem; using Content.Server._White.Other.ChangeThrowForceSystem;
using Content.Server.Damage.Components;
using Content.Server.Inventory; using Content.Server.Inventory;
using Content.Server.Pulling; using Content.Server.Pulling;
using Content.Server.Stack; using Content.Server.Stack;
using Content.Server.Stunnable; using Content.Server.Stunnable;
using Content.Shared._White.Cult.Systems;
using Content.Shared._White.MagGloves; using Content.Shared._White.MagGloves;
using Content.Shared.ActionBlocker; using Content.Shared.ActionBlocker;
using Content.Shared.Body.Part; using Content.Shared.Body.Part;
@@ -40,6 +42,7 @@ namespace Content.Server.Hands.Systems
[Dependency] private readonly SharedHandsSystem _handsSystem = default!; [Dependency] private readonly SharedHandsSystem _handsSystem = default!;
[Dependency] private readonly PullingSystem _pullingSystem = default!; [Dependency] private readonly PullingSystem _pullingSystem = default!;
[Dependency] private readonly ThrowingSystem _throwingSystem = default!; [Dependency] private readonly ThrowingSystem _throwingSystem = default!;
[Dependency] private readonly CultItemSystem _cultItem = default!;
public override void Initialize() public override void Initialize()
{ {
@@ -194,6 +197,11 @@ namespace Content.Server.Hands.Systems
!_actionBlockerSystem.CanThrow(player, throwEnt)) !_actionBlockerSystem.CanThrow(player, throwEnt))
return false; return false;
// WD EDIT START
if (HasComp<DamageOtherOnHitComponent>(throwEnt) && !_cultItem.CanThrow(player, throwEnt))
return false;
// WD EDIT END
if (_timing.CurTime < hands.NextThrowTime) if (_timing.CurTime < hands.NextThrowTime)
return false; return false;
hands.NextThrowTime = _timing.CurTime + hands.ThrowCooldown; hands.NextThrowTime = _timing.CurTime + hands.ThrowCooldown;

View File

@@ -62,13 +62,14 @@ public sealed partial class CultRuleComponent : Component
public List<ConstructComponent> Constructs = new(); public List<ConstructComponent> Constructs = new();
public CultWinCondition WinCondition; public CultWinCondition WinCondition = CultWinCondition.CultDraw;
} }
public enum CultWinCondition : byte public enum CultWinCondition : byte
{ {
CultDraw,
CultWin, CultWin,
CultFailure CultFailure,
} }
public sealed class CultNarsieSummoned : EntityEventArgs public sealed class CultNarsieSummoned : EntityEventArgs

View File

@@ -1,14 +1,15 @@
using System.Linq; using System.Linq;
using Content.Server._Miracle.Components;
using Content.Server._Miracle.GulagSystem; using Content.Server._Miracle.GulagSystem;
using Content.Server.Actions; using Content.Server.Actions;
using Content.Server.Chat.Managers; using Content.Server.Chat.Managers;
using Content.Server.GameTicking; using Content.Server.GameTicking;
using Content.Server.GameTicking.Rules; using Content.Server.GameTicking.Rules;
using Content.Server.NPC.Systems; using Content.Server.NPC.Systems;
using Content.Server.Roles;
using Content.Server.Roles.Jobs; using Content.Server.Roles.Jobs;
using Content.Server.RoundEnd; using Content.Server.RoundEnd;
using Content.Server.Shuttles.Components; using Content.Server.Shuttles.Components;
using Content.Server.StationEvents.Components;
using Content.Server.Storage.EntitySystems; using Content.Server.Storage.EntitySystems;
using Content.Shared.Body.Systems; using Content.Shared.Body.Systems;
using Content.Shared.Humanoid; using Content.Shared.Humanoid;
@@ -25,6 +26,7 @@ using Robust.Shared.Random;
using Content.Shared._White; using Content.Shared._White;
using Content.Shared._White.Chaplain; using Content.Shared._White.Chaplain;
using Content.Shared._White.Cult.Components; using Content.Shared._White.Cult.Components;
using Content.Shared._White.Cult.Systems;
using Content.Shared.Mind; using Content.Shared.Mind;
using Robust.Shared.Audio.Systems; using Robust.Shared.Audio.Systems;
@@ -47,6 +49,7 @@ public sealed class CultRuleSystem : GameRuleSystem<CultRuleComponent>
[Dependency] private readonly SharedMindSystem _mindSystem = default!; [Dependency] private readonly SharedMindSystem _mindSystem = default!;
[Dependency] private readonly ActionsSystem _actions = default!; [Dependency] private readonly ActionsSystem _actions = default!;
[Dependency] private readonly GulagSystem _gulag = default!; [Dependency] private readonly GulagSystem _gulag = default!;
[Dependency] private readonly BloodSpearSystem _bloodSpear = default!;
private ISawmill _sawmill = default!; private ISawmill _sawmill = default!;
@@ -69,6 +72,13 @@ public sealed class CultRuleSystem : GameRuleSystem<CultRuleComponent>
SubscribeLocalEvent<CultistComponent, ComponentInit>(OnCultistComponentInit); SubscribeLocalEvent<CultistComponent, ComponentInit>(OnCultistComponentInit);
SubscribeLocalEvent<CultistComponent, ComponentRemove>(OnCultistComponentRemoved); SubscribeLocalEvent<CultistComponent, ComponentRemove>(OnCultistComponentRemoved);
SubscribeLocalEvent<CultistComponent, MobStateChangedEvent>(OnCultistsStateChanged); SubscribeLocalEvent<CultistComponent, MobStateChangedEvent>(OnCultistsStateChanged);
SubscribeLocalEvent<CultistRoleComponent, GetBriefingEvent>(OnGetBriefing);
}
private void OnGetBriefing(Entity<CultistRoleComponent> ent, ref GetBriefingEvent args)
{
args.Append(Loc.GetString("cult-role-briefing-short"));
} }
private void OnCultistsStateChanged(EntityUid uid, CultistComponent component, MobStateChangedEvent ev) private void OnCultistsStateChanged(EntityUid uid, CultistComponent component, MobStateChangedEvent ev)
@@ -130,7 +140,7 @@ public sealed class CultRuleSystem : GameRuleSystem<CultRuleComponent>
if (!TryComp<MobStateComponent>(owner, out var mobState)) if (!TryComp<MobStateComponent>(owner, out var mobState))
continue; continue;
if (_mobStateSystem.IsAlive(owner, mobState)) if (!_mobStateSystem.IsDead(owner, mobState))
{ {
aliveCultists++; aliveCultists++;
} }
@@ -140,7 +150,10 @@ public sealed class CultRuleSystem : GameRuleSystem<CultRuleComponent>
return; return;
cultistsRule.WinCondition = CultWinCondition.CultFailure; cultistsRule.WinCondition = CultWinCondition.CultFailure;
_roundEndSystem.EndRound();
// Check for all in once gamemode
if (!GameTicker.GetActiveGameRules().Where(HasComp<RampingStationEventSchedulerComponent>).Any())
_roundEndSystem.EndRound();
} }
private void OnCultistComponentInit(EntityUid uid, CultistComponent component, ComponentInit args) private void OnCultistComponentInit(EntityUid uid, CultistComponent component, ComponentInit args)
@@ -165,6 +178,8 @@ public sealed class CultRuleSystem : GameRuleSystem<CultRuleComponent>
} }
UpdateCultistsAppearance(cultistsRule); UpdateCultistsAppearance(cultistsRule);
component.OriginalMind = (mindComponent.Mind.Value, Comp<MindComponent>(mindComponent.Mind.Value));
} }
private void OnCultistComponentRemoved(EntityUid uid, CultistComponent component, ComponentRemove args) private void OnCultistComponentRemoved(EntityUid uid, CultistComponent component, ComponentRemove args)
@@ -177,6 +192,8 @@ public sealed class CultRuleSystem : GameRuleSystem<CultRuleComponent>
cultistsRule.CurrentCultists.Remove(component); cultistsRule.CurrentCultists.Remove(component);
_bloodSpear.DetachSpearFromUser((uid, component));
foreach (var empower in component.SelectedEmpowers) foreach (var empower in component.SelectedEmpowers)
{ {
_actions.RemoveAction(uid, GetEntity(empower)); _actions.RemoveAction(uid, GetEntity(empower));
@@ -438,7 +455,7 @@ public sealed class CultRuleSystem : GameRuleSystem<CultRuleComponent>
PrototypeId = cultistRule.CultistRolePrototype PrototypeId = cultistRule.CultistRolePrototype
}; };
_roleSystem.MindAddRole(mindId, cultistComponent); _roleSystem.MindAddRole(mindId, cultistComponent, mind);
EnsureComp<CultistComponent>(playerEntity); EnsureComp<CultistComponent>(playerEntity);
_factionSystem.RemoveFaction(playerEntity, "NanoTrasen", false); _factionSystem.RemoveFaction(playerEntity, "NanoTrasen", false);
@@ -478,19 +495,27 @@ public sealed class CultRuleSystem : GameRuleSystem<CultRuleComponent>
_roundEndSystem.EndRound(); _roundEndSystem.EndRound();
var query = EntityQueryEnumerator<MobStateComponent, MindContainerComponent, CultistComponent>(); var query =
EntityQueryEnumerator<MobStateComponent, MindContainerComponent, CultistComponent, TransformComponent>();
while (query.MoveNext(out var uid, out _, out var mindContainer, out _)) List<Entity<MindContainerComponent, TransformComponent>> 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; continue;
} }
var reaper = Spawn(CultRuleComponent.ReaperPrototype, Transform(uid).Coordinates); var reaper = Spawn(CultRuleComponent.ReaperPrototype, ent.Comp2.Coordinates);
_mindSystem.TransferTo(mindContainer.Mind.Value, reaper); _mindSystem.TransferTo(ent.Comp1.Mind.Value, reaper);
_bodySystem.GibBody(uid); _bodySystem.GibBody(ent);
} }
} }

View File

@@ -13,6 +13,7 @@ using Content.Shared.Popups;
using Content.Shared.Tag; using Content.Shared.Tag;
using Content.Shared._White.Cult; using Content.Shared._White.Cult;
using Content.Shared._White.Cult.Pylon; using Content.Shared._White.Cult.Pylon;
using Content.Shared._White.Cult.Systems;
using Robust.Server.GameObjects; using Robust.Server.GameObjects;
using Robust.Server.Player; using Robust.Server.Player;
using Robust.Shared.Audio; using Robust.Shared.Audio;
@@ -40,6 +41,8 @@ public sealed class PylonSystem : EntitySystem
[Dependency] private readonly BloodstreamSystem _blood = default!; [Dependency] private readonly BloodstreamSystem _blood = default!;
[Dependency] private readonly TurfSystem _turf = default!; [Dependency] private readonly TurfSystem _turf = default!;
[Dependency] private readonly EntityLookupSystem _lookup = default!; [Dependency] private readonly EntityLookupSystem _lookup = default!;
[Dependency] private readonly PointLightSystem _pointLight = default!;
[Dependency] private readonly PhysicsSystem _physics = default!;
public override void Initialize() public override void Initialize()
{ {
@@ -47,6 +50,15 @@ public sealed class PylonSystem : EntitySystem
SubscribeLocalEvent<SharedPylonComponent, InteractHandEvent>(OnInteract); SubscribeLocalEvent<SharedPylonComponent, InteractHandEvent>(OnInteract);
SubscribeLocalEvent<SharedPylonComponent, ComponentInit>(OnInit); SubscribeLocalEvent<SharedPylonComponent, ComponentInit>(OnInit);
SubscribeLocalEvent<SharedPylonComponent, ConcealEvent>(OnConceal);
}
private void OnConceal(Entity<SharedPylonComponent> 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) 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) if (player.AttachedEntity is not { Valid: true } playerEntity)
continue; continue;
if (!EntityManager.TryGetComponent<CultistComponent>(playerEntity, out _)) if (!HasComp<CultistComponent>(playerEntity) && !HasComp<ConstructComponent>(playerEntity))
continue; continue;
if (_mobStateSystem.IsDead(playerEntity)) if (_mobStateSystem.IsDead(playerEntity))
@@ -221,12 +233,7 @@ public sealed class PylonSystem : EntitySystem
UpdateAppearance(uid, comp); UpdateAppearance(uid, comp);
if (!TryComp<PointLightComponent>(uid, out var light)) _pointLight.SetEnabled(uid, comp.Activated);
return;
#pragma warning disable RA0002
light.Enabled = comp.Activated;
#pragma warning restore RA0002
var toggleMsg = Loc.GetString(comp.Activated ? "pylon-toggle-on" : "pylon-toggle-off"); var toggleMsg = Loc.GetString(comp.Activated ? "pylon-toggle-on" : "pylon-toggle-off");
_popupSystem.PopupEntity(toggleMsg, uid); _popupSystem.PopupEntity(toggleMsg, uid);

View File

@@ -1,6 +1,7 @@
using Content.Server.UserInterface; using Content.Server.UserInterface;
using Content.Shared._White.Cult.UI; using Content.Shared._White.Cult.UI;
using Robust.Server.GameObjects; using Robust.Server.GameObjects;
using Robust.Shared.Prototypes;
namespace Content.Shared._White.Cult; namespace Content.Shared._White.Cult;
@@ -11,5 +12,5 @@ public sealed partial class RuneDrawerProviderComponent : Component
public Enum UserInterfaceKey = ListViewSelectorUiKey.Key; public Enum UserInterfaceKey = ListViewSelectorUiKey.Key;
[DataField("runePrototypes")] [DataField("runePrototypes")]
public List<string> RunePrototypes = new(); public List<EntProtoId> RunePrototypes = new();
} }

View File

@@ -1,10 +1,12 @@
using System.Linq; using System.Linq;
using System.Numerics;
using Content.Server.Body.Components; using Content.Server.Body.Components;
using Content.Server.Chemistry.Containers.EntitySystems; using Content.Server.Chemistry.Containers.EntitySystems;
using Content.Server.Emp; using Content.Server.Emp;
using Content.Server.EUI; using Content.Server.EUI;
using Content.Server._White.Cult.UI; using Content.Server._White.Cult.UI;
using Content.Shared._White.Chaplain; using Content.Shared._White.Chaplain;
using Content.Shared._White.Cult;
using Content.Shared.Chemistry.Components; using Content.Shared.Chemistry.Components;
using Content.Shared.Damage; using Content.Shared.Damage;
using Content.Shared.Damage.Prototypes; using Content.Shared.Damage.Prototypes;
@@ -17,12 +19,19 @@ using Content.Shared.Stacks;
using Content.Shared.StatusEffect; using Content.Shared.StatusEffect;
using Content.Shared.Stunnable; using Content.Shared.Stunnable;
using Content.Shared._White.Cult.Actions; 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.Actions;
using Content.Shared.Cuffs.Components; using Content.Shared.Cuffs.Components;
using Content.Shared.DoAfter; using Content.Shared.DoAfter;
using Content.Shared.Maps;
using Content.Shared.Mindshield.Components; using Content.Shared.Mindshield.Components;
using Robust.Server.GameObjects; using Robust.Server.GameObjects;
using Robust.Shared.Audio; using Robust.Shared.Audio;
using Robust.Shared.Map.Components;
using Robust.Shared.Physics;
using Robust.Shared.Physics.Components;
using Robust.Shared.Player; using Robust.Shared.Player;
using CultistComponent = Content.Shared._White.Cult.Components.CultistComponent; 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 EuiManager _euiManager = default!;
[Dependency] private readonly InventorySystem _inventorySystem = default!; [Dependency] private readonly InventorySystem _inventorySystem = default!;
[Dependency] private readonly TransformSystem _transform = 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() public void InitializeActions()
{ {
@@ -44,8 +60,10 @@ public partial class CultSystem
SubscribeLocalEvent<CultistComponent, CultShadowShacklesTargetActionEvent>(OnShadowShackles); SubscribeLocalEvent<CultistComponent, CultShadowShacklesTargetActionEvent>(OnShadowShackles);
SubscribeLocalEvent<CultistComponent, CultElectromagneticPulseInstantActionEvent>(OnElectromagneticPulse); SubscribeLocalEvent<CultistComponent, CultElectromagneticPulseInstantActionEvent>(OnElectromagneticPulse);
SubscribeLocalEvent<CultistComponent, CultSummonCombatEquipmentTargetActionEvent>(OnSummonCombatEquipment); SubscribeLocalEvent<CultistComponent, CultSummonCombatEquipmentTargetActionEvent>(OnSummonCombatEquipment);
SubscribeLocalEvent<CultistComponent, CultConcealPresenceWorldActionEvent>(OnConcealPresence); SubscribeLocalEvent<CultistComponent, CultConcealInstantActionEvent>(OnConcealPresence);
SubscribeLocalEvent<CultistComponent, CultRevealInstantActionEvent>(OnConcealPresence);
SubscribeLocalEvent<CultistComponent, CultBloodRitesInstantActionEvent>(OnBloodRites); SubscribeLocalEvent<CultistComponent, CultBloodRitesInstantActionEvent>(OnBloodRites);
SubscribeLocalEvent<CultistComponent, CultBloodSpearRecallInstantActionEvent>(OnBloodSpearRecall);
SubscribeLocalEvent<CultistComponent, CultTeleportTargetActionEvent>(OnTeleport); SubscribeLocalEvent<CultistComponent, CultTeleportTargetActionEvent>(OnTeleport);
SubscribeLocalEvent<CultistComponent, CultStunTargetActionEvent>(OnStunTarget); SubscribeLocalEvent<CultistComponent, CultStunTargetActionEvent>(OnStunTarget);
SubscribeLocalEvent<CultistComponent, ActionGettingRemovedEvent>(OnActionRemoved); SubscribeLocalEvent<CultistComponent, ActionGettingRemovedEvent>(OnActionRemoved);
@@ -74,9 +92,15 @@ public partial class CultSystem
private void OnStunTarget(EntityUid uid, CultistComponent component, CultStunTargetActionEvent args) private void OnStunTarget(EntityUid uid, CultistComponent component, CultStunTargetActionEvent args)
{ {
if (args.Target == uid || !TryComp<BloodstreamComponent>(args.Performer, out var bloodstream) || if (args.Target == uid || !TryComp<BloodstreamComponent>(args.Performer, out var bloodstream) ||
HasComp<HolyComponent>(args.Target) || !TryComp<StatusEffectsComponent>(args.Target, out var status)) !TryComp<StatusEffectsComponent>(args.Target, out var status))
return; return;
if (HasComp<HolyComponent>(args.Target))
{
_popupSystem.PopupEntity("Священная сила препятствует магии.", args.Performer, args.Performer);
return;
}
if (HasComp<MindShieldComponent>(args.Target)) if (HasComp<MindShieldComponent>(args.Target))
{ {
_popupSystem.PopupEntity("Он имплантирован чипом защиты разума.", args.Performer, args.Performer); _popupSystem.PopupEntity("Он имплантирован чипом защиты разума.", args.Performer, args.Performer);
@@ -94,14 +118,23 @@ public partial class CultSystem
private void OnTeleport(EntityUid uid, CultistComponent component, CultTeleportTargetActionEvent args) private void OnTeleport(EntityUid uid, CultistComponent component, CultTeleportTargetActionEvent args)
{ {
if (!TryComp<BloodstreamComponent>(args.Performer, out var bloodstream) || !TryComp<ActorComponent>(uid, out var actor)) if (!TryComp<BloodstreamComponent>(args.Performer, out var bloodstream) ||
!TryComp<ActorComponent>(uid, out var actor))
return; return;
if (!TryComp<CultistComponent>(args.Target, out _) && if (HasComp<HolyComponent>(args.Target))
!(TryComp<MobStateComponent>(args.Target, out var mobStateComponent) &&
mobStateComponent.CurrentState is not MobState.Alive))
{ {
_popupSystem.PopupEntity("Цель должна быть культистом или лежать.", args.Performer, args.Performer); _popupSystem.PopupEntity("Священная сила препятствует магии.", args.Performer, args.Performer);
return;
}
if (!HasComp<CultistComponent>(args.Target) && !HasComp<ConstructComponent>(args.Target) &&
(!TryComp<MobStateComponent>(args.Target, out var mobStateComponent) ||
mobStateComponent.CurrentState is MobState.Alive) &&
(!TryComp<CuffableComponent>(args.Target, out var cuffable) || cuffable.Container.Count == 0))
{
_popupSystem.PopupEntity("Цель должна быть культистом, быть связанной или лежать.", args.Performer,
args.Performer);
return; return;
} }
@@ -147,25 +180,28 @@ public partial class CultSystem
totalBloodAmount += solutionContent.Quantity; totalBloodAmount += solutionContent.Quantity;
_bloodstreamSystem.TryModifyBloodLevel(uid, solutionContent.Quantity / 6f); _bloodstreamSystem.TryModifyBloodLevel(uid, solutionContent.Quantity / 6f, bloodstreamComponent);
_solutionSystem.RemoveReagent((Entity<SolutionComponent>) solution, "Blood", FixedPoint2.MaxValue); _solutionSystem.RemoveReagent((Entity<SolutionComponent>) solution, "Blood", FixedPoint2.MaxValue);
if (GetMissingBloodValue(bloodstreamComponent) == 0) /*if (GetMissingBloodValue(bloodstreamComponent) == 0)
{ {
breakLoop = true; breakLoop = true;
} }*/
} }
} }
if (totalBloodAmount == 0f) if (totalBloodAmount == 0f)
{
return; return;
}
component.RitesBloodAmount += totalBloodAmount;
_audio.PlayPvs("/Audio/White/Cult/enter_blood.ogg", uid, AudioParams.Default); _audio.PlayPvs("/Audio/White/Cult/enter_blood.ogg", uid, AudioParams.Default);
_damageableSystem.TryChangeDamage(uid, new DamageSpecifier(bruteDamageGroup, -20)); _damageableSystem.TryChangeDamage(uid, new DamageSpecifier(bruteDamageGroup, -20));
_damageableSystem.TryChangeDamage(uid, new DamageSpecifier(burnDamageGroup, -20)); _damageableSystem.TryChangeDamage(uid, new DamageSpecifier(burnDamageGroup, -20));
_popupSystem.PopupEntity(Loc.GetString("verb-blood-rites-message", ("blood", component.RitesBloodAmount)), uid,
uid);
args.Handled = true; args.Handled = true;
} }
@@ -174,10 +210,163 @@ public partial class CultSystem
return bloodstreamComponent.BloodMaxVolume - bloodstreamComponent.BloodSolution!.Value.Comp.Solution.Volume; return bloodstreamComponent.BloodMaxVolume - bloodstreamComponent.BloodSolution!.Value.Comp.Solution.Volume;
} }
private void OnConcealPresence(EntityUid uid, CultistComponent component, CultConcealPresenceWorldActionEvent args) private void OnBloodSpearRecall(Entity<CultistComponent> ent, ref CultBloodSpearRecallInstantActionEvent args)
{ {
if (!TryComp<BloodstreamComponent>(args.Performer, out _)) if (ent.Comp.BloodSpear == null)
{
_bloodSpear.DetachSpearFromUser(ent);
return; 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<PhysicsComponent>(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<BloodstreamComponent>(args.Performer, out var bloodstream) ||
!TryComp<TransformComponent>(args.Performer, out var xform))
return;
var conceal = args is CultConcealInstantActionEvent;
var concealableQuery = GetEntityQuery<ConcealableComponent>();
var appearanceQuery = GetEntityQuery<AppearanceComponent>();
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<ConcealPresenceSpellComponent>();
var actionQuery = GetEntityQuery<InstantActionComponent>();
// 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( private void OnSummonCombatEquipment(
@@ -300,7 +489,7 @@ public partial class CultSystem
var xform = Transform(args.Performer).Coordinates; var xform = Transform(args.Performer).Coordinates;
var dagger = _entityManager.SpawnEntity(RitualDaggerPrototypeId, xform); 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); _handsSystem.TryPickupAnyHand(args.Performer, dagger);
args.Handled = true; args.Handled = true;
} }

View File

@@ -10,10 +10,12 @@ using Content.Server.Hands.Systems;
using Content.Server.Weapons.Ranged.Systems; using Content.Server.Weapons.Ranged.Systems;
using Content.Server._White.Cult.GameRule; using Content.Server._White.Cult.GameRule;
using Content.Server._White.Cult.Runes.Comps; using Content.Server._White.Cult.Runes.Comps;
using Content.Server._White.Cult.UI;
using Content.Server.Bible.Components; using Content.Server.Bible.Components;
using Content.Server.Chemistry.Components; using Content.Server.Chemistry.Components;
using Content.Server.Chemistry.Containers.EntitySystems; using Content.Server.Chemistry.Containers.EntitySystems;
using Content.Server.Fluids.Components; using Content.Server.Fluids.Components;
using Content.Server.Ghost;
using Content.Shared._White.Chaplain; using Content.Shared._White.Chaplain;
using Content.Shared.Chemistry.Components.SolutionManager; using Content.Shared.Chemistry.Components.SolutionManager;
using Content.Shared.Cuffs.Components; 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.Runes;
using Content.Shared._White.Cult.UI; using Content.Shared._White.Cult.UI;
using Content.Shared.Cuffs; using Content.Shared.Cuffs;
using Content.Shared.GameTicking;
using Content.Shared.Mindshield.Components; using Content.Shared.Mindshield.Components;
using Content.Shared.Pulling; using Content.Shared.Pulling;
using Content.Shared.UserInterface;
using Robust.Server.GameObjects; using Robust.Server.GameObjects;
using Robust.Shared.Audio; using Robust.Shared.Audio;
using Robust.Shared.Audio.Components;
using Robust.Shared.Audio.Systems; using Robust.Shared.Audio.Systems;
using Robust.Shared.Map; using Robust.Shared.Map;
using Robust.Shared.Physics.Events; using Robust.Shared.Physics.Events;
@@ -92,7 +97,8 @@ public sealed partial class CultSystem : EntitySystem
SubscribeLocalEvent<CultEmpowerComponent, ActivateInWorldEvent>(OnActiveInWorld); SubscribeLocalEvent<CultEmpowerComponent, ActivateInWorldEvent>(OnActiveInWorld);
// UI // UI
SubscribeLocalEvent<RuneDrawerProviderComponent, UseInHandEvent>(OnRuneDrawerUseInHand); SubscribeLocalEvent<RuneDrawerProviderComponent, ActivatableUIOpenAttemptEvent>(OnRuneDrawAttempt);
SubscribeLocalEvent<RuneDrawerProviderComponent, BeforeActivatableUIOpenEvent>(BeforeRuneDraw);
SubscribeLocalEvent<RuneDrawerProviderComponent, ListViewItemSelectedMessage>(OnRuneSelected); SubscribeLocalEvent<RuneDrawerProviderComponent, ListViewItemSelectedMessage>(OnRuneSelected);
SubscribeLocalEvent<CultTeleportRuneProviderComponent, TeleportRunesListWindowItemSelectedMessage>( SubscribeLocalEvent<CultTeleportRuneProviderComponent, TeleportRunesListWindowItemSelectedMessage>(
OnTeleportRuneSelected); OnTeleportRuneSelected);
@@ -107,6 +113,8 @@ public sealed partial class CultSystem : EntitySystem
SubscribeLocalEvent<CultRuneBaseComponent, CultEraseEvent>(OnErase); SubscribeLocalEvent<CultRuneBaseComponent, CultEraseEvent>(OnErase);
SubscribeLocalEvent<CultRuneBaseComponent, StartCollideEvent>(HandleCollision); SubscribeLocalEvent<CultRuneBaseComponent, StartCollideEvent>(HandleCollision);
SubscribeLocalEvent<RoundRestartCleanupEvent>(OnRoundRestart);
InitializeBuffSystem(); InitializeBuffSystem();
InitializeNarsie(); InitializeNarsie();
InitializeSoulShard(); InitializeSoulShard();
@@ -134,30 +142,30 @@ public sealed partial class CultSystem : EntitySystem
private readonly SoundPathSpecifier _magic = new("/Audio/White/Cult/magic.ogg"); 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 _apocRuneEndDrawing = new("/Audio/White/Cult/finisheddraw.ogg");
private readonly SoundPathSpecifier _narsie40Sec = new("/Audio/White/Cult/40sec.ogg"); private readonly SoundPathSpecifier _narsie40Sec = new("/Audio/White/Cult/40sec.ogg");
private Entity<AudioComponent>? _narsieSummonningAudio = null;
private void OnRoundRestart(RoundRestartCleanupEvent ev)
{
CultRuneReviveComponent.ChargesLeft = 3;
}
/* /*
* Rune draw start ---- * Rune draw start ----
*/ */
private void OnRuneDrawerUseInHand(EntityUid uid, RuneDrawerProviderComponent component, UseInHandEvent args) private void OnRuneDrawAttempt(Entity<RuneDrawerProviderComponent> ent, ref ActivatableUIOpenAttemptEvent args)
{ {
if (!_ui.TryGetUi(uid, component.UserInterfaceKey, out _))
return;
if (!TryComp<ActorComponent>(args.User, out var actorComponent))
return;
if (!HasComp<CultistComponent>(args.User)) if (!HasComp<CultistComponent>(args.User))
return; args.Cancel();
}
if (_ui.TryGetUi(uid, ListViewSelectorUiKey.Key, out var bui)) private void BeforeRuneDraw(Entity<RuneDrawerProviderComponent> ent, ref BeforeActivatableUIOpenEvent args)
{ {
_ui.SetUiState(bui, new ListViewBUIState(component.RunePrototypes, false)); if (_ui.TryGetUi(ent, ListViewSelectorUiKey.Key, out var bui))
_ui.OpenUi(bui, actorComponent.PlayerSession); _ui.SetUiState(bui, new ListViewBUIState(ent.Comp.RunePrototypes, true));
}
} }
private void OnRuneSelected(EntityUid uid, RuneDrawerProviderComponent component, ListViewItemSelectedMessage args) private void OnRuneSelected(EntityUid uid, RuneDrawerProviderComponent component, ListViewItemSelectedMessage args)
@@ -185,16 +193,20 @@ public sealed partial class CultSystem : EntitySystem
if (HasComp<CultBuffComponent>(whoCalled)) if (HasComp<CultBuffComponent>(whoCalled))
_timeToDraw /= 2; _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)) if (!IsAllowedToDraw(whoCalled))
return false; 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 var ev = new CultDrawEvent
{ {
Rune = runePrototype Rune = runePrototype
@@ -769,7 +781,7 @@ public sealed partial class CultSystem : EntitySystem
_chat.DispatchGlobalAnnouncement(Loc.GetString("cult-ritual-started"), "CULT", false, _chat.DispatchGlobalAnnouncement(Loc.GetString("cult-ritual-started"), "CULT", false,
colorOverride: Color.DarkRed); 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; return true;
} }
@@ -778,6 +790,8 @@ public sealed partial class CultSystem : EntitySystem
{ {
_doAfterAlreadyStarted = false; _doAfterAlreadyStarted = false;
_audio.Stop(_narsieSummonningAudio?.Owner, _narsieSummonningAudio?.Comp);
if (args.Cancelled) if (args.Cancelled)
{ {
_chat.DispatchGlobalAnnouncement(Loc.GetString("cult-ritual-prevented"), "CULT", false, _chat.DispatchGlobalAnnouncement(Loc.GetString("cult-ritual-prevented"), "CULT", false,
@@ -790,13 +804,13 @@ public sealed partial class CultSystem : EntitySystem
if (transform == null) if (transform == null)
return; return;
_entityManager.SpawnEntity(NarsiePrototypeId, transform.Value);
_chat.DispatchGlobalAnnouncement(Loc.GetString("cult-narsie-summoned"), "CULT", true, _apocRuneEndDrawing,
colorOverride: Color.DarkRed);
var ev = new CultNarsieSummoned(); var ev = new CultNarsieSummoned();
RaiseLocalEvent(ev); 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--; CultRuneReviveComponent.ChargesLeft--;
_entityManager.EventBus.RaiseLocalEvent(target, new RejuvenateEvent()); _entityManager.EventBus.RaiseLocalEvent(target, new RejuvenateEvent());
EntityUid? transferTo = null;
if (!_mindSystem.TryGetMind(target, out var mindId, out var mind))
{
if (!TryComp<CultistComponent>(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; return true;
} }
@@ -1258,6 +1293,8 @@ public sealed partial class CultSystem : EntitySystem
return; return;
} }
var damage = 10;
if (rune == ApocalypseRunePrototypeId) if (rune == ApocalypseRunePrototypeId)
{ {
if (!_entityManager.TryGetComponent(uid, out TransformComponent? transComp)) if (!_entityManager.TryGetComponent(uid, out TransformComponent? transComp))
@@ -1265,6 +1302,7 @@ public sealed partial class CultSystem : EntitySystem
return; return;
} }
damage = 40;
var pos = transComp.MapPosition; var pos = transComp.MapPosition;
var x = (int) pos.X; var x = (int) pos.X;
var y = (int) pos.Y; var y = (int) pos.Y;
@@ -1273,7 +1311,7 @@ public sealed partial class CultSystem : EntitySystem
"CULT", true, _apocRuneEndDrawing, colorOverride: Color.DarkRed); "CULT", true, _apocRuneEndDrawing, colorOverride: Color.DarkRed);
} }
var damageSpecifier = new DamageSpecifier(_prototypeManager.Index<DamageTypePrototype>("Slash"), 10); var damageSpecifier = new DamageSpecifier(_prototypeManager.Index<DamageTypePrototype>("Slash"), damage);
_damageableSystem.TryChangeDamage(uid, damageSpecifier, true, false); _damageableSystem.TryChangeDamage(uid, damageSpecifier, true, false);
_xform.AttachToGridOrMap(_entityManager.SpawnEntity(rune, transform.Value)); _xform.AttachToGridOrMap(_entityManager.SpawnEntity(rune, transform.Value));

View File

@@ -2,6 +2,7 @@ using System.Linq;
using Content.Server.Body.Components; using Content.Server.Body.Components;
using Content.Shared._White.Cult; using Content.Shared._White.Cult;
using Content.Shared._White.Cult.Components; using Content.Shared._White.Cult.Components;
using Content.Shared._White.Cult.UI;
using Content.Shared.DoAfter; using Content.Shared.DoAfter;
using Content.Shared.Verbs; using Content.Shared.Verbs;
using Robust.Shared.Player; using Robust.Shared.Player;
@@ -16,6 +17,7 @@ public sealed partial class CultSystem
SubscribeLocalEvent<CultistComponent, CultEmpowerSelectedBuiMessage>(OnCultistEmpowerSelected); SubscribeLocalEvent<CultistComponent, CultEmpowerSelectedBuiMessage>(OnCultistEmpowerSelected);
SubscribeLocalEvent<CultistComponent, CultEmpowerRemoveBuiMessage>(OnCultistEmpowerRemove); SubscribeLocalEvent<CultistComponent, CultEmpowerRemoveBuiMessage>(OnCultistEmpowerRemove);
SubscribeLocalEvent<CultistComponent, SpellCreatedEvent>(OnSpellCreated); SubscribeLocalEvent<CultistComponent, SpellCreatedEvent>(OnSpellCreated);
SubscribeLocalEvent<CultistComponent, CultistFactoryItemSelectedMessage>(OnBloodRitesSelected);
} }
private void OnCultistEmpowerRemove(Entity<CultistComponent> ent, ref CultEmpowerRemoveBuiMessage args) private void OnCultistEmpowerRemove(Entity<CultistComponent> ent, ref CultEmpowerRemoveBuiMessage args)
@@ -28,7 +30,7 @@ public sealed partial class CultSystem
private void OnSpellCreated(EntityUid ent, CultistComponent comp, SpellCreatedEvent args) private void OnSpellCreated(EntityUid ent, CultistComponent comp, SpellCreatedEvent args)
{ {
if (args.Cancelled || comp.SelectedEmpowers.Count >= 1) if (args.Cancelled || comp.SelectedEmpowers.Count >= 2)
return; return;
var action = CultistComponent.CultistActions.FirstOrDefault(x => x.Equals(args.Spell)); var action = CultistComponent.CultistActions.FirstOrDefault(x => x.Equals(args.Spell));
@@ -55,7 +57,7 @@ public sealed partial class CultSystem
if (action == null) if (action == null)
return; return;
if (comp.SelectedEmpowers.Count >= 1) if (comp.SelectedEmpowers.Count >= 2)
{ {
_popupSystem.PopupEntity(Loc.GetString("verb-spell-create-too-much"), ent, ent); _popupSystem.PopupEntity(Loc.GetString("verb-spell-create-too-much"), ent, ent);
return; 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(createSpellVerb);
args.Verbs.Add(removeSpellVerb); args.Verbs.Add(removeSpellVerb);
args.Verbs.Add(bloodRitesVerb);
} }
private void RemoveSpell(Entity<CultistComponent> ent, ICommonSession session) private void RemoveSpell(Entity<CultistComponent> ent, ICommonSession session)
@@ -112,4 +130,36 @@ public sealed partial class CultSystem
_ui.TryOpen(ent, CultEmpowerRemoveUiKey.Key, session); _ui.TryOpen(ent, CultEmpowerRemoveUiKey.Key, session);
} }
private void OnBloodRitesSelected(Entity<CultistComponent> ent, ref CultistFactoryItemSelectedMessage args)
{
if (!_prototypeManager.TryIndex<CultistFactoryProductionPrototype>(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;
}
} }

View File

@@ -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._White.Chaplain;
using Content.Shared.Doors; using Content.Shared.Doors;
using Content.Shared.Humanoid; using Content.Shared.Humanoid;
using Content.Shared.Stunnable; 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.Doors.Components;
using Content.Shared.Mobs.Systems;
using Content.Shared.Weapons.Melee.Components;
using Content.Shared.Weapons.Melee.Events; using Content.Shared.Weapons.Melee.Events;
using Robust.Shared.Audio; using Robust.Shared.Audio;
using Robust.Shared.Audio.Systems; using Robust.Shared.Audio.Systems;
using Robust.Shared.Physics.Components;
using Robust.Shared.Physics.Systems; using Robust.Shared.Physics.Systems;
using CultistComponent = Content.Shared._White.Cult.Components.CultistComponent; 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 SharedPhysicsSystem _physics = default!;
[Dependency] private readonly SharedStunSystem _stunSystem = default!; [Dependency] private readonly SharedStunSystem _stunSystem = default!;
[Dependency] private readonly SharedAudioSystem _audio = 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() public override void Initialize()
{ {
@@ -27,6 +36,31 @@ public sealed class RunicDoorSystem : EntitySystem
SubscribeLocalEvent<RunicDoorComponent, BeforeDoorOpenedEvent>(OnBeforeDoorOpened); SubscribeLocalEvent<RunicDoorComponent, BeforeDoorOpenedEvent>(OnBeforeDoorOpened);
SubscribeLocalEvent<RunicDoorComponent, BeforeDoorClosedEvent>(OnBeforeDoorClosed); SubscribeLocalEvent<RunicDoorComponent, BeforeDoorClosedEvent>(OnBeforeDoorClosed);
SubscribeLocalEvent<RunicDoorComponent, AttackedEvent>(OnGetAttacked); SubscribeLocalEvent<RunicDoorComponent, AttackedEvent>(OnGetAttacked);
SubscribeLocalEvent<RunicDoorComponent, ConcealEvent>(OnConceal);
}
private void OnConceal(Entity<RunicDoorComponent> 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<RunicDoorComponent> ent, ref AttackedEvent args) private void OnGetAttacked(Entity<RunicDoorComponent> ent, ref AttackedEvent args)
@@ -79,7 +113,8 @@ public sealed class RunicDoorSystem : EntitySystem
_doorSystem.Deny(airlock); _doorSystem.Deny(airlock);
if (!HasComp<HumanoidAppearanceComponent>(user) || HasComp<HolyComponent>(user)) if (!HasComp<HumanoidAppearanceComponent>(user) || HasComp<HolyComponent>(user) ||
TryComp(airlock, out ConcealableComponent? concealable) && concealable.Concealed)
return false; return false;
var direction = Transform(user).MapPosition.Position - Transform(airlock).MapPosition.Position; 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); _stunSystem.TryParalyze(user, TimeSpan.FromSeconds(3), true);
return false; return false;
} }
public bool CanBumpOpen(EntityUid uid, EntityUid otherUid)
{
return !HasComp<RunicDoorComponent>(uid) || !HasComp<ConstructComponent>(otherUid) &&
(!HasComp<CultistComponent>(otherUid) || !_mobState.IsAlive(otherUid) ||
TryComp(otherUid, out CuffableComponent? cuffable) && _cuffable.GetAllCuffs(cuffable).Count > 0);
}
} }

View File

@@ -5,6 +5,7 @@ using Content.Shared.Interaction;
using Content.Shared.Popups; using Content.Shared.Popups;
using Content.Shared._White.Cult; using Content.Shared._White.Cult;
using Content.Shared._White.Cult.Structures; using Content.Shared._White.Cult.Structures;
using Content.Shared._White.Cult.Systems;
using Content.Shared._White.Cult.UI; using Content.Shared._White.Cult.UI;
using Robust.Server.GameObjects; using Robust.Server.GameObjects;
using Robust.Shared.Audio.Systems; using Robust.Shared.Audio.Systems;
@@ -28,6 +29,7 @@ public sealed class CultistFactorySystem : EntitySystem
[Dependency] private readonly SharedDoAfterSystem _doAfterSystem = default!; [Dependency] private readonly SharedDoAfterSystem _doAfterSystem = default!;
[Dependency] private readonly SharedPopupSystem _popup = default!; [Dependency] private readonly SharedPopupSystem _popup = default!;
[Dependency] private readonly SharedAudioSystem _audio = default!; [Dependency] private readonly SharedAudioSystem _audio = default!;
[Dependency] private readonly PhysicsSystem _physics = default!;
private const string RitualDaggerPrototypeId = "RitualDagger"; private const string RitualDaggerPrototypeId = "RitualDagger";
@@ -42,6 +44,12 @@ public sealed class CultistFactorySystem : EntitySystem
SubscribeLocalEvent<CultistFactoryComponent, InteractUsingEvent>(TryToggleAnchor); SubscribeLocalEvent<CultistFactoryComponent, InteractUsingEvent>(TryToggleAnchor);
SubscribeLocalEvent<CultistFactoryComponent, CultAnchorDoAfterEvent>(OnAnchorDoAfter); SubscribeLocalEvent<CultistFactoryComponent, CultAnchorDoAfterEvent>(OnAnchorDoAfter);
SubscribeLocalEvent<CultistFactoryComponent, ExaminedEvent>(OnExamine); SubscribeLocalEvent<CultistFactoryComponent, ExaminedEvent>(OnExamine);
SubscribeLocalEvent<CultistFactoryComponent, ConcealEvent>(OnConceal);
}
private void OnConceal(Entity<CultistFactoryComponent> ent, ref ConcealEvent args)
{
_physics.SetCanCollide(ent, !args.Conceal);
} }
public override void Update(float frameTime) public override void Update(float frameTime)

View File

@@ -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<DoAfterSystem>().TryStartDoAfter(argsDoAfterEvent))
{
Close();
return;
}
_entityManager.System<ChatSystem>().DispatchGlobalAnnouncement(Loc.GetString("cult-started-drawing-rune-end"),
"CULT", true, _apocRuneStartDrawing, colorOverride: Color.DarkRed);
_entityManager.System<AudioSystem>().PlayPvs("/Audio/White/Cult/butcher.ogg", _whoCalled,
AudioParams.Default.WithMaxDistance(2f));
Close();
}
}

View File

@@ -123,10 +123,10 @@ public abstract class SharedTentacleGun : EntitySystem
foreach (var activeItem in _handsSystem.EnumerateHeld(args.Embedded)) foreach (var activeItem in _handsSystem.EnumerateHeld(args.Embedded))
{ {
if(!TryComp<PhysicsComponent>(activeItem, out var physicsComponent)) if(!TryComp<PhysicsComponent>(activeItem, out var physicsComponent))
return; continue;
var coords = Transform(args.Embedded).Coordinates; var coords = Transform(args.Embedded).Coordinates;
_handsSystem.TryDrop(args.Embedded, coords); _handsSystem.TryDrop(args.Embedded, activeItem, coords);
var force = physicsComponent.Mass * 2.5f / 2; var force = physicsComponent.Mass * 2.5f / 2;

View File

@@ -1,4 +1,5 @@
using System.Linq; using System.Linq;
using Content.Shared._White.Cult.Components;
using Content.Shared.Examine; using Content.Shared.Examine;
using Content.Shared.Hands.Components; using Content.Shared.Hands.Components;
using Content.Shared.IdentityManagement; using Content.Shared.IdentityManagement;
@@ -7,12 +8,15 @@ using Content.Shared.Inventory.VirtualItem;
using Content.Shared.Localizations; using Content.Shared.Localizations;
using Robust.Shared.Input.Binding; using Robust.Shared.Input.Binding;
using Robust.Shared.Map; using Robust.Shared.Map;
using Robust.Shared.Network;
using Robust.Shared.Player; using Robust.Shared.Player;
namespace Content.Shared.Hands.EntitySystems; namespace Content.Shared.Hands.EntitySystems;
public abstract partial class SharedHandsSystem : EntitySystem public abstract partial class SharedHandsSystem : EntitySystem
{ {
[Dependency] private readonly INetManager _net = default!; // WD
private void InitializeInteractions() private void InitializeInteractions()
{ {
SubscribeAllEvent<RequestSetHandEvent>(HandleSetHand); SubscribeAllEvent<RequestSetHandEvent>(HandleSetHand);
@@ -95,7 +99,17 @@ public abstract partial class SharedHandsSystem : EntitySystem
private bool DropPressed(ICommonSession? session, EntityCoordinates coords, EntityUid netEntity) private bool DropPressed(ICommonSession? session, EntityCoordinates coords, EntityUid netEntity)
{ {
if (TryComp(session?.AttachedEntity, out HandsComponent? hands) && hands.ActiveHand != null) if (TryComp(session?.AttachedEntity, out HandsComponent? hands) && hands.ActiveHand != null)
// WD EDIT START
{
if (HasComp<BoltBarrageComponent>(hands.ActiveHandEntity))
{
if (_net.IsServer)
QueueDel(hands.ActiveHandEntity.Value);
return false;
}
TryDrop(session.AttachedEntity.Value, hands.ActiveHand, coords, handsComp: hands); TryDrop(session.AttachedEntity.Value, hands.ActiveHand, coords, handsComp: hands);
}
// WD EDIT END
// always send to server. // always send to server.
return false; return false;

View File

@@ -1,5 +1,6 @@
using Content.Shared.Damage.Prototypes; using Content.Shared.Damage.Prototypes;
using Robust.Shared.Audio; using Robust.Shared.Audio;
using Robust.Shared.GameStates;
using Robust.Shared.Serialization.TypeSerializers.Implementations.Custom.Prototype.Dictionary; using Robust.Shared.Serialization.TypeSerializers.Implementations.Custom.Prototype.Dictionary;
namespace Content.Shared.Weapons.Melee.Components; 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. /// Plays the specified sound upon receiving damage of the specified type.
/// </summary> /// </summary>
[RegisterComponent] [RegisterComponent]
[NetworkedComponent, AutoGenerateComponentState] // WD
public sealed partial class MeleeSoundComponent : Component public sealed partial class MeleeSoundComponent : Component
{ {
/// <summary> /// <summary>
@@ -16,6 +18,7 @@ public sealed partial class MeleeSoundComponent : Component
/// </summary> /// </summary>
[DataField("soundGroups", [DataField("soundGroups",
customTypeSerializer: typeof(PrototypeIdDictionarySerializer<SoundSpecifier, DamageGroupPrototype>))] customTypeSerializer: typeof(PrototypeIdDictionarySerializer<SoundSpecifier, DamageGroupPrototype>))]
[AutoNetworkedField] // WD
public Dictionary<string, SoundSpecifier>? SoundGroups; public Dictionary<string, SoundSpecifier>? SoundGroups;
/// <summary> /// <summary>
@@ -24,10 +27,12 @@ public sealed partial class MeleeSoundComponent : Component
/// </summary> /// </summary>
[DataField("soundTypes", [DataField("soundTypes",
customTypeSerializer: typeof(PrototypeIdDictionarySerializer<SoundSpecifier, DamageTypePrototype>))] customTypeSerializer: typeof(PrototypeIdDictionarySerializer<SoundSpecifier, DamageTypePrototype>))]
[AutoNetworkedField] // WD
public Dictionary<string, SoundSpecifier>? SoundTypes; public Dictionary<string, SoundSpecifier>? SoundTypes;
/// <summary> /// <summary>
/// Sound that plays if no damage is done. /// Sound that plays if no damage is done.
/// </summary> /// </summary>
[AutoNetworkedField] // WD
[DataField("noDamageSound")] public SoundSpecifier? NoDamageSound; [DataField("noDamageSound")] public SoundSpecifier? NoDamageSound;
} }

View File

@@ -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<BodyComponent, RejuvenateEvent>(OnRejuvenate);
}
private void OnRejuvenate(Entity<BodyComponent> 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 _);
}
}
}
}

View File

@@ -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 CultBloodRitesInstantActionEvent : InstantActionEvent
{ {
} }
public sealed partial class CultBloodSpearRecallInstantActionEvent : InstantActionEvent
{
}

View File

@@ -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<CultistComponent>? User;
[DataField]
public EntProtoId Action;
[DataField]
public SoundSpecifier ShatterSound = new SoundCollectionSpecifier("GlassBreak");
}

View File

@@ -0,0 +1,7 @@
namespace Content.Shared._White.Cult.Components;
[RegisterComponent]
public sealed partial class BoltBarrageComponent : Component
{
public bool Unremoveable = true;
}

View File

@@ -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;
}

View File

@@ -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
}

View File

@@ -9,8 +9,8 @@ public sealed partial class CultEmpowerComponent : Component
[DataField("isRune")] [DataField("isRune")]
public bool IsRune; public bool IsRune;
public int MaxAllowedCultistActions = 4; public int MaxAllowedCultistActions = 5;
public int MinRequiredCultistActions = 1; public int MinRequiredCultistActions = 2;
} }
[Serializable, NetSerializable] [Serializable, NetSerializable]

View File

@@ -1,6 +1,9 @@
using System.Threading; using System.Threading;
using Content.Shared.FixedPoint;
using Content.Shared.Mind;
using Robust.Shared.Audio; using Robust.Shared.Audio;
using Robust.Shared.GameStates; using Robust.Shared.GameStates;
using Robust.Shared.Serialization.TypeSerializers.Implementations.Custom.Prototype.List;
namespace Content.Shared._White.Cult.Components; 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. /// This is used for tagging a mob as a cultist.
/// </summary> /// </summary>
[RegisterComponent, NetworkedComponent, AutoGenerateComponentState] [RegisterComponent, NetworkedComponent, AutoGenerateComponentState]
public sealed partial class CultistComponent : Component public sealed partial class CultistComponent : ShowCultHudComponent
{ {
[DataField("greetSound", customTypeSerializer: typeof(SoundSpecifierTypeSerializer))] [DataField("greetSound", customTypeSerializer: typeof(SoundSpecifierTypeSerializer))]
public SoundSpecifier? CultistGreetSound = new SoundPathSpecifier("/Audio/CultSounds/fart.ogg"); public SoundSpecifier? CultistGreetSound = new SoundPathSpecifier("/Audio/CultSounds/fart.ogg");
@@ -21,12 +24,17 @@ public sealed partial class CultistComponent : Component
[AutoNetworkedField] [AutoNetworkedField]
public List<NetEntity?> SelectedEmpowers = new(); public List<NetEntity?> SelectedEmpowers = new();
[ViewVariables(VVAccess.ReadWrite)]
public FixedPoint2 RitesBloodAmount = FixedPoint2.Zero;
public static string SummonCultDaggerAction = "InstantActionSummonCultDagger"; public static string SummonCultDaggerAction = "InstantActionSummonCultDagger";
public static string BloodRitesAction = "InstantActionBloodRites"; public static string BloodRitesAction = "InstantActionBloodRites";
public static string EmpPulseAction = "InstantActionEmpPulse"; public static string EmpPulseAction = "InstantActionEmpPulse";
public static string ConcealPresenceAction = "InstantActionConcealPresence";
public static string CultTwistedConstructionAction = "ActionCultTwistedConstruction"; public static string CultTwistedConstructionAction = "ActionCultTwistedConstruction";
public static string CultTeleportAction = "ActionCultTeleport"; public static string CultTeleportAction = "ActionCultTeleport";
@@ -40,6 +48,22 @@ public sealed partial class CultistComponent : Component
public static List<string> CultistActions = new() public static List<string> CultistActions = new()
{ {
SummonCultDaggerAction, BloodRitesAction, CultTwistedConstructionAction, CultTeleportAction, SummonCultDaggerAction, BloodRitesAction, CultTwistedConstructionAction, CultTeleportAction,
CultSummonCombatEquipmentAction, CultStunAction, EmpPulseAction, CultShadowShacklesAction CultSummonCombatEquipmentAction, CultStunAction, EmpPulseAction, ConcealPresenceAction, CultShadowShacklesAction
}; };
[DataField("bloodRites", customTypeSerializer: typeof(PrototypeIdListSerializer<CultistFactoryProductionPrototype>))]
public List<string> BloodRites = new ()
{
"FactoryCultBloodSpear",
"FactoryCultBloodBarrage"
};
[ViewVariables, NonSerialized]
public Entity<BloodSpearComponent>? BloodSpear;
[ViewVariables, NonSerialized]
public EntityUid? BloodSpearActionEntity;
[ViewVariables, NonSerialized]
public Entity<MindComponent>? OriginalMind;
} }

View File

@@ -0,0 +1,8 @@
using Robust.Shared.GameStates;
namespace Content.Shared._White.Cult.Components;
[Virtual, RegisterComponent, NetworkedComponent]
public partial class ShowCultHudComponent : Component
{
}

View File

@@ -17,6 +17,9 @@ public sealed class CultistFactoryProductionPrototype : IPrototype
[DataField("name", required: true)] [DataField("name", required: true)]
public string Name = default!; public string Name = default!;
[DataField]
public int BloodCost;
} }
[Serializable, NetSerializable] [Serializable, NetSerializable]

View File

@@ -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<BloodSpearComponent, ComponentRemove>(OnRemove);
SubscribeLocalEvent<BloodSpearComponent, GotEquippedHandEvent>(OnEquip);
SubscribeLocalEvent<BloodSpearComponent, ThrowDoHitEvent>(OnThrowDoHit);
}
private void OnThrowDoHit(Entity<BloodSpearComponent> 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<BloodSpearComponent> ent, ref GotEquippedHandEvent args)
{
if (!TryComp(args.User, out CultistComponent? cultist))
return;
Entity<CultistComponent> 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<CultistComponent> 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<CultistComponent> user, Entity<BloodSpearComponent> spear)
{
_actionsSystem.AddAction(user, ref user.Comp.BloodSpearActionEntity, spear.Comp.Action);
user.Comp.BloodSpear = spear;
spear.Comp.User = user;
}
private void OnRemove(Entity<BloodSpearComponent> ent, ref ComponentRemove args)
{
if (ent.Comp.User != null)
DetachSpearFromUser(ent.Comp.User.Value);
}
}

View File

@@ -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<BoltBarrageComponent, AttemptShootEvent>(OnShootAttempt);
SubscribeLocalEvent<BoltBarrageComponent, GunShotEvent>(OnGunShot);
SubscribeLocalEvent<BoltBarrageComponent, DroppedEvent>(OnDrop);
SubscribeLocalEvent<BoltBarrageComponent, UnequippedHandEvent>(OnUnequipHand);
SubscribeLocalEvent<BoltBarrageComponent, ContainerGettingRemovedAttemptEvent>(OnRemoveAttempt);
SubscribeLocalEvent<BoltBarrageComponent, OnEmptyGunShotEvent>(OnEmptyShot);
}
private void OnUnequipHand(Entity<BoltBarrageComponent> ent, ref UnequippedHandEvent args)
{
if (_net.IsServer && ent.Comp.Unremoveable)
QueueDel(ent);
}
private void OnRemoveAttempt(Entity<BoltBarrageComponent> ent, ref ContainerGettingRemovedAttemptEvent args)
{
if (!_timing.ApplyingState && ent.Comp.Unremoveable)
args.Cancel();
}
private void OnEmptyShot(Entity<BoltBarrageComponent> ent, ref OnEmptyGunShotEvent args)
{
if (_net.IsServer)
QueueDel(ent);
}
private void OnDrop(Entity<BoltBarrageComponent> ent, ref DroppedEvent args)
{
if (_net.IsServer)
QueueDel(ent);
}
private void OnGunShot(Entity<BoltBarrageComponent> 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<BoltBarrageComponent> ent, ref AttemptShootEvent args)
{
if (!HasComp<CultistComponent>(args.User) && !HasComp<GhostComponent>(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");
}
}

View File

@@ -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<ConcealableComponent, ExamineAttemptEvent>(OnExamine);
SubscribeLocalEvent<ConcealableComponent, InteractionAttemptEvent>(OnInteract);
}
private void OnInteract(Entity<ConcealableComponent> ent, ref InteractionAttemptEvent args)
{
if (ent.Comp is {Concealed: true, ExaminableWhileConcealed: false})
args.Cancel();
}
private void OnExamine(Entity<ConcealableComponent> 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;
}
}

View File

@@ -49,6 +49,15 @@ public sealed class CultItemSystem : EntitySystem
_popupSystem.PopupClient(Loc.GetString("cult-item-component-pickup-fail", ("name", Name(uid))), uid, args.User); _popupSystem.PopupClient(Loc.GetString("cult-item-component-pickup-fail", ("name", Name(uid))), uid, args.User);
} }
public bool CanThrow(EntityUid player, EntityUid throwEnt)
{
if (!HasComp<CultItemComponent>(throwEnt) || CanUse(player))
return true;
_popupSystem.PopupEntity(Loc.GetString("cult-item-component-throw-fail"), player, player);
return false;
}
private bool CanUse(EntityUid? uid) private bool CanUse(EntityUid? uid)
{ {
return HasComp<CultistComponent>(uid) || HasComp<GhostComponent>(uid); return HasComp<CultistComponent>(uid) || HasComp<GhostComponent>(uid);

View File

@@ -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;
}
}

View File

@@ -0,0 +1,9 @@
using Robust.Shared.Serialization;
namespace Content.Shared._White.Cult.UI;
[Serializable, NetSerializable]
public enum BloodRitesUi
{
Key
}

View File

@@ -12,10 +12,10 @@ public enum ListViewSelectorUiKey
[Serializable, NetSerializable] [Serializable, NetSerializable]
public class ListViewBUIState : BoundUserInterfaceState public class ListViewBUIState : BoundUserInterfaceState
{ {
public List<string> Items { get; set; } public List<EntProtoId> Items { get; set; }
public bool IsUsingPrototypes { get; set; } public bool IsUsingPrototypes { get; set; }
public ListViewBUIState(List<string> items, bool isUsingPrototypes) public ListViewBUIState(List<EntProtoId> items, bool isUsingPrototypes)
{ {
Items = items; Items = items;
IsUsingPrototypes = isUsingPrototypes; IsUsingPrototypes = isUsingPrototypes;

Binary file not shown.

Binary file not shown.

Binary file not shown.

View File

@@ -1,12 +1,15 @@
cult-role-greeting = cult-role-greeting =
Вы - член культа! Вы - член культа!
Ваши цели перечислены в меню персонажа. Ваши цели перечислены в меню персонажа.
В ваш рюкзак были добавлены предметы, которые помогут вам. В ваш рюкзак были добавлены предметы, которые помогут вам.
И помните - вы не единственный. И помните - вы не единственный.
Слава Нар`си! Слава Нар`си!
cult-role-briefing-short = Используйте '^' для связи с другими членами культа.
cult-cond-cultwin = Культ одержал победу cult-cond-cultwin = Культ одержал победу
cult-cond-cultfailure = Экипаж уничтожил культ cult-cond-cultfailure = Экипаж уничтожил культ
cult-cond-cultdraw = Ничейный исход
cultists-list-start = Культистами были: cultists-list-start = Культистами были:
cultists-list-name = - [color=White]{ $name }[/color] ([color=gray]{ $user }[/color]) cultists-list-name = - [color=White]{ $name }[/color] ([color=gray]{ $user }[/color])

View File

@@ -1,4 +1,4 @@
pylon-toggle-on = Кристалл воспаряет над пьедесталом, начиная пульсировать pylon-toggle-on = Кристалл воспаряет над пьедесталом, начиная пульсировать
pylon-toggle-off = Кристалл перестаёт пульсировать, опускаясь на пьедестал pylon-toggle-off = Кристалл перестаёт пульсировать, опускаясь на пьедестал
ent-CultPylon = пилон ent-CultPylon = пилон
.desc = Мистический конструкция. .desc = Мистическая конструкция.

View File

@@ -1,11 +1,28 @@
ent-OfferingRune = руна предпонесения ent-OfferingRune = руна предпонесения
.desc = Мгновенно превращает обычного члена экипажа в культиста, для чего требуется 2 культиста вокруг руны. Члена экипажа с имплантом защиты разума нельзя перевоплотить, можно только принести в жертву, для чего нужно 3 культиста, которые встанут вокруг руны. Если цель мертва, то она будет принесена в жертву, для чего требуется 1 культист.
ent-BuffRune = руна усиления ent-BuffRune = руна усиления
.desc = При активации усиливает вас, уменьшая затраты и ускоряя процесс подготовки заклинаний крови и черчения рун.
ent-EmpoweringRune = руна могущества ent-EmpoweringRune = руна могущества
.desc = Позволяет культистам приготовить до 5 заклинаний крови.
ent-TeleportRune = руна телепортации ent-TeleportRune = руна телепортации
.desc = Эта руна при использовании переносит всё, что находится на ней, к другой руне телепортации.
ent-SummoningRune = руна призыва ent-SummoningRune = руна призыва
.desc = Эта руна позволяет мгновенно призвать к руне любого несвязанного культиста. Для использования требуются 2 культиста, стоящих вокруг руны.
ent-ReviveRune = руна воскрешения ent-ReviveRune = руна воскрешения
.desc = Каждый раз, когда кого-то приносят в жертву на Руне Преподнесения, этой руне добавляется один глобальный заряд. Размещение трупа культиста на руне и её активация вернет его к жизни, расходуя при этом заряд.
ent-BarrierRune = руна барьера ent-BarrierRune = руна барьера
.desc = При активации на создаёт барьер, блокирующую проход. Может быть повторно активирована, чтобы отменить заклинание.
ent-BloodBoilRune = руна кипящей крови ent-BloodBoilRune = руна кипящей крови
.desc = При активации отнимает у призывателей некоторое количество крови, чтобы отправить несколько разрушительных импульсов любому не-культисту поблизости. Когда эффект закончится, руна ненадолго подожжет все, что находится вокруг нее. Для активации требуются 3 культиста, стоящих вокруг руны.
ent-ApocalypseRune = ритуал пространственного разрыва ent-ApocalypseRune = ритуал пространственного разрыва
.desc = Руна призыва Нар'Си.
runes-window-title = Руны runes-window-title = Руны

View File

@@ -35,3 +35,9 @@ ent-ActionCultShadowShackles = Теневые Узы
ent-InstantActionEmpPulse = Электромагнитный Импульс ent-InstantActionEmpPulse = Электромагнитный Импульс
.desc = Большое заклинание, которое позволяет пользователю направлять темную энергию в ЭМИ. .desc = Большое заклинание, которое позволяет пользователю направлять темную энергию в ЭМИ.
ent-InstantActionConcealPresence = Сокрытие Присутствия
.desc = Многофункциональное заклинание, чередующееся между скрытием и обнаружением ближайших рун и структур культа.
ent-InstantActionRecallBloodSpear = Призвать Кровавое Копьё
.desc = Призывает кровавое копье в вашу руку.

View File

@@ -0,0 +1,2 @@
bolt-barrage-component-no-empty-hand = Вам нужно иметь свободную руку, чтобы стрелять.
bolt-barrage-component-not-cultist = Вы не умеете пользоваться магией.

View File

@@ -1,3 +1,4 @@
cult-item-component-pickup-fail = Вы не можете подобрать {$name} cult-item-component-pickup-fail = Вы не можете подобрать {$name}
cult-item-component-attack-fail = Оружие не позволяет вам атаковать cult-item-component-attack-fail = Оружие не позволяет вам атаковать
cult-item-component-equip-fail = Вы не можете это надеть cult-item-component-equip-fail = Вы не можете это надеть
cult-item-component-throw-fail = Оружие не позволяет вам его бросить

View File

@@ -9,3 +9,9 @@ ent-CultMirrorShield = зеркальный щит
.desc = Щит с зеркалом на лицевой стороне, на котором изображен какой-то религиозный знак. .desc = Щит с зеркалом на лицевой стороне, на котором изображен какой-то религиозный знак.
ent-CultOuterArmor = бронированная мантия ent-CultOuterArmor = бронированная мантия
.desc = С первого взгляда кажется, что это простая мантия, но на ней имеется элементы брони. .desc = С первого взгляда кажется, что это простая мантия, но на ней имеется элементы брони.
ent-ShadowShackles = теневые оковы
.desc = Оковы, сковывающие запястья с помощью зловещей магии.
ent-BloodSpear = кровавое копьё
.desc = Ужасающее копьё, полностью состоящее из кристаллизованной крови.
ent-BloodBarrage = залп кровавых снарядов
.desc = Кровь за кровь.

View File

@@ -0,0 +1,4 @@
apocalypse-rune-title = Вы готовы к финальной битве?
apocalypse-rune-text = Это последний шаг к вызову Нар'Си; это долгий, болезненный ритуал, и экипаж будет предупрежден о вашем присутствии.
apocalypse-rune-accept-button = Жизнь за Нар'Си!
apocalypse-rune-deny-button = Нет

View File

@@ -1,4 +1,4 @@
pylon-toggle-on = Кристалл воспаряет над пьедесталом, начиная пульсировать pylon-toggle-on = Кристалл воспаряет над пьедесталом, начиная пульсировать
pylon-toggle-off = Кристалл перестаёт пульсировать, опускаясь на пьедестал pylon-toggle-off = Кристалл перестаёт пульсировать, опускаясь на пьедестал
ent-CultPylon = пилон ent-CultPylon = пилон
.desc = Мистический конструкция. .desc = Мистическая конструкция.

View File

@@ -1,9 +1,14 @@
verb-categories-cult = Культ verb-categories-cult = Культ
verb-spell-create-text = Создать заклинание крови verb-spell-create-text = Создать заклинание крови
verb-spell-create-message = Вы можете создать одно заклинание крови без руны могущества. verb-spell-create-message = Вы можете создать два заклинания крови без руны могущества.
verb-spell-create-too-much = Начертите руну могущества, чтобы создать больше одного заклинания крови. verb-spell-create-too-much = Начертите руну могущества, чтобы создать больше двух заклинаний крови.
verb-spell-remove-text = Удалить заклинание крови verb-spell-remove-text = Удалить заклинание крови
verb-spell-remove-message = Убрать любое из созданных заклинаний крови. verb-spell-remove-message = Убрать любое из созданных заклинаний крови.
verb-spell-remove-no-spells = Заклинания крови отсутствуют. 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 = Вам нужна свободная рука для обряда.

View File

@@ -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 = Слишком много способностей

View File

@@ -40,6 +40,7 @@
whitelist: whitelist:
tags: tags:
- CartridgePistol - CartridgePistol
- type: GiftIgnore
- type: entity - type: entity
id: MagazinePistolDebug id: MagazinePistolDebug
@@ -55,6 +56,7 @@
capacity: 1000 capacity: 1000
- type: Sprite - type: Sprite
sprite: Objects/Weapons/Guns/Ammunition/Magazine/Pistol/pistol_mag.rsi sprite: Objects/Weapons/Guns/Ammunition/Magazine/Pistol/pistol_mag.rsi
- type: GiftIgnore
- type: entity - type: entity
id: BulletDebug id: BulletDebug
@@ -103,6 +105,7 @@
- type: Item - type: Item
size: Tiny size: Tiny
sprite: Objects/Weapons/Melee/debug.rsi sprite: Objects/Weapons/Melee/debug.rsi
- type: GiftIgnore
- type: entity - type: entity
name: bang stick 100dmg name: bang stick 100dmg
@@ -116,6 +119,7 @@
damage: damage:
types: types:
Blunt: 100 Blunt: 100
- type: GiftIgnore
- type: entity - type: entity
name: bang stick 200dmg name: bang stick 200dmg
@@ -129,3 +133,4 @@
damage: damage:
types: types:
Blunt: 200 Blunt: 200
- type: GiftIgnore

View File

@@ -87,6 +87,7 @@
- type: IgnoreUIRange - type: IgnoreUIRange
- type: ShowRevIcons - type: ShowRevIcons
- type: ShowZombieIcons - type: ShowZombieIcons
- type: ShowCultHud
- type: Inventory - type: Inventory
templateId: aghost templateId: aghost
- type: InventorySlots - type: InventorySlots

View File

@@ -217,6 +217,8 @@
type: SpellSelectorBUI type: SpellSelectorBUI
- key: enum.CultEmpowerRemoveUiKey.Key - key: enum.CultEmpowerRemoveUiKey.Key
type: SpellRemoverBUI type: SpellRemoverBUI
- key: enum.BloodRitesUi.Key
type: CultistFactoryBUI
# WD-EDIT END # WD-EDIT END
- type: Puller - type: Puller
- type: Butcherable - type: Butcherable

View File

@@ -85,7 +85,6 @@
preset: StorePresetUplink preset: StorePresetUplink
balance: balance:
Telecrystal: 20 Telecrystal: 20
- type: GiftIgnore
- type: entity - type: entity
parent: BaseUplinkRadio parent: BaseUplinkRadio
@@ -96,7 +95,6 @@
preset: StorePresetUplink preset: StorePresetUplink
balance: balance:
Telecrystal: 25 Telecrystal: 25
- type: GiftIgnore
#this uplink MUST be used for nukeops, as it has the tag for filtering the listing. #this uplink MUST be used for nukeops, as it has the tag for filtering the listing.
- type: entity - type: entity
@@ -111,7 +109,6 @@
- type: Tag - type: Tag
tags: tags:
- NukeOpsUplink - NukeOpsUplink
- type: GiftIgnore
- type: entity - type: entity
parent: BaseUplinkRadio parent: BaseUplinkRadio

View File

@@ -340,6 +340,7 @@
unshaded: unshaded:
True: { visible: true } True: { visible: true }
False: { visible: false } False: { visible: false }
- type: GiftIgnore
- type: entity - type: entity
name: force gun name: force gun
@@ -382,6 +383,7 @@
unshaded: unshaded:
True: { visible: true } True: { visible: true }
False: { visible: false } False: { visible: false }
- type: GiftIgnore
- type: entity - type: entity
name: meteor launcher name: meteor launcher
@@ -395,6 +397,7 @@
tags: tags:
- CartridgeRocket - CartridgeRocket
proto: MeteorLarge proto: MeteorLarge
- type: GiftIgnore
- type: entity - type: entity
name: immovable rod launcher name: immovable rod launcher
@@ -408,3 +411,4 @@
tags: tags:
- CartridgeRocket - CartridgeRocket
proto: ImmovableRodSlow proto: ImmovableRodSlow
- type: GiftIgnore

View File

@@ -1379,3 +1379,19 @@
shader: unshaded shader: unshaded
- type: ChangeTemperatureOnCollide - type: ChangeTemperatureOnCollide
temperature: -20 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

View File

@@ -20,8 +20,10 @@
size: Normal size: Normal
- type: Clothing - type: Clothing
sprite: Objects/Weapons/Melee/cult_dagger.rsi sprite: Objects/Weapons/Melee/cult_dagger.rsi
quickEquip: false
slots: slots:
- back - back
- suitStorage
- type: DisarmMalus - type: DisarmMalus
- type: RuneDrawerProvider - type: RuneDrawerProvider
runePrototypes: [ OfferingRune, runePrototypes: [ OfferingRune,
@@ -33,6 +35,10 @@
SummoningRune, SummoningRune,
BloodBoilRune, BloodBoilRune,
ApocalypseRune ] ApocalypseRune ]
- type: ActivatableUI
key: enum.ListViewSelectorUiKey.Key
inHandsOnly: true
closeOnHandDeselect: true
- type: UserInterface - type: UserInterface
interfaces: interfaces:
- key: enum.ListViewSelectorUiKey.Key - key: enum.ListViewSelectorUiKey.Key
@@ -64,8 +70,10 @@
size: Huge size: Huge
- type: Clothing - type: Clothing
sprite: Objects/Weapons/Melee/cult_blade.rsi sprite: Objects/Weapons/Melee/cult_blade.rsi
quickEquip: false
slots: slots:
- back - back
- suitStorage
- type: DisarmMalus - type: DisarmMalus
- type: CultItem - type: CultItem

View File

@@ -197,13 +197,13 @@
thresholds: thresholds:
- trigger: - trigger:
!type:DamageTrigger !type:DamageTrigger
damage: 1200 damage: 600
behaviors: behaviors:
- !type:DoActsBehavior - !type:DoActsBehavior
acts: ["Destruction"] acts: ["Destruction"]
- trigger: - trigger:
!type:DamageTrigger !type:DamageTrigger
damage: 600 damage: 300
behaviors: behaviors:
- !type:PlaySoundBehavior - !type:PlaySoundBehavior
sound: sound:
@@ -220,6 +220,16 @@
- type: Construction - type: Construction
graph: CultGirder graph: CultGirder
node: wall 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 - type: entity
parent: BaseWall parent: BaseWall

View File

@@ -45,6 +45,8 @@
type: SpellSelectorBUI type: SpellSelectorBUI
- key: enum.CultEmpowerRemoveUiKey.Key - key: enum.CultEmpowerRemoveUiKey.Key
type: SpellRemoverBUI type: SpellRemoverBUI
- key: enum.BloodRitesUi.Key
type: CultistFactoryBUI
# WD-EDIT END # WD-EDIT END
- type: Sprite - type: Sprite
scale: 0.9, 0.9 scale: 0.9, 0.9

View File

@@ -27,8 +27,6 @@
- HumanoidAppearance - HumanoidAppearance
- Cultist - Cultist
canTargetSelf: true canTargetSelf: true
deselectOnMiss: true
repeat: false
icon: icon:
sprite: /Textures/White/Cult/actions_cult.rsi sprite: /Textures/White/Cult/actions_cult.rsi
state: teleport state: teleport
@@ -50,8 +48,6 @@
- HumanoidAppearance - HumanoidAppearance
- Cultist - Cultist
canTargetSelf: true canTargetSelf: true
deselectOnMiss: true
repeat: false
icon: icon:
sprite: /Textures/White/Cult/actions_cult.rsi sprite: /Textures/White/Cult/actions_cult.rsi
state: armor state: armor
@@ -72,8 +68,6 @@
components: components:
- HumanoidAppearance - HumanoidAppearance
canTargetSelf: false canTargetSelf: false
deselectOnMiss: true
repeat: false
icon: icon:
sprite: /Textures/White/Cult/actions_cult.rsi sprite: /Textures/White/Cult/actions_cult.rsi
state: stun state: stun
@@ -94,8 +88,6 @@
components: components:
- HumanoidAppearance - HumanoidAppearance
canTargetSelf: false canTargetSelf: false
deselectOnMiss: true
repeat: false
icon: icon:
sprite: /Textures/White/Cult/actions_cult.rsi sprite: /Textures/White/Cult/actions_cult.rsi
state: shackles state: shackles
@@ -131,7 +123,7 @@
sprite: /Textures/White/Cult/actions_cult.rsi sprite: /Textures/White/Cult/actions_cult.rsi
state: blood_rites state: blood_rites
event: !type:CultBloodRitesInstantActionEvent event: !type:CultBloodRitesInstantActionEvent
charges: 4 charges: 5
temporary: true temporary: true
removeOnNoCharges: true removeOnNoCharges: true
@@ -149,3 +141,27 @@
charges: 1 charges: 1
temporary: true temporary: true
removeOnNoCharges: 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

View File

@@ -64,6 +64,18 @@
doAfter: 1 doAfter: 1
- node: airlock - node: airlock
entity: AirlockGlassCult entity: AirlockGlassCult
edges:
- to: start
completed:
- !type:SpawnPrototype
prototype: CultRunicMetal1
amount: 1
- !type:DeleteEntity { }
conditions:
- !type:EntityAnchored
steps:
- tool: Dagger
cultistOnly: true
- type: constructionGraph - type: constructionGraph
id: CultPylon id: CultPylon

View File

@@ -1,4 +1,4 @@
- type: construction - type: construction
id: CultGirder id: CultGirder
name: руническая балка name: руническая балка
description: Большой конструктивный элемент, изготовленный из металла. На этом есть руна. description: Большой конструктивный элемент, изготовленный из металла. На этом есть руна.
@@ -37,7 +37,7 @@
- type: construction - type: construction
id: CultPylon id: CultPylon
name: пилон name: пилон
description: Мистический конструкция. description: Мистическая конструкция.
graph: CultPylon graph: CultPylon
startNode: start startNode: start
targetNode: pylon targetNode: pylon

View File

@@ -68,6 +68,7 @@
- type: Construction - type: Construction
graph: CultPylon graph: CultPylon
node: pylon node: pylon
- type: Concealable
- type: entity - type: entity
id: AltarTome id: AltarTome
@@ -127,6 +128,7 @@
- type: Construction - type: Construction
graph: AltarTome graph: AltarTome
node: tome node: tome
- type: Concealable
- type: entity - type: entity
id: CultBloodAltar id: CultBloodAltar
@@ -185,6 +187,7 @@
- type: Construction - type: Construction
graph: CultBloodAltar graph: CultBloodAltar
node: altar node: altar
- type: Concealable
- type: entity - type: entity
id: CultForge id: CultForge
@@ -247,6 +250,7 @@
- type: Construction - type: Construction
graph: CultForge graph: CultForge
node: forge node: forge
- type: Concealable
- type: cultStructure - type: cultStructure
id: CultStructureAltarTome id: CultStructureAltarTome

View File

@@ -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

View File

@@ -1,4 +1,4 @@
- type: entity - type: entity
name: soul shard name: soul shard
description: Mysterious glowing shard. description: Mysterious glowing shard.
parent: BaseItem parent: BaseItem
@@ -25,6 +25,7 @@
True: { state: "soulstone2" } True: { state: "soulstone2" }
False: { state: "soulstone" } False: { state: "soulstone" }
- type: Speech - type: Speech
- type: IsDeadIC
- type: entity - type: entity
parent: SoulShard parent: SoulShard

View File

@@ -49,6 +49,7 @@
- type: ShowHealthIcons - type: ShowHealthIcons
damageContainers: damageContainers:
- Biological - Biological
- type: ShowSecurityIcons
- type: entity - type: entity
name: veil shifter name: veil shifter

View File

@@ -14,6 +14,7 @@
invokePhrase: "Mah'weyh pleggh at e'ntrath!" invokePhrase: "Mah'weyh pleggh at e'ntrath!"
- type: CultRuneOffering - type: CultRuneOffering
- type: CultRune - type: CultRune
- type: Concealable
- type: entity - type: entity
parent: CollideRune parent: CollideRune
@@ -31,6 +32,7 @@
invokePhrase: "Qu'laris ver'don, thal'sorin mik'thar!" invokePhrase: "Qu'laris ver'don, thal'sorin mik'thar!"
- type: CultRuneBuff - type: CultRuneBuff
- type: CultRune - type: CultRune
- type: Concealable
- type: entity - type: entity
parent: CollideRune parent: CollideRune
@@ -52,6 +54,8 @@
- key: enum.CultEmpowerUiKey.Key - key: enum.CultEmpowerUiKey.Key
type: SpellSelectorBUI type: SpellSelectorBUI
- type: CultRune - type: CultRune
- type: Concealable
examinableWhileConcealed: true
- type: entity - type: entity
parent: CollideRune parent: CollideRune
@@ -71,6 +75,7 @@
- type: CultRuneTeleport - type: CultRuneTeleport
label: "безымянная метка" label: "безымянная метка"
- type: CultRune - type: CultRune
- type: Concealable
- type: entity - type: entity
parent: CollideRune parent: CollideRune
@@ -89,6 +94,7 @@
gatherInvokers: true gatherInvokers: true
- type: CultRuneSummoning - type: CultRuneSummoning
- type: CultRune - type: CultRune
- type: Concealable
- type: entity - type: entity
parent: CollideRune parent: CollideRune
@@ -106,6 +112,7 @@
invokePhrase: "Pasnar val'keriam usinar. Savrae ines amutan. Yam'toth remium il'tarat!" invokePhrase: "Pasnar val'keriam usinar. Savrae ines amutan. Yam'toth remium il'tarat!"
- type: CultRuneRevive - type: CultRuneRevive
- type: CultRune - type: CultRune
- type: Concealable
- type: entity - type: entity
parent: CollideRune parent: CollideRune
@@ -123,6 +130,7 @@
invokePhrase: "Khari'd! Eske'te tannin!" invokePhrase: "Khari'd! Eske'te tannin!"
- type: CultRuneBarrier - type: CultRuneBarrier
- type: CultRune - type: CultRune
- type: Concealable
- type: entity - type: entity
parent: CollideRune parent: CollideRune
@@ -141,6 +149,7 @@
- type: CultRuneBloodBoil - type: CultRuneBloodBoil
projectilePrototype: ProjectileCult projectilePrototype: ProjectileCult
- type: CultRune - type: CultRune
- type: Concealable
- type: entity - type: entity
parent: BaseRune parent: BaseRune
@@ -155,4 +164,5 @@
- type: Appearance - type: Appearance
- type: CultRuneBase - type: CultRuneBase
invokePhrase: "TOK-LYR RQA-NAP G'OLT-ULOFT!" invokePhrase: "TOK-LYR RQA-NAP G'OLT-ULOFT!"
cultistGatheringRange: 1.5
- type: CultRuneApocalypse - type: CultRuneApocalypse

View File

@@ -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

View File

@@ -60,6 +60,18 @@
- type: Pullable - type: Pullable
- type: ContentEye - type: ContentEye
- type: Actions - type: Actions
- type: Hands
- type: ShowCultHud
- type: IsDeadIC
- type: NightVision
toggleSound: null
color: White
- type: ShowWhiteHealthBars
damageContainers:
- Biological
- type: ShowHealthIcons
damageContainers:
- Biological
- type: Tag - type: Tag
tags: tags:
- CannotSuicide - CannotSuicide
@@ -77,17 +89,15 @@
thresholds: thresholds:
0: Alive 0: Alive
150: Dead 150: Dead
- type: MovementSpeedModifier
baseWalkSpeed: 2
baseSprintSpeed: 2
- type: Construct - type: Construct
actions: actions:
- InstantActionJuggernautCreateWall - InstantActionJuggernautCreateWall
- type: MeleeWeapon - type: MeleeWeapon
canHeavyAttack: false
hidden: true hidden: true
angle: 30 angle: 30
animation: WeaponArcFist animation: WeaponArcSmash
attackRate: 0.25 attackRate: 1
damage: damage:
types: types:
Structural: 90 Structural: 90
@@ -114,13 +124,18 @@
InstantActionArtificerCreateCultistWall, InstantActionArtificerCreateCultistWall,
InstantActionArtificerCreateCultistAirlock, InstantActionArtificerCreateCultistAirlock,
] ]
- type: MovementSpeedModifier
baseWalkSpeed: 3
baseSprintSpeed: 3
- type: MovementIgnoreGravity
- type: Puller - type: Puller
needsHands: false needsHands: false
- type: MeleeWeapon - type: MeleeWeapon
canHeavyAttack: false
hidden: true hidden: true
angle: 30 angle: 30
animation: WeaponArcFist animation: WeaponArcPunch
attackRate: 0.85 attackRate: 1
damage: damage:
types: types:
Blunt: 5 Blunt: 5
@@ -133,8 +148,8 @@
description: run... description: run...
components: components:
- type: MovementSpeedModifier - type: MovementSpeedModifier
baseWalkSpeed: 3.0 baseWalkSpeed: 4.0
baseSprintSpeed: 3.0 baseSprintSpeed: 4.0
- type: MobThresholds - type: MobThresholds
thresholds: thresholds:
0: Alive 0: Alive
@@ -148,14 +163,16 @@
actions: [InstantActionWraithPhase] actions: [InstantActionWraithPhase]
- type: MovementIgnoreGravity - type: MovementIgnoreGravity
- type: MeleeWeapon - type: MeleeWeapon
canHeavyAttack: false
hidden: true hidden: true
angle: 30 angle: 30
animation: WeaponArcFist animation: WeaponArcPunch
attackRate: 0.5 attackRate: 1
damage: damage:
types: types:
Blunt: 10 Blunt: 10
Slash: 10 Slash: 10
Structural: 40
- type: entity - type: entity
id: ReaperConstruct id: ReaperConstruct
@@ -165,8 +182,8 @@
components: components:
- type: MovementIgnoreGravity - type: MovementIgnoreGravity
- type: MovementSpeedModifier - type: MovementSpeedModifier
baseWalkSpeed: 3.0 baseWalkSpeed: 5.0
baseSprintSpeed: 3.0 baseSprintSpeed: 5.0
- type: MobThresholds - type: MobThresholds
thresholds: thresholds:
0: Alive 0: Alive
@@ -176,10 +193,11 @@
- type: Puller - type: Puller
needsHands: false needsHands: false
- type: MeleeWeapon - type: MeleeWeapon
canHeavyAttack: false
hidden: true hidden: true
angle: 30 angle: 30
animation: WeaponArcFist animation: WeaponArcPunch
attackRate: 0.9 attackRate: 1
damage: damage:
types: types:
Blunt: 20 Blunt: 20

View File

@@ -9,7 +9,7 @@
Brute: Brute:
collection: GlassSmash collection: GlassSmash
- type: Sprite - type: Sprite
sprite: /Textures/White/Cult/Structures/cult_airlock.rsi sprite: White/Cult/Structures/cult_airlock.rsi
layers: layers:
- state: closed - state: closed
map: ["enum.DoorVisualLayers.Base"] map: ["enum.DoorVisualLayers.Base"]
@@ -18,8 +18,8 @@
- type: Occluder - type: Occluder
enabled: false enabled: false
- type: Door - type: Door
occludes: false
bumpOpen: true bumpOpen: true
occludes: false
crushDamage: crushDamage:
types: types:
Blunt: 15 Blunt: 15
@@ -27,8 +27,6 @@
path: /Audio/Effects/stonedoor_openclose.ogg path: /Audio/Effects/stonedoor_openclose.ogg
closeSound: closeSound:
path: /Audio/Effects/stonedoor_openclose.ogg path: /Audio/Effects/stonedoor_openclose.ogg
denySound:
path: /Audio/Machines/airlock_deny.ogg
- type: Airtight - type: Airtight
fixVacuum: true fixVacuum: true
noAirWhenFullyAirBlocked: false noAirWhenFullyAirBlocked: false
@@ -38,7 +36,7 @@
thresholds: thresholds:
- trigger: - trigger:
!type:DamageTrigger !type:DamageTrigger
damage: 500 damage: 200
behaviors: behaviors:
- !type:DoActsBehavior - !type:DoActsBehavior
acts: ["Destruction"] acts: ["Destruction"]
@@ -48,6 +46,22 @@
node: airlock node: airlock
- type: PlacementReplacement - type: PlacementReplacement
key: walls 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: placement:
mode: SnapgridCenter mode: SnapgridCenter
@@ -75,7 +89,7 @@
- GlassAirlockLayer - GlassAirlockLayer
- type: InteractionOutline - type: InteractionOutline
- type: Sprite - type: Sprite
sprite: /Textures/White/Cult/Structures/cult_girder.rsi sprite: White/Cult/Structures/cult_girder.rsi
state: cultgirder state: cultgirder
- type: Damageable - type: Damageable
damageContainer: StructuralInorganic damageContainer: StructuralInorganic
@@ -104,3 +118,13 @@
- type: Construction - type: Construction
graph: CultGirder graph: CultGirder
node: girder 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: Большой конструктивный элемент, изготовленный из металла. На этом есть руна.

View File

@@ -1,4 +1,4 @@
- type: tile - type: tile
id: CultFloor id: CultFloor
name: tiles-cult-floor name: tiles-cult-floor
sprite: /Textures/White/Cult/Tiles/cult_tile.rsi/cult.png sprite: /Textures/White/Cult/Tiles/cult_tile.rsi/cult.png
@@ -10,3 +10,20 @@
collection: FootstepFloor collection: FootstepFloor
itemDrop: FloorTileItemSteel itemDrop: FloorTileItemSteel
heatCapacity: 10000 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

View File

@@ -1,7 +1,20 @@
- type: entity - type: entity
id: Narsie id: Narsie
parent: BaseMob
name: Nar'si name: Nar'si
components: 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 - type: Sprite
layers: layers:
- map: [ "enum.NarsieLayer.Default" ] - map: [ "enum.NarsieLayer.Default" ]
@@ -15,6 +28,8 @@
- type: Narsie - type: Narsie
- type: Physics - type: Physics
bodyType: Dynamic bodyType: Dynamic
bodyStatus: InAir
- type: CanMoveInAir
- type: Fixtures - type: Fixtures
fixtures: fixtures:
Penis: Penis:

Binary file not shown.

After

Width:  |  Height:  |  Size: 545 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 588 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 491 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 455 B

View File

@@ -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
]
]
}
]
}

Binary file not shown.

After

Width:  |  Height:  |  Size: 441 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 556 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 553 B

View File

@@ -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
}
]
}

Binary file not shown.

After

Width:  |  Height:  |  Size: 590 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 590 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 2.1 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 2.5 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 2.1 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 2.5 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 2.5 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 2.6 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 2.5 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 2.1 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.8 KiB

View File

@@ -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"}]}

Some files were not shown because too many files have changed in this diff Show More