add syndicate singularity beacon (#18486)
* implement singulo attraction * add attractor syndie item * cleanup * fix name/desc * actually fix name * singulo toolbox no longer whitelisted * add custom sprite, overhaul prototype * address review * beacon real * webedit 1 * webedit 2 * webedit 3 * permalink to copyright --------- Co-authored-by: deltanedas <39013340+deltanedas@users.noreply.github.com>
This commit is contained in:
@@ -1,3 +1,4 @@
|
|||||||
|
using System.Numerics;
|
||||||
using Content.Server.Physics.Controllers;
|
using Content.Server.Physics.Controllers;
|
||||||
|
|
||||||
namespace Content.Server.Physics.Components;
|
namespace Content.Server.Physics.Components;
|
||||||
@@ -29,6 +30,18 @@ public sealed partial class RandomWalkComponent : Component
|
|||||||
[ViewVariables(VVAccess.ReadWrite)]
|
[ViewVariables(VVAccess.ReadWrite)]
|
||||||
public float AccumulatorRatio = 0.0f;
|
public float AccumulatorRatio = 0.0f;
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// The vector by which the random walk direction is biased.
|
||||||
|
/// </summary>
|
||||||
|
[DataField, ViewVariables(VVAccess.ReadWrite)]
|
||||||
|
public Vector2 BiasVector = new Vector2(0f, 0f);
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Whether to set BiasVector to (0, 0) every random walk update.
|
||||||
|
/// </summary>
|
||||||
|
[DataField, ViewVariables(VVAccess.ReadWrite)]
|
||||||
|
public bool ResetBiasOnWalk = true;
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// Whether this random walker should take a step immediately when it starts up.
|
/// Whether this random walker should take a step immediately when it starts up.
|
||||||
/// </summary>
|
/// </summary>
|
||||||
|
|||||||
@@ -1,3 +1,4 @@
|
|||||||
|
using System.Numerics;
|
||||||
using Content.Server.Physics.Components;
|
using Content.Server.Physics.Components;
|
||||||
using Content.Shared.Follower.Components;
|
using Content.Shared.Follower.Components;
|
||||||
using Content.Shared.Throwing;
|
using Content.Shared.Throwing;
|
||||||
@@ -69,11 +70,15 @@ internal sealed class RandomWalkController : VirtualController
|
|||||||
if(!Resolve(uid, ref physics))
|
if(!Resolve(uid, ref physics))
|
||||||
return;
|
return;
|
||||||
|
|
||||||
var pushAngle = _random.NextAngle();
|
var pushVec = _random.NextAngle().ToVec();
|
||||||
|
pushVec += randomWalk.BiasVector;
|
||||||
|
pushVec.Normalize();
|
||||||
|
if (randomWalk.ResetBiasOnWalk)
|
||||||
|
randomWalk.BiasVector *= 0f;
|
||||||
var pushStrength = _random.NextFloat(randomWalk.MinSpeed, randomWalk.MaxSpeed);
|
var pushStrength = _random.NextFloat(randomWalk.MinSpeed, randomWalk.MaxSpeed);
|
||||||
|
|
||||||
_physics.SetLinearVelocity(uid, physics.LinearVelocity * randomWalk.AccumulatorRatio, body: physics);
|
_physics.SetLinearVelocity(uid, physics.LinearVelocity * randomWalk.AccumulatorRatio, body: physics);
|
||||||
_physics.ApplyLinearImpulse(uid, pushAngle.ToVec() * (pushStrength * physics.Mass), body: physics);
|
_physics.ApplyLinearImpulse(uid, pushVec * (pushStrength * physics.Mass), body: physics);
|
||||||
}
|
}
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
|
|||||||
@@ -0,0 +1,30 @@
|
|||||||
|
using Content.Server.Singularity.EntitySystems;
|
||||||
|
using Robust.Shared.Serialization.TypeSerializers.Implementations.Custom;
|
||||||
|
|
||||||
|
namespace Content.Server.Singularity.Components;
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Attracts the singularity.
|
||||||
|
/// </summary>
|
||||||
|
[RegisterComponent]
|
||||||
|
[Access(typeof(SingularityAttractorSystem))]
|
||||||
|
public sealed partial class SingularityAttractorComponent : Component
|
||||||
|
{
|
||||||
|
/// <summary>
|
||||||
|
/// The range at which singularities will be unable to go away from the attractor.
|
||||||
|
/// </summary>
|
||||||
|
[DataField, ViewVariables(VVAccess.ReadWrite)]
|
||||||
|
public float BaseRange = 25f;
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// The amount of time that should elapse between pulses of this attractor.
|
||||||
|
/// </summary>
|
||||||
|
[DataField, ViewVariables(VVAccess.ReadOnly)]
|
||||||
|
public TimeSpan TargetPulsePeriod = TimeSpan.FromSeconds(2);
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// The last time this attractor pulsed.
|
||||||
|
/// </summary>
|
||||||
|
[DataField(customTypeSerializer: typeof(TimeOffsetSerializer))]
|
||||||
|
public TimeSpan LastPulseTime = default!;
|
||||||
|
}
|
||||||
@@ -0,0 +1,106 @@
|
|||||||
|
using Content.Server.Physics.Components;
|
||||||
|
using Content.Server.Power.EntitySystems;
|
||||||
|
using Content.Server.Singularity.Components;
|
||||||
|
using Content.Shared.Singularity.Components;
|
||||||
|
using Content.Shared.Singularity.EntitySystems;
|
||||||
|
using Robust.Shared.Map;
|
||||||
|
using Robust.Shared.Map.Components;
|
||||||
|
using Robust.Shared.Physics;
|
||||||
|
using Robust.Shared.Physics.Components;
|
||||||
|
using Robust.Shared.Physics.Systems;
|
||||||
|
using Robust.Shared.Timing;
|
||||||
|
using System.Numerics;
|
||||||
|
|
||||||
|
namespace Content.Server.Singularity.EntitySystems;
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Handles singularity attractors.
|
||||||
|
/// </summary>
|
||||||
|
public sealed class SingularityAttractorSystem : EntitySystem
|
||||||
|
{
|
||||||
|
[Dependency] private readonly EntityLookupSystem _lookup = default!;
|
||||||
|
[Dependency] private readonly IGameTiming _timing = default!;
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// The minimum range at which the attraction will act.
|
||||||
|
/// Prevents division by zero problems.
|
||||||
|
/// </summary>
|
||||||
|
public const float MinAttractRange = 0.00001f;
|
||||||
|
|
||||||
|
public override void Initialize()
|
||||||
|
{
|
||||||
|
base.Initialize();
|
||||||
|
|
||||||
|
SubscribeLocalEvent<SingularityAttractorComponent, MapInitEvent>(OnMapInit);
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Updates the pulse cooldowns of all singularity attractors.
|
||||||
|
/// If they are off cooldown it makes them emit an attraction pulse and reset their cooldown.
|
||||||
|
/// </summary>
|
||||||
|
/// <param name="frameTime">The time elapsed since the last set of updates.</param>
|
||||||
|
public override void Update(float frameTime)
|
||||||
|
{
|
||||||
|
if (!_timing.IsFirstTimePredicted)
|
||||||
|
return;
|
||||||
|
|
||||||
|
var query = EntityQueryEnumerator<SingularityAttractorComponent, TransformComponent>();
|
||||||
|
var now = _timing.CurTime;
|
||||||
|
while (query.MoveNext(out var uid, out var attractor, out var xform))
|
||||||
|
{
|
||||||
|
if (attractor.LastPulseTime + attractor.TargetPulsePeriod <= now)
|
||||||
|
Update(uid, attractor, xform);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Makes an attractor attract all singularities and puts it on cooldown.
|
||||||
|
/// </summary>
|
||||||
|
/// <param name="uid">The uid of the attractor to make pulse.</param>
|
||||||
|
/// <param name="attractor">The state of the attractor to make pulse.</param>
|
||||||
|
/// <param name="xform">The transform of the attractor to make pulse.</param>
|
||||||
|
private void Update(EntityUid uid, SingularityAttractorComponent? attractor = null, TransformComponent? xform = null)
|
||||||
|
{
|
||||||
|
if (!Resolve(uid, ref attractor, ref xform))
|
||||||
|
return;
|
||||||
|
|
||||||
|
if (!this.IsPowered(uid, EntityManager))
|
||||||
|
return;
|
||||||
|
|
||||||
|
attractor.LastPulseTime = _timing.CurTime;
|
||||||
|
|
||||||
|
var mapPos = xform.Coordinates.ToMap(EntityManager);
|
||||||
|
|
||||||
|
if (mapPos == MapCoordinates.Nullspace)
|
||||||
|
return;
|
||||||
|
|
||||||
|
var query = EntityQuery<SingularityComponent, RandomWalkComponent, TransformComponent>();
|
||||||
|
foreach (var (singulo, walk, singuloXform) in query)
|
||||||
|
{
|
||||||
|
var singuloMapPos = singuloXform.Coordinates.ToMap(EntityManager);
|
||||||
|
|
||||||
|
if (singuloMapPos.MapId != mapPos.MapId)
|
||||||
|
continue;
|
||||||
|
|
||||||
|
var biasBy = mapPos.Position - singuloMapPos.Position;
|
||||||
|
var length = biasBy.Length();
|
||||||
|
if (length <= MinAttractRange)
|
||||||
|
return;
|
||||||
|
|
||||||
|
biasBy = Vector2.Normalize(biasBy) * (attractor.BaseRange / length);
|
||||||
|
|
||||||
|
walk.BiasVector += biasBy;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Resets the pulse timings of the attractor when the component starts up.
|
||||||
|
/// </summary>
|
||||||
|
/// <param name="uid">The uid of the attractor to start up.</param>
|
||||||
|
/// <param name="comp">The state of the attractor to start up.</param>
|
||||||
|
/// <param name="args">The startup prompt arguments.</param>
|
||||||
|
private void OnMapInit(Entity<SingularityAttractorComponent> ent, ref MapInitEvent args)
|
||||||
|
{
|
||||||
|
ent.Comp.LastPulseTime = _timing.CurTime;
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -142,6 +142,9 @@ uplink-voice-mask-desc = A gas mask that lets you adjust your voice to whoever y
|
|||||||
uplink-radio-jammer-name = Radio Jammer
|
uplink-radio-jammer-name = Radio Jammer
|
||||||
uplink-radio-jammer-desc = This device will disrupt any nearby outgoing radio communication when activated.
|
uplink-radio-jammer-desc = This device will disrupt any nearby outgoing radio communication when activated.
|
||||||
|
|
||||||
|
uplink-singularity-beacon-name = Singularity Beacon
|
||||||
|
uplink-singularity-beacon-desc = A device that attracts singularities. Has to be anchored and powered. Causes singularities to grow when consumed.
|
||||||
|
|
||||||
# Implants
|
# Implants
|
||||||
uplink-storage-implanter-name = Storage Implanter
|
uplink-storage-implanter-name = Storage Implanter
|
||||||
uplink-storage-implanter-desc = Hide goodies inside of yourself with new bluespace technology!
|
uplink-storage-implanter-desc = Hide goodies inside of yourself with new bluespace technology!
|
||||||
|
|||||||
@@ -1123,6 +1123,16 @@
|
|||||||
- ResearchDirector
|
- ResearchDirector
|
||||||
- Chef
|
- Chef
|
||||||
|
|
||||||
|
- type: listing
|
||||||
|
id: UplinkSingarityBeacon
|
||||||
|
name: uplink-singularity-beacon-name
|
||||||
|
description: uplink-singularity-beacon-desc
|
||||||
|
productEntity: SingularityBeacon
|
||||||
|
cost:
|
||||||
|
Telecrystal: 12
|
||||||
|
categories:
|
||||||
|
- UplinkUtility
|
||||||
|
|
||||||
# Armor
|
# Armor
|
||||||
|
|
||||||
- type: listing
|
- type: listing
|
||||||
|
|||||||
@@ -0,0 +1,40 @@
|
|||||||
|
- type: entity
|
||||||
|
id: SingularityBeacon
|
||||||
|
parent: BaseMachinePowered
|
||||||
|
name: singularity beacon
|
||||||
|
description: A syndicate device that attracts the singularity. If it's loose and you're seeing this, run.
|
||||||
|
components:
|
||||||
|
- type: Sprite
|
||||||
|
sprite: Objects/Devices/singularity_beacon.rsi
|
||||||
|
layers:
|
||||||
|
- state: icon
|
||||||
|
- type: Item
|
||||||
|
size: Huge
|
||||||
|
- type: Fixtures
|
||||||
|
fixtures:
|
||||||
|
fix1:
|
||||||
|
shape:
|
||||||
|
!type:PhysShapeAabb
|
||||||
|
bounds: "-0.40,-0.40,0.40,0.40"
|
||||||
|
density: 80
|
||||||
|
mask:
|
||||||
|
- MachineMask
|
||||||
|
layer:
|
||||||
|
- MachineLayer
|
||||||
|
- type: SingularityAttractor
|
||||||
|
baseRange: 80
|
||||||
|
- type: SinguloFood
|
||||||
|
energy: 120
|
||||||
|
- type: Destructible
|
||||||
|
thresholds:
|
||||||
|
- trigger:
|
||||||
|
!type:DamageTrigger
|
||||||
|
damage: 50
|
||||||
|
behaviors:
|
||||||
|
- !type:DoActsBehavior
|
||||||
|
acts: [ "Destruction" ]
|
||||||
|
- type: ApcPowerReceiver
|
||||||
|
powerLoad: 15000
|
||||||
|
priority: High
|
||||||
|
- type: StaticPrice
|
||||||
|
price: 1500
|
||||||
Binary file not shown.
|
After Width: | Height: | Size: 1005 B |
@@ -0,0 +1,20 @@
|
|||||||
|
{
|
||||||
|
"version": 1,
|
||||||
|
"license": "CC-BY-SA-3.0",
|
||||||
|
"copyright": "tgstation at https://github.com/tgstation/tgstation/blob/c46d689e2b300bdc8c5b153855b1a5bbb2c5b168/icons/obj/singularity.dmi",
|
||||||
|
"size": {
|
||||||
|
"x": 32,
|
||||||
|
"y": 32
|
||||||
|
},
|
||||||
|
"states": [
|
||||||
|
{
|
||||||
|
"name": "icon",
|
||||||
|
"delays": [
|
||||||
|
[
|
||||||
|
0.1,
|
||||||
|
0.1
|
||||||
|
]
|
||||||
|
]
|
||||||
|
}
|
||||||
|
]
|
||||||
|
}
|
||||||
Reference in New Issue
Block a user