Vent Hordes (#43047)
* init * port hostiles to hordes, fakeout horde * cleaning, docs * i may be blind sometimes * i may be even blinder * remove friendly announcements * fuck it * i love microbalancing * review * review * review p2 * Update Resources/Prototypes/GameRules/pests.yml Co-authored-by: SnappingOpossum <snappingopossum@outlook.com> * review + powercreep * lower the times to account for the gamerule delay * review * review * fuck this return actually * fial --------- Co-authored-by: SnappingOpossum <snappingopossum@outlook.com> # Conflicts: # Resources/Prototypes/GameRules/pests.yml
This commit is contained in:
parent
86c4c900eb
commit
9b5a09e8a8
|
|
@ -0,0 +1,26 @@
|
|||
using Content.Server.StationEvents.Events;
|
||||
using Content.Shared.EntityTable.EntitySelectors;
|
||||
|
||||
namespace Content.Server.StationEvents.Components;
|
||||
|
||||
/// <summary>
|
||||
/// Component used for the vent horde gamerule.
|
||||
/// Picks a random entity with <see cref="VentCritterSpawnLocationComponent"/>
|
||||
/// and spawns entities picked from the <see cref="Table"/> on it after a delay.
|
||||
/// </summary>
|
||||
[RegisterComponent, Access(typeof(VentHordeRule))]
|
||||
public sealed partial class VentHordeRuleComponent : Component
|
||||
{
|
||||
/// <summary>
|
||||
/// The table of possible mobs to spawn from the vent.
|
||||
/// </summary>
|
||||
[DataField(required: true)]
|
||||
public EntityTableSelector Table = default!;
|
||||
|
||||
/// <summary>
|
||||
/// The vent that has been chosen to spawn the entities.
|
||||
/// Spawning logic is handled by <see cref="VentHordeSpawnerComponent"/>
|
||||
/// </summary>
|
||||
[DataField]
|
||||
public EntityUid? ChosenVent;
|
||||
}
|
||||
|
|
@ -90,6 +90,9 @@ public sealed class VentCrittersRule : StationEventSystem<VentCrittersRuleCompon
|
|||
_locations.Clear();
|
||||
while (locations.MoveNext(out var uid, out _, out var transform))
|
||||
{
|
||||
if (!transform.Anchored)
|
||||
continue;
|
||||
|
||||
if (CompOrNull<StationMemberComponent>(transform.GridUid)?.Station == station)
|
||||
{
|
||||
_locations.Add(transform.Coordinates);
|
||||
|
|
|
|||
|
|
@ -0,0 +1,122 @@
|
|||
using System.Linq;
|
||||
using Content.Server.Pinpointer;
|
||||
using Content.Server.StationEvents.Components;
|
||||
using Content.Server.VentHorde.Components;
|
||||
using Content.Server.VentHorde.Systems;
|
||||
using Content.Shared.EntityTable;
|
||||
using Content.Shared.GameTicking.Components;
|
||||
using Content.Shared.Station.Components;
|
||||
using Robust.Shared.Random;
|
||||
using Robust.Shared.Timing;
|
||||
|
||||
namespace Content.Server.StationEvents.Events;
|
||||
|
||||
/// <summary>
|
||||
/// Variant of <see cref="VentCrittersRule"/> that selects a single vent and spawns all entities there.
|
||||
/// </summary>
|
||||
public sealed class VentHordeRule : StationEventSystem<VentHordeRuleComponent>
|
||||
{
|
||||
/*
|
||||
* DO NOT COPY PASTE THIS TO MAKE YOUR MOB EVENT.
|
||||
* USE THE PROTOTYPE.
|
||||
*/
|
||||
|
||||
[Dependency] private readonly IRobustRandom _random = default!;
|
||||
[Dependency] private readonly NavMapSystem _navMap = default!;
|
||||
[Dependency] private readonly SharedTransformSystem _transform = default!;
|
||||
[Dependency] private readonly EntityTableSystem _table = default!;
|
||||
[Dependency] private readonly VentHordeSystem _horde = default!;
|
||||
[Dependency] private readonly IGameTiming _timing = default!;
|
||||
|
||||
protected override void Added(EntityUid uid, VentHordeRuleComponent component, GameRuleComponent gameRule, GameRuleAddedEvent args)
|
||||
{
|
||||
// Choose location and make sure it's not null
|
||||
component.ChosenVent = ChooseVent();
|
||||
|
||||
if (component.ChosenVent is not { } vent)
|
||||
{
|
||||
Log.Warning($"Unable to find a valid vent for {args.RuleId}!");
|
||||
ForceEndSelf(uid, gameRule);
|
||||
return;
|
||||
}
|
||||
|
||||
// Get the event component so we can format the announcement
|
||||
if (TryComp<StationEventComponent>(uid, out var stationEventComp) && stationEventComp.StartAnnouncement != null)
|
||||
{
|
||||
// Get the nearest beacon
|
||||
var mapLocation = _transform.ToMapCoordinates(Transform(vent).Coordinates);
|
||||
var nearestBeacon = _navMap.GetNearestBeaconString(mapLocation, onlyName: true);
|
||||
|
||||
// Format the announcement with the location, if the string doesn't have them it'll still work fine
|
||||
// time is not said on purpose to keep the players on their toes.
|
||||
// also because we cannot tell the end time inside of Added().
|
||||
stationEventComp.StartAnnouncement =
|
||||
Loc.GetString(stationEventComp.StartAnnouncement,
|
||||
("location", nearestBeacon));
|
||||
}
|
||||
|
||||
base.Added(uid, component, gameRule, args);
|
||||
}
|
||||
|
||||
protected override void Started(EntityUid uid, VentHordeRuleComponent component, GameRuleComponent gameRule, GameRuleStartedEvent args)
|
||||
{
|
||||
base.Started(uid, component, gameRule, args);
|
||||
|
||||
if (!Exists(component.ChosenVent))
|
||||
{
|
||||
Log.Warning($"Chosen vent for {args.RuleId} does not exist!");
|
||||
ForceEndSelf(uid, gameRule);
|
||||
return;
|
||||
}
|
||||
|
||||
if (!TryComp<StationEventComponent>(uid, out var stationEventComp))
|
||||
return;
|
||||
|
||||
// We grab when the gamerule is expected to end and subtract the current time from it to get the duration.
|
||||
var duration = (stationEventComp.EndTime - _timing.CurTime) ?? TimeSpan.Zero;
|
||||
|
||||
var spawns = _table.GetSpawns(component.Table);
|
||||
|
||||
if (component.ChosenVent == null)
|
||||
return;
|
||||
|
||||
// And start the spawn at the chosen vent.
|
||||
// The duration is the same as the time until expected gamerule end time, but that is only for convenience.
|
||||
// The spawn can happen early in certain circumstances anyway.
|
||||
_horde.StartHordeSpawn(component.ChosenVent.Value, spawns.ToList(), duration);
|
||||
}
|
||||
|
||||
private EntityUid? ChooseVent()
|
||||
{
|
||||
// Get a station
|
||||
if (!TryGetRandomStation(out var station))
|
||||
{
|
||||
return null;
|
||||
}
|
||||
|
||||
// Query the possible locations
|
||||
var locations = EntityQueryEnumerator<VentCritterSpawnLocationComponent, TransformComponent>();
|
||||
var validLocations = new List<EntityUid>();
|
||||
|
||||
// Filter to things on the same station
|
||||
while (locations.MoveNext(out var uid, out _, out var transform))
|
||||
{
|
||||
if (!transform.Anchored)
|
||||
continue;
|
||||
|
||||
if (HasComp<VentHordeSpawnerComponent>(uid))
|
||||
continue;
|
||||
|
||||
if (CompOrNull<StationMemberComponent>(transform.GridUid)?.Station == station)
|
||||
{
|
||||
validLocations.Add(uid);
|
||||
}
|
||||
}
|
||||
|
||||
// Pick one at random
|
||||
if (validLocations.Count != 0)
|
||||
return _random.Pick(validLocations);
|
||||
|
||||
return null;
|
||||
}
|
||||
}
|
||||
|
|
@ -0,0 +1,77 @@
|
|||
using Content.Server.VentHorde.Systems;
|
||||
using Robust.Shared.Audio;
|
||||
using Robust.Shared.Prototypes;
|
||||
using Robust.Shared.Serialization.TypeSerializers.Implementations.Custom;
|
||||
|
||||
namespace Content.Server.VentHorde.Components;
|
||||
|
||||
/// <summary>
|
||||
/// Marks an entity as selected by the <see cref="VentHordeRuleComponent"/>.
|
||||
/// Will spawn all entities contained within <see cref="Entities"/> on its location at <see cref="SpawnTime"/>.
|
||||
/// </summary>
|
||||
[RegisterComponent, Access(typeof(VentHordeSystem))]
|
||||
[AutoGenerateComponentPause]
|
||||
public sealed partial class VentHordeSpawnerComponent : Component
|
||||
{
|
||||
/// <summary>
|
||||
/// The mobs to spawn from the vent.
|
||||
/// </summary>
|
||||
[DataField(required: true)]
|
||||
public List<EntProtoId> Entities = new ();
|
||||
|
||||
/// <summary>
|
||||
/// Maximum speed at which the entities will be thrown out of the vent.
|
||||
/// </summary>
|
||||
[DataField]
|
||||
public float MaxThrowSpeed = 1.5f;
|
||||
|
||||
/// <summary>
|
||||
/// Minimum speed at which the entities will be thrown out of the vent.
|
||||
/// </summary>
|
||||
[DataField]
|
||||
public float MinThrowSpeed = 0.5f;
|
||||
|
||||
/// <summary>
|
||||
/// Maximum distance which travel when thrown out of the vent.
|
||||
/// </summary>
|
||||
[DataField]
|
||||
public float MaxThrowDistance = 4f;
|
||||
|
||||
/// <summary>
|
||||
/// Minimum distance which travel when thrown out of the vent.
|
||||
/// </summary>
|
||||
[DataField]
|
||||
public float MinThrowDistance = 2f;
|
||||
|
||||
/// <summary>
|
||||
/// The time at which the entities will spawn.
|
||||
/// </summary>
|
||||
[DataField(customTypeSerializer:typeof(TimeOffsetSerializer))]
|
||||
[AutoPausedField]
|
||||
public TimeSpan? SpawnTime;
|
||||
|
||||
/// <summary>
|
||||
/// Plays on loop when a vent is selected as a spawner.
|
||||
/// </summary>
|
||||
[DataField]
|
||||
public SoundSpecifier PassiveSound = new SoundPathSpecifier("/Audio/Machines/airlock_creaking.ogg")
|
||||
{
|
||||
Params = AudioParams.Default.WithVolume(-3f),
|
||||
};
|
||||
|
||||
/// <summary>
|
||||
/// Plays when the entities are thrown out of the vent.
|
||||
/// </summary>
|
||||
[DataField]
|
||||
public SoundSpecifier EndSound = new SoundPathSpecifier("/Audio/Weapons/Guns/Gunshots/grenade_launcher.ogg")
|
||||
{
|
||||
Params = AudioParams.Default.WithVolume(-3f),
|
||||
};
|
||||
|
||||
/// <summary>
|
||||
/// The PassiveSound entity, used to cancel the audio.
|
||||
/// </summary>
|
||||
[DataField]
|
||||
[ViewVariables(VVAccess.ReadOnly)]
|
||||
public EntityUid? AudioStream;
|
||||
}
|
||||
|
|
@ -0,0 +1,119 @@
|
|||
using Content.Server.VentHorde.Components;
|
||||
using Content.Shared.Destructible;
|
||||
using Content.Shared.Jittering;
|
||||
using Content.Shared.Throwing;
|
||||
using Robust.Shared.Audio.Systems;
|
||||
using Robust.Shared.Prototypes;
|
||||
using Robust.Shared.Random;
|
||||
using Robust.Shared.Timing;
|
||||
|
||||
namespace Content.Server.VentHorde.Systems;
|
||||
|
||||
public sealed class VentHordeSystem : EntitySystem
|
||||
{
|
||||
[Dependency] private readonly IGameTiming _timing = default!;
|
||||
[Dependency] private readonly IRobustRandom _random = default!;
|
||||
[Dependency] private readonly ThrowingSystem _throwing = default!;
|
||||
[Dependency] private readonly SharedAudioSystem _audio = default!;
|
||||
[Dependency] private readonly SharedJitteringSystem _jitter = default!;
|
||||
|
||||
public override void Initialize()
|
||||
{
|
||||
base.Initialize();
|
||||
|
||||
SubscribeLocalEvent<VentHordeSpawnerComponent, MapInitEvent>(OnSpawnerInit);
|
||||
SubscribeLocalEvent<VentHordeSpawnerComponent, ComponentShutdown>(OnSpawnerShutdown);
|
||||
|
||||
SubscribeLocalEvent<VentHordeSpawnerComponent, BreakageEventArgs>(OnSpawnerBreakage);
|
||||
SubscribeLocalEvent<VentHordeSpawnerComponent, AnchorStateChangedEvent>(OnSpawnerAnchored);
|
||||
}
|
||||
|
||||
private void OnSpawnerInit(Entity<VentHordeSpawnerComponent> entity, ref MapInitEvent args)
|
||||
{
|
||||
_jitter.AddJitter(entity);
|
||||
}
|
||||
|
||||
private void OnSpawnerShutdown(Entity<VentHordeSpawnerComponent> entity, ref ComponentShutdown args)
|
||||
{
|
||||
_audio.Stop(entity.Comp.AudioStream);
|
||||
RemCompDeferred<JitteringComponent>(entity);
|
||||
}
|
||||
|
||||
private void OnSpawnerBreakage(Entity<VentHordeSpawnerComponent> entity, ref BreakageEventArgs args)
|
||||
{
|
||||
// There is no escape.
|
||||
EndHordeSpawn(entity);
|
||||
}
|
||||
|
||||
private void OnSpawnerAnchored(Entity<VentHordeSpawnerComponent> entity, ref AnchorStateChangedEvent args)
|
||||
{
|
||||
// Anchor state changes when the entity is broken, to avoid double spawning we check if the entity is gonna be deleted.
|
||||
if (TerminatingOrDeleted(entity))
|
||||
return;
|
||||
|
||||
// There is no escape.
|
||||
EndHordeSpawn(entity);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Starts a horde spawn at an entity.
|
||||
/// </summary>
|
||||
/// <param name="uid">The entity to spawn the horde at.</param>
|
||||
/// <param name="spawns">List of entities to spawn.</param>
|
||||
/// <param name="spawnDelay">Time after which to spawn the entities.</param>
|
||||
/// <param name="append">If an already active spawner is selected, will add entities to its list. Otherwise, will fail.</param>
|
||||
public void StartHordeSpawn(EntityUid uid, List<EntProtoId> spawns, TimeSpan spawnDelay, bool append = true)
|
||||
{
|
||||
if (TryComp<VentHordeSpawnerComponent>(uid, out var hordeSpawner))
|
||||
{
|
||||
if (append)
|
||||
{
|
||||
hordeSpawner.Entities.AddRange(spawns);
|
||||
}
|
||||
|
||||
return;
|
||||
}
|
||||
|
||||
hordeSpawner = EnsureComp<VentHordeSpawnerComponent>(uid);
|
||||
|
||||
hordeSpawner.AudioStream = _audio.PlayPvs(hordeSpawner.PassiveSound, uid, hordeSpawner.PassiveSound.Params.WithLoop(true))?.Entity;
|
||||
|
||||
hordeSpawner.Entities = spawns;
|
||||
hordeSpawner.SpawnTime = _timing.CurTime + spawnDelay;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Ends a horde spawn, causing all entities to spawn at once.
|
||||
/// </summary>
|
||||
/// <param name="entity">The horde spawner entity.</param>
|
||||
public void EndHordeSpawn(Entity<VentHordeSpawnerComponent> entity)
|
||||
{
|
||||
entity.Comp.AudioStream = _audio.Stop(entity.Comp.AudioStream);
|
||||
|
||||
_audio.PlayPvs(entity.Comp.EndSound, entity);
|
||||
|
||||
foreach (var spawn in entity.Comp.Entities)
|
||||
{
|
||||
var spawned = Spawn(spawn, Transform(entity).Coordinates);
|
||||
var direction = _random.NextVector2() * _random.NextFloat(entity.Comp.MinThrowDistance, entity.Comp.MaxThrowDistance);
|
||||
var throwSpeed = _random.NextFloat(entity.Comp.MinThrowSpeed, entity.Comp.MaxThrowSpeed);
|
||||
_throwing.TryThrow(spawned, direction, throwSpeed);
|
||||
}
|
||||
|
||||
RemCompDeferred<VentHordeSpawnerComponent>(entity);
|
||||
}
|
||||
|
||||
public override void Update(float frameTime)
|
||||
{
|
||||
base.Update(frameTime);
|
||||
|
||||
var query = EntityQueryEnumerator<VentHordeSpawnerComponent>();
|
||||
while (query.MoveNext(out var uid, out var comp))
|
||||
{
|
||||
if (comp.SpawnTime != null && _timing.CurTime > comp.SpawnTime)
|
||||
{
|
||||
EndHordeSpawn((uid, comp));
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -1 +1 @@
|
|||
station-event-vent-creatures-start-announcement = Attention. A large influx of unknown life forms have been detected residing within the station's ventilation systems. Please be rid of these creatures before it begins to affect productivity.
|
||||
station-event-vent-creatures-start-horde-announcement = Attention. A large influx of unknown life forms have been detected moving through the station's ventilation systems. They are expected to emerge near {$location}. Please evacuate the area to avoid loss of personnel.
|
||||
|
|
|
|||
|
|
@ -20,11 +20,12 @@
|
|||
- id: MouseMigration
|
||||
- id: PowerGridCheck
|
||||
#- id: RandomSentience # DeltaV - replaced with RandomSentienceGlimmer
|
||||
- id: SlimesSpawnHorde
|
||||
- id: SlimesSpawn
|
||||
- id: SolarFlare
|
||||
- id: SnakeSpawn
|
||||
- id: SpiderClownSpawn
|
||||
- id: SpiderSpawn
|
||||
- id: SnakeSpawnHorde
|
||||
- id: SpiderClownSpawnHorde
|
||||
- id: SpiderSpawnHorde
|
||||
- id: VentClog
|
||||
|
||||
- type: entityTable
|
||||
|
|
@ -502,13 +503,15 @@
|
|||
- type: PrecognitionResult # DeltaV - Precogniton
|
||||
message: psionic-power-precognition-vent-clog-result-message
|
||||
- type: VentClogRule
|
||||
# Slime Spawn
|
||||
|
||||
# Critters
|
||||
|
||||
- type: entity
|
||||
id: SlimesSpawn
|
||||
id: SlimesSpawnHorde
|
||||
parent: BaseStationEventShortDelay
|
||||
components:
|
||||
- type: StationEvent
|
||||
startAnnouncement: station-event-vent-creatures-start-announcement
|
||||
startAnnouncement: station-event-vent-creatures-start-horde-announcement
|
||||
startAudio:
|
||||
path: /Audio/_DV/Announcements/attention.ogg # DeltaV - custom announcer
|
||||
earliestStart: 20
|
||||
|
|
@ -517,19 +520,20 @@
|
|||
duration: 30 # DeltaV: was 60, used as a delay now
|
||||
- type: PrecognitionResult # DeltaV - Precogniton
|
||||
message: psionic-power-precognition-slimes-spawn-result-message
|
||||
- type: VentCrittersRule
|
||||
table: !type:GroupSelector # DeltaV: EntityTable instead of spawn entries
|
||||
- type: VentHordeRule
|
||||
table: !type:GroupSelector
|
||||
rolls: 4, 8
|
||||
children:
|
||||
- id: MobAdultSlimesBlueAngry
|
||||
- id: MobAdultSlimesGreenAngry
|
||||
- id: MobAdultSlimesYellowAngry
|
||||
# Snake Spawns
|
||||
|
||||
- type: entity
|
||||
id: SnakeSpawn
|
||||
id: SnakeSpawnHorde
|
||||
parent: BaseStationEventShortDelay
|
||||
components:
|
||||
- type: StationEvent
|
||||
startAnnouncement: station-event-vent-creatures-start-announcement
|
||||
startAnnouncement: station-event-vent-creatures-start-horde-announcement
|
||||
startAudio:
|
||||
path: /Audio/_DV/Announcements/attention.ogg # DeltaV - custom announcer
|
||||
earliestStart: 20
|
||||
|
|
@ -538,19 +542,20 @@
|
|||
duration: 30 # DeltaV: was 60, used as a delay now
|
||||
- type: PrecognitionResult # DeltaV - Precogniton
|
||||
message: psionic-power-precognition-snake-spawn-result-message
|
||||
- type: VentCrittersRule
|
||||
table: !type:GroupSelector # DeltaV: EntityTable instead of spawn entries
|
||||
- type: VentHordeRule
|
||||
table: !type:GroupSelector
|
||||
rolls: 4, 8
|
||||
children:
|
||||
- id: MobPurpleSnake
|
||||
- id: MobSmallPurpleSnake
|
||||
- id: MobCobraSpace
|
||||
# Spider Spawns
|
||||
|
||||
- type: entity
|
||||
id: SpiderSpawn
|
||||
id: SpiderSpawnHorde
|
||||
parent: BaseStationEventShortDelay
|
||||
components:
|
||||
- type: StationEvent
|
||||
startAnnouncement: station-event-vent-creatures-start-announcement
|
||||
startAnnouncement: station-event-vent-creatures-start-horde-announcement
|
||||
startAudio:
|
||||
path: /Audio/_DV/Announcements/attention.ogg # DeltaV - custom announcer
|
||||
earliestStart: 20
|
||||
|
|
@ -559,16 +564,18 @@
|
|||
duration: 30 # DeltaV: was 60, used as a delay now
|
||||
- type: PrecognitionResult # DeltaV - Precogniton
|
||||
message: psionic-power-precognition-spider-spawn-result-message
|
||||
- type: VentCrittersRule
|
||||
table: # DeltaV: EntityTable instead of spawn entries
|
||||
id: MobGiantSpiderAngry
|
||||
# Clown Spider Spawns
|
||||
- type: VentHordeRule
|
||||
table: !type:GroupSelector
|
||||
rolls: 4, 8
|
||||
children:
|
||||
- id: MobGiantSpiderAngry
|
||||
|
||||
- type: entity
|
||||
id: SpiderClownSpawn
|
||||
id: SpiderClownSpawnHorde
|
||||
parent: BaseStationEventShortDelay
|
||||
components:
|
||||
- type: StationEvent
|
||||
startAnnouncement: station-event-vent-creatures-start-announcement
|
||||
startAnnouncement: station-event-vent-creatures-start-horde-announcement
|
||||
startAudio:
|
||||
path: /Audio/_DV/Announcements/attention.ogg # DeltaV - custom announcer
|
||||
earliestStart: 45 # DeltaV - was 20
|
||||
|
|
@ -577,11 +584,12 @@
|
|||
duration: 30 # DeltaV: was 60, used as a delay now
|
||||
- type: PrecognitionResult # DeltaV - Precogniton
|
||||
message: psionic-power-precognition-spider-clown-spawn-result-message
|
||||
- type: VentCrittersRule
|
||||
playerRatio: 35 # DeltaV: Clown spiders are very robust
|
||||
table: # DeltaV: EntityTable instead of spawn entries
|
||||
id: MobClownSpider
|
||||
# Zombie outbreak
|
||||
- type: VentHordeRule
|
||||
table: !type:GroupSelector
|
||||
rolls: 4, 8
|
||||
children:
|
||||
- id: MobClownSpider
|
||||
|
||||
- type: entity
|
||||
id: ZombieOutbreak
|
||||
parent: BaseGameRule
|
||||
|
|
|
|||
|
|
@ -7,6 +7,7 @@
|
|||
- id: SnailMigrationLowPop
|
||||
- id: CockroachMigration
|
||||
- id: MouseMigration
|
||||
- id: RandomCritterHorde
|
||||
- id: PitbullMigration # DeltaV - Ventbull event
|
||||
|
||||
- type: entityTable
|
||||
|
|
@ -151,5 +152,39 @@
|
|||
- id: MobSnailMoth
|
||||
weight: 0.07 # DeltaV - was 0.08
|
||||
#- id: MobSnailInstantDeath # DeltaV - no
|
||||
#- id: MobCorticalBorer # DeltaV - ported Borer
|
||||
# weight: 0.02 # DeltaV - 2% chance on snails
|
||||
# - id: MobCorticalBorer # DeltaV - ported Borer
|
||||
# weight: 0.02 # DeltaV - 2% chance on snails
|
||||
|
||||
- type: entity
|
||||
parent: BaseStationEventShortDelay
|
||||
id: RandomCritterHorde # My deepest apologies, Mr. Secoff. You just wasted your time :)
|
||||
components:
|
||||
- type: StationEvent
|
||||
startAnnouncement: station-event-vent-creatures-start-horde-announcement
|
||||
startAudio:
|
||||
path: /Audio/Announcements/attention.ogg
|
||||
earliestStart: 15
|
||||
weight: 6 # Slightly more common than other hordes
|
||||
duration: 5
|
||||
maxDuration: 20
|
||||
reoccurrenceDelay: 15 # Shorter because this event is basically a nat zero on the round. It only exists to potentially confuse security or the crew
|
||||
- type: VentHordeRule
|
||||
table: !type:GroupSelector
|
||||
rolls: 3,5
|
||||
children:
|
||||
- !type:GroupSelector
|
||||
children: # Increased chances for special snails on purpose. Makes it a bit more interesting.
|
||||
- id: MobSnail
|
||||
- id: MobSnailSpeed
|
||||
- id: MobSnailMoth
|
||||
- !type:GroupSelector
|
||||
children: # Increased chances for special roaches on purpose. Makes it a bit more interesting.
|
||||
- id: MobCockroach
|
||||
- id: MobMothroach
|
||||
- id: MobMoproach
|
||||
weight: 0.5
|
||||
- !type:GroupSelector
|
||||
children:
|
||||
- id: MobMouse
|
||||
- id: MobMouse1
|
||||
- id: MobMouse2
|
||||
|
|
|
|||
Loading…
Reference in New Issue