View Javadoc

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