View Javadoc

1   /*
2    *   Magic-Project is a turn based strategy simulator
3    *   Copyright (C) 2003-2007 Fabrice Daugan
4    *
5    *   This program is free software; you can redistribute it and/or modify it 
6    * under the terms of the GNU General Public License as published by the Free 
7    * Software Foundation; either version 2 of the License, or (at your option) any
8    * later version.
9    *
10   *   This program is distributed in the hope that it will be useful, but WITHOUT 
11   * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS
12   * FOR A PARTICULAR PURPOSE.  See the GNU General Public License for more 
13   * details.
14   *
15   *   You should have received a copy of the GNU General Public License along  
16   * with this program; if not, write to the Free Software Foundation, Inc., 
17   * 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
18   * 
19   */
20  package net.sf.magicproject.clickable.targetable.player;
21  
22  import java.awt.BorderLayout;
23  import java.awt.Color;
24  import java.awt.Dimension;
25  import java.awt.FlowLayout;
26  import java.awt.Font;
27  import java.awt.Graphics;
28  import java.awt.Graphics2D;
29  import java.awt.Image;
30  import java.awt.event.MouseEvent;
31  import java.io.IOException;
32  import java.io.InputStream;
33  import java.util.List;
34  
35  import javax.swing.BoxLayout;
36  import javax.swing.JButton;
37  import javax.swing.JLabel;
38  import javax.swing.JPanel;
39  import javax.swing.JSplitPane;
40  import javax.swing.border.EtchedBorder;
41  
42  import net.sf.magicproject.action.PayMana;
43  import net.sf.magicproject.action.WaitActivatedChoice;
44  import net.sf.magicproject.action.WaitTriggeredBufferChoice;
45  import net.sf.magicproject.action.target.ChoosenTarget;
46  import net.sf.magicproject.clickable.ability.Ability;
47  import net.sf.magicproject.clickable.mana.ManaPool;
48  import net.sf.magicproject.clickable.targetable.Targetable;
49  import net.sf.magicproject.clickable.targetable.TargetableFactory;
50  import net.sf.magicproject.clickable.targetable.card.CardFactory;
51  import net.sf.magicproject.clickable.targetable.card.TriggeredCard;
52  import net.sf.magicproject.deckbuilder.Deck;
53  import net.sf.magicproject.modifier.RegisterIndirection;
54  import net.sf.magicproject.modifier.RegisterModifier;
55  import net.sf.magicproject.network.ConnectionManager;
56  import net.sf.magicproject.network.IdMessages;
57  import net.sf.magicproject.network.Synchronizer;
58  import net.sf.magicproject.operation.Operation;
59  import net.sf.magicproject.stack.ActionManager;
60  import net.sf.magicproject.stack.EventManager;
61  import net.sf.magicproject.stack.MPhase;
62  import net.sf.magicproject.stack.StackManager;
63  import net.sf.magicproject.stack.TargetHelper;
64  import net.sf.magicproject.token.IdTokens;
65  import net.sf.magicproject.token.MCommonVars;
66  import net.sf.magicproject.tools.Configuration;
67  import net.sf.magicproject.tools.Log;
68  import net.sf.magicproject.ui.MagicUIComponents;
69  import net.sf.magicproject.ui.UIHelper;
70  import net.sf.magicproject.ui.i18n.LanguageManager;
71  import net.sf.magicproject.zone.ZoneManager;
72  
73  /***
74   * Represents a player : name (avatar, preferences,...), mana and zones. Has
75   * also a register like cards to store data as life, poison, maximal number of
76   * cards in hand and number of cards to draw counter.<br>
77   * TODO support modifiers for player components, like cards.
78   * 
79   * @author <a href="mailto:fabdouglas@users.sourceforge.net">Fabrice Daugan </a>
80   */
81  public abstract class Player extends Targetable {
82  
83  	/***
84  	 * Player view width
85  	 */
86  	public static final int PLAYER_SIZE_WIDTH = 80;
87  
88  	/***
89  	 * Player view height
90  	 */
91  	public static final int PLAYER_SIZE_HEIGHT = 40;
92  
93  	/***
94  	 * creates a new instance of MPlayer
95  	 * 
96  	 * @param idPlayer
97  	 *          id of this player
98  	 * @param mana
99  	 *          manas of this player
100 	 * @param zoneManager
101 	 *          the zoneManager of this player
102 	 * @param morePanel
103 	 *          the panel containing player info.
104 	 */
105 	protected Player(int idPlayer, ManaPool mana, ZoneManager zoneManager,
106 			JPanel morePanel) {
107 		this.idPlayer = idPlayer;
108 		this.zoneManager = zoneManager;
109 		this.mana = mana;
110 		this.morePanel = morePanel;
111 		setForeground(Color.RED);
112 		setBorder(new EtchedBorder());
113 		addMouseListener(this);
114 		setPreferredSize(new Dimension(130, PLAYER_SIZE_HEIGHT));
115 		playerCard = new PlayerCard(this);
116 		avatarButton = new AvatarButton();
117 		avatarButton.setMinimumSize(new Dimension(1, 180));
118 		avatarButton.addMouseListener(this);
119 		mainPanel = new JPanel(new BorderLayout());
120 		mainPanel.setBorder(null);
121 		infoPanel = new JPanel(new FlowLayout(FlowLayout.LEFT, 0, 0));
122 
123 		lifeLabel = new JLabel("", UIHelper.getIcon("life.gif"), 0);
124 		lifeLabel.setBackground(Color.pink);
125 		lifeLabel.setForeground(new Color(0, 153, 153));
126 		lifeLabel.setToolTipText(LanguageManager.getString("life"));
127 		lifeLabel.setBorder(new EtchedBorder());
128 		lifeLabel.setIconTextGap(6);
129 		lifeLabel.setPreferredSize(new Dimension(45, PLAYER_SIZE_HEIGHT));
130 		lifeLabel.setOpaque(true);
131 
132 		poisonLabel = new JLabel("", UIHelper.getIcon("poison.gif"), 0);
133 		poisonLabel.setBackground(new Color(0, 102, 0));
134 		poisonLabel.setForeground(new Color(153, 255, 153));
135 		poisonLabel.setToolTipText(LanguageManager.getString("poison"));
136 		poisonLabel.setBorder(new EtchedBorder());
137 		poisonLabel.setIconTextGap(6);
138 		poisonLabel.setPreferredSize(new Dimension(45, PLAYER_SIZE_HEIGHT));
139 		poisonLabel.setOpaque(true);
140 
141 		phases = new JPanel();
142 		phases.setLayout(new BoxLayout(phases, BoxLayout.Y_AXIS));
143 		phases.setOpaque(false);
144 		phases.setMinimumSize(new Dimension(43, PLAYER_SIZE_WIDTH));
145 
146 		handSplitter = new JSplitPane();
147 		handSplitter.setDividerSize(12);
148 		handSplitter.setOneTouchExpandable(true);
149 		handSplitter.setAutoscrolls(true);
150 		handSplitter.setOpaque(false);
151 		handSplitter.setBorder(null);
152 		initComponents();
153 		mainPanel.add(handSplitter, BorderLayout.CENTER);
154 		JPanel namePanel = new JPanel();
155 		namePanel.setLayout(new BoxLayout(namePanel, BoxLayout.Y_AXIS));
156 
157 		// nick name
158 		nickNamePanel = new JPanel();
159 		nickNamePanel.setLayout(new BoxLayout(nickNamePanel, BoxLayout.X_AXIS));
160 		JLabel nickLabel = new JLabel(UIHelper.getIcon("nickname.gif"));
161 		nickLabel.setPreferredSize(new Dimension(18, 18));
162 		nickNamePanel.add(nickLabel);
163 		namePanel.add(nickNamePanel);
164 
165 		// real name
166 		realNamePanel = new JPanel();
167 		realNamePanel.setLayout(new BoxLayout(realNamePanel, BoxLayout.X_AXIS));
168 		// by default the real name panel is not visible
169 		realNamePanel.setVisible(false);
170 		JLabel realLabel = new JLabel(UIHelper.getIcon("id.gif"));
171 		realLabel.setPreferredSize(new Dimension(18, 18));
172 		realNamePanel.add(realLabel);
173 		namePanel.add(realNamePanel);
174 
175 		// avatar picture
176 		morePanel.add(avatarButton, BorderLayout.CENTER);
177 		morePanel.add(namePanel, BorderLayout.NORTH);
178 
179 		// toggle buttons
180 		togglePanel = new JPanel();
181 		togglePanel.setLayout(new BoxLayout(togglePanel, BoxLayout.X_AXIS));
182 		morePanel.add(togglePanel, BorderLayout.SOUTH);
183 
184 		abilitiesPanel = new JPanel();
185 		abilitiesPanel.setLayout(new BoxLayout(abilitiesPanel, BoxLayout.Y_AXIS));
186 		morePanel.add(abilitiesPanel, BorderLayout.SOUTH);
187 	}
188 
189 	/***
190 	 * Init components of this player.
191 	 */
192 	protected abstract void initComponents();
193 
194 	/***
195 	 * Update the opponent side depending on the "enable reverse" options.
196 	 */
197 	public abstract void updateReversed();
198 
199 	/***
200 	 * Remove, and then fill the phases of this player
201 	 * 
202 	 * @param phases
203 	 *          the phases to add.
204 	 */
205 	public void reset(MPhase[] phases) {
206 		this.phases.removeAll();
207 		for (MPhase phase : phases) {
208 			this.phases.add(phase);
209 		}
210 		this.phases.doLayout();
211 	}
212 
213 	@Override
214 	public boolean isCard() {
215 		return false;
216 	}
217 
218 	@Override
219 	public final boolean isAbility() {
220 		return false;
221 	}
222 
223 	@Override
224 	public final boolean isSpell() {
225 		return false;
226 	}
227 
228 	/***
229 	 * tell if this player is you
230 	 * 
231 	 * @return true if this player is you
232 	 */
233 	public abstract boolean isYou();
234 
235 	/***
236 	 * tell if this player is the current one
237 	 * 
238 	 * @return true if this player is the current one
239 	 */
240 	public boolean isCurrentPlayer() {
241 		return StackManager.idCurrentPlayer == idPlayer;
242 	}
243 
244 	/***
245 	 * Return the opponent player
246 	 * 
247 	 * @return the opponent player
248 	 */
249 	public Player getOpponent() {
250 		return StackManager.PLAYERS[1 - idPlayer];
251 	}
252 
253 	@Override
254 	protected final void highLight(Color highLightColor) {
255 		setBackground(highLightColor);
256 		if (CardFactory.ACTIVATED_COLOR.equals(highLightColor)) {
257 			// this player has activated abilities, we add them as JButton
258 			final List<Ability> abilities = WaitActivatedChoice.getInstance()
259 					.abilitiesOf(playerCard);
260 			if (abilities.size() > 0) {
261 				abilitiesPanel.add(new JLabel(LanguageManager.getString("abilities")
262 						+ " : "));
263 			}
264 			for (int i = abilities.size(); i-- > 0;) {
265 				final JButton button = new JButton(abilities.get(i).toString());
266 				button.setActionCommand("" + i);
267 				button.addActionListener(this);
268 				abilitiesPanel.add(button);
269 			}
270 		}
271 		avatarButton.setBackground(highLightColor);
272 		MagicUIComponents.playerTabbedPanel.setSelectedComponent(morePanel);
273 		MagicUIComponents.playerTabbedPanel.setBackgroundAt(
274 				MagicUIComponents.playerTabbedPanel.indexOfComponent(morePanel),
275 				highLightColor);
276 		super.highLight(highLightColor);
277 	}
278 
279 	@Override
280 	public void disHighLight() {
281 		isHighLighted = false;
282 		setBackground(new Color(204, 204, 204));
283 		avatarButton.setBackground(null);
284 		abilitiesPanel.removeAll();
285 		try {
286 			MagicUIComponents.playerTabbedPanel
287 					.setBackgroundAt(MagicUIComponents.playerTabbedPanel
288 							.indexOfComponent(morePanel), null);
289 		} catch (Exception e) {
290 			Log.error("Error in tabbedpanel : " + e);
291 		}
292 		super.disHighLight();
293 	}
294 
295 	/***
296 	 * Set to the register of this card a value to a specified index. The given
297 	 * operation is uesed to apply operation on old and the given value. To set
298 	 * the given value as the new one, use the "set" operation.
299 	 * 
300 	 * @param index
301 	 *          is the index of register to modify
302 	 * @param operation
303 	 *          the operation to use
304 	 * @param rightValue
305 	 *          is the value to use as right operande for the operation
306 	 */
307 	public void setValue(int index, Operation operation, int rightValue) {
308 		registers[index] = operation.process(registers[index], rightValue);
309 
310 		if (index < 6) {
311 			// update mana pool buttons
312 			mana.manaButtons[index].repaint();
313 		} else if (index == IdTokens.LIFE) {
314 			// update life button
315 			updateLife();
316 		} else if (index == IdTokens.POISON) {
317 			// update poison button
318 			updatePoison();
319 		}
320 	}
321 
322 	@Override
323 	public void update(Graphics g) {
324 		super.paintComponent(g);
325 		// draw the highlighted rectangle
326 		if (isHighLighted) {
327 			g.setColor(highLightColor);
328 			g.draw3DRect(0, 0, getWidth() - 1, getHeight() - 1, true);
329 			g.draw3DRect(1, 1, getWidth() - 3, getHeight() - 3, true);
330 		}
331 		g.dispose();
332 	}
333 
334 	/***
335 	 * update the life counter label
336 	 */
337 	private void updateLife() {
338 		lifeLabel.setText(String.valueOf(registers[IdTokens.LIFE]));
339 	}
340 
341 	/***
342 	 * update the life counter label
343 	 */
344 	private void updatePoison() {
345 		poisonLabel.setText(String.valueOf(registers[IdTokens.POISON]));
346 	}
347 
348 	/***
349 	 * Initialize the labels referencing to the registers of the MPlayer
350 	 * components.
351 	 */
352 	public static void init() {
353 		for (Player player : StackManager.PLAYERS) {
354 			player.updateLife();
355 			player.updatePoison();
356 			player.mana.setVisible(PayMana.useMana);
357 		}
358 		MagicUIComponents.chatHistoryText.setContact(StackManager.PLAYERS[0]
359 				.getNickName(), StackManager.PLAYERS[1].getName());
360 		MagicUIComponents.logListing.setContact(StackManager.PLAYERS[0]
361 				.getNickName(), StackManager.PLAYERS[1].getNickName());
362 	}
363 
364 	/***
365 	 * return the player's name
366 	 * 
367 	 * @return the player's name
368 	 */
369 	@Override
370 	public String toString() {
371 		return getNickName();
372 	}
373 
374 	/***
375 	 * Return the player's name.
376 	 * 
377 	 * @return the player's name
378 	 */
379 	public abstract String getNickName();
380 
381 	/***
382 	 * Indicates if this player decline to response to the current effect.
383 	 * 
384 	 * @return true if this player decline to response to the current effect.
385 	 * @see MPhase#declineResponseMe()
386 	 * @since 0.30
387 	 * @since 0.31 an option "skip all even opponent's spell" is suported
388 	 * @since 0.80 "medium decline" is suported
389 	 */
390 	public boolean declineResponseMe() {
391 		if (MPhase.phases[StackManager.idCurrentPlayer][EventManager.phaseIndex]
392 				.declineResponseMe()) {
393 			return false;
394 		}
395 		for (int i = EventManager.phaseIndex + 1; i < EventManager.turnStructure.length; i++) {
396 			if (MPhase.phases[StackManager.idCurrentPlayer][i].declineResponseMe()) {
397 				return true;
398 			}
399 		}
400 		for (int i = 0; i < EventManager.turnStructure.length; i++) {
401 			if (MPhase.phases[1 - StackManager.idCurrentPlayer][i]
402 					.declineResponseMe()) {
403 				return true;
404 			}
405 		}
406 		return false;
407 	}
408 
409 	/***
410 	 * Indicates if this player decline to play any ability with an empty stack.
411 	 * 
412 	 * @return if this player decline to play any ability with an empty stack.
413 	 * @see MPhase#breakpoint()
414 	 * @since 0.30
415 	 * @since 0.31 an option "skip all even opponent's spell" is suported
416 	 * @since 0.80 "medium decline" is suported
417 	 */
418 	public boolean declinePlay() {
419 		if (MPhase.phases[StackManager.idCurrentPlayer][EventManager.phaseIndex]
420 				.breakpoint()) {
421 			return false;
422 		}
423 		for (int i = EventManager.phaseIndex + 1; i < EventManager.turnStructure.length; i++) {
424 			if (MPhase.phases[StackManager.idCurrentPlayer][i].declineResponseMe()) {
425 				return true;
426 			}
427 		}
428 		for (int i = 0; i < EventManager.turnStructure.length; i++) {
429 			if (MPhase.phases[1 - StackManager.idCurrentPlayer][i]
430 					.declineResponseMe()) {
431 				return true;
432 			}
433 		}
434 		return false;
435 	}
436 
437 	/***
438 	 * Indicates if this player decline to response to the current effect owned by
439 	 * opponent.
440 	 * 
441 	 * @return true if this player decline to response to the current effect owned
442 	 *         by opponent.
443 	 * @see MPhase#declineResponseOpponent()
444 	 * @since 0.30
445 	 * @since 0.31 an option "skip all even opponent's spell" is suported
446 	 * @since 0.80 "medium decline" is suported
447 	 */
448 	public boolean declineResponseOpponent() {
449 		if (MPhase.phases[StackManager.idCurrentPlayer][EventManager.phaseIndex]
450 				.declineResponseOpponent()) {
451 			return false;
452 		}
453 		for (int i = EventManager.phaseIndex + 1; i < EventManager.turnStructure.length; i++) {
454 			if (MPhase.phases[StackManager.idCurrentPlayer][i]
455 					.declineResponseOpponent()) {
456 				return true;
457 			}
458 		}
459 		for (int i = 0; i < EventManager.turnStructure.length; i++) {
460 			if (MPhase.phases[1 - StackManager.idCurrentPlayer][i]
461 					.declineResponseOpponent()) {
462 				return true;
463 			}
464 		}
465 		return false;
466 	}
467 
468 	/***
469 	 * Set this player as active one
470 	 */
471 	public void setActivePlayer() {
472 		StackManager.idActivePlayer = idPlayer;
473 		setHandedPlayer();
474 		EventManager.updatePhasesGUI();
475 	}
476 
477 	/***
478 	 * Set this player as handed one
479 	 */
480 	public void setHandedPlayer() {
481 		MagicUIComponents.targetTimer.resetCounter();
482 		StackManager.idHandedPlayer = idPlayer;
483 		infoPanel.setBackground(Color.ORANGE);
484 	}
485 
486 	/***
487 	 * Remove to all players the possibility to do something
488 	 */
489 	public static void unsetHandedPlayer() {
490 		StackManager.oldIdHandedPlayer = StackManager.idHandedPlayer;
491 		StackManager.idHandedPlayer = -1;
492 		for (Player player : StackManager.PLAYERS) {
493 			player.infoPanel.setBackground(null);
494 		}
495 		MagicUIComponents.skipButton.setEnabled(false);
496 		MagicUIComponents.skipMenu.setEnabled(false);
497 		MagicUIComponents.waitingLabel.setText(HANDED_NOBODY);
498 		MagicUIComponents.waitingLabel.setForeground(Color.ORANGE);
499 	}
500 
501 	/***
502 	 * Wait for the active player, then the non-active player to make choice of
503 	 * the order of triggered abilities to be put from the buffer to the stack
504 	 * 
505 	 * @param resolveOnEmpty
506 	 *          if true, the stack resolution would be broken if no triggered
507 	 *          abilities have been played instead of playing the
508 	 *          WaitActivatedChoice action.
509 	 * @return true if NO high priority triggered ability has been played from the
510 	 *         TBZ. So the stack can be resolved.
511 	 */
512 	public boolean waitTriggeredBufferChoice(boolean resolveOnEmpty) {
513 		StackManager.actionManager.currentAction = WaitTriggeredBufferChoice
514 				.getInstance();
515 
516 		// process now the posted refresh requests to simule a real-time update
517 		StackManager.processRefreshRequests();
518 		if (waitTriggeredBufferChoiceRec()) {
519 			// no player has stacked triggered any ability
520 			if (resolveOnEmpty) {
521 				StackManager.actionManager.waitingOnMiddle = false;
522 				WaitTriggeredBufferChoice.getInstance().finished();
523 			}
524 			return true;
525 		}
526 		return false;
527 	}
528 
529 	/***
530 	 * Return true if there was one or several processed hidden triggered
531 	 * abilities
532 	 * 
533 	 * @return true if there was one or several processed hidden triggered
534 	 *         abilities
535 	 */
536 	public boolean processHiddenTriggered() {
537 		return zoneManager.triggeredBuffer.resolveHiddenHighLevel(idPlayer)
538 				|| getOpponent().zoneManager.triggeredBuffer
539 						.resolveHiddenHighLevel(1 - idPlayer)
540 				|| zoneManager.triggeredBuffer.resolveHiddenNormalLevel(idPlayer)
541 				|| getOpponent().zoneManager.triggeredBuffer
542 						.resolveHiddenNormalLevel(1 - idPlayer)
543 				|| zoneManager.triggeredBuffer.resolveHiddenLowestLevel(idPlayer)
544 				|| getOpponent().zoneManager.triggeredBuffer
545 						.resolveHiddenLowestLevel(1 - idPlayer);
546 	}
547 
548 	/***
549 	 * List, purge, recheck, add found triggered abilities into the stack. If only
550 	 * one ability has been found, it is played automatically. If several
551 	 * abilities are found for YOU, and if the 'auto-stack' option is enabled, the
552 	 * abilities are all added sequentially added to the stack.
553 	 * 
554 	 * @return true if NO triggered ability has been found in the TBZ.
555 	 */
556 	private boolean waitTriggeredBufferChoiceRec() {
557 		if (processHiddenTriggered()) {
558 			// The triggered buffer zone has just handled this wait
559 			return false;
560 		}
561 
562 		/*
563 		 * The triggered buffer zone contains no more abstract abilities. The
564 		 * remaining abilities are normal ones and must be choosen by players to
565 		 * determine the order they go to the stack.
566 		 */
567 		if (zoneManager.triggeredBuffer.getCardCount() > 0) {
568 			// Try to get automatically the ability to play
569 			TriggeredCard triggered = zoneManager.triggeredBuffer.chooseAbility();
570 			if (triggered != null) {
571 				/*
572 				 * only one ability has been found
573 				 */
574 				StackManager.idActivePlayer = idPlayer;
575 				StackManager.actionManager.succeedClickOn(triggered);
576 			} else if (zoneManager.triggeredBuffer.getCardCount() > 1
577 					&& MCommonVars.autoStack && isYou()) {
578 				// Several abilities have been found, but 'auto-stack' option is on.
579 				StackManager.idActivePlayer = idPlayer;
580 				System.out.println("\t>>>> outAuto :" + counter++);
581 				TriggeredCard triggered0 = (TriggeredCard) StackManager.PLAYERS[0].zoneManager.triggeredBuffer
582 						.getComponent(0);
583 				Synchronizer.setAsHanded();
584 				Log.debug("clickedOn triggeredcard " + triggered0
585 						+ "- AUTO STACK OPTION");
586 				triggered0.sendClickToOpponent();
587 				StackManager.actionManager.succeedClickOn(triggered0);
588 			} else if (zoneManager.triggeredBuffer.getCardCount() == 0) {
589 				/*
590 				 * There is no more playable ability, they have been all been purged
591 				 * during the "recheck" process in the chooseAbility() method.
592 				 */
593 				return true;
594 			} else {
595 				/*
596 				 * player gets the hand to choose the order the triggered abilities go
597 				 * to the stack
598 				 */
599 				System.out.println("\t>>>> outPrompt :" + counter++ + "(idPlayer="
600 						+ idPlayer + ", triggered size="
601 						+ zoneManager.triggeredBuffer.getCardCount() + ")");
602 				setActivePlayer();
603 				zoneManager.triggeredBuffer.highlightStackable();
604 			}
605 			/*
606 			 * At least one triggered ability has been found. If we are in the
607 			 * End-of-Phase event, we break the stack resolution in order to
608 			 * re-dispatch this event later.
609 			 */
610 			return false;
611 		}
612 
613 		// no triggered ability has been found
614 		if (idPlayer == StackManager.idActivePlayer) {
615 			// now, non-active player stacks the waiting triggered abilities
616 			return getOpponent().waitTriggeredBufferChoiceRec();
617 		}
618 		return true;
619 	}
620 
621 	static int counter = 0;
622 
623 	@Override
624 	public void mouseClicked(MouseEvent e) {
625 		// only if left button is pressed
626 		StackManager.noReplayToken.take();
627 		try {
628 			if (ConnectionManager.isConnected()
629 					&& e.getButton() == MouseEvent.BUTTON1
630 					&& StackManager.idHandedPlayer == 0
631 					&& StackManager.actionManager.clickOn(this)) {
632 				Log.debug("clickOn(Mouse):player");
633 				sendClickToOpponent();
634 				StackManager.actionManager.succeedClickOn(this);
635 			}
636 		} catch (Throwable t) {
637 			t.printStackTrace();
638 		} finally {
639 			StackManager.noReplayToken.release();
640 		}
641 	}
642 
643 	/***
644 	 * This method is invoked when opponent has clicked on this object. this call
645 	 * should be done from the net.sf.magicproject.network listener
646 	 * 
647 	 * @param input
648 	 *          input stream of our net.sf.magicproject.network connection
649 	 * @throws IOException
650 	 *           if error occurred when reading the message
651 	 */
652 	public static void clickOn(InputStream input) throws IOException {
653 		// waiting for player information
654 		Log.debug("clickOn(VirtualInput):player");
655 		StackManager.actionManager
656 				.succeedClickOn(StackManager.PLAYERS[input.read()]);
657 	}
658 
659 	@Override
660 	public void sendClickToOpponent() {
661 		// send this information to our opponent
662 		ConnectionManager.sendToOpponent(IdMessages.MSG_CLICK_PLAYER, 1 - idPlayer);
663 	}
664 
665 	@Override
666 	public int getValue(int index) {
667 		if (index == IdTokens.MANA_POOL) {
668 			if (StackManager.actionManager.idHandler == ActionManager.HANDLER_INITIALIZATION
669 					&& StackManager.getSpellController() == this) {
670 				return mana.allManas()
671 						- StackManager.getTotalManaCost(StackManager.tokenCard);
672 			}
673 			return mana.allManas();
674 		}
675 		if (index == IdTokens.ID) {
676 			return getId();
677 		}
678 		if (index < IdTokens.FIRST_FREE_CARD_INDEX) {
679 			return registers[index];
680 		}
681 		// TODO support modifiers for player here
682 		return registers[index];
683 	}
684 
685 	@Override
686 	public int getTimestamp() {
687 		return 0;
688 	}
689 
690 	@Override
691 	public void mouseEntered(MouseEvent e) {
692 		if (StackManager.currentAbility != null
693 				&& StackManager.actionManager.currentAction != null
694 				&& StackManager.actionManager.currentAction instanceof ChoosenTarget) {
695 			// append the "this is [not] a valid target" text
696 			if (((ChoosenTarget) StackManager.actionManager.currentAction)
697 					.isValidTarget(this)) {
698 				setToolTipText("<html>" + getNickName()
699 						+ TargetableFactory.tooltipValidTarget);
700 			} else {
701 				setToolTipText("<html>" + getNickName()
702 						+ TargetableFactory.tooltipInvalidTarget);
703 			}
704 		} else {
705 			setToolTipText(getNickName());
706 		}
707 	}
708 
709 	@Override
710 	public void removeModifier(RegisterModifier modifier, int index) {
711 		throw new InternalError("not yet implemented");
712 	}
713 
714 	@Override
715 	public void removeModifier(RegisterIndirection indirection, int index) {
716 		throw new InternalError("not yet implemented");
717 	}
718 
719 	@Override
720 	public Targetable getLastKnownTargetable(int timeStamp) {
721 		return this;
722 	}
723 
724 	/***
725 	 * Increment the reference counter for the current timestamp of this card.
726 	 */
727 	@Override
728 	public void addTimestampReference() {
729 		// Nothing to do
730 	}
731 
732 	@Override
733 	public void decrementTimestampReference(int timestamp) {
734 		// Nothing to do
735 	}
736 
737 	/***
738 	 * Set the icon (32x32) of this player
739 	 * 
740 	 * @param name
741 	 *          player's nickname.
742 	 * @param imageIcon
743 	 *          the small avatar.
744 	 */
745 	protected void init(String name, Image imageIcon) {
746 		this.imageIcon = imageIcon;
747 		repaint();
748 	}
749 
750 	@Override
751 	public void paint(Graphics g) {
752 		// draw the highlighted rectangle
753 		super.paint(g);
754 		final Graphics2D g2D = (Graphics2D) g;
755 		if (!isYou() && Configuration.getBoolean("reverseSide", false)) {
756 			g2D.translate(getWidth() - 1, getHeight() - 1);
757 			g2D.rotate(Math.PI);
758 		}
759 		g2D.drawImage(imageIcon, 3, 4, null);
760 		if (isHighLighted) {
761 			g.setColor(highLightColor);
762 			g.draw3DRect(0, 0, getWidth() - 1, getHeight() - 1, true);
763 			g.draw3DRect(1, 1, getWidth() - 3, getHeight() - 3, true);
764 		}
765 
766 		// Draw the target Id if helper said it
767 		final String id = TargetHelper.getInstance().getMyId(this);
768 		if (id != null) {
769 			if (id == TargetHelper.STR_CONTEXT1) {
770 				// TODO I am in the context 1, draw a picture
771 				g2D.setColor(Color.BLUE);
772 				g2D
773 						.setFont(g2D.getFont()
774 								.deriveFont(Font.BOLD, PLAYER_SIZE_HEIGHT - 4));
775 				g2D.drawString(String.valueOf(id), 25, PLAYER_SIZE_HEIGHT - 2);
776 			} else if (id == TargetHelper.STR_CONTEXT2) {
777 				// TODO I am in the context 2, draw a picture
778 				g2D.setColor(Color.BLUE);
779 				g2D
780 						.setFont(g2D.getFont()
781 								.deriveFont(Font.BOLD, PLAYER_SIZE_HEIGHT - 4));
782 				g2D.drawString(String.valueOf(id), 25, PLAYER_SIZE_HEIGHT - 2);
783 			} else if (id != TargetHelper.STR_SOURCE) {
784 				// } else if (id == TargetHelper.STR_SOURCE) {
785 				// TODO I am the source, draw a picture
786 				// } else {
787 				// I am a target
788 				g2D.drawImage(TargetHelper.getInstance().getTargetPictureSml(), 30, 5,
789 						null);
790 			}
791 		}
792 
793 		g2D.dispose();
794 	}
795 
796 	@Override
797 	public int getId() {
798 		return idPlayer;
799 	}
800 
801 	/***
802 	 * where all triggered abilities would go before to go to the stack
803 	 */
804 	public ZoneManager zoneManager;
805 
806 	/***
807 	 * id of the player
808 	 */
809 	public int idPlayer;
810 
811 	/***
812 	 * is the manas of this player
813 	 */
814 	public ManaPool mana;
815 
816 	/***
817 	 * This card is used to represent this player as a card/
818 	 */
819 	public PlayerCard playerCard;
820 
821 	/***
822 	 * The button containig the avatar picture
823 	 */
824 	protected AvatarButton avatarButton;
825 
826 	/***
827 	 * Available string settings of any player.
828 	 */
829 	protected static final String[] SETTINGS = { "email", "yahoo", "msn", "icq" };
830 
831 	/***
832 	 * Panel containing some information about player
833 	 */
834 	protected JPanel morePanel;
835 
836 	/***
837 	 * The label representing player's lives.
838 	 */
839 	protected JLabel lifeLabel;
840 
841 	/***
842 	 * The label representing player's poison.
843 	 */
844 	protected JLabel poisonLabel;
845 
846 	/***
847 	 * The panel containing the hand and the play
848 	 */
849 	public JPanel mainPanel;
850 
851 	/***
852 	 * The main panel containing player's lives+poison+mana+buttons.
853 	 */
854 	protected JPanel infoPanel;
855 
856 	/***
857 	 * The panel representing player's phases.
858 	 */
859 	protected JPanel phases;
860 
861 	/***
862 	 * Abilities panel (not yet implemented)
863 	 */
864 	protected JPanel abilitiesPanel;
865 
866 	/***
867 	 * The splitter of game/hand zones.
868 	 */
869 	public JSplitPane handSplitter;
870 
871 	/***
872 	 * The panel containing all icons representing contact info.
873 	 */
874 	protected JPanel togglePanel;
875 
876 	/***
877 	 * Nickname panel : button and label.
878 	 */
879 	protected JPanel nickNamePanel;
880 
881 	/***
882 	 * real name panel : button and label.
883 	 */
884 	protected JPanel realNamePanel;
885 
886 	private Image imageIcon;
887 
888 	/***
889 	 * The deck of this player.
890 	 */
891 	private Deck deck;
892 
893 	private static final String HANDED_NOBODY = LanguageManager
894 			.getString("handed-nobody");
895 
896 	/***
897 	 * Set the deck of this player.
898 	 * 
899 	 * @param deck
900 	 *          the deck of this player.
901 	 */
902 	public void setDeck(Deck deck) {
903 		this.deck = deck;
904 	}
905 
906 	/***
907 	 * Return the current deck of player.
908 	 * 
909 	 * @return the current deck of player.
910 	 */
911 	public Deck getDeck() {
912 		return deck;
913 	}
914 }