using System.Text; using Content.Client.Message; using Content.Client.Resources; using Content.Client.UserInterface.Controls; using Content.Client.Xenoarchaeology.Artifact; using Content.Client.Xenoarchaeology.Equipment; using Content.Shared.Xenoarchaeology.Artifact.Components; using Content.Shared.Xenoarchaeology.Equipment.Components; using Robust.Client.Audio; using Robust.Client.AutoGenerated; using Robust.Client.Graphics; using Robust.Client.ResourceManagement; using Robust.Client.UserInterface.Controls; using Robust.Client.UserInterface.XAML; using Robust.Shared.Audio; using Robust.Shared.Configuration; using Robust.Shared.Timing; using Robust.Shared.Utility; namespace Content.Client.Xenoarchaeology.Ui; [GenerateTypedNameReferences] public sealed partial class AnalysisConsoleMenu : FancyWindow { private static readonly TimeSpan ExtractInfoDisplayForDuration = TimeSpan.FromSeconds(3); [Dependency] private readonly IEntityManager _ent = default!; [Dependency] private readonly IResourceCache _resCache = default!; [Dependency] private readonly IGameTiming _timing = default!; private readonly ArtifactAnalyzerSystem _artifactAnalyzer; private readonly XenoArtifactSystem _xenoArtifact; private readonly AudioSystem _audio; private readonly MetaDataSystem _meta = default!; private Entity _owner; private Entity? _currentNode; private TimeSpan? _hideExtractInfoIn; private int _extractionSum; public event Action? OnServerSelectionButtonPressed; public event Action? OnExtractButtonPressed; public AnalysisConsoleMenu() { RobustXamlLoader.Load(this); IoCManager.InjectDependencies(this); _xenoArtifact = _ent.System(); _artifactAnalyzer = _ent.System(); _audio = _ent.System(); _meta = _ent.System(); if (BackPanel.PanelOverride is StyleBoxTexture tex) tex.Texture = _resCache.GetTexture("/Textures/Interface/Nano/button.svg.96dpi.png"); GraphControl.OnNodeSelected += node => { _currentNode = node; SetSelectedNode(node); }; ServerButton.OnPressed += _ => { OnServerSelectionButtonPressed?.Invoke(); }; ExtractButton.OnPressed += StartExtract; } /// /// Set entity that corresponds analysis console, for which window is opened. /// Closes window if is not present on entity. /// public void SetOwner(EntityUid owner) { if (!_ent.TryGetComponent(owner, out var comp)) { Close(); return; } _owner = (owner, comp); Update(_owner); } private void StartExtract(BaseButton.ButtonEventArgs obj) { if (!_artifactAnalyzer.TryGetArtifactFromConsole(_owner, out var artifact)) return; ExtractContainer.Visible = true; NodeViewContainer.Visible = false; _extractionSum = 0; var extractionMessage = new FormattedMessage(); var nodes = _xenoArtifact.GetAllNodes(artifact.Value); var count = 0; foreach (var node in nodes) { var pointValue = _xenoArtifact.GetResearchValue(node); if (pointValue <= 0) continue; count++; var nodeId = _xenoArtifact.GetNodeId(node); var text = Loc.GetString("analysis-console-extract-value", ("id", nodeId), ("value", pointValue)); extractionMessage.AddMarkupOrThrow(text); extractionMessage.PushNewline(); } if (count == 0) extractionMessage.AddMarkupOrThrow(Loc.GetString("analysis-console-extract-none")); _hideExtractInfoIn = _timing.CurTime + ExtractInfoDisplayForDuration; ExtractionResearchLabel.SetMessage(extractionMessage); ExtractionSumLabel.SetMarkup(Loc.GetString("analysis-console-extract-sum", ("value", _extractionSum))); _audio.PlayGlobal(_owner.Comp.ScanFinishedSound, _owner, AudioParams.Default.WithVolume(1f)); OnExtractButtonPressed?.Invoke(); } protected override void FrameUpdate(FrameEventArgs args) { base.FrameUpdate(args); if (_hideExtractInfoIn == null || _timing.CurTime + _meta.GetPauseTime(_owner) < _hideExtractInfoIn) return; ExtractContainer.Visible = false; NodeViewContainer.Visible = true; _hideExtractInfoIn = null; } public void Update(Entity ent) { _artifactAnalyzer.TryGetArtifactFromConsole(ent, out var arti); ArtifactView.SetEntity(arti); GraphControl.SetArtifact(arti); ExtractButton.Disabled = arti == null; if (arti == null) NoneSelectedLabel.Visible = false; NoArtiLabel.Visible = true; if (!_artifactAnalyzer.TryGetAnalyzer(ent, out _)) NoArtiLabel.Text = Loc.GetString("analysis-console-info-no-scanner"); else if (arti == null) NoArtiLabel.Text = Loc.GetString("analysis-console-info-no-artifact"); else NoArtiLabel.Visible = false; if (_currentNode == null || arti == null || !_xenoArtifact.TryGetIndex((arti.Value, arti.Value), _currentNode.Value, out _)) { SetSelectedNode(null); } } public void SetSelectedNode(Entity? node) { InfoContainer.Visible = node != null; if (!_artifactAnalyzer.TryGetArtifactFromConsole(_owner, out var artifact)) return; NoneSelectedLabel.Visible = node == null; if (node == null) return; var nodeId = _xenoArtifact.GetNodeId(node.Value); IDValueLabel.SetMarkup(Loc.GetString("analysis-console-info-id-value", ("id", nodeId))); // If active, state is 2. else, it is 0 or 1 based on whether it is unlocked, or not. int lockedState; if (_xenoArtifact.IsNodeActive(artifact.Value, node.Value)) lockedState = 2; else lockedState = node.Value.Comp.Locked ? 0 : 1; LockedValueLabel.SetMarkup(Loc.GetString("analysis-console-info-locked-value", ("state", lockedState))); // Frontier: handle zero max durability if (node.Value.Comp.MaxDurability <= 1) { DurabilityValueLabel.SetMarkup(Loc.GetString("analysis-console-info-durability-triggered", ("current", node.Value.Comp.Durability))); } else { var percent = node.Value.Comp.MaxDurability <= 0 ? 0f : (float)node.Value.Comp.Durability / node.Value.Comp.MaxDurability; var color = percent switch { >= 0.75f => Color.Lime, >= 0.50f => Color.Yellow, _ => Color.Red }; DurabilityValueLabel.SetMarkup(Loc.GetString("analysis-console-info-durability-value", ("color", color), ("current", node.Value.Comp.Durability), ("max", node.Value.Comp.MaxDurability))); } // End Frontier var hasInfo = _xenoArtifact.HasUnlockedPredecessor(artifact.Value, node.Value); // Frontier: hide xenoarch effects var description = (lockedState == 0) ? Loc.GetString("analysis-console-info-effect-unknown") : _ent.GetComponentOrNull(node.Value)?.EntityDescription ?? string.Empty; EffectValueLabel.SetMarkup(Loc.GetString("analysis-console-info-effect-value", ("state", hasInfo), ("info", description))); // End Frontier: hide xenoarch effects var predecessorNodes = _xenoArtifact.GetPredecessorNodes(artifact.Value.Owner, node.Value); if (!hasInfo) { TriggerValueLabel.SetMarkup(Loc.GetString("analysis-console-info-effect-value", ("state", false))); } else { var triggerStr = new StringBuilder(); triggerStr.Append("- "); triggerStr.Append(Loc.GetString(node.Value.Comp.TriggerTip!)); foreach (var predecessor in predecessorNodes) { triggerStr.AppendLine(); triggerStr.Append("- "); triggerStr.Append(Loc.GetString(predecessor.Comp.TriggerTip!)); } TriggerValueLabel.SetMarkup(Loc.GetString("analysis-console-info-triggered-value", ("triggers", triggerStr.ToString()))); } ClassValueLabel.SetMarkup(Loc.GetString("analysis-console-info-class-value", ("class", Loc.GetString($"artifact-node-nf-class-{Math.Min(8, predecessorNodes.Count + 1)}")))); // Frontier: add nf-, 6<8 } }