Psionic Eruption (#4078)
* Psionic Eruption
* Art Pass
* Detangles spaghetti
Removed from Nyano namespaces, moved entirely into _DV namespaces and folders where possible.
* Remove nuke behavior (Maybe next time 😢 )
* Various maintainer requested patches
* More cleanup
* I'm blind
---------
Signed-off-by: William Lemon <William.Lemon2@gmail.com>
This commit is contained in:
parent
27ceb097f6
commit
31718cfbd1
|
|
@ -0,0 +1,10 @@
|
|||
<controls:FancyWindow xmlns="https://spacestation14.io"
|
||||
xmlns:controls="clr-namespace:Content.Client.UserInterface.Controls"
|
||||
Title="{Loc 'eruption-warning-window-title'}">
|
||||
<BoxContainer Orientation="Vertical" Margin="10">
|
||||
<Label Text="{Loc 'eruption-warning-window-prompt-text-part'}" Margin="5" Align="Center"/>
|
||||
<BoxContainer Orientation="Horizontal" Margin="5" Align="Center">
|
||||
<Button Name="ConfirmButton" Text="{Loc 'eruption-warning-window-acknowledge-button'}"/>
|
||||
</BoxContainer>
|
||||
</BoxContainer>
|
||||
</controls:FancyWindow>
|
||||
|
|
@ -0,0 +1,16 @@
|
|||
using Content.Client.UserInterface.Controls;
|
||||
using Robust.Client.AutoGenerated;
|
||||
using Robust.Client.UserInterface.XAML;
|
||||
|
||||
namespace Content.Client._DV.Abilities.Psionics;
|
||||
|
||||
[GenerateTypedNameReferences]
|
||||
public sealed partial class EruptionWarning : FancyWindow
|
||||
{
|
||||
public EruptionWarning()
|
||||
{
|
||||
RobustXamlLoader.Load(this);
|
||||
|
||||
ConfirmButton.OnPressed += _ => Close();
|
||||
}
|
||||
}
|
||||
|
|
@ -0,0 +1,29 @@
|
|||
using Content.Client._DV.Abilities.Psionics;
|
||||
using Content.Client.Eui;
|
||||
using JetBrains.Annotations;
|
||||
|
||||
namespace Content.Client._DV.Psionics.UI;
|
||||
|
||||
[UsedImplicitly]
|
||||
public sealed class EruptionWarningEui : BaseEui
|
||||
{
|
||||
private readonly EruptionWarning _window;
|
||||
|
||||
public EruptionWarningEui()
|
||||
{
|
||||
_window = new EruptionWarning();
|
||||
}
|
||||
|
||||
public override void Opened()
|
||||
{
|
||||
_window.OpenCentered();
|
||||
}
|
||||
|
||||
public override void Closed()
|
||||
{
|
||||
base.Closed();
|
||||
|
||||
_window.Close();
|
||||
}
|
||||
|
||||
}
|
||||
|
|
@ -0,0 +1,28 @@
|
|||
using Robust.Shared.Audio;
|
||||
using Content.Shared.Actions;
|
||||
using Content.Shared.DoAfter;
|
||||
using Robust.Shared.Prototypes;
|
||||
using Robust.Shared.Serialization.TypeSerializers.Implementations.Custom.Prototype;
|
||||
|
||||
namespace Content.Shared._DV.Abilities.Psionics;
|
||||
|
||||
[RegisterComponent]
|
||||
public sealed partial class PsionicEruptionPowerComponent : Component
|
||||
{
|
||||
[DataField]
|
||||
public DoAfterId? DoAfter;
|
||||
[DataField]
|
||||
public EntProtoId EruptionActionId = "ActionEruption";
|
||||
[DataField]
|
||||
public EntityUid? EruptionActionEntity;
|
||||
[DataField]
|
||||
public SoundSpecifier SoundUse = new SoundPathSpecifier("/Audio/Nyanotrasen/Psionics/heartbeat_fast.ogg");
|
||||
[DataField]
|
||||
public SoundSpecifier SoundDetonate = new SoundPathSpecifier("/Audio/Nyanotrasen/Psionics/eruption.ogg");
|
||||
[DataField]
|
||||
public TimeSpan NextAnnoy = TimeSpan.FromSeconds(5);
|
||||
[DataField]
|
||||
public TimeSpan NextSpark = TimeSpan.MaxValue;
|
||||
[DataField]
|
||||
public bool Warned = false;
|
||||
}
|
||||
|
|
@ -0,0 +1,229 @@
|
|||
using Content.Server._DV.Psionics;
|
||||
using Content.Server.Abilities.Psionics;
|
||||
using Content.Server.DoAfter;
|
||||
using Content.Server.EUI;
|
||||
using Content.Server.Explosion.EntitySystems;
|
||||
using Content.Server.Jittering;
|
||||
using Content.Server.Lightning;
|
||||
using Content.Server.Mind;
|
||||
using Content.Shared._DV.Abilities.Psionics;
|
||||
using Content.Shared.Abilities.Psionics;
|
||||
using Content.Shared.Actions;
|
||||
using Content.Shared.Actions.Events;
|
||||
using Content.Shared.Body.Components;
|
||||
using Content.Shared.Body.Systems;
|
||||
using Content.Shared.DoAfter;
|
||||
using Content.Shared.Mind;
|
||||
using Content.Shared.Popups;
|
||||
using Content.Shared.Psionics.Events;
|
||||
using Content.Shared.Psionics.Glimmer;
|
||||
using Robust.Server.Audio;
|
||||
using Robust.Server.Player;
|
||||
using Robust.Shared.Audio;
|
||||
using Robust.Shared.Prototypes;
|
||||
using Robust.Shared.Random;
|
||||
using Robust.Shared.Timing;
|
||||
|
||||
namespace Content.Server._DV.Abilities.Psionics;
|
||||
|
||||
public sealed class PsionicEruptionSystem : EntitySystem
|
||||
{
|
||||
[Dependency] private readonly AudioSystem _audio = default!;
|
||||
[Dependency] private readonly DoAfterSystem _doAfter = default!;
|
||||
[Dependency] private readonly EuiManager _eui = default!;
|
||||
[Dependency] private readonly ExplosionSystem _explosion = default!;
|
||||
[Dependency] private readonly GlimmerSystem _glimmer = default!;
|
||||
[Dependency] private readonly IGameTiming _gameTiming = default!;
|
||||
[Dependency] private readonly IPlayerManager _player = default!;
|
||||
[Dependency] private readonly IRobustRandom _random = default!;
|
||||
[Dependency] private readonly JitteringSystem _jittering = default!;
|
||||
[Dependency] private readonly LightningSystem _lightning = default!;
|
||||
[Dependency] private readonly MindSystem _mind = default!;
|
||||
[Dependency] private readonly SharedActionsSystem _actions = default!;
|
||||
[Dependency] private readonly SharedBodySystem _body = default!;
|
||||
[Dependency] private readonly SharedPopupSystem _popups = default!;
|
||||
[Dependency] private readonly SharedPsionicAbilitiesSystem _psionics = default!;
|
||||
[Dependency] private readonly SharedTransformSystem _transform = default!;
|
||||
|
||||
private static readonly EntProtoId? Sparks = "EffectSparks";
|
||||
|
||||
public override void Initialize()
|
||||
{
|
||||
base.Initialize();
|
||||
SubscribeLocalEvent<PsionicEruptionPowerComponent, ComponentInit>(OnInit);
|
||||
SubscribeLocalEvent<PsionicEruptionPowerComponent, ComponentShutdown>(OnShutdown);
|
||||
SubscribeLocalEvent<PsionicEruptionPowerComponent, PsionicEruptionPowerActionEvent>(OnPowerUsed);
|
||||
|
||||
SubscribeLocalEvent<PsionicEruptionPowerComponent, DispelledEvent>(OnDispelled);
|
||||
SubscribeLocalEvent<PsionicEruptionPowerComponent, PsionicEruptionDoAfterEvent>(OnDoAfter);
|
||||
}
|
||||
|
||||
private void OnInit(Entity<PsionicEruptionPowerComponent> entity, ref ComponentInit args)
|
||||
{
|
||||
var component = entity.Comp;
|
||||
_actions.AddAction(entity, ref component.EruptionActionEntity, component.EruptionActionId);
|
||||
_actions.TryGetActionData(component.EruptionActionEntity, out var actionData);
|
||||
if (actionData is { UseDelay: not null })
|
||||
_actions.StartUseDelay(component.EruptionActionEntity);
|
||||
if (TryComp<PsionicComponent>(entity, out var psionic) && psionic.PsionicAbility == null)
|
||||
{
|
||||
psionic.PsionicAbility = component.EruptionActionEntity;
|
||||
psionic.ActivePowers.Add(component);
|
||||
}
|
||||
}
|
||||
|
||||
private void ShowWarning(Entity<PsionicEruptionPowerComponent> entity)
|
||||
{
|
||||
var comp = entity.Comp;
|
||||
if (comp.Warned)
|
||||
return;
|
||||
MindComponent? mind;
|
||||
if (!_mind.TryGetMind(entity, out var _, out mind))
|
||||
return;
|
||||
if (mind.UserId == null || !_player.TryGetSessionById(mind.UserId.Value, out var client))
|
||||
return;
|
||||
_eui.OpenEui(new EruptionWarningEui(), client);
|
||||
comp.Warned = true;
|
||||
}
|
||||
|
||||
private void OnShutdown(Entity<PsionicEruptionPowerComponent> entity, ref ComponentShutdown args)
|
||||
{
|
||||
_actions.RemoveAction(entity, entity.Comp.EruptionActionEntity);
|
||||
|
||||
if (TryComp<PsionicComponent>(entity, out var psionic))
|
||||
{
|
||||
psionic.ActivePowers.Remove(entity.Comp);
|
||||
}
|
||||
}
|
||||
|
||||
private void OnDispelled(Entity<PsionicEruptionPowerComponent> entity, ref DispelledEvent args)
|
||||
{
|
||||
if (entity.Comp.DoAfter == null)
|
||||
return;
|
||||
|
||||
_doAfter.Cancel(entity.Comp.DoAfter);
|
||||
entity.Comp.DoAfter = null;
|
||||
|
||||
args.Handled = true;
|
||||
}
|
||||
|
||||
private void OnPowerUsed(Entity<PsionicEruptionPowerComponent> entity, ref PsionicEruptionPowerActionEvent args)
|
||||
{
|
||||
var component = entity.Comp;
|
||||
int detonateTime = 10;
|
||||
int sparkFrom = 5;
|
||||
|
||||
if (_glimmer.GetGlimmerTier(_glimmer.Glimmer) == GlimmerTier.Critical)
|
||||
{
|
||||
detonateTime = 30;
|
||||
sparkFrom = 15;
|
||||
}
|
||||
|
||||
var ev = new PsionicEruptionDoAfterEvent();
|
||||
var doAfterArgs = new DoAfterArgs(EntityManager, entity, detonateTime, ev, entity);
|
||||
_doAfter.TryStartDoAfter(doAfterArgs, out var doAfterId);
|
||||
component.DoAfter = doAfterId;
|
||||
_popups.PopupEntity(Loc.GetString("psionic-eruption-begin", ("user", entity)), entity, PopupType.LargeCaution); // Loc.GetString("psionic-regeneration-begin")
|
||||
_audio.PlayPvs(component.SoundUse, entity, AudioParams.Default.WithVolume(8f).WithMaxDistance(1.5f).WithRolloffFactor(3.5f));
|
||||
_psionics.LogPowerUsed(entity, "psionic eruption", 2, 4);
|
||||
// Start Jittering
|
||||
_jittering.DoJitter(entity, TimeSpan.FromSeconds(sparkFrom), true, 10, 16);
|
||||
|
||||
component.NextSpark = _gameTiming.CurTime + TimeSpan.FromSeconds(sparkFrom);
|
||||
args.Handled = true;
|
||||
}
|
||||
|
||||
public override void Update(float frameTime)
|
||||
{
|
||||
base.Update(frameTime);
|
||||
|
||||
// Occasionally pester users of the Psionic Eruption power to use it.
|
||||
var t = _gameTiming.CurTime;
|
||||
var query = EntityQueryEnumerator<PsionicEruptionPowerComponent>();
|
||||
while (query.MoveNext(out var uid, out var comp))
|
||||
{
|
||||
ShowWarning(new(uid, comp)); // I'm a bad coder.
|
||||
if (comp.DoAfter != null)
|
||||
{
|
||||
if (t > comp.NextSpark)
|
||||
{
|
||||
if (_glimmer.GetGlimmerTier(_glimmer.Glimmer) == GlimmerTier.Critical && _random.Prob(0.125f))
|
||||
{
|
||||
_lightning.ShootRandomLightnings(uid, 5f, _random.Next(1, 3));
|
||||
}
|
||||
_jittering.DoJitter(uid, TimeSpan.FromSeconds(5), true, 10, 32);
|
||||
Spawn(Sparks, Transform(uid).Coordinates);
|
||||
|
||||
comp.NextSpark = t + TimeSpan.FromMilliseconds(500);
|
||||
}
|
||||
continue;
|
||||
}
|
||||
if (t < comp.NextAnnoy)
|
||||
continue;
|
||||
var msg = "psionic-eruption-annoy-minimal";
|
||||
var msgSize = PopupType.Small;
|
||||
var minwait = TimeSpan.FromSeconds(60); // How many seconds to wait before the next annoyance.
|
||||
_glimmer.Glimmer += _random.Next(1, 5); // Increase glimmer by a random amount.
|
||||
switch (_glimmer.GetGlimmerTier(_glimmer.Glimmer))
|
||||
{
|
||||
case GlimmerTier.Minimal:
|
||||
msg = "psionic-eruption-annoy-minimal";
|
||||
minwait = TimeSpan.FromSeconds(60);
|
||||
break;
|
||||
case GlimmerTier.Low:
|
||||
msg = "psionic-eruption-annoy-low";
|
||||
minwait = TimeSpan.FromSeconds(45);
|
||||
break;
|
||||
case GlimmerTier.Moderate:
|
||||
msg = "psionic-eruption-annoy-moderate";
|
||||
minwait = TimeSpan.FromSeconds(30);
|
||||
break;
|
||||
case GlimmerTier.High:
|
||||
msg = "psionic-eruption-annoy-high";
|
||||
minwait = TimeSpan.FromSeconds(25);
|
||||
msgSize = PopupType.Medium;
|
||||
Spawn(Sparks, Transform(uid).Coordinates);
|
||||
break;
|
||||
case GlimmerTier.Dangerous:
|
||||
msg = "psionic-eruption-annoy-dangerous";
|
||||
minwait = TimeSpan.FromSeconds(20);
|
||||
msgSize = PopupType.Large;
|
||||
Spawn(Sparks, Transform(uid).Coordinates);
|
||||
break;
|
||||
case GlimmerTier.Critical:
|
||||
msg = "psionic-eruption-annoy-critical";
|
||||
minwait = TimeSpan.FromSeconds(10);
|
||||
msgSize = PopupType.LargeCaution;
|
||||
Spawn(Sparks, Transform(uid).Coordinates);
|
||||
break;
|
||||
}
|
||||
// Prompt the user to use the power.
|
||||
_popups.PopupEntity(Loc.GetString(msg, ("user", uid)), uid, uid, msgSize);
|
||||
comp.NextAnnoy = t + minwait + TimeSpan.FromSeconds(_random.Next(0, 10)); // Add a random delay to the next annoyance.
|
||||
}
|
||||
}
|
||||
private void OnDoAfter(Entity<PsionicEruptionPowerComponent> entity, ref PsionicEruptionDoAfterEvent args)
|
||||
{
|
||||
entity.Comp.DoAfter = null;
|
||||
|
||||
if (args.Cancelled || args.Handled)
|
||||
return;
|
||||
|
||||
if (!TryComp<BodyComponent>(entity, out var body))
|
||||
return;
|
||||
|
||||
var pos = _transform.GetMapCoordinates(entity);
|
||||
_body.GibBody(entity, acidify: true, body, launchGibs: true);
|
||||
int boom = _glimmer.GetGlimmerTier(_glimmer.Glimmer) switch
|
||||
{
|
||||
GlimmerTier.Minimal => 4,
|
||||
GlimmerTier.Low => 8,
|
||||
GlimmerTier.Moderate => 12,
|
||||
GlimmerTier.High => 16,
|
||||
GlimmerTier.Dangerous => 32,
|
||||
GlimmerTier.Critical => 64,
|
||||
_ => 0
|
||||
};
|
||||
_explosion.QueueExplosion(pos, ExplosionSystem.DefaultExplosionPrototypeId, boom, 1, 5, entity, maxTileBreak: 0);
|
||||
}
|
||||
}
|
||||
|
|
@ -0,0 +1,8 @@
|
|||
using Content.Server.EUI;
|
||||
|
||||
namespace Content.Server._DV.Psionics;
|
||||
|
||||
/// <summary>
|
||||
/// Does nothing on the server as this popup has no interactions
|
||||
/// </summary>
|
||||
public sealed class EruptionWarningEui : BaseEui;
|
||||
|
|
@ -0,0 +1,3 @@
|
|||
namespace Content.Shared.Actions.Events;
|
||||
|
||||
public sealed partial class PsionicEruptionPowerActionEvent : InstantActionEvent { }
|
||||
|
|
@ -8,3 +8,6 @@ public sealed partial class PrecognitionDoAfterEvent : SimpleDoAfterEvent;
|
|||
|
||||
[Serializable, NetSerializable]
|
||||
public sealed partial class GlimmerWispDrainDoAfterEvent : SimpleDoAfterEvent;
|
||||
|
||||
[Serializable, NetSerializable]
|
||||
public sealed partial class PsionicEruptionDoAfterEvent : SimpleDoAfterEvent;
|
||||
|
|
|
|||
|
|
@ -48,4 +48,28 @@ psionic-power-precognition-unknown-shuttle-cargo-lost-result-message = You see a
|
|||
psionic-power-precognition-unknown-shuttle-traveling-cuisine-result-message = You see a vision of peace, a cozy meal sizzling on a warm stove. A delicious smells wafts through the air.
|
||||
psionic-power-precognition-unknown-shuttle-disaster-evac-pod-result-message = You see a vision of death and blood, of a destruction so complete only few survive, drifting through the coldness of space.
|
||||
|
||||
psionic-eruption-begin = {CAPITALIZE(THE($user))} is being consumed by a psionic energy!
|
||||
psionic-eruption-annoy-minimal = You feel a pressure building up in your mind.
|
||||
psionic-eruption-annoy-low = Your head aches from the psionic energy.
|
||||
psionic-eruption-annoy-medium = You feel a strong pressure in your mind. Make it stop!
|
||||
psionic-eruption-annoy-high = Your head is pounding from the psionic energy. You need to release it!
|
||||
psionic-eruption-annoy-dangerous = Your head is about to explode from the psionic energy!
|
||||
psionic-eruption-annoy-critical = Make it stop! Make it stop! Make it stop!
|
||||
|
||||
psionic-eruption-nuke-warning = Attention! An overwhelming psionic energy has been detected at {$location}.
|
||||
psionic-eruption-nuke-sender = ???
|
||||
|
||||
psionic-eruption-not-enough-creatures = You need at least 3 living creatures nearby to unleash the eruption. You only have {$count}.
|
||||
|
||||
eruption-warning-window-title = Your Brain isn't Ready!
|
||||
eruption-warning-window-prompt-text-part = You feel a strong pressure building up in your mind
|
||||
and you need to release it before it overwhelms you.
|
||||
When you are ready, you can unleash a psionic eruption.
|
||||
Doing so will cause a massive psionic discharge,
|
||||
which will destroy yourself and the station around you.
|
||||
THIS ALONE DOES NOT MAKE YOU AN ANTAGONIST.
|
||||
Do not detonately randomly, ensure proper buildup.
|
||||
Do you understand?
|
||||
eruption-warning-window-acknowledge-button = I Understand
|
||||
|
||||
telegnosis-power-ssd = { CAPITALIZE(POSS-ADJ($ent)) } eyes are unfocused and darting around, as if trying to see something that isn't there.
|
||||
|
|
|
|||
|
|
@ -10,3 +10,4 @@
|
|||
NoosphericZapPower: 0.3
|
||||
# PsionicInvisibilityPower: 0.15
|
||||
MindSwapPower: 0.15
|
||||
PsionicEruptionPower: 0.1
|
||||
|
|
|
|||
|
|
@ -103,6 +103,20 @@
|
|||
checkCanInteract: false
|
||||
event: !type:MetapsionicPowerActionEvent
|
||||
|
||||
- type: entity
|
||||
id: ActionEruption
|
||||
name: Psionic Eruption
|
||||
description: Unleash a powerful psionic eruption destroying you and everything around you.
|
||||
components:
|
||||
- type: InstantAction
|
||||
icon:
|
||||
sprite: _DV/Interface/Actions/actions_psionics.rsi
|
||||
state: eruption
|
||||
useDelay: 45
|
||||
checkCanInteract: false
|
||||
event: !type:PsionicEruptionPowerActionEvent
|
||||
|
||||
|
||||
- type: entity
|
||||
id: ActionPsionicRegeneration
|
||||
name: Psionic Regeneration
|
||||
|
|
|
|||
Binary file not shown.
|
After Width: | Height: | Size: 8.3 KiB |
|
|
@ -1,7 +1,7 @@
|
|||
{
|
||||
"version": 1,
|
||||
"license": "CC-BY-SA-4.0",
|
||||
"copyright": "Created by @Vordenburg (github) for Nyanotrasen. Precognition sprited by chamomileteatime on Discord for DeltaV",
|
||||
"copyright": "Created by @Vordenburg (github) for Nyanotrasen. Precognition sprited by chamomileteatime on Discord for DeltaV. Eruption sprited by TehFlaminTAco on Discord for DeltaV.",
|
||||
"size": {
|
||||
"x": 64,
|
||||
"y": 64
|
||||
|
|
@ -42,6 +42,9 @@
|
|||
},
|
||||
{
|
||||
"name": "telegnosis"
|
||||
},
|
||||
{
|
||||
"name": "eruption"
|
||||
}
|
||||
]
|
||||
}
|
||||
|
|
|
|||
Loading…
Reference in New Issue