refactor event schedulers to use explicit game rules (#29320)

* works, still has testing values, im sure I did stupid shit.

* shitvent crapfactor

* snap extra word out of existence

* shit I died of old

* remove useless inaccurate design comments

* Oopsie, handle requirement params in RandomRuleSystem too

* I'm a slash slinging hasher

* Address reviews, add admin alerts I forgor

* EntityMan saves the day

* address reviews 1

* eh, I actually don't care about the cargo gifts thing.

* started

* Do reviews

* you actually meant 1.2 lmao

* dependency inheritance is a fickle bitch

* I have no idea.

* Threads are for sheets not computers.

* fix traitor rule test

* fix round type tattling

* break things

* It worky

* Toolshed makes we want to drink depresso.

* Finished?

* remove debug values

* timings

* use defaults

* alphabetize

* bobby drop tables

* Float required fr fr

* continue

* more continence

* uno mas

* obsolution

* cleanup and documentations

* Yell at self

* use the right value defaults

* housekeeping
This commit is contained in:
IProduceWidgets
2024-08-14 01:21:01 -04:00
committed by GitHub
parent 58a33b2593
commit 07ec00ed05
21 changed files with 600 additions and 241 deletions

View File

@@ -1,10 +1,7 @@
using Content.Server.GameTicking; using Content.Server.GameTicking;
using Content.Server.GameTicking.Commands;
using Content.Server.GameTicking.Rules; using Content.Server.GameTicking.Rules;
using Content.Server.GameTicking.Rules.Components; using Content.Server.GameTicking.Rules.Components;
using Content.Shared.CCVar;
using Content.Shared.GameTicking.Components; using Content.Shared.GameTicking.Components;
using Robust.Shared.Configuration;
using Robust.Shared.GameObjects; using Robust.Shared.GameObjects;
using Robust.Shared.Timing; using Robust.Shared.Timing;
@@ -27,8 +24,12 @@ namespace Content.IntegrationTests.Tests.GameRules
var sGameTicker = server.ResolveDependency<IEntitySystemManager>().GetEntitySystem<GameTicker>(); var sGameTicker = server.ResolveDependency<IEntitySystemManager>().GetEntitySystem<GameTicker>();
var sGameTiming = server.ResolveDependency<IGameTiming>(); var sGameTiming = server.ResolveDependency<IGameTiming>();
MaxTimeRestartRuleComponent maxTime = null;
await server.WaitPost(() =>
{
sGameTicker.StartGameRule("MaxTimeRestart", out var ruleEntity); sGameTicker.StartGameRule("MaxTimeRestart", out var ruleEntity);
Assert.That(entityManager.TryGetComponent<MaxTimeRestartRuleComponent>(ruleEntity, out var maxTime)); Assert.That(entityManager.TryGetComponent<MaxTimeRestartRuleComponent>(ruleEntity, out maxTime));
});
Assert.That(server.EntMan.Count<GameRuleComponent>(), Is.EqualTo(1)); Assert.That(server.EntMan.Count<GameRuleComponent>(), Is.EqualTo(1));
Assert.That(server.EntMan.Count<ActiveGameRuleComponent>(), Is.EqualTo(1)); Assert.That(server.EntMan.Count<ActiveGameRuleComponent>(), Is.EqualTo(1));

View File

@@ -77,16 +77,17 @@ public sealed class TraitorRuleTest
await pair.SetAntagPreference(TraitorAntagRoleName, true); await pair.SetAntagPreference(TraitorAntagRoleName, true);
// Add the game rule // Add the game rule
TraitorRuleComponent traitorRule = null;
await server.WaitPost(() =>
{
var gameRuleEnt = ticker.AddGameRule(TraitorGameRuleProtoId); var gameRuleEnt = ticker.AddGameRule(TraitorGameRuleProtoId);
Assert.That(entMan.TryGetComponent<TraitorRuleComponent>(gameRuleEnt, out var traitorRule)); Assert.That(entMan.TryGetComponent<TraitorRuleComponent>(gameRuleEnt, out traitorRule));
// Ready up // Ready up
ticker.ToggleReadyAll(true); ticker.ToggleReadyAll(true);
Assert.That(ticker.PlayerGameStatuses.Values.All(x => x == PlayerGameStatus.ReadyToPlay)); Assert.That(ticker.PlayerGameStatuses.Values.All(x => x == PlayerGameStatus.ReadyToPlay));
// Start the round // Start the round
await server.WaitPost(() =>
{
ticker.StartRound(); ticker.StartRound();
// Force traitor mode to start (skip the delay) // Force traitor mode to start (skip the delay)
ticker.StartGameRule(gameRuleEnt); ticker.StartGameRule(gameRuleEnt);

View File

@@ -9,6 +9,7 @@ using JetBrains.Annotations;
using Robust.Shared.Console; using Robust.Shared.Console;
using Robust.Shared.Map; using Robust.Shared.Map;
using Robust.Shared.Prototypes; using Robust.Shared.Prototypes;
using Robust.Shared.Localization;
namespace Content.Server.GameTicking; namespace Content.Server.GameTicking;
@@ -71,6 +72,16 @@ public sealed partial class GameTicker
var ruleEntity = Spawn(ruleId, MapCoordinates.Nullspace); var ruleEntity = Spawn(ruleId, MapCoordinates.Nullspace);
_sawmill.Info($"Added game rule {ToPrettyString(ruleEntity)}"); _sawmill.Info($"Added game rule {ToPrettyString(ruleEntity)}");
_adminLogger.Add(LogType.EventStarted, $"Added game rule {ToPrettyString(ruleEntity)}"); _adminLogger.Add(LogType.EventStarted, $"Added game rule {ToPrettyString(ruleEntity)}");
var str = Loc.GetString("station-event-system-run-event", ("eventName", ToPrettyString(ruleEntity)));
#if DEBUG
_chatManager.SendAdminAlert(str);
#else
if (RunLevel == GameRunLevel.InRound) // avoids telling admins the round type before it starts so that can be handled elsewhere.
{
_chatManager.SendAdminAlert(str);
}
#endif
Log.Info(str);
var ev = new GameRuleAddedEvent(ruleEntity, ruleId); var ev = new GameRuleAddedEvent(ruleEntity, ruleId);
RaiseLocalEvent(ruleEntity, ref ev, true); RaiseLocalEvent(ruleEntity, ref ev, true);

View File

@@ -2,11 +2,12 @@ using System.Linq;
using Content.Server.Administration; using Content.Server.Administration;
using Content.Server.GameTicking; using Content.Server.GameTicking;
using Content.Server.GameTicking.Rules; using Content.Server.GameTicking.Rules;
using Content.Server.GameTicking.Rules.Components;
using Content.Server.StationEvents.Components; using Content.Server.StationEvents.Components;
using Content.Shared.Administration; using Content.Shared.Administration;
using Content.Shared.EntityTable;
using Content.Shared.GameTicking.Components; using Content.Shared.GameTicking.Components;
using JetBrains.Annotations; using JetBrains.Annotations;
using Robust.Shared.Prototypes;
using Robust.Shared.Random; using Robust.Shared.Random;
using Robust.Shared.Toolshed; using Robust.Shared.Toolshed;
using Robust.Shared.Utility; using Robust.Shared.Utility;
@@ -23,13 +24,17 @@ namespace Content.Server.StationEvents
[Dependency] private readonly IRobustRandom _random = default!; [Dependency] private readonly IRobustRandom _random = default!;
[Dependency] private readonly EventManagerSystem _event = default!; [Dependency] private readonly EventManagerSystem _event = default!;
public const float MinEventTime = 60 * 3; protected override void Started(EntityUid uid, BasicStationEventSchedulerComponent component, GameRuleComponent gameRule,
public const float MaxEventTime = 60 * 10; GameRuleStartedEvent args)
{
// A little starting variance so schedulers dont all proc at once.
component.TimeUntilNextEvent = RobustRandom.NextFloat(component.MinimumTimeUntilFirstEvent, component.MinimumTimeUntilFirstEvent + 120);
}
protected override void Ended(EntityUid uid, BasicStationEventSchedulerComponent component, GameRuleComponent gameRule, protected override void Ended(EntityUid uid, BasicStationEventSchedulerComponent component, GameRuleComponent gameRule,
GameRuleEndedEvent args) GameRuleEndedEvent args)
{ {
component.TimeUntilNextEvent = BasicStationEventSchedulerComponent.MinimumTimeUntilFirstEvent; component.TimeUntilNextEvent = component.MinimumTimeUntilFirstEvent;
} }
@@ -49,10 +54,10 @@ namespace Content.Server.StationEvents
if (eventScheduler.TimeUntilNextEvent > 0) if (eventScheduler.TimeUntilNextEvent > 0)
{ {
eventScheduler.TimeUntilNextEvent -= frameTime; eventScheduler.TimeUntilNextEvent -= frameTime;
return; continue;
} }
_event.RunRandomEvent(); _event.RunRandomEvent(eventScheduler.ScheduledGameRules);
ResetTimer(eventScheduler); ResetTimer(eventScheduler);
} }
} }
@@ -62,7 +67,7 @@ namespace Content.Server.StationEvents
/// </summary> /// </summary>
private void ResetTimer(BasicStationEventSchedulerComponent component) private void ResetTimer(BasicStationEventSchedulerComponent component)
{ {
component.TimeUntilNextEvent = _random.NextFloat(MinEventTime, MaxEventTime); component.TimeUntilNextEvent = component.MinMaxEventTiming.Next(_random);
} }
} }
@@ -70,7 +75,8 @@ namespace Content.Server.StationEvents
public sealed class StationEventCommand : ToolshedCommand public sealed class StationEventCommand : ToolshedCommand
{ {
private EventManagerSystem? _stationEvent; private EventManagerSystem? _stationEvent;
private BasicStationEventSchedulerSystem? _basicScheduler; private EntityTableSystem? _entityTable;
private IComponentFactory? _compFac;
private IRobustRandom? _random; private IRobustRandom? _random;
/// <summary> /// <summary>
@@ -88,10 +94,11 @@ namespace Content.Server.StationEvents
/// to even exist) so I think it's fine. /// to even exist) so I think it's fine.
/// </remarks> /// </remarks>
[CommandImplementation("simulate")] [CommandImplementation("simulate")]
public IEnumerable<(string, float)> Simulate([CommandArgument] int rounds, [CommandArgument] int playerCount, [CommandArgument] float roundEndMean, [CommandArgument] float roundEndStdDev) public IEnumerable<(string, float)> Simulate([CommandArgument] EntityPrototype eventScheduler, [CommandArgument] int rounds, [CommandArgument] int playerCount, [CommandArgument] float roundEndMean, [CommandArgument] float roundEndStdDev)
{ {
_stationEvent ??= GetSys<EventManagerSystem>(); _stationEvent ??= GetSys<EventManagerSystem>();
_basicScheduler ??= GetSys<BasicStationEventSchedulerSystem>(); _entityTable ??= GetSys<EntityTableSystem>();
_compFac ??= IoCManager.Resolve<IComponentFactory>();
_random ??= IoCManager.Resolve<IRobustRandom>(); _random ??= IoCManager.Resolve<IRobustRandom>();
var occurrences = new Dictionary<string, int>(); var occurrences = new Dictionary<string, int>();
@@ -101,6 +108,13 @@ namespace Content.Server.StationEvents
occurrences.Add(ev.Key.ID, 0); occurrences.Add(ev.Key.ID, 0);
} }
if (!eventScheduler.TryGetComponent<BasicStationEventSchedulerComponent>(out var basicScheduler, _compFac))
{
return occurrences.Select(p => (p.Key, (float)p.Value)).OrderByDescending(p => p.Item2);
}
var compMinMax = basicScheduler.MinMaxEventTiming; // we gotta do this since we cant execute on comp w/o an ent.
for (var i = 0; i < rounds; i++) for (var i = 0; i < rounds; i++)
{ {
var curTime = TimeSpan.Zero; var curTime = TimeSpan.Zero;
@@ -111,9 +125,16 @@ namespace Content.Server.StationEvents
while (curTime.TotalSeconds < randomEndTime) while (curTime.TotalSeconds < randomEndTime)
{ {
// sim an event // sim an event
curTime += TimeSpan.FromSeconds(_random.NextFloat(BasicStationEventSchedulerSystem.MinEventTime, BasicStationEventSchedulerSystem.MaxEventTime)); curTime += TimeSpan.FromSeconds(compMinMax.Next(_random));
if (!_stationEvent.TryBuildLimitedEvents(basicScheduler.ScheduledGameRules, out var selectedEvents))
{
continue; // doesnt break because maybe the time is preventing events being available.
}
var available = _stationEvent.AvailableEvents(false, playerCount, curTime); var available = _stationEvent.AvailableEvents(false, playerCount, curTime);
var ev = _stationEvent.FindEvent(available); var plausibleEvents = new Dictionary<EntityPrototype, StationEventComponent>(available.Intersect(selectedEvents)); // C# makes me sad
var ev = _stationEvent.FindEvent(plausibleEvents);
if (ev == null) if (ev == null)
continue; continue;
@@ -125,26 +146,40 @@ namespace Content.Server.StationEvents
} }
[CommandImplementation("lsprob")] [CommandImplementation("lsprob")]
public IEnumerable<(string, float)> LsProb() public IEnumerable<(string, float)> LsProb([CommandArgument] EntityPrototype eventScheduler)
{ {
_compFac ??= IoCManager.Resolve<IComponentFactory>();
_stationEvent ??= GetSys<EventManagerSystem>(); _stationEvent ??= GetSys<EventManagerSystem>();
var events = _stationEvent.AllEvents();
var totalWeight = events.Sum(x => x.Value.Weight); if (!eventScheduler.TryGetComponent<BasicStationEventSchedulerComponent>(out var basicScheduler, _compFac))
yield break;
foreach (var (proto, comp) in events) if (!_stationEvent.TryBuildLimitedEvents(basicScheduler.ScheduledGameRules, out var events))
yield break;
var totalWeight = events.Sum(x => x.Value.Weight); // Well this shit definitely isnt correct now, and I see no way to make it correct.
// Its probably *fine* but it wont be accurate if the EntityTableSelector does any subsetting.
foreach (var (proto, comp) in events) // The only solution I see is to do a simulation, and we already have that, so...!
{ {
yield return (proto.ID, comp.Weight / totalWeight); yield return (proto.ID, comp.Weight / totalWeight);
} }
} }
[CommandImplementation("lsprobtime")] [CommandImplementation("lsprobtime")]
public IEnumerable<(string, float)> LsProbTime([CommandArgument] float time) public IEnumerable<(string, float)> LsProbTime([CommandArgument] EntityPrototype eventScheduler, [CommandArgument] float time)
{ {
_compFac ??= IoCManager.Resolve<IComponentFactory>();
_stationEvent ??= GetSys<EventManagerSystem>(); _stationEvent ??= GetSys<EventManagerSystem>();
var events = _stationEvent.AllEvents().Where(pair => pair.Value.EarliestStart <= time).ToList();
var totalWeight = events.Sum(x => x.Value.Weight); if (!eventScheduler.TryGetComponent<BasicStationEventSchedulerComponent>(out var basicScheduler, _compFac))
yield break;
if (!_stationEvent.TryBuildLimitedEvents(basicScheduler.ScheduledGameRules, out var untimedEvents))
yield break;
var events = untimedEvents.Where(pair => pair.Value.EarliestStart <= time).ToList();
var totalWeight = events.Sum(x => x.Value.Weight); // same subsetting issue as lsprob.
foreach (var (proto, comp) in events) foreach (var (proto, comp) in events)
{ {
@@ -153,12 +188,18 @@ namespace Content.Server.StationEvents
} }
[CommandImplementation("prob")] [CommandImplementation("prob")]
public float Prob([CommandArgument] string eventId) public float Prob([CommandArgument] EntityPrototype eventScheduler, [CommandArgument] string eventId)
{ {
_compFac ??= IoCManager.Resolve<IComponentFactory>();
_stationEvent ??= GetSys<EventManagerSystem>(); _stationEvent ??= GetSys<EventManagerSystem>();
var events = _stationEvent.AllEvents();
var totalWeight = events.Sum(x => x.Value.Weight); if (!eventScheduler.TryGetComponent<BasicStationEventSchedulerComponent>(out var basicScheduler, _compFac))
return 0f;
if (!_stationEvent.TryBuildLimitedEvents(basicScheduler.ScheduledGameRules, out var events))
return 0f;
var totalWeight = events.Sum(x => x.Value.Weight); // same subsetting issue as lsprob.
var weight = 0f; var weight = 0f;
if (events.TryFirstOrNull(p => p.Key.ID == eventId, out var pair)) if (events.TryFirstOrNull(p => p.Key.ID == eventId, out var pair))
{ {

View File

@@ -1,14 +1,35 @@
namespace Content.Server.StationEvents.Components; using Content.Shared.Destructible.Thresholds;
using Content.Shared.EntityTable.EntitySelectors;
namespace Content.Server.StationEvents.Components;
[RegisterComponent, Access(typeof(BasicStationEventSchedulerSystem))] [RegisterComponent, Access(typeof(BasicStationEventSchedulerSystem))]
public sealed partial class BasicStationEventSchedulerComponent : Component public sealed partial class BasicStationEventSchedulerComponent : Component
{ {
public const float MinimumTimeUntilFirstEvent = 300; /// <summary>
/// How long the the scheduler waits to begin starting rules.
/// </summary>
[DataField]
public float MinimumTimeUntilFirstEvent = 200;
/// <summary> /// <summary>
/// How long until the next check for an event runs /// The minimum and maximum time between rule starts in seconds.
/// </summary> /// </summary>
/// Default value is how long until first event is allowed [DataField]
[ViewVariables(VVAccess.ReadWrite)] public MinMax MinMaxEventTiming = new(3 * 60, 10 * 60);
public float TimeUntilNextEvent = MinimumTimeUntilFirstEvent;
/// <summary>
/// How long until the next check for an event runs, is initially set based on MinimumTimeUntilFirstEvent & MinMaxEventTiming.
/// </summary>
[DataField]
public float TimeUntilNextEvent;
/// <summary>
/// The gamerules that the scheduler can choose from
/// </summary>
/// Reminder that though we could do all selection via the EntityTableSelector, we also need to consider various <see cref="StationEventComponent"/> restrictions.
/// As such, we want to pass a list of acceptable game rules, which are then parsed for restrictions by the <see cref="EventManagerSystem"/>.
[DataField(required: true)]
public EntityTableSelector ScheduledGameRules = default!;
} }

View File

@@ -1,24 +0,0 @@
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;
}

View File

@@ -1,17 +1,41 @@
namespace Content.Server.StationEvents.Components; using Content.Shared.EntityTable.EntitySelectors;
namespace Content.Server.StationEvents.Components;
[RegisterComponent, Access(typeof(RampingStationEventSchedulerSystem))] [RegisterComponent, Access(typeof(RampingStationEventSchedulerSystem))]
public sealed partial class RampingStationEventSchedulerComponent : Component public sealed partial class RampingStationEventSchedulerComponent : Component
{ {
[DataField("endTime"), ViewVariables(VVAccess.ReadWrite)] /// <summary>
/// Average ending chaos modifier for the ramping event scheduler. Higher means faster.
/// Max chaos chosen for a round will deviate from this
/// </summary>
[DataField]
public float AverageChaos = 6f;
/// <summary>
/// Average time (in minutes) for when the ramping event scheduler should stop increasing the chaos modifier.
/// Close to how long you expect a round to last, so you'll probably have to tweak this on downstreams.
/// </summary>
[DataField]
public float AverageEndTime = 40f;
[DataField]
public float EndTime; public float EndTime;
[DataField("maxChaos"), ViewVariables(VVAccess.ReadWrite)] [DataField]
public float MaxChaos; public float MaxChaos;
[DataField("startingChaos"), ViewVariables(VVAccess.ReadWrite)] [DataField]
public float StartingChaos; public float StartingChaos;
[DataField("timeUntilNextEvent"), ViewVariables(VVAccess.ReadWrite)] [DataField]
public float TimeUntilNextEvent; public float TimeUntilNextEvent;
/// <summary>
/// The gamerules that the scheduler can choose from
/// </summary>
/// Reminder that though we could do all selection via the EntityTableSelector, we also need to consider various <see cref="StationEventComponent"/> restrictions.
/// As such, we want to pass a list of acceptable game rules, which are then parsed for restrictions by the <see cref="EventManagerSystem"/>.
[DataField(required: true)]
public EntityTableSelector ScheduledGameRules = default!;
} }

View File

@@ -1,5 +1,4 @@
using System.Linq; using System.Linq;
using Content.Server.Chat.Managers;
using Content.Server.GameTicking; using Content.Server.GameTicking;
using Content.Server.RoundEnd; using Content.Server.RoundEnd;
using Content.Server.StationEvents.Components; using Content.Server.StationEvents.Components;
@@ -8,6 +7,8 @@ using Robust.Server.Player;
using Robust.Shared.Configuration; using Robust.Shared.Configuration;
using Robust.Shared.Prototypes; using Robust.Shared.Prototypes;
using Robust.Shared.Random; using Robust.Shared.Random;
using Content.Shared.EntityTable.EntitySelectors;
using Content.Shared.EntityTable;
namespace Content.Server.StationEvents; namespace Content.Server.StationEvents;
@@ -17,7 +18,7 @@ public sealed class EventManagerSystem : EntitySystem
[Dependency] private readonly IPlayerManager _playerManager = default!; [Dependency] private readonly IPlayerManager _playerManager = default!;
[Dependency] private readonly IRobustRandom _random = default!; [Dependency] private readonly IRobustRandom _random = default!;
[Dependency] private readonly IPrototypeManager _prototype = default!; [Dependency] private readonly IPrototypeManager _prototype = default!;
[Dependency] private readonly IChatManager _chat = default!; [Dependency] private readonly EntityTableSystem _entityTable = default!;
[Dependency] public readonly GameTicker GameTicker = default!; [Dependency] public readonly GameTicker GameTicker = default!;
[Dependency] private readonly RoundEndSystem _roundEnd = default!; [Dependency] private readonly RoundEndSystem _roundEnd = default!;
@@ -34,7 +35,8 @@ public sealed class EventManagerSystem : EntitySystem
/// <summary> /// <summary>
/// Randomly runs a valid event. /// Randomly runs a valid event.
/// </summary> /// </summary>
public string RunRandomEvent() [Obsolete("use overload taking EnityTableSelector instead or risk unexpected results")]
public void RunRandomEvent()
{ {
var randomEvent = PickRandomEvent(); var randomEvent = PickRandomEvent();
@@ -42,14 +44,86 @@ public sealed class EventManagerSystem : EntitySystem
{ {
var errStr = Loc.GetString("station-event-system-run-random-event-no-valid-events"); var errStr = Loc.GetString("station-event-system-run-random-event-no-valid-events");
Log.Error(errStr); Log.Error(errStr);
return errStr; return;
} }
var ent = GameTicker.AddGameRule(randomEvent); GameTicker.AddGameRule(randomEvent);
var str = Loc.GetString("station-event-system-run-event",("eventName", ToPrettyString(ent))); }
_chat.SendAdminAlert(str);
Log.Info(str); /// <summary>
return str; /// Randomly runs an event from provided EntityTableSelector.
/// </summary>
public void RunRandomEvent(EntityTableSelector limitedEventsTable)
{
if (!TryBuildLimitedEvents(limitedEventsTable, out var limitedEvents))
{
Log.Warning("Provided event table could not build dict!");
return;
}
var randomLimitedEvent = FindEvent(limitedEvents); // this picks the event, It might be better to use the GetSpawns to do it, but that will be a major rebalancing fuck.
if (randomLimitedEvent == null)
{
Log.Warning("The selected random event is null!");
return;
}
if (!_prototype.TryIndex(randomLimitedEvent, out _))
{
Log.Warning("A requested event is not available!");
return;
}
GameTicker.AddGameRule(randomLimitedEvent);
}
/// <summary>
/// Returns true if the provided EntityTableSelector gives at least one prototype with a StationEvent comp.
/// </summary>
public bool TryBuildLimitedEvents(EntityTableSelector limitedEventsTable, out Dictionary<EntityPrototype, StationEventComponent> limitedEvents)
{
limitedEvents = new Dictionary<EntityPrototype, StationEventComponent>();
var availableEvents = AvailableEvents(); // handles the player counts and individual event restrictions
if (availableEvents.Count == 0)
{
Log.Warning("No events were available to run!");
return false;
}
var selectedEvents = _entityTable.GetSpawns(limitedEventsTable);
if (selectedEvents.Any() != true) // This is here so if you fuck up the table it wont die.
return false;
foreach (var eventid in selectedEvents)
{
if (!_prototype.TryIndex(eventid, out var eventproto))
{
Log.Warning("An event ID has no prototype index!");
continue;
}
if (limitedEvents.ContainsKey(eventproto)) // This stops it from dying if you add duplicate entries in a fucked table
continue;
if (eventproto.Abstract)
continue;
if (!eventproto.TryGetComponent<StationEventComponent>(out var stationEvent, EntityManager.ComponentFactory))
continue;
if (!availableEvents.ContainsKey(eventproto))
continue;
limitedEvents.Add(eventproto, stationEvent);
}
if (!limitedEvents.Any())
return false;
return true;
} }
/// <summary> /// <summary>

View File

@@ -1,54 +0,0 @@
using Content.Server.GameTicking.Rules;
using Content.Server.StationEvents.Components;
using Content.Shared.CCVar;
using Content.Shared.GameTicking.Components;
using Content.Shared.Random.Helpers;
using Robust.Shared.Configuration;
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!;
[Dependency] private readonly IConfigurationManager _cfg = default!;
private TimeSpan _meteorMinDelay;
private TimeSpan _meteorMaxDelay;
public override void Initialize()
{
base.Initialize();
_cfg.OnValueChanged(CCVars.MeteorSwarmMinTime, f => { _meteorMinDelay = TimeSpan.FromMinutes(f); }, true);
_cfg.OnValueChanged(CCVars.MeteorSwarmMaxTime, f => { _meteorMaxDelay = TimeSpan.FromMinutes(f); }, true);
}
protected override void Started(EntityUid uid, MeteorSchedulerComponent component, GameRuleComponent gameRule, GameRuleStartedEvent args)
{
base.Started(uid, component, gameRule, args);
component.NextSwarmTime = Timing.CurTime + RobustRandom.Next(_meteorMinDelay, _meteorMaxDelay);
}
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(_meteorMinDelay, _meteorMaxDelay);
}
private void RunSwarm(Entity<MeteorSchedulerComponent> ent)
{
var swarmWeights = _prototypeManager.Index(ent.Comp.Config);
GameTicker.StartGameRule(swarmWeights.Pick(RobustRandom));
}
}

View File

@@ -1,22 +1,20 @@
using Content.Server.GameTicking; using Content.Server.GameTicking;
using Content.Server.GameTicking.Rules; using Content.Server.GameTicking.Rules;
using Content.Server.GameTicking.Rules.Components;
using Content.Server.StationEvents.Components; using Content.Server.StationEvents.Components;
using Content.Server.StationEvents.Events;
using Content.Shared.CCVar;
using Content.Shared.GameTicking.Components; using Content.Shared.GameTicking.Components;
using Robust.Shared.Configuration;
using Robust.Shared.Random; using Robust.Shared.Random;
namespace Content.Server.StationEvents; namespace Content.Server.StationEvents;
public sealed class RampingStationEventSchedulerSystem : GameRuleSystem<RampingStationEventSchedulerComponent> public sealed class RampingStationEventSchedulerSystem : GameRuleSystem<RampingStationEventSchedulerComponent>
{ {
[Dependency] private readonly IConfigurationManager _cfg = default!;
[Dependency] private readonly IRobustRandom _random = default!; [Dependency] private readonly IRobustRandom _random = default!;
[Dependency] private readonly EventManagerSystem _event = default!; [Dependency] private readonly EventManagerSystem _event = default!;
[Dependency] private readonly GameTicker _gameTicker = default!; [Dependency] private readonly GameTicker _gameTicker = default!;
/// <summary>
/// Returns the ChaosModifier which increases as round time increases to a point.
/// </summary>
public float GetChaosModifier(EntityUid uid, RampingStationEventSchedulerComponent component) public float GetChaosModifier(EntityUid uid, RampingStationEventSchedulerComponent component)
{ {
var roundTime = (float) _gameTicker.RoundDuration().TotalSeconds; var roundTime = (float) _gameTicker.RoundDuration().TotalSeconds;
@@ -30,14 +28,11 @@ public sealed class RampingStationEventSchedulerSystem : GameRuleSystem<RampingS
{ {
base.Started(uid, component, gameRule, args); base.Started(uid, component, gameRule, args);
var avgChaos = _cfg.GetCVar(CCVars.EventsRampingAverageChaos);
var avgTime = _cfg.GetCVar(CCVars.EventsRampingAverageEndTime);
// Worlds shittiest probability distribution // Worlds shittiest probability distribution
// Got a complaint? Send them to // Got a complaint? Send them to
component.MaxChaos = _random.NextFloat(avgChaos - avgChaos / 4, avgChaos + avgChaos / 4); component.MaxChaos = _random.NextFloat(component.AverageChaos - component.AverageChaos / 4, component.AverageChaos + component.AverageChaos / 4);
// This is in minutes, so *60 for seconds (for the chaos calc) // This is in minutes, so *60 for seconds (for the chaos calc)
component.EndTime = _random.NextFloat(avgTime - avgTime / 4, avgTime + avgTime / 4) * 60f; component.EndTime = _random.NextFloat(component.AverageEndTime - component.AverageEndTime / 4, component.AverageEndTime + component.AverageEndTime / 4) * 60f;
component.StartingChaos = component.MaxChaos / 10; component.StartingChaos = component.MaxChaos / 10;
PickNextEventTime(uid, component); PickNextEventTime(uid, component);
@@ -54,19 +49,22 @@ public sealed class RampingStationEventSchedulerSystem : GameRuleSystem<RampingS
while (query.MoveNext(out var uid, out var scheduler, out var gameRule)) while (query.MoveNext(out var uid, out var scheduler, out var gameRule))
{ {
if (!GameTicker.IsGameRuleActive(uid, gameRule)) if (!GameTicker.IsGameRuleActive(uid, gameRule))
return; continue;
if (scheduler.TimeUntilNextEvent > 0f) if (scheduler.TimeUntilNextEvent > 0f)
{ {
scheduler.TimeUntilNextEvent -= frameTime; scheduler.TimeUntilNextEvent -= frameTime;
return; continue;
} }
PickNextEventTime(uid, scheduler); PickNextEventTime(uid, scheduler);
_event.RunRandomEvent(); _event.RunRandomEvent(scheduler.ScheduledGameRules);
} }
} }
/// <summary>
/// Sets the timing of the next event addition.
/// </summary>
private void PickNextEventTime(EntityUid uid, RampingStationEventSchedulerComponent component) private void PickNextEventTime(EntityUid uid, RampingStationEventSchedulerComponent component)
{ {
var mod = GetChaosModifier(uid, component); var mod = GetChaosModifier(uid, component);

View File

@@ -110,32 +110,6 @@ namespace Content.Shared.CCVar
public static readonly CVarDef<bool> public static readonly CVarDef<bool>
EventsEnabled = CVarDef.Create("events.enabled", true, CVar.ARCHIVE | CVar.SERVERONLY); EventsEnabled = CVarDef.Create("events.enabled", true, CVar.ARCHIVE | CVar.SERVERONLY);
/// <summary>
/// Average time (in minutes) for when the ramping event scheduler should stop increasing the chaos modifier.
/// Close to how long you expect a round to last, so you'll probably have to tweak this on downstreams.
/// </summary>
public static readonly CVarDef<float>
EventsRampingAverageEndTime = CVarDef.Create("events.ramping_average_end_time", 40f, CVar.ARCHIVE | CVar.SERVERONLY);
/// <summary>
/// Average ending chaos modifier for the ramping event scheduler.
/// Max chaos chosen for a round will deviate from this
/// </summary>
public static readonly CVarDef<float>
EventsRampingAverageChaos = CVarDef.Create("events.ramping_average_chaos", 6f, CVar.ARCHIVE | CVar.SERVERONLY);
/// <summary>
/// Minimum time between meteor swarms in minutes.
/// </summary>
public static readonly CVarDef<float>
MeteorSwarmMinTime = CVarDef.Create("events.meteor_swarm_min_time", 12.5f, CVar.ARCHIVE | CVar.SERVERONLY);
/// <summary>
/// Maximum time between meteor swarms in minutes.
/// </summary>
public static readonly CVarDef<float>
MeteorSwarmMaxTime = CVarDef.Create("events.meteor_swarm_max_time", 17.5f, CVar.ARCHIVE | CVar.SERVERONLY);
/* /*
* Game * Game
*/ */

View File

@@ -1,2 +1,5 @@
all-at-once-title = All at once all-at-once-title = All at once
all-at-once-description = It's just not your day... all-at-once-description = It's just not your day...
aller-at-once-title = Aller at once
aller-at-once-description = You have fucked up now. You *have* fucked up now.

View File

@@ -1,2 +1,5 @@
survival-title = Survival survival-title = Survival
survival-description = No internal threats, but how long can the station survive increasingly chaotic and frequent events? survival-description = No internal threats, but how long can the station survive increasingly chaotic and frequent events?
kessler-syndrome-title = Kessler Syndrome
kessler-syndrome-description = No internal threats, but the station is quickly falling into a belt of meteors!

View File

@@ -1,6 +1,9 @@
zombie-title = Zombies zombie-title = Zombies
zombie-description = The undead have been unleashed on the station! Work with the crew to survive the outbreak and secure the station. zombie-description = The undead have been unleashed on the station! Work with the crew to survive the outbreak and secure the station.
zombieteors-title = Zombieteors
zombieteors-description = The undead have been unleashed on the station amid a cataclysmic meteor shower! Work with your fellow crew and do your best to survive!
zombie-not-enough-ready-players = Not enough players readied up for the game! There were {$readyPlayersCount} players readied up out of {$minimumPlayers} needed. Can't start Zombies. zombie-not-enough-ready-players = Not enough players readied up for the game! There were {$readyPlayersCount} players readied up out of {$minimumPlayers} needed. Can't start Zombies.
zombie-no-one-ready = No players readied up! Can't start Zombies. zombie-no-one-ready = No players readied up! Can't start Zombies.

View File

@@ -1,3 +1,22 @@
# Tables -- Add your new event to this Table if you want it to happen via the basic/ramping schedulers.
- type: entityTable
id: CargoGiftsTable
table: !type:GroupSelector # ~~we need to pass a list of rules, since rules have further restrictions to consider via StationEventComp~~ But we arent doing that shit yet, it picks a random one StationEventComp be damned.
children:
- id: GiftsEngineering
- id: GiftsFireProtection
- id: GiftsJanitor
- id: GiftsMedical
- id: GiftsPizzaPartyLarge
- id: GiftsPizzaPartySmall
- id: GiftsSecurityGuns
- id: GiftsSecurityRiot
- id: GiftsSpacingSupplies
- id: GiftsVendingRestock
# Game Rules
- type: entity - type: entity
id: CargoGiftsBase id: CargoGiftsBase
parent: BaseGameRule parent: BaseGameRule

View File

@@ -1,3 +1,40 @@
- type: entityTable
id: BasicCalmEventsTable
table: !type:AllSelector # we need to pass a list of rules, since rules have further restrictions to consider via StationEventComp
children:
- id: AnomalySpawn
- id: BluespaceArtifact
- id: BluespaceLocker
- id: BreakerFlip
- id: BureaucraticError
- id: ClericalError
- id: CockroachMigration
- id: GasLeak
- id: IonStorm # its calm like 90% of the time smh
- id: KudzuGrowth
- id: MassHallucinations
- id: MimicVendorRule
- id: MouseMigration
- id: PowerGridCheck
- id: SlimesSpawn
- id: SolarFlare
- id: SpiderClownSpawn
- id: SpiderSpawn
- id: VentClog
- type: entityTable
id: BasicAntagEventsTable
table: !type:AllSelector # we need to pass a list of rules, since rules have further restrictions to consider via StationEventComp
children:
- id: ClosetSkeleton
- id: DragonSpawn
- id: KingRatMigration
- id: NinjaSpawn
- id: RevenantSpawn
- id: SleeperAgents
- id: ZombieOutbreak
- type: entity - type: entity
id: BaseStationEvent id: BaseStationEvent
parent: BaseGameRule parent: BaseGameRule
@@ -526,45 +563,6 @@
sounds: sounds:
collection: Paracusia collection: Paracusia
- type: entity
id: ImmovableRodSpawn
parent: BaseGameRule
components:
- type: StationEvent
startAnnouncement: station-event-immovable-rod-start-announcement
startAudio:
path: /Audio/Announcements/attention.ogg
weight: 3.5
duration: 1
earliestStart: 30
minimumPlayers: 20
- type: ImmovableRodRule
rodPrototypes:
- id: ImmovableRodKeepTilesStill
prob: 0.95
orGroup: rodProto
- id: ImmovableRodMop
prob: 0.0072
orGroup: rodProto
- id: ImmovableRodShark
prob: 0.0072
orGroup: rodProto
- id: ImmovableRodClown
prob: 0.0072
orGroup: rodProto
- id: ImmovableRodBanana
prob: 0.0072
orGroup: rodProto
- id: ImmovableRodHammer
prob: 0.0072
orGroup: rodProto
- id: ImmovableRodThrongler
prob: 0.0072
orGroup: rodProto
- id: ImmovableRodGibstick
prob: 0.0072
orGroup: rodProto
- type: entity - type: entity
parent: BaseGameRule parent: BaseGameRule
id: IonStorm id: IonStorm

View File

@@ -1,21 +1,39 @@
- type: entity # Event Tables
parent: BaseGameRule
id: GameRuleMeteorScheduler
components:
- type: GameRule
minPlayers: 25
cancelPresetOnTooFewPlayers: false
- type: MeteorScheduler
- type: weightedRandomEntity - type: entityTable
id: DefaultConfig id: MeteorSwarmDustEventsTable
weights: table: !type:AllSelector # we need to pass a list of rules, since rules have further restrictions to consider via StationEventComp
GameRuleSpaceDustMinor: 44 children:
GameRuleSpaceDustMajor: 22 - id: GameRuleSpaceDustMinor
GameRuleMeteorSwarmSmall: 18 - id: GameRuleSpaceDustMajor
GameRuleMeteorSwarmMedium: 10
GameRuleMeteorSwarmLarge: 5 - type: entityTable
GameRuleUristSwarm: 0.05 id: MeteorSwarmSmallChanceEventsTable
table: !type:AllSelector # we need to pass a list of rules, since rules have further restrictions to consider via StationEventComp
children:
- id: GameRuleMeteorSwarmSmall
prob: 0.15
- type: entityTable
id: MeteorSwarmMildTable
table: !type:AllSelector # we need to pass a list of rules, since rules have further restrictions to consider via StationEventComp
children:
- !type:NestedSelector
tableId: MeteorSwarmDustEventsTable
- !type:NestedSelector
tableId: MeteorSwarmSmallChanceEventsTable
- type: entityTable
id: BasicMeteorSwarmEventsTable
table: !type:AllSelector # we need to pass a list of rules, since rules have further restrictions to consider via StationEventComp
children:
- !type:NestedSelector
tableId: MeteorSwarmDustEventsTable
- id: GameRuleMeteorSwarmSmall
- id: GameRuleMeteorSwarmMedium
- id: GameRuleMeteorSwarmLarge
- id: GameRuleUristSwarm
- id: ImmovableRodSpawn
- type: weightedRandomEntity - type: weightedRandomEntity
id: MeteorSpawnAsteroidWallTable id: MeteorSpawnAsteroidWallTable
@@ -31,18 +49,64 @@
AsteroidRockUranium: 0.5 AsteroidRockUranium: 0.5
AsteroidRockBananium: 0.5 AsteroidRockBananium: 0.5
# Event Schedulers
- type: entity
parent: BaseGameRule
id: MeteorSwarmScheduler
components:
- type: GameRule
- type: BasicStationEventScheduler
minimumTimeUntilFirstEvent: 300 # 5 min
minMaxEventTiming:
min: 750 # 12.5 min
max: 930 # 17.5 min
scheduledGameRules: !type:NestedSelector
tableId: BasicMeteorSwarmEventsTable
- type: entity
parent: BaseGameRule
id: MeteorSwarmMildScheduler
components:
- type: GameRule
- type: BasicStationEventScheduler
minimumTimeUntilFirstEvent: 300 # 5 min
minMaxEventTiming:
min: 750 # 12.5 min
max: 930 # 17.5 min
scheduledGameRules: !type:NestedSelector
tableId: MeteorSwarmMildTable
- type: entity
parent: BaseGameRule
id: KesslerSyndromeScheduler
components:
- type: GameRule
- type: RampingStationEventScheduler
scheduledGameRules: !type:NestedSelector
tableId: BasicMeteorSwarmEventsTable
# Game Rules
- type: entity - type: entity
parent: BaseGameRule parent: BaseGameRule
id: GameRuleMeteorSwarm id: GameRuleMeteorSwarm
abstract: true abstract: true
components: components:
- type: GameRule - type: GameRule
- type: StationEvent
earliestStart: 12
minimumPlayers: 25
- type: MeteorSwarm - type: MeteorSwarm
- type: entity - type: entity
parent: GameRuleMeteorSwarm parent: GameRuleMeteorSwarm
id: GameRuleSpaceDustMinor id: GameRuleSpaceDustMinor
components: components:
- type: StationEvent
weight: 44
earliestStart: 2
minimumPlayers: 0
- type: MeteorSwarm - type: MeteorSwarm
announcement: null announcement: null
announcementSound: null announcementSound: null
@@ -60,6 +124,9 @@
parent: GameRuleMeteorSwarm parent: GameRuleMeteorSwarm
id: GameRuleSpaceDustMajor id: GameRuleSpaceDustMajor
components: components:
- type: StationEvent
weight: 22
minimumPlayers: 0
- type: MeteorSwarm - type: MeteorSwarm
announcement: station-event-space-dust-start-announcement announcement: station-event-space-dust-start-announcement
announcementSound: /Audio/Announcements/attention.ogg announcementSound: /Audio/Announcements/attention.ogg
@@ -77,6 +144,9 @@
parent: GameRuleMeteorSwarm parent: GameRuleMeteorSwarm
id: GameRuleMeteorSwarmSmall id: GameRuleMeteorSwarmSmall
components: components:
- type: StationEvent
weight: 18
minimumPlayers: 15
- type: MeteorSwarm - type: MeteorSwarm
meteors: meteors:
MeteorSmall: 7 MeteorSmall: 7
@@ -86,6 +156,8 @@
parent: GameRuleMeteorSwarm parent: GameRuleMeteorSwarm
id: GameRuleMeteorSwarmMedium id: GameRuleMeteorSwarmMedium
components: components:
- type: StationEvent
weight: 10
- type: MeteorSwarm - type: MeteorSwarm
meteors: meteors:
MeteorSmall: 3 MeteorSmall: 3
@@ -96,6 +168,8 @@
parent: GameRuleMeteorSwarm parent: GameRuleMeteorSwarm
id: GameRuleMeteorSwarmLarge id: GameRuleMeteorSwarmLarge
components: components:
- type: StationEvent
weight: 5
- type: MeteorSwarm - type: MeteorSwarm
meteors: meteors:
MeteorSmall: 2 MeteorSmall: 2
@@ -106,6 +180,8 @@
parent: GameRuleMeteorSwarm parent: GameRuleMeteorSwarm
id: GameRuleUristSwarm id: GameRuleUristSwarm
components: components:
- type: StationEvent
weight: 0.05
- type: MeteorSwarm - type: MeteorSwarm
announcement: station-event-meteor-urist-start-announcement announcement: station-event-meteor-urist-start-announcement
announcementSound: /Audio/Announcements/attention.ogg announcementSound: /Audio/Announcements/attention.ogg
@@ -117,3 +193,42 @@
meteorsPerWave: meteorsPerWave:
min: 10 min: 10
max: 10 max: 10
- type: entity
id: ImmovableRodSpawn
parent: BaseGameRule
components:
- type: StationEvent
startAnnouncement: station-event-immovable-rod-start-announcement
startAudio:
path: /Audio/Announcements/attention.ogg
weight: 3.5
duration: 1
earliestStart: 30
minimumPlayers: 25
- type: ImmovableRodRule
rodPrototypes:
- id: ImmovableRodKeepTilesStill
prob: 0.95
orGroup: rodProto
- id: ImmovableRodMop
prob: 0.0072
orGroup: rodProto
- id: ImmovableRodShark
prob: 0.0072
orGroup: rodProto
- id: ImmovableRodClown
prob: 0.0072
orGroup: rodProto
- id: ImmovableRodBanana
prob: 0.0072
orGroup: rodProto
- id: ImmovableRodHammer
prob: 0.0072
orGroup: rodProto
- id: ImmovableRodThrongler
prob: 0.0072
orGroup: rodProto
- id: ImmovableRodGibstick
prob: 0.0072
orGroup: rodProto

View File

@@ -262,17 +262,68 @@
prototype: InitialInfected prototype: InitialInfected
# event schedulers # event schedulers
- type: entityTable
id: BasicGameRulesTable
table: !type:AllSelector # we need to pass a list of rules, since rules have further restrictions to consider via StationEventComp
children:
- !type:NestedSelector
tableId: BasicCalmEventsTable
- !type:NestedSelector
tableId: BasicAntagEventsTable
- !type:NestedSelector
tableId: CargoGiftsTable
- type: entityTable
id: SpaceTrafficControlTable
table: !type:AllSelector # we need to pass a list of rules, since rules have further restrictions to consider via StationEventComp
children:
- !type:NestedSelector
tableId: UnknownShuttlesFriendlyTable
- !type:NestedSelector
tableId: UnknownShuttlesFreelanceTable
- !type:NestedSelector
tableId: UnknownShuttlesHostileTable
- type: entity - type: entity
id: BasicStationEventScheduler id: BasicStationEventScheduler
parent: BaseGameRule parent: BaseGameRule
components: components:
- type: BasicStationEventScheduler - type: BasicStationEventScheduler
scheduledGameRules: !type:NestedSelector
tableId: BasicGameRulesTable
- type: entity - type: entity
id: RampingStationEventScheduler id: RampingStationEventScheduler
parent: BaseGameRule parent: BaseGameRule
components: components:
- type: RampingStationEventScheduler - type: RampingStationEventScheduler
scheduledGameRules: !type:NestedSelector
tableId: BasicGameRulesTable
- type: entity
id: SpaceTrafficControlEventScheduler # iff we make a selector for EntityTables that can respect StationEventComp restrictions, or somehow impliment them otherwise in said tables,
parent: BaseGameRule # we can remerge this with the other schedulers, but it will silently fail due to that limitation without a separate scheduler to balance atm.
components:
- type: BasicStationEventScheduler
minimumTimeUntilFirstEvent: 1200 # 20 mins
minMaxEventTiming:
min: 600 # 10 mins
max: 1800 # 30 mins
scheduledGameRules: !type:NestedSelector
tableId: SpaceTrafficControlTable
- type: entity
id: SpaceTrafficControlFriendlyEventScheduler
parent: BaseGameRule
components:
- type: BasicStationEventScheduler
minimumTimeUntilFirstEvent: 1200 # 20 mins
minMaxEventTiming:
min: 600 # 10 mins
max: 1800 # 30 mins
scheduledGameRules: !type:NestedSelector
tableId: UnknownShuttlesFriendlyTable
# variation passes # variation passes
- type: entity - type: entity

View File

@@ -1,3 +1,28 @@
# Shuttle Game Rule Tables -- If you dont add your rules to these they wont be used by the games schedulers.
- type: entityTable
id: UnknownShuttlesFriendlyTable
table: !type:AllSelector # we need to pass a list of rules, since rules have further restrictions to consider via StationEventComp
children:
- id: UnknownShuttleCargoLost
- id: UnknownShuttleTravelingCuisine
- id: UnknownShuttleDisasterEvacPod
- id: UnknownShuttleHonki
- type: entityTable
id: UnknownShuttlesFreelanceTable
table: !type:AllSelector # we need to pass a list of rules, since rules have further restrictions to consider via StationEventComp
children:
- id: UnknownShuttleSyndieEvacPod
- type: entityTable
id: UnknownShuttlesHostileTable
table: !type:AllSelector # we need to pass a list of rules, since rules have further restrictions to consider via StationEventComp
children:
- id: LoneOpsSpawn
# Shuttle Game Rules
- type: entity - type: entity
abstract: true abstract: true
parent: BaseGameRule parent: BaseGameRule

View File

@@ -6,12 +6,31 @@
showInVote: false # secret showInVote: false # secret
description: survival-description description: survival-description
rules: rules:
- MeteorSwarmScheduler
- RampingStationEventScheduler - RampingStationEventScheduler
- GameRuleMeteorScheduler - SpaceTrafficControlEventScheduler
- SpaceTrafficControlFriendlyEventScheduler
- BasicRoundstartVariation
- type: gamePreset
id: KesslerSyndrome
alias:
- kessler
- junk
- meteorhell
name: kessler-syndrome-title
showInVote: false # secret
description: kessler-syndrome-description
rules:
- KesslerSyndromeScheduler
- RampingStationEventScheduler
- SpaceTrafficControlEventScheduler
- BasicRoundstartVariation - BasicRoundstartVariation
- type: gamePreset - type: gamePreset
id: AllAtOnce id: AllAtOnce
alias:
- all
name: all-at-once-title name: all-at-once-title
description: all-at-once-description description: all-at-once-description
showInVote: false showInVote: false
@@ -20,8 +39,34 @@
- Traitor - Traitor
- Revolutionary - Revolutionary
- Zombie - Zombie
- KesslerSyndromeScheduler
- RampingStationEventScheduler - RampingStationEventScheduler
- GameRuleMeteorScheduler - SpaceTrafficControlEventScheduler
- BasicRoundstartVariation
- type: gamePreset
id: AllerAtOnce
alias:
- allall
- aller
- badidea
- punishment
name: aller-at-once-title
description: all-at-once-description
showInVote: false #Please god dont do this
rules:
- Nukeops
- Traitor
- Revolutionary
- Zombie
- BasicStationEventScheduler
- KesslerSyndromeScheduler
- MeteorSwarmMildScheduler
- MeteorSwarmScheduler
- RampingStationEventScheduler
- SpaceTrafficControlEventScheduler
- SpaceTrafficControlFriendlyEventScheduler
- BasicRoundstartVariation
- type: gamePreset - type: gamePreset
id: Extended id: Extended
@@ -33,7 +78,8 @@
description: extended-description description: extended-description
rules: rules:
- BasicStationEventScheduler - BasicStationEventScheduler
- GameRuleMeteorScheduler - MeteorSwarmScheduler
- SpaceTrafficControlEventScheduler
- BasicRoundstartVariation - BasicRoundstartVariation
- type: gamePreset - type: gamePreset
@@ -45,6 +91,7 @@
showInVote: false #4boring4vote showInVote: false #4boring4vote
description: greenshift-description description: greenshift-description
rules: rules:
- SpaceTrafficControlFriendlyEventScheduler
- BasicRoundstartVariation - BasicRoundstartVariation
- type: gamePreset - type: gamePreset
@@ -67,7 +114,9 @@
description: secret-description description: secret-description
rules: rules:
- BasicStationEventScheduler - BasicStationEventScheduler
- GameRuleMeteorScheduler - MeteorSwarmScheduler
- SpaceTrafficControlEventScheduler
- BasicRoundstartVariation
- 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.
@@ -76,6 +125,9 @@
name: secret-title name: secret-title
showInVote: false #Admin Use showInVote: false #Admin Use
description: secret-description description: secret-description
rules:
- SpaceTrafficControlFriendlyEventScheduler
- BasicRoundstartVariation
- type: gamePreset - type: gamePreset
id: Sandbox id: Sandbox
@@ -92,6 +144,7 @@
id: Traitor id: Traitor
alias: alias:
- traitor - traitor
- tator
name: traitor-title name: traitor-title
description: traitor-description description: traitor-description
showInVote: false showInVote: false
@@ -99,7 +152,8 @@
- Traitor - Traitor
- SubGamemodesRule - SubGamemodesRule
- BasicStationEventScheduler - BasicStationEventScheduler
- GameRuleMeteorScheduler - MeteorSwarmScheduler
- SpaceTrafficControlEventScheduler
- BasicRoundstartVariation - BasicRoundstartVariation
- type: gamePreset - type: gamePreset
@@ -126,7 +180,8 @@
- Nukeops - Nukeops
- SubGamemodesRule - SubGamemodesRule
- BasicStationEventScheduler - BasicStationEventScheduler
- GameRuleMeteorScheduler - MeteorSwarmScheduler
- SpaceTrafficControlEventScheduler
- BasicRoundstartVariation - BasicRoundstartVariation
- type: gamePreset - type: gamePreset
@@ -142,7 +197,8 @@
- Revolutionary - Revolutionary
- SubGamemodesRule - SubGamemodesRule
- BasicStationEventScheduler - BasicStationEventScheduler
- GameRuleMeteorScheduler - MeteorSwarmScheduler
- SpaceTrafficControlEventScheduler
- BasicRoundstartVariation - BasicRoundstartVariation
- type: gamePreset - type: gamePreset
@@ -159,5 +215,22 @@
rules: rules:
- Zombie - Zombie
- BasicStationEventScheduler - BasicStationEventScheduler
- GameRuleMeteorScheduler - MeteorSwarmScheduler
- SpaceTrafficControlEventScheduler
- BasicRoundstartVariation
- type: gamePreset
id: Zombieteors
alias:
- zombieteors
- zombombies
- meteombies
name: zombieteors-title
description: zombieteors-description
showInVote: false
rules:
- Zombie
- BasicStationEventScheduler
- KesslerSyndromeScheduler
- SpaceTrafficControlEventScheduler
- BasicRoundstartVariation - BasicRoundstartVariation

View File

@@ -3,6 +3,8 @@
weights: weights:
Nukeops: 0.20 Nukeops: 0.20
Traitor: 0.60 Traitor: 0.60
Zombie: 0.05 Zombie: 0.04
Survival: 0.10 Zombieteors: 0.01
Survival: 0.09
KesslerSyndrome: 0.01
Revolutionary: 0.05 Revolutionary: 0.05