Tesla (#21124)
* upload all textures finished easiest part - TeslaGenerator * Added Tesla energy logic * add lightning pulse priority * work * optimise lightning arc system * now tesla moving to LightningTarget entity and consume it * some audio work add airlock and computers to the LightningTarget * add nice visual and explosions add crashing to game * rsi meta fix * disabling explosions (crashing server) I'll get back to that problem later. * adding important admin logging * a little bit of cleaning and documentation * Persistent attempts to fix the server crashing on explosions. Accidental cleaning of everything I see. * now the tesla incinerates everything it touches except the containment field. * colliders work * fix falling tesla on ground after being exploded * add consume sound, add spawn and collapses sound * added TeslaGenerator to cargo trading console * add all tesla part to cargo trading console * Tesla coils: Turn on and off, get energy from lightning, give energy to the grid. * tesla coil is ready * tesla grounding rod is ready * clean up * clean up 2 * grounding rods now working without power * add LightningResistance parameter for LightningTarget Component * add chaotic teleport * eletrocution remove? * deltanedas fix pack * more fixes * FIXES * FIIIXEEES * The "Grounding Rod" component is removed, and replaced with "LightiningSparking", which is responsible for changing the visuals when hit by lightning. Duplicate code from the coil is removed. * ops * fix * nah, is escaped anyway * increase tesla collider size * keron bb * try fix test? * fix * bruh * check turn off sus comp * prototype cleaning * FIIX * return and fix sus component * fix tesla eating lightnings, now mini tesla is electrocuted * commented some issues * remove linq sorting fix jumping system minor fixes * fix second Linq * fix tesla colliders! Yeah, it works * fix componentregistration * Just retests * not fix * FIX TESLA * fixes * store targets * back * make dictionary of hashsets * some sloth fixes * stump * playtest balance energy generation, return to unpotimized (but working) lightning shoot * parity * work on * some new fix, some new bug (chasingComponent not chasing) * comment * fix ChasingWalkSystem * fix collider tesla problem * revert old unoptimized shoot lightning * new fix pack --------- Co-authored-by: metalgearsloth <comedian_vs_clown@hotmail.com>
@@ -1,4 +1,4 @@
|
|||||||
using Content.Shared.Lightning.Components;
|
using Content.Shared.Lightning.Components;
|
||||||
|
|
||||||
namespace Content.Server.Lightning.Components;
|
namespace Content.Server.Lightning.Components;
|
||||||
[RegisterComponent]
|
[RegisterComponent]
|
||||||
|
|||||||
@@ -0,0 +1,59 @@
|
|||||||
|
using Content.Server.Tesla.EntitySystems;
|
||||||
|
using Content.Shared.Explosion;
|
||||||
|
using Robust.Shared.Prototypes;
|
||||||
|
|
||||||
|
namespace Content.Server.Lightning.Components;
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// This component allows the lightning system to select a given entity as the target of a lightning strike.
|
||||||
|
/// It also determines the priority of selecting this target, and the behavior of the explosion. Used for tesla.
|
||||||
|
/// </summary>
|
||||||
|
[RegisterComponent, Access(typeof(LightningSystem), typeof(LightningTargetSystem))]
|
||||||
|
public sealed partial class LightningTargetComponent : Component
|
||||||
|
{
|
||||||
|
/// <summary>
|
||||||
|
/// Priority level for selecting a lightning target.
|
||||||
|
/// </summary>
|
||||||
|
[DataField, ViewVariables(VVAccess.ReadWrite)]
|
||||||
|
public int Priority;
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Lightning has a number of bounces into neighboring targets.
|
||||||
|
/// This number controls how many bounces the lightning bolt has left after hitting that target.
|
||||||
|
/// At high values, the lightning will not travel farther than that entity.
|
||||||
|
/// </summary>
|
||||||
|
[DataField, ViewVariables(VVAccess.ReadWrite)]
|
||||||
|
public int LightningResistance = 1; //by default, reduces the number of bounces from this target by 1
|
||||||
|
|
||||||
|
// BOOM PART
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Will the entity explode after being struck by lightning?
|
||||||
|
/// </summary>
|
||||||
|
[DataField, ViewVariables(VVAccess.ReadWrite)]
|
||||||
|
public bool LightningExplode = true;
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// The explosion prototype to spawn
|
||||||
|
/// </summary>
|
||||||
|
[DataField, ViewVariables(VVAccess.ReadWrite)]
|
||||||
|
public ProtoId<ExplosionPrototype> ExplosionPrototype = "Default";
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// The total amount of intensity an explosion can achieve
|
||||||
|
/// </summary>
|
||||||
|
[DataField, ViewVariables(VVAccess.ReadWrite)]
|
||||||
|
public float TotalIntensity = 25f;
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// How quickly does the explosion's power slope? Higher = smaller area and more concentrated damage, lower = larger area and more spread out damage
|
||||||
|
/// </summary>
|
||||||
|
[DataField, ViewVariables(VVAccess.ReadWrite)]
|
||||||
|
public float Dropoff = 2f;
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// How much intensity can be applied per tile?
|
||||||
|
/// </summary>
|
||||||
|
[DataField, ViewVariables(VVAccess.ReadWrite)]
|
||||||
|
public float MaxTileIntensity = 5f;
|
||||||
|
}
|
||||||
@@ -1,27 +1,34 @@
|
|||||||
using System.Linq;
|
using System.Linq;
|
||||||
using Content.Server.Beam;
|
using Content.Server.Beam;
|
||||||
using Content.Server.Beam.Components;
|
using Content.Server.Beam.Components;
|
||||||
using Content.Server.Lightning.Components;
|
using Content.Server.Lightning.Components;
|
||||||
using Content.Shared.Lightning;
|
using Content.Shared.Lightning;
|
||||||
using Robust.Server.GameObjects;
|
using Robust.Server.GameObjects;
|
||||||
using Robust.Shared.Physics;
|
|
||||||
using Robust.Shared.Physics.Dynamics;
|
|
||||||
using Robust.Shared.Physics.Events;
|
|
||||||
using Robust.Shared.Random;
|
using Robust.Shared.Random;
|
||||||
|
|
||||||
namespace Content.Server.Lightning;
|
namespace Content.Server.Lightning;
|
||||||
|
|
||||||
|
// TheShuEd:
|
||||||
|
//I've redesigned the lightning system to be more optimized.
|
||||||
|
//Previously, each lightning element, when it touched something, would try to branch into nearby entities.
|
||||||
|
//So if a lightning bolt was 20 entities long, each one would check its surroundings and have a chance to create additional lightning...
|
||||||
|
//which could lead to recursive creation of more and more lightning bolts and checks.
|
||||||
|
|
||||||
|
//I redesigned so that lightning branches can only be created from the point where the lightning struck, no more collide checks
|
||||||
|
//and the number of these branches is explicitly controlled in the new function.
|
||||||
public sealed class LightningSystem : SharedLightningSystem
|
public sealed class LightningSystem : SharedLightningSystem
|
||||||
{
|
{
|
||||||
[Dependency] private readonly PhysicsSystem _physics = default!;
|
|
||||||
[Dependency] private readonly BeamSystem _beam = default!;
|
[Dependency] private readonly BeamSystem _beam = default!;
|
||||||
[Dependency] private readonly IRobustRandom _random = default!;
|
[Dependency] private readonly IRobustRandom _random = default!;
|
||||||
|
[Dependency] private readonly EntityLookupSystem _lookup = default!;
|
||||||
|
|
||||||
|
private List<Entity<LightningTargetComponent>> _lookupTargetsList = new();
|
||||||
|
private HashSet<Entity<LightningTargetComponent>> _lookupTargets = new();
|
||||||
|
|
||||||
public override void Initialize()
|
public override void Initialize()
|
||||||
{
|
{
|
||||||
base.Initialize();
|
base.Initialize();
|
||||||
|
|
||||||
SubscribeLocalEvent<LightningComponent, StartCollideEvent>(OnCollide);
|
|
||||||
SubscribeLocalEvent<LightningComponent, ComponentRemove>(OnRemove);
|
SubscribeLocalEvent<LightningComponent, ComponentRemove>(OnRemove);
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -35,32 +42,6 @@ public sealed class LightningSystem : SharedLightningSystem
|
|||||||
beamController.CreatedBeams.Remove(uid);
|
beamController.CreatedBeams.Remove(uid);
|
||||||
}
|
}
|
||||||
|
|
||||||
private void OnCollide(EntityUid uid, LightningComponent component, ref StartCollideEvent args)
|
|
||||||
{
|
|
||||||
if (!TryComp<BeamComponent>(uid, out var lightningBeam)
|
|
||||||
|| !TryComp<BeamComponent>(lightningBeam.VirtualBeamController, out var beamController))
|
|
||||||
return;
|
|
||||||
|
|
||||||
if (!component.CanArc || beamController.CreatedBeams.Count >= component.MaxTotalArcs)
|
|
||||||
return;
|
|
||||||
|
|
||||||
Arc(component, args.OtherEntity, lightningBeam.VirtualBeamController.Value);
|
|
||||||
|
|
||||||
if (component.ArcTarget == null)
|
|
||||||
return;
|
|
||||||
|
|
||||||
var spriteState = LightningRandomizer();
|
|
||||||
component.ArcTargets.Add(args.OtherEntity);
|
|
||||||
component.ArcTargets.Add(component.ArcTarget.Value);
|
|
||||||
|
|
||||||
_beam.TryCreateBeam(
|
|
||||||
args.OtherEntity,
|
|
||||||
component.ArcTarget.Value,
|
|
||||||
component.LightningPrototype,
|
|
||||||
spriteState,
|
|
||||||
controller: lightningBeam.VirtualBeamController.Value);
|
|
||||||
}
|
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// Fires lightning from user to target
|
/// Fires lightning from user to target
|
||||||
/// </summary>
|
/// </summary>
|
||||||
@@ -71,68 +52,47 @@ public sealed class LightningSystem : SharedLightningSystem
|
|||||||
{
|
{
|
||||||
var spriteState = LightningRandomizer();
|
var spriteState = LightningRandomizer();
|
||||||
_beam.TryCreateBeam(user, target, lightningPrototype, spriteState);
|
_beam.TryCreateBeam(user, target, lightningPrototype, spriteState);
|
||||||
|
|
||||||
|
var ev = new HitByLightningEvent(user, target);
|
||||||
|
RaiseLocalEvent(target, ref ev);
|
||||||
}
|
}
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// Looks for a target to arc to in all 8 directions, adds the closest to a local dictionary and picks at random
|
/// Looks for objects with a LightningTarget component in the radius, prioritizes them, and hits the highest priority targets with lightning.
|
||||||
/// </summary>
|
/// </summary>
|
||||||
/// <param name="component"></param>
|
/// <param name="user">Where the lightning fires from</param>
|
||||||
/// <param name="target"></param>
|
/// <param name="range">Targets selection radius</param>
|
||||||
/// <param name="controllerEntity"></param>
|
/// <param name="boltCount">Number of lightning bolts</param>
|
||||||
private void Arc(LightningComponent component, EntityUid target, EntityUid controllerEntity)
|
/// <param name="lightningPrototype">The prototype for the lightning to be created</param>
|
||||||
|
/// <param name="arcDepth">how many times to recursively fire lightning bolts from the target points of the first shot.</param>
|
||||||
|
public void ShootRandomLightnings(EntityUid user, float range, int boltCount, string lightningPrototype = "Lightning", int arcDepth = 0)
|
||||||
{
|
{
|
||||||
if (!TryComp<BeamComponent>(controllerEntity, out var controller))
|
//To Do: add support to different priority target tablem for different lightning types
|
||||||
|
//To Do: Remove Hardcode LightningTargetComponent (this should be a parameter of the SharedLightningComponent)
|
||||||
|
//To Do: This is still pretty bad for perf but better than before and at least it doesn't re-allocate
|
||||||
|
// several hashsets every time
|
||||||
|
|
||||||
|
var targets = _lookup.GetComponentsInRange<LightningTargetComponent>(Transform(user).MapPosition, range).ToList();
|
||||||
|
_random.Shuffle(targets);
|
||||||
|
targets.Sort((x, y) => y.Priority.CompareTo(x.Priority));
|
||||||
|
var realCount = Math.Min(targets.Count, boltCount);
|
||||||
|
if (realCount <= 0)
|
||||||
return;
|
return;
|
||||||
|
for (int i = 0; i < realCount; i++)
|
||||||
var targetXForm = Transform(target);
|
|
||||||
var directions = Enum.GetValues<Direction>().Length;
|
|
||||||
|
|
||||||
var lightningQuery = GetEntityQuery<LightningComponent>();
|
|
||||||
var beamQuery = GetEntityQuery<BeamComponent>();
|
|
||||||
var xformQuery = GetEntityQuery<TransformComponent>();
|
|
||||||
|
|
||||||
Dictionary<Direction, EntityUid> arcDirections = new();
|
|
||||||
|
|
||||||
//TODO: Add scoring system for the Tesla PR which will have grounding rods
|
|
||||||
for (int i = 0; i < directions; i++)
|
|
||||||
{
|
{
|
||||||
var direction = (Direction) i;
|
ShootLightning(user, targets[i].Owner, lightningPrototype);
|
||||||
var (targetPos, targetRot) = targetXForm.GetWorldPositionRotation(xformQuery);
|
if (arcDepth > 0)
|
||||||
var dirRad = direction.ToAngle() + targetRot;
|
|
||||||
var ray = new CollisionRay(targetPos, dirRad.ToVec(), component.CollisionMask);
|
|
||||||
var rayCastResults = _physics.IntersectRay(targetXForm.MapID, ray, component.MaxLength, target, false).ToList();
|
|
||||||
|
|
||||||
RayCastResults? closestResult = null;
|
|
||||||
|
|
||||||
foreach (var result in rayCastResults)
|
|
||||||
{
|
{
|
||||||
if (lightningQuery.HasComponent(result.HitEntity)
|
ShootRandomLightnings(targets[i].Owner, range, 1, lightningPrototype, arcDepth - targets[i].LightningResistance);
|
||||||
|| beamQuery.HasComponent(result.HitEntity)
|
|
||||||
|| component.ArcTargets.Contains(result.HitEntity)
|
|
||||||
|| controller.HitTargets.Contains(result.HitEntity)
|
|
||||||
|| controller.BeamShooter == result.HitEntity)
|
|
||||||
{
|
|
||||||
continue;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
closestResult = result;
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (closestResult == null)
|
|
||||||
{
|
|
||||||
continue;
|
|
||||||
}
|
|
||||||
|
|
||||||
arcDirections.Add(direction, closestResult.Value.HitEntity);
|
|
||||||
}
|
|
||||||
|
|
||||||
var randomDirection = (Direction) _random.Next(0, 7);
|
|
||||||
|
|
||||||
if (arcDirections.ContainsKey(randomDirection))
|
|
||||||
{
|
|
||||||
component.ArcTarget = arcDirections.GetValueOrDefault(randomDirection);
|
|
||||||
arcDirections.Clear();
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Raised directed on the target when an entity becomes the target of a lightning strike (not when touched)
|
||||||
|
/// </summary>
|
||||||
|
/// <param name="Source">The entity that created the lightning</param>
|
||||||
|
/// <param name="Target">The entity that was struck by lightning.</param>
|
||||||
|
[ByRefEvent]
|
||||||
|
public readonly record struct HitByLightningEvent(EntityUid Source, EntityUid Target);
|
||||||
|
|||||||
34
Content.Server/Lightning/LightningTargetSystem.cs
Normal file
@@ -0,0 +1,34 @@
|
|||||||
|
using Content.Server.Explosion.EntitySystems;
|
||||||
|
using Content.Server.Lightning;
|
||||||
|
using Content.Server.Lightning.Components;
|
||||||
|
|
||||||
|
namespace Content.Server.Tesla.EntitySystems;
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// The component allows lightning to strike this target. And determining the behavior of the target when struck by lightning.
|
||||||
|
/// </summary>
|
||||||
|
public sealed class LightningTargetSystem : EntitySystem
|
||||||
|
{
|
||||||
|
[Dependency] private readonly ExplosionSystem _explosionSystem = default!;
|
||||||
|
|
||||||
|
public override void Initialize()
|
||||||
|
{
|
||||||
|
base.Initialize();
|
||||||
|
|
||||||
|
SubscribeLocalEvent<LightningTargetComponent, HitByLightningEvent>(OnHitByLightning);
|
||||||
|
}
|
||||||
|
|
||||||
|
private void OnHitByLightning(Entity<LightningTargetComponent> uid, ref HitByLightningEvent args)
|
||||||
|
{
|
||||||
|
|
||||||
|
if (!uid.Comp.LightningExplode)
|
||||||
|
return;
|
||||||
|
|
||||||
|
_explosionSystem.QueueExplosion(
|
||||||
|
Transform(uid).MapPosition,
|
||||||
|
uid.Comp.ExplosionPrototype,
|
||||||
|
uid.Comp.TotalIntensity, uid.Comp.Dropoff,
|
||||||
|
uid.Comp.MaxTileIntensity,
|
||||||
|
canCreateVacuum: false);
|
||||||
|
}
|
||||||
|
}
|
||||||
54
Content.Server/Physics/Components/ChaoticJumpComponent.cs
Normal file
@@ -0,0 +1,54 @@
|
|||||||
|
using Content.Server.Physics.Controllers;
|
||||||
|
using Content.Shared.Physics;
|
||||||
|
using Robust.Shared.Prototypes;
|
||||||
|
using Robust.Shared.Serialization.TypeSerializers.Implementations.Custom;
|
||||||
|
|
||||||
|
namespace Content.Server.Physics.Components;
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// A component which makes its entity periodically chaotic jumps arounds
|
||||||
|
/// </summary>
|
||||||
|
[RegisterComponent, Access(typeof(ChaoticJumpSystem))]
|
||||||
|
public sealed partial class ChaoticJumpComponent : Component
|
||||||
|
{
|
||||||
|
/// <summary>
|
||||||
|
/// The next moment in time when the entity is pushed toward its goal
|
||||||
|
/// </summary>
|
||||||
|
[DataField(customTypeSerializer: typeof(TimeOffsetSerializer)), ViewVariables(VVAccess.ReadWrite)]
|
||||||
|
public TimeSpan NextJumpTime;
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Minimum interval between jumps
|
||||||
|
/// </summary>
|
||||||
|
[DataField, ViewVariables(VVAccess.ReadWrite)]
|
||||||
|
public float JumpMinInterval = 5f;
|
||||||
|
/// <summary>
|
||||||
|
/// Maximum interval between jumps
|
||||||
|
/// </summary>
|
||||||
|
[DataField, ViewVariables(VVAccess.ReadWrite)]
|
||||||
|
public float JumpMaxInterval = 15f;
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// collision limits for which it is impossible to make a jump
|
||||||
|
/// </summary>
|
||||||
|
[DataField, ViewVariables(VVAccess.ReadWrite)]
|
||||||
|
public int CollisionMask = (int) CollisionGroup.Impassable;
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Minimum jump range
|
||||||
|
/// </summary>
|
||||||
|
[DataField, ViewVariables(VVAccess.ReadWrite)]
|
||||||
|
public float RangeMin = 5f;
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Maximum jump range
|
||||||
|
/// </summary>
|
||||||
|
[DataField, ViewVariables(VVAccess.ReadWrite)]
|
||||||
|
public float RangeMax = 10f;
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Spawn before jump
|
||||||
|
/// </summary>
|
||||||
|
[DataField, ViewVariables(VVAccess.ReadWrite)]
|
||||||
|
public EntProtoId Effect = "EffectEmpPulse";
|
||||||
|
}
|
||||||
79
Content.Server/Physics/Components/ChasingWalkComponent.cs
Normal file
@@ -0,0 +1,79 @@
|
|||||||
|
|
||||||
|
using Content.Server.Physics.Controllers;
|
||||||
|
using Robust.Shared.Prototypes;
|
||||||
|
using Robust.Shared.Serialization.TypeSerializers.Implementations.Custom;
|
||||||
|
|
||||||
|
namespace Content.Server.Physics.Components;
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// A component which makes its entity chasing entity with selected component.
|
||||||
|
/// </summary>
|
||||||
|
[RegisterComponent, Access(typeof(ChasingWalkSystem))]
|
||||||
|
public sealed partial class ChasingWalkComponent : Component
|
||||||
|
{
|
||||||
|
/// <summary>
|
||||||
|
/// The next moment in time when the entity is pushed toward its goal
|
||||||
|
/// </summary>
|
||||||
|
[DataField, ViewVariables(VVAccess.ReadWrite)]
|
||||||
|
public TimeSpan NextImpulseTime;
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Push-to-target frequency.
|
||||||
|
/// </summary>
|
||||||
|
[DataField, ViewVariables(VVAccess.ReadWrite)]
|
||||||
|
public float ImpulseInterval = 2f;
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// The minimum speed at which this entity will move.
|
||||||
|
/// </summary>
|
||||||
|
[DataField, ViewVariables(VVAccess.ReadWrite)]
|
||||||
|
public float MinSpeed = 1.5f;
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// The maximum speed at which this entity will move.
|
||||||
|
/// </summary>
|
||||||
|
[DataField, ViewVariables(VVAccess.ReadWrite)]
|
||||||
|
public float MaxSpeed = 3f;
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// The current speed.
|
||||||
|
/// </summary>
|
||||||
|
[DataField, ViewVariables(VVAccess.ReadWrite)]
|
||||||
|
public float Speed;
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// The minimum time interval in which an object can change its motion target.
|
||||||
|
/// </summary>
|
||||||
|
[DataField, ViewVariables(VVAccess.ReadWrite)]
|
||||||
|
public float ChangeVectorMinInterval = 5f;
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// The maximum time interval in which an object can change its motion target.
|
||||||
|
/// </summary>
|
||||||
|
[DataField, ViewVariables(VVAccess.ReadWrite)]
|
||||||
|
public float ChangeVectorMaxInterval = 25f;
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// The next change of direction time.
|
||||||
|
/// </summary>
|
||||||
|
[DataField(customTypeSerializer: typeof(TimeOffsetSerializer)), ViewVariables(VVAccess.ReadWrite)]
|
||||||
|
public TimeSpan NextChangeVectorTime;
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// The component that the entity is chasing
|
||||||
|
/// </summary>
|
||||||
|
[DataField(required: true)]
|
||||||
|
public ComponentRegistry ChasingComponent = default!;
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// The maximum radius in which the entity chooses the target component to follow
|
||||||
|
/// </summary>
|
||||||
|
[DataField, ViewVariables(VVAccess.ReadWrite)]
|
||||||
|
public float MaxChaseRadius = 25;
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// The entity uid, chasing by the component owner
|
||||||
|
/// </summary>
|
||||||
|
[DataField, ViewVariables(VVAccess.ReadWrite)]
|
||||||
|
public EntityUid? ChasingEntity;
|
||||||
|
}
|
||||||
78
Content.Server/Physics/Controllers/ChaoticJumpSystem.cs
Normal file
@@ -0,0 +1,78 @@
|
|||||||
|
using Content.Server.Physics.Components;
|
||||||
|
using Robust.Shared.Random;
|
||||||
|
using Robust.Shared.Timing;
|
||||||
|
using Robust.Shared.Physics.Systems;
|
||||||
|
using Robust.Shared.Physics;
|
||||||
|
using System.Numerics;
|
||||||
|
using Robust.Shared.Physics.Controllers;
|
||||||
|
using Robust.Shared.Utility;
|
||||||
|
|
||||||
|
namespace Content.Server.Physics.Controllers;
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// A component which makes its entity periodically chaotic jumps arounds
|
||||||
|
/// </summary>
|
||||||
|
public sealed class ChaoticJumpSystem : VirtualController
|
||||||
|
{
|
||||||
|
[Dependency] private readonly IGameTiming _gameTiming = default!;
|
||||||
|
[Dependency] private readonly SharedTransformSystem _transform = default!;
|
||||||
|
[Dependency] private readonly IRobustRandom _random = default!;
|
||||||
|
[Dependency] private readonly SharedPhysicsSystem _physics = default!;
|
||||||
|
[Dependency] private readonly SharedTransformSystem _xform = default!;
|
||||||
|
|
||||||
|
public override void Initialize()
|
||||||
|
{
|
||||||
|
base.Initialize();
|
||||||
|
|
||||||
|
SubscribeLocalEvent<ChaoticJumpComponent, MapInitEvent>(OnMapInit);
|
||||||
|
}
|
||||||
|
|
||||||
|
private void OnMapInit(Entity<ChaoticJumpComponent> chaotic, ref MapInitEvent args)
|
||||||
|
{
|
||||||
|
//So the entity doesn't teleport instantly. For tesla, for example, it's important for it to eat tesla's generator.
|
||||||
|
chaotic.Comp.NextJumpTime = _gameTiming.CurTime + TimeSpan.FromSeconds(_random.NextFloat(chaotic.Comp.JumpMinInterval, chaotic.Comp.JumpMaxInterval));
|
||||||
|
}
|
||||||
|
|
||||||
|
public override void UpdateBeforeSolve(bool prediction, float frameTime)
|
||||||
|
{
|
||||||
|
base.UpdateBeforeSolve(prediction, frameTime);
|
||||||
|
|
||||||
|
var query = EntityQueryEnumerator<ChaoticJumpComponent>();
|
||||||
|
while (query.MoveNext(out var uid, out var chaotic))
|
||||||
|
{
|
||||||
|
//Jump
|
||||||
|
if (chaotic.NextJumpTime <= _gameTiming.CurTime)
|
||||||
|
{
|
||||||
|
Jump(uid, chaotic);
|
||||||
|
chaotic.NextJumpTime += TimeSpan.FromSeconds(_random.NextFloat(chaotic.JumpMinInterval, chaotic.JumpMaxInterval));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private void Jump(EntityUid uid, ChaoticJumpComponent component)
|
||||||
|
{
|
||||||
|
var transform = Transform(uid);
|
||||||
|
|
||||||
|
var startPos = _transform.GetWorldPosition(uid);
|
||||||
|
Vector2 targetPos;
|
||||||
|
|
||||||
|
var direction = _random.NextAngle();
|
||||||
|
var range = _random.NextFloat(component.RangeMin, component.RangeMax);
|
||||||
|
var ray = new CollisionRay(startPos, direction.ToVec(), component.CollisionMask);
|
||||||
|
var rayCastResults = _physics.IntersectRay(transform.MapID, ray, range, uid, returnOnFirstHit: false).FirstOrNull();
|
||||||
|
|
||||||
|
if (rayCastResults != null)
|
||||||
|
{
|
||||||
|
targetPos = rayCastResults.Value.HitPos;
|
||||||
|
targetPos = new Vector2(targetPos.X - (float) Math.Cos(direction), targetPos.Y - (float) Math.Sin(direction)); //offset so that the teleport does not take place directly inside the target
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
targetPos = new Vector2(startPos.X + range * (float) Math.Cos(direction), startPos.Y + range * (float) Math.Sin(direction));
|
||||||
|
}
|
||||||
|
|
||||||
|
Spawn(component.Effect, transform.Coordinates);
|
||||||
|
|
||||||
|
_xform.SetWorldPosition(uid, targetPos);
|
||||||
|
}
|
||||||
|
}
|
||||||
109
Content.Server/Physics/Controllers/ChasingWalkSystem.cs
Normal file
@@ -0,0 +1,109 @@
|
|||||||
|
using System.Linq;
|
||||||
|
using System.Numerics;
|
||||||
|
using Content.Server.Physics.Components;
|
||||||
|
using Robust.Shared.Random;
|
||||||
|
using Robust.Shared.Timing;
|
||||||
|
using Robust.Shared.Physics.Systems;
|
||||||
|
using Robust.Shared.Physics.Components;
|
||||||
|
using Robust.Shared.Physics.Controllers;
|
||||||
|
|
||||||
|
namespace Content.Server.Physics.Controllers;
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// A system which makes its entity chasing another entity with selected component.
|
||||||
|
/// </summary>
|
||||||
|
public sealed class ChasingWalkSystem : VirtualController
|
||||||
|
{
|
||||||
|
[Dependency] private readonly IGameTiming _gameTiming = default!;
|
||||||
|
[Dependency] private readonly IRobustRandom _random = default!;
|
||||||
|
[Dependency] private readonly SharedTransformSystem _transform = default!;
|
||||||
|
[Dependency] private readonly EntityLookupSystem _lookup = default!;
|
||||||
|
[Dependency] private readonly SharedPhysicsSystem _physics = default!;
|
||||||
|
|
||||||
|
private readonly HashSet<Entity<IComponent>> _potentialChaseTargets = new();
|
||||||
|
|
||||||
|
public override void Initialize()
|
||||||
|
{
|
||||||
|
base.Initialize();
|
||||||
|
|
||||||
|
SubscribeLocalEvent<ChasingWalkComponent, MapInitEvent>(OnChasingMapInit);
|
||||||
|
SubscribeLocalEvent<ChasingWalkComponent, EntityUnpausedEvent>(OnChasingUnpaused);
|
||||||
|
}
|
||||||
|
|
||||||
|
private void OnChasingMapInit(EntityUid uid, ChasingWalkComponent component, MapInitEvent args)
|
||||||
|
{
|
||||||
|
component.NextImpulseTime = _gameTiming.CurTime;
|
||||||
|
component.NextChangeVectorTime = _gameTiming.CurTime;
|
||||||
|
}
|
||||||
|
|
||||||
|
private void OnChasingUnpaused(EntityUid uid, ChasingWalkComponent component, ref EntityUnpausedEvent args)
|
||||||
|
{
|
||||||
|
component.NextImpulseTime += args.PausedTime;
|
||||||
|
component.NextChangeVectorTime += args.PausedTime;
|
||||||
|
}
|
||||||
|
|
||||||
|
public override void UpdateBeforeSolve(bool prediction, float frameTime)
|
||||||
|
{
|
||||||
|
base.UpdateBeforeSolve(prediction, frameTime);
|
||||||
|
|
||||||
|
var query = EntityQueryEnumerator<ChasingWalkComponent>();
|
||||||
|
while (query.MoveNext(out var uid, out var chasing))
|
||||||
|
{
|
||||||
|
//Set Velocity to Target
|
||||||
|
if (chasing.NextImpulseTime <= _gameTiming.CurTime)
|
||||||
|
{
|
||||||
|
ForceImpulse(uid, chasing);
|
||||||
|
chasing.NextImpulseTime += TimeSpan.FromSeconds(chasing.ImpulseInterval);
|
||||||
|
}
|
||||||
|
//Change Target
|
||||||
|
if (chasing.NextChangeVectorTime <= _gameTiming.CurTime)
|
||||||
|
{
|
||||||
|
ChangeTarget(uid, chasing);
|
||||||
|
|
||||||
|
var delay = TimeSpan.FromSeconds(_random.NextFloat(chasing.ChangeVectorMinInterval, chasing.ChangeVectorMaxInterval));
|
||||||
|
chasing.NextChangeVectorTime += delay;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private void ChangeTarget(EntityUid uid, ChasingWalkComponent component)
|
||||||
|
{
|
||||||
|
//We find our coordinates and calculate the radius of the target search.
|
||||||
|
var xform = Transform(uid);
|
||||||
|
var range = component.MaxChaseRadius;
|
||||||
|
var compType = _random.Pick(component.ChasingComponent.Values).Component.GetType();
|
||||||
|
_potentialChaseTargets.Clear();
|
||||||
|
_lookup.GetEntitiesInRange(compType, _transform.GetMapCoordinates(xform), range, _potentialChaseTargets, LookupFlags.Uncontained);
|
||||||
|
|
||||||
|
//If there are no required components in the radius, don't moving.
|
||||||
|
if (_potentialChaseTargets.Count <= 0)
|
||||||
|
return;
|
||||||
|
|
||||||
|
//In the case of finding required components, we choose a random one of them and remember its uid.
|
||||||
|
component.ChasingEntity = _random.Pick(_potentialChaseTargets).Owner;
|
||||||
|
component.Speed = _random.NextFloat(component.MinSpeed, component.MaxSpeed);
|
||||||
|
}
|
||||||
|
|
||||||
|
//pushing the entity toward its target
|
||||||
|
private void ForceImpulse(EntityUid uid, ChasingWalkComponent component)
|
||||||
|
{
|
||||||
|
if (Deleted(component.ChasingEntity) || component.ChasingEntity == null)
|
||||||
|
{
|
||||||
|
ChangeTarget(uid, component);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!TryComp<PhysicsComponent>(uid, out var physics))
|
||||||
|
return;
|
||||||
|
|
||||||
|
//Calculating direction to the target.
|
||||||
|
var pos1 = _transform.GetWorldPosition(uid);
|
||||||
|
var pos2 = _transform.GetWorldPosition(component.ChasingEntity.Value);
|
||||||
|
|
||||||
|
var delta = pos2 - pos1;
|
||||||
|
var speed = delta.Length() > 0 ? delta.Normalized() * component.Speed : Vector2.Zero;
|
||||||
|
|
||||||
|
_physics.SetLinearVelocity(uid, speed);
|
||||||
|
_physics.SetBodyStatus(physics, BodyStatus.InAir); //If this is not done, from the explosion up close, the tesla will "Fall" to the ground, and almost stop moving.
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -325,7 +325,9 @@ public sealed class EventHorizonSystem : SharedEventHorizonSystem
|
|||||||
if (!Resolve(uid, ref xform, ref eventHorizon))
|
if (!Resolve(uid, ref xform, ref eventHorizon))
|
||||||
return;
|
return;
|
||||||
|
|
||||||
|
if (eventHorizon.ConsumeEntities)
|
||||||
ConsumeEntitiesInRange(uid, range, xform, eventHorizon);
|
ConsumeEntitiesInRange(uid, range, xform, eventHorizon);
|
||||||
|
if (eventHorizon.ConsumeTiles)
|
||||||
ConsumeTilesInRange(uid, range, xform, eventHorizon);
|
ConsumeTilesInRange(uid, range, xform, eventHorizon);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -1,4 +1,4 @@
|
|||||||
using Content.Server.ParticleAccelerator.Components;
|
using Content.Server.ParticleAccelerator.Components;
|
||||||
using Content.Server.Singularity.Components;
|
using Content.Server.Singularity.Components;
|
||||||
using Content.Shared.Singularity.Components;
|
using Content.Shared.Singularity.Components;
|
||||||
using Robust.Shared.Physics.Events;
|
using Robust.Shared.Physics.Events;
|
||||||
|
|||||||
@@ -0,0 +1,55 @@
|
|||||||
|
using Content.Server.Tesla.EntitySystems;
|
||||||
|
using Robust.Shared.Prototypes;
|
||||||
|
|
||||||
|
namespace Content.Server.Tesla.Components;
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Periodically fires electric arcs at surrounding objects.
|
||||||
|
/// </summary>
|
||||||
|
[RegisterComponent, Access(typeof(LightningArcShooterSystem))]
|
||||||
|
public sealed partial class LightningArcShooterComponent : Component
|
||||||
|
{
|
||||||
|
/// <summary>
|
||||||
|
/// The number of lightning bolts that are fired at the same time. From 0 to N
|
||||||
|
/// Important balance value: if there aren't a N number of coils or grounders around the tesla,
|
||||||
|
/// the tesla will have a chance to shoot into something important and break.
|
||||||
|
/// </summary>
|
||||||
|
[DataField, ViewVariables(VVAccess.ReadWrite)]
|
||||||
|
public int MaxLightningArc = 1;
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Minimum interval between shooting.
|
||||||
|
/// </summary>
|
||||||
|
[DataField, ViewVariables(VVAccess.ReadWrite)]
|
||||||
|
public float ShootMinInterval = 0.5f;
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Maximum interval between shooting.
|
||||||
|
/// </summary>
|
||||||
|
[DataField, ViewVariables(VVAccess.ReadWrite)]
|
||||||
|
public float ShootMaxInterval = 8.0f;
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// the target selection radius for lightning bolts.
|
||||||
|
/// </summary>
|
||||||
|
[DataField, ViewVariables(VVAccess.ReadWrite)]
|
||||||
|
public float ShootRange = 5f;
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// How many times after a hit the lightning bolt will bounce into an adjacent target
|
||||||
|
/// </summary>
|
||||||
|
[DataField, ViewVariables(VVAccess.ReadWrite)]
|
||||||
|
public int ArcDepth = 1;
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// The time, upon reaching which the next batch of lightning bolts will be fired.
|
||||||
|
/// </summary>
|
||||||
|
[DataField, ViewVariables(VVAccess.ReadWrite)]
|
||||||
|
public TimeSpan NextShootTime;
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// The type of lightning bolts we shoot
|
||||||
|
/// </summary>
|
||||||
|
[DataField, ViewVariables(VVAccess.ReadWrite)]
|
||||||
|
public EntProtoId LightningPrototype = "Lightning";
|
||||||
|
}
|
||||||
@@ -0,0 +1,27 @@
|
|||||||
|
using Content.Server.Tesla.EntitySystems;
|
||||||
|
using Robust.Shared.Audio;
|
||||||
|
using Robust.Shared.Serialization.TypeSerializers.Implementations.Custom;
|
||||||
|
|
||||||
|
namespace Content.Server.Tesla.Components;
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// The component changes the visual of an object after it is struck by lightning
|
||||||
|
/// </summary>
|
||||||
|
[RegisterComponent, Access(typeof(LightningSparkingSystem))]
|
||||||
|
public sealed partial class LightningSparkingComponent : Component
|
||||||
|
{
|
||||||
|
/// <summary>
|
||||||
|
/// Spark duration.
|
||||||
|
/// </summary>
|
||||||
|
[DataField, ViewVariables(VVAccess.ReadWrite)]
|
||||||
|
public float LightningTime = 4;
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// When the spark visual should turn off.
|
||||||
|
/// </summary>
|
||||||
|
[DataField(customTypeSerializer: typeof(TimeOffsetSerializer))]
|
||||||
|
public TimeSpan LightningEndTime;
|
||||||
|
|
||||||
|
[DataField, ViewVariables(VVAccess.ReadWrite)]
|
||||||
|
public bool IsSparking;
|
||||||
|
}
|
||||||
19
Content.Server/Tesla/Components/TeslaCoilComponent.cs
Normal file
@@ -0,0 +1,19 @@
|
|||||||
|
using Content.Server.Tesla.EntitySystems;
|
||||||
|
using Robust.Shared.Audio;
|
||||||
|
using Robust.Shared.Serialization.TypeSerializers.Implementations.Custom;
|
||||||
|
|
||||||
|
namespace Content.Server.Tesla.Components;
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Generates electricity from lightning bolts
|
||||||
|
/// </summary>
|
||||||
|
[RegisterComponent, Access(typeof(TeslaCoilSystem))]
|
||||||
|
public sealed partial class TeslaCoilComponent : Component
|
||||||
|
{
|
||||||
|
/// <summary>
|
||||||
|
/// How much power will the coil generate from a lightning strike
|
||||||
|
/// </summary>
|
||||||
|
// To Do: Different lightning bolts have different powers and generate different amounts of energy
|
||||||
|
[DataField, ViewVariables(VVAccess.ReadWrite)]
|
||||||
|
public float ChargeFromLightning = 50000f;
|
||||||
|
}
|
||||||
42
Content.Server/Tesla/Components/TeslaEnergyBallComponent.cs
Normal file
@@ -0,0 +1,42 @@
|
|||||||
|
using Content.Server.Tesla.EntitySystems;
|
||||||
|
using Robust.Shared.Audio;
|
||||||
|
using Robust.Shared.Prototypes;
|
||||||
|
|
||||||
|
namespace Content.Server.Tesla.Components;
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// A component that tracks an entity's saturation level from absorbing other creatures by touch, and spawns new entities when the saturation limit is reached.
|
||||||
|
/// </summary>
|
||||||
|
[RegisterComponent, Access(typeof(TeslaEnergyBallSystem))]
|
||||||
|
public sealed partial class TeslaEnergyBallComponent : Component
|
||||||
|
{
|
||||||
|
/// <summary>
|
||||||
|
/// how much energy will Tesla get by eating various things. Walls, people, anything.
|
||||||
|
/// </summary>
|
||||||
|
[DataField, ViewVariables(VVAccess.ReadWrite)]
|
||||||
|
public float ConsumeStuffEnergy = 2f;
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// The amount of energy this entity contains. Once the limit is reached, the energy will be spent to spawn mini-energy balls
|
||||||
|
/// </summary>
|
||||||
|
[DataField, ViewVariables(VVAccess.ReadWrite)]
|
||||||
|
public float Energy;
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// The amount of energy an entity must reach in order to zero the energy and create another entity
|
||||||
|
/// </summary>
|
||||||
|
[DataField, ViewVariables(VVAccess.ReadWrite)]
|
||||||
|
public float NeedEnergyToSpawn = 100f;
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Entities that spawn when the energy limit is reached
|
||||||
|
/// </summary>
|
||||||
|
[DataField, ViewVariables(VVAccess.ReadWrite)]
|
||||||
|
public EntProtoId SpawnProto = "TeslaMiniEnergyBall";
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Entity, spun when tesla gobbles with touch.
|
||||||
|
/// </summary>
|
||||||
|
[DataField, ViewVariables(VVAccess.ReadWrite)]
|
||||||
|
public EntProtoId ConsumeEffectProto = "EffectTeslaSparks";
|
||||||
|
}
|
||||||
@@ -0,0 +1,55 @@
|
|||||||
|
using Content.Server.Lightning;
|
||||||
|
using Content.Server.Tesla.Components;
|
||||||
|
using Robust.Shared.Random;
|
||||||
|
using Robust.Shared.Timing;
|
||||||
|
|
||||||
|
namespace Content.Server.Tesla.EntitySystems;
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Fires electric arcs at surrounding objects.
|
||||||
|
/// </summary>
|
||||||
|
public sealed class LightningArcShooterSystem : EntitySystem
|
||||||
|
{
|
||||||
|
[Dependency] private readonly IGameTiming _gameTiming = default!;
|
||||||
|
[Dependency] private readonly LightningSystem _lightning = default!;
|
||||||
|
[Dependency] private readonly IRobustRandom _random = default!;
|
||||||
|
|
||||||
|
public override void Initialize()
|
||||||
|
{
|
||||||
|
base.Initialize();
|
||||||
|
SubscribeLocalEvent<LightningArcShooterComponent, MapInitEvent>(OnShooterMapInit);
|
||||||
|
SubscribeLocalEvent<LightningArcShooterComponent, EntityUnpausedEvent>(OnShooterUnpaused);
|
||||||
|
}
|
||||||
|
|
||||||
|
private void OnShooterMapInit(EntityUid uid, LightningArcShooterComponent component, ref MapInitEvent args)
|
||||||
|
{
|
||||||
|
component.NextShootTime = _gameTiming.CurTime;
|
||||||
|
}
|
||||||
|
|
||||||
|
private void OnShooterUnpaused(EntityUid uid, LightningArcShooterComponent component, ref EntityUnpausedEvent args)
|
||||||
|
{
|
||||||
|
component.NextShootTime += args.PausedTime;
|
||||||
|
}
|
||||||
|
|
||||||
|
public override void Update(float frameTime)
|
||||||
|
{
|
||||||
|
base.Update(frameTime);
|
||||||
|
|
||||||
|
var query = EntityQueryEnumerator<LightningArcShooterComponent>();
|
||||||
|
while (query.MoveNext(out var uid, out var arcShooter))
|
||||||
|
{
|
||||||
|
if (arcShooter.NextShootTime > _gameTiming.CurTime)
|
||||||
|
continue;
|
||||||
|
|
||||||
|
ArcShoot(uid, arcShooter);
|
||||||
|
var delay = TimeSpan.FromSeconds(_random.NextFloat(arcShooter.ShootMinInterval, arcShooter.ShootMaxInterval));
|
||||||
|
arcShooter.NextShootTime += delay;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private void ArcShoot(EntityUid uid, LightningArcShooterComponent component)
|
||||||
|
{
|
||||||
|
var arcs = _random.Next(1, component.MaxLightningArc);
|
||||||
|
_lightning.ShootRandomLightnings(uid, component.ShootRange, arcs, component.LightningPrototype, component.ArcDepth);
|
||||||
|
}
|
||||||
|
}
|
||||||
53
Content.Server/Tesla/EntitySystem/LightningSparkingSystem.cs
Normal file
@@ -0,0 +1,53 @@
|
|||||||
|
using Content.Server.Tesla.Components;
|
||||||
|
using Content.Server.Lightning;
|
||||||
|
using Content.Shared.Power;
|
||||||
|
using Robust.Shared.Timing;
|
||||||
|
|
||||||
|
namespace Content.Server.Tesla.EntitySystems;
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// The component changes the visual of an object after it is struck by lightning
|
||||||
|
/// </summary>
|
||||||
|
public sealed class LightningSparkingSystem : EntitySystem
|
||||||
|
{
|
||||||
|
[Dependency] private readonly SharedAppearanceSystem _appearance = default!;
|
||||||
|
[Dependency] private readonly IGameTiming _gameTiming = default!;
|
||||||
|
|
||||||
|
public override void Initialize()
|
||||||
|
{
|
||||||
|
base.Initialize();
|
||||||
|
|
||||||
|
SubscribeLocalEvent<LightningSparkingComponent, HitByLightningEvent>(OnHitByLightning);
|
||||||
|
SubscribeLocalEvent<LightningSparkingComponent, EntityUnpausedEvent>(OnLightningUnpaused);
|
||||||
|
}
|
||||||
|
|
||||||
|
private void OnLightningUnpaused(EntityUid uid, LightningSparkingComponent component, ref EntityUnpausedEvent args)
|
||||||
|
{
|
||||||
|
component.LightningEndTime += args.PausedTime;
|
||||||
|
}
|
||||||
|
|
||||||
|
private void OnHitByLightning(Entity<LightningSparkingComponent> uid, ref HitByLightningEvent args)
|
||||||
|
{
|
||||||
|
_appearance.SetData(uid.Owner, TeslaCoilVisuals.Lightning, true);
|
||||||
|
uid.Comp.LightningEndTime = _gameTiming.CurTime + TimeSpan.FromSeconds(uid.Comp.LightningTime);
|
||||||
|
uid.Comp.IsSparking = true;
|
||||||
|
}
|
||||||
|
|
||||||
|
public override void Update(float frameTime)
|
||||||
|
{
|
||||||
|
base.Update(frameTime);
|
||||||
|
|
||||||
|
var query = EntityQueryEnumerator<LightningSparkingComponent>();
|
||||||
|
while (query.MoveNext(out var uid, out var component))
|
||||||
|
{
|
||||||
|
if (!component.IsSparking)
|
||||||
|
continue;
|
||||||
|
|
||||||
|
if (component.LightningEndTime < _gameTiming.CurTime)
|
||||||
|
{
|
||||||
|
_appearance.SetData(uid, TeslaCoilVisuals.Lightning, false);
|
||||||
|
component.IsSparking = false;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
32
Content.Server/Tesla/EntitySystem/TeslaCoilSystem.cs
Normal file
@@ -0,0 +1,32 @@
|
|||||||
|
using Content.Server.Popups;
|
||||||
|
using Content.Server.Power.Components;
|
||||||
|
using Content.Server.Power.EntitySystems;
|
||||||
|
using Content.Server.Tesla.Components;
|
||||||
|
using Content.Server.Lightning;
|
||||||
|
using Robust.Shared.Timing;
|
||||||
|
|
||||||
|
namespace Content.Server.Tesla.EntitySystems;
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Generates electricity from lightning bolts
|
||||||
|
/// </summary>
|
||||||
|
public sealed class TeslaCoilSystem : EntitySystem
|
||||||
|
{
|
||||||
|
[Dependency] private readonly BatterySystem _battery = default!;
|
||||||
|
|
||||||
|
public override void Initialize()
|
||||||
|
{
|
||||||
|
base.Initialize();
|
||||||
|
|
||||||
|
SubscribeLocalEvent<TeslaCoilComponent, HitByLightningEvent>(OnHitByLightning);
|
||||||
|
}
|
||||||
|
|
||||||
|
//When struck by lightning, charge the internal battery
|
||||||
|
private void OnHitByLightning(Entity<TeslaCoilComponent> coil, ref HitByLightningEvent args)
|
||||||
|
{
|
||||||
|
if (!TryComp<BatteryComponent>(coil, out var batteryComponent))
|
||||||
|
return;
|
||||||
|
|
||||||
|
_battery.SetCharge(coil, batteryComponent.CurrentCharge + coil.Comp.ChargeFromLightning);
|
||||||
|
}
|
||||||
|
}
|
||||||
51
Content.Server/Tesla/EntitySystem/TeslaEnergyBallSystem.cs
Normal file
@@ -0,0 +1,51 @@
|
|||||||
|
using Content.Server.Administration.Logs;
|
||||||
|
using Content.Server.Singularity.Components;
|
||||||
|
using Content.Server.Tesla.Components;
|
||||||
|
using Content.Shared.Database;
|
||||||
|
using Content.Shared.Singularity.Components;
|
||||||
|
using Content.Shared.Mind.Components;
|
||||||
|
using Content.Shared.Tag;
|
||||||
|
using Robust.Shared.Physics.Events;
|
||||||
|
using Content.Server.Lightning.Components;
|
||||||
|
using Robust.Server.Audio;
|
||||||
|
using Content.Server.Singularity.Events;
|
||||||
|
|
||||||
|
namespace Content.Server.Tesla.EntitySystems;
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// A component that tracks an entity's saturation level from absorbing other creatures by touch, and spawns new entities when the saturation limit is reached.
|
||||||
|
/// </summary>
|
||||||
|
public sealed class TeslaEnergyBallSystem : EntitySystem
|
||||||
|
{
|
||||||
|
[Dependency] private readonly AudioSystem _audio = default!;
|
||||||
|
|
||||||
|
public override void Initialize()
|
||||||
|
{
|
||||||
|
base.Initialize();
|
||||||
|
|
||||||
|
SubscribeLocalEvent<TeslaEnergyBallComponent, EntityConsumedByEventHorizonEvent>(OnConsumed);
|
||||||
|
}
|
||||||
|
|
||||||
|
private void OnConsumed(Entity<TeslaEnergyBallComponent> tesla, ref EntityConsumedByEventHorizonEvent args)
|
||||||
|
{
|
||||||
|
Spawn(tesla.Comp.ConsumeEffectProto, Transform(args.Entity).Coordinates);
|
||||||
|
if (TryComp<SinguloFoodComponent>(args.Entity, out var singuloFood))
|
||||||
|
{
|
||||||
|
AdjustEnergy(tesla, tesla.Comp, singuloFood.Energy);
|
||||||
|
} else
|
||||||
|
{
|
||||||
|
AdjustEnergy(tesla, tesla.Comp, tesla.Comp.ConsumeStuffEnergy);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public void AdjustEnergy(EntityUid uid, TeslaEnergyBallComponent component, float delta)
|
||||||
|
{
|
||||||
|
component.Energy += delta;
|
||||||
|
|
||||||
|
if (component.Energy > component.NeedEnergyToSpawn)
|
||||||
|
{
|
||||||
|
component.Energy -= component.NeedEnergyToSpawn;
|
||||||
|
Spawn(component.SpawnProto, Transform(uid).Coordinates);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -1,4 +1,4 @@
|
|||||||
using Content.Shared.Physics;
|
using Content.Shared.Physics;
|
||||||
using Robust.Shared.Prototypes;
|
using Robust.Shared.Prototypes;
|
||||||
using Robust.Shared.Serialization.TypeSerializers.Implementations.Custom.Prototype;
|
using Robust.Shared.Serialization.TypeSerializers.Implementations.Custom.Prototype;
|
||||||
|
|
||||||
|
|||||||
10
Content.Shared/Power/TeslaCoilVisuals.cs
Normal file
@@ -0,0 +1,10 @@
|
|||||||
|
using Robust.Shared.Serialization;
|
||||||
|
|
||||||
|
namespace Content.Shared.Power;
|
||||||
|
|
||||||
|
[Serializable, NetSerializable]
|
||||||
|
public enum TeslaCoilVisuals : byte
|
||||||
|
{
|
||||||
|
Enabled,
|
||||||
|
Lightning
|
||||||
|
}
|
||||||
@@ -21,6 +21,18 @@ public sealed partial class EventHorizonComponent : Component
|
|||||||
[DataField("radius")]
|
[DataField("radius")]
|
||||||
public float Radius;
|
public float Radius;
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// involves periodically destroying tiles within a specified radius
|
||||||
|
/// </summary>
|
||||||
|
[DataField]
|
||||||
|
public bool ConsumeTiles = true;
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// involves periodically destroying entities within a specified radius. Does not affect collide destruction of entities.
|
||||||
|
/// </summary>
|
||||||
|
[DataField]
|
||||||
|
public bool ConsumeEntities = true;
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// Whether the event horizon can consume/destroy the devices built to contain it.
|
/// Whether the event horizon can consume/destroy the devices built to contain it.
|
||||||
/// If you want to set this go through <see cref="SharedEventHorizonSystem.SetCanBreachContainment"/>.
|
/// If you want to set this go through <see cref="SharedEventHorizonSystem.SetCanBreachContainment"/>.
|
||||||
|
|||||||
@@ -68,6 +68,21 @@
|
|||||||
copyright: '"Pop, High, A (H1).wav" by InspectorJ (www.jshaw.co.uk) of Freesound.org. Mixed from stereo to mono.'
|
copyright: '"Pop, High, A (H1).wav" by InspectorJ (www.jshaw.co.uk) of Freesound.org. Mixed from stereo to mono.'
|
||||||
source: "https://freesound.org/people/InspectorJ/sounds/411642/"
|
source: "https://freesound.org/people/InspectorJ/sounds/411642/"
|
||||||
|
|
||||||
|
- files: ["tesla.ogg"]
|
||||||
|
license: "CC-BY-NC-4.0"
|
||||||
|
copyright: "Taken from bzakirasantjago via freesound.org and mixed from stereo to mono."
|
||||||
|
source: "https://freesound.org/people/bzakirasantjago/sounds/404462/"
|
||||||
|
|
||||||
|
- files: ["tesla_collapse.ogg"]
|
||||||
|
license: "CC-BY-NC-SA-3.0"
|
||||||
|
copyright: "Taken from DragishaRambo21 via freesound.org and mixed from stereo to mono."
|
||||||
|
source: "https://freesound.org/people/DragishaRambo21/sounds/345920/"
|
||||||
|
|
||||||
|
- files: ["tesla_consume.ogg"]
|
||||||
|
license: "CC0-1.0"
|
||||||
|
copyright: "Taken from egomassive via freesound.org and mixed from stereo to mono."
|
||||||
|
source: "https://freesound.org/people/egomassive/sounds/536741/"
|
||||||
|
|
||||||
- files: ["sizzle.ogg"]
|
- files: ["sizzle.ogg"]
|
||||||
license: "CC-BY-SA-3.0"
|
license: "CC-BY-SA-3.0"
|
||||||
copyright: "Recorded by deltanedas for SS14"
|
copyright: "Recorded by deltanedas for SS14"
|
||||||
|
|||||||
BIN
Resources/Audio/Effects/tesla.ogg
Normal file
BIN
Resources/Audio/Effects/tesla_collapse.ogg
Normal file
BIN
Resources/Audio/Effects/tesla_consume.ogg
Normal file
@@ -21,3 +21,12 @@ ent-EngineParticleAccelerator = { ent-CrateEngineeringParticleAccelerator }
|
|||||||
|
|
||||||
ent-EngineSolar = { ent-CrateEngineeringSolar }
|
ent-EngineSolar = { ent-CrateEngineeringSolar }
|
||||||
.desc = { ent-CrateEngineeringSolar.desc }
|
.desc = { ent-CrateEngineeringSolar.desc }
|
||||||
|
|
||||||
|
ent-EngineTeslaGenerator = { ent-CrateEngineeringTeslaGenerator }
|
||||||
|
.desc = { ent-CrateEngineeringTeslaGenerator.desc }
|
||||||
|
|
||||||
|
ent-EngineTeslaCoil = { ent-CrateEngineeringTeslaCoil }
|
||||||
|
.desc = { ent-CrateEngineeringTeslaCoil.desc }
|
||||||
|
|
||||||
|
ent-EngineTeslaGroundingRod = { ent-CrateEngineeringTeslaGroundingRod }
|
||||||
|
.desc = { ent-CrateEngineeringTeslaGroundingRod.desc }
|
||||||
@@ -28,3 +28,12 @@ ent-CrateEngineeringSolar = Solar assembly crate
|
|||||||
.desc = Parts for constructing solar panels and trackers.
|
.desc = Parts for constructing solar panels and trackers.
|
||||||
|
|
||||||
ent-CrateEngineeringShuttle = Shuttle powering crate
|
ent-CrateEngineeringShuttle = Shuttle powering crate
|
||||||
|
|
||||||
|
ent-CrateEngineeringTeslaGenerator = Tesla generator crate
|
||||||
|
.desc = A tesla generator. God save you.
|
||||||
|
|
||||||
|
ent-CrateEngineeringTeslaCoil = Tesla coil crate
|
||||||
|
.desc = Tesla coil. Attracts lightning and generates energy from it.
|
||||||
|
|
||||||
|
ent-CrateEngineeringTeslaGroundingRod = Tesla grounding rod crate
|
||||||
|
.desc = Grounding rod, best for lightning protection.
|
||||||
5
Resources/Locale/en-US/tesla/tesla-components.ftl
Normal file
@@ -0,0 +1,5 @@
|
|||||||
|
tesla-coil-on = The tesla coil turns on.
|
||||||
|
tesla-coil-off = The tesla coil turns off.
|
||||||
|
|
||||||
|
tesla-grounding-on = The grounding rod turns on.
|
||||||
|
tesla-grounding-off = The grounding rod turns off.
|
||||||
@@ -89,3 +89,33 @@
|
|||||||
cost: 500
|
cost: 500
|
||||||
category: Engineering
|
category: Engineering
|
||||||
group: market
|
group: market
|
||||||
|
|
||||||
|
- type: cargoProduct
|
||||||
|
id: EngineTeslaGenerator
|
||||||
|
icon:
|
||||||
|
sprite: Structures/Power/Generation/Tesla/generator.rsi
|
||||||
|
state: icon
|
||||||
|
product: CrateEngineeringTeslaGenerator
|
||||||
|
cost: 4000
|
||||||
|
category: Engineering
|
||||||
|
group: market
|
||||||
|
|
||||||
|
- type: cargoProduct
|
||||||
|
id: EngineTeslaCoil
|
||||||
|
icon:
|
||||||
|
sprite: Structures/Power/Generation/Tesla/coil.rsi
|
||||||
|
state: coil
|
||||||
|
product: CrateEngineeringTeslaCoil
|
||||||
|
cost: 1200
|
||||||
|
category: Engineering
|
||||||
|
group: market
|
||||||
|
|
||||||
|
- type: cargoProduct
|
||||||
|
id: EngineTeslaGroundingRod
|
||||||
|
icon:
|
||||||
|
sprite: Structures/Power/Generation/Tesla/coil.rsi
|
||||||
|
state: grounding_rod
|
||||||
|
product: CrateEngineeringTeslaGroundingRod
|
||||||
|
cost: 400
|
||||||
|
category: Engineering
|
||||||
|
group: market
|
||||||
@@ -116,3 +116,27 @@
|
|||||||
- id: WallmountGeneratorAPUElectronics
|
- id: WallmountGeneratorAPUElectronics
|
||||||
- id: HandheldGPSBasic
|
- id: HandheldGPSBasic
|
||||||
- id: InflatableDoorStack1
|
- id: InflatableDoorStack1
|
||||||
|
|
||||||
|
- type: entity
|
||||||
|
id: CrateEngineeringTeslaGenerator
|
||||||
|
parent: CrateEngineeringSecure
|
||||||
|
components:
|
||||||
|
- type: StorageFill
|
||||||
|
contents:
|
||||||
|
- id: TeslaGenerator
|
||||||
|
|
||||||
|
- type: entity
|
||||||
|
id: CrateEngineeringTeslaCoil
|
||||||
|
parent: CrateEngineeringSecure
|
||||||
|
components:
|
||||||
|
- type: StorageFill
|
||||||
|
contents:
|
||||||
|
- id: TeslaCoil
|
||||||
|
|
||||||
|
- type: entity
|
||||||
|
id: CrateEngineeringTeslaGroundingRod
|
||||||
|
parent: CrateEngineeringSecure
|
||||||
|
components:
|
||||||
|
- type: StorageFill
|
||||||
|
contents:
|
||||||
|
- id: TeslaGroundingRod
|
||||||
@@ -17,3 +17,28 @@
|
|||||||
tags:
|
tags:
|
||||||
- HideContextMenu
|
- HideContextMenu
|
||||||
- type: AnimationPlayer
|
- type: AnimationPlayer
|
||||||
|
|
||||||
|
- type: entity
|
||||||
|
id: EffectTeslaSparks
|
||||||
|
noSpawn: true
|
||||||
|
components:
|
||||||
|
- type: TimedDespawn
|
||||||
|
lifetime: 0.5
|
||||||
|
- type: Sprite
|
||||||
|
drawdepth: Effects
|
||||||
|
noRot: true
|
||||||
|
layers:
|
||||||
|
- shader: unshaded
|
||||||
|
map: ["enum.EffectLayers.Unshaded"]
|
||||||
|
sprite: Effects/atmospherics.rsi
|
||||||
|
state: frezon_old
|
||||||
|
- type: EffectVisuals
|
||||||
|
- type: Tag
|
||||||
|
tags:
|
||||||
|
- HideContextMenu
|
||||||
|
- type: AnimationPlayer
|
||||||
|
- type: EmitSoundOnSpawn
|
||||||
|
sound:
|
||||||
|
path: /Audio/Effects/tesla_consume.ogg
|
||||||
|
params:
|
||||||
|
variation: 0.3
|
||||||
@@ -89,6 +89,9 @@
|
|||||||
soundHit:
|
soundHit:
|
||||||
path: /Audio/Effects/hit_kick.ogg
|
path: /Audio/Effects/hit_kick.ogg
|
||||||
- type: Pullable
|
- type: Pullable
|
||||||
|
- type: LightningTarget
|
||||||
|
priority: 2
|
||||||
|
lightningExplode: false
|
||||||
|
|
||||||
# Used for mobs that can enter combat mode and can attack.
|
# Used for mobs that can enter combat mode and can attack.
|
||||||
- type: entity
|
- type: entity
|
||||||
|
|||||||
@@ -132,6 +132,8 @@
|
|||||||
- type: AccessReader
|
- type: AccessReader
|
||||||
- type: StaticPrice
|
- type: StaticPrice
|
||||||
price: 150
|
price: 150
|
||||||
|
- type: LightningTarget
|
||||||
|
priority: 1
|
||||||
- type: Tag
|
- type: Tag
|
||||||
tags:
|
tags:
|
||||||
- Airlock
|
- Airlock
|
||||||
@@ -140,3 +142,4 @@
|
|||||||
placement:
|
placement:
|
||||||
mode: SnapgridCenter
|
mode: SnapgridCenter
|
||||||
|
|
||||||
|
|
||||||
|
|||||||
@@ -57,3 +57,5 @@
|
|||||||
containers:
|
containers:
|
||||||
board: !type:Container
|
board: !type:Container
|
||||||
ents: []
|
ents: []
|
||||||
|
- type: LightningTarget
|
||||||
|
priority: 1
|
||||||
|
|||||||
@@ -50,6 +50,8 @@
|
|||||||
- type: ApcPowerReceiver
|
- type: ApcPowerReceiver
|
||||||
powerLoad: 1000
|
powerLoad: 1000
|
||||||
- type: ExtensionCableReceiver
|
- type: ExtensionCableReceiver
|
||||||
|
- type: LightningTarget
|
||||||
|
priority: 1
|
||||||
|
|
||||||
- type: entity
|
- type: entity
|
||||||
abstract: true
|
abstract: true
|
||||||
@@ -66,3 +68,5 @@
|
|||||||
containers:
|
containers:
|
||||||
- machine_parts
|
- machine_parts
|
||||||
- machine_board
|
- machine_board
|
||||||
|
- type: LightningTarget
|
||||||
|
priority: 1
|
||||||
|
|||||||
@@ -0,0 +1,125 @@
|
|||||||
|
- type: entity
|
||||||
|
id: TeslaCoil
|
||||||
|
name: tesla coil
|
||||||
|
description: A machine that converts lightning strikes into an electric current.
|
||||||
|
placement:
|
||||||
|
mode: SnapgridCenter
|
||||||
|
components:
|
||||||
|
- type: Transform
|
||||||
|
anchored: true
|
||||||
|
- type: Physics
|
||||||
|
bodyType: Static
|
||||||
|
- type: Fixtures
|
||||||
|
fixtures:
|
||||||
|
fix1:
|
||||||
|
shape:
|
||||||
|
!type:PhysShapeAabb
|
||||||
|
bounds: "-0.45,-0.45,0.45,0.45"
|
||||||
|
density: 190
|
||||||
|
mask:
|
||||||
|
- MachineMask
|
||||||
|
layer:
|
||||||
|
- MachineLayer
|
||||||
|
- type: Sprite
|
||||||
|
sprite: Structures/Power/Generation/Tesla/coil.rsi
|
||||||
|
snapCardinals: true
|
||||||
|
noRot: true
|
||||||
|
layers:
|
||||||
|
- state: coil
|
||||||
|
map: ["enabled"]
|
||||||
|
- state: coilhit
|
||||||
|
visible: false
|
||||||
|
map: ["hit"]
|
||||||
|
- type: Appearance
|
||||||
|
- type: GenericVisualizer
|
||||||
|
visuals:
|
||||||
|
enum.TeslaCoilVisuals.Enabled:
|
||||||
|
enabled:
|
||||||
|
True: { state: coil_open }
|
||||||
|
False: { state: coil }
|
||||||
|
enum.TeslaCoilVisuals.Lightning:
|
||||||
|
hit:
|
||||||
|
True: { visible: true }
|
||||||
|
False: { visible: false }
|
||||||
|
- type: LightningSparking
|
||||||
|
- type: AnimationPlayer
|
||||||
|
- type: NodeContainer
|
||||||
|
examinable: true
|
||||||
|
nodes:
|
||||||
|
input:
|
||||||
|
!type:CableDeviceNode
|
||||||
|
nodeGroupID: HVPower
|
||||||
|
- type: Battery
|
||||||
|
maxCharge: 1000000
|
||||||
|
startingCharge: 0
|
||||||
|
- type: BatteryDischarger
|
||||||
|
activeSupplyRate: 15000
|
||||||
|
- type: TeslaCoil
|
||||||
|
chargeFromLightning: 1000000
|
||||||
|
- type: LightningTarget
|
||||||
|
priority: 4
|
||||||
|
lightningExplode: false
|
||||||
|
- type: PowerNetworkBattery
|
||||||
|
maxSupply: 1000000
|
||||||
|
supplyRampTolerance: 1000000
|
||||||
|
- type: Anchorable
|
||||||
|
- type: Rotatable
|
||||||
|
- type: Pullable
|
||||||
|
- type: Clickable
|
||||||
|
- type: InteractionOutline
|
||||||
|
#- type: GuideHelp # To Do - add Tesla Guide
|
||||||
|
|
||||||
|
- type: entity
|
||||||
|
id: TeslaGroundingRod
|
||||||
|
name: grounding rod
|
||||||
|
description: A machine that keeps lightning from striking too far away.
|
||||||
|
placement:
|
||||||
|
mode: SnapgridCenter
|
||||||
|
components:
|
||||||
|
- type: Transform
|
||||||
|
anchored: true
|
||||||
|
- type: Physics
|
||||||
|
bodyType: Static
|
||||||
|
- type: Fixtures
|
||||||
|
fixtures:
|
||||||
|
fix1:
|
||||||
|
shape:
|
||||||
|
!type:PhysShapeAabb
|
||||||
|
bounds: "-0.45,-0.45,0.45,0.45"
|
||||||
|
density: 110
|
||||||
|
mask:
|
||||||
|
- MachineMask
|
||||||
|
layer:
|
||||||
|
- MachineLayer
|
||||||
|
- type: Sprite
|
||||||
|
sprite: Structures/Power/Generation/Tesla/coil.rsi
|
||||||
|
noRot: true
|
||||||
|
snapCardinals: true
|
||||||
|
layers:
|
||||||
|
- state: grounding_rod
|
||||||
|
map: ["power"]
|
||||||
|
- state: grounding_rodhit
|
||||||
|
visible: false
|
||||||
|
map: ["hit"]
|
||||||
|
- type: Appearance
|
||||||
|
- type: GenericVisualizer
|
||||||
|
visuals:
|
||||||
|
enum.TeslaCoilVisuals.Lightning:
|
||||||
|
hit:
|
||||||
|
True: { visible: true }
|
||||||
|
False: { visible: false }
|
||||||
|
enum.TeslaCoilVisuals.Enabled:
|
||||||
|
power:
|
||||||
|
True: { state: grounding_rod }
|
||||||
|
False: { state: grounding_rod_open }
|
||||||
|
- type: LightningSparking
|
||||||
|
- type: LightningTarget
|
||||||
|
priority: 3
|
||||||
|
lightningResistance: 10
|
||||||
|
lightningExplode: false
|
||||||
|
- type: Anchorable
|
||||||
|
- type: Rotatable
|
||||||
|
- type: Pullable
|
||||||
|
- type: Clickable
|
||||||
|
- type: InteractionOutline
|
||||||
|
#- type: GuideHelp # To Do - add Tesla Guide
|
||||||
@@ -0,0 +1,133 @@
|
|||||||
|
- type: entity
|
||||||
|
id: BaseEnergyBall
|
||||||
|
abstract: true
|
||||||
|
components:
|
||||||
|
- type: Clickable
|
||||||
|
- type: Physics
|
||||||
|
bodyType: Dynamic
|
||||||
|
bodyStatus: InAir
|
||||||
|
sleepingAllowed: false
|
||||||
|
- type: CanMoveInAir
|
||||||
|
- type: ChasingWalk
|
||||||
|
minSpeed: 1
|
||||||
|
maxSpeed: 3
|
||||||
|
chasingComponent:
|
||||||
|
- type: LightningTarget
|
||||||
|
- type: AmbientSound
|
||||||
|
volume: 3
|
||||||
|
range: 15
|
||||||
|
sound:
|
||||||
|
path: /Audio/Effects/tesla.ogg
|
||||||
|
- type: PointLight
|
||||||
|
enabled: true
|
||||||
|
radius: 5
|
||||||
|
color: "#4080FF"
|
||||||
|
- type: Appearance
|
||||||
|
- type: LightningArcShooter
|
||||||
|
arcDepth: 2
|
||||||
|
maxLightningArc: 1
|
||||||
|
shootMinInterval: 4
|
||||||
|
shootMaxInterval: 10
|
||||||
|
shootRange: 3
|
||||||
|
lightningPrototype: Lightning
|
||||||
|
|
||||||
|
- type: entity
|
||||||
|
id: TeslaEnergyBall
|
||||||
|
parent: BaseEnergyBall
|
||||||
|
name: ball lightning
|
||||||
|
description: A giant ball of pure energy. The space around it is humming and melting.
|
||||||
|
components:
|
||||||
|
- type: Fixtures
|
||||||
|
fixtures:
|
||||||
|
EventHorizonCollider:
|
||||||
|
shape:
|
||||||
|
!type:PhysShapeCircle
|
||||||
|
radius: 0.55
|
||||||
|
hard: true
|
||||||
|
restitution: 0.8
|
||||||
|
density: 99999 # to do: remove the interaction with the explosions. Now the explosions can push the Tesla very far away from the station.
|
||||||
|
mask:
|
||||||
|
- Opaque
|
||||||
|
layer:
|
||||||
|
- GlassLayer
|
||||||
|
EventHorizonConsumer:
|
||||||
|
shape:
|
||||||
|
!type:PhysShapeCircle
|
||||||
|
radius: 0.65
|
||||||
|
hard: false
|
||||||
|
mask:
|
||||||
|
- Opaque
|
||||||
|
layer:
|
||||||
|
- GlassLayer
|
||||||
|
- type: EventHorizon
|
||||||
|
radius: 0.5
|
||||||
|
canBreachContainment: false
|
||||||
|
colliderFixtureId: EventHorizonCollider
|
||||||
|
consumerFixtureId: EventHorizonConsumer
|
||||||
|
consumeTiles: false
|
||||||
|
consumeEntities: false
|
||||||
|
- type: TeslaEnergyBall
|
||||||
|
spawnProto: TeslaMiniEnergyBall
|
||||||
|
- type: LightningArcShooter
|
||||||
|
arcDepth: 3
|
||||||
|
maxLightningArc: 4
|
||||||
|
shootMinInterval: 3
|
||||||
|
shootMaxInterval: 5
|
||||||
|
shootRange: 7
|
||||||
|
lightningPrototype: Lightning #To do: change to HyperchargedLightning, after fix beam system
|
||||||
|
- type: ChasingWalk
|
||||||
|
minSpeed: 1
|
||||||
|
maxSpeed: 3
|
||||||
|
- type: ChaoticJump
|
||||||
|
jumpMinInterval: 8
|
||||||
|
jumpMaxInterval: 15
|
||||||
|
offset: 1
|
||||||
|
- type: WarpPoint
|
||||||
|
follow: true
|
||||||
|
location: tesla ball
|
||||||
|
- type: Sprite
|
||||||
|
drawdepth: Effects
|
||||||
|
sprite: Structures/Power/Generation/Tesla/energy_ball.rsi
|
||||||
|
shader: unshaded
|
||||||
|
layers:
|
||||||
|
- state: energy_ball
|
||||||
|
- type: EmitSoundOnSpawn
|
||||||
|
sound:
|
||||||
|
path: /Audio/Effects/tesla_collapse.ogg
|
||||||
|
params:
|
||||||
|
variation: 0.3
|
||||||
|
|
||||||
|
- type: entity
|
||||||
|
id: TeslaMiniEnergyBall
|
||||||
|
parent: BaseEnergyBall
|
||||||
|
name: mini ball lightning
|
||||||
|
description: The cub of a destructive energy cage. Not as dangerous, but still not worth touching with bare hands.
|
||||||
|
components:
|
||||||
|
- type: ChasingWalk
|
||||||
|
minSpeed: 2
|
||||||
|
maxSpeed: 3
|
||||||
|
chasingComponent:
|
||||||
|
- type: TeslaEnergyBall
|
||||||
|
- type: Fixtures
|
||||||
|
fixtures:
|
||||||
|
TeslaCollider:
|
||||||
|
shape:
|
||||||
|
!type:PhysShapeCircle
|
||||||
|
radius: 0.35
|
||||||
|
hard: true
|
||||||
|
restitution: 0.8
|
||||||
|
density: 10
|
||||||
|
mask:
|
||||||
|
- None
|
||||||
|
layer:
|
||||||
|
- None
|
||||||
|
- type: TimedDespawn
|
||||||
|
lifetime: 120
|
||||||
|
- type: Sprite
|
||||||
|
drawdepth: Effects
|
||||||
|
sprite: Structures/Power/Generation/Tesla/energy_miniball.rsi
|
||||||
|
shader: unshaded
|
||||||
|
layers:
|
||||||
|
- state: tesla_projectile
|
||||||
|
- type: Electrified
|
||||||
|
requirePower: false
|
||||||
@@ -0,0 +1,29 @@
|
|||||||
|
- type: entity
|
||||||
|
id: TeslaGenerator
|
||||||
|
name: tesla generator
|
||||||
|
parent: BaseStructureDynamic
|
||||||
|
description: An Odd Device which produces a powerful Tesla ball when set up.
|
||||||
|
components:
|
||||||
|
- type: Sprite
|
||||||
|
noRot: true
|
||||||
|
sprite: Structures/Power/Generation/Tesla/generator.rsi
|
||||||
|
state: icon
|
||||||
|
- type: SingularityGenerator #To do: rename the generator
|
||||||
|
spawnId: TeslaEnergyBall
|
||||||
|
- type: InteractionOutline
|
||||||
|
- type: Fixtures
|
||||||
|
fixtures:
|
||||||
|
fix1:
|
||||||
|
shape:
|
||||||
|
# Using a circle here makes it a lot easier to pull it all the way from Cargo
|
||||||
|
!type:PhysShapeCircle
|
||||||
|
radius: 0.45
|
||||||
|
density: 190
|
||||||
|
# Keep an eye on ParticlesProjectile when adjusting these
|
||||||
|
mask:
|
||||||
|
- MachineMask
|
||||||
|
layer:
|
||||||
|
- Opaque
|
||||||
|
- type: Anchorable
|
||||||
|
#- type: GuideHelp # To Do - add Tesla Guide
|
||||||
|
|
||||||
@@ -131,6 +131,8 @@
|
|||||||
requirePower: true
|
requirePower: true
|
||||||
mediumVoltageNode: input
|
mediumVoltageNode: input
|
||||||
lowVoltageNode: output
|
lowVoltageNode: output
|
||||||
|
- type: LightningTarget
|
||||||
|
priority: 1
|
||||||
- type: StaticPrice
|
- type: StaticPrice
|
||||||
price: 500
|
price: 500
|
||||||
|
|
||||||
|
|||||||
|
After Width: | Height: | Size: 676 B |
|
After Width: | Height: | Size: 6.1 KiB |
|
After Width: | Height: | Size: 733 B |
|
After Width: | Height: | Size: 2.2 KiB |
@@ -0,0 +1,42 @@
|
|||||||
|
{
|
||||||
|
"version": 1,
|
||||||
|
"license": "CC-BY-SA-3.0",
|
||||||
|
"copyright": "taken from tgstation https://github.com/tgstation/tgstation/blob/master/icons/obj/machines/engine/tesla_coil.dmi",
|
||||||
|
"size": {
|
||||||
|
"x": 32,
|
||||||
|
"y": 32
|
||||||
|
},
|
||||||
|
"states": [
|
||||||
|
{
|
||||||
|
"name": "coil"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"name": "coilhit",
|
||||||
|
"delays": [
|
||||||
|
[
|
||||||
|
0.1,
|
||||||
|
0.08,
|
||||||
|
0.08,
|
||||||
|
0.08,
|
||||||
|
0.120000005,
|
||||||
|
0.1
|
||||||
|
]
|
||||||
|
]
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"name": "grounding_rod"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"name": "grounding_rodhit",
|
||||||
|
"delays": [
|
||||||
|
[
|
||||||
|
0.1,
|
||||||
|
0.1,
|
||||||
|
0.1,
|
||||||
|
0.1,
|
||||||
|
0.1
|
||||||
|
]
|
||||||
|
]
|
||||||
|
}
|
||||||
|
]
|
||||||
|
}
|
||||||
|
After Width: | Height: | Size: 68 KiB |
|
After Width: | Height: | Size: 68 KiB |
@@ -0,0 +1,49 @@
|
|||||||
|
{
|
||||||
|
"version": 1,
|
||||||
|
"license": "CC-BY-SA-3.0",
|
||||||
|
"copyright": "taken from tgstation https://github.com/tgstation/tgstation/blob/master/icons/obj/machines/engine/energy_ball.dmi",
|
||||||
|
"size": {
|
||||||
|
"x": 96,
|
||||||
|
"y": 96
|
||||||
|
},
|
||||||
|
"states": [
|
||||||
|
{
|
||||||
|
"name": "energy_ball",
|
||||||
|
"delays": [
|
||||||
|
[
|
||||||
|
0.120000005,
|
||||||
|
0.04,
|
||||||
|
0.120000005,
|
||||||
|
0.08,
|
||||||
|
0.04,
|
||||||
|
0.120000005,
|
||||||
|
0.04,
|
||||||
|
0.120000005,
|
||||||
|
0.08,
|
||||||
|
0.04,
|
||||||
|
0.120000005,
|
||||||
|
0.04
|
||||||
|
]
|
||||||
|
]
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"name": "energy_ball_fast",
|
||||||
|
"delays": [
|
||||||
|
[
|
||||||
|
0.05,
|
||||||
|
0.08,
|
||||||
|
0.08,
|
||||||
|
0.08,
|
||||||
|
0.08,
|
||||||
|
0.08,
|
||||||
|
0.08,
|
||||||
|
0.08,
|
||||||
|
0.08,
|
||||||
|
0.08,
|
||||||
|
0.08,
|
||||||
|
0.08
|
||||||
|
]
|
||||||
|
]
|
||||||
|
}
|
||||||
|
]
|
||||||
|
}
|
||||||
@@ -0,0 +1,30 @@
|
|||||||
|
{
|
||||||
|
"version": 1,
|
||||||
|
"license": "CC-BY-SA-3.0",
|
||||||
|
"copyright": "taken from tgstation https://github.com/tgstation/tgstation/blob/HEAD/icons/obj/weapons/guns/projectiles.dmi",
|
||||||
|
"size": {
|
||||||
|
"x": 32,
|
||||||
|
"y": 32
|
||||||
|
},
|
||||||
|
"states": [
|
||||||
|
{
|
||||||
|
"name": "tesla_projectile",
|
||||||
|
"delays": [
|
||||||
|
[
|
||||||
|
0.05,
|
||||||
|
0.08,
|
||||||
|
0.08,
|
||||||
|
0.08,
|
||||||
|
0.08,
|
||||||
|
0.08,
|
||||||
|
0.08,
|
||||||
|
0.08,
|
||||||
|
0.08,
|
||||||
|
0.08,
|
||||||
|
0.08,
|
||||||
|
0.08
|
||||||
|
]
|
||||||
|
]
|
||||||
|
}
|
||||||
|
]
|
||||||
|
}
|
||||||
|
After Width: | Height: | Size: 14 KiB |
|
After Width: | Height: | Size: 382 B |
@@ -0,0 +1,14 @@
|
|||||||
|
{
|
||||||
|
"version": 1,
|
||||||
|
"license": "CC-BY-SA-3.0",
|
||||||
|
"copyright": "Taken from tgstation https://github.com/tgstation/tgstation/blob/master/icons/obj/machines/engine/tesla_generator.dmi",
|
||||||
|
"size": {
|
||||||
|
"x": 32,
|
||||||
|
"y": 32
|
||||||
|
},
|
||||||
|
"states": [
|
||||||
|
{
|
||||||
|
"name": "icon"
|
||||||
|
}
|
||||||
|
]
|
||||||
|
}
|
||||||