using Content.Server.Cargo.Components; using Content.Server.Salvage.Expeditions; using Content.Server.Salvage.Expeditions.Structure; using Content.Shared.CCVar; using Content.Shared.Examine; using Content.Shared.Salvage; using Robust.Shared.CPUJob.JobQueues; using Robust.Shared.CPUJob.JobQueues.Queues; using System.Linq; using System.Threading; using Content.Shared.Procedural; using Content.Shared.Salvage.Expeditions; using Robust.Shared.GameStates; 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(OnDataUnpaused); SubscribeLocalEvent(OnExpeditionShutdown); SubscribeLocalEvent(OnExpeditionUnpaused); SubscribeLocalEvent(OnExpeditionGetState); SubscribeLocalEvent(OnStructureExamine); _cooldown = _configurationManager.GetCVar(CCVars.SalvageExpeditionCooldown); _configurationManager.OnValueChanged(CCVars.SalvageExpeditionCooldown, SetCooldownChange); } private void OnExpeditionGetState(EntityUid uid, SalvageExpeditionComponent component, ref ComponentGetState args) { args.State = new SalvageExpeditionComponentState() { Stage = component.Stage }; } private void ShutdownExpeditions() { _configurationManager.UnsubValueChanged(CCVars.SalvageExpeditionCooldown, SetCooldownChange); } 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 OnExpeditionShutdown(EntityUid uid, SalvageExpeditionComponent component, ComponentShutdown args) { component.Stream?.Stop(); 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(data, uid); } } private void OnDataUnpaused(EntityUid uid, SalvageExpeditionDataComponent component, ref EntityUnpausedEvent args) { component.NextOffer += args.PausedTime; } private void OnExpeditionUnpaused(EntityUid uid, SalvageExpeditionComponent component, ref EntityUnpausedEvent args) { component.EndTime += args.PausedTime; } 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; } } foreach (var comp in EntityQuery()) { // Update offers if (comp.NextOffer > currentTime || comp.Claimed) continue; comp.Cooldown = false; comp.NextOffer += TimeSpan.FromSeconds(_cooldown); GenerateMissions(comp); UpdateConsoles(comp); } } private void FinishExpedition(SalvageExpeditionDataComponent component, EntityUid uid) { component.NextOffer = _timing.CurTime + TimeSpan.FromSeconds(_cooldown); Announce(uid, Loc.GetString("salvage-expedition-mission-completed")); component.ActiveMission = 0; component.Cooldown = true; UpdateConsoles(component); } 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) { var cancelToken = new CancellationTokenSource(); var job = new SpawnSalvageMissionJob( SalvageJobTime, EntityManager, _timing, _logManager, _mapManager, _prototypeManager, _anchorable, _biome, _dungeon, _metaData, station, 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")); } }