Solitaire - my first card game :-)

A big “shout out” to forum user ‘SoundGoddess’, who inspired me to finally do that old card game that’s been on hold since I started programming

-Do a search for her open solitaire, it has sounds and animation, and its all good


My version of solitaire is just aimed at the basics
 Its important that my mum can manage the interface, and see things clearly :slightly_smiling:
It has a button to start new game, and a button to toggle through some color presets


Programmed thusly:

Card.cs has int value from 1-13, color red or black, and string type (hearts, spades, etc
)
Stack.cs holds a list of cards, int card_spacing

Solitaire.cs holds stacks, and handles the rules, transfer between stacks, etc


A few shots to show some color variants:


Uploading


3 Likes

I like the look of the first screen shot the “dirty looking” cards really go well on the background.

I have a question though - care to share the code to this? I’ve just started learning monogame development recently and would love to get my hands on some full game code.

@Harag I suggest that you download the source code for the solitaire made by @SoundGoddess since the code for that is already publicly available.

Solitaire GitHub by SoundGoddess

@MasterQuestMaster Excellent, thanks for pointing me that way, will do :slight_smile:

@Harag, somehow I missed this post but it really feels good being able to help out and inspire :slight_smile:

@monopalle - I like your art style, good job!

1 Like

@SoundGoddess No problem - I found your post after seeing the solitaire here by @monopalle - I have to agree, I like the the art style of this as well.

1 Like

Thanks guys! And Harag, in case you still would like to see the code, I’ll just dump it here real quick:

Here is the Solitaire class
 My game class just creates an instance of this and updates/draw it
 This way, I can add other games later, no problem


using System;
using System.Collections.Generic;
using System.ComponentModel;
using System.Linq;
using Microsoft.Xna.Framework.Audio;
using Microsoft.Xna.Framework;
using Microsoft.Xna.Framework.Content;
using Microsoft.Xna.Framework.Graphics;
using Microsoft.Xna.Framework.Input;
using Microsoft.Xna.Framework.Media;
using System.IO;
using System.Security.Cryptography;

namespace Kabale
{
class Solitaire : GameType
{
// these are the positions for interface, and the various components of the game, like specific card stacks

//----------------------------------------------------------------------------------------------------------------
static Vector2 star_pos = new Vector2(10,10);
static Vector2 star_pos2 = new Vector2(1520, 10);
static Vector2 star_origin = new Vector2(32,32);
static Texture2D table;
static Texture2D star;
Stack deck;
Stack pile ;
Stack ace_1;
Stack ace_2;
Stack ace_3;
Stack ace_4;

