diff --git a/Content.Server/_White/Cult/Items/Components/BaseMagicHandComponent.cs b/Content.Server/_White/Cult/Items/Components/BaseMagicHandComponent.cs new file mode 100644 index 0000000000..58c1dedd84 --- /dev/null +++ b/Content.Server/_White/Cult/Items/Components/BaseMagicHandComponent.cs @@ -0,0 +1,7 @@ +namespace Content.Server._White.Cult.Items.Components; + +public abstract partial class BaseMagicHandComponent : Component +{ + [DataField] + public string Speech; +} diff --git a/Content.Server/_White/Cult/Items/Components/CultRitesHandComponent.cs b/Content.Server/_White/Cult/Items/Components/CultRitesHandComponent.cs new file mode 100644 index 0000000000..9ed2b0dad6 --- /dev/null +++ b/Content.Server/_White/Cult/Items/Components/CultRitesHandComponent.cs @@ -0,0 +1,25 @@ +using Content.Shared._White.Cult; +using Robust.Shared.Audio; +using Robust.Shared.Serialization.TypeSerializers.Implementations.Custom.Prototype.List; + +namespace Content.Server._White.Cult.Items.Components; + +[RegisterComponent] +public sealed partial class CultRitesHandComponent : BaseMagicHandComponent +{ + [DataField] + public SoundSpecifier HealSound = new SoundPathSpecifier("/Audio/White/Cult/rites.ogg"); + + [DataField] + public SoundSpecifier SuckSound = new SoundPathSpecifier("/Audio/White/Cult/enter_blood.ogg"); + + [DataField] + public float HealModifier = 0.5f; + + [DataField(customTypeSerializer: typeof(PrototypeIdListSerializer))] + public List BloodRites = new () + { + "FactoryCultBloodSpear", + "FactoryCultBloodBarrage" + }; +} diff --git a/Content.Server/_White/Cult/Items/Components/CultStunHandComponent.cs b/Content.Server/_White/Cult/Items/Components/CultStunHandComponent.cs index e7e43a730e..26ad1ed723 100644 --- a/Content.Server/_White/Cult/Items/Components/CultStunHandComponent.cs +++ b/Content.Server/_White/Cult/Items/Components/CultStunHandComponent.cs @@ -1,7 +1,7 @@ namespace Content.Server._White.Cult.Items.Components; [RegisterComponent] -public sealed partial class CultStunHandComponent : Component +public sealed partial class CultStunHandComponent : BaseMagicHandComponent { [DataField] public TimeSpan Duration = TimeSpan.FromSeconds(16); @@ -14,7 +14,4 @@ public sealed partial class CultStunHandComponent : Component [DataField] public TimeSpan HaloMuteDuration = TimeSpan.FromSeconds(1); - - [DataField] - public string Speech = "Fuu ma'jin!"; } diff --git a/Content.Server/_White/Cult/Items/Systems/CultStunHandSystem.cs b/Content.Server/_White/Cult/Items/Systems/CultStunHandSystem.cs deleted file mode 100644 index 57b4006d7a..0000000000 --- a/Content.Server/_White/Cult/Items/Systems/CultStunHandSystem.cs +++ /dev/null @@ -1,56 +0,0 @@ -using Content.Server._White.Cult.Items.Components; -using Content.Server.Body.Components; -using Content.Server.Body.Systems; -using Content.Server.Chat.Systems; -using Content.Server.Popups; -using Content.Server.Stunnable; -using Content.Shared._White.Chaplain; -using Content.Shared.Popups; -using Content.Shared.StatusEffect; -using Content.Shared.Weapons.Melee.Events; - -namespace Content.Server._White.Cult.Items.Systems; - -public sealed class CultStunHandSystem : EntitySystem -{ - [Dependency] private readonly StunSystem _stun = default!; - [Dependency] private readonly ChatSystem _chat = default!; - [Dependency] private readonly HolyWeaponSystem _holyWeapon = default!; - [Dependency] private readonly PopupSystem _popupSystem = default!; - [Dependency] private readonly BloodstreamSystem _bloodstream = default!; - [Dependency] private readonly StatusEffectsSystem _statusEffects = default!; - - public override void Initialize() - { - base.Initialize(); - - SubscribeLocalEvent(OnHit); - } - - private void OnHit(Entity ent, ref MeleeHitEvent args) - { - if (args.HitEntities.Count == 0) - return; - - var target = args.HitEntities[0]; - var (uid, comp) = ent; - - QueueDel(uid); - Spawn("CultStunFlashEffect", Transform(target).Coordinates); - _chat.TrySendInGameICMessage(args.User, comp.Speech, InGameICChatType.Whisper, false); - if (TryComp(args.User, out BloodstreamComponent? bloodstream)) - _bloodstream.TryModifyBloodLevel(args.User, -10, bloodstream, createPuddle: false); - - if (_holyWeapon.IsHoldingHolyWeapon(target)) - { - _popupSystem.PopupEntity(Loc.GetString("cult-magic-holy"), args.User, args.User, PopupType.MediumCaution); - return; - } - - var halo = HasComp(args.User); - - _statusEffects.TryAddStatusEffect(target, "Muted", halo ? comp.HaloMuteDuration : comp.MuteDuration, true, - "Muted"); - _stun.TryParalyze(target, halo ? comp.HaloDuration : comp.Duration, true); - } -} diff --git a/Content.Server/_White/Cult/Items/Systems/MagicHandSystem.cs b/Content.Server/_White/Cult/Items/Systems/MagicHandSystem.cs new file mode 100644 index 0000000000..1fb79ba403 --- /dev/null +++ b/Content.Server/_White/Cult/Items/Systems/MagicHandSystem.cs @@ -0,0 +1,257 @@ +using System.Linq; +using Content.Server._White.Cult.Items.Components; +using Content.Server.Body.Components; +using Content.Server.Body.Systems; +using Content.Server.Chat.Systems; +using Content.Server.Chemistry.Containers.EntitySystems; +using Content.Server.Hands.Systems; +using Content.Server.Popups; +using Content.Server.Stunnable; +using Content.Shared._White.Chaplain; +using Content.Shared._White.Cult; +using Content.Shared._White.Cult.Components; +using Content.Shared._White.Cult.UI; +using Content.Shared.Chemistry.Components; +using Content.Shared.Damage; +using Content.Shared.Examine; +using Content.Shared.FixedPoint; +using Content.Shared.Fluids.Components; +using Content.Shared.Interaction; +using Content.Shared.Mobs.Systems; +using Content.Shared.Popups; +using Content.Shared.StatusEffect; +using Content.Shared.UserInterface; +using Content.Shared.Weapons.Melee.Events; +using Robust.Server.Audio; +using Robust.Server.GameObjects; +using Robust.Shared.Prototypes; + +namespace Content.Server._White.Cult.Items.Systems; + +public sealed class MagicHandSystem : EntitySystem +{ + [Dependency] private readonly StunSystem _stun = default!; + [Dependency] private readonly ChatSystem _chat = default!; + [Dependency] private readonly HolyWeaponSystem _holyWeapon = default!; + [Dependency] private readonly PopupSystem _popupSystem = default!; + [Dependency] private readonly BloodstreamSystem _bloodstream = default!; + [Dependency] private readonly StatusEffectsSystem _statusEffects = default!; + [Dependency] private readonly DamageableSystem _damageable = default!; + [Dependency] private readonly MobStateSystem _mobState = default!; + [Dependency] private readonly AudioSystem _audio = default!; + [Dependency] private readonly EntityLookupSystem _lookup = default!; + [Dependency] private readonly TransformSystem _transform = default!; + [Dependency] private readonly SolutionContainerSystem _solutionSystem = default!; + [Dependency] private readonly HandsSystem _handsSystem = default!; + [Dependency] private readonly UserInterfaceSystem _ui = default!; + [Dependency] private readonly IPrototypeManager _prototypeManager = default!; + + public override void Initialize() + { + base.Initialize(); + + SubscribeLocalEvent(OnStunHit); + SubscribeLocalEvent(OnRitesHit); + SubscribeLocalEvent(OnInteract); + SubscribeLocalEvent(OnBloodRitesSelected); + SubscribeLocalEvent(OnRitesSelectAttempt); + SubscribeLocalEvent(BeforeRitesSelect); + SubscribeLocalEvent(OnExamine); + } + + private void OnExamine(Entity ent, ref ExaminedEvent args) + { + var user = args.Examiner; + if (user != Transform(ent).ParentUid || !TryComp(user, out CultistComponent? cultist)) + return; + args.PushMarkup(Loc.GetString("cult-rites-examine", ("blood", cultist.RitesBloodAmount))); + } + + private void BeforeRitesSelect(Entity ent, ref BeforeActivatableUIOpenEvent args) + { + if (_ui.TryGetUi(ent, BloodRitesUi.Key, out var bui)) + _ui.SetUiState(bui, new CultistFactoryBUIState(ent.Comp.BloodRites)); + } + + private void OnRitesSelectAttempt(Entity ent, ref ActivatableUIOpenAttemptEvent args) + { + if (!HasComp(args.User)) + args.Cancel(); + } + + private void OnBloodRitesSelected(Entity ent, ref CultistFactoryItemSelectedMessage args) + { + var attachedEntity = args.Session.AttachedEntity; + + if (!TryComp(attachedEntity, out CultistComponent? cultist)) + return; + + if (!_prototypeManager.TryIndex(args.Item, out var prototype)) + return; + + var uid = attachedEntity.Value; + + if (cultist.RitesBloodAmount < prototype.BloodCost) + { + var message = Loc.GetString("cult-rites-no-blood", ("required", prototype.BloodCost), + ("blood", cultist.RitesBloodAmount)); + Popup(message, uid); + return; + } + + Speak(uid, ent.Comp); + Del(ent); + + var success = false; + foreach (var entity in prototype.Item.Select(item => Spawn(item, Transform(uid).Coordinates))) + { + if (_handsSystem.TryPickupAnyHand(uid, entity)) + { + success = true; + continue; + } + + Popup(Loc.GetString("cult-rites-no-hand"), uid); + QueueDel(entity); + break; + } + + if (!success) + return; + + cultist.RitesBloodAmount -= prototype.BloodCost; + } + + private void OnInteract(Entity ent, ref AfterInteractEvent args) + { + if (!args.CanReach || args.Target is not { } target) + return; + + var puddleQuery = GetEntityQuery(); + if (!puddleQuery.HasComp(target)) + return; + + if (!TryComp(args.User, out BloodstreamComponent? bloodstreamComponent) || + !TryComp(args.User, out CultistComponent? cultist)) + return; + + var xform = Transform(target); + + var entitiesInRange = _lookup.GetEntitiesInRange(_transform.GetMapCoordinates(xform), 1.5f); + + var totalBloodAmount = FixedPoint2.Zero; + + foreach (var solutionEntity in entitiesInRange.ToList()) + { + if (!puddleQuery.TryComp(solutionEntity, out var puddleComponent)) + continue; + + if (!_solutionSystem.TryGetSolution(solutionEntity, puddleComponent.SolutionName, out var solution)) + continue; + + var hasBlood = false; + foreach (var solutionContent in solution.Value.Comp.Solution.Contents.ToList() + .Where(solutionContent => solutionContent.Reagent.Prototype == "Blood")) + { + hasBlood = true; + totalBloodAmount += solutionContent.Quantity; + + _bloodstream.TryModifyBloodLevel(args.User, solutionContent.Quantity / 6f, bloodstreamComponent); + _solutionSystem.RemoveReagent((Entity) solution, "Blood", FixedPoint2.MaxValue); + } + + if (hasBlood) + Spawn("CultTileEffect", Transform(solutionEntity).Coordinates); + } + + if (totalBloodAmount <= FixedPoint2.Zero) + return; + + cultist.RitesBloodAmount += totalBloodAmount; + + _audio.PlayPvs(ent.Comp.SuckSound, args.User); + _popupSystem.PopupEntity(Loc.GetString("cult-rites-message", ("blood", cultist.RitesBloodAmount)), + args.User, args.User); + + args.Handled = true; + } + + private void OnRitesHit(Entity ent, ref MeleeHitEvent args) + { + if (args.HitEntities.Count == 0) + return; + + var target = args.HitEntities[0]; + var (uid, comp) = ent; + + QueueDel(uid); + + if (!TryComp(args.User, out CultistComponent? cultist) || !TryComp(target, out DamageableComponent? damageable)) + return; + + if (_mobState.IsDead(target)) + { + Popup(Loc.GetString("cult-rites-dead"), args.User); + return; + } + + if (cultist.RitesBloodAmount <= FixedPoint2.Zero) + { + Popup(Loc.GetString("cult-rites-heal-no-blood"), args.User); + return; + } + + var damage = damageable.Damage; + var totalDamage = damage.GetTotal(); + if (totalDamage <= FixedPoint2.Zero) + { + Popup(Loc.GetString("cult-rites-already-healed"), args.User); + return; + } + + var coef = FixedPoint2.Min(cultist.RitesBloodAmount * comp.HealModifier, totalDamage) / totalDamage; + cultist.RitesBloodAmount = + FixedPoint2.Max(FixedPoint2.Zero, cultist.RitesBloodAmount - totalDamage / comp.HealModifier); + _damageable.TryChangeDamage(target, -damage * coef, true, false, damageable, args.User); + Popup(Loc.GetString("cult-rites-after-heal", ("blood", cultist.RitesBloodAmount)), args.User); + _audio.PlayPvs(comp.HealSound, target); + Speak(args.User, comp); + } + + private void OnStunHit(Entity ent, ref MeleeHitEvent args) + { + if (args.HitEntities.Count == 0) + return; + + var target = args.HitEntities[0]; + var (uid, comp) = ent; + + QueueDel(uid); + Spawn("CultStunFlashEffect", Transform(target).Coordinates); + Speak(args.User, comp); + if (TryComp(args.User, out BloodstreamComponent? bloodstream)) + _bloodstream.TryModifyBloodLevel(args.User, -10, bloodstream, createPuddle: false); + + if (_holyWeapon.IsHoldingHolyWeapon(target)) + { + Popup(Loc.GetString("cult-magic-holy"), args.User, PopupType.MediumCaution); + return; + } + + var halo = HasComp(args.User); + + _statusEffects.TryAddStatusEffect(target, "Muted", halo ? comp.HaloMuteDuration : comp.MuteDuration, true, + "Muted"); + _stun.TryParalyze(target, halo ? comp.HaloDuration : comp.Duration, true); + } + + private void Popup(string msg, EntityUid user, PopupType type = PopupType.Small) + { + _popupSystem.PopupEntity(msg, user, user, type); + } + + private void Speak(EntityUid user, BaseMagicHandComponent comp) + { + _chat.TrySendInGameICMessage(user, comp.Speech, InGameICChatType.Whisper, false); + } +} diff --git a/Content.Server/_White/Cult/Runes/Systems/CultSystem.Actions.cs b/Content.Server/_White/Cult/Runes/Systems/CultSystem.Actions.cs index 4e900eaae4..f1c700316b 100644 --- a/Content.Server/_White/Cult/Runes/Systems/CultSystem.Actions.cs +++ b/Content.Server/_White/Cult/Runes/Systems/CultSystem.Actions.cs @@ -106,15 +106,7 @@ public partial class CultSystem private void OnStun(EntityUid uid, CultistComponent component, CultStunActionEvent args) { - var entity = Spawn("StunHand", Transform(uid).Coordinates); - if (!_handsSystem.TryPickupAnyHand(uid, entity)) - { - _popupSystem.PopupEntity(Loc.GetString("cult-magic-no-empty-hand"), uid, uid); - QueueDel(entity); - _actionsSystem.SetCooldown(args.Action, TimeSpan.FromSeconds(1)); - return; - } - args.Handled = true; + GrantItem(uid, "StunHand", args); } private void OnTeleport(EntityUid uid, CultistComponent component, CultTeleportTargetActionEvent args) @@ -150,61 +142,7 @@ public partial class CultSystem private void OnBloodRites(EntityUid uid, CultistComponent component, CultBloodRitesInstantActionEvent args) { - if (!TryComp(args.Performer, out var bloodstreamComponent)) - return; - - var bruteDamageGroup = _prototypeManager.Index("Brute"); - var burnDamageGroup = _prototypeManager.Index("Burn"); - - var xform = Transform(uid); - - var entitiesInRange = _lookup.GetEntitiesInRange(_transform.GetMapCoordinates(xform), 1.5f); - - FixedPoint2 totalBloodAmount = 0f; - - var breakLoop = false; - foreach (var solutionEntity in entitiesInRange.ToList()) - { - if (breakLoop) - break; - - if (!TryComp(solutionEntity, out var puddleComponent)) - continue; - - if (!_solutionSystem.TryGetSolution(solutionEntity, puddleComponent.SolutionName, out var solution)) - continue; - - foreach (var solutionContent in solution.Value.Comp.Solution.Contents.ToList()) - { - if (solutionContent.Reagent.Prototype != "Blood") - continue; - - totalBloodAmount += solutionContent.Quantity; - - _bloodstreamSystem.TryModifyBloodLevel(uid, solutionContent.Quantity / 6f, bloodstreamComponent); - _solutionSystem.RemoveReagent((Entity) solution, "Blood", FixedPoint2.MaxValue); - - /*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); - - Speak(args); - args.Handled = true; + GrantItem(uid, "RitesHand", args); } private static FixedPoint2 GetMissingBloodValue(BloodstreamComponent bloodstreamComponent) @@ -497,4 +435,17 @@ public partial class CultSystem { _spells.Speak(args, InGameICChatType.Whisper); } + + public void GrantItem(EntityUid uid, string proto, InstantActionEvent args) + { + var entity = Spawn(proto, Transform(uid).Coordinates); + if (!_handsSystem.TryPickupAnyHand(uid, entity)) + { + _popupSystem.PopupEntity(Loc.GetString("cult-magic-no-empty-hand"), uid, uid); + QueueDel(entity); + _actionsSystem.SetCooldown(args.Action, TimeSpan.FromSeconds(1)); + return; + } + args.Handled = true; + } } diff --git a/Content.Server/_White/Cult/Runes/Systems/CultSystem.ConstructsAbilities.cs b/Content.Server/_White/Cult/Runes/Systems/CultSystem.ConstructsAbilities.cs index 401e7fcba6..1b6d0d01b5 100644 --- a/Content.Server/_White/Cult/Runes/Systems/CultSystem.ConstructsAbilities.cs +++ b/Content.Server/_White/Cult/Runes/Systems/CultSystem.ConstructsAbilities.cs @@ -1,4 +1,5 @@ -using Content.Server.GameTicking; +using System.Linq; +using Content.Server.GameTicking; using Content.Server.GameTicking.Rules.Components; using Content.Server.Popups; using Content.Server._White.Cult.GameRule; @@ -46,9 +47,10 @@ public partial class CultSystem private void OnMapInit(EntityUid uid, ConstructComponent component, MapInitEvent args) { - foreach (var id in component.Actions) + foreach (var action in component.Actions.Select(id => _actionsSystem.AddAction(uid, id))) { - component.ActionEntities.Add(_actionsSystem.AddAction(uid, id)); + _actionsSystem.StartUseDelay(action); + component.ActionEntities.Add(action); } } diff --git a/Content.Server/_White/Cult/Runes/Systems/CultSystem.Rune.cs b/Content.Server/_White/Cult/Runes/Systems/CultSystem.Rune.cs index 1c5ceb6e7e..c066b74872 100644 --- a/Content.Server/_White/Cult/Runes/Systems/CultSystem.Rune.cs +++ b/Content.Server/_White/Cult/Runes/Systems/CultSystem.Rune.cs @@ -99,7 +99,7 @@ public sealed partial class CultSystem : EntitySystem SubscribeLocalEvent(OnEmpowerSelected); SubscribeLocalEvent(OnUseInHand); - SubscribeLocalEvent(OnActiveInWorld); + SubscribeLocalEvent(OnActiveInWorld); // UI SubscribeLocalEvent(OnRuneDrawAttempt); @@ -396,19 +396,19 @@ public sealed partial class CultSystem : EntitySystem if (ev.Result) { - OnAfterInvoke(uid, cultists); + OnAfterInvoke(uid, cultists, ev.InvokePhraseOverride); } } - private void OnAfterInvoke(EntityUid rune, HashSet cultists) + private void OnAfterInvoke(EntityUid rune, HashSet cultists, string? invokePhraseOverride = null) { if (!_entityManager.TryGetComponent(rune, out var component)) return; foreach (var cultist in cultists) { - _chat.TrySendInGameICMessage(cultist, component.InvokePhrase, component.InvokeChatType, false, false, null, - null, null, false); + _chat.TrySendInGameICMessage(cultist, invokePhraseOverride ?? component.InvokePhrase, + component.InvokeChatType, false, false, null, null, null, false); } } @@ -455,16 +455,19 @@ public sealed partial class CultSystem : EntitySystem !HasComp(victim.Value)) { result = Convert(uid, victim.Value, args.User, args.Cultists); + args.InvokePhraseOverride = "Mah'weyh pleggh at e'ntrath!"; } else { result = Sacrifice(uid, victim.Value, args.User, args.Cultists); + args.InvokePhraseOverride = "Barhah hra zar'garis!"; } } else { // Жертва мертва, выполняется альтернативное действие result = SacrificeNonObjectiveDead(uid, victim.Value, args.User, args.Cultists); + args.InvokePhraseOverride = "Barhah hra zar'garis!"; } args.Result = result; @@ -1217,13 +1220,14 @@ public sealed partial class CultSystem : EntitySystem * Empower rune start ---- */ - private void OnActiveInWorld(EntityUid uid, CultEmpowerComponent component, ActivateInWorldEvent args) + private void OnActiveInWorld(EntityUid uid, CultEmpowerComponent component, CultRuneInvokeEvent args) { if (!component.IsRune || !TryComp(args.User, out _) || !TryComp(args.User, out var actor)) return; - _ui.TryOpen(uid, CultEmpowerUiKey.Key, actor.PlayerSession); + args.Result = !_ui.SessionHasOpenUi(uid, CultEmpowerUiKey.Key, actor.PlayerSession) && + _ui.TryOpen(uid, CultEmpowerUiKey.Key, actor.PlayerSession); } private void OnUseInHand(EntityUid uid, CultEmpowerComponent component, UseInHandEvent args) @@ -1377,6 +1381,7 @@ public sealed partial class CultSystem : EntitySystem var shard = _entityManager.SpawnEntity("SoulShard", transform.Value); _mindSystem.TransferTo(mindComponent.Mind.Value, shard); + _mindSystem.UnVisit(mindComponent.Mind.Value); _bodySystem.GibBody(target); diff --git a/Content.Server/_White/Cult/Runes/Systems/CultSystem.Verb.cs b/Content.Server/_White/Cult/Runes/Systems/CultSystem.Verb.cs index 1cd8efc886..0633eb0457 100644 --- a/Content.Server/_White/Cult/Runes/Systems/CultSystem.Verb.cs +++ b/Content.Server/_White/Cult/Runes/Systems/CultSystem.Verb.cs @@ -2,7 +2,6 @@ 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; @@ -17,7 +16,6 @@ public sealed partial class CultSystem SubscribeLocalEvent(OnCultistEmpowerSelected); SubscribeLocalEvent(OnCultistEmpowerRemove); SubscribeLocalEvent(OnSpellCreated); - SubscribeLocalEvent(OnBloodRitesSelected); } private void OnCultistEmpowerRemove(Entity ent, ref CultEmpowerRemoveBuiMessage args) @@ -100,24 +98,8 @@ public sealed partial class CultSystem } }; - var bloodRitesVerb = new Verb - { - Text = Loc.GetString("verb-blood-rites-text"), - Message = Loc.GetString("verb-blood-rites-message", ("blood", ent.Comp.RitesBloodAmount)), - Category = VerbCategory.Cult, - Act = () => - { - if (!_ui.TryGetUi(ent, BloodRitesUi.Key, out var bui)) - return; - - _ui.SetUiState(bui, new CultistFactoryBUIState(ent.Comp.BloodRites)); - _ui.OpenUi(bui, actor.PlayerSession); - } - }; - args.Verbs.Add(createSpellVerb); args.Verbs.Add(removeSpellVerb); - args.Verbs.Add(bloodRitesVerb); } private void RemoveSpell(Entity ent, ICommonSession session) @@ -130,36 +112,4 @@ public sealed partial class CultSystem _ui.TryOpen(ent, CultEmpowerRemoveUiKey.Key, session); } - - private void OnBloodRitesSelected(Entity ent, ref CultistFactoryItemSelectedMessage args) - { - if (!_prototypeManager.TryIndex(args.Item, out var prototype)) - return; - - if (ent.Comp.RitesBloodAmount < prototype.BloodCost) - { - var message = Loc.GetString("verb-blood-rites-no-blood", ("required", prototype.BloodCost), - ("blood", ent.Comp.RitesBloodAmount)); - _popupSystem.PopupEntity(message, ent, ent); - return; - } - - var success = false; - foreach (var item in prototype.Item) - { - var entity = Spawn(item, Transform(ent).Coordinates); - if (_handsSystem.TryPickupAnyHand(ent, entity)) - { - success = true; - continue; - } - - _popupSystem.PopupEntity(Loc.GetString("verb-blood-rites-no-hand"), ent, ent); - QueueDel(entity); - break; - } - - if (success) - ent.Comp.RitesBloodAmount -= prototype.BloodCost; - } } diff --git a/Content.Shared/_White/Cult/Components/CultistComponent.cs b/Content.Shared/_White/Cult/Components/CultistComponent.cs index a61f3a5f83..006d2a000b 100644 --- a/Content.Shared/_White/Cult/Components/CultistComponent.cs +++ b/Content.Shared/_White/Cult/Components/CultistComponent.cs @@ -3,7 +3,6 @@ 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; @@ -51,13 +50,6 @@ public sealed partial class CultistComponent : Component CultSummonCombatEquipmentAction, CultStunAction, EmpPulseAction, ConcealPresenceAction, CultShadowShacklesAction }; - [DataField("bloodRites", customTypeSerializer: typeof(PrototypeIdListSerializer))] - public List BloodRites = new () - { - "FactoryCultBloodSpear", - "FactoryCultBloodBarrage" - }; - [ViewVariables, NonSerialized] public Entity? BloodSpear; diff --git a/Content.Shared/_White/Cult/Runes/CultRuneInvokeEvent.cs b/Content.Shared/_White/Cult/Runes/CultRuneInvokeEvent.cs index 422057be06..db07f1d4e9 100644 --- a/Content.Shared/_White/Cult/Runes/CultRuneInvokeEvent.cs +++ b/Content.Shared/_White/Cult/Runes/CultRuneInvokeEvent.cs @@ -7,6 +7,8 @@ public sealed class CultRuneInvokeEvent : EntityEventArgs public HashSet Cultists { get; } public bool Result { get; set; } + public string? InvokePhraseOverride { get; set; } = null; + public CultRuneInvokeEvent(EntityUid rune, EntityUid user, HashSet cultists) { Rune = rune; diff --git a/Resources/Audio/White/Cult/rites.ogg b/Resources/Audio/White/Cult/rites.ogg new file mode 100644 index 0000000000..486940a414 Binary files /dev/null and b/Resources/Audio/White/Cult/rites.ogg differ diff --git a/Resources/Locale/ru-RU/_white/cult/entities.ftl b/Resources/Locale/ru-RU/_white/cult/entities.ftl index 1c16bb87ab..6f0853d15c 100644 --- a/Resources/Locale/ru-RU/_white/cult/entities.ftl +++ b/Resources/Locale/ru-RU/_white/cult/entities.ftl @@ -8,6 +8,9 @@ ent-SoulShardGhost = камень душ ent-StunHand = оглушающая аура .desc = Оглушит и обезмолвит жертву при ударе. +ent-RitesHand = аура кровавых обрядов + .desc = Впитывает кровь из всего, к чему прикасается. При ударе по культистам и конструктам может исцелить их. Используйте в руке, чтобы провести продвинутый обряд. + ent-ShadowShackles = теневые оковы .desc = Оковы, сковывающие запястья с помощью зловещей магии. diff --git a/Resources/Locale/ru-RU/_white/cult/messages.ftl b/Resources/Locale/ru-RU/_white/cult/messages.ftl index 560d37bedf..28ef024ebf 100644 --- a/Resources/Locale/ru-RU/_white/cult/messages.ftl +++ b/Resources/Locale/ru-RU/_white/cult/messages.ftl @@ -1,3 +1,12 @@ -cult-stun-component-extra-message = [color=darkgray]Выбросите ауру, чтобы навсегда избавиться от неё.[/color] +cult-hand-component-extra-message = [color=darkgray]Выбросите ауру, чтобы навсегда избавиться от неё.[/color] cult-magic-holy = Сила священного предмета в руках цели препятствует магии! cult-magic-no-empty-hand = Вам нужна свободная рука для использования заклинания! + +cult-rites-examine = [color=darkred]Всего высосано крови: { $blood }.[/color] +cult-rites-message = Всего высосано крови: { $blood }. +cult-rites-dead = Цель мертва. +cult-rites-heal-no-blood = Нет крови для лечения. +cult-rites-already-healed = Он не имеет повреждений. +cult-rites-after-heal = Осталось крови: { $blood }. +cult-rites-no-blood = Для обряда необходимо высосать { $required } крови с помощью заклинания Кровавые Обряды. Всего высосано: { $blood }. +cult-rites-no-hand = Вам нужна свободная рука для обряда. diff --git a/Resources/Locale/ru-RU/_white/cult/verb.ftl b/Resources/Locale/ru-RU/_white/cult/verb.ftl index 08f35fca69..cb1f9c275b 100644 --- a/Resources/Locale/ru-RU/_white/cult/verb.ftl +++ b/Resources/Locale/ru-RU/_white/cult/verb.ftl @@ -7,8 +7,3 @@ verb-spell-create-too-much = Начертите руну могущества, verb-spell-remove-text = Удалить заклинание крови verb-spell-remove-message = Убрать любое из созданных заклинаний крови. verb-spell-remove-no-spells = Заклинания крови отсутствуют. - -verb-blood-rites-text = Кровавые обряды -verb-blood-rites-message = Всего высосано крови: { $blood }. -verb-blood-rites-no-blood = Для обряда необходимо высосать { $required } крови с помощью заклинания Кровавые Обряды. Всего высосано: { $blood }. -verb-blood-rites-no-hand = Вам нужна свободная рука для обряда. diff --git a/Resources/Prototypes/Entities/Mobs/Species/base.yml b/Resources/Prototypes/Entities/Mobs/Species/base.yml index 49d4b49dd4..4999048e76 100644 --- a/Resources/Prototypes/Entities/Mobs/Species/base.yml +++ b/Resources/Prototypes/Entities/Mobs/Species/base.yml @@ -221,8 +221,6 @@ type: SpellSelectorBUI - key: enum.CultEmpowerRemoveUiKey.Key type: SpellRemoverBUI - - key: enum.BloodRitesUi.Key - type: CultistFactoryBUI # WD-EDIT END - type: Puller - type: Butcherable diff --git a/Resources/Prototypes/_White/Actions/cult_actions.yml b/Resources/Prototypes/_White/Actions/cult_actions.yml index 864f0f93cd..415007c8eb 100644 --- a/Resources/Prototypes/_White/Actions/cult_actions.yml +++ b/Resources/Prototypes/_White/Actions/cult_actions.yml @@ -123,7 +123,6 @@ sprite: /Textures/White/Cult/actions_cult.rsi state: blood_rites event: !type:CultBloodRitesInstantActionEvent - speech: "Fel'th Dol Ab'orod!" charges: 5 temporary: true removeOnNoCharges: true diff --git a/Resources/Prototypes/_White/Entities/Cult/Effects/effects.yml b/Resources/Prototypes/_White/Entities/Cult/Effects/effects.yml index 885844e63b..5282cb3c7b 100644 --- a/Resources/Prototypes/_White/Entities/Cult/Effects/effects.yml +++ b/Resources/Prototypes/_White/Entities/Cult/Effects/effects.yml @@ -1,13 +1,11 @@ - type: entity - id: CultTileSpawnEffect - name: Sparkle + id: CultTileEffect + name: cult tile effect placement: mode: SnapgridCenter components: - # Animation is like 3 something seconds so we just need to despawn it before then. - type: TimedDespawn lifetime: 0.5 - - type: EvaporationSparkle - type: Transform noRot: true anchored: true @@ -19,6 +17,14 @@ netsync: false drawdepth: FloorObjects color: "#FF0000" + +- type: entity + parent: CultTileEffect + id: CultTileSpawnEffect + name: cult tile glow + placement: + mode: SnapgridCenter + components: - type: PointLight color: "#FF0000" @@ -26,10 +32,8 @@ id: CultTeleportInEffect name: Teleport in components: - # Animation is like 3 something seconds so we just need to despawn it before then. - type: TimedDespawn lifetime: 0.8 - - type: EvaporationSparkle - type: Transform noRot: true anchored: true @@ -47,10 +51,8 @@ id: CultTeleportOutEffect name: Teleport out components: - # Animation is like 3 something seconds so we just need to despawn it before then. - type: TimedDespawn lifetime: 0.8 - - type: EvaporationSparkle - type: Transform noRot: true anchored: true @@ -70,7 +72,6 @@ components: - type: TimedDespawn lifetime: 1 - - type: EvaporationSparkle - type: Transform noRot: true anchored: true @@ -90,7 +91,6 @@ components: - type: TimedDespawn lifetime: 1 - - type: EvaporationSparkle - type: Transform noRot: true anchored: true diff --git a/Resources/Prototypes/_White/Entities/Cult/Items/hands.yml b/Resources/Prototypes/_White/Entities/Cult/Items/hands.yml new file mode 100644 index 0000000000..d21ac1879d --- /dev/null +++ b/Resources/Prototypes/_White/Entities/Cult/Items/hands.yml @@ -0,0 +1,74 @@ +- type: entity + parent: BaseItem + id: BaseCultHand + abstract: true + noSpawn: true + name: magical aura + description: A sinister looking aura that distorts the flow of reality around it. + components: + - type: Item + size: Ginormous + - type: MeleeWeapon + canHeavyAttack: false + canMiss: false + attackRate: 2 + wideAnimationRotation: 180 + damage: + types: + Heat: 0 + - type: DeleteOnDropAttempt + message: cult-hand-component-extra-message + - type: Unremoveable + deleteOnDrop: true + - type: CultItem + canPickUp: false + +- type: entity + parent: BaseCultHand + id: StunHand + name: stunning aura + description: Will stun and mute a weak-minded victim on hit. + components: + - type: Sprite + sprite: White/Cult/Entities/stun.rsi + state: icon + - type: Item + sprite: White/Cult/Entities/stun.rsi + - type: MeleeWeapon + canAttackSelf: false + attackWhitelist: + components: + - StatusEffects + attackBlacklist: + components: + - Cultist + - Construct + - type: CultStunHand + speech: "Fuu ma'jin!" + +- type: entity + parent: BaseCultHand + id: RitesHand + name: blood rite aura + description: Absorbs blood from anything you touch. Touching cultists and constructs can heal them. Use in-hand to cast an advanced rite. + components: + - type: Sprite + sprite: White/Cult/Entities/rites.rsi + state: icon + - type: Item + sprite: White/Cult/Entities/rites.rsi + - type: MeleeWeapon + attackWhitelist: + components: + - Cultist + - Construct + - type: CultRitesHand + speech: "Fel'th Dol Ab'orod!" + - type: ActivatableUI + key: enum.BloodRitesUi.Key + inHandsOnly: true + closeOnHandDeselect: true + - type: UserInterface + interfaces: + - key: enum.BloodRitesUi.Key + type: CultistFactoryBUI diff --git a/Resources/Prototypes/_White/Entities/Cult/Items/stun_hand.yml b/Resources/Prototypes/_White/Entities/Cult/Items/stun_hand.yml deleted file mode 100644 index c73620a53f..0000000000 --- a/Resources/Prototypes/_White/Entities/Cult/Items/stun_hand.yml +++ /dev/null @@ -1,34 +0,0 @@ -- type: entity - parent: BaseItem - id: StunHand - name: stunning aura - description: Will stun and mute a weak-minded victim on hit. - components: - - type: Sprite - sprite: White/Cult/Entities/stun.rsi - state: icon - - type: Item - size: Ginormous - sprite: White/Cult/Entities/stun.rsi - - type: MeleeWeapon - canHeavyAttack: false - canAttackSelf: false - canMiss: false - attackWhitelist: - components: - - StatusEffects - attackBlacklist: - components: - - Cultist - - Construct - wideAnimationRotation: 180 - damage: - types: - Heat: 0 - - type: CultStunHand - - type: DeleteOnDropAttempt - message: cult-stun-component-extra-message - - type: Unremoveable - deleteOnDrop: true - - type: CultItem - canPickUp: false diff --git a/Resources/Prototypes/_White/Entities/Cult/Runes/cult_runes.yml b/Resources/Prototypes/_White/Entities/Cult/Runes/cult_runes.yml index 262acf8e10..f185afda37 100644 --- a/Resources/Prototypes/_White/Entities/Cult/Runes/cult_runes.yml +++ b/Resources/Prototypes/_White/Entities/Cult/Runes/cult_runes.yml @@ -30,6 +30,7 @@ - type: Appearance - type: CultRuneBase invokePhrase: "Qu'laris ver'don, thal'sorin mik'thar!" + gatherInvokers: false - type: CultRuneBuff - type: CultRune - type: Concealable @@ -47,6 +48,8 @@ color: '#F80000' - type: Appearance - type: CultRuneBase + invokePhrase: "H'drak v'loso, mir'kanas verbot!" + gatherInvokers: false - type: CultEmpower isRune: true - type: UserInterface @@ -55,7 +58,6 @@ type: SpellSelectorBUI - type: CultRune - type: Concealable - examinableWhileConcealed: true - type: entity parent: CollideRune @@ -91,7 +93,6 @@ - type: Appearance - type: CultRuneBase invokePhrase: "N'ath reth sh'yro eth d'rekkathnor!" - gatherInvokers: true - type: CultRuneSummoning - type: CultRune - type: Concealable diff --git a/Resources/Prototypes/_White/Entities/Cult/constructs.yml b/Resources/Prototypes/_White/Entities/Cult/constructs.yml index 44071134e3..e28c658f3d 100644 --- a/Resources/Prototypes/_White/Entities/Cult/constructs.yml +++ b/Resources/Prototypes/_White/Entities/Cult/constructs.yml @@ -7,8 +7,8 @@ context: "human" - type: InputMover - type: MovementSpeedModifier - baseWalkSpeed: 2.5 - baseSprintSpeed: 2.5 + baseWalkSpeed: 3 + baseSprintSpeed: 3 - type: DamageOnHighSpeedImpact damage: types: @@ -61,6 +61,8 @@ - type: ContentEye - type: Actions - type: Hands + - type: Puller + needsHands: false - type: ShowCultHud - type: IsDeadIC - type: NightVision @@ -138,8 +140,6 @@ baseWalkSpeed: 3 baseSprintSpeed: 3 - type: MovementIgnoreGravity - - type: Puller - needsHands: false - type: MeleeWeapon canHeavyAttack: false hidden: true @@ -202,8 +202,6 @@ 150: Dead - type: Sprite state: harvester - - type: Puller - needsHands: false - type: MeleeWeapon canHeavyAttack: false hidden: true diff --git a/Resources/Prototypes/_White/Mobs/Species/harpy.yml b/Resources/Prototypes/_White/Mobs/Species/harpy.yml index 1f16f43833..351a45d5ae 100644 --- a/Resources/Prototypes/_White/Mobs/Species/harpy.yml +++ b/Resources/Prototypes/_White/Mobs/Species/harpy.yml @@ -45,8 +45,6 @@ type: SpellSelectorBUI - key: enum.CultEmpowerRemoveUiKey.Key type: SpellRemoverBUI - - key: enum.BloodRitesUi.Key - type: CultistFactoryBUI # WD-EDIT END - type: Sprite scale: 0.9, 0.9 diff --git a/Resources/Textures/White/Cult/Entities/rites.rsi/icon.png b/Resources/Textures/White/Cult/Entities/rites.rsi/icon.png new file mode 100644 index 0000000000..e7df415ac8 Binary files /dev/null and b/Resources/Textures/White/Cult/Entities/rites.rsi/icon.png differ diff --git a/Resources/Textures/White/Cult/Entities/rites.rsi/inhand-left.png b/Resources/Textures/White/Cult/Entities/rites.rsi/inhand-left.png new file mode 100644 index 0000000000..516c310227 Binary files /dev/null and b/Resources/Textures/White/Cult/Entities/rites.rsi/inhand-left.png differ diff --git a/Resources/Textures/White/Cult/Entities/rites.rsi/inhand-right.png b/Resources/Textures/White/Cult/Entities/rites.rsi/inhand-right.png new file mode 100644 index 0000000000..a159a468a0 Binary files /dev/null and b/Resources/Textures/White/Cult/Entities/rites.rsi/inhand-right.png differ diff --git a/Resources/Textures/White/Cult/Entities/rites.rsi/meta.json b/Resources/Textures/White/Cult/Entities/rites.rsi/meta.json new file mode 100644 index 0000000000..489466f74a --- /dev/null +++ b/Resources/Textures/White/Cult/Entities/rites.rsi/meta.json @@ -0,0 +1,66 @@ +{ + "version": 1, + "license": "CC-BY-SA-3.0", + "copyright": "Taken from tgstation at https://github.com/tgstation/tgstation/pull/49264/commits/d0dffe7ca643db2624424fdcebf45863f85c0448", + "size": { + "x": 32, + "y": 32 + }, + "states": [ + { + "name": "icon" + }, + { + "name": "inhand-left", + "directions": 4, + "delays": [ + [ + 0.1, + 0.1, + 0.1 + ], + [ + 0.1, + 0.1, + 0.1 + ], + [ + 0.1, + 0.1, + 0.1 + ], + [ + 0.1, + 0.1, + 0.1 + ] + ] + }, + { + "name": "inhand-right", + "directions": 4, + "delays": [ + [ + 0.1, + 0.1, + 0.1 + ], + [ + 0.1, + 0.1, + 0.1 + ], + [ + 0.1, + 0.1, + 0.1 + ], + [ + 0.1, + 0.1, + 0.1 + ] + ] + } + ] +} diff --git a/Resources/Textures/White/Cult/Entities/stun.rsi/inhand-left.png b/Resources/Textures/White/Cult/Entities/stun.rsi/inhand-left.png index 19acec50cf..1c2504631f 100644 Binary files a/Resources/Textures/White/Cult/Entities/stun.rsi/inhand-left.png and b/Resources/Textures/White/Cult/Entities/stun.rsi/inhand-left.png differ diff --git a/Resources/Textures/White/Cult/Entities/stun.rsi/inhand-right.png b/Resources/Textures/White/Cult/Entities/stun.rsi/inhand-right.png index 12410f5e0b..1c05259688 100644 Binary files a/Resources/Textures/White/Cult/Entities/stun.rsi/inhand-right.png and b/Resources/Textures/White/Cult/Entities/stun.rsi/inhand-right.png differ