]> git.smokeofanarchy.ru Git - space-station-14.git/commitdiff
Revamped Meteor Swarm (#28974)
authorNemanja <98561806+EmoGarbage404@users.noreply.github.com>
Sat, 15 Jun 2024 03:38:43 +0000 (23:38 -0400)
committerGitHub <noreply@github.com>
Sat, 15 Jun 2024 03:38:43 +0000 (20:38 -0700)
* meteor code and balanced values

* Meteor Swarms

* Update meteors.yml

* Update meteors.yml

* HOO! (fix overkill bug and buff space dust)

* undo BloodstreamComponent.cs changes

* DamageDistribution -> DamageTypes

* part 2.

36 files changed:
Content.Server/Destructible/DestructibleSystem.cs
Content.Server/Mining/MeteorComponent.cs [new file with mode: 0644]
Content.Server/Mining/MeteorSystem.cs [new file with mode: 0644]
Content.Server/StationEvents/Components/MeteorSchedulerComponent.cs [new file with mode: 0644]
Content.Server/StationEvents/Components/MeteorSwarmComponent.cs [new file with mode: 0644]
Content.Server/StationEvents/Components/MeteorSwarmRuleComponent.cs [deleted file]
Content.Server/StationEvents/Events/MeteorSwarmRule.cs [deleted file]
Content.Server/StationEvents/Events/MeteorSwarmSystem.cs [new file with mode: 0644]
Content.Server/StationEvents/MeteorSchedulerSystem.cs [new file with mode: 0644]
Content.Shared/Mobs/Systems/MobThresholdSystem.cs
Resources/Locale/en-US/station-events/events/meteor-swarm.ftl
Resources/Prototypes/Entities/Objects/Weapons/Guns/Launchers/launchers.yml
Resources/Prototypes/Entities/Objects/Weapons/Guns/Projectiles/meteors.yml
Resources/Prototypes/GameRules/events.yml
Resources/Prototypes/GameRules/meteorswarms.yml [new file with mode: 0644]
Resources/Prototypes/game_presets.yml
Resources/Textures/Objects/Misc/meteor.rsi/big.png [new file with mode: 0644]
Resources/Textures/Objects/Misc/meteor.rsi/big_cluster.png [new file with mode: 0644]
Resources/Textures/Objects/Misc/meteor.rsi/big_cluster_pixel.png [new file with mode: 0644]
Resources/Textures/Objects/Misc/meteor.rsi/big_pixel.png [new file with mode: 0644]
Resources/Textures/Objects/Misc/meteor.rsi/firework.png [new file with mode: 0644]
Resources/Textures/Objects/Misc/meteor.rsi/firework_pixel.png [new file with mode: 0644]
Resources/Textures/Objects/Misc/meteor.rsi/human.png [new file with mode: 0644]
Resources/Textures/Objects/Misc/meteor.rsi/human_pixel.png [new file with mode: 0644]
Resources/Textures/Objects/Misc/meteor.rsi/medium.png [new file with mode: 0644]
Resources/Textures/Objects/Misc/meteor.rsi/medium_piercing.png [new file with mode: 0644]
Resources/Textures/Objects/Misc/meteor.rsi/medium_piercing_pixel.png [new file with mode: 0644]
Resources/Textures/Objects/Misc/meteor.rsi/medium_pixel.png [new file with mode: 0644]
Resources/Textures/Objects/Misc/meteor.rsi/medium_radioactive.png [new file with mode: 0644]
Resources/Textures/Objects/Misc/meteor.rsi/medium_radioactive_pixel.png [new file with mode: 0644]
Resources/Textures/Objects/Misc/meteor.rsi/meta.json [new file with mode: 0644]
Resources/Textures/Objects/Misc/meteor.rsi/small.png [new file with mode: 0644]
Resources/Textures/Objects/Misc/meteor.rsi/small_flash.png [new file with mode: 0644]
Resources/Textures/Objects/Misc/meteor.rsi/small_flash_pixel.png [new file with mode: 0644]
Resources/Textures/Objects/Misc/meteor.rsi/small_pixel.png [new file with mode: 0644]
Resources/Textures/Objects/Misc/meteor.rsi/space_dust.png [new file with mode: 0644]

index 16c54fd3b02af0d39518858647cd65483ea83d1e..e0183a037dd738aa48d2009bd7ec503462dac35c 100644 (file)
@@ -1,3 +1,4 @@
+using System.Diagnostics.CodeAnalysis;
 using Content.Server.Administration.Logs;
 using Content.Server.Atmos.EntitySystems;
 using Content.Server.Body.Systems;
@@ -90,6 +91,16 @@ namespace Content.Server.Destructible
             }
         }
 
+        public bool TryGetDestroyedAt(Entity<DestructibleComponent?> ent, [NotNullWhen(true)] out FixedPoint2? destroyedAt)
+        {
+            destroyedAt = null;
+            if (!Resolve(ent, ref ent.Comp, false))
+                return false;
+
+            destroyedAt = DestroyedAt(ent, ent.Comp);
+            return true;
+        }
+
         // FFS this shouldn't be this hard. Maybe this should just be a field of the destructible component. Its not
         // like there is currently any entity that is NOT just destroyed upon reaching a total-damage value.
         /// <summary>
