package magic.model.event; import java.util.Collections; import java.util.List; import magic.ai.ArtificialScoringSystem; import magic.model.*; import magic.model.action.MagicCardAction; import magic.model.action.MagicCardOnStackAction; import magic.model.action.MagicItemOnStackAction; import magic.model.action.MagicPermanentAction; import magic.model.action.MagicPlayerAction; import magic.model.action.MagicTargetAction; import magic.model.choice.MagicCardChoiceResult; import magic.model.choice.MagicChoice; import magic.model.choice.MagicDeclareAttackersResult; import magic.model.choice.MagicDeclareBlockersResult; import magic.model.choice.MagicExcludeResult; import magic.model.choice.MagicPayManaCostResult; import magic.model.choice.MagicPlayChoiceResult; import magic.model.choice.MagicTargetChoice; import magic.model.stack.MagicCardOnStack; import magic.model.stack.MagicItemOnStack; import magic.model.target.MagicDefaultTargetPicker; import magic.model.target.MagicTarget; import magic.model.target.MagicTargetNone; import magic.model.target.MagicTargetPicker; public class MagicEvent implements MagicCopyable { public static final Object[] NO_CHOICE_RESULTS = new Object[0]; public static final MagicCopyable NO_REF = new MagicInteger(-1); static class MagicInteger implements MagicCopyable { public final int value; public MagicInteger(final int v) { value = v; } @Override public MagicCopyable copy(final MagicCopyMap copyMap) { return this; } @Override public int hashCode() { return value; } @Override public String toString() { return Integer.toString(value); } } public static final MagicEvent NONE = new MagicEvent(MagicSource.NONE, MagicPlayer.NONE, NO_REF, MagicEventAction.NONE, "") { @Override public boolean isValid() { return false; } @Override public MagicEvent copy(final MagicCopyMap copyMap) { return this; } @Override public final MagicTargetChoice getTargetChoice() { throw new RuntimeException("getTargetChoice called on MagicEvent.NONE"); } }; private final MagicSource source; private final MagicPlayer player; private final MagicChoice choice; private final MagicTargetPicker<?> targetPicker; private final MagicEventAction action; private final String description; private final MagicCopyable ref; private Object[] chosen; private MagicTarget chosenTarget; public MagicEvent( final MagicSource source, final MagicPlayer player, final MagicChoice choice, final MagicTargetPicker<?> targetPicker, final MagicCopyable ref, final MagicEventAction action, final String description ) { assert source != null : "source is null"; assert player != null : "player is null"; assert choice != null : "choice is null"; assert targetPicker != null : "targetPicker is null"; assert ref != null : "ref is null"; assert action != null : "action is null"; assert description != null : "description is null"; this.source=source; this.player=player; this.choice=choice; this.targetPicker=targetPicker; this.ref=ref; this.action=action; this.description=MagicMessage.replaceName(description,source,player,ref); } public MagicEvent( final MagicSource source, final MagicPlayer player, final MagicChoice choice, final MagicTargetPicker<?> targetPicker, final int ref, final MagicEventAction action, final String description ) { this(source,player,choice,targetPicker,new MagicInteger(ref),action,description); } public MagicEvent( final MagicSource source, final MagicChoice choice, final MagicTargetPicker<?> targetPicker, final MagicCopyable ref, final MagicEventAction action, final String description ) { this(source,source.getController(),choice,targetPicker,ref,action,description); } public MagicEvent( final MagicSource source, final MagicChoice choice, final MagicTargetPicker<?> targetPicker, final int ref, final MagicEventAction action, final String description ) { this(source,source.getController(),choice,targetPicker,new MagicInteger(ref),action,description); } public MagicEvent( final MagicSource source, final MagicPlayer player, final MagicChoice choice, final MagicTargetPicker<?> targetPicker, final MagicEventAction action, final String description ) { this(source,player,choice,targetPicker,NO_REF,action,description); } public MagicEvent( final MagicSource source, final MagicChoice choice, final MagicTargetPicker<?> targetPicker, final MagicEventAction action, final String description ) { this(source,source.getController(),choice,targetPicker,NO_REF,action,description); } public MagicEvent( final MagicSource source, final MagicPlayer player, final MagicChoice choice, final MagicCopyable ref, final MagicEventAction action, final String description ) { this(source,player,choice,MagicDefaultTargetPicker.create(),ref,action,description); } public MagicEvent( final MagicSource source, final MagicPlayer player, final MagicChoice choice, final int ref, final MagicEventAction action, final String description ) { this(source,player,choice,MagicDefaultTargetPicker.create(),new MagicInteger(ref),action,description); } public MagicEvent( final MagicSource source, final MagicChoice choice, final MagicCopyable ref, final MagicEventAction action, final String description ) { this(source,source.getController(),choice,MagicDefaultTargetPicker.create(),ref,action,description); } public MagicEvent( final MagicSource source, final MagicChoice choice, final int ref, final MagicEventAction action, final String description ) { this(source,source.getController(),choice,MagicDefaultTargetPicker.create(),new MagicInteger(ref),action,description); } public MagicEvent( final MagicSource source, final MagicPlayer player, final MagicChoice choice, final MagicEventAction action, final String description ) { this(source,player,choice,MagicDefaultTargetPicker.create(),NO_REF,action,description); } public MagicEvent( final MagicSource source, final MagicChoice choice, final MagicEventAction action, final String description ) { this(source,source.getController(),choice,MagicDefaultTargetPicker.create(),NO_REF,action,description); } public MagicEvent( final MagicSource source, final MagicPlayer player, final MagicCopyable ref, final MagicEventAction action, final String description ) { this(source,player,MagicChoice.NONE,MagicDefaultTargetPicker.create(),ref,action,description); } public MagicEvent( final MagicSource source, final MagicPlayer player, final int ref, final MagicEventAction action, final String description ) { this(source,player,MagicChoice.NONE,MagicDefaultTargetPicker.create(),new MagicInteger(ref),action,description); } public MagicEvent( final MagicSource source, final MagicCopyable ref, final MagicEventAction action, final String description ) { this(source,source.getController(),MagicChoice.NONE,MagicDefaultTargetPicker.create(),ref,action,description); } public MagicEvent( final MagicSource source, final int ref, final MagicEventAction action, final String description ) { this(source,source.getController(),MagicChoice.NONE,MagicDefaultTargetPicker.create(),new MagicInteger(ref),action,description); } public MagicEvent( final MagicSource source, final MagicPlayer player, final MagicEventAction action, final String description ) { this(source,player,MagicChoice.NONE,MagicDefaultTargetPicker.create(),NO_REF,action,description); } public MagicEvent( final MagicSource source, final MagicEventAction action, final String description ) { this(source,source.getController(),MagicChoice.NONE,MagicDefaultTargetPicker.create(),NO_REF,action,description); } private MagicEvent(final MagicCopyMap copyMap, final MagicEvent sourceEvent) { copyMap.put(sourceEvent,this); source = copyMap.copy(sourceEvent.source); player = copyMap.copy(sourceEvent.player); choice = sourceEvent.choice; targetPicker = sourceEvent.targetPicker; ref = copyMap.copy(sourceEvent.ref); action = sourceEvent.action; description = sourceEvent.description; } @Override public MagicEvent copy(final MagicCopyMap copyMap) { return new MagicEvent(copyMap, this); } public boolean isValid() { return true; } public final MagicSource getSource() { return source; } public final MagicPlayer getPlayer() { return player; } public final boolean hasRef() { return ref != NO_REF; } public final MagicPermanent getPermanent() { return (MagicPermanent)source; } public final MagicCopyable getRef() { return ref; } public final MagicObject getRefObject() { return (MagicObject)ref; } public final MagicPermanent getRefPermanent() { return (MagicPermanent)ref; } public final MagicPermanentList getRefPermanentList() { return (MagicPermanentList)ref; } public final MagicCardList getRefCardList() { return (MagicCardList)ref; } public final MagicPlayer getRefPlayer() { return (MagicPlayer)ref; } public final MagicItemOnStack getRefItemOnStack() { return (MagicItemOnStack)ref; } public final MagicCardOnStack getRefCardOnStack() { return (MagicCardOnStack)ref; } public final MagicCard getRefCard() { return (MagicCard)ref; } public final MagicSource getRefSource() { return (MagicSource)ref; } public final MagicTarget getRefTarget() { return (MagicTarget)ref; } public final MagicPermanentActivation getRefPermanentActivation() { return (MagicPermanentActivation)ref; } public final int getRefInt() { return ((MagicInteger)ref).value; } public final MagicPayedCost getRefPayedCost() { return (MagicPayedCost)ref; } public final MagicCard getCard() { return (MagicCard)source; } public final MagicCard getSourceCard() { return source.isPermanent() ? getPermanent().getCard() : getCard(); } public final MagicCardOnStack getCardOnStack() { return (MagicCardOnStack)source; } public final MagicItemOnStack getItemOnStack() { return (MagicItemOnStack)source; } public final int getX() { if (ref instanceof MagicPayedCost) { return getRefPayedCost().getX(); } else { // occurs when event is MagicStackGetChoicesEvent return getRefItemOnStack().getEvent().getX(); } } public final boolean hasChoice() { return choice.isValid(); } public final MagicChoice getChoice() { return choice; } public final MagicTargetPicker<?> getTargetPicker() { return targetPicker; } public final List<Object[]> getArtificialChoiceResults(final MagicGame game) { final long start = System.currentTimeMillis(); final List<Object[]> choices = choice.getArtificialChoiceResults(game,this); final long time = System.currentTimeMillis() - start; if (time > 1000) { System.err.println("WARNING. ACR: " + choice.getDescription() + description + " time: " + time); /* if (getClass().desiredAssertionStatus()) { throw new GameException("ACR: " + choice.getDescription() + description + " time: " + time, game); } */ } return choices; } public final Object[] getSimulationChoiceResult(final MagicGame game) { final long start = System.currentTimeMillis(); final Object[] res = choice.getSimulationChoiceResult(game,this); final long time = System.currentTimeMillis() - start; if (time > 1000) { System.err.println("WARNING. RCR: " + choice.getDescription() + description + " time: " + time); /* if (getClass().desiredAssertionStatus()) { throw new GameException("RCR: " + choice.getDescription() + description + " time: " + time, game); } */ } return res; } public MagicTargetChoice getTargetChoice() { return chosen != null ? choice.getTargetChoice(chosen) : choice.getTargetChoice(); } public void clearTargetChoice(Object[] choiceResults) { choiceResults[getTargetChoiceResultIndex()] = null; } public void setTargetChoice(Object[] choiceResults) { choiceResults[getTargetChoiceResultIndex()] = getTarget(); } public final int getTargetChoiceResultIndex() { return choice.getTargetChoiceResultIndex(); } public final int getManaChoiceResultIndex() { return choice.getManaChoiceResultIndex(); } public final String getDescription(final Object[] choiceResults) { return MagicMessage.replaceChoices(description, choiceResults); } public final String getChoiceDescription() { final String tDescription=getDescription(MagicEvent.NO_CHOICE_RESULTS); if (tDescription.length() > 0) { return tDescription; } return hasChoice()?choice.getDescription():""; } public final MagicTarget getTarget() { for (final Object obj : chosen) { if (obj instanceof MagicTarget) { return (MagicTarget)obj; } } throw new RuntimeException("Unable to find target"); } public boolean isMode(final int mode) { return (Integer)chosen[0] == mode; } public boolean isYes() { return MagicChoice.isYesChoice(chosen[0]); } public boolean isNo() { return MagicChoice.isNoChoice(chosen[0]); } public boolean isBuyback() { return getCardOnStack().isKicked(); } public boolean isKicked() { return getCardOnStack().isKicked(); } public int getKickerFromChosen() { return (Integer)chosen[1]; } public MagicDeclareBlockersResult getBlockers() { return (MagicDeclareBlockersResult)chosen[0]; } public MagicDeclareAttackersResult getAttackers() { return (MagicDeclareAttackersResult)chosen[0]; } public MagicExcludeResult getExclude() { return (MagicExcludeResult)chosen[0]; } public MagicPlayChoiceResult getPlayChoice() { return (MagicPlayChoiceResult)chosen[0]; } public MagicCardChoiceResult getCardChoice() { return (MagicCardChoiceResult)chosen[0]; } public MagicColor getChosenColor() { for (Object obj : chosen) { if (obj instanceof MagicColor) { return (MagicColor)obj; } } throw new RuntimeException("Unable to find chosen color"); } public MagicSubType getChosenSubType() { for (Object obj : chosen) { if (obj instanceof MagicSubType) { return (MagicSubType)obj; } } throw new RuntimeException("Unable to find chosen subType"); } public MagicPayManaCostResult getPaidMana() { for (Object obj : chosen) { if (obj instanceof MagicPayManaCostResult) { return (MagicPayManaCostResult)obj; } } throw new RuntimeException("Unable to find paid mana"); } public Object[] getChosen() { return chosen; } private MagicTarget getLegalTarget(final MagicGame game) { for (final Object obj : chosen) { if (obj instanceof MagicTarget) { return getLegalTarget(game, (MagicTarget)obj); } } return null; } private final MagicTarget getLegalTarget(final MagicGame game, final MagicTarget target) { final MagicTargetChoice targetChoice = getTargetChoice(); if (game.isLegalTarget(player,source,targetChoice,target)) { return target; } else { return MagicTargetNone.getInstance(); } } public final boolean hasLegalTarget() { return chosenTarget != MagicTargetNone.getInstance(); } public final boolean isValid(final MagicGame game, final Object[] choiceResults) { chosen = choiceResults; chosenTarget = getLegalTarget(game); final boolean countered = getTargetChoice().isValid() && getTargetChoice().isTargeted() && chosenTarget == MagicTargetNone.getInstance(); chosen = null; chosenTarget = null; return countered == false; } public final boolean processTarget(final MagicGame game, final MagicTargetAction effect) { final MagicTarget target = chosenTarget; if (target != MagicTargetNone.getInstance()) { effect.doAction(target); return true; } else { return false; } } public final List<MagicTarget> listTarget() { final MagicTarget target = chosenTarget; return target != MagicTargetNone.getInstance() ? Collections.singletonList(target) : Collections.emptyList(); } public final boolean processPermanent(final MagicGame game, final MagicPermanentAction effect) { final MagicPermanent target = getPermanent(); if (target.isValid()) { effect.doAction(target); return true; } else { return false; } } public final boolean processRefPermanent(final MagicGame game, final MagicPermanentAction effect) { final MagicPermanent target = getRefPermanent(); if (target.isValid()) { effect.doAction(target); return true; } else { return false; } } public final boolean processTargetPermanent(final MagicGame game, final MagicPermanentAction effect) { final MagicTarget target = chosenTarget; if (target.isPermanent()) { effect.doAction((MagicPermanent)target); return true; } else { return false; } } public final List<MagicPermanent> listTargetPermanent() { final MagicTarget target = chosenTarget; return target.isPermanent() ? Collections.singletonList((MagicPermanent)target) : Collections.emptyList(); } public final List<MagicPlayer> listTargetController() { final MagicTarget target = chosenTarget; return target != MagicTargetNone.getInstance() ? Collections.singletonList(target.getController()) : Collections.emptyList(); } public final boolean processTargetCardOnStack(final MagicGame game, final MagicCardOnStackAction effect) { final MagicTarget target = chosenTarget; if (target.isSpell()) { effect.doAction((MagicCardOnStack)target); return true; } else { return false; } } public final boolean processTargetItemOnStack(final MagicGame game, final MagicItemOnStackAction effect) { final MagicTarget target = chosenTarget; if (target instanceof MagicItemOnStack) { effect.doAction((MagicItemOnStack)target); return true; } else { return false; } } public final List<MagicItemOnStack> listTargetItem() { final MagicTarget target = chosenTarget; return target instanceof MagicItemOnStack ? Collections.singletonList((MagicItemOnStack)target) : Collections.emptyList(); } public final void processChosenCards(final MagicGame game, final MagicCardAction effect) { for (final MagicCard card : getCardChoice()) { effect.doAction(card); } } public final boolean processTargetCard(final MagicGame game, final MagicCardAction effect) { final MagicTarget target = chosenTarget; if (target.isSpell()) { effect.doAction((MagicCard)target); return true; } else { return false; } } public final List<MagicCard> listTargetCard() { final MagicTarget target = chosenTarget; return target.isSpell() ? Collections.singletonList((MagicCard)target) : Collections.emptyList(); } public final boolean processTargetPlayer(final MagicGame game, final MagicPlayerAction effect) { final MagicTarget target = chosenTarget; if (target.isPlayer()) { effect.doAction((MagicPlayer)target); return true; } else { return false; } } public final List<MagicPlayer> listTargetPlayer() { final MagicTarget target = chosenTarget; return target.isPlayer() ? Collections.singletonList((MagicPlayer)target) : Collections.emptyList(); } private static final void payManaCost(final MagicGame game, final MagicPlayer player, final MagicPayManaCostResult result) { result.doAction(game,player); // Let each payed mana cost influence score. game.changeScore(ArtificialScoringSystem.getManaScore(result.getConverted())); } public final void payManaCost(final MagicGame game) { final int manaIndex=getManaChoiceResultIndex(); assert manaIndex < chosen.length : this + "\n" + "manaIndex " + manaIndex + " from " + choice + " is outside of chosen with size " + chosen.length; // Result can be null when paying cost is optional. if (manaIndex >= 0 && chosen[manaIndex] != null) { assert chosen[manaIndex] instanceof MagicPayManaCostResult : this + "\n" + "manaIndex " + manaIndex + " from " + choice + " is " + chosen[manaIndex].getClass() + " instead of MagicPayManaCostResult"; payManaCost(game,player,(MagicPayManaCostResult)chosen[manaIndex]); } } public final void executeEvent(final MagicGame game,final Object[] choiceResults) { chosen = choiceResults; chosenTarget = getLegalTarget(game); payManaCost(game); action.executeEvent(game,this); chosen = null; chosenTarget = null; } public final void executeAllEvents(final MagicGame game, final MagicSourceEvent... sourceEvents) { for (int i = 0; i < sourceEvents.length; i++) { sourceEvents[i].getAction().executeEvent(game, this); } } public final void executeModalEvent(final MagicGame game, final MagicSourceEvent... sourceEvents) { for (int i = 0; i < sourceEvents.length; i++) { if (isMode(i+1)) { sourceEvents[i].getAction().executeEvent(game, this); break; } } } public boolean isSatisfied() { return choice.hasOptions(player.getGame(), player, source, false); } public String toString() { return "EVENT: " + source + " " + description + " " + (hasChoice() ? choice.getDescription() : ""); } public long getStateId() { return MurmurHash3.hash(new long[] { //don't call getStateId if source is MagicItemOnStack to avoid infinite loop (source instanceof MagicItemOnStack) ? -1L : source.getStateId(), player.getId(), choice.getStateId(), targetPicker.hashCode(), action.hashCode(), description.hashCode(), MagicObjectImpl.getStateId(ref), }); } public MagicEventAction getEventAction() { return action; } public String getDescription() { return description; } }