    List<Stack> kingStacks;
    Stack held_stack;  // this is the stack held or lifted by the player...
    Stack return_stack; // this is where the held stack was liftet from, so they know were to return to if action is cancelled.
    public static void LoadContent(ContentManager content)
    {
        table = content.Load<Texture2D>(".\\table");
        star = content.Load<Texture2D>(".\\star");
    }
    // Variables used for "luck of the shuffle"....
    //-------------------------------------------------------
    static RandomNumberGenerator rng;
    static byte[] rand; //Set the length of this array to
    // the number of random numbers you want
    static RandomNumberGenerator _rand = RandomNumberGenerator.Create();
    static int dice_roll;
    void Shuffle(Stack stack_to_shuffle)
    {
        {
            rng = RandomNumberGenerator.Create();
            rand = new byte[4];
            rng.GetBytes(rand);
            Stack tempStack = new Stack(Vector2.Zero);
            while (deck.cards.Count >0)
            {
                dice_roll = CommonMethods.RandomNext(0, deck.cards.Count );
                tempStack.Add(deck.cards[dice_roll]);
                deck.cards.RemoveAt(dice_roll);
            }
            foreach (Card card in tempStack.cards)
            {
                deck.Add(card);
            }
        }
    }
    public Solitaire()
    {
        New_Game();
    }
    void New_Game()
    {
        held_stack = new Stack(Vector2.Zero);
        held_stack.Open();
        return_stack = new Stack(Vector2.Zero);
        float border_x = 50;
        float border_y = 50;
        float spacing_x = Card.card_dimensions.X + border_x;
        float spacing_y = Card.card_dimensions.Y + border_y;
        deck = new Stack(new Vector2(border_x, border_y));
        pile = new Stack(new Vector2(border_x + spacing_x, border_y));
        ace_1 = new Stack(new Vector2(border_x + (3 * spacing_x), border_y));
        ace_2 = new Stack(new Vector2(border_x + (4 * spacing_x), border_y));
        ace_3 = new Stack(new Vector2(border_x + (5 * spacing_x), border_y));
        ace_4 = new Stack(new Vector2(border_x + (6 * spacing_x), border_y));
        kingStacks = new List<Stack>();
        for (int i = 0; i < 7; i++)
        {
            kingStacks.Add(new Stack(new Vector2(i * spacing_x + border_x, border_y + spacing_y)));
            kingStacks.Last().Open();
        }
        // add all the cards
        for (int i = 1; i < 14; i++)
        {
            deck.Add(new Card("Diamonds", Card.red_color, i));
        }
        for (int i = 1; i < 14; i++)
        {
            deck.Add(new Card("Clovers", Card.black_color, i));
        }
        for (int i = 1; i < 14; i++)
        {
            deck.Add(new Card("Hearts", Card.red_color, i));
        }
        for (int i = 1; i < 14; i++)
        {
            deck.Add(new Card("Spades", Card.black_color, i));
        }
        //shuffle here
        Shuffle(deck);
        Shuffle(deck);
        Shuffle(deck);
        //add to king stacks
        kingStacks[0].Add(deck.cards[0]);
        deck.cards.RemoveAt(0);
        kingStacks[1].Add(deck.cards[0]);
        deck.cards.RemoveAt(0);
        kingStacks[2].Add(deck.cards[0]);
        deck.cards.RemoveAt(0);
        kingStacks[3].Add(deck.cards[0]);
        deck.cards.RemoveAt(0);
        kingStacks[4].Add(deck.cards[0]);
        deck.cards.RemoveAt(0);
        kingStacks[5].Add(deck.cards[0]);
        deck.cards.RemoveAt(0);
        kingStacks[6].Add(deck.cards[0]);
        deck.cards.RemoveAt(0);
        kingStacks[1].Add(deck.cards[0]);
        deck.cards.RemoveAt(0);
        kingStacks[2].Add(deck.cards[0]);
        deck.cards.RemoveAt(0);
        kingStacks[3].Add(deck.cards[0]);
        deck.cards.RemoveAt(0);
        kingStacks[4].Add(deck.cards[0]);
        deck.cards.RemoveAt(0);
        kingStacks[5].Add(deck.cards[0]);
        deck.cards.RemoveAt(0);
        kingStacks[6].Add(deck.cards[0]);
        deck.cards.RemoveAt(0);
        kingStacks[2].Add(deck.cards[0]);
        deck.cards.RemoveAt(0);
        kingStacks[3].Add(deck.cards[0]);
        deck.cards.RemoveAt(0);
        kingStacks[4].Add(deck.cards[0]);
        deck.cards.RemoveAt(0);
        kingStacks[5].Add(deck.cards[0]);
        deck.cards.RemoveAt(0);
        kingStacks[6].Add(deck.cards[0]);
        deck.cards.RemoveAt(0);
        kingStacks[3].Add(deck.cards[0]);
        deck.cards.RemoveAt(0);
        kingStacks[4].Add(deck.cards[0]);
        deck.cards.RemoveAt(0);
        kingStacks[5].Add(deck.cards[0]);
        deck.cards.RemoveAt(0);
        kingStacks[6].Add(deck.cards[0]);
        deck.cards.RemoveAt(0);
        kingStacks[4].Add(deck.cards[0]);
        deck.cards.RemoveAt(0);
        kingStacks[5].Add(deck.cards[0]);
        deck.cards.RemoveAt(0);
        kingStacks[6].Add(deck.cards[0]);
        deck.cards.RemoveAt(0);
        kingStacks[5].Add(deck.cards[0]);
        deck.cards.RemoveAt(0);
        kingStacks[6].Add(deck.cards[0]);
        deck.cards.RemoveAt(0);
        kingStacks[6].Add(deck.cards[0]);
        deck.cards.RemoveAt(0);
        foreach (Stack kings in kingStacks)
        {
            if (kings.cards.Count > 0)
                kings.cards.Last().visible = true;
        }
    }
    public override void ReleaseLeft(Vector2 click_pos)
    {
        if (mouse_held && held_stack.cards.Count() > 0)
        {
           
            mouse_held = false;
            //try release on kings place !
            bool lands_on_kingstack = false;
            foreach (Stack kingstack in kingStacks)
            {
                if (kingstack.my_rect.Intersects(held_stack.my_rect))
                {
                    if (kingstack.cards.Count == 0)
                    {
                        if (held_stack.cards[0].my_value == 13)
                        {
                            foreach (Card card in held_stack.cards)
                                kingstack.cards.Add(card);
                            kingstack.Update();
                            held_stack.cards.Clear();
                            lands_on_kingstack = true;
                            break;
                        }
                    }
                    else
                    {
                        if (kingstack.cards.Last().my_color != held_stack.cards[0].my_color && kingstack.cards.Last().my_value == held_stack.cards[0].my_value + 1)
                        {
                            foreach (Card card in held_stack.cards)
                                kingstack.Add(card);
                            //kingstack.Update();
                            held_stack.cards.Clear();
                            lands_on_kingstack = true;
                            break;
                        }
                    }
                }
            }
            if (lands_on_kingstack)
            {
                // already done, just here for the else statements....
            }
            //release on ace piles
           else if ((ace_1.my_rect.Intersects(held_stack.my_rect)) && ((ace_1.cards.Count == 0 && held_stack.cards[0].my_value == 1) || (ace_1.cards.Count > 0 && ace_1.cards.Last().my_value + 1 == held_stack.cards[0].my_value && ace_1.cards.Last().my_type == held_stack.cards[0].my_type)))
            {
                ace_1.cards.Add(held_stack.cards[0]);
                ace_1.Update();
                held_stack.cards.Clear();
            }
            else if ((ace_2.my_rect.Intersects(held_stack.my_rect)) && ((ace_2.cards.Count == 0 && held_stack.cards[0].my_value == 1) || (ace_2.cards.Count > 0 && ace_2.cards.Last().my_value + 1 == held_stack.cards[0].my_value && ace_2.cards.Last().my_type == held_stack.cards[0].my_type)))
            {
                ace_2.cards.Add(held_stack.cards[0]);
                ace_2.Update();
                held_stack.cards.Clear();
            }
            else if ((ace_3.my_rect.Intersects(held_stack.my_rect)) && ((ace_3.cards.Count == 0 && held_stack.cards[0].my_value == 1) || (ace_3.cards.Count > 0 && ace_3.cards.Last().my_value + 1 == held_stack.cards[0].my_value && ace_3.cards.Last().my_type == held_stack.cards[0].my_type)))
            {
                ace_3.cards.Add(held_stack.cards[0]);
                ace_3.Update();
                held_stack.cards.Clear();
            }
            else if ((ace_4.my_rect.Intersects(held_stack.my_rect)) && ((ace_4.cards.Count == 0 && held_stack.cards[0].my_value == 1) || (ace_4.cards.Count > 0 && ace_4.cards.Last().my_value + 1 == held_stack.cards[0].my_value && ace_4.cards.Last().my_type == held_stack.cards[0].my_type)))
            {
                ace_4.cards.Add(held_stack.cards[0]);
                ace_4.Update();
                held_stack.cards.Clear();
            }
            else
            {
                foreach (Card card in held_stack.cards)
                {
                    return_stack.Add(card);
                }
            
                held_stack.cards.Clear();
            }
            foreach (Stack k_stack in kingStacks)
            {
                if (return_stack == k_stack)
                {
                    if (k_stack.cards.Count > 0)
                    k_stack.cards.Last().visible = true;
                }
            }
        }
    }
    // variables used for mouse movement and drag/drop interface.
    bool mouse_held = false;
    Vector2 mouseGrabDIFF;
    //SET COLORS,  like if you want spades and clovers to be green, and hearts and diamonds to be blue.
    // The card icon sprites are white, and will be drawn as specified here:
    //-----------------------------------------------------------------------------------------------------------------------
    void ChangeCardColors(Color red, Color black)
    {
        foreach ( Stack k in kingStacks)
        {
            foreach (Card card in k.cards)
            {
                if (card.my_type == "Hearts" || card.my_type == "Diamonds")
                    card.my_color = red;
                else
                    card.my_color = black;
            }
        }
        foreach (Card card in deck.cards)
        {
            if (card.my_type == "Hearts" || card.my_type == "Diamonds")
                card.my_color = red;
            else
                card.my_color = black;
        }
        foreach (Card card in pile.cards)
        {
            if (card.my_type == "Hearts" || card.my_type == "Diamonds")
                card.my_color = red;
            else
                card.my_color = black;
        }
        foreach (Card card in ace_1.cards)
        {
            if (card.my_type == "Hearts" || card.my_type == "Diamonds")
                card.my_color = red;
            else
                card.my_color = black;
        }
        foreach (Card card in ace_2.cards)
        {
            if (card.my_type == "Hearts" || card.my_type == "Diamonds")
                card.my_color = red;
            else
                card.my_color = black;
        }
        foreach (Card card in ace_3.cards)
        {
            if (card.my_type == "Hearts" || card.my_type == "Diamonds")
                card.my_color = red;
            else
                card.my_color = black;
        }
        foreach (Card card in ace_4.cards)
        {
            if (card.my_type == "Hearts" || card.my_type == "Diamonds")
                card.my_color = red;
            else
                card.my_color = black;
        }
    }
    public override void LeftClick(Vector2 click_POS)
    {
        mouse_held = true;
        if (Vector2.Distance(click_POS, star_pos) < 20)
        {
            New_Game();
        }
        else if (Vector2.Distance(click_POS, star_pos2) < 20)
        {
            //set colors
            Card.current_back += 1;
            if (Card.current_back > Card.backs.Count-1)
            {
                Card.current_back = 0;
            }
            switch (Card.current_back)
            {
                case 0:
                    
                    Card.red_color = Color.Red;
                    Card.black_color = Color.Black;
                    ChangeCardColors(Card.red_color,Card.black_color);
                    Card.pic_color = Color.Wheat;
                    Card.back_color = Color.White;
                    Card.face_color = Color.Wheat;
                    break;
                case 1:
                    Card.pic_color = Color.White;
                    Card.back_color = Color.White;
                    Card.face_color = Color.White;
                    Card.red_color = Color.Red;
                    Card.black_color = Color.Black;
                    ChangeCardColors(Card.red_color, Card.black_color);
                    break;
                case 2:
                    Card.red_color = Color.Red;
                    Card.black_color = Color.Black;
                    ChangeCardColors(Card.red_color, Card.black_color);
                    Card.pic_color = Color.White;
                    Card.back_color = Color.White;
                    Card.face_color = Color.White;
                    break;
                case 3:
                    Card.red_color = new Color(240, 50, 0);
                    Card.black_color = new Color(10,10,10);
                    ChangeCardColors(Card.red_color, Card.black_color);
                    Card.pic_color = new Color(250, 100, 30);
                    Card.back_color = Color.White;
                    Card.face_color = Color.WhiteSmoke;
                    break;
                case 4:
                    Card.red_color = new Color(240, 50, 0);
                    Card.black_color = new Color(0, 0, 0);
                    ChangeCardColors(Card.red_color, Card.black_color);
                     Card.pic_color = new Color(200, 70, 0);
                    Card.back_color = Color.White;
                    Card.face_color = Color.WhiteSmoke;
                    break;
                case 5:
                    Card.red_color = new Color(205,20,0);
                    Card.black_color = Color.Black;
                    ChangeCardColors(Card.red_color, Card.black_color);
                    Card.pic_color = Color.Brown;
                    Card.back_color = Color.White;
                    Card.face_color = new Color(0.9f,0.9f,0.9f);
                    break;
                case 6:
                    Card.pic_color = Color.White;
                    Card.back_color = Color.White;
                    Card.face_color = Color.White;
                    Card.red_color = Color.Red;
                    Card.black_color = Color.Black;
                    ChangeCardColors(Card.red_color, Card.black_color);
                    break;
            }
        }
        foreach (Stack kingstack in kingStacks)
        {
            bool got_one = false;
            if (kingstack.my_rect.Contains(click_POS))
            {
                held_stack.my_pos = kingstack.my_pos;
                
                //    BUGGY SHIT......   click in the stack, and if that card is visible, it, and every card AFTER it, are added to the held stack....
                for (int i = 0; i < kingstack.cards.Count  ; i++)
                {
                    
                   if (got_one == false)
                      {
                      if (kingstack.cards[i].visible)
                            {
                            if ((kingstack.cards[i].my_pos.Y < click_POS.Y && click_POS.Y - kingstack.cards[i].my_pos.Y < Stack.cardSpacing)  ||  (i == kingstack.cards.Count -1 && kingstack.cards[i].my_rect.Contains(click_POS))   )  //my_rect.Contains(click_POS))
                            {
                                got_one = true;
                                return_stack = kingstack;
                                mouseGrabDIFF = click_POS - kingstack.cards[i].my_pos;
                                held_stack.Add(kingstack.cards[i]);
                            }
                        }
                   
                    }
                   else // got_one == true
                    {
                        held_stack.Add(kingstack.cards[i]);
                    }
                }
                foreach (Card card in held_stack.cards)
                {
                    kingstack.cards.Remove(card);
                    kingstack.Update();
                }
            }
        }
        if (held_stack.cards.Count > 0)
        { 
            // just for the if structure
        }
        //click on deck, draw next card
        else if (deck.my_rect.Contains(click_POS))
        {
            if (deck.cards.Count > 2)
            {
                pile.Add(deck.cards.Last());
                pile.cards.Last().visible = true;
                deck.cards.RemoveAt(deck.cards.Count - 1);
                pile.Add(deck.cards.Last());
                pile.cards.Last().visible = true;
                deck.cards.RemoveAt(deck.cards.Count - 1);
                pile.Add(deck.cards.Last());
                pile.cards.Last().visible = true;
                deck.cards.RemoveAt(deck.cards.Count - 1);
            }
            else if (deck.cards.Count == 2)
            {
                pile.Add(deck.cards.Last());
                pile.cards.Last().visible = true;
                deck.cards.RemoveAt(deck.cards.Count -1);
                pile.Add(deck.cards.Last());
                pile.cards.Last().visible = true;
                deck.cards.RemoveAt(deck.cards.Count - 1);
            }
            else if (deck.cards.Count == 1)
            {
                pile.Add(deck.cards.Last());
                pile.cards.Last().visible = true;
                deck.cards.RemoveAt(deck.cards.Count - 1);
            }
            // if the deck is EMPTY, take the pile as the deck...
            else if (deck.cards.Count == 0)
            {
                if (pile.cards.Count > 0)
                {
                    foreach (Card c in pile.cards)
                    {
                        c.visible = false;
                        deck.Add(c);
                    }
                    deck.cards.Reverse();
                    pile.cards.Clear();
                }
                
            }
        }
        //click on the pile    add the card to lifted_stack, subtract it from the old stack. Set return stack, home of lifted stack......
        else if (pile.my_rect.Contains(click_POS))
        {
            if (pile.cards.Count > 0)
            {
                mouseGrabDIFF = click_POS - pile.my_pos;
                held_stack.Add(pile.cards[pile.cards.Count() - 1]);
                pile.cards.RemoveAt(pile.cards.Count() - 1);
                return_stack = pile;
            }
        }
    }
    static Color starColor = new Color(1.0f,1.0f,1.0f);
    static float g = 1f;
    static float b = 1f;
    public override void Update(MouseState currentMouse)
    {
        if (held_stack.cards.Count > 0)
        {
            held_stack.my_pos.X = currentMouse.X - mouseGrabDIFF.X;
            held_stack.my_pos.Y = currentMouse.Y - mouseGrabDIFF.Y;
            held_stack.Update();
        }
        if (Vector2.Distance(new Vector2(currentMouse.Position.X, currentMouse.Y), star_pos) < 20)
        {
            starAlpha = 0.65f;
        }
        else
        {
            if (starAlpha > 0.15f)
              starAlpha -= 0.01f;
            if (Vector2.Distance(new Vector2(currentMouse.Position.X, currentMouse.Y), star_pos2) < 20)
            {
                if (b > 0f)
                {
                    b -= 0.02f;
                    g -= 0.02f;
                    starColor = new Color(b,g,1f);
                }
            }
            else
            {
                if (b < 1f)
                {
                    b += 0.02f;
                    g += 0.02f;
                    starColor = new Color(b, g, 1f);
                }
            }
        }
    }
    static float starAlpha = 1.0f;
    public override void Draw(SpriteBatch spriteBatch)
    {
        spriteBatch.Draw(table, new Rectangle(0,0,1530,900), Color.White);
        spriteBatch.Draw(star, star_pos,null,Color.White*starAlpha,0,star_origin,0.777f,SpriteEffects.None,0);
        spriteBatch.Draw(star, star_pos2, null, starColor * (1.2f-g), 0, star_origin, 0.777f, SpriteEffects.None, 0);
        spriteBatch.Draw(Card.face, new Rectangle(deck.my_rect.X , deck.my_rect.Y , deck.my_rect.Width, deck.my_rect.Height), Color.Black * 0.2f);
            spriteBatch.Draw(Card.face, new Rectangle(pile.my_rect.X , pile.my_rect.Y , pile.my_rect.Width, pile.my_rect.Height), Color.Black * 0.2f);
        spriteBatch.Draw(Card.face, new Rectangle(ace_1.my_rect.X , ace_1.my_rect.Y , ace_1.my_rect.Width, ace_1.my_rect.Height), Color.Black * 0.2f);
        spriteBatch.Draw(Card.face, new Rectangle(ace_2.my_rect.X, ace_2.my_rect.Y, ace_2.my_rect.Width, ace_2.my_rect.Height), Color.Black * 0.2f);
        spriteBatch.Draw(Card.face, new Rectangle(ace_3.my_rect.X, ace_3.my_rect.Y, ace_3.my_rect.Width, ace_3.my_rect.Height), Color.Black * 0.2f);
        spriteBatch.Draw(Card.face, new Rectangle(ace_4.my_rect.X, ace_4.my_rect.Y, ace_4.my_rect.Width, ace_4.my_rect.Height), Color.Black * 0.2f);
        //SHADOW OF EACH DECK
        if (deck.cards.Count > 0)
        spriteBatch.Draw(Card.face, new Rectangle(deck.my_rect.X - deck.cards.Count, deck.my_rect.Y + 4, deck.my_rect.Width+ deck.cards.Count, deck.my_rect.Height), Color.Black * 0.2f);
        if (pile.cards.Count > 0)
            spriteBatch.Draw(Card.face, new Rectangle(pile.my_rect.X - pile.cards.Count, pile.my_rect.Y + 4, pile.my_rect.Width + pile.cards.Count, pile.my_rect.Height), Color.Black * 0.2f);
        foreach (Stack kingStack in kingStacks)
        {
            if (kingStack.cards.Count == 0)
                spriteBatch.Draw(Card.face, kingStack.my_rect, Color.Black * 0.2f);
            else
            {
                spriteBatch.Draw(Card.face, new Rectangle(kingStack.my_rect.X - 4, kingStack.my_rect.Y + 4, kingStack.my_rect.Width, kingStack.my_rect.Height), Color.Black * 0.2f);
                kingStack.Draw(spriteBatch);
            }
        }
        if (ace_1.cards.Count > 0)
        {
            spriteBatch.Draw(Card.face, new Rectangle(ace_1.my_rect.X - ace_1.cards.Count, ace_1.my_rect.Y + 4, ace_1.my_rect.Width+ace_1.cards.Count, ace_1.my_rect.Height), Color.Black * 0.2f);
            ace_1.cards[ace_1.cards.Count() - 1].Draw(spriteBatch);
        }
        if (ace_2.cards.Count > 0)
        {
            spriteBatch.Draw(Card.face, new Rectangle(ace_2.my_rect.X - ace_2.cards.Count, ace_2.my_rect.Y + 4, ace_2.my_rect.Width + ace_2.cards.Count, ace_2.my_rect.Height), Color.Black * 0.2f);
            ace_2.cards[ace_2.cards.Count() - 1].Draw(spriteBatch);
        }
        if (ace_3.cards.Count > 0)
        {
            spriteBatch.Draw(Card.face, new Rectangle(ace_3.my_rect.X - ace_3.cards.Count, ace_3.my_rect.Y + 4, ace_3.my_rect.Width + ace_3.cards.Count, ace_3.my_rect.Height), Color.Black * 0.2f);
            ace_3.cards[ace_3.cards.Count() - 1].Draw(spriteBatch);
        }
        if (ace_4.cards.Count > 0)
        {
            spriteBatch.Draw(Card.face, new Rectangle(ace_4.my_rect.X - ace_4.cards.Count, ace_4.my_rect.Y + 4, ace_4.my_rect.Width + ace_4.cards.Count, ace_4.my_rect.Height), Color.Black * 0.2f);
            ace_4.cards[ace_4.cards.Count() - 1].Draw(spriteBatch);
        }
        if (deck.cards.Count > 0)
        deck.cards[deck.cards.Count()-1].Draw(spriteBatch);
        if (pile.cards.Count > 0)
            pile.cards[pile.cards.Count()-1].Draw(spriteBatch);
        if (held_stack.cards.Count > 0)
        {
            spriteBatch.Draw(Card.face, new Rectangle(held_stack.my_rect.X - 10, held_stack.my_rect.Y + 10, held_stack.my_rect.Width, held_stack.my_rect.Height), Color.Black * 0.2f);
            foreach (Card card in held_stack.cards)
            card.Draw(spriteBatch);
        }
    }
}

}

