/// <summary> /// Generates the amount of cards needed based on this.Colums and this.Rows. /// Cards get assigned images based on the currently selected theme. /// Also handles randomizing the deck each time a game is played. /// </summary> private void PopulateDeck() { this.ConfigurateDeckStyling(); this.Deck = new List <CardPictureBox>(); this.SelectedCards = new List <CardPictureBox>(); for (int i = 0; i < (this.Rows * this.Collumns); i++) { CardPictureBox card = new CardPictureBox() { Dock = DockStyle.Fill, SizeMode = PictureBoxSizeMode.StretchImage, Name = $"{i}", //Couuld make a property that holds an int but we need to cast it to a string later on anyway PairName = this.ThemeImages[this.SelectedTheme][i].Name, CardImage = this.ThemeImages[this.SelectedTheme][i].Resource }; card.Click += this.CardClicked; this.Deck.Add(card); } //Randomize the location of the cards in the deck this.Deck.Shuffle(); this.Deck.Shuffle(); foreach (CardPictureBox card in this.Deck) { this.Panel.Controls.Add(card); } }
/// <summary> /// Handles whatever happends when clicking on one of the PictureBoxes(cards) on the playing field. /// First we have to run a couple checks to determine if we can proceed; /// - We need to check if the game is frozen, this happends after clicking on a card. We need to freeze the game /// because if we do not do so the player has no time to see the second card he tried to match with the first. /// So when we do not freeze the game the player has a (300)ms window to click other cards and mess up what is happenin /// - Check if the currently clicked card is already in the this.SelectedCards list, we do not want a user to be able /// to gain points for matching the card with the card he just clicked. /// - If a card is solved we do not want to be able to gain points for that either. /// /// When we have two cards in the this.SelectedCards list we want to proceed and check if they match. /// </summary> /// <param name="sender"></param> /// <param name="e"></param> public void CardClicked(object sender, System.EventArgs e) { CardPictureBox selectedCard = (CardPictureBox)sender; //First check all the conditions on which we want to exit early if (this.GameIsFrozen || this.SelectedCards.Contains(selectedCard) || selectedCard.IsSolved) { return; } else { //Show the image that we stored in CardImage selectedCard.Image = selectedCard.CardImage; this.SelectedCards.Add(selectedCard); } //Only when 2 cards have been selected we want to freeze the game and check if they match if (this.SelectedCards.Count == 2) { this.GameIsFrozen = true; //Delay checking if they match with (300)ms. This is so that the user has a few ms to see what image they flipped //when they do not match. Without this its pretty much instant and the user wont be able to see what card they matched Task.Delay(300).ContinueWith(x => { this.CheckIfMatch(); }); } }
/// <summary> /// Resumes paused game. If it needs to resume game from savefile load file contents back into the memory class /// and rebuild the deck. /// </summary> /// <param name="loadFromSaveFile"></param> public void ResumeGame(bool loadFromSaveFile = false) { if (loadFromSaveFile) { //Since we are bypassing this.Populatedeck() we need to do some things again //Create new empty lists for deck and selectedcards this.Deck = new List <CardPictureBox>(); this.SelectedCards = new List <CardPictureBox>(); //Use a dynamic object for the next part, could also create a custom class for this dynamic gameState = new System.Dynamic.ExpandoObject(); //Load the data from the savegamefile string json = Files.GetFileContent(Path.Combine(Directory.GetCurrentDirectory(), "savegame.txt")); //Deserialize the json gameState = JsonConvert.DeserializeObject(json); //For the next part we need to cast/convert all properties that are stored in our dyanmic list to the appropriate type this.IsPlayerOnesTurn = (bool)gameState.IsPlayerOnesTurn; this.SelectedTheme = (int)gameState.SelectedTheme; this.Rows = (int)gameState.Rows; this.Collumns = (int)gameState.Collumns; this.Players = gameState.Players.ToObject <List <Player> >().ToArray(); //Seems dirty //It is important that we style our deck after we loaded in all the settings, otherwise we get a default 4*4 playing field //even though the settings may state otherwise this.ConfigurateDeckStyling(); List <CardPictureBoxJson> deck = new List <CardPictureBoxJson>(); deck = gameState.Deck.ToObject <List <CardPictureBoxJson> >(); foreach (CardPictureBoxJson jsonCard in deck) { CardNameAndImage pairNameAndImage = this.ThemeImages[this.SelectedTheme].Find(item => item.Name == jsonCard.PairName); CardPictureBox card = new CardPictureBox() { Dock = DockStyle.Fill, SizeMode = PictureBoxSizeMode.StretchImage, Name = jsonCard.Name, IsSolved = jsonCard.IsSolved, HasBeenVisible = jsonCard.HasBeenVisible, PairName = pairNameAndImage.Name, CardImage = pairNameAndImage.Resource, Image = (jsonCard.IsSolved) ? pairNameAndImage.Resource : null //Show image based on if it was solved }; card.Click += this.CardClicked; //Check if the card is currently selected, if so add it to the selectedCards list. if (jsonCard.IsSelected) { this.SelectedCards.Add(card); card.Image = pairNameAndImage.Resource; } this.Deck.Add(card); } foreach (CardPictureBox card in this.Deck) { this.Panel.Controls.Add(card); } } //Remove the savegame from the savefile to prevent abuse Files.WriteToFile(Path.Combine(Directory.GetCurrentDirectory(), "savegame.txt"), "", overwrite: true); this.HasUnfinishedGame = false; this.Form1.ShowLoadGameCheckbox(); //Pass back control to the player this.GameIsFrozen = false; }