From 20db0b8c9cb7a3a4e98156e818f39e2088330fb9 Mon Sep 17 00:00:00 2001 From: LevelX2 Date: Mon, 24 Jun 2013 21:33:19 +0200 Subject: [PATCH] * Added Prowl ability. --- .../condition/common/ProwlCondition.java | 70 ++++++ .../mage/abilities/keyword/ProwlAbility.java | 199 ++++++++++++++++++ .../mage/watchers/common/ProwlWatcher.java | 97 +++++++++ 3 files changed, 366 insertions(+) create mode 100644 Mage/src/mage/abilities/condition/common/ProwlCondition.java create mode 100644 Mage/src/mage/abilities/keyword/ProwlAbility.java create mode 100644 Mage/src/mage/watchers/common/ProwlWatcher.java diff --git a/Mage/src/mage/abilities/condition/common/ProwlCondition.java b/Mage/src/mage/abilities/condition/common/ProwlCondition.java new file mode 100644 index 0000000000..86006f1a5d --- /dev/null +++ b/Mage/src/mage/abilities/condition/common/ProwlCondition.java @@ -0,0 +1,70 @@ +/* +* Copyright 2010 BetaSteward_at_googlemail.com. All rights reserved. +* +* Redistribution and use in source and binary forms, with or without modification, are +* permitted provided that the following conditions are met: +* +* 1. Redistributions of source code must retain the above copyright notice, this list of +* conditions and the following disclaimer. +* +* 2. Redistributions in binary form must reproduce the above copyright notice, this list +* of conditions and the following disclaimer in the documentation and/or other materials +* provided with the distribution. +* +* THIS SOFTWARE IS PROVIDED BY BetaSteward_at_googlemail.com ``AS IS'' AND ANY EXPRESS OR IMPLIED +* WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND +* FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL BetaSteward_at_googlemail.com OR +* CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR +* CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR +* SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON +* ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING +* NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF +* ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +* +* The views and conclusions contained in the software and documentation are those of the +* authors and should not be interpreted as representing official policies, either expressed +* or implied, of BetaSteward_at_googlemail.com. +*/ + +package mage.abilities.condition.common; + +import mage.abilities.Ability; +import mage.abilities.condition.Condition; +import mage.abilities.keyword.ProwlAbility; +import mage.cards.Card; +import mage.game.Game; + +/** + * Checks if a the spell was cast with the alternate prowl costs + * + * @author LevelX2 + */ + +public class ProwlCondition implements Condition { + + private static ProwlCondition fInstance = null; + + private ProwlCondition() {} + + public static Condition getInstance() { + if (fInstance == null) { + fInstance = new ProwlCondition(); + } + return fInstance; + } + + @Override + public boolean apply(Game game, Ability source) { + Card card = game.getCard(source.getSourceId()); + if (card != null) { + for (Ability ability: card.getAbilities()) { + if (ability instanceof ProwlAbility) { + if(((ProwlAbility) ability).isActivated()) { + return true; + } + } + } + } + return false; + } +} diff --git a/Mage/src/mage/abilities/keyword/ProwlAbility.java b/Mage/src/mage/abilities/keyword/ProwlAbility.java new file mode 100644 index 0000000000..4ca59aa9f3 --- /dev/null +++ b/Mage/src/mage/abilities/keyword/ProwlAbility.java @@ -0,0 +1,199 @@ +/* +* Copyright 2010 BetaSteward_at_googlemail.com. All rights reserved. +* +* Redistribution and use in source and binary forms, with or without modification, are +* permitted provided that the following conditions are met: +* +* 1. Redistributions of source code must retain the above copyright notice, this list of +* conditions and the following disclaimer. +* +* 2. Redistributions in binary form must reproduce the above copyright notice, this list +* of conditions and the following disclaimer in the documentation and/or other materials +* provided with the distribution. +* +* THIS SOFTWARE IS PROVIDED BY BetaSteward_at_googlemail.com ``AS IS'' AND ANY EXPRESS OR IMPLIED +* WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND +* FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL BetaSteward_at_googlemail.com OR +* CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR +* CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR +* SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON +* ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING +* NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF +* ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +* +* The views and conclusions contained in the software and documentation are those of the +* authors and should not be interpreted as representing official policies, either expressed +* or implied, of BetaSteward_at_googlemail.com. +*/ + +package mage.abilities.keyword; + +import java.util.Iterator; +import java.util.LinkedList; +import java.util.List; +import mage.abilities.Ability; +import mage.abilities.SpellAbility; +import mage.abilities.StaticAbility; +import mage.abilities.costs.AlternativeCost2; +import mage.abilities.costs.AlternativeCost2Impl; +import mage.abilities.costs.AlternativeSourceCosts; +import mage.abilities.costs.Cost; +import mage.abilities.costs.Costs; +import mage.abilities.costs.mana.ManaCostsImpl; +import mage.cards.Card; +import mage.constants.Outcome; +import mage.constants.Zone; +import mage.game.Game; +import mage.players.Player; +import mage.watchers.common.ProwlWatcher; + +/** + * 702.74. Prowl # + * + * 702.74a Prowl is a static ability that functions on the stack. "Prowl [cost]" means + * "You may pay [cost] rather than pay this spell's mana cost if a player was dealt combat + * damage this turn by a source that, at the time it dealt that damage, was under your + * control and had any of this spell's creature types." Paying a spell's prowl cost follows + * the rules for paying alternative costs in rules 601.2b and 601.2e-g + * + * @author LevelX2 + */ + +public class ProwlAbility extends StaticAbility implements AlternativeSourceCosts { + + private static final String PROWL_KEYWORD = "Prowl"; + private List prowlCosts = new LinkedList(); + private String reminderText; + + public ProwlAbility(Card card, String manaString) { + super(Zone.STACK, null); + setRuleAtTheTop(true); + name = PROWL_KEYWORD; + setReminderText(card); + this.addProwlCost(manaString); + card.addWatcher(new ProwlWatcher()); + + } + + public ProwlAbility(final ProwlAbility ability) { + super(ability); + this.prowlCosts.addAll(ability.prowlCosts); + this.reminderText = ability.reminderText; + } + + @Override + public ProwlAbility copy() { + return new ProwlAbility(this); + } + + public final AlternativeCost2 addProwlCost(String manaString) { + AlternativeCost2 prowlCost = new AlternativeCost2Impl(PROWL_KEYWORD, reminderText, new ManaCostsImpl(manaString)); + prowlCosts.add(prowlCost); + return prowlCost; + } + + public void resetProwl() { + for (AlternativeCost2 cost: prowlCosts) { + cost.reset(); + } + } + @Override + public boolean isActivated() { + for (AlternativeCost2 cost: prowlCosts) { + if(cost.isActivated()) { + return true; + } + } + return false; + } + + @Override + public boolean askToActivateAlternativeCosts(Ability ability, Game game) { + if (ability instanceof SpellAbility) { + Player player = game.getPlayer(controllerId); + ProwlWatcher prowlWatcher = (ProwlWatcher) game.getState().getWatchers().get("Prowl"); + Card card = game.getCard(ability.getSourceId()); + if (player == null || prowlWatcher == null || card == null) { + throw new IllegalArgumentException("Params can't be null"); + } + boolean canProwl = false; + if (prowlWatcher.getDamagingSubtypes(ability.getControllerId()) != null) { + for (String subtype : prowlWatcher.getDamagingSubtypes(ability.getControllerId())) { + if (card.getSubtype().contains(subtype)) { + canProwl = true; + break; + } + } + } + if (canProwl) { + this.resetProwl(); + for (AlternativeCost2 prowlCost: prowlCosts) { + if (prowlCost.canPay(sourceId, controllerId, game) && + player.chooseUse(Outcome.Benefit, new StringBuilder("Cast for ").append(PROWL_KEYWORD).append(" cost ").append(prowlCost.getText(true)).append(" ?").toString(), game)) { + prowlCost.activate(); + ability.getManaCostsToPay().clear(); + ability.getCosts().clear(); + for (Iterator it = ((Costs) prowlCost).iterator(); it.hasNext();) { + Cost cost = (Cost) it.next(); + if (cost instanceof ManaCostsImpl) { + ability.getManaCostsToPay().add((ManaCostsImpl) cost.copy()); + } else { + ability.getCosts().add(cost.copy()); + } + } + } + } + } + } + return isActivated(); + } + + @Override + public String getRule() { + StringBuilder sb = new StringBuilder(); + int numberCosts = 0; + String remarkText = ""; + for (AlternativeCost2 prowlCost: prowlCosts) { + if (numberCosts == 0) { + sb.append(prowlCost.getText(false)); + remarkText = prowlCost.getReminderText(); + } else { + sb.append(" and/or ").append(prowlCost.getText(true)); + } + ++numberCosts; + } + if (numberCosts == 1) { + sb.append(" ").append(remarkText); + } + + return sb.toString(); + } + + @Override + public String getCastMessageSuffix() { + StringBuilder sb = new StringBuilder(); + int position = 0; + for (AlternativeCost2 cost : prowlCosts) { + if (cost.isActivated()) { + sb.append(cost.getCastSuffixMessage(position)); + ++position; + } + } + return sb.toString(); + } + + private void setReminderText(Card card) { + StringBuilder sb = new StringBuilder("(You may cast this for its prowl cost if you dealt combat damage to a player this turn with a "); + int i = 0; + for (String subtype: card.getSubtype()) { + i++; + sb.append(subtype); + if (card.getSupertype().size() > 1 && i < card.getSupertype().size()) { + sb.append(" or "); + } + } + //private static final String REMINDER_TEXT = "{subtypes}.)"; + + reminderText = sb.toString(); + } +} diff --git a/Mage/src/mage/watchers/common/ProwlWatcher.java b/Mage/src/mage/watchers/common/ProwlWatcher.java new file mode 100644 index 0000000000..0f8fa533e2 --- /dev/null +++ b/Mage/src/mage/watchers/common/ProwlWatcher.java @@ -0,0 +1,97 @@ +/* + * Copyright 2011 BetaSteward_at_googlemail.com. All rights reserved. + * + * Redistribution and use in source and binary forms, with or without modification, are + * permitted provided that the following conditions are met: + * + * 1. Redistributions of source code must retain the above copyright notice, this list of + * conditions and the following disclaimer. + * + * 2. Redistributions in binary form must reproduce the above copyright notice, this list + * of conditions and the following disclaimer in the documentation and/or other materials + * provided with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY BetaSteward_at_googlemail.com ``AS IS'' AND ANY EXPRESS OR IMPLIED + * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND + * FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL BetaSteward_at_googlemail.com OR + * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR + * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR + * SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON + * ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING + * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF + * ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + * + * The views and conclusions contained in the software and documentation are those of the + * authors and should not be interpreted as representing official policies, either expressed + * or implied, of BetaSteward_at_googlemail.com. + */ +package mage.watchers.common; + +import java.util.HashMap; +import java.util.LinkedHashSet; +import java.util.Map; +import java.util.Map.Entry; +import java.util.Set; +import java.util.UUID; +import mage.constants.WatcherScope; +import mage.game.Game; +import mage.game.events.DamagedPlayerEvent; +import mage.game.events.GameEvent; +import mage.game.events.GameEvent.EventType; +import mage.game.permanent.Permanent; +import mage.watchers.WatcherImpl; + +/** + * Watcher stores with which creature subtypes a player made combat damage to + * other players during a turn. + * + * @author LevelX + */ +public class ProwlWatcher extends WatcherImpl { + + private Map> damagingSubtypes = new HashMap>(); + + public ProwlWatcher() { + super("Prowl", WatcherScope.GAME); + } + + public ProwlWatcher(final ProwlWatcher watcher) { + super(watcher); + for (Entry> entry : watcher.damagingSubtypes.entrySet()) { + damagingSubtypes.put(entry.getKey(), entry.getValue()); + } + } + + @Override + public ProwlWatcher copy() { + return new ProwlWatcher(this); + } + + @Override + public void watch(GameEvent event, Game game) { + if (event.getType() == EventType.DAMAGED_PLAYER) { + DamagedPlayerEvent dEvent = (DamagedPlayerEvent) event; + if (dEvent.isCombatDamage()) { + Permanent creature = game.getPermanent(dEvent.getSourceId()); + if (creature != null) { + Set subtypes = damagingSubtypes.get(creature.getControllerId()); + if (subtypes == null) { + subtypes = new LinkedHashSet(); + } + subtypes.addAll(creature.getSubtype()); + damagingSubtypes.put(creature.getControllerId(), subtypes); + } + } + } + } + + @Override + public void reset() { + super.reset(); + damagingSubtypes.clear(); + } + + public Set getDamagingSubtypes(UUID playerId) { + return damagingSubtypes.get(playerId); + } +}