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.Components;
using Robust.Client.Graphics;
using Robust.Client.Player;
using Robust.Shared.Player;
@@ -20,10 +21,15 @@ public sealed class ShowCultHudSystem : EntitySystem
SubscribeLocalEvent<CultistComponent, PlayerAttachedEvent>(OnPlayerAttached);
SubscribeLocalEvent<CultistComponent, PlayerDetachedEvent>(OnPlayerDetached);
SubscribeLocalEvent<ShowCultHudComponent, ComponentInit>(OnComponentInit);
SubscribeLocalEvent<ShowCultHudComponent, ComponentRemove>(OnComponentRemoved);
SubscribeLocalEvent<ShowCultHudComponent, PlayerAttachedEvent>(OnPlayerAttached);
SubscribeLocalEvent<ShowCultHudComponent, PlayerDetachedEvent>(OnPlayerDetached);
_overlay = new CultHudOverlay(EntityManager);
}
private void OnComponentInit(EntityUid uid, CultistComponent component, ComponentInit args)
private void OnComponentInit(EntityUid uid, ShowCultHudComponent component, ComponentInit args)
{
if (_player.LocalSession?.AttachedEntity != uid)
return;
@@ -32,7 +38,7 @@ public sealed class ShowCultHudSystem : EntitySystem
}
private void OnComponentRemoved(EntityUid uid, CultistComponent component, ComponentRemove args)
private void OnComponentRemoved(EntityUid uid, ShowCultHudComponent component, ComponentRemove args)
{
if (_player.LocalSession?.AttachedEntity != uid)
return;
@@ -41,7 +47,7 @@ public sealed class ShowCultHudSystem : EntitySystem
}
private void OnPlayerAttached(EntityUid uid, CultistComponent component, PlayerAttachedEvent args)
private void OnPlayerAttached(EntityUid uid, ShowCultHudComponent component, PlayerAttachedEvent args)
{
if (_player.LocalSession != args.Player)
return;
@@ -49,7 +55,7 @@ public sealed class ShowCultHudSystem : EntitySystem
_overlayManager.AddOverlay(_overlay);
}
private void OnPlayerDetached(EntityUid uid, CultistComponent component, PlayerDetachedEvent args)
private void OnPlayerDetached(EntityUid uid, ShowCultHudComponent component, PlayerDetachedEvent args)
{
if (_player.LocalSession != args.Player)
return;

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

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)
return;
var player = _player.LocalPlayer?.ControlledEntity;
var player = _player.LocalEntity;
if (player == null)
return;
if (construct.ID == "CultPylon" && CheckForStructure(player, id))
{
var popup = _entMan.System<SharedPopupSystem>();
popup.PopupClient(Loc.GetString("cult-structure-craft-another-structure-nearby"), player.Value, player.Value);
return;
}
PlacementHijack hijack;
var constructSystem = _systemManager.GetEntitySystem<ConstructionSystem>();
var hijack = new ConstructionPlacementHijack(constructSystem, construct);
if (construct.ID == "CultPylon")
{
hijack = new CultPylonPlacementHijack(construct, _entMan, player.Value);
}
else
{
var constructSystem = _systemManager.GetEntitySystem<ConstructionSystem>();
hijack = new ConstructionPlacementHijack(constructSystem, construct);
}
_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<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(
@@ -176,13 +183,6 @@ public sealed class ChangelingRuleSystem : GameRuleSystem<ChangelingRuleComponen
PrototypeId = changelingRule.ChangelingPrototypeId
}, mind);
var briefing = Loc.GetString("changeling-role-briefing-short");
_roleSystem.MindAddRole(mindId, new RoleBriefingComponent
{
Briefing = briefing
}, mind, true);
_roleSystem.MindPlaySound(mindId, changelingRule.GreetSoundNotification, mind);
SendChangelingBriefing(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 AirtightSystem _airtightSystem = default!;
[Dependency] private readonly PryingSystem _pryingSystem = default!;
[Dependency] private readonly RunicDoorSystem _runicDoor = default!; // WD
public override void Initialize()
{
@@ -142,6 +143,9 @@ public sealed class DoorSystem : SharedDoorSystem
var otherUid = args.OtherEntity;
if (!_runicDoor.CanBumpOpen(uid, otherUid)) // WD
return;
if (Tags.HasTag(otherUid, "DoorBumpOpener"))
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))
{
if (!_runicDoor.CanBumpOpen(uid, other)) // WD
continue;
if (Tags.HasTag(other, "DoorBumpOpener") && TryOpen(uid, door, other, quiet: true))
break;
}

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

