- Added the new static ability CantAttackUnless.

This commit is contained in:
Sloth
2012-09-26 18:28:57 +00:00
parent eb8ca4bd29
commit d94843e38c
15 changed files with 230 additions and 121 deletions

1
.gitattributes vendored
View File

@@ -12177,6 +12177,7 @@ src/main/java/forge/card/spellability/TargetChoices.java svneol=native#text/plai
src/main/java/forge/card/spellability/TargetSelection.java svneol=native#text/plain src/main/java/forge/card/spellability/TargetSelection.java svneol=native#text/plain
src/main/java/forge/card/spellability/package-info.java svneol=native#text/plain src/main/java/forge/card/spellability/package-info.java svneol=native#text/plain
src/main/java/forge/card/staticability/StaticAbility.java svneol=native#text/plain src/main/java/forge/card/staticability/StaticAbility.java svneol=native#text/plain
src/main/java/forge/card/staticability/StaticAbilityCantAttackBlock.java -text
src/main/java/forge/card/staticability/StaticAbilityCantBeCast.java svneol=native#text/plain src/main/java/forge/card/staticability/StaticAbilityCantBeCast.java svneol=native#text/plain
src/main/java/forge/card/staticability/StaticAbilityCantTarget.java -text src/main/java/forge/card/staticability/StaticAbilityCantTarget.java -text
src/main/java/forge/card/staticability/StaticAbilityContinuous.java svneol=native#text/plain src/main/java/forge/card/staticability/StaticAbilityContinuous.java svneol=native#text/plain

View File

@@ -4,7 +4,7 @@ Types:Enchantment Aura
Text:no text Text:no text
K:Enchant creature K:Enchant creature
A:SP$ Attach | Cost$ W | ValidTgts$ Creature | AILogic$ Curse A:SP$ Attach | Cost$ W | ValidTgts$ Creature | AILogic$ Curse
K:Creatures can't attack unless their controller pays:Creature.AttachedBy:3:Enchanted creature can't attack unless its controller pays 3. S:Mode$ CantAttackUnless | ValidCard$ Creature.AttachedBy | Cost$ 3 | Description$ Enchanted creature can't attack unless its controller pays 3.
SVar:Rarity:Common SVar:Rarity:Common
SVar:Picture:http://www.wizards.com/global/images/magic/general/brainwash.jpg SVar:Picture:http://www.wizards.com/global/images/magic/general/brainwash.jpg
SetInfo:5ED|Common|http://magiccards.info/scans/en/5e/289.jpg SetInfo:5ED|Common|http://magiccards.info/scans/en/5e/289.jpg

View File

@@ -2,7 +2,7 @@ Name:Collective Restraint
ManaCost:3 U ManaCost:3 U
Types:Enchantment Types:Enchantment
Text:no text Text:no text
K:Creatures can't attack unless their controller pays:Creature.YouDontCtrl:X:Domain - Creatures can't attack you unless their controller pays X for each creature he or she controls that's attacking you, where X is the number of basic land types among lands you control. S:Mode$ CantAttackUnless | ValidCard$ Creature | Target$ You | Cost$ X | Description$ Domain - Creatures can't attack you unless their controller pays X for each creature he or she controls that's attacking you, where X is the number of basic land types among lands you control.
SVar:X:Count$Domain SVar:X:Count$Domain
SVar:Rarity:Rare SVar:Rarity:Rare
SVar:Picture:http://www.wizards.com/global/images/magic/general/collective_restraint.jpg SVar:Picture:http://www.wizards.com/global/images/magic/general/collective_restraint.jpg

View File

