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