using System.Linq; using System.Threading; using Content.Server.Salvage.Expeditions; using Content.Server.Salvage.Expeditions.Structure; using Content.Shared.CCVar; using Content.Shared.Examine; using Content.Shared.Random.Helpers; using Content.Shared.Salvage.Expeditions; using Robust.Shared.Audio; using Robust.Shared.CPUJob.JobQueues; using Robust.Shared.CPUJob.JobQueues.Queues; using Robust.Shared.GameStates; using Robust.Shared.Map; namespace Content.Server.Salvage; public sealed partial class SalvageSystem { /* * Handles setup / teardown of salvage expeditions. */ private const int MissionLimit = 3; private readonly JobQueue _salvageQueue = new(); private readonly List<(SpawnSalvageMissionJob Job, CancellationTokenSource CancelToken)> _salvageJobs = new(); private const double SalvageJobTime = 0.002; private float _cooldown; private void InitializeExpeditions() { SubscribeLocalEvent(OnSalvageConsoleInit); SubscribeLocalEvent(OnSalvageConsoleParent); SubscribeLocalEvent(OnSalvageClaimMessage); SubscribeLocalEvent(OnExpeditionMapInit); SubscribeLocalEvent(OnExpeditionShutdown); SubscribeLocalEvent(OnExpeditionGetState); SubscribeLocalEvent(OnStructureExamine); _cooldown = _configurationManager.GetCVar(CCVars.SalvageExpeditionCooldown); Subs.CVar(_configurationManager, CCVars.SalvageExpeditionCooldown, SetCooldownChange); } private void OnExpeditionGetState(EntityUid uid, SalvageExpeditionComponent component, ref ComponentGetState args) { args.State = new SalvageExpeditionComponentState() { Stage = component.Stage }; } private void SetCooldownChange(float obj) { // Update the active cooldowns if we change it. var diff = obj - _cooldown; var query = AllEntityQuery(); while (query.MoveNext(out var comp)) { comp.NextOffer += TimeSpan.FromSeconds(diff); } _cooldown = obj; } private void OnExpeditionMapInit(EntityUid uid, SalvageExpeditionComponent component, MapInitEvent args) { component.SelectedSong = _audio.ResolveSound(component.Sound); } private void OnExpeditionShutdown(EntityUid uid, SalvageExpeditionComponent component, ComponentShutdown args) { component.Stream = _audio.Stop(component.Stream); foreach (var (job, cancelToken) in _salvageJobs.ToArray()) { if (job.Station == component.Station) { cancelToken.Cancel(); _salvageJobs.Remove((job, cancelToken)); } } if (Deleted(component.Station)) return; // Finish mission if (TryComp(component.Station, out var data)) { FinishExpedition((component.Station, data), uid); } } private void UpdateExpeditions() { var currentTime = _timing.CurTime; _salvageQueue.Process(); foreach (var (job, cancelToken) in _salvageJobs.ToArray()) { switch (job.Status) { case JobStatus.Finished: _salvageJobs.Remove((job, cancelToken)); break; } } var query = EntityQueryEnumerator(); while (query.MoveNext(out var uid, out var comp)) { // Update offers if (comp.NextOffer > currentTime || comp.Claimed) continue; comp.Cooldown = false; comp.NextOffer += TimeSpan.FromSeconds(_cooldown); GenerateMissions(comp); UpdateConsoles((uid, comp)); } } private void FinishExpedition(Entity expedition, EntityUid uid) { var component = expedition.Comp; component.NextOffer = _timing.CurTime + TimeSpan.FromSeconds(_cooldown); Announce(uid, Loc.GetString("salvage-expedition-mission-completed")); component.ActiveMission = 0; component.Cooldown = true; UpdateConsoles(expedition); } private void GenerateMissions(SalvageExpeditionDataComponent component) { component.Missions.Clear(); for (var i = 0; i < MissionLimit; i++) { var mission = new SalvageMissionParams { Index = component.NextIndex, Seed = _random.Next(), Difficulty = "Moderate", }; component.Missions[component.NextIndex++] = mission; } } private SalvageExpeditionConsoleState GetState(SalvageExpeditionDataComponent component) { var missions = component.Missions.Values.ToList(); return new SalvageExpeditionConsoleState(component.NextOffer, component.Claimed, component.Cooldown, component.ActiveMission, missions); } private void SpawnMission(SalvageMissionParams missionParams, EntityUid station, EntityUid? coordinatesDisk) { var cancelToken = new CancellationTokenSource(); var job = new SpawnSalvageMissionJob( SalvageJobTime, EntityManager, _timing, _logManager, _mapManager, _prototypeManager, _anchorable, _biome, _dungeon, _metaData, _transform, _mapSystem, station, coordinatesDisk, missionParams, cancelToken.Token); _salvageJobs.Add((job, cancelToken)); _salvageQueue.EnqueueJob(job); } private void OnStructureExamine(EntityUid uid, SalvageStructureComponent component, ExaminedEvent args) { args.PushMarkup(Loc.GetString("salvage-expedition-structure-examine")); } }