import java.awt.*; import java.awt.event.*; import java.util.*; /** * This class is the external listener that catches all mouse, mouse motion, key, * action, and item events that are fired by the roulette applet. * * @author Chris Juffre, cjuffre@cs.uml.edu * @since 1.0 * @version 1.0 */ public class RouletteListener extends MouseAdapter implements MouseMotionListener, KeyListener, ActionListener, ItemListener, FocusListener { /** * The applet that instantiates this class * @since 1.0 */ public Roulette app = null ; /** * The graphics context of the applet * @since 1.0 */ public Graphics g = null ; /** * The constructor for the external listener. * * @param app Applet object to listener to events for * @param g Graphics context for the applet * @since 1.0 */ public RouletteListener( Roulette app, Graphics g ) { this.app = app ; this.g = g ; } /** * This function is called by the system when the mouse is clicked, that is, * pressed and immediately released. *
* (This function was provided by the original Hall&Brown program.) * * @param event MouseEvent object supplied by system * @since 1.0 */ public void mousePressed( MouseEvent event ) { // Just logs position the mouse was clicked at for debugging purposes System.out.println("Mouse Clicked at: X:" + Integer.toString(event.getX()) + " Y:" + Integer.toString(event.getY())); /* Set focus back to applet in rare case that is doesn't have it. */ app.requestFocus(); /* Sets the iRouletteIndexSelected to the index on the board that was selected. This is only set if the mouse event takes place on the roulette board. This is the only time a mouse event is actually needed. This is done for efficiency purposes so not all position have to be check individually on every mouse click. The system.out calls provide debugging information in case of problems. */ int iRouletteIndex = -1; if( new Rectangle(app.iSideBorders, app.iTopBorder, 900, 400). contains(event.getX(), event.getY())) { iRouletteIndex = FindClick(event); System.out.println("FindClick returned roulette index: " + Integer.toString(iRouletteIndex)); } else System.out.println("Not Clicked on the roulette board."); /* If the mouse click was a right-mouse button and a valid roulette index and can process by processing a left or mouse click. */ if(event.getModifiers() == InputEvent.BUTTON3_MASK && iRouletteIndex != -1) { // Log a right mouse click System.out.println("Right Mouse Click"); /* If there are no current bets, then the report must be visible so delete it. */ if(app.vecBets.isEmpty()) { app.txtCurrentBets.setText(""); app.lblCurrentBets.setText("Current Bets:"); } /* If there are current bets and the click was on a valid square. */ if(app.vecBets.isEmpty() == false && iRouletteIndex != -1) { /* Start at the end of the current bets vector and go down checking for a bet equal to the one clicked on. This is done because it is more likely people will remove a bet that they just made than a while back. */ for(int iVecPosition = app.vecBets.size() - 1; iVecPosition > -1; iVecPosition--) { /* Get the Bet at current position. */ Bet temp = (Bet) app.vecBets.elementAt(iVecPosition); /* If the bet is the position clicked on. */ if(temp.Position == iRouletteIndex) { /* If a bet exists in the current position clicked, remove the bet on the vector so we can manipulate it and put it back if needed. */ app.vecBets.removeElementAt(iVecPosition); /* If the denomination is bigger then the wager, then add the wager back to the current funds and don't add the bet back to the current bets vector. */ if(app.iDonom >= temp.Wager) app.iCurrentFunds += temp.Wager; /* Otherwise, if the wager is bigger then the current denomination, then subtract the denomination from the wager, add the denomination back to our current funds, and add the manipulated bet back to the vector containing the list of bets made. */ else { temp.Wager -= app.iDonom; app.iCurrentFunds += app.iDonom; app.vecBets.add(temp); } /* Update the display box of our current funds with the new value. */ app.txtCurrentFunds.setText(" $" + Integer.toString(app.iCurrentFunds)); /* Construct a string of all the bets by going through the vector containing the bets and getting them in string form then adding the string and setting the text area that holds the current bets to be that of the string just created. */ String txtBets = new String(""); for(int iBets = 0; iBets < app.vecBets.size(); iBets++) txtBets += ((Bet)app.vecBets.elementAt(iBets)).printWager(); app.txtCurrentBets.setText(txtBets); /* If we removed the last bet, then disable all the remove buttons and the spin button. */ if(app.vecBets.isEmpty()) { app.bRemoveLast.setEnabled(false); app.bResetBoard.setEnabled(false); app.bSpinWheel.setEnabled(false); } /* Repaint the applet. */ app.repaint(app.iSideBorders, app.iTopBorder, 900, 400); /* If we matched the betting position we don't need to go through the vector anymore so break the loop. */ break; } } } } /* If the mouse click was a regular mouse button and valid position. */ else if(iRouletteIndex != -1) { /* The report must be on the screen so clean the box first. */ if(app.vecBets.isEmpty()) { app.txtCurrentBets.setText(""); app.lblCurrentBets.setText("Current Bets:"); } /* We can only place a bet if we have funds available to cover it, so if we subtract the current Denomination from the current funds and it is positive then we can continue. */ if( (app.iCurrentFunds - app.iDonom) >= 0) { // Log regular mouse click System.out.println("Regular Valid Mouse Click"); /* Enable all of the buttons. */ app.bRemoveLast.setEnabled(true); app.bResetBoard.setEnabled(true); app.bRestartGame.setEnabled(true); app.bSpinWheel.setEnabled(true); /* Initialize that a bet on that position was already placed to false. */ boolean inVec = false; /* Go through the vector backwards again for efficiency to check it a bet was placed on that number. If a bet was placed we want to update the wager instead of creating a new bet. */ for(int iVecPosition = app.vecBets.size() - 1; iVecPosition > -1; iVecPosition--) { /* Get the current bet. */ Bet temp = (Bet) app.vecBets.elementAt(iVecPosition); /* If a bet on that position already exists. */ if(temp.Position == iRouletteIndex) { /* Remove the Bet from the vector */ app.vecBets.removeElementAt(iVecPosition); /* Update the bet's wager by adding to it the current denomination, subtract from our current funds the denomination bet, set the current funds text field to have the new current available funds, and add the bet back to the current bets vector. */ temp.Wager += app.iDonom; app.iCurrentFunds -= app.iDonom; app.txtCurrentFunds.setText(" $" + Integer.toString(app.iCurrentFunds)); app.vecBets.add(temp); /* Construct a string containing a list of all the current bets. */ String txtBets = new String(""); for(int iBets = 0; iBets < app.vecBets.size(); iBets++) txtBets += ((Bet)app.vecBets.elementAt(iBets)).printWager(); /* Set the current bets text area to have the updated bet's list. */ app.txtCurrentBets.setText(txtBets); /* Set the flag to say we found the bet in the vector so we don't have to create a new bet and break the loop since we no longer need to search the vector. */ inVec = true; break; } } /* If we didn't find the bet in the list of current bets we need to create a new one. */ if(inVec == false) { /* Create a new bet with the position chosen and the amount bet, subtract the wager from our current funds, update the current funds text field, and add the bet to the list of bets. */ Bet temp = new Bet(iRouletteIndex, app.iDonom); app.iCurrentFunds -= app.iDonom; app.txtCurrentFunds.setText(" $" + Integer.toString(app.iCurrentFunds)); app.vecBets.add(temp); /* Set new betting list to the old one with the new bet appended to it. */ app.txtCurrentBets.setText(app.txtCurrentBets.getText() + temp.printWager()); } /* repaint the applet. */ app.repaint(app.iSideBorders, app.iTopBorder, 900, 400); } else // Not enough money to place bet { /* Tell user they don't have enough funds available to place the bet. */ new DialogBox("Error - Not enough funds", "Not enough funds available to make this wager.", app, false).show(); } } } /** * This function must be defined even though it does nothing because * this class implements MouseMotionListener, an abstract * class, and therefore all methods in that abstract class must be implemented * or the implementing class is itself abstract. * * @param event MouseEvent object supplied by system * @since 1.0 */ public void mouseMoved( MouseEvent event ) {} /** * This function is called by the JRE (Java Runtime Environment) when the * mouse is moved with the left mouse button held down * * @param event MouseEvent object supplied by system * @since 1.0 */ public void mouseDragged( MouseEvent event ) {} /** * This function is called whenever the mouse leaves a component. * * @param event MouseEvent fired. * @since 1.0 */ public void mouseExited(MouseEvent event) {} /** * This function is called when a key has been pressed, and a * KeyEvent has been fired. This is the event handling method that * handles the processing of hotkeys. * * @param event KeyEvent Event object fired. * @since 1.0 */ public void keyTyped(KeyEvent event) { if( event.isAltDown() ) // Then we are in hotkey mode { /* Get the key pressed */ char key = event.getKeyChar(); if(key == '1') { app.iDonom = 1; app.cDonom.select(0); } else if(key == '2') { app.iDonom = 5; app.cDonom.select(1); } else if(key == '3') { app.iDonom = 25; app.cDonom.select(2); } else if(key == '4') { app.iDonom = 100; app.cDonom.select(3); } else if(key == '5') { app.iDonom = 500; app.cDonom.select(4); } /* Give button back to applet. */ app.requestFocus(); } } /** * Another function that is not needed. Just here because it is abstract. * * @param event KeyEvent object fired. * @since 1.0 */ public void keyReleased( KeyEvent event ) {} /** * Is called when ever a key is pressed. This function is similar to * keyTyped, but fires on different keys. * * @param event KeyEvent object fired * @since 1.0 */ public void keyPressed( KeyEvent event ) { if( event.getKeyCode( ) == KeyEvent.VK_F1 ) new DialogBox("Help", RouletteConstants.HELP_TEXT, app, true).show(); } /** * Is called by the system when a component has received the focus. * * @param event FocusEvent fired. * @since 1.0 */ public void focusGained(FocusEvent event) { app.requestFocus(); // We never want any component with a focus listener attached to ever keep the focus } /** * Is called by the system when a component loses the focus. * * @param event FocusEvent fired. * @since 1.0 */ public void focusLost(FocusEvent event) {} /** * This function is called by the system when a button has been pressed. * * @param event ActionEvent the system has fired * @since 1.0 */ public void actionPerformed(ActionEvent event) { /* Boolean that flags whether to set the focus back to the applet or not. This is required because if the replay couldn't be done then an error dialog box will pop up and we want that to have the focus. We will initialize to true because there is only one case in which the applet will not receive the focus back. */ boolean bGiveAppletFocus = true; /* Remove last bet in list action button was pressed. */ if(event.getSource() == app.bRemoveLast) { try // try block just case, but really not needed { /* Get last bet in list, and add its wager back to the current funds, reset the text field with the new current funds value, and remove the last element in the last. */ Bet temp = (Bet) app.vecBets.elementAt(app.vecBets.size() - 1); app.iCurrentFunds += temp.Wager; app.txtCurrentFunds.setText(" $" + Integer.toString(app.iCurrentFunds)); app.vecBets.removeElementAt(app.vecBets.size() - 1); /* Create a string containing a list of all the bets and set the current bets text area equal to it. */ String txtBets = new String(""); for(int iBets = 0; iBets < app.vecBets.size(); iBets++) txtBets += ((Bet)app.vecBets.elementAt(iBets)).printWager(); app.txtCurrentBets.setText(txtBets); /* If the list of current bets is now empty, disable all the buttons that shouldn't be allowed. */ if(app.vecBets.isEmpty()) { /* Can't reset or remove the last bet if no bets in the list. Also can't still the wheel if no bets exist in the list. */ app.bRemoveLast.setEnabled(false); app.bResetBoard.setEnabled(false); app.bSpinWheel.setEnabled(false); } } catch(ArrayIndexOutOfBoundsException e) { /* Should never get here, but incase somehow gets here we will do what should be done and disable all buttons. */ app.bRemoveLast.setEnabled(false); app.bResetBoard.setEnabled(false); app.bSpinWheel.setEnabled(false); } } /* Reset board button was pressed. */ else if(event.getSource() == app.bResetBoard) { /* Add all the wagers of all the bets back to current funds. */ for(int iBets = 0; iBets < app.vecBets.size(); iBets++) { Bet temp = (Bet) app.vecBets.elementAt(iBets); app.iCurrentFunds += temp.Wager; } /* Clear all the bets, remove all the current bets listed in the text area, update text field with new current funds value, and disable all the buttons that can't be done right now. */ app.vecBets.clear(); app.txtCurrentBets.setText(""); app.txtCurrentFunds.setText(" $" + Integer.toString(app.iCurrentFunds)); app.bRemoveLast.setEnabled(false); app.bResetBoard.setEnabled(false); app.bSpinWheel.setEnabled(false); } /* Restart game button was pressed. */ else if(event.getSource() == app.bRestartGame) { /* Reset all fields back to starting values. */ app.vecBets.clear(); app.vecPreviousRoundBets.clear(); app.txtCurrentBets.setText(""); app.txtCurrentFunds.setText(" $1000"); app.lblCurrentBets.setText("Current Bets:"); app.lblSelectedNumber.setText(""); app.bRemoveLast.setEnabled(false); app.bResetBoard.setEnabled(false); app.bRestartGame.setEnabled(false); app.bSpinWheel.setEnabled(false); app.bReplay.setEnabled(false); app.cDonom.select(1); app.iCurrentFunds = 1000; app.iDonom = 5; } /* Spin wheel button was pressed. */ else if(event.getSource() == app.bSpinWheel) { /* Calculate winning number by using a number created by the random number generator, multiply it by 1000, and use the modulus operator with 38 so it generates a number between 0 and 37. */ WinningNumber spin = new WinningNumber((int)(Math.random() * 1000) % 38); /* Debugging line printing the number chosen by the generator. */ System.out.println("Spin Chose: " + Integer.toString(spin.iNumber)); /* Disable the remove last bet, reset button, and spin button since there are no bets in the current bet list. */ app.bRemoveLast.setEnabled(false); app.bResetBoard.setEnabled(false); app.bSpinWheel.setEnabled(false); /* Run the routine that calculates and displays the information about the current bets. This function performs the displaying part also. */ CalculateWinsAndLoses(spin); /* Display number in the last number selected label with its corresponding color. */ if((spin.iNumber & 1) == 1) app.lblSelectedNumber.setForeground(Color.red); else app.lblSelectedNumber.setForeground(Color.black); app.lblSelectedNumber.setText(Integer.toString(spin.iNumber)); /* Copy the contents of the current bets to the previous rounds bets and enable the button that allow to place a previous rounds bets. */ app.vecPreviousRoundBets = (Vector) app.vecBets.clone(); app.bReplay.setEnabled(true); /* Clears the list of current bets since the spin is over. */ app.vecBets.clear(); } else if(event.getSource() == app.bReplay) { /* Before clearing the current bets vector, we must add all of the wagers back to the current funds. No need to update it though since we are going to be repainting previous bets. */ for(int i = 0; i < app.vecBets.size(); i++) app.iCurrentFunds += ( (Bet) app.vecBets.elementAt(i)).Wager; /* Clear the current vector and clone the previous rounds vector to the current vector. */ app.vecBets.clear(); app.vecBets = (Vector) app.vecPreviousRoundBets.clone(); /* Set up the list of bets and keep a total of all the wagers. */ String txtBets = new String(""); int totalAmount = 0; /* Add up all the wagers */ for(int j = 0; j < app.vecBets.size(); j++) totalAmount += ( (Bet) app.vecBets.elementAt(j)).Wager; /* If the total amount is less then the current funds left. */ if(totalAmount <= app.iCurrentFunds) { /* Subtract all wagers from current funds and store them in the betting list. */ for(int k = 0; k < app.vecBets.size(); k++) { Bet temp = (Bet) app.vecBets.elementAt(k); app.iCurrentFunds -= temp.Wager; txtBets += temp.printWager(); } /* Update the text fields that display the current funds and bets. Also enable all the buttons. */ app.txtCurrentBets.setText(txtBets); app.txtCurrentFunds.setText(" $" + Integer.toString(app.iCurrentFunds)); app.lblCurrentBets.setText("Current Bets:"); app.bSpinWheel.setEnabled(true); app.bRemoveLast.setEnabled(true); app.bResetBoard.setEnabled(true); } else // If we don't have a enough funds to replay the bet { /* Disable the replay button and clear the both list of vectors since we are not going to be able to replay previous round no matter what. */ app.bReplay.setEnabled(false); app.vecBets.clear(); app.vecPreviousRoundBets.clear(); /* Display an error message and set the flag to give the applet back the focus to false since we want the error dialog box to have the focus. */ new DialogBox("Error - Not enough funds", "Not enough funds available to replay previous betting round.", app, false).show(); bGiveAppletFocus = false; } } /* If help button is pressed, then show the help dialog box and don't give the focus back to the applet. */ else if(event.getSource() == app.bHelp) { new DialogBox("Help", RouletteConstants.HELP_TEXT, app, true).show(); bGiveAppletFocus = false; } /* Give focus back to applet if the flag it is set. */ if(bGiveAppletFocus) app.requestFocus(); /* Repaint the table to display most current betting information. */ app.repaint(app.iSideBorders, app.iTopBorder, 900, 400); } /** * This function is called by the system when the state of a * List or Choice component is changed. * * @param event ItemEvent fired. * @since 1.0 */ public void itemStateChanged(ItemEvent event) { /* Get the index of the selected item. */ int index = ( (Choice) event.getSource() ).getSelectedIndex(); /* Set price depending on what value is chosen from the box. */ if(index == 0) app.iDonom = 1; else if(index == 1) app.iDonom = 5; else if(index == 2) app.iDonom = 25; else if(index == 3) app.iDonom = 100; else if(index == 4) app.iDonom = 500; /* Give button back to applet. */ app.requestFocus(); } /** * This function determines the winning and losing bets. This function * keeps a list of winning bets, losing bets, and the total payout is * also calculated. This function also displays the information in the * correct places. * * @param num A WinningNumber object containing the information about all winning bets * @since 1.0 */ public void CalculateWinsAndLoses(WinningNumber num) { /* String containing the information about the winning number, winning bets, losing bets, and payout information. */ String txtWinningNumber = new String("Winning Number: " + Integer.toString(num.iNumber) + "\n"); String txtWinners = new String("\nWinning Bets:\n"); String txtLosers = new String("\nLosing Bets:\n"); String txtTotalPayout = new String(""); /* gets the return from the calcWinnings function. */ int iWinning = 0; /* holds the running total of winning bets and losing bets. */ int iBettingDifference = 0; /* holds the running total or winning bets only. this is what we need need to calculated current funds since we already deducted the wager from the current funds. */ int iRunningPayout = 0; /* Go through all the vectors in the list of current bets. */ for(int iBets = 0; iBets < app.vecBets.size(); iBets++) { /* Get the current bet in the list given the position and calculate the amount one given the current bet. */ Bet temp = (Bet) app.vecBets.elementAt(iBets); iWinning = num.calcWinnings(temp); iBettingDifference += iWinning; // Keep running total whether winning or losing bet if(iWinning > 0) // If a winning bet { /* Add to the running payout and add to the winning bets text report. */ iRunningPayout += iWinning; txtWinners += temp.printResult() + " (payoff is $" + Integer.toString(iWinning) + ")\n"; } else // If a losing bet just add to losing bet report txtLosers += temp.printResult() + "\n"; } /* Add the running payout to current funds if and only if the running total is greater than zero (at least one winning bet) */ if(iRunningPayout != 0) { app.iCurrentFunds += iRunningPayout; app.txtCurrentFunds.setText(" $" + Integer.toString(app.iCurrentFunds)); } /* Create results string depending on a winning. losing, or even bet. */ if(iBettingDifference < 0) txtTotalPayout = "\nTotal Loses: $" + Integer.toString(0 - iBettingDifference); else if(iBettingDifference > 0) txtTotalPayout = "\nTotal Winnings (includes amount betted): $" + Integer.toString(iBettingDifference); else txtTotalPayout = "\nYou broke even!"; /* Reset the current bets text area. */ app.lblCurrentBets.setText("Betting Report:"); app.txtCurrentBets.setText(txtWinningNumber + txtWinners + txtLosers + txtTotalPayout); } /** * This function finds clicks on the roulette board and processes them. It will return * the integer representation of the spot clicked on the roulette board. If clicked in an * invalid location, then -1 is returned. * * @param event The mouse event that holds the location on the applet the click had taken place. * @return Integer Representation of the position in the roulette board, and -1 if not a valid betting position. * @since 1.0 */ public int FindClick(MouseEvent event) { /* These are counters used by the loops in order to calculate positions on the board where a click has taken place. The iBoardIndex is the integer value of the board configuration. */ int iRowCounter, iColumnCounter, iBoardIndex; /* The following two if clauses determine if zero or double zero have been clicked. */ if( new Rectangle( app.iSideBorders, app.iTopBorder + 25, app.iNumberWidth, app.iNumberWidth). contains( event.getX(), event.getY() ) ) return RouletteConstants.POS_NUM_00; if( new Rectangle( app.iSideBorders, app.iTopBorder + 75, app.iNumberWidth, app.iNumberWidth). contains( event.getX(), event.getY() ) ) return RouletteConstants.POS_NUM_0; /* Determines if any of the 2:1 columns were clicked. */ iBoardIndex = 40; // Index of the top 2:1 row for(iRowCounter = 0; iRowCounter < 3; iRowCounter++) { if( new Rectangle( app.iSideBorders + (app.iNumberWidth * 13), app.iTopBorder + (app.iNumberWidth * iRowCounter), 75, app.iNumberWidth). contains( event.getX(), event.getY() ) ) return iBoardIndex; iBoardIndex--; } /* Determines if any of the chunks of the board were chosen. i.e. The 1st 12, the second 12, and the third 12. */ iBoardIndex = 41; // Index of the first 12 for(iColumnCounter = 0; iColumnCounter < 3; iColumnCounter++) { if( new Rectangle( app.iSideBorders + app.iNumberWidth + (200 * iColumnCounter), 250, 200, 100). contains( event.getX(), event.getY() ) ) return iBoardIndex; iBoardIndex++; } /* Determines if any of the odd, even, red, black, 1 to 18, or 19 to 36 have been chosen. */ iBoardIndex = 44; // Index of the 1 to 18 block on the board for(iColumnCounter = 0; iColumnCounter < 6; iColumnCounter++) { if( new Rectangle( app.iSideBorders + app.iNumberWidth + (100 * iColumnCounter), 350, 100, 50). contains( event.getX(), event.getY() ) ) return iBoardIndex; iBoardIndex++; } /* Determines if any of the board numbers were clicked except 0 or double zero. */ iBoardIndex = 3; for(iColumnCounter = 1; iColumnCounter < 13; iColumnCounter++) { iBoardIndex = 3 * iColumnCounter; for(iRowCounter = 0; iRowCounter < 3; iRowCounter++) { if( new Rectangle( app.iSideBorders + (app.iNumberWidth * iColumnCounter), app.iTopBorder + (app.iNumberWidth * iRowCounter), app.iNumberWidth, app.iNumberWidth). contains( event.getX(), event.getY() ) ) return iBoardIndex; iBoardIndex--; } } /* If no click was in a proper area of the board. */ return -1; } }