Merge remote-tracking branch 'origin/master' into weapon-modules

This commit is contained in:
CaYpeN1
2024-03-24 14:03:24 +05:00
156 changed files with 2625 additions and 373 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 (_prototypeManager.TryIndex<EntityPrototype>(item, out var itemPrototype))
if (isPrototypes && _prototypeManager.TryIndex<EntityPrototype>(item, out var itemPrototype))
{
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;
if (construct.ID == "CultPylon")
{
hijack = new CultPylonPlacementHijack(construct, _entMan, player.Value);
}
else
{
var constructSystem = _systemManager.GetEntitySystem<ConstructionSystem>();
var hijack = new ConstructionPlacementHijack(constructSystem, construct);
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

@@ -4,6 +4,7 @@ using Content.Shared.Coordinates.Helpers;
using Content.Shared.Interaction;
using Content.Shared.Interaction.Events;
using Content.Shared.Popups;
using Content.Shared.Storage;
namespace Content.Server.Holosign;
@@ -59,7 +60,7 @@ public sealed class HolosignSystem : EntitySystem
return;
}
if (args.Handled || !args.CanReach)
if (args.Handled || !args.CanReach || HasComp<StorageComponent>(args.Target))
return;
if (component.Signs.Count >= component.Uses) // wd edit

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,6 +150,9 @@ public sealed class CultRuleSystem : GameRuleSystem<CultRuleComponent>
return;
cultistsRule.WinCondition = CultWinCondition.CultFailure;
// Check for all in once gamemode
if (!GameTicker.GetActiveGameRules().Where(HasComp<RampingStationEventSchedulerComponent>).Any())
_roundEndSystem.EndRound();
}
@@ -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,12 +210,165 @@ 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(
EntityUid uid,
CultistComponent component,
@@ -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;
if (_ui.TryGetUi(uid, ListViewSelectorUiKey.Key, out var bui))
{
_ui.SetUiState(bui, new ListViewBUIState(component.RunePrototypes, false));
_ui.OpenUi(bui, actorComponent.PlayerSession);
args.Cancel();
}
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

@@ -0,0 +1,99 @@
using System.Threading;
using Content.Shared._White.Mood;
using Content.Shared.Chemistry.Components;
using Content.Shared.Damage.Systems;
using Content.Shared.Drunk;
using Content.Shared.Standing;
using Content.Shared.StatusEffect;
using Robust.Shared.Random;
using Timer = Robust.Shared.Timing.Timer;
namespace Content.Shared._White.Chemistry;
public sealed class NarcoticEffect : EntitySystem
{
[Dependency] private readonly IRobustRandom _robustRandom = default!;
[Dependency] private readonly StatusEffectsSystem _statusEffectsSystem = default!;
[Dependency] private readonly StaminaSystem _stamina = default!;
[Dependency] private readonly StandingStateSystem _standingStateSystem = default!;
/// <inheritdoc/>
public override void Initialize()
{
base.Initialize();
SubscribeLocalEvent<NarcoticEffectComponent, ComponentInit>(OnInit);
SubscribeLocalEvent<NarcoticEffectComponent, ComponentRemove>(OnRemove);
}
private void OnInit(EntityUid uid, NarcoticEffectComponent component, ComponentInit args)
{
int index = _robustRandom.Next(0, Enum.GetNames(typeof(NarcoticEffects)).Length);
Effects(uid, component, index);
}
private void OnRemove(EntityUid uid, NarcoticEffectComponent component, ComponentRemove args)
{
if (TryComp<MovespeedModifierMetabolismComponent>(uid, out var movespeedModifierComponent))
{
if (movespeedModifierComponent.ModifierTimer != TimeSpan.Zero)
Timer.Spawn(movespeedModifierComponent.ModifierTimer, () => component.CancelTokenSource.Cancel());
return;
}
component.CancelTokenSource.Cancel();
}
private void Effects(EntityUid uid, NarcoticEffectComponent component, int index)
{
if(!TryComp<StandingStateComponent>(uid, out var standingComp))
return;
RaiseLocalEvent(uid, new MoodEffectEvent("Stimulator"));
CancellationToken token = component.CancelTokenSource.Token;
TryComp<StatusEffectsComponent>(uid, out var statusEffectsComp);
int timer = component.TimerInterval[_robustRandom.Next(0, component.TimerInterval.Count)];
int slur = component.SlurTime[_robustRandom.Next(0, component.SlurTime.Count)];
switch (Enum.GetValues(typeof(NarcoticEffects)).GetValue(index))
{
case NarcoticEffects.TremorAndShake when _statusEffectsSystem.HasStatusEffect(uid, "Drunk", statusEffectsComp):
Timer.SpawnRepeating(timer, () => _stamina.TakeStaminaDamage(uid, 15F), token);
_statusEffectsSystem.TryAddTime(uid, "Drunk", TimeSpan.FromSeconds(slur), statusEffectsComp);
break;
case NarcoticEffects.Shake when _statusEffectsSystem.HasStatusEffect(uid, "Drunk", statusEffectsComp):
_statusEffectsSystem.TryAddTime(uid, "Drunk", TimeSpan.FromSeconds(slur), statusEffectsComp);
break;
case NarcoticEffects.StunAndShake when _statusEffectsSystem.HasStatusEffect(uid, "Drunk", statusEffectsComp):
Timer.SpawnRepeating(timer, () => _standingStateSystem.TryLieDown(uid, standingComp), token);
_statusEffectsSystem.TryAddTime(uid, "Drunk", TimeSpan.FromSeconds(slur), statusEffectsComp);
break;
case NarcoticEffects.Stun:
Timer.SpawnRepeating(timer, () => _standingStateSystem.TryLieDown(uid, standingComp), token);
break;
case NarcoticEffects.TremorAndShake:
Timer.SpawnRepeating(timer, () => _stamina.TakeStaminaDamage(uid, 15F), token);
_statusEffectsSystem.TryAddStatusEffect<DrunkComponent>(uid, "Drunk", TimeSpan.FromSeconds(slur), true, statusEffectsComp);
break;
case NarcoticEffects.Tremor:
Timer.SpawnRepeating(timer, () => _stamina.TakeStaminaDamage(uid, 15F), token);
break;
case NarcoticEffects.Shake:
_statusEffectsSystem.TryAddStatusEffect<DrunkComponent>(uid, "Drunk", TimeSpan.FromSeconds(slur), true, statusEffectsComp);
break;
case NarcoticEffects.StunAndShake:
Timer.SpawnRepeating(timer, () => _standingStateSystem.TryLieDown(uid, standingComp), token);
_statusEffectsSystem.TryAddStatusEffect<DrunkComponent>(uid, "Drunk", TimeSpan.FromSeconds(slur), true, statusEffectsComp);
break;
}
}
}

View File

@@ -0,0 +1,30 @@
using System.Threading;
using Robust.Shared.Serialization;
namespace Content.Shared._White.Chemistry;
/// <summary>
/// This is used for...
/// </summary>
[RegisterComponent]
public sealed partial class NarcoticEffectComponent : Component
{
[ViewVariables(VVAccess.ReadWrite), DataField]
public CancellationTokenSource CancelTokenSource = new();
[ViewVariables(VVAccess.ReadOnly), DataField]
public List<int> TimerInterval = new() { 3000, 6000, 3800, 7000, 5000 };
[ViewVariables(VVAccess.ReadOnly), DataField]
public List<int> SlurTime = new() { 35, 60, 80, 90, 45 };
}
[Serializable, NetSerializable]
public enum NarcoticEffects
{
Stun,
Tremor,
Shake,
TremorAndShake,
StunAndShake
}

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

@@ -2604,3 +2604,171 @@
id: 212
time: '2024-03-21T20:20:10.0000000+00:00'
url: https://api.github.com/repos/frosty-dev/ss14-core/pulls/228
- author: Aviu
changes:
- message: "\u0417\u0430\u043A\u043B\u0438\u043D\u0430\u043D\u0438\u0435 \u0441\u043E\
\u043A\u0440\u044B\u0442\u0438\u044F \u043F\u0440\u0438\u0441\u0443\u0442\u0441\
\u0442\u0432\u0438\u044F \u0434\u043B\u044F \u043A\u0443\u043B\u044C\u0442\u0430\
."
type: Add
- message: "\u0422\u0435\u043F\u0435\u0440\u044C \u043C\u043E\u0436\u043D\u043E\
\ \u0441\u0434\u0435\u043B\u0430\u0442\u044C \u043A\u0440\u043E\u0432\u0430\u0432\
\u043E\u0435 \u043A\u043E\u043F\u044C\u0435 \u0438 \u0437\u0430\u043B\u043F\
\ \u043A\u0440\u043E\u0432\u0430\u0432\u044B\u0445 \u0441\u043D\u0430\u0440\u044F\
\u0434\u043E\u0432, \u043A\u043B\u0438\u043A\u043D\u0443\u0432 \u041F\u041A\u041C\
\ \u043F\u043E \u0441\u0435\u0431\u0435 -> \u041A\u0443\u043B\u044C\u0442 ->\
\ \u041A\u0440\u043E\u0432\u0430\u0432\u044B\u0435 \u043E\u0431\u0440\u044F\u0434\
\u044B."
type: Add
- message: "\u0414\u043E\u0431\u0430\u0432\u043B\u0435\u043D\u043E \u043E\u043F\u0438\
\u0441\u0430\u043D\u0438\u0435 \u043A \u0440\u0443\u043D\u0430\u043C."
type: Add
- message: "\u041A\u043E\u043D\u0441\u0442\u0440\u0443\u043A\u0442\u044B \u043F\u043E\
\u043B\u0443\u0447\u0438\u043B\u0438 \u043F\u0435\u0440\u0435\u043A\u043B\u044E\
\u0447\u0430\u0435\u043C\u043E\u0435 \u043D\u043E\u0447\u043D\u043E\u0435 \u0437\
\u0440\u0435\u043D\u0438\u0435."
type: Add
- message: "\u0414\u043E\u0431\u0430\u0432\u043B\u0435\u043D\u044B \u043F\u043E\u043F\
\u0430\u043F\u044B \u043F\u0440\u0438 \u0432\u043E\u0441\u043A\u0440\u0435\u0448\
\u0435\u043D\u0438\u0438\u0438 \u043D\u0430 \u0440\u0443\u043D\u0435 \u0438\
\ \u043F\u0440\u0438 \u043F\u043E\u043F\u044B\u0442\u043A\u0435 \u043D\u0430\
\u0447\u0435\u0440\u0442\u0438\u0442\u044C \u0440\u0438\u0442\u0443\u0430\u043B\
\ \u043F\u0440\u043E\u0441\u0442\u0440\u0430\u043D\u0441\u0442\u0432\u0435\u043D\
\u043D\u043E\u0433\u043E \u0440\u0430\u0437\u0440\u044B\u0432\u0430."
type: Add
- message: "\u041A\u043E\u043D\u0441\u0442\u0440\u0443\u043A\u0442\u044B \u0442\u0435\
\u043F\u0435\u0440\u044C \u0432\u0438\u0434\u044F\u0442 \u0438\u043A\u043E\u043D\
\u043A\u0438 \u043A\u0443\u043B\u044C\u0442\u0430."
type: Add
- message: "\u041A\u043E\u043D\u0441\u0442\u0440\u0443\u043A\u0442\u044B \u0442\u0435\
\u043F\u0435\u0440\u044C \u043C\u043E\u0433\u0443\u0442 \u043B\u0435\u0447\u0438\
\u0442\u044C\u0441\u044F \u043E\u0442 \u043F\u0438\u043B\u043E\u043D\u0430."
type: Add
- message: "\u0414\u0432\u0435\u0440\u044C \u043A\u0443\u043B\u044C\u0442\u0430\
\ \u0442\u0435\u043F\u0435\u0440\u044C \u043C\u0433\u043D\u043E\u0432\u0435\u043D\
\u043D\u043E \u0440\u0430\u0437\u0431\u0438\u0440\u0430\u0435\u0442\u0441\u044F\
\ \u0440\u0438\u0442\u0443\u0430\u043B\u044C\u043D\u044B\u043C \u043A\u0438\u043D\
\u0436\u0430\u043B\u043E\u043C."
type: Add
- message: "\u041A\u0443\u043B\u044C\u0442\u0438\u0441\u0442 \u0441 \u0432\u044B\
\u0440\u0435\u0437\u0430\u043D\u043D\u044B\u043C \u043C\u043E\u0437\u0433\u043E\
\u043C \u043C\u043E\u0436\u0435\u0442 \u0432\u0435\u0440\u043D\u0443\u0442\u044C\
\u0441\u044F \u0432 \u0442\u0435\u043B\u043E \u043F\u0440\u0438 \u0432\u043E\
\u0441\u043A\u0440\u0435\u0448\u0435\u043D\u0438\u0438 \u043D\u0430 \u0440\u0443\
\u043D\u0435."
type: Add
- message: "\u0417\u0430\u0440\u044F\u0434\u044B \u0440\u0443\u043D\u044B \u0432\
\u043E\u0441\u043A\u0440\u0435\u0448\u0435\u043D\u0438\u044F \u0431\u043E\u043B\
\u044C\u0448\u0435 \u043D\u0435 \u0441\u043E\u0445\u0440\u0430\u043D\u044F\u044E\
\u0442\u0441\u044F \u043C\u0435\u0436\u0434\u0443 \u0440\u0430\u0443\u043D\u0434\
\u0430\u043C\u0438."
type: Fix
- message: "\u0411\u043E\u043B\u044C\u0448\u0435 \u043D\u0435\u043B\u044C\u0437\u044F\
\ \u0447\u0435\u0440\u0442\u0438\u0442\u044C \u0440\u0443\u043D\u044B \u0431\
\u0435\u0437 \u043A\u0438\u043D\u0436\u0430\u043B\u0430 \u0432 \u0440\u0443\u043A\
\u0430\u0445."
type: Fix
- message: "\u0426\u0435\u043B\u044C \u043A\u0443\u043B\u044C\u0442\u0430 \u0431\
\u0443\u0434\u0435\u0442 \u0441\u0447\u0438\u0442\u0430\u0442\u044C\u0441\u044F\
\ \u043C\u0435\u0440\u0442\u0432\u043E\u0439, \u0435\u0441\u043B\u0438 \u043F\
\u0440\u0435\u0432\u0440\u0430\u0442\u0438\u0442\u044C \u0435\u0451 \u0432 \u043A\
\u043E\u043D\u0441\u0442\u0440\u0443\u043A\u0442."
type: Fix
- message: "\u0423\u043C\u0435\u043D\u044C\u0448\u0435\u043D\u043E \u0445\u043F\
\ \u0441\u0442\u0435\u043D\u0435 \u043A\u0443\u043B\u044C\u0442\u0430 \u0434\
\u043E 300, \u0434\u0432\u0435\u0440\u0438 \u043A\u0443\u043B\u044C\u0442\u0430\
\ \u0434\u043E 200."
type: Tweak
- message: "\u041A\u043E\u043D\u0441\u0442\u0440\u0443\u043A\u0442\u044B \u0442\u0435\
\u043F\u0435\u0440\u044C \u0434\u0432\u0438\u0433\u0430\u044E\u0442\u0441\u044F\
\ \u0438 \u0430\u0442\u0430\u043A\u0443\u044E\u0442 \u0431\u044B\u0441\u0442\
\u0440\u0435\u0435."
type: Tweak
- message: "\u0424\u0430\u043D\u0442\u043E\u043C \u0442\u0435\u043F\u0435\u0440\u044C\
\ \u043D\u0430\u043D\u043E\u0441\u0438\u0442 40 \u0441\u0442\u0440\u0443\u043A\
\u0442\u0443\u0440\u043D\u043E\u0433\u043E \u0443\u0440\u043E\u043D\u0430."
type: Tweak
- message: "\u041C\u0430\u043A\u0441\u0438\u043C\u0443\u043C \u0437\u0430\u043A\u043B\
\u0438\u043D\u0430\u043D\u0438\u0439 \u043A\u0440\u043E\u0432\u0438 \u0431\u0435\
\u0437 \u0440\u0443\u043D\u044B \u043C\u043E\u0433\u0443\u0449\u0435\u0441\u0442\
\u0432\u0430: 1 -> 2, \u0441 \u0440\u0443\u043D\u043E\u0439: 4 -> 5."
type: Tweak
- message: "\u0417\u0430\u043A\u043B\u0438\u043D\u0430\u043D\u0438\u0435 \u0442\u0435\
\u043B\u0435\u043F\u043E\u0440\u0442\u0430 \u0442\u0435\u043F\u0435\u0440\u044C\
\ \u0440\u0430\u0431\u043E\u0442\u0430\u0435\u0442 \u043D\u0430 \u0441\u0432\
\u044F\u0437\u0430\u043D\u043D\u044B\u0445 \u0446\u0435\u043B\u0435\u0439."
type: Tweak
- message: "\u0416\u0443\u0442\u043A\u0438\u0439 \u043A\u043B\u0438\u043D\u043E\u043A\
\ \u0442\u0435\u043F\u0435\u0440\u044C \u043C\u043E\u0436\u043D\u043E \u043F\
\u043E\u0432\u0435\u0441\u0438\u0442\u044C \u043D\u0430 \u0431\u0440\u043E\u043D\
\u044E."
type: Tweak
- message: "\u0414\u0432\u0435\u0440\u044C \u043A\u0443\u043B\u044C\u0442\u0430\
\ \u0442\u0435\u043F\u0435\u0440\u044C \u043D\u0435 \u043E\u0442\u043A\u0440\
\u044B\u0432\u0430\u0435\u0442\u0441\u044F \u043F\u0440\u0438 \u043A\u043E\u043D\
\u0442\u0430\u043A\u0442\u0435 \u0441 \u0436\u0438\u0432\u044B\u043C \u043D\u0435\
\u0441\u0432\u044F\u0437\u0430\u043D\u043D\u044B\u043C \u043A\u0443\u043B\u044C\
\u0442\u0438\u0441\u0442\u043E\u043C \u0438\u043B\u0438 \u043A\u043E\u043D\u0441\
\u0442\u0440\u0443\u043A\u0442\u043E\u043C."
type: Tweak
id: 213
time: '2024-03-22T08:23:33.0000000+00:00'
url: https://api.github.com/repos/frosty-dev/ss14-core/pulls/220
- author: ThereDrD
changes:
- message: "\u041F\u043E\u0444\u0438\u043A\u0448\u0435\u043D \u0434\u044E\u043F\
\ \u0434\u0435\u043D\u0435\u0433 \u0432 \u043A\u0430\u0440\u0433\u043E"
type: Fix
id: 214
time: '2024-03-24T02:10:19.0000000+00:00'
url: https://api.github.com/repos/frosty-dev/ss14-core/pulls/234
- author: ThereDrD
changes:
- message: "\u0414\u043E\u0431\u0430\u0432\u043B\u0435\u043D\u044B \u0437\u0430\u043F\
\u043E\u043B\u043D\u0435\u043D\u043D\u044B\u0435 \u0432\u0435\u0440\u0441\u0438\
\u0438 \u0448\u043A\u0430\u0444\u043E\u0432 \u043A\u043B\u043E\u0443\u043D\u0430\
\ \u0438 \u043C\u0438\u043C\u0430"
type: Add
- message: "\u0414\u043E\u0431\u0430\u0432\u043B\u0435\u043D\u044B \u0440\u0430\u0437\
\u043B\u0438\u0447\u043D\u044B\u0435 \u0436\u0438\u0432\u043E\u0442\u043D\u044B\
\u0435 \u0432 \u0442\u0435\u0445\u043D\u0438\u0447\u0435\u0441\u043A\u0438\u0435\
\ \u0448\u043A\u0430\u0444\u044B. \u041A\u0430\u043A \u0430\u0433\u0440\u0435\
\u0441\u0441\u0438\u0432\u043D\u044B\u0435, \u0441 \u043D\u0438\u0437\u043A\u0438\
\u043C \u0448\u0430\u043D\u0441\u043E\u043C, \u0442\u0430\u043A \u0438 \u043C\
\u0438\u0440\u043D\u044B\u0435."
type: Add
- message: "\u041F\u0435\u0440\u0435\u0440\u0430\u0431\u043E\u0442\u0430\u043D\u043E\
\ \u0441\u043E\u0434\u0435\u0440\u0436\u0438\u043C\u043E\u0435 \u0448\u043A\u0430\
\u0444\u043E\u0432 \u0433\u043B\u0430\u0432. \u0412\u0441\u044F \u043E\u0434\
\u0435\u0436\u0434\u0430 \u0442\u0435\u043F\u0435\u0440\u044C \u0445\u0440\u0430\
\u043D\u0438\u0442\u0441\u044F \u0432 \u043A\u043E\u043C\u043E\u0434\u0430\u0445\
."
type: Tweak
- message: "\u041D\u0435\u043A\u043E\u0442\u043E\u0440\u044B\u0435 \u0448\u043A\u0430\
\u0444\u044B \u0440\u043E\u043B\u0435\u0439 \u043F\u043E\u043F\u043E\u043B\u043D\
\u0435\u043D\u044B \u043D\u043E\u0432\u044B\u043C\u0438 \u043F\u0440\u0435\u0434\
\u043C\u0435\u0442\u0430\u043C\u0438"
type: Tweak
- message: "\u041F\u043E\u0444\u0438\u043A\u0448\u0435\u043D\u044B \u043F\u0443\u0441\
\u0442\u044B\u0435 \u043F\u043E\u044F\u0441\u0430, \u0430\u043F\u0442\u0435\u0447\
\u043A\u0438 \u0438 \u043F\u0440\u043E\u0447\u0438\u0435 \u043F\u0440\u0435\u0434\
\u043C\u0435\u0442\u044B \u0432 \u0448\u043A\u0430\u0444\u0430\u0445."
type: Fix
id: 215
time: '2024-03-24T03:21:27.0000000+00:00'
url: https://api.github.com/repos/frosty-dev/ss14-core/pulls/193
- author: CaypenNow
changes:
- message: "\u041F\u0435\u0440\u0435\u0434\u0435\u043B\u0430\u043D\u044B \u043D\u0435\
\u0433\u0430\u0442\u0438\u0432\u043D\u044B\u0435 \u044D\u0444\u0444\u0435\u043A\
\u0442\u044B \u0441\u0442\u0438\u043C\u0443\u043B\u044F\u0442\u043E\u0440\u043E\
\u0432."
type: Add
- message: "\u0410\u0442\u043C\u043E\u0441 \u0433\u043E\u043B\u043E\u043F\u0440\u043E\
\u0435\u043A\u0442\u043E\u0440 \u0441\u043D\u043E\u0432\u0430 \u043C\u043E\u0436\
\u043D\u043E \u043F\u043E\u043B\u043E\u0436\u0438\u0442\u044C \u0432 \u0440\u044E\
\u043A\u0437\u0430\u043A!"
type: Fix
id: 216
time: '2024-03-24T08:59:57.0000000+00:00'
url: https://api.github.com/repos/frosty-dev/ss14-core/pulls/227

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

@@ -0,0 +1,2 @@
ent-BoxMindshield = коробка имплантов
.desc = Хранит набор имплантов защиты разума.

View File

@@ -0,0 +1,3 @@
ent-MobMonkeyAngry = { ent-MobMonkey }
.desc = { ent-MobMonkey.desc }
.suffix = Агрессивная

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

@@ -16,3 +16,9 @@ ent-LockerBotanistFilled = { ent-LockerBotanist }
ent-LockerBotanistLoot = { ent-LockerBotanist }
.suffix = Лут
.desc = { ent-LockerBotanist.desc }
ent-LockerClownFilled = { ent-LockerClown }
.suffix = Заполненный
.desc = { ent-LockerClown.desc }
ent-LockerMimeFilled = { ent-LockerMime }
.suffix = Заполненный
.desc = { ent-LockerMime.desc }

View File

@@ -73,3 +73,9 @@ ent-LockerSyndicatePersonal = оружейный шкаф
ent-LockerInspector = шкаф инспектора
.desc = Содержит уникальный гардероб по последнему слову моды.
.suffix = { "" }
ent-LockerMime = шкаф клоуна
.desc = Содержит рабочую экипировку станционного клоуна. Для хранения смешного
.suffix = { "" }
ent-LockerClown = шкаф мима
.desc = Содержит рабочую экипировку станционного мима.
.suffix = { "" }

View File

@@ -34,8 +34,8 @@ ent-WardrobeSecurity = гардероб службы безопасности
ent-WardrobeAtmospherics = гардероб атмосферного снаряжения
.desc = { ent-WardrobeBase.desc }
.suffix = { "" }
ent-ClosetJanitor = шкаф уборщика
.desc = Здесь хранится санитарно-технические одежда и оборудование .
ent-ClosetJanitor = шкаф оборудования уборщика
.desc = Здесь хранится санитарно-техническое оборудование.
.suffix = { "" }
ent-WardrobeFormal = официально-деловой гардероб
.desc = Хранилище одежды строгого стиля.

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

@@ -138,6 +138,6 @@
sprite: Structures/Storage/canister.rsi
state: darkblue
product: PluoxiumCanister
cost: 3500
cost: 12000
category: Atmospherics
group: market

View File

@@ -33,7 +33,7 @@
sprite: Objects/Specific/Service/vending_machine_restock.rsi
state: base
product: CrateVendingMachineRestockClothesFilled
cost: 10500
cost: 14000
category: Service
group: market

View File

@@ -14,6 +14,11 @@
- id: ClothingHandsGlovesCaptain
- id: ClothingNeckMantleCap
- id: ClothingEyesGlassesSunglasses
- id: ClothingOuterArmorCaptainCarapace
- id: ClothingHeadsetAltCommand
- id: ClothingMaskGasCaptain
- id: ClothingUniformJumpskirtCaptain
- id: ClothingOuterWinterCap
- type: entity
id: DresserChiefEngineerFilled
@@ -27,6 +32,11 @@
- id: ClothingUniformJumpskirtChiefEngineerTurtle
- id: ClothingNeckCloakCe
- id: ClothingHeadHatBeretEngineering
- id: ClothingEyesGlassesMeson
- id: ClothingHeadsetAltEngineering
- id: ClothingHeadHatHardhatWhite
- id: ClothingHandsGlovesColorYellow
- id: ClothingShoesBootsMagAdv
- type: entity
id: DresserChiefMedicalOfficerFilled
@@ -35,10 +45,18 @@
components:
- type: StorageFill
contents:
- id: ClothingUniformJumpsuitCMO
- id: ClothingUniformJumpskirtCMO
- id: ClothingOuterWinterCMO
- id: ClothingOuterCoatLabCmo
- id: ClothingNeckMantleCMO
- id: ClothingCloakCmo
- id: ClothingOuterCoatLabCmo
- id: ClothingHeadHatBeretCmo
- id: ClothingHandsGlovesNitrile
- id: ClothingEyesHudMedical
- id: ClothingHeadsetAltMedical
- id: ClothingMaskSterile
- id: ClothingEyesEyepatchHudMedical
- type: entity
id: DresserHeadOfPersonnelFilled
@@ -52,6 +70,10 @@
- id: ClothingHeadHatHopcap
- id: ClothingOuterWinterHoP
- id: ClothingHandsGlovesHop
- id: ClothingNeckGoldmedal
- id: ClothingShoesLeather
- id: ClothingUniformJumpsuitHoP
- id: ClothingUniformJumpskirtHoP
- type: entity
id: DresserHeadOfSecurityFilled
@@ -70,6 +92,10 @@
- id: ClothingUniformJumpsuitHosFormal
- id: ClothingMaskNeckGaiter
- id: ClothingHandsGlovesCombat
- id: ClothingShoesBootsJack
- id: ClothingEyesGlassesSecurity
- id: ClothingHeadsetAltSecurity
- id: ClothingMaskGasSwat
- type: entity
id: DresserQuarterMasterFilled
@@ -86,6 +112,11 @@
- id: ClothingUniformJumpskirtQMTurtleneck
- id: ClothingHandsGlovesColorBrown
- id: ClothingNeckMantleQM
- id: ClothingHeadsetAltCargo
- id: ClothingOuterWinterQM
- id: ClothingUniformJumpsuitQM
- id: ClothingUniformJumpskirtQM
- id: ClothingHeadHatQMsoft
- type: entity
id: DresserResearchDirectorFilled
@@ -99,6 +130,10 @@
- id: ClothingHeadHatBeretRND
- id: ClothingHandsGlovesColorPurple
- id: ClothingHeadsetAltScience
- id: ClothingUniformJumpskirtResearchDirector
- id: ClothingUniformJumpsuitResearchDirector
- id: ClothingOuterWinterRD
- id: ClothingOuterCoatRD
- type: entity
id: DresserWardenFilled
@@ -109,3 +144,11 @@
contents:
- id: ClothingHeadHatWarden
- id: ClothingHeadHatBeretWarden
- id: ClothingEyesGlassesSecurity
- id: ClothingHeadsetAltSecurity
- id: ClothingHandsGlovesCombat
- id: ClothingShoesBootsJack
- id: ClothingOuterCoatWarden
- id: ClothingOuterWinterWarden
- id: ClothingUniformJumpsuitWarden
- id: ClothingUniformJumpskirtWarden

View File

@@ -92,6 +92,8 @@
- id: DoorRemoteFirefight
- id: ClothingBeltUtilityEngineering
- id: ClothingEyesGlassesMeson
- id: ClothingShoesBootsMag
- id: ClothingHandsGlovesColorYellow
- type: entity
id: LockerAtmosphericsFilled
@@ -109,6 +111,8 @@
- id: DoorRemoteFirefight
- id: ClothingBeltUtilityEngineering
- id: ClothingEyesGlassesMeson
- id: ClothingShoesBootsMag
- id: ClothingHandsGlovesColorYellow
- type: entity
id: LockerEngineerFilledHardsuit

View File

@@ -11,12 +11,10 @@
- id: CargoShuttleConsoleCircuitboard
- id: SalvageShuttleConsoleCircuitboard
- id: CargoBountyComputerCircuitboard
- id: CigPackGreen
prob: 0.50
- id: DoorRemoteCargo
- id: RubberStampQm
- id: ClothingHeadsetAltCargo
- id: BoxEncryptionKeyCargo
- id: BoxFolderClipboard
- type: entity
id: LockerCaptainFilledHardsuit
@@ -25,19 +23,13 @@
components:
- type: StorageFill
contents:
- id: ClothingOuterArmorCaptainCarapace
- id: WeaponEgun
- id: NukeDisk
- id: PinpointerNuclear
- id: CaptainIDCard
- id: ClothingOuterHardsuitCap
- id: ClothingMaskGasCaptain
- id: WeaponEgun
- id: CommsComputerCircuitboard
- id: ClothingHeadsetAltCommand
- id: PlushieNuke
prob: 0.1
- id: CigarGoldCase
prob: 0.25
- id: ClothingBeltSheathFilled
- id: DoorRemoteCommand
- id: RubberStampCaptain
@@ -46,6 +38,9 @@
- id: MedalCase
- id: CommandBudgetCard
- id: CommandBudgetPinPaper
- id: MedalCase
- id: ClothingOuterHardsuitCap
- id: OxygenTankFilled
- type: entity
id: LockerCaptainFilled
@@ -54,17 +49,13 @@
components:
- type: StorageFill
contents:
- id: ClothingOuterArmorCaptainCarapace
- id: WeaponEgun
- id: NukeDisk
- id: PinpointerNuclear
- id: CaptainIDCard
- id: WeaponDisabler
- id: CommsComputerCircuitboard
- id: ClothingHeadsetAltCommand
- id: PlushieNuke
prob: 0.1
- id: CigarGoldCase
prob: 0.25
- id: ClothingBeltSheathFilled
- id: DoorRemoteCommand
- id: RubberStampCaptain
@@ -73,6 +64,7 @@
- id: MedalCase
- id: CommandBudgetCard
- id: CommandBudgetPinPaper
- id: MedalCase
- type: entity
id: LockerCaptainFilledNoLaser
@@ -81,23 +73,22 @@
components:
- type: StorageFill
contents:
- id: ClothingOuterArmorCaptainCarapace
- id: WeaponEgun
- id: NukeDisk
- id: PinpointerNuclear
- id: CaptainIDCard
- id: WeaponDisabler
- id: CommsComputerCircuitboard
- id: ClothingHeadsetAltCommand
- id: SpaceCash1000
- id: PlushieNuke
prob: 0.1
- id: CigarGoldCase
prob: 0.25
- id: ClothingBeltSheathFilled
- id: DoorRemoteCommand
- id: RubberStampCaptain
- id: JetpackCaptainFilled
- id: MedalCase
- id: CommandBudgetCard
- id: CommandBudgetPinPaper
- id: MedalCase
- type: entity
id: LockerHeadOfPersonnelFilled
@@ -113,18 +104,35 @@
- id: BoxHeadset
- id: IDComputerCircuitboard
- id: WeaponEgun
- id: CigarGoldCase
prob: 0.25
# Fuck the HoP they don't deserve fucking cigars.
# Yes they do fuck you.
- id: DoorRemoteService
- id: ClothingNeckGoldmedal
- id: RubberStampHop
- id: BoxEncryptionKeyPassenger
- id: BoxEncryptionKeyService
- id: ClothingBackpackIan
prob: 0.5
- id: AccessConfigurator
- id: BoxFolderClipboard
- type: entity
id: LockerHeadOfPersonnelFilledHardsuit
suffix: Filled, Hardsuit
parent: LockerHeadOfPersonnel
components:
- type: StorageFill
contents:
- id: HoPIDCard
- id: ClothingHeadsetCommand
- id: BoxPDA
- id: BoxID
- id: BoxHeadset
- id: IDComputerCircuitboard
- id: WeaponEgun
- id: DoorRemoteService
- id: ClothingNeckGoldmedal
- id: RubberStampHop
- id: BoxEncryptionKeyPassenger
- id: BoxEncryptionKeyService
- id: BoxFolderClipboard
- id: ClothingOuterHardsuitHOP
- id: OxygenTankFilled
- type: entity
id: LockerChiefEngineerFilledHardsuit
@@ -133,24 +141,18 @@
components:
- type: StorageFill
contents:
- id: ClothingOuterHardsuitEngineeringWhite
- id: ClothingMaskBreath
- id: OxygenTankFilled
- id: NitrogenTankFilled
- id: ClothingEyesGlassesMeson
- id: ClothingBeltChiefEngineerFilled
- id: ClothingShoesBootsMagAdv
- id: ClothingHandsGlovesColorYellow
- id: CigarCase
prob: 0.15
- id: DoorRemoteEngineering
- id: RubberStampCE
- id: JetpackVoidFilled
- id: ClothingHeadsetAltEngineering
- id: BoxEncryptionKeyEngineering
- id: AccessConfigurator
- id: RCD
- id: RCDAmmo
amount: 2
- id: HolofanProjector
- id: ClothingOuterHardsuitEngineeringWhite
- id: ClothingMaskBreath
- id: OxygenTankFilled
- type: entity
id: LockerChiefEngineerFilled
@@ -159,19 +161,15 @@
components:
- type: StorageFill
contents:
- id: ClothingEyesGlassesMeson
- id: ClothingBeltChiefEngineerFilled
- id: ClothingHandsGlovesColorYellow
- id: CigarCase
prob: 0.15
- id: DoorRemoteEngineering
- id: RubberStampCE
- id: ClothingHeadsetAltEngineering
- id: BoxEncryptionKeyEngineering
- id: AccessConfigurator
- id: RCD
- id: RCDAmmo
- id: ClothingHeadHatHardhatWhite
amount: 2
- id: HolofanProjector
- type: entity
id: LockerChiefMedicalOfficerFilledHardsuit
@@ -181,15 +179,6 @@
- type: StorageFill
contents:
- id: MedkitFilled
- id: ClothingHandsGlovesNitrile
- id: ClothingEyesHudMedical
- id: ClothingHeadsetAltMedical
- id: ClothingCloakCmo
- id: ClothingBackpackDuffelSurgeryFilled
- id: ClothingOuterCoatLabCmo
- id: ClothingMaskSterile
- id: ClothingHeadHatBeretCmo
- id: ClothingOuterHardsuitMedical
- id: Hypospray
- id: HandheldCrewMonitor
- id: DoorRemoteMedical
@@ -197,7 +186,10 @@
- id: MedicalTechFabCircuitboard
- id: BoxEncryptionKeyMedical
- id: Eftpos
- id: ClothingBeltMedical
- id: ClothingBeltMedicalFilled
- id: MedkitFilled
- id: ClothingOuterHardsuitMedical
- id: OxygenTankFilled
- type: entity
id: LockerChiefMedicalOfficerFilled
@@ -207,11 +199,6 @@
- type: StorageFill
contents:
- id: MedkitFilled
- id: ClothingHandsGlovesNitrile
- id: ClothingEyesHudMedical
- id: ClothingHeadsetAltMedical
- id: ClothingBackpackDuffelSurgeryFilled
- id: ClothingMaskSterile
- id: Hypospray
- id: HandheldCrewMonitor
- id: DoorRemoteMedical
@@ -219,7 +206,7 @@
- id: MedicalTechFabCircuitboard
- id: BoxEncryptionKeyMedical
- id: Eftpos
- id: ClothingBeltMedical
- id: ClothingBeltMedicalFilled
- type: entity
id: LockerResearchDirectorFilledHardsuit
@@ -261,28 +248,24 @@
- type: entity
id: LockerHeadOfSecurityFilledHardsuit
suffix: Filled, Hardsuit
parent: LockerHeadOfSecurity
parent: LockerHeadOfSecurityFilled
components:
- type: StorageFill
contents:
- id: WeaponDisabler
- id: ClothingOuterCoatHoSTrench
- id: ClothingOuterHardsuitSecurityRed
- id: ClothingMaskGasSwat
- id: ClothingBeltSecurityFilled
- id: ClothingHeadsetAltSecurity
- id: ClothingEyesGlassesSecurity
- id: ClothingShoesBootsJack
- id: CigarGoldCase
prob: 0.50
- id: WeaponEgun
- id: ClothingBeltAssaultFilled
- id: DoorRemoteSecurity
- id: RubberStampHos
- id: SecurityTechFabCircuitboard
- id: JetpackSecurityFilled
- id: BoxEncryptionKeySecurity
- id: HoloprojectorSecurity
- id: BookSecretDocuments
- id: WeaponPistolMk58Nonlethal
- id: MagazinePistol
- id: BoxMindshield
- id: ClothingOuterHardsuitSecurityRed
- id: JetpackSecurityFilled
- id: OxygenTankFilled
- type: entity
id: LockerHeadOfSecurityFilled
@@ -292,16 +275,7 @@
- type: StorageFill
contents:
- id: WeaponEgun
- id: ClothingHeadHatBeretHoS
- id: ClothingHeadHatHoshat
- id: ClothingNeckCloakHos
- id: ClothingOuterCoatHoSTrench
- id: ClothingBeltSecurityFilled
- id: ClothingHeadsetAltSecurity
- id: ClothingEyesGlassesSecurity
- id: ClothingShoesBootsJack
- id: CigarGoldCase
prob: 0.50
- id: ClothingBeltAssaultFilled
- id: DoorRemoteSecurity
- id: RubberStampHos
- id: SecurityTechFabCircuitboard
@@ -310,6 +284,7 @@
- id: BookSecretDocuments
- id: WeaponPistolMk58Nonlethal
- id: MagazinePistol
- id: BoxMindshield
- type: entity
id: LockerFreezerVaultFilled

View File

@@ -15,6 +15,9 @@
- id: Bloodpack
amount: 2
- id: Gauze
- id: MedkitFilled
- id: BoxPillCanister
- id: BoxBottle
- type: entity
id: LockerWallMedicalFilled
@@ -34,6 +37,7 @@
- id: Bloodpack
amount: 2
- id: Gauze
- id: MedkitFilled
- type: entity
@@ -44,7 +48,6 @@
- type: StorageFill
contents:
- id: HandheldHealthAnalyzer
prob: 0.6
- id: ClothingHandsGlovesLatex
- id: ClothingHeadsetMedical
- id: ClothingEyesHudMedical
@@ -58,6 +61,8 @@
- id: ClothingHeadHatSurgcapBlue
prob: 0.90
orGroup: Surgcaps
- id: ClothingUniformJumpsuitMedicalDoctor
- id: ClothingUniformJumpskirtMedicalDoctor
- id: UniformScrubsColorBlue
prob: 0.5
orGroup: Surgshrubs
@@ -78,7 +83,6 @@
- type: StorageFill
contents:
- id: HandheldHealthAnalyzer
prob: 0.6
- id: ClothingHandsGlovesLatex
- id: ClothingHeadsetMedical
- id: ClothingEyesHudMedical
@@ -92,6 +96,8 @@
- id: ClothingHeadHatSurgcapBlue
prob: 0.90
orGroup: Surgcaps
- id: ClothingUniformJumpsuitMedicalDoctor
- id: ClothingUniformJumpskirtMedicalDoctor
- id: UniformScrubsColorBlue
prob: 0.5
orGroup: Surgshrubs
@@ -149,5 +155,6 @@
- id: ClothingMaskSterile
- id: HandheldGPSBasic
- id: MedkitFilled
prob: 0.3
- id: Eftpos
- id: ClothingBeltMedicalEMTFilled
- id: HandheldCrewMonitor

View File

@@ -191,8 +191,37 @@
prob: 0.005
orGroup: syndiemaintloot
- id: WeaponSniperMosin
prob: 0.0010
prob: 0.001
orGroup: syndiemaintloot
# Angry animals
- id: null
prob: 0.95
orGroup: angryanimalsmaintloot
- id: MobCarp
prob: 0.05
orGroup: angryanimalsmaintloot
- id: MobGiantSpiderAngry
prob: 0.05
orGroup: angryanimalsmaintloot
- id: MobAngryBee
prob: 0.05
orGroup: angryanimalsmaintloot
- id: MobMonkeyAngry
prob: 0.05
orGroup: angryanimalsmaintloot
# Animals
- id: null
prob: 0.7
orGroup: animalsmaintloot
- id: MobMouse
prob: 0.1
orGroup: animalsmaintloot
- id: MobMouse1
prob: 0.1
orGroup: animalsmaintloot
- id: MobMouse2
prob: 0.1
orGroup: animalsmaintloot
- type: entity
id: ClosetWallMaintenanceFilledRandom
@@ -284,3 +313,32 @@
- id: WeaponSniperMosin
prob: 0.0010
orGroup: syndiemaintloot
# Angry animals
- id: null
prob: 0.95
orGroup: angryanimalsmaintloot
- id: MobCarp
prob: 0.005
orGroup: angryanimalsmaintloot
- id: MobGiantSpiderAngry
prob: 0.005
orGroup: angryanimalsmaintloot
- id: MobAngryBee
prob: 0.005
orGroup: angryanimalsmaintloot
- id: MobMonkeyAngry
prob: 0.005
orGroup: angryanimalsmaintloot
# Animals
- id: null
prob: 0.7
orGroup: animalsmaintloot
- id: MobMouse
prob: 0.1
orGroup: animalsmaintloot
- id: MobMouse1
prob: 0.1
orGroup: animalsmaintloot
- id: MobMouse2
prob: 0.1
orGroup: animalsmaintloot

View File

@@ -7,23 +7,17 @@
contents:
- id: FlashlightSeclite
- id: WeaponDisabler
prob: 0.3
- id: ClothingBeltSecurityFilled
- id: Flash
- id: ClothingEyesGlassesSecurity
- id: ClothingHeadsetAltSecurity
- id: ClothingHandsGlovesCombat
- id: ClothingShoesBootsJack
- id: ClothingOuterCoatWarden
- id: ClothingHeadHatWarden
- id: ClothingOuterWinterWarden
- id: RubberStampWarden
- id: DoorRemoteArmory
- id: ClothingOuterHardsuitWarden
- id: HoloprojectorSecurity
- id: BoxBodyCamera
- id: WeaponPistolMk58Nonlethal
- id: MagazinePistol
- id: BoxMindshield
- id: ClothingOuterHardsuitWarden
- id: OxygenTankFilled
- type: entity
id: LockerWardenFilled
@@ -34,22 +28,15 @@
contents:
- id: FlashlightSeclite
- id: WeaponDisabler
- id: ClothingHeadHatWarden
- id: ClothingHeadHatBeretWarden
- id: ClothingBeltSecurityFilled
- id: Flash
- id: ClothingEyesGlassesSecurity
- id: ClothingHeadsetAltSecurity
- id: ClothingHandsGlovesCombat
- id: ClothingShoesBootsJack
- id: ClothingOuterCoatWarden
- id: ClothingOuterWinterWarden
- id: RubberStampWarden
- id: DoorRemoteArmory
- id: HoloprojectorSecurity
- id: BoxBodyCamera
- id: WeaponPistolMk58Nonlethal
- id: MagazinePistol
- id: BoxMindshield
- type: entity
id: LockerSecurityFilled
@@ -77,7 +64,7 @@
prob: 0.6
- id: WeaponTempGun
- id: WeaponPistolMk58Nonlethal
- id: BoxBodyCamera
- id: SurveillanceBodyCamera
- id: MagazinePistol
- type: entity

View File

@@ -209,6 +209,21 @@
sprite: Clothing/Belt/belt_overlay.rsi
- type: Appearance
- type: entity
parent: ClothingBeltAssault # WD edit
id: ClothingBeltAssaultFilled
suffix: Filled
name: assault belt
description: A tactical assault belt.
components:
- type: StorageFill
contents:
- id: GrenadeFlashBang
- id: TearGasGrenade
- id: Stunbaton
- id: Handcuffs
- id: Handcuffs
- type: entity
parent: ClothingBeltStorageBase
id: ClothingBeltJanitor

View File

@@ -250,10 +250,10 @@
- WhitelistChameleon
- type: entity
parent: ClothingOuterBaseLarge
parent: ClothingOuterBaseMedium
id: ClothingOuterArmorCaptainCarapace
name: "captain's carapace"
description: "An armored chestpiece that provides protection whilst still offering maximum mobility and flexibility. Issued only to the station's finest."
name: captain's carapace
description: An armored chestpiece that provides protection whilst still offering maximum mobility and flexibility. Issued only to the station's finest.
components:
- type: Sprite
sprite: Clothing/OuterClothing/Armor/captain_carapace.rsi
@@ -262,14 +262,11 @@
- type: Armor
modifiers:
coefficients:
Blunt: 0.5
Slash: 0.5
Blunt: 0.6
Slash: 0.6
Piercing: 0.6
Heat: 0.5
Heat: 0.6
Caustic: 0.9
- type: ClothingSpeedModifier
walkModifier: 1.0
sprintModifier: 1.0
- type: HeldSpeedModifier
- type: ExplosionResistance
damageCoefficient: 0.65

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

@@ -2188,6 +2188,21 @@
description: ghost-role-information-giant-spider-description
- type: GhostTakeoverAvailable
- type: entity
name: angry monkey
parent: MobMonkey
id: MobMonkeyAngry
suffix: Angry
components:
- type: NpcFactionMember
factions:
- SimpleHostile
- type: InputMover
- type: MobMover
- type: HTN
rootTask:
task: SimpleHostileCompound
- type: entity
name: clown spider
parent: MobGiantSpiderAngry

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

@@ -40,6 +40,7 @@
map: [ "implantFull" ]
visible: false
- type: Item
storedRotation: -90
sprite: Objects/Specific/Medical/implanter.rsi
heldPrefix: 0
size: Small

View File

@@ -5,7 +5,7 @@
description: MP3 player sized pocket camera for security officers.
components:
- type: SurveillanceBodyCamera
- type: Sprite # Sprites section
- type: Sprite
sprite: Objects/Specific/Security/body-camera.rsi
layers:
- state: unpowered
@@ -66,6 +66,7 @@
slots:
cell_slot:
name: power-cell-slot-component-slot-name-default
startingItem: PowerCellMedium
- type: UserInterface
interfaces:
- key: enum.SurveillanceCameraSetupUiKey.Camera

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

@@ -15,7 +15,6 @@
- trigger:
!type:DamageTrigger
damage: 50
behaviors:
- !type:SpawnEntitiesBehavior
spawn:
@@ -26,8 +25,8 @@
acts: [ "Destruction" ]
- type: Storage
grid:
- 0,0,6,3
maxItemSize: Normal
- 0,0,8,5
maxItemSize: Huge
- type: ContainerContainer
containers:
storagebase: !type:Container

View File

@@ -422,6 +422,47 @@
stateDoorOpen: clown_open
stateDoorClosed: clown_door
- type: entity
id: LockerClownFilled
name: clown locker
parent: LockerClown
suffix: Filled
components:
- type: StorageFill
contents:
- id: ClothingUniformJumpsuitClown
- id: ClothingShoesClown
- id: ClothingMaskClown
- id: ClownRecorder
- id: RubberStampClown
- id: ClothingHeadsetService
- id: CrayonRainbow
- id: ClothingBackpackClown
prob: 0.33
orGroup: clownbackpack
- id: ClothingBackpackSatchelClown
prob: 0.33
orGroup: clownbackpack
- id: ClothingBackpackDuffelClown
prob: 0.33
orGroup: clownbackpack
- id: ClothingShoesClownLarge
prob: 0.66
- id: ClothingNeckClownmedal
prob: 0.33
- id: ClothingOuterHardsuitClown
prob: 0.2
- id: ClothingShoesClownBanana
prob: 0.05
- id: ClothingUniformJumpsuitClownBanana
prob: 0.05
- id: ClothingMaskClownBanana
prob: 0.05
- id: BananiumHorn
prob: 0.1
- id: GoldenBikeHorn
prob: 0.1
# Mime
- type: entity
id: LockerMime
@@ -436,6 +477,45 @@
stateDoorOpen: mime_open
stateDoorClosed: mime_door
- type: entity
id: LockerMimeFilled
name: mime locker
parent: LockerMime
suffix: Filled
components:
- type: StorageFill
contents:
- id: ClothingUniformJumpsuitMime
- id: ClothingUniformJumpskirtMime
- id: ClothingMaskMime
prob: 0.25
onGroup: mimemask
- id: ClothingMaskSadMime
prob: 0.25
onGroup: mimemask
- id: ClothingMaskScaredMime
prob: 0.25
onGroup: mimemask
- id: ClothingMaskSexyMime
prob: 0.25
onGroup: mimemask
- id: ClothingShoesColorWhite
- id: ClothingHeadHatBeret
- id: ClothingHandsGlovesLatex
- id: ClothingHeadsetService
- id: RubberStampMime
- id: CrayonMime
- id: ClothingBackpackSatchelMime
prob: 0.5
onGroup: mimebackpack
- id: ClothingBackpackMime
prob: 0.5
onGroup: mimebackpack
- id: ClothingOuterWinterMime
prob: 0.5
- id: ClothingOuterHardsuitMime
prob: 0.2
# Representative
- type: entity
id: LockerRepresentative

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

@@ -32,8 +32,8 @@
key: Stutter
component: StutteringAccent
- !type:GenericStatusEffect
key: BlurryVision
component: BlurryVision
key: NarcoticEffect
component: NarcoticEffect
- !type:Jitter
- !type:GenericStatusEffect
key: Stun
@@ -76,8 +76,8 @@
Asphyxiation: 2
- !type:Jitter
- !type:GenericStatusEffect
key: BlurryVision
component: BlurryVision
key: NarcoticEffect
component: NarcoticEffect
- !type:GenericStatusEffect
key: Stun
time: 1

View File

@@ -1014,34 +1014,33 @@
# kreses
- type: entity
parent: ClothingBackpack
id: KresesClothingBackpackDeath
name: рюкзак из исследователя пустоты
suffix: fluff
description: Кажется когда то это не было рюкзаком. Вы чувствуйте на себя взгляды из пустоты.
parent: ClothingBackpackSatchel
id: KresesClothingBackpackVoid
name: Сумка солдата сингулярности
description: В ней может храниться все, что угодно, даже сама сингулярность.
components:
- type: Sprite
sprite: Clothing/Back/Backpacks/ertleader.rsi
sprite: White/Fluff/kreses/sachel.rsi
- type: entity
parent: ClothingUniformJumpsuitDeathSquad
id: KresesClothingJumpsuitDeath
name: комбинезон исследователя из пустоты
suffix: fluff
description: Костюм тех, кто видел что внутри черной дыры. Если слишком долго смотреть в пустоту, то пустота начнёт смотреть в тебя.
- type: entity
parent: ClothingMaskGas
id: KresesClothingMaskGasDeath
name: противогаз исследователя пустоты
suffix: fluff
description: Стандартный противогаз для тех, кто любит полетать в космосе и понаблюдать вблизи за чёрной дырой.
parent: ClothingShoesBaseButcherable
id: KresesClothingShoesVoid
name: Ботинки солдата пустоты
description: Стандартная обувь иседователей бездны, все еще покрыты бурлящей кровью.
components:
- type: Sprite
sprite: Clothing/Mask/squadron.rsi
- type: Clothing
sprite: Clothing/Mask/squadron.rsi
sprite: White/Fluff/kreses/boots.rsi
- type: entity
parent: ClothingOuterBase
id: KresesClothingOuterVoid
name: Шинель солдата пустоты
description: Прямиком из сингулярности, как долго ты сможешь её носить?
components:
- type: Sprite
sprite: White/Fluff/kreses/coat.rsi
#Reider207
- type: entity
parent: ClothingOuterStorageBase
id: ClothingOuterJacketReider207

View File

@@ -388,18 +388,18 @@
# kreses
- type: loadout
id: KresesGasMaskDeath
entity: KresesClothingMaskGasDeath
id: KresesBackpackVoid
entity: KresesClothingBackpackVoid
sponsorOnly: true
- type: loadout
id: KresesJumpsuitDeath
entity: KresesClothingJumpsuitDeath
id: KresesShoesVoid
entity: KresesClothingShoesVoid
sponsorOnly: true
- type: loadout
id: KresesBackpackDeath
entity: KresesClothingBackpackDeath
id: KresesCoatVoid
entity: KresesClothingOuterVoid
# Reider207
- type: loadout

View File

@@ -409,5 +409,5 @@
ckey: kreses
sprite: White/Ghosts/kreses-ghost.rsi
alpha: 0.9
ghostName: Gawr
ghostDescription: Из океана...
ghostName: Дракон сингулярности
ghostDescription: Владыка сингулярности

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

@@ -50,3 +50,10 @@
desc: "Знаю правду, славим великого!"
moodChange: enum.MoodChangeLevel.Big
positiveEffect: true
- type: moodEffect
id: Stimulator
desc: "Я ЧУВСТВУЮ ЭТО, В МОЕЙ КРОВИ НАХОДИТСЯ ЧТО-ТО НЕОБЫЧНОЕ!!"
moodChange: enum.MoodChangeLevel.Medium
positiveEffect: true
timeout: 2

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

@@ -0,0 +1,14 @@
- type: entity
name: mindshield implanters box
parent: BoxCardboard
id: BoxMindshield
description: A box full of implants.
components:
- type: StorageFill
contents:
- id: MindShieldImplanter
amount: 4
- type: Sprite
layers:
- state: box
- state: syringe

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

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