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