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:
Ilya246
2024-01-06 12:21:24 +04:00
committed by GitHub
parent 2629a8d92c
commit 154416787e
9 changed files with 229 additions and 2 deletions

View File

@@ -1,3 +1,4 @@
using System.Numerics;
using Content.Server.Physics.Controllers;
namespace Content.Server.Physics.Components;
@@ -29,6 +30,18 @@ public sealed partial class RandomWalkComponent : Component
[ViewVariables(VVAccess.ReadWrite)]
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>
/// Whether this random walker should take a step immediately when it starts up.
/// </summary>

View File

@@ -1,3 +1,4 @@
using System.Numerics;
using Content.Server.Physics.Components;
using Content.Shared.Follower.Components;
using Content.Shared.Throwing;
@@ -69,11 +70,15 @@ internal sealed class RandomWalkController : VirtualController
if(!Resolve(uid, ref physics))
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);
_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>

View File

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

View File

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

View File

@@ -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-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
uplink-storage-implanter-name = Storage Implanter
uplink-storage-implanter-desc = Hide goodies inside of yourself with new bluespace technology!

View File

@@ -1123,6 +1123,16 @@
- ResearchDirector
- Chef
- type: listing
id: UplinkSingarityBeacon
name: uplink-singularity-beacon-name
description: uplink-singularity-beacon-desc
productEntity: SingularityBeacon
cost:
Telecrystal: 12
categories:
- UplinkUtility
# Armor
- type: listing

View File

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

View File

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