View Javadoc
1   /******************************************************************************
2    * BoardGui.java - Main object of the GUI board.
3    * $Id$
4    * 
5    * BuckoFIBS - Backgammon by BuckoSoft
6    * Copyright© 2009,2010 - Dick Balaska - BuckoSoft, Corp.
7    * 
8    * $Log$
9    * Revision 1.7  2011/07/16 02:41:21  dick
10   * startWatching() turns off any hover that's active.
11   *
12   * Revision 1.6  2011/07/04 03:37:05  dick
13   * Check for Resign first, before Double.
14   *
15   * Revision 1.5  2011/06/13 04:42:18  dick
16   * Handle isAcceptDeclineResign().
17   *
18   * Revision 1.4  2011/05/23 05:57:48  dick
19   * moveEvent() becomes displayGameEvent().
20   * playSound() convienence helper.
21   *
22   * Revision 1.3  2011/05/22 22:58:23  dick
23   * c.b.f.B.g.boardTab.board becomes c.b.f.B.g.boardTab.boardPane .
24   *
25   * Revision 1.2  2011/05/21 05:01:17  dick
26   * doubling attributes live in Board, not Document.
27   *
28   * Revision 1.1  2011/05/15 02:17:33  dick
29   * c.b.f.B.gui.board becomes c.b.f.B.gui.boardTab.
30   *
31   * Revision 1.9  2011/05/14 05:19:31  dick
32   * Still need work on the doubles handling for board/document split.
33   *
34   * Revision 1.8  2011/05/14 04:43:01  dick
35   * I needed to deal with some lightweight Boards.
36   * So finally make the primary document Document
37   * and demote just the Board handling to a domain object.
38   *
39   * Revision 1.7  2011/05/13 14:56:15  dick
40   * Add support for animating checkers on the board.
41   *
42   * Revision 1.6  2011/05/11 22:22:54  dick
43   * Working on animating moves.
44   *
45   * Revision 1.5  2011/01/04 17:38:59  dick
46   * Disable the game gui if we startWatching().
47   *
48   * Revision 1.4  2010/12/30 04:12:58  dick
49   * Bug 18 -
50   * If you Left Click first that will use the Left die,
51   * if you Left Click again you should be able to then use the Right die.
52   *
53   * Revision 1.3  2010/03/03 13:12:21  inim
54   * Replaced (c) sign in comment mangled by CVS default encoding back to UTF-8
55   *
56   * Revision 1.2  2010/03/03 12:19:49  inim
57   * Moved source to UTF8 encoding from CP1252 encoding. To this end all source files' (c) message was updated to "Copyright© 2009,2010 - Dick Balaska - BuckoSoft, Corp.". This replaces the (c) sign to UTF8, and adds the new year 2010.
58   *
59   * Revision 1.1  2010/02/04 05:57:53  inim
60   * Mavenized project folder layout
61   *
62   * Revision 1.8  2009/02/17 14:44:30  dick
63   * Turn off DEBUG.
64   *
65   * Revision 1.7  2009/02/14 12:27:11  dick
66   * Ignore double-clicks on the double cube.
67   *
68   * Revision 1.6  2009/02/12 06:27:48  dick
69   * Check double-click before checkers-on-bar so we can move only one checker off the bar.
70   *
71   * Revision 1.5  2009/02/11 09:06:28  dick
72   * Add youCantMove() to disable the hover helper after it got turned on at the beginning of your turn.
73   *
74   * Revision 1.4  2009/02/06 07:56:49  dick
75   * Working on doubles.
76   *
77   * Revision 1.3  2009/02/03 09:47:08  dick
78   * Use SEND_MOVE instead of SEND_COMMAND so we can make a "pick up the dice" sound.
79   *
80   * Revision 1.2  2009/01/31 08:49:44  dick
81   * YourMove is initiated by GameManager and doesn't need the 2-shot setup anymore.
82   *
83   * Revision 1.1  2009/01/28 19:37:28  dick
84   * package com.buckosoft.fibs.gui.board becomes com.buckosoft.fibs.BuckoFIBS.gui.board.
85   *
86   * Revision 1.8  2009/01/28 08:32:49  dick
87   * Turn off DEBUG.
88   *
89   * Revision 1.7  2009/01/27 06:57:01  dick
90   * Adhere to bearing off rules.
91   *
92   * Revision 1.6  2009/01/27 05:46:29  dick
93   * Played first game against myself on Jibs!
94   *
95   * Revision 1.5  2009/01/26 07:23:43  dick
96   * Working on moving checkers.
97   *
98   * Revision 1.4  2009/01/24 17:06:55  dick
99   * Starting your turn is a 2 part operation.
100  * First we get a "It's your turn" messages, then we get a board with the dice.
101  *
102  * Revision 1.3  2009/01/22 05:04:20  dick
103  * More moving checkers on the board.  There are issues with playing 1-24 vs. 24-1.
104  *
105  * Revision 1.2  2009/01/20 07:42:26  dick
106  * Move checkers on the board.
107  *
108  * Revision 1.1  2009/01/18 05:04:58  dick
109  * Main object of the GUI board.
110  */
111 
112 /* 
113  * This program is free software: you can redistribute it and/or modify
114  * it under the terms of the GNU General Public License as published by
115  * the Free Software Foundation, either version 3 of the License, or
116  * (at your option) any later version.
117  *
118  * This program is distributed in the hope that it will be useful,
119  * but WITHOUT ANY WARRANTY; without even the implied warranty of
120  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
121  * GNU General Public License for more details.
122  *
123  * You should have received a copy of the GNU General Public License
124  * along with this program.  If not, see <http://www.gnu.org/licenses/>.
125  *
126  * The Original Code is BuckoFIBS, <http://www.buckosoft.com/BuckoFIBS/>.
127  * The Initial Developer of the Original Code is Dick Balaska and BuckoSoft, Corp.
128  * 
129  */
130 package com.buckosoft.fibs.BuckoFIBS.gui.boardTab;
131 
132 import java.awt.Point;
133 
134 import org.slf4j.Logger;
135 import org.slf4j.LoggerFactory;
136 
137 import com.buckosoft.fibs.BuckoFIBS.AudioManager;
138 import com.buckosoft.fibs.BuckoFIBS.CommandDispatcher;
139 import com.buckosoft.fibs.BuckoFIBS.gui.boardTab.boardPane.BoardPane;
140 import com.buckosoft.fibs.domain.gameEvent.GameEvent;
141 
142 /** Main object of the GUI board.  
143  * This object contains the controlling routines for the user to manipulate the board.
144  * It is a superclass of {@link BoardPane} so that we can get at its drawing/sizing  methods. 
145  * @author Dick Balaska
146  * @since 2009/01/17
147  * @version $Revision$ <br> $Date$
148  * @see <a href="http://cvs.buckosoft.com/Projects/BuckoFIBS/BuckoFIBS/src/main/java/com/buckosoft/fibs/BuckoFIBS/gui/boardTab/BoardGui.java">cvs BoardGui.java</a>
149  */
150 public class BoardGui extends BoardPane {
151 	private	final static boolean DEBUG = true;
152 	private	final static boolean DEBUG_POINT_POSITION = false;	// what point we are on
153     private Logger logger = LoggerFactory.getLogger(getClass());
154 	
155 	private static final long serialVersionUID = 1L;
156 
157 	private	CommandDispatcher commandDispatcher;  //  @jve:decl-index=0:
158 
159 	private int	lastMousePosition = POINT_UNKNOWN;
160 
161 	/** Are we in the middle of taking our turn? */
162 	private boolean boardActive = false;
163 	
164 	/** build up a string of our move to send to fibs */
165 	private	String	moveString;
166 
167 	private int[]	myDice;
168 	
169 	/** Set the reference to the command dispatcher
170 	 * @param commandDispatcher Our system CommandDispatcher
171 	 */
172 	public	void	setCommandDispatcher(CommandDispatcher commandDispatcher) {
173 		this.commandDispatcher = commandDispatcher;
174 		this.properties = this.commandDispatcher.getProperties();
175 		this.animateManager.setProperties(this.properties);
176 	}
177 
178 	/** The constructor for the BoardGui
179 	 */
180 	BoardGui() {
181 		super();
182 		diceUsed[0] = false;
183 		diceUsed[1] = false;
184 	}
185 
186 	/** The board has changed and needs to be redrawn and possibly manipulated
187 	 */
188 	public void updateBoard() {
189 		// this.repaint();		// handled in BoardTab.updateBoard();
190 	}
191 
192 	/** Turn off this player's turn
193 	 */
194 	public void youCantMove() {
195 		this.boardActive = false;
196 	}
197 
198 	public void startWatching() {
199 		if (DEBUG)
200 			logger.info("startWatching");
201 		setHoverPoint(-1);
202 		diceUsed = new boolean[2];
203 		diceUsed[0] = false;
204 		diceUsed[1] = false;
205 		youCantMove();
206 	}
207 	
208 	public void displayGameEvent(GameEvent gameEvent) {
209 		animateManager.addEvent(gameEvent);
210 	}
211 
212 	/** Callback when the Animation has tipped
213 	 */
214 	protected void animateUpdate() {
215 	}
216 
217 	public void playSound(AudioManager.Cue cue) {
218 		this.commandDispatcher.dispatch(CommandDispatcher.Command.PLAY_CUE, cue);
219 	}
220 	/** Start your turn Part 1.  Set "our turn" and wait for the dice (a board command)
221 	 */
222 	public void yourMove(int diceToMove) {
223 		if (DEBUG)
224 			logger.info("yourMove: " + diceToMove);
225 		boardActive = true;
226 		myDice = board.getDice()[0];
227 		if (myDice[0] == 0)
228 			return;
229 		moveString = "move";
230 		if (myDice[0] == myDice[1]) {
231 			if (diceUsed.length != 4)
232 				diceUsed = new boolean[4];
233 			int d = myDice[0];
234 			myDice = new int[4];
235 			for (int i=0; i<4; i++) {
236 				diceUsed[i] = false;
237 				myDice[i] = d;
238 			}
239 		} else {
240 			if (diceUsed.length != 2)
241 				diceUsed = new boolean[2];
242 			diceUsed[0] = false;
243 			diceUsed[1] = false;
244 		}
245 		if (DEBUG)
246 			logger.info("yourMove: diceToMove=" + diceToMove + " diceUsed.length=" + diceUsed.length);
247 	}
248 
249 	/** Handle the mouse being pressed.
250 	 * @param e The Event 
251 	 */
252 	protected void onMousePressed(java.awt.event.MouseEvent e) {
253 		if (!boardActive 
254 				&& !this.board.isYourTurnToRollOrDouble()
255 				&& !this.board.isAcceptDeclineDouble()
256 				&& !this.board.isAcceptDeclineResign())
257 			return;
258 		int p = getPointFromXY(e.getX(), e.getY());
259 		if (DEBUG)
260 			System.out.println("press:" + e.getButton() + " x/y=" + e.getX() + "/" + e.getY() + " p=" + p);
261 		if (board.isAcceptDeclineResign()) {		// A resign can come at any time, so look for it first
262 			if (e.getClickCount() == 2)
263 				return;
264 			if (p == POINT_DICE) {
265 				this.commandDispatcher.dispatch(CommandDispatcher.Command.SEND_ACCEPT);
266 				return;
267 			}
268 			if (p == POINT_REJECT) {
269 				this.commandDispatcher.dispatch(CommandDispatcher.Command.SEND_REJECT);
270 				return;
271 			}
272 			return;
273 		}
274 		if (board.isAcceptDeclineDouble()) {
275 			if (e.getClickCount() == 2)
276 				return;
277 			if (p == POINT_DOUBLE) {
278 				this.commandDispatcher.dispatch(CommandDispatcher.Command.SEND_ACCEPT);
279 				return;
280 			}
281 			if (p == POINT_REJECT) {
282 				this.commandDispatcher.dispatch(CommandDispatcher.Command.SEND_REJECT);
283 				return;
284 			}
285 			return;
286 		}
287 		if (this.board.isYourTurnToRollOrDouble()) {
288 			if (e.getClickCount() == 2)
289 				return;
290 			if (p == POINT_BAR_DICE) {
291 				this.commandDispatcher.dispatch(CommandDispatcher.Command.SEND_ROLL);
292 				return;
293 			}
294 			if (p == POINT_DOUBLE) {
295 				this.commandDispatcher.dispatch(CommandDispatcher.Command.SEND_DOUBLE);
296 				return;
297 			}
298 			return;
299 		}
300 //		if (e.getClickCount() == 2) {
301 			if (DEBUG)
302 				System.out.println("doubleclick:" + p);
303 			if (p == POINT_DICE) {
304 				this.boardActive = false;
305 				this.commandDispatcher.dispatch(CommandDispatcher.Command.SEND_MOVE, this.moveString);
306 				return;
307 			}
308 //		}
309 		if (p == POINT_DICE && e.getButton() == 3) {
310 			int x = myDice[0];
311 			myDice[0] = myDice[1];
312 			myDice[1] = x;
313 			repaint();
314 			return;
315 		}
316 		if (board.getBar()[0] != 0 && p != POINT_BAR)	//must move off the bar first
317 			return;
318 		if (p > 0 || p == POINT_BAR) {
319 			int d = e.getButton() == 1 ? 0 : 1;
320 			if (myDice[0] == myDice[1]) {
321 				if (diceUsed.length > 3 && !diceUsed[3])
322 					d = 3;
323 				else if (diceUsed.length > 2 && !diceUsed[2])
324 					d = 2;
325 				else if (diceUsed.length > 1 && !diceUsed[1])
326 					d = 1;
327 				else if (diceUsed.length > 0 && !diceUsed[0])
328 					d = 0;
329 				else
330 					return;
331 			} else {
332 				if (diceUsed[d]) {
333 					d = d == 1 ? 0 : 1;
334 					if (diceUsed[d])
335 						return;
336 				}
337 			}
338 			moveChecker(p, d);		// XXX: We need to start an animate here. 2013, why?
339 			hoverOverPoint(p);
340 			repaint();
341 			return;
342 		}
343 	}
344 
345 	protected void onMouseMoved(java.awt.event.MouseEvent e) {
346 		if (!boardActive)
347 			return;
348 		int p = getPointFromXY(e.getX(), e.getY());
349 		if (lastMousePosition != p) {
350 			lastMousePosition = p;
351 			if (DEBUG_POINT_POSITION)
352 				System.out.println("p=" + p);
353 			onMouseReallyMoved(p);
354 		}
355 	}
356 	
357 	/** Mouse has entered a new point on the board
358 	 */
359 	private void onMouseReallyMoved(int p) {
360 		if (p > 0 || p == POINT_BAR)
361 			hoverOverPoint(p);
362 		else {
363 			setHoverPoint(-1);
364 			repaint();
365 		}
366 	}
367 
368 	private void hoverOverPoint(int p) {
369 		int i;
370 		if (myDice == null)		// bogus hover detected
371 			return;
372 		// hover over bar?
373 		if (p == POINT_BAR) {
374 			if (board.getBar()[0] == 0) {
375 				setHoverPoint(-1);
376 				repaint();
377 				return;
378 			}
379 		} else if (board.getPoints()[p] >= 0) {
380 			if (hoverPoint != -1) {
381 				setHoverPoint(-1);
382 				repaint();
383 			}
384 			return;
385 		}
386 		for (i=0; i<diceUsed.length; i++) {
387 			if (!diceUsed[i])
388 				break;
389 		}
390 		if (i == diceUsed.length) {
391 			setHoverPoint(-1);
392 			repaint();
393 			return;
394 		}
395 		setHoverPoint(p);
396 		if (myDice[0] == myDice[1]) {
397 			for (i=diceUsed.length-1; i>=0; i--) {
398 				if (!diceUsed[i])
399 					checkDiceMove(p, myDice[0]);
400 			}
401 		} else {
402 			if (!diceUsed[0])
403 				checkDiceMove(p, myDice[0]);
404 			if (!diceUsed[1])
405 				checkDiceMove(p, myDice[1]);
406 		}
407 		repaint();
408 	}
409 
410 	private void checkDiceMove(int p, int d) {
411 		if (isBearingOff()) {
412 			if (board.getDirection() == -1) {
413 				if (d >= p) {
414 					hoverTo[0] = 1;		// hover to home
415 					return;
416 				}
417 			} else {
418 				int r = 25-p;
419 				if (d >= r) {
420 					hoverTo[0] = 1;		// hover to home
421 					return;
422 				}
423 			}
424 		}
425 		if (p == POINT_BAR) {
426 			if (board.getDirection() == 1)
427 				p = d;
428 			else
429 				p = 25 - d;
430 			if (board.getPoints()[p] <= 1)
431 				hoverTo[p] = 1;
432 			return;
433 		}
434 		if (board.getDirection() == -1)
435 			p = p-d;
436 		else
437 			p = p+d;
438 		if (p > 24 || p < 1)
439 			return;
440 		if (board.getPoints()[p] <= 1)
441 			hoverTo[p] = 1;
442 	}
443 
444 	private void moveChecker(int p, int di) {
445 		int p1;
446 		int d = myDice[di];
447 		if (p == POINT_BAR) {
448 			if (board.getDirection() == 1)
449 				p = d;
450 			else
451 				p = 25 - d;
452 			if (board.getBar()[0] > 0) {
453 				if (board.getPoints()[p] >= 2)
454 					return;
455 				if (board.getPoints()[p] == 1) {
456 					board.getBar()[1]++;
457 					board.getPoints()[p] = 0;
458 				}
459 				board.getPoints()[p]--;
460 				board.getBar()[0]--;
461 				diceUsed[di] = true;
462 				moveString += " bar-" + p;
463 			}
464 			return;
465 		}
466 		if (board.getDirection() == -1)
467 			p1 = p-d;
468 		else
469 			p1 = p+d;
470 		if (board.getPoints()[p] < 0) {
471 			if (isBearingOff()) {
472 				if (board.getDirection() == -1) {
473 					if (d >= p) {
474 						moveToHome(p, di);
475 						return;
476 					}
477 				} else {
478 					int r = 25-p;
479 					if (d >= r) {
480 						moveToHome(p, di);
481 						return;
482 					}
483 				}
484 			}
485 			if (board.getPoints()[p1] == 1) {
486 				board.getBar()[1]++;
487 				board.getPoints()[p1] = 0;
488 			}
489 			if (board.getPoints()[p1] >= 2) {
490 				playSound(AudioManager.Cue.CantMoveHere);
491 				return;
492 			}
493 			board.getPoints()[p1]--;
494 			board.getPoints()[p]++;
495 			diceUsed[di] = true;
496 			moveString += " " + p + "-" + p1;
497 		}
498 	}
499 
500 	private void moveToHome(int p, int di) {	// need to animate this
501 		board.getPoints()[p]++;
502 		board.getHome()[0]++;
503 		diceUsed[di] = true;
504 		moveString += " " + p + "-off";
505 	}
506 
507 	private boolean isBearingOff() {
508 		int i;
509 		if (board.getDirection() == -1) {
510 			for (i=7; i<=24; i++)
511 				if (board.getPoints()[i] < 0)
512 					return(false);
513 		} else {
514 			for (i=1; i<=18; i++)
515 				if (board.getPoints()[i] < 0)
516 					return(false);
517 		}
518 		return(true);
519 	}
520 
521 	private int getPointFromXY(int x, int y) {
522 		int p = POINT_UNKNOWN;
523 		int dx, dy;
524 		// Check where in the x axis did we click 
525 		// click in the double cube area?
526 		int w2 = getDoubleCubeWidth()/2;
527 		Point t = getDoubleCubeXY();
528 		if (x >= t.x-w2 && x <= t.x+w2
529 		 && y >= t.y-w2 && y <= t.y+w2) {
530 			return(POINT_DOUBLE);
531 		}
532 		if (this.board.isAcceptDeclineDouble()) {
533 			dx = getDeclineX();
534 			dy = getDeclineY();
535 			w2 = bs.c15;
536 			if (x >= dx && x <= dx+w2
537 			 && y >= dy && y <= dy+w2) {
538 				return(POINT_REJECT);
539 			}
540 			return(POINT_UNKNOWN);
541 			
542 		}
543 		if (x < bs.w/2-bs.barWidth/2) {
544 			p = (x-bs.c15) / bs.checkerSize + 1;
545 			if (p == 7)		// why is this slop in here??
546 				p = 6;
547 		// click in the bar?
548 		} else if (x < bs.w/2-bs.barWidth/2 + bs.barWidth) {
549 			p = POINT_BAR;
550 			if (this.board.isYourTurnToRollOrDouble()) {
551 				if (y >= bs.h-bs.barWidth/2)
552 					p = POINT_BAR_DICE;
553 			}
554 		} else if (x < bs.w-bs.c15) {
555 			dy = bs.h/2-bs.c2;
556 			if (y > dy && y < dy+bs.dieSize) {
557 				dx = getPointX(4); 
558 				if (x > dx && x < dx + bs.c15 + bs.dieSize)
559 					p = POINT_DICE;
560 			}
561 			if (p != POINT_DICE) {
562 				p = (x-(bs.w/2-bs.barWidth/2 + bs.barWidth)) / bs.checkerSize + 7;
563 				if (p == 13)
564 					p = 12;
565 			}
566 		}
567 		if (p > 0) {
568 			if (y > bs.h/2)
569 				p = 13 - p;
570 			else
571 				p = 12 + p;
572 			if (board.getDirection() > 0)
573 				p = 25 - p;
574 		}
575 		return(p);
576 	}
577 }