From 8fa7f902a14b49dd937beab7e6a44adeb07e6f6a Mon Sep 17 00:00:00 2001 From: metalgearsloth <31366439+metalgearsloth@users.noreply.github.com> Date: Mon, 23 Aug 2021 13:28:47 +1000 Subject: [PATCH] Add basic meteor swarm (#4420) * Add basic meteor swarm * Map fixes * Dependency cache * Fix projectile crash * Last of the reviews --- .../Components/ProjectileComponent.cs | 2 +- .../Projectiles/ProjectileSystem.cs | 5 +- .../StationEvents/Events/MeteorSwarm.cs | 127 ++++++++++++++++++ .../StationEvents/StationEventSystem.cs | 1 + .../Weapons/Guns/Projectiles/meteors.yml | 39 ++++++ .../Guns/Projectiles/meteor.rsi/large.png | Bin 0 -> 317 bytes .../Guns/Projectiles/meteor.rsi/meta.json | 15 +++ 7 files changed, 187 insertions(+), 2 deletions(-) create mode 100644 Content.Server/StationEvents/Events/MeteorSwarm.cs create mode 100644 Resources/Prototypes/Entities/Objects/Weapons/Guns/Projectiles/meteors.yml create mode 100644 Resources/Textures/Objects/Weapons/Guns/Projectiles/meteor.rsi/large.png create mode 100644 Resources/Textures/Objects/Weapons/Guns/Projectiles/meteor.rsi/meta.json diff --git a/Content.Server/Projectiles/Components/ProjectileComponent.cs b/Content.Server/Projectiles/Components/ProjectileComponent.cs index 990a2da08a..d307be7458 100644 --- a/Content.Server/Projectiles/Components/ProjectileComponent.cs +++ b/Content.Server/Projectiles/Components/ProjectileComponent.cs @@ -26,7 +26,7 @@ namespace Content.Server.Projectiles.Components public bool DeleteOnCollide { get; } = true; // Get that juicy FPS hit sound - [DataField("soundHit", required: true)] public SoundSpecifier SoundHit = default!; + [DataField("soundHit", required: true)] public SoundSpecifier? SoundHit = default!; [DataField("soundHitSpecies")] public SoundSpecifier? SoundHitSpecies = null; public bool DamagedEntity; diff --git a/Content.Server/Projectiles/ProjectileSystem.cs b/Content.Server/Projectiles/ProjectileSystem.cs index 7837a728d4..4894e81878 100644 --- a/Content.Server/Projectiles/ProjectileSystem.cs +++ b/Content.Server/Projectiles/ProjectileSystem.cs @@ -39,7 +39,10 @@ namespace Content.Server.Projectiles } else { - SoundSystem.Play(playerFilter, component.SoundHit.GetSound(), coordinates); + var soundHit = component.SoundHit?.GetSound(); + + if (!string.IsNullOrEmpty(soundHit)) + SoundSystem.Play(playerFilter, soundHit, coordinates); } if (!otherEntity.Deleted && otherEntity.TryGetComponent(out IDamageableComponent? damage)) diff --git a/Content.Server/StationEvents/Events/MeteorSwarm.cs b/Content.Server/StationEvents/Events/MeteorSwarm.cs new file mode 100644 index 0000000000..99656db046 --- /dev/null +++ b/Content.Server/StationEvents/Events/MeteorSwarm.cs @@ -0,0 +1,127 @@ +using System; +using System.Linq; +using Content.Server.GameTicking; +using Content.Server.Projectiles.Components; +using Robust.Shared.GameObjects; +using Robust.Shared.IoC; +using Robust.Shared.Map; +using Robust.Shared.Maths; +using Robust.Shared.Random; +using Robust.Shared.Timing; + +namespace Content.Server.StationEvents.Events +{ + public sealed class MeteorSwarm : StationEvent + { + [Dependency] private readonly IComponentManager _compManager = default!; + [Dependency] private readonly IEntityManager _entityManager = default!; + [Dependency] private readonly IMapManager _mapManager = default!; + [Dependency] private readonly IRobustRandom _robustRandom = default!; + + public override string Name => "MeteorSwarm"; + + public override int EarliestStart => 30; + public override float Weight => WeightLow; + public override int? MaxOccurrences => 2; + public override int MinimumPlayers => 20; + + public override string StartAnnouncement => "Meteors are on a collision course with the station. Brace for impact."; + protected override string EndAnnouncement => "The meteor swarm has passed. Please return to your stations."; + + public override string? StartAudio => "/Audio/Announcements/bloblarm.ogg"; + + protected override float StartAfter => 30f; + protected override float EndAfter => float.MaxValue; + + private float _cooldown; + + /// + /// We'll send a specific amount of waves of meteors towards the station per ending rather than using a timer. + /// + private int _waveCounter; + + private const int MinimumWaves = 3; + private const int MaximumWaves = 8; + + private const float MinimumCooldown = 10f; + private const float MaximumCooldown = 60f; + + private const int MeteorsPerWave = 5; + private const float MeteorVelocity = 10f; + private const float MaxAngularVelocity = 0.25f; + private const float MinAngularVelocity = -0.25f; + + public override void Startup() + { + base.Startup(); + var robustRandom = IoCManager.Resolve(); + _waveCounter = robustRandom.Next(MinimumWaves, MaximumWaves); + } + + public override void Shutdown() + { + base.Shutdown(); + _waveCounter = 0; + _cooldown = 0f; + EndAfter = float.MaxValue; + } + + public override void Update(float frameTime) + { + base.Update(frameTime); + + if (!Started) return; + + if (_waveCounter <= 0) + { + EndAfter = float.MinValue; + return; + } + _cooldown -= frameTime; + + if (_cooldown > 0f) return; + + _waveCounter--; + + _cooldown += (MaximumCooldown - MinimumCooldown) * _robustRandom.NextFloat() + MinimumCooldown; + + Box2? playableArea = null; + var mapId = EntitySystem.Get().DefaultMap; + + foreach (var grid in _mapManager.GetAllGrids()) + { + if (grid.ParentMapId != mapId || !_compManager.TryGetComponent(grid.GridEntityId, out PhysicsComponent? gridBody)) continue; + var aabb = gridBody.GetWorldAABB(); + playableArea = playableArea?.Union(aabb) ?? aabb; + } + + if (playableArea == null) + { + EndAfter = float.MinValue; + 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 < 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 = _entityManager.SpawnEntity("MeteorLarge", spawnPosition); + var physics = _compManager.GetComponent(meteor.Uid); + physics.BodyStatus = BodyStatus.InAir; + physics.ApplyLinearImpulse(-offset.Normalized * MeteorVelocity * physics.Mass); + physics.ApplyAngularImpulse( + // Get a random angular velocity. + physics.Mass * ((MaxAngularVelocity - MinAngularVelocity) * _robustRandom.NextFloat() + + MinAngularVelocity)); + // TODO: God this disgusts me but projectile needs a refactor. + meteor.GetComponent().TimeLeft = 120f; + } + } + } +} diff --git a/Content.Server/StationEvents/StationEventSystem.cs b/Content.Server/StationEvents/StationEventSystem.cs index 736590a5c3..aeab52e2c7 100644 --- a/Content.Server/StationEvents/StationEventSystem.cs +++ b/Content.Server/StationEvents/StationEventSystem.cs @@ -175,6 +175,7 @@ namespace Content.Server.StationEvents if (type.IsAbstract) continue; var stationEvent = (StationEvent) typeFactory.CreateInstance(type); + IoCManager.InjectDependencies(stationEvent); _stationEvents.Add(stationEvent); } diff --git a/Resources/Prototypes/Entities/Objects/Weapons/Guns/Projectiles/meteors.yml b/Resources/Prototypes/Entities/Objects/Weapons/Guns/Projectiles/meteors.yml new file mode 100644 index 0000000000..58e99697d8 --- /dev/null +++ b/Resources/Prototypes/Entities/Objects/Weapons/Guns/Projectiles/meteors.yml @@ -0,0 +1,39 @@ +- type: entity + id: MeteorLarge + name: meteor + abstract: true + components: + - type: Sprite + noRot: false + netsync: false + sprite: Objects/Weapons/Guns/Projectiles/meteor.rsi + scale: 4,4 + layers: + - state: large + shader: unshaded + - type: ExplodeOnTrigger + - type: DeleteOnTrigger + - type: TriggerOnCollide + - type: Projectile + deleteOnCollide: false + - type: Explosive + devastationRange: 3 + heavyImpactRange: 5 + lightImpactRange: 7 + flashRange: 10 + - type: Physics + bodyType: Dynamic + fixedRotation: false + fixtures: + - shape: + !type:PhysShapeCircle + radius: 0.5 + mass: 200 + hard: true + # Didn't use MapGrid for now as the bounds are stuffed. + layer: + - Impassable + - SmallImpassable + - VaultImpassable + mask: + - Impassable diff --git a/Resources/Textures/Objects/Weapons/Guns/Projectiles/meteor.rsi/large.png b/Resources/Textures/Objects/Weapons/Guns/Projectiles/meteor.rsi/large.png new file mode 100644 index 0000000000000000000000000000000000000000..bb27b759677c3598b917a68218f0adcc1d29a830 GIT binary patch literal 317 zcmeAS@N?(olHy`uVBq!ia0vp^3LwnE3?yBabR7dy1_3@Ht_KbraBy(2u(0s+^K)@= ziHL~s@bE}XOmuW~^!E0SiHWhYvhw%$4+;vhv$ON^@_Ka7iw&rku_VYZn8D%MjWi%< zx2KC^h(+(!N!~)tiX5!Ya`OWpSNy-tb|%E^(Hh~huB&87jJ3@Ox~=L>?_SN2=yu@Cet-8q$B#^3!M2VSGj~qd+qCRK?*qpZRiaHZx0Urx*{zUTBv-kf z^C-8^zCRCzmOOa!;HSy+4aYlv&(5@Kys`XAd-xHvdv9)E-+6GqR6h4Y*3v|-6p%L< NJYD@<);T3K0RZdselq|7 literal 0 HcmV?d00001 diff --git a/Resources/Textures/Objects/Weapons/Guns/Projectiles/meteor.rsi/meta.json b/Resources/Textures/Objects/Weapons/Guns/Projectiles/meteor.rsi/meta.json new file mode 100644 index 0000000000..86a5b835c6 --- /dev/null +++ b/Resources/Textures/Objects/Weapons/Guns/Projectiles/meteor.rsi/meta.json @@ -0,0 +1,15 @@ +{ + "version": 1, + "license": "CC-BY-SA-3.0", + "copyright": "Taken from cev-eris at https://github.com/discordia-space/CEV-Eris/raw/2acc4d34a894dbcc9dbf3779b696ddf296aa2c56/icons/obj/projectiles.dmi", + "size": { + "x": 32, + "y": 32 + }, + "states": [ + { + "name": "large" + } + ] + } + \ No newline at end of file