diff --git a/Content.Server/DeltaV/Xenoarchaeology/XenoArtifacts/Effects/Components/GlimmerArtifactComponent.cs b/Content.Server/DeltaV/Xenoarchaeology/XenoArtifacts/Effects/Components/GlimmerArtifactComponent.cs new file mode 100644 index 00000000000..a096fc354a0 --- /dev/null +++ b/Content.Server/DeltaV/Xenoarchaeology/XenoArtifacts/Effects/Components/GlimmerArtifactComponent.cs @@ -0,0 +1,24 @@ +using Content.Server.DeltaV.Xenoarchaeology.XenoArtifacts.Effects.Systems; +using Content.Shared.Destructible.Thresholds; + +namespace Content.Server.DeltaV.Xenoarchaeology.XenoArtifacts.Effects.Components; + +/// +/// Raises or lowers glimmer when this artifact is triggered. +/// +[RegisterComponent, Access(typeof(GlimmerArtifactSystem))] +public sealed partial class GlimmerArtifactComponent : Component +{ + /// + /// If glimmer is not in this range it won't do anything. + /// Prevents the trigger being too extreme or too beneficial. + /// + [DataField(required: true)] + public MinMax Range; + + /// + /// Number to add to glimmer when triggering. + /// + [DataField(required: true)] + public int Change; +} diff --git a/Content.Server/DeltaV/Xenoarchaeology/XenoArtifacts/Effects/Components/PsionicProducingArtifactComponent.cs b/Content.Server/DeltaV/Xenoarchaeology/XenoArtifacts/Effects/Components/PsionicProducingArtifactComponent.cs new file mode 100644 index 00000000000..b4d58c9913e --- /dev/null +++ b/Content.Server/DeltaV/Xenoarchaeology/XenoArtifacts/Effects/Components/PsionicProducingArtifactComponent.cs @@ -0,0 +1,22 @@ +using Content.Server.DeltaV.Xenoarchaeology.XenoArtifacts.Effects.Systems; + +namespace Content.Server.DeltaV.Xenoarchaeology.XenoArtifacts.Effects.Components; + +/// +/// Makes people in a radius around it psionic when triggered. +/// +[RegisterComponent, Access(typeof(PsionicProducingArtifactSystem))] +public sealed partial class PsionicProducingArtifactComponent : Component +{ + /// + /// Range to look for potential psionics in. + /// + [DataField(required: true)] + public float Range; + + /// + /// Number of times this node can trigger before it switches to doing nothing. + /// + [DataField] + public int Limit = 1; +} diff --git a/Content.Server/DeltaV/Xenoarchaeology/XenoArtifacts/Effects/Systems/GlimmerArtifactSystem.cs b/Content.Server/DeltaV/Xenoarchaeology/XenoArtifacts/Effects/Systems/GlimmerArtifactSystem.cs new file mode 100644 index 00000000000..059e1a8cc92 --- /dev/null +++ b/Content.Server/DeltaV/Xenoarchaeology/XenoArtifacts/Effects/Systems/GlimmerArtifactSystem.cs @@ -0,0 +1,27 @@ +using Content.Server.DeltaV.Xenoarchaeology.XenoArtifacts.Effects.Components; +using Content.Server.Xenoarchaeology.XenoArtifacts.Events; +using Content.Shared.Psionics.Glimmer; + +namespace Content.Server.DeltaV.Xenoarchaeology.XenoArtifacts.Effects.Systems; + +public sealed class GlimmerArtifactSystem : EntitySystem +{ + [Dependency] private readonly GlimmerSystem _glimmer = default!; + + public override void Initialize() + { + base.Initialize(); + + SubscribeLocalEvent(OnActivated); + } + + private void OnActivated(Entity ent, ref ArtifactActivatedEvent args) + { + var range = ent.Comp.Range; + var current = _glimmer.Glimmer; + if (current < range.Min || current > range.Max) + return; + + _glimmer.Glimmer += ent.Comp.Change; + } +} diff --git a/Content.Server/DeltaV/Xenoarchaeology/XenoArtifacts/Effects/Systems/PsionicProducingArtifactSystem.cs b/Content.Server/DeltaV/Xenoarchaeology/XenoArtifacts/Effects/Systems/PsionicProducingArtifactSystem.cs new file mode 100644 index 00000000000..d77c00134cf --- /dev/null +++ b/Content.Server/DeltaV/Xenoarchaeology/XenoArtifacts/Effects/Systems/PsionicProducingArtifactSystem.cs @@ -0,0 +1,38 @@ +using Content.Server.DeltaV.Xenoarchaeology.XenoArtifacts.Effects.Components; +using Content.Server.Xenoarchaeology.XenoArtifacts; +using Content.Server.Xenoarchaeology.XenoArtifacts.Events; +using Content.Server.Psionics; + +public sealed class PsionicProducingArtifactSystem : EntitySystem +{ + [Dependency] private readonly ArtifactSystem _artifact = default!; + [Dependency] private readonly EntityLookupSystem _lookup = default!; + [Dependency] private readonly PsionicsSystem _psionics = default!; + + public const string NodeDataPsionicAmount = "nodeDataPsionicAmount"; + + public override void Initialize() + { + base.Initialize(); + + SubscribeLocalEvent(OnActivated); + } + + private void OnActivated(Entity ent, ref ArtifactActivatedEvent args) + { + var (uid, comp) = ent; + if (!_artifact.TryGetNodeData(uid, NodeDataPsionicAmount, out int amount)) + amount = 0; + + if (amount >= comp.Limit) + return; + + var coords = Transform(uid).Coordinates; + foreach (var target in _lookup.GetEntitiesInRange(coords, comp.Range)) + { + _psionics.TryMakePsionic(target); + } + + _artifact.SetNodeData(uid, NodeDataPsionicAmount, amount + 1); + } +} diff --git a/Content.Server/DeltaV/Xenoarchaeology/XenoArtifacts/Triggers/Components/ArtifactMetapsionicTriggerComponent.cs b/Content.Server/DeltaV/Xenoarchaeology/XenoArtifacts/Triggers/Components/ArtifactMetapsionicTriggerComponent.cs new file mode 100644 index 00000000000..7ac1d63e6ad --- /dev/null +++ b/Content.Server/DeltaV/Xenoarchaeology/XenoArtifacts/Triggers/Components/ArtifactMetapsionicTriggerComponent.cs @@ -0,0 +1,8 @@ +namespace Content.Server.DeltaV.Xenoarchaeology.XenoArtifacts.Triggers.Components; + +/// +/// Triggers if a psionic power is used nearby. +/// Requires MetapsionicPowerComponent to be added too. +/// +[RegisterComponent] +public sealed partial class ArtifactMetapsionicTriggerComponent : Component; diff --git a/Content.Server/DeltaV/Xenoarchaeology/XenoArtifacts/Triggers/Systems/ArtifactMetapsionicTriggerSystem.cs b/Content.Server/DeltaV/Xenoarchaeology/XenoArtifacts/Triggers/Systems/ArtifactMetapsionicTriggerSystem.cs new file mode 100644 index 00000000000..8d9a216d658 --- /dev/null +++ b/Content.Server/DeltaV/Xenoarchaeology/XenoArtifacts/Triggers/Systems/ArtifactMetapsionicTriggerSystem.cs @@ -0,0 +1,22 @@ +using Content.Server.DeltaV.Xenoarchaeology.XenoArtifacts.Triggers.Components; +using Content.Server.Xenoarchaeology.XenoArtifacts.Triggers.Systems; +using Content.Shared.Abilities.Psionics; + +namespace Content.Server.Xenoarchaeology.XenoArtifacts.Triggers.Systems; + +public sealed class ArtifactMetapsionicTriggerSystem : EntitySystem +{ + [Dependency] private readonly ArtifactSystem _artifact = default!; + + public override void Initialize() + { + base.Initialize(); + + SubscribeLocalEvent(OnPowerDetected); + } + + private void OnPowerDetected(Entity ent, ref PsionicPowerDetectedEvent args) + { + _artifact.TryActivateArtifact(ent); + } +} diff --git a/Content.Server/Nyanotrasen/Psionics/PsionicsSystem.cs b/Content.Server/Nyanotrasen/Psionics/PsionicsSystem.cs index 889a0d27225..6519d519aa9 100644 --- a/Content.Server/Nyanotrasen/Psionics/PsionicsSystem.cs +++ b/Content.Server/Nyanotrasen/Psionics/PsionicsSystem.cs @@ -128,21 +128,31 @@ private void OnStamHit(EntityUid uid, AntiPsionicWeaponComponent component, Stam args.FlatModifier += component.PsychicStaminaDamage; } - public void RollPsionics(EntityUid uid, PotentialPsionicComponent component, bool applyGlimmer = true, float multiplier = 1f) + /// + /// Makes the entity psionic if it is possible. + /// Ignores rolling and rerolling prevention. + /// + public bool TryMakePsionic(Entity ent) { - if (HasComp(uid)) - return; + if (HasComp(ent)) + return false; if (!_cfg.GetCVar(CCVars.PsionicRollsEnabled)) - return; + return false; + + var warn = CompOrNull(ent)?.Warn ?? true; + _psionicAbilitiesSystem.AddPsionics(ent, warn); + return true; + } + + public void RollPsionics(EntityUid uid, PotentialPsionicComponent component, bool applyGlimmer = true, float multiplier = 1f) + { var chance = component.Chance; - var warn = true; if (TryComp(uid, out var bonus)) { chance *= bonus.Multiplier; chance += bonus.FlatBonus; - warn = bonus.Warn; } if (applyGlimmer) @@ -153,7 +163,7 @@ public void RollPsionics(EntityUid uid, PotentialPsionicComponent component, boo chance = Math.Clamp(chance, 0, 1); if (_random.Prob(chance)) - _psionicAbilitiesSystem.AddPsionics(uid, warn); + TryMakePsionic((uid, component)); } public void RerollPsionics(EntityUid uid, PotentialPsionicComponent? psionic = null, float bonusMuliplier = 1f) diff --git a/Content.Shared/Nyanotrasen/Abilities/Psionics/SharedPsionicAbilitiesSystem.cs b/Content.Shared/Nyanotrasen/Abilities/Psionics/SharedPsionicAbilitiesSystem.cs index 2739d5ba31a..734c7aa00e8 100644 --- a/Content.Shared/Nyanotrasen/Abilities/Psionics/SharedPsionicAbilitiesSystem.cs +++ b/Content.Shared/Nyanotrasen/Abilities/Psionics/SharedPsionicAbilitiesSystem.cs @@ -7,111 +7,118 @@ using Robust.Shared.Random; using Robust.Shared.Serialization; -namespace Content.Shared.Abilities.Psionics +namespace Content.Shared.Abilities.Psionics; + +public sealed class SharedPsionicAbilitiesSystem : EntitySystem { - public sealed class SharedPsionicAbilitiesSystem : EntitySystem + [Dependency] private readonly SharedActionsSystem _actions = default!; + [Dependency] private readonly EntityLookupSystem _lookup = default!; + [Dependency] private readonly SharedPopupSystem _popups = default!; + [Dependency] private readonly ISharedAdminLogManager _adminLogger = default!; + [Dependency] private readonly GlimmerSystem _glimmerSystem = default!; + [Dependency] private readonly IRobustRandom _robustRandom = default!; + + public override void Initialize() { - [Dependency] private readonly SharedActionsSystem _actions = default!; - [Dependency] private readonly EntityLookupSystem _lookup = default!; - [Dependency] private readonly SharedPopupSystem _popups = default!; - [Dependency] private readonly ISharedAdminLogManager _adminLogger = default!; - [Dependency] private readonly GlimmerSystem _glimmerSystem = default!; - [Dependency] private readonly IRobustRandom _robustRandom = default!; - - public override void Initialize() - { - base.Initialize(); - SubscribeLocalEvent(OnInit); - SubscribeLocalEvent(OnShutdown); - SubscribeLocalEvent(OnPowerUsed); + base.Initialize(); + SubscribeLocalEvent(OnInit); + SubscribeLocalEvent(OnShutdown); + SubscribeLocalEvent(OnPowerUsed); - SubscribeLocalEvent(OnMobStateChanged); - } + SubscribeLocalEvent(OnMobStateChanged); + } - private void OnPowerUsed(EntityUid uid, PsionicComponent component, PsionicPowerUsedEvent args) + private void OnPowerUsed(EntityUid uid, PsionicComponent component, PsionicPowerUsedEvent args) + { + var ev = new PsionicPowerDetectedEvent(uid, args.Power); + var coords = Transform(uid).Coordinates; + foreach (var ent in _lookup.GetEntitiesInRange(coords, 10f)) { - foreach (var entity in _lookup.GetEntitiesInRange(uid, 10f)) + if (ent.Owner != uid && !(TryComp(ent, out var insul) && !insul.Passthrough)) { - if (HasComp(entity) && entity != uid && !(TryComp(entity, out var insul) && !insul.Passthrough)) - { - _popups.PopupEntity(Loc.GetString("metapsionic-pulse-power", ("power", args.Power)), entity, entity, PopupType.LargeCaution); - args.Handled = true; - return; - } + RaiseLocalEvent(ent, ref ev); + _popups.PopupEntity(Loc.GetString("metapsionic-pulse-power", ("power", args.Power)), ent, ent, PopupType.LargeCaution); + args.Handled = true; } } + } - private void OnInit(EntityUid uid, PsionicsDisabledComponent component, ComponentInit args) - { - SetPsionicsThroughEligibility(uid); - } + private void OnInit(EntityUid uid, PsionicsDisabledComponent component, ComponentInit args) + { + SetPsionicsThroughEligibility(uid); + } - private void OnShutdown(EntityUid uid, PsionicsDisabledComponent component, ComponentShutdown args) - { - SetPsionicsThroughEligibility(uid); - } + private void OnShutdown(EntityUid uid, PsionicsDisabledComponent component, ComponentShutdown args) + { + SetPsionicsThroughEligibility(uid); + } - private void OnMobStateChanged(EntityUid uid, PsionicComponent component, MobStateChangedEvent args) - { - SetPsionicsThroughEligibility(uid); - } + private void OnMobStateChanged(EntityUid uid, PsionicComponent component, MobStateChangedEvent args) + { + SetPsionicsThroughEligibility(uid); + } - /// - /// Checks whether the entity is eligible to use its psionic ability. This should be run after anything that could effect psionic eligibility. - /// - public void SetPsionicsThroughEligibility(EntityUid uid) - { - PsionicComponent? component = null; - if (!Resolve(uid, ref component, false)) - return; + /// + /// Checks whether the entity is eligible to use its psionic ability. This should be run after anything that could effect psionic eligibility. + /// + public void SetPsionicsThroughEligibility(EntityUid uid) + { + PsionicComponent? component = null; + if (!Resolve(uid, ref component, false)) + return; - if (component.PsionicAbility == null) - return; + if (component.PsionicAbility == null) + return; - _actions.TryGetActionData( component.PsionicAbility, out var actionData ); + _actions.TryGetActionData( component.PsionicAbility, out var actionData ); - if (actionData == null) - return; + if (actionData == null) + return; - _actions.SetEnabled(actionData.Owner, IsEligibleForPsionics(uid)); - } + _actions.SetEnabled(actionData.Owner, IsEligibleForPsionics(uid)); + } - private bool IsEligibleForPsionics(EntityUid uid) - { - return !HasComp(uid) - && (!TryComp(uid, out var mobstate) || mobstate.CurrentState == MobState.Alive); - } + private bool IsEligibleForPsionics(EntityUid uid) + { + return !HasComp(uid) + && (!TryComp(uid, out var mobstate) || mobstate.CurrentState == MobState.Alive); + } - public void LogPowerUsed(EntityUid uid, string power, int minGlimmer = 8, int maxGlimmer = 12) - { - _adminLogger.Add(Database.LogType.Psionics, Database.LogImpact.Medium, $"{ToPrettyString(uid):player} used {power}"); - var ev = new PsionicPowerUsedEvent(uid, power); - RaiseLocalEvent(uid, ev, false); + public void LogPowerUsed(EntityUid uid, string power, int minGlimmer = 8, int maxGlimmer = 12) + { + _adminLogger.Add(Database.LogType.Psionics, Database.LogImpact.Medium, $"{ToPrettyString(uid):player} used {power}"); + var ev = new PsionicPowerUsedEvent(uid, power); + RaiseLocalEvent(uid, ev, false); - _glimmerSystem.Glimmer += _robustRandom.Next(minGlimmer, maxGlimmer); - } + _glimmerSystem.Glimmer += _robustRandom.Next(minGlimmer, maxGlimmer); } +} - public sealed class PsionicPowerUsedEvent : HandledEntityEventArgs - { - public EntityUid User { get; } - public string Power = string.Empty; +/// +/// Event raised on a metapsionic entity when someone used a psionic power nearby. +/// +[ByRefEvent] +public record struct PsionicPowerDetectedEvent(EntityUid Psionic, string Power); - public PsionicPowerUsedEvent(EntityUid user, string power) - { - User = user; - Power = power; - } +public sealed class PsionicPowerUsedEvent : HandledEntityEventArgs +{ + public EntityUid User { get; } + public string Power = string.Empty; + + public PsionicPowerUsedEvent(EntityUid user, string power) + { + User = user; + Power = power; } +} - [Serializable] - [NetSerializable] - public sealed class PsionicsChangedEvent : EntityEventArgs +[Serializable] +[NetSerializable] +public sealed class PsionicsChangedEvent : EntityEventArgs +{ + public readonly NetEntity Euid; + public PsionicsChangedEvent(NetEntity euid) { - public readonly NetEntity Euid; - public PsionicsChangedEvent(NetEntity euid) - { - Euid = euid; - } + Euid = euid; } } diff --git a/Resources/Locale/en-US/deltav/xenoarchaeology/artifact-hints.ftl b/Resources/Locale/en-US/deltav/xenoarchaeology/artifact-hints.ftl new file mode 100644 index 00000000000..ef450187981 --- /dev/null +++ b/Resources/Locale/en-US/deltav/xenoarchaeology/artifact-hints.ftl @@ -0,0 +1,7 @@ +# effects + +artifact-effect-hint-noosphere = Noöspheric shift + +# triggers + +artifact-trigger-hint-psionic = Psionic disturbance diff --git a/Resources/Prototypes/DeltaV/XenoArch/Effects/glimmer.yml b/Resources/Prototypes/DeltaV/XenoArch/Effects/glimmer.yml new file mode 100644 index 00000000000..227859b05c2 --- /dev/null +++ b/Resources/Prototypes/DeltaV/XenoArch/Effects/glimmer.yml @@ -0,0 +1,32 @@ +- type: artifactEffect + id: EffectRaiseGlimmer + targetDepth: 2 + effectHint: artifact-effect-hint-noosphere + components: + - type: GlimmerArtifact + range: + min: 0 + max: 450 # cant instantly get ended by a looping artifact + change: 30 + +- type: artifactEffect + id: EffectRaiseGlimmerLarge + targetDepth: 4 + effectHint: artifact-effect-hint-noosphere + components: + - type: GlimmerArtifact + range: + min: 0 + max: 700 # cant get to 1000 from a looping artifact + change: 70 + +- type: artifactEffect + id: EffectLowerGlimmer + targetDepth: 4 + effectHint: artifact-effect-hint-noosphere + components: + - type: GlimmerArtifact + range: + min: 400 # for recovering from serious glimmer emergencies, not a full drain replacement + max: 1000 + change: -50 diff --git a/Resources/Prototypes/DeltaV/XenoArch/Effects/psionic.yml b/Resources/Prototypes/DeltaV/XenoArch/Effects/psionic.yml new file mode 100644 index 00000000000..8004305b4bf --- /dev/null +++ b/Resources/Prototypes/DeltaV/XenoArch/Effects/psionic.yml @@ -0,0 +1,7 @@ +- type: artifactEffect + id: EffectMakePsionic + targetDepth: 4 + effectHint: artifact-effect-hint-noosphere + components: + - type: PsionicProducingArtifact + range: 5 diff --git a/Resources/Prototypes/DeltaV/XenoArch/artifact_triggers.yml b/Resources/Prototypes/DeltaV/XenoArch/artifact_triggers.yml new file mode 100644 index 00000000000..51390e5dd61 --- /dev/null +++ b/Resources/Prototypes/DeltaV/XenoArch/artifact_triggers.yml @@ -0,0 +1,9 @@ +- type: artifactTrigger + id: TriggerMetapsionic + targetDepth: 3 + triggerHint: artifact-trigger-hint-psionic + components: + - type: ArtifactMetapsionicTrigger + - type: MetapsionicPower + +# TODO: when golemization is ported, add a trigger for golemizing someone, target 7 or something