mirror of
https://github.com/WWhiteDreamProject/wwdpublic.git
synced 2026-04-17 13:37:47 +03:00
* Use new Subs.CVar helper Removes manual config OnValueChanged calls, removes need to remember to manually unsubscribe. This both reduces boilerplate and fixes many issues where subscriptions weren't removed on entity system shutdown. * Fix a bunch of warnings * More warning fixes * Use new DateTime serializer to get rid of ISerializationHooks in changelog code. * Get rid of some more ISerializationHooks for enums * And a little more * Apply suggestions from code review Co-authored-by: 0x6273 <0x40@keemail.me> --------- Co-authored-by: 0x6273 <0x40@keemail.me> (cherry picked from commit 68ce53ae17985876d6d112b764b2144964a9f42e)
217 lines
6.8 KiB
C#
217 lines
6.8 KiB
C#
using System.Linq;
|
|
using Content.Server.GameTicking;
|
|
using Content.Server.StationEvents.Components;
|
|
using Content.Shared.CCVar;
|
|
using Robust.Server.Player;
|
|
using Robust.Shared.Configuration;
|
|
using Robust.Shared.Prototypes;
|
|
using Robust.Shared.Random;
|
|
using Content.Server.Psionics.Glimmer;
|
|
using Content.Shared.Psionics.Glimmer;
|
|
namespace Content.Server.StationEvents;
|
|
|
|
public sealed class EventManagerSystem : EntitySystem
|
|
{
|
|
[Dependency] private readonly IConfigurationManager _configurationManager = default!;
|
|
[Dependency] private readonly IPlayerManager _playerManager = default!;
|
|
[Dependency] private readonly IRobustRandom _random = default!;
|
|
[Dependency] private readonly IPrototypeManager _prototype = default!;
|
|
[Dependency] public readonly GameTicker GameTicker = default!;
|
|
[Dependency] private readonly GlimmerSystem _glimmerSystem = default!; //Nyano - Summary: pulls in the glimmer system.
|
|
|
|
private ISawmill _sawmill = default!;
|
|
|
|
public bool EventsEnabled { get; private set; }
|
|
private void SetEnabled(bool value) => EventsEnabled = value;
|
|
|
|
public override void Initialize()
|
|
{
|
|
base.Initialize();
|
|
|
|
_sawmill = Logger.GetSawmill("events");
|
|
|
|
Subs.CVar(_configurationManager, CCVars.EventsEnabled, SetEnabled, true);
|
|
|
|
SubscribeLocalEvent<StationEventComponent, EntityUnpausedEvent>(OnUnpaused);
|
|
}
|
|
|
|
private void OnUnpaused(EntityUid uid, StationEventComponent component, ref EntityUnpausedEvent args)
|
|
{
|
|
component.StartTime += args.PausedTime;
|
|
if (component.EndTime != null)
|
|
component.EndTime = component.EndTime.Value + args.PausedTime;
|
|
}
|
|
|
|
/// <summary>
|
|
/// Randomly runs a valid event.
|
|
/// </summary>
|
|
public string RunRandomEvent()
|
|
{
|
|
var randomEvent = PickRandomEvent();
|
|
|
|
if (randomEvent == null)
|
|
{
|
|
var errStr = Loc.GetString("station-event-system-run-random-event-no-valid-events");
|
|
_sawmill.Error(errStr);
|
|
return errStr;
|
|
}
|
|
|
|
var ent = GameTicker.AddGameRule(randomEvent);
|
|
var str = Loc.GetString("station-event-system-run-event",("eventName", ToPrettyString(ent)));
|
|
_sawmill.Info(str);
|
|
return str;
|
|
}
|
|
|
|
/// <summary>
|
|
/// Randomly picks a valid event.
|
|
/// </summary>
|
|
public string? PickRandomEvent()
|
|
{
|
|
var availableEvents = AvailableEvents();
|
|
_sawmill.Info($"Picking from {availableEvents.Count} total available events");
|
|
return FindEvent(availableEvents);
|
|
}
|
|
|
|
/// <summary>
|
|
/// Pick a random event from the available events at this time, also considering their weightings.
|
|
/// </summary>
|
|
/// <returns></returns>
|
|
private string? FindEvent(Dictionary<EntityPrototype, StationEventComponent> availableEvents)
|
|
{
|
|
if (availableEvents.Count == 0)
|
|
{
|
|
_sawmill.Warning("No events were available to run!");
|
|
return null;
|
|
}
|
|
|
|
var sumOfWeights = 0;
|
|
|
|
foreach (var stationEvent in availableEvents.Values)
|
|
{
|
|
sumOfWeights += (int) stationEvent.Weight;
|
|
}
|
|
|
|
sumOfWeights = _random.Next(sumOfWeights);
|
|
|
|
foreach (var (proto, stationEvent) in availableEvents)
|
|
{
|
|
sumOfWeights -= (int) stationEvent.Weight;
|
|
|
|
if (sumOfWeights <= 0)
|
|
{
|
|
return proto.ID;
|
|
}
|
|
}
|
|
|
|
_sawmill.Error("Event was not found after weighted pick process!");
|
|
return null;
|
|
}
|
|
|
|
/// <summary>
|
|
/// Gets the events that have met their player count, time-until start, etc.
|
|
/// </summary>
|
|
/// <param name="ignoreEarliestStart"></param>
|
|
/// <returns></returns>
|
|
private Dictionary<EntityPrototype, StationEventComponent> AvailableEvents(bool ignoreEarliestStart = false)
|
|
{
|
|
var playerCount = _playerManager.PlayerCount;
|
|
|
|
// playerCount does a lock so we'll just keep the variable here
|
|
var currentTime = !ignoreEarliestStart
|
|
? GameTicker.RoundDuration()
|
|
: TimeSpan.Zero;
|
|
|
|
var result = new Dictionary<EntityPrototype, StationEventComponent>();
|
|
|
|
foreach (var (proto, stationEvent) in AllEvents())
|
|
{
|
|
if (CanRun(proto, stationEvent, playerCount, currentTime))
|
|
{
|
|
_sawmill.Debug($"Adding event {proto.ID} to possibilities");
|
|
result.Add(proto, stationEvent);
|
|
}
|
|
}
|
|
|
|
return result;
|
|
}
|
|
|
|
public Dictionary<EntityPrototype, StationEventComponent> AllEvents()
|
|
{
|
|
var allEvents = new Dictionary<EntityPrototype, StationEventComponent>();
|
|
foreach (var prototype in _prototype.EnumeratePrototypes<EntityPrototype>())
|
|
{
|
|
if (prototype.Abstract)
|
|
continue;
|
|
|
|
if (!prototype.TryGetComponent<StationEventComponent>(out var stationEvent))
|
|
continue;
|
|
|
|
allEvents.Add(prototype, stationEvent);
|
|
}
|
|
|
|
return allEvents;
|
|
}
|
|
|
|
private int GetOccurrences(EntityPrototype stationEvent)
|
|
{
|
|
return GetOccurrences(stationEvent.ID);
|
|
}
|
|
|
|
private int GetOccurrences(string stationEvent)
|
|
{
|
|
return GameTicker.AllPreviousGameRules.Count(p => p.Item2 == stationEvent);
|
|
}
|
|
|
|
public TimeSpan TimeSinceLastEvent(EntityPrototype stationEvent)
|
|
{
|
|
foreach (var (time, rule) in GameTicker.AllPreviousGameRules.Reverse())
|
|
{
|
|
if (rule == stationEvent.ID)
|
|
return time;
|
|
}
|
|
|
|
return TimeSpan.Zero;
|
|
}
|
|
|
|
private bool CanRun(EntityPrototype prototype, StationEventComponent stationEvent, int playerCount, TimeSpan currentTime)
|
|
{
|
|
if (GameTicker.IsGameRuleActive(prototype.ID))
|
|
return false;
|
|
|
|
if (stationEvent.MaxOccurrences.HasValue && GetOccurrences(prototype) >= stationEvent.MaxOccurrences.Value)
|
|
{
|
|
return false;
|
|
}
|
|
|
|
if (playerCount < stationEvent.MinimumPlayers)
|
|
{
|
|
return false;
|
|
}
|
|
|
|
if (currentTime != TimeSpan.Zero && currentTime.TotalMinutes < stationEvent.EarliestStart)
|
|
{
|
|
return false;
|
|
}
|
|
|
|
var lastRun = TimeSinceLastEvent(prototype);
|
|
if (lastRun != TimeSpan.Zero && currentTime.TotalMinutes <
|
|
stationEvent.ReoccurrenceDelay + lastRun.TotalMinutes)
|
|
{
|
|
return false;
|
|
}
|
|
|
|
// Nyano - Summary: - Begin modified code block: check for glimmer events.
|
|
// This could not be cleanly done anywhere else.
|
|
if (_configurationManager.GetCVar(CCVars.GlimmerEnabled) &&
|
|
prototype.TryGetComponent<GlimmerEventComponent>(out var glimmerEvent) &&
|
|
(_glimmerSystem.Glimmer < glimmerEvent.MinimumGlimmer ||
|
|
_glimmerSystem.Glimmer > glimmerEvent.MaximumGlimmer))
|
|
{
|
|
return false;
|
|
}
|
|
// Nyano - End modified code block.
|
|
|
|
return true;
|
|
}
|
|
}
|