@@ -4,7 +4,7 @@ Types:Enchantment
Text:no text Text:no text
K:Cumulative upkeep:1 K:Cumulative upkeep:1
S:Mode$ Continuous | Affected$ Creature.Black+YouDontCtrl | AddHiddenKeyword$ HIDDEN CARDNAME can't attack. | Description$ Black creatures can't attack you. S:Mode$ Continuous | Affected$ Creature.Black+YouDontCtrl | AddHiddenKeyword$ HIDDEN CARDNAME can't attack. | Description$ Black creatures can't attack you.
K:Creatures can't attack unless their controller pays:Creature.nonBlack+YouDontCtrl:2:Nonblack creatures can't attack you unless their controller pays 2 for each creature he or she controls that's attacking you. S:Mode$ CantAttackUnless | ValidCard$ Creature.nonBlack | Target$ You | Cost$ 2 | Description$ Nonblack creatures can't attack you unless their controller pays 2 for each creature he or she controls that's attacking you.
SVar:Rarity:Uncommon SVar:Rarity:Uncommon
SVar:Picture:http://www.wizards.com/global/images/magic/general/elephant_grass.jpg SVar:Picture:http://www.wizards.com/global/images/magic/general/elephant_grass.jpg
SetInfo:VIS|Uncommon|http://magiccards.info/scans/en/vi/54.jpg SetInfo:VIS|Uncommon|http://magiccards.info/scans/en/vi/54.jpg

View File

@@ -2,7 +2,7 @@ Name:Ghostly Prison
ManaCost:2 W ManaCost:2 W
Types:Enchantment Types:Enchantment
Text:no text Text:no text
K:Creatures can't attack unless their controller pays:Creature.YouDontCtrl:2:Creatures can't attack you unless their controller pays 2 for each creature he or she controls that's attacking you. S:Mode$ CantAttackUnless | ValidCard$ Creature | Target$ You | Cost$ 2 | Description$ Creatures can't attack you unless their controller pays 2 for each creature he or she controls that's attacking you.
SVar:Rarity:Uncommon SVar:Rarity:Uncommon
SVar:Picture:http://www.wizards.com/global/images/magic/general/ghostly_prison.jpg SVar:Picture:http://www.wizards.com/global/images/magic/general/ghostly_prison.jpg
SetInfo:CHK|Uncommon|http://magiccards.info/scans/en/chk/10.jpg SetInfo:CHK|Uncommon|http://magiccards.info/scans/en/chk/10.jpg

View File

@@ -4,7 +4,7 @@ Types:Artifact Creature Construct
Text:no text Text:no text
PT:0/0 PT:0/0
K:CARDNAME can't block. K:CARDNAME can't block.
K:Creatures can't attack unless their controller pays:Card.Self:Y:CARDNAME can't attack unless you pay 1 for each +1/+1 counter on it. S:Mode$ CantAttackUnless | ValidCard$ Card.Self | Cost$ Y | Description$ CARDNAME can't attack unless you pay 1 for each +1/+1 counter on it.
K:etbCounter:P1P1:X K:etbCounter:P1P1:X
SVar:X:Count$xPaid SVar:X:Count$xPaid
SVar:Y:Count$NumCounters.P1P1 SVar:Y:Count$NumCounters.P1P1

View File

@@ -2,7 +2,7 @@ Name:Propaganda
ManaCost:2 U ManaCost:2 U
Types:Enchantment Types:Enchantment
Text:no text Text:no text
K:Creatures can't attack unless their controller pays:Creature.YouDontCtrl:2:Creatures can't attack you unless their controller pays 2 for each creature he or she controls that's attacking you. S:Mode$ CantAttackUnless | ValidCard$ Creature | Target$ You | Cost$ 2 | Description$ Creatures can't attack you unless their controller pays 2 for each creature he or she controls that's attacking you.
SVar:Rarity:Uncommon SVar:Rarity:Uncommon
SVar:Picture:http://www.wizards.com/global/images/magic/general/propaganda.jpg SVar:Picture:http://www.wizards.com/global/images/magic/general/propaganda.jpg
SetInfo:TMP|Uncommon|http://magiccards.info/scans/en/tp/80.jpg SetInfo:TMP|Uncommon|http://magiccards.info/scans/en/tp/80.jpg

View File

