1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23 package net.sf.magicproject.clickable.targetable.card;
24
25 import java.awt.Color;
26 import java.awt.Dimension;
27 import java.awt.Graphics;
28 import java.awt.Graphics2D;
29 import java.awt.Image;
30 import java.awt.RenderingHints;
31 import java.awt.event.MouseEvent;
32 import java.awt.event.MouseListener;
33 import java.io.IOException;
34 import java.io.InputStream;
35 import java.net.MalformedURLException;
36 import java.util.List;
37
38 import net.sf.magicproject.clickable.ability.Ability;
39 import net.sf.magicproject.clickable.ability.TriggeredAbility;
40 import net.sf.magicproject.clickable.targetable.Targetable;
41 import net.sf.magicproject.clickable.targetable.player.Player;
42 import net.sf.magicproject.event.context.ContextEventListener;
43 import net.sf.magicproject.modifier.RegisterIndirection;
44 import net.sf.magicproject.modifier.RegisterModifier;
45 import net.sf.magicproject.network.ConnectionManager;
46 import net.sf.magicproject.network.IdMessages;
47 import net.sf.magicproject.stack.ActionManager;
48 import net.sf.magicproject.stack.StackContext;
49 import net.sf.magicproject.stack.StackManager;
50 import net.sf.magicproject.stack.TargetHelper;
51 import net.sf.magicproject.stack.TargetedList;
52 import net.sf.magicproject.test.Test;
53 import net.sf.magicproject.token.IdZones;
54 import net.sf.magicproject.token.Visibility;
55 import net.sf.magicproject.tools.Log;
56 import net.sf.magicproject.tools.MToolKit;
57 import net.sf.magicproject.tools.Picture;
58 import net.sf.magicproject.ui.i18n.LanguageManager;
59 import net.sf.magicproject.ui.wizard.Replacement;
60 import net.sf.magicproject.zone.TriggeredBuffer;
61 import net.sf.magicproject.zone.ZoneManager;
62
63 /***
64 * @author <a href="mailto:fabdouglas@users.sourceforge.net">Fabrice Daugan </a>
65 * @since 0.54
66 * @since 0.86 Ability source is saved.
67 */
68 public class TriggeredCard extends AbstractCard implements MouseListener,
69 StackContext {
70
71 /***
72 * @param triggeredAbility
73 * the triggered ability associated to this card
74 * @param context
75 * the context of the associated triggered ability
76 * @param abilityID
77 * is the ability's Id making this triggered ability to be created.
78 */
79 public TriggeredCard(Ability triggeredAbility, ContextEventListener context,
80 long abilityID) {
81 super(triggeredAbility.getCard().database);
82 this.triggeredAbility = triggeredAbility;
83 this.context = context;
84 this.abilityID = abilityID;
85 this.visibility = Visibility.PUBLIC;
86 setSize(new Dimension(CardFactory.cardWidth, CardFactory.cardHeight));
87 setPreferredSize(getSize());
88 addMouseListener(this);
89 controller = triggeredAbility.getCard().getController();
90 }
91
92 /***
93 * Return the target option of the current spell. this target option is owned
94 * by the current spell. May be reseted, changed by the spell itself.
95 *
96 * @return the targeted list of this context.
97 */
98 public TargetedList getTargetedList() {
99 return null;
100 }
101
102 /***
103 * Return the current context. Null if current ability is not a triggered one.
104 *
105 * @return the current context. Null if current ability is not a triggered
106 * one.
107 */
108 public ContextEventListener getAbilityContext() {
109 return context;
110 }
111
112 /***
113 * Return the action manager of this context.
114 *
115 * @return the action manager of this context.
116 */
117 public ActionManager getActionManager() {
118 return null;
119 }
120
121 /***
122 * Return the card source of the current capcity/spell in the stack
123 *
124 * @return the card source of the current capcity/spell in the stack
125 */
126 public MCard getSourceCard() {
127 return triggeredAbility.getCard();
128 }
129
130 /***
131 * Play this card as a spell.
132 *
133 * @return true if this card has been completky played and if the stack can be
134 * resolved after this call.
135 */
136 public boolean newSpell() {
137 return StackManager.newSpell(this);
138 }
139
140 @Override
141 public boolean isACopy() {
142 return false;
143 }
144
145 @Override
146 public int countAllCardsOf(Test test, Ability ability, boolean canBePreempted) {
147 if (triggeredAbility.getCard() == SystemCard.instance)
148 return 0;
149 if (canBePreempted)
150 return test.test(ability, this) ? 1 : 0;
151 return test.testPreemption(ability, this) ? 1 : 0;
152 }
153
154 @Override
155 public void checkAllCardsOf(Test test, List<Targetable> list, Ability ability) {
156 if (triggeredAbility.getCard() != SystemCard.instance
157 && test.test(ability, this)) {
158 list.add(this);
159 }
160 }
161
162 @Override
163 public final boolean isAbility() {
164 return true;
165 }
166
167 @Override
168 public final boolean isSpell() {
169 return false;
170 }
171
172 @Override
173 public void paint(Graphics g) {
174 Graphics2D g2D = (Graphics2D) g;
175 g2D.setRenderingHint(RenderingHints.KEY_INTERPOLATION,
176 RenderingHints.VALUE_INTERPOLATION_BICUBIC);
177
178 if (getParent() == ZoneManager.stack) {
179
180 g2D.setColor(Color.magenta);
181 } else {
182
183
184
185
186
187 if (isHighLighted) {
188 g2D.setColor(highLightColor);
189 } else {
190 g2D.setColor(Color.BLACK);
191 }
192 }
193
194 g2D
195 .fillRoundRect(0, 0, CardFactory.cardWidth, CardFactory.cardHeight, 3,
196 3);
197
198
199 g2D.drawImage(scaledImage(), null, null);
200 g2D.dispose();
201 }
202
203 @Override
204 public Image image() {
205 if (cachedImage == null) {
206 if (triggeredAbility.getPictureName() == null) {
207 cachedImage = super.image();
208 } else {
209 try {
210 cachedImage = Picture.loadImage(MToolKit
211 .getTbsPicture(triggeredAbility.getPictureName() + ".jpg"));
212 } catch (MalformedURLException e) {
213
214 }
215 }
216 }
217 return cachedImage;
218 }
219
220 /***
221 * The cached image. Is <code>null</code> while the associated image of this
222 * ability is not loaded.
223 */
224 private Image cachedImage;
225
226 @Override
227 public void mouseClicked(MouseEvent e) {
228 if (Replacement.isRunning) {
229 Log
230 .debug("Replacement : considere the mouse click as replacement choice.");
231 return;
232 }
233 StackManager.noReplayToken.take();
234 try {
235 if (triggeredAbility.isHidden()) {
236 throw new InternalError(
237 "hidden triggered abilities should not be visible in TBZ");
238 }
239 Log.debug("mouseClicked triggered ability");
240
241 if (ConnectionManager.isConnected()
242 && e.getButton() == MouseEvent.BUTTON1
243 && StackManager.idHandedPlayer == 0
244 && StackManager.actionManager.clickOn(this)) {
245
246 sendClickToOpponent();
247 StackManager.actionManager.succeedClickOn(this);
248 }
249 } catch (Throwable t) {
250 t.printStackTrace();
251 } finally {
252 StackManager.noReplayToken.release();
253 }
254 }
255
256 @Override
257 public void sendClickToOpponent() {
258
259 TriggeredBuffer cont = StackManager.PLAYERS[0].zoneManager.triggeredBuffer;
260
261 int index = -1;
262 for (index = cont.getCardCount(); index-- > 0;) {
263 if (cont.getTriggeredAbility(index) == this) {
264
265 ConnectionManager.sendToOpponent(IdMessages.MSG_CLICK_TRIGGERED_CARD,
266 index);
267 return;
268 }
269 }
270 throw new InternalError("Could not find the triggered card to send");
271 }
272
273 /***
274 * This method is invoked when opponent has clicked on this object. this call
275 * should be done from the net.sf.magicproject.network listener
276 *
277 * @param input
278 * input stream of our net.sf.magicproject.network connection
279 * @throws IOException
280 * if error occurred when reading the message
281 */
282 public static void clickOn(InputStream input) throws IOException {
283
284 Log.debug("clickedOn triggeredcard throw input");
285 TriggeredCard triggered = getTriggeredCard(input);
286 StackManager.actionManager.clickOn(triggered);
287 StackManager.actionManager.succeedClickOn(triggered);
288 }
289
290 /***
291 * Return the component from information read throw
292 * net.sf.magicproject.network
293 *
294 * @param input
295 * input stream of our net.sf.magicproject.network connection
296 * @return the triggered card read from the input stream
297 * @throws IOException
298 * if error occurred when reading the message
299 */
300 public static TriggeredCard getTriggeredCard(InputStream input)
301 throws IOException {
302
303 int index = input.read();
304 return StackManager.PLAYERS[1].zoneManager.triggeredBuffer
305 .getTriggeredAbility(index);
306 }
307
308 @Override
309 public void moveCard(int newIdPlace, Player newController,
310 boolean newIsTapped, int idPosition) {
311
312 if (newIdPlace != IdZones.STACK) {
313 throw new InternalError(
314 "A wrong destination place has been specified for a triggered:"
315 + newIdPlace);
316 }
317 StackManager.PLAYERS[newController.idPlayer].zoneManager.triggeredBuffer
318 .removeTriggered(this);
319 reverse(false);
320 setSize(new Dimension(CardFactory.cardWidth, CardFactory.cardHeight));
321 setPreferredSize(getSize());
322 ZoneManager.stack.add(this, 0);
323
324 }
325
326 @Override
327 public String getTooltipString() {
328 StringBuilder toolTip = new StringBuilder(300);
329
330
331 toolTip.append("<html><b>");
332 toolTip.append(LanguageManager.getString("cardname"));
333 toolTip.append(": </b>");
334 if (visibility == null || !visibility.isVisibleForYou()) {
335
336
337 toolTip.append("??");
338 } else {
339 toolTip.append(database.getLocalName());
340 toolTip.append(CardFactory.ttSource);
341 toolTip.append(triggeredAbility.getCard().toString());
342 toolTip.append("<br><b>");
343 toolTip.append(LanguageManager.getString("triggeredability"));
344 toolTip.append(": </b>");
345 toolTip.append(triggeredAbility.toHtmlString(context));
346
347
348 if (database.getRulesCredit() != null) {
349 toolTip.append(CardFactory.ttRulesAuthor);
350 toolTip.append(database.getRulesCredit());
351 }
352 }
353 toolTip.append("</html>");
354 return toolTip.toString();
355 }
356
357 @Override
358 public final void mouseEntered(MouseEvent e) {
359 CardFactory.previewCard.setImage(image(), database.getLocalName());
360 setToolTipText(getTooltipString());
361 if (getParent() == ZoneManager.stack) {
362
363 TargetHelper.getInstance().addTargetedBy(StackManager.getContextOf(this));
364 } else {
365
366 TargetHelper.getInstance().addTargetedBy(this);
367 }
368 }
369
370 @Override
371 public String toString() {
372 return ""
373 + triggeredAbility
374 + ", card="
375 + triggeredAbility.getCard()
376 + (triggeredAbility.getCard() != null
377 && triggeredAbility.getCard() != SystemCard.instance ? "@"
378 + Integer.toHexString(triggeredAbility.getCard().hashCode()) : "")
379 + (triggeredAbility.isHidden() ? ", hidden=true" : "");
380 }
381
382 @Override
383 public int getValue(int index) {
384 throw new InternalError("Triggered Card have no registers");
385 }
386
387 @Override
388 public void removeModifier(RegisterModifier modifier, int index) {
389 throw new InternalError("Should not be called");
390 }
391
392 @Override
393 public void removeModifier(RegisterIndirection indirection, int index) {
394 throw new InternalError("Should not be called");
395 }
396
397 /***
398 * The border will be highligthed to a color identifying it easily as a token
399 * component.
400 *
401 * @see #STACKABLE_COLOR
402 */
403 public void highlightStackable() {
404 highLight(STACKABLE_COLOR);
405 }
406
407 @Override
408 public Targetable getLastKnownTargetable(int timeStamp) {
409 throw new InternalError("Should not be called");
410 }
411
412 @Override
413 public Targetable getOriginalTargetable() {
414 throw new InternalError("Should not be called");
415 }
416
417 @Override
418 public void addTimestampReference() {
419 throw new InternalError("Should not be called");
420 }
421
422 @Override
423 public int getTimestamp() {
424 throw new InternalError("Should not be called");
425 }
426
427 @Override
428 public void decrementTimestampReference(int timestamp) {
429 throw new InternalError("Should not be called");
430 }
431
432 public void abortion(AbstractCard card, Ability source) {
433 throw new InternalError("Should not be called");
434 }
435
436 public Ability getAbortingAbility() {
437 throw new InternalError("Should not be called");
438 }
439
440 /***
441 * Return the delayed card attached to this ability.
442 *
443 * @return the delayed card attached to this ability.
444 */
445 public DelayedCard getDelayedCard() {
446 if (triggeredAbility instanceof TriggeredAbility)
447 return ((TriggeredAbility) triggeredAbility).getDelayedCard();
448 return null;
449 }
450
451 /***
452 * Triggered ability
453 */
454 public Ability triggeredAbility;
455
456 /***
457 * Is the ability making this triggered ability to be created.
458 */
459 public long abilityID;
460
461 /***
462 * Context of triggered ability
463 */
464 protected ContextEventListener context;
465
466 /***
467 * Height of original card to display
468 */
469 public static int cardHeight;
470
471 /***
472 * Width of original card to display
473 */
474 public static int cardWidth;
475
476 /***
477 * The color used to color the stackable component
478 */
479 public static final Color STACKABLE_COLOR = Color.RED;
480
481 }