1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19 package net.sf.magicproject.stack;
20
21 import net.sf.magicproject.action.BackgroundMessaging;
22 import net.sf.magicproject.action.Hop;
23 import net.sf.magicproject.action.LoopAction;
24 import net.sf.magicproject.action.MAction;
25 import net.sf.magicproject.action.PayMana;
26 import net.sf.magicproject.action.Repeat;
27 import net.sf.magicproject.action.WaitTriggeredBufferChoice;
28 import net.sf.magicproject.action.context.ActionContextWrapper;
29 import net.sf.magicproject.action.context.Int;
30 import net.sf.magicproject.action.handler.ChoosenAction;
31 import net.sf.magicproject.action.handler.FollowAction;
32 import net.sf.magicproject.action.handler.InitAction;
33 import net.sf.magicproject.action.handler.Replayable;
34 import net.sf.magicproject.action.handler.RollBackAction;
35 import net.sf.magicproject.action.handler.StandardAction;
36 import net.sf.magicproject.action.listener.Waiting;
37 import net.sf.magicproject.action.listener.WaitingAbility;
38 import net.sf.magicproject.action.listener.WaitingAction;
39 import net.sf.magicproject.action.listener.WaitingCard;
40 import net.sf.magicproject.action.listener.WaitingMana;
41 import net.sf.magicproject.action.listener.WaitingPlayer;
42 import net.sf.magicproject.action.listener.WaitingTriggeredCard;
43 import net.sf.magicproject.action.target.RealTarget;
44 import net.sf.magicproject.clickable.ability.Ability;
45 import net.sf.magicproject.clickable.ability.SystemAbility;
46 import net.sf.magicproject.clickable.action.JChoosenAction;
47 import net.sf.magicproject.clickable.mana.Mana;
48 import net.sf.magicproject.clickable.targetable.card.MCard;
49 import net.sf.magicproject.clickable.targetable.card.TriggeredCard;
50 import net.sf.magicproject.clickable.targetable.player.Player;
51 import net.sf.magicproject.event.Casting;
52 import net.sf.magicproject.event.MovedCard;
53 import net.sf.magicproject.event.context.ContextEventListener;
54 import net.sf.magicproject.operation.Operation;
55 import net.sf.magicproject.token.IdCardColors;
56 import net.sf.magicproject.token.IdConst;
57 import net.sf.magicproject.token.IdZones;
58 import net.sf.magicproject.tools.Log;
59 import net.sf.magicproject.ui.MagicUIComponents;
60
61 /***
62 * The most important class of this application, and also the hardest to
63 * understand. This manager scheduls the actions handlers and the ability stack
64 * process.
65 *
66 * @author <a href="mailto:fabdouglas@users.sourceforge.net">Fabrice Daugan </a>
67 * @since 0.86
68 */
69 public class ActionManager {
70
71 /***
72 * Create a new instance from the StackManager.
73 */
74 ActionManager(Ability currentAbility, TriggeredCard triggered,
75 MAction[] additionalCost) {
76 this.currentAbility = currentAbility;
77 this.advancedEffectMode = false;
78 this.requiredMana = new int[IdCardColors.CARD_COLOR_NAMES.length];
79 if (currentAbility == null) {
80 nbCosts = 0;
81 this.effectList = null;
82 this.actionList = null;
83 } else {
84 if (additionalCost != null) {
85 this.actionList = new MAction[currentAbility.actionList().length
86 + additionalCost.length];
87 System.arraycopy(additionalCost, 0, actionList, 0,
88 additionalCost.length);
89 System.arraycopy(currentAbility.actionList(), 0, actionList,
90 additionalCost.length, currentAbility.actionList().length);
91 } else {
92 this.actionList = currentAbility.actionList();
93 }
94 this.nbCosts = this.actionList.length;
95 this.effectList = currentAbility.effectList();
96 }
97 restart(currentAbility, triggered);
98 }
99
100 void restart(Ability currentAbility, TriggeredCard triggered) {
101 hop = 1;
102 loopingIndex = 0;
103 currentIdAction = -1;
104 internalCounter = 0;
105 actionsContextsWrapper = null;
106 advancedMode = nbCosts > 0 && !currentAbility.isHidden();
107 restoreTargetList();
108 idHandler = nbCosts > 0 ? HANDLER_INITIALIZATION : HANDLER_MIDDLE;
109 }
110
111 /***
112 * Clean up this object, and initialize the previous interrupted ActionManager
113 * instance. The hop value of this one is reseted to <code>1</code>
114 *
115 * @param previousInterrupted
116 * the previous interrupted action manager.
117 */
118 void restore(ActionManager previousInterrupted) {
119 actionsContextsWrapper = null;
120 validatedActionsContextsWrapper = null;
121 currentAbility = null;
122 currentAction = null;
123
124 previousInterrupted.hop = 1;
125 }
126
127 /***
128 * Rollback a specified action index.
129 *
130 * @param actionIndex
131 * the action index to rollback.
132 */
133 private void rollbackAction(int actionIndex) {
134 final MAction action = actionList()[actionIndex];
135 final ContextEventListener context = StackManager.getInstance()
136 .getAbilityContext();
137 if (action instanceof RollBackAction) {
138 ((RollBackAction) action).rollback(actionsContextsWrapper[actionIndex],
139 context, currentAbility);
140 } else if (action instanceof Replayable) {
141 ((Replayable) action).replay(actionsContextsWrapper[actionIndex],
142 context, currentAbility);
143
144
145
146
147 }
148 }
149
150 /***
151 * Rollback all actions starting from the last executed action.
152 */
153 void rollback() {
154 if (currentIdAction >= nbCosts) {
155 currentIdAction = nbCosts - 1;
156 }
157 for (int actionIndex = currentIdAction + 1; actionIndex-- > 0;) {
158 if (rollbackPath[actionIndex]) {
159 rollbackAction(actionIndex);
160 }
161 }
162 }
163
164 /***
165 * Pause stack resolution : wait a mana source ability or a ChoosenAction
166 * activation
167 */
168 void waitActionChoice() {
169 completeChoosenAction(MagicUIComponents.choosenCostPanel
170 .playFirstUncompleted());
171 }
172
173 /***
174 * Action/Effect list.
175 *
176 * @return Action/Effect list.
177 */
178 public MAction[] actionList() {
179 if (advancedEffectMode) {
180 return effectList;
181 }
182 return actionList;
183 }
184
185 /***
186 * Restore the target list as it was at the beginning of the current bloc
187 * (cost/effects)
188 */
189 private void restoreTargetList() {
190 if (!advancedEffectMode
191 && StackManager.getInstance().getTargetedList() != null) {
192 StackManager.getInstance().getTargetedList().clear();
193 } else {
194 StackManager.getInstance().setTargetedList(savedTargeted);
195 }
196 }
197
198 /***
199 * Return the current ability's context.
200 *
201 * @return the current ability's context.
202 */
203 public ContextEventListener getAbilityContext() {
204 return StackManager.getInstance().getAbilityContext();
205 }
206
207 boolean playNextAction() {
208
209 if (waitingOnMiddle) {
210 StackManager.activePlayer().waitTriggeredBufferChoice(true);
211 return false;
212 }
213
214
215 if (loopingIndex > 0) {
216 loopingIndex--;
217 return playLoopingAction();
218 }
219
220
221 goNextAction();
222
223
224 if (!advancedMode && idHandler == HANDLER_INITIALIZATION) {
225 if (currentIdAction >= nbCosts) {
226 idHandler = HANDLER_MIDDLE;
227 currentIdAction = 0;
228 } else {
229 currentAction = actionList[currentIdAction];
230 return playCurrentAction();
231 }
232 } else if (advancedMode && idHandler == HANDLER_INITIALIZATION) {
233
234
235 while (currentIdAction < nbCosts) {
236 currentAction = actionList()[currentIdAction];
237 if (currentAction instanceof InitAction) {
238 if (!(((InitAction) currentAction).init(getActionContext(),
239 getAbilityContext(), currentAbility))) {
240 return false;
241 }
242 } else if (currentAction instanceof ChoosenAction) {
243
244 getActionContext();
245 }
246 goNextAction();
247 }
248
249
250 currentIdAction = 0;
251 idHandler = HANDLER_AD_SERIALIZATION;
252
253
254 restoreTargetList();
255
256
257 while (currentIdAction < nbCosts) {
258 currentAction = actionList()[currentIdAction];
259 rollbackPath[currentIdAction] = true;
260
261 if (!(currentAction instanceof Repeat)) {
262 if (currentAction instanceof Hop) {
263 ((Hop) currentAction).replay(getActionContext(), StackManager
264 .getInstance().getAbilityContext(), currentAbility);
265 goNextAction();
266 } else if (currentAction instanceof ChoosenAction) {
267
268 return MagicUIComponents.choosenCostPanel.reset(StackManager
269 .getInstance().getSourceCard(), actionsContextsWrapper);
270 } else if (currentAction instanceof InitAction) {
271 ((InitAction) currentAction).replay(getActionContext(),
272 getAbilityContext(), currentAbility);
273 } else if (currentAction instanceof FollowAction) {
274 ((FollowAction) currentAction).simulate(getActionContext(),
275 getAbilityContext(), currentAbility);
276 } else {
277 throw new InternalError("Unsupported action (id=" + currentIdAction
278 + ") for serialization purpose : " + currentAction.getClass());
279 }
280 }
281 goNextAction();
282 }
283
284 idHandler = HANDLER_AD_PREPARE_REPLAY;
285 } else if (advancedMode && idHandler == HANDLER_AD_SERIALIZATION) {
286
287 if (!(currentAction instanceof ChoosenAction)) {
288 throw new InternalError(
289 "The serialization handler must start to process a ChoosenAction,class="
290 + currentAction.getClass().getName() + " : " + currentAction);
291 }
292
293 if (currentIdAction < nbCosts) {
294
295 while (currentIdAction < nbCosts) {
296 currentAction = actionList()[currentIdAction];
297 rollbackPath[currentIdAction] = true;
298
299 if (!(currentAction instanceof Repeat)) {
300 if (currentAction instanceof Hop) {
301 ((Hop) currentAction).replay(getActionContext(), StackManager
302 .getInstance().getAbilityContext(), currentAbility);
303 } else if (currentAction instanceof FollowAction) {
304 ((FollowAction) currentAction).simulate(getActionContext(),
305 getAbilityContext(), currentAbility);
306 } else if (currentAction instanceof RealTarget) {
307
308 if (!getActionContext().isCompleted()) {
309 throw new InternalError(
310 "RealTarget should be completed in Serialization");
311 }
312 getActionContext().done++;
313 ((InitAction) currentAction).replay(getActionContext(),
314 getAbilityContext(), currentAbility);
315 } else if (currentAction instanceof ChoosenAction) {
316 if (!getActionContext().isCompleted()) {
317
318 waitActionChoice();
319 return false;
320 }
321 } else if (currentAction instanceof InitAction) {
322 ((InitAction) currentAction).replay(getActionContext(),
323 getAbilityContext(), currentAbility);
324 }
325 }
326 goNextAction();
327 }
328 }
329
330 idHandler = HANDLER_AD_PREPARE_REPLAY;
331 }
332
333 if (advancedMode && idHandler == HANDLER_AD_PREPARE_REPLAY) {
334
335 currentIdAction = nbCosts;
336 rollback();
337 idHandler = HANDLER_AD_REPLAY;
338 currentIdAction = 0;
339 }
340
341 if (advancedMode && idHandler == HANDLER_PLAY_INIT) {
342
343 while (currentIdAction < nbCosts) {
344 currentAction = actionList()[currentIdAction];
345 if (currentAction instanceof ChoosenAction) {
346
347 if (!((ChoosenAction) currentAction).choose(getActionContext(),
348 getAbilityContext(), currentAbility)) {
349
350 return false;
351 }
352 } else if (currentAction instanceof InitAction) {
353
354 ((InitAction) currentAction).replay(getActionContext(), StackManager
355 .getInstance().getAbilityContext(), currentAbility);
356 } else {
357
358 if (!playCurrentAction()) {
359
360 return false;
361 }
362 }
363 goNextAction();
364 }
365
366 idHandler = HANDLER_MIDDLE;
367 }
368
369 if (advancedMode && idHandler == HANDLER_AD_REPLAY) {
370 while (currentIdAction < nbCosts) {
371 currentAction = actionList()[currentIdAction];
372 if (currentAction instanceof Repeat) {
373 if (currentIdAction + 1 < actionList().length
374 && !(actionList()[currentIdAction + 1] instanceof InitAction)
375 && !(actionList()[currentIdAction + 1] instanceof ChoosenAction)) {
376 ((Repeat) currentAction).replayOnDemand(getActionContext(),
377 getAbilityContext(), currentAbility);
378 }
379 } else {
380
381
382
383
384 if (currentAction instanceof InitAction) {
385
386 if (!((InitAction) currentAction).replay(getActionContext(),
387 getAbilityContext(), currentAbility)) {
388 return false;
389 }
390 } else if (currentAction instanceof ChoosenAction) {
391
392 ((ChoosenAction) currentAction).replay(getActionContext(),
393 getAbilityContext(), currentAbility);
394 } else {
395
396 if (!playCurrentAction()) {
397 return false;
398 }
399 }
400 }
401 goNextAction();
402 }
403 idHandler = HANDLER_MIDDLE;
404 }
405
406 if (idHandler == HANDLER_MIDDLE) {
407 if (advancedEffectMode) {
408 idHandler = HANDLER_EFFECTS;
409 } else {
410 if (currentIdAction > nbCosts) {
411
412
413
414 Log
415 .debug("WARNING : action id is greater than the amount of actions in cost part of "
416 + currentAbility);
417 }
418
419 if (StackManager.processGameLost()) {
420
421 StackManager.gameLostProceed = true;
422 return false;
423 }
424 rollbackPath = null;
425
426 validatedActionsContextsWrapper = actionsContextsWrapper;
427 actionsContextsWrapper = null;
428 advancedMode = false;
429
430
431 if (StackManager.triggered != null || currentAbility.isAutoResolve()) {
432
433
434
435
436 if (!currentAbility.isHidden()) {
437 StackManager.actionManager.currentAction = WaitTriggeredBufferChoice
438 .getInstance();
439 if (!StackManager.activePlayer().processHiddenTriggered()
440 && !StackManager.nonActivePlayer().processHiddenTriggered()) {
441 if (StackManager.tokenCard instanceof MCard) {
442 Casting.dispatchEvent((MCard) StackManager.tokenCard);
443 }
444 } else {
445 return false;
446 }
447 }
448 prepareEffects();
449 currentIdAction = 0;
450 } else {
451
452
453
454
455
456
457
458 prepareEffects();
459 currentIdAction = -1;
460 MCard currentCard = (MCard) StackManager.tokenCard;
461 if (StackManager.triggered == null
462 && currentCard.getIdZone() == IdZones.STACK) {
463
464
465
466
467 currentCard.setIdZone(StackManager.previousPlace);
468 MovedCard.dispatchEvent(currentCard, IdZones.STACK, currentCard
469 .getController(), false);
470 currentCard.setIdZone(IdZones.STACK);
471 }
472 Casting.dispatchEvent(currentCard);
473 waitingOnMiddle = true;
474
475 StackManager.activePlayer().waitTriggeredBufferChoice(true);
476 waitingOnMiddle = false;
477 return false;
478 }
479 }
480 }
481
482 if (idHandler == HANDLER_EFFECTS) {
483
484
485 if (currentIdAction == effectList.length) {
486
487 StackManager.getInstance().finishSpell();
488 } else {
489
490 if (advancedEffectMode) {
491 throw new IllegalStateException(
492 "In HANDLER_EFFECTS & advancedEffectMode with uncompleted ability");
493 }
494 if (currentIdAction == 0) {
495
496 if (!currentAbility.recheckTargets()) {
497
498 StackManager.getInstance().abortion(StackManager.tokenCard,
499 currentAbility);
500 return false;
501 }
502
503
504 if (!(currentAbility instanceof SystemAbility)) {
505 while (currentIdAction < effectList.length) {
506 if (effectList[currentIdAction] instanceof PayMana
507 || effectList[currentIdAction] instanceof RealTarget) {
508
509 advancedEffectMode = true;
510 advancedMode = true;
511 savedTargeted = StackManager.getInstance().getTargetedList()
512 .cloneList();
513 nbCosts = effectList.length;
514 restart(currentAbility, StackManager.triggered);
515 playNextAction();
516 return false;
517 }
518 currentIdAction++;
519 }
520 currentIdAction = 0;
521 }
522 }
523 currentAction = effectList[currentIdAction];
524 return playCurrentAction();
525 }
526 }
527 return false;
528 }
529
530 private void prepareEffects() {
531 idHandler = HANDLER_EFFECTS;
532 }
533
534 private boolean playLoopingAction() {
535 if (StackManager.triggered != null) {
536 return ((LoopAction) currentAction).continueLoop(StackManager
537 .getInstance().getAbilityContext(), loopingIndex, currentAbility);
538 }
539 return ((LoopAction) currentAction).continueLoop(null, loopingIndex,
540 currentAbility);
541 }
542
543 /***
544 * Play the current action
545 *
546 * @return true if the stack resolution can continue. False if the stack
547 * resolution is broken.
548 */
549 private boolean playCurrentAction() {
550 if (currentAction instanceof LoopAction) {
551 if (currentAction instanceof BackgroundMessaging) {
552
553 final int loopingIndexTMP = ((LoopAction) currentAction)
554 .getStartIndex();
555 if (loopingIndexTMP == Integer.MAX_VALUE) {
556 return false;
557 }
558 loopingIndex = loopingIndexTMP;
559 } else {
560 loopingIndex = ((LoopAction) currentAction).getStartIndex();
561 }
562 if (loopingIndex > -1) {
563 return playLoopingAction();
564 }
565 return true;
566 }
567
568 return ((StandardAction) currentAction).play(StackManager.getInstance()
569 .getAbilityContext(), currentAbility);
570 }
571
572 /***
573 * Send the message "manualSkip" to the active action of play. If the
574 * "skip/cancel" event is accepted by this action, we resolve the stack.
575 */
576 public void manualSkip() {
577 if (currentAction instanceof Waiting
578 && ((Waiting) currentAction).manualSkip()) {
579 StackManager.resolveStack();
580 }
581 }
582
583 /***
584 * Will repeat the next action <code>nbNextAction</code> times
585 *
586 * @param nbNextAction
587 * is the times that the next action will be repeated
588 */
589 public void setRepeat(int nbNextAction) {
590 internalCounter = nbNextAction;
591 currentIdAction += 1;
592 hop = 1;
593 }
594
595 /***
596 * The current action will become the current action index + "hop". The "hop"
597 * value is reseted to 1
598 */
599 private void goNextAction() {
600 if (internalCounter > 0) {
601
602 internalCounter--;
603 } else {
604 currentIdAction += hop;
605 }
606 hop = 1;
607 }
608
609 /***
610 * Returns the context associated to the specified action id.
611 *
612 * @return the context associated to the specified action id.
613 */
614 public ActionContextWrapper getActionContextNull() {
615 if (actionsContextsWrapper == null) {
616 return null;
617 }
618 return actionsContextsWrapper[currentIdAction];
619 }
620
621 /***
622 * Returns the context associated to the specified action id.
623 *
624 * @param contextId
625 * the requestion acion id.
626 * @return the context associated to the specified action id.
627 */
628 public ActionContextWrapper getActionContext(int contextId) {
629 if (actionsContextsWrapper == null) {
630
631 actionsContextsWrapper = new ActionContextWrapper[actionList().length];
632
633
634 rollbackPath = new boolean[nbCosts];
635 }
636 if (actionsContextsWrapper[contextId] == null) {
637
638 if (contextId > 0 && actionsContextsWrapper[contextId - 1] != null
639 && actionsContextsWrapper[contextId - 1].action instanceof Repeat) {
640 actionsContextsWrapper[contextId] = new ActionContextWrapper(contextId,
641 actionList()[contextId], null,
642 ((Int) actionsContextsWrapper[contextId - 1].actionContext)
643 .getInt());
644 } else {
645 actionsContextsWrapper[contextId] = new ActionContextWrapper(contextId,
646 actionList()[contextId], null, 1);
647 }
648 }
649 return actionsContextsWrapper[contextId];
650 }
651
652 /***
653 * Returns the context associated to the current action id.
654 *
655 * @return the context associated to the current action id.
656 */
657 public ActionContextWrapper getActionContext() {
658 return getActionContext(currentIdAction);
659 }
660
661 /***
662 * Returns the whole action contexts of current step : cost OR effect.
663 *
664 * @return the whole action contexts of current step : cost OR effect.
665 * @see #getAllActionContexts()
666 */
667 public ActionContextWrapper[] getAllActionContexts() {
668 return actionsContextsWrapper;
669 }
670
671 /***
672 * Returns the whole action contexts : cost AND effect.
673 *
674 * @return the whole action contexts : cost AND effect.
675 * @see #getAllActionContexts()
676 */
677 public ActionContextWrapper[] getTotalActionContexts() {
678 if (validatedActionsContextsWrapper != null) {
679 if (actionsContextsWrapper != null) {
680 final ActionContextWrapper[] total = new ActionContextWrapper[validatedActionsContextsWrapper.length
681 + actionsContextsWrapper.length];
682 System.arraycopy(validatedActionsContextsWrapper, 0, total, 0,
683 validatedActionsContextsWrapper.length);
684 System.arraycopy(actionsContextsWrapper, 0, total,
685 validatedActionsContextsWrapper.length,
686 actionsContextsWrapper.length);
687 } else {
688 return validatedActionsContextsWrapper;
689 }
690 } else if (actionsContextsWrapper != null) {
691 return actionsContextsWrapper;
692 }
693 return new ActionContextWrapper[0];
694 }
695
696 /***
697 * Set the jump to do for the next action. If the specified "hop" is 0, the
698 * next action would be the current one.
699 *
700 * @param hop
701 * the jump to do for the next action.
702 */
703 public void setHop(int hop) {
704 if (hop == IdConst.ALL) {
705 currentIdAction = effectList.length - 1;
706 this.hop = 1;
707 prepareEffects();
708 } else if (idHandler == HANDLER_EFFECTS || advancedEffectMode) {
709 this.hop = hop;
710 } else if (hop + currentIdAction >= actionList().length) {
711 this.hop = hop + 1;
712 prepareEffects();
713 } else {
714 this.hop = hop;
715 }
716 internalCounter = 0;
717 loopingIndex = -1;
718 }
719
720 /***
721 * Called to specify the player choice for the current action
722 *
723 * @param card
724 * the clicked card by the active player for the current action
725 * @return true if this click has been managed. Return false if this click has
726 * been ignored
727 */
728 public boolean clickOn(MCard card) {
729 if (currentAction instanceof WaitingCard) {
730 return ((WaitingCard) currentAction).clickOn(card);
731 }
732 return false;
733 }
734
735 /***
736 * Called to specify the player choice for the current action
737 *
738 * @param player
739 * the clicked player by the active player for the current action
740 * @return true if this click has been managed. Return false if this click has
741 * been ignored
742 */
743 public boolean clickOn(Player player) {
744 if (currentAction instanceof WaitingPlayer) {
745 return ((WaitingPlayer) currentAction).clickOn(player);
746 }
747 return false;
748 }
749
750 /***
751 * Called to specify the player choice for the current action
752 *
753 * @param triggeredCard
754 * the clicked triggered card by the active player for the current
755 * action
756 * @return true if this click has been managed. Return false if this click has
757 * been ignored
758 */
759 public boolean clickOn(TriggeredCard triggeredCard) {
760 if (currentAction instanceof WaitingTriggeredCard) {
761 return ((WaitingTriggeredCard) currentAction).clickOn(triggeredCard);
762 }
763 return false;
764 }
765
766 /***
767 * Called to specify the player choice for the current action
768 *
769 * @param ability
770 * the clicked ability by the active player for the current action
771 * @return true if this click has been managed. Return false if this click has
772 * been ignored
773 */
774 public boolean clickOn(Ability ability) {
775 if (currentAction instanceof WaitingAbility) {
776 return ((WaitingAbility) currentAction).clickOn(ability);
777 }
778 return false;
779 }
780
781 /***
782 * Called to specify the player choice for the current action
783 *
784 * @param mana
785 * the clicked mana by the active player for the current action
786 * @return true if this click has been managed. Return false if this click has
787 * been ignored
788 */
789 public boolean clickOn(Mana mana) {
790 if (currentAction instanceof WaitingMana) {
791 return ((WaitingMana) currentAction).clickOn(mana);
792 }
793 return false;
794 }
795
796 /***
797 * Called to specify the player choice for the current action.
798 *
799 * @param action
800 * the clicked action by the active player for the current action
801 * @return true if this click has been managed. Return false if this click has
802 * been ignored
803 */
804 public boolean clickOn(JChoosenAction action) {
805 if (currentAction instanceof WaitingAction) {
806 return ((WaitingAction) currentAction).clickOn(action);
807 }
808 return false;
809 }
810
811 /***
812 * This function should be called by the 'clickOn' caller in case of the
813 * specified card has been handled during the checking validity of this click
814 * in the <code>clickOn(Card)</code> function. <br>
815 * <ul>
816 * The calls chain is :
817 * <li>actionListener call clickOn(Card)
818 * <li>if returned value is false we give hand to the player and exit, else
819 * we continue
820 * <li>actionListener call succeedClickOn(Card)
821 * </ul>
822 *
823 * @param card
824 * the card that was clicked and successfuly handled by the
825 * <code>clickOn(Card)</code> function.
826 * @see #clickOn(MCard)
827 */
828 public void succeedClickOn(MCard card) {
829 Player.unsetHandedPlayer();
830 final boolean res = ((WaitingCard) currentAction).succeedClickOn(card);
831 StackManager.actionManager.completeChoosenAction(res);
832 }
833
834 /***
835 * This function should be called by the 'clickOn' caller in case of the
836 * specified ability has been handled during the checking validity of this
837 * click in the <code>clickOn(Ability)</code> function. <br>
838 * <ul>
839 * The calls chain is :
840 * <li>actionListener call clickOn(Ability)
841 * <li>if returned value is false we give hand to the player and exit, else
842 * we continue
843 * <li>actionListener call succeedClickOn(Ability)
844 * </ul>
845 *
846 * @param ability
847 * the ability that was clicked and successfuly handled by the
848 * <code>clickOn(Ability)</code> function.
849 * @see #clickOn(Ability)
850 */
851 public void succeedClickOn(Ability ability) {
852 Player.unsetHandedPlayer();
853 if (((WaitingAbility) currentAction).succeedClickOn(ability)) {
854 StackManager.resolveStack();
855 }
856 }
857
858 /***
859 * This function should be called by the 'clickOn' caller in case of the
860 * specified triggered card has been handled during the checking validity of
861 * this click in the <code>clickOn(MTriggeredCard)</code> function. <br>
862 * <ul>
863 * The calls chain is :
864 * <li>actionListener call clickOn(MTriggeredCard)
865 * <li>if returned value is false we give hand to the player and exit, else
866 * we continue
867 * <li>actionListener call succeedClickOn(MTriggeredCard)
868 * </ul>
869 *
870 * @param card
871 * the triggered card that was clicked and successfuly handled by the
872 * <code>clickOn(MTriggeredCard)</code> function.
873 * @see #clickOn(TriggeredCard)
874 */
875 public void succeedClickOn(TriggeredCard card) {
876 Player.unsetHandedPlayer();
877 final boolean res = ((WaitingTriggeredCard) currentAction)
878 .succeedClickOn(card);
879 StackManager.actionManager.completeChoosenAction(res);
880 }
881
882 /***
883 * This function should be called by the 'clickOn' caller in case of the
884 * specified mana has been handled during the checking validity of this click
885 * in the <code>clickOn(MMana)</code> function. <br>
886 * <ul>
887 * The calls chain is :
888 * <li>actionListener call clickOn(MMana)
889 * <li>if returned value is false we give hand to the player and exit, else
890 * we continue
891 * <li>actionListener call succeedClickOn(MMana)
892 * </ul>
893 *
894 * @param mana
895 * the mana that was clicked and successfuly handled by the
896 * <code>clickOn(MMana)</code> function.
897 * @see #clickOn(Mana)
898 */
899 public void succeedClickOn(Mana mana) {
900 Player.unsetHandedPlayer();
901 final boolean res = ((WaitingMana) currentAction).succeedClickOn(mana);
902 StackManager.actionManager.completeChoosenAction(res);
903 }
904
905 /***
906 * This function should be called by the 'clickOn' caller in case of the
907 * specified player has been handled during the checking validity of this
908 * click in the <code>clickOn(Player)</code> function. <br>
909 * <ul>
910 * The calls chain is :
911 * <li>actionListener call clickOn(Player)
912 * <li>if returned value is false we give hand to the player and exit, else
913 * we continue
914 * <li>actionListener call succeedClickOn(Player)
915 * </ul>
916 *
917 * @param player
918 * the player that was clicked and successfuly handled by the
919 * <code>clickOn(Player)</code> function.
920 * @see #clickOn(Player)
921 */
922 public void succeedClickOn(Player player) {
923 Player.unsetHandedPlayer();
924 final boolean res = ((WaitingPlayer) currentAction).succeedClickOn(player);
925 StackManager.actionManager.completeChoosenAction(res);
926 }
927
928 /***
929 * This function should be called by the 'clickOn' caller in case of the
930 * specified player has been handled during the checking validity of this
931 * click in the <code>clickOn(JChoosenAction)</code> function. <br>
932 * <ul>
933 * The calls chain is :
934 * <li>actionListener call clickOn(JChoosenAction)
935 * <li>if returned value is false we give hand to the player and exit, else
936 * we continue
937 * <li>actionListener call succeedClickOn(JChoosenAction)
938 * </ul>
939 *
940 * @param action
941 * the action that was clicked and successfuly handled by the
942 * <code>clickOn(JChoosenAction)</code> function.
943 * @see #clickOn(JChoosenAction)
944 */
945 public void succeedClickOn(JChoosenAction action) {
946 Player.unsetHandedPlayer();
947 final boolean res = ((WaitingAction) currentAction).succeedClickOn(action);
948 StackManager.actionManager.completeChoosenAction(res);
949 }
950
951 /***
952 * Complete the current action.
953 *
954 * @param unitaryCompleted
955 * is this action is completed as many times as required?
956 */
957 public void completeChoosenAction(boolean unitaryCompleted) {
958 if (advancedMode) {
959 if (unitaryCompleted) {
960
961 if (MagicUIComponents.choosenCostPanel.completeAction(
962 StackManager.currentAbility, StackManager.getInstance()
963 .getAbilityContext(), getActionContext())) {
964 internalCounter = 0;
965
966 StackManager.resolveStack();
967 } else {
968
969 if (currentAction instanceof InitAction) {
970 if (((InitAction) currentAction).init(getActionContext(),
971 getAbilityContext(), StackManager.currentAbility)) {
972 StackManager.resolveStack();
973 }
974 } else if (((ChoosenAction) currentAction).choose(getActionContext(),
975 getAbilityContext(), StackManager.currentAbility)) {
976 StackManager.resolveStack();
977 }
978 }
979 } else {
980
981
982 StackManager.enableAbort();
983 }
984 } else if (unitaryCompleted) {
985 StackManager.resolveStack();
986 }
987 }
988
989 /***
990 * Reactivate the current action
991 */
992 public void reactivate() {
993 if (!(currentAction instanceof Waiting)
994 || !(currentAction instanceof ChoosenAction)) {
995 Log
996 .error(
997 "The serialization handler must start to process a Waiting/ChoosenAction,class="
998 + currentAction.getClass().getName() + " : " + currentAction,
999 new InternalError());
1000
1001 playNextAction();
1002 } else {
1003 ((ChoosenAction) currentAction).choose(getActionContext(), StackManager
1004 .getInstance().getAbilityContext(), currentAbility);
1005 MagicUIComponents.choosenCostPanel.initUI(StackManager.getInstance()
1006 .getSourceCard(), actionsContextsWrapper);
1007 }
1008 }
1009
1010 /***
1011 * Update the required mana of current ability.
1012 *
1013 * @param op
1014 * the operation to apply to the required mana.
1015 * @param reg
1016 * the register index to update
1017 * @param value
1018 * the value to update.
1019 */
1020 public void updateRequiredMana(Operation op, int reg, int value) {
1021 requiredMana[reg] = op.process(requiredMana[reg], value);
1022 }
1023
1024 /***
1025 * Update the given required mana with the global required mana.
1026 *
1027 * @param requiredMana
1028 * the required mana to update.
1029 */
1030 public void updateRequiredMana(int[] requiredMana) {
1031 for (int i = this.requiredMana.length; i-- > 0;) {
1032 requiredMana[i] += this.requiredMana[i];
1033 if (requiredMana[i] < 0) {
1034 this.requiredMana[i] = requiredMana[i];
1035 requiredMana[i] = 0;
1036 } else {
1037 this.requiredMana[i] = 0;
1038 }
1039 }
1040 }
1041
1042 /***
1043 * like M68k processor bra instruction, indicates the jump to do to go to the
1044 * next action. This jump is equal to 1 for normal ability, but for hop=3, the
1045 * 2 actions following the current one will be skipped. Hop=0 means infinite
1046 * loop.
1047 *
1048 * @since 0.52
1049 */
1050 public int hop;
1051
1052 /***
1053 * the current ability in the stack
1054 */
1055 public Ability currentAbility;
1056
1057 /***
1058 * the active action managing the next player action
1059 */
1060 public MAction currentAction;
1061
1062 /***
1063 * This tag indicate the index of current action using a 'for' or 'while'
1064 * instruction, generating several events. While this tag is greater than 0,
1065 * instead of resolving stacks, the current action still called as if the
1066 * setRepeat() method was called.
1067 */
1068 public int loopingIndex;
1069
1070 /***
1071 * The index of current action
1072 */
1073 public int currentIdAction;
1074
1075 /***
1076 * The current context of actions.
1077 */
1078 private ActionContextWrapper[] actionsContextsWrapper = null;
1079
1080 /***
1081 * The validated context of actions. Is <code>null</code> while no contextes
1082 * have been totally validated for a cost part, so arriving to the
1083 * HANDLER_MIDDLE handler.
1084 */
1085 private ActionContextWrapper[] validatedActionsContextsWrapper = null;
1086
1087 /***
1088 * Are we waiting for triggered/activated choice
1089 */
1090 public boolean waitingOnMiddle;
1091
1092 /***
1093 * counter used for draw/targets count spells like a loop "for(i = 0 ... )"
1094 */
1095 private int internalCounter = 0;
1096
1097 private TargetedList savedTargeted;
1098
1099 /***
1100 * Values are :<br>
1101 * HANDLER_INITIALIZATION<br>
1102 * HANDLER_AD_SERIALIZATION<br>
1103 * HANDLER_AD_PREPARE_REPLAY<br>
1104 * HANDLER_AD_REPLAY<br>
1105 * HANDLER_PLAY_INIT<br>
1106 * HANDLER_MIDDLE<br>
1107 * HANDLER_EFFECTS<br>
1108 */
1109 public int idHandler = 0;
1110
1111 /***
1112 * The first handler : initialization.
1113 */
1114 public static final int HANDLER_INITIALIZATION = 0;
1115
1116 /***
1117 * The secand handler : serialization.
1118 */
1119 private static final int HANDLER_AD_SERIALIZATION = 1;
1120
1121 /***
1122 * The third handler : prepare replay.
1123 */
1124 private static final int HANDLER_AD_PREPARE_REPLAY = 2;
1125
1126 /***
1127 * The final handler : replay.
1128 */
1129 public static final int HANDLER_AD_REPLAY = 3;
1130
1131 /***
1132 * The old init handler without rollback use.
1133 */
1134 private static final int HANDLER_PLAY_INIT = 4;
1135
1136 /***
1137 * The old middle handler without rollback use.
1138 */
1139 private static final int HANDLER_MIDDLE = 5;
1140
1141 /***
1142 * The old effects handler without rollback use.
1143 */
1144 private static final int HANDLER_EFFECTS = 6;
1145
1146 /***
1147 * Is the advanced mode is used there for cost part.
1148 */
1149 public boolean advancedMode;
1150
1151 private int nbCosts;
1152
1153 private boolean[] rollbackPath;
1154
1155 /***
1156 * The cost part of current ability.
1157 */
1158 private final MAction[] actionList;
1159
1160 /***
1161 * The effect part of current ability.
1162 */
1163 private final MAction[] effectList;
1164
1165 /***
1166 * Is the advanced mode is used there for effects part.
1167 */
1168 public boolean advancedEffectMode;
1169
1170 /***
1171 * Additional required mana. May contain some negative amount.
1172 */
1173 public final int[] requiredMana;
1174 }