And here is the CARD code:

using System;
using System.Collections.Generic;
using System.ComponentModel;
using System.Linq;
using Microsoft.Xna.Framework.Audio;
using Microsoft.Xna.Framework;
using Microsoft.Xna.Framework.Content;
using Microsoft.Xna.Framework.Graphics;
using Microsoft.Xna.Framework.Input;
using Microsoft.Xna.Framework.Media;
using System.IO;

namespace Kabale
{
class Card
{
// individual cards store individual values:
public int my_value;
public Color my_color;
public string my_type;
public Vector2 my_pos;
public Rectangle my_rect;

    Rectangle picture_rect = new Rectangle(0,0,111,176);
    Rectangle shadow_rect = new Rectangle(0,0,111,176);
    Rectangle value_rect;
    Vector2   value_pos_1;
    Vector2   value_pos_2;
    Rectangle symbol_rect;
    Vector2   symbol_pos_1;
    Vector2   symbol_pos_2;
    // But SOME card values are STATIC, apply to ALL cards.... Like their size and what not.
    public static Vector2 card_dimensions = new Vector2(160,210);
    public static Texture2D face;
    public static Texture2D face2;
    public static List<Texture2D> backs = new List<Texture2D>();
    public static Texture2D sprites;
    public static Texture2D pictures;
    public static Vector2 value_spacing = new Vector2(16,16); // just a valueable to tweak the poition of things on the card... 
    public static List<List<Vector4>> vector_lists = new List<List<Vector4>>();
    static float symbol_scale = 0.08f;  //  how big should the card icons be?
    public static void LoadContent(ContentManager content)
    {
        // add vector 4s here...  this creates a list of icon draw positions for each different card VALUE...  This way, we can adjust were icons are drawn, based on the TOTAL value of the card.....
        // I use a vector 4 to ALSO pass things like SCALE, so ACES can be drawn exceptionally big.....
        //1   If this card is an ACE......
        vector_lists.Add(new List<Vector4>());
        vector_lists[0].Add(new Vector4((4f/8f) * card_dimensions.X, (4f/8f) * card_dimensions.Y,0,0.12f ));
        //2
        vector_lists.Add(new List<Vector4>());
        vector_lists[1].Add(new Vector4((4f / 8f) * card_dimensions.X, (1.2f / 8f) * card_dimensions.Y, 0, symbol_scale));
        vector_lists[1].Add(new Vector4((4f / 8f) * card_dimensions.X, (6.8f / 8f) * card_dimensions.Y, MathHelper.Pi, symbol_scale));
        //3
        vector_lists.Add(new List<Vector4>());
        vector_lists[2].Add(new Vector4((4f / 8f) * card_dimensions.X, (1.2f / 8f) * card_dimensions.Y, 0, symbol_scale));
        vector_lists[2].Add(new Vector4((4f / 8f) * card_dimensions.X, (4f / 8f) * card_dimensions.Y, 0, symbol_scale));
        vector_lists[2].Add(new Vector4((4f / 8f) * card_dimensions.X, (6.8f / 8f) * card_dimensions.Y, MathHelper.Pi, symbol_scale));
        //4
        vector_lists.Add(new List<Vector4>());
        vector_lists[3].Add(new Vector4((2f / 7f) * card_dimensions.X, (1.2f / 8f) * card_dimensions.Y, 0, symbol_scale));
        vector_lists[3].Add(new Vector4((2f / 7f) * card_dimensions.X, (6.8f / 8f) * card_dimensions.Y, MathHelper.Pi, symbol_scale));
        vector_lists[3].Add(new Vector4((5f / 7f) * card_dimensions.X, (1.2f / 8f) * card_dimensions.Y, 0, symbol_scale));
        vector_lists[3].Add(new Vector4((5f / 7f) * card_dimensions.X, (6.8f / 8f) * card_dimensions.Y, MathHelper.Pi, symbol_scale));
        //5
        vector_lists.Add(new List<Vector4>());
        vector_lists[4].Add(new Vector4((2f / 7f) * card_dimensions.X, (1.2f / 8f) * card_dimensions.Y, 0, symbol_scale));
        vector_lists[4].Add(new Vector4((2f / 7f) * card_dimensions.X, (6.8f / 8f) * card_dimensions.Y, MathHelper.Pi, symbol_scale));
        vector_lists[4].Add(new Vector4((4f / 8f) * card_dimensions.X, (4f / 8f) * card_dimensions.Y, 0, symbol_scale));
        vector_lists[4].Add(new Vector4((5f / 7f) * card_dimensions.X, (1.2f / 8f) * card_dimensions.Y, 0, symbol_scale));
        vector_lists[4].Add(new Vector4((5f / 7f) * card_dimensions.X, (6.8f / 8f) * card_dimensions.Y, MathHelper.Pi, symbol_scale));
        //6
        vector_lists.Add(new List<Vector4>());
        vector_lists[5].Add(new Vector4((2f / 8f) * card_dimensions.X, (1.2f / 8f) * card_dimensions.Y, 0, symbol_scale));
        vector_lists[5].Add(new Vector4((2f / 8f) * card_dimensions.X, (4f / 8f) * card_dimensions.Y, 0, symbol_scale));
        vector_lists[5].Add(new Vector4((2f / 8f) * card_dimensions.X, (6.8f / 8f) * card_dimensions.Y, MathHelper.Pi, symbol_scale));
        vector_lists[5].Add(new Vector4((6f / 8f) * card_dimensions.X, (1.2f / 8f) * card_dimensions.Y, 0, symbol_scale));
        vector_lists[5].Add(new Vector4((6f / 8f) * card_dimensions.X, (4f / 8f) * card_dimensions.Y, 0, symbol_scale));
        vector_lists[5].Add(new Vector4((6f / 8f) * card_dimensions.X, (6.8f / 8f) * card_dimensions.Y, MathHelper.Pi, symbol_scale));
        //7
        vector_lists.Add(new List<Vector4>());
        vector_lists[6].Add(new Vector4((2f / 8f) * card_dimensions.X, (1.2f / 8f) * card_dimensions.Y, 0, symbol_scale));
        vector_lists[6].Add(new Vector4((2f / 8f) * card_dimensions.X, (4f / 8f) * card_dimensions.Y, 0, symbol_scale));
        vector_lists[6].Add(new Vector4((2f / 8f) * card_dimensions.X, (6.8f / 8f) * card_dimensions.Y, MathHelper.Pi, symbol_scale));
        vector_lists[6].Add(new Vector4((4f / 8f) * card_dimensions.X, (2.6f / 8f) * card_dimensions.Y, 0, symbol_scale));
        vector_lists[6].Add(new Vector4((6f / 8f) * card_dimensions.X, (1.2f / 8f) * card_dimensions.Y, 0, symbol_scale));
        vector_lists[6].Add(new Vector4((6f / 8f) * card_dimensions.X, (4f / 8f) * card_dimensions.Y, 0, symbol_scale));
        vector_lists[6].Add(new Vector4((6f / 8f) * card_dimensions.X, (6.8f / 8f) * card_dimensions.Y, MathHelper.Pi, symbol_scale));
        //8
        vector_lists.Add(new List<Vector4>());
        vector_lists[7].Add(new Vector4((2f / 7f) * card_dimensions.X, (1f / 8f) * card_dimensions.Y, 0, symbol_scale));
        vector_lists[7].Add(new Vector4((2f / 7f) * card_dimensions.X, (3f / 8f) * card_dimensions.Y, 0, symbol_scale));
        vector_lists[7].Add(new Vector4((2f / 7f) * card_dimensions.X, (5f / 8f) * card_dimensions.Y, MathHelper.Pi, symbol_scale));
        vector_lists[7].Add(new Vector4((2f / 7f) * card_dimensions.X, (7f / 8f) * card_dimensions.Y, MathHelper.Pi, symbol_scale));
        vector_lists[7].Add(new Vector4((5f / 7f) * card_dimensions.X, (1f / 8f) * card_dimensions.Y, 0, symbol_scale));
        vector_lists[7].Add(new Vector4((5f / 7f) * card_dimensions.X, (3f / 8f) * card_dimensions.Y, 0, symbol_scale));
        vector_lists[7].Add(new Vector4((5f / 7f) * card_dimensions.X, (5f / 8f) * card_dimensions.Y, MathHelper.Pi, symbol_scale));
        vector_lists[7].Add(new Vector4((5f / 7f) * card_dimensions.X, (7f / 8f) * card_dimensions.Y, MathHelper.Pi, symbol_scale));
        //9
        vector_lists.Add(new List<Vector4>());
        vector_lists[8].Add(new Vector4((2f / 7f) * card_dimensions.X, (1f / 8f) * card_dimensions.Y, 0, symbol_scale));
        vector_lists[8].Add(new Vector4((2f / 7f) * card_dimensions.X, (3f / 8f) * card_dimensions.Y, 0, symbol_scale));
        vector_lists[8].Add(new Vector4((2f / 7f) * card_dimensions.X, (5f / 8f) * card_dimensions.Y, MathHelper.Pi, symbol_scale));
        vector_lists[8].Add(new Vector4((2f / 7f) * card_dimensions.X, (7f / 8f) * card_dimensions.Y, MathHelper.Pi, symbol_scale));
        vector_lists[8].Add(new Vector4((4f / 8f) * card_dimensions.X, (4f / 8f) * card_dimensions.Y, 0, symbol_scale));
        vector_lists[8].Add(new Vector4((5f / 7f) * card_dimensions.X, (1f / 8f) * card_dimensions.Y, 0, symbol_scale));
        vector_lists[8].Add(new Vector4((5f / 7f) * card_dimensions.X, (3f / 8f) * card_dimensions.Y, 0, symbol_scale));
        vector_lists[8].Add(new Vector4((5f / 7f) * card_dimensions.X, (5f / 8f) * card_dimensions.Y, MathHelper.Pi, symbol_scale));
        vector_lists[8].Add(new Vector4((5f / 7f) * card_dimensions.X, (7f / 8f) * card_dimensions.Y, MathHelper.Pi, symbol_scale));
        //10
        vector_lists.Add(new List<Vector4>());
        vector_lists[9].Add(new Vector4((2f / 7f) * card_dimensions.X, (1f / 8f) * card_dimensions.Y, 0, symbol_scale));
        vector_lists[9].Add(new Vector4((2f / 7f) * card_dimensions.X, (3f / 8f) * card_dimensions.Y, 0, symbol_scale));
        vector_lists[9].Add(new Vector4((2f / 7f) * card_dimensions.X, (5f / 8f) * card_dimensions.Y, MathHelper.Pi, symbol_scale));
        vector_lists[9].Add(new Vector4((2f / 7f) * card_dimensions.X, (7f / 8f) * card_dimensions.Y, MathHelper.Pi, symbol_scale));
        vector_lists[9].Add(new Vector4((4f / 8f) * card_dimensions.X, (2f / 8f) * card_dimensions.Y, 0, symbol_scale));
        vector_lists[9].Add(new Vector4((4f / 8f) * card_dimensions.X, (6f / 8f) * card_dimensions.Y, 0, symbol_scale));
        vector_lists[9].Add(new Vector4((5f / 7f) * card_dimensions.X, (1f / 8f) * card_dimensions.Y, 0, symbol_scale));
        vector_lists[9].Add(new Vector4((5f / 7f) * card_dimensions.X, (3f / 8f) * card_dimensions.Y, 0, symbol_scale));
        vector_lists[9].Add(new Vector4((5f / 7f) * card_dimensions.X, (5f / 8f) * card_dimensions.Y, MathHelper.Pi, symbol_scale));
        vector_lists[9].Add(new Vector4((5f / 7f) * card_dimensions.X, (7f / 8f) * card_dimensions.Y, MathHelper.Pi, symbol_scale));
        pictures = content.Load<Texture2D>(".\\pictures");
        face = content.Load<Texture2D>(".\\face");
        face2 = content.Load<Texture2D>(".\\face2");
        backs.Add(content.Load<Texture2D>(".\\back"));
        backs.Add(content.Load<Texture2D>(".\\back3"));
        backs.Add(backs[0]);
        backs.Add(content.Load<Texture2D>(".\\back2"));
        
        backs.Add(content.Load<Texture2D>(".\\back4"));
        backs.Add(content.Load<Texture2D>(".\\back5"));
        backs.Add(content.Load<Texture2D>(".\\back6"));
        sprites = content.Load<Texture2D>(".\\sprites_1");
    }
    public void Update()
    {
     
        my_rect = new Rectangle((int)my_pos.X, (int)my_pos.Y,(int)card_dimensions.X,(int)card_dimensions.Y);
        shadow_rect = my_rect;
        shadow_rect.Y -= 1;
        value_pos_1 = new Vector2(my_pos.X + value_spacing.X, my_pos.Y + value_spacing.Y);
        value_pos_2 = new Vector2(my_pos.X + card_dimensions.X - value_spacing.X, my_pos.Y + card_dimensions.Y - value_spacing.Y);
        symbol_pos_1 = new Vector2(my_pos.X + value_spacing.X, my_pos.Y + 3  * value_spacing.Y);
        symbol_pos_2 = new Vector2(my_pos.X + card_dimensions.X - value_spacing.X, my_pos.Y + card_dimensions.Y - 3 * value_spacing.Y);
    }
    public bool visible = false;
    public Card(string type,Color color, int value)
    {
        my_color = color;
        my_value = value;
        my_type = type;
        switch (type)
        {
            case "Hearts":
                symbol_rect = new Rectangle(384,0,384,448);
                picture_rect.Y = 176;
                break;
            case "Diamonds":
                symbol_rect = new Rectangle(0, 0, 384, 448);
                picture_rect.Y = 176*3;
                break;
            case "Clovers":
                symbol_rect = new Rectangle(384,448,384,448);
                picture_rect.Y = 0;
                break;
            case "Spades":
                symbol_rect = new Rectangle(0, 448, 384, 448);
                picture_rect.Y = 176*2;
                break;
        }
        switch (value)
        {
            case 1:
                value_rect = new Rectangle(768,0,64,64);
                
                break;
            case 2:
                value_rect = new Rectangle(768, 64, 64, 64);
                break;
            case 3:
                value_rect = new Rectangle(768, 2 * 64, 64, 64);
                break;
            case 4:
                value_rect = new Rectangle(768, 3 * 64, 64, 64);
                break;
            case 5:
                value_rect = new Rectangle(768, 4 * 64, 64, 64);
                break;
            case 6:
                value_rect = new Rectangle(768, 5 * 64, 64, 64);
                break;
            case 7:
                value_rect = new Rectangle(768, 6 * 64, 64, 64);
                break;
            case 8:
                value_rect = new Rectangle(768, 7 * 64, 64, 64);
                break;
            case 9:
                value_rect = new Rectangle(768, 8 * 64, 64, 64);
                break;
            case 10:
                value_rect = new Rectangle(768, 9 * 64, 64, 64);
                break;
            case 11:
                value_rect = new Rectangle(768, 10 * 64, 64, 64);
                picture_rect.X = 0;
                break;
            case 12:
                value_rect = new Rectangle(768, 11 * 64, 64, 64);
                picture_rect.X = 111;
                break;
            case 13:
                value_rect = new Rectangle(768, 12 * 64, 64, 64);
                picture_rect.X = 222;
                break;
        }
    }
    //sprite origins...
    static Vector2 typeOrigin = new Vector2(384/2, 224);
    static Vector2 valueOrigin = new Vector2(32,32);
    static Vector2 pictureOrigin = new Vector2(111/2f,176/2f);
    public static int current_back = 0;
    public static Color face_color = Color.Wheat;
    public static Color pic_color = Color.Wheat;
    public static Color back_color = Color.White;
    public static Color red_color = Color.Red;
    public static Color black_color = Color.Black;
    public void Draw(SpriteBatch spriteBatch)
    {
        spriteBatch.Draw(face, shadow_rect, Color.Black * 0.002f);
        if (visible)
        {
            
            spriteBatch.Draw(face,my_rect,face_color);
            spriteBatch.Draw(face2, my_rect, Color.White * 0.28f);
            spriteBatch.Draw(sprites, value_pos_1, value_rect, my_color, 0, valueOrigin, 0.6f, SpriteEffects.None, 0);
            spriteBatch.Draw(sprites, value_pos_2, value_rect, my_color, 0, valueOrigin, 0.6f, SpriteEffects.None, 0);
            spriteBatch.Draw(sprites, symbol_pos_1, symbol_rect, my_color, 0, typeOrigin, 0.05f, SpriteEffects.None, 0);
            spriteBatch.Draw(sprites, symbol_pos_2, symbol_rect, my_color, 0, typeOrigin, 0.05f, SpriteEffects.None, 0);
            // number cards
            if (my_value < 11)
            {
                if (vector_lists.Count > my_value - 1)
                    foreach (Vector4 vec in vector_lists[my_value - 1])
                    {
                        spriteBatch.Draw(sprites, new Vector2(vec.X, vec.Y) + my_pos, symbol_rect, my_color, vec.Z, typeOrigin, vec.W, SpriteEffects.None, 0);
                    }
            }
            // picture cards
            else
            {
                foreach (Vector4 vec in vector_lists[0])
                {
                    spriteBatch.Draw(pictures, new Vector2(vec.X, vec.Y) + my_pos, picture_rect, pic_color, 0,pictureOrigin , 0.9f, SpriteEffects.None, 0);
                }
                // spriteBatch.Draw(pictures,  my_pos+card_dimensions/2f, picture_rect, my_color, vec.Z, typeOrigin, vec.W, SpriteEffects.None, 0);
            }
        }
        // back side...
        else
        {
            spriteBatch.Draw(backs[current_back], my_rect, back_color);
           // spriteBatch.Draw(face2, my_rect, null,Color.White * 0.98f,0,Vector2.Zero,SpriteEffects.FlipHorizontally,0);
        }
    }
}

}

