1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20 package net.sf.magicproject.action;
21
22 import java.io.IOException;
23 import java.io.InputStream;
24 import java.util.ArrayList;
25 import java.util.Arrays;
26 import java.util.List;
27
28 import net.sf.magicproject.action.context.ActionContextWrapper;
29 import net.sf.magicproject.action.context.MoveContext;
30 import net.sf.magicproject.action.handler.FollowAction;
31 import net.sf.magicproject.clickable.ability.Ability;
32 import net.sf.magicproject.clickable.targetable.Targetable;
33 import net.sf.magicproject.clickable.targetable.card.MCard;
34 import net.sf.magicproject.clickable.targetable.player.Player;
35 import net.sf.magicproject.event.Detached;
36 import net.sf.magicproject.event.MovedCard;
37 import net.sf.magicproject.event.context.ContextEventListener;
38 import net.sf.magicproject.network.ConnectionManager;
39 import net.sf.magicproject.network.IdMessages;
40 import net.sf.magicproject.stack.StackManager;
41 import net.sf.magicproject.test.TestOn;
42 import net.sf.magicproject.token.IdPositions;
43 import net.sf.magicproject.token.IdZones;
44 import net.sf.magicproject.tools.Log;
45 import net.sf.magicproject.tools.MToolKit;
46 import net.sf.magicproject.ui.i18n.LanguageManagerMDB;
47 import net.sf.magicproject.ui.wizard.Arrange;
48 import net.sf.magicproject.ui.wizard.Wizard;
49 import net.sf.magicproject.zone.MZone;
50 import net.sf.magicproject.zone.ZoneManager;
51
52 /***
53 * To move the current target list from their place to another. New position
54 * within the new zone, and the new controller have to be specified. <br>
55 *
56 * @version 0.91
57 * @author <a href="mailto:fabdouglas@users.sourceforge.net">Fabrice Daugan </a>
58 * @author <a href="mailto:kismet-sl@users.sourceforge.net">Stefano "Kismet"
59 * Lenzi</a>
60 * @since 0.54
61 * @since 0.80 activable abilities of card are registered before the 'moved
62 * card' is generated
63 * @since 0.80 support replacement
64 * @since 0.82 card is moved into the destination zone before the event is
65 * generated. During the event dispatching there is an incoherence.
66 * @since 0.82 timestamp is checked
67 * @since 0.82 if there are several cards to move, controller chooses order
68 * @since 0.86 action ignore non-card element present in the target list
69 */
70 public class MoveCard extends UserAction implements LoopAction, FollowAction,
71 BackgroundMessaging, AccessibleContext {
72
73 /***
74 * Create an instance of MoveCardList by reading a file Offset's file must
75 * pointing on the first byte of this action <br>
76 * <ul>
77 * Structure of InputStream : Data[size]
78 * <li>new controller [TestOn]</li>
79 * <li>destination zone [IdZone]</li>
80 * <li>idPosition [int16]</li>
81 * <li>silent [boolean]</li>
82 * </ul>
83 *
84 * @param inputFile
85 * file containing this action
86 * @throws IOException
87 * if error occurred during the reading process from the specified
88 * input stream
89 */
90 MoveCard(InputStream inputFile) throws IOException {
91 super(inputFile);
92 controller = TestOn.deserialize(inputFile);
93 destination = inputFile.read();
94 idPosition = MToolKit.readInt16(inputFile);
95 silent = inputFile.read() == 1;
96 }
97
98 public final void replayAction(ContextEventListener context, Ability ability,
99 Wizard wizard) {
100 wizard.setVisible(true);
101 if (Wizard.optionAnswer != Wizard.BACKGROUND_OPTION) {
102
103 boolean taken = false;
104 if (!StackManager.noReplayToken.takeNoBlock()) {
105 taken = true;
106 }
107
108 final List<MCard> toBeSortedCardsCurrent = new ArrayList<MCard>();
109 final List<MCard> toBeSortedCardsNonCurrent = new ArrayList<MCard>();
110 fillList(toBeSortedCardsCurrent, toBeSortedCardsNonCurrent);
111 final int[] order = ((Arrange) wizard).order;
112 final int[] toSend = new int[2 + order.length];
113 toSend[0] = IdMessages.MOVE_ORDER_ANSWER;
114 toSend[1] = order.length;
115 if (((Arrange) wizard).owner == StackManager.currentPlayer()) {
116
117
118 for (int i = order.length; i-- > 0;) {
119 toSend[i + 2] = order[i];
120 StackManager.getTargetListAccess().remove(
121 toBeSortedCardsCurrent.get(order[i]));
122 StackManager.getTargetListAccess().add(
123 toBeSortedCardsCurrent.get(order[i]));
124 }
125
126 Log.debug("Order sent : " + Arrays.toString(toSend));
127 ConnectionManager.sendToOpponent(toSend);
128
129
130 if (toBeSortedCardsNonCurrent.size() > 1) {
131
132 Log.debug("Opponent is arranging moving cards he/she owns");
133 StackManager.currentPlayer().getOpponent().setHandedPlayer();
134
135 } else {
136
137 StackManager.actionManager.loopingIndex = StackManager
138 .getTargetListAccess().size();
139
140
141 if (taken) {
142 StackManager.noReplayToken.release();
143 }
144
145 StackManager.resolveStack();
146 }
147 } else {
148
149
150 for (int i = order.length; i-- > 0;) {
151 toSend[i + 2] = order[i];
152 StackManager.getTargetListAccess().remove(
153 toBeSortedCardsNonCurrent.get(order[i]));
154 StackManager.getTargetListAccess().add(
155 toBeSortedCardsNonCurrent.get(order[i]));
156 }
157
158
159 Log.debug("Order sent : " + Arrays.toString(toSend));
160 ConnectionManager.sendToOpponent(toSend);
161
162
163 StackManager.actionManager.loopingIndex = StackManager
164 .getTargetListAccess().size();
165
166
167 if (taken) {
168 StackManager.noReplayToken.release();
169 }
170
171 StackManager.resolveStack();
172 }
173 }
174 }
175
176 /***
177 * return the id of this action. This action has been read from the mdb file.
178 *
179 * @see Actiontype
180 * @return the id of this action
181 */
182 @Override
183 public final Actiontype getIdAction() {
184 return Actiontype.MOVE_CARD;
185 }
186
187 /***
188 * Move a card in a zone with a specified new controller.
189 *
190 * @param card
191 * the card to move.
192 * @param controller
193 * the new controller.
194 * @param destination
195 * the new zone.
196 * @param context
197 * the context of current ability.
198 * @param idPosition
199 * the new state of this card.
200 * @param ability
201 * the current ability.
202 * @param silentMode
203 * Is the silent mode is enabled while playing.
204 * @return true if the card has been moved.
205 */
206 public static boolean moveCard(MCard card, TestOn controller,
207 int destination, ContextEventListener context, int idPosition,
208 Ability ability, boolean silentMode) {
209
210
211 if (!checkTimeStamp(context, card)) {
212
213 return true;
214 }
215
216 return moveCard(card, (Player) controller.getTargetable(ability, card,
217 context, null), destination, context, idPosition, ability, silentMode);
218 }
219
220 /***
221 * @param movingCard
222 * the card to move.
223 * @param controller
224 * the new controller.
225 * @param destination
226 * the new zone.
227 * @param context
228 * the context of current ability.
229 * @param idPosition
230 * the new state of this card.
231 * @param ability
232 * the current ability.
233 * @param silentMode
234 * Is the silent mode is enabled for this move.
235 * @return true if the card has been moved.
236 */
237 public static boolean moveCard(MCard movingCard, Player controller,
238 int destination, ContextEventListener context, int idPosition,
239 Ability ability, boolean silentMode) {
240 final MCard card = (MCard) movingCard.getOriginalTargetable();
241 final int idDestination = MCard.getIdZone(destination, context);
242
243 card.registerReplacementAbilities(idDestination);
244 if (!MovedCard.tryAction(card, idDestination, controller, silentMode)) {
245
246 return false;
247 }
248
249 if (idDestination == IdZones.PLAY) {
250
251
252
253
254 final int previousIdZone = card.getIdZone();
255 card.moveCard(idDestination, controller,
256 (destination & IdZones.PLAY_TAPPED) == IdZones.PLAY_TAPPED,
257 idPosition);
258
259
260 if (card.getModifierModels() != null && previousIdZone != IdZones.PLAY) {
261 card.getModifierModels().addModifierFromModel(ability, card);
262 }
263
264
265 card.registerAbilities(IdZones.PLAY);
266
267
268 card.setIdZone(previousIdZone);
269
270
271
272
273
274
275
276
277 MovedCard.dispatchEvent(card, idDestination, controller, silentMode);
278
279
280 card.setIdZone(IdZones.PLAY);
281
282
283
284
285 } else {
286
287
288
289
290
291
292 MovedCard.dispatchEvent(card, idDestination, controller, silentMode);
293
294
295 card.registerAbilities(idDestination);
296
297
298 final int previousIdZone = card.getIdZone();
299 card.moveCard(idDestination, controller, false, idPosition);
300
301
302 if (card.getModifierModels() != null) {
303 card.getModifierModels().addStaticModifierFromModel(
304 card.getDummyAbility());
305 }
306
307
308 if (previousIdZone == IdZones.PLAY) {
309
310
311
312
313 if (!silentMode) {
314 for (MCard attachedCard : card.getAttachedCards()) {
315 Detached.dispatchEvent(attachedCard, card);
316 }
317 }
318 }
319 }
320
321 if (silentMode) {
322 for (MCard attachedCard : card.getAttachedCards()) {
323 attachedCard.setIdZone(idDestination);
324 attachedCard.clearDamages();
325 }
326 }
327
328
329 card.unregisterAbilities();
330 return true;
331 }
332
333 public boolean continueLoop(ContextEventListener context, int loopingIndex,
334 Ability ability) {
335 Log.debug("\t...continue loop, index : " + loopingIndex);
336 if (!StackManager.getInstance().getTargetedList().get(loopingIndex)
337 .isCard()
338 || moveCard((MCard) StackManager.getInstance().getTargetedList().get(
339 loopingIndex), controller, destination, context, idPosition,
340 ability, silent)) {
341 return true;
342 }
343 return false;
344 }
345
346 /***
347 * This method is called when the oponent has finished to choose the order of
348 * moves.
349 *
350 * @param order
351 * integer array corresponding to the order of cards owned by
352 * opponent.
353 */
354 public synchronized void receiveMoveOrder(int[] order) {
355 Log.debug("receiveMoveOrder : " + Arrays.toString(order));
356 final List<MCard> toBeSortedCardsCurrnt = new ArrayList<MCard>();
357 final List<MCard> toBeSortedCardsNonCurrent = new ArrayList<MCard>();
358 fillList(toBeSortedCardsCurrnt, toBeSortedCardsNonCurrent);
359 if (StackManager.currentIsYou()) {
360
361
362
363
364
365 for (int i : order) {
366 StackManager.getTargetListAccess().remove(
367 toBeSortedCardsNonCurrent.get(i));
368 StackManager.getTargetListAccess()
369 .add(toBeSortedCardsNonCurrent.get(i));
370 }
371
372 StackManager.actionManager.loopingIndex = StackManager
373 .getTargetListAccess().size();
374 StackManager.resolveStack();
375 } else {
376
377
378
379
380 for (int i = order.length; i-- > 0;) {
381 StackManager.getTargetListAccess().remove(
382 toBeSortedCardsCurrnt.get(order[i]));
383 StackManager.getTargetListAccess().add(
384 toBeSortedCardsCurrnt.get(order[i]));
385 }
386
387
388 if (toBeSortedCardsNonCurrent.size() > 1) {
389 Log.debug("You are arranging moving cards you own");
390 StackManager.currentPlayer().getOpponent().setHandedPlayer();
391 replayAction(StackManager.getInstance().getAbilityContext(),
392 StackManager.currentAbility, new Arrange(MCard.getIdZone(
393 destination, StackManager.getInstance().getAbilityContext()),
394 toBeSortedCardsNonCurrent, new int[toBeSortedCardsNonCurrent
395 .size()], StackManager.currentPlayer().getOpponent()));
396 } else {
397
398 StackManager.actionManager.loopingIndex = StackManager
399 .getTargetListAccess().size();
400 StackManager.resolveStack();
401 }
402 }
403 }
404
405 private void fillList(List<MCard> toBeSortedCardsCurrnt,
406 List<MCard> toBeSortedCardsNonCurrent) {
407
408
409
410
411 for (int i = 0; i < StackManager.getInstance().getTargetedList().size(); i++) {
412 if (StackManager.getInstance().getTargetedList().get(i).isCard()) {
413 if (((MCard) StackManager.getInstance().getTargetedList().get(i))
414 .getOwner().isCurrentPlayer()) {
415 toBeSortedCardsCurrnt.add((MCard) StackManager.getTargetListAccess()
416 .get(i));
417 } else {
418 toBeSortedCardsNonCurrent.add((MCard) StackManager
419 .getTargetListAccess().get(i));
420 }
421 }
422 }
423 }
424
425 public synchronized int getStartIndex() {
426 final int count = StackManager.getInstance().getTargetedList().size();
427 final int idDestination = MCard.getIdZone(destination, StackManager
428 .getInstance().getAbilityContext());
429 if (count > 1 && idDestination >= IdZones.FIRST_ADDITIONAL_ZONE
430 && idDestination <= IdZones.LAST_ADDITIONAL_ZONE) {
431 final List<MCard> toBeSortedCardsCurrent = new ArrayList<MCard>(count);
432 final List<MCard> toBeSortedCardsNonCurrent = new ArrayList<MCard>(count);
433 fillList(toBeSortedCardsCurrent, toBeSortedCardsNonCurrent);
434
435 if (toBeSortedCardsCurrent.size() > 1) {
436
437 final int[] order = new int[toBeSortedCardsCurrent.size()];
438 if (StackManager.currentIsYou()) {
439 Log.debug("You (current player) are arranging moving cards you own");
440 StackManager.currentPlayer().setHandedPlayer();
441 replayAction(StackManager.getInstance().getAbilityContext(),
442 StackManager.currentAbility, new Arrange(idDestination,
443 toBeSortedCardsCurrent, order, StackManager.currentPlayer()));
444 } else {
445 Log
446 .debug("Opponent (current player) is arranging moving cards he/she owns");
447 StackManager.currentPlayer().setHandedPlayer();
448 }
449 return Integer.MAX_VALUE;
450 }
451 if (toBeSortedCardsNonCurrent.size() > 1) {
452
453 if (StackManager.currentIsYou()) {
454 Log
455 .debug("Opponent (non-current player) is arranging moving cards he/she owns");
456 StackManager.currentPlayer().getOpponent().setHandedPlayer();
457 } else {
458 Log
459 .debug("You (non-current player) are arranging moving cards you own");
460 StackManager.currentPlayer().getOpponent().setHandedPlayer();
461 final int[] order = new int[toBeSortedCardsNonCurrent.size()];
462 StackManager.currentPlayer().getOpponent().setHandedPlayer();
463 replayAction(StackManager.getInstance().getAbilityContext(),
464 StackManager.currentAbility, new Arrange(idDestination,
465 toBeSortedCardsNonCurrent, order, StackManager
466 .currentPlayer().getOpponent()));
467 }
468 return Integer.MAX_VALUE;
469 }
470 }
471 return count - 1;
472 }
473
474 public void rollback(ActionContextWrapper actionContext,
475 ContextEventListener context, Ability ability) {
476 final MoveContext bContext = (MoveContext) actionContext.actionContext;
477 for (int loopingIndex = 0; loopingIndex < StackManager.getInstance()
478 .getTargetedList().size(); loopingIndex++) {
479 if (StackManager.getInstance().getTargetedList().get(loopingIndex)
480 .isCard()) {
481 final MCard card = (MCard) ((MCard) StackManager.getInstance()
482 .getTargetedList().get(loopingIndex)).getOriginalTargetable();
483 final MZone zoneSrc = card.controller.zoneManager.getContainer(card
484 .getIdZone());
485
486 zoneSrc.remove(card);
487 card.setIdZone(bContext.idZones[loopingIndex]);
488 card.controller = bContext.controllers[loopingIndex];
489 if (bContext.attachedTo[loopingIndex] != null) {
490 bContext.attachedTo[loopingIndex].add(card);
491
492 bContext.attachedTo[loopingIndex].getMUI().updateLayout();
493 }
494
495
496 card.isHighLighted = false;
497 card.reversed = card.needReverse();
498 card.getMUI().updateLayout();
499
500
501 switch (bContext.idZones[loopingIndex]) {
502 case IdZones.PLAY:
503
504 card.tap(bContext.tapPosition[loopingIndex]);
505 if ((Integer) bContext.indexes[loopingIndex].value != 0) {
506 card.controller.zoneManager.play.getCard(
507 bContext.indexes[loopingIndex].key).add(card,
508 bContext.indexes[loopingIndex].value.intValue());
509 } else {
510 card.controller.zoneManager.play.add(card,
511 bContext.indexes[loopingIndex].key);
512 }
513 break;
514 case IdZones.NOWHERE:
515
516 break;
517 default:
518 MZone zone = card.controller.zoneManager
519 .getContainer(bContext.idZones[loopingIndex]);
520 if ((Integer) bContext.indexes[loopingIndex].value != 0) {
521 zone.getCard(bContext.indexes[loopingIndex].key).add(card,
522 bContext.indexes[loopingIndex].value.intValue());
523 } else {
524 zone.add(card, bContext.indexes[loopingIndex].key);
525 }
526 }
527 } else {
528 Log
529 .warn("In MOVE-CARD action, target list contains non 'Card' object. Ignored");
530 }
531 }
532 }
533
534 public void simulate(ActionContextWrapper actionContext,
535 ContextEventListener context, Ability ability) {
536 final MoveContext bContext = new MoveContext(StackManager.getInstance()
537 .getTargetedList().size());
538 actionContext.actionContext = bContext;
539 final int idDestination = MCard.getIdZone(destination, context);
540 final boolean newIsTapped = (destination & IdZones.PLAY_TAPPED) == IdZones.PLAY_TAPPED;
541 for (int loopingIndex = StackManager.getInstance().getTargetedList().size(); loopingIndex-- > 0;) {
542 if (StackManager.getInstance().getTargetedList().get(loopingIndex)
543 .isCard()) {
544 final MCard card = (MCard) ((MCard) StackManager.getInstance()
545 .getTargetedList().get(loopingIndex)).getOriginalTargetable();
546 final Player newController = (Player) controller.getTargetable(ability,
547 card, context, null);
548 final MZone zoneSrc = card.controller.zoneManager.getContainer(card
549 .getIdZone());
550
551 bContext.tapPosition[loopingIndex] = card.tapped;
552 bContext.controllers[loopingIndex] = card.controller;
553 bContext.idZones[loopingIndex] = card.getIdZone();
554 bContext.indexes[loopingIndex] = zoneSrc.getRealIndexOf(card);
555
556 if (card.getIdZone() == IdZones.PLAY) {
557 final MCard attachedTo = card.getParent() instanceof MCard ? (MCard) card
558 .getParent()
559 : null;
560 bContext.attachedTo[loopingIndex] = attachedTo;
561 zoneSrc.remove(card);
562 card.tap(false);
563 if (attachedTo != null) {
564
565 attachedTo.getMUI().updateLayout();
566 }
567 } else if (card.getParent() != null) {
568 zoneSrc.remove(card);
569 }
570
571
572 card.controller = newController;
573 card.setIdZone(idDestination);
574 card.isHighLighted = false;
575 card.reversed = card.needReverse();
576 card.getMUI().updateLayout();
577
578
579 switch (idDestination) {
580 case IdZones.PLAY:
581
582 card.tap(newIsTapped);
583 newController.zoneManager.play.addBottom(card);
584 break;
585 case IdZones.NOWHERE:
586
587 break;
588 default:
589 switch (idPosition) {
590 case IdPositions.ON_THE_TOP:
591 newController.zoneManager.getContainer(idDestination).addTop(card);
592 break;
593 case IdPositions.ON_THE_BOTTOM:
594 default:
595 newController.zoneManager.getContainer(idDestination).addBottom(
596 card);
597 }
598 }
599 } else {
600 Log
601 .warn("In MOVE-CARD action, target list contains non 'Card' object. Ignored");
602 }
603 }
604 }
605
606 @Override
607 public String toString(Ability ability) {
608 if (destination == IdZones.PLAY_TAPPED) {
609 return LanguageManagerMDB.getString("move-"
610 + IdZones.ZONE_NAMES[IdZones.PLAY] + "-tapped");
611 }
612 if (destination == IdZones.PLAY) {
613 return LanguageManagerMDB.getString("move-"
614 + IdZones.ZONE_NAMES[IdZones.PLAY]);
615 }
616 if (idPosition == IdPositions.ON_THE_BOTTOM) {
617 return LanguageManagerMDB.getString("move-"
618 + ZoneManager.getZoneName(MCard.getIdZone(destination, null))
619 + "-bottom");
620 }
621 return LanguageManagerMDB.getString("move-"
622 + ZoneManager.getZoneName(MCard.getIdZone(destination, null)) + "-top");
623 }
624
625 /***
626 * the destination place
627 *
628 * @see IdZones
629 */
630 private final int destination;
631
632 /***
633 * The new controller
634 */
635 private final TestOn controller;
636
637 /***
638 * The position where card would be placed
639 *
640 * @see net.sf.magicproject.token.IdPositions
641 */
642 private final int idPosition;
643
644 /***
645 * Is the silent mode is enabled while playing.
646 */
647 private final boolean silent;
648
649 public int getAccessibleInt(String attribute) {
650
651 return 0;
652 }
653
654 public Targetable getAccessibleTargetable(String attribute) {
655
656 return null;
657 }
658
659 /***
660 * Shared attrribute to access to the destination zone id.
661 */
662
663 /***
664 * Shared attrribute to access to the destination controller player.
665 */
666
667 }