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