package forge.card.abilityFactory; import forge.*; import forge.card.cardFactory.CardFactoryUtil; import forge.card.spellability.*; import forge.card.trigger.Trigger; import forge.card.trigger.TriggerHandler; import java.util.HashMap; import java.util.Random; /** * <p>AbilityFactory_Token class.</p> * * @author Forge * @version $Id: $ */ public class AbilityFactory_Token extends AbilityFactory { private AbilityFactory AF = null; private String tokenAmount; private String tokenName; private String[] tokenTypes; private String tokenOwner; private String[] tokenColors; private String[] tokenKeywords; private String tokenPower; private String tokenToughness; private String tokenImage; private String[] tokenAbilities; private String[] tokenTriggers; private String[] tokenSVars; private boolean tokenTapped; private boolean tokenAttacking; /** * <p>Constructor for AbilityFactory_Token.</p> * * @param af a {@link forge.card.abilityFactory.AbilityFactory} object. */ public AbilityFactory_Token(final AbilityFactory af) { AF = af; HashMap<String, String> mapParams = af.getMapParams(); String image; String[] keywords; if (mapParams.containsKey("TokenKeywords")) { // TODO: Change this Split to a semicolon or something else keywords = mapParams.get("TokenKeywords").split("<>"); } else { keywords = new String[0]; } if (mapParams.containsKey("TokenImage")) { image = mapParams.get("TokenImage"); } else { image = ""; } if (mapParams.containsKey("TokenTapped")) { tokenTapped = mapParams.get("TokenTapped").equals("True"); } else { tokenTapped = false; } if (mapParams.containsKey("TokenAttacking")) { tokenAttacking = mapParams.get("TokenAttacking").equals("True"); } else { tokenAttacking = false; } if (mapParams.containsKey("TokenAbilities")) { tokenAbilities = mapParams.get("TokenAbilities").split(","); } else { tokenAbilities = null; } if (mapParams.containsKey("TokenTriggers")) { tokenTriggers = mapParams.get("TokenTriggers").split(","); } else { tokenTriggers = null; } if (mapParams.containsKey("TokenSVars")) { tokenSVars = mapParams.get("TokenSVars").split(","); } else { tokenSVars = null; } tokenAmount = mapParams.get("TokenAmount"); tokenPower = mapParams.get("TokenPower"); tokenToughness = mapParams.get("TokenToughness"); tokenName = mapParams.get("TokenName"); tokenTypes = mapParams.get("TokenTypes").split(","); tokenColors = mapParams.get("TokenColors").split(","); tokenKeywords = keywords; tokenImage = image; if (mapParams.containsKey("TokenOwner")) tokenOwner = mapParams.get("TokenOwner"); else tokenOwner = "You"; } /** * <p>getAbility.</p> * * @return a {@link forge.card.spellability.SpellAbility} object. */ public SpellAbility getAbility() { final SpellAbility abToken = new Ability_Activated(AF.getHostCard(), AF.getAbCost(), AF.getAbTgt()) { private static final long serialVersionUID = 8460074843405764620L; @Override public boolean canPlayAI() { return tokenCanPlayAI(this); } @Override public void resolve() { doResolve(this); AF.getHostCard().setAbilityUsed(AF.getHostCard().getAbilityUsed() + 1); } @Override public String getStackDescription() { return doStackDescription(this); } @Override public boolean doTrigger(boolean mandatory) { return tokenDoTriggerAI(this, mandatory); } }; return abToken; } /** * <p>getSpell.</p> * * @return a {@link forge.card.spellability.SpellAbility} object. */ public SpellAbility getSpell() { final SpellAbility spToken = new Spell(AF.getHostCard(), AF.getAbCost(), AF.getAbTgt()) { private static final long serialVersionUID = -8041427947613029670L; @Override public boolean canPlayAI() { return tokenCanPlayAI(this); } @Override public void resolve() { doResolve(this); } @Override public String getStackDescription() { return doStackDescription(this); } }; return spToken; } /** * <p>getDrawback.</p> * * @return a {@link forge.card.spellability.SpellAbility} object. */ public SpellAbility getDrawback() { final SpellAbility dbDealDamage = new Ability_Sub(AF.getHostCard(), AF.getAbTgt()) { private static final long serialVersionUID = 7239608350643325111L; @Override public boolean chkAI_Drawback() { return true; } @Override public String getStackDescription() { return doStackDescription(this); } @Override public void resolve() { doResolve(this); } @Override public boolean doTrigger(boolean mandatory) { return tokenDoTriggerAI(this, mandatory); } }; // Spell return dbDealDamage; } /** * <p>tokenCanPlayAI.</p> * * @param sa a {@link forge.card.spellability.SpellAbility} object. * @return a boolean. */ private boolean tokenCanPlayAI(SpellAbility sa) { Cost cost = sa.getPayCosts(); if (!ComputerUtil.canPayCost(sa)) // If there is a cost payment it's usually not mandatory return false; for (String type : tokenTypes) { if (type.equals("Legendary")) { // Don't kill AIs Legendary tokens if (AllZoneUtil.getPlayerCardsInPlay(AllZone.getComputerPlayer(), tokenName).size() > 0) return false; } } boolean haste = false; boolean oneShot = false; for (String kw : tokenKeywords) { if (kw.equals("Haste")) haste = true; if (kw.equals("At the beginning of the end step, exile CARDNAME.") || kw.equals("At the beginning of the end step, sacrifice CARDNAME.")) oneShot = true; } //Don't generate tokens without haste before main 2 if possible if (AllZone.getPhase().isBefore(Constant.Phase.Main2) && AllZone.getPhase().isPlayerTurn(AllZone.getComputerPlayer()) && !haste) return false; if ((AllZone.getPhase().isAfter(Constant.Phase.Combat_Begin) || AllZone.getPhase().isPlayerTurn(AllZone.getHumanPlayer())) && oneShot) return false; // TODO: if i don't have enough blockers and my token can block one of the unblocked creatures // create it after attackers are declared //if (AllZone.getPhase().is(Constant.Phase.Combat_Declare_Attackers_InstantAbility, AllZone.getHumanPlayer())) // return true; // prevent run-away activations - first time will always return true Random r = MyRandom.random; final Card source = sa.getSourceCard(); boolean chance = r.nextFloat() <= Math.pow(.9, source.getAbilityUsed()); Target tgt = sa.getTarget(); if (tgt != null) { tgt.resetTargets(); if (tgt.canOnlyTgtOpponent()) tgt.addTarget(AllZone.getHumanPlayer()); else tgt.addTarget(AllZone.getComputerPlayer()); } if (cost != null) { if (cost.getSacCost() && !cost.getSacThis()) { //only sacrifice something that's supposed to be sacrificed String type = cost.getSacType(); CardList typeList = AllZoneUtil.getPlayerCardsInPlay(AllZone.getComputerPlayer()); typeList = typeList.getValidCards(type.split(","), source.getController(), source); if (ComputerUtil.getCardPreference(source, "SacCost", typeList) == null) return false; } if (cost.getSubCounter()) { if (cost.getCounterType().equals(Counters.P1P1)) { // A card has a 25% chance per counter to be able to pass through here // 4+ counters will always pass. 0 counters will never int currentNum = source.getCounters(cost.getCounterType()); double percent = .25 * (currentNum / cost.getCounterNum()); if (percent <= r.nextFloat()) return false; } } if (cost.getLifeCost()) { if (AllZone.getComputerPlayer().getLife() - cost.getLifeAmount() < 4) return false; } } if (tokenAmount.equals("X")) { if (source.getSVar(tokenAmount).equals("Count$xPaid")) { // Set PayX here to maximum value. int xPay = ComputerUtil.determineLeftoverMana(sa); source.setSVar("PayX", Integer.toString(xPay)); } if (AbilityFactory.calculateAmount(AF.getHostCard(), tokenAmount, sa) <= 0) return false; } if (AbilityFactory.playReusable(sa)) return chance; if (sa.isAbility()) return (r.nextFloat() < .9 && chance); return (r.nextFloat() < .6667 && chance); } /** * <p>tokenDoTriggerAI.</p> * * @param sa a {@link forge.card.spellability.SpellAbility} object. * @param mandatory a boolean. * @return a boolean. */ private boolean tokenDoTriggerAI(SpellAbility sa, boolean mandatory) { if (!ComputerUtil.canPayCost(sa)) return false; return true; } /** * <p>doStackDescription.</p> * * @param sa a {@link forge.card.spellability.SpellAbility} object. * @return a {@link java.lang.String} object. */ private String doStackDescription(SpellAbility sa) { int finalPower = AbilityFactory.calculateAmount(AF.getHostCard(), tokenPower, sa); int finalToughness = AbilityFactory.calculateAmount(AF.getHostCard(), tokenToughness, sa); int finalAmount = AbilityFactory.calculateAmount(AF.getHostCard(), tokenAmount, sa); StringBuilder sb = new StringBuilder(); if (sa instanceof Ability_Sub) sb.append(" "); else sb.append(AF.getHostCard().getName()).append(" - "); sb.append("Put (").append(finalAmount).append(") ").append(finalPower).append("/").append(finalToughness); sb.append(" ").append(tokenName).append(" token"); if (finalAmount != 1) sb.append("s"); sb.append(" onto the battlefield"); if (tokenOwner.equals("Opponent")) { sb.append(" under your opponent's control."); } else { sb.append("."); } if (sa.getSubAbility() != null) { sb.append(sa.getSubAbility().getStackDescription()); } return sb.toString(); } /** * <p>doResolve.</p> * * @param sa a {@link forge.card.spellability.SpellAbility} object. */ private void doResolve(SpellAbility sa) { String imageName = ""; Player controller; String cost = ""; //Construct colors String colorDesc = ""; for (String col : tokenColors) { if (col.equals("White")) { colorDesc += "W"; } else if (col.equals("Blue")) { colorDesc += "U"; } else if (col.equals("Black")) { colorDesc += "B"; } else if (col.equals("Red")) { colorDesc += "R"; } else if (col.equals("Green")) { colorDesc += "G"; } else if (col.equals("Colorless")) { colorDesc = "C"; } } if (tokenImage.equals("")) { imageName += colorDesc + " " + tokenPower + " " + tokenToughness + " " + tokenName; } else { imageName = tokenImage; } System.out.println("AF_Token imageName = " + imageName); for (char c : colorDesc.toCharArray()) { cost += c + ' '; } cost = colorDesc.replace('C', '1').trim(); controller = AbilityFactory.getDefinedPlayers(AF.getHostCard(), tokenOwner, sa).get(0); int finalPower = AbilityFactory.calculateAmount(AF.getHostCard(), tokenPower, sa); int finalToughness = AbilityFactory.calculateAmount(AF.getHostCard(), tokenToughness, sa); int finalAmount = AbilityFactory.calculateAmount(AF.getHostCard(), tokenAmount, sa); for (int i = 0; i < finalAmount; i++) { CardList tokens = CardFactoryUtil.makeToken(tokenName, imageName, controller, cost, tokenTypes, finalPower, finalToughness, tokenKeywords); //Grant abilities if (tokenAbilities != null) { AbilityFactory af = new AbilityFactory(); for (String s : tokenAbilities) { String actualAbility = AF.getHostCard().getSVar(s); for (Card c : tokens) { SpellAbility grantedAbility = af.getAbility(actualAbility, c); c.addSpellAbility(grantedAbility); } } } //Grant triggers if (tokenTriggers != null) { for (String s : tokenTriggers) { String actualTrigger = AF.getHostCard().getSVar(s); for (final Card c : tokens) { //Needs to do some voodoo when the token disappears to remove the triggers at the same time. Command LPCommand = new Command() { private static final long serialVersionUID = -9007707442828928732L; public void execute() { AllZone.getTriggerHandler().removeAllFromCard(c); } }; c.addLeavesPlayCommand(LPCommand); Trigger parsedTrigger = TriggerHandler.parseTrigger(actualTrigger, c); String ability = AF.getHostCard().getSVar(parsedTrigger.getMapParams().get("Execute")); parsedTrigger.setOverridingAbility(new AbilityFactory().getAbility(ability, c)); c.addTrigger(parsedTrigger); AllZone.getTriggerHandler().registerTrigger(parsedTrigger); } } } //Grant SVars if (tokenSVars != null) { for (String s : tokenSVars) { String actualSVar = AF.getHostCard().getSVar(s); for (Card c : tokens) { c.setSVar(s, actualSVar); } } } for (Card c : tokens) { if (tokenTapped) { c.tap(); } if (tokenAttacking) { AllZone.getCombat().addAttacker(c); } } } } }