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;
|
||||
[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.Components;
|
||||
using Content.Server.Lightning.Components;
|
||||
using Content.Shared.Lightning;
|
||||
using Robust.Server.GameObjects;
|
||||
using Robust.Shared.Physics;
|
||||
using Robust.Shared.Physics.Dynamics;
|
||||
using Robust.Shared.Physics.Events;
|
||||
using Robust.Shared.Random;
|
||||
|
||||
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
|
||||
{
|
||||
[Dependency] private readonly PhysicsSystem _physics = default!;
|
||||
[Dependency] private readonly BeamSystem _beam = 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()
|
||||
{
|
||||
base.Initialize();
|
||||
|
||||
SubscribeLocalEvent<LightningComponent, StartCollideEvent>(OnCollide);
|
||||
SubscribeLocalEvent<LightningComponent, ComponentRemove>(OnRemove);
|
||||
}
|
||||
|
||||
@@ -35,32 +42,6 @@ public sealed class LightningSystem : SharedLightningSystem
|
||||
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>
|
||||
/// Fires lightning from user to target
|
||||
/// </summary>
|
||||
@@ -71,68 +52,47 @@ public sealed class LightningSystem : SharedLightningSystem
|
||||
{
|
||||
var spriteState = LightningRandomizer();
|
||||
_beam.TryCreateBeam(user, target, lightningPrototype, spriteState);
|
||||
|
||||
var ev = new HitByLightningEvent(user, target);
|
||||
RaiseLocalEvent(target, ref ev);
|
||||
}
|
||||
|
||||
/// <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>
|
||||
/// <param name="component"></param>
|
||||
/// <param name="target"></param>
|
||||
/// <param name="controllerEntity"></param>
|
||||
private void Arc(LightningComponent component, EntityUid target, EntityUid controllerEntity)
|
||||
/// <param name="user">Where the lightning fires from</param>
|
||||
/// <param name="range">Targets selection radius</param>
|
||||
/// <param name="boltCount">Number of lightning bolts</param>
|
||||
/// <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;
|
||||
|
||||
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++)
|
||||
for (int i = 0; i < realCount; i++)
|
||||
{
|
||||
var direction = (Direction) i;
|
||||
var (targetPos, targetRot) = targetXForm.GetWorldPositionRotation(xformQuery);
|
||||
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)
|
||||
ShootLightning(user, targets[i].Owner, lightningPrototype);
|
||||
if (arcDepth > 0)
|
||||
{
|
||||
if (lightningQuery.HasComponent(result.HitEntity)
|
||||
|| beamQuery.HasComponent(result.HitEntity)
|
||||
|| component.ArcTargets.Contains(result.HitEntity)
|
||||
|| controller.HitTargets.Contains(result.HitEntity)
|
||||
|| controller.BeamShooter == result.HitEntity)
|
||||
{
|
||||
continue;
|
||||
}
|
||||
|
||||
closestResult = result;
|
||||
break;
|
||||
ShootRandomLightnings(targets[i].Owner, range, 1, lightningPrototype, arcDepth - targets[i].LightningResistance);
|
||||
}
|
||||
|
||||
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,8 +325,10 @@ public sealed class EventHorizonSystem : SharedEventHorizonSystem
|
||||
if (!Resolve(uid, ref xform, ref eventHorizon))
|
||||
return;
|
||||
|
||||
ConsumeEntitiesInRange(uid, range, xform, eventHorizon);
|
||||
ConsumeTilesInRange(uid, range, xform, eventHorizon);
|
||||
if (eventHorizon.ConsumeEntities)
|
||||
ConsumeEntitiesInRange(uid, range, xform, eventHorizon);
|
||||
if (eventHorizon.ConsumeTiles)
|
||||
ConsumeTilesInRange(uid, range, xform, eventHorizon);
|
||||
}
|
||||
|
||||
#endregion Consume
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
using Content.Server.ParticleAccelerator.Components;
|
||||
using Content.Server.ParticleAccelerator.Components;
|
||||
using Content.Server.Singularity.Components;
|
||||
using Content.Shared.Singularity.Components;
|
||||
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.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")]
|
||||
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>
|
||||
/// 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"/>.
|
||||
|
||||
@@ -68,6 +68,21 @@
|
||||
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/"
|
||||
|
||||
- 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"]
|
||||
license: "CC-BY-SA-3.0"
|
||||
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 }
|
||||
.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.
|
||||
|
||||
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
|
||||
category: Engineering
|
||||
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: HandheldGPSBasic
|
||||
- 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:
|
||||
- HideContextMenu
|
||||
- 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:
|
||||
path: /Audio/Effects/hit_kick.ogg
|
||||
- type: Pullable
|
||||
- type: LightningTarget
|
||||
priority: 2
|
||||
lightningExplode: false
|
||||
|
||||
# Used for mobs that can enter combat mode and can attack.
|
||||
- type: entity
|
||||
|
||||
@@ -132,6 +132,8 @@
|
||||
- type: AccessReader
|
||||
- type: StaticPrice
|
||||
price: 150
|
||||
- type: LightningTarget
|
||||
priority: 1
|
||||
- type: Tag
|
||||
tags:
|
||||
- Airlock
|
||||
@@ -140,3 +142,4 @@
|
||||
placement:
|
||||
mode: SnapgridCenter
|
||||
|
||||
|
||||
|
||||
@@ -57,3 +57,5 @@
|
||||
containers:
|
||||
board: !type:Container
|
||||
ents: []
|
||||
- type: LightningTarget
|
||||
priority: 1
|
||||
|
||||
@@ -50,6 +50,8 @@
|
||||
- type: ApcPowerReceiver
|
||||
powerLoad: 1000
|
||||
- type: ExtensionCableReceiver
|
||||
- type: LightningTarget
|
||||
priority: 1
|
||||
|
||||
- type: entity
|
||||
abstract: true
|
||||
@@ -66,3 +68,5 @@
|
||||
containers:
|
||||
- machine_parts
|
||||
- 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
|
||||
mediumVoltageNode: input
|
||||
lowVoltageNode: output
|
||||
- type: LightningTarget
|
||||
priority: 1
|
||||
- type: StaticPrice
|
||||
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"
|
||||
}
|
||||
]
|
||||
}
|
||||