diff --git a/project.godot b/project.godot index 7600f5e..e5dd0be 100644 --- a/project.godot +++ b/project.godot @@ -11,7 +11,7 @@ config_version=5 [application] config/name="connect4-moderator-observer" -run/main_scene="uid://cr8fi0e4r88s8" +run/main_scene="uid://dcx5nvs0pa7me" config/features=PackedStringArray("4.5", "C#", "Forward Plus") config/icon="uid://ckmfi0cjgxgyk" diff --git a/scenes/board_screen.tscn b/scenes/board_screen.tscn index bb74e96..9aca32c 100644 --- a/scenes/board_screen.tscn +++ b/scenes/board_screen.tscn @@ -1,6 +1,7 @@ -[gd_scene load_steps=13 format=3 uid="uid://m542qwlp7hl7"] +[gd_scene load_steps=14 format=3 uid="uid://m542qwlp7hl7"] [ext_resource type="Script" uid="uid://dg5jt0o0r0v3r" path="res://scripts/BoardScreen.cs" id="1_b3w8x"] +[ext_resource type="PackedScene" uid="uid://rl33x81cxlh0" path="res://scenes/bracket_view.tscn" id="2_u1oi2"] [ext_resource type="Texture2D" uid="uid://dlx02qat7j6lf" path="res://assets/sprites/AssetTileset.png" id="3_1tlhv"] [ext_resource type="FontFile" uid="uid://c3jmev24lo6ci" path="res://assets/fonts/PixelOperator8.ttf" id="3_rjcmr"] [ext_resource type="Texture2D" uid="uid://ckmfi0cjgxgyk" path="res://assets/sprites/RedChip.png" id="4_1hrcj"] @@ -30,8 +31,10 @@ sources/0 = SubResource("TileSetAtlasSource_i2o8i") atlas = ExtResource("3_1tlhv") region = Rect2(112, 32, 16, 16) -[node name="BoardScreen" type="Node2D"] +[node name="BoardScreen" type="Node2D" node_paths=PackedStringArray("BackButton")] script = ExtResource("1_b3w8x") +BracketScene = ExtResource("2_u1oi2") +BackButton = NodePath("BracketButton") [node name="Floor Collider" type="StaticBody2D" parent="."] position = Vector2(0, 200) diff --git a/scenes/bracket_view.tscn b/scenes/bracket_view.tscn index 77b028f..7b399c2 100644 --- a/scenes/bracket_view.tscn +++ b/scenes/bracket_view.tscn @@ -1,8 +1,9 @@ -[gd_scene load_steps=4 format=3 uid="uid://rl33x81cxlh0"] +[gd_scene load_steps=5 format=3 uid="uid://rl33x81cxlh0"] [ext_resource type="Script" uid="uid://dm25u0a2lqk2x" path="res://scripts/BracketScene.cs" id="1_dvj3m"] [ext_resource type="Texture2D" uid="uid://ckmfi0cjgxgyk" path="res://assets/sprites/RedChip.png" id="2_7c11m"] [ext_resource type="PackedScene" uid="uid://cr8fi0e4r88s8" path="res://scenes/game.tscn" id="3_1511b"] +[ext_resource type="Script" uid="uid://1y72woiynf31" path="res://scripts/AdminControls.cs" id="4_mbqc8"] [node name="BracketView" type="Control" node_paths=PackedStringArray("Players", "Matches")] layout_mode = 3 @@ -17,9 +18,36 @@ Matches = NodePath("HBoxContainer/MatchList") JoinButton = ExtResource("2_7c11m") BoardScene = ExtResource("3_1511b") +[node name="ColorRect" type="ColorRect" parent="."] +custom_minimum_size = Vector2(0, 36) +layout_mode = 1 +anchors_preset = 10 +anchor_right = 1.0 +grow_horizontal = 2 +color = Color(0.13319641, 0.13319641, 0.13319638, 1) + +[node name="AdminControls" type="HBoxContainer" parent="ColorRect" node_paths=PackedStringArray("BecomeAdmin", "StartTournament")] +custom_minimum_size = Vector2(0, 36) +layout_mode = 1 +anchors_preset = 10 +anchor_right = 1.0 +offset_bottom = 36.0 +grow_horizontal = 2 +script = ExtResource("4_mbqc8") +BecomeAdmin = NodePath("BecomeAdmin") +StartTournament = NodePath("StartTournament") + +[node name="BecomeAdmin" type="Button" parent="ColorRect/AdminControls"] +layout_mode = 2 +text = "Become Admin" + +[node name="StartTournament" type="Button" parent="ColorRect/AdminControls"] +layout_mode = 2 +text = "Start Tournament" + [node name="HBoxContainer" type="HBoxContainer" parent="."] layout_mode = 1 -anchors_preset = 15 +anchors_preset = -1 anchor_right = 1.0 anchor_bottom = 1.0 offset_top = 36.0 diff --git a/scripts/AdminControls.cs b/scripts/AdminControls.cs new file mode 100644 index 0000000..6091613 --- /dev/null +++ b/scripts/AdminControls.cs @@ -0,0 +1,123 @@ +using Godot; +using System; +using System.Collections.Generic; + +public partial class AdminControls : HBoxContainer +{ + [Export] public Button BecomeAdmin; + [Export] public Button StartTournament; + + public override void _Ready() + { + Connection.Instance.OnBecomeAdmin += OnBecomeAdmin; + Connection.Instance.OnTournamentEnd += OnEndTournament; + + StartTournament.Pressed += StartTournamentCommand; + if (!Connection.Instance.IsAdmin || Connection.Instance.ActiveTournament) + { + StartTournament.Hide(); + } + + BecomeAdmin.Pressed += ShowAuthPopup; + } + + public override void _ExitTree() + { + Connection.Instance.OnBecomeAdmin -= OnBecomeAdmin; + Connection.Instance.OnTournamentEnd -= OnEndTournament; + } + + private void StartTournamentCommand() + { + Connection.Instance.StartTournament(); + } + + private void OnEndTournament(List<(string, int)> playerScoreboard) + { + StartTournament.Show(); + ShowTournamentScoreboard(playerScoreboard); + } + + private void ShowAuthPopup() + { + var authWindow = new Window(); + authWindow.AlwaysOnTop = true; + authWindow.MaximizeDisabled = true; + authWindow.Unresizable = true; + authWindow.InitialPosition = Window.WindowInitialPosition.CenterMainWindowScreen; + authWindow.Size = new Vector2I(256, 128); + authWindow.CloseRequested += () => + { + GetTree().Root.RemoveChild(authWindow); + }; + + var vbox = new VBoxContainer(); + vbox.LayoutMode = 1; + vbox.AnchorBottom = 1.0f; + vbox.AnchorRight = 1.0f; + vbox.GrowHorizontal = GrowDirection.Both; + vbox.GrowVertical = GrowDirection.Both; + vbox.Alignment = AlignmentMode.Center; + + var passwordBox = new TextEdit(); + passwordBox.PlaceholderText = "Password"; + passwordBox.SetCustomMinimumSize(new Vector2(32, 32)); + + var button = new Button(); + button.Text = "Login"; + button.Pressed += () => + { + Connection.Instance.AdminAuth(passwordBox.Text); + GetTree().Root.RemoveChild(authWindow); + }; + + vbox.AddChild(passwordBox); + vbox.AddChild(button); + + authWindow.AddChild(vbox); + + GetTree().Root.AddChild(authWindow); + } + + private void ShowTournamentScoreboard(List<(string, int)> playerScoreboard) + { + var scoreboardWindow = new Window(); + scoreboardWindow.AlwaysOnTop = true; + scoreboardWindow.MaximizeDisabled = true; + scoreboardWindow.Unresizable = true; + scoreboardWindow.InitialPosition = Window.WindowInitialPosition.CenterMainWindowScreen; + scoreboardWindow.Size = new Vector2I(256, 512); + scoreboardWindow.CloseRequested += () => + { + GetTree().Root.RemoveChild(scoreboardWindow); + }; + + var tree = new Tree(); + tree.HideRoot = true; + tree.Columns = 2; + tree.ColumnTitlesVisible = true; + tree.SetColumnTitle(0, "Player"); + tree.SetColumnTitle(1, "Score"); + var root = tree.CreateItem(); + + foreach ((string, int) entry in playerScoreboard) + { + var item = tree.CreateItem(root); + item.SetText(0, entry.Item1); + item.SetText(1, entry.Item2.ToString()); + } + + scoreboardWindow.AddChild(tree); + + GetTree().Root.AddChild(scoreboardWindow); + } + + private void OnBecomeAdmin() + { + BecomeAdmin.Hide(); + if (!Connection.Instance.ActiveTournament) + { + StartTournament.Show(); + } + } +} diff --git a/scripts/AdminControls.cs.uid b/scripts/AdminControls.cs.uid new file mode 100644 index 0000000..979f4bf --- /dev/null +++ b/scripts/AdminControls.cs.uid @@ -0,0 +1 @@ +uid://1y72woiynf31 diff --git a/scripts/BoardScreen.cs b/scripts/BoardScreen.cs index faa83a6..e784f07 100644 --- a/scripts/BoardScreen.cs +++ b/scripts/BoardScreen.cs @@ -1,9 +1,11 @@ using Godot; using System; +using System.Collections.Generic; public partial class BoardScreen : Node2D { [Export] public PackedScene BracketScene; + [Export] public BaseButton BackButton; private const string RED_CHIP_PATH = "res://scenes/red_chip.tscn"; private const string YELLOW_CHIP_PATH = "res://scenes/yellow_chip.tscn"; @@ -45,6 +47,12 @@ public partial class BoardScreen : Node2D { Connection.Instance.OnObserveDraw += ObserveDraw; Connection.Instance.OnObserveTerminated += ObserveTerminated; Connection.Instance.OnObserveMove += ObserveMove; + Connection.Instance.OnTournamentEnd += ShowTournamentScoreboard; + + BackButton.Pressed += () => + { + TransitionToBracket(); + }; } public override void _ExitTree() @@ -53,6 +61,7 @@ public partial class BoardScreen : Node2D { Connection.Instance.OnObserveDraw -= ObserveDraw; Connection.Instance.OnObserveTerminated -= ObserveTerminated; Connection.Instance.OnObserveMove -= ObserveMove; + Connection.Instance.OnTournamentEnd -= ShowTournamentScoreboard; } private void ObserveMove(string username, int column) @@ -69,22 +78,76 @@ public partial class BoardScreen : Node2D { private void ObserveWin(string winner) { - // TODO + var popup = new Popup(); + popup.AlwaysOnTop = true; + popup.PopupCentered(); + popup.Size = new Vector2I(128, 128); + var text = new Label(); + text.Text = winner + " wins!"; + popup.AddChild(text); + GetTree().Root.AddChild(popup); TransitionToBracket(); } private void ObserveDraw() { - // TODO + var popup = new Popup(); + popup.AlwaysOnTop = true; + popup.PopupCentered(); + popup.Size = new Vector2I(128, 128); + var text = new Label(); + text.Text = "Draw!"; + popup.AddChild(text); + GetTree().Root.AddChild(popup); TransitionToBracket(); } private void ObserveTerminated() { - // TODO + var popup = new Popup(); + popup.AlwaysOnTop = true; + popup.PopupCentered(); + popup.Size = new Vector2I(128, 128); + var text = new Label(); + text.Text = "Match Terminated"; + popup.AddChild(text); + GetTree().Root.AddChild(popup); TransitionToBracket(); } + private void ShowTournamentScoreboard(List<(string, int)> playerScoreboard) + { + var scoreboardWindow = new Window(); + scoreboardWindow.AlwaysOnTop = true; + scoreboardWindow.MaximizeDisabled = true; + scoreboardWindow.Unresizable = true; + scoreboardWindow.InitialPosition = Window.WindowInitialPosition.CenterMainWindowScreen; + scoreboardWindow.Size = new Vector2I(256, 512); + scoreboardWindow.CloseRequested += () => + { + GetTree().Root.RemoveChild(scoreboardWindow); + }; + + var tree = new Tree(); + tree.HideRoot = true; + tree.Columns = 2; + tree.ColumnTitlesVisible = true; + tree.SetColumnTitle(0, "Player"); + tree.SetColumnTitle(1, "Score"); + var root = tree.CreateItem(); + + foreach ((string, int) entry in playerScoreboard) + { + var item = tree.CreateItem(root); + item.SetText(0, entry.Item1); + item.SetText(1, entry.Item2.ToString()); + } + + scoreboardWindow.AddChild(tree); + + GetTree().Root.AddChild(scoreboardWindow); + } + private void TransitionToBracket() { GetTree().ChangeSceneToPacked(BracketScene); diff --git a/scripts/BracketScene.cs b/scripts/BracketScene.cs index 690d268..917e3b4 100644 --- a/scripts/BracketScene.cs +++ b/scripts/BracketScene.cs @@ -29,7 +29,6 @@ public partial class BracketScene : Control Connection.Instance.OnUpdatedPlayers += UpdatePlayers; Connection.Instance.OnUpdatedMatches += UpdateMatches; - Connection.Instance.OnBecomeAdmin += BecomeAdmin; Connection.Instance.OnWatchGameAck += TransitionToBoard; } @@ -37,7 +36,6 @@ public partial class BracketScene : Control { Connection.Instance.OnUpdatedPlayers -= UpdatePlayers; Connection.Instance.OnUpdatedMatches -= UpdateMatches; - Connection.Instance.OnBecomeAdmin -= BecomeAdmin; Connection.Instance.OnWatchGameAck -= TransitionToBoard; } @@ -90,15 +88,8 @@ public partial class BracketScene : Control } } - private void BecomeAdmin() - { - // TODO - } - private void TransitionToBoard() { - // TODO - GD.Print("Watch game worked!"); GetTree().ChangeSceneToPacked(BoardScene); } } diff --git a/scripts/Connection.cs b/scripts/Connection.cs index 73003d7..523e766 100644 --- a/scripts/Connection.cs +++ b/scripts/Connection.cs @@ -30,6 +30,7 @@ public partial class Connection : Node public event Action OnObserveMove; public event Action> OnUpdatedMatches; public event Action> OnUpdatedPlayers; + public event Action OnStartTournamentAck; public event Action> OnTournamentEnd; public event Action OnBecomeAdmin; @@ -39,6 +40,8 @@ public partial class Connection : Node public bool IsAdmin { get; private set; } public bool IsPlayer { get; private set; } + public bool ActiveTournament { get; private set; } + public MatchData CurrentObservingMatch { get; private set; } @@ -334,6 +337,7 @@ public partial class Connection : Node { case "END": { + ActiveTournament = false; List<(string, int)> playerScoreboard = new List<(string, int)>(); string[] entries = segments[1].Split("|"); foreach (string entry in entries) @@ -345,6 +349,12 @@ public partial class Connection : Node OnTournamentEnd?.Invoke(playerScoreboard); break; } + case "START": + { + OnStartTournamentAck?.Invoke(); + ActiveTournament = true; + break; + } } }