using System.Numerics; using Robust.Client.AutoGenerated; using Robust.Client.Graphics; using Robust.Client.UserInterface; using Robust.Client.UserInterface.Controls; using Robust.Client.UserInterface.XAML; using Robust.Client.Utility; using Robust.Shared.Input; using Serilog; namespace Content.Client._CP14.UserInterface.Systems.NodeTree; [GenerateTypedNameReferences] public sealed partial class CP14NodeTreeGraphControl : BoxContainer { private const float LocalUIScaleMax = 4f; private const float LocalUIScaleMin = 1f; [Dependency] private readonly IEntityManager _entManager = default!; private CP14NodeTreeUiState? _state; private CP14NodeTreeElement? _hoveredNode; private CP14NodeTreeElement? _selectedNode; private Dictionary _nodeDict = new(); private bool _dragging = false; private Vector2 _previousMousePosition = Vector2.Zero; private Vector2 _globalOffset = new (60,60); private float _localUIScale = LocalUIScaleMin; private float Scale => UIScale * _localUIScale; public event Action? OnNodeSelected; public event Action? OnOffsetChanged; public CP14NodeTreeGraphControl() { IoCManager.InjectDependencies(this); RobustXamlLoader.Load(this); OnOffsetChanged?.Invoke(_globalOffset); } public void UpdateState(CP14NodeTreeUiState state) { _state = state; _nodeDict.Clear(); foreach (var node in state.Nodes) { _nodeDict[node.NodeKey] = node; } OnOffsetChanged?.Invoke(_globalOffset); } protected override void MouseWheel(GUIMouseWheelEventArgs args) { base.MouseWheel(args); if (args.Handled) return; _localUIScale = MathHelper.Clamp(_localUIScale + 0.1f * args.Delta.Y, LocalUIScaleMin, LocalUIScaleMax); } protected override void KeyBindDown(GUIBoundKeyEventArgs args) { base.KeyBindDown(args); if (args.Handled) return; if (args.Function == EngineKeyFunctions.UIClick) { _dragging = true; if (_hoveredNode == null) return; OnNodeSelected?.Invoke(_hoveredNode); UserInterfaceManager.ClickSound(); _selectedNode = _hoveredNode; } if (args.Function == EngineKeyFunctions.UIRightClick) { _globalOffset = new Vector2(60, 60); // Reset offset on right click _localUIScale = LocalUIScaleMin; OnOffsetChanged?.Invoke(_globalOffset); } } protected override void KeyBindUp(GUIBoundKeyEventArgs args) { base.KeyBindUp(args); if (args.Handled || args.Function != EngineKeyFunctions.UIClick) return; _dragging = false; } protected override void ExitedTree() { base.ExitedTree(); OnNodeSelected?.Invoke(null); } protected override void Draw(DrawingHandleScreen handle) { base.Draw(handle); if (_state is null) return; _hoveredNode = null; var cursor = (UserInterfaceManager.MousePositionScaled.Position * UIScale) - GlobalPixelPosition; if (_dragging) { var delta = cursor - _previousMousePosition; _globalOffset += delta; OnOffsetChanged?.Invoke(_globalOffset); } _previousMousePosition = cursor; //Draw connection lines foreach (var edge in _state.Edges) { if (!_nodeDict.ContainsKey(edge.Item1) || !_nodeDict.ContainsKey(edge.Item2)) continue; var node1 = _nodeDict[edge.Item1]; var node2 = _nodeDict[edge.Item2]; var fromPos = node1.UiPosition * Scale + _globalOffset; var toPos = node2.UiPosition * Scale + _globalOffset; var lineColor = node1.Gained || node2.Gained ? _state.ActiveLineColor : _state.LineColor; handle.DrawLine(fromPos, toPos, lineColor); } //Draw Node icons over lines foreach (var node in _state.Nodes) { var pos = node.UiPosition * Scale + _globalOffset; // Frame var frameTexture = _state.FrameIcon.Frame0(); var frameSize = new Vector2(frameTexture.Width, frameTexture.Height) * 1.5f * Scale; var frameQuad = new UIBox2(pos - frameSize / 2, pos + frameSize / 2); handle.DrawTextureRect(frameTexture, frameQuad); // Selected Node if (_selectedNode == node) { var selectedTexture = _state.SelectedIcon.Frame0(); var selectedSize = new Vector2(selectedTexture.Width, selectedTexture.Height) * 1.5f * Scale; var selectedQuad = new UIBox2(pos - selectedSize / 2, pos + selectedSize / 2); handle.DrawTextureRect(selectedTexture, selectedQuad); } // Hovered Node var hovered = (cursor - pos).LengthSquared() <= (frameSize.X / 2) * (frameSize.X / 2); if (hovered) { _hoveredNode = node; var hoveredTexture = _state.HoveredIcon.Frame0(); var hoveredSize = new Vector2(hoveredTexture.Width, hoveredTexture.Height) * 1.5f * Scale; var hoveredQuad = new UIBox2(pos - hoveredSize / 2, pos + hoveredSize / 2); handle.DrawTextureRect(hoveredTexture, hoveredQuad); } // Learned Node if (node.Gained) { var learnedTexture = _state.LearnedIcon.Frame0(); var learnedSize = new Vector2(learnedTexture.Width, learnedTexture.Height) * 1.5f * Scale; var learnedQuad = new UIBox2(pos - learnedSize / 2, pos + learnedSize / 2); handle.DrawTextureRect(learnedTexture, learnedQuad); } // Base Node icon if (node.Icon is not null) { var baseTexture = node.Icon.Frame0(); var baseSize = new Vector2(baseTexture.Width, baseTexture.Height) * 1.5f * Scale; var baseQuad = new UIBox2(pos - baseSize / 2, pos + baseSize / 2); var tint = node.Gained || node.Active ? Color.White : Color.FromSrgb(new Color(0.6f, 0.6f, 0.6f)); handle.DrawTextureRect(baseTexture, baseQuad, tint); } } } }