@@ -2,7 +2,7 @@ Name:Windborn Muse
ManaCost:3 W ManaCost:3 W
Types:Creature Spirit Types:Creature Spirit
Text:no text Text:no text
K:Creatures can't attack unless their controller pays:Creature.YouDontCtrl:2:Creatures can't attack you unless their controller pays 2 for each creature he or she controls that's attacking you. S:Mode$ CantAttackUnless | ValidCard$ Creature | Target$ You | Cost$ 2 | Description$ Creatures can't attack you unless their controller pays 2 for each creature he or she controls that's attacking you.
PT:2/3 PT:2/3
K:Flying K:Flying
SVar:Rarity:Rare SVar:Rarity:Rare

View File

@@ -34,7 +34,7 @@ import forge.card.cost.CostMana;
import forge.card.cost.CostPutCounter; import forge.card.cost.CostPutCounter;
import forge.card.cost.CostReturn; import forge.card.cost.CostReturn;
import forge.card.cost.CostSacrifice; import forge.card.cost.CostSacrifice;
import forge.card.mana.ManaCost; import forge.card.cost.CostUtil;
import forge.card.spellability.Ability; import forge.card.spellability.Ability;
import forge.card.spellability.AbilityMana; import forge.card.spellability.AbilityMana;
import forge.card.spellability.AbilitySub; import forge.card.spellability.AbilitySub;
@@ -447,7 +447,8 @@ public final class GameActionUtil {
* a {@link forge.Command} object. * a {@link forge.Command} object.
* @param sourceAbility TODO * @param sourceAbility TODO
*/ */
public static void payCostDuringAbilityResolve(final SpellAbility ability, final Cost cost, final Command paid, final Command unpaid, SpellAbility sourceAbility) { public static void payCostDuringAbilityResolve(final SpellAbility ability, final Cost cost, final Command paid,
final Command unpaid, SpellAbility sourceAbility) {
final Card source = ability.getSourceCard(); final Card source = ability.getSourceCard();
final ArrayList<CostPart> parts = cost.getCostParts(); final ArrayList<CostPart> parts = cost.getCostParts();
if (parts.size() > 1) { if (parts.size() > 1) {
@@ -1687,24 +1688,7 @@ public final class GameActionUtil {
public static Cost combineCosts(SpellAbility sa, String additionalCost) { public static Cost combineCosts(SpellAbility sa, String additionalCost) {
final Cost newCost = new Cost(sa.getSourceCard(), additionalCost, false); final Cost newCost = new Cost(sa.getSourceCard(), additionalCost, false);
Cost oldCost = sa.getPayCosts(); Cost oldCost = sa.getPayCosts();
if (sa.getPayCosts() != null) { return CostUtil.combineCosts(oldCost, newCost);
for (final CostPart part : oldCost.getCostParts()) {
if (!(part instanceof CostMana)) {
newCost.getCostParts().add(part);
} else {
CostMana newCostMana = newCost.getCostMana();
if (newCostMana != null) {
ManaCost oldManaCost = new ManaCost(part.toString());
newCostMana.setXMana(oldManaCost.getXcounter() + newCostMana.getXMana());
oldManaCost.combineManaCost(newCostMana.toString());
newCostMana.setMana(oldManaCost.toString(false));
} else {
newCost.getCostParts().add(part);
}
}
}
}
return newCost;
} }
/** /**

View File

@@ -3433,48 +3433,6 @@ public class CardFactoryUtil {
return maxColor; return maxColor;
} }
/**
* <p>
* Get the total cost to pay for an attacker c, due to cards like
* Propaganda, Ghostly Prison, Collective Restraint, ...
* </p>
*
* @param c
* a {@link forge.Card} object.
* @return a {@link java.lang.String} object.
*/
public static String getPropagandaCost(final Card c) {
int cost = 0;
final CardList list = AllZoneUtil.getCardsIn(ZoneType.Battlefield);
for (final Card card : list) {
if (card.hasStartOfKeyword("Creatures can't attack unless their controller pays")) {
final int keywordPosition = card
.getKeywordPosition("Creatures can't attack unless their controller pays");
final String parse = card.getKeyword().get(keywordPosition).toString();
final String[] k = parse.split(":");
final String[] restrictions = k[1].split(",");
if (!c.isValid(restrictions, card.getController(), card)) {
continue;
}
final String costString = k[2];
if (costString.equals("X")) {
cost += CardFactoryUtil.xCount(card, card.getSVar("X"));
} else if (costString.equals("Y")) {
cost += CardFactoryUtil.xCount(card, card.getSVar("Y"));
} else {
cost += Integer.parseInt(k[2]);
}
}
}
final String s = Integer.toString(cost);
return s;
}
/** /**
* <p> * <p>
* getUsableManaSources. * getUsableManaSources.

View File

@@ -184,7 +184,11 @@ public class Cost {
public Cost(final Card card, String parse, final boolean bAbility) { public Cost(final Card card, String parse, final boolean bAbility) {
this.isAbility = bAbility; this.isAbility = bAbility;
// when adding new costs for cost string, place them here // when adding new costs for cost string, place them here
this.name = card.getName(); String name = "";
if (card != null) {
name = card.getName();
}
this.name = name;
while (parse.contains(Cost.TAP_X_STR)) { while (parse.contains(Cost.TAP_X_STR)) {
final String[] splitStr = this.abCostParse(parse, Cost.TAP_X_STR, 3); final String[] splitStr = this.abCostParse(parse, Cost.TAP_X_STR, 3);

View File

@@ -24,6 +24,7 @@ import forge.Card;
import forge.CardList; import forge.CardList;
import forge.Counters; import forge.Counters;
import forge.card.abilityfactory.AbilityFactory; import forge.card.abilityfactory.AbilityFactory;
import forge.card.mana.ManaCost;
import forge.card.spellability.SpellAbility; import forge.card.spellability.SpellAbility;
import forge.control.input.Input; import forge.control.input.Input;
import forge.game.player.ComputerUtil; import forge.game.player.ComputerUtil;
@@ -456,4 +457,36 @@ public class CostUtil {
// Just a shortcut.. // Just a shortcut..
AllZone.getInputControl().setInput(in, true); AllZone.getInputControl().setInput(in, true);
} }
public static Cost combineCosts(Cost cost1, Cost cost2) {
if (cost1 == null) {
if (cost2 == null) {
return null;
} else {
return cost2;
}
}
if (cost2 == null) {
return cost1;
}
for (final CostPart part : cost1.getCostParts()) {
if (!(part instanceof CostMana)) {
cost2.getCostParts().add(part);
} else {
CostMana newCostMana = cost2.getCostMana();
if (newCostMana != null) {
ManaCost oldManaCost = new ManaCost(part.toString());
newCostMana.setXMana(oldManaCost.getXcounter() + newCostMana.getXMana());
oldManaCost.combineManaCost(newCostMana.toString());
newCostMana.setMana(oldManaCost.toString(false));
} else {
cost2.getCostParts().add(part);
}
}
}
return cost2;
}
} }

View File

@@ -27,6 +27,7 @@ import forge.Card;
import forge.GameEntity; import forge.GameEntity;
import forge.Singletons; import forge.Singletons;
import forge.card.abilityfactory.AbilityFactory; import forge.card.abilityfactory.AbilityFactory;
import forge.card.cost.Cost;
import forge.card.mana.ManaCost; import forge.card.mana.ManaCost;
import forge.card.spellability.SpellAbility; import forge.card.spellability.SpellAbility;
import forge.game.phase.PhaseType; import forge.game.phase.PhaseType;
@@ -408,6 +409,39 @@ public class StaticAbility {
return false; return false;
} }
/**
* Apply ability.
*
* @param mode
* the mode
* @param card
* the card
* @param target
* the target
* @return true, if successful
*/
public final Cost getCostAbility(final String mode, final Card card, final GameEntity target) {
// don't apply the ability if it hasn't got the right mode
if (!this.mapParams.get("Mode").equals(mode)) {
return null;
}
if (this.isSuppressed() || !this.checkConditions()) {
return null;
}
if (mode.equals("CantAttackUnless")) {
return StaticAbilityCantAttackBlock.applyCantAttackUnlessAbility(this, card, target);
}
if (mode.equals("CantBlockUnless")) {
return StaticAbilityCantAttackBlock.applyCantBlockUnlessAbility(this, card);
}
return null;
}
/** /**
* Check conditions. * Check conditions.
* *

View File

@@ -0,0 +1,89 @@
/*
* 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 <http://www.gnu.org/licenses/>.
*/
package forge.card.staticability;
import java.util.HashMap;
import forge.Card;
import forge.GameEntity;
import forge.card.cardfactory.CardFactoryUtil;
import forge.card.cost.Cost;
/**
* The Class StaticAbility_CantBeCast.
*/
public class StaticAbilityCantAttackBlock{
/**
* TODO Write javadoc for this method.
*
* @param stAb
* a StaticAbility
* @param card
* the card
* @return a Cost
*/
public static Cost applyCantAttackUnlessAbility(final StaticAbility stAb, final Card card, final GameEntity target) {
final HashMap<String, String> params = stAb.getMapParams();
final Card hostCard = stAb.getHostCard();
if (params.containsKey("ValidCard")
&& !card.isValid(params.get("ValidCard").split(","), hostCard.getController(), hostCard)) {
return null;
}
if (params.containsKey("Target")
&& !target.isValid(params.get("Target").split(","), hostCard.getController(), hostCard)) {
return null;
}
String costString = params.get("Cost");
if ("X".equals(costString)) {
costString = Integer.toString(CardFactoryUtil.xCount(hostCard, hostCard.getSVar("X")));
} else if ("Y".equals(costString)) {
costString = Integer.toString(CardFactoryUtil.xCount(hostCard, hostCard.getSVar("Y")));
}
final Cost cost = new Cost(hostCard, costString, true);
return cost;
}
/**
* TODO Write javadoc for this method.
*
* @param stAb
* a StaticAbility
* @param card
* the card
* @return a Cost
*/
public static Cost applyCantBlockUnlessAbility(final StaticAbility stAb, final Card card) {
final HashMap<String, String> params = stAb.getMapParams();
final Card hostCard = stAb.getHostCard();
if (params.containsKey("ValidCard")
&& !card.isValid(params.get("ValidCard").split(","), hostCard.getController(), hostCard)) {
return null;
}
final Cost cost = new Cost(hostCard, params.get("Cost"), true);
return cost;
}
}

View File

@@ -43,14 +43,16 @@ import forge.card.TriggerReplacementBase;
import forge.card.abilityfactory.AbilityFactory; import forge.card.abilityfactory.AbilityFactory;
import forge.card.abilityfactory.AbilityFactorySacrifice; import forge.card.abilityfactory.AbilityFactorySacrifice;
import forge.card.cardfactory.CardFactoryUtil; import forge.card.cardfactory.CardFactoryUtil;
import forge.card.cost.Cost;
import forge.card.cost.CostUtil;
import forge.card.spellability.Ability; import forge.card.spellability.Ability;
import forge.card.spellability.AbilityActivated; import forge.card.spellability.AbilityActivated;
import forge.card.spellability.AbilityStatic;
import forge.card.spellability.SpellAbility; import forge.card.spellability.SpellAbility;
import forge.card.staticability.StaticAbility; import forge.card.staticability.StaticAbility;
import forge.card.trigger.Trigger; import forge.card.trigger.Trigger;
import forge.card.trigger.TriggerHandler; import forge.card.trigger.TriggerHandler;
import forge.card.trigger.TriggerType; import forge.card.trigger.TriggerType;
import forge.control.input.InputPayManaCostAbility;
import forge.game.player.ComputerUtil; import forge.game.player.ComputerUtil;
import forge.game.player.ComputerUtilBlock; import forge.game.player.ComputerUtilBlock;
import forge.game.player.Player; import forge.game.player.Player;
@@ -2650,8 +2652,16 @@ public class CombatUtil {
* a boolean. * a boolean.
*/ */
public static void checkPropagandaEffects(final Card c, final boolean bLast) { public static void checkPropagandaEffects(final Card c, final boolean bLast) {
final String cost = CardFactoryUtil.getPropagandaCost(c); Cost attackCost = new Cost(c, "0", true);
if (cost.equals("0")) { // Sort abilities to apply them in proper order
for (Card card : AllZoneUtil.getCardsIn(ZoneType.Battlefield)) {
final ArrayList<StaticAbility> staticAbilities = card.getStaticAbilities();
for (final StaticAbility stAb : staticAbilities) {
Cost additionalCost = stAb.getCostAbility("CantAttackUnless", c, AllZone.getCombat().getDefenderByAttacker(c));
attackCost = CostUtil.combineCosts(attackCost, additionalCost);
}
}
if (attackCost.toSimpleString().equals("")) {
if (!c.hasKeyword("Vigilance")) { if (!c.hasKeyword("Vigilance")) {
c.tap(); c.tap();
} }
@@ -2667,63 +2677,59 @@ public class CombatUtil {
final PhaseType phase = Singletons.getModel().getGameState().getPhaseHandler().getPhase(); final PhaseType phase = Singletons.getModel().getGameState().getPhaseHandler().getPhase();
if (phase == PhaseType.COMBAT_DECLARE_ATTACKERS || phase == PhaseType.COMBAT_DECLARE_ATTACKERS_INSTANT_ABILITY) { if (phase == PhaseType.COMBAT_DECLARE_ATTACKERS || phase == PhaseType.COMBAT_DECLARE_ATTACKERS_INSTANT_ABILITY) {
if (!cost.equals("0")) { final Ability ability = new AbilityStatic(c, attackCost, null) {
final Ability ability = new Ability(c, cost) { @Override
@Override public void resolve() {
public void resolve() {
} }
}; };
final Command unpaidCommand = new Command() { final Command unpaidCommand = new Command() {
private static final long serialVersionUID = -6483405139208343935L; private static final long serialVersionUID = -6483405139208343935L;
@Override @Override
public void execute() { public void execute() {
AllZone.getCombat().removeFromCombat(crd); AllZone.getCombat().removeFromCombat(crd);
if (bLast) {
PhaseUtil.handleAttackingTriggers();
}
}
};
final Command paidCommand = new Command() {
private static final long serialVersionUID = -8303368287601871955L;
@Override
public void execute() {
// if Propaganda is paid, tap this card
if (!crd.hasKeyword("Vigilance")) {
crd.tap();
}
if (bLast) {
PhaseUtil.handleAttackingTriggers();
}
}
};
if (c.getController().isHuman()) {
AllZone.getInputControl().setInput(
new InputPayManaCostAbility(c + " - Pay to Attack\r\n", ability.getManaCost(), paidCommand,
unpaidCommand));
} else { // computer
if (ComputerUtil.canPayCost(ability)) {
ComputerUtil.playNoStack(ability);
if (!crd.hasKeyword("Vigilance")) {
crd.tap();
}
} else {
// TODO remove the below line after Propaganda occurs
// during Declare_Attackers
AllZone.getCombat().removeFromCombat(crd);
}
if (bLast) { if (bLast) {
PhaseUtil.handleAttackingTriggers(); PhaseUtil.handleAttackingTriggers();
} }
} }
};
final Command paidCommand = new Command() {
private static final long serialVersionUID = -8303368287601871955L;
@Override
public void execute() {
// if Propaganda is paid, tap this card
if (!crd.hasKeyword("Vigilance")) {
crd.tap();
}
if (bLast) {
PhaseUtil.handleAttackingTriggers();
}
}
};
if (c.getController().isHuman()) {
GameActionUtil.payCostDuringAbilityResolve(ability, attackCost, paidCommand, unpaidCommand, null);
} else { // computer
if (ComputerUtil.canPayCost(ability)) {
ComputerUtil.playNoStack(ability);
if (!crd.hasKeyword("Vigilance")) {
crd.tap();
}
} else {
// TODO remove the below line after Propaganda occurs
// during Declare_Attackers
AllZone.getCombat().removeFromCombat(crd);
}
if (bLast) {
PhaseUtil.handleAttackingTriggers();
}
} }
} }
} }