Revamped Meteor Swarm (#28974)

* 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.
This commit is contained in:
Nemanja
2024-06-14 23:38:43 -04:00
committed by GitHub
parent 1d5840c279
commit f136657b38
36 changed files with 887 additions and 164 deletions

View File

@@ -1,3 +1,4 @@
using System.Diagnostics.CodeAnalysis;
using Content.Server.Administration.Logs; using Content.Server.Administration.Logs;
using Content.Server.Atmos.EntitySystems; using Content.Server.Atmos.EntitySystems;
using Content.Server.Body.Systems; 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 // 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. // like there is currently any entity that is NOT just destroyed upon reaching a total-damage value.
/// <summary> /// <summary>

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

@@ -212,7 +212,7 @@ public sealed class MobThresholdSystem : EntitySystem
MobThresholdsComponent? thresholdComponent = null) MobThresholdsComponent? thresholdComponent = null)
{ {
threshold = null; threshold = null;
if (!Resolve(target, ref thresholdComponent)) if (!Resolve(target, ref thresholdComponent, false))
return false; return false;
return TryGetThresholdForState(target, MobState.Dead, out threshold, thresholdComponent); return TryGetThresholdForState(target, MobState.Dead, out threshold, thresholdComponent);

View 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-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.

View File

@@ -361,7 +361,7 @@
whitelist: whitelist:
tags: tags:
- CartridgeRocket - CartridgeRocket
proto: MeteorLarge proto: MeteorMedium
- type: entity - type: entity
name: immovable rod launcher name: immovable rod launcher

View File

@@ -1,40 +1,206 @@
- type: entity - type: entity
id: MeteorLarge id: BaseMeteor
name: meteor name: meteor
noSpawn: true description: You prefer them when they're burning up in the atmosphere.
abstract: true
components: components:
- type: Sprite - type: Sprite
noRot: false noRot: false
sprite: Objects/Weapons/Guns/Projectiles/meteor.rsi sprite: Objects/Misc/meteor.rsi
scale: 4,4
layers:
- state: large
shader: unshaded
- type: ExplodeOnTrigger
- type: DeleteOnTrigger
- type: TriggerOnCollide
fixtureID: projectile
- type: Projectile - type: Projectile
damage: {} damage: {}
deleteOnCollide: false deleteOnCollide: false
- type: Explosive - type: Meteor
explosionType: Default damageTypes:
totalIntensity: 600.0 types:
intensitySlope: 30 Blunt: 1
maxIntensity: 45 - type: TimedDespawn
lifetime: 120
- type: Clickable
- type: Physics - type: Physics
bodyType: Dynamic bodyType: Dynamic
bodyStatus: InAir
angularDamping: 0
linearDamping: 0
- type: Fixtures - type: Fixtures
fixtures: fixtures:
projectile: projectile:
shape: shape:
!type:PhysShapeCircle !type:PhysShapeCircle
radius: 0.8 radius: 0.4
density: 100 density: 100
hard: true hard: false
# Didn't use MapGridComponent for now as the bounds are stuffed.
layer: layer:
- LargeMobLayer - LargeMobLayer
mask: mask:
- Impassable - Impassable
- BulletImpassable - 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.45
density: 100
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

View File

@@ -196,23 +196,6 @@
duration: 240 duration: 240
- type: KudzuGrowthRule - 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 - type: entity
id: MouseMigration id: MouseMigration
parent: BaseStationEventShortDelay parent: BaseStationEventShortDelay

View File

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

View File

@@ -31,6 +31,7 @@
description: extended-description description: extended-description
rules: rules:
- BasicStationEventScheduler - BasicStationEventScheduler
- GameRuleMeteorScheduler
- BasicRoundstartVariation - BasicRoundstartVariation
- type: gamePreset - type: gamePreset
@@ -64,6 +65,7 @@
description: secret-description description: secret-description
rules: rules:
- BasicStationEventScheduler - BasicStationEventScheduler
- GameRuleMeteorScheduler
- type: gamePreset - type: gamePreset
id: SecretGreenshift #For Admin Use: Runs Greenshift but shows "Secret" in lobby. id: SecretGreenshift #For Admin Use: Runs Greenshift but shows "Secret" in lobby.
@@ -95,6 +97,7 @@
- Traitor - Traitor
- SubGamemodesRule - SubGamemodesRule
- BasicStationEventScheduler - BasicStationEventScheduler
- GameRuleMeteorScheduler
- BasicRoundstartVariation - BasicRoundstartVariation
- type: gamePreset - type: gamePreset
@@ -121,6 +124,7 @@
- Nukeops - Nukeops
- SubGamemodesRule - SubGamemodesRule
- BasicStationEventScheduler - BasicStationEventScheduler
- GameRuleMeteorScheduler
- BasicRoundstartVariation - BasicRoundstartVariation
- type: gamePreset - type: gamePreset
@@ -136,6 +140,7 @@
- Revolutionary - Revolutionary
- SubGamemodesRule - SubGamemodesRule
- BasicStationEventScheduler - BasicStationEventScheduler
- GameRuleMeteorScheduler
- BasicRoundstartVariation - BasicRoundstartVariation
- type: gamePreset - type: gamePreset
@@ -152,4 +157,5 @@
rules: rules:
- Zombie - Zombie
- BasicStationEventScheduler - BasicStationEventScheduler
- GameRuleMeteorScheduler
- BasicRoundstartVariation - BasicRoundstartVariation

Binary file not shown.

After

Width:  |  Height:  |  Size: 4.5 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 4.4 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 4.4 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 4.5 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 367 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 430 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 4.9 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.8 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 2.9 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 3.5 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 3.5 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 2.9 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 2.0 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 2.0 KiB

View File

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

Binary file not shown.

After

Width:  |  Height:  |  Size: 2.4 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 2.4 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 2.4 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 2.4 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 328 B