Classes in this File | Line Coverage | Branch Coverage | Complexity | ||||
AnimateManager |
|
| 5.518518518518518;5.519 | ||||
AnimateManager$1 |
|
| 5.518518518518518;5.519 | ||||
AnimateManager$EventListCommand |
|
| 5.518518518518518;5.519 | ||||
AnimateManager$MoveTimerTask |
|
| 5.518518518518518;5.519 | ||||
AnimateManager$PointOffset |
|
| 5.518518518518518;5.519 |
1 | /****************************************************************************** | |
2 | * AnimateManager.java - Manage displaying game moves as they happen | |
3 | * $Id$ | |
4 | * | |
5 | * BuckoFIBS - Backgammon by BuckoSoft | |
6 | * Copyright© 2011 - Dick Balaska - BuckoSoft, Corp. | |
7 | * | |
8 | * $Log$ | |
9 | * Revision 1.9 2013/09/12 06:42:28 dick | |
10 | * Animating the double cube. | |
11 | * | |
12 | * Revision 1.8 2013/09/10 00:22:54 dick | |
13 | * Spline2D and Flasher move MoveSpline2D and MoveFlasher | |
14 | * | |
15 | * Revision 1.7 2011/07/16 03:52:55 dick | |
16 | * Add YouDouble | |
17 | * | |
18 | * Revision 1.6 2011/07/04 03:43:53 dick | |
19 | * Add FirstRoll handling. | |
20 | * | |
21 | * Revision 1.5 2011/06/18 19:32:58 dick | |
22 | * Set the resigning points in the AcceptAndWin object. | |
23 | * | |
24 | * Revision 1.4 2011/06/05 06:55:43 dick | |
25 | * Handle AcceptAndWin. | |
26 | * | |
27 | * Revision 1.3 2011/06/02 19:17:48 dick | |
28 | * If the last event on the queue is removed and it isGui(), then keep it special off the queue, | |
29 | * so that BoardGui knows what kind of gui event to deal with (but we don't run the queue). | |
30 | * | |
31 | * Deal with Resign events. | |
32 | * | |
33 | * Revision 1.2 2011/05/23 05:59:12 dick | |
34 | * AnimateManager is responsible for playing game sounds as they come up. | |
35 | * | |
36 | * Revision 1.1 2011/05/22 22:56:08 dick | |
37 | * c.b.f.B.g.boardTab.board becomes c.b.f.B.g.boardTab.boardPane . | |
38 | * | |
39 | * Revision 1.13 2011/05/22 05:23:28 dick | |
40 | * Add "Please" handling, RollOrDouble and AcceptRejectDouble. | |
41 | * All GameEvent objects are named starting with GameEvent. | |
42 | * | |
43 | * Revision 1.12 2011/05/21 20:18:30 dick | |
44 | * Sorting dice works. | |
45 | * | |
46 | * Revision 1.11 2011/05/21 06:09:12 dick | |
47 | * Put the dice back in the board in the correct place. | |
48 | * | |
49 | * Revision 1.10 2011/05/21 05:06:35 dick | |
50 | * Handle the PleaseMove event. | |
51 | * | |
52 | * Revision 1.9 2011/05/18 05:54:43 dick | |
53 | * Animating uses a clone of the board so that when we muck with it, | |
54 | * we don't mess up the game boards (in case we replay them). | |
55 | * | |
56 | * Revision 1.8 2011/05/17 22:52:38 dick | |
57 | * extraCheckers is only used for the drawing, and should not be used in calculations; it is not at the same time as the calcs. | |
58 | * | |
59 | * Revision 1.7 2011/05/16 21:25:32 dick | |
60 | * Handle Home and Bar during Move. | |
61 | * | |
62 | * Revision 1.6 2011/05/16 17:03:34 dick | |
63 | * Erase the dice after a CantMove. | |
64 | * | |
65 | * Revision 1.5 2011/05/16 15:59:01 dick | |
66 | * Add finalize(). | |
67 | * Clear dice from the board during the roll animation. | |
68 | * | |
69 | * Revision 1.4 2011/05/16 14:17:58 dick | |
70 | * Add support for Type.CantMove. | |
71 | * | |
72 | * Revision 1.3 2011/05/16 11:40:57 dick | |
73 | * All access for the event list goes through only one synchronized function. | |
74 | * | |
75 | * Revision 1.2 2011/05/15 04:37:30 dick | |
76 | * All access to eventList is through one synchronized function. | |
77 | * Calculate the checkers positions from lastBoard thru the moves on the queue. | |
78 | * | |
79 | * Revision 1.1 2011/05/15 02:17:54 dick | |
80 | * Move the AnimateEvents to their own package. | |
81 | * | |
82 | * Revision 1.4 2011/05/14 04:43:01 dick | |
83 | * I needed to deal with some lightweight Boards. | |
84 | * So finally make the primary document Document | |
85 | * and demote just the Board handling to a domain object. | |
86 | * | |
87 | * Revision 1.3 2011/05/14 00:12:21 dick | |
88 | * Redraw the board after erasing extraCheckers. | |
89 | * | |
90 | * Revision 1.2 2011/05/13 18:25:28 dick | |
91 | * Debugging animation. Still needs help with the Y checker position. | |
92 | * Also need to do bar/home positions. | |
93 | * | |
94 | * Revision 1.1 2011/05/13 14:28:13 dick | |
95 | * Manage displaying game moves as they happen | |
96 | * | |
97 | */ | |
98 | ||
99 | /* | |
100 | * This program is free software: you can redistribute it and/or modify | |
101 | * it under the terms of the GNU General Public License as published by | |
102 | * the Free Software Foundation, either version 3 of the License, or | |
103 | * (at your option) any later version. | |
104 | * | |
105 | * This program is distributed in the hope that it will be useful, | |
106 | * but WITHOUT ANY WARRANTY; without even the implied warranty of | |
107 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the | |
108 | * GNU General Public License for more details. | |
109 | * | |
110 | * You should have received a copy of the GNU General Public License | |
111 | * along with this program. If not, see <http://www.gnu.org/licenses/>. | |
112 | * | |
113 | * The Original Code is BuckoFIBS, <http://www.buckosoft.com/BuckoFIBS/>. | |
114 | * The Initial Developer of the Original Code is Dick Balaska and BuckoSoft, Corp. | |
115 | * | |
116 | */ | |
117 | package com.buckosoft.fibs.BuckoFIBS.gui.boardTab.boardPane; | |
118 | ||
119 | import java.util.Iterator; | |
120 | import java.util.LinkedList; | |
121 | import java.util.Timer; | |
122 | import java.util.TimerTask; | |
123 | ||
124 | import javax.swing.ImageIcon; | |
125 | ||
126 | import org.slf4j.Logger; | |
127 | import org.slf4j.LoggerFactory; | |
128 | ||
129 | import com.buckosoft.fibs.BuckoFIBS.AudioManager; | |
130 | import com.buckosoft.fibs.BuckoFIBS.BFProperties; | |
131 | import com.buckosoft.fibs.BuckoFIBS.gui.boardTab.BoardGui; | |
132 | import com.buckosoft.fibs.BuckoFIBS.gui.boardTab.boardPane.animateType.DoubleSpline2D; | |
133 | import com.buckosoft.fibs.BuckoFIBS.gui.boardTab.boardPane.animateType.MoveFlasher; | |
134 | import com.buckosoft.fibs.BuckoFIBS.gui.boardTab.boardPane.animateType.MoveSpline2D; | |
135 | import com.buckosoft.fibs.domain.Board; | |
136 | import com.buckosoft.fibs.domain.gameEvent.GameEvent; | |
137 | import com.buckosoft.fibs.domain.gameEvent.GameEvent.Type; | |
138 | import com.buckosoft.fibs.domain.gameEvent.GameEventAcceptAndWin; | |
139 | import com.buckosoft.fibs.domain.gameEvent.GameEventBoard; | |
140 | import com.buckosoft.fibs.domain.gameEvent.GameEventCantMove; | |
141 | import com.buckosoft.fibs.domain.gameEvent.GameEventDouble; | |
142 | import com.buckosoft.fibs.domain.gameEvent.GameEventFirstRoll; | |
143 | import com.buckosoft.fibs.domain.gameEvent.GameEventMove; | |
144 | import com.buckosoft.fibs.domain.gameEvent.GameEventPleaseAcceptOrRejectDouble; | |
145 | import com.buckosoft.fibs.domain.gameEvent.GameEventPleaseAcceptOrRejectResign; | |
146 | import com.buckosoft.fibs.domain.gameEvent.GameEventPleaseMove; | |
147 | import com.buckosoft.fibs.domain.gameEvent.GameEventPleaseRollOrDouble; | |
148 | import com.buckosoft.fibs.domain.gameEvent.GameEventRejectResign; | |
149 | import com.buckosoft.fibs.domain.gameEvent.GameEventResign; | |
150 | import com.buckosoft.fibs.domain.gameEvent.GameEventRoll; | |
151 | ||
152 | /** Manage animating game events | |
153 | * @author Dick Balaska | |
154 | * @since 2011/05/12 | |
155 | * @version $Revision$ <br> $Date$ | |
156 | * @see <a href="http://cvs.buckosoft.com/Projects/BuckoFIBS/BuckoFIBS/src/main/java/com/buckosoft/fibs/BuckoFIBS/gui/boardTab/boardPane/AnimateManager.java">cvs AnimateManager.java</a> | |
157 | */ | |
158 | 0 | public class AnimateManager { |
159 | private final static boolean DEBUG = true; | |
160 | private final static boolean DEBUGqueue = false; | |
161 | private final static boolean DEBUGmove = false; | |
162 | ||
163 | 0 | private Logger logger = LoggerFactory.getLogger(getClass()); |
164 | private BoardPane boardPane; | |
165 | private BoardGui boardGui; | |
166 | private BFProperties bfProps; | |
167 | 0 | private ImageIcon resignIcon = null; |
168 | 0 | private ImageIcon acceptIcon = null; |
169 | ||
170 | /** The last Board processed by the TimerTask. */ | |
171 | private Board lastBoard; | |
172 | ||
173 | /** 4 checkers and 4 positions and 4 zero spares for in process compound moves. | |
174 | * These are the translucent checkers in transit between Boards. | |
175 | * These are pairs; 0/1 = point/offset */ | |
176 | 0 | protected int[] extraCheckers = new int[12]; |
177 | /** The number of used checkers in the extraCheckers array */ | |
178 | 0 | protected int extraCheckersIndex = 0; |
179 | /** Who's extra Checkers (are in home) */ | |
180 | 0 | protected int extraCheckersWho = 0; |
181 | ||
182 | /** Default constructor */ | |
183 | 0 | public AnimateManager() {} |
184 | ||
185 | /** Shut down the timer queue on cleanup. | |
186 | * Failing to do so causes the app to keep running... | |
187 | */ | |
188 | @Override | |
189 | public void finalize() { | |
190 | 0 | accessEventList(EventListCommand.removeAll, null, null); |
191 | 0 | if (moveTimer != null) |
192 | 0 | moveTimer.cancel(); |
193 | 0 | if (moveTimerTask != null) |
194 | 0 | moveTimerTask.cancel(); |
195 | 0 | moveTimer = null; |
196 | 0 | moveTimerTask = null; |
197 | 0 | } |
198 | ||
199 | /** Set the back reference to the boardGui for notifications | |
200 | * @param boardPane The BoardPane | |
201 | */ | |
202 | public void setBoardPane(BoardPane boardPane) { | |
203 | 0 | this.boardGui = (BoardGui)boardPane; |
204 | 0 | this.boardPane = boardPane; // yeah they are the same object, but logically separate. |
205 | 0 | } |
206 | ||
207 | /** Set the reference to the properties | |
208 | * @param bfProperties | |
209 | */ | |
210 | public void setProperties(BFProperties bfProperties) { | |
211 | 0 | this.bfProps = bfProperties; |
212 | 0 | } |
213 | ||
214 | /** Return the first {@link AnimateEvent} in the queue. | |
215 | * Does not remove the element. | |
216 | * @return The head of the queue or null if the queue is empty. | |
217 | */ | |
218 | public AnimateEvent getHeadEvent() { | |
219 | 0 | return(accessEventList(EventListCommand.head, null, null)); |
220 | } | |
221 | ||
222 | /** Queue this animation event for display.<br> | |
223 | * Note that if !props.isAnimateMoves() then we just discard the moves | |
224 | * except for Board, which is important. | |
225 | * | |
226 | * @param gameEvent The event to queue and eventually display | |
227 | */ | |
228 | public void addEvent(GameEvent gameEvent) { | |
229 | if (DEBUGqueue || DEBUG) | |
230 | 0 | logger.info("addEvent: " + gameEvent.getType().toString() + ":: " + gameEvent.toString()); |
231 | 0 | if (gameEvent.getType() == Type.Board) |
232 | 0 | addBoard((GameEventBoard)gameEvent); |
233 | else { | |
234 | 0 | if (!bfProps.isAnimateMoves()) |
235 | 0 | return; |
236 | 0 | switch (gameEvent.getType()) { |
237 | case Move: | |
238 | 0 | addMove((GameEventMove)gameEvent); |
239 | 0 | break; |
240 | case Roll: | |
241 | 0 | addRoll((GameEventRoll)gameEvent); |
242 | 0 | break; |
243 | case CantMove: | |
244 | 0 | addCantMove((GameEventCantMove)gameEvent); |
245 | 0 | break; |
246 | case PleaseMove: | |
247 | 0 | addPleaseMove((GameEventPleaseMove)gameEvent); |
248 | 0 | break; |
249 | case PleaseRollOrDouble: | |
250 | 0 | addPleaseRollOrDouble((GameEventPleaseRollOrDouble)gameEvent); |
251 | 0 | break; |
252 | case PleaseAcceptOrRejectDouble: | |
253 | 0 | addPleaseAcceptOrRejectDouble((GameEventPleaseAcceptOrRejectDouble)gameEvent); |
254 | 0 | break; |
255 | case Resign: | |
256 | 0 | addResign((GameEventResign)gameEvent); |
257 | 0 | break; |
258 | case PleaseAcceptOrRejectResign: | |
259 | 0 | addPleaseAcceptOrRejectResign((GameEventPleaseAcceptOrRejectResign)gameEvent); |
260 | 0 | break; |
261 | case RejectResign: | |
262 | 0 | addRejectResign((GameEventRejectResign)gameEvent); |
263 | 0 | break; |
264 | case AcceptAndWin: | |
265 | 0 | addAcceptAndWin((GameEventAcceptAndWin)gameEvent); |
266 | 0 | break; |
267 | case FirstRoll: | |
268 | 0 | addFirstRoll((GameEventFirstRoll)gameEvent); |
269 | 0 | break; |
270 | case Double: | |
271 | 0 | addDouble((GameEventDouble)gameEvent); |
272 | 0 | break; |
273 | //case OpponentDouble: | |
274 | // addPleaseAcceptOrRejectDouble((GameEventPleaseAcceptOrRejectDouble)gameEvent); | |
275 | // break; | |
276 | default: | |
277 | 0 | logger.warn("unhandled addEvent: " + gameEvent.getType().toString() + ":: " + gameEvent.toString()); |
278 | } | |
279 | } | |
280 | 0 | } |
281 | ||
282 | // put the home and bar values at the end of the point array for convienence | |
283 | private final static int paHOME = 26; | |
284 | private final static int paBAR = 28; | |
285 | 0 | private int[] pointArray = new int[paBAR+2]; |
286 | ||
287 | private void addBoard(GameEventBoard gameBoard) { | |
288 | // this.board = boardPane.board; | |
289 | 0 | AnimateEventBoard aeb = new AnimateEventBoard(); |
290 | 0 | aeb.setBoard(gameBoard.getBoard().clone()); |
291 | 0 | queueEvent(aeb); |
292 | 0 | if (gameBoard.getPostEvent() != null) |
293 | 0 | addEvent(gameBoard.getPostEvent()); |
294 | 0 | } |
295 | private void addCantMove(GameEventCantMove gameCantMove) { | |
296 | 0 | AnimateEventCantMove acm = new AnimateEventCantMove(); |
297 | 0 | acm.setWho(gameCantMove.getWho()); |
298 | 0 | queueEvent(acm); |
299 | 0 | } |
300 | private void addRoll(GameEventRoll gameRoll) { | |
301 | 0 | AnimateEventDiceRoll aedr = new AnimateEventDiceRoll(); |
302 | 0 | aedr.setWho(gameRoll.getWho()); |
303 | 0 | int[] dice = gameRoll.getDice(); |
304 | 0 | maybeSortDice(dice); |
305 | 0 | aedr.dice[0] = dice[0]; |
306 | 0 | aedr.dice[1] = dice[1]; |
307 | if (DEBUG) | |
308 | 0 | logger.info("addRoll: dice=" + dice[0] + "-" + dice[1] + " aedr=" + aedr.dice[0] + "-" + aedr.dice[1]); |
309 | 0 | queueEvent(aedr); |
310 | 0 | } |
311 | private void addFirstRoll(GameEventFirstRoll gameFirstRoll) { | |
312 | 0 | AnimateEventFirstRoll aefr = new AnimateEventFirstRoll(); |
313 | 0 | aefr.setWhiteDie(gameFirstRoll.getDice()[0]); |
314 | 0 | aefr.setBlackDie(gameFirstRoll.getDice()[1]); |
315 | 0 | aefr.setWhitePlayer(gameFirstRoll.getPlayerNames()[0]); |
316 | 0 | aefr.setBlackPlayer(gameFirstRoll.getPlayerNames()[1]); |
317 | 0 | queueEvent(aefr); |
318 | 0 | } |
319 | private void addPleaseMove(GameEventPleaseMove gamePleaseMove) { | |
320 | 0 | AnimateEventPleaseMove aepm = new AnimateEventPleaseMove(); |
321 | 0 | aepm.setCheckersToMove(gamePleaseMove.getCheckersToMove()); |
322 | 0 | aepm.setDice(gamePleaseMove.getDice()); |
323 | 0 | maybeSortDice(aepm.getDice()); |
324 | 0 | queueEvent(aepm); |
325 | 0 | } |
326 | private void addPleaseRollOrDouble(GameEventPleaseRollOrDouble unused) { | |
327 | 0 | AnimateEventPleaseRollOrDouble aeprd = new AnimateEventPleaseRollOrDouble(); |
328 | 0 | queueEvent(aeprd); |
329 | 0 | } |
330 | private void addPleaseAcceptOrRejectDouble(GameEventPleaseAcceptOrRejectDouble gepaord) { | |
331 | // XXX | |
332 | 0 | AnimateEventPleaseAcceptOrRejectDouble aepard = new AnimateEventPleaseAcceptOrRejectDouble(); |
333 | 0 | aepard.setGui(gepaord.isGuiEvent()); |
334 | 0 | aepard.setWhoDoubled(gepaord.getWhoDoubled()); |
335 | 0 | queueEvent(aepard); |
336 | 0 | } |
337 | private void addResign(GameEventResign gameEventResign) { | |
338 | 0 | if (resignIcon == null) |
339 | 0 | resignIcon = new ImageIcon(getClass().getResource("/g/resign_flag-366x353.png")); |
340 | 0 | AnimateEventResign aer = new AnimateEventResign(); |
341 | 0 | aer.setIcon(resignIcon); |
342 | 0 | aer.setWho(gameEventResign.getWho()); |
343 | 0 | aer.setResigningPoints(gameEventResign.getResigningPoints()); |
344 | 0 | queueEvent(aer); |
345 | 0 | } |
346 | private void addPleaseAcceptOrRejectResign(GameEventPleaseAcceptOrRejectResign gameEventPleaseAcceptRejectResign) { | |
347 | 0 | AnimateEventPleaseAcceptOrRejectResign aeparr = new AnimateEventPleaseAcceptOrRejectResign(); |
348 | 0 | if (resignIcon == null) |
349 | 0 | resignIcon = new ImageIcon(getClass().getResource("/g/resign_flag-366x353.png")); |
350 | 0 | aeparr.setIcon(resignIcon); |
351 | 0 | aeparr.setWho(gameEventPleaseAcceptRejectResign.getWho()); |
352 | 0 | aeparr.setResigningPoints(gameEventPleaseAcceptRejectResign.getResigningPoints()); |
353 | 0 | queueEvent(aeparr); |
354 | 0 | } |
355 | private void addRejectResign(GameEventRejectResign gameEventRejectResign) { | |
356 | 0 | AnimateEventRejectResign aerr = new AnimateEventRejectResign(); |
357 | 0 | if (resignIcon == null) |
358 | 0 | resignIcon = new ImageIcon(getClass().getResource("/g/resign_flag-366x353.png")); |
359 | 0 | aerr.setIcon(resignIcon); |
360 | 0 | aerr.setWho(gameEventRejectResign.getWho() == Board.X ? Board.O : Board.X); |
361 | 0 | aerr.setResigningPoints(gameEventRejectResign.getResigningPoints()); |
362 | 0 | queueEvent(aerr); |
363 | 0 | } |
364 | private void addAcceptAndWin(GameEventAcceptAndWin gameEventAcceptAndWin) { | |
365 | 0 | AnimateEventAcceptAndWin aeaaw = new AnimateEventAcceptAndWin(); |
366 | 0 | if (acceptIcon == null) |
367 | 0 | acceptIcon = new ImageIcon(getClass().getResource("/g/green-check-mark-217x250.png")); |
368 | 0 | aeaaw.setIcon(acceptIcon); |
369 | 0 | aeaaw.setWho(gameEventAcceptAndWin.getWho() == Board.X ? Board.O : Board.X); |
370 | 0 | aeaaw.setResigningPoints(gameEventAcceptAndWin.getResigningPoints()); |
371 | 0 | queueEvent(aeaaw); |
372 | 0 | } |
373 | // XXX | |
374 | private void addDouble(GameEventDouble gameDouble) { | |
375 | 0 | AnimateEventDouble aed = createAnimateEventDouble(); |
376 | 0 | aed.setBoardPane(boardPane); |
377 | 0 | aed.setCubeBefore(gameDouble.getCubeBefore()); |
378 | 0 | aed.setWhoDoubled(gameDouble.getWhoDoubled()); |
379 | 0 | queueEvent(aed); |
380 | 0 | } |
381 | ||
382 | /////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// | |
383 | /** A little helper class for keeping track of moving checkers | |
384 | * @author dick | |
385 | */ | |
386 | private class PointOffset { | |
387 | 0 | PointOffset(int point, int offset) { |
388 | 0 | this.point = point; this.offset = offset; |
389 | 0 | } |
390 | public int point; | |
391 | public int offset; | |
392 | } | |
393 | ||
394 | private void addMove(GameEventMove gameMove) { | |
395 | 0 | int[] m = gameMove.getMoves(); |
396 | 0 | accessEventList(EventListCommand.calcCheckers, null, pointArray); |
397 | if (DEBUGmove) | |
398 | logger.info("who=" + gameMove.getWho() | |
399 | + " bar=" + pointArray[paBAR+gameMove.getWho()] + " home=" + pointArray[paHOME+gameMove.getWho()]); | |
400 | ||
401 | 0 | LinkedList<PointOffset> movingCheckers = new LinkedList<PointOffset>(); |
402 | Iterator<PointOffset> iter; | |
403 | ||
404 | 0 | for (int i=0; i<m.length; i+=2) { |
405 | 0 | AnimateEventMove am = createAnimateEventMove(); |
406 | 0 | am.setBoardPane(boardPane); |
407 | 0 | am.setWho(gameMove.getWho()); |
408 | 0 | int startPoint = m[i]; |
409 | 0 | int startOffset = Math.abs(pointArray[startPoint]); |
410 | 0 | startOffset--; |
411 | 0 | iter = movingCheckers.iterator(); |
412 | 0 | while (iter.hasNext()) { |
413 | 0 | PointOffset po = iter.next(); |
414 | 0 | if (po.point == startPoint) { |
415 | 0 | startOffset = po.offset; |
416 | 0 | iter.remove(); |
417 | } | |
418 | 0 | } |
419 | 0 | if (m[i] == Board.Bar) { |
420 | 0 | startOffset = pointArray[paBAR+gameMove.getWho()]-1; |
421 | 0 | pointArray[paBAR+gameMove.getWho()]--; |
422 | } else { | |
423 | 0 | pointArray[m[i]] -= gameMove.getDepth(); |
424 | } | |
425 | 0 | am.setStartPointAndOffset(m[i], startOffset); |
426 | 0 | int endPoint = m[i+1]; |
427 | 0 | int endOffset = Math.abs(pointArray[m[i+1]]); |
428 | 0 | for (PointOffset po : movingCheckers) { |
429 | 0 | if (po.point == endPoint) { |
430 | 0 | endOffset = po.offset+1; |
431 | 0 | break; |
432 | } | |
433 | 0 | } |
434 | 0 | if (m[i+1] == Board.Home) { |
435 | 0 | endOffset = pointArray[paHOME+gameMove.getWho()]; |
436 | 0 | pointArray[paHOME+gameMove.getWho()]++; |
437 | } else | |
438 | 0 | pointArray[m[i+1]] += gameMove.getDepth(); |
439 | 0 | am.setEndPointAndOffset(endPoint, endOffset); |
440 | 0 | movingCheckers.addFirst(new PointOffset(endPoint, endOffset)); |
441 | if (DEBUGmove) { | |
442 | logger.info("addMove: checkers on start point[" + m[i] + "]=" + pointArray[m[i]]); | |
443 | logger.info("addMove: checkers on end point[" + m[i+1] + "]=" + pointArray[m[i+1]]); | |
444 | logger.info("addMove: start=" + am.pointNumberStart + "/" + am.checkerNumberStart + " end=" | |
445 | + am.pointNumberEnd + "/" + am.checkerNumberEnd); | |
446 | } | |
447 | 0 | am.calculate(); |
448 | 0 | queueEvent(am); |
449 | } | |
450 | 0 | } |
451 | ||
452 | private void maybeSortDice(int[] dice) { | |
453 | 0 | if (this.bfProps.isHighDieLeft() && dice[0] < dice[1]) { |
454 | int x; | |
455 | 0 | x = dice[0]; |
456 | 0 | dice[0] = dice[1]; |
457 | 0 | dice[1] = x; |
458 | } | |
459 | 0 | } |
460 | ||
461 | /** Get a new AnimateMove object depending on the user's preference | |
462 | * of which kind to use. | |
463 | * @return The new AnimateEventMove | |
464 | */ | |
465 | private AnimateEventMove createAnimateEventMove() { | |
466 | 0 | AnimateEventMove am = null; |
467 | 0 | switch (bfProps.getAnimateType()) { |
468 | case AnimateEventMove.ANIMATE_FLASHER: | |
469 | 0 | am = new MoveFlasher(); |
470 | 0 | break; |
471 | case AnimateEventMove.ANIMATE_SPLINE2D: | |
472 | 0 | am = new MoveSpline2D(); |
473 | break; | |
474 | } | |
475 | 0 | return(am); |
476 | } | |
477 | ||
478 | /** Get a new AnimateMove object depending on the user's preference | |
479 | * of which kind to use. | |
480 | * @return The new AnimateEventMove | |
481 | */ | |
482 | private AnimateEventDouble createAnimateEventDouble() { | |
483 | 0 | AnimateEventDouble ad = null; |
484 | 0 | switch (bfProps.getAnimateType()) { |
485 | case AnimateEventMove.ANIMATE_FLASHER: | |
486 | // XXX: ad = new DoubleFlasher(); | |
487 | 0 | ad = new DoubleSpline2D(); // XXX: Temp! |
488 | 0 | break; |
489 | case AnimateEventMove.ANIMATE_SPLINE2D: | |
490 | 0 | ad = new DoubleSpline2D(); |
491 | break; | |
492 | } | |
493 | 0 | return(ad); |
494 | } | |
495 | ||
496 | /** Queue this event for execution. If the queue isn't running, then start it | |
497 | * @param am The Event to queue | |
498 | */ | |
499 | private void queueEvent(AnimateEvent am) { | |
500 | //boolean start = accessEventList(EventListCommand.head, null, null) == null; | |
501 | 0 | accessEventList(EventListCommand.add, am, null); |
502 | 0 | if (moveTimer == null) |
503 | 0 | startMoveList(); |
504 | 0 | } |
505 | ||
506 | /////////////////////////////////////////////////////////////////////////////// | |
507 | /////////////////////////////////////////////////////////////////////////////// | |
508 | /////////////////////////////////////////////////////////////////////////////// | |
509 | /////////////////////////////////////////////////////////////////////////////// | |
510 | 0 | private LinkedList<AnimateEvent> _private_eventList = new LinkedList<AnimateEvent>(); |
511 | 0 | private AnimateEvent guiEvent = null; |
512 | 0 | private Timer moveTimer = null; |
513 | 0 | private MoveTimerTask moveTimerTask = null; |
514 | ||
515 | /** Commands handled by the synchronized list function below */ | |
516 | 0 | private enum EventListCommand { |
517 | 0 | head, |
518 | 0 | add, |
519 | 0 | remove, |
520 | 0 | calcCheckers, |
521 | 0 | size, |
522 | 0 | removeAll |
523 | } | |
524 | /** provide access to the cross-thread eventList. | |
525 | * We perform several functions, based on {@link EventListCommand}<br> | |
526 | * head = return the first element of the list.<br> | |
527 | * add = queue this AnimateEvent at the end of the list.<br> | |
528 | * remove = remove the head element from the list.<br> | |
529 | * calcCheckers = fill out the derived "current" checkerPositions by walking the list.<br> | |
530 | * size = fill out checkerPositions[0] with the size of the list.<br> | |
531 | * removeAll = empty the list. | |
532 | * | |
533 | * @param command The {@link EventListCommand} to execute on the _private_eventList | |
534 | * @param ae The AnimateEvent to queue | |
535 | * @param checkerPositions The checkerPositions to fill out | |
536 | * @return Some commands return the head of the list, most return null. | |
537 | */ | |
538 | synchronized private AnimateEvent accessEventList(EventListCommand command, AnimateEvent ae, int[] checkerPositions) { | |
539 | 0 | switch (command) { |
540 | case remove: | |
541 | 0 | if (_private_eventList.isEmpty()) |
542 | 0 | return(null); |
543 | 0 | ae = _private_eventList.removeFirst(); |
544 | 0 | if (_private_eventList.size() == 0 && ae.isGui()) |
545 | 0 | guiEvent = ae; |
546 | 0 | return(ae); |
547 | case add: | |
548 | 0 | _private_eventList.add(ae); |
549 | 0 | guiEvent = null; |
550 | 0 | return(null); |
551 | case head: | |
552 | 0 | if (guiEvent != null) |
553 | 0 | return(guiEvent); |
554 | 0 | if (_private_eventList.isEmpty()) |
555 | 0 | return(null); |
556 | 0 | return(_private_eventList.getFirst()); |
557 | case calcCheckers: | |
558 | int i; | |
559 | 0 | if (lastBoard != null) { |
560 | 0 | for (i=0; i<25; i++) // set some default positions |
561 | 0 | checkerPositions[i] = lastBoard.getPoints()[i]; |
562 | 0 | checkerPositions[paHOME] = lastBoard.getHome()[0]; |
563 | 0 | checkerPositions[paHOME+1] = lastBoard.getHome()[1]; |
564 | 0 | checkerPositions[paBAR] = lastBoard.getBar()[0]; |
565 | 0 | checkerPositions[paBAR+1] = lastBoard.getBar()[1]; |
566 | if (DEBUG && false) { | |
567 | logger.info("calcCheckers: home:" + checkerPositions[paHOME] + "-" + checkerPositions[paHOME+1] | |
568 | + " bar:" + checkerPositions[paBAR] + "-" + checkerPositions[paBAR+1]); | |
569 | } | |
570 | } | |
571 | 0 | for (AnimateEvent e : _private_eventList) { |
572 | 0 | switch (e.getType()) { |
573 | case Board: | |
574 | 0 | AnimateEventBoard aeb = (AnimateEventBoard)e; |
575 | 0 | Board b = aeb.getBoard(); |
576 | 0 | int[] p = b.getPoints(); |
577 | 0 | for (i=0; i<25; i++) |
578 | 0 | checkerPositions[i] = p[i]; |
579 | 0 | checkerPositions[Board.Bar] = b.getBar()[aeb.getWho()]; |
580 | 0 | checkerPositions[Board.Home] = b.getHome()[aeb.getWho()]; |
581 | if (DEBUG && false) | |
582 | logger.info("calcCheckers: home=" + checkerPositions[Board.Home] | |
583 | + " from " + b.getHome()[0] + "-" + b.getHome()[1]); | |
584 | 0 | break; |
585 | case Move: | |
586 | 0 | AnimateEventMove aem = (AnimateEventMove)e; |
587 | 0 | if (checkerPositions[aem.pointNumberStart] < 0) { |
588 | 0 | checkerPositions[aem.pointNumberStart]++; |
589 | 0 | checkerPositions[aem.pointNumberEnd]--; |
590 | } else { | |
591 | 0 | checkerPositions[aem.pointNumberStart]++; |
592 | 0 | checkerPositions[aem.pointNumberEnd]--; |
593 | } | |
594 | } | |
595 | 0 | } |
596 | 0 | return(null); |
597 | case size: | |
598 | 0 | checkerPositions[0] = _private_eventList.size(); |
599 | 0 | return(null); |
600 | case removeAll: | |
601 | 0 | _private_eventList.clear(); |
602 | 0 | return(null); |
603 | } | |
604 | 0 | logger.error("unhandled accessEventList command: " + command.toString()); |
605 | 0 | return(null); |
606 | } | |
607 | /////////////////////////////////////////////////////////////////////////////// | |
608 | /////////////////////////////////////////////////////////////////////////////// | |
609 | /////////////////////////////////////////////////////////////////////////////// | |
610 | /////////////////////////////////////////////////////////////////////////////// | |
611 | ||
612 | 0 | private int timerIncrement = 50; |
613 | ||
614 | private void startMoveList() { | |
615 | 0 | moveTimer = new Timer(); |
616 | 0 | moveTimerTask = new MoveTimerTask(); |
617 | 0 | moveTimer.schedule(moveTimerTask, 0, timerIncrement); |
618 | 0 | } |
619 | ||
620 | 0 | private class MoveTimerTask extends TimerTask { |
621 | 0 | private double offset = 0.0; |
622 | 0 | private int[] queueLength = new int[1]; |
623 | 0 | private double timerIncrementD = (double)timerIncrement/1000.0; |
624 | ||
625 | @Override | |
626 | public void run() { | |
627 | ||
628 | 0 | AnimateEvent ae = accessEventList(EventListCommand.head, null, null); |
629 | 0 | if (ae != null) { |
630 | if (DEBUGqueue) | |
631 | logger.info("mtt: process Event: " + ae.getType().toString() + " " + ae.toString()); | |
632 | // Figure out our time increment based on queueLength | |
633 | //timerIncrementD = (double)timerIncrement/1000.0; | |
634 | 0 | timerIncrementD = (double)timerIncrement/(double)ae.getDuration(); |
635 | 0 | accessEventList(EventListCommand.size, null, queueLength); |
636 | 0 | if (queueLength[0] > 7) |
637 | 0 | timerIncrementD *= 2; |
638 | 0 | if (queueLength[0] > 10) |
639 | 0 | timerIncrementD *= 2; |
640 | 0 | if (queueLength[0] > 12) |
641 | 0 | timerIncrementD *= 1.2; |
642 | ||
643 | // Is this a board? Special handling | |
644 | 0 | if (ae.getType() == AnimateEvent.Type.Board) { |
645 | 0 | AnimateEventBoard aeb = (AnimateEventBoard)ae; |
646 | 0 | lastBoard = aeb.getBoard(); |
647 | 0 | boardPane.setBoard(lastBoard); |
648 | 0 | accessEventList(EventListCommand.remove, null, null); |
649 | if (DEBUGqueue) | |
650 | logger.info("mtt: remove EventBoard: " + aeb.getType().toString()); | |
651 | 0 | offset = 0.0; |
652 | 0 | for (int i=0; i<extraCheckers.length; i++) |
653 | 0 | extraCheckers[i] = 0; |
654 | 0 | extraCheckersIndex = 0; |
655 | 0 | boardPane.updateBoardTab(); |
656 | 0 | } else { |
657 | 0 | ae.setOffset(offset); |
658 | 0 | switch (ae.getType()) { |
659 | case Move: | |
660 | 0 | AnimateEventMove am = (AnimateEventMove)ae; |
661 | 0 | if (offset == 0.0) { |
662 | 0 | extraCheckersWho = am.getWho(); |
663 | 0 | boolean extraFound = false; |
664 | 0 | for (int i=0; i<extraCheckersIndex; i+=2) { |
665 | 0 | if (extraCheckers[i] == am.pointNumberStart) { |
666 | 0 | extraFound = true; |
667 | 0 | for (int j=i; j<extraCheckersIndex; j+=2) { |
668 | 0 | extraCheckers[j] = extraCheckers[j+2]; |
669 | 0 | extraCheckers[j+1] = extraCheckers[j+3]; |
670 | } | |
671 | 0 | extraCheckersIndex -= 2; |
672 | 0 | break; |
673 | } | |
674 | } | |
675 | 0 | if (!extraFound && lastBoard != null) { |
676 | 0 | if (lastBoard.getPoints()[am.pointNumberStart] < 0) |
677 | 0 | lastBoard.getPoints()[am.pointNumberStart]++; |
678 | else | |
679 | 0 | lastBoard.getPoints()[am.pointNumberStart]--; |
680 | 0 | if (am.pointNumberStart == Board.Bar) |
681 | 0 | lastBoard.getBar()[ae.getWho()]--; |
682 | } | |
683 | ||
684 | 0 | } |
685 | break; | |
686 | case Double: | |
687 | 0 | AnimateEventDouble aed = (AnimateEventDouble)ae; |
688 | 0 | if (offset == 0.0) { |
689 | 0 | aed.calculate(lastBoard); |
690 | } | |
691 | break; | |
692 | case Roll: | |
693 | 0 | if (lastBoard != null) { // clear out any dice in the board during roll |
694 | 0 | int[][] d = lastBoard.getDice(); |
695 | 0 | d[Board.X][0] = 0; |
696 | 0 | d[Board.X][1] = 0; |
697 | 0 | d[Board.O][0] = 0; |
698 | 0 | d[Board.O][1] = 0; |
699 | 0 | } |
700 | break; | |
701 | case PleaseMove: | |
702 | 0 | AnimateEventPleaseMove aepm = (AnimateEventPleaseMove) ae; |
703 | 0 | if (lastBoard != null) { |
704 | 0 | int[][] d = lastBoard.getDice(); |
705 | 0 | d[Board.O][0] = aepm.getDice()[0]; |
706 | 0 | d[Board.O][1] = aepm.getDice()[1]; |
707 | 0 | boardGui.yourMove(aepm.getCheckersToMove()); |
708 | 0 | boardGui.playSound(AudioManager.Cue.YourTurn); |
709 | if (DEBUG) | |
710 | 0 | logger.info("******DING YOUR TURN dice=" + d[Board.O][0] + "-" + d[Board.O][1] |
711 | 0 | + " who=" + aepm.getWho() + " offset=" + aepm.offset); |
712 | } | |
713 | 0 | offset = 1.0; |
714 | 0 | break; |
715 | case PleaseRollOrDouble: | |
716 | 0 | if (lastBoard != null) { |
717 | 0 | lastBoard.setYourTurnToRollOrDouble(true); |
718 | 0 | boardGui.playSound(AudioManager.Cue.RollOrDouble); |
719 | } | |
720 | 0 | offset = 1.0; |
721 | 0 | break; |
722 | case PleaseAcceptOrRejectDouble: | |
723 | 0 | if (lastBoard != null) { |
724 | 0 | lastBoard.setAcceptDeclineDouble(); |
725 | 0 | boardGui.playSound(AudioManager.Cue.Doubled); |
726 | } | |
727 | 0 | offset = 1.0; |
728 | 0 | break; |
729 | case PleaseAcceptOrRejectResign: | |
730 | 0 | AnimateEventPleaseAcceptOrRejectResign ar = (AnimateEventPleaseAcceptOrRejectResign)ae; |
731 | 0 | if (lastBoard != null) { |
732 | 0 | if (ar.getWho() != Board.O) |
733 | 0 | lastBoard.setAcceptDeclineResign(true); |
734 | 0 | lastBoard.setWhoIsResigning(ar.getWho()); |
735 | 0 | lastBoard.setResigningPoints(ar.getResigningPoints()); |
736 | 0 | boardGui.playSound(AudioManager.Cue.Doubled); |
737 | } | |
738 | 0 | offset = 1.0; |
739 | 0 | break; |
740 | case RejectResign: | |
741 | 0 | if (lastBoard != null) { |
742 | 0 | lastBoard.setAcceptDeclineResign(false); |
743 | } | |
744 | break; | |
745 | case FirstRoll: | |
746 | 0 | AnimateEventFirstRoll aefr = (AnimateEventFirstRoll)ae; |
747 | 0 | if (lastBoard == null) { |
748 | 0 | lastBoard = new Board(); |
749 | 0 | lastBoard.setPlayerName(Board.O, aefr.getWhitePlayer()); |
750 | 0 | lastBoard.setPlayerName(Board.X, aefr.getBlackPlayer()); |
751 | } | |
752 | break; | |
753 | } | |
754 | ///////////////////////////////////////////////////////////////// | |
755 | // Repaint the board and update | |
756 | 0 | boardPane.repaint(); |
757 | 0 | offset += timerIncrementD; |
758 | //logger.info("event:" + ae + " "+ ae.getType().toString() + " offset=" + offset + " timerD=" + timerIncrementD); | |
759 | ||
760 | // If this event is terminating, maybe special handling. | |
761 | 0 | if (offset > 1.0) { |
762 | 0 | switch (ae.getType()) { |
763 | case Move: | |
764 | 0 | AnimateEventMove am = (AnimateEventMove)ae; |
765 | 0 | extraCheckers[extraCheckersIndex++] = am.pointNumberEnd; |
766 | 0 | extraCheckers[extraCheckersIndex++] = am.checkerNumberEnd; |
767 | 0 | if (lastBoard != null) { // clear out any dice in the board after move |
768 | 0 | int[][] d = lastBoard.getDice(); |
769 | 0 | d[Board.X][0] = 0; |
770 | 0 | d[Board.X][1] = 0; |
771 | 0 | d[Board.O][0] = 0; |
772 | 0 | d[Board.O][1] = 0; |
773 | 0 | } |
774 | break; | |
775 | case Roll: | |
776 | 0 | AnimateEventDiceRoll ar = (AnimateEventDiceRoll)ae; |
777 | 0 | if (lastBoard != null) { // restore the dice on the board so we don't flash the screen |
778 | 0 | int[][] d = lastBoard.getDice(); |
779 | 0 | if (ar.getWho() == Board.X) { |
780 | 0 | d[Board.X][0] = ar.dice[0]; |
781 | 0 | d[Board.X][1] = ar.dice[1]; |
782 | } else { | |
783 | 0 | d[Board.O][0] = ar.dice[0]; |
784 | 0 | d[Board.O][1] = ar.dice[1]; |
785 | } | |
786 | 0 | } |
787 | break; | |
788 | case CantMove: | |
789 | 0 | if (lastBoard != null) { // clear out any dice in the board after cantmove |
790 | 0 | int[][] d = lastBoard.getDice(); |
791 | 0 | d[Board.X][0] = 0; |
792 | 0 | d[Board.X][1] = 0; |
793 | 0 | d[Board.O][0] = 0; |
794 | 0 | d[Board.O][1] = 0; |
795 | } | |
796 | break; | |
797 | } | |
798 | 0 | AnimateEvent aer = accessEventList(EventListCommand.remove, null, null); |
799 | 0 | offset = 0.0; |
800 | if (DEBUGqueue) | |
801 | logger.info("mtt: remove Event: " + aer.getType().toString()); | |
802 | } | |
803 | } | |
804 | } | |
805 | ||
806 | // if there are no events in the list, then shut down the timer. | |
807 | 0 | int[] i = new int[1]; |
808 | 0 | accessEventList(EventListCommand.size, null, i); |
809 | 0 | if (i[0] == 0) { |
810 | if (DEBUGqueue) | |
811 | logger.info("mtt: terminate timer"); | |
812 | 0 | if (moveTimer != null) |
813 | 0 | moveTimer.cancel(); |
814 | 0 | if (moveTimerTask != null) |
815 | 0 | moveTimerTask.cancel(); |
816 | 0 | moveTimer = null; |
817 | 0 | moveTimerTask = null; |
818 | 0 | return; |
819 | } | |
820 | 0 | } |
821 | } | |
822 | } |