//Author: ^-^ Veerle ^-^ import java.awt.*; import java.awt.event.*; import javax.swing.JOptionPane; public class solitareWindow extends solitareDefaultLayout implements MouseListener, MouseMotionListener, KeyListener { private final int refreshRate = 5; //How many pixels must be moved before a repaint happens private int refreshCounter = 0; //Used with the refresh rate, counts how many pixels have been moved private int backCardImageUsing = 2; //The back card image to be used private int currentDeckCard = 0, deckCardsShowing = 0; //The current card number in the deck private int columnDraggedFrom = 0, dragStartCard = 0; //If a drag was unsucessful, this can be used to restore the card (0 - 6 columns, 7 - 10 aces, 11 deck) private int mouseCurrentX = 0, mouseCurrentY = 0, mouseStartingX = 0, mouseStartingY = 0; private boolean hasWon = true, isDragging = false, updateDrag = false; private solitareCard currentCard = null; //Used to store the current card that is being processed public solitareWindow () { this.addMouseListener(this); this.addMouseMotionListener(this); this.addKeyListener(this); } private void resetGame () { currentDeckCard = 0; deckCardsShowing = 0; isDragging = false; columnDraggedFrom = 0; hasWon = false; currentCard = null; } private void usedDeckCard () //Method called if a deck card was sucessfully used { for (int showing = 0; showing < 4; showing++) //Loop 4 times { if (currentDeckCard == showing || showing == 3) { deckCardsShowing = showing; if (showing != 0) { currentDeckCard--; } break; } } } private void flipDeckCards () //Method called if the deck was clicked { if (deckCardsShowing == 0) //If no cards are showing { for (int size = 0; size < 4; size++) //loop 4 times { if (vecDeck.size() == size || size == 3) //If the current size is equal to the size of the deck (or last loop, meaning the size of the deck is 3 or greater) { deckCardsShowing = size; //Number of cards showing is the size of the deck (or 3 if size is greater then 3) currentDeckCard = deckCardsShowing - 1; //Current card number is size - 1 (2 if size is greater then 3) break; //Exits the loop } } } else //If cards are showing { for (int size = 3; size >= 0; size--) //Loop 4 times { if ((currentDeckCard + size) > (vecDeck.size() - 1)) //If adding so many cards will take it over the limit { continue; } else //If adding the cards won't take it over the limit { deckCardsShowing = size; //Amount of cards to show equals the loop number if (size == 0) //If size is 0 (last card in the deck) { currentDeckCard = 0; //Start again } else //If at least one card was added { currentDeckCard += deckCardsShowing; //Current card is itself + the number of cards added } break; //Exits the loop } } } repaint(); //Show the change (if any) } protected void drawExtra (Graphics g) { //Draw the deck if (vecDeck.size() != 0) //If the deck has at least one card in it { drawCard(g, 10, 10, imgCardBack[backCardImageUsing]); } //Draw the deck cards that are showing for (int cards = deckCardsShowing; cards > 0; cards--) //Loop for how many deck cards are showing { currentCard = (solitareCard)vecDeck.elementAt(currentDeckCard - (cards - 1)); drawCard(g, (100 + (20 * (deckCardsShowing - cards))), 10, currentCard.getImg()); } //Draw the columns for (int column = 0; column < 7; column++) { if (vecColumns[column].size() == 0) { continue; } int gap = 0; for (int row = 0; row < vecColumns[column].size(); row++) { currentCard = (solitareCard)vecColumns[column].elementAt(row); if (currentCard.isFaceDown()) { drawCard(g, ((column * 90) + 10), (120 + gap), imgCardBack[backCardImageUsing]); gap += gapFaceDown; } else { drawCard(g, ((column * 90) + 10), (120 + gap), currentCard.getImg()); gap += gapFaceUp; } } } //Draw the Aces for (int column = 0; column < 4; column++) { if (vecAces[column].size() == 0) { continue; } else { currentCard = (solitareCard)vecAces[column].elementAt(vecAces[column].size() - 1); drawCard(g, ((column * 90) + 280), 10, currentCard.getImg()); } } //Draw a dragged stack if (isDragging) { for (int card = 0; card < vecDrag.size(); card++) { currentCard = (solitareCard)vecDrag.elementAt(card); drawCard(g, mouseStartingX + mouseCurrentX - 30, mouseStartingY + (mouseCurrentY + (gapFaceUp * card)) - 30, currentCard.getImg()); } } } private void drawCard (Graphics g, int startX, int startY, Image imgCard) { g.drawImage(imgCard, startX, startY, this); g.setColor(new Color(149,146,140)); //Grey g.drawRect(startX, startY, 71, 96); //Draw a border around the card } public void checkAceMove (int colour, int rank, int column, int card) { boolean canBePlaced = false; //Flags if the card can be placed in this ace stack for (int ace = 0; ace < 4; ace++) //Check each ace stack { if (vecAces[ace].size() != 0) //If the current ace stack isn't empty { currentCard = (solitareCard)vecAces[ace].lastElement(); //Get the last card in the current ace stack canBePlaced = currentCard.checkLegalAceMove(rank, colour); //Test if it can legally be placed } else //If the stack is empty { if (rank == 1) //If the card is an ACE { canBePlaced = true; } } if (canBePlaced) //If can be legally placed { if (column != -1) //If getting the card from a column stack { vecAces[ace].addElement(vecColumns[column].remove(card)); //Removes the card from the column stack and adds it to the ace stack columnFaceUpStartIndex[column] = 0; columnFaceUpStartLocation[column] = 120; } else //If getting from the deck { vecAces[ace].addElement(vecDeck.remove(card)); usedDeckCard(); } repaint(); break; } } //End looping through all the ace stacks } public void updateColumnSize (int column) { columnFaceUpStartLocation[column] = 120; columnFaceUpStartIndex[column] = 0; for (int card = 0; card < vecColumns[column].size(); card++) { currentCard = (solitareCard)vecColumns[column].elementAt(card); if (currentCard.isFaceDown()) { columnFaceUpStartLocation[column] += gapFaceDown; } else { columnFaceUpStartIndex[column] = card; break; } } } public void newGame () { resetGame(); shuffleCards(); layoutCards(); repaint(); } public void setCardBack (int backCardImageUsing) { this.backCardImageUsing = backCardImageUsing; repaint(); } public void mouseClicked (MouseEvent e) //These actions are performed if the mouse is simply clicked, not dragged { if (e.getButton() == MouseEvent.BUTTON1 && !isDragging && !hasWon) //If the left click was used and not currently dragging { int x = e.getX(); int y = e.getY(); if (deckCardsShowing != 0) //Make sure deck cards showing isn't empty { //Checks that the top deck card showing was double clicked if ((x >= (100 + ((deckCardsShowing - 1) * 20)) && x <= (171 + ((deckCardsShowing - 1) * 20))) && (y >= 10 && y <= 106) && e.getClickCount() >= 2) { currentCard = (solitareCard)vecDeck.elementAt(currentDeckCard); checkAceMove(currentCard.getColour(), currentCard.getRank(), -1, currentDeckCard); //Checks if the card can be moved to an ace pile } } if ((x >= 10 && x <= 81) && (y >= 10 && y <= 106)) //If the deck was clicked { flipDeckCards(); //Make the deck perform an action, such as showing you new cards } else if ((x >= 10 && x <= 621) && y >= 120) //If the click was in the columns area { for (int column = 0; column < 7; column++) //Loop 7 times for all the columns { if ((x >= ((column * 90) + 10) && x <= ((column * 90) + 81))) //If the current column is the one that was clicked within { int cardStart = 120; //starting point that is raised per face down card for (int card = 0; card < vecColumns[column].size(); card++) //Loop for how many cards are in the column { currentCard = (solitareCard)vecColumns[column].elementAt(card); if (card != vecColumns[column].size() - 1) //If not last card { if (currentCard.isFaceDown()) //If the current card is face down { cardStart += gapFaceDown; } else { cardStart += gapFaceUp; } } else //If last card { if (y >= cardStart && y <= (cardStart + 96)) //If mouse was clicked on the last card { if (currentCard.isFaceDown()) { currentCard.setFaceDown(false); updateColumnSize(column); repaint(); } else if (!currentCard.isFaceDown() && e.getClickCount() >= 2) //If the card face is showing and the user double clicked { checkAceMove(currentCard.getColour(), currentCard.getRank(), column, card); updateColumnSize(column); } //End testing what to do with the card clicked } //End testing if the last card was clicked } //End testing if the current card is the last card } //End looping through all the cards in the current column break; } //End testing if the current column is the one that was clicked within } //End looping through all the columns } //End testing which area the mouse was clicked in } //End testing if the left click was used } public void mouseEntered (MouseEvent e) { } public void mouseExited (MouseEvent e) { mouseReleased(e); } public void mousePressed (MouseEvent e) { if (e.getButton() == MouseEvent.BUTTON1 && !hasWon) { int x = e.getX(); int y = e.getY(); mouseStartingX = x; mouseStartingY = y; if (deckCardsShowing != 0) //If at least one card is showing { //If the cursor is over the top level deck card showing if ((x >= 100 + (20 * (deckCardsShowing - 1)) && x <= 171 + (20 * (deckCardsShowing - 1))) && (y >= 10 && y <= 106)) { columnDraggedFrom = 11; updateDrag = true; } } if ((x >= 10 && x <= 621) && y >= 120) //If the column areas were clicked { for (int column = 0; column < 7; column++) { if ((x >= ((column * 90) + 10) && x <= ((column * 90) + 81))) //If the column clicked within is the current { int newStart = columnFaceUpStartLocation[column]; boolean clickedCard = false; for (int card = columnFaceUpStartIndex[column]; card < vecColumns[column].size(); card++) //Starting from the first face up card, loop through them all { if (card == (vecColumns[column].size() - 1)) //Last card { if (y >= newStart && y <= newStart + 96) { clickedCard = true; } } else { if (y >= newStart && y <= newStart + gapFaceUp) { clickedCard = true; } else { newStart += gapFaceUp; } } if (clickedCard) //Found clicked card { columnDraggedFrom = column; dragStartCard = card; updateDrag = true; break; } } break; } } } else if ((x >= 280 && x <= 621) && (y >= 10 && y <= 106)) //Clicking one of the ace stacks { for (int ace = 0; ace < 4; ace++) { if (x >= ((ace * 90) + 280) && x <= ((ace * 90) + 351)) //If the current ace stack is the one you clicked { if (vecAces[ace].size() != 0) { columnDraggedFrom = ace + 7; updateDrag = true; } break; } } } } } public void mouseReleased (MouseEvent e) //Checks if a dragged stack can be placed { int x = e.getX(); int y = e.getY(); updateDrag = false; if (isDragging) //If been dragging { currentCard = (solitareCard)vecDrag.elementAt(0); boolean restoreStack = true; if ((x >= 10 && x <= 621) && y >= 120) //If dropping over the columns area { for (int column = 0; column < 7; column++) //check each column { if ((x >= ((column * 90) + 10) && x <= ((column * 90) + 81))) //If current column is the one you are dropping over { if (vecColumns[column].size() == 0 && y <= 216) //If placed on an empty column stack { if (currentCard.getRank() == 13) //If the first card being placed is king { while (vecDrag.size() != 0) { vecColumns[column].addElement(vecDrag.remove(0)); } updateColumnSize(column); restoreStack = false; } } else //If current stack is occupied { boolean isBlack = currentCard.isBlack(); int rank = currentCard.getRank(); currentCard = (solitareCard)vecColumns[column].lastElement(); if (!currentCard.isFaceDown()) { int height = (((vecColumns[column].size() - 1) - columnFaceUpStartIndex[column]) * gapFaceUp); if (y <= height + columnFaceUpStartLocation[column] + 96) //If releasing the mouse anywhere in the stack { if (currentCard.checkLegalColumnMove(rank, isBlack)) { while (vecDrag.size() != 0) { vecColumns[column].addElement(vecDrag.remove(0)); } updateColumnSize(column); restoreStack = false; } } } } break; } } } else if ((x >= 280 && x <= 621) && (y >= 10 && y <= 106)) //If the aces area was released over { if (vecDrag.size() == 1) //If only one card is being dragged { for (int ace = 0; ace < 4; ace++) //Search all 4 ace stacks { if (x >= ((ace * 90) + 280) && x <= ((ace * 90) + 351)) //If current ace stack is the one dropping over { if (vecAces[ace].size() == 0) { if (currentCard.getRank() == 1) { vecAces[ace].addElement(vecDrag.remove(0)); restoreStack = false; } } else { int colour = currentCard.getColour(); int rank = currentCard.getRank(); currentCard = (solitareCard)vecAces[ace].lastElement(); if (currentCard.checkLegalAceMove(rank, colour)) { vecAces[ace].addElement(vecDrag.remove(0)); restoreStack = false; } } break; } } } } if (restoreStack) //If move was not sucessful { if (columnDraggedFrom <= 6) { while (vecDrag.size() != 0) { vecColumns[columnDraggedFrom].addElement(vecDrag.remove(0)); } } else if (columnDraggedFrom >= 7 && columnDraggedFrom <= 10) { while (vecDrag.size() != 0) { vecAces[columnDraggedFrom - 7].addElement(vecDrag.remove(0)); } } else if (columnDraggedFrom == 11) { deckCardsShowing++; currentDeckCard++; vecDeck.add(currentDeckCard, vecDrag.remove(0)); } } else //If the move was sucessful { if (columnDraggedFrom == 11) { if (currentDeckCard > 2) { deckCardsShowing++; } } for (int ace = 0; ace < 4; ace++) { if (vecAces[ace].size() != 13) { hasWon = false; } } if (hasWon) { JOptionPane.showMessageDialog(null, "Congrats, you have completed the game! Press 'n' to start a new game", "Game complete", JOptionPane.INFORMATION_MESSAGE); } } repaint(); } isDragging = false; mouseStartingX = 0; mouseStartingY = 0; columnDraggedFrom = 0; } public void mouseDragged (MouseEvent e) { if (updateDrag) { if (columnDraggedFrom >= 0 && columnDraggedFrom <= 6) { while (vecColumns[columnDraggedFrom].size() > dragStartCard) { vecDrag.addElement(vecColumns[columnDraggedFrom].remove(dragStartCard)); } } else if (columnDraggedFrom >= 7 && columnDraggedFrom <= 10) { vecDrag.addElement(vecAces[columnDraggedFrom].remove(vecAces[columnDraggedFrom].size() - 1)); } else { vecDrag.addElement(vecDeck.remove(currentDeckCard)); deckCardsShowing--; currentDeckCard--; } updateDrag = false; isDragging = true; } if (isDragging) { if (refreshCounter >= refreshRate) { mouseCurrentX = e.getX() - mouseStartingX; mouseCurrentY = e.getY() - mouseStartingY; refreshCounter = 0; repaint(); } else { refreshCounter++; } } } public void mouseMoved (MouseEvent e) { } public void keyReleased (KeyEvent e) { } public void keyTyped (KeyEvent e) { } public void keyPressed (KeyEvent e) //Let you change the card back by pressing a number 0 - 5 { char c = e.getKeyChar(); if (Character.isDigit(c)) //current key is a number { int keyNum = Integer.parseInt("" + c); if (keyNum >= 0 && keyNum <= 5) { setCardBack(keyNum); } } else if (c == 'n') { newGame(); } } }