using Content.Shared._DV.Psionics.Components; using Content.Shared._DV.Psionics.Events; using Content.Shared.Jittering; using Content.Shared.Popups; using Content.Shared.Psionics.Glimmer; using Content.Shared.Speech.EntitySystems; using Content.Shared.Stunnable; using JetBrains.Annotations; using Robust.Shared.Random; namespace Content.Shared._DV.Psionics.Systems; /// /// The system to deal with all psionics. Each part of the System is in a subsystem. /// public abstract partial class SharedPsionicSystem : EntitySystem { [Dependency] protected readonly IRobustRandom Random = default!; [Dependency] protected readonly GlimmerSystem Glimmer = default!; [Dependency] private readonly SharedJitteringSystem _jittering = default!; [Dependency] private readonly EntityLookupSystem _lookup = default!; [Dependency] protected readonly SharedPopupSystem Popup = default!; [Dependency] private readonly SharedStunSystem _stun = default!; [Dependency] private readonly SharedStutteringSystem _stuttering = default!; protected EntityQuery PotentialQuery; protected EntityQuery PsionicQuery; public override void Initialize() { base.Initialize(); PotentialQuery = GetEntityQuery(); PsionicQuery = GetEntityQuery(); InitializeDetection(); InitializeStatusEffects(); InitializeInvisibility(); InitializeItems(); } /// /// Call this when you want to mindbreak an entity, causing them to lose all psionic abilities. /// /// The entity to be mindbroken. /// Whether this should stun the entity. /// Whether this should remove unremovable psionics too. /// Zombies shouldn't be stunned on zombification, but also lose ALL psionic abilities.> [PublicAPI] public void MindBreakEntity(Entity psionic, bool stun = true, bool force = false) { if (!Resolve(psionic, ref psionic.Comp, logMissing: false)) return; var ev = new PsionicMindBrokenEvent(force); RaiseLocalEvent(psionic, ref ev); // If no abilities got removed, refrain from doing anything further. Nothing ever happens. if (!ev.Success) return; // Reduce glimmer a bit. Glimmer.Glimmer -= Random.Next(50, 70); // Stun if stun is desired. Zombies shouldn't get stunned on zombification. if (stun && psionic.Comp.StunOnRemoval) { _stuttering.DoStutter(psionic, TimeSpan.FromMinutes(1), false); _stun.TryKnockdown(psionic.Owner, TimeSpan.FromSeconds(5), false, drop: false); _jittering.DoJitter(psionic, TimeSpan.FromSeconds(5), false); } // If all were removed, remove the psionic component. if (!ev.AllRemoved) { Popup.PopupClient(Loc.GetString("psionic-partly-mindbrokenpsionic-partly-mindbroken"), psionic, PopupType.Medium); return; } RemComp(psionic); GrantPsionicRoll(psionic.Owner); Popup.PopupClient(Loc.GetString("psionic-mindbroken"), psionic, PopupType.Medium); } // TODO: Fix prediction issues. When calling from a server side, there is no popups. // Introducing server-side popups will cause them to be spammed client-side. // PopupPredicted() cannot be used or it'll not show any popups clientside when called serverside. /// /// Whether the target can be targeted by psionic influence. /// /// The target of the psionic ability. /// Whether the target needs to be a potential psionic to be eligible. /// Whether it should show popups. /// Whether the psionic influence originates from an entity. /// Returns true if targetable, false otherwise. Will handle popups itself. [PublicAPI] public bool CanBeTargeted(EntityUid target, bool ignorePsionicRequirement = false, bool showPopup = true, EntityUid? hasAggressor = null) { // Normal abilities cannot target Borgs and Simplemobs that aren't psionic. if (!PotentialQuery.HasComp(target) && !PsionicQuery.HasComp(target) && !ignorePsionicRequirement) return false; var ev = new TargetedByPsionicPowerEvent(); RaiseLocalEvent(target, ref ev); if (!showPopup || !ev.IsShielded) return !ev.IsShielded; Popup.PopupEntity(Loc.GetString("psionic-shielded-from-attempt"), target, target, PopupType.MediumCaution); if (hasAggressor is { } aggressor) { var message = Loc.GetString("psionic-cannot-target-shielded"); Popup.PopupClient(message, aggressor, aggressor, PopupType.SmallCaution); } return !ev.IsShielded; } /// /// Whether the entity can use psionic abilities. /// /// The entity attempting to use psionic abilities. /// Returns true if yes, false otherwise. public bool CanUsePsionicAbility(EntityUid psionic) { var ev = new PsionicPowerUseAttemptEvent(); RaiseLocalEvent(psionic, ref ev); return ev.CanUsePower; } }