* 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>
This commit is contained in:
Ed
2023-12-28 16:11:50 +03:00
committed by GitHub
parent 06719a3232
commit 88d7cfb4c0
51 changed files with 1374 additions and 90 deletions

View File

@@ -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]

View File

@@ -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;
}

View File

@@ -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);

View 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);
}
}

View 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";
}

View 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;
}

View 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);
}
}

View 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.
}
}

View File

@@ -325,8 +325,10 @@ public sealed class EventHorizonSystem : SharedEventHorizonSystem
if (!Resolve(uid, ref xform, ref eventHorizon)) if (!Resolve(uid, ref xform, ref eventHorizon))
return; return;
ConsumeEntitiesInRange(uid, range, xform, eventHorizon); if (eventHorizon.ConsumeEntities)
ConsumeTilesInRange(uid, range, xform, eventHorizon); ConsumeEntitiesInRange(uid, range, xform, eventHorizon);
if (eventHorizon.ConsumeTiles)
ConsumeTilesInRange(uid, range, xform, eventHorizon);
} }
#endregion Consume #endregion Consume

View File

@@ -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;

View File

@@ -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";
}

View File

@@ -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;
}

View 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;
}

View 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";
}

View File

@@ -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);
}
}

View 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;
}
}
}
}

View 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);
}
}

View 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);
}
}
}

View File

@@ -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;

View File

@@ -0,0 +1,10 @@
using Robust.Shared.Serialization;
namespace Content.Shared.Power;
[Serializable, NetSerializable]
public enum TeslaCoilVisuals : byte
{
Enabled,
Lightning
}

View File

@@ -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"/>.

View File

@@ -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"

Binary file not shown.

Binary file not shown.

Binary file not shown.

View 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 }

View File

@@ -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.

View 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.

View File

@@ -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

View File

@@ -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

View File

@@ -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

View File

@@ -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

View File

@@ -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

View File

@@ -57,3 +57,5 @@
containers: containers:
board: !type:Container board: !type:Container
ents: [] ents: []
- type: LightningTarget
priority: 1

View File

@@ -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

View File

@@ -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

View File

@@ -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

View File

@@ -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

View File

@@ -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

Binary file not shown.

After

Width:  |  Height:  |  Size: 676 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 6.1 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 733 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 2.2 KiB

View File

@@ -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
]
]
}
]
}

Binary file not shown.

After

Width:  |  Height:  |  Size: 68 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 68 KiB

View File

@@ -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
]
]
}
]
}

View File

@@ -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
]
]
}
]
}

Binary file not shown.

After

Width:  |  Height:  |  Size: 14 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 382 B

View File

@@ -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"
}
]
}