1 Like

And the stack.cs looks like this:

using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
using Microsoft.Xna.Framework;
using Microsoft.Xna.Framework.Graphics;
using Microsoft.Xna.Framework.Input;

namespace Kabale
{
class Stack
{
public Vector2 my_pos;
public Rectangle my_rect;
public bool open = false;
public List cards;
public static int cardSpacing = 32;
public Stack(Vector2 pos)
{
my_pos = pos;

        my_rect = new Rectangle((int)my_pos.X,(int)my_pos.Y,(int)Card.card_dimensions.X,(int)Card.card_dimensions.Y);
        cards = new List<Card>();
    }
    public void Open()
    {
        open = true;
        Update();
    }
    public void Add(Card card)
    {
        cards.Add(card);
        Update();
    }
    public void Update()
    {
        my_rect = new Rectangle((int)my_pos.X, (int)my_pos.Y, (int)Card.card_dimensions.X, (int)Card.card_dimensions.Y);
        if (open)
        {
            if (cards.Count > 0)
                my_rect.Height = Stack.cardSpacing * cards.Count - cardSpacing + (int)Card.card_dimensions.Y;
            else
                my_rect.Height = (int)Card.card_dimensions.Y;
            int counter = 0;
            foreach (Card card in cards)
            {
                card.my_pos = new Vector2( my_pos.X , my_pos.Y + cardSpacing * counter);
                counter++;
                card.Update();
            }
        }
        else
        {
            foreach (Card card in cards)
            {
                card.my_pos = my_pos;
                card.Update();
            }
         }
    }
    public void Draw(SpriteBatch spriteBatch)
    {
        if (cards.Count > 0)
        {
         
            foreach (Card card in cards)
            {
                card.Draw(spriteBatch);
            }
        }
    }
}

}

