View Javadoc

1   /*
2    * Mana.java
3    * Created on 27 octobre 2002, 16:11
4    * 
5    *   Magic-Project is a turn based strategy simulator
6    *   Copyright (C) 2003-2007 Fabrice Daugan
7    *
8    *   This program is free software; you can redistribute it and/or modify it 
9    * under the terms of the GNU General Public License as published by the Free 
10   * Software Foundation; either version 2 of the License, or (at your option) any
11   * later version.
12   *
13   *   This program is distributed in the hope that it will be useful, but WITHOUT 
14   * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS
15   * FOR A PARTICULAR PURPOSE.  See the GNU General Public License for more 
16   * details.
17   *
18   *   You should have received a copy of the GNU General Public License along  
19   * with this program; if not, write to the Free Software Foundation, Inc., 
20   * 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
21   */
22  package net.sf.magicproject.clickable.mana;
23  
24  import java.awt.Color;
25  import java.awt.Dimension;
26  import java.awt.Graphics;
27  import java.awt.Graphics2D;
28  import java.awt.Image;
29  import java.awt.event.MouseEvent;
30  import java.awt.geom.Rectangle2D;
31  import java.awt.image.PixelGrabber;
32  import java.io.InputStream;
33  import java.net.URL;
34  import java.util.ArrayList;
35  import java.util.List;
36  
37  import javax.swing.JOptionPane;
38  
39  import net.sf.magicproject.action.PayMana;
40  import net.sf.magicproject.action.context.ManaCost;
41  import net.sf.magicproject.clickable.Clickable;
42  import net.sf.magicproject.clickable.ability.Ability;
43  import net.sf.magicproject.clickable.targetable.player.Player;
44  import net.sf.magicproject.deckbuilder.MdbLoader;
45  import net.sf.magicproject.network.ConnectionManager;
46  import net.sf.magicproject.network.IdMessages;
47  import net.sf.magicproject.stack.StackManager;
48  import net.sf.magicproject.test.Test;
49  import net.sf.magicproject.test.True;
50  import net.sf.magicproject.token.IdCommonToken;
51  import net.sf.magicproject.tools.Configuration;
52  import net.sf.magicproject.tools.Log;
53  import net.sf.magicproject.tools.MToolKit;
54  import net.sf.magicproject.tools.PairIntObject;
55  import net.sf.magicproject.tools.Picture;
56  import net.sf.magicproject.ui.MagicUIComponents;
57  import net.sf.magicproject.ui.i18n.LanguageManager;
58  
59  /***
60   * Representes the mana pool of one color of one player : BLACK,BLUE,GREEN,RED,
61   * WHITE,COLORLESS. A graphic, in function of mana's color is displayed with a
62   * label representing the amount of manas for this colored(less) mana. if we
63   * consider the opponent case, we have a reversed a rotation of PI to do.
64   * 
65   * @author <a href="mailto:fabdouglas@users.sourceforge.net">Fabrice Daugan </a>
66   * @see net.sf.magicproject.clickable.mana.ManaPool
67   * @see net.sf.magicproject.token.IdCommonToken#COLORLESS_MANA
68   * @see net.sf.magicproject.token.IdCommonToken#BLACK_MANA
69   * @see net.sf.magicproject.token.IdCommonToken#BLUE_MANA
70   * @see net.sf.magicproject.token.IdCommonToken#GREEN_MANA
71   * @see net.sf.magicproject.token.IdCommonToken#RED_MANA
72   * @see net.sf.magicproject.token.IdCommonToken#WHITE_MANA
73   * @since 0.3 support reversed mana picture for opponent
74   * @since 0.53 "enableReverse" option
75   * @since 0.60 extends MClickable class
76   * @since 0.83 new mana representation of mana pool
77   */
78  public class Mana extends Clickable {
79  
80  	// the dimensions of graphics
81  	private static final int MANA_WIDTH = Player.PLAYER_SIZE_HEIGHT;
82  
83  	private static final int MANA_HEIGHT = MANA_WIDTH;
84  
85  	/***
86  	 * Creates a new instance of MMana
87  	 * 
88  	 * @param idColor
89  	 *          if the color of this mana
90  	 * @param reverseImage
91  	 *          if true the mana picture will be flipped horizontally and
92  	 *          vertically.
93  	 */
94  	Mana(int idColor, boolean reverseImage) {
95  		super();
96  		this.color = idColor;
97  		this.reverseImage = reverseImage;
98  		setPreferredSize(new Dimension(MANA_WIDTH, MANA_HEIGHT));
99  		setToolTipText(LanguageManager.getString("Mana" + idColor));
100 		addMouseListener(this);
101 	}
102 
103 	/***
104 	 * Upadte the mana pictures following the specified emulated TBS
105 	 * 
106 	 * @param mdb
107 	 */
108 	public static void init(String mdb) {
109 		// load the mana picture
110 		int error = 0;
111 		for (int i = IdCommonToken.COLOR_NAMES.length; i-- > 0;) {
112 			try {
113 				if (error == 0 && i != 0) {
114 					IMAGES[i] = Picture.loadImage(
115 							MToolKit.getTbsPicture("mana/colored/big/"
116 									+ MdbLoader.coloredBigManas[i], false),
117 							new URL(MdbLoader.coloredManaBigURL
118 									+ MdbLoader.coloredBigManas[i])).getContent();
119 					if (IMAGES[i] == null) {
120 						error += (i + 1) * 2;
121 					} else {
122 						// check the small colored mana
123 						Picture.download(MToolKit.getTbsPicture("mana/colored/small/"
124 								+ MdbLoader.coloredSmlManas[i], false), new URL(
125 								MdbLoader.coloredManaSmlURL + MdbLoader.coloredSmlManas[i]));
126 					}
127 				} else if (error == 0) {
128 					IMAGES[0] = Picture.loadImage(
129 							MToolKit.getTbsPicture("mana/colorless/big/"
130 									+ MdbLoader.colorlessBigURL, false),
131 							new URL(MdbLoader.colorlessURL + MdbLoader.colorlessBigURL))
132 							.getContent();
133 					if (IMAGES[0] == null) {
134 						error += (i + 1) * 2 + MdbLoader.colorlessSmlManas.length;
135 					} else {
136 						// check the small colorless mana
137 						for (int j = MdbLoader.colorlessSmlManas.length; j-- > 0;) {
138 							Picture.download(MToolKit.getTbsPicture("mana/colorless/small/"
139 									+ MdbLoader.colorlessSmlManas[j], false), new URL(
140 									MdbLoader.colorlessURL + MdbLoader.colorlessSmlManas[j]));
141 						}
142 						Picture.download(MToolKit.getTbsPicture("mana/colorless/small/"
143 								+ MdbLoader.unknownSmlMana, false), new URL(
144 								MdbLoader.colorlessURL + MdbLoader.unknownSmlMana));
145 					}
146 				}
147 			} catch (Exception e1) {
148 				error += (i + 1) * 2;
149 				Log.error("ERROR : could not load mana picture " + i, e1);
150 			}
151 
152 			// read a pixel to determine foreground colors
153 			if (IMAGES[i] != null) {
154 				final int[] pixels = new int[1];
155 				try {
156 					new PixelGrabber(IMAGES[i], 18, 1, 1, 1, pixels, 0, 1).grabPixels();
157 					TEXT_COLOR[i] = new Color(pixels[0]).darker();
158 				} catch (InterruptedException e) {
159 					TEXT_COLOR[i] = Color.black;
160 				}
161 			} else {
162 				TEXT_COLOR[i] = Color.black;
163 			}
164 		}
165 
166 		if (error > 0) {
167 			// One or several error have been found, display a warning message
168 			JOptionPane
169 					.showMessageDialog(
170 							MagicUIComponents.magicForm,
171 							"One or several mana pictures ("
172 									+ error
173 									+ ") have not been found\nCheck your internet connection and/or you proxy configuration\nHave a look in console output for more details.",
174 							"Load error", JOptionPane.WARNING_MESSAGE);
175 		}
176 	}
177 
178 	/***
179 	 * return the amount of mana of this color
180 	 * 
181 	 * @return the amount of mana of this color
182 	 */
183 	public int getMana() {
184 		return getMana(null);
185 	}
186 
187 	/***
188 	 * return the amount of mana of this color
189 	 * 
190 	 * @param abilityRequest
191 	 *          the ability containing action requesting this mana
192 	 * @return the amount of mana of this color
193 	 */
194 	public int getMana(Ability abilityRequest) {
195 		if (abilityRequest != null) {
196 			// mana get with restriction
197 			int result = getPlayer().registers[color];
198 			for (PairIntObject<Test> pair : restrictionList) {
199 				// restriction exists already, add this mana
200 				if (!pair.value.test(abilityRequest, abilityRequest.getCard())) {
201 					// restriction doen't allow this ability to be played
202 					result -= pair.key;
203 				}
204 			}
205 			return result;
206 		}
207 		return getPlayer().registers[color];
208 	}
209 
210 	/***
211 	 * empty the mana pool of this mana
212 	 * 
213 	 * @return the old pool of this mana
214 	 */
215 	public int setToZero() {
216 		final int oldValue = getPlayer().registers[color];
217 		setTo(0);
218 		return oldValue;
219 	}
220 
221 	/***
222 	 * set the pool of this mana
223 	 * 
224 	 * @param idNumber
225 	 *          is the new pool of this mana
226 	 * @return the new pool of this mana ( is idNumber)
227 	 */
228 	private int setTo(int idNumber) {
229 		getPlayer().registers[color] = idNumber;
230 		return idNumber;
231 	}
232 
233 	/***
234 	 * Add a number of mana of this color
235 	 * 
236 	 * @param idNumber
237 	 *          is the number of mana to add to the mana pool
238 	 * @param restriction
239 	 *          the test defining mana usage
240 	 * @return the new pool of this mana
241 	 */
242 	public int addMana(int idNumber, Test restriction) {
243 		if (idNumber > 0) {
244 			setTo(getPlayer().registers[color] + idNumber);
245 			if (restriction != null && restriction != True.getInstance()) {
246 				// mana added with restriction
247 				for (PairIntObject<Test> pair : restrictionList) {
248 					if (pair.value == restriction) {
249 						// restriction exists already, add this mana
250 						pair.key += idNumber;
251 						return getPlayer().registers[color];
252 					}
253 				}
254 				restrictionList.add(new PairIntObject<Test>(idNumber, restriction));
255 			}
256 		}
257 		return getPlayer().registers[color];
258 	}
259 
260 	/***
261 	 * Remove a number of mana of this color from the mana pool.
262 	 * 
263 	 * @param nb
264 	 *          is the number of mana to remove from the mana pool
265 	 * @param abilityRequest
266 	 *          the ability containing action requesting this mana
267 	 * @return amount of mana effectively removed from the mana pool.
268 	 */
269 	public int removeMana(int nb, Ability abilityRequest) {
270 		return removeMana(nb, abilityRequest, null);
271 	}
272 
273 	/***
274 	 * Remove a number of mana of this color from the mana pool.
275 	 * 
276 	 * @param nb
277 	 *          is the number of mana to remove from the mana pool
278 	 * @param abilityRequest
279 	 *          the ability containing action requesting this mana
280 	 * @param manaContext
281 	 *          is the action context containing information on mana cost of the
282 	 *          requesting ability;
283 	 * @return amount of mana effectively removed from the mana pool.
284 	 */
285 	public int removeMana(int nb, Ability abilityRequest, ManaCost manaContext) {
286 		if (abilityRequest != null) {
287 			// mana removed with restriction
288 			int toRemove = nb;
289 			int i = restrictionList.size();
290 			while (i-- > 0 && toRemove > 0) {
291 				final PairIntObject<Test> restriction = restrictionList.get(i);
292 				if (!(restriction.value).test(abilityRequest, abilityRequest.getCard())) {
293 					// ability is ok, remove this mana with this restriction
294 					if (restriction.key - toRemove <= 0) {
295 						// no more mana with this restriction and no more mana to remove
296 						restrictionList.remove(i);
297 						if (restriction.key - toRemove == 0) {
298 							// no more mana to remove
299 							if (manaContext != null) {
300 								manaContext.addRestriction(color, restriction.value, toRemove);
301 							}
302 							break;
303 						}
304 						// else, it remains mana to remove but without this restriction
305 						if (manaContext != null) {
306 							manaContext.addRestriction(color, restriction.value,
307 									restriction.key);
308 						}
309 						toRemove -= restriction.key;
310 					} else {
311 						// no more mana to remove
312 						if (manaContext != null) {
313 							manaContext.addRestriction(color, restriction.value, toRemove);
314 						}
315 						restriction.key -= toRemove;
316 						break;
317 					}
318 				}
319 			}
320 		}
321 		if (nb <= getPlayer().registers[color]) {
322 			setTo(getPlayer().registers[color] - nb);
323 			return nb;
324 		}
325 		// no negative mana allowed --> 0
326 		return setToZero();
327 	}
328 
329 	/***
330 	 * @see java.awt.Component#paint(java.awt.Graphics)
331 	 * @since 0.3 faster reverse draw without use of rotation
332 	 */
333 	@Override
334 	public void paint(Graphics g) {
335 		if (PayMana.useMana && IMAGES[color] != null) {
336 			super.paint(g);
337 			final Graphics2D g2d = (Graphics2D) g;
338 			if (reverseImage && Configuration.getBoolean("reverseSide", false)) {
339 				g2d.rotate(Math.PI, MANA_WIDTH / 2, MANA_HEIGHT / 2);
340 			}
341 			g2d.drawImage(IMAGES[color], 0, 0, MANA_WIDTH, MANA_WIDTH, null);
342 			g2d.setColor(TEXT_COLOR[color]);
343 
344 			if (StackManager.PLAYERS[0] != null
345 					&& StackManager.PLAYERS[0].registers != null) {
346 				final Rectangle2D stringDim = g2d.getFontMetrics().getStringBounds(
347 						String.valueOf(getPlayer().registers[color]), g);
348 				g2d.fill3DRect(MANA_WIDTH - (int) stringDim.getWidth() - 4,
349 						MANA_HEIGHT - 11, (int) stringDim.getWidth() + 3, 10, true);
350 				g2d.setColor(Color.black);
351 				g2d.drawString(String.valueOf(getPlayer().registers[color]), MANA_WIDTH
352 						- (int) stringDim.getWidth() - 2, MANA_HEIGHT - 2);
353 
354 			}
355 		}
356 		/*
357 		 * draw the highlighted rectangle arround the card if not returned, or if we
358 		 * are in target mode.
359 		 */
360 		if (isHighLighted) {
361 			g.setColor(highLightColor);
362 			g.draw3DRect(0, 0, MANA_WIDTH - 1, MANA_HEIGHT - 1, true);
363 			g.draw3DRect(1, 1, MANA_WIDTH - 3, MANA_HEIGHT - 3, true);
364 		}
365 
366 	}
367 
368 	@Override
369 	public void mouseClicked(MouseEvent e) {
370 		// only if left button is pressed
371 		StackManager.noReplayToken.take();
372 		try {
373 			if (ConnectionManager.isConnected()
374 					&& e.getButton() == MouseEvent.BUTTON1
375 					&& StackManager.idHandedPlayer == 0
376 					&& StackManager.actionManager.clickOn(this)) {
377 				StackManager.actionManager.succeedClickOn(this);
378 			}
379 		} catch (Throwable t) {
380 			t.printStackTrace();
381 		} finally {
382 			StackManager.noReplayToken.release();
383 		}
384 	}
385 
386 	/***
387 	 * This method is invoked when opponent has clicked on this object. this call
388 	 * should be done from the net.sf.magicproject.network listener
389 	 * 
390 	 * @param input
391 	 *          input stream of our net.sf.magicproject.network connection
392 	 */
393 	public void clickOn(InputStream input) {
394 		// waiting for player information
395 		StackManager.actionManager.succeedClickOn(this);
396 	}
397 
398 	@Override
399 	public void sendClickToOpponent() {
400 		// send this information to our opponent
401 		ConnectionManager.sendToOpponent(IdMessages.MSG_CLICK_MANA,
402 				reverseImage ? 0 : 1, color);
403 	}
404 
405 	@Override
406 	public String toString() {
407 		return IdCommonToken.COLOR_NAMES[color];
408 	}
409 
410 	/***
411 	 * return the player owning this mana
412 	 * 
413 	 * @return the player owning this mana
414 	 */
415 	public Player getPlayer() {
416 		return reverseImage ? StackManager.PLAYERS[1] : StackManager.PLAYERS[0];
417 	}
418 
419 	/***
420 	 * represent all images used to represents energy source for each sort
421 	 */
422 	private static final Image[] IMAGES = new Image[6];
423 
424 	/***
425 	 * represent all foreground colors used to represent the amount of energy for
426 	 * each type
427 	 */
428 	private static final Color[] TEXT_COLOR = new Color[6];
429 
430 	/***
431 	 * color of this mana
432 	 */
433 	public int color;
434 
435 	/***
436 	 * List of pair [mana, restriction]
437 	 */
438 	private List<PairIntObject<Test>> restrictionList = new ArrayList<PairIntObject<Test>>();
439 
440 	/***
441 	 * Indicates if graphics are reversed (PI rotation)
442 	 */
443 	private boolean reverseImage;
444 
445 }