]> git.smokeofanarchy.ru Git - space-station-14.git/commitdiff
add syndicate singularity beacon (#18486)
authorIlya246 <57039557+Ilya246@users.noreply.github.com>
Sat, 6 Jan 2024 08:21:24 +0000 (12:21 +0400)
committerGitHub <noreply@github.com>
Sat, 6 Jan 2024 08:21:24 +0000 (00:21 -0800)
* 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>
Content.Server/Physics/Components/RandomWalkComponent.cs
Content.Server/Physics/Controllers/RandomWalkController.cs
Content.Server/Singularity/Components/SingularityAttractorComponent.cs [new file with mode: 0644]
Content.Server/Singularity/EntitySystems/SingularityAttractorSystem.cs [new file with mode: 0644]
Resources/Locale/en-US/store/uplink-catalog.ftl
Resources/Prototypes/Catalog/uplink_catalog.yml
Resources/Prototypes/Entities/Objects/Devices/Syndicate_Gadgets/singularity_beacon.yml [new file with mode: 0644]
Resources/Textures/Objects/Devices/singularity_beacon.rsi/icon.png [new file with mode: 0644]
Resources/Textures/Objects/Devices/singularity_beacon.rsi/meta.json [new file with mode: 0644]

index e017f61dddd1cb778d9cf90e5b8871fbacfe33f3..90f035b84d1821d5e3a9d154855dcaf04b0b5e5b 100644 (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>
index 4a93a9e706a12bcc4bcd346390fd96ee0ff6af18..61d393c488dd84be7407c7ca0d35b9be9c6f5c27 100644 (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>
diff --git a/Content.Server/Singularity/Components/SingularityAttractorComponent.cs b/Content.Server/Singularity/Components/SingularityAttractorComponent.cs
new file mode 100644 (file)
index 0000000..d5adfd9
--- /dev/null
@@ -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!;
+}
diff --git a/Content.Server/Singularity/EntitySystems/SingularityAttractorSystem.cs b/Content.Server/Singularity/EntitySystems/SingularityAttractorSystem.cs
new file mode 100644 (file)
index 0000000..3c44a7f
--- /dev/null
@@ -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;
+    }
+}
index 50823a58973be8c6a002e62872a83e269ab34390..746ae3d9765df022951887572b9adcfe9e7de3f6 100644 (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!
index 36c3c1435f40ba8ad0aaf987a3e18e821bc31a7c..aa6bf7409644ea078a9ccb3fbc44777c0f963aec 100644 (file)
     - 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
diff --git a/Resources/Prototypes/Entities/Objects/Devices/Syndicate_Gadgets/singularity_beacon.yml b/Resources/Prototypes/Entities/Objects/Devices/Syndicate_Gadgets/singularity_beacon.yml
new file mode 100644 (file)
index 0000000..372b689
--- /dev/null
@@ -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
diff --git a/Resources/Textures/Objects/Devices/singularity_beacon.rsi/icon.png b/Resources/Textures/Objects/Devices/singularity_beacon.rsi/icon.png
new file mode 100644 (file)
index 0000000..78bb95b
Binary files /dev/null and b/Resources/Textures/Objects/Devices/singularity_beacon.rsi/icon.png differ
diff --git a/Resources/Textures/Objects/Devices/singularity_beacon.rsi/meta.json b/Resources/Textures/Objects/Devices/singularity_beacon.rsi/meta.json
new file mode 100644 (file)
index 0000000..2347b23
--- /dev/null
@@ -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
+        ]
+      ]
+    }
+  ]
+}