@Override public void resolve(SpellAbility sa) { final Card host = sa.getHostCard(); final String[] choices = sa.getParam("Choices").split(","); final List<SpellAbility> abilities = new ArrayList<SpellAbility>(); for (String s : choices) { abilities.add(AbilityFactory.getAbility(host.getSVar(s), host)); } final List<Player> tgtPlayers = getDefinedPlayersOrTargeted(sa); final TargetRestrictions tgt = sa.getTargetRestrictions(); for (final Player p : tgtPlayers) { if (tgt != null && !p.canBeTargetedBy(sa)) { continue; } int idxChosen = 0; String chosenName; if (sa.hasParam("AtRandom")) { idxChosen = MyRandom.getRandom().nextInt(choices.length); chosenName = choices[idxChosen]; } else { SpellAbility saChosen = p.getController().chooseSingleSpellForEffect(abilities, sa, "Choose one"); idxChosen = abilities.indexOf(saChosen); chosenName = choices[idxChosen]; } SpellAbility chosenSA = AbilityFactory.getAbility(host.getSVar(chosenName), host); if (sa.hasParam("ShowChoice")) { p.getGame() .getAction() .nofityOfValue(sa, p, abilities.get(idxChosen).getDescription(), null); } chosenSA.setActivatingPlayer(sa.getActivatingPlayer()); ((AbilitySub) chosenSA).setParent(sa); AbilityUtils.resolve(chosenSA); } }
private boolean pumpTgtAI( final Player ai, final SpellAbility sa, final int defense, final int attack, final boolean mandatory) { final List<String> keywords = sa.hasParam("KW") ? Arrays.asList(sa.getParam("KW").split(" & ")) : new ArrayList<String>(); final Game game = ai.getGame(); final Card source = sa.getHostCard(); if (!mandatory && !sa.isTrigger() && game.getPhaseHandler().getPhase().isAfter(PhaseType.COMBAT_DECLARE_BLOCKERS) && !(sa.isCurse() && defense < 0) && !this.containsNonCombatKeyword(keywords) && !sa.hasParam("UntilYourNextTurn")) { return false; } final Player opp = ai.getOpponent(); final TargetRestrictions tgt = sa.getTargetRestrictions(); sa.resetTargets(); if (sa.hasParam("TargetingPlayer") && sa.getActivatingPlayer().equals(ai) && !sa.isTrigger()) { Player targetingPlayer = AbilityUtils.getDefinedPlayers(source, sa.getParam("TargetingPlayer"), sa).get(0); sa.setTargetingPlayer(targetingPlayer); return targetingPlayer.getController().chooseTargetsFor(sa); } List<Card> list = new ArrayList<Card>(); if (sa.hasParam("AILogic")) { if (sa.getParam("AILogic").equals("HighestPower")) { list = CardLists.getValidCards( CardLists.filter(game.getCardsIn(ZoneType.Battlefield), Presets.CREATURES), tgt.getValidTgts(), ai, source); list = CardLists.getTargetableCards(list, sa); CardLists.sortByPowerDesc(list); if (!list.isEmpty()) { sa.getTargets().add(list.get(0)); return true; } else { return false; } } if (sa.getParam("AILogic").equals("Fight") || sa.getParam("AILogic").equals("PowerDmg")) { final AbilitySub tgtFight = sa.getSubAbility(); List<Card> aiCreatures = ai.getCreaturesInPlay(); aiCreatures = CardLists.getTargetableCards(aiCreatures, sa); aiCreatures = ComputerUtil.getSafeTargets(ai, sa, aiCreatures); ComputerUtilCard.sortByEvaluateCreature(aiCreatures); // sort is suboptimal due to conflicting needs depending on game state: // -deathtouch for deal damage // -max power for damage to player // -survivability for generic "fight" // -no support for "heroic" List<Card> humCreatures = ai.getOpponent().getCreaturesInPlay(); humCreatures = CardLists.getTargetableCards(humCreatures, tgtFight); ComputerUtilCard.sortByEvaluateCreature(humCreatures); if (humCreatures.isEmpty() || aiCreatures.isEmpty()) { return false; } int buffedAtk = attack, buffedDef = defense; for (Card humanCreature : humCreatures) { for (Card aiCreature : aiCreatures) { if (sa.isSpell()) { // heroic triggers adding counters for (Trigger t : aiCreature.getTriggers()) { if (t.getMode() == TriggerType.SpellCast) { final Map<String, String> params = t.getMapParams(); if ("Card.Self".equals(params.get("TargetsValid")) && "You".equals(params.get("ValidActivatingPlayer")) && params.containsKey("Execute")) { SpellAbility heroic = AbilityFactory.getAbility( aiCreature.getSVar(params.get("Execute")), aiCreature); if ("Self".equals(heroic.getParam("Defined")) && "P1P1".equals(heroic.getParam("CounterType"))) { int amount = AbilityUtils.calculateAmount( aiCreature, heroic.getParam("CounterNum"), heroic); buffedAtk += amount; buffedDef += amount; } break; } } } } if (sa.getParam("AILogic").equals("PowerDmg")) { if (FightAi.canKill(aiCreature, humanCreature, buffedAtk)) { sa.getTargets().add(aiCreature); tgtFight.getTargets().add(humanCreature); return true; } } else { if (FightAi.shouldFight(aiCreature, humanCreature, buffedAtk, buffedDef)) { sa.getTargets().add(aiCreature); tgtFight.getTargets().add(humanCreature); return true; } } } } } return false; } else if (sa.isCurse()) { if (sa.canTarget(opp)) { sa.getTargets().add(opp); return true; } list = this.getCurseCreatures(ai, sa, defense, attack, keywords); } else { if (!tgt.canTgtCreature()) { ZoneType zone = tgt.getZone().get(0); list = game.getCardsIn(zone); } else { list = this.getPumpCreatures(ai, sa, defense, attack, keywords); } if (sa.canTarget(ai)) { sa.getTargets().add(ai); return true; } } list = CardLists.getValidCards(list, tgt.getValidTgts(), ai, source); if (game.getStack().isEmpty()) { // If the cost is tapping, don't activate before declare // attack/block if (sa.getPayCosts() != null && sa.getPayCosts().hasTapCost()) { if (game.getPhaseHandler().getPhase().isBefore(PhaseType.COMBAT_DECLARE_ATTACKERS) && game.getPhaseHandler().isPlayerTurn(ai)) { list.remove(sa.getHostCard()); } if (game.getPhaseHandler().getPhase().isBefore(PhaseType.COMBAT_DECLARE_BLOCKERS) && game.getPhaseHandler().isPlayerTurn(opp)) { list.remove(sa.getHostCard()); } } } if (list.isEmpty()) { return mandatory && this.pumpMandatoryTarget(ai, sa, mandatory); } if (!sa.isCurse()) { // Don't target cards that will die. list = ComputerUtil.getSafeTargets(ai, sa, list); } while (sa.getTargets().getNumTargeted() < tgt.getMaxTargets(source, sa)) { Card t = null; // boolean goodt = false; if (list.isEmpty()) { if ((sa.getTargets().getNumTargeted() < tgt.getMinTargets(source, sa)) || (sa.getTargets().getNumTargeted() == 0)) { if (mandatory) { return this.pumpMandatoryTarget(ai, sa, mandatory); } sa.resetTargets(); return false; } else { // TODO is this good enough? for up to amounts? break; } } t = ComputerUtilCard.getBestAI(list); // option to hold removal instead only applies for single targeted removal if (!sa.isTrigger() && tgt.getMaxTargets(source, sa) == 1 && sa.isCurse()) { if (!ComputerUtilCard.useRemovalNow(sa, t, -defense, ZoneType.Graveyard)) { return false; } } sa.getTargets().add(t); list.remove(t); } return true; } // pumpTgtAI()
@Override public void resolve(SpellAbility sa) { final Card card = sa.getHostCard(); // final int min = sa.containsKey("Min") ? Integer.parseInt(sa.get("Min")) : 0; // final int max = sa.containsKey("Max") ? Integer.parseInt(sa.get("Max")) : 99; final boolean random = sa.hasParam("Random"); final boolean anyNumber = sa.hasParam("ChooseAnyNumber"); final boolean secretlyChoose = sa.hasParam("SecretlyChoose"); final String sMin = sa.getParamOrDefault("Min", "0"); final int min = AbilityUtils.calculateAmount(card, sMin, sa); final String sMax = sa.getParamOrDefault("Max", "99"); final int max = AbilityUtils.calculateAmount(card, sMax, sa); final List<Player> tgtPlayers = getTargetPlayers(sa); final TargetRestrictions tgt = sa.getTargetRestrictions(); final Map<Player, Integer> chooseMap = new HashMap<Player, Integer>(); for (final Player p : tgtPlayers) { if ((tgt == null) || p.canBeTargetedBy(sa)) { int chosen; if (random) { final Random randomGen = new Random(); chosen = randomGen.nextInt(max - min) + min; p.getGame().getAction().nofityOfValue(sa, p, Integer.toString(chosen), null); } else { String title = sa.hasParam("ListTitle") ? sa.getParam("ListTitle") : "Choose a number"; if (anyNumber) { chosen = p.getController().announceRequirements(sa, title, true); } else { chosen = p.getController().chooseNumber(sa, title, min, max); } // don't notify here, because most scripts I've seen don't store that number in a long // term } if (secretlyChoose) { chooseMap.put(p, chosen); } else { card.setChosenNumber(chosen); } if (sa.hasParam("Notify")) { p.getGame().getAction().nofityOfValue(sa, card, p.getName() + " picked " + chosen, p); } } } if (secretlyChoose) { StringBuilder sb = new StringBuilder(); List<Player> highestNum = new ArrayList<Player>(); List<Player> lowestNum = new ArrayList<Player>(); int highest = 0; int lowest = Integer.MAX_VALUE; for (Entry<Player, Integer> ev : chooseMap.entrySet()) { int num = ev.getValue(); Player player = ev.getKey(); sb.append(player).append(" chose ").append(num); sb.append("\r\n"); if (num > highest) { highestNum.clear(); highest = num; } if (num == highest) { highestNum.add(player); } if (num < lowest) { lowestNum.clear(); lowest = num; } if (num == lowest) { lowestNum.add(player); } } card.getGame().getAction().nofityOfValue(sa, card, sb.toString(), null); if (sa.hasParam("ChooseNumberSubAbility")) { SpellAbility sub = AbilityFactory.getAbility(card.getSVar(sa.getParam("ChooseNumberSubAbility")), card); sub.setActivatingPlayer(sa.getActivatingPlayer()); ((AbilitySub) sub).setParent(sa); for (Player p : chooseMap.keySet()) { card.addRemembered(p); card.setChosenNumber(chooseMap.get(p)); AbilityUtils.resolve(sub); card.clearRemembered(); } } if (sa.hasParam("Lowest")) { SpellAbility action = AbilityFactory.getAbility(card.getSVar(sa.getParam("Lowest")), card); action.setActivatingPlayer(sa.getActivatingPlayer()); ((AbilitySub) action).setParent(sa); for (Player p : lowestNum) { card.addRemembered(p); card.setChosenNumber(lowest); AbilityUtils.resolve(action); card.clearRemembered(); } } if (sa.hasParam("Highest")) { SpellAbility action = AbilityFactory.getAbility(card.getSVar(sa.getParam("Highest")), card); action.setActivatingPlayer(sa.getActivatingPlayer()); ((AbilitySub) action).setParent(sa); for (Player p : highestNum) { card.addRemembered(p); card.setChosenNumber(highest); AbilityUtils.resolve(action); card.clearRemembered(); } if (sa.hasParam("RememberHighest")) { card.addRemembered(highestNum); } } } }