diff --git a/Content.Client/_White/Cult/UI/SpellRemover/SpellRemoverBUI.cs b/Content.Client/_White/Cult/UI/SpellRemover/SpellRemoverBUI.cs new file mode 100644 index 0000000000..5bf9ebef07 --- /dev/null +++ b/Content.Client/_White/Cult/UI/SpellRemover/SpellRemoverBUI.cs @@ -0,0 +1,87 @@ +using System.Linq; +using Content.Client._White.UserInterface.Radial; +using Content.Shared._White.Cult; +using Content.Shared.Actions; +using Content.Shared._White.Cult.Components; +using Robust.Client.Player; +using Robust.Client.Utility; +using Robust.Shared.Prototypes; +using Robust.Shared.Utility; +using CultistComponent = Content.Shared._White.Cult.Components.CultistComponent; + +namespace Content.Client._White.Cult.UI.SpellRemover; + +public sealed class SpellRemoverBUI : BoundUserInterface +{ + private RadialContainer? _radialContainer; + + public SpellRemoverBUI(EntityUid owner, Enum uiKey) : base(owner, uiKey) + { + IoCManager.InjectDependencies(this); + } + + protected override void Open() + { + base.Open(); + _radialContainer = new RadialContainer(); + _radialContainer.Closed += Close; + var entityManager = IoCManager.Resolve(); + var ent = IoCManager.Resolve().LocalEntity; + + if (ent == null || !entityManager.TryGetComponent(ent, out CultistComponent? comp) || + comp.SelectedEmpowers.Count == 0) + { + Close(); + return; + } + + var metaQuery = entityManager.GetEntityQuery(); + var instantQuery = entityManager.GetEntityQuery(); + var targetQuery = entityManager.GetEntityQuery(); + + foreach (var action in comp.SelectedEmpowers) + { + if (action == null) + continue; + + var spell = entityManager.GetEntity(action.Value); + + if (!entityManager.EntityExists(spell)) + continue; + + SpriteSpecifier? icon; + if (instantQuery.TryGetComponent(spell, out var instantComp)) + icon = instantComp.Icon; + else + { + if (!targetQuery.TryGetComponent(spell, out var targetComp)) + continue; + icon = targetComp.Icon; + } + + if (icon == null) + continue; + + var texture = icon.Frame0(); + + if (!metaQuery.TryGetComponent(spell, out var meta)) + continue; + + var button = _radialContainer.AddButton(meta.EntityName, texture); + + button.Controller.OnPressed += _ => + { + SendMessage(new CultEmpowerRemoveBuiMessage(action.Value)); + Close(); + }; + } + + _radialContainer.OpenAttachedLocalPlayer(); + } + + protected override void Dispose(bool disposing) + { + base.Dispose(disposing); + _radialContainer?.Close(); + } +} diff --git a/Content.Server/_White/Cult/GameRule/CultRuleSystem.cs b/Content.Server/_White/Cult/GameRule/CultRuleSystem.cs index af9ab98432..056c48e57d 100644 --- a/Content.Server/_White/Cult/GameRule/CultRuleSystem.cs +++ b/Content.Server/_White/Cult/GameRule/CultRuleSystem.cs @@ -179,7 +179,7 @@ public sealed class CultRuleSystem : GameRuleSystem foreach (var empower in component.SelectedEmpowers) { - _actions.RemoveAction(uid, empower); + _actions.RemoveAction(uid, GetEntity(empower)); } RemoveCultistAppearance(uid); diff --git a/Content.Server/_White/Cult/Runes/Systems/CultSystem.Actions.cs b/Content.Server/_White/Cult/Runes/Systems/CultSystem.Actions.cs index 672a1713e6..5cddd043be 100644 --- a/Content.Server/_White/Cult/Runes/Systems/CultSystem.Actions.cs +++ b/Content.Server/_White/Cult/Runes/Systems/CultSystem.Actions.cs @@ -4,6 +4,7 @@ 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.Chemistry.Components; using Content.Shared.Damage; using Content.Shared.Damage.Prototypes; @@ -15,8 +16,11 @@ using Content.Shared.Mobs.Components; using Content.Shared.Stacks; using Content.Shared.StatusEffect; using Content.Shared.Stunnable; -using Content.Shared._White.Cult; using Content.Shared._White.Cult.Actions; +using Content.Shared.Actions; +using Content.Shared.Cuffs.Components; +using Content.Shared.DoAfter; +using Content.Shared.Mindshield.Components; using Robust.Server.GameObjects; using Robust.Shared.Audio; using Robust.Shared.Player; @@ -38,38 +42,71 @@ public partial class CultSystem SubscribeLocalEvent(OnTwistedConstructionAction); SubscribeLocalEvent(OnSummonDaggerAction); SubscribeLocalEvent(OnShadowShackles); - SubscribeLocalEvent(OnElectromagneticPulse); + SubscribeLocalEvent(OnElectromagneticPulse); SubscribeLocalEvent(OnSummonCombatEquipment); SubscribeLocalEvent(OnConcealPresence); SubscribeLocalEvent(OnBloodRites); SubscribeLocalEvent(OnTeleport); SubscribeLocalEvent(OnStunTarget); + SubscribeLocalEvent(OnActionRemoved); + SubscribeLocalEvent(OnShackles); + } + + private void OnShackles(Entity ent, ref ShacklesEvent args) + { + if (args.Cancelled || args.Target == null) + return; + + if (!TryComp(args.Target, out CuffableComponent? cuffable) || cuffable.Container.ContainedEntities.Count > 0) + return; + + var cuffs = Spawn("ShadowShackles", Transform(ent).Coordinates); + if (!_cuffable.TryAddNewCuffs(args.Target.Value, args.User, cuffs, cuffable)) + QueueDel(cuffs); + } + + private void OnActionRemoved(Entity ent, ref ActionGettingRemovedEvent args) + { + ent.Comp.SelectedEmpowers.Remove(GetNetEntity(args.Action)); + Dirty(ent); } private void OnStunTarget(EntityUid uid, CultistComponent component, CultStunTargetActionEvent args) { - if (args.Target == uid || !HasComp(args.Target)) + if (args.Target == uid || !TryComp(args.Performer, out var bloodstream) || + HasComp(args.Target) || !TryComp(args.Target, out var status)) return; - if (_stunSystem.TryStun(args.Target, TimeSpan.FromSeconds(6), true)) + if (HasComp(args.Target)) { - args.Handled = true; + _popupSystem.PopupEntity("Он имплантирован чипом защиты разума.", args.Performer, args.Performer); + return; } + + if (!_stunSystem.TryParalyze(args.Target, TimeSpan.FromSeconds(6), true, status) & + !_statusEffectsSystem.TryAddStatusEffect(args.Target, "Muted", TimeSpan.FromSeconds(12), true, "Muted", + status)) + return; + + _bloodstreamSystem.TryModifyBloodLevel(uid, -10, bloodstream, createPuddle: false); + args.Handled = true; } private void OnTeleport(EntityUid uid, CultistComponent component, CultTeleportTargetActionEvent args) { - if (!TryComp(args.Performer, out _) || !TryComp(uid, out var actor)) + if (!TryComp(args.Performer, out var bloodstream) || !TryComp(uid, out var actor)) return; if (!TryComp(args.Target, out _) && !(TryComp(args.Target, out var mobStateComponent) && mobStateComponent.CurrentState is not MobState.Alive)) { - _popupSystem.PopupEntity("Цель должна быть культистом или лежать", args.Performer); + _popupSystem.PopupEntity("Цель должна быть культистом или лежать.", args.Performer, args.Performer); return; } + _bloodstreamSystem.TryModifyBloodLevel(uid, -5, bloodstream, createPuddle: false); + var eui = new TeleportSpellEui(args.Performer, args.Target); _euiManager.OpenEui(eui, actor.PlayerSession); eui.StateDirty(); @@ -148,10 +185,10 @@ public partial class CultSystem CultistComponent component, CultSummonCombatEquipmentTargetActionEvent args) { - if (!TryComp(args.Performer, out _)) + if (!TryComp(args.Performer, out var bloodstream)) return; - _bloodstreamSystem.TryModifyBloodLevel(uid, -20, createPuddle: false); + _bloodstreamSystem.TryModifyBloodLevel(uid, -20, bloodstream, createPuddle: false); var coordinates = Transform(uid).Coordinates; var helmet = Spawn("ClothingHeadHelmetCult", coordinates); @@ -177,30 +214,42 @@ public partial class CultSystem private void OnElectromagneticPulse( EntityUid uid, CultistComponent component, - CultElectromagneticPulseTargetActionEvent args) + CultElectromagneticPulseInstantActionEvent args) { - if (!TryComp(args.Performer, out _)) + if (!TryComp(args.Performer, out var bloodstream)) return; - _bloodstreamSystem.TryModifyBloodLevel(uid, -20, createPuddle: false); + _bloodstreamSystem.TryModifyBloodLevel(uid, -10, bloodstream, createPuddle: false); - var xform = Transform(uid); - - _empSystem.EmpPulse(xform.MapPosition, 10, 100000, 10f); - _bloodstreamSystem.TryModifyBloodLevel(uid, -20, createPuddle: false); + _empSystem.EmpPulse(_transform.GetMapCoordinates(uid), 5, 100000, 10f); args.Handled = true; } private void OnShadowShackles(EntityUid uid, CultistComponent component, CultShadowShacklesTargetActionEvent args) { - if (!TryComp(args.Performer, out _)) + if (args.Target == uid || !TryComp(args.Performer, out var bloodstream)) return; - _bloodstreamSystem.TryModifyBloodLevel(uid, -20, createPuddle: false); + _bloodstreamSystem.TryModifyBloodLevel(uid, -5, bloodstream, createPuddle: false); - var cuffs = Spawn("CultistCuffs", Transform(uid).Coordinates); - _handsSystem.TryPickupAnyHand(uid, cuffs); + if (!HasComp(args.Target) && + _statusEffectsSystem.TryAddStatusEffect(args.Target, "Muted", TimeSpan.FromSeconds(10), true, "Muted")) + { + _popupSystem.PopupEntity("Цель обезмолвлена.", args.Performer, args.Performer); + args.Handled = true; + } + + if (!TryComp(args.Target, out CuffableComponent? cuffs) || cuffs.Container.ContainedEntities.Count > 0) + return; + + _doAfterSystem.TryStartDoAfter(new DoAfterArgs(EntityManager, args.Performer, TimeSpan.FromSeconds(2), + new ShacklesEvent(), args.Performer, args.Target) + { + BreakOnTargetMove = true, + BreakOnUserMove = true, + BreakOnDamage = true + }); args.Handled = true; } @@ -229,14 +278,14 @@ public partial class CultSystem var material = _entityManager.SpawnEntity(RunicMetalPrototypeId, transform); - _bloodstreamSystem.TryModifyBloodLevel(args.Performer, -15, bloodstreamComponent, false); + _bloodstreamSystem.TryModifyBloodLevel(args.Performer, -stack.Count / 2f, bloodstreamComponent, false); if (!_entityManager.TryGetComponent(material, out var stackNew)) return; stackNew.Count = count; - _popupSystem.PopupEntity(Loc.GetString("Конвертируем сталь в руиник металл!"), args.Performer, args.Performer); + _popupSystem.PopupEntity("Конвертируем сталь в руинический металл!", args.Performer, args.Performer); args.Handled = true; } @@ -251,7 +300,7 @@ public partial class CultSystem var xform = Transform(args.Performer).Coordinates; var dagger = _entityManager.SpawnEntity(RitualDaggerPrototypeId, xform); - _bloodstreamSystem.TryModifyBloodLevel(args.Performer, -30, bloodstreamComponent, false); + _bloodstreamSystem.TryModifyBloodLevel(args.Performer, -20, bloodstreamComponent, false); _handsSystem.TryPickupAnyHand(args.Performer, dagger); args.Handled = true; } diff --git a/Content.Server/_White/Cult/Runes/Systems/CultSystem.Rune.cs b/Content.Server/_White/Cult/Runes/Systems/CultSystem.Rune.cs index e070e90b34..7361636f1b 100644 --- a/Content.Server/_White/Cult/Runes/Systems/CultSystem.Rune.cs +++ b/Content.Server/_White/Cult/Runes/Systems/CultSystem.Rune.cs @@ -11,7 +11,6 @@ using Content.Server.Weapons.Ranged.Systems; using Content.Server._White.Cult.GameRule; using Content.Server._White.Cult.Runes.Comps; using Content.Shared._White.Chaplain; -using Content.Shared.Actions; using Content.Shared.Chemistry.Components.SolutionManager; using Content.Shared.Cuffs.Components; using Content.Shared.Damage; @@ -28,16 +27,15 @@ using Content.Shared.Popups; using Content.Shared.Projectiles; using Content.Shared.Pulling.Components; using Content.Shared.Rejuvenate; -using Content.Shared.Roles.Jobs; using Content.Shared._White.Cult; 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.Mindshield.Components; using Content.Shared.Pulling; 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; @@ -65,6 +63,7 @@ public sealed partial class CultSystem : EntitySystem [Dependency] private readonly IRobustRandom _random = default!; [Dependency] private readonly FlammableSystem _flammableSystem = default!; [Dependency] private readonly SharedPullingSystem _pulling = default!; + [Dependency] private readonly SharedCuffableSystem _cuffable = default!; public override void Initialize() @@ -110,6 +109,7 @@ public sealed partial class CultSystem : EntitySystem InitializeBarrierSystem(); InitializeConstructsAbilities(); InitializeActions(); + InitializeVerb(); } private float _timeToDraw; @@ -518,6 +518,14 @@ public sealed partial class CultSystem : EntitySystem _stunSystem.TryStun(target, TimeSpan.FromSeconds(2f), false); HealCultist(target); + if (TryComp(target, out CuffableComponent? cuffs) && cuffs.Container.ContainedEntities.Count >= 1) + { + var lastAddedCuffs = cuffs.LastAddedCuffs; + _cuffable.Uncuff(target, user, lastAddedCuffs); + } + + _statusEffectsSystem.TryRemoveStatusEffect(target, "Muted"); + return true; } @@ -1143,17 +1151,19 @@ public sealed partial class CultSystem : EntitySystem if (component.IsRune) { - if (comp.SelectedEmpowers.Count > component.MaxAllowedCultistActions) + if (comp.SelectedEmpowers.Count >= component.MaxAllowedCultistActions) { _popupSystem.PopupEntity(Loc.GetString("cult-too-much-empowers"), uid); return; } - comp.SelectedEmpowers.Add(_actionsSystem.AddAction(playerEntity.Value, action)); + comp.SelectedEmpowers.Add(GetNetEntity(_actionsSystem.AddAction(playerEntity.Value, action))); + Dirty(playerEntity.Value, comp); } else if (comp.SelectedEmpowers.Count < component.MinRequiredCultistActions) { - comp.SelectedEmpowers.Add(_actionsSystem.AddAction(playerEntity.Value, action)); + comp.SelectedEmpowers.Add(GetNetEntity(_actionsSystem.AddAction(playerEntity.Value, action))); + Dirty(playerEntity.Value, comp); } } diff --git a/Content.Server/_White/Cult/Runes/Systems/CultSystem.Verb.cs b/Content.Server/_White/Cult/Runes/Systems/CultSystem.Verb.cs new file mode 100644 index 0000000000..d4b8cb238f --- /dev/null +++ b/Content.Server/_White/Cult/Runes/Systems/CultSystem.Verb.cs @@ -0,0 +1,115 @@ +using System.Linq; +using Content.Server.Body.Components; +using Content.Shared._White.Cult; +using Content.Shared._White.Cult.Components; +using Content.Shared.DoAfter; +using Content.Shared.Verbs; +using Robust.Shared.Player; + +namespace Content.Server._White.Cult.Runes.Systems; + +public sealed partial class CultSystem +{ + public void InitializeVerb() + { + SubscribeLocalEvent>(OnGetVerbs); + SubscribeLocalEvent(OnCultistEmpowerSelected); + SubscribeLocalEvent(OnCultistEmpowerRemove); + SubscribeLocalEvent(OnSpellCreated); + } + + private void OnCultistEmpowerRemove(Entity ent, ref CultEmpowerRemoveBuiMessage args) + { + var entity = GetEntity(args.ActionType); + ent.Comp.SelectedEmpowers.Remove(args.ActionType); + _actionsSystem.RemoveAction(entity); + Dirty(ent); + } + + private void OnSpellCreated(EntityUid ent, CultistComponent comp, SpellCreatedEvent args) + { + if (args.Cancelled || comp.SelectedEmpowers.Count >= 1) + return; + + var action = CultistComponent.CultistActions.FirstOrDefault(x => x.Equals(args.Spell)); + + if (action == null) + return; + + var howMuchBloodTake = HasComp(ent) ? -10f : -20f; + + if (!TryComp(ent, out var bloodstreamComponent)) + return; + + _bloodstreamSystem.TryModifyBloodLevel(ent, howMuchBloodTake, bloodstreamComponent, createPuddle: false); + + comp.SelectedEmpowers.Add(GetNetEntity(_actionsSystem.AddAction(ent, args.Spell))); + + Dirty(ent, comp); + } + + private void OnCultistEmpowerSelected(EntityUid ent, CultistComponent comp, CultEmpowerSelectedBuiMessage args) + { + var action = CultistComponent.CultistActions.FirstOrDefault(x => x.Equals(args.ActionType)); + + if (action == null) + return; + + if (comp.SelectedEmpowers.Count >= 1) + { + _popupSystem.PopupEntity(Loc.GetString("verb-spell-create-too-much"), ent); + return; + } + + var creationTime = HasComp(ent) ? 2.5f : 5f; + + _doAfterSystem.TryStartDoAfter( + new DoAfterArgs(_entityManager, ent, creationTime, new SpellCreatedEvent {Spell = action}, ent) + { + BreakOnDamage = true, + BreakOnUserMove = true + }); + } + + private void OnGetVerbs(Entity ent, ref GetVerbsEvent args) + { + if (ent.Owner != args.User || !TryComp(args.User, out var actor)) + return; + + var createSpellVerb = new Verb + { + Text = Loc.GetString("verb-spell-create-text"), + Message = Loc.GetString("verb-spell-create-message"), + Category = VerbCategory.Cult, + Act = () => + { + _ui.TryOpen(ent, CultEmpowerUiKey.Key, actor.PlayerSession); + } + }; + + var removeSpellVerb = new Verb + { + Text = Loc.GetString("verb-spell-remove-text"), + Message = Loc.GetString("verb-spell-remove-message"), + Category = VerbCategory.Cult, + Act = () => + { + RemoveSpell(ent, actor.PlayerSession); + } + }; + + args.Verbs.Add(createSpellVerb); + args.Verbs.Add(removeSpellVerb); + } + + private void RemoveSpell(Entity ent, ICommonSession session) + { + if (ent.Comp.SelectedEmpowers.Count == 0) + { + _popupSystem.PopupEntity(Loc.GetString("verb-spell-remove-no-spells"), ent); + return; + } + + _ui.TryOpen(ent, CultEmpowerRemoveUiKey.Key, session); + } +} diff --git a/Content.Shared/Actions/ActionEvents.cs b/Content.Shared/Actions/ActionEvents.cs index 72a566b8c8..4715f03c70 100644 --- a/Content.Shared/Actions/ActionEvents.cs +++ b/Content.Shared/Actions/ActionEvents.cs @@ -102,6 +102,13 @@ public sealed class RequestPerformActionEvent : EntityEventArgs } } +// WD START +public sealed partial class ActionGettingRemovedEvent : EntityEventArgs +{ + public EntityUid Action; +} +// WD END + /// /// This is the type of event that gets raised when an is performed. The field is automatically filled out by the . diff --git a/Content.Shared/Actions/BaseActionComponent.cs b/Content.Shared/Actions/BaseActionComponent.cs index cce7b912c7..241270edae 100644 --- a/Content.Shared/Actions/BaseActionComponent.cs +++ b/Content.Shared/Actions/BaseActionComponent.cs @@ -144,6 +144,11 @@ public abstract partial class BaseActionComponent : Component /// [DataField("autoPopulate")] public bool AutoPopulate = true; + // WD START + [DataField] + public bool RemoveOnNoCharges; + // WD END + /// /// Temporary actions are deleted when they get removed a . /// diff --git a/Content.Shared/Actions/SharedActionsSystem.cs b/Content.Shared/Actions/SharedActionsSystem.cs index a6c40c7ae3..df469f4ac1 100644 --- a/Content.Shared/Actions/SharedActionsSystem.cs +++ b/Content.Shared/Actions/SharedActionsSystem.cs @@ -15,6 +15,7 @@ using Robust.Shared.Map; using Robust.Shared.Timing; using Robust.Shared.Utility; using Content.Shared.Rejuvenate; +using Robust.Shared.Network; namespace Content.Shared.Actions; @@ -29,6 +30,7 @@ public abstract class SharedActionsSystem : EntitySystem [Dependency] private readonly SharedAudioSystem _audio = default!; [Dependency] private readonly SharedTransformSystem _transformSystem = default!; [Dependency] private readonly ActionContainerSystem _actionContainer = default!; + [Dependency] private readonly INetManager _net = default!; // WD public override void Initialize() { @@ -537,6 +539,13 @@ public abstract class SharedActionsSystem : EntitySystem action.Charges--; if (action is { Charges: 0, RenewCharges: false }) action.Enabled = false; + // WD START + if (action is {Charges: 0, RemoveOnNoCharges: true}) + { + RaiseLocalEvent(performer, new ActionGettingRemovedEvent {Action = actionId}); + RemoveAction(performer, actionId, component, action); + } + // WD END } action.Cooldown = null; @@ -816,7 +825,7 @@ public abstract class SharedActionsSystem : EntitySystem Dirty(actionId.Value, action); Dirty(performer, comp); ActionRemoved(performer, actionId.Value, comp, action); - if (action.Temporary) + if (action.Temporary && (!_net.IsClient || action.ClientExclusive)) // WD EDIT QueueDel(actionId.Value); } diff --git a/Content.Shared/Cuffs/SharedCuffableSystem.cs b/Content.Shared/Cuffs/SharedCuffableSystem.cs index a5fe935de8..16e91be478 100644 --- a/Content.Shared/Cuffs/SharedCuffableSystem.cs +++ b/Content.Shared/Cuffs/SharedCuffableSystem.cs @@ -680,8 +680,13 @@ namespace Content.Shared.Cuffs if (cuff.BreakOnRemove) { QueueDel(cuffsToRemove); - var trash = Spawn(cuff.BrokenPrototype, Transform(cuffsToRemove).Coordinates); - _hands.PickupOrDrop(user, trash); + // WD EDIT START + if (cuff.BrokenPrototype != null) + { + var trash = Spawn(cuff.BrokenPrototype, Transform(cuffsToRemove).Coordinates); + _hands.PickupOrDrop(user, trash); + } + // WD EDIT END } else { diff --git a/Content.Shared/Verbs/VerbCategory.cs b/Content.Shared/Verbs/VerbCategory.cs index 269f5f7804..87aaff6a37 100644 --- a/Content.Shared/Verbs/VerbCategory.cs +++ b/Content.Shared/Verbs/VerbCategory.cs @@ -41,6 +41,9 @@ namespace Content.Shared.Verbs public static readonly VerbCategory MeatyOre = new("MeatyOre", null); + public static readonly VerbCategory Cult = + new("verb-categories-cult", "/Textures/White/Cult/interface.rsi/icon.png"); + public static readonly VerbCategory Antag = new("verb-categories-antag", "/Textures/Interface/VerbIcons/antag-e_sword-temp.192dpi.png", iconsOnly: true) { Columns = 5 }; diff --git a/Content.Shared/_White/Cult/Actions/CultActions.cs b/Content.Shared/_White/Cult/Actions/CultActions.cs index 92104a70b7..4abc4b31f1 100644 --- a/Content.Shared/_White/Cult/Actions/CultActions.cs +++ b/Content.Shared/_White/Cult/Actions/CultActions.cs @@ -18,7 +18,7 @@ public sealed partial class CultTeleportTargetActionEvent : EntityTargetActionEv { } -public sealed partial class CultElectromagneticPulseTargetActionEvent : EntityTargetActionEvent +public sealed partial class CultElectromagneticPulseInstantActionEvent : InstantActionEvent { } diff --git a/Content.Shared/_White/Cult/Actions/ShacklesEvent.cs b/Content.Shared/_White/Cult/Actions/ShacklesEvent.cs new file mode 100644 index 0000000000..6c27ce1aab --- /dev/null +++ b/Content.Shared/_White/Cult/Actions/ShacklesEvent.cs @@ -0,0 +1,9 @@ +using Content.Shared.DoAfter; +using Robust.Shared.Serialization; + +namespace Content.Shared._White.Cult.Actions; + +[Serializable, NetSerializable] +public sealed partial class ShacklesEvent : SimpleDoAfterEvent +{ +} diff --git a/Content.Shared/_White/Cult/Components/CultistComponent.cs b/Content.Shared/_White/Cult/Components/CultistComponent.cs index 69048860ae..0c822b41e6 100644 --- a/Content.Shared/_White/Cult/Components/CultistComponent.cs +++ b/Content.Shared/_White/Cult/Components/CultistComponent.cs @@ -7,7 +7,7 @@ namespace Content.Shared._White.Cult.Components; /// /// This is used for tagging a mob as a cultist. /// -[RegisterComponent, NetworkedComponent] +[RegisterComponent, NetworkedComponent, AutoGenerateComponentState] public sealed partial class CultistComponent : Component { [DataField("greetSound", customTypeSerializer: typeof(SoundSpecifierTypeSerializer))] @@ -18,22 +18,28 @@ public sealed partial class CultistComponent : Component public CancellationTokenSource? HolyConvertToken; - [NonSerialized] - public List SelectedEmpowers = new(); + [AutoNetworkedField] + public List SelectedEmpowers = new(); public static string SummonCultDaggerAction = "InstantActionSummonCultDagger"; public static string BloodRitesAction = "InstantActionBloodRites"; + public static string EmpPulseAction = "InstantActionEmpPulse"; + public static string CultTwistedConstructionAction = "ActionCultTwistedConstruction"; public static string CultTeleportAction = "ActionCultTeleport"; public static string CultSummonCombatEquipmentAction = "ActionCultSummonCombatEquipment"; + public static string CultStunAction = "ActionCultStun"; + + public static string CultShadowShacklesAction = "ActionCultShadowShackles"; + public static List CultistActions = new() { SummonCultDaggerAction, BloodRitesAction, CultTwistedConstructionAction, CultTeleportAction, - CultSummonCombatEquipmentAction + CultSummonCombatEquipmentAction, CultStunAction, EmpPulseAction, CultShadowShacklesAction }; } diff --git a/Content.Shared/_White/Cult/SpellVerbEvents.cs b/Content.Shared/_White/Cult/SpellVerbEvents.cs new file mode 100644 index 0000000000..9122c906d6 --- /dev/null +++ b/Content.Shared/_White/Cult/SpellVerbEvents.cs @@ -0,0 +1,27 @@ +using Content.Shared.DoAfter; +using Robust.Shared.Serialization; + +namespace Content.Shared._White.Cult; + +[Serializable, NetSerializable] +public sealed partial class SpellCreatedEvent : SimpleDoAfterEvent +{ + public string Spell = ""; +} + +[Serializable, NetSerializable] +public sealed class CultEmpowerRemoveBuiMessage : BoundUserInterfaceMessage +{ + public NetEntity ActionType; + + public CultEmpowerRemoveBuiMessage(NetEntity actionType) + { + ActionType = actionType; + } +} + +[Serializable, NetSerializable] +public enum CultEmpowerRemoveUiKey : byte +{ + Key +} diff --git a/Resources/Locale/ru-RU/cult/abilities.ftl b/Resources/Locale/ru-RU/cult/abilities.ftl index 98a6ae6fbf..0575e85db3 100644 --- a/Resources/Locale/ru-RU/cult/abilities.ftl +++ b/Resources/Locale/ru-RU/cult/abilities.ftl @@ -10,3 +10,28 @@ wraith-phase-action-name = Фазовый Сдвиг wraith-phase-action-description = Это заклинание позволяет проходить сквозь стены, подобно бесплотному полету волшебника. juggernaut-create-wall-action-name = Щит juggernaut-create-wall-action-description = Это заклинание создает временное, невидимое, силовое поле для защиты себя и союзников от подавляющего огня. + + +ent-ActionCultTwistedConstruction = Искажённое Воздействие + .desc = Зловещее заклинание, которое используют для превращения металла в рунический металл. + +ent-ActionCultTeleport = Телепорт + .desc = Полезное заклинание, которое телепортирует культистов в выбранное место. + +ent-ActionCultSummonCombatEquipment = Призыв Боевого Снаряжения + .desc = Важное заклинание, которое позволяет вам вызвать полный набор боевого снаряжения. + +ent-InstantActionSummonCultDagger = Призыв Ритуального Кинжала + .desc = Призывает ритуальный кинжал. + +ent-InstantActionBloodRites = Кровавые Обряды + .desc = Высасывает кровь и исцеляет вас. + +ent-ActionCultStun = Оглушение + .desc = Сильное заклинание, которое оглушает и обезмолвливает жертв. + +ent-ActionCultShadowShackles = Теневые Узы + .desc = Бесшумное заклинание, которое наложит на человека теневые наручники и заставит вашу жертву замолчать на 10 секунд. + +ent-InstantActionEmpPulse = Электромагнитный Импульс + .desc = Большое заклинание, которое позволяет пользователю направлять темную энергию в ЭМИ. diff --git a/Resources/Locale/ru-RU/cult/verb.ftl b/Resources/Locale/ru-RU/cult/verb.ftl new file mode 100644 index 0000000000..0721351b41 --- /dev/null +++ b/Resources/Locale/ru-RU/cult/verb.ftl @@ -0,0 +1,9 @@ +verb-categories-cult = Культ + +verb-spell-create-text = Создать заклинание крови +verb-spell-create-message = Вы можете создать одно заклинание крови без руны могущества. +verb-spell-create-too-much = Начертите руну могущества, чтобы создать больше одного заклинания крови. + +verb-spell-remove-text = Удалить заклинание крови +verb-spell-remove-message = Убрать любое из созданных заклинаний крови. +verb-spell-remove-no-spells = Заклинания крови отсутствуют. diff --git a/Resources/Prototypes/Entities/Mobs/Species/base.yml b/Resources/Prototypes/Entities/Mobs/Species/base.yml index 221ac82df1..5ca7620f41 100644 --- a/Resources/Prototypes/Entities/Mobs/Species/base.yml +++ b/Resources/Prototypes/Entities/Mobs/Species/base.yml @@ -212,6 +212,10 @@ type: TeleportRunesListWindowBUI - key: enum.SummonCultistUiKey.Key type: SummonCultistListWindowBUI + - key: enum.CultEmpowerUiKey.Key + type: SpellSelectorBUI + - key: enum.CultEmpowerRemoveUiKey.Key + type: SpellRemoverBUI # WD-EDIT END - type: Puller - type: Butcherable diff --git a/Resources/Prototypes/White/Mobs/Species/harpy.yml b/Resources/Prototypes/White/Mobs/Species/harpy.yml index 4a91d31b2d..351a45d5ae 100644 --- a/Resources/Prototypes/White/Mobs/Species/harpy.yml +++ b/Resources/Prototypes/White/Mobs/Species/harpy.yml @@ -41,6 +41,10 @@ type: TeleportRunesListWindowBUI - key: enum.SummonCultistUiKey.Key type: SummonCultistListWindowBUI + - key: enum.CultEmpowerUiKey.Key + type: SpellSelectorBUI + - key: enum.CultEmpowerRemoveUiKey.Key + type: SpellRemoverBUI # WD-EDIT END - type: Sprite scale: 0.9, 0.9 diff --git a/Resources/Prototypes/_White/Actions/cult_actions.yml b/Resources/Prototypes/_White/Actions/cult_actions.yml index 456fcc0e0d..099a61f336 100644 --- a/Resources/Prototypes/_White/Actions/cult_actions.yml +++ b/Resources/Prototypes/_White/Actions/cult_actions.yml @@ -5,13 +5,15 @@ noSpawn: true components: - type: EntityTargetAction - useDelay: 50 canTargetSelf: false icon: sprite: /Textures/Objects/Materials/Sheets/metal.rsi state: steel event: !type:CultTwistedConstructionActionEvent itemIconStyle: BigAction + charges: 1 + temporary: true + removeOnNoCharges: true - type: entity id: ActionCultTeleport @@ -20,7 +22,6 @@ noSpawn: true components: - type: EntityTargetAction - useDelay: 30 whitelist: components: - HumanoidAppearance @@ -33,6 +34,9 @@ state: teleport event: !type:CultTeleportTargetActionEvent itemIconStyle: BigAction + charges: 1 + temporary: true + removeOnNoCharges: true - type: entity id: ActionCultSummonCombatEquipment @@ -41,7 +45,6 @@ noSpawn: true components: - type: EntityTargetAction - useDelay: 300 whitelist: components: - HumanoidAppearance @@ -54,6 +57,53 @@ state: armor event: !type:CultSummonCombatEquipmentTargetActionEvent itemIconStyle: BigAction + charges: 1 + temporary: true + removeOnNoCharges: true + +- type: entity + id: ActionCultStun + name: Stun + description: A potent spell that will stun and mute victims. + noSpawn: true + components: + - type: EntityTargetAction + whitelist: + components: + - HumanoidAppearance + canTargetSelf: false + deselectOnMiss: true + repeat: false + icon: + sprite: /Textures/White/Cult/actions_cult.rsi + state: stun + event: !type:CultStunTargetActionEvent + itemIconStyle: BigAction + charges: 1 + temporary: true + removeOnNoCharges: true + +- type: entity + id: ActionCultShadowShackles + name: Shadow Shackles + description: A stealthy spell that will summon shadowy handcuffs on a person, and temporarily silence your victim for 10 seconds. + noSpawn: true + components: + - type: EntityTargetAction + whitelist: + components: + - HumanoidAppearance + canTargetSelf: false + deselectOnMiss: true + repeat: false + icon: + sprite: /Textures/White/Cult/actions_cult.rsi + state: shackles + event: !type:CultShadowShacklesTargetActionEvent + itemIconStyle: BigAction + charges: 4 + temporary: true + removeOnNoCharges: true - type: entity id: InstantActionSummonCultDagger @@ -66,7 +116,9 @@ sprite: /Textures/White/Cult/interface.rsi state: icon event: !type:CultSummonDaggerActionEvent - useDelay: 200 + charges: 1 + temporary: true + removeOnNoCharges: true - type: entity id: InstantActionBloodRites @@ -79,4 +131,21 @@ sprite: /Textures/White/Cult/actions_cult.rsi state: blood_rites event: !type:CultBloodRitesInstantActionEvent - useDelay: 35 + charges: 4 + temporary: true + removeOnNoCharges: true + +- type: entity + id: InstantActionEmpPulse + name: Electromagnetic Pulse + description: A large spell that allows a user to channel dark energy into an EMP. + noSpawn: true + components: + - type: InstantAction + icon: + sprite: /Textures/White/Cult/actions_cult.rsi + state: emp + event: !type:CultElectromagneticPulseInstantActionEvent + charges: 1 + temporary: true + removeOnNoCharges: true diff --git a/Resources/Prototypes/_White/Entities/Cult/Items/shadow_shackles.yml b/Resources/Prototypes/_White/Entities/Cult/Items/shadow_shackles.yml new file mode 100644 index 0000000000..68fc425b9d --- /dev/null +++ b/Resources/Prototypes/_White/Entities/Cult/Items/shadow_shackles.yml @@ -0,0 +1,12 @@ +- type: entity + name: shadow shackles + description: Shackles that bind the wrists with sinister magic. + id: ShadowShackles + parent: Handcuffs + components: + - type: Handcuff + breakoutTime: 15 + color: black + breakOnRemove: true + - type: Sprite + color: black