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