diff --git a/Content.Server/Mining/MeteorComponent.cs b/Content.Server/Mining/MeteorComponent.cs
new file mode 100644 (file)
index 0000000..059c450
--- /dev/null
@@ -0,0 +1,25 @@
+using Content.Shared.Damage;
+
+namespace Content.Server.Mining;
+
+/// <summary>
+/// This is used for meteors which hit objects, dealing damage to destroy/kill the object and dealing equal damage back to itself.
+/// </summary>
+[RegisterComponent, Access(typeof(MeteorSystem))]
+public sealed partial class MeteorComponent : Component
+{
+    /// <summary>
+    /// Damage specifier that is multiplied against the calculated damage amount to determine what damage is applied to the colliding entity.
+    /// </summary>
+    /// <remarks>
+    /// The values of this should add up to 1 or else the damage will be scaled.
+    /// </remarks>
+    [DataField]
+    public DamageSpecifier DamageTypes = new();
+
+    /// <summary>
+    /// A list of entities that this meteor has collided with. used to ensure no double collisions occur.
+    /// </summary>
+    [DataField]
+    public HashSet<EntityUid> HitList = new();
+}
diff --git a/Content.Server/Mining/MeteorSystem.cs b/Content.Server/Mining/MeteorSystem.cs
new file mode 100644 (file)
index 0000000..fc00147
--- /dev/null
@@ -0,0 +1,65 @@
+using Content.Server.Administration.Logs;
+using Content.Server.Destructible;
+using Content.Shared.Damage;
+using Content.Shared.Database;
+using Content.Shared.FixedPoint;
+using Content.Shared.Mobs.Systems;
+using Robust.Shared.Physics.Events;
+using Robust.Shared.Player;
+
+namespace Content.Server.Mining;
+
+public sealed class MeteorSystem : EntitySystem
+{
+    [Dependency] private readonly IAdminLogManager _adminLog = default!;
+    [Dependency] private readonly DamageableSystem _damageable = default!;
+    [Dependency] private readonly DestructibleSystem _destructible = default!;
+    [Dependency] private readonly MobThresholdSystem _mobThreshold = default!;
+
+    /// <inheritdoc/>
+    public override void Initialize()
+    {
+        SubscribeLocalEvent<MeteorComponent, StartCollideEvent>(OnCollide);
+    }
+
+    private void OnCollide(EntityUid uid, MeteorComponent component, ref StartCollideEvent args)
+    {
+        if (TerminatingOrDeleted(args.OtherEntity) || TerminatingOrDeleted(uid))
+            return;
+
+        if (component.HitList.Contains(args.OtherEntity))
+            return;
+
+        FixedPoint2 threshold;
+        if (_mobThreshold.TryGetDeadThreshold(args.OtherEntity, out var mobThreshold))
+        {
+            threshold = mobThreshold.Value;
+            if (HasComp<ActorComponent>(args.OtherEntity))
+                _adminLog.Add(LogType.Action, LogImpact.Extreme, $"{ToPrettyString(args.OtherEntity):player} was struck by meteor {ToPrettyString(uid):ent} and killed instantly.");
+        }
+        else if (_destructible.TryGetDestroyedAt(args.OtherEntity, out var destroyThreshold))
+        {
+            threshold = destroyThreshold.Value;
+        }
+        else
+        {
+            threshold = FixedPoint2.MaxValue;
+        }
+        var otherEntDamage = CompOrNull<DamageableComponent>(args.OtherEntity)?.TotalDamage ?? FixedPoint2.Zero;
+        // account for the damage that the other entity has already taken: don't overkill
+        threshold -= otherEntDamage;
+
+        // The max amount of damage our meteor can take before breaking.
+        var maxMeteorDamage = _destructible.DestroyedAt(uid) - CompOrNull<DamageableComponent>(uid)?.TotalDamage ?? FixedPoint2.Zero;
+
+        // Cap damage so we don't overkill the meteor
+        var trueDamage = FixedPoint2.Min(maxMeteorDamage, threshold);
+
+        var damage = component.DamageTypes * trueDamage;
+        _damageable.TryChangeDamage(args.OtherEntity, damage, true, origin: uid);
+        _damageable.TryChangeDamage(uid, damage);
+
+        if (!TerminatingOrDeleted(args.OtherEntity))
+            component.HitList.Add(args.OtherEntity);
+    }
+}
diff --git a/Content.Server/StationEvents/Components/MeteorSchedulerComponent.cs b/Content.Server/StationEvents/Components/MeteorSchedulerComponent.cs
new file mode 100644 (file)
index 0000000..e71a284
--- /dev/null
@@ -0,0 +1,35 @@
+using Content.Shared.Random;
+using Robust.Shared.Prototypes;
+
+namespace Content.Server.StationEvents.Components;
+
+/// <summary>
+/// This is used for running meteor swarm events at regular intervals.
+/// </summary>
+[RegisterComponent, Access(typeof(MeteorSchedulerSystem)), AutoGenerateComponentPause]
+public sealed partial class MeteorSchedulerComponent : Component
+{
+    /// <summary>
+    /// The weights for which swarms will be selected.
+    /// </summary>
+    [DataField]
+    public ProtoId<WeightedRandomEntityPrototype> Config = "DefaultConfig";
+
+    /// <summary>
+    /// The time at which the next swarm occurs.
+    /// </summary>
+    [DataField, AutoPausedField]
+    public TimeSpan NextSwarmTime = TimeSpan.Zero;
+
+    /// <summary>
+    /// The minimum time between swarms
+    /// </summary>
+    [DataField]
+    public TimeSpan MinSwarmDelay = TimeSpan.FromMinutes(7.5f);
+
+    /// <summary>
+    /// The maximum time between swarms
+    /// </summary>
+    [DataField]
+    public TimeSpan MaxSwarmDelay = TimeSpan.FromMinutes(12.5f);
+}
diff --git a/Content.Server/StationEvents/Components/MeteorSwarmComponent.cs b/Content.Server/StationEvents/Components/MeteorSwarmComponent.cs
new file mode 100644 (file)
index 0000000..1cde2ac
--- /dev/null
@@ -0,0 +1,58 @@
+using Content.Server.StationEvents.Events;
+using Content.Shared.Destructible.Thresholds;
+using Robust.Shared.Audio;
+using Robust.Shared.Prototypes;
+
+namespace Content.Server.StationEvents.Components;
+
+[RegisterComponent, Access(typeof(MeteorSwarmSystem)), AutoGenerateComponentPause]
+public sealed partial class MeteorSwarmComponent : Component
+{
+    [DataField, AutoPausedField]
+    public TimeSpan NextWaveTime;
+
+    /// <summary>
+    /// We'll send a specific amount of waves of meteors towards the station per ending rather than using a timer.
+    /// </summary>
+    [DataField]
+    public int WaveCounter;
+
+    [DataField]
+    public float MeteorVelocity = 10f;
+
+    /// <summary>
+    /// If true, meteors will be thrown from all angles instead of from a singular source
+    /// </summary>
+    [DataField]
+    public bool NonDirectional;
+
+    /// <summary>
+    /// The announcement played when a meteor swarm begins.
+    /// </summary>
+    [DataField]
+    public LocId? Announcement = "station-event-meteor-swarm-start-announcement";
+
+    [DataField]
+    public SoundSpecifier? AnnouncementSound = new SoundPathSpecifier("/Audio/Announcements/meteors.ogg")
+    {
+        Params = new()
+        {
+            Volume = -4
+        }
+    };
+
+    /// <summary>
+    /// Each meteor entity prototype and their corresponding weight in being picked.
+    /// </summary>
+    [DataField]
+    public Dictionary<EntProtoId, float> Meteors = new();
+
+    [DataField]
+    public MinMax Waves = new(3, 3);
+
+    [DataField]
+    public MinMax MeteorsPerWave = new(3, 4);
+
+    [DataField]
+    public MinMax WaveCooldown = new (10, 60);
+}
diff --git a/Content.Server/StationEvents/Components/MeteorSwarmRuleComponent.cs b/Content.Server/StationEvents/Components/MeteorSwarmRuleComponent.cs
deleted file mode 100644 (file)
index 3927f94..0000000
+++ /dev/null
@@ -1,40 +0,0 @@
-using Content.Server.StationEvents.Events;
-
-namespace Content.Server.StationEvents.Components;
-
-[RegisterComponent, Access(typeof(MeteorSwarmRule))]
-public sealed partial class MeteorSwarmRuleComponent : Component
-{
-    [DataField("cooldown")]
-    public float Cooldown;
-
-    /// <summary>
-    /// We'll send a specific amount of waves of meteors towards the station per ending rather than using a timer.
-    /// </summary>
-    [DataField("waveCounter")]
-    public int WaveCounter;
-
-    [DataField("minimumWaves")]
-    public int MinimumWaves = 3;
-
-    [DataField("maximumWaves")]
-    public int MaximumWaves = 8;
-
-    [DataField("minimumCooldown")]
-    public float MinimumCooldown = 10f;
-
-    [DataField("maximumCooldown")]
-    public float MaximumCooldown = 60f;
-
-    [DataField("meteorsPerWave")]
-    public int MeteorsPerWave = 5;
-
-    [DataField("meteorVelocity")]
-    public float MeteorVelocity = 10f;
-
-    [DataField("maxAngularVelocity")]
-    public float MaxAngularVelocity = 0.25f;
-
-    [DataField("minAngularVelocity")]
-    public float MinAngularVelocity = -0.25f;
-}
diff --git a/Content.Server/StationEvents/Events/MeteorSwarmRule.cs b/Content.Server/StationEvents/Events/MeteorSwarmRule.cs
deleted file mode 100644 (file)
index b97cde8..0000000
+++ /dev/null
@@ -1,85 +0,0 @@
-using System.Numerics;
-using Content.Server.GameTicking.Rules.Components;
-using Content.Server.StationEvents.Components;
-using Content.Shared.GameTicking.Components;
-using Robust.Shared.Map;
-using Robust.Shared.Map.Components;
-using Robust.Shared.Physics.Components;
-using Robust.Shared.Physics.Systems;
-using Robust.Shared.Spawners;
-
-namespace Content.Server.StationEvents.Events
-{
-    public sealed class MeteorSwarmRule : StationEventSystem<MeteorSwarmRuleComponent>
-    {
-        [Dependency] private readonly SharedPhysicsSystem _physics = default!;
-
-        protected override void Started(EntityUid uid, MeteorSwarmRuleComponent component, GameRuleComponent gameRule, GameRuleStartedEvent args)
-        {
-            base.Started(uid, component, gameRule, args);
-
-            component.WaveCounter = RobustRandom.Next(component.MinimumWaves, component.MaximumWaves);
-        }
-
-        protected override void ActiveTick(EntityUid uid, MeteorSwarmRuleComponent component, GameRuleComponent gameRule, float frameTime)
-        {
-            if (component.WaveCounter <= 0)
-            {
-                ForceEndSelf(uid, gameRule);
-                return;
-            }
-
-            component.Cooldown -= frameTime;
-
-            if (component.Cooldown > 0f)
-                return;
-
-            component.WaveCounter--;
-
-            component.Cooldown += (component.MaximumCooldown - component.MinimumCooldown) * RobustRandom.NextFloat() + component.MinimumCooldown;
-
-            Box2? playableArea = null;
-            var mapId = GameTicker.DefaultMap;
-
-            var query = AllEntityQuery<MapGridComponent, TransformComponent>();
-            while (query.MoveNext(out var gridId, out _, out var xform))
-            {
-                if (xform.MapID != mapId)
-                    continue;
-
-                var aabb = _physics.GetWorldAABB(gridId);
-                playableArea = playableArea?.Union(aabb) ?? aabb;
-            }
-
-            if (playableArea == null)
-            {
-                ForceEndSelf(uid, gameRule);
-                return;
-            }
-
-            var minimumDistance = (playableArea.Value.TopRight - playableArea.Value.Center).Length() + 50f;
-            var maximumDistance = minimumDistance + 100f;
-
-            var center = playableArea.Value.Center;
-
-            for (var i = 0; i < component.MeteorsPerWave; i++)
-            {
-                var angle = new Angle(RobustRandom.NextFloat() * MathF.Tau);
-                var offset = angle.RotateVec(new Vector2((maximumDistance - minimumDistance) * RobustRandom.NextFloat() + minimumDistance, 0));
-                var spawnPosition = new MapCoordinates(center + offset, mapId);
-                var meteor = Spawn("MeteorLarge", spawnPosition);
-                var physics = EntityManager.GetComponent<PhysicsComponent>(meteor);
-                _physics.SetBodyStatus(meteor, physics, BodyStatus.InAir);
-                _physics.SetLinearDamping(meteor, physics, 0f);
-                _physics.SetAngularDamping(meteor, physics, 0f);
-                _physics.ApplyLinearImpulse(meteor, -offset.Normalized() * component.MeteorVelocity * physics.Mass, body: physics);
-                _physics.ApplyAngularImpulse(
-                    meteor,
-                    physics.Mass * ((component.MaxAngularVelocity - component.MinAngularVelocity) * RobustRandom.NextFloat() + component.MinAngularVelocity),
-                    body: physics);
-
-                EnsureComp<TimedDespawnComponent>(meteor).Lifetime = 120f;
-            }
-        }
-    }
-}
diff --git a/Content.Server/StationEvents/Events/MeteorSwarmSystem.cs b/Content.Server/StationEvents/Events/MeteorSwarmSystem.cs
new file mode 100644 (file)
index 0000000..e085a2e
--- /dev/null
@@ -0,0 +1,82 @@
+using System.Numerics;
+using Content.Server.Chat.Systems;
+using Content.Server.GameTicking.Rules;
+using Content.Server.Station.Components;
+using Content.Server.Station.Systems;
+using Content.Server.StationEvents.Components;
+using Content.Shared.GameTicking.Components;
+using Content.Shared.Random.Helpers;
+using Robust.Server.Audio;
+using Robust.Shared.Map;
+using Robust.Shared.Physics.Components;
+using Robust.Shared.Physics.Systems;
+using Robust.Shared.Player;
+using Robust.Shared.Random;
+
+namespace Content.Server.StationEvents.Events;
+
+public sealed class MeteorSwarmSystem : GameRuleSystem<MeteorSwarmComponent>
+{
+    [Dependency] private readonly SharedPhysicsSystem _physics = default!;
+    [Dependency] private readonly AudioSystem _audio = default!;
+    [Dependency] private readonly ChatSystem _chat = default!;
+    [Dependency] private readonly StationSystem _station = default!;
+
+    protected override void Added(EntityUid uid, MeteorSwarmComponent component, GameRuleComponent gameRule, GameRuleAddedEvent args)
+    {
+        base.Added(uid, component, gameRule, args);
+
+        component.WaveCounter = component.Waves.Next(RobustRandom);
+
+        if (component.Announcement is { } locId)
+            _chat.DispatchGlobalAnnouncement(Loc.GetString(locId), playSound: false, colorOverride: Color.Yellow);
+        _audio.PlayGlobal(component.AnnouncementSound, Filter.Broadcast(), true);
+    }
+
+    protected override void ActiveTick(EntityUid uid, MeteorSwarmComponent component, GameRuleComponent gameRule, float frameTime)
+    {
+        if (Timing.CurTime < component.NextWaveTime)
+            return;
+
+        component.NextWaveTime += TimeSpan.FromSeconds(component.WaveCooldown.Next(RobustRandom));
+
+
+        if (_station.GetStations().Count == 0)
+            return;
+
+        var station = RobustRandom.Pick(_station.GetStations());
+        if (_station.GetLargestGrid(Comp<StationDataComponent>(station)) is not { } grid)
+            return;
+
+        var mapId = Transform(grid).MapID;
+        var playableArea = _physics.GetWorldAABB(grid);
+
+        var minimumDistance = (playableArea.TopRight - playableArea.Center).Length() + 50f;
+        var maximumDistance = minimumDistance + 100f;
+
+        var center = playableArea.Center;
+
+        var meteorsToSpawn = component.MeteorsPerWave.Next(RobustRandom);
+        for (var i = 0; i < meteorsToSpawn; i++)
+        {
+            var spawnProto = RobustRandom.Pick(component.Meteors);
+
+            var angle = component.NonDirectional
+                ? RobustRandom.NextAngle()
+                : new Random(uid.Id).NextAngle();
+
+            var offset = angle.RotateVec(new Vector2((maximumDistance - minimumDistance) * RobustRandom.NextFloat() + minimumDistance, 0));
+            var subOffset = RobustRandom.NextAngle().RotateVec(new Vector2( (playableArea.TopRight - playableArea.Center).Length() / 2 * RobustRandom.NextFloat(), 0));
+            var spawnPosition = new MapCoordinates(center + offset + subOffset, mapId);
+            var meteor = Spawn(spawnProto, spawnPosition);
+            var physics = Comp<PhysicsComponent>(meteor);
+            _physics.ApplyLinearImpulse(meteor, -offset.Normalized() * component.MeteorVelocity * physics.Mass, body: physics);
+        }
+
+        component.WaveCounter--;
+        if (component.WaveCounter <= 0)
+        {
+            ForceEndSelf(uid, gameRule);
+        }
+    }
+}
diff --git a/Content.Server/StationEvents/MeteorSchedulerSystem.cs b/Content.Server/StationEvents/MeteorSchedulerSystem.cs
new file mode 100644 (file)
index 0000000..3ad88a7
--- /dev/null
@@ -0,0 +1,39 @@
+using Content.Server.GameTicking.Rules;
+using Content.Server.StationEvents.Components;
+using Content.Shared.GameTicking.Components;
+using Content.Shared.Random.Helpers;
+using Robust.Shared.Prototypes;
+
+namespace Content.Server.StationEvents;
+
+/// <summary>
+/// This handles scheduling and launching meteors at a station at regular intervals.
+/// TODO: there is 100% a world in which this is genericized and can be used for lots of basic event scheduling
+/// </summary>
+public sealed class MeteorSchedulerSystem : GameRuleSystem<MeteorSchedulerComponent>
+{
+    [Dependency] private readonly IPrototypeManager _prototypeManager = default!;
+
+    protected override void Started(EntityUid uid, MeteorSchedulerComponent component, GameRuleComponent gameRule, GameRuleStartedEvent args)
+    {
+        base.Started(uid, component, gameRule, args);
+
+        component.NextSwarmTime = Timing.CurTime + RobustRandom.Next(component.MinSwarmDelay, component.MaxSwarmDelay);
+    }
+
+    protected override void ActiveTick(EntityUid uid, MeteorSchedulerComponent component, GameRuleComponent gameRule, float frameTime)
+    {
+        base.ActiveTick(uid, component, gameRule, frameTime);
+
+        if (Timing.CurTime < component.NextSwarmTime)
+            return;
+        RunSwarm((uid, component));
+        component.NextSwarmTime += RobustRandom.Next(component.MinSwarmDelay, component.MaxSwarmDelay);
+    }
+
+    private void RunSwarm(Entity<MeteorSchedulerComponent> ent)
+    {
+        var swarmWeights = _prototypeManager.Index(ent.Comp.Config);
+        GameTicker.StartGameRule(swarmWeights.Pick(RobustRandom));
+    }
+}
index b11de9eac56d874f00a344e42faac1a267ba5adb..eeaecc24d800b6bb023d2c23b0f0a3bde57b8583 100644 (file)
@@ -212,7 +212,7 @@ public sealed class MobThresholdSystem : EntitySystem
         MobThresholdsComponent? thresholdComponent = null)
     {
         threshold = null;
-        if (!Resolve(target, ref thresholdComponent))
+        if (!Resolve(target, ref thresholdComponent, false))
             return false;
 
         return TryGetThresholdForState(target, MobState.Dead, out threshold, thresholdComponent);
index 6a96c5604812d9a8b18eb7beb2bb3b1c8162cd2e..0090c170caa227a2f389524f82fdb471912ad3af 100644 (file)
@@ -1,2 +1,5 @@
-station-event-meteor-swarm-start-announcement = Meteors are on a collision course with the station. Brace for impact.
+station-event-meteor-swarm-start-announcement = Meteors have been detected on collision course with the station.
 station-event-meteor-swarm-end-announcement = The meteor swarm has passed. Please return to your stations.
+
+station-event-space-dust-start-announcement = The station is passing through a debris cloud, expect minor damage to external fittings and fixtures.
+station-event-meteor-urist-start-announcement = The station is colliding with an unidentified swarm of debris. Please stay calm and do not listen to them.
index 728783fb3eafff298b02299351922340979d00ca..0d10411cfbbf5ed32ec05585512907a8eae785f1 100644 (file)
     whitelist:
       tags:
         - CartridgeRocket
-    proto: MeteorLarge
+    proto: MeteorMedium
 
 - type: entity
   name: immovable rod launcher
index 6bdac1e85f0ecc6eb3b1917fc51943d53b09658a..3468cade76b564c897caa3f6c04a5be736ad7c78 100644 (file)
 - type: entity
-  id: MeteorLarge
+  id: BaseMeteor
   name: meteor
-  noSpawn: true
+  description: You prefer them when they're burning up in the atmosphere.
+  abstract: true
   components:
   - type: Sprite
     noRot: false
-    sprite: Objects/Weapons/Guns/Projectiles/meteor.rsi
-    scale: 4,4
-    layers:
-    - state: large
-      shader: unshaded
-  - type: ExplodeOnTrigger
-  - type: DeleteOnTrigger
-  - type: TriggerOnCollide
-    fixtureID: projectile
+    sprite: Objects/Misc/meteor.rsi
   - type: Projectile
     damage: {}
     deleteOnCollide: false
-  - type: Explosive
-    explosionType: Default
-    totalIntensity: 600.0
-    intensitySlope: 30
-    maxIntensity: 45
+  - type: Meteor
+    damageTypes:
+      types:
+        Blunt: 1
+  - type: TimedDespawn
+    lifetime: 120
+  - type: Clickable
   - type: Physics
     bodyType: Dynamic
+    bodyStatus: InAir
+    angularDamping: 0
+    linearDamping: 0
+  - type: Fixtures
+    fixtures:
+      projectile:
+        shape:
+          !type:PhysShapeCircle
+          radius: 0.4
+        density: 100
+        hard: false
+        layer:
+        - LargeMobLayer
+        mask:
+        - Impassable
+        - BulletImpassable
+  - type: Damageable
+    damageContainer: Inorganic
+  - type: Explosive
+    explosionType: Default
+    intensitySlope: 4
+    maxIntensity: 100
+
+- type: entity
+  parent: BaseMeteor
+  id: MeteorSpaceDust
+  name: space dust
+  description: Makes a station sneeze.
+  components:
+  - type: Sprite
+    state: space_dust
   - type: Fixtures
     fixtures:
       projectile:
         shape:
           !type:PhysShapeCircle
-          radius: 0.8
+          radius: 0.45
         density: 100
-        hard: true
-        # Didn't use MapGridComponent for now as the bounds are stuffed.
+        hard: false
         layer:
         - LargeMobLayer
         mask:
         - Impassable
         - BulletImpassable
+  - type: Explosive
+    totalIntensity: 25
+  - type: Destructible
+    thresholds:
+    - trigger:
+        !type:DamageTrigger
+        damage: 100
+      behaviors:
+      - !type:DoActsBehavior
+        acts: [ "Destruction" ]
+      - !type:PlaySoundBehavior
+        sound:
+          collection: MetalBreak
+      - !type:ExplodeBehavior
+
+- type: entity
+  parent: BaseMeteor
+  id: MeteorSmall
+  suffix: Small
+  components:
+  - type: Sprite
+    state: small
+  - type: Fixtures
+    fixtures:
+      projectile:
+        shape:
+          !type:PhysShapeCircle
+          radius: 0.25
+        density: 100
+        hard: false
+        layer:
+        - LargeMobLayer
+        mask:
+        - Impassable
+        - BulletImpassable
+  - type: Explosive
+    totalIntensity: 100
+  - type: Destructible
+    thresholds:
+    - trigger:
+        !type:DamageTrigger
+        damage: 1250
+      behaviors:
+      - !type:DoActsBehavior
+        acts: [ "Destruction" ]
+      - !type:PlaySoundBehavior
+        sound:
+          collection: MetalBreak
+      - !type:ExplodeBehavior
+
+- type: entity
+  parent: BaseMeteor
+  id: MeteorMedium
+  suffix: Medium
+  components:
+  - type: Sprite
+    state: medium
+  - type: Fixtures
+    fixtures:
+      projectile:
+        shape:
+          !type:PhysShapeCircle
+          radius: 0.3
+        density: 100
+        hard: false
+        layer:
+        - LargeMobLayer
+        mask:
+        - Impassable
+        - BulletImpassable
+  - type: Explosive
+    totalIntensity: 200
+  - type: Destructible
+    thresholds:
+    - trigger:
+        !type:DamageTrigger
+        damage: 1750
+      behaviors:
+      - !type:DoActsBehavior
+        acts: [ "Destruction" ]
+      - !type:PlaySoundBehavior
+        sound:
+          collection: MetalBreak
+      - !type:ExplodeBehavior
+
+- type: entity
+  parent: BaseMeteor
+  id: MeteorLarge
+  suffix: Large
+  components:
+  - type: Sprite
+    state: big
+  - type: Explosive
+    totalIntensity: 300
+  - type: Destructible
+    thresholds:
+    - trigger:
+        !type:DamageTrigger
+        damage: 2500
+      behaviors:
+      - !type:DoActsBehavior
+        acts: [ "Destruction" ]
+      - !type:PlaySoundBehavior
+        sound:
+          collection: MetalBreak
+      - !type:ExplodeBehavior
+
+- type: entity
+  parent: BaseMeteor
+  id: MeteorUrist
+  name: Urist McMeteor
+  description: As a successful member of society with a stable unflinching psyche and limitless drive, natural affinity for finance and domination, you have been selected, no, you have been effortlessly guided by divine (biological) trauma towards this moment. The gates of destiny fling open, and once again you're left standing on pulsating nothingness. A strobing headache of the soul.
+  suffix: Meteor
+  components:
+  - type: Sprite
+    state: human_pixel
+  - type: SolutionContainerManager
+    solutions:
+      blood:
+        maxVol: 1000
+        reagents:
+        - ReagentId: Blood
+          Quantity: 1000
+  - type: Explosive
+    totalIntensity: 25
+  - type: Destructible
+    thresholds:
+    - trigger:
+        !type:DamageTrigger
+        damage: 3000
+      behaviors:
+      - !type:DoActsBehavior
+        acts: [ "Destruction" ]
+      - !type:PlaySoundBehavior
+        sound:
+          collection: MaleScreams
+          params:
+            volume: 10
+      - !type:SpillBehavior
+        solution: blood
+      - !type:ExplodeBehavior
index 04918578fa3e0a5fab2f75bfc026368e2d7d8697..853853fb22b1ef5e6917ad08f70d4c7204d4e656 100644 (file)
     duration: 240
   - type: KudzuGrowthRule
 
-- type: entity
-  id: MeteorSwarm
-  parent: BaseStationEventLongDelay
-  components:
-  - type: StationEvent
-    earliestStart: 30
-    weight: 7.5
-    minimumPlayers: 10 #Enough to hopefully have at least one engineering guy
-    startAnnouncement: station-event-meteor-swarm-start-announcement
-    endAnnouncement: station-event-meteor-swarm-end-announcement
-    startAudio:
-      path: /Audio/Announcements/meteors.ogg
-      params:
-        volume: -4
-    duration: null #ending is handled by MeteorSwarmRule
-  - type: MeteorSwarmRule
-
 - type: entity
   id: MouseMigration
   parent: BaseStationEventShortDelay
diff --git a/Resources/Prototypes/GameRules/meteorswarms.yml b/Resources/Prototypes/GameRules/meteorswarms.yml
new file mode 100644 (file)
index 0000000..70dd526
--- /dev/null
@@ -0,0 +1,104 @@
+- type: entity
+  parent: BaseGameRule
+  id: GameRuleMeteorScheduler
+  components:
+  - type: GameRule
+    minPlayers: 25
+  - type: MeteorScheduler
+
+- type: weightedRandomEntity
+  id: DefaultConfig
+  weights:
+    GameRuleSpaceDustMinor: 44
+    GameRuleSpaceDustMajor: 22
+    GameRuleMeteorSwarmSmall: 18
+    GameRuleMeteorSwarmMedium: 10
+    GameRuleMeteorSwarmLarge: 5
+    GameRuleUristSwarm: 0.05
+
+- type: entity
+  parent: BaseGameRule
+  id: GameRuleMeteorSwarm
+  abstract: true
+  components:
+  - type: GameRule
+  - type: MeteorSwarm
+
+- type: entity
+  parent: GameRuleMeteorSwarm
+  id: GameRuleSpaceDustMinor
+  components:
+  - type: MeteorSwarm
+    announcement: null
+    announcementSound: null
+    nonDirectional: true
+    meteors:
+      MeteorSpaceDust: 1
+    waves:
+      min: 2
+      max: 3
+    meteorsPerWave:
+      min: 3
+      max: 5
+
+- type: entity
+  parent: GameRuleMeteorSwarm
+  id: GameRuleSpaceDustMajor
+  components:
+  - type: MeteorSwarm
+    announcement: station-event-space-dust-start-announcement
+    announcementSound: /Audio/Announcements/attention.ogg
+    nonDirectional: true
+    meteors:
+      MeteorSpaceDust: 1
+    waves:
+      min: 2
+      max: 3
+    meteorsPerWave:
+      min: 8
+      max: 12
+
+- type: entity
+  parent: GameRuleMeteorSwarm
+  id: GameRuleMeteorSwarmSmall
+  components:
+  - type: MeteorSwarm
+    meteors:
+      MeteorSmall: 7
+      MeteorMedium: 3
+
+- type: entity
+  parent: GameRuleMeteorSwarm
+  id: GameRuleMeteorSwarmMedium
+  components:
+  - type: MeteorSwarm
+    meteors:
+      MeteorSmall: 3
+      MeteorMedium: 6
+      MeteorLarge: 1
+
+- type: entity
+  parent: GameRuleMeteorSwarm
+  id: GameRuleMeteorSwarmLarge
+  components:
+  - type: MeteorSwarm
+    meteors:
+      MeteorSmall: 2
+      MeteorMedium: 4
+      MeteorLarge: 4
+
+- type: entity
+  parent: GameRuleMeteorSwarm
+  id: GameRuleUristSwarm
+  components:
+  - type: MeteorSwarm
+    announcement: station-event-meteor-urist-start-announcement
+    announcementSound: /Audio/Announcements/attention.ogg
+    meteors:
+      MeteorUrist: 1
+    waves:
+      min: 3
+      max: 3
+    meteorsPerWave:
+      min: 10
+      max: 10
index 3bc6ae6208c01958d10c9e14ff000cbc3324c3cd..8876468bbdb5d1f14cae8ab863c4d2337109efb7 100644 (file)
@@ -31,6 +31,7 @@
   description: extended-description
   rules:
   - BasicStationEventScheduler
+  - GameRuleMeteorScheduler
   - BasicRoundstartVariation
 
 - type: gamePreset
@@ -64,6 +65,7 @@
   description: secret-description
   rules:
     - BasicStationEventScheduler
+    - GameRuleMeteorScheduler
 
 - type: gamePreset
   id: SecretGreenshift #For Admin Use: Runs Greenshift but shows "Secret" in lobby.
@@ -95,6 +97,7 @@
     - Traitor
     - SubGamemodesRule
     - BasicStationEventScheduler
+    - GameRuleMeteorScheduler
     - BasicRoundstartVariation
 
 - type: gamePreset
     - Nukeops
     - SubGamemodesRule
     - BasicStationEventScheduler
+    - GameRuleMeteorScheduler
     - BasicRoundstartVariation
 
 - type: gamePreset
     - Revolutionary
     - SubGamemodesRule
     - BasicStationEventScheduler
+    - GameRuleMeteorScheduler
     - BasicRoundstartVariation
 
 - type: gamePreset
   rules:
   - Zombie
   - BasicStationEventScheduler
+  - GameRuleMeteorScheduler
   - BasicRoundstartVariation
diff --git a/Resources/Textures/Objects/Misc/meteor.rsi/big.png b/Resources/Textures/Objects/Misc/meteor.rsi/big.png
new file mode 100644 (file)
index 0000000..34062b9
Binary files /dev/null and b/Resources/Textures/Objects/Misc/meteor.rsi/big.png differ
diff --git a/Resources/Textures/Objects/Misc/meteor.rsi/big_cluster.png b/Resources/Textures/Objects/Misc/meteor.rsi/big_cluster.png
new file mode 100644 (file)
index 0000000..292c93f
Binary files /dev/null and b/Resources/Textures/Objects/Misc/meteor.rsi/big_cluster.png differ
diff --git a/Resources/Textures/Objects/Misc/meteor.rsi/big_cluster_pixel.png b/Resources/Textures/Objects/Misc/meteor.rsi/big_cluster_pixel.png
new file mode 100644 (file)
index 0000000..292c93f
Binary files /dev/null and b/Resources/Textures/Objects/Misc/meteor.rsi/big_cluster_pixel.png differ
diff --git a/Resources/Textures/Objects/Misc/meteor.rsi/big_pixel.png b/Resources/Textures/Objects/Misc/meteor.rsi/big_pixel.png
new file mode 100644 (file)
index 0000000..34062b9
Binary files /dev/null and b/Resources/Textures/Objects/Misc/meteor.rsi/big_pixel.png differ
diff --git a/Resources/Textures/Objects/Misc/meteor.rsi/firework.png b/Resources/Textures/Objects/Misc/meteor.rsi/firework.png
new file mode 100644 (file)
index 0000000..1c661fc
Binary files /dev/null and b/Resources/Textures/Objects/Misc/meteor.rsi/firework.png differ
diff --git a/Resources/Textures/Objects/Misc/meteor.rsi/firework_pixel.png b/Resources/Textures/Objects/Misc/meteor.rsi/firework_pixel.png
new file mode 100644 (file)
index 0000000..24b0847
Binary files /dev/null and b/Resources/Textures/Objects/Misc/meteor.rsi/firework_pixel.png differ
diff --git a/Resources/Textures/Objects/Misc/meteor.rsi/human.png b/Resources/Textures/Objects/Misc/meteor.rsi/human.png
new file mode 100644 (file)
index 0000000..d73b573
Binary files /dev/null and b/Resources/Textures/Objects/Misc/meteor.rsi/human.png differ
diff --git a/Resources/Textures/Objects/Misc/meteor.rsi/human_pixel.png b/Resources/Textures/Objects/Misc/meteor.rsi/human_pixel.png
new file mode 100644 (file)
index 0000000..8207cd5
Binary files /dev/null and b/Resources/Textures/Objects/Misc/meteor.rsi/human_pixel.png differ
diff --git a/Resources/Textures/Objects/Misc/meteor.rsi/medium.png b/Resources/Textures/Objects/Misc/meteor.rsi/medium.png
new file mode 100644 (file)
index 0000000..27b6b98
Binary files /dev/null and b/Resources/Textures/Objects/Misc/meteor.rsi/medium.png differ
diff --git a/Resources/Textures/Objects/Misc/meteor.rsi/medium_piercing.png b/Resources/Textures/Objects/Misc/meteor.rsi/medium_piercing.png
new file mode 100644 (file)
index 0000000..b758c2c
Binary files /dev/null and b/Resources/Textures/Objects/Misc/meteor.rsi/medium_piercing.png differ
diff --git a/Resources/Textures/Objects/Misc/meteor.rsi/medium_piercing_pixel.png b/Resources/Textures/Objects/Misc/meteor.rsi/medium_piercing_pixel.png
new file mode 100644 (file)
index 0000000..b758c2c
Binary files /dev/null and b/Resources/Textures/Objects/Misc/meteor.rsi/medium_piercing_pixel.png differ
diff --git a/Resources/Textures/Objects/Misc/meteor.rsi/medium_pixel.png b/Resources/Textures/Objects/Misc/meteor.rsi/medium_pixel.png
new file mode 100644 (file)
index 0000000..27b6b98
Binary files /dev/null and b/Resources/Textures/Objects/Misc/meteor.rsi/medium_pixel.png differ
diff --git a/Resources/Textures/Objects/Misc/meteor.rsi/medium_radioactive.png b/Resources/Textures/Objects/Misc/meteor.rsi/medium_radioactive.png
new file mode 100644 (file)
index 0000000..a4e0cf2
Binary files /dev/null and b/Resources/Textures/Objects/Misc/meteor.rsi/medium_radioactive.png differ
diff --git a/Resources/Textures/Objects/Misc/meteor.rsi/medium_radioactive_pixel.png b/Resources/Textures/Objects/Misc/meteor.rsi/medium_radioactive_pixel.png
new file mode 100644 (file)
index 0000000..a4e0cf2
Binary files /dev/null and b/Resources/Textures/Objects/Misc/meteor.rsi/medium_radioactive_pixel.png differ
diff --git a/Resources/Textures/Objects/Misc/meteor.rsi/meta.json b/Resources/Textures/Objects/Misc/meteor.rsi/meta.json
new file mode 100644 (file)
index 0000000..1e857be
--- /dev/null
@@ -0,0 +1,271 @@
+{
+  "version": 1,
+  "license": "CC-BY-SA-3.0",
+  "copyright": "Taken from vgstation13 at https://github.com/vgstation-coders/vgstation13/blob/31dd6749bfe32810c46e7913efc99a187479cd51/icons/obj/meteor.dmi",
+  "size": {
+    "x": 32,
+    "y": 32
+  },
+  "states": [
+    {
+      "name": "small",
+      "delays": [
+        [
+          0.1,
+          0.1,
+          0.1,
+          0.1,
+          0.1,
+          0.1,
+          0.1,
+          0.1
+        ]
+      ]
+    },
+    {
+      "name": "small_pixel",
+      "delays": [
+        [
+          0.1,
+          0.1,
+          0.1,
+          0.1,
+          0.1,
+          0.1,
+          0.1,
+          0.1
+        ]
+      ]
+    },
+    {
+      "name": "small_flash",
+      "delays": [
+        [
+          0.1,
+          0.1,
+          0.1,
+          0.1,
+          0.1,
+          0.1,
+          0.1,
+          0.1
+        ]
+      ]
+    },
+    {
+      "name": "small_flash_pixel",
+      "delays": [
+        [
+          0.1,
+          0.1,
+          0.1,
+          0.1,
+          0.1,
+          0.1,
+          0.1,
+          0.1
+        ]
+      ]
+    },
+    {
+      "name": "space_dust"
+    },
+    {
+      "name": "medium",
+      "delays": [
+        [
+          0.1,
+          0.1,
+          0.1,
+          0.1,
+          0.1,
+          0.1,
+          0.1,
+          0.1
+        ]
+      ]
+    },
+    {
+      "name": "medium_pixel",
+      "delays": [
+        [
+          0.1,
+          0.1,
+          0.1,
+          0.1,
+          0.1,
+          0.1,
+          0.1,
+          0.1
+        ]
+      ]
+    },
+    {
+      "name": "medium_piercing",
+      "delays": [
+        [
+          0.1,
+          0.1,
+          0.1,
+          0.1,
+          0.1,
+          0.1,
+          0.1,
+          0.1
+        ]
+      ]
+    },
+    {
+      "name": "medium_piercing_pixel",
+      "delays": [
+        [
+          0.1,
+          0.1,
+          0.1,
+          0.1,
+          0.1,
+          0.1,
+          0.1,
+          0.1
+        ]
+      ]
+    },
+    {
+      "name": "medium_radioactive",
+      "delays": [
+        [
+          0.1,
+          0.1,
+          0.1,
+          0.1,
+          0.1,
+          0.1,
+          0.1,
+          0.1
+        ]
+      ]
+    },
+    {
+      "name": "medium_radioactive_pixel",
+      "delays": [
+        [
+          0.1,
+          0.1,
+          0.1,
+          0.1,
+          0.1,
+          0.1,
+          0.1,
+          0.1
+        ]
+      ]
+    },
+    {
+      "name": "big",
+      "delays": [
+        [
+          0.1,
+          0.1,
+          0.1,
+          0.1,
+          0.1,
+          0.1,
+          0.1,
+          0.1
+        ]
+      ]
+    },
+    {
+      "name": "big_pixel",
+      "delays": [
+        [
+          0.1,
+          0.1,
+          0.1,
+          0.1,
+          0.1,
+          0.1,
+          0.1,
+          0.1
+        ]
+      ]
+    },
+    {
+      "name": "big_cluster",
+      "delays": [
+        [
+          0.1,
+          0.1,
+          0.1,
+          0.1,
+          0.1,
+          0.1,
+          0.1,
+          0.1
+        ]
+      ]
+    },
+    {
+      "name": "big_cluster_pixel",
+      "delays": [
+        [
+          0.1,
+          0.1,
+          0.1,
+          0.1,
+          0.1,
+          0.1,
+          0.1,
+          0.1
+        ]
+      ]
+    },
+    {
+      "name": "human",
+      "directions": 4,
+      "delays": [
+        [
+          0.1,
+          0.1,
+          0.1,
+          0.1
+        ],
+        [
+          0.1,
+          0.1,
+          0.1,
+          0.1
+        ],
+        [
+          0.1,
+          0.1,
+          0.1,
+          0.1
+        ],
+        [
+          0.1,
+          0.1,
+          0.1,
+          0.1
+        ]
+      ]
+    },
+    {
+      "name": "human_pixel",
+      "delays": [
+        [
+          0.1,
+          0.1,
+          0.1,
+          0.1
+        ]
+      ]
+    },
+    {
+      "name": "firework_pixel"
+    },
+    {
+      "name": "firework"
+    }
+  ]
+}
\ No newline at end of file
diff --git a/Resources/Textures/Objects/Misc/meteor.rsi/small.png b/Resources/Textures/Objects/Misc/meteor.rsi/small.png
new file mode 100644 (file)
index 0000000..2c729ac
Binary files /dev/null and b/Resources/Textures/Objects/Misc/meteor.rsi/small.png differ
diff --git a/Resources/Textures/Objects/Misc/meteor.rsi/small_flash.png b/Resources/Textures/Objects/Misc/meteor.rsi/small_flash.png
new file mode 100644 (file)
index 0000000..7ff19cd
Binary files /dev/null and b/Resources/Textures/Objects/Misc/meteor.rsi/small_flash.png differ
diff --git a/Resources/Textures/Objects/Misc/meteor.rsi/small_flash_pixel.png b/Resources/Textures/Objects/Misc/meteor.rsi/small_flash_pixel.png
new file mode 100644 (file)
index 0000000..7ff19cd
Binary files /dev/null and b/Resources/Textures/Objects/Misc/meteor.rsi/small_flash_pixel.png differ
diff --git a/Resources/Textures/Objects/Misc/meteor.rsi/small_pixel.png b/Resources/Textures/Objects/Misc/meteor.rsi/small_pixel.png
new file mode 100644 (file)
index 0000000..2c729ac
Binary files /dev/null and b/Resources/Textures/Objects/Misc/meteor.rsi/small_pixel.png differ
diff --git a/Resources/Textures/Objects/Misc/meteor.rsi/space_dust.png b/Resources/Textures/Objects/Misc/meteor.rsi/space_dust.png
new file mode 100644 (file)
index 0000000..6e13a86
Binary files /dev/null and b/Resources/Textures/Objects/Misc/meteor.rsi/space_dust.png differ