526 lines
20 KiB
C#
526 lines
20 KiB
C#
using Microsoft.AspNetCore.SignalR.Client;
|
|
using qtc_net_client_2.Forms;
|
|
using qtc_net_client_2.Services;
|
|
using QtCNETAPI.Enums;
|
|
using QtCNETAPI.Models;
|
|
using QtCNETAPI.Schema;
|
|
using QtCNETAPI.Services.ApiService;
|
|
using System.Threading.Tasks;
|
|
using qtc_net_client_2.ClientModel;
|
|
|
|
namespace qtc_net_client_2
|
|
{
|
|
public partial class TicTacToeGame : Form
|
|
{
|
|
private HubConnection? GameHubConnection { get; set; }
|
|
private GameRoom? CurrentRoom { get; set; }
|
|
|
|
private IApiService _apiService;
|
|
private Config _config;
|
|
private AudioService _audioService = new();
|
|
|
|
private int WinCount { get; set; }
|
|
private bool JackpotWon { get; set; } = false;
|
|
public TicTacToeGame(IApiService apiService, Config config)
|
|
{
|
|
_apiService = apiService;
|
|
_config = config;
|
|
|
|
InitializeComponent();
|
|
}
|
|
|
|
private async void Main_Load(object sender, EventArgs e)
|
|
{
|
|
// first, connect to the game hub
|
|
GameHubConnection = new HubConnectionBuilder()
|
|
.WithUrl($"{_config.ServerUrl}/tttgame", options =>
|
|
{
|
|
options.AccessTokenProvider = () => Task.FromResult(_apiService.SessionToken)!;
|
|
})
|
|
.WithAutomaticReconnect()
|
|
.Build();
|
|
|
|
pLoading.Visible = true;
|
|
lblLoadStatus.Text = "Connecting\nTo\nHub...";
|
|
|
|
GameHubConnection.On<GameRoom>("WaitingForPlayer", (room) =>
|
|
{
|
|
CurrentRoom = room;
|
|
|
|
BeginInvoke(delegate ()
|
|
{
|
|
if (room.Player1 != null)
|
|
{
|
|
lblP1Username.Text = room.Player1.Username;
|
|
lvUserlist.Items.Add(room.Player1.Id, room.Player1.Username, room.Player1.Status);
|
|
}
|
|
|
|
lblLoadStatus.Text = "Waiting\nFor\nPlayer...";
|
|
});
|
|
});
|
|
|
|
GameHubConnection.On<GameRoom>("GameStart", (room) =>
|
|
{
|
|
CurrentRoom = room;
|
|
|
|
BeginInvoke(delegate ()
|
|
{
|
|
pLoading.Visible = false;
|
|
pSymbolSelection.Visible = false;
|
|
});
|
|
});
|
|
|
|
GameHubConnection.On<GameRoom, string?>("GameEnd", (room, usernameDisconnected) =>
|
|
{
|
|
CurrentRoom = room;
|
|
|
|
BeginInvoke(delegate ()
|
|
{
|
|
if (room.Player1 == null) lblP1Username.Text = "Username";
|
|
if (room.Player2 == null) lblP2Username.Text = "Username";
|
|
|
|
ToggleBoardControl(false);
|
|
|
|
switch (room.Status)
|
|
{
|
|
case GameStatus.P1Win:
|
|
{
|
|
if (_apiService.CurrentUser.Id == room.Player1?.Id)
|
|
{
|
|
lblLoadStatus.Text = "You Won!";
|
|
WinCount++;
|
|
|
|
if (WinCount >= 3)
|
|
{
|
|
JackpotWon = true;
|
|
lblJackpotWon.Text = "You Won A Spin!";
|
|
}
|
|
}
|
|
else lblLoadStatus.Text = "You Lost!";
|
|
lblPlayAgain.Visible = true;
|
|
btnPlayAgain.Visible = true;
|
|
break;
|
|
}
|
|
case GameStatus.P2Win:
|
|
{
|
|
if (_apiService.CurrentUser.Id == room.Player2?.Id)
|
|
{
|
|
lblLoadStatus.Text = "You Won!";
|
|
WinCount++;
|
|
|
|
if (WinCount >= 3)
|
|
{
|
|
JackpotWon = true;
|
|
lblJackpotWon.Text = "You Won A Spin!";
|
|
}
|
|
}
|
|
else lblLoadStatus.Text = "You Lost!";
|
|
lblPlayAgain.Visible = true;
|
|
btnPlayAgain.Visible = true;
|
|
break;
|
|
}
|
|
case GameStatus.PlayerDisconnected:
|
|
{
|
|
if (!string.IsNullOrEmpty(usernameDisconnected))
|
|
lblLoadStatus.Text = $"Game Ended.\nUser {usernameDisconnected}\nDisconnected.";
|
|
lblPlayAgain.Visible = false;
|
|
btnPlayAgain.Visible = false;
|
|
break;
|
|
}
|
|
case GameStatus.NoWin:
|
|
lblLoadStatus.Text = "It's a draw!";
|
|
lblPlayAgain.Visible = true;
|
|
btnPlayAgain.Visible = true;
|
|
break;
|
|
}
|
|
|
|
pLoading.Visible = true;
|
|
});
|
|
});
|
|
|
|
GameHubConnection.On<GameRoom>("RestartGame", (room) =>
|
|
{
|
|
CurrentRoom = room;
|
|
|
|
BeginInvoke(delegate ()
|
|
{
|
|
if (pLoading.Visible) pLoading.Visible = false;
|
|
});
|
|
ClearBoard();
|
|
ToggleBoardControl(true);
|
|
|
|
BeginInvoke(delegate ()
|
|
{
|
|
List<Button> buttons = [btnSquare1, btnSquare2, btnSquare3, btnSquare4, btnSquare5, btnSquare6, btnSquare7, btnSquare8, btnSquare9];
|
|
|
|
foreach (Button button in buttons)
|
|
{
|
|
if (button.BackgroundImage == null)
|
|
button.Enabled = true;
|
|
}
|
|
});
|
|
});
|
|
|
|
GameHubConnection.On<GameRoom>("SelectSymbol", (room) =>
|
|
{
|
|
CurrentRoom = room;
|
|
|
|
BeginInvoke(delegate ()
|
|
{
|
|
if (room.Player1 != null)
|
|
{
|
|
lblP1Username.Text = room.Player1.Username;
|
|
if (!lvUserlist.Items.ContainsKey(room.Player1.Id)) lvUserlist.Items.Add(room.Player1.Id, room.Player1.Username, room.Player1.Status);
|
|
}
|
|
if (room.Player2 != null)
|
|
{
|
|
lblP2Username.Text = room.Player2.Username;
|
|
if (!lvUserlist.Items.ContainsKey(room.Player2.Id)) lvUserlist.Items.Add(room.Player2.Id, room.Player2.Username, room.Player2.Status);
|
|
}
|
|
|
|
if (pLoading.Visible) pLoading.Visible = false;
|
|
pSymbolSelection.Visible = true;
|
|
pBoard.Enabled = true;
|
|
ToggleAllSquareButtons(false);
|
|
});
|
|
});
|
|
|
|
GameHubConnection.On<GameRoom>("SelectingSymbol", (room) =>
|
|
{
|
|
CurrentRoom = room;
|
|
|
|
BeginInvoke(delegate ()
|
|
{
|
|
if (pSymbolSelection.Visible) return;
|
|
|
|
if (room.Player1 != null)
|
|
{
|
|
lblP1Username.Text = room.Player1.Username;
|
|
if (!lvUserlist.Items.ContainsKey(room.Player1.Id)) lvUserlist.Items.Add(room.Player1.Id, room.Player1.Username, room.Player1.Status);
|
|
}
|
|
if (room.Player2 != null)
|
|
{
|
|
lblP2Username.Text = room.Player2.Username;
|
|
if (!lvUserlist.Items.ContainsKey(room.Player2.Id)) lvUserlist.Items.Add(room.Player2.Id, room.Player2.Username, room.Player2.Status);
|
|
}
|
|
|
|
pLoading.Visible = true;
|
|
lblLoadStatus.Text = $"Player 1\nIs Selecting\nSymbol...";
|
|
});
|
|
});
|
|
|
|
GameHubConnection.On<GameRoom, TicTacToeMove>("UpdateBoard", (room, move) =>
|
|
{
|
|
CurrentRoom = room;
|
|
|
|
UpdateBoardUI(move);
|
|
});
|
|
|
|
GameHubConnection.On<GameRoom>("StartTurn", (room) =>
|
|
{
|
|
CurrentRoom = room;
|
|
ToggleBoardControl(true);
|
|
AddChatMessage("[SERVER] Your Turn!");
|
|
});
|
|
|
|
GameHubConnection.On<GameRoom>("EndTurn", (room) =>
|
|
{
|
|
CurrentRoom = room;
|
|
ToggleBoardControl(false);
|
|
AddChatMessage("[SERVER] Other Players Turn");
|
|
});
|
|
|
|
GameHubConnection.On<string>("ReceiveMessage", (msg) =>
|
|
{
|
|
AddChatMessage(msg);
|
|
if (msg.Split('[')[1] != _apiService.CurrentUser.Username)
|
|
_audioService.PlaySoundEffect("sndMessage");
|
|
});
|
|
|
|
try { await GameHubConnection.StartAsync(); }
|
|
catch (HttpRequestException ex)
|
|
{
|
|
MessageBox.Show($"Sorry, We Couldn't Connect To The Game Server Due To An Error. Please Try Again Later.\n\n{ex.Message}", "Oops.", MessageBoxButtons.OK, MessageBoxIcon.Error);
|
|
Environment.Exit(0);
|
|
}
|
|
|
|
lblLoadStatus.Text = "Finding\nRoom...";
|
|
await GameHubConnection.SendAsync("FindRoom");
|
|
}
|
|
|
|
private async void TicTacToeGame_FormClosed(object sender, FormClosedEventArgs e)
|
|
{
|
|
if (GameHubConnection != null && GameHubConnection.State == HubConnectionState.Connected)
|
|
{
|
|
if(JackpotWon)
|
|
{
|
|
// start a jackpot spin
|
|
CurrencyJackpotSpinner currencyJackpotSpinner = new CurrencyJackpotSpinner();
|
|
var dialogResult = currencyJackpotSpinner.ShowDialog();
|
|
if (dialogResult == DialogResult.OK)
|
|
{
|
|
await _apiService.AddCurrencyToCurrentUser(currencyJackpotSpinner.TokensWon, false);
|
|
_apiService.CurrentUser.CurrencyAmount += currencyJackpotSpinner.TokensWon;
|
|
|
|
// find the main form to refresh currency count
|
|
Main? mainForm = (Main?)Application.OpenForms[0];
|
|
if (mainForm != null)
|
|
mainForm.RefreshCurrencyCounter();
|
|
}
|
|
}
|
|
|
|
// stop and dispose connection
|
|
await GameHubConnection.StopAsync();
|
|
await GameHubConnection.DisposeAsync();
|
|
GameHubConnection = null;
|
|
}
|
|
}
|
|
|
|
private async void btnXSelect_Click(object sender, EventArgs e) => await SelectSymbol(TicTacToeSymbol.X);
|
|
private async void btnOSelect_Click(object sender, EventArgs e) => await SelectSymbol(TicTacToeSymbol.O);
|
|
|
|
private async void btnPlayAgain_Click(object sender, EventArgs e)
|
|
{
|
|
if (GameHubConnection != null && GameHubConnection.State == HubConnectionState.Connected && CurrentRoom != null)
|
|
{
|
|
await GameHubConnection.SendAsync("RestartGame", CurrentRoom.Id);
|
|
}
|
|
}
|
|
|
|
private async void btnSquareX_Click(object sender, EventArgs e)
|
|
{
|
|
// create move
|
|
TicTacToeMove move = new TicTacToeMove
|
|
{
|
|
User = _apiService.CurrentUser
|
|
};
|
|
|
|
if (sender is Button btn)
|
|
{
|
|
switch (btn.Name)
|
|
{
|
|
case "btnSquare1":
|
|
move.Point = 1;
|
|
break;
|
|
case "btnSquare2":
|
|
move.Point = 2;
|
|
break;
|
|
case "btnSquare3":
|
|
move.Point = 3;
|
|
break;
|
|
case "btnSquare4":
|
|
move.Point = 4;
|
|
break;
|
|
case "btnSquare5":
|
|
move.Point = 5;
|
|
break;
|
|
case "btnSquare6":
|
|
move.Point = 6;
|
|
break;
|
|
case "btnSquare7":
|
|
move.Point = 7;
|
|
break;
|
|
case "btnSquare8":
|
|
move.Point = 8;
|
|
break;
|
|
case "btnSquare9":
|
|
move.Point = 9;
|
|
break;
|
|
}
|
|
}
|
|
|
|
// send move
|
|
if (GameHubConnection != null && GameHubConnection.State == HubConnectionState.Connected && CurrentRoom != null)
|
|
await GameHubConnection.SendAsync("MakeMove", CurrentRoom.Id, move);
|
|
}
|
|
|
|
private async void btnSend_Click(object sender, EventArgs e)
|
|
{
|
|
if ((GameHubConnection != null && GameHubConnection.State == HubConnectionState.Connected && CurrentRoom != null) &&
|
|
!string.IsNullOrWhiteSpace(rtxtChatbox.Text))
|
|
{
|
|
await GameHubConnection.SendAsync("SendRoomMessage", CurrentRoom.Id, $"[{_apiService.CurrentUser.Username}] {rtxtChatbox.Text}");
|
|
_audioService.PlaySoundEffect("sndSendClick");
|
|
rtxtChatbox.Clear();
|
|
}
|
|
}
|
|
|
|
private void rtxtChatbox_KeyDown(object sender, KeyEventArgs e)
|
|
{
|
|
if(e.KeyCode == Keys.Enter)
|
|
btnSend_Click(sender, e);
|
|
}
|
|
|
|
private void ToggleBoardControl(bool toggle)
|
|
{
|
|
if (IsHandleCreated && !IsDisposed)
|
|
{
|
|
Invoke(delegate ()
|
|
{
|
|
pBoard.Enabled = toggle;
|
|
});
|
|
}
|
|
}
|
|
|
|
private void ToggleAllSquareButtons(bool toggle)
|
|
{
|
|
if (IsHandleCreated && !IsDisposed)
|
|
{
|
|
Invoke(delegate ()
|
|
{
|
|
List<Button> buttons = [btnSquare1, btnSquare2, btnSquare3, btnSquare4, btnSquare5, btnSquare6, btnSquare7, btnSquare8, btnSquare9];
|
|
|
|
foreach (Button button in buttons)
|
|
button.Enabled = toggle;
|
|
});
|
|
}
|
|
}
|
|
|
|
private async Task SelectSymbol(TicTacToeSymbol symbol)
|
|
{
|
|
if (GameHubConnection != null && GameHubConnection.State == HubConnectionState.Connected && CurrentRoom != null)
|
|
{
|
|
await GameHubConnection.SendAsync("SetSymbol", CurrentRoom.Id, symbol);
|
|
ToggleAllSquareButtons(true);
|
|
}
|
|
}
|
|
|
|
private void UpdateBoardUI(TicTacToeMove move)
|
|
{
|
|
if (IsHandleCreated && !IsDisposed && CurrentRoom != null)
|
|
{
|
|
// play move made sound
|
|
_audioService.PlaySoundEffect("sndTTTMoveMade");
|
|
|
|
Invoke(delegate ()
|
|
{
|
|
switch (CurrentRoom.Board.Square1)
|
|
{
|
|
case TicTacToeSymbol.X:
|
|
btnSquare1.BackgroundImage = Properties.Resources.X;
|
|
btnSquare1.Enabled = false;
|
|
break;
|
|
case TicTacToeSymbol.O:
|
|
btnSquare1.BackgroundImage = Properties.Resources.O;
|
|
btnSquare1.Enabled = false;
|
|
break;
|
|
}
|
|
switch (CurrentRoom.Board.Square2)
|
|
{
|
|
case TicTacToeSymbol.X:
|
|
btnSquare2.BackgroundImage = Properties.Resources.X;
|
|
btnSquare2.Enabled = false;
|
|
break;
|
|
case TicTacToeSymbol.O:
|
|
btnSquare2.BackgroundImage = Properties.Resources.O;
|
|
btnSquare2.Enabled = false;
|
|
break;
|
|
}
|
|
switch (CurrentRoom.Board.Square3)
|
|
{
|
|
case TicTacToeSymbol.X:
|
|
btnSquare3.BackgroundImage = Properties.Resources.X;
|
|
btnSquare3.Enabled = false;
|
|
break;
|
|
case TicTacToeSymbol.O:
|
|
btnSquare3.BackgroundImage = Properties.Resources.O;
|
|
btnSquare3.Enabled = false;
|
|
break;
|
|
}
|
|
switch (CurrentRoom.Board.Square4)
|
|
{
|
|
case TicTacToeSymbol.X:
|
|
btnSquare4.BackgroundImage = Properties.Resources.X;
|
|
btnSquare4.Enabled = false;
|
|
break;
|
|
case TicTacToeSymbol.O:
|
|
btnSquare4.BackgroundImage = Properties.Resources.O;
|
|
btnSquare4.Enabled = false;
|
|
break;
|
|
}
|
|
switch (CurrentRoom.Board.Square5)
|
|
{
|
|
case TicTacToeSymbol.X:
|
|
btnSquare5.BackgroundImage = Properties.Resources.X;
|
|
btnSquare5.Enabled = false;
|
|
break;
|
|
case TicTacToeSymbol.O:
|
|
btnSquare5.BackgroundImage = Properties.Resources.O;
|
|
btnSquare5.Enabled = false;
|
|
break;
|
|
}
|
|
switch (CurrentRoom.Board.Square6)
|
|
{
|
|
case TicTacToeSymbol.X:
|
|
btnSquare6.BackgroundImage = Properties.Resources.X;
|
|
btnSquare6.Enabled = false;
|
|
break;
|
|
case TicTacToeSymbol.O:
|
|
btnSquare6.BackgroundImage = Properties.Resources.O;
|
|
btnSquare6.Enabled = false;
|
|
break;
|
|
}
|
|
switch (CurrentRoom.Board.Square7)
|
|
{
|
|
case TicTacToeSymbol.X:
|
|
btnSquare7.BackgroundImage = Properties.Resources.X;
|
|
btnSquare7.Enabled = false;
|
|
break;
|
|
case TicTacToeSymbol.O:
|
|
btnSquare7.BackgroundImage = Properties.Resources.O;
|
|
btnSquare7.Enabled = false;
|
|
break;
|
|
}
|
|
switch (CurrentRoom.Board.Square8)
|
|
{
|
|
case TicTacToeSymbol.X:
|
|
btnSquare8.BackgroundImage = Properties.Resources.X;
|
|
btnSquare8.Enabled = false;
|
|
break;
|
|
case TicTacToeSymbol.O:
|
|
btnSquare8.BackgroundImage = Properties.Resources.O;
|
|
btnSquare8.Enabled = false;
|
|
break;
|
|
}
|
|
switch (CurrentRoom.Board.Square9)
|
|
{
|
|
case TicTacToeSymbol.X:
|
|
btnSquare9.BackgroundImage = Properties.Resources.X;
|
|
btnSquare9.Enabled = false;
|
|
break;
|
|
case TicTacToeSymbol.O:
|
|
btnSquare9.BackgroundImage = Properties.Resources.O;
|
|
btnSquare9.Enabled = false;
|
|
break;
|
|
}
|
|
});
|
|
}
|
|
}
|
|
|
|
private void AddChatMessage(string msg)
|
|
{
|
|
if (IsHandleCreated && !IsDisposed)
|
|
{
|
|
Invoke(delegate ()
|
|
{
|
|
rtxtChat.AppendText($"{msg}\n");
|
|
});
|
|
}
|
|
}
|
|
|
|
private void ClearBoard()
|
|
{
|
|
if (IsHandleCreated && !IsDisposed)
|
|
{
|
|
Invoke(delegate ()
|
|
{
|
|
List<Button> buttons = [btnSquare1, btnSquare2, btnSquare3, btnSquare4, btnSquare5, btnSquare6, btnSquare7, btnSquare8, btnSquare9];
|
|
|
|
foreach (Button button in buttons)
|
|
button.BackgroundImage = null;
|
|
});
|
|
}
|
|
}
|
|
}
|
|
}
|