Skip to content

Commit

Permalink
Anomalist Power System (#991)
Browse files Browse the repository at this point in the history
# Description

This PR extends the system originally created for Shadeskip, into a
system that allows any (currently existing) anomaly effect to be
replicated as an Instant Action psionic power. This will be needed for
Wizard/Rogue Psion antagonists. I'll be making a metric shitton of new
powers using this in separate PRs.

This is extremely blatantly re-using code as-is from the various Anomaly
Systems, because it unfortunately turns out that it's simply not
possible to build a constructor to interface between Psionics and
Anomalies.

# Changelog
No changelog because this isn't player facing.

---------

Signed-off-by: VMSolidus <evilexecutive@gmail.com>
  • Loading branch information
VMSolidus authored Oct 2, 2024
1 parent 1dbce62 commit 2de4f6d
Show file tree
Hide file tree
Showing 13 changed files with 973 additions and 102 deletions.
Original file line number Diff line number Diff line change
@@ -0,0 +1,79 @@
using Content.Shared.Abilities.Psionics;
using Content.Shared.Actions.Events;
using Content.Shared.Mobs.Components;
using System.Linq;
using System.Numerics;
using Content.Shared.Database;
using Robust.Shared.Collections;

namespace Content.Server.Abilities.Psionics;

public sealed partial class AnomalyPowerSystem
{
/// <summary>
/// This function handles emulating the effects of a "Bluespace Anomaly", using the caster as the "Anomaly",
/// while substituting their Psionic casting stats for "Severity and Stability".
/// Essentially, scramble the location of entities near the caster(possibly to include the caster).
/// </summary>
private void DoBluespaceAnomalyEffects(EntityUid uid, PsionicComponent component, AnomalyPowerActionEvent args, bool overcharged = false)
{
if (args.Bluespace is null)
return;

if (overcharged)
BluespaceSupercrit(uid, component, args);
else BluespacePulse(uid, component, args);
}

private void BluespaceSupercrit(EntityUid uid, PsionicComponent component, AnomalyPowerActionEvent args)
{
var xform = Transform(uid);
var mapPos = _xform.GetWorldPosition(xform);
var radius = args.Bluespace!.Value.SupercriticalTeleportRadius * component.CurrentAmplification;
var gridBounds = new Box2(mapPos - new Vector2(radius, radius), mapPos + new Vector2(radius, radius));
var mobs = new HashSet<Entity<MobStateComponent>>();
_lookup.GetEntitiesInRange(xform.Coordinates, args.Bluespace!.Value.MaxShuffleRadius, mobs);
foreach (var comp in mobs)
{
if (args.Bluespace!.Value.SupercritTeleportsCaster && comp.Owner == uid)
continue;

var ent = comp.Owner;
var randomX = _random.NextFloat(gridBounds.Left, gridBounds.Right);
var randomY = _random.NextFloat(gridBounds.Bottom, gridBounds.Top);

var pos = new Vector2(randomX, randomY);

_adminLogger.Add(LogType.Teleport, $"{ToPrettyString(ent)} has been teleported to {pos} by the supercritical {ToPrettyString(uid)} at {mapPos}");

_xform.SetWorldPosition(ent, pos);
_audio.PlayPvs(args.Bluespace!.Value.TeleportSound, ent);
}
}

private void BluespacePulse(EntityUid uid, PsionicComponent component, AnomalyPowerActionEvent args)
{
var xformQuery = GetEntityQuery<TransformComponent>();
var xform = xformQuery.GetComponent(uid);
var range = args.Bluespace!.Value.MaxShuffleRadius * component.CurrentAmplification;
var mobs = new HashSet<Entity<MobStateComponent>>();
_lookup.GetEntitiesInRange(xform.Coordinates, range, mobs);
var allEnts = new ValueList<EntityUid>(mobs.Select(m => m.Owner)) { uid };
var coords = new ValueList<Vector2>();
foreach (var ent in allEnts)
{
if (args.Bluespace!.Value.PulseTeleportsCaster && ent == uid
|| !xformQuery.TryGetComponent(ent, out var allXform))
continue;

coords.Add(_xform.GetWorldPosition(allXform));
}

_random.Shuffle(coords);
for (var i = 0; i < allEnts.Count; i++)
{
_adminLogger.Add(LogType.Teleport, $"{ToPrettyString(allEnts[i])} has been shuffled to {coords[i]} by the {ToPrettyString(uid)} at {xform.Coordinates}");
_xform.SetWorldPosition(allEnts[i], coords[i]);
}
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,39 @@
using Content.Shared.Abilities.Psionics;
using Content.Shared.Actions.Events;

namespace Content.Server.Abilities.Psionics;

public sealed partial class AnomalyPowerSystem
{
/// <summary>
/// This function handles emulating the effects of a "Electrical Anomaly", using the caster as the "Anomaly",
/// while substituting their Psionic casting stats for "Severity and Stability".
/// This fires lightning bolts at random entities near the caster.
/// </summary>
private void DoElectricityAnomalyEffects(EntityUid uid, PsionicComponent component, AnomalyPowerActionEvent args, bool overcharged = false)
{
if (args.Electricity is null)
return;

if (overcharged)
ElectricitySupercrit(uid, component, args);
else ElectricityPulse(uid, component, args);
}

private void ElectricitySupercrit(EntityUid uid, PsionicComponent component, AnomalyPowerActionEvent args)
{
var range = args.Electricity!.Value.MaxElectrocuteRange * component.CurrentAmplification;

_emp.EmpPulse(_xform.GetMapCoordinates(uid), range, args.Electricity!.Value.EmpEnergyConsumption, args.Electricity!.Value.EmpDisabledDuration);
_lightning.ShootRandomLightnings(uid, range, args.Electricity!.Value.MaxBoltCount * (int) component.CurrentAmplification, arcDepth: (int) component.CurrentDampening);
}

private void ElectricityPulse(EntityUid uid, PsionicComponent component, AnomalyPowerActionEvent args)
{
var range = args.Electricity!.Value.MaxElectrocuteRange * component.CurrentAmplification;

int boltCount = (int) MathF.Floor(MathHelper.Lerp(args.Electricity!.Value.MinBoltCount, args.Electricity!.Value.MaxBoltCount, component.CurrentAmplification));

_lightning.ShootRandomLightnings(uid, range, boltCount);
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,79 @@
using Content.Shared.Abilities.Psionics;
using Content.Shared.Actions.Events;
using Content.Shared.Random.Helpers;
using Robust.Shared.Random;
using Content.Shared.Anomaly.Effects.Components;
using Robust.Shared.Map.Components;

namespace Content.Server.Abilities.Psionics;

public sealed partial class AnomalyPowerSystem
{
private const string NoGrid = "entity-anomaly-no-grid";

/// <summary>
/// This function handles emulating the effects of an "Entity Anomaly", using the caster as the "Anomaly",
/// while substituting their Psionic casting stats for "Severity and Stability".
/// Essentially, spawn entities on random tiles in a radius around the caster.
/// </summary>
private void DoEntityAnomalyEffects(EntityUid uid, PsionicComponent component, AnomalyPowerActionEvent args, bool overcharged = false)
{
if (args.EntitySpawnEntries is null)
return;

if (Transform(uid).GridUid is null)
{
_popup.PopupEntity(Loc.GetString(NoGrid), uid, uid);
return;
}

if (overcharged)
EntitySupercrit(uid, component, args);
else EntityPulse(uid, component, args);
}

private void EntitySupercrit(EntityUid uid, PsionicComponent component, AnomalyPowerActionEvent args)
{
foreach (var entry in args.EntitySpawnEntries!)
{
if (!entry.Settings.SpawnOnSuperCritical)
continue;

SpawnEntities(uid, component, entry);
}
}

private void EntityPulse(EntityUid uid, PsionicComponent component, AnomalyPowerActionEvent args)
{
if (args.EntitySpawnEntries is null)
return;

foreach (var entry in args.EntitySpawnEntries!)
{
if (!entry.Settings.SpawnOnPulse)
continue;

SpawnEntities(uid, component, entry);
}
}

private void SpawnEntities(EntityUid uid, PsionicComponent component, EntitySpawnSettingsEntry entry)
{
if (!TryComp<MapGridComponent>(Transform(uid).GridUid, out var grid))
return;

var tiles = _anomalySystem.GetSpawningPoints(uid,
component.CurrentDampening,
component.CurrentAmplification,
entry.Settings,
_glimmerSystem.Glimmer / 1000,
component.CurrentAmplification,
component.CurrentAmplification);

if (tiles is null)
return;

foreach (var tileref in tiles)
Spawn(_random.Pick(entry.Spawns), _mapSystem.ToCenterCoordinates(tileref, grid));
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,52 @@
using Content.Shared.Abilities.Psionics;
using Content.Shared.Actions.Events;

namespace Content.Server.Abilities.Psionics;

public sealed partial class AnomalyPowerSystem
{
/// <summary>
/// This function handles emulating the effects of a "Explosion Anomaly", using the caster as the "Anomaly",
/// while substituting their Psionic casting stats for "Severity and Stability".
/// Generates an explosion centered on the caster.
/// </summary>
private void DoExplosionAnomalyEffects(EntityUid uid, PsionicComponent component, AnomalyPowerActionEvent args, bool overcharged = false)
{
if (args.Explosion is null)
return;

if (overcharged)
ExplosionSupercrit(uid, component, args);
else ExplosionPulse(uid, component, args);
}

private void ExplosionSupercrit(EntityUid uid, PsionicComponent component, AnomalyPowerActionEvent args)
{
if (args.Explosion!.Value.SupercritExplosionPrototype is null)
return;

var explosion = args.Explosion!.Value;
_boom.QueueExplosion(
uid,
explosion.SupercritExplosionPrototype,
explosion.SupercritTotalIntensity * component.CurrentAmplification,
explosion.SupercritDropoff / component.CurrentDampening,
explosion.SupercritMaxTileIntensity * component.CurrentDampening
);
}

private void ExplosionPulse(EntityUid uid, PsionicComponent component, AnomalyPowerActionEvent args)
{
if (args.Explosion!.Value.ExplosionPrototype is null)
return;

var explosion = args.Explosion!.Value;
_boom.QueueExplosion(
uid,
explosion.ExplosionPrototype,
explosion.TotalIntensity * component.CurrentAmplification,
explosion.Dropoff / component.CurrentDampening,
explosion.MaxTileIntensity * component.CurrentDampening
);
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,110 @@
using Content.Shared.Abilities.Psionics;
using Content.Shared.Actions.Events;
using Robust.Shared.Map.Components;
using System.Linq;
using System.Numerics;

namespace Content.Server.Abilities.Psionics;

public sealed partial class AnomalyPowerSystem
{
private void DoGasProducerAnomalyEffects(EntityUid uid, PsionicComponent component, AnomalyPowerActionEvent args, bool overcharged = false)
{
if (args.Gas is not null)
return;

if (overcharged)
GasProducerSupercrit(uid, component, args);
else GasProducerPulse(uid, component, args);
}

private void GasProducerSupercrit(EntityUid uid, PsionicComponent component, AnomalyPowerActionEvent args)
{
var xform = Transform(uid);
if (!TryComp<MapGridComponent>(xform.GridUid, out var grid))
return;

var gas = args.Gas!.Value.SupercritReleasedGas;
var mols = args.Gas!.Value.SupercritMoleAmount * component.CurrentAmplification;
var radius = args.Gas!.Value.SupercritSpawnRadius * component.CurrentAmplification;
var count = args.Gas!.Value.SupercritTileCount * component.CurrentDampening;
var temp = args.Gas!.Value.SupercritTempChange * component.CurrentDampening;
var localpos = xform.Coordinates.Position;
var tilerefs = grid.GetLocalTilesIntersecting(
new Box2(localpos + new Vector2(-radius, -radius), localpos + new Vector2(radius, radius))).ToArray();

if (tilerefs.Length == 0)
return;

var mixture = _atmosphere.GetTileMixture((uid, xform), true);
if (mixture != null)
{
mixture.AdjustMoles(gas, mols);
mixture.Temperature += temp;
}

if (count == 0)
return;

_random.Shuffle(tilerefs);
var amountCounter = 0;
foreach (var tileref in tilerefs)
{
var mix = _atmosphere.GetTileMixture(xform.GridUid, xform.MapUid, tileref.GridIndices, true);
amountCounter++;
if (mix is not { })
continue;

mix.AdjustMoles(gas, mols);
mix.Temperature += temp;

if (amountCounter >= count)
return;
}
}

private void GasProducerPulse(EntityUid uid, PsionicComponent component, AnomalyPowerActionEvent args)
{
var xform = Transform(uid);
if (!TryComp<MapGridComponent>(xform.GridUid, out var grid))
return;

var gas = args.Gas!.Value.ReleasedGas;
var mols = args.Gas!.Value.MoleAmount * component.CurrentAmplification;
var radius = args.Gas!.Value.SpawnRadius * component.CurrentAmplification;
var count = args.Gas!.Value.TileCount * component.CurrentDampening;
var temp = args.Gas!.Value.TempChange * component.CurrentDampening;
var localpos = xform.Coordinates.Position;
var tilerefs = grid.GetLocalTilesIntersecting(
new Box2(localpos + new Vector2(-radius, -radius), localpos + new Vector2(radius, radius))).ToArray();

if (tilerefs.Length == 0)
return;

var mixture = _atmosphere.GetTileMixture((uid, xform), true);
if (mixture != null)
{
mixture.AdjustMoles(gas, mols);
mixture.Temperature += temp;
}

if (count == 0)
return;

_random.Shuffle(tilerefs);
var amountCounter = 0;
foreach (var tileref in tilerefs)
{
var mix = _atmosphere.GetTileMixture(xform.GridUid, xform.MapUid, tileref.GridIndices, true);
amountCounter++;
if (mix is not { })
continue;

mix.AdjustMoles(gas, mols);
mix.Temperature += temp;

if (amountCounter >= count)
return;
}
}
}
Loading

0 comments on commit 2de4f6d

Please sign in to comment.