1 Like

@monopalle Thanks for posting this, much appreciated.

Finallly, I have a method in a seperate class to generate numbers, but it can go anywhere, it goes like this:

using System;
using System.Collections.Generic;
using System.ComponentModel;
using System.Linq;
using Microsoft.Xna.Framework.Audio;
using Microsoft.Xna.Framework;
using Microsoft.Xna.Framework.Content;
using Microsoft.Xna.Framework.Graphics;
using Microsoft.Xna.Framework.Input;
using Microsoft.Xna.Framework.Media;
using System.IO;
using System.Security.Cryptography;

namespace Kabale
{
class CommonMethods
{
static RandomNumberGenerator _rand = RandomNumberGenerator.Create();
public static int RandomNext(int min, int max)
{
if (min > max) throw new ArgumentOutOfRangeException(“min”);

        byte[] bytes = new byte[4];
        _rand.GetBytes(bytes);
        uint next = BitConverter.ToUInt32(bytes, 0);
        int range = max - min;
        return (int)((next % range) + min);
    }
}

}

and, heres spritesheets: I generate my cards from components, so I can tweak the same assets programmatically


It may be hard to see, its white on transparant :slightly_smiling:

The picture-cards, or royal family is just internet pics
 -They can of coarse be over-written by any desired texture.

This gradient is used on top at different alpha values to give some reflective gloss / casual lighting feel

And the table, is just a texture I made by changing the perspective on a picture of some random wood, and adding shadow/light to make cracks/gaps


1 Like

hmm that is pretty cool @monopalle, making new assets is kind of a pain so generating assets on the fly seems like a good approach :slight_smile:

1 Like