1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
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
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
226 if (breakpoint()) {
227
228 g2D.drawImage(breakpointImg, 19, 2, 8, 8, null);
229 }
230
231
232 if (hasMaskSkipAllVery()) {
233
234 g2D.drawImage(skipAllVeryImg, 26, 2, 8, 8, null);
235 } else if (hasMaskTmpSkipAllVery()) {
236
237 g2D.drawImage(skipAllOnceVeryImg, 26, 2, 8, 8, null);
238 } else if (hasMaskSkipAllMedium()) {
239
240 g2D.drawImage(skipAllMediumImg, 26, 2, 8, 8, null);
241 } else if (hasMaskTmpSkipAllMedium()) {
242
243 g2D.drawImage(skipAllMediumOnceImg, 26, 2, 8, 8, null);
244 } else if (hasMaskSkipAll()) {
245
246 g2D.drawImage(skipAllImg, 26, 2, 8, 8, null);
247 } else if (hasMaskTmpSkipAll()) {
248
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
442
443
444
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
462 optionsMenu.show(e.getComponent(), e.getX(), e.getY());
463 } else {
464
465
466
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
508 }
509
510 public void mouseReleased(MouseEvent e) {
511
512 }
513
514 public void mouseEntered(MouseEvent e) {
515
516 }
517
518 public void mouseExited(MouseEvent e) {
519
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 }