View Javadoc

1   /*
2    * Created on 23 oct. 2003
3    * 
4    *   Magic-Project is a turn based strategy simulator
5    *   Copyright (C) 2003-2007 Fabrice Daugan
6    *
7    *   This program is free software; you can redistribute it and/or modify it 
8    * under the terms of the GNU General Public License as published by the Free 
9    * Software Foundation; either version 2 of the License, or (at your option) any
10   * later version.
11   *
12   *   This program is distributed in the hope that it will be useful, but WITHOUT 
13   * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS
14   * FOR A PARTICULAR PURPOSE.  See the GNU General Public License for more 
15   * details.
16   *
17   *   You should have received a copy of the GNU General Public License along  
18   * with this program; if not, write to the Free Software Foundation, Inc., 
19   * 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
20   * 
21   */
22  package net.sf.magicproject.stack;
23  
24  import static net.sf.magicproject.token.IdConst.IMAGES_DIR;
25  
26  import java.awt.Color;
27  import java.awt.Dimension;
28  import java.awt.Graphics;
29  import java.awt.Graphics2D;
30  import java.awt.Image;
31  import java.awt.event.MouseEvent;
32  import java.awt.event.MouseListener;
33  import java.io.FileOutputStream;
34  import java.io.IOException;
35  import java.io.InputStream;
36  
37  import javax.swing.JCheckBoxMenuItem;
38  import javax.swing.JPanel;
39  import javax.swing.JPopupMenu;
40  import javax.swing.border.EtchedBorder;
41  
42  import net.sf.magicproject.network.ConnectionManager;
43  import net.sf.magicproject.stack.phasetype.PhaseType;
44  import net.sf.magicproject.tools.Configuration;
45  import net.sf.magicproject.tools.Picture;
46  import net.sf.magicproject.ui.MagicUIComponents;
47  import net.sf.magicproject.ui.i18n.LanguageManagerMDB;
48  
49  /***
50   * Represents a phase of turn of one player. Severals breakpoint flags can be
51   * attached to a phase to control the turn flow.
52   * 
53   * @author Fabrice Daugan
54   * @since 0.21 a graphical representation of phase
55   * @since 0.31 a graphical representation for each players
56   * @since 0.4 phase settings are saved.
57   * @since 0.80 medium skip flag added.
58   */
59  public class MPhase extends JPanel implements MouseListener {
60  
61  	/***
62  	 * The phase picture width
63  	 */
64  	private static final int PHASE_STD_WIDTH = 40;
65  
66  	/***
67  	 * The phase picture height
68  	 */
69  	private static final int PHASE_STD_HEIGHT = 22;
70  
71  	/***
72  	 * Mask used to indicate if this phase has the option "breakpoint to this
73  	 * phase" if any ability is activated during this phase, even if the option
74  	 * "skippAll" is set in the following phases.
75  	 * 
76  	 * @see MPhase#breakpoint()
77  	 * @see net.sf.magicproject.Magic#manualSkip()
78  	 */
79  	private static final int MASK_BREAKPOINT = 0x01;
80  
81  	/***
82  	 * Mask used to indicate if this phase has the option Set if this phase has
83  	 * the option "decline response to my effects until this phase".
84  	 * 
85  	 * @see MPhase#declineResponseMe()
86  	 * @see MPhase#setSkipAll(boolean)
87  	 * @see net.sf.magicproject.Magic#manualSkip()
88  	 */
89  	private static final int MASK_SKIP_ALL = 0x0002;
90  
91  	/***
92  	 * Mask used to indicate if this phase has the option Set if this phase has
93  	 * the option "decline response to my effects until this phase". This option
94  	 * will be disabled arriving to this phase.
95  	 * 
96  	 * @see MPhase#declineResponseMe()
97  	 * @see MPhase#setSkipAllTmp(boolean)
98  	 */
99  	private static final int MASK_TMP_SKIP_ALL = 0x0004;
100 
101 	/***
102 	 * Mask used to indicate if this phase has the option "decline response to all
103 	 * effects until this phase".
104 	 * 
105 	 * @see MPhase#declineResponseOpponent()
106 	 * @see MPhase#setSkipAllVery(boolean)
107 	 */
108 	private static final int MASK_SKIP_ALL_VERY = 0x0008;
109 
110 	/***
111 	 * Mask used to indicate if this phase has the option "decline response to all
112 	 * effects until this phase". This option will be disabled arriving to this
113 	 * phase.
114 	 * 
115 	 * @see MPhase#declineResponseOpponent()
116 	 * @see MPhase#setSkipAllVeryTmp(boolean)
117 	 */
118 	private static final int MASK_TMP_SKIP_ALL_VERY = 0x0010;
119 
120 	/***
121 	 * Mask used to indicate if this phase has the option "decline response to all
122 	 * opponent's effects until this phase".
123 	 * 
124 	 * @see MPhase#declineResponseOpponent()
125 	 * @see MPhase#setSkipMedium(boolean)
126 	 */
127 	private static final int MASK_SKIP_ALL_MEDIUM = 0x0020;
128 
129 	/***
130 	 * Mask used to indicate if this phase has the option "decline response to all
131 	 * opponent's effects until this phase".This option will be disabled arriving
132 	 * to this phase.
133 	 * 
134 	 * @see MPhase#declineResponseOpponent()
135 	 * @see MPhase#setSkipMediumTmp(boolean)
136 	 */
137 	private static final int MASK_TMP_SKIP_ALL_MEDIUM = 0x0040;
138 
139 	/***
140 	 * Create a new instance of MPhase
141 	 * 
142 	 * @param phaseType
143 	 *          is the phase type associated to this phase
144 	 * @param idPlayer
145 	 *          is the player ownig this phase
146 	 * @param settingFile
147 	 *          the setting file where phase settings would be read
148 	 * @throws IOException
149 	 *           if error occurred while reading settings from settingFile
150 	 */
151 	public MPhase(PhaseType phaseType, int idPlayer, InputStream settingFile)
152 			throws IOException {
153 		super();
154 		setMinimumSize(new Dimension(PHASE_STD_WIDTH, PHASE_STD_HEIGHT));
155 		setPreferredSize(new Dimension(PHASE_STD_WIDTH, PHASE_STD_HEIGHT));
156 		this.phaseType = phaseType;
157 		this.idPlayer = idPlayer;
158 		if (popupImg == null) {
159 			popupImg = Picture.loadImage(IMAGES_DIR + "popup.gif");
160 			breakpointImg = Picture.loadImage(IMAGES_DIR + "smlbreak.gif");
161 			skipAllImg = Picture.loadImage(IMAGES_DIR + "smlskip1.gif");
162 			skipAllOnceImg = Picture.loadImage(IMAGES_DIR + "smlskip2.gif");
163 			skipAllVeryImg = Picture.loadImage(IMAGES_DIR + "smlskip3.gif");
164 			skipAllOnceVeryImg = Picture.loadImage(IMAGES_DIR + "smlskip4.gif");
165 			skipAllMediumImg = Picture.loadImage(IMAGES_DIR + "smlskip5.gif");
166 			skipAllMediumOnceImg = Picture.loadImage(IMAGES_DIR + "smlskip4.gif");
167 		}
168 		mask = settingFile.read();
169 		setBorder(new EtchedBorder());
170 		setToolTipText(LanguageManagerMDB.getString(phaseType.phaseName));
171 		addMouseListener(this);
172 		reset();
173 	}
174 
175 	/***
176 	 * Update the backgroung following the active player
177 	 * 
178 	 * @param currentPhase
179 	 *          if true, set this phase as activated
180 	 * @param activePlayer
181 	 *          if true, set this phase with normal backgroung, otherwise it's
182 	 *          backgroung is darker as normal
183 	 */
184 	void setActive(boolean currentPhase, boolean activePlayer) {
185 		if (currentPhase != currentPhase || activePlayer != activePlayer) {
186 			this.currentPhase = currentPhase;
187 		}
188 		if (currentPhase) {
189 			if (activePlayer) {
190 				setBackground(Color.RED);
191 			} else {
192 				setBackground(Color.RED.darker());
193 			}
194 		} else if (activePlayer) {
195 			setBackground(null);
196 		} else {
197 			setBackground(Color.DARK_GRAY);
198 		}
199 	}
200 
201 	/***
202 	 * update this component
203 	 * 
204 	 * @param g
205 	 *          the graphics of this component
206 	 */
207 	@Override
208 	public void paint(Graphics g) {
209 		final Graphics2D g2D = (Graphics2D) g;
210 		super.paint(g);
211 		if (idPlayer == 1 && Configuration.getBoolean("reverseSide", false)) {
212 			g2D.translate(PHASE_STD_WIDTH - 1, PHASE_STD_HEIGHT - 1);
213 			g2D.rotate(Math.PI);
214 		}
215 
216 		if (currentPhase) {
217 			g2D.drawImage(phaseType.highLightedIcon, 2, 2, 16, 16, null);
218 		} else {
219 			g2D.drawImage(phaseType.normalIcon, 2, 2, 16, 16, null);
220 		}
221 
222 		// update the little popupMenu's button
223 		if (activatedPopup) {
224 			// draw the little popupMenu's button
225 			g2D.drawImage(popupImg, 28, 12, 9, 8, null);
226 		}
227 
228 		// update the little breakpoint button
229 		if (breakpoint()) {
230 			// draw the little breakpoint button
231 			g2D.drawImage(breakpointImg, 19, 2, 8, 8, null);
232 		}
233 
234 		// draw the little skipAllVery button
235 		if (hasMaskSkipAllVery()) {
236 			// draw the little skipAllVery (once) button
237 			g2D.drawImage(skipAllVeryImg, 26, 2, 8, 8, null);
238 		} else if (hasMaskTmpSkipAllVery()) {
239 			// draw the little skipAllMedium button
240 			g2D.drawImage(skipAllOnceVeryImg, 26, 2, 8, 8, null);
241 		} else if (hasMaskSkipAllMedium()) {
242 			// draw the little skipAllMedium (once) button
243 			g2D.drawImage(skipAllMediumImg, 26, 2, 8, 8, null);
244 		} else if (hasMaskTmpSkipAllMedium()) {
245 			// draw the little skipAllVery button
246 			g2D.drawImage(skipAllMediumOnceImg, 26, 2, 8, 8, null);
247 		} else if (hasMaskSkipAll()) {
248 			// draw the little skipAll button
249 			g2D.drawImage(skipAllImg, 26, 2, 8, 8, null);
250 		} else if (hasMaskTmpSkipAll()) {
251 			// draw the little skipAl (once) button
252 			g2D.drawImage(skipAllOnceImg, 26, 2, 8, 8, null);
253 		}
254 	}
255 
256 	/***
257 	 * Save settings of this phase to the specified output stream
258 	 * 
259 	 * @param out
260 	 *          is output stream where settings of this phase would be saved
261 	 * @throws IOException
262 	 *           if error occurred while writting settings to settingFile
263 	 */
264 	public void saveSettings(FileOutputStream out) throws IOException {
265 		out.write(mask);
266 	}
267 
268 	/***
269 	 * Load settings of this phase from the specified input stream
270 	 * 
271 	 * @param input
272 	 *          is input stream where settings of this phase is saved
273 	 * @throws IOException
274 	 *           if error occurred while reading settings from settingFile
275 	 */
276 	public void loadSettings(InputStream input) throws IOException {
277 		mask = input.read();
278 	}
279 
280 	/***
281 	 * remove all breakpoints and options of this phase
282 	 */
283 	public void reset() {
284 		activatedPopup = false;
285 		setOpaque(true);
286 	}
287 
288 	/***
289 	 * Tell if this phase has the option "breapoint to this phase"
290 	 * 
291 	 * @return true if there is a breakpoint on this phase
292 	 * @see MPhase#MASK_BREAKPOINT
293 	 * @see net.sf.magicproject.Magic#manualSkip()
294 	 */
295 	public boolean breakpoint() {
296 		return (mask & MASK_BREAKPOINT) == MASK_BREAKPOINT;
297 	}
298 
299 	/***
300 	 * Set if this phase has the option "breapoint to this phase"
301 	 * 
302 	 * @param really
303 	 *          indication if we set or unset this option
304 	 * @see MPhase#MASK_BREAKPOINT
305 	 */
306 	void setBreakpoint(boolean really) {
307 		if (really) {
308 			mask |= MASK_BREAKPOINT;
309 		} else {
310 			mask &= ~MASK_BREAKPOINT;
311 		}
312 	}
313 
314 	/***
315 	 * Indicates wether this phase has the option "decline response to my effects
316 	 * until this phase".
317 	 * 
318 	 * @return true if there is a 'skip all' on this phase
319 	 * @see MPhase#MASK_SKIP_ALL
320 	 * @see MPhase#MASK_TMP_SKIP_ALL
321 	 * @see MPhase#MASK_SKIP_ALL_VERY
322 	 * @see MPhase#MASK_TMP_SKIP_ALL_VERY
323 	 * @see net.sf.magicproject.Magic#manualSkip()
324 	 */
325 	public boolean declineResponseMe() {
326 		return hasMaskSkipAllVery() || hasMaskTmpSkipAllVery() || hasMaskSkipAll()
327 				|| hasMaskTmpSkipAll();
328 	}
329 
330 	/***
331 	 * Set if this phase has the option "decline response to all effects until
332 	 * this phase".
333 	 * 
334 	 * @param really
335 	 *          indication if we set or unset this option
336 	 * @see MPhase#MASK_BREAKPOINT
337 	 */
338 	void setSkipAll(boolean really) {
339 		if (really) {
340 			mask |= MASK_SKIP_ALL;
341 		} else {
342 			mask &= ~MASK_SKIP_ALL;
343 		}
344 	}
345 
346 	/***
347 	 * Set if this phase has the option "decline response to my effects until this
348 	 * phase". This option will be disabled arriving to this phase.
349 	 * 
350 	 * @param really
351 	 *          indication if we set or unset this option
352 	 * @see MPhase#MASK_TMP_SKIP_ALL
353 	 */
354 	void setSkipAllTmp(boolean really) {
355 		if (really) {
356 			mask |= MASK_TMP_SKIP_ALL;
357 		} else {
358 			mask &= ~MASK_TMP_SKIP_ALL;
359 		}
360 	}
361 
362 	/***
363 	 * Indicates wether this phase has the option "decline response to opponent's
364 	 * effects until this phase".
365 	 * 
366 	 * @return true if this phase has the option "decline response to opponent's
367 	 *         effects until this phase".
368 	 * @see MPhase#MASK_SKIP_ALL_VERY
369 	 * @see MPhase#MASK_TMP_SKIP_ALL_VERY
370 	 * @see MPhase#MASK_SKIP_ALL_MEDIUM
371 	 * @see MPhase#MASK_TMP_SKIP_ALL_MEDIUM
372 	 * @see net.sf.magicproject.Magic#manualSkip()
373 	 */
374 	public boolean declineResponseOpponent() {
375 		return hasMaskSkipAllVery() || hasMaskTmpSkipAllVery()
376 				|| hasMaskTmpSkipAllMedium() || hasMaskSkipAllMedium();
377 	}
378 
379 	/***
380 	 * Set if this phase has the option "decline response to all opponent's
381 	 * effects until this phase".
382 	 * 
383 	 * @param really
384 	 *          indication if we set or unset this option
385 	 * @see MPhase#MASK_SKIP_ALL_MEDIUM
386 	 */
387 	void setSkipMedium(boolean really) {
388 		if (really) {
389 			mask |= MASK_SKIP_ALL_MEDIUM;
390 		} else {
391 			mask &= ~MASK_SKIP_ALL_MEDIUM;
392 		}
393 	}
394 
395 	/***
396 	 * Set if this phase has the option "decline response to all opponent's
397 	 * effects until this phase". This option will be disabled arriving to this
398 	 * phase.
399 	 * 
400 	 * @param really
401 	 *          indication if we set or unset this option
402 	 * @see MPhase#MASK_TMP_SKIP_ALL_MEDIUM
403 	 */
404 	void setSkipMediumTmp(boolean really) {
405 		if (really) {
406 			mask |= MASK_TMP_SKIP_ALL_MEDIUM;
407 		} else {
408 			mask &= ~MASK_TMP_SKIP_ALL_MEDIUM;
409 		}
410 	}
411 
412 	/***
413 	 * Set if this phase has the option "breapoint to this phase"
414 	 * 
415 	 * @param really
416 	 *          indication if we set or unset this option
417 	 * @see MPhase#MASK_BREAKPOINT
418 	 */
419 	void setSkipAllVery(boolean really) {
420 		if (really) {
421 			mask |= MASK_SKIP_ALL_VERY;
422 		} else {
423 			mask &= ~MASK_SKIP_ALL_VERY;
424 		}
425 	}
426 
427 	/***
428 	 * Set if this phase has the option "decline response to all effects until
429 	 * this phase". This option will be disabled arriving to this phase.
430 	 * 
431 	 * @param really
432 	 *          indication if we set or unset this option
433 	 * @see MPhase#MASK_BREAKPOINT
434 	 */
435 	void setSkipAllVeryTmp(boolean really) {
436 		if (really) {
437 			mask |= MASK_TMP_SKIP_ALL_VERY;
438 		} else {
439 			mask &= ~MASK_TMP_SKIP_ALL_VERY;
440 		}
441 	}
442 
443 	/***
444 	 * is called when you click on me
445 	 * 
446 	 * @param e
447 	 *          is the mouse event
448 	 */
449 	public void mouseClicked(MouseEvent e) {
450 		StackManager.noReplayToken.take();
451 		try {
452 			if (ConnectionManager.isConnected()) {
453 				if (e.getButton() != MouseEvent.BUTTON1) {
454 					// e.isPopupTrigger() may not work
455 					/*
456 					 * right button is pressed, show the options popup menu and mark the
457 					 * phase clicked triggerPhase as this.
458 					 */
459 					triggerPhase = this;
460 					((JCheckBoxMenuItem) optionsMenu.getComponent(0))
461 							.setSelected(breakpoint());
462 					((JCheckBoxMenuItem) optionsMenu.getComponent(1))
463 							.setSelected(hasMaskSkipAll());
464 					((JCheckBoxMenuItem) optionsMenu.getComponent(2))
465 							.setSelected(hasMaskTmpSkipAll());
466 					((JCheckBoxMenuItem) optionsMenu.getComponent(3))
467 							.setSelected(hasMaskSkipAllMedium());
468 					((JCheckBoxMenuItem) optionsMenu.getComponent(4))
469 							.setSelected(hasMaskTmpSkipAllMedium());
470 					((JCheckBoxMenuItem) optionsMenu.getComponent(5))
471 							.setSelected(hasMaskSkipAllVery());
472 					((JCheckBoxMenuItem) optionsMenu.getComponent(6))
473 							.setSelected(hasMaskTmpSkipAllVery());
474 					// show the history popup menu
475 					optionsMenu.show(e.getComponent(), e.getX(), e.getY());
476 				} else {
477 					// left button is pressed
478 					if (EventManager.currentPhase() == this && activatedPopup
479 							&& e.getX() >= 13 && e.getY() >= 8) {
480 						// show the history popup menu
481 						popupMenu.show(e.getComponent(), e.getX(), e.getY());
482 					} else {
483 						// set/unset the skip as longer as opponent doesn't do anything
484 						setSkipAllTmp(true);
485 						repaint();
486 						if (StackManager.idHandedPlayer == 0
487 								&& this != EventManager.currentPhase()) {
488 							MagicUIComponents.magicForm.manualSkip();
489 						}
490 					}
491 				}
492 			}
493 		} catch (Throwable t) {
494 			t.printStackTrace();
495 		} finally {
496 			StackManager.noReplayToken.release();
497 		}
498 	}
499 
500 	private boolean hasMaskTmpSkipAllVery() {
501 		return (mask & MASK_TMP_SKIP_ALL_VERY) == MASK_TMP_SKIP_ALL_VERY;
502 	}
503 
504 	private boolean hasMaskSkipAllMedium() {
505 		return (mask & MASK_SKIP_ALL_MEDIUM) == MASK_SKIP_ALL_MEDIUM;
506 	}
507 
508 	private boolean hasMaskSkipAllVery() {
509 		return (mask & MASK_SKIP_ALL_VERY) == MASK_SKIP_ALL_VERY;
510 	}
511 
512 	private boolean hasMaskTmpSkipAllMedium() {
513 		return (mask & MASK_TMP_SKIP_ALL_MEDIUM) == MASK_TMP_SKIP_ALL_MEDIUM;
514 	}
515 
516 	private boolean hasMaskTmpSkipAll() {
517 		return (mask & MASK_TMP_SKIP_ALL) == MASK_TMP_SKIP_ALL;
518 	}
519 
520 	private boolean hasMaskSkipAll() {
521 		return (mask & MASK_SKIP_ALL) == MASK_SKIP_ALL;
522 	}
523 
524 	public void mousePressed(MouseEvent e) {
525 		// Ignore this event
526 	}
527 
528 	public void mouseReleased(MouseEvent e) {
529 		// Ignore this event
530 	}
531 
532 	public void mouseEntered(MouseEvent e) {
533 		// Ignore this event
534 	}
535 
536 	public void mouseExited(MouseEvent e) {
537 		// Ignore this event
538 	}
539 
540 	/***
541 	 * is the popupMenu displayable when click to see hystory of stack
542 	 */
543 	public static JPopupMenu popupMenu;
544 
545 	/***
546 	 * is the popupMenu displayable when you right-click to see options of this
547 	 * phase.
548 	 */
549 	public static JPopupMenu optionsMenu;
550 
551 	/***
552 	 * image for litle popupMenu's button
553 	 */
554 	private static Image popupImg;
555 
556 	/***
557 	 * image for litle bookmark button
558 	 */
559 	private static Image breakpointImg;
560 
561 	/***
562 	 * image for litle skipAll button
563 	 */
564 	private static Image skipAllImg;
565 
566 	/***
567 	 * image for litle skipAll (once) button
568 	 */
569 	private static Image skipAllOnceImg;
570 
571 	/***
572 	 * image for litle skipAllVery button
573 	 */
574 	private static Image skipAllVeryImg;
575 
576 	/***
577 	 * image for litle skipAllVery button (once)
578 	 */
579 	private static Image skipAllOnceVeryImg;
580 
581 	/***
582 	 * image for litle skipAllMedium button
583 	 */
584 	private static Image skipAllMediumOnceImg;
585 
586 	/***
587 	 * image for litle skipAllMedium (once) button
588 	 */
589 	private static Image skipAllMediumImg;
590 
591 	/***
592 	 * is the popupMenu's button is visible
593 	 */
594 	private static boolean activatedPopup = false;
595 
596 	/***
597 	 * the last phase where popup trigger has been recorded
598 	 */
599 	public static MPhase triggerPhase;
600 
601 	/***
602 	 * will contain all MPhase object of players
603 	 */
604 	public static MPhase[][] phases = null;
605 
606 	/***
607 	 * Indicates if this phase is the current one or not
608 	 */
609 	private boolean currentPhase = false;
610 
611 	/***
612 	 * Mask used for skip options
613 	 */
614 	private int mask;
615 
616 	/***
617 	 * idPlayer of this player
618 	 */
619 	private int idPlayer;
620 
621 	/***
622 	 * Phase type associated to this phase component
623 	 */
624 	public PhaseType phaseType;
625 
626 	/***
627 	 * Indicates if this phase has to be skipped. The beginning_of_... and
628 	 * phase_... events would not be raised. Nevertheless, the before_phase_...
629 	 * trigger the current phase, the the awakened abilities should be mana source
630 	 * and played in the background as abstract abilities.
631 	 */
632 	public boolean skipThisPhase;
633 
634 }