using System; using System.Collections.Generic; using System.Linq; using Content.Server.Chat.Managers; using Content.Server.GameTicking; using Content.Server.Maps; using Content.Shared.Roles; using Content.Shared.Station; using Robust.Shared.GameObjects; using Robust.Shared.IoC; using Robust.Shared.Log; using Robust.Shared.Utility; namespace Content.Server.Station; /// /// System that manages the jobs available on a station, and maybe other things later. /// public sealed class StationSystem : EntitySystem { [Dependency] private GameTicker _gameTicker = default!; [Dependency] private IChatManager _chatManager = default!; [Dependency] private IGameMapManager _gameMapManager = default!; private uint _idCounter = 1; private Dictionary _stationInfo = new(); /// /// List of stations for the current round. /// public IReadOnlyDictionary StationInfo => _stationInfo; public override void Initialize() { base.Initialize(); SubscribeLocalEvent(OnRoundEnd); } /// /// Cleans up station info. /// private void OnRoundEnd(GameRunLevelChangedEvent eventArgs) { if (eventArgs.New == GameRunLevel.PreRoundLobby) _stationInfo = new(); } public sealed class StationInfoData { public string Name; /// /// Job list associated with the game map. /// public readonly GameMapPrototype MapPrototype; /// /// The round job list. /// private readonly Dictionary _jobList; public IReadOnlyDictionary JobList => _jobList; public StationInfoData(string name, GameMapPrototype mapPrototype, Dictionary jobList) { Name = name; MapPrototype = mapPrototype; _jobList = jobList; } public bool TryAssignJob(string jobName) { if (_jobList.ContainsKey(jobName)) { switch (_jobList[jobName]) { case > 0: _jobList[jobName]--; return true; case -1: return true; default: return false; } } else { return false; } } public bool AdjustJobAmount(string jobName, int amount) { DebugTools.Assert(amount >= -1); _jobList[jobName] = amount; return true; } } /// /// Creates a new station and attaches it to the given grid. /// /// grid to attach to /// game map prototype of the station /// name of the station to assign, if not the default /// optional grid component of the grid. /// The ID of the resulting station /// Thrown when the given entity is not a grid. public StationId InitialSetupStationGrid(EntityUid mapGrid, GameMapPrototype mapPrototype, string? stationName = null, IMapGridComponent? gridComponent = null) { if (!Resolve(mapGrid, ref gridComponent)) throw new ArgumentException("Tried to initialize a station on a non-grid entity!"); var jobListDict = mapPrototype.AvailableJobs.ToDictionary(x => x.Key, x => x.Value[1]); var id = AllocateStationInfo(); _stationInfo[id] = new StationInfoData(stationName ?? _gameMapManager.GenerateMapName(mapPrototype), mapPrototype, jobListDict); var station = EntityManager.AddComponent(mapGrid); station.Station = id; _gameTicker.UpdateJobsAvailable(); // new station means new jobs, tell any lobby-goers. Logger.InfoS("stations", $"Setting up new {mapPrototype.ID} called {_stationInfo[id].Name} on grid {mapGrid}:{gridComponent.GridIndex}"); return id; } /// /// Adds the given grid to the given station. /// /// grid to attach /// station to attach the grid to /// optional grid component of the grid. /// Thrown when the given entity is not a grid. public void AddGridToStation(EntityUid mapGrid, StationId station, IMapGridComponent? gridComponent = null) { if (!Resolve(mapGrid, ref gridComponent)) throw new ArgumentException("Tried to initialize a station on a non-grid entity!"); var stationComponent = EntityManager.AddComponent(mapGrid); stationComponent.Station = station; Logger.InfoS("stations", $"Adding grid {mapGrid}:{gridComponent.GridIndex} to station {station} named {_stationInfo[station].Name}"); } /// /// Attempts to assign a job on the given station. /// Does NOT inform the gameticker that the job roster has changed. /// /// station to assign to /// name of the job /// assignment success public bool TryAssignJobToStation(StationId stationId, JobPrototype job) { if (stationId != StationId.Invalid) return _stationInfo[stationId].TryAssignJob(job.ID); else return false; } /// /// Checks if the given job is available. /// /// station to check /// name of the job /// job availability public bool IsJobAvailableOnStation(StationId stationId, JobPrototype job) { if (_stationInfo[stationId].JobList.TryGetValue(job.ID, out var amount)) return amount != 0; return false; } private StationId AllocateStationInfo() { return new StationId(_idCounter++); } public bool AdjustJobsAvailableOnStation(StationId stationId, JobPrototype job, int amount) { var ret = _stationInfo[stationId].AdjustJobAmount(job.ID, amount); _gameTicker.UpdateJobsAvailable(); return ret; } public void RenameStation(StationId stationId, string name, bool loud = true) { var oldName = _stationInfo[stationId].Name; _stationInfo[stationId].Name = name; if (loud) { _chatManager.DispatchStationAnnouncement($"The station {oldName} has been renamed to {name}."); } // Make sure lobby gets the memo. _gameTicker.UpdateJobsAvailable(); } }