Add basic meteor swarm (#4420)
* Add basic meteor swarm * Map fixes * Dependency cache * Fix projectile crash * Last of the reviews
This commit is contained in:
@@ -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;
|
||||
|
||||
@@ -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))
|
||||
|
||||
127
Content.Server/StationEvents/Events/MeteorSwarm.cs
Normal file
127
Content.Server/StationEvents/Events/MeteorSwarm.cs
Normal file
@@ -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;
|
||||
|
||||
/// <summary>
|
||||
/// We'll send a specific amount of waves of meteors towards the station per ending rather than using a timer.
|
||||
/// </summary>
|
||||
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<IRobustRandom>();
|
||||
_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<GameTicker>().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<PhysicsComponent>(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<ProjectileComponent>().TimeLeft = 120f;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -175,6 +175,7 @@ namespace Content.Server.StationEvents
|
||||
if (type.IsAbstract) continue;
|
||||
|
||||
var stationEvent = (StationEvent) typeFactory.CreateInstance(type);
|
||||
IoCManager.InjectDependencies(stationEvent);
|
||||
_stationEvents.Add(stationEvent);
|
||||
}
|
||||
|
||||
|
||||
@@ -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
|
||||
Binary file not shown.
|
After Width: | Height: | Size: 317 B |
@@ -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"
|
||||
}
|
||||
]
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user