View Javadoc

1   /*
2    * Created on 23 oct. 2003
3    * 
4    *   Firemox 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.firemox.stack;
23  
24  import static net.sf.firemox.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.firemox.network.ConnectionManager;
43  import net.sf.firemox.stack.phasetype.PhaseType;
44  import net.sf.firemox.tools.Configuration;
45  import net.sf.firemox.tools.Picture;
46  import net.sf.firemox.ui.MagicUIComponents;
47  import net.sf.firemox.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.firemox.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.firemox.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 owning 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 		setOpaque(true);
155 		setMinimumSize(new Dimension(PHASE_STD_WIDTH, PHASE_STD_HEIGHT));
156 		setPreferredSize(new Dimension(PHASE_STD_WIDTH, PHASE_STD_HEIGHT));
157 		this.phaseType = phaseType;
158 		this.idPlayer = idPlayer;
159 		if (popupImg == null) {
160 			popupImg = Picture.loadImage(IMAGES_DIR + "popup.gif");
161 			breakpointImg = Picture.loadImage(IMAGES_DIR + "smlbreak.gif");
162 			skipAllImg = Picture.loadImage(IMAGES_DIR + "smlskip1.gif");
163 			skipAllOnceImg = Picture.loadImage(IMAGES_DIR + "smlskip2.gif");
164 			skipAllVeryImg = Picture.loadImage(IMAGES_DIR + "smlskip3.gif");
165 			skipAllOnceVeryImg = Picture.loadImage(IMAGES_DIR + "smlskip4.gif");
166 			skipAllMediumImg = Picture.loadImage(IMAGES_DIR + "smlskip5.gif");
167 			skipAllMediumOnceImg = Picture.loadImage(IMAGES_DIR + "smlskip4.gif");
168 		}
169 		mask = settingFile.read();
170 		setBorder(new EtchedBorder());
171 		setToolTipText(LanguageManagerMDB.getString(phaseType.phaseName));
172 		addMouseListener(this);
173 		reset();
174 	}
175 
176 	/***
177 	 * Update the background following the active player
178 	 * 
179 	 * @param currentPhase
180 	 *          if true, set this phase as activated
181 	 * @param activePlayer
182 	 *          if true, set this phase with normal background, otherwise it's
183 	 *          background is darker as normal
184 	 */
185 	void setActive(boolean currentPhase, boolean activePlayer) {
186 		if (this.currentPhase != currentPhase || this.activePlayer != activePlayer) {
187 			this.currentPhase = currentPhase;
188 			this.activePlayer = activePlayer;
189 			if (currentPhase) {
190 				if (activePlayer) {
191 					setBackground(Color.RED);
192 				} else {
193 					setBackground(Color.RED.darker());
194 				}
195 			} else if (activePlayer) {
196 				setBackground(null);
197 			} else {
198 				setBackground(Color.DARK_GRAY);
199 			}
200 		}
201 	}
202 
203 	/***
204 	 * update this component
205 	 * 
206 	 * @param g
207 	 *          the graphics of this component
208 	 */
209 	@Override
210 	public void paint(Graphics g) {
211 		final Graphics2D g2D = (Graphics2D) g;
212 		super.paint(g);
213 		if (idPlayer == 1 && Configuration.getBoolean("reverseSide", false)) {
214 			g2D.translate(PHASE_STD_WIDTH - 1, PHASE_STD_HEIGHT - 1);
215 			g2D.rotate(Math.PI);
216 		}
217 
218 		// Draw the phase picture
219 		if (currentPhase) {
220 			g2D.drawImage(phaseType.highLightedIcon, 2, 2, 16, 16, null);
221 		} else {
222 			g2D.drawImage(phaseType.normalIcon, 2, 2, 16, 16, null);
223 		}
224 
225 		// update the little breakpoint button
226 		if (breakpoint()) {
227 			// draw the little breakpoint button
228 			g2D.drawImage(breakpointImg, 19, 2, 8, 8, null);
229 		}
230 
231 		// draw the little skipAllVery button
232 		if (hasMaskSkipAllVery()) {
233 			// draw the little skipAllVery (once) button
234 			g2D.drawImage(skipAllVeryImg, 26, 2, 8, 8, null);
235 		} else if (hasMaskTmpSkipAllVery()) {
236 			// draw the little skipAllMedium button
237 			g2D.drawImage(skipAllOnceVeryImg, 26, 2, 8, 8, null);
238 		} else if (hasMaskSkipAllMedium()) {
239 			// draw the little skipAllMedium (once) button
240 			g2D.drawImage(skipAllMediumImg, 26, 2, 8, 8, null);
241 		} else if (hasMaskTmpSkipAllMedium()) {
242 			// draw the little skipAllVery button
243 			g2D.drawImage(skipAllMediumOnceImg, 26, 2, 8, 8, null);
244 		} else if (hasMaskSkipAll()) {
245 			// draw the little skipAll button
246 			g2D.drawImage(skipAllImg, 26, 2, 8, 8, null);
247 		} else if (hasMaskTmpSkipAll()) {
248 			// draw the little skipAl (once) button
249 			g2D.drawImage(skipAllOnceImg, 26, 2, 8, 8, null);
250 		}
251 	}
252 
253 	/***
254 	 * Save settings of this phase to the specified output stream
255 	 * 
256 	 * @param out
257 	 *          is output stream where settings of this phase would be saved
258 	 * @throws IOException
259 	 *           if error occurred while writing settings to settingFile
260 	 */
261 	public void saveSettings(FileOutputStream out) throws IOException {
262 		out.write(mask);
263 	}
264 
265 	/***
266 	 * Load settings of this phase from the specified input stream
267 	 * 
268 	 * @param input
269 	 *          is input stream where settings of this phase is saved
270 	 * @throws IOException
271 	 *           if error occurred while reading settings from settingFile
272 	 */
273 	public void loadSettings(InputStream input) throws IOException {
274 		mask = input.read();
275 	}
276 
277 	/***
278 	 * remove all breakpoints and options of this phase
279 	 */
280 	public void reset() {
281 		setOpaque(true);
282 	}
283 
284 	/***
285 	 * Tell if this phase has the option "breakpoint to this phase"
286 	 * 
287 	 * @return true if there is a breakpoint on this phase
288 	 * @see net.sf.firemox.Magic#manualSkip()
289 	 */
290 	public boolean breakpoint() {
291 		return (mask & MASK_BREAKPOINT) == MASK_BREAKPOINT;
292 	}
293 
294 	/***
295 	 * Set if this phase has the option "breakpoint to this phase"
296 	 * 
297 	 * @param really
298 	 *          indication if we enable or disable this option
299 	 * @see MPhase#MASK_BREAKPOINT
300 	 */
301 	void setBreakpoint(boolean really) {
302 		if (really) {
303 			mask |= MASK_BREAKPOINT;
304 		} else {
305 			mask &= ~MASK_BREAKPOINT;
306 		}
307 	}
308 
309 	/***
310 	 * Indicates whether this phase has the option "decline response to my effects
311 	 * until this phase".
312 	 * 
313 	 * @return true if there is a 'skip all' on this phase
314 	 * @see net.sf.firemox.Magic#manualSkip()
315 	 */
316 	public boolean declineResponseMe() {
317 		return hasMaskSkipAllVery() || hasMaskTmpSkipAllVery() || hasMaskSkipAll()
318 				|| hasMaskTmpSkipAll();
319 	}
320 
321 	/***
322 	 * Set if this phase has the option "decline response to all effects until
323 	 * this phase".
324 	 * 
325 	 * @param really
326 	 *          indication if we enable or disable this option
327 	 * @see MPhase#MASK_BREAKPOINT
328 	 */
329 	void setSkipAll(boolean really) {
330 		if (really) {
331 			mask |= MASK_SKIP_ALL;
332 		} else {
333 			mask &= ~MASK_SKIP_ALL;
334 		}
335 	}
336 
337 	/***
338 	 * Set if this phase has the option "decline response to my effects until this
339 	 * phase". This option will be disabled arriving to this phase.
340 	 * 
341 	 * @param really
342 	 *          indication if we enable or disable this option
343 	 * @see MPhase#MASK_TMP_SKIP_ALL
344 	 */
345 	void setSkipAllTmp(boolean really) {
346 		if (really) {
347 			mask |= MASK_TMP_SKIP_ALL;
348 		} else {
349 			mask &= ~MASK_TMP_SKIP_ALL;
350 		}
351 	}
352 
353 	/***
354 	 * Indicates whether this phase has the option "decline response to opponent's
355 	 * effects until this phase".
356 	 * 
357 	 * @return true if this phase has the option "decline response to opponent's
358 	 *         effects until this phase".
359 	 * @see net.sf.firemox.Magic#manualSkip()
360 	 */
361 	public boolean declineResponseOpponent() {
362 		return hasMaskSkipAllVery() || hasMaskTmpSkipAllVery()
363 				|| hasMaskTmpSkipAllMedium() || hasMaskSkipAllMedium();
364 	}
365 
366 	/***
367 	 * Set if this phase has the option "decline response to all opponent's
368 	 * effects until this phase".
369 	 * 
370 	 * @param really
371 	 *          indication if we enable or disable this option
372 	 * @see MPhase#MASK_SKIP_ALL_MEDIUM
373 	 */
374 	void setSkipMedium(boolean really) {
375 		if (really) {
376 			mask |= MASK_SKIP_ALL_MEDIUM;
377 		} else {
378 			mask &= ~MASK_SKIP_ALL_MEDIUM;
379 		}
380 	}
381 
382 	/***
383 	 * Set if this phase has the option "decline response to all opponent's
384 	 * effects until this phase". This option will be disabled arriving to this
385 	 * phase.
386 	 * 
387 	 * @param really
388 	 *          indication if we enable or disable this option
389 	 * @see MPhase#MASK_TMP_SKIP_ALL_MEDIUM
390 	 */
391 	void setSkipMediumTmp(boolean really) {
392 		if (really) {
393 			mask |= MASK_TMP_SKIP_ALL_MEDIUM;
394 		} else {
395 			mask &= ~MASK_TMP_SKIP_ALL_MEDIUM;
396 		}
397 	}
398 
399 	/***
400 	 * Set if this phase has the option "breakpoint to this phase"
401 	 * 
402 	 * @param really
403 	 *          indication if we enable or disable this option
404 	 * @see MPhase#MASK_BREAKPOINT
405 	 */
406 	void setSkipAllVery(boolean really) {
407 		if (really) {
408 			mask |= MASK_SKIP_ALL_VERY;
409 		} else {
410 			mask &= ~MASK_SKIP_ALL_VERY;
411 		}
412 	}
413 
414 	/***
415 	 * Set if this phase has the option "decline response to all effects until
416 	 * this phase". This option will be disabled arriving to this phase.
417 	 * 
418 	 * @param really
419 	 *          indication if we enable or disable this option
420 	 * @see MPhase#MASK_BREAKPOINT
421 	 */
422 	void setSkipAllVeryTmp(boolean really) {
423 		if (really) {
424 			mask |= MASK_TMP_SKIP_ALL_VERY;
425 		} else {
426 			mask &= ~MASK_TMP_SKIP_ALL_VERY;
427 		}
428 	}
429 
430 	/***
431 	 * is called when you click on me
432 	 * 
433 	 * @param e
434 	 *          is the mouse event
435 	 */
436 	public void mouseClicked(MouseEvent e) {
437 		StackManager.noReplayToken.take();
438 		try {
439 			if (ConnectionManager.isConnected()) {
440 				if (e.getButton() != MouseEvent.BUTTON1) {
441 					// e.isPopupTrigger() may not work
442 					/*
443 					 * right button is pressed, show the options popup menu and mark the
444 					 * phase clicked triggerPhase as this.
445 					 */
446 					triggerPhase = this;
447 					((JCheckBoxMenuItem) optionsMenu.getComponent(0))
448 							.setSelected(breakpoint());
449 					((JCheckBoxMenuItem) optionsMenu.getComponent(1))
450 							.setSelected(hasMaskSkipAll());
451 					((JCheckBoxMenuItem) optionsMenu.getComponent(2))
452 							.setSelected(hasMaskTmpSkipAll());
453 					((JCheckBoxMenuItem) optionsMenu.getComponent(3))
454 							.setSelected(hasMaskSkipAllMedium());
455 					((JCheckBoxMenuItem) optionsMenu.getComponent(4))
456 							.setSelected(hasMaskTmpSkipAllMedium());
457 					((JCheckBoxMenuItem) optionsMenu.getComponent(5))
458 							.setSelected(hasMaskSkipAllVery());
459 					((JCheckBoxMenuItem) optionsMenu.getComponent(6))
460 							.setSelected(hasMaskTmpSkipAllVery());
461 					// show the history popup menu
462 					optionsMenu.show(e.getComponent(), e.getX(), e.getY());
463 				} else {
464 					// left button is pressed
465 					// enable or disable the skip as longer as opponent doesn't do
466 					// anything
467 					setSkipAllTmp(true);
468 					repaint();
469 					if (StackManager.idHandedPlayer == 0
470 							&& this != EventManager.currentPhase()) {
471 						MagicUIComponents.magicForm.manualSkip();
472 					}
473 				}
474 			}
475 		} catch (Throwable t) {
476 			t.printStackTrace();
477 		} finally {
478 			StackManager.noReplayToken.release();
479 		}
480 	}
481 
482 	private boolean hasMaskTmpSkipAllVery() {
483 		return (mask & MASK_TMP_SKIP_ALL_VERY) == MASK_TMP_SKIP_ALL_VERY;
484 	}
485 
486 	private boolean hasMaskSkipAllMedium() {
487 		return (mask & MASK_SKIP_ALL_MEDIUM) == MASK_SKIP_ALL_MEDIUM;
488 	}
489 
490 	private boolean hasMaskSkipAllVery() {
491 		return (mask & MASK_SKIP_ALL_VERY) == MASK_SKIP_ALL_VERY;
492 	}
493 
494 	private boolean hasMaskTmpSkipAllMedium() {
495 		return (mask & MASK_TMP_SKIP_ALL_MEDIUM) == MASK_TMP_SKIP_ALL_MEDIUM;
496 	}
497 
498 	private boolean hasMaskTmpSkipAll() {
499 		return (mask & MASK_TMP_SKIP_ALL) == MASK_TMP_SKIP_ALL;
500 	}
501 
502 	private boolean hasMaskSkipAll() {
503 		return (mask & MASK_SKIP_ALL) == MASK_SKIP_ALL;
504 	}
505 
506 	public void mousePressed(MouseEvent e) {
507 		// Ignore this event
508 	}
509 
510 	public void mouseReleased(MouseEvent e) {
511 		// Ignore this event
512 	}
513 
514 	public void mouseEntered(MouseEvent e) {
515 		// Ignore this event
516 	}
517 
518 	public void mouseExited(MouseEvent e) {
519 		// Ignore this event
520 	}
521 
522 	/***
523 	 * is the popupMenu displayed when you right-click to see options of this
524 	 * phase.
525 	 */
526 	public static JPopupMenu optionsMenu;
527 
528 	/***
529 	 * image for little popupMenu's button
530 	 */
531 	private static Image popupImg;
532 
533 	/***
534 	 * image for little bookmark button
535 	 */
536 	private static Image breakpointImg;
537 
538 	/***
539 	 * image for little skipAll button
540 	 */
541 	private static Image skipAllImg;
542 
543 	/***
544 	 * image for little skipAll (once) button
545 	 */
546 	private static Image skipAllOnceImg;
547 
548 	/***
549 	 * image for little skipAllVery button
550 	 */
551 	private static Image skipAllVeryImg;
552 
553 	/***
554 	 * image for little skipAllVery button (once)
555 	 */
556 	private static Image skipAllOnceVeryImg;
557 
558 	/***
559 	 * image for little skipAllMedium button
560 	 */
561 	private static Image skipAllMediumOnceImg;
562 
563 	/***
564 	 * image for little skipAllMedium (once) button
565 	 */
566 	private static Image skipAllMediumImg;
567 
568 	/***
569 	 * the last phase where popup trigger has been recorded
570 	 */
571 	public static MPhase triggerPhase;
572 
573 	/***
574 	 * Contain all phase objects of players
575 	 */
576 	public static MPhase[][] phases = null;
577 
578 	/***
579 	 * Indicates if this phase is the current one or not
580 	 */
581 	private boolean currentPhase = false;
582 
583 	/***
584 	 * Mask used for skip options
585 	 */
586 	private int mask;
587 
588 	/***
589 	 * idPlayer of this player
590 	 */
591 	private int idPlayer;
592 
593 	/***
594 	 * Phase type associated to this phase component
595 	 */
596 	public PhaseType phaseType;
597 
598 	private boolean activePlayer;
599 
600 	/***
601 	 * Indicates if this phase has to be skipped. The beginning_of_... and
602 	 * phase_... events would not be raised. Nevertheless, the before_phase_...
603 	 * trigger the current phase, the the awakened abilities should be mana source
604 	 * and played in the background as abstract abilities.
605 	 */
606 	public boolean skipThisPhase;
607 
608 }