diff --git a/.gitattributes b/.gitattributes index e5de16f5b3b..7c85e9ae196 100644 --- a/.gitattributes +++ b/.gitattributes @@ -12488,7 +12488,6 @@ src/main/java/forge/card/abilityfactory/AbilityFactoryReveal.java svneol=native# src/main/java/forge/card/abilityfactory/AbilityFactorySacrifice.java svneol=native#text/plain src/main/java/forge/card/abilityfactory/AbilityFactorySetState.java svneol=native#text/plain src/main/java/forge/card/abilityfactory/AbilityFactoryStoreSVar.java -text -src/main/java/forge/card/abilityfactory/AbilityFactoryToken.java svneol=native#text/plain src/main/java/forge/card/abilityfactory/AbilityFactoryZoneAffecting.java svneol=native#text/plain src/main/java/forge/card/abilityfactory/SpellAiLogic.java -text src/main/java/forge/card/abilityfactory/SpellEffect.java -text @@ -12504,6 +12503,7 @@ src/main/java/forge/card/abilityfactory/ai/GainLifeAi.java -text src/main/java/forge/card/abilityfactory/ai/LoseLifeAi.java -text src/main/java/forge/card/abilityfactory/ai/PoisonAi.java -text src/main/java/forge/card/abilityfactory/ai/SetLifeAi.java -text +src/main/java/forge/card/abilityfactory/ai/TokenAi.java -text src/main/java/forge/card/abilityfactory/effects/AddTurnEffect.java -text src/main/java/forge/card/abilityfactory/effects/AnimateAllEffect.java -text src/main/java/forge/card/abilityfactory/effects/AnimateEffect.java -text @@ -12516,6 +12516,7 @@ src/main/java/forge/card/abilityfactory/effects/HelperAnimate.java svneol=native src/main/java/forge/card/abilityfactory/effects/LoseLifeEffect.java -text src/main/java/forge/card/abilityfactory/effects/PoisonEffect.java -text src/main/java/forge/card/abilityfactory/effects/SetLifeEffect.java -text +src/main/java/forge/card/abilityfactory/effects/TokenEffect.java svneol=native#text/plain src/main/java/forge/card/abilityfactory/package-info.java svneol=native#text/plain src/main/java/forge/card/cardfactory/CardFactory.java svneol=native#text/plain src/main/java/forge/card/cardfactory/CardFactoryArtifacts.java -text diff --git a/src/main/java/forge/card/abilityfactory/AbilityFactory.java b/src/main/java/forge/card/abilityfactory/AbilityFactory.java index 73fdb83c0be..23d3c39e3d3 100644 --- a/src/main/java/forge/card/abilityfactory/AbilityFactory.java +++ b/src/main/java/forge/card/abilityfactory/AbilityFactory.java @@ -21,6 +21,8 @@ import java.util.ArrayList; import java.util.HashMap; import java.util.List; +import org.apache.tools.ant.types.resources.Tokens; + import forge.Card; import forge.CardLists; @@ -1237,15 +1239,8 @@ public class AbilityFactory { } else if (this.api.equals("Token")) { - final AbilityFactoryToken aft = new AbilityFactoryToken(this); - - if (this.isAb) { - spellAbility = aft.getAbility(); - } else if (this.isSp) { - spellAbility = aft.getSpell(); - } else if (this.isDb) { - spellAbility = aft.getDrawback(); - } + ai = new TokenAi(); + se = new TokenEffect(); } else if (this.api.equals("TwoPiles")) { diff --git a/src/main/java/forge/card/abilityfactory/AbilityFactoryToken.java b/src/main/java/forge/card/abilityfactory/AbilityFactoryToken.java deleted file mode 100644 index efdd36707df..00000000000 --- a/src/main/java/forge/card/abilityfactory/AbilityFactoryToken.java +++ /dev/null @@ -1,653 +0,0 @@ -/* - * Forge: Play Magic: the Gathering. - * Copyright (C) 2011 Forge Team - * - * This program is free software: you can redistribute it and/or modify - * it under the terms of the GNU General Public License as published by - * the Free Software Foundation, either version 3 of the License, or - * (at your option) any later version. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. - * - * You should have received a copy of the GNU General Public License - * along with this program. If not, see . - */ -package forge.card.abilityfactory; - -import java.util.Arrays; -import java.util.HashMap; -import java.util.List; -import java.util.Random; - -import forge.Card; - -import forge.Singletons; -import forge.card.cardfactory.CardFactoryUtil; -import forge.card.cost.Cost; -import forge.card.cost.CostUtil; -import forge.card.spellability.AbilityActivated; -import forge.card.spellability.AbilitySub; -import forge.card.spellability.Spell; -import forge.card.spellability.SpellAbility; -import forge.card.spellability.Target; -import forge.card.trigger.Trigger; -import forge.card.trigger.TriggerHandler; -import forge.game.phase.PhaseHandler; -import forge.game.phase.PhaseType; -import forge.game.player.ComputerUtil; -import forge.game.player.Player; -import forge.game.zone.ZoneType; -import forge.util.MyRandom; - -/** - *