@@ -2,6 +2,7 @@ using System.Linq;
using Content.Server.Body.Components;
using Content.Shared._White.Cult;
using Content.Shared._White.Cult.Components;
using Content.Shared._White.Cult.UI;
using Content.Shared.DoAfter;
using Content.Shared.Verbs;
using Robust.Shared.Player;
@@ -16,6 +17,7 @@ public sealed partial class CultSystem
SubscribeLocalEvent<CultistComponent, CultEmpowerSelectedBuiMessage>(OnCultistEmpowerSelected);
SubscribeLocalEvent<CultistComponent, CultEmpowerRemoveBuiMessage>(OnCultistEmpowerRemove);
SubscribeLocalEvent<CultistComponent, SpellCreatedEvent>(OnSpellCreated);
SubscribeLocalEvent<CultistComponent, CultistFactoryItemSelectedMessage>(OnBloodRitesSelected);
}
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)
{
if (args.Cancelled || comp.SelectedEmpowers.Count >= 1)
if (args.Cancelled || comp.SelectedEmpowers.Count >= 2)
return;
var action = CultistComponent.CultistActions.FirstOrDefault(x => x.Equals(args.Spell));
@@ -55,7 +57,7 @@ public sealed partial class CultSystem
if (action == null)
return;
if (comp.SelectedEmpowers.Count >= 1)
if (comp.SelectedEmpowers.Count >= 2)
{
_popupSystem.PopupEntity(Loc.GetString("verb-spell-create-too-much"), ent, ent);
return;
@@ -98,8 +100,24 @@ public sealed partial class CultSystem
}
};
var bloodRitesVerb = new Verb
{
Text = Loc.GetString("verb-blood-rites-text"),
Message = Loc.GetString("verb-blood-rites-message", ("blood", ent.Comp.RitesBloodAmount)),
Category = VerbCategory.Cult,
Act = () =>
{
if (!_ui.TryGetUi(ent, BloodRitesUi.Key, out var bui))
return;
_ui.SetUiState(bui, new CultistFactoryBUIState(ent.Comp.BloodRites));
_ui.OpenUi(bui, actor.PlayerSession);
}
};
args.Verbs.Add(createSpellVerb);
args.Verbs.Add(removeSpellVerb);
args.Verbs.Add(bloodRitesVerb);
}
private void RemoveSpell(Entity<CultistComponent> ent, ICommonSession session)
@@ -112,4 +130,36 @@ public sealed partial class CultSystem
_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.Doors;
using Content.Shared.Humanoid;
using Content.Shared.Stunnable;
using Content.Shared._White.Cult;
using Content.Shared._White.Cult.Components;
using Content.Shared._White.Cult.Systems;
using Content.Shared.Cuffs.Components;
using Content.Shared.Doors.Components;
using Content.Shared.Mobs.Systems;
using Content.Shared.Weapons.Melee.Components;
using Content.Shared.Weapons.Melee.Events;
using Robust.Shared.Audio;
using Robust.Shared.Audio.Systems;
using Robust.Shared.Physics.Components;
using Robust.Shared.Physics.Systems;
using CultistComponent = Content.Shared._White.Cult.Components.CultistComponent;
@@ -19,6 +25,9 @@ public sealed class RunicDoorSystem : EntitySystem
[Dependency] private readonly SharedPhysicsSystem _physics = default!;
[Dependency] private readonly SharedStunSystem _stunSystem = default!;
[Dependency] private readonly SharedAudioSystem _audio = default!;
[Dependency] private readonly OccluderSystem _occluder = default!;
[Dependency] private readonly MobStateSystem _mobState = default!;
[Dependency] private readonly CuffableSystem _cuffable = default!;
public override void Initialize()
{
@@ -27,6 +36,31 @@ public sealed class RunicDoorSystem : EntitySystem
SubscribeLocalEvent<RunicDoorComponent, BeforeDoorOpenedEvent>(OnBeforeDoorOpened);
SubscribeLocalEvent<RunicDoorComponent, BeforeDoorClosedEvent>(OnBeforeDoorClosed);
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)
@@ -79,7 +113,8 @@ public sealed class RunicDoorSystem : EntitySystem
_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;
var direction = Transform(user).MapPosition.Position - Transform(airlock).MapPosition.Position;
@@ -90,4 +125,11 @@ public sealed class RunicDoorSystem : EntitySystem
_stunSystem.TryParalyze(user, TimeSpan.FromSeconds(3), true);
return false;
}
public bool CanBumpOpen(EntityUid uid, EntityUid otherUid)
{
return !HasComp<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._White.Cult;
using Content.Shared._White.Cult.Structures;
using Content.Shared._White.Cult.Systems;
using Content.Shared._White.Cult.UI;
using Robust.Server.GameObjects;
using Robust.Shared.Audio.Systems;
@@ -28,6 +29,7 @@ public sealed class CultistFactorySystem : EntitySystem
[Dependency] private readonly SharedDoAfterSystem _doAfterSystem = default!;
[Dependency] private readonly SharedPopupSystem _popup = default!;
[Dependency] private readonly SharedAudioSystem _audio = default!;
[Dependency] private readonly PhysicsSystem _physics = default!;
private const string RitualDaggerPrototypeId = "RitualDagger";
@@ -42,6 +44,12 @@ public sealed class CultistFactorySystem : EntitySystem
SubscribeLocalEvent<CultistFactoryComponent, InteractUsingEvent>(TryToggleAnchor);
SubscribeLocalEvent<CultistFactoryComponent, CultAnchorDoAfterEvent>(OnAnchorDoAfter);
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)

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))
{
if(!TryComp<PhysicsComponent>(activeItem, out var physicsComponent))
return;
continue;
var coords = Transform(args.Embedded).Coordinates;
_handsSystem.TryDrop(args.Embedded, coords);
_handsSystem.TryDrop(args.Embedded, activeItem, coords);
var force = physicsComponent.Mass * 2.5f / 2;

View File

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

View File

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

View File

@@ -1,6 +1,9 @@
using System.Threading;
using Content.Shared.FixedPoint;
using Content.Shared.Mind;
using Robust.Shared.Audio;
using Robust.Shared.GameStates;
using Robust.Shared.Serialization.TypeSerializers.Implementations.Custom.Prototype.List;
namespace Content.Shared._White.Cult.Components;
@@ -8,7 +11,7 @@ namespace Content.Shared._White.Cult.Components;
/// This is used for tagging a mob as a cultist.
/// </summary>
[RegisterComponent, NetworkedComponent, AutoGenerateComponentState]
public sealed partial class CultistComponent : Component
public sealed partial class CultistComponent : ShowCultHudComponent
{
[DataField("greetSound", customTypeSerializer: typeof(SoundSpecifierTypeSerializer))]
public SoundSpecifier? CultistGreetSound = new SoundPathSpecifier("/Audio/CultSounds/fart.ogg");
@@ -21,12 +24,17 @@ public sealed partial class CultistComponent : Component
[AutoNetworkedField]
public List<NetEntity?> SelectedEmpowers = new();
[ViewVariables(VVAccess.ReadWrite)]
public FixedPoint2 RitesBloodAmount = FixedPoint2.Zero;
public static string SummonCultDaggerAction = "InstantActionSummonCultDagger";
public static string BloodRitesAction = "InstantActionBloodRites";
public static string EmpPulseAction = "InstantActionEmpPulse";
public static string ConcealPresenceAction = "InstantActionConcealPresence";
public static string CultTwistedConstructionAction = "ActionCultTwistedConstruction";
public static string CultTeleportAction = "ActionCultTeleport";
@@ -40,6 +48,22 @@ public sealed partial class CultistComponent : Component
public static List<string> CultistActions = new()
{
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)]
public string Name = default!;
[DataField]
public int BloodCost;
}
[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);
}
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)
{
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]
public class ListViewBUIState : BoundUserInterfaceState
{
public List<string> Items { get; set; }
public List<EntProtoId> Items { get; set; }
public bool IsUsingPrototypes { get; set; }
public ListViewBUIState(List<string> items, bool isUsingPrototypes)
public ListViewBUIState(List<EntProtoId> items, bool isUsingPrototypes)
{
Items = items;
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-cultfailure = Экипаж уничтожил культ
cult-cond-cultdraw = Ничейный исход
cultists-list-start = Культистами были:
cultists-list-name = - [color=White]{ $name }[/color] ([color=gray]{ $user }[/color])

View File

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

View File

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

View File

@@ -35,3 +35,9 @@ ent-ActionCultShadowShackles = Теневые Узы
ent-InstantActionEmpPulse = Электромагнитный Импульс
.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-attack-fail = Оружие не позволяет вам атаковать
cult-item-component-equip-fail = Вы не можете это надеть
cult-item-component-throw-fail = Оружие не позволяет вам его бросить

View File

@@ -9,3 +9,9 @@ ent-CultMirrorShield = зеркальный щит
.desc = Щит с зеркалом на лицевой стороне, на котором изображен какой-то религиозный знак.
ent-CultOuterArmor = бронированная мантия
.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-off = Кристалл перестаёт пульсировать, опускаясь на пьедестал
ent-CultPylon = пилон
.desc = Мистический конструкция.
.desc = Мистическая конструкция.

View File

@@ -1,9 +1,14 @@
verb-categories-cult = Культ
verb-spell-create-text = Создать заклинание крови
verb-spell-create-message = Вы можете создать одно заклинание крови без руны могущества.
verb-spell-create-too-much = Начертите руну могущества, чтобы создать больше одного заклинания крови.
verb-spell-create-message = Вы можете создать два заклинания крови без руны могущества.
verb-spell-create-too-much = Начертите руну могущества, чтобы создать больше двух заклинаний крови.
verb-spell-remove-text = Удалить заклинание крови
verb-spell-remove-message = Убрать любое из созданных заклинаний крови.
verb-spell-remove-no-spells = Заклинания крови отсутствуют.
verb-blood-rites-text = Кровавые обряды
verb-blood-rites-message = Всего высосано крови: { $blood }.
verb-blood-rites-no-blood = Для обряда необходимо высосать { $required } крови с помощью заклинания Кровавые Обряды. Всего высосано: { $blood }.
verb-blood-rites-no-hand = Вам нужна свободная рука для обряда.

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

View File

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

View File

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

View File

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

View File

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

View File

@@ -1379,3 +1379,19 @@
shader: unshaded
- type: ChangeTemperatureOnCollide
temperature: -20
- type: entity
name: blood bolt
id: BloodBolt
parent: BaseBullet
noSpawn: true
components:
- type: Sprite
sprite: White/Cult/Entities/arcane_barrage.rsi
state: bullet
- type: Ammo
muzzleFlash: null
- type: Projectile
damage:
types:
Piercing: 20

View File

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

View File

@@ -197,13 +197,13 @@
thresholds:
- trigger:
!type:DamageTrigger
damage: 1200
damage: 600
behaviors:
- !type:DoActsBehavior
acts: ["Destruction"]
- trigger:
!type:DamageTrigger
damage: 600
damage: 300
behaviors:
- !type:PlaySoundBehavior
sound:
@@ -220,6 +220,16 @@
- type: Construction
graph: CultGirder
node: wall
- type: Appearance
- type: Concealable
examinableWhileConcealed: true
concealedSprite: White/Cult/Structures/Concealed/cult.rsi
revealedSprite: Structures/Walls/cult.rsi
changeMeta: true
concealedName: обычная стена
concealedDesc: Удерживает воздух внутри, а ассистентов снаружи.
revealedName: стена культа
revealedDesc: Удерживает воздух внутри, а ассистентов снаружи.
- type: entity
parent: BaseWall

View File

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

View File

@@ -27,8 +27,6 @@
- HumanoidAppearance
- Cultist
canTargetSelf: true
deselectOnMiss: true
repeat: false
icon:
sprite: /Textures/White/Cult/actions_cult.rsi
state: teleport
@@ -50,8 +48,6 @@
- HumanoidAppearance
- Cultist
canTargetSelf: true
deselectOnMiss: true
repeat: false
icon:
sprite: /Textures/White/Cult/actions_cult.rsi
state: armor
@@ -72,8 +68,6 @@
components:
- HumanoidAppearance
canTargetSelf: false
deselectOnMiss: true
repeat: false
icon:
sprite: /Textures/White/Cult/actions_cult.rsi
state: stun
@@ -94,8 +88,6 @@
components:
- HumanoidAppearance
canTargetSelf: false
deselectOnMiss: true
repeat: false
icon:
sprite: /Textures/White/Cult/actions_cult.rsi
state: shackles
@@ -131,7 +123,7 @@
sprite: /Textures/White/Cult/actions_cult.rsi
state: blood_rites
event: !type:CultBloodRitesInstantActionEvent
charges: 4
charges: 5
temporary: true
removeOnNoCharges: true
@@ -149,3 +141,27 @@
charges: 1
temporary: true
removeOnNoCharges: true
- type: entity
id: InstantActionConcealPresence
name: Conceal Presence
description: A multi-function spell that alternates between hiding and revealing nearby runes and cult structures.
noSpawn: true
components:
- type: InstantAction
icon:
sprite: /Textures/White/Cult/actions_cult.rsi
state: gone
event: !type:CultConcealInstantActionEvent
charges: 10
temporary: true
removeOnNoCharges: true
- type: ConcealPresenceSpell
concealEvent: !type:CultConcealInstantActionEvent
revealEvent: !type:CultRevealInstantActionEvent
concealIcon:
sprite: /Textures/White/Cult/actions_cult.rsi
state: gone
revealIcon:
sprite: /Textures/White/Cult/actions_cult.rsi
state: telerune

View File

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

View File

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

View File

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

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
description: Mysterious glowing shard.
parent: BaseItem
@@ -25,6 +25,7 @@
True: { state: "soulstone2" }
False: { state: "soulstone" }
- type: Speech
- type: IsDeadIC
- type: entity
parent: SoulShard

View File

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

View File

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

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

View File

@@ -9,7 +9,7 @@
Brute:
collection: GlassSmash
- type: Sprite
sprite: /Textures/White/Cult/Structures/cult_airlock.rsi
sprite: White/Cult/Structures/cult_airlock.rsi
layers:
- state: closed
map: ["enum.DoorVisualLayers.Base"]
@@ -18,8 +18,8 @@
- type: Occluder
enabled: false
- type: Door
occludes: false
bumpOpen: true
occludes: false
crushDamage:
types:
Blunt: 15
@@ -27,8 +27,6 @@
path: /Audio/Effects/stonedoor_openclose.ogg
closeSound:
path: /Audio/Effects/stonedoor_openclose.ogg
denySound:
path: /Audio/Machines/airlock_deny.ogg
- type: Airtight
fixVacuum: true
noAirWhenFullyAirBlocked: false
@@ -38,7 +36,7 @@
thresholds:
- trigger:
!type:DamageTrigger
damage: 500
damage: 200
behaviors:
- !type:DoActsBehavior
acts: ["Destruction"]
@@ -48,6 +46,22 @@
node: airlock
- type: PlacementReplacement
key: walls
- type: Appearance
- type: Concealable
examinableWhileConcealed: true
iconSmooth: true
interactionOutline: true
concealedSprite: White/Cult/Structures/Concealed/cult_airlock.rsi
revealedSprite: White/Cult/Structures/cult_airlock.rsi
changeMeta: true
concealedName: обычная стена
concealedDesc: Удерживает воздух внутри, а ассистентов снаружи.
revealedName: рунический шлюз
revealedDesc: Странный стеклянный шлюз с руной.
- type: IconSmooth
enabled: false
key: walls
mode: NoSprite
placement:
mode: SnapgridCenter
@@ -75,7 +89,7 @@
- GlassAirlockLayer
- type: InteractionOutline
- type: Sprite
sprite: /Textures/White/Cult/Structures/cult_girder.rsi
sprite: White/Cult/Structures/cult_girder.rsi
state: cultgirder
- type: Damageable
damageContainer: StructuralInorganic
@@ -104,3 +118,13 @@
- type: Construction
graph: CultGirder
node: girder
- type: Appearance
- type: Concealable
examinableWhileConcealed: true
concealedSprite: White/Cult/Structures/Concealed/cult_girder.rsi
revealedSprite: White/Cult/Structures/cult_girder.rsi
changeMeta: true
concealedName: каркас
concealedDesc: Большой металлический каркас; Необходимо покрыть листами металла, чтобы он считался стеной.
revealedName: руническая балка
revealedDesc: Большой конструктивный элемент, изготовленный из металла. На этом есть руна.

View File

@@ -1,4 +1,4 @@
- type: tile
- type: tile
id: CultFloor
name: tiles-cult-floor
sprite: /Textures/White/Cult/Tiles/cult_tile.rsi/cult.png
@@ -10,3 +10,20 @@
collection: FootstepFloor
itemDrop: FloorTileItemSteel
heatCapacity: 10000
- type: tile
id: CultFloorConcealed
name: tiles-steel-floor
sprite: /Textures/White/Cult/Tiles/concealed.png
variants: 3
placementVariants:
- 1.0
- 1.0
- 1.0
baseTurf: Plating
isSubfloor: false
deconstructTools: [ Prying ]
footstepSounds:
collection: FootstepFloor
itemDrop: FloorTileItemSteel
heatCapacity: 10000

View File

@@ -1,7 +1,20 @@
- type: entity
- type: entity
id: Narsie
parent: BaseMob
name: Nar'si
components:
- type: AnnounceOnSpawn
message: narsie-has-risen
sender: narsie-has-risen-sender
sound:
path: /Audio/Misc/narsie_rises.ogg
color: darkred
- type: CargoSellBlacklist
- type: ContentEye
maxZoom: 2.0,2.0
- type: WarpPoint
follow: true
location: Nar'Sie
- type: Sprite
layers:
- map: [ "enum.NarsieLayer.Default" ]
@@ -15,6 +28,8 @@
- type: Narsie
- type: Physics
bodyType: Dynamic
bodyStatus: InAir
- type: CanMoveInAir
- type: Fixtures
fixtures:
Penis:

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