1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21 package net.sf.magicproject.clickable.targetable.card;
22
23 import static net.sf.magicproject.clickable.targetable.card.CardFactory.ACTIVATED_COLOR;
24 import static net.sf.magicproject.clickable.targetable.card.CardFactory.WARNING_PICTURE;
25
26 import java.awt.Component;
27 import java.awt.LayoutManager;
28 import java.awt.event.MouseEvent;
29 import java.awt.event.MouseWheelEvent;
30 import java.awt.event.MouseWheelListener;
31 import java.io.IOException;
32 import java.io.InputStream;
33 import java.util.ArrayList;
34 import java.util.Arrays;
35 import java.util.Collection;
36 import java.util.HashMap;
37 import java.util.HashSet;
38 import java.util.List;
39 import java.util.Map;
40
41 import javax.swing.JMenuItem;
42 import javax.swing.JSeparator;
43
44 import net.sf.magicproject.action.MoveCard;
45 import net.sf.magicproject.action.listener.WaitingAbility;
46 import net.sf.magicproject.clickable.ability.Ability;
47 import net.sf.magicproject.clickable.ability.ActivatedAbility;
48 import net.sf.magicproject.clickable.ability.ReplacementAbility;
49 import net.sf.magicproject.clickable.ability.UserAbility;
50 import net.sf.magicproject.clickable.targetable.Targetable;
51 import net.sf.magicproject.clickable.targetable.TargetableFactory;
52 import net.sf.magicproject.clickable.targetable.player.Player;
53 import net.sf.magicproject.database.DatabaseCard;
54 import net.sf.magicproject.database.DatabaseFactory;
55 import net.sf.magicproject.event.ModifiedIdCard;
56 import net.sf.magicproject.event.ModifiedIdColor;
57 import net.sf.magicproject.event.ModifiedProperty;
58 import net.sf.magicproject.event.ModifiedRegister;
59 import net.sf.magicproject.event.context.ContextEventListener;
60 import net.sf.magicproject.event.context.MContextCardCardIntInt;
61 import net.sf.magicproject.modifier.AbilityModifier;
62 import net.sf.magicproject.modifier.ColorModifier;
63 import net.sf.magicproject.modifier.ControllerModifier;
64 import net.sf.magicproject.modifier.IdCardModifier;
65 import net.sf.magicproject.modifier.ModifierModel;
66 import net.sf.magicproject.modifier.ObjectFactory;
67 import net.sf.magicproject.modifier.PlayableZoneModifier;
68 import net.sf.magicproject.modifier.PropertyModifier;
69 import net.sf.magicproject.modifier.RegisterIndirection;
70 import net.sf.magicproject.modifier.RegisterModifier;
71 import net.sf.magicproject.network.ConnectionManager;
72 import net.sf.magicproject.network.IdMessages;
73 import net.sf.magicproject.operation.Add;
74 import net.sf.magicproject.operation.Operation;
75 import net.sf.magicproject.operation.Set;
76 import net.sf.magicproject.stack.StackManager;
77 import net.sf.magicproject.test.Test;
78 import net.sf.magicproject.token.IdCommonToken;
79 import net.sf.magicproject.token.IdConst;
80 import net.sf.magicproject.token.IdPositions;
81 import net.sf.magicproject.token.IdTokens;
82 import net.sf.magicproject.token.IdZones;
83 import net.sf.magicproject.token.Visibility;
84 import net.sf.magicproject.tools.Log;
85 import net.sf.magicproject.tools.MToolKit;
86 import net.sf.magicproject.tools.Pair;
87 import net.sf.magicproject.ui.Tappable;
88 import net.sf.magicproject.ui.i18n.LanguageManager;
89 import net.sf.magicproject.ui.layout.AttachmentLayout;
90 import net.sf.magicproject.zone.ExpandableZone;
91 import net.sf.magicproject.zone.MZone;
92 import net.sf.magicproject.zone.ZoneManager;
93
94 /***
95 * This class corresponds to the graphical element of a specified card, and
96 * correponds to an actor. When loading a new card, first we take care that a
97 * card with same name has not been already loaded in order to save memory, so
98 * all cards having the same name share the same Image object. <br>
99 *
100 * @since 0.52 "enableReverse" option
101 * @since 0.70 support modifiers
102 * @since 0.71 art and rule author
103 * @since 0.72 support static effects, removed art-author
104 * @since 0.85 property pictures are displayed
105 * @since 0.90 database + card model
106 * @since 0.91 keywords added, externalized to CardModel, explicit attachment
107 * object, and is movable.
108 * @see net.sf.magicproject.token.IdCardColors
109 * @author <a href="mailto:fabdouglas@users.sourceforge.net">Fabrice Daugan </a>
110 * @author <a href="mailto:kismet-sl@users.sourceforge.net">Stefano "Kismet"
111 * Lenzi</a>
112 */
113 public class MCard extends AbstractCard implements Tappable, MouseWheelListener {
114
115 /***
116 * Create a new instance of Card reading from a file.
117 *
118 * @param cardName
119 * the card name
120 * @param inputFile
121 * is the file readed, containing information
122 * @param controller
123 * is the controller of this card
124 * @param owner
125 * is the owner of this card
126 * @param constraints
127 * the constraints of the card for database management
128 */
129 public MCard(String cardName, InputStream inputFile, Player controller,
130 Player owner, Map<String, String> constraints) {
131 super();
132
133 initUI(null, CardFactory.getCardModel(cardName, inputFile), constraints);
134 initModel();
135 this.copiedCard = null;
136 this.owner = owner;
137 this.controller = controller;
138 this.originalController = controller;
139 lastKnownInstances = new HashMap<Integer, LastKnownCardInfo>();
140 }
141
142 /***
143 *
144 */
145 private void initModel() {
146 final CardModel cardModel = getCardModel();
147
148 registers = cardModel.getStaticRegisters().clone();
149 cachedRegisters = registers.clone();
150
151
152 cachedIdCard = cardModel.getIdCard();
153
154
155 cachedIdColor = cardModel.getIdColor();
156
157
158 cachedAbilities = new ArrayList<Ability>(cardModel.getAbilities().length);
159 for (Ability ability : cardModel.getAbilities()) {
160 cachedAbilities.add(ability.clone(this));
161 }
162
163
164 cachedProperties = new HashSet<Integer>();
165 for (int property : cardModel.getProperties()) {
166 cachedProperties.add(property);
167 }
168
169
170 indirections = new RegisterIndirection[IdTokens.CARD_REGISTER_SIZE];
171 registerModifiers = new RegisterModifier[IdTokens.CARD_REGISTER_SIZE];
172 }
173
174 /***
175 * Create a new instance of Card exactly like cardRef instance. Is called only
176 * from clone method.
177 *
178 * @param cardRef
179 * is the model for this new instance
180 * @param database
181 * the database of this new card.
182 */
183 public MCard(MCard cardRef, DatabaseCard database) {
184
185 this.database = database;
186 this.copiedCard = null;
187
188
189 initUI(null, database.getCardModel(), null);
190 initModel();
191 lastKnownInstances = new HashMap<Integer, LastKnownCardInfo>();
192
193 if (cardRef != null) {
194 controller = cardRef.controller;
195 owner = cardRef.owner;
196 }
197 originalController = controller;
198 }
199
200 /***
201 * Create a new instance of Card tokenized. No field is initialized.
202 */
203
204 protected MCard() {
205 super();
206 cachedAbilities = null;
207 database = null;
208 copiedCard = null;
209 originalController = null;
210 }
211
212 /***
213 * Create a new instance of tokenized Card. This constructor should be called
214 * only to obtain the a copy of the specified card sharing registers and
215 * abilities. Also, any modification done on registers or ability of one of
216 * these cards will be visible on the other. The zone information of cards is
217 * not shared, and the new card will have it's zone initialized with stack
218 * identifier instead of side. The picture used for this card will be the
219 * specified one if it is not null. Otherwise the cardRef picture will be
220 * used.
221 *
222 * @param pictureName
223 * is the picture name used to represent the copy. May be null.
224 * @param cardRef
225 * is the model for this instance
226 * @see IdZones#STACK
227 */
228 public MCard(String pictureName, MCard cardRef) {
229
230 database = cardRef.database;
231
232
233 initUI(pictureName, cardRef.database.getCardModel(), null);
234
235 this.idZone = IdZones.STACK;
236 this.registers = cardRef.registers;
237 this.cachedRegisters = cardRef.cachedRegisters;
238 this.cachedProperties = cardRef.cachedProperties;
239 this.cachedAbilities = null;
240 this.copiedCard = cardRef;
241 this.cachedIdCard = cardRef.cachedIdCard;
242 this.cachedIdColor = cardRef.cachedIdColor;
243 this.indirections = new RegisterIndirection[IdTokens.CARD_REGISTER_SIZE];
244 this.registerModifiers = new RegisterModifier[IdTokens.CARD_REGISTER_SIZE];
245 this.controller = cardRef.controller;
246 this.originalController = controller;
247 this.owner = cardRef.owner;
248 tokenize();
249 }
250
251 @Override
252 public boolean isAbility() {
253 return false;
254 }
255
256 @Override
257 public boolean isSpell() {
258 return true;
259 }
260
261 /***
262 * Indicates wether this card suits to the specified position code.
263 *
264 * @param position
265 * the matching position code
266 * @return true if this card suits to the specified position code.
267 * @see net.sf.magicproject.token.IdPositions#ON_THE_BOTTOM
268 * @see net.sf.magicproject.token.IdPositions#ON_THE_TOP
269 */
270 public boolean isSamePosition(int position) {
271 return getContainer().isSamePosition(this, position);
272 }
273
274 /***
275 * Return the container of this card.
276 *
277 * @return the container of this card.
278 */
279 public MZone getContainer() {
280 return controller.zoneManager.getContainer(getIdZone());
281 }
282
283 @Override
284 public boolean isACopy() {
285 return copiedCard != null;
286 }
287
288 /***
289 * Indicates if the specified sort of card idCard contains the card's sort.
290 *
291 * @param idCard
292 * are the sorts we match.
293 * @return true if the specified sort idCard contains the card's sort.
294 * @see #hasIdCard(int,int)
295 */
296 public boolean hasIdCard(int idCard) {
297 return hasIdCard(getIdCard(), idCard);
298 }
299
300 /***
301 * Indicates if idCardBIG contains completely idCardMatched. <br>
302 *
303 * @param idCardBIG
304 * are the set of types.
305 * @param idCardMatched
306 * are the type we match.
307 * @return true if idCardBIG contains completely idCardMatched. <br>
308 */
309 public static boolean hasIdCard(int idCardBIG, int idCardMatched) {
310 return (idCardBIG & idCardMatched) == idCardMatched;
311 }
312
313 /***
314 * Indicates if idCardBIG contains partially idCardMatched. <br>
315 *
316 * @param idCardBIG
317 * are the set of types.
318 * @param idCardMatched
319 * are the type we match.
320 * @return true if idCardBIG contains partially idCardMatched. <br>
321 */
322 public static boolean intersectionIdCard(int idCardBIG, int idCardMatched) {
323 return (idCardBIG & idCardMatched) != 0;
324 }
325
326 /***
327 * Indicates if the specified color idColor contains the card's colors. <br>
328 *
329 * @param idColor
330 * are the colors we match.
331 * @return true if the specified color idColor contains the card's colors.
332 * @see #hasIdColor(int,int)
333 */
334 public boolean hasIdColor(int idColor) {
335 return hasIdColor(getIdColor(), idColor);
336 }
337
338 /***
339 * Indicates if the specified color idColorBIG contains the other specified
340 * color idColorMatched. <br>
341 *
342 * @param idColorBIG
343 * are the colors that idColorMatched must have
344 * @param idColorMatched
345 * are the colors we match.
346 * @return true if the specified color idColorBIG contains the other specified
347 * color idColorMatched.
348 */
349 public static boolean hasIdColor(int idColorBIG, int idColorMatched) {
350 return (idColorBIG & idColorMatched) == idColorMatched;
351 }
352
353 /***
354 * <ul>
355 * Indicates if this card match with the specified place and constraint. As
356 * the specified place is an integer, it may contain another information about
357 * constraint in the high bits : xxyy
358 * <li>xx represents the constraint, may be : "must be untapped" or "must be
359 * tapped".
360 * <li>yy represents the zone identifiant.
361 * </ul>
362 * The zones are compared, then the constraint tapped/untapped is checked.
363 * <br>
364 * If <code>zoneConstaint</code> is ID__ANYWHERE, return true. <br>
365 * If <code>zoneConstaint</code> is same as current zone of this card and
366 * the constraint is validated, return true <br>
367 *
368 * @param zoneConstaint
369 * the zone id and optional tapped information.
370 * @return true if <code>zoneConstaint</code> is ID__ANYWHERE or
371 * <code>zoneConstaint</code> is same as current place of this card
372 * and the constraint is verified.
373 * @see IdZones#PLAY_TAPPED
374 * @see IdZones#PLAY_UNTAPPED
375 * @see IdZones
376 */
377 public boolean isSameState(int zoneConstaint) {
378 switch (zoneConstaint) {
379 case IdZones.PLAY_TAPPED:
380 return idZone == IdZones.PLAY && tapped;
381 case IdZones.PLAY_UNTAPPED:
382 return idZone == IdZones.PLAY && !tapped;
383 default:
384 return idZone == zoneConstaint;
385 }
386 }
387
388 /***
389 * Compare a zone with the current card'szone
390 *
391 * @param idZone
392 * the other zone
393 * @return true the specified zone, and the current card's zone are the same.
394 */
395 public boolean isSameIdZone(int idZone) {
396 return isSameIdZone(this.idZone, idZone);
397 }
398
399 /***
400 * @param idZone
401 * the other zone
402 * @param other
403 * another zone
404 * @return true the specified zones are the same.
405 */
406 public static boolean isSameIdZone(int idZone, int other) {
407 return getIdZone(idZone, null) == getIdZone(other, null);
408 }
409
410 /***
411 * Return the zone identifant of this card.
412 *
413 * @see IdZones
414 * @return the zone identifant of this card. IdZones#PLAY, IdZones#HAND,...
415 * @see IdZones
416 */
417 public int getIdZone() {
418 return idZone;
419 }
420
421 /***
422 * Return the place where is this card.
423 *
424 * @param idZone
425 * the zone identifiantt This identifiant may contain staus
426 * information like "tapped", "untapped"
427 * @param context
428 * The optional context attached to the request.
429 * @return the place identifiant corresponding to the specified zone, and with
430 * any status information
431 * @see IdZones
432 */
433 public static int getIdZone(int idZone, ContextEventListener context) {
434 if (idZone == IdZones.CONTEXT && context != null) {
435
436 return context.getZoneContext();
437 }
438 return idZone & 0x0F;
439 }
440
441 /***
442 * Return a zone code representing the zone and the state of this card.
443 *
444 * @param idZone
445 * the zone identifiantt without status information like "tapped",
446 * "untapped"
447 * @param tapped
448 * indicate the state of this car.
449 * @return the place identifiant corresponding to the specified zone, and with
450 * any status information
451 * @see IdZones
452 */
453 public static int getIdZone(int idZone, boolean tapped) {
454 return idZone == IdZones.PLAY ? tapped ? IdZones.PLAY_TAPPED
455 : IdZones.PLAY_UNTAPPED : idZone;
456 }
457
458 /***
459 * returns all IdColors of this card
460 *
461 * @return all IdColors of this card
462 * @see net.sf.magicproject.token.IdCardColors
463 */
464 public int getIdColor() {
465 return cachedIdColor;
466 }
467
468 /***
469 * returns all IdCards of this card
470 *
471 * @return all IdCards of this card
472 */
473 public int getIdCard() {
474 return cachedIdCard;
475 }
476
477 /***
478 * indicates if this card has this idType
479 *
480 * @param idType
481 * is the type required
482 * @return true if this card has the required type
483 */
484 public boolean hasIdType(int idType) {
485 return cachedProperties != null && cachedProperties.contains(idType);
486 }
487
488 /***
489 * indicates if this card has this idType.
490 *
491 * @param idType
492 * is the type required
493 * @param creator
494 * the card that has created the modifier to ignore.
495 * @return true if this card has the required type
496 */
497 public boolean hasPropertyNotFromCreator(int idType, MCard creator) {
498 boolean found;
499 int[] properties = getCardModel().getProperties();
500 found = properties != null && Arrays.binarySearch(properties, idType) >= 0;
501 if (creator == this) {
502 found = false;
503 }
504 if (propertyModifier != null) {
505 return propertyModifier.hasPropertyNotFromCreator(idType, found, creator);
506 }
507 return found;
508 }
509
510 @Override
511 public boolean needReverse() {
512 if (getParent() != null) {
513 if (isAttached()) {
514 return !StackManager.isYou(((MCard) getParent()).controller);
515 }
516 switch (getIdZone()) {
517 case IdZones.STACK:
518 case IdZones.DELAYED:
519 case IdZones.TRIGGERED:
520 case IdZones.SIDE:
521 return false;
522 default:
523 return !StackManager.isYou(controller);
524 }
525 }
526 return !StackManager.isYou(controller);
527 }
528
529 @Override
530 public void moveCard(int destinationZone, Player newController,
531 boolean newIsTapped, int idPosition) {
532
533
534 if (timestampReferences > 0) {
535
536 lastKnownInstances.put(timeStamp, new LastKnownCardInfoImpl(this,
537 timestampReferences, destinationZone));
538 timestampReferences = 0;
539 }
540 boolean fromPlayToPlay = idZone == IdZones.PLAY
541 && destinationZone == IdZones.PLAY;
542
543
544 if (idZone == IdZones.PLAY) {
545 if (!fromPlayToPlay) {
546
547 clearDamages();
548 }
549
550 final MCard attachedTo = isAttached() ? (MCard) getParent() : null;
551 controller.zoneManager.play.remove(this);
552
553 if (!fromPlayToPlay) {
554 tap(false);
555 }
556
557 if (attachedTo != null) {
558
559 attachedTo.ui.updateLayout();
560 }
561 controller.zoneManager.play.repaint();
562 } else if (getParent() != null) {
563 getContainer().remove(this);
564 }
565
566
567 if (controller == null) {
568 owner = newController;
569 }
570 controller = newController;
571 idZone = destinationZone;
572 timeStamp++;
573 isHighLighted = false;
574 reversed = needReverse();
575 clearPrivateNamedObject();
576 ui.updateLayout();
577
578
579 switch (destinationZone) {
580 case IdZones.PLAY:
581
582 if (!fromPlayToPlay) {
583 int[] staticregisters = getCardModel().getStaticRegisters();
584 System.arraycopy(staticregisters, 0, registers, 0,
585 staticregisters.length);
586 }
587
588 tap(newIsTapped);
589 newController.zoneManager.play.addBottom(this);
590 break;
591 case IdZones.NOWHERE:
592
593 break;
594 default:
595 switch (idPosition) {
596 case IdPositions.ON_THE_TOP:
597 newController.zoneManager.getContainer(destinationZone).addTop(this);
598 break;
599 case IdPositions.ON_THE_BOTTOM:
600 default:
601 newController.zoneManager.getContainer(destinationZone).addBottom(this);
602 }
603 }
604 }
605
606 /***
607 * Indicates wether the card with the specified zone identifiant is tapped or
608 * not.
609 *
610 * @param idZone
611 * the zone id + tapped position information.
612 * @return true if the card with the specified zone identifiant is tapped or
613 * not.
614 */
615 public static boolean isTapped(int idZone) {
616 return (idZone & IdZones.PLAY_TAPPED) == IdZones.PLAY_TAPPED;
617 }
618
619 /***
620 * Register abilities of this card, supposing card is in the specified zone.
621 *
622 * @param zone
623 * The supposed zone this card will go to
624 */
625 public void registerAbilities(int zone) {
626 for (Ability ability : cachedAbilities) {
627 if (ability.eventComing().isWellPlaced(zone)) {
628 ability.registerToManager();
629 }
630 }
631 }
632
633 /***
634 * Return a default ability associated to this card. There is no garantee this
635 * method returns always the samae ability.
636 *
637 * @return a default ability associated to this card.
638 */
639 public Ability getDummyAbility() {
640 if (cachedAbilities == null || cachedAbilities.isEmpty())
641
642 return null;
643 return cachedAbilities.get(0);
644 }
645
646 /***
647 * Register abilities of this card, supposing card is in the specified zone.
648 *
649 * @param zone
650 * The supposed zone this card will go to
651 */
652 public void registerReplacementAbilities(int zone) {
653 for (Ability ability : cachedAbilities) {
654 if (ability instanceof ReplacementAbility
655 && ability.eventComing().isWellPlaced(zone)) {
656 ability.registerToManager();
657 }
658 }
659 }
660
661 /***
662 * Unregister useless abilities from the eventManager.
663 */
664 public void unregisterAbilities() {
665 for (Ability ability : cachedAbilities) {
666 if (!ability.eventComing().isWellPlaced()) {
667 ability.removeFromManager();
668 }
669 }
670 }
671
672 /***
673 * Initialize card picture, database, layout and zone location (SIDE). If the
674 * specified card picture is null, the picture associated to the model will be
675 * used.
676 *
677 * @param pictureName
678 * the picture name used to represent the copy. May be null.
679 * @param cardModel
680 * rules author of this card
681 * @param constraints
682 * the constraints of the card
683 */
684 private void initUI(String pictureName, CardModel cardModel,
685 Map<String, String> constraints) {
686 isHighLighted = false;
687 idZone = IdZones.SIDE;
688
689
690 if (database == null) {
691 database = DatabaseFactory.getDatabase(pictureName, cardModel,
692 constraints);
693 }
694 this.originalDatabase = database;
695 setName(cardModel.getCardName());
696
697 ui = new VirtualCard(this);
698 add(ui);
699 setLayout(new AttachmentLayout());
700 ui.updateSizes();
701 addMouseWheelListener(this);
702 }
703
704 public void tap(boolean tapped) {
705 if (this.tapped != tapped) {
706 this.tapped = tapped;
707
708 for (int i = getComponentCount(); i-- > 0;) {
709 ((Tappable) getComponent(i)).tap(tapped);
710 }
711 ui.updateLayout();
712 }
713 }
714
715 @Override
716 public void reverse(boolean reversed) {
717 super.reverse(reversed);
718 ui.updateLayout();
719 }
720
721 /***
722 * Show/hide the back face of the card. You have to call the repaint() method
723 * to update the graphics if necessary.
724 *
725 * @param visibility
726 * the visibility of this card
727 */
728 public void returnCard(Visibility visibility) {
729 if (this.visibility == null
730 || visibility.isVisibleForYou() != this.visibility.isVisibleForYou()) {
731 this.visibility = visibility;
732 repaint();
733 } else {
734 this.visibility = visibility;
735 }
736 }
737
738 @Override
739 public void highLight(boolean... highlightedZones) {
740 super.highLight(ACTIVATED_COLOR);
741 switch (getIdZone()) {
742 case IdZones.SIDE:
743 case IdZones.STACK:
744 highlightedZones[idZone] = true;
745 break;
746 default:
747 highlightedZones[controller.idPlayer * IdZones.NB_ZONE + idZone] = true;
748 }
749 }
750
751 @Override
752 public void targetize(boolean... highlightedZones) {
753 super.highLight(TargetableFactory.TARGET_COLOR);
754 switch (getIdZone()) {
755 case IdZones.SIDE:
756 case IdZones.STACK:
757 highlightedZones[idZone] = true;
758 break;
759 default:
760 highlightedZones[controller.idPlayer * IdZones.NB_ZONE + idZone] = true;
761 }
762 }
763
764 public void mouseWheelMoved(MouseWheelEvent e) {
765
766 if (getIdZone() == IdZones.PLAY && getComponentCount() > 1) {
767 final LayoutManager layoutManager = getLayout();
768 if (layoutManager != null && layoutManager instanceof AttachmentLayout
769 && e.getScrollType() == MouseWheelEvent.WHEEL_UNIT_SCROLL) {
770 if (e.getWheelRotation() < 0) {
771 ((AttachmentLayout) layoutManager).decreaseCardLayout(-e
772 .getWheelRotation());
773 } else {
774 ((AttachmentLayout) layoutManager).increaseCardLayout(e
775 .getWheelRotation());
776 }
777 }
778 doLayout();
779 getParent().doLayout();
780 getContainer().doLayout();
781 }
782 }
783
784 @Override
785 public void mouseClicked(MouseEvent e) {
786 if (!(getParent() instanceof MZone) && !(getParent() instanceof MCard)) {
787 return;
788 }
789 StackManager.noReplayToken.take();
790 MZone container = getContainer();
791 try {
792 TargetableFactory.triggerTargetable = this;
793 if (ConnectionManager.isConnected()
794 && e.getButton() == MouseEvent.BUTTON1) {
795
796 if (!StackManager.actionManager.clickOn(this)) {
797
798 if (!(StackManager.actionManager.currentAction instanceof WaitingAbility)) {
799
800 if (container instanceof ExpandableZone) {
801 ((ExpandableZone) container).toggle();
802 }
803 return;
804 }
805 final List<Ability> abilities = ((WaitingAbility) StackManager.actionManager.currentAction)
806 .abilitiesOf(this);
807 final List<Ability> advAbilities = ((WaitingAbility) StackManager.actionManager.currentAction)
808 .advancedAbilitiesOf(this);
809
810
811 if ((abilities == null || abilities.isEmpty())
812 && (advAbilities == null || advAbilities.isEmpty())) {
813
814 if (container instanceof ExpandableZone) {
815 ((ExpandableZone) container).toggle();
816 }
817 return;
818 }
819
820
821 if (advAbilities != null && !advAbilities.isEmpty()
822 || abilities != null && abilities.size() > 1) {
823
824
825
826 TargetableFactory.abilitiesMenu.removeAll();
827 if (abilities != null) {
828 for (Ability ability : abilities) {
829 final JMenuItem item = new JMenuItem("<html>"
830 + ability.toHtmlString(null) + "</html>");
831 item.addActionListener(this);
832 TargetableFactory.abilitiesMenu.add(item);
833 }
834 }
835 if (advAbilities != null && !advAbilities.isEmpty()) {
836 TargetableFactory.abilitiesMenu.add(new JSeparator());
837 for (Ability ability : advAbilities) {
838 final JMenuItem item = new JMenuItem("<html>"
839 + ability.toHtmlString(null) + "</html>", WARNING_PICTURE);
840 item.addActionListener(this);
841 TargetableFactory.abilitiesMenu.add(item);
842 }
843 }
844
845 TargetableFactory.abilitiesMenu.show(e.getComponent(), e.getX(), e
846 .getY());
847 return;
848 } else if (abilities != null && abilities.size() == 1
849 && (advAbilities == null || advAbilities.isEmpty())) {
850
851 ((UserAbility) abilities.get(0)).mouseClicked(0);
852 }
853 } else if (StackManager.idHandedPlayer != 0) {
854 if (container instanceof ExpandableZone) {
855 ((ExpandableZone) container).toggle();
856 }
857 } else {
858
859 sendClickToOpponent();
860 StackManager.actionManager.succeedClickOn(this);
861 }
862 } else if (ConnectionManager.isConnected()) {
863
864 CardFactory.contextMenu.removeAll();
865 TargetableFactory.triggerTargetable = this;
866
867
868 CardFactory.countItem.setText(LanguageManager.getString("countcard",
869 container.toString(), String.valueOf(container.getCardCount())));
870 CardFactory.contextMenu.add(CardFactory.countItem);
871
872
873 if (container instanceof ExpandableZone) {
874 if (container == ZoneManager.expandedZone) {
875 CardFactory.contextMenu.add(CardFactory.gatherItem);
876 } else {
877 CardFactory.contextMenu.add(CardFactory.expandItem);
878 }
879 }
880 CardFactory.contextMenu.add(new JSeparator());
881
882
883 CardFactory.contextMenu.add(CardFactory.databaseCardInfoItem);
884
885
886
887
888
889
890
891 CardFactory.contextMenu.show(e.getComponent(), e.getX(), e.getY());
892 }
893 } catch (Throwable t) {
894 t.printStackTrace();
895 } finally {
896 StackManager.noReplayToken.release();
897 }
898 }
899
900 @Override
901 public void sendClickToOpponent() {
902 ConnectionManager.sendToOpponent(getBytes());
903 }
904
905 /***
906 * Return the int array representing this card. This array can be send throw
907 * network.
908 *
909 * @return bytes representing this card.
910 */
911 public final int[] getBytes() {
912 Pair<Integer, Integer> index = controller.zoneManager.getContainer(idZone)
913 .getRealIndexOf(this);
914
915 return new int[] { IdMessages.MSG_CLICK_CARD, 1 - controller.idPlayer,
916 getIdZone(), index.key, index.value };
917 }
918
919 /***
920 * This method is invoked when opponent has clicked on this object. this call
921 * should be done from the net.sf.magicproject.network listener
922 *
923 * @param input
924 * input stream of our net.sf.magicproject.network connection
925 * @throws IOException
926 * if error occurred when reading the message
927 */
928 public static void clickOn(InputStream input) throws IOException {
929
930 MCard card = getCard(input);
931 StackManager.actionManager.clickOn(card);
932 StackManager.actionManager.succeedClickOn(card);
933 }
934
935 /***
936 * Return the component from information read throw
937 * net.sf.magicproject.network
938 *
939 * @param input
940 * input stream of our net.sf.magicproject.network connection
941 * @return the card read from the specified input stream
942 * @throws IOException
943 * if error occurred when reading the message
944 */
945 public static MCard getCard(InputStream input) throws IOException {
946
947 int idPlayer = input.read();
948 int idZone = input.read();
949 int index = input.read();
950 int childIndex = input.read();
951 if (childIndex == 0) {
952
953 return StackManager.PLAYERS[idPlayer].zoneManager.getContainer(idZone)
954 .getCard(index);
955 }
956
957 return (MCard) StackManager.PLAYERS[idPlayer].zoneManager.getContainer(
958 idZone).getCard(index).getComponent(childIndex);
959 }
960
961 @Override
962 public int getValue(int index) {
963 if (index == IdTokens.MANA_POOL) {
964 if (idZone == IdZones.STACK) {
965
966 return StackManager.getTotalManaPaid(this);
967 }
968 return MToolKit.manaPool(cachedRegisters);
969 }
970 if (index == IdTokens.ID) {
971 return getId();
972 }
973
974 if (index >= IdTokens.FIRST_FREE_CARD_INDEX) {
975 return registers[index];
976 }
977 if (cachedRegisters == null) {
978 throw new InternalError("modifiedRegisters is null in getValue in card");
979 }
980 return cachedRegisters[index];
981 }
982
983 @Override
984 public int getValueIndirection(int index) {
985
986 if (indirections[index] != null) {
987 return indirections[index].getValue(registers[index]);
988 }
989 return registers[index];
990 }
991
992 /***
993 * Add a modifier to this object
994 *
995 * @param modifier
996 * the color-modifier to add to this object
997 */
998 public void addModifier(ColorModifier modifier) {
999 if (colorModifier != null) {
1000 colorModifier = (ColorModifier) colorModifier.addModifier(modifier);
1001 } else {
1002 colorModifier = modifier;
1003 }
1004 }
1005
1006 /***
1007 * Add a modifier to this object
1008 *
1009 * @param modifier
1010 * the idcard-modifier to add to this object
1011 */
1012 public void addModifier(IdCardModifier modifier) {
1013 if (idCardModifier != null) {
1014 idCardModifier = (IdCardModifier) idCardModifier.addModifier(modifier);
1015 } else {
1016 idCardModifier = modifier;
1017 }
1018 }
1019
1020 /***
1021 * Add a modifier to this object
1022 *
1023 * @param modifier
1024 * the ability-modifier to add to this object
1025 */
1026 public void addModifier(AbilityModifier modifier) {
1027 if (abilityModifier != null) {
1028 abilityModifier = (AbilityModifier) abilityModifier.addModifier(modifier);
1029 } else {
1030 abilityModifier = modifier;
1031 }
1032 }
1033
1034 /***
1035 * Add a modifier to this object
1036 *
1037 * @param modifier
1038 * the property-modifier to add to this object
1039 */
1040 public void addModifier(PropertyModifier modifier) {
1041 if (propertyModifier != null) {
1042 propertyModifier = (PropertyModifier) propertyModifier
1043 .addModifier(modifier);
1044 } else {
1045 propertyModifier = modifier;
1046 }
1047 }
1048
1049 /***
1050 * Add a modifier to this object
1051 *
1052 * @param modifier
1053 * the controller-modifier to add to this object
1054 */
1055 public void addModifier(ControllerModifier modifier) {
1056 if (controllerModifier != null) {
1057 controllerModifier = (ControllerModifier) controllerModifier
1058 .addModifier(modifier);
1059 } else {
1060 controllerModifier = modifier;
1061 }
1062 }
1063
1064 /***
1065 * Add a modifier to this object
1066 *
1067 * @param modifier
1068 * the playable zone-modifier to add to this object
1069 */
1070 public void addModifier(PlayableZoneModifier modifier) {
1071 if (playableZoneModifier != null) {
1072 playableZoneModifier = (PlayableZoneModifier) playableZoneModifier
1073 .addModifier(modifier);
1074 } else {
1075 playableZoneModifier = modifier;
1076 }
1077 }
1078
1079 @Override
1080 public void removeModifier(RegisterModifier modifier, int index) {
1081 if (registerModifiers[index] != null) {
1082 registerModifiers[index] = (RegisterModifier) registerModifiers[index]
1083 .removeModifier(modifier);
1084 } else {
1085 Log.warn("Card moved, unable to unregister " + modifier);
1086 }
1087
1088 }
1089
1090 @Override
1091 public void removeModifier(RegisterIndirection indirection, int index) {
1092 if (indirections[index] != null) {
1093 indirections[index] = (RegisterIndirection) indirections[index]
1094 .removeModifier(indirection);
1095 } else {
1096 Log.debug("\t...Card moved, unable to unregister " + indirection);
1097 }
1098 }
1099
1100 /***
1101 * Remove the specified idcard-modifier
1102 *
1103 * @param modifier
1104 * the idcard-modifier to be removed from this object
1105 */
1106 public void removeModifier(IdCardModifier modifier) {
1107 if (modifier != null) {
1108 idCardModifier = (IdCardModifier) idCardModifier.removeModifier(modifier);
1109 } else {
1110 Log.warn("Card moved, unable to unregister " + modifier);
1111 }
1112 }
1113
1114 /***
1115 * Remove the specified ability-modifier
1116 *
1117 * @param modifier
1118 * the ability-modifier to be removed from this object
1119 */
1120 public void removeModifier(AbilityModifier modifier) {
1121 if (modifier != null) {
1122 abilityModifier = (AbilityModifier) abilityModifier
1123 .removeModifier(modifier);
1124 } else {
1125 Log.warn("Card moved, unable to unregister " + modifier);
1126 }
1127 }
1128
1129 /***
1130 * Remove the specified controller-modifier
1131 *
1132 * @param modifier
1133 * the controller-modifier to be removed from this object
1134 */
1135 public void removeModifier(ControllerModifier modifier) {
1136 if (modifier != null) {
1137 controllerModifier = (ControllerModifier) controllerModifier
1138 .removeModifier(modifier);
1139 } else {
1140 Log.warn("Card moved, unable to unregister " + modifier);
1141 }
1142 }
1143
1144 /***
1145 * Remove the specified property-modifier
1146 *
1147 * @param modifier
1148 * the property-modifier to be removed from this object
1149 */
1150 public void removeModifier(PropertyModifier modifier) {
1151 if (modifier != null) {
1152 propertyModifier = (PropertyModifier) propertyModifier
1153 .removeModifier(modifier);
1154 } else {
1155 Log.warn("Card moved, unable to unregister " + modifier);
1156 }
1157 }
1158
1159 /***
1160 * Remove the specified playable zone-modifier
1161 *
1162 * @param modifier
1163 * the playable zone-modifier to be removed from this object
1164 */
1165 public void removeModifier(PlayableZoneModifier modifier) {
1166 if (modifier != null) {
1167 playableZoneModifier = (PlayableZoneModifier) playableZoneModifier
1168 .removeModifier(modifier);
1169 } else {
1170 Log.warn("Card moved, unable to unregister " + modifier);
1171 }
1172 }
1173
1174 /***
1175 * Remove the specified color-modifier.
1176 *
1177 * @param modifier
1178 * the color-modifier to be removed from this object
1179 */
1180 public void removeModifier(ColorModifier modifier) {
1181 if (modifier != null) {
1182 colorModifier = (ColorModifier) colorModifier.removeModifier(modifier);
1183 } else {
1184 Log.warn("Card moved, unable to unregister " + modifier);
1185 }
1186 }
1187
1188 /***
1189 * Indicated if this card is attached or not.
1190 *
1191 * @return true value if this card is attached.
1192 */
1193 public boolean isAttached() {
1194 return getParent() != null && getParent() instanceof MCard;
1195 }
1196
1197 @Override
1198 public String getTooltipString() {
1199 return ui.getTooltipString();
1200 }
1201
1202 /***
1203 * Refresh the idcard of this card, and raise an event if the value has been
1204 * changed.
1205 */
1206 public void refreshIdCard() {
1207 int modifiedIdCard = idCardModifier != null ? idCardModifier
1208 .getIdCard(getCardModel().getIdCard()) : getCardModel().getIdCard();
1209 if (this.cachedIdCard != modifiedIdCard) {
1210
1211 final int modifiedIdCardTmp = this.cachedIdCard;
1212 this.cachedIdCard = modifiedIdCard;
1213 ModifiedIdCard.dispatchEvent(this, modifiedIdCard | modifiedIdCardTmp);
1214 ui.resetCachedData();
1215 }
1216 }
1217
1218 /***
1219 * Refresh the granted abilities of this card. No event raised.
1220 */
1221 public void refreshAbilities() {
1222 if (abilityModifier == null) {
1223 registerAbilities(idZone);
1224 } else {
1225 final List<Ability> toRegister = new ArrayList<Ability>(cachedAbilities
1226 .size() + 2);
1227
1228 for (Ability ability : cachedAbilities) {
1229 if (ability.eventComing().isWellPlaced(idZone)) {
1230 toRegister.add(ability);
1231 }
1232 }
1233
1234 abilityModifier.calculateDeltaAbilities(toRegister);
1235
1236
1237 for (Ability ability : cachedAbilities) {
1238 if (!toRegister.contains(ability)) {
1239
1240 ability.removeFromManager();
1241 }
1242 }
1243
1244
1245 for (Ability ability : toRegister) {
1246 ability.registerToManager();
1247 }
1248 }
1249 }
1250
1251 /***
1252 * Refresh the colors of this card, and raise an event if the value has been
1253 * changed.
1254 */
1255 public void refreshIdColor() {
1256 int modifiedIdColor = colorModifier != null ? colorModifier
1257 .getIdColor(getCardModel().getIdColor()) : getCardModel().getIdColor();
1258 if (this.cachedIdColor != modifiedIdColor) {
1259
1260 int modifiedIdColorTmp = this.cachedIdColor;
1261 this.cachedIdColor = modifiedIdColor;
1262 ModifiedIdColor.dispatchEvent(this, modifiedIdColor | modifiedIdColorTmp);
1263 this.cachedIdColor = modifiedIdColor;
1264 ui.resetCachedData();
1265 }
1266 }
1267
1268 /***
1269 * Refresh the properties of this card, and raise an event if the value has
1270 * been changed. NOTE : there is no cache for properties.
1271 *
1272 * @param property
1273 * the property to refresh.
1274 */
1275 public void refreshProperties(int property) {
1276 final CardModel cardModel = getCardModel();
1277 if (property == IdConst.ALL) {
1278
1279 java.util.Set<Integer> oldProperties = new HashSet<Integer>(
1280 cachedProperties);
1281 cachedProperties.clear();
1282 for (int initProperty : cardModel.getProperties()) {
1283 cachedProperties.add(initProperty);
1284 }
1285 boolean raised = false;
1286 if (propertyModifier != null) {
1287 propertyModifier.fillProperties(cachedProperties);
1288 }
1289 for (Integer cacheProperty : cachedProperties) {
1290 if (!oldProperties.contains(cacheProperty)) {
1291 ModifiedProperty.dispatchEvent(this, cacheProperty);
1292 raised = true;
1293 }
1294 }
1295 for (Integer oldProperty : oldProperties) {
1296 if (!cachedProperties.contains(oldProperty)) {
1297 ModifiedProperty.dispatchEvent(this, oldProperty);
1298 raised = true;
1299 }
1300 }
1301 if (raised) {
1302 ui.resetCachedData();
1303 repaint();
1304 }
1305 } else {
1306 final boolean oldFound = cachedProperties.contains(property);
1307 int[] properties = cardModel.getProperties();
1308 boolean found = properties != null
1309 && Arrays.binarySearch(properties, property) >= 0;
1310 if (propertyModifier != null) {
1311 found = propertyModifier.hasProperty(property, found);
1312 }
1313 if (oldFound != found) {
1314 if (found) {
1315 cachedProperties.add(property);
1316 } else {
1317 cachedProperties.remove(property);
1318 }
1319 ModifiedProperty.dispatchEvent(this, property);
1320 ui.resetCachedData();
1321 repaint();
1322 }
1323 }
1324 }
1325
1326 /***
1327 * Refresh the registers of this card, and raise an event if the value has
1328 * been changed.
1329 *
1330 * @param index
1331 * is the register index to refresh
1332 */
1333 public void refreshRegisters(int index) {
1334 if (index == IdConst.ALL) {
1335 for (int i = 0; i < registerModifiers.length; i++) {
1336 refreshRegisters(i);
1337 }
1338 } else {
1339 final int modifiedRegister;
1340 if (registerModifiers[index] != null)
1341 modifiedRegister = registerModifiers[index]
1342 .getValue(getValueIndirection(index));
1343 else {
1344 modifiedRegister = getValueIndirection(index);
1345 }
1346 if (this.cachedRegisters[index] != modifiedRegister) {
1347
1348
1349 cachedRegisters[index] = modifiedRegister < 0 ? 0 : modifiedRegister;
1350 ModifiedRegister.dispatchEvent(this, this, IdTokens.CARD, index, Set
1351 .getInstance(), modifiedRegister);
1352 ui.resetCachedData();
1353 repaint();
1354 }
1355 }
1356 }
1357
1358 /***
1359 * Refresh the controller of this card, and raise an event if the value has
1360 * been changed.
1361 */
1362 public void refreshController() {
1363 final Player controller = controllerModifier != null ? controllerModifier
1364 .getPlayer(originalController) : originalController;
1365 if (this.controller != controller) {
1366
1367 MoveCard.moveCard(this, controller, getIdZone(idZone, tapped), null, 0,
1368 null, false);
1369 }
1370 ui.resetCachedData();
1371 }
1372
1373 @Override
1374 public int countAllCardsOf(Test test, Ability ability, boolean canBePreempted) {
1375 int result = 0;
1376 if (canBePreempted) {
1377 result = test.test(ability, this) ? 1 : 0;
1378 } else {
1379 result = test.testPreemption(ability, this) ? 1 : 0;
1380 }
1381 for (int j = getComponentCount(); j-- > 1;) {
1382 if (getComponent(j) instanceof MCard) {
1383 if (canBePreempted) {
1384 if (test.test(ability, (MCard) getComponent(j))) {
1385 result++;
1386 }
1387 } else if (!canBePreempted) {
1388 if (test.testPreemption(ability, (MCard) getComponent(j))) {
1389 result++;
1390 }
1391 }
1392 }
1393 }
1394 return result;
1395 }
1396
1397 @Override
1398 public void checkAllCardsOf(Test test, List<Targetable> list, Ability ability) {
1399 if (test.test(ability, this)) {
1400 list.add(this);
1401 }
1402 for (int j = getComponentCount(); j-- > 1;) {
1403 if (getComponent(j) instanceof MCard
1404 && test.test(ability, (MCard) getComponent(j))) {
1405 list.add((MCard) getComponent(j));
1406 }
1407 }
1408 }
1409
1410 /***
1411 * Indicates this card can be played from a specified zone.
1412 *
1413 * @param supposedZone
1414 * the zone where the this card would be played from.
1415 * @param idZone
1416 * the zone where this card can be played from.
1417 * @return true if this card is playable from the supposed zone
1418 */
1419 public boolean playableZone(int supposedZone, int idZone) {
1420 if (playableZoneModifier != null) {
1421 return playableZoneModifier.playableIn(idZone, idZone == supposedZone);
1422 }
1423 return supposedZone == idZone;
1424 }
1425
1426 /***
1427 * [un]Register ActivatedAbilities depending on the current zone of this card
1428 */
1429 public void updateAbilities() {
1430 for (Ability ability : cachedAbilities) {
1431 if (ability instanceof ActivatedAbility) {
1432 ability.removeFromManager();
1433 for (int j = IdZones.STACK; j-- > 0;) {
1434 if (ability.eventComing().isWellPlaced(j)) {
1435 ability.registerToManager();
1436 break;
1437 }
1438 }
1439 }
1440 }
1441 }
1442
1443 /***
1444 * Set to the register of this card a value to a specified index. The given
1445 * operation is uesed to apply operation on old and the given value. To set
1446 * the given value as the new one, use the "set" operation.
1447 *
1448 * @param index
1449 * is the index of register to modify
1450 * @param operation
1451 * the operation to use
1452 * @param rightValue
1453 * is the value to use as right operande for the operation
1454 */
1455 public void setValue(int index, Operation operation, int rightValue) {
1456 registers[index] = operation.process(registers[index], rightValue);
1457 if (index == IdCommonToken.DAMAGE) {
1458 MContextCardCardIntInt context = null;
1459 if (StackManager.getInstance().getAbilityContext() != null
1460 && StackManager.triggered.getAbilityContext() instanceof MContextCardCardIntInt
1461 && operation instanceof Add) {
1462 context = (MContextCardCardIntInt) StackManager.triggered
1463 .getAbilityContext();
1464 if (context.getValue() != rightValue) {
1465 throw new InternalError("wrong damage value regValue=" + rightValue
1466 + ", context.int=" + context.getValue());
1467 }
1468 }
1469 if (operation instanceof Add) {
1470 Log.debug(new StringBuilder("Add ").append(rightValue).append(
1471 " damage to card ").append(getCardName()).append("@").append(
1472 hashCode()).append(":").append(rightValue));
1473 Damage damage = new Damage(context == null ? SystemCard.instance
1474 : context.getCard2(), rightValue, context == null ? 0 : context
1475 .getValue2());
1476 damage.tap(tapped);
1477 add(damage);
1478 } else {
1479
1480 clearDamages();
1481 }
1482 ui.updateLayout();
1483 }
1484 if (index < IdTokens.FIRST_FREE_CARD_INDEX) {
1485
1486 int modifiedRegister = registerModifiers[index] != null ? registerModifiers[index]
1487 .getValue(getValueIndirection(index))
1488 : getValueIndirection(index);
1489 if (this.cachedRegisters[index] != modifiedRegister) {
1490
1491
1492 cachedRegisters[index] = modifiedRegister < 0 ? 0 : modifiedRegister;
1493 ui.resetCachedData();
1494
1495 }
1496 } else {
1497 repaint();
1498 }
1499 }
1500
1501 /***
1502 * Set a new zone for this card.
1503 *
1504 * @param idZone
1505 * the new zone.
1506 */
1507 public void setIdZone(int idZone) {
1508 this.idZone = idZone;
1509 }
1510
1511 @Override
1512 public void addTimestampReference() {
1513 timestampReferences++;
1514 }
1515
1516 @Override
1517 public void decrementTimestampReference(int timestamp) {
1518 if (this.timeStamp == timestamp) {
1519 timestampReferences--;
1520 } else if (lastKnownInstances.get(timestamp) != null) {
1521 if (lastKnownInstances.get(timestamp).removeTimestamp(timestamp)) {
1522
1523
1524
1525
1526 lastKnownInstances.remove(timestamp);
1527 }
1528 }
1529 }
1530
1531 /***
1532 * Return all properties of this card
1533 *
1534 * @return all properties of this card
1535 */
1536 public java.util.Set<Integer> getProperties() {
1537 return cachedProperties;
1538 }
1539
1540 @Override
1541 public Targetable getLastKnownTargetable(int timeStamp) {
1542 if (timeStamp == this.timeStamp) {
1543 assert timestampReferences > 0;
1544 return this;
1545 }
1546 if (lastKnownInstances.get(timeStamp) == null) {
1547 return this;
1548 }
1549 return lastKnownInstances.get(timeStamp).createLastKnownCard();
1550 }
1551
1552 @Override
1553 public int getTimestamp() {
1554 return timeStamp;
1555 }
1556
1557 /***
1558 * Return occurences number of the given object with the given name attached
1559 * to this card.
1560 *
1561 * @param objectName
1562 * the object's name to find within the register modfiers chain.
1563 * @param objectTest
1564 * The test applied on specific modifier to be removed.
1565 * @return occurences number of the given object with the given name attached
1566 * to this card.
1567 */
1568 public int getNbObjects(String objectName, Test objectTest) {
1569 return ObjectFactory.getObjectModifierModel(objectName).getNbObject(this,
1570 objectTest);
1571 }
1572
1573 @Override
1574 public void clearDamages() {
1575 for (int i = getComponentCount(); i-- > 0;) {
1576 if (getComponent(i) instanceof Damage) {
1577 remove(getComponent(i));
1578 }
1579 registers[IdCommonToken.DAMAGE] = 0;
1580 cachedRegisters[IdCommonToken.DAMAGE] = 0;
1581 }
1582 }
1583
1584 /***
1585 * The database configuration of this card
1586 */
1587 private DatabaseCard originalDatabase;
1588
1589 /***
1590 * The modified idcard.
1591 */
1592 public int cachedIdCard;
1593
1594 /***
1595 * The modified color.
1596 */
1597 public int cachedIdColor;
1598
1599 /***
1600 * The modified registers.
1601 */
1602 public int[] cachedRegisters;
1603
1604 /***
1605 * The original player.
1606 */
1607 public final Player originalController;
1608
1609 /***
1610 * Player owner
1611 */
1612 protected Player owner;
1613
1614 /***
1615 * the zone identifiant
1616 *
1617 * @see IdZones
1618 */
1619 protected int idZone;
1620
1621 /***
1622 * Indicates if this card should be tapped or not.
1623 */
1624 public boolean tapped;
1625
1626 /***
1627 * original card wich staid at it's place not really moved to stack.
1628 */
1629 private final MCard copiedCard;
1630
1631 /***
1632 * The colors modifiers on this object.
1633 */
1634 public ColorModifier colorModifier;
1635
1636 /***
1637 * The idcard modifiers on this object.
1638 */
1639 public IdCardModifier idCardModifier;
1640
1641 /***
1642 * The ability modifiers on this object.
1643 */
1644 public AbilityModifier abilityModifier;
1645
1646 /***
1647 * The properties modifiers on this object.
1648 */
1649 public PropertyModifier propertyModifier;
1650
1651 /***
1652 * The playable zone modifiers on this object.
1653 */
1654 public PlayableZoneModifier playableZoneModifier;
1655
1656 /***
1657 * Represents all referenced instances of this card. <br>
1658 * Key is timastamp of this card. <br>
1659 * Value is LastKnownCardInfo instance.
1660 */
1661 private Map<Integer, LastKnownCardInfo> lastKnownInstances;
1662
1663 /***
1664 * This timestamp corresponds to the amount of card movements.
1665 */
1666 protected int timeStamp;
1667
1668 /***
1669 * the reference counter for the current timestamp of this card.
1670 */
1671 protected int timestampReferences;
1672
1673 /***
1674 * List of properties of this card.
1675 */
1676 public java.util.Set<Integer> cachedProperties;
1677
1678 /***
1679 * @since 0.91
1680 * @see net.sf.magicproject.clickable.targetable.card.CardModel#getModifierModels()
1681 * @return the shared modifier models.
1682 */
1683 public ModifierModel getModifierModels() {
1684 return this.getCardModel().getModifierModels();
1685 }
1686
1687 @Override
1688 public int hashCode() {
1689 return getCardName().hashCode();
1690 }
1691
1692 @Override
1693 public boolean equals(Object obj) {
1694 return obj == this;
1695 }
1696
1697 /***
1698 * Returns the cardModel reference of this card.
1699 *
1700 * @return the cardModel reference of this card.
1701 */
1702 public CardModel getCardModel() {
1703 return database.getCardModel();
1704 }
1705
1706 /***
1707 * Set the new owner of this card.
1708 *
1709 * @param owner
1710 * the new owner.
1711 */
1712 public void setOwner(Player owner) {
1713 this.owner = owner;
1714 }
1715
1716 /***
1717 * Return card's owner.
1718 *
1719 * @return card's owner.
1720 */
1721 public Player getOwner() {
1722 return owner;
1723 }
1724
1725 /***
1726 * Update the card model. Also, post some refresh request on some attributes
1727 * of this card.
1728 *
1729 * @param database
1730 * the new database of this card.
1731 */
1732 public void setDataBase(DatabaseCard database) {
1733 unregisterAbilities();
1734 this.database = database;
1735 final CardModel cardModel = getCardModel();
1736 setName(getCardModel().getCardName());
1737 cachedAbilities = new ArrayList<Ability>(cardModel.getAbilities().length);
1738 for (Ability ability : cardModel.getAbilities()) {
1739 cachedAbilities.add(ability.clone(this));
1740 }
1741 registers = getCardModel().getStaticRegisters().clone();
1742 StackManager.postRefreshAbilities(this);
1743 StackManager.postRefreshColor(this);
1744 StackManager.postRefreshIdCard(this);
1745 StackManager.postRefreshProperties(this, IdConst.ALL);
1746 StackManager.postRefreshRegisters(this, IdConst.ALL);
1747 repaint();
1748 }
1749
1750 /***
1751 * Return <code>true</code> if this card has a database configuration
1752 * different from the original one.
1753 *
1754 * @return <code>true</code> if this card has a database configuration
1755 * different from the original one.
1756 */
1757 public boolean hasDirtyDataBase() {
1758 return originalDatabase != getDatabase();
1759 }
1760
1761 /***
1762 * Return the original database as imprinted.
1763 *
1764 * @return the original database.
1765 */
1766 public DatabaseCard getOriginalDatabase() {
1767 return originalDatabase;
1768 }
1769
1770 /***
1771 * Return the attached cards.
1772 *
1773 * @return the attached cards.
1774 */
1775 public Collection<MCard> getAttachedCards() {
1776 final Collection<MCard> attachedCards = new ArrayList<MCard>(2);
1777 for (Component component : getComponents()) {
1778 if (component instanceof MCard)
1779 attachedCards.add((MCard) component);
1780 }
1781 return attachedCards;
1782 }
1783
1784 }