#nullable enable using Godot; namespace ShrinkTutorial.Godot; public partial class ShrinkGodotTutorialOverlay : Control { private readonly Label _title = new(); private readonly Label _body = new(); private ShrinkTutorialRunner? _runner; private Control? _target; public override void _Ready() { SetAnchorsAndOffsetsPreset(LayoutPreset.FullRect); MouseFilter = MouseFilterEnum.Stop; var panel = new PanelContainer { CustomMinimumSize = new Vector2(320, 100) }; panel.Position = new Vector2(24, 24); var text = new VBoxContainer(); text.AddChild(_title); text.AddChild(_body); panel.AddChild(text); AddChild(panel); } public void Bind(ShrinkTutorialRunner runner) { if (_runner != null) _runner.StepChanged -= ShowStep; _runner = runner; _runner.StepChanged += ShowStep; if (_runner.CurrentStep != null) ShowStep(_runner.CurrentStep); } public override void _ExitTree() { if (_runner != null) _runner.StepChanged -= ShowStep; _runner = null; _target = null; } public override void _Process(double delta) { if (_runner?.CurrentStep is not { } step) return; _target = ResolveTarget(step); QueueRedraw(); } public override void _Draw() { var viewport = GetViewportRect(); if (_target == null) { DrawRect(viewport, new Color(0, 0, 0, 0.72f)); return; } var hole = _target.GetGlobalRect().Grow(8); var shade = new Color(0, 0, 0, 0.72f); DrawRect(new Rect2(viewport.Position, new Vector2(viewport.Size.X, hole.Position.Y)), shade); DrawRect(new Rect2(new Vector2(0, hole.End.Y), new Vector2(viewport.Size.X, viewport.Size.Y - hole.End.Y)), shade); DrawRect(new Rect2(new Vector2(0, hole.Position.Y), new Vector2(hole.Position.X, hole.Size.Y)), shade); DrawRect(new Rect2(new Vector2(hole.End.X, hole.Position.Y), new Vector2(viewport.Size.X - hole.End.X, hole.Size.Y)), shade); } public override void _GuiInput(InputEvent @event) { if (@event is not InputEventMouseButton { Pressed: true }) return; var step = _runner?.CurrentStep; if (step == null) return; if (step.CompleteCondition == ShrinkTutorialCompleteCondition.AnyClick || step.CompleteCondition == ShrinkTutorialCompleteCondition.ClickTarget && _target?.GetGlobalRect().HasPoint(GetGlobalMousePosition()) == true) _runner!.CompleteStep(); AcceptEvent(); } private void ShowStep(ShrinkTutorialStep step) { _title.Text = step.Title; _body.Text = step.Body; Visible = true; } private Control? ResolveTarget(ShrinkTutorialStep step) { if (step.TargetMode == ShrinkTutorialTargetMode.Path) return GetTree().Root.GetNodeOrNull(step.Target); if (step.TargetMode == ShrinkTutorialTargetMode.AnchorId) return GetTree().GetFirstNodeInGroup("shrink_tutorial_" + step.Target) as Control; return null; } }