- * AbilityFactory_Token class. - *

- * - * @author Forge - * @version $Id$ - */ -public class AbilityFactoryToken extends AbilityFactory { - private AbilityFactory abilityFactory = null; - - private final String tokenAmount; - private final String tokenName; - private final String[] tokenTypes; - private String tokenOwner; - private final String[] tokenColors; - private final String[] tokenKeywords; - private final String tokenPower; - private final String tokenToughness; - private final String tokenImage; - private String[] tokenAbilities; - private String[] tokenTriggers; - private String[] tokenSVars; - private String[] tokenStaticAbilities; - private boolean tokenTapped; - private boolean tokenAttacking; - - /** - *

- * Constructor for AbilityFactory_Token. - *

- * - * @param af - * a {@link forge.card.abilityfactory.AbilityFactory} object. - */ - public AbilityFactoryToken(final AbilityFactory af) { - this.abilityFactory = af; - - final HashMap 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")) { - this.tokenTapped = mapParams.get("TokenTapped").equals("True"); - } else { - this.tokenTapped = false; - } - if (mapParams.containsKey("TokenAttacking")) { - this.tokenAttacking = mapParams.get("TokenAttacking").equals("True"); - } else { - this.tokenAttacking = false; - } - - if (mapParams.containsKey("TokenAbilities")) { - this.tokenAbilities = mapParams.get("TokenAbilities").split(","); - } else { - this.tokenAbilities = null; - } - if (mapParams.containsKey("TokenTriggers")) { - this.tokenTriggers = mapParams.get("TokenTriggers").split(","); - } else { - this.tokenTriggers = null; - } - if (mapParams.containsKey("TokenSVars")) { - this.tokenSVars = mapParams.get("TokenSVars").split(","); - } else { - this.tokenSVars = null; - } - if (mapParams.containsKey("TokenStaticAbilities")) { - this.tokenStaticAbilities = mapParams.get("TokenStaticAbilities").split(","); - } else { - this.tokenStaticAbilities = null; - } - - this.tokenAmount = mapParams.get("TokenAmount"); - this.tokenPower = mapParams.get("TokenPower"); - this.tokenToughness = mapParams.get("TokenToughness"); - this.tokenName = mapParams.get("TokenName"); - this.tokenTypes = mapParams.get("TokenTypes").split(","); - this.tokenColors = mapParams.get("TokenColors").split(","); - this.tokenKeywords = keywords; - this.tokenImage = image; - if (mapParams.containsKey("TokenOwner")) { - this.tokenOwner = mapParams.get("TokenOwner"); - } else { - this.tokenOwner = "You"; - } - } - - /** - *

- * getAbility. - *

- * - * @return a {@link forge.card.spellability.SpellAbility} object. - */ - public final SpellAbility getAbility() { - class AbilityToken extends AbilityActivated { - public AbilityToken(final Card ca, final Cost co, final Target t) { - super(ca, co, t); - } - - @Override - public AbilityActivated getCopy() { - AbilityActivated res = new AbilityToken(getSourceCard(), - getPayCosts(), getTarget() == null ? null : new Target(getTarget())); - CardFactoryUtil.copySpellAbility(this, res); - return res; - } - - private static final long serialVersionUID = 8460074843405764620L; - - @Override - public boolean canPlayAI() { - return AbilityFactoryToken.this.tokenCanPlayAI(getActivatingPlayer(), this); - } - - @Override - public void resolve() { - AbilityFactoryToken.this.doResolve(this); - } - - @Override - public String getStackDescription() { - return AbilityFactoryToken.this.doStackDescription(this); - } - - @Override - public boolean doTrigger(final boolean mandatory) { - return AbilityFactoryToken.this.tokenDoTriggerAI(getActivatingPlayer(), this, mandatory); - } - } - final SpellAbility abToken = new AbilityToken(this.abilityFactory.getHostCard(), - this.abilityFactory.getAbCost(), this.abilityFactory.getAbTgt()); - - return abToken; - } - - /** - *

- * getSpell. - *

- * - * @return a {@link forge.card.spellability.SpellAbility} object. - */ - public final SpellAbility getSpell() { - final SpellAbility spToken = new Spell(this.abilityFactory.getHostCard(), this.abilityFactory.getAbCost(), - this.abilityFactory.getAbTgt()) { - private static final long serialVersionUID = -8041427947613029670L; - - @Override - public boolean canPlayAI() { - return AbilityFactoryToken.this.tokenCanPlayAI(getActivatingPlayer(), this); - } - - @Override - public void resolve() { - AbilityFactoryToken.this.doResolve(this); - } - - @Override - public String getStackDescription() { - return AbilityFactoryToken.this.doStackDescription(this); - } - - @Override - public boolean canPlayFromEffectAI(final boolean mandatory, final boolean withOutManaCost) { - if (withOutManaCost) { - return AbilityFactoryToken.this.tokenDoTriggerAINoCost(getActivatingPlayer(), this, mandatory); - } - return AbilityFactoryToken.this.tokenDoTriggerAI(getActivatingPlayer(), this, mandatory); - } - }; - - return spToken; - } - - /** - *

- * getDrawback. - *

- * - * @return a {@link forge.card.spellability.SpellAbility} object. - */ - public final SpellAbility getDrawback() { - class DrawbackToken extends AbilitySub { - public DrawbackToken(final Card ca, final Target t) { - super(ca, t); - } - - @Override - public AbilitySub getCopy() { - AbilitySub res = new DrawbackToken(getSourceCard(), - getTarget() == null ? null : new Target(getTarget())); - CardFactoryUtil.copySpellAbility(this, res); - return res; - } - - private static final long serialVersionUID = 7239608350643325111L; - - @Override - public boolean chkAIDrawback() { - return true; - } - - @Override - public String getStackDescription() { - return AbilityFactoryToken.this.doStackDescription(this); - } - - @Override - public void resolve() { - AbilityFactoryToken.this.doResolve(this); - } - - @Override - public boolean doTrigger(final boolean mandatory) { - return AbilityFactoryToken.this.tokenDoTriggerAI(getActivatingPlayer(), this, mandatory); - } - } - final SpellAbility dbToken = new DrawbackToken(this.abilityFactory.getHostCard(), - this.abilityFactory.getAbTgt()); // Spell - - return dbToken; - } - - /** - *

- * tokenCanPlayAI. - *

- * - * @param sa - * a {@link forge.card.spellability.SpellAbility} object. - * @return a boolean. - */ - private boolean tokenCanPlayAI(final Player ai, final SpellAbility sa) { - final Cost cost = sa.getPayCosts(); - final AbilityFactory af = sa.getAbilityFactory(); - final HashMap mapParams = af.getMapParams(); - - if (ComputerUtil.preventRunAwayActivations(sa)) { - return false; - } - - Player opp = ai.getOpponent(); - for (final String type : this.tokenTypes) { - if (type.equals("Legendary")) { - // Don't kill AIs Legendary tokens - if (ai.getCardsIn(ZoneType.Battlefield, this.tokenName).size() > 0) { - return false; - } - } - } - - boolean haste = false; - boolean oneShot = false; - for (final String kw : this.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; - } - } - - PhaseHandler ph = Singletons.getModel().getGame().getPhaseHandler(); - // Don't generate tokens without haste before main 2 if possible - if (ph.getPhase().isBefore(PhaseType.MAIN2) - && ph.isPlayerTurn(ai) && !haste - && !mapParams.containsKey("ActivationPhases")) { - return false; - } - if ((ph.isPlayerTurn(ai) - || ph.getPhase().isBefore( - PhaseType.COMBAT_DECLARE_ATTACKERS_INSTANT_ABILITY)) - && !mapParams.containsKey("ActivationPhases") && !mapParams.containsKey("PlayerTurn") - && !AbilityFactory.isSorcerySpeed(sa) && !haste) { - return false; - } - if ((ph.getPhase().isAfter(PhaseType.COMBAT_BEGIN) || Singletons.getModel().getGame().getPhaseHandler().isPlayerTurn( - opp)) - && oneShot) { - return false; - } - - // prevent run-away activations - first time will always return true - final Random r = MyRandom.getRandom(); - final Card source = sa.getSourceCard(); - - final Target tgt = sa.getTarget(); - if (tgt != null) { - tgt.resetTargets(); - if (tgt.canOnlyTgtOpponent()) { - tgt.addTarget(opp); - } else { - tgt.addTarget(ai); - } - } - - if (cost != null) { - if (!CostUtil.checkLifeCost(ai, cost, source, 4, null)) { - return false; - } - - if (!CostUtil.checkDiscardCost(ai, cost, source)) { - return false; - } - - if (!CostUtil.checkSacrificeCost(ai, cost, source)) { - return false; - } - - if (!CostUtil.checkRemoveCounterCost(cost, source)) { - return false; - } - } - - if (this.tokenAmount.equals("X") || this.tokenPower.equals("X") || this.tokenToughness.equals("X")) { - int x = AbilityFactory.calculateAmount(this.abilityFactory.getHostCard(), this.tokenAmount, sa); - if (source.getSVar("X").equals("Count$xPaid")) { - // Set PayX here to maximum value. - x = ComputerUtil.determineLeftoverMana(sa, ai); - source.setSVar("PayX", Integer.toString(x)); - } - if (x <= 0) { - return false; - } - } - - if (AbilityFactory.playReusable(ai, sa)) { - return true; - } - - if (Singletons.getModel().getGame().getPhaseHandler().is(PhaseType.COMBAT_DECLARE_ATTACKERS_INSTANT_ABILITY)) { - return true; - } - if (sa.isAbility()) { - return (r.nextFloat() < .9); - } - - return (r.nextFloat() < .8); - } - - /** - *

- * tokenDoTriggerAI. - *

- * - * @param sa - * a {@link forge.card.spellability.SpellAbility} object. - * @param mandatory - * a boolean. - * @return a boolean. - */ - private boolean tokenDoTriggerAI(final Player ai, final SpellAbility sa, final boolean mandatory) { - if (!ComputerUtil.canPayCost(sa, ai)) { - return false; - } - - return tokenDoTriggerAINoCost(ai, sa, mandatory); - } - - /** - *

- * tokenDoTriggerAINoCost. - *

- * - * @param sa - * a {@link forge.card.spellability.SpellAbility} object. - * @param mandatory - * a boolean. - * @return a boolean. - */ - private boolean tokenDoTriggerAINoCost(final Player ai, final SpellAbility sa, final boolean mandatory) { - final Card source = sa.getSourceCard(); - final Target tgt = sa.getTarget(); - if (tgt != null) { - tgt.resetTargets(); - if (tgt.canOnlyTgtOpponent()) { - tgt.addTarget(ai.getOpponent()); - } else { - tgt.addTarget(ai); - } - } - if (this.tokenAmount.equals("X") || this.tokenPower.equals("X") || this.tokenToughness.equals("X")) { - int x = AbilityFactory.calculateAmount(this.abilityFactory.getHostCard(), this.tokenAmount, sa); - if (source.getSVar("X").equals("Count$xPaid")) { - // Set PayX here to maximum value. - x = ComputerUtil.determineLeftoverMana(sa, ai); - source.setSVar("PayX", Integer.toString(x)); - } - if (x <= 0) { - return false; - } - } - - return true; - } - - /** - *

- * doStackDescription. - *

- * - * @param sa - * a {@link forge.card.spellability.SpellAbility} object. - * @return a {@link java.lang.String} object. - */ - private String doStackDescription(final SpellAbility sa) { - - final HashMap params = this.abilityFactory.getMapParams(); - final StringBuilder sb = new StringBuilder(); - final Card host = this.abilityFactory.getHostCard(); - - if (sa instanceof AbilitySub) { - sb.append(" "); - } else { - sb.append(host.getName()).append(" - "); - } - - if (params.containsKey("StackDescription")) { - sb.append(params.get("StackDescription")); - } - else { - - final int finalPower = AbilityFactory.calculateAmount(this.abilityFactory.getHostCard(), this.tokenPower, sa); - final int finalToughness = AbilityFactory.calculateAmount(this.abilityFactory.getHostCard(), - this.tokenToughness, sa); - final int finalAmount = AbilityFactory.calculateAmount(this.abilityFactory.getHostCard(), this.tokenAmount, sa); - - final String substitutedName = this.tokenName.equals("ChosenType") ? host.getChosenType() : this.tokenName; - - sb.append("Put (").append(finalAmount).append(") ").append(finalPower).append("/").append(finalToughness); - sb.append(" ").append(substitutedName).append(" token"); - if (finalAmount != 1) { - sb.append("s"); - } - sb.append(" onto the battlefield"); - - if (this.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(); - } - - /** - *

- * doResolve. - *

- * - * @param sa - * a {@link forge.card.spellability.SpellAbility} object. - */ - private void doResolve(final SpellAbility sa) { - final Card host = this.abilityFactory.getHostCard(); - String imageName = ""; - Player controller; - String cost = ""; - // Construct colors - final String[] substitutedColors = Arrays.copyOf(this.tokenColors, this.tokenColors.length); - for (int i = 0; i < substitutedColors.length; i++) { - if (substitutedColors[i].equals("ChosenColor")) { - // this currently only supports 1 chosen color - substitutedColors[i] = host.getChosenColor().get(0); - } - } - String colorDesc = ""; - for (final String col : substitutedColors) { - if (col.equalsIgnoreCase("White")) { - colorDesc += "W "; - } else if (col.equalsIgnoreCase("Blue")) { - colorDesc += "U "; - } else if (col.equalsIgnoreCase("Black")) { - colorDesc += "B "; - } else if (col.equalsIgnoreCase("Red")) { - colorDesc += "R "; - } else if (col.equalsIgnoreCase("Green")) { - colorDesc += "G "; - } else if (col.equalsIgnoreCase("Colorless")) { - colorDesc = "C"; - } - } - if (this.tokenImage.equals("")) { - imageName += colorDesc.replace(" ", "") + " " + this.tokenPower + " " + this.tokenToughness + " " + this.tokenName; - } else { - imageName = this.tokenImage; - } - // System.out.println("AF_Token imageName = " + imageName); - - for (final char c : colorDesc.toCharArray()) { - cost += c + ' '; - } - - cost = colorDesc.replace('C', '1').trim(); - - controller = AbilityFactory.getDefinedPlayers(this.abilityFactory.getHostCard(), this.tokenOwner, sa).get(0); - - final int finalPower = AbilityFactory.calculateAmount(this.abilityFactory.getHostCard(), this.tokenPower, sa); - final int finalToughness = AbilityFactory.calculateAmount(this.abilityFactory.getHostCard(), - this.tokenToughness, sa); - final int finalAmount = AbilityFactory.calculateAmount(this.abilityFactory.getHostCard(), this.tokenAmount, sa); - - final String[] substitutedTypes = Arrays.copyOf(this.tokenTypes, this.tokenTypes.length); - for (int i = 0; i < substitutedTypes.length; i++) { - if (substitutedTypes[i].equals("ChosenType")) { - substitutedTypes[i] = host.getChosenType(); - } - } - final String substitutedName = this.tokenName.equals("ChosenType") ? host.getChosenType() : this.tokenName; - - final String remember = this.abilityFactory.getMapParams().get("RememberTokens"); - for (int i = 0; i < finalAmount; i++) { - final List tokens = CardFactoryUtil.makeToken(substitutedName, imageName, controller, cost, - substitutedTypes, finalPower, finalToughness, this.tokenKeywords); - - // Grant abilities - if (this.tokenAbilities != null) { - final AbilityFactory af = new AbilityFactory(); - for (final String s : this.tokenAbilities) { - final String actualAbility = this.abilityFactory.getHostCard().getSVar(s); - for (final Card c : tokens) { - final SpellAbility grantedAbility = af.getAbility(actualAbility, c); - c.addSpellAbility(grantedAbility); - } - } - } - - // Grant triggers - if (this.tokenTriggers != null) { - - for (final String s : this.tokenTriggers) { - final String actualTrigger = this.abilityFactory.getHostCard().getSVar(s); - - for (final Card c : tokens) { - - final Trigger parsedTrigger = TriggerHandler.parseTrigger(actualTrigger, c, true); - final String ability = this.abilityFactory.getHostCard().getSVar( - parsedTrigger.getMapParams().get("Execute")); - parsedTrigger.setOverridingAbility(new AbilityFactory().getAbility(ability, c)); - c.addTrigger(parsedTrigger); - } - } - } - - // Grant SVars - if (this.tokenSVars != null) { - for (final String s : this.tokenSVars) { - String actualSVar = this.abilityFactory.getHostCard().getSVar(s); - String name = s; - if (actualSVar.startsWith("SVar")) { - actualSVar = actualSVar.split("SVar:")[1]; - name = actualSVar.split(":")[0]; - actualSVar = actualSVar.split(":")[1]; - } - for (final Card c : tokens) { - c.setSVar(name, actualSVar); - } - } - } - - // Grant static abilities - if (this.tokenStaticAbilities != null) { - for (final String s : this.tokenStaticAbilities) { - final String actualAbility = this.abilityFactory.getHostCard().getSVar(s); - for (final Card c : tokens) { - c.addStaticAbility(actualAbility); - } - } - } - - for (final Card c : tokens) { - if (this.tokenTapped) { - c.setTapped(true); - } - if (this.tokenAttacking) { - Singletons.getModel().getGame().getCombat().addAttacker(c); - } - if (remember != null) { - Singletons.getModel().getGame().getCardState(sa.getSourceCard()).addRemembered(c); - } - if (this.abilityFactory.getMapParams().get("RememberSource") != null) { - Singletons.getModel().getGame().getCardState(c).addRemembered(host); - } - } - } - } -} diff --git a/src/main/java/forge/card/abilityfactory/ai/TokenAi.java b/src/main/java/forge/card/abilityfactory/ai/TokenAi.java new file mode 100644 index 00000000000..2dcdb53d6b1 --- /dev/null +++ b/src/main/java/forge/card/abilityfactory/ai/TokenAi.java @@ -0,0 +1,226 @@ +package forge.card.abilityfactory.ai; + +import java.util.Map; +import java.util.Random; + +import forge.Card; +import forge.Singletons; +import forge.card.abilityfactory.AbilityFactory; +import forge.card.abilityfactory.SpellAiLogic; +import forge.card.cost.Cost; +import forge.card.cost.CostUtil; +import forge.card.spellability.SpellAbility; +import forge.card.spellability.Target; +import forge.game.phase.PhaseHandler; +import forge.game.phase.PhaseType; +import forge.game.player.ComputerUtil; +import forge.game.player.Player; +import forge.game.zone.ZoneType; +import forge.util.MyRandom; + +/** + *

+ * AbilityFactory_Token class. + *

+ * + * @author Forge + * @version $Id: AbilityFactoryToken.java 17656 2012-10-22 19:32:56Z Max mtg $ + */ +public class TokenAi extends SpellAiLogic { + + + private String tokenAmount; + private String tokenName; + private String[] tokenTypes; + private String[] tokenKeywords; + private String tokenPower; + private String tokenToughness; + /** + *

+ * Constructor for AbilityFactory_Token. + *

+ * + * @param af + * a {@link forge.card.abilityfactory.AbilityFactory} object. + */ + private void readParameters(final Map mapParams ) { + 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]; + } + + + this.tokenAmount = mapParams.get("TokenAmount"); + this.tokenPower = mapParams.get("TokenPower"); + this.tokenToughness = mapParams.get("TokenToughness"); + this.tokenName = mapParams.get("TokenName"); + this.tokenTypes = mapParams.get("TokenTypes").split(","); + this.tokenKeywords = keywords; + + } + + + + /** + *

+ * tokenCanPlayAI. + *

+ * + * @param sa + * a {@link forge.card.spellability.SpellAbility} object. + * @return a boolean. + */ + @Override + public boolean canPlayAI(Player ai, java.util.Map params, SpellAbility sa) { + final Cost cost = sa.getPayCosts(); + readParameters(params); + + if (ComputerUtil.preventRunAwayActivations(sa)) { + return false; + } + + Player opp = ai.getOpponent(); + for (final String type : this.tokenTypes) { + if (type.equals("Legendary")) { + // Don't kill AIs Legendary tokens + if (ai.getCardsIn(ZoneType.Battlefield, this.tokenName).size() > 0) { + return false; + } + } + } + + boolean haste = false; + boolean oneShot = false; + for (final String kw : this.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; + } + } + + PhaseHandler ph = Singletons.getModel().getGame().getPhaseHandler(); + // Don't generate tokens without haste before main 2 if possible + if (ph.getPhase().isBefore(PhaseType.MAIN2) + && ph.isPlayerTurn(ai) && !haste + && !params.containsKey("ActivationPhases")) { + return false; + } + if ((ph.isPlayerTurn(ai) + || ph.getPhase().isBefore( + PhaseType.COMBAT_DECLARE_ATTACKERS_INSTANT_ABILITY)) + && !params.containsKey("ActivationPhases") && !params.containsKey("PlayerTurn") + && !AbilityFactory.isSorcerySpeed(sa) && !haste) { + return false; + } + if ((ph.getPhase().isAfter(PhaseType.COMBAT_BEGIN) || Singletons.getModel().getGame().getPhaseHandler().isPlayerTurn( + opp)) + && oneShot) { + return false; + } + + // prevent run-away activations - first time will always return true + final Random r = MyRandom.getRandom(); + final Card source = sa.getSourceCard(); + + final Target tgt = sa.getTarget(); + if (tgt != null) { + tgt.resetTargets(); + if (tgt.canOnlyTgtOpponent()) { + tgt.addTarget(opp); + } else { + tgt.addTarget(ai); + } + } + + if (cost != null) { + if (!CostUtil.checkLifeCost(ai, cost, source, 4, null)) { + return false; + } + + if (!CostUtil.checkDiscardCost(ai, cost, source)) { + return false; + } + + if (!CostUtil.checkSacrificeCost(ai, cost, source)) { + return false; + } + + if (!CostUtil.checkRemoveCounterCost(cost, source)) { + return false; + } + } + + if (this.tokenAmount.equals("X") || this.tokenPower.equals("X") || this.tokenToughness.equals("X")) { + int x = AbilityFactory.calculateAmount(sa.getSourceCard(), this.tokenAmount, sa); + if (source.getSVar("X").equals("Count$xPaid")) { + // Set PayX here to maximum value. + x = ComputerUtil.determineLeftoverMana(sa, ai); + source.setSVar("PayX", Integer.toString(x)); + } + if (x <= 0) { + return false; + } + } + + if (AbilityFactory.playReusable(ai, sa)) { + return true; + } + + if (Singletons.getModel().getGame().getPhaseHandler().is(PhaseType.COMBAT_DECLARE_ATTACKERS_INSTANT_ABILITY)) { + return true; + } + if (sa.isAbility()) { + return (r.nextFloat() < .9); + } + + return (r.nextFloat() < .8); + } + + /** + *

+ * tokenDoTriggerAINoCost. + *

+ * + * @param sa + * a {@link forge.card.spellability.SpellAbility} object. + * @param mandatory + * a boolean. + * @return a boolean. + */ + @Override + public boolean doTriggerAINoCost(Player ai, java.util.Map params, SpellAbility sa, boolean mandatory) { + readParameters(params); + final Card source = sa.getSourceCard(); + final Target tgt = sa.getTarget(); + if (tgt != null) { + tgt.resetTargets(); + if (tgt.canOnlyTgtOpponent()) { + tgt.addTarget(ai.getOpponent()); + } else { + tgt.addTarget(ai); + } + } + if (this.tokenAmount.equals("X") || this.tokenPower.equals("X") || this.tokenToughness.equals("X")) { + int x = AbilityFactory.calculateAmount(source, this.tokenAmount, sa); + if (source.getSVar("X").equals("Count$xPaid")) { + // Set PayX here to maximum value. + x = ComputerUtil.determineLeftoverMana(sa, ai); + source.setSVar("PayX", Integer.toString(x)); + } + if (x <= 0) { + return false; + } + } + + return true; + } + + +} \ No newline at end of file diff --git a/src/main/java/forge/card/abilityfactory/effects/TokenEffect.java b/src/main/java/forge/card/abilityfactory/effects/TokenEffect.java new file mode 100644 index 00000000000..8edf5b40d77 --- /dev/null +++ b/src/main/java/forge/card/abilityfactory/effects/TokenEffect.java @@ -0,0 +1,300 @@ +/* + * Forge: Play Magic: the Gathering. + * Copyright (C) 2011 Forge Team + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + */ +package forge.card.abilityfactory.effects; + +import java.util.Arrays; +import java.util.HashMap; +import java.util.List; +import java.util.Map; + +import forge.Card; + +import forge.Singletons; +import forge.card.abilityfactory.AbilityFactory; +import forge.card.abilityfactory.SpellEffect; +import forge.card.cardfactory.CardFactoryUtil; +import forge.card.spellability.AbilityActivated; +import forge.card.spellability.AbilitySub; +import forge.card.spellability.Spell; +import forge.card.spellability.SpellAbility; +import forge.card.trigger.Trigger; +import forge.card.trigger.TriggerHandler; +import forge.game.player.Player; + +public class TokenEffect extends SpellEffect { + + private String tokenOwner; + private String[] tokenColors; + private String tokenImage; + private String[] tokenAbilities; + private String[] tokenTriggers; + private String[] tokenSVars; + private String[] tokenStaticAbilities; + private boolean tokenTapped; + private boolean tokenAttacking; + private String tokenAmount; + private String tokenToughness; + private String tokenPower; + private String[] tokenTypes; + private String tokenName; + private String[] tokenKeywords; + + private void readParameters(final Map mapParams) { + 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 = ""; + } + + this.tokenTapped = mapParams.containsKey("TokenTapped") && mapParams.get("TokenTapped").equals("True"); + this.tokenAttacking = mapParams.containsKey("TokenAttacking") && mapParams.get("TokenAttacking").equals("True"); + + if (mapParams.containsKey("TokenAbilities")) { + this.tokenAbilities = mapParams.get("TokenAbilities").split(","); + } else { + this.tokenAbilities = null; + } + if (mapParams.containsKey("TokenTriggers")) { + this.tokenTriggers = mapParams.get("TokenTriggers").split(","); + } else { + this.tokenTriggers = null; + } + if (mapParams.containsKey("TokenSVars")) { + this.tokenSVars = mapParams.get("TokenSVars").split(","); + } else { + this.tokenSVars = null; + } + if (mapParams.containsKey("TokenStaticAbilities")) { + this.tokenStaticAbilities = mapParams.get("TokenStaticAbilities").split(","); + } else { + this.tokenStaticAbilities = null; + } + + this.tokenAmount = mapParams.get("TokenAmount"); + this.tokenPower = mapParams.get("TokenPower"); + this.tokenToughness = mapParams.get("TokenToughness"); + this.tokenName = mapParams.get("TokenName"); + this.tokenTypes = mapParams.get("TokenTypes").split(","); + this.tokenColors = mapParams.get("TokenColors").split(","); + this.tokenKeywords = keywords; + this.tokenImage = image; + if (mapParams.containsKey("TokenOwner")) { + this.tokenOwner = mapParams.get("TokenOwner"); + } else { + this.tokenOwner = "You"; + } + } + + @Override + public String getStackDescription(java.util.Map params, SpellAbility sa) { + final StringBuilder sb = new StringBuilder(); + final Card host = sa.getSourceCard(); + + readParameters(params); + + if (sa instanceof AbilitySub) { + sb.append(" "); + } else { + sb.append(host.getName()).append(" - "); + } + + if (params.containsKey("StackDescription")) { + sb.append(params.get("StackDescription")); + } + else { + + final int finalPower = AbilityFactory.calculateAmount(host, this.tokenPower, sa); + final int finalToughness = AbilityFactory.calculateAmount(host, this.tokenToughness, sa); + final int finalAmount = AbilityFactory.calculateAmount(host, this.tokenAmount, sa); + + final String substitutedName = this.tokenName.equals("ChosenType") ? host.getChosenType() : this.tokenName; + + sb.append("Put (").append(finalAmount).append(") ").append(finalPower).append("/").append(finalToughness); + sb.append(" ").append(substitutedName).append(" token"); + if (finalAmount != 1) { + sb.append("s"); + } + sb.append(" onto the battlefield"); + + if (this.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(); + } + + /** + *

+ * doResolve. + *

+ * + * @param sa + * a {@link forge.card.spellability.SpellAbility} object. + */ + @Override + public void resolve(java.util.Map params, SpellAbility sa) { + final Card host = sa.getSourceCard(); + readParameters(params); + + String imageName = ""; + Player controller; + String cost = ""; + // Construct colors + final String[] substitutedColors = Arrays.copyOf(this.tokenColors, this.tokenColors.length); + for (int i = 0; i < substitutedColors.length; i++) { + if (substitutedColors[i].equals("ChosenColor")) { + // this currently only supports 1 chosen color + substitutedColors[i] = host.getChosenColor().get(0); + } + } + String colorDesc = ""; + for (final String col : substitutedColors) { + if (col.equalsIgnoreCase("White")) { + colorDesc += "W "; + } else if (col.equalsIgnoreCase("Blue")) { + colorDesc += "U "; + } else if (col.equalsIgnoreCase("Black")) { + colorDesc += "B "; + } else if (col.equalsIgnoreCase("Red")) { + colorDesc += "R "; + } else if (col.equalsIgnoreCase("Green")) { + colorDesc += "G "; + } else if (col.equalsIgnoreCase("Colorless")) { + colorDesc = "C"; + } + } + if (this.tokenImage.equals("")) { + imageName += colorDesc.replace(" ", "") + " " + this.tokenPower + " " + this.tokenToughness + " " + this.tokenName; + } else { + imageName = this.tokenImage; + } + // System.out.println("AF_Token imageName = " + imageName); + + for (final char c : colorDesc.toCharArray()) { + cost += c + ' '; + } + + cost = colorDesc.replace('C', '1').trim(); + + controller = AbilityFactory.getDefinedPlayers(host, this.tokenOwner, sa).get(0); + + final int finalPower = AbilityFactory.calculateAmount(host, this.tokenPower, sa); + final int finalToughness = AbilityFactory.calculateAmount(host, this.tokenToughness, sa); + final int finalAmount = AbilityFactory.calculateAmount(host, this.tokenAmount, sa); + + final String[] substitutedTypes = Arrays.copyOf(this.tokenTypes, this.tokenTypes.length); + for (int i = 0; i < substitutedTypes.length; i++) { + if (substitutedTypes[i].equals("ChosenType")) { + substitutedTypes[i] = host.getChosenType(); + } + } + final String substitutedName = this.tokenName.equals("ChosenType") ? host.getChosenType() : this.tokenName; + + final String remember = params.get("RememberTokens"); + for (int i = 0; i < finalAmount; i++) { + final List tokens = CardFactoryUtil.makeToken(substitutedName, imageName, controller, cost, + substitutedTypes, finalPower, finalToughness, this.tokenKeywords); + + // Grant abilities + if (this.tokenAbilities != null) { + final AbilityFactory af = new AbilityFactory(); + for (final String s : this.tokenAbilities) { + final String actualAbility = host.getSVar(s); + for (final Card c : tokens) { + final SpellAbility grantedAbility = af.getAbility(actualAbility, c); + c.addSpellAbility(grantedAbility); + } + } + } + + // Grant triggers + if (this.tokenTriggers != null) { + + for (final String s : this.tokenTriggers) { + final String actualTrigger = host.getSVar(s); + + for (final Card c : tokens) { + + final Trigger parsedTrigger = TriggerHandler.parseTrigger(actualTrigger, c, true); + final String ability = host.getSVar(parsedTrigger.getMapParams().get("Execute")); + parsedTrigger.setOverridingAbility(new AbilityFactory().getAbility(ability, c)); + c.addTrigger(parsedTrigger); + } + } + } + + // Grant SVars + if (this.tokenSVars != null) { + for (final String s : this.tokenSVars) { + String actualSVar = host.getSVar(s); + String name = s; + if (actualSVar.startsWith("SVar")) { + actualSVar = actualSVar.split("SVar:")[1]; + name = actualSVar.split(":")[0]; + actualSVar = actualSVar.split(":")[1]; + } + for (final Card c : tokens) { + c.setSVar(name, actualSVar); + } + } + } + + // Grant static abilities + if (this.tokenStaticAbilities != null) { + for (final String s : this.tokenStaticAbilities) { + final String actualAbility = host.getSVar(s); + for (final Card c : tokens) { + c.addStaticAbility(actualAbility); + } + } + } + + for (final Card c : tokens) { + if (this.tokenTapped) { + c.setTapped(true); + } + if (this.tokenAttacking) { + Singletons.getModel().getGame().getCombat().addAttacker(c); + } + if (remember != null) { + Singletons.getModel().getGame().getCardState(sa.getSourceCard()).addRemembered(c); + } + if (params.get("RememberSource") != null) { + Singletons.getModel().getGame().getCardState(c).addRemembered(host); + } + } + } + } +}