Merge branch 'master' into Network_Upgrade

Conflicts:
	Mage.Client/src/main/java/mage/client/dialog/PreferencesDialog.java
	Mage.Client/src/main/java/mage/client/game/GamePanel.java
	Mage.Client/src/main/java/mage/client/remote/CallbackClientImpl.java
	Mage.Server/src/main/java/mage/server/Session.java
	Mage.Server/src/main/java/mage/server/User.java
	Mage.Server/src/main/java/mage/server/game/GameController.java
	Mage.Server/src/main/java/mage/server/game/GameSessionPlayer.java
	Mage.Server/src/main/java/mage/server/game/GamesRoomImpl.java
This commit is contained in:
betasteward 2015-07-12 12:25:44 -04:00
commit 5c829b79d5
552 changed files with 30213 additions and 6146 deletions

View file

@ -7,7 +7,7 @@
<parent>
<groupId>org.mage</groupId>
<artifactId>mage-root</artifactId>
<version>1.4.1</version>
<version>1.4.2</version>
</parent>
<artifactId>mage</artifactId>
@ -24,7 +24,7 @@
<dependency>
<groupId>com.h2database</groupId>
<artifactId>h2</artifactId>
<version>1.4.185</version>
<version>1.4.187</version>
<scope>runtime</scope>
</dependency>
<dependency>

View file

@ -1,31 +1,30 @@
/*
* 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.
*/
* 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;
import java.io.Serializable;
@ -50,26 +49,27 @@ public class ObjectColor implements Serializable, Copyable<ObjectColor>, Compara
private boolean red;
private boolean green;
public ObjectColor() {}
public ObjectColor() {
}
public ObjectColor(String color) {
for (int i = 0; i < color.length(); i++) {
switch (color.charAt(i)) {
case 'W':
white = true;
break;
case 'U':
blue = true;
break;
case 'B':
black = true;
break;
case 'R':
red = true;
break;
case 'G':
green = true;
break;
case 'W':
white = true;
break;
case 'U':
blue = true;
break;
case 'B':
black = true;
break;
case 'R':
red = true;
break;
case 'G':
green = true;
break;
}
}
}
@ -157,30 +157,39 @@ public class ObjectColor implements Serializable, Copyable<ObjectColor>, Compara
public boolean isWhite() {
return white;
}
public void setWhite(boolean white) {
this.white = white;
}
public boolean isBlue() {
return blue;
}
public void setBlue(boolean blue) {
this.blue = blue;
}
public boolean isBlack() {
return black;
}
public void setBlack(boolean black) {
this.black = black;
}
public boolean isRed() {
return red;
}
public void setRed(boolean red) {
this.red = red;
}
public boolean isGreen() {
return green;
}
public void setGreen(boolean green) {
this.green = green;
}
@ -287,14 +296,10 @@ public class ObjectColor implements Serializable, Copyable<ObjectColor>, Compara
}
public boolean shares(ObjectColor color) {
if (this == color) {
return true;
}
if (!hasColor() && !color.hasColor()) {
return true;
}
return color.white && white || color.blue && blue || color.black && black ||
color.red && red || color.green && green;
// 105.4. [...] Multicolored is not a color. Neither is colorless.
return !color.isColorless()
&& (color.white && white || color.blue && blue || color.black && black
|| color.red && red || color.green && green);
}
@Override
@ -309,32 +314,32 @@ public class ObjectColor implements Serializable, Copyable<ObjectColor>, Compara
if (this.isMulticolored()) {
o1 = 6;
} else if(this.isColorless()) {
} else if (this.isColorless()) {
o1 = 0;
} else if(this.isBlack()) {
} else if (this.isBlack()) {
o1 = 1;
} else if(this.isBlue()) {
} else if (this.isBlue()) {
o1 = 2;
} else if(this.isGreen()) {
} else if (this.isGreen()) {
o1 = 3;
} else if(this.isRed()) {
} else if (this.isRed()) {
o1 = 4;
} else if(this.isWhite()) {
} else if (this.isWhite()) {
o1 = 5;
}
if (o.isMulticolored()) {
o2 = 6;
} else if(o.isColorless()) {
} else if (o.isColorless()) {
o2 = 0;
} else if(o.isBlack()) {
} else if (o.isBlack()) {
o2 = 1;
} else if(o.isBlue()) {
} else if (o.isBlue()) {
o2 = 2;
} else if(o.isGreen()) {
} else if (o.isGreen()) {
o2 = 3;
} else if(o.isRed()) {
} else if (o.isRed()) {
o2 = 4;
} else if(o.isWhite()) {
} else if (o.isWhite()) {
o2 = 5;
}
return o1 - o2;

View file

@ -872,9 +872,13 @@ public abstract class AbilityImpl implements Ability {
@Override
public boolean canChooseTarget(Game game) {
int found = 0;
for (Mode mode : modes.values()) {
if (mode.getTargets().canChoose(sourceId, controllerId, game)) {
return true;
found++;
if (found >= getModes().getMinModes()) {
return true;
}
}
}
return false;

View file

@ -47,9 +47,10 @@ import mage.game.permanent.Permanent;
/**
*
* @author BetaSteward_at_googlemail.com
*
*
* This class uses ConcurrentHashMap to avoid ConcurrentModificationExceptions.
* See ticket https://github.com/magefree/mage/issues/966 and https://github.com/magefree/mage/issues/473
* See ticket https://github.com/magefree/mage/issues/966 and
* https://github.com/magefree/mage/issues/473
*/
public class TriggeredAbilities extends ConcurrentHashMap<String, TriggeredAbility> {
@ -100,7 +101,6 @@ public class TriggeredAbilities extends ConcurrentHashMap<String, TriggeredAbili
}
}
// ability.setSourceObject(object);
if (ability.checkTrigger(event, game)) {
ability.trigger(game, ability.getControllerId());
}

View file

@ -67,7 +67,7 @@ public abstract class TriggeredAbilityImpl extends AbilityImpl implements Trigge
public void trigger(Game game, UUID controllerId) {
//20091005 - 603.4
if (checkInterveningIfClause(game)) {
setSourceObject(null, game); // set the source object the time the trigger goes off
setSourceObject(null, game);
game.addTriggeredAbility(this);
}
}

View file

@ -1,96 +1,95 @@
/*
* 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.common;
import mage.abilities.TriggeredAbilityImpl;
import mage.abilities.effects.Effect;
import mage.constants.Zone;
import mage.filter.common.FilterCreaturePermanent;
import mage.game.Game;
import mage.game.events.GameEvent;
import mage.game.events.GameEvent.EventType;
import mage.game.permanent.Permanent;
import mage.target.targetpointer.FixedTarget;
/**
*
* @author LevelX2
*/
public class BecomesBlockedAllTriggeredAbility extends TriggeredAbilityImpl {
private FilterCreaturePermanent filter;
private boolean setTargetPointer;
public BecomesBlockedAllTriggeredAbility(Effect effect, boolean optional) {
this(effect, optional, new FilterCreaturePermanent("a creature"), false);
}
public BecomesBlockedAllTriggeredAbility(Effect effect, boolean optional, FilterCreaturePermanent filter, boolean setTargetPointer) {
super(Zone.BATTLEFIELD, effect, optional);
this.filter = filter;
this.setTargetPointer = setTargetPointer;
}
public BecomesBlockedAllTriggeredAbility(final BecomesBlockedAllTriggeredAbility ability) {
super(ability);
this.filter = ability.filter;
this.setTargetPointer = ability.setTargetPointer;
}
@Override
public boolean checkEventType(GameEvent event, Game game) {
return event.getType() == GameEvent.EventType.CREATURE_BLOCKED;
}
@Override
public boolean checkTrigger(GameEvent event, Game game) {
Permanent permanent = game.getPermanent(event.getTargetId());
if (permanent != null && filter.match(permanent, getSourceId(), getControllerId(), game)) {
if (setTargetPointer) {
for(Effect effect :this.getEffects()) {
effect.setTargetPointer(new FixedTarget(event.getTargetId()));
}
}
return true;
}
return false;
}
@Override
public String getRule() {
StringBuilder sb = new StringBuilder("Whenever ").append(filter.getMessage());
sb.append(" becomes blocked, ").append(super.getRule());
return sb.toString();
}
@Override
public BecomesBlockedAllTriggeredAbility copy() {
return new BecomesBlockedAllTriggeredAbility(this);
}
}
/*
* 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.common;
import mage.abilities.TriggeredAbilityImpl;
import mage.abilities.effects.Effect;
import mage.constants.Zone;
import mage.filter.common.FilterCreaturePermanent;
import mage.game.Game;
import mage.game.events.GameEvent;
import mage.game.permanent.Permanent;
import mage.target.targetpointer.FixedTarget;
/**
*
* @author LevelX2
*/
public class BecomesBlockedAllTriggeredAbility extends TriggeredAbilityImpl {
private FilterCreaturePermanent filter;
private boolean setTargetPointer;
public BecomesBlockedAllTriggeredAbility(Effect effect, boolean optional) {
this(effect, optional, new FilterCreaturePermanent("a creature"), false);
}
public BecomesBlockedAllTriggeredAbility(Effect effect, boolean optional, FilterCreaturePermanent filter, boolean setTargetPointer) {
super(Zone.BATTLEFIELD, effect, optional);
this.filter = filter;
this.setTargetPointer = setTargetPointer;
}
public BecomesBlockedAllTriggeredAbility(final BecomesBlockedAllTriggeredAbility ability) {
super(ability);
this.filter = ability.filter;
this.setTargetPointer = ability.setTargetPointer;
}
@Override
public boolean checkEventType(GameEvent event, Game game) {
return event.getType() == GameEvent.EventType.CREATURE_BLOCKED;
}
@Override
public boolean checkTrigger(GameEvent event, Game game) {
Permanent permanent = game.getPermanent(event.getTargetId());
if (permanent != null && filter.match(permanent, getSourceId(), getControllerId(), game)) {
if (setTargetPointer) {
for(Effect effect :this.getEffects()) {
effect.setTargetPointer(new FixedTarget(event.getTargetId()));
}
}
return true;
}
return false;
}
@Override
public String getRule() {
StringBuilder sb = new StringBuilder("Whenever ").append(filter.getMessage());
sb.append(" becomes blocked, ").append(super.getRule());
return sb.toString();
}
@Override
public BecomesBlockedAllTriggeredAbility copy() {
return new BecomesBlockedAllTriggeredAbility(this);
}
}

View file

@ -15,28 +15,27 @@ import mage.game.events.GameEvent;
*
* @author LevelX2
*/
public class BecomesRenownSourceTriggeredAbility extends TriggeredAbilityImpl {
public class BecomesRenownedSourceTriggeredAbility extends TriggeredAbilityImpl {
private int renownValue;
public BecomesRenownSourceTriggeredAbility(Effect effect, boolean optional) {
public BecomesRenownedSourceTriggeredAbility(Effect effect, boolean optional) {
super(Zone.BATTLEFIELD, effect, optional);
}
public BecomesRenownSourceTriggeredAbility(final BecomesRenownSourceTriggeredAbility ability) {
public BecomesRenownedSourceTriggeredAbility(final BecomesRenownedSourceTriggeredAbility ability) {
super(ability);
this.renownValue = ability.renownValue;
}
@Override
public BecomesRenownSourceTriggeredAbility copy() {
return new BecomesRenownSourceTriggeredAbility(this);
public BecomesRenownedSourceTriggeredAbility copy() {
return new BecomesRenownedSourceTriggeredAbility(this);
}
@Override
public boolean checkEventType(GameEvent event, Game game) {
return event.getType() == GameEvent.EventType.BECOMES_RENOWN;
return event.getType() == GameEvent.EventType.BECOMES_RENOWNED;
}
@Override
@ -54,6 +53,6 @@ public class BecomesRenownSourceTriggeredAbility extends TriggeredAbilityImpl {
@Override
public String getRule() {
return "When {this} becomes monstrous, " + super.getRule();
return "When {this} becomes renowned, " + super.getRule();
}
}
}

View file

@ -0,0 +1,52 @@
/*
* To change this license header, choose License Headers in Project Properties.
* To change this template file, choose Tools | Templates
* and open the template in the editor.
*/
package mage.abilities.common;
import mage.abilities.SpellAbility;
import mage.abilities.costs.mana.ManaCosts;
import mage.cards.Card;
import mage.constants.SpellAbilityType;
import mage.constants.TimingRule;
import mage.constants.Zone;
import mage.util.CardUtil;
/**
*
* @author LevelX2
*/
public class PayMoreToCastAsThoughtItHadFlashAbility extends SpellAbility {
private final ManaCosts costsToAdd;
public PayMoreToCastAsThoughtItHadFlashAbility(Card card, ManaCosts costsToAdd) {
super(card.getSpellAbility().getManaCosts().copy(), card.getName() + " as though it had flash", Zone.HAND, SpellAbilityType.BASE_ALTERNATE);
this.costsToAdd = costsToAdd;
this.timing = TimingRule.INSTANT;
CardUtil.increaseCost(this, costsToAdd);
}
public PayMoreToCastAsThoughtItHadFlashAbility(final PayMoreToCastAsThoughtItHadFlashAbility ability) {
super(ability);
this.costsToAdd = ability.costsToAdd;
}
@Override
public PayMoreToCastAsThoughtItHadFlashAbility copy() {
return new PayMoreToCastAsThoughtItHadFlashAbility(this);
}
@Override
public String getRule(boolean all) {
return getRule();
}
@Override
public String getRule() {
return "You may cast {this} as though it had flash if you pay " + costsToAdd.getText() + " more to cast it. <i>(You may cast it any time you could cast an instant)</i>";
}
}

View file

@ -2,8 +2,10 @@ package mage.abilities.condition.common;
import mage.abilities.Ability;
import mage.abilities.condition.Condition;
import mage.filter.FilterCard;
import mage.game.Game;
import mage.players.Player;
import mage.util.CardUtil;
/**
* Condition for - Controller has X or more cards in his or her graveyard
@ -13,14 +15,31 @@ import mage.players.Player;
public class CardsInControllerGraveCondition implements Condition {
private final int value;
private final FilterCard filter;
public CardsInControllerGraveCondition(int value) {
this(value, null);
}
public CardsInControllerGraveCondition(int value, FilterCard filter) {
this.value = value;
this.filter = filter;
}
@Override
public boolean apply(Game game, Ability source) {
Player player = game.getPlayer(source.getControllerId());
if (filter != null) {
return player != null && player.getGraveyard().count(filter, source.getSourceId(), source.getControllerId(), game) >= value;
}
return player != null && player.getGraveyard().size() >= value;
}
@Override
public String toString() {
return "there are " + CardUtil.numberToText(value, "one") + " or more "
+ (filter == null ? "cards" : filter.getMessage())
+ " in your graveyard";
}
}

View file

@ -16,15 +16,15 @@ import mage.game.permanent.Permanent;
* @author LevelX2
*/
public class RenownCondition implements Condition {
public class RenownedSourceCondition implements Condition {
private static RenownCondition fInstance = null;
private static RenownedSourceCondition fInstance = null;
private RenownCondition() {}
private RenownedSourceCondition() {}
public static RenownCondition getInstance() {
public static RenownedSourceCondition getInstance() {
if (fInstance == null) {
fInstance = new RenownCondition();
fInstance = new RenownedSourceCondition();
}
return fInstance;
}
@ -33,7 +33,7 @@ public class RenownCondition implements Condition {
public boolean apply(Game game, Ability source) {
Permanent permanent = game.getPermanent(source.getSourceId());
if (permanent != null) {
return permanent.isRenown();
return permanent.isRenowned();
}
return false;
}

View file

@ -0,0 +1,67 @@
/*
* 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.game.Game;
import mage.game.permanent.Permanent;
/**
*
* @author LevelX2
*/
public class RenownedTargetCondition implements Condition {
private static RenownedTargetCondition fInstance = null;
private RenownedTargetCondition() {
}
public static RenownedTargetCondition getInstance() {
if (fInstance == null) {
fInstance = new RenownedTargetCondition();
}
return fInstance;
}
@Override
public boolean apply(Game game, Ability source) {
Permanent permanent = game.getPermanent(source.getTargets().getFirstTarget());
if (permanent != null) {
return permanent.isRenowned();
}
return false;
}
@Override
public String toString() {
return "it's renowned";
}
}

View file

@ -207,7 +207,7 @@ public class AlternativeCostSourceAbility extends StaticAbility implements Alter
@Override
public String getCastMessageSuffix(Game game) {
return alternateCosts.isEmpty() ? " without paying it's mana costs" : " using alternative casting costs";
return alternateCosts.isEmpty() ? " without paying its mana costs" : " using alternative casting costs";
}
@Override

View file

@ -0,0 +1,86 @@
/*
* 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.costs.common;
import java.util.UUID;
import mage.abilities.Ability;
import mage.abilities.costs.CostImpl;
import mage.constants.Outcome;
import mage.constants.Zone;
import mage.game.Game;
import mage.players.Player;
import mage.target.TargetCard;
import mage.target.common.TargetCardInYourGraveyard;
/**
*
* @author markedagain
*/
public class ReturnToHandFromGraveyardCost extends CostImpl {
public ReturnToHandFromGraveyardCost(TargetCardInYourGraveyard target) {
this.addTarget(target);
if (target.getMaxNumberOfTargets() > 1 && target.getMaxNumberOfTargets() == target.getNumberOfTargets()) {
this.text = new StringBuilder("return ").append(target.getMaxNumberOfTargets()).append(" ").append(target.getTargetName()).append(" from graveyard to it's owner's hand").toString();
} else {
this.text = new StringBuilder("return ").append(target.getTargetName()).append(" from graveyard to it's owner's hand").toString();
}
}
public ReturnToHandFromGraveyardCost(ReturnToHandFromGraveyardCost cost) {
super(cost);
}
@Override
public boolean pay(Ability ability, Game game, UUID sourceId, UUID controllerId, boolean noMana) {
Player controller = game.getPlayer(controllerId);
if (controller != null) {
if (targets.choose(Outcome.ReturnToHand, controllerId, sourceId, game)) {
for (UUID targetId: targets.get(0).getTargets()) {
mage.cards.Card targetCard = game.getCard(targetId);
if (targetCard == null) {
return false;
}
paid |= controller.moveCardToHandWithInfo(targetCard, sourceId, game, Zone.HAND);
}
}
}
return paid;
}
@Override
public boolean canPay(Ability ability, UUID sourceId, UUID controllerId, Game game) {
return targets.canChoose(controllerId, game);
}
@Override
public ReturnToHandFromGraveyardCost copy() {
return new ReturnToHandFromGraveyardCost(this);
}
}

View file

@ -1,16 +1,16 @@
/*
* 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
@ -20,7 +20,7 @@
* 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.
@ -42,7 +42,7 @@ import mage.game.permanent.Permanent;
public class ReturnToHandSourceCost extends CostImpl {
public ReturnToHandSourceCost() {
this.text = "return {this} to it's owner's hand";
this.text = "return {this} to its owner's hand";
}
public ReturnToHandSourceCost(ReturnToHandSourceCost cost) {

View file

@ -1,16 +1,16 @@
/*
* 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
@ -20,7 +20,7 @@
* 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.
@ -47,9 +47,9 @@ public class ReturnToHandTargetPermanentCost extends CostImpl {
public ReturnToHandTargetPermanentCost(TargetControlledPermanent target) {
this.addTarget(target);
if (target.getMaxNumberOfTargets() > 1 && target.getMaxNumberOfTargets() == target.getNumberOfTargets()) {
this.text = new StringBuilder("return ").append(target.getMaxNumberOfTargets()).append(" ").append(target.getTargetName()).append(" you control to it's owner's hand").toString();
this.text = new StringBuilder("return ").append(target.getMaxNumberOfTargets()).append(" ").append(target.getTargetName()).append(" you control to its owner's hand").toString();
} else {
this.text = new StringBuilder("return ").append(target.getTargetName()).append(" you control to it's owner's hand").toString();
this.text = new StringBuilder("return ").append(target.getTargetName()).append(" you control to its owner's hand").toString();
}
}

View file

@ -1,6 +1,5 @@
package mage.abilities.decorator;
import mage.MageObject;
import mage.abilities.TriggeredAbility;
import mage.abilities.TriggeredAbilityImpl;
import mage.abilities.condition.Condition;
@ -9,7 +8,8 @@ import mage.game.Game;
import mage.game.events.GameEvent;
/**
* Adds condition to {@link mage.abilities.effects.ContinuousEffect}. Acts as decorator.
* Adds condition to {@link mage.abilities.effects.ContinuousEffect}. Acts as
* decorator.
*
* @author nantuko
*/
@ -19,7 +19,7 @@ public class ConditionalTriggeredAbility extends TriggeredAbilityImpl {
protected Condition condition;
protected String text;
public ConditionalTriggeredAbility(TriggeredAbility ability, Condition condition, String text) {
public ConditionalTriggeredAbility(TriggeredAbility ability, Condition condition, String text) {
this(ability, condition, text, false);
}
@ -39,7 +39,6 @@ public class ConditionalTriggeredAbility extends TriggeredAbilityImpl {
this.text = triggered.text;
}
@Override
public boolean checkInterveningIfClause(Game game) {
return condition.apply(game, this);
@ -64,7 +63,7 @@ public class ConditionalTriggeredAbility extends TriggeredAbilityImpl {
@Override
public String getRule() {
if (text == null || text.isEmpty()) {
if (text == null || text.isEmpty()) {
return ability.getRule();
}
return text;
@ -75,21 +74,4 @@ public class ConditionalTriggeredAbility extends TriggeredAbilityImpl {
return ability.getEffects();
}
@Override
public MageObject getSourceObjectIfItStillExists(Game game) {
return ability.getSourceObjectIfItStillExists(game);
}
@Override
public MageObject getSourceObject(Game game) {
return ability.getSourceObject(game);
}
@Override
public int getSourceObjectZoneChangeCounter() {
return ability.getSourceObjectZoneChangeCounter();
}
}

View file

@ -214,12 +214,16 @@ public class ContinuousEffects implements Serializable {
case WhileOnStack:
case WhileInGraveyard:
HashSet<Ability> abilities = layeredEffects.getAbility(effect.getId());
for (Ability ability : abilities) {
// If e.g. triggerd abilities (non static) created the effect, the ability must not be in usable zone (e.g. Unearth giving Haste effect)
if (!(ability instanceof StaticAbility) || ability.isInUseableZone(game, null, null)) {
layerEffects.add(effect);
break;
if (abilities != null) {
for (Ability ability : abilities) {
// If e.g. triggerd abilities (non static) created the effect, the ability must not be in usable zone (e.g. Unearth giving Haste effect)
if (!(ability instanceof StaticAbility) || ability.isInUseableZone(game, null, null)) {
layerEffects.add(effect);
break;
}
}
} else {
logger.error("No abilities for continuous effect: " + effect.toString());
}
break;
default:
@ -336,7 +340,8 @@ public class ContinuousEffects implements Serializable {
if (ability.getAbilityType() != AbilityType.STATIC || ability.isInUseableZone(game, null, event)) {
if (effect.getDuration() != Duration.OneUse || !effect.isUsed()) {
if (!game.getScopeRelevant() || effect.hasSelfScope() || !event.getTargetId().equals(ability.getSourceId())) {
if (effect.applies(event, ability, game)) {
if (effect.applies(event, ability, game)
&& !((PayCostToAttackBlockEffect) effect).isCostless(event, ability, game)) {
return true;
}
}
@ -1057,14 +1062,16 @@ public class ContinuousEffects implements Serializable {
private void setControllerForEffect(ContinuousEffectsList<?> effects, UUID cardId, UUID controllerId) {
for (Effect effect : effects) {
HashSet<Ability> abilities = effects.getAbility(effect.getId());
for (Ability ability : abilities) {
if (ability.getSourceId() != null) {
if (ability.getSourceId().equals(cardId)) {
ability.setControllerId(controllerId);
}
} else {
if (!ability.getZone().equals(Zone.COMMAND)) {
logger.fatal(new StringBuilder("No sourceId Ability: ").append(ability));
if (abilities != null) {
for (Ability ability : abilities) {
if (ability.getSourceId() != null) {
if (ability.getSourceId().equals(cardId)) {
ability.setControllerId(controllerId);
}
} else {
if (!ability.getZone().equals(Zone.COMMAND)) {
logger.fatal(new StringBuilder("No sourceId Ability: ").append(ability));
}
}
}
}

View file

@ -42,4 +42,6 @@ public interface PayCostToAttackBlockEffect extends ReplacementEffect {
ManaCosts getManaCostToPay(GameEvent event, Ability source, Game game);
Cost getOtherCostToPay(GameEvent event, Ability source, Game game);
boolean isCostless(GameEvent event, Ability source, Game game);
}

View file

@ -46,13 +46,26 @@ public abstract class PayCostToAttackBlockEffectImpl extends ReplacementEffectIm
public static enum RestrictType {
ATTACK, ATTACK_AND_BLOCK, BLOCK
ATTACK("attack"),
ATTACK_AND_BLOCK("attack or block"),
BLOCK("block");
private final String text;
RestrictType(String text) {
this.text = text;
}
@Override
public String toString() {
return text;
}
}
private final Cost cost;
private final ManaCosts manaCosts;
protected final Cost cost;
protected final ManaCosts manaCosts;
private final RestrictType restrictType;
protected final RestrictType restrictType;
public PayCostToAttackBlockEffectImpl(Duration duration, Outcome outcome, RestrictType restrictType) {
super(duration, outcome, false);
@ -165,4 +178,13 @@ public abstract class PayCostToAttackBlockEffectImpl extends ReplacementEffectIm
return manaCosts;
}
@Override
public boolean isCostless(GameEvent event, Ability source, Game game) {
ManaCosts currentManaCosts = getManaCostToPay(event, source, game);
if (currentManaCosts != null && currentManaCosts.convertedManaCost() > 0) {
return false;
}
return getOtherCostToPay(event, source, game) == null;
}
}

View file

@ -1,38 +1,36 @@
/*
* 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.
*/
* 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.effects.common;
import mage.constants.Outcome;
import mage.abilities.Ability;
import mage.abilities.Mode;
import mage.abilities.effects.OneShotEffect;
import mage.constants.Outcome;
import mage.constants.Zone;
import mage.game.Game;
import mage.game.stack.Spell;
@ -82,8 +80,11 @@ public class CopyTargetSpellEffect extends OneShotEffect {
return new CopyTargetSpellEffect(this);
}
@Override
@Override
public String getText(Mode mode) {
if (staticText != null && !staticText.isEmpty()) {
return staticText;
}
StringBuilder sb = new StringBuilder();
sb.append("copy target ").append(mode.getTargets().get(0).getTargetName()).append(". You may choose new targets for the copy");
return sb.toString();

View file

@ -1,31 +1,30 @@
/*
* 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.
*/
* 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.effects.common;
import java.util.UUID;
@ -50,9 +49,10 @@ public class DontUntapInControllersNextUntapStepTargetEffect extends ContinuousR
private String targetName;
/**
* Attention: This effect won't work with targets controlled by different controllers
* If this is needed, the validForTurnNum has to be saved per controller.
*
* Attention: This effect won't work with targets controlled by different
* controllers If this is needed, the validForTurnNum has to be saved per
* controller.
*
*/
public DontUntapInControllersNextUntapStepTargetEffect() {
super(Duration.Custom, Outcome.Detriment, false, true);
@ -94,13 +94,13 @@ public class DontUntapInControllersNextUntapStepTargetEffect extends ContinuousR
public boolean checksEventType(GameEvent event, Game game) {
return event.getType() == EventType.UNTAP_STEP || event.getType() == EventType.UNTAP;
}
@Override
public boolean applies(GameEvent event, Ability source, Game game) {
// the check for turn number is needed if multiple effects are added to prevent untap in next untap step of controller
// if we don't check for turn number, every untap step of a turn only one effect would be used instead of correctly only one time
// to skip the untap effect.
// Discard effect if it's related to a previous turn
if (validForTurnNum > 0 && validForTurnNum < game.getTurnNum()) {
discard();
@ -109,7 +109,7 @@ public class DontUntapInControllersNextUntapStepTargetEffect extends ContinuousR
// remember the turn of the untap step the effect has to be applied
if (GameEvent.EventType.UNTAP_STEP.equals(event.getType())) {
UUID controllerId = null;
for(UUID targetId : getTargetPointer().getTargets(game, source)) {
for (UUID targetId : getTargetPointer().getTargets(game, source)) {
Permanent permanent = game.getPermanent(targetId);
if (permanent != null) {
controllerId = permanent.getControllerId();
@ -128,7 +128,7 @@ public class DontUntapInControllersNextUntapStepTargetEffect extends ContinuousR
validForTurnNum = game.getTurnNum();
}
}
if (game.getTurn().getStepType() == PhaseStep.UNTAP && event.getType() == EventType.UNTAP) {
if (targetPointer.getTargets(game, source).contains(event.getTargetId())) {
Permanent permanent = game.getPermanent(event.getTargetId());
@ -147,9 +147,8 @@ public class DontUntapInControllersNextUntapStepTargetEffect extends ContinuousR
}
if (targetName != null && targetName.length() > 0) {
return targetName + " doesn't untap during its controller's next untap step";
}
else {
return "Target " + mode.getTargets().get(0).getTargetName() + " doesn't untap during its controller's next untap step";
} else {
return "Target " + (mode == null ? "creature" : mode.getTargets().get(0).getTargetName()) + " doesn't untap during its controller's next untap step";
}
}

View file

@ -25,12 +25,11 @@
* authors and should not be interpreted as representing official policies, either expressed
* or implied, of BetaSteward_at_googlemail.com.
*/
package mage.abilities.effects.common;
import mage.constants.Outcome;
import mage.abilities.Ability;
import mage.abilities.effects.OneShotEffect;
import mage.constants.Outcome;
import mage.game.Game;
public class EndTurnEffect extends OneShotEffect {
@ -46,8 +45,9 @@ public class EndTurnEffect extends OneShotEffect {
@Override
public boolean apply(Game game, Ability source) {
if (!game.isSimulation())
if (!game.isSimulation()) {
game.informPlayers("The current turn ends");
}
return game.endTurn();
}
@ -56,4 +56,3 @@ public class EndTurnEffect extends OneShotEffect {
return new EndTurnEffect(this);
}
}

View file

@ -5,49 +5,73 @@
*/
package mage.abilities.effects.common;
import mage.MageObject;
import mage.abilities.Ability;
import mage.abilities.effects.ContinuousEffect;
import mage.abilities.effects.Effect;
import mage.abilities.effects.OneShotEffect;
import mage.abilities.keyword.TransformAbility;
import mage.cards.Card;
import mage.constants.Outcome;
import mage.constants.Zone;
import mage.game.Game;
import mage.game.permanent.Permanent;
import mage.players.Player;
/**
*
* @author LevelX2
*/
public class ExileAndReturnTransformedSourceEffect extends OneShotEffect {
public static enum Gender { MALE, FEMAL };
public static enum Gender {
MALE, FEMAL
};
protected Effect additionalEffect;
public ExileAndReturnTransformedSourceEffect(Gender gender) {
super(Outcome.Benefit);
this.staticText = "exile {this}, then return " + (gender.equals(Gender.MALE) ? "him":"her")
+ " to the battlefield transformed under" + (gender.equals(Gender.MALE) ? "his":"her")+ " owner's control";
this(gender, null);
}
/**
* @param gender
* @param additionalEffect that effect is applies as source is exiled
*/
public ExileAndReturnTransformedSourceEffect(Gender gender, Effect additionalEffect) {
super(Outcome.Benefit);
this.additionalEffect = additionalEffect;
this.staticText = "exile {this}, then return " + (gender.equals(Gender.MALE) ? "him" : "her")
+ " to the battlefield transformed under" + (gender.equals(Gender.MALE) ? "his" : "her") + " owner's control";
}
public ExileAndReturnTransformedSourceEffect(final ExileAndReturnTransformedSourceEffect effect) {
super(effect);
this.additionalEffect = effect.additionalEffect;
}
@Override
public ExileAndReturnTransformedSourceEffect copy() {
return new ExileAndReturnTransformedSourceEffect(this);
}
@Override
public boolean apply(Game game, Ability source) {
MageObject sourceObject = source.getSourceObjectIfItStillExists(game);
// Creature has to be on the battlefield to get exiled and be able to return transformed
Permanent sourceObject = game.getPermanent(source.getSourceId());
Player controller = game.getPlayer(source.getControllerId());
if (sourceObject != null && controller != null) {
Card card = (Card) sourceObject;
if (controller.moveCards(card, Zone.BATTLEFIELD, Zone.EXILED, source, game)) {
game.getState().setValue(TransformAbility.VALUE_KEY_ENTER_TRANSFORMED + source.getSourceId(), Boolean.TRUE);
controller.putOntoBattlefieldWithInfo(card, game, Zone.EXILED, source.getSourceId());
if (additionalEffect != null) {
if (additionalEffect instanceof ContinuousEffect) {
game.addEffect((ContinuousEffect) additionalEffect, source);
} else {
additionalEffect.apply(game, source);
}
}
}
}
return true;

View file

@ -0,0 +1,83 @@
/*
* 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.effects.common;
import mage.abilities.Ability;
import mage.abilities.common.delayed.AtTheBeginOfNextEndStepDelayedTriggeredAbility;
import mage.abilities.effects.OneShotEffect;
import mage.constants.Outcome;
import mage.constants.Zone;
import mage.game.Game;
import mage.game.permanent.Permanent;
import mage.players.Player;
/**
*
* @author LevelX2
*/
public class ExileReturnToBattlefieldOwnerNextEndStepEffect extends OneShotEffect {
private static final String effectText = "exile {this}. Return it to the battlefield under its owner's control at the beginning of the next end step";
public ExileReturnToBattlefieldOwnerNextEndStepEffect() {
super(Outcome.Benefit);
staticText = effectText;
}
public ExileReturnToBattlefieldOwnerNextEndStepEffect(ExileReturnToBattlefieldOwnerNextEndStepEffect effect) {
super(effect);
}
@Override
public boolean apply(Game game, Ability source) {
Player controller = game.getPlayer(source.getControllerId());
if (controller != null) {
Permanent permanent = game.getPermanent(source.getSourceId());
if (permanent != null) {
int zcc = game.getState().getZoneChangeCounter(permanent.getId());
if (controller.moveCardToExileWithInfo(permanent, source.getSourceId(), permanent.getIdName(), source.getSourceId(), game, Zone.BATTLEFIELD, true)) {
//create delayed triggered ability and return it from every public zone he was next moved to
AtTheBeginOfNextEndStepDelayedTriggeredAbility delayedAbility = new AtTheBeginOfNextEndStepDelayedTriggeredAbility(
new ReturnToBattlefieldUnderOwnerControlSourceEffect(false, zcc + 1));
delayedAbility.setSourceId(source.getSourceId());
delayedAbility.setControllerId(source.getControllerId());
delayedAbility.setSourceObject(source.getSourceObject(game), game);
game.addDelayedTriggeredAbility(delayedAbility);
}
}
return true;
}
return false;
}
@Override
public ExileReturnToBattlefieldOwnerNextEndStepEffect copy() {
return new ExileReturnToBattlefieldOwnerNextEndStepEffect(this);
}
}

View file

@ -82,7 +82,7 @@ public class HideawayPlayEffect extends OneShotEffect {
// The land's last ability allows you to play the removed card as part of the resolution of that ability.
// Timing restrictions based on the card's type are ignored (for instance, if it's a creature or sorcery).
// Other play restrictions are not (such as "Play [this card] only during combat").
if (controller.chooseUse(Outcome.Benefit, "Cast "+ card.getLogName() + " without paying it's mana cost?", source, game)) {
if (controller.chooseUse(Outcome.Benefit, "Cast "+ card.getLogName() + " without paying its mana cost?", source, game)) {
card.setFaceDown(false, game);
return controller.cast(card.getSpellAbility(), game, true);
}

View file

@ -1,6 +1,7 @@
package mage.abilities.effects.common;
import mage.abilities.Ability;
import mage.abilities.Mode;
import mage.abilities.effects.OneShotEffect;
import mage.cards.Card;
import mage.constants.Outcome;
@ -15,15 +16,20 @@ import mage.target.common.TargetCardInHand;
*/
public class PutCreatureOnBattlefieldEffect extends OneShotEffect {
private static final String choiceText = "Put a creature card from your hand onto the battlefield?";
private final FilterCreatureCard filter;
public PutCreatureOnBattlefieldEffect() {
this(new FilterCreatureCard("a creature card"));
}
public PutCreatureOnBattlefieldEffect(FilterCreatureCard filter) {
super(Outcome.PutCreatureInPlay);
this.staticText = "You may put a creature card from your hand onto the battlefield";
this.filter = filter;
}
public PutCreatureOnBattlefieldEffect(final PutCreatureOnBattlefieldEffect effect) {
super(effect);
this.filter = effect.filter.copy();
}
@Override
@ -34,11 +40,12 @@ public class PutCreatureOnBattlefieldEffect extends OneShotEffect {
@Override
public boolean apply(Game game, Ability source) {
Player player = game.getPlayer(source.getControllerId());
String choiceText = "Put " + filter.getMessage() + " from your hand onto the battlefield?";
if (player == null || !player.chooseUse(Outcome.PutCreatureInPlay, choiceText, source, game)) {
return false;
}
TargetCardInHand target = new TargetCardInHand(new FilterCreatureCard());
TargetCardInHand target = new TargetCardInHand(filter);
if (player.choose(Outcome.PutCreatureInPlay, target, source.getSourceId(), game)) {
Card card = game.getCard(target.getFirstTarget());
if (card != null) {
@ -48,4 +55,13 @@ public class PutCreatureOnBattlefieldEffect extends OneShotEffect {
}
return false;
}
}
@Override
public String getText(Mode mode) {
if(this.staticText != null && !this.staticText.isEmpty()) {
return staticText;
}
return "You may put " + filter.getMessage() + " from your hand onto the battlefield";
}
}

View file

@ -98,7 +98,7 @@ public class PutOnLibrarySourceEffect extends OneShotEffect {
} else {
// Put Champion of Stray Souls on top of your library from your graveyard
sb.append("put {this} on ");
sb.append(onTop ? "top" : "the bottom").append(" of it's owner's library");
sb.append(onTop ? "top" : "the bottom").append(" of its owner's library");
}
return sb.toString();

View file

@ -1,16 +1,16 @@
/*
* 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
@ -20,7 +20,7 @@
* 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.
@ -147,7 +147,7 @@ public class PutOnLibraryTargetEffect extends OneShotEffect {
if (this.staticText != null && !this.staticText.isEmpty()) {
return staticText;
}
StringBuilder sb = new StringBuilder();
StringBuilder sb = new StringBuilder();
Target target = mode.getTargets().get(0);
sb.append("Put ");
if (target.getMaxNumberOfTargets() == 0) {
@ -161,8 +161,8 @@ public class PutOnLibraryTargetEffect extends OneShotEffect {
}
}
sb.append("target ").append(mode.getTargets().get(0).getTargetName()).append(" on ");
sb.append(onTop ? "top" : "the bottom").append(" of it's owner's library");
sb.append(onTop ? "top" : "the bottom").append(" of its owner's library");
return sb.toString();
}

View file

@ -25,15 +25,12 @@
* authors and should not be interpreted as representing official policies, either expressed
* or implied, of BetaSteward_at_googlemail.com.
*/
package mage.abilities.effects.common;
import mage.abilities.Ability;
import mage.abilities.dynamicvalue.DynamicValue;
import mage.abilities.dynamicvalue.common.StaticValue;
import mage.abilities.effects.OneShotEffect;
import mage.cards.Card;
import mage.cards.Cards;
import mage.constants.Outcome;
import mage.constants.Zone;
import mage.game.Game;
@ -43,7 +40,6 @@ import mage.util.CardUtil;
/**
* @author LevelX2
*/
public class PutTopCardOfLibraryIntoGraveTargetEffect extends OneShotEffect {
private DynamicValue numberCards;
@ -51,6 +47,7 @@ public class PutTopCardOfLibraryIntoGraveTargetEffect extends OneShotEffect {
public PutTopCardOfLibraryIntoGraveTargetEffect(int numberCards) {
this(new StaticValue(numberCards));
}
public PutTopCardOfLibraryIntoGraveTargetEffect(DynamicValue numberCards) {
super(Outcome.Discard);
this.numberCards = numberCards;

View file

@ -25,12 +25,11 @@
* authors and should not be interpreted as representing official policies, either expressed
* or implied, of BetaSteward_at_googlemail.com.
*/
package mage.abilities.effects.common;
import mage.constants.Outcome;
import mage.abilities.Ability;
import mage.abilities.effects.OneShotEffect;
import mage.constants.Outcome;
import mage.filter.FilterPermanent;
import mage.game.Game;
import mage.game.permanent.Permanent;
@ -40,10 +39,9 @@ import mage.target.targetpointer.FixedTarget;
*
* @author LevelX2
*/
public class RegenerateAllEffect extends OneShotEffect {
public class RegenerateAllEffect extends OneShotEffect {
private FilterPermanent filter;
private final FilterPermanent filter;
public RegenerateAllEffect(FilterPermanent filter) {
super(Outcome.DestroyPermanent);

View file

@ -1,35 +1,32 @@
/*
* 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.
*/
* 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.effects.common;
import java.util.LinkedList;
import java.util.UUID;
import mage.MageObject;
import mage.abilities.Ability;
import mage.abilities.effects.OneShotEffect;
@ -41,6 +38,7 @@ import static mage.constants.Zone.GRAVEYARD;
import static mage.constants.Zone.HAND;
import mage.game.ExileZone;
import mage.game.Game;
import mage.game.permanent.PermanentToken;
import mage.players.Player;
import mage.util.CardUtil;
@ -55,7 +53,7 @@ public class ReturnFromExileForSourceEffect extends OneShotEffect {
private boolean previousZone;
/**
*
*
* @param zone Zone the card should return to
*/
public ReturnFromExileForSourceEffect(Zone zone) {
@ -65,12 +63,13 @@ public class ReturnFromExileForSourceEffect extends OneShotEffect {
public ReturnFromExileForSourceEffect(Zone zone, boolean tapped) {
this(zone, tapped, true);
}
/**
*
*
* @param zone
* @param tapped
* @param previousZone if this is used from a dies leave battlefield or destroyed trigger, the exile zone is based on previous zone of the object
* @param previousZone if this is used from a dies leave battlefield or
* destroyed trigger, the exile zone is based on previous zone of the object
*/
public ReturnFromExileForSourceEffect(Zone zone, boolean tapped, boolean previousZone) {
super(Outcome.PutCardInPlay);
@ -94,24 +93,25 @@ public class ReturnFromExileForSourceEffect extends OneShotEffect {
@Override
public boolean apply(Game game, Ability source) {
Player controller = game.getPlayer(source.getControllerId());
if (controller != null) {
int zoneChangeCounter = source.getSourceObjectZoneChangeCounter() - (previousZone ? 1:0);
UUID exileId = CardUtil.getExileZoneId(game, source.getSourceId(), zoneChangeCounter);
ExileZone exile = game.getExile().getExileZone(exileId);
Player controller = game.getPlayer(source.getControllerId());
MageObject sourceObject = source.getSourceObject(game);
if (sourceObject != null && controller != null) {
int zoneChangeCounter = source.getSourceObjectZoneChangeCounter();
if (zoneChangeCounter > 0 && previousZone && !(sourceObject instanceof PermanentToken)) {
zoneChangeCounter--;
}
ExileZone exile = game.getExile().getExileZone(CardUtil.getExileZoneId(game, source.getSourceId(), zoneChangeCounter));
if (exile != null) { // null is valid if source left battlefield before enters the battlefield effect resolved
LinkedList<UUID> cards = new LinkedList<>(exile);
for (UUID cardId: cards) {
Card card = game.getCard(cardId);
if (card == null) {
return false;
if (returnToZone.equals(Zone.BATTLEFIELD)) {
for (Card card : exile.getCards(game)) {
Player owner = game.getPlayer(card.getOwnerId());
if (owner != null) {
owner.putOntoBattlefieldWithInfo(card, game, Zone.EXILED, source.getSourceId());
}
}
if (!game.isSimulation()) {
game.informPlayers(controller.getLogName() + " moves " + card.getLogName() + " from exile to " + returnToZone.toString().toLowerCase());
}
card.moveToZone(returnToZone, source.getSourceId(), game, tapped);
} else {
controller.moveCards(exile, Zone.EXILED, returnToZone, source, game);
}
exile.clear();
}
return true;
}
@ -121,7 +121,7 @@ public class ReturnFromExileForSourceEffect extends OneShotEffect {
private void setText() {
StringBuilder sb = new StringBuilder();
sb.append("return the exiled cards ");
switch(returnToZone) {
switch (returnToZone) {
case BATTLEFIELD:
sb.append("to the battlefield under its owner's control");
if (tapped) {

View file

@ -1,16 +1,16 @@
/*
* 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
@ -20,12 +20,11 @@
* 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.effects.common;
import java.util.UUID;
@ -69,18 +68,12 @@ public class ReturnFromGraveyardToBattlefieldTargetEffect extends OneShotEffect
@Override
public boolean apply(Game game, Ability source) {
Player player = game.getPlayer(source.getControllerId());
if (player != null) {
Player controller = game.getPlayer(source.getControllerId());
if (controller != null) {
for (UUID targetId : getTargetPointer().getTargets(game, source)) {
Card card = game.getCard(targetId);
if (card != null) {
if (player.putOntoBattlefieldWithInfo(card, game, Zone.GRAVEYARD, source.getSourceId(), tapped)) {
// why is this neccessary - it contradicts Gather Specimens in play
// Permanent permanent = game.getPermanent(targetId);
// if (permanent != null) {
// permanent.changeControllerId(source.getControllerId(), game);
// }
}
controller.putOntoBattlefieldWithInfo(card, game, Zone.GRAVEYARD, source.getSourceId(), tapped);
}
}
return true;

View file

@ -25,7 +25,6 @@
* authors and should not be interpreted as representing official policies, either expressed
* or implied, of BetaSteward_at_googlemail.com.
*/
package mage.abilities.effects.common;
import mage.abilities.Ability;
@ -42,20 +41,27 @@ import mage.game.Game;
public class ReturnToBattlefieldUnderOwnerControlSourceEffect extends OneShotEffect {
private boolean tapped;
private int zoneChangeCounter;
public ReturnToBattlefieldUnderOwnerControlSourceEffect() {
this(false);
}
public ReturnToBattlefieldUnderOwnerControlSourceEffect(boolean tapped) {
this(tapped, -1);
}
public ReturnToBattlefieldUnderOwnerControlSourceEffect(boolean tapped, int zoneChangeCounter) {
super(Outcome.Benefit);
this.tapped = tapped;
staticText = new StringBuilder("return that card to the battlefield").append(tapped?" tapped":"").append(" under it's owner's control").toString();
this.zoneChangeCounter = zoneChangeCounter;
staticText = new StringBuilder("return that card to the battlefield").append(tapped ? " tapped" : "").append(" under its owner's control").toString();
}
public ReturnToBattlefieldUnderOwnerControlSourceEffect(final ReturnToBattlefieldUnderOwnerControlSourceEffect effect) {
super(effect);
this.tapped = effect.tapped;
this.zoneChangeCounter = effect.zoneChangeCounter;
}
@Override
@ -67,12 +73,19 @@ public class ReturnToBattlefieldUnderOwnerControlSourceEffect extends OneShotEff
public boolean apply(Game game, Ability source) {
Card card = game.getCard(source.getSourceId());
if (card != null) {
Zone currentZone = game.getState().getZone(card.getId());
if (card.putOntoBattlefield(game, currentZone, source.getSourceId(), card.getOwnerId(),tapped)) {
return true;
// return only from public zones
switch (game.getState().getZone(card.getId())) {
case EXILED:
case COMMAND:
case GRAVEYARD:
if (zoneChangeCounter < 0 || game.getState().getZoneChangeCounter(card.getId()) == zoneChangeCounter) {
Zone currentZone = game.getState().getZone(card.getId());
card.putOntoBattlefield(game, currentZone, source.getSourceId(), card.getOwnerId(), tapped);
}
break;
}
return true;
}
return false;
}
}

View file

@ -0,0 +1,86 @@
/*
* 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.effects.common;
import java.util.List;
import mage.abilities.Ability;
import mage.abilities.Mode;
import mage.abilities.effects.OneShotEffect;
import mage.constants.Outcome;
import mage.filter.FilterPermanent;
import mage.filter.predicate.permanent.ControllerIdPredicate;
import mage.game.Game;
import mage.game.permanent.Permanent;
import mage.players.Player;
/**
*
* @author LoneFox
*/
public class TapAllTargetPlayerControlsEffect extends OneShotEffect {
private FilterPermanent filter;
public TapAllTargetPlayerControlsEffect(FilterPermanent filter) {
super(Outcome.Tap);
this.filter = filter;
}
public TapAllTargetPlayerControlsEffect(final TapAllTargetPlayerControlsEffect effect) {
super(effect);
filter = effect.filter.copy();
}
@Override
public boolean apply(Game game, Ability source) {
Player player = game.getPlayer(targetPointer.getFirst(game, source));
if(player != null) {
filter.add(new ControllerIdPredicate(player.getId()));
List<Permanent> permanents = game.getBattlefield().getActivePermanents(filter, source.getControllerId(), source.getSourceId(), game);
for(Permanent p : permanents) {
p.tap(game);
}
return true;
}
return false;
}
@Override
public TapAllTargetPlayerControlsEffect copy() {
return new TapAllTargetPlayerControlsEffect(this);
}
@Override
public String getText(Mode mode) {
if(staticText != null && !staticText.isEmpty()) {
return staticText;
}
return "tap all " + filter.getMessage() + " target player controls";
}
}

View file

@ -0,0 +1,77 @@
/*
* 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.effects.common.combat;
import mage.abilities.Ability;
import mage.abilities.costs.mana.ManaCosts;
import mage.abilities.effects.PayCostToAttackBlockEffectImpl;
import mage.abilities.effects.PayCostToAttackBlockEffectImpl.RestrictType;
import mage.constants.AttachmentType;
import mage.constants.Duration;
import mage.constants.Outcome;
import mage.game.Game;
import mage.game.events.GameEvent;
import mage.game.events.GameEvent.EventType;
import mage.game.permanent.Permanent;
/**
*
* @author LevelX2
*/
public class CantAttackBlockUnlessPaysAttachedEffect extends PayCostToAttackBlockEffectImpl {
public CantAttackBlockUnlessPaysAttachedEffect(ManaCosts manaCosts, AttachmentType attachmentType) {
super(Duration.WhileOnBattlefield, Outcome.Detriment, RestrictType.ATTACK_AND_BLOCK, manaCosts);
staticText = (attachmentType.equals(AttachmentType.AURA) ? "Enchanted " : "Equipped ")
+ "creature can't attack or block unless its controller pays "
+ (manaCosts == null ? "" : manaCosts.getText());
}
public CantAttackBlockUnlessPaysAttachedEffect(CantAttackBlockUnlessPaysAttachedEffect effect) {
super(effect);
}
@Override
public boolean applies(GameEvent event, Ability source, Game game) {
Permanent enchantment = game.getPermanent(source.getSourceId());
if (enchantment != null && enchantment.getAttachedTo() != null) {
if (event.getType().equals(EventType.DECLARE_ATTACKER)) {
return event.getSourceId().equals(enchantment.getAttachedTo());
}
if (event.getType().equals(EventType.DECLARE_BLOCKER)) {
return event.getSourceId().equals(enchantment.getAttachedTo());
}
}
return false;
}
@Override
public CantAttackBlockUnlessPaysAttachedEffect copy() {
return new CantAttackBlockUnlessPaysAttachedEffect(this);
}
}

View file

@ -0,0 +1,78 @@
/*
* 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.effects.common.combat;
import mage.abilities.Ability;
import mage.abilities.costs.Cost;
import mage.abilities.costs.mana.ManaCosts;
import mage.abilities.effects.PayCostToAttackBlockEffectImpl;
import mage.constants.Duration;
import mage.constants.Outcome;
import mage.game.Game;
import mage.game.events.GameEvent;
import mage.game.events.GameEvent.EventType;
/**
*
* @author LevelX2
*/
public class CantAttackBlockUnlessPaysSourceEffect extends PayCostToAttackBlockEffectImpl {
public CantAttackBlockUnlessPaysSourceEffect(Cost cost, RestrictType restrictType) {
super(Duration.WhileOnBattlefield, Outcome.Detriment, restrictType, cost);
staticText = "{this} can't " + restrictType.toString() + " unless you "
+ cost == null ? "" : cost.getText()
+ (restrictType.equals(RestrictType.ATTACK) ? " <i>(This cost is paid as attackers are declared.)</i>" : "");
}
public CantAttackBlockUnlessPaysSourceEffect(ManaCosts manaCosts, RestrictType restrictType) {
super(Duration.WhileOnBattlefield, Outcome.Detriment, RestrictType.ATTACK_AND_BLOCK, manaCosts);
staticText = "{this} can't " + restrictType.toString() + " unless you pay "
+ manaCosts == null ? "" : manaCosts.getText();
}
public CantAttackBlockUnlessPaysSourceEffect(CantAttackBlockUnlessPaysSourceEffect effect) {
super(effect);
}
@Override
public boolean applies(GameEvent event, Ability source, Game game) {
if (!restrictType.equals(RestrictType.BLOCK) && event.getType().equals(EventType.DECLARE_ATTACKER)) {
return event.getSourceId().equals(source.getSourceId());
}
if (!restrictType.equals(RestrictType.ATTACK) && event.getType().equals(EventType.DECLARE_BLOCKER)) {
return event.getSourceId().equals(source.getSourceId());
}
return false;
}
@Override
public CantAttackBlockUnlessPaysSourceEffect copy() {
return new CantAttackBlockUnlessPaysSourceEffect(this);
}
}

View file

@ -0,0 +1,76 @@
/*
* 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.effects.common.combat;
import java.util.UUID;
import mage.abilities.Ability;
import mage.abilities.effects.RestrictionEffect;
import mage.constants.Duration;
import mage.constants.Outcome;
import mage.filter.common.FilterCreaturePermanent;
import mage.game.Game;
import mage.game.permanent.Permanent;
/**
*
* @author LevelX2
*/
public class CantAttackYouAllEffect extends RestrictionEffect {
private final FilterCreaturePermanent filterAttacker;
public CantAttackYouAllEffect(Duration duration) {
this(duration, new FilterCreaturePermanent());
}
public CantAttackYouAllEffect(Duration duration, FilterCreaturePermanent filter) {
super(duration, Outcome.Benefit);
this.filterAttacker = filter;
staticText = "Creatures can't attack you";
}
CantAttackYouAllEffect(final CantAttackYouAllEffect effect) {
super(effect);
this.filterAttacker = effect.filterAttacker;
}
@Override
public boolean applies(Permanent permanent, Ability source, Game game) {
return filterAttacker.match(permanent, source.getSourceId(), source.getControllerId(), game);
}
@Override
public boolean canAttack(UUID defenderId, Ability source, Game game) {
return !defenderId.equals(source.getControllerId());
}
@Override
public CantAttackYouAllEffect copy() {
return new CantAttackYouAllEffect(this);
}
}

View file

@ -0,0 +1,106 @@
/*
* 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.effects.common.combat;
import mage.abilities.Ability;
import mage.abilities.costs.mana.ManaCosts;
import mage.abilities.effects.PayCostToAttackBlockEffectImpl;
import mage.abilities.effects.PayCostToAttackBlockEffectImpl.RestrictType;
import mage.constants.CardType;
import mage.constants.Duration;
import mage.constants.Outcome;
import mage.filter.common.FilterCreaturePermanent;
import mage.game.Game;
import mage.game.events.GameEvent;
import mage.game.permanent.Permanent;
/**
*
* @author LevelX2
*/
public class CantAttackYouUnlessPayManaAllEffect extends PayCostToAttackBlockEffectImpl {
private final FilterCreaturePermanent filterCreaturePermanent;
private final boolean payAlsoForAttackingPlaneswalker;
public CantAttackYouUnlessPayManaAllEffect(ManaCosts manaCosts) {
this(manaCosts, false);
}
public CantAttackYouUnlessPayManaAllEffect(ManaCosts manaCosts, boolean payAlsoForAttackingPlaneswalker) {
this(manaCosts, payAlsoForAttackingPlaneswalker, null);
}
public CantAttackYouUnlessPayManaAllEffect(ManaCosts manaCosts, boolean payAlsoForAttackingPlaneswalker, FilterCreaturePermanent filter) {
super(Duration.WhileOnBattlefield, Outcome.Detriment, RestrictType.ATTACK, manaCosts);
this.payAlsoForAttackingPlaneswalker = payAlsoForAttackingPlaneswalker;
this.filterCreaturePermanent = filter;
staticText = (filterCreaturePermanent == null ? "Creatures" : filterCreaturePermanent.getMessage())
+ " can't attack you "
+ (payAlsoForAttackingPlaneswalker ? "or a planeswalker you control " : "")
+ "unless their controller pays "
+ (manaCosts == null ? "" : manaCosts.getText())
+ " for each creature he or she controls that's attacking you";
}
public CantAttackYouUnlessPayManaAllEffect(CantAttackYouUnlessPayManaAllEffect effect) {
super(effect);
this.payAlsoForAttackingPlaneswalker = effect.payAlsoForAttackingPlaneswalker;
this.filterCreaturePermanent = effect.filterCreaturePermanent;
}
@Override
public boolean applies(GameEvent event, Ability source, Game game) {
// check if attacking creature fullfills filter criteria
if (filterCreaturePermanent != null) {
Permanent permanent = game.getPermanent(event.getSourceId());
if (!filterCreaturePermanent.match(permanent, source.getSourceId(), source.getControllerId(), game)) {
return false;
}
}
// attack target is controlling player
if (source.getControllerId().equals(event.getTargetId())) {
return true;
}
// or attack target is a planeswalker of the controlling player
if (payAlsoForAttackingPlaneswalker) {
Permanent permanent = game.getPermanent(event.getTargetId());
if (permanent != null
&& permanent.getCardType().contains(CardType.PLANESWALKER)
&& permanent.getControllerId().equals(source.getControllerId())) {
return true;
}
}
return false;
}
@Override
public CantAttackYouUnlessPayManaAllEffect copy() {
return new CantAttackYouUnlessPayManaAllEffect(this);
}
}

View file

@ -0,0 +1,88 @@
/*
* 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.effects.common.combat;
import mage.abilities.Ability;
import mage.abilities.costs.mana.ManaCosts;
import mage.abilities.effects.PayCostToAttackBlockEffectImpl;
import mage.abilities.effects.PayCostToAttackBlockEffectImpl.RestrictType;
import mage.constants.Duration;
import mage.constants.Outcome;
import mage.filter.common.FilterCreaturePermanent;
import mage.game.Game;
import mage.game.events.GameEvent;
import mage.game.permanent.Permanent;
/**
*
* @author LevelX2
*/
public class CantBlockUnlessPayManaAllEffect extends PayCostToAttackBlockEffectImpl {
private final FilterCreaturePermanent filterCreaturePermanent;
public CantBlockUnlessPayManaAllEffect(ManaCosts manaCosts) {
this(manaCosts, false);
}
public CantBlockUnlessPayManaAllEffect(ManaCosts manaCosts, boolean payAlsoForAttackingPlaneswalker) {
this(manaCosts, payAlsoForAttackingPlaneswalker, null);
}
public CantBlockUnlessPayManaAllEffect(ManaCosts manaCosts, boolean payAlsoForAttackingPlaneswalker, FilterCreaturePermanent filter) {
super(Duration.WhileOnBattlefield, Outcome.Detriment, RestrictType.BLOCK, manaCosts);
this.filterCreaturePermanent = filter;
staticText = (filterCreaturePermanent == null ? "Creatures" : filterCreaturePermanent.getMessage())
+ " can't block "
+ "unless their controller pays "
+ (manaCosts == null ? "" : manaCosts.getText())
+ " for each blocking creature he or she controls";
}
public CantBlockUnlessPayManaAllEffect(CantBlockUnlessPayManaAllEffect effect) {
super(effect);
this.filterCreaturePermanent = effect.filterCreaturePermanent;
}
@Override
public boolean applies(GameEvent event, Ability source, Game game) {
// check if blocking creature fullfills filter criteria
if (filterCreaturePermanent != null) {
Permanent permanent = game.getPermanent(event.getSourceId());
if (!filterCreaturePermanent.match(permanent, source.getSourceId(), source.getControllerId(), game)) {
return false;
}
}
return true;
}
@Override
public CantBlockUnlessPayManaAllEffect copy() {
return new CantBlockUnlessPayManaAllEffect(this);
}
}

View file

@ -0,0 +1,66 @@
package mage.abilities.effects.common.continuous;
import java.util.Set;
import mage.abilities.Ability;
import mage.abilities.effects.ContinuousEffect;
import mage.abilities.effects.Effect;
import mage.abilities.effects.OneShotEffect;
import mage.cards.repository.CardRepository;
import mage.choices.Choice;
import mage.choices.ChoiceImpl;
import mage.constants.Duration;
import mage.constants.Outcome;
import mage.game.Game;
import mage.game.permanent.Permanent;
import mage.players.Player;
import mage.target.targetpointer.FixedTarget;
public class BecomesChosenNonWallCreatureTypeTargetEffect extends OneShotEffect {
public BecomesChosenNonWallCreatureTypeTargetEffect() {
super(Outcome.BoostCreature);
staticText = "choose a creature type other than wall, target creature's type becomes that type until end of turn";
}
public BecomesChosenNonWallCreatureTypeTargetEffect(final BecomesChosenNonWallCreatureTypeTargetEffect effect) {
super(effect);
}
@Override
public boolean apply(Game game, Ability source) {
Player player = game.getPlayer(source.getControllerId());
Permanent permanent = game.getPermanent(source.getSourceId());
String chosenType = "";
if (player != null && permanent != null) {
Choice typeChoice = new ChoiceImpl(true);
typeChoice.setMessage("Choose creature type other than Wall");
Set<String> types = CardRepository.instance.getCreatureTypes();
types.remove("Wall");
typeChoice.setChoices(types);
while (!player.choose(Outcome.BoostCreature, typeChoice, game)) {
if (!player.isInGame()) {
return false;
}
}
game.informPlayers(permanent.getName() + ": " + player.getLogName() + " has chosen " + typeChoice.getChoice());
chosenType = typeChoice.getChoice();
if (chosenType != null && !chosenType.isEmpty()) {
// ADD TYPE TO TARGET
ContinuousEffect effect = new BecomesSubtypeTargetEffect(Duration.EndOfTurn, chosenType);
effect.setTargetPointer(new FixedTarget(getTargetPointer().getFirst(game, source)));
game.addEffect(effect, source);
return true;
}
}
return false;
}
@Override
public Effect copy() {
return new BecomesChosenNonWallCreatureTypeTargetEffect(this);
}
}

View file

@ -1,16 +1,16 @@
/*
* 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
@ -20,12 +20,11 @@
* 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.effects.common.continuous;
import mage.abilities.Ability;
@ -45,18 +44,23 @@ import mage.game.permanent.token.Token;
*/
public class BecomesCreatureAttachedEffect extends ContinuousEffectImpl {
public enum LoseType {
NONE, ALL, ALL_BUT_COLOR, ABILITIES, ABILITIES_AND_PT
};
protected Token token;
protected String type;
protected boolean loseOther; // loses all other abilities, card types, and creature types
protected LoseType loseType; // what attributes are lost
public BecomesCreatureAttachedEffect(Token token, String text, Duration duration) {
this(token, text, duration, false);
this(token, text, duration, LoseType.NONE);
}
public BecomesCreatureAttachedEffect(Token token, String text, Duration duration, boolean loseOther) {
super(duration, Layer.TypeChangingEffects_4, SubLayer.NA, Outcome.BecomeCreature);
public BecomesCreatureAttachedEffect(Token token, String text, Duration duration, LoseType loseType) {
super(duration, Layer.TypeChangingEffects_4, SubLayer.NA, Outcome.BecomeCreature);
this.token = token;
this.loseOther = loseOther;
this.loseType = loseType;
staticText = text;
}
@ -64,7 +68,7 @@ public class BecomesCreatureAttachedEffect extends ContinuousEffectImpl {
super(effect);
this.token = effect.token.copy();
this.type = effect.type;
this.loseOther = effect.loseOther;
this.loseType = effect.loseType;
}
@Override
@ -89,8 +93,11 @@ public class BecomesCreatureAttachedEffect extends ContinuousEffectImpl {
}
}
// card type
if (loseOther) {
permanent.getCardType().clear();
switch (loseType) {
case ALL:
case ALL_BUT_COLOR:
permanent.getCardType().clear();
break;
}
if (token.getCardType().size() > 0) {
for (CardType t : token.getCardType()) {
@ -100,8 +107,11 @@ public class BecomesCreatureAttachedEffect extends ContinuousEffectImpl {
}
}
// sub type
if (loseOther) {
permanent.getSubtype().clear();
switch (loseType) {
case ALL:
case ALL_BUT_COLOR:
permanent.getSubtype().clear();
break;
}
if (token.getSubtype().size() > 0) {
for (String t : token.getSubtype()) {
@ -114,7 +124,7 @@ public class BecomesCreatureAttachedEffect extends ContinuousEffectImpl {
break;
case ColorChangingEffects_5:
if (sublayer == SubLayer.NA) {
if (loseOther) {
if (loseType.equals(LoseType.ALL)) {
permanent.getColor(game).setBlack(false);
permanent.getColor(game).setGreen(false);
permanent.getColor(game).setBlue(false);
@ -128,11 +138,16 @@ public class BecomesCreatureAttachedEffect extends ContinuousEffectImpl {
break;
case AbilityAddingRemovingEffects_6:
if (sublayer == SubLayer.NA) {
if (loseOther) {
permanent.removeAllAbilities(source.getSourceId(), game);
switch (loseType) {
case ALL:
case ALL_BUT_COLOR:
case ABILITIES:
case ABILITIES_AND_PT:
permanent.removeAllAbilities(source.getSourceId(), game);
break;
}
if (token.getAbilities().size() > 0) {
for (Ability ability: token.getAbilities()) {
for (Ability ability : token.getAbilities()) {
permanent.addAbility(ability, source.getSourceId(), game);
}
}
@ -142,6 +157,7 @@ public class BecomesCreatureAttachedEffect extends ContinuousEffectImpl {
if (sublayer == SubLayer.SetPT_7b) {
permanent.getPower().setValue(token.getPower().getValue());
permanent.getToughness().setValue(token.getToughness().getValue());
break;
}
}
}

View file

@ -0,0 +1,112 @@
/*
* To change this license header, choose License Headers in Project Properties.
* To change this template file, choose Tools | Templates
* and open the template in the editor.
*/
package mage.abilities.effects.common.continuous;
import java.util.ArrayList;
import mage.abilities.Ability;
import mage.abilities.effects.ContinuousEffectImpl;
import mage.constants.Duration;
import mage.constants.Layer;
import mage.constants.Outcome;
import mage.constants.SubLayer;
import mage.filter.common.FilterCreaturePermanent;
import mage.game.Game;
import mage.game.permanent.Permanent;
/**
*
* @author LevelX2
*/
public class BecomesSubtypeAllEffect extends ContinuousEffectImpl {
protected ArrayList<String> subtypes = new ArrayList();
protected boolean loseOther; // loses other subtypes
public BecomesSubtypeAllEffect(Duration duration, String subtype) {
this(duration, createArrayList(subtype));
}
public BecomesSubtypeAllEffect(Duration duration, ArrayList<String> subtypes) {
this(duration, subtypes, true);
}
public BecomesSubtypeAllEffect(Duration duration,
ArrayList<String> subtypes, boolean loseOther) {
super(duration, Outcome.Detriment);
this.subtypes = subtypes;
this.staticText = setText();
this.loseOther = loseOther;
}
private static ArrayList<String> createArrayList(String subtype) {
ArrayList<String> subtypes = new ArrayList<>();
subtypes.add(subtype);
return subtypes;
}
public BecomesSubtypeAllEffect(final BecomesSubtypeAllEffect effect) {
super(effect);
this.subtypes.addAll(effect.subtypes);
this.loseOther = effect.loseOther;
this.loseOther = effect.loseOther;
}
@Override
public boolean apply(Game game, Ability source) {
return false;
}
@Override
public BecomesSubtypeAllEffect copy() {
return new BecomesSubtypeAllEffect(this);
}
@Override
public boolean apply(Layer layer, SubLayer sublayer, Ability source,
Game game) {
for (Permanent permanent : game.getBattlefield()
.getAllActivePermanents(new FilterCreaturePermanent(), game)) {
if (permanent != null) {
switch (layer) {
case TypeChangingEffects_4:
if (loseOther) {
permanent.getSubtype().clear();
permanent.getSubtype().addAll(subtypes);
} else {
for (String subtype : subtypes) {
if (!permanent.getSubtype().contains(subtype)) {
permanent.getSubtype().add(subtype);
}
}
}
break;
}
} else {
if (duration.equals(Duration.Custom)) {
discard();
}
}
}
return true;
}
@Override
public boolean hasLayer(Layer layer) {
return layer == Layer.TypeChangingEffects_4;
}
private String setText() {
StringBuilder sb = new StringBuilder();
sb.append("Target creature becomes that type");
if (!duration.toString().isEmpty()
&& !duration.equals(Duration.EndOfGame)) {
sb.append(" ").append(duration.toString());
}
return sb.toString();
}
}

View file

@ -1,16 +1,16 @@
/*
* 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
@ -20,7 +20,7 @@
* 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.
@ -33,6 +33,7 @@ import mage.abilities.Mode;
import mage.abilities.dynamicvalue.DynamicValue;
import mage.abilities.dynamicvalue.common.StaticValue;
import mage.abilities.effects.ContinuousEffectImpl;
import mage.constants.CardType;
import mage.constants.Duration;
import mage.constants.Layer;
import mage.constants.Outcome;
@ -64,7 +65,8 @@ public class BoostTargetEffect extends ContinuousEffectImpl {
* @param power
* @param toughness
* @param duration
* @param lockedIn if true, power and toughness will be calculated only once, when the ability resolves
* @param lockedIn if true, power and toughness will be calculated only
* once, when the ability resolves
*/
public BoostTargetEffect(DynamicValue power, DynamicValue toughness, Duration duration, boolean lockedIn) {
super(duration, Layer.PTChangingEffects_7, SubLayer.ModifyPT_7c, isCanKill(toughness) ? Outcome.UnboostCreature : Outcome.BoostCreature);
@ -99,7 +101,7 @@ public class BoostTargetEffect extends ContinuousEffectImpl {
int affectedTargets = 0;
for (UUID permanentId : targetPointer.getTargets(game, source)) {
Permanent target = game.getPermanent(permanentId);
if (target != null) {
if (target != null && target.getCardType().contains(CardType.CREATURE)) {
target.addPower(power.calculate(game, source, this));
target.addToughness(toughness.calculate(game, source, this));
affectedTargets++;
@ -115,7 +117,7 @@ public class BoostTargetEffect extends ContinuousEffectImpl {
}
StringBuilder sb = new StringBuilder();
Target target = mode.getTargets().get(0);
if(target.getMaxNumberOfTargets() > 1){
if (target.getMaxNumberOfTargets() > 1) {
if (target.getNumberOfTargets() < target.getNumberOfTargets()) {
sb.append("up to ");
}
@ -127,16 +129,15 @@ public class BoostTargetEffect extends ContinuousEffectImpl {
sb.append(target.getTargetName()).append(" gets ");
}
String p = power.toString();
if(!p.startsWith("-")) {
if (!p.startsWith("-")) {
sb.append("+");
}
sb.append(p).append("/");
String t = toughness.toString();
if(!t.startsWith("-")){
if(p.startsWith("-")) {
if (!t.startsWith("-")) {
if (t.equals("0") && p.startsWith("-")) {
sb.append("-");
}
else {
} else {
sb.append("+");
}
}

View file

@ -25,7 +25,6 @@
* authors and should not be interpreted as representing official policies, either expressed
* or implied, of BetaSteward_at_googlemail.com.
*/
package mage.abilities.effects.common.cost;
import mage.abilities.Ability;
@ -39,19 +38,17 @@ import mage.constants.Outcome;
import mage.game.Game;
import mage.util.CardUtil;
/**
*
* @author LevelX2
*/
public class SpellCostReductionSourceEffect extends CostModificationEffectImpl {
private final int amount;
private ManaCosts<ManaCost> manaCostsToReduce = null;
private final Condition condition;
public SpellCostReductionSourceEffect(ManaCosts<ManaCost> manaCostsToReduce, Condition condition) {
public SpellCostReductionSourceEffect(ManaCosts<ManaCost> manaCostsToReduce, Condition condition) {
super(Duration.WhileOnBattlefield, Outcome.Benefit, CostModificationType.REDUCE_COST);
this.amount = 0;
this.manaCostsToReduce = manaCostsToReduce;
@ -59,14 +56,13 @@ public class SpellCostReductionSourceEffect extends CostModificationEffectImpl {
StringBuilder sb = new StringBuilder();
sb.append("{this} costs ");
for (String manaSymbol :manaCostsToReduce.getSymbols()) {
for (String manaSymbol : manaCostsToReduce.getSymbols()) {
sb.append(manaSymbol);
}
sb.append(" less to if ").append(this.condition.toString());
this.staticText = sb.toString();
}
public SpellCostReductionSourceEffect(int amount, Condition condition) {
super(Duration.WhileOnBattlefield, Outcome.Benefit, CostModificationType.REDUCE_COST);
this.amount = amount;
@ -76,7 +72,7 @@ public class SpellCostReductionSourceEffect extends CostModificationEffectImpl {
this.staticText = sb.toString();
}
protected SpellCostReductionSourceEffect(SpellCostReductionSourceEffect effect) {
protected SpellCostReductionSourceEffect(final SpellCostReductionSourceEffect effect) {
super(effect);
this.amount = effect.amount;
this.manaCostsToReduce = effect.manaCostsToReduce;
@ -85,7 +81,7 @@ public class SpellCostReductionSourceEffect extends CostModificationEffectImpl {
@Override
public boolean apply(Game game, Ability source, Ability abilityToModify) {
if (manaCostsToReduce != null){
if (manaCostsToReduce != null) {
CardUtil.adjustCost((SpellAbility) abilityToModify, manaCostsToReduce, false);
} else {
CardUtil.reduceCost(abilityToModify, this.amount);
@ -95,7 +91,7 @@ public class SpellCostReductionSourceEffect extends CostModificationEffectImpl {
@Override
public boolean applies(Ability abilityToModify, Ability source, Game game) {
if (abilityToModify.getSourceId().equals(source.getSourceId()) && (abilityToModify instanceof SpellAbility)) {
if (abilityToModify.getSourceId().equals(source.getSourceId()) && (abilityToModify instanceof SpellAbility)) {
return condition.apply(game, source);
}
return false;

View file

@ -73,9 +73,9 @@ public class DiscardCardYouChooseTargetEffect extends OneShotEffect {
public DiscardCardYouChooseTargetEffect(FilterCard filter) {
this(filter, TargetController.OPPONENT);
}
public DiscardCardYouChooseTargetEffect(TargetController targetController, int numberCardsToReveal) {
this(new FilterCard("one card"), targetController,
this(new FilterCard("one card"), targetController,
new StaticValue(numberCardsToReveal, new StringBuilder(CardUtil.numberToText(numberCardsToReveal)).append(" cards").toString()));
}
@ -87,28 +87,28 @@ public class DiscardCardYouChooseTargetEffect extends OneShotEffect {
super(Outcome.Discard);
this.targetController = targetController;
this.filter = filter;
this.revealAllCards = false;
this.numberCardsToReveal = numberCardsToReveal;
this.numberCardsToDiscard = new StaticValue(1);
staticText = this.setText();
staticText = this.setText();
}
public DiscardCardYouChooseTargetEffect(FilterCard filter, TargetController targetController) {
this(new StaticValue(1), filter, targetController);
}
public DiscardCardYouChooseTargetEffect(DynamicValue numberCardsToDiscard, FilterCard filter, TargetController targetController) {
super(Outcome.Discard);
this.targetController = targetController;
this.filter = filter;
this.numberCardsToDiscard = numberCardsToDiscard;
this.numberCardsToReveal = null;
this.revealAllCards = true;
staticText = this.setText();
staticText = this.setText();
}
public DiscardCardYouChooseTargetEffect(final DiscardCardYouChooseTargetEffect effect) {
@ -117,12 +117,12 @@ public class DiscardCardYouChooseTargetEffect extends OneShotEffect {
this.targetController = effect.targetController;
this.numberCardsToDiscard = effect.numberCardsToDiscard;
this.numberCardsToReveal = effect.numberCardsToReveal;
this.revealAllCards = effect.revealAllCards;
this.revealAllCards = effect.revealAllCards;
}
@Override
public boolean apply(Game game, Ability source) {
Player player = game.getPlayer(source.getFirstTarget());
Player player = game.getPlayer(targetPointer.getFirst(game, source));
Player controller = game.getPlayer(source.getControllerId());
Card sourceCard = game.getCard(source.getSourceId());
if (player != null && controller != null) {
@ -131,7 +131,7 @@ public class DiscardCardYouChooseTargetEffect extends OneShotEffect {
}
int numberToReveal = this.numberCardsToReveal.calculate(game, source, this);
if (numberToReveal > 0) {
Cards revealedCards = new CardsImpl(Zone.HAND);
Cards revealedCards = new CardsImpl(Zone.HAND);
numberToReveal = Math.min(player.getHand().size(), numberToReveal);
if (player.getHand().size() > numberToReveal) {
TargetCardInHand chosenCards = new TargetCardInHand(numberToReveal, numberToReveal, new FilterCard("card in "+ player.getLogName() +"'s hand"));
@ -149,10 +149,10 @@ public class DiscardCardYouChooseTargetEffect extends OneShotEffect {
}
} else {
revealedCards.addAll(player.getHand());
}
}
player.revealCards(sourceCard != null ? sourceCard.getName() :"Discard", revealedCards, game);
boolean result = true;
int filteredCardsCount = revealedCards.count(filter, source.getSourceId(), source.getControllerId(), game);
int numberToDiscard = Math.min(this.numberCardsToDiscard.calculate(game, source, this), filteredCardsCount);
@ -220,7 +220,7 @@ public class DiscardCardYouChooseTargetEffect extends OneShotEffect {
} else {
sb.append(" of them.");
}
sb.append(" That player discards ").append(discardMultipleCards ? "those cards" : "that card").toString();
return sb.toString();
}

View file

@ -1,41 +0,0 @@
/*
* To change this license header, choose License Headers in Project Properties.
* To change this template file, choose Tools | Templates
* and open the template in the editor.
*/
package mage.abilities.effects.common.replacement;
import mage.abilities.Ability;
import mage.abilities.costs.mana.ManaCosts;
import mage.abilities.effects.PayCostToAttackBlockEffectImpl;
import mage.abilities.effects.PayCostToAttackBlockEffectImpl.RestrictType;
import mage.constants.Duration;
import mage.constants.Outcome;
import mage.game.Game;
import mage.game.events.GameEvent;
/**
*
* @author LevelX2
*/
public class CantAttackYouUnlessPayManaAllEffect extends PayCostToAttackBlockEffectImpl {
public CantAttackYouUnlessPayManaAllEffect(ManaCosts manaCosts) {
super(Duration.WhileOnBattlefield, Outcome.Detriment, RestrictType.ATTACK, manaCosts);
staticText = "Creatures can't attack you unless their controller pays " + manaCosts.getText() + " for each creature he or she controls that's attacking you";
}
CantAttackYouUnlessPayManaAllEffect(CantAttackYouUnlessPayManaAllEffect effect) {
super(effect);
}
@Override
public boolean applies(GameEvent event, Ability source, Game game) {
return source.getControllerId().equals(event.getTargetId());
}
@Override
public CantAttackYouUnlessPayManaAllEffect copy() {
return new CantAttackYouUnlessPayManaAllEffect(this);
}
}

View file

@ -31,6 +31,7 @@ import java.util.Iterator;
import java.util.LinkedList;
import java.util.List;
import mage.abilities.Ability;
import mage.abilities.DelayedTriggeredAbility;
import mage.abilities.SpellAbility;
import mage.abilities.StaticAbility;
import mage.abilities.common.EntersBattlefieldAbility;
@ -44,7 +45,7 @@ import mage.abilities.costs.Costs;
import mage.abilities.costs.CostsImpl;
import mage.abilities.costs.mana.ManaCostsImpl;
import mage.abilities.effects.Effect;
import mage.abilities.effects.common.CreateDelayedTriggeredAbilityEffect;
import mage.abilities.effects.OneShotEffect;
import mage.abilities.effects.common.ReturnToHandTargetEffect;
import mage.abilities.effects.common.continuous.GainAbilitySourceEffect;
import mage.cards.Card;
@ -76,12 +77,9 @@ public class DashAbility extends StaticAbility implements AlternativeSourceCosts
Ability ability = new EntersBattlefieldAbility(
new GainAbilitySourceEffect(HasteAbility.getInstance(), Duration.Custom, false),
DashedCondition.getInstance(), false, "", "");
Effect effect = new ReturnToHandTargetEffect();
effect.setText("return the dashed creature from the battlefield to its owner's hand");
effect.setTargetPointer(new FixedTarget(card.getId()));
ability.addEffect(new CreateDelayedTriggeredAbilityEffect(new AtTheBeginOfNextEndStepDelayedTriggeredAbility(effect), false));
ability.addEffect(new DashAddDelayedTriggeredAbilityEffect());
addSubAbility(ability);
}
public DashAbility(final DashAbility ability) {
@ -134,7 +132,7 @@ public class DashAbility extends StaticAbility implements AlternativeSourceCosts
this.resetDash();
for (AlternativeCost2 dashCost : alternativeSourceCosts) {
if (dashCost.canPay(ability, sourceId, controllerId, game)
&& player.chooseUse(Outcome.Benefit, new StringBuilder(KEYWORD).append(" the creature for ").append(dashCost.getText(true)).append(" ?").toString(), ability, game)) {
&& player.chooseUse(Outcome.Benefit, KEYWORD + " the creature for " + dashCost.getText(true) + " ?", ability, game)) {
activateDash(dashCost, game);
ability.getManaCostsToPay().clear();
ability.getCosts().clear();
@ -209,3 +207,35 @@ public class DashAbility extends StaticAbility implements AlternativeSourceCosts
return alterCosts;
}
}
class DashAddDelayedTriggeredAbilityEffect extends OneShotEffect {
public DashAddDelayedTriggeredAbilityEffect() {
super(Outcome.Benefit);
this.staticText = "return the dashed creature from the battlefield to its owner's hand";
}
public DashAddDelayedTriggeredAbilityEffect(final DashAddDelayedTriggeredAbilityEffect effect) {
super(effect);
}
@Override
public DashAddDelayedTriggeredAbilityEffect copy() {
return new DashAddDelayedTriggeredAbilityEffect(this);
}
@Override
public boolean apply(Game game, Ability source) {
Effect effect = new ReturnToHandTargetEffect();
effect.setText("return the dashed creature from the battlefield to its owner's hand");
effect.setTargetPointer(new FixedTarget(source.getSourceId()));
// init target pointer now because the dashed creature will only be returned from current zone
effect.getTargetPointer().init(game, source);
DelayedTriggeredAbility delayedAbility = new AtTheBeginOfNextEndStepDelayedTriggeredAbility(effect);
delayedAbility.setSourceId(source.getSourceId());
delayedAbility.setControllerId(source.getControllerId());
delayedAbility.setSourceObject(source.getSourceObject(game), game);
game.addDelayedTriggeredAbility(delayedAbility);
return false;
}
}

View file

@ -1,40 +1,38 @@
/*
* 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.
*/
* 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 mage.constants.Duration;
import mage.constants.Zone;
import mage.abilities.TriggeredAbilityImpl;
import mage.abilities.effects.common.continuous.BoostTargetEffect;
import mage.constants.Duration;
import mage.constants.Zone;
import mage.game.Game;
import mage.game.events.GameEvent;
import mage.game.events.GameEvent.EventType;
import mage.target.targetpointer.FixedTarget;
/**
@ -63,7 +61,7 @@ public class ExaltedAbility extends TriggeredAbilityImpl {
@Override
public boolean checkTrigger(GameEvent event, Game game) {
if (game.getActivePlayerId().equals(this.controllerId) ) {
if (game.getActivePlayerId().equals(this.controllerId)) {
if (game.getCombat().attacksAlone()) {
this.getEffects().get(0).setTargetPointer(new FixedTarget(game.getCombat().getAttackers().get(0)));
return true;
@ -74,7 +72,7 @@ public class ExaltedAbility extends TriggeredAbilityImpl {
@Override
public String getRule() {
return "Exalted";
return "Exalted <i>(Whenever a creature you control attacks alone, that creature gets +1/+1 until end of turn.)</i>";
}
}

View file

@ -139,8 +139,6 @@ public class KickerAbility extends StaticAbility implements OptionalAdditionalSo
}
public void resetKicker(Game game, Ability source) {
String key = getActivationKey(source, "", game);
activations.remove(key);
for (OptionalAdditionalCost cost : kickerCosts) {
cost.reset();
}
@ -180,8 +178,11 @@ public class KickerAbility extends StaticAbility implements OptionalAdditionalSo
}
private String getActivationKey(Ability source, String costText, Game game) {
int zcc = source.getSourceObjectZoneChangeCounter();
if (source.getSourceObjectZoneChangeCounter() == 0) {
int zcc = 0;
if (source.getAbilityType().equals(AbilityType.TRIGGERED)) {
zcc = source.getSourceObjectZoneChangeCounter();
}
if (zcc == 0) {
zcc = game.getState().getZoneChangeCounter(source.getSourceId());
}
if (zcc > 0 && (source.getAbilityType().equals(AbilityType.TRIGGERED) || source.getAbilityType().equals(AbilityType.STATIC))) {

View file

@ -1,38 +1,37 @@
/*
* To change this license header, choose License Headers in Project Properties.
* To change this template file, choose Tools | Templates
* and open the template in the editor.
*/
package mage.abilities.keyword;
import mage.abilities.Ability;
import mage.abilities.StaticAbility;
import mage.abilities.effects.common.combat.CantBeBlockedByMoreThanOneSourceEffect;
import mage.abilities.effects.common.combat.CantBeBlockedByOneEffect;
import mage.constants.Zone;
/**
*
* @author LevelX2
*/
public class MenaceAbility extends StaticAbility {
public MenaceAbility() {
super(Zone.BATTLEFIELD, new CantBeBlockedByOneEffect(2));
}
public MenaceAbility(MenaceAbility ability) {
super(ability);
}
@Override
public Ability copy() {
return new MenaceAbility(this);
}
@Override
public String getRule() {
return "Menace <i>(This creature can't be blocked except by two or more creatures.)</i>";
}
}
/*
* To change this license header, choose License Headers in Project Properties.
* To change this template file, choose Tools | Templates
* and open the template in the editor.
*/
package mage.abilities.keyword;
import mage.abilities.Ability;
import mage.abilities.StaticAbility;
import mage.abilities.effects.common.combat.CantBeBlockedByOneEffect;
import mage.constants.Zone;
/**
*
* @author LevelX2
*/
public class MenaceAbility extends StaticAbility {
public MenaceAbility() {
super(Zone.BATTLEFIELD, new CantBeBlockedByOneEffect(2));
}
public MenaceAbility(final MenaceAbility ability) {
super(ability);
}
@Override
public Ability copy() {
return new MenaceAbility(this);
}
@Override
public String getRule() {
return "Menace <i>(This creature can't be blocked except by two or more creatures.)</i>";
}
}

View file

@ -94,7 +94,7 @@ import mage.watchers.common.MiracleWatcher;
public class MiracleAbility extends TriggeredAbilityImpl {
private static final String staticRule = " <i>(You may cast this card for its miracle cost when you draw it if it's the first card you drew this turn.)<i/>";
private String ruleText;
@SuppressWarnings("unchecked")
public MiracleAbility(Card card, ManaCosts miracleCosts) {
super(Zone.HAND, new MiracleEffect((ManaCosts<ManaCost>)miracleCosts), true);
@ -141,7 +141,7 @@ class MiracleEffect extends OneShotEffect {
public MiracleEffect(ManaCosts<ManaCost> miracleCosts) {
super(Outcome.Benefit);
this.staticText = "cast this card for it's miracle cost";
this.staticText = "cast this card for its miracle cost";
this.miracleCosts = miracleCosts;
}

View file

@ -22,14 +22,12 @@ import mage.util.CardUtil;
*
* @author LevelX2
*/
public class RenownAbility extends TriggeredAbilityImpl {
private int renownValue;
public RenownAbility(int renownValue) {
super(Zone.BATTLEFIELD, new BecomeRenownSourceEffect(renownValue), false);
super(Zone.BATTLEFIELD, new BecomesRenownedSourceEffect(renownValue), false);
this.renownValue = renownValue;
}
@ -50,7 +48,8 @@ public class RenownAbility extends TriggeredAbilityImpl {
@Override
public boolean checkInterveningIfClause(Game game) {
return getSourceObject(game) != null && !((Permanent)getSourceObject(game)).isRenown();
Permanent sourcePermanent = game.getPermanentOrLKIBattlefield(getSourceId());
return sourcePermanent != null && !sourcePermanent.isRenowned();
}
@Override
@ -59,45 +58,40 @@ public class RenownAbility extends TriggeredAbilityImpl {
&& ((DamagedPlayerEvent) event).isCombatDamage();
}
@Override
public String getRule() {
return "Whenever {this} deals combat damage to a player, " + super.getRule();
}
public int getRenownValue() {
return renownValue;
}
}
class BecomeRenownSourceEffect extends OneShotEffect {
class BecomesRenownedSourceEffect extends OneShotEffect {
public BecomeRenownSourceEffect(int renownValue) {
public BecomesRenownedSourceEffect(int renownValue) {
super(Outcome.BoostCreature);
this.staticText = setText(renownValue);
}
public BecomeRenownSourceEffect(final BecomeRenownSourceEffect effect) {
public BecomesRenownedSourceEffect(final BecomesRenownedSourceEffect effect) {
super(effect);
}
@Override
public BecomeRenownSourceEffect copy() {
return new BecomeRenownSourceEffect(this);
public BecomesRenownedSourceEffect copy() {
return new BecomesRenownedSourceEffect(this);
}
@Override
public boolean apply(Game game, Ability source) {
Permanent permanent = game.getPermanent(source.getSourceId());
if (permanent != null && source instanceof RenownAbility) {
game.informPlayers(permanent.getLogName() + " is now renown");
game.informPlayers(permanent.getLogName() + " is now renowned");
int renownValue = ((RenownAbility) source).getRenownValue();
// handle renown = X
if (renownValue == Integer.MAX_VALUE) {
renownValue = source.getManaCostsToPay().getX();
}
new AddCountersSourceEffect(CounterType.P1P1.createInstance(renownValue),true).apply(game, source);
permanent.setRenown(true);
game.fireEvent(GameEvent.getEvent(GameEvent.EventType.BECOMES_RENOWN, source.getSourceId(), source.getSourceId(), source.getControllerId(), renownValue));
new AddCountersSourceEffect(CounterType.P1P1.createInstance(renownValue), true).apply(game, source);
permanent.setRenowned(true);
game.fireEvent(GameEvent.getEvent(GameEvent.EventType.BECOMES_RENOWNED, source.getSourceId(), source.getSourceId(), source.getControllerId(), renownValue));
return true;
}
return false;
@ -106,9 +100,9 @@ class BecomeRenownSourceEffect extends OneShotEffect {
private String setText(int renownValue) {
// Renown 1 (When this creature deals combat damage to a player, if it isn't renowned, put a +1/+1 counter on it and it becomes renowned.)
StringBuilder sb = new StringBuilder("Renown ");
sb.append(renownValue == Integer.MAX_VALUE ? "X":renownValue)
sb.append(renownValue == Integer.MAX_VALUE ? "X" : renownValue)
.append(". <i>(When this creature deals combat damage to a player, if it isn't renowned, put ")
.append(renownValue == Integer.MAX_VALUE ? "X":CardUtil.numberToText(renownValue, "a"))
.append(renownValue == Integer.MAX_VALUE ? "X" : CardUtil.numberToText(renownValue, "a"))
.append(" +1/+1 counter on it and it becomes renowned.)</i>").toString();
return sb.toString();
}

View file

@ -292,7 +292,9 @@ public abstract class CardImpl extends MageObjectImpl implements Card {
if (spellAbility == null) {
for (Ability ability : abilities.getActivatedAbilities(Zone.HAND)) {
// name check prevents that alternate casting methods (like "cast [card name] using bestow") are returned here
if (ability instanceof SpellAbility && ability.toString().endsWith(getName())) {
// BUG #1024: Bestow bug
//if (ability instanceof SpellAbility && ability.toString().endsWith(getName())) {
if (ability instanceof SpellAbility) {
spellAbility = (SpellAbility) ability;
}
}

View file

@ -58,9 +58,9 @@ public enum CardRepository {
private static final String JDBC_URL = "jdbc:h2:file:./db/cards.h2;AUTO_SERVER=TRUE";
private static final String VERSION_ENTITY_NAME = "card";
// raise this if db structure was changed
private static final long CARD_DB_VERSION = 39;
private static final long CARD_DB_VERSION = 41;
// raise this if new cards were added to the server
private static final long CARD_CONTENT_VERSION = 20;
private static final long CARD_CONTENT_VERSION = 26;
private final Random random = new Random();
private Dao<CardInfo, Object> cardDao;
@ -135,7 +135,7 @@ public enum CardRepository {
int result = card.getName().indexOf(" // ");
if (result > 0) {
names.add(card.getName().substring(0, result));
names.add(card.getName().substring(result+4));
names.add(card.getName().substring(result + 4));
} else {
names.add(card.getName());
}
@ -156,7 +156,7 @@ public enum CardRepository {
int result = card.getName().indexOf(" // ");
if (result > 0) {
names.add(card.getName().substring(0, result));
names.add(card.getName().substring(result+4));
names.add(card.getName().substring(result + 4));
} else {
names.add(card.getName());
}
@ -165,7 +165,7 @@ public enum CardRepository {
}
return names;
}
public Set<String> getCreatureNames() {
Set<String> names = new TreeSet<>();
try {
@ -177,7 +177,7 @@ public enum CardRepository {
int result = card.getName().indexOf(" // ");
if (result > 0) {
names.add(card.getName().substring(0, result));
names.add(card.getName().substring(result+4));
names.add(card.getName().substring(result + 4));
} else {
names.add(card.getName());
}
@ -193,7 +193,7 @@ public enum CardRepository {
QueryBuilder<CardInfo, Object> qb = cardDao.queryBuilder();
qb.distinct().selectColumns("name");
Where where = qb.where();
where.and(where.not().like("types", '%' + CardType.CREATURE.name() +'%'),where.not().like("types", '%' + CardType.LAND.name() + '%'));
where.and(where.not().like("types", '%' + CardType.CREATURE.name() + '%'), where.not().like("types", '%' + CardType.LAND.name() + '%'));
List<CardInfo> results = cardDao.query(qb.prepare());
for (CardInfo card : results) {
int result = card.getName().indexOf(" // ");
@ -261,7 +261,6 @@ public enum CardRepository {
return null;
}
public List<String> getClassNames() {
List<String> names = new ArrayList<>();
try {
@ -313,7 +312,7 @@ public enum CardRepository {
try {
QueryBuilder<CardInfo, Object> queryBuilder = cardDao.queryBuilder();
criteria.buildQuery(queryBuilder);
return cardDao.query(queryBuilder.prepare());
} catch (SQLException ex) {
}
@ -333,7 +332,7 @@ public enum CardRepository {
public void setContentVersion(long version) {
try {
ConnectionSource connectionSource = new JdbcConnectionSource(JDBC_URL);
RepositoryUtil.updateVersion(connectionSource, VERSION_ENTITY_NAME + "Content", version);
RepositoryUtil.updateVersion(connectionSource, VERSION_ENTITY_NAME + "Content", version);
} catch (SQLException ex) {
ex.printStackTrace();
}

View file

@ -21,13 +21,13 @@ import org.apache.log4j.Logger;
public enum ExpansionRepository {
instance;
private static final Logger logger = Logger.getLogger(ExpansionRepository.class);
private static final Logger logger = Logger.getLogger(ExpansionRepository.class);
private static final String JDBC_URL = "jdbc:h2:file:./db/cards.h2;AUTO_SERVER=TRUE";
private static final String VERSION_ENTITY_NAME = "expansion";
private static final long EXPANSION_DB_VERSION = 5;
private static final long EXPANSION_CONTENT_VERSION = 8;
private static final long EXPANSION_CONTENT_VERSION = 9;
private Dao<ExpansionInfo, Object> expansionDao;
@ -73,7 +73,7 @@ public enum ExpansionRepository {
}
public ExpansionInfo[] getWithBoostersSortedByReleaseDate() {
ExpansionInfo[] sets = new ExpansionInfo[0];
ExpansionInfo[] sets = new ExpansionInfo[0];
try {
QueryBuilder<ExpansionInfo, Object> qb = expansionDao.queryBuilder();
qb.orderBy("releaseDate", false);
@ -96,9 +96,9 @@ public enum ExpansionRepository {
}
return sets;
}
public List<ExpansionInfo> getSetsFromBlock(String blockName) {
List<ExpansionInfo> sets = new LinkedList<>();
List<ExpansionInfo> sets = new LinkedList<>();
try {
QueryBuilder<ExpansionInfo, Object> qb = expansionDao.queryBuilder();
qb.where().eq("blockName", new SelectArg(blockName));
@ -119,9 +119,9 @@ public enum ExpansionRepository {
}
} catch (SQLException ex) {
}
return set;
return set;
}
public ExpansionInfo getSetByName(String setName) {
ExpansionInfo set = null;
try {
@ -133,7 +133,7 @@ public enum ExpansionRepository {
}
} catch (SQLException ex) {
}
return set;
return set;
}
public List<ExpansionInfo> getAll() {
@ -145,7 +145,7 @@ public enum ExpansionRepository {
}
return new ArrayList<>();
}
public List<String> getAllSetNames() {
try {
QueryBuilder<ExpansionInfo, Object> qb = expansionDao.queryBuilder();
@ -153,14 +153,14 @@ public enum ExpansionRepository {
List<ExpansionInfo> expansions = expansionDao.query(qb.prepare());
List<String> setNames = new LinkedList<>();
for (ExpansionInfo expansionInfo : expansions) {
setNames.add(expansionInfo.getName());
}
setNames.add(expansionInfo.getName());
}
return setNames;
} catch (SQLException ex) {
}
return new ArrayList<>();
}
public long getContentVersionFromDB() {
try {
ConnectionSource connectionSource = new JdbcConnectionSource(JDBC_URL);

View file

@ -1,16 +1,16 @@
/*
* 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
@ -20,12 +20,11 @@
* 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.filter.common;
import mage.constants.CardType;
@ -40,7 +39,11 @@ import mage.filter.predicate.mageobject.SupertypePredicate;
public class FilterBasicLandCard extends FilterCard {
public FilterBasicLandCard() {
super("basic land card");
this("basic land card");
}
public FilterBasicLandCard(String name) {
super(name);
this.add(new CardTypePredicate(CardType.LAND));
this.add(new SupertypePredicate("Basic"));
}

View file

@ -233,7 +233,7 @@ public interface Game extends MageItem, Serializable {
void firePriorityEvent(UUID playerId);
void firePlayManaEvent(UUID playerId, String message);
void firePlayManaEvent(UUID playerId, String message, Map<String, Serializable> options);
void firePlayXManaEvent(UUID playerId, String message);
@ -322,7 +322,6 @@ public interface Game extends MageItem, Serializable {
void endMulligan(UUID playerId);
// void quit(UUID playerId);
void timerTimeout(UUID playerId);
void idleTimeout(UUID playerId);

View file

@ -1155,6 +1155,7 @@ public abstract class GameImpl implements Game, Serializable {
public synchronized void setManaPaymentMode(UUID playerId, boolean autoPayment) {
Player player = state.getPlayer(playerId);
if (player != null) {
player.getUserData().setManaPoolAutomatic(autoPayment);
player.getManaPool().setAutoPayment(autoPayment);
}
}
@ -1163,6 +1164,7 @@ public abstract class GameImpl implements Game, Serializable {
public synchronized void setManaPaymentModeRestricted(UUID playerId, boolean autoPaymentRestricted) {
Player player = state.getPlayer(playerId);
if (player != null) {
player.getUserData().setManaPoolAutomaticRestricted(autoPaymentRestricted);
player.getManaPool().setAutoPaymentRestricted(autoPaymentRestricted);
}
}
@ -1866,11 +1868,11 @@ public abstract class GameImpl implements Game, Serializable {
}
@Override
public void firePlayManaEvent(UUID playerId, String message) {
public void firePlayManaEvent(UUID playerId, String message, Map<String, Serializable> options) {
if (simulation) {
return;
}
playerQueryEventSource.playMana(playerId, message);
playerQueryEventSource.playMana(playerId, message, options);
}
@Override
@ -2122,7 +2124,7 @@ public abstract class GameImpl implements Game, Serializable {
}
// check if it's a creature and must be removed from combat
if (perm.getCardType().contains(CardType.CREATURE) && this.getCombat() != null) {
this.getCombat().removeFromCombat(perm.getId(), this);
perm.removeFromCombat(this, true);
}
it.remove();
}

View file

@ -534,9 +534,10 @@ public class GameState implements Serializable, Copyable<GameState> {
player.reset();
}
battlefield.reset(game);
combat.reset();
combat.reset(game);
this.reset();
effects.apply(game);
combat.checkForRemoveFromCombat(game);
}
// Remove End of Combat effects
@ -757,6 +758,13 @@ public class GameState implements Serializable, Copyable<GameState> {
}
}
/**
* Removes all waiting triggers (needed for turn end effects)
*/
public void clearTriggeredAbilities() {
this.triggered.clear();
}
public void addTriggeredAbility(TriggeredAbility ability) {
this.triggered.add(ability);
}

View file

@ -42,6 +42,7 @@ import mage.abilities.effects.RestrictionEffect;
import mage.abilities.keyword.CanAttackOnlyAloneAbility;
import mage.abilities.keyword.CantAttackAloneAbility;
import mage.abilities.keyword.VigilanceAbility;
import mage.constants.CardType;
import mage.constants.Outcome;
import mage.constants.Zone;
import mage.filter.common.FilterControlledCreaturePermanent;
@ -71,6 +72,7 @@ public class Combat implements Serializable, Copyable<Combat> {
protected List<CombatGroup> groups = new ArrayList<>();
protected Map<UUID, CombatGroup> blockingGroups = new HashMap<>();
// player and plainswalker ids
protected Set<UUID> defenders = new HashSet<>();
// how many creatures attack defending player
protected Map<UUID, Set<UUID>> numberCreaturesDefenderAttackedBy = new HashMap<>();
@ -156,11 +158,26 @@ public class Combat implements Serializable, Copyable<Combat> {
this.useToughnessForDamageFilters.add(filter);
}
public void reset() {
public void reset(Game game) {
this.useToughnessForDamage = false;
this.useToughnessForDamageFilters.clear();
}
public void checkForRemoveFromCombat(Game game) {
for (UUID creatureId : getAttackers()) {
Permanent creature = game.getPermanent(creatureId);
if (!creature.getCardType().contains(CardType.CREATURE)) {
removeFromCombat(creatureId, game, true);
}
}
for (UUID creatureId : getBlockers()) {
Permanent creature = game.getPermanent(creatureId);
if (!creature.getCardType().contains(CardType.CREATURE)) {
removeFromCombat(creatureId, game, true);
}
}
}
public void clear() {
groups.clear();
blockingGroups.clear();
@ -262,7 +279,7 @@ public class Combat implements Serializable, Copyable<Combat> {
}
}
if (mustAttack) {
// check which defenders the forced to attck creature can attack without paying a cost
// check which defenders the forced to attack creature can attack without paying a cost
HashSet<UUID> defendersCostlessAttackable = new HashSet<>();
defendersCostlessAttackable.addAll(defenders);
for (UUID defenderId : defenders) {
@ -500,6 +517,12 @@ public class Combat implements Serializable, Copyable<Combat> {
UUID attackingCreatureId = requirementEntry.getKey().mustBlockAttacker(ability, game);
Player defender = game.getPlayer(possibleBlocker.getControllerId());
if (attackingCreatureId != null && defender != null && possibleBlocker.canBlock(attackingCreatureId, game)) {
// check if the possible blocker has to pay cost to block, if so don't force
if (game.getContinuousEffects().checkIfThereArePayCostToAttackBlockEffects(
GameEvent.getEvent(GameEvent.EventType.DECLARE_BLOCKER, attackingCreatureId, possibleBlocker.getId(), possibleBlocker.getControllerId()), game)) {
// has cost to block to pay so remove this attacker
continue;
}
if (creatureMustBlockAttackers.containsKey(possibleBlocker.getId())) {
creatureMustBlockAttackers.get(possibleBlocker.getId()).add(attackingCreatureId);
} else {
@ -658,7 +681,7 @@ public class Combat implements Serializable, Copyable<Combat> {
}
// check attacking creature mustBeBlockedByAtLeastOne
// check if for attacking creatures with mustBeBlockedByAtLeastOne requirements are fulfilled
for (UUID toBeBlockedCreatureId : mustBeBlockedByAtLeastOne.keySet()) {
for (CombatGroup combatGroup : game.getCombat().getGroups()) {
if (combatGroup.getBlockers().isEmpty() && combatGroup.getAttackers().contains(toBeBlockedCreatureId)) {
@ -668,56 +691,27 @@ public class Combat implements Serializable, Copyable<Combat> {
if (toBeBlockedCreature != null) {
// check if all possible blocker block other creatures they are forced to block
// read through all possible blockers
boolean possibleBlockerAvailable = false;
for (UUID possibleBlockerId : mustBeBlockedByAtLeastOne.get(toBeBlockedCreatureId)) {
Set<UUID> forcingAttackers = creatureMustBlockAttackers.get(possibleBlockerId);
if (forcingAttackers == null) {
// no other creature forces the blocker to block -> it's available
possibleBlockerAvailable = true;
break;
String blockRequiredMessage = isCreatureDoingARequiredBlock(possibleBlockerId, mustBeBlockedByAtLeastOne, game);
if (blockRequiredMessage != null) { // message means not required
game.informPlayer(controller, blockRequiredMessage + "It's a requirement to block " + toBeBlockedCreature.getIdName());
return false;
}
// get the attackers he blocks
List<UUID> blockedAttackers = null;
for (CombatGroup combatGroupToCheck : game.getCombat().getGroups()) {
if (combatGroupToCheck.getBlockers().contains(possibleBlockerId)) {
blockedAttackers = combatGroupToCheck.getAttackers();
break;
}
}
if (blockedAttackers == null) {
// he blocks no other creature -> it's available
possibleBlockerAvailable = true;
break;
}
// get attackers forcing the possible blocker to block
possibleBlockerAvailable = true;
for (UUID blockedAttackerId : blockedAttackers) {
if (creatureMustBlockAttackers.get(possibleBlockerId).contains(blockedAttackerId)) {
possibleBlockerAvailable = false;
break;
}
}
if (possibleBlockerAvailable) {
break;
}
}
if (possibleBlockerAvailable) {
if (!game.isSimulation()) {
game.informPlayer(controller, new StringBuilder(toBeBlockedCreature.getLogName()).append(" has to be blocked by at least one creature.").toString());
}
return false;
}
}
} else {
// take the first potential blocker from the set to block for the AI
UUID blockingCreatureId = mustBeBlockedByAtLeastOne.get(toBeBlockedCreatureId).iterator().next();
Permanent blockingCreature = game.getPermanent(blockingCreatureId);
if (blockingCreature != null) {
Player defender = game.getPlayer(blockingCreature.getControllerId());
if (defender != null) {
defender.declareBlocker(defender.getId(), blockingCreatureId, toBeBlockedCreatureId, game);
for (UUID possibleBlockerId : mustBeBlockedByAtLeastOne.get(toBeBlockedCreatureId)) {
String blockRequiredMessage = isCreatureDoingARequiredBlock(possibleBlockerId, mustBeBlockedByAtLeastOne, game);
if (blockRequiredMessage != null) {
// set the block
Permanent possibleBlocker = game.getPermanent(possibleBlockerId);
Player defender = game.getPlayer(possibleBlocker.getControllerId());
if (defender != null) {
defender.declareBlocker(defender.getId(), possibleBlockerId, toBeBlockedCreatureId, game);
}
break;
}
}
}
@ -733,6 +727,21 @@ public class Combat implements Serializable, Copyable<Combat> {
if (creatureForcedToBlock == null) {
break;
}
// // check if creature has to pay a cost to block so it's not mandatory to block
// boolean removedAttacker = false;
// for (Iterator<UUID> iterator = entry.getValue().iterator(); iterator.hasNext();) {
// UUID possibleAttackerId = iterator.next();
// if (game.getContinuousEffects().checkIfThereArePayCostToAttackBlockEffects(
// GameEvent.getEvent(GameEvent.EventType.DECLARE_BLOCKER, possibleAttackerId, creatureForcedToBlock.getId(), creatureForcedToBlock.getControllerId()), game)) {
// // has cost to block to pay so remove this attacker
// iterator.remove();
// removedAttacker = true;
// }
// }
// if (removedAttacker && entry.getValue().isEmpty()) {
// continue;
// }
// creature does not block -> not allowed
if (creatureForcedToBlock.getBlocking() == 0) {
blockIsValid = false;
@ -764,7 +773,7 @@ public class Combat implements Serializable, Copyable<Combat> {
}
if (!blockIsValid) {
sb.append(" ").append(creatureForcedToBlock.getLogName());
sb.append(" ").append(creatureForcedToBlock.getIdName());
}
}
if (sb.length() > 0) {
@ -778,6 +787,48 @@ public class Combat implements Serializable, Copyable<Combat> {
return true;
}
/**
* Checks if a possible creature for a block is already doing another
* required block
*
* @param possibleBlockerId
* @param mustBeBlockedByAtLeastOne
* @param game
* @return null block is required otherwise message with reason why not
*/
protected String isCreatureDoingARequiredBlock(UUID possibleBlockerId, Map<UUID, Set<UUID>> mustBeBlockedByAtLeastOne, Game game) {
Permanent possibleBlocker = game.getPermanent(possibleBlockerId);
if (possibleBlocker != null) {
if (possibleBlocker.getBlocking() == 0) {
return possibleBlocker.getIdName() + " does not block, but could block creatures with requirement to be blocked.";
}
Set<UUID> forcingAttackers = creatureMustBlockAttackers.get(possibleBlockerId);
if (forcingAttackers == null) {
// no other creature forces the blocker to block -> it's available
// check now, if it already blocks a creature that mustBeBlockedByAtLeastOne
if (possibleBlocker.getBlocking() > 0) {
CombatGroup combatGroupOfPossibleBlocker = findGroupOfBlocker(possibleBlockerId);
for (UUID blockedAttackerId : combatGroupOfPossibleBlocker.getAttackers()) {
if (mustBeBlockedByAtLeastOne.containsKey(blockedAttackerId)) {
// blocks a creature that has to be blocked by at least one
if (combatGroupOfPossibleBlocker.getBlockers().size() == 1) {
// the creature blocks alone already a creature that has to be blocked by at least one,
// so this is ok
return null;
}
// TODO: Check if the attacker is already blocked by another creature
// and despite there is need that this attacker blocks this attacker also
// I don't know why
Permanent blockedAttacker = game.getPermanent(blockedAttackerId);
return possibleBlocker.getIdName() + " blocks with other creatures " + blockedAttacker.getIdName() + ", which has to be blocked by only one creature. ";
}
}
}
}
}
return null;
}
/**
* Checks the canBeBlockedCheckAfter RestrictionEffect Is the block still
* valid after all block decisions are done
@ -958,7 +1009,7 @@ public class Combat implements Serializable, Copyable<Combat> {
}
}
public boolean removeFromCombat(UUID creatureId, Game game) {
public boolean removeFromCombat(UUID creatureId, Game game, boolean withInfo) {
boolean result = false;
Permanent creature = game.getPermanent(creatureId);
if (creature != null) {
@ -968,6 +1019,9 @@ public class Combat implements Serializable, Copyable<Combat> {
for (CombatGroup group : groups) {
result |= group.remove(creatureId);
}
if (result && withInfo) {
game.informPlayers(creature.getLogName() + " removed from combat");
}
}
return result;
}
@ -1015,6 +1069,15 @@ public class Combat implements Serializable, Copyable<Combat> {
return null;
}
public CombatGroup findGroupOfBlocker(UUID blockerId) {
for (CombatGroup group : groups) {
if (group.getBlockers().contains(blockerId)) {
return group;
}
}
return null;
}
// public int totalUnblockedDamage(Game game) {
// int total = 0;
// for (CombatGroup group : groups) {

View file

@ -1,31 +1,30 @@
/*
* 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.
*/
* 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.game.events;
import java.io.Serializable;
@ -52,9 +51,9 @@ public class GameEvent implements Serializable {
public enum EventType {
//Game events
//Game events
BEGINNING,
PREVENT_DAMAGE, PREVENTED_DAMAGE,
//Turn-based events
PLAY_TURN, EXTRA_TURN,
CHANGE_PHASE, PHASE_CHANGED,
@ -78,15 +77,14 @@ public class GameEvent implements Serializable {
CLEANUP_STEP_PRE, CLEANUP_STEP, CLEANUP_STEP_POST,
EMPTY_MANA_POOL,
AT_END_OF_TURN,
//player events
/* ZONE_CHANGE
targetId id of the zone changing object
sourceId sourceId of the ability with the object moving effect
playerId controller of the moved object
amount not used for this event
flag not used for this event
*/
targetId id of the zone changing object
sourceId sourceId of the ability with the object moving effect
playerId controller of the moved object
amount not used for this event
flag not used for this event
*/
ZONE_CHANGE,
ZONE_CHANGE_GROUP,
EMPTY_DRAW,
@ -96,77 +94,69 @@ public class GameEvent implements Serializable {
DISCARDED_CARD,
CYCLE_CARD, CYCLED_CARD,
CLASH, CLASHED,
DAMAGE_PLAYER,
DAMAGE_PLAYER,
/* DAMAGED_PLAYER
targetId the id of the damged player
sourceId sourceId of the ability which caused the damage
playerId the id of the damged player
amount amount of damage
flag true = comabat damage - other damage = false
*/
targetId the id of the damged player
sourceId sourceId of the ability which caused the damage
playerId the id of the damged player
amount amount of damage
flag true = comabat damage - other damage = false
*/
DAMAGED_PLAYER,
DAMAGE_CAUSES_LIFE_LOSS,
PLAYER_LIFE_CHANGE,
GAIN_LIFE, GAINED_LIFE,
LOSE_LIFE, LOST_LIFE,
PLAY_LAND, LAND_PLAYED,
CAST_SPELL,
CAST_SPELL,
/* SPELL_CAST
targetId id of the spell that's cast
sourceId sourceId of the spell that's cast
playerId player that casts the spell
amount not used for this event
flag not used for this event
zone zone the spell is cast from
*/
targetId id of the spell that's cast
sourceId sourceId of the spell that's cast
playerId player that casts the spell
amount not used for this event
flag not used for this event
zone zone the spell is cast from
*/
SPELL_CAST,
ACTIVATE_ABILITY, ACTIVATED_ABILITY,
ADD_MANA, MANA_ADDED,
ADD_MANA, MANA_ADDED,
/* MANA_PAYED
targetId id if the ability the mana was paid for (not the sourceId)
sourceId sourceId of the mana source
playerId controller of the ability the mana was paid for
amount not used for this event
flag indicates a special condition
*/
MANA_PAYED,
targetId id if the ability the mana was paid for (not the sourceId)
sourceId sourceId of the mana source
playerId controller of the ability the mana was paid for
amount not used for this event
flag indicates a special condition
data originalId of the mana producing ability
*/
MANA_PAYED,
LOSES, LOST, WINS,
TARGET, TARGETED,
/* COUNTER
targetId id of the spell or id of stack ability
sourceId sourceId of the ability countering the spell or stack ability
playerId controller of the countered spell or stack ability
amount not used for this event
flag not used for this event
*/
targetId id of the spell or id of stack ability
sourceId sourceId of the ability countering the spell or stack ability
playerId controller of the countered spell or stack ability
amount not used for this event
flag not used for this event
*/
COUNTER,
COUNTERED,
DECLARING_ATTACKERS, DECLARED_ATTACKERS,
DECLARE_ATTACKER,
/* ATTACKER_DECLARED
targetId id of the defending player or planeswalker attacked
sourceId id of the attacking creature
playerId player defining the attacking creatures
amount not used for this event
flag not used for this event
*/
targetId id of the defending player or planeswalker attacked
sourceId id of the attacking creature
playerId player defining the attacking creatures
amount not used for this event
flag not used for this event
*/
ATTACKER_DECLARED,
/* DECLARING_BLOCKERS
targetId attackerId
sourceId not used for this event
playerId attackerId
amount not used for this event
flag not used for this event
*/
targetId attackerId
sourceId not used for this event
playerId attackerId
amount not used for this event
flag not used for this event
*/
DECLARING_BLOCKERS,
DECLARED_BLOCKERS,
DECLARE_BLOCKER, BLOCKER_DECLARED,
@ -176,7 +166,6 @@ public class GameEvent implements Serializable {
ENCHANT_PLAYER, ENCHANTED_PLAYER,
CAN_TAKE_MULLIGAN,
FLIP_COIN, COIN_FLIPPED, SCRY, FATESEAL,
//permanent events
ENTERS_THE_BATTLEFIELD,
TAP, TAPPED, TAPPED_FOR_MANA,
@ -185,24 +174,22 @@ public class GameEvent implements Serializable {
UNFLIP, UNFLIPPED,
TRANSFORM, TRANSFORMED,
BECOMES_MONSTROUS,
BECOMES_RENOWN,
BECOMES_RENOWNED,
PHASE_OUT, PHASED_OUT,
PHASE_IN, PHASED_IN,
TURNFACEUP, TURNEDFACEUP,
TURNFACEDOWN, TURNEDFACEDOWN,
DAMAGE_CREATURE, DAMAGED_CREATURE,
DAMAGE_PLANESWALKER, DAMAGED_PLANESWALKER,
DESTROY_PERMANENT,
DESTROY_PERMANENT,
/* DESTROYED_PERMANENT
targetId id of the destroyed creature
sourceId sourceId of the ability with the destroy effect
playerId controller of the creature
amount not used for this event
flag true if no regeneration is allowed
*/
targetId id of the destroyed creature
sourceId sourceId of the ability with the destroy effect
playerId controller of the creature
amount not used for this event
flag true if no regeneration is allowed
*/
DESTROYED_PERMANENT,
SACRIFICE_PERMANENT, SACRIFICED_PERMANENT,
FIGHTED_PERMANENT,
EXPLOITED_CREATURE,
@ -211,31 +198,27 @@ public class GameEvent implements Serializable {
ADD_COUNTER, COUNTER_ADDED,
ADD_COUNTERS, COUNTERS_ADDED,
COUNTER_REMOVED,
LOSE_CONTROL,
LOSE_CONTROL,
/* LOST_CONTROL
targetId id of the creature that lost control
sourceId id of the creature that lost control
playerId player that controlles the creature before
amount not used for this event
flag not used for this event
*/
targetId id of the creature that lost control
sourceId id of the creature that lost control
playerId player that controlles the creature before
amount not used for this event
flag not used for this event
*/
LOST_CONTROL,
GAIN_CONTROL, GAINED_CONTROL,
CREATE_TOKEN,
/* REGENERATE
targetId id of the creature to regenerate
sourceId sourceId of the effect doing the regeneration
playerId controller of the creature
amount not used for this event
flag not used for this event
*/
targetId id of the creature to regenerate
sourceId sourceId of the effect doing the regeneration
playerId controller of the creature
amount not used for this event
flag not used for this event
*/
REGENERATE,
REGENERATED,
CHANGE_COLOR, COLOR_CHANGED,
//combat events
COMBAT_DAMAGE_APPLIED,
SELECTED_ATTACKER, SELECTED_BLOCKER;
@ -268,7 +251,7 @@ public class GameEvent implements Serializable {
}
public static GameEvent getEvent(EventType type, UUID targetId, UUID playerId, String data, int amount) {
GameEvent event = getEvent(type, targetId,playerId);
GameEvent event = getEvent(type, targetId, playerId);
event.setAmount(amount);
event.setData(data);
return event;
@ -310,7 +293,6 @@ public class GameEvent implements Serializable {
this.flag = flag;
}
public String getData() {
return data;
}
@ -326,17 +308,18 @@ public class GameEvent implements Serializable {
public void setZone(Zone zone) {
this.zone = zone;
}
/**
* used to store which replacement effects were already applied to an event
* or or any modified events that may replace it
*
* 614.5. A replacement effect doesn't invoke itself repeatedly; it gets only one
* opportunity to affect an event or any modified events that may replace it.
* Example: A player controls two permanents, each with an ability that reads
* "If a creature you control would deal damage to a creature or player, it
* deals double that damage to that creature or player instead." A creature
* that normally deals 2 damage will deal 8 damage--not just 4, and not an
* infinite amount.
* 614.5. A replacement effect doesn't invoke itself repeatedly; it gets
* only one opportunity to affect an event or any modified events that may
* replace it. Example: A player controls two permanents, each with an
* ability that reads "If a creature you control would deal damage to a
* creature or player, it deals double that damage to that creature or
* player instead." A creature that normally deals 2 damage will deal 8
* damage--not just 4, and not an infinite amount.
*
* @return
*/

View file

@ -1,43 +1,46 @@
/*
* 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.
*/
* 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.game.events;
import java.io.Serializable;
import java.util.EventObject;
import java.util.HashSet;
import java.util.List;
import java.util.Map;
import java.util.Set;
import java.util.UUID;
import mage.abilities.Ability;
import mage.abilities.ActivatedAbility;
import mage.abilities.TriggeredAbility;
import mage.cards.Card;
import mage.cards.Cards;
import mage.game.permanent.Permanent;
import java.io.Serializable;
import java.util.*;
import mage.choices.Choice;
import mage.game.permanent.Permanent;
/**
*
@ -46,6 +49,7 @@ import mage.choices.Choice;
public class PlayerQueryEvent extends EventObject implements ExternalEvent, Serializable {
public enum QueryType {
ASK, CHOOSE_CHOICE, CHOOSE_ABILITY, CHOOSE_MODE, PICK_TARGET, PICK_ABILITY, SELECT, PLAY_MANA, PLAY_X_MANA, AMOUNT, PICK_CARD, CONSTRUCT, CHOOSE_PILE, PERSONAL_MESSAGE
}
@ -67,7 +71,6 @@ public class PlayerQueryEvent extends EventObject implements ExternalEvent, Seri
private List<? extends Card> pile2;
private Choice choice;
private PlayerQueryEvent(UUID playerId, String message, List<? extends Ability> abilities, Set<String> choices, Set<UUID> targets, Cards cards, QueryType queryType, int min, int max, boolean required, Map<String, Serializable> options) {
this(playerId, message, abilities, choices, targets, cards, queryType, min, max, required);
this.options = options;
@ -197,15 +200,15 @@ public class PlayerQueryEvent extends EventObject implements ExternalEvent, Seri
return new PlayerQueryEvent(playerId, message, null, null, null, null, QueryType.SELECT, 0, 0, false, options);
}
public static PlayerQueryEvent playManaEvent(UUID playerId, String message) {
return new PlayerQueryEvent(playerId, message, null, null, null, null, QueryType.PLAY_MANA, 0, 0, false);
public static PlayerQueryEvent playManaEvent(UUID playerId, String message, Map<String, Serializable> options) {
return new PlayerQueryEvent(playerId, message, null, null, null, null, QueryType.PLAY_MANA, 0, 0, false, options);
}
public static PlayerQueryEvent playXManaEvent(UUID playerId, String message) {
return new PlayerQueryEvent(playerId, message, null, null, null, null, QueryType.PLAY_X_MANA, 0, 0, false);
}
public static PlayerQueryEvent amountEvent(UUID playerId, String message, int min , int max) {
public static PlayerQueryEvent amountEvent(UUID playerId, String message, int min, int max) {
return new PlayerQueryEvent(playerId, message, null, null, null, null, QueryType.AMOUNT, min, max, false);
}

View file

@ -1,42 +1,43 @@
/*
* 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.
*/
* 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.game.events;
import java.io.Serializable;
import java.util.List;
import java.util.Map;
import java.util.Set;
import java.util.UUID;
import mage.abilities.ActivatedAbility;
import mage.abilities.TriggeredAbility;
import mage.cards.Card;
import mage.cards.Cards;
import mage.game.permanent.Permanent;
import java.io.Serializable;
import java.util.*;
import mage.choices.Choice;
import mage.game.permanent.Permanent;
/**
*
@ -44,7 +45,8 @@ import mage.choices.Choice;
*/
public class PlayerQueryEventSource implements EventSource<PlayerQueryEvent>, Serializable {
protected final EventDispatcher<PlayerQueryEvent> dispatcher = new EventDispatcher<PlayerQueryEvent>() {};
protected final EventDispatcher<PlayerQueryEvent> dispatcher = new EventDispatcher<PlayerQueryEvent>() {
};
@Override
public void addListener(Listener<PlayerQueryEvent> listener) {
@ -100,8 +102,8 @@ public class PlayerQueryEventSource implements EventSource<PlayerQueryEvent>, Se
dispatcher.fireEvent(PlayerQueryEvent.targetEvent(playerId, message, perms, required));
}
public void playMana(UUID playerId, String message) {
dispatcher.fireEvent(PlayerQueryEvent.playManaEvent(playerId, message));
public void playMana(UUID playerId, String message, Map<String, Serializable> options) {
dispatcher.fireEvent(PlayerQueryEvent.playManaEvent(playerId, message, options));
}
public void amount(UUID playerId, String message, int min, int max) {

View file

@ -75,8 +75,8 @@ public interface Permanent extends Card, Controllable {
boolean isMonstrous();
void setMonstrous(boolean value);
boolean isRenown();
void setRenown(boolean value);
boolean isRenowned();
void setRenowned(boolean value);
void setCardNumber(int cid);
void setExpansionSetCode(String expansionSetCode);

View file

@ -90,7 +90,7 @@ public abstract class PermanentImpl extends CardImpl implements Permanent {
protected boolean flipped;
protected boolean transformed;
protected boolean monstrous;
protected boolean renown;
protected boolean renowned;
protected boolean manifested = false;
protected boolean morphed = false;
protected UUID originalControllerId;
@ -176,6 +176,7 @@ public abstract class PermanentImpl extends CardImpl implements Permanent {
this.maxBlockedBy = permanent.maxBlockedBy;
this.transformed = permanent.transformed;
this.monstrous = permanent.monstrous;
this.renowned = permanent.renowned;
this.pairedCard = permanent.pairedCard;
this.timesLoyaltyUsed = permanent.timesLoyaltyUsed;
@ -1199,11 +1200,9 @@ public abstract class PermanentImpl extends CardImpl implements Permanent {
@Override
public boolean removeFromCombat(Game game, boolean withInfo) {
if (this.isAttacking() || this.blocking > 0) {
if (game.getCombat().removeFromCombat(objectId, game) && withInfo && !game.isSimulation()) {
game.informPlayers(new StringBuilder(this.getLogName()).append(" removed from combat").toString());
}
return game.getCombat().removeFromCombat(objectId, game, withInfo);
}
return true;
return false;
}
@Override
@ -1267,13 +1266,13 @@ public abstract class PermanentImpl extends CardImpl implements Permanent {
}
@Override
public boolean isRenown() {
return this.renown;
public boolean isRenowned() {
return this.renowned;
}
@Override
public void setRenown(boolean value) {
this.renown = value;
public void setRenowned(boolean value) {
this.renowned = value;
}
@Override

View file

@ -1,31 +1,30 @@
/*
* 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.
*/
* 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.game.stack;
import java.util.ArrayList;
@ -101,10 +100,10 @@ public class Spell extends StackObjImpl implements Card {
public Spell(final Spell spell) {
this.id = spell.id;
for (SpellAbility spellAbility: spell.spellAbilities) {
for (SpellAbility spellAbility : spell.spellAbilities) {
this.spellAbilities.add(spellAbility.copy());
}
for (Card spellCard: spell.spellCards) {
for (Card spellCard : spell.spellCards) {
this.spellCards.add(spellCard.copy());
}
if (spell.spellAbilities.get(0).equals(spell.ability)) {
@ -124,7 +123,6 @@ public class Spell extends StackObjImpl implements Card {
this.color = spell.color.copy();
}
public boolean activate(Game game, boolean noMana) {
if (!spellAbilities.get(0).activate(game, noMana)) {
return false;
@ -132,7 +130,7 @@ public class Spell extends StackObjImpl implements Card {
// if there are more abilities (fused split spell) or first ability added new abilities (splice), activate the additional abilities
boolean ignoreAbility = true;
boolean payNoMana = noMana;
for (SpellAbility spellAbility: spellAbilities) {
for (SpellAbility spellAbility : spellAbilities) {
// costs for spliced abilities were added to main spellAbility, so pay no mana for spliced abilities
payNoMana |= spellAbility.getSpellAbilityType().equals(SpellAbilityType.SPLICE);
if (ignoreAbility) {
@ -174,19 +172,19 @@ public class Spell extends StackObjImpl implements Card {
result = false;
boolean legalParts = false;
// check for legal parts
for(SpellAbility spellAbility: this.spellAbilities) {
// if muliple modes are selected, and there are modes with targets, then at least one mode has to have a legal target or
// When resolving a fused split spell with multiple targets, treat it as you would any spell with multiple targets.
// If all targets are illegal when the spell tries to resolve, the spell is countered and none of its effects happen.
for (SpellAbility spellAbility : this.spellAbilities) {
// if muliple modes are selected, and there are modes with targets, then at least one mode has to have a legal target or
// When resolving a fused split spell with multiple targets, treat it as you would any spell with multiple targets.
// If all targets are illegal when the spell tries to resolve, the spell is countered and none of its effects happen.
// If at least one target is still legal at that time, the spell resolves, but an illegal target can't perform any actions
// or have any actions performed on it.
// or have any actions performed on it.
legalParts |= spellAbilityHasLegalParts(spellAbility, game);
}
// resolve if legal parts
if (legalParts) {
for(SpellAbility spellAbility: this.spellAbilities) {
for (SpellAbility spellAbility : this.spellAbilities) {
if (spellAbilityHasLegalParts(spellAbility, game)) {
for (UUID modeId :spellAbility.getModes().getSelectedModes()) {
for (UUID modeId : spellAbility.getModes().getSelectedModes()) {
spellAbility.getModes().setActiveMode(modeId);
if (spellAbility.getTargets().stillLegal(spellAbility, game)) {
if (!spellAbility.getSpellAbilityType().equals(SpellAbilityType.SPLICE)) {
@ -216,14 +214,14 @@ public class Spell extends StackObjImpl implements Card {
if (ability.getTargets().stillLegal(ability, game)) {
updateOptionalCosts(0);
boolean bestow = this.getSpellAbility() instanceof BestowAbility;
if (bestow) {
if (bestow) {
// Must be removed first time, after that will be removed by continous effect
// Otherwise effects like evolve trigger from creature comes into play event
card.getCardType().remove(CardType.CREATURE);
card.getSubtype().add("Aura");
card.getSubtype().add("Aura");
}
if (card.putOntoBattlefield(game, fromZone, ability.getSourceId(), controllerId)) {
if (bestow) {
if (bestow) {
// card will be copied during putOntoBattlefield, so the card of CardPermanent has to be changed
// TODO: Find a better way to prevent bestow creatures from being effected by creature affecting abilities
Permanent permanent = game.getPermanent(card.getId());
@ -234,16 +232,16 @@ public class Spell extends StackObjImpl implements Card {
}
card.getCardType().add(CardType.CREATURE);
card.getSubtype().remove("Aura");
}
}
return ability.resolve(game);
}
if (bestow) {
if (bestow) {
card.getCardType().add(CardType.CREATURE);
}
}
return false;
}
// Aura has no legal target and its a bestow enchantment -> Add it to battlefield as creature
if (this.getSpellAbility() instanceof BestowAbility) {
if (this.getSpellAbility() instanceof BestowAbility) {
updateOptionalCosts(0);
result = card.putOntoBattlefield(game, fromZone, ability.getSourceId(), controllerId);
return result;
@ -263,17 +261,17 @@ public class Spell extends StackObjImpl implements Card {
}
private boolean spellAbilityHasLegalParts(SpellAbility spellAbility, Game game) {
if (spellAbility.getModes().getSelectedModes().size() > 1) {
if (spellAbility.getModes().getSelectedModes().size() > 1) {
boolean targetedMode = false;
boolean legalTargetedMode = false;
for (UUID modeId :spellAbility.getModes().getSelectedModes()) {
for (UUID modeId : spellAbility.getModes().getSelectedModes()) {
spellAbility.getModes().setActiveMode(modeId);
if (spellAbility.getTargets().size() > 0) {
targetedMode = true;
if (spellAbility.getTargets().stillLegal(spellAbility, game)) {
legalTargetedMode = true;
}
}
}
}
}
if (targetedMode) {
return legalTargetedMode;
@ -283,11 +281,11 @@ public class Spell extends StackObjImpl implements Card {
return spellAbility.getTargets().stillLegal(spellAbility, game);
}
}
/**
* As we have ability in the stack, we need to update optional costs in original card.
* This information will be used later by effects, e.g. to determine whether card was kicked or not.
* E.g. Desolation Angel
* As we have ability in the stack, we need to update optional costs in
* original card. This information will be used later by effects, e.g. to
* determine whether card was kicked or not. E.g. Desolation Angel
*/
private void updateOptionalCosts(int index) {
Ability abilityOrig = spellCards.get(index).getAbilities().get(spellAbilities.get(index).getId());
@ -307,9 +305,7 @@ public class Spell extends StackObjImpl implements Card {
}
}
}
@Override
public void counter(UUID sourceId, Game game) {
this.countered = true;
@ -319,10 +315,10 @@ public class Spell extends StackObjImpl implements Card {
Ability counteringAbility = null;
MageObject counteringObject = game.getObject(sourceId);
if (counteringObject instanceof StackObject) {
counteringAbility = ((StackObject)counteringObject).getStackAbility();
counteringAbility = ((StackObject) counteringObject).getStackAbility();
}
player.moveCards(card, Zone.STACK, Zone.GRAVEYARD, counteringAbility, game);
}
}
}
}
@ -340,18 +336,18 @@ public class Spell extends StackObjImpl implements Card {
public String getName() {
return card.getName();
}
@Override
public String getIdName() {
String idName;
if (card != null) {
idName = card.getId().toString().substring(0,3);
idName = card.getId().toString().substring(0, 3);
} else {
idName = getId().toString().substring(0,3);
idName = getId().toString().substring(0, 3);
}
return getName() + " ["+idName+"]";
return getName() + " [" + idName + "]";
}
@Override
public String getLogName() {
return GameLog.getColoredObjectIdName(card);
@ -363,7 +359,8 @@ public class Spell extends StackObjImpl implements Card {
}
@Override
public void setName(String name) {}
public void setName(String name) {
}
@Override
public Rarity getRarity() {
@ -415,7 +412,6 @@ public class Spell extends StackObjImpl implements Card {
return card.getSupertype();
}
public List<SpellAbility> getSpellAbilities() {
return spellAbilities;
}
@ -429,7 +425,7 @@ public class Spell extends StackObjImpl implements Card {
public Abilities<Ability> getAbilities(Game game) {
return card.getAbilities(game);
}
@Override
public boolean hasAbility(UUID abilityId, Game game) {
return card.hasAbility(abilityId, game);
@ -446,9 +442,11 @@ public class Spell extends StackObjImpl implements Card {
}
/**
* 202.3b When calculating the converted mana cost of an object with an {X} in its
* mana cost, X is treated as 0 while the object is not on the stack, and X is
* treated as the number chosen for it while the object is on the stack.
* 202.3b When calculating the converted mana cost of an object with an {X}
* in its mana cost, X is treated as 0 while the object is not on the stack,
* and X is treated as the number chosen for it while the object is on the
* stack.
*
* @return
*/
@Override
@ -457,9 +455,9 @@ public class Spell extends StackObjImpl implements Card {
if (faceDown) {
return 0;
}
for (Ability spellAbility: spellAbilities) {
for (Ability spellAbility : spellAbilities) {
int xMultiplier = 0;
for (String symbolString :spellAbility.getManaCosts().getSymbols()) {
for (String symbolString : spellAbility.getManaCosts().getSymbols()) {
int index = symbolString.indexOf("{X}");
while (index != -1) {
xMultiplier++;
@ -471,14 +469,14 @@ public class Spell extends StackObjImpl implements Card {
cmc += spellAbility.getManaCostsToPay().getX() * xMultiplier;
} else {
cmc += spellAbility.getManaCosts().convertedManaCost() + spellAbility.getManaCostsToPay().getX() * xMultiplier;
}
}
}
if (this.getSpellAbility().getSpellAbilityType().equals(SpellAbilityType.BASE_ALTERNATE)) {
cmc += getCard().getManaCost().convertedManaCost();
}
return cmc;
}
@Override
public MageInt getPower() {
return card.getPower();
@ -503,7 +501,8 @@ public class Spell extends StackObjImpl implements Card {
spellAbilities.add(spellAbility);
}
public void addAbility(Ability ability) {}
public void addAbility(Ability ability) {
}
@Override
public SpellAbility getSpellAbility() {
@ -512,14 +511,15 @@ public class Spell extends StackObjImpl implements Card {
public void setControllerId(UUID controllerId) {
this.ability.setControllerId(controllerId);
for (SpellAbility spellAbility: spellAbilities) {
for (SpellAbility spellAbility : spellAbilities) {
spellAbility.setControllerId(controllerId);
}
this.controllerId = controllerId;
}
@Override
public void setOwnerId(UUID controllerId) {}
public void setOwnerId(UUID controllerId) {
}
@Override
public List<String> getRules() {
@ -608,7 +608,7 @@ public class Spell extends StackObjImpl implements Card {
card.adjustChoices(ability, game);
}
}
@Override
public void adjustCosts(Ability ability, Game game) {
if (card != null) {
@ -623,7 +623,6 @@ public class Spell extends StackObjImpl implements Card {
}
}
@Override
public boolean moveToZone(Zone zone, UUID sourceId, Game game, boolean flag) {
return moveToZone(zone, sourceId, game, flag, null);
@ -635,7 +634,7 @@ public class Spell extends StackObjImpl implements Card {
// If a copy of a card is in any zone other than the stack or the battlefield, it ceases to exist.
// These are state-based actions. See rule 704.
if (this.isCopiedSpell() && !zone.equals(Zone.STACK)) {
return true;
return true;
}
throw new UnsupportedOperationException("Unsupported operation");
}
@ -646,7 +645,7 @@ public class Spell extends StackObjImpl implements Card {
}
@Override
public boolean moveToExile(UUID exileId, String name, UUID sourceId, Game game, ArrayList<UUID> appliedEffects) {
public boolean moveToExile(UUID exileId, String name, UUID sourceId, Game game, ArrayList<UUID> appliedEffects) {
ZoneChangeEvent event = new ZoneChangeEvent(this.getId(), sourceId, this.getOwnerId(), Zone.STACK, Zone.EXILED, appliedEffects);
if (!game.replaceEvent(event)) {
game.getStack().remove(this);
@ -667,8 +666,6 @@ public class Spell extends StackObjImpl implements Card {
return false;
}
@Override
public boolean putOntoBattlefield(Game game, Zone fromZone, UUID sourceId, UUID controllerId) {
throw new UnsupportedOperationException("Unsupported operation");
@ -711,7 +708,7 @@ public class Spell extends StackObjImpl implements Card {
@Override
public void updateZoneChangeCounter(Game game) {
throw new UnsupportedOperationException("Unsupported operation");
throw new UnsupportedOperationException("Unsupported operation");
}
@Override
@ -762,7 +759,8 @@ public class Spell extends StackObjImpl implements Card {
}
@Override
public void build() {}
public void build() {
}
@Override
public Counters getCounters(Game game) {
@ -815,7 +813,7 @@ public class Spell extends StackObjImpl implements Card {
@Override
public void setSpellAbility(SpellAbility ability) {
throw new UnsupportedOperationException("Not supported.");
throw new UnsupportedOperationException("Not supported.");
}
public boolean isCountered() {

View file

@ -1,31 +1,30 @@
/*
* 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.
*/
* 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.game.stack;
import java.util.ArrayDeque;
@ -44,11 +43,12 @@ public class SpellStack extends ArrayDeque<StackObject> {
protected Date dateLastAdded;
public SpellStack () {}
public SpellStack() {
}
public SpellStack(final SpellStack stack) {
for (StackObject spell: stack) {
for (StackObject spell : stack) {
this.addLast(spell.copy());
}
}
@ -67,7 +67,7 @@ public class SpellStack extends ArrayDeque<StackObject> {
}
public void remove(StackObject object) {
for (StackObject spell: this) {
for (StackObject spell : this) {
if (spell.getId().equals(object.getId())) {
super.remove(spell);
return;
@ -77,7 +77,7 @@ public class SpellStack extends ArrayDeque<StackObject> {
public boolean counter(UUID objectId, UUID sourceId, Game game) {
// the counter logic is copied by some spells to handle replacement effects of the countered spell
// so if logic is changed here check those spells for needed changes too
// so if logic is changed here check those spells for needed changes too
// Concerned cards to check: Hinder, Spell Crumple
StackObject stackObject = getStackObject(objectId);
MageObject sourceObject = game.getObject(sourceId);
@ -87,7 +87,7 @@ public class SpellStack extends ArrayDeque<StackObject> {
if (targetSourceObject == null) {
targetSourceName = "[Object not found]";
} else {
targetSourceName = game.getObject(stackObject.getSourceId()).getName();
targetSourceName = game.getObject(stackObject.getSourceId()).getLogName();
}
if (stackObject instanceof Spell) {
counteredObjectName = targetSourceName;
@ -95,12 +95,12 @@ public class SpellStack extends ArrayDeque<StackObject> {
counteredObjectName = "Ability (" + stackObject.getStackAbility().getRule(targetSourceName) + ") of " + targetSourceName;
}
if (!game.replaceEvent(GameEvent.getEvent(GameEvent.EventType.COUNTER, objectId, sourceId, stackObject.getControllerId()))) {
if ( stackObject instanceof Spell ) {
game.rememberLKI(objectId, Zone.STACK, (Spell)stackObject);
if (stackObject instanceof Spell) {
game.rememberLKI(objectId, Zone.STACK, (Spell) stackObject);
} else {
this.remove(stackObject);
this.remove(stackObject);
}
stackObject.counter(sourceId, game);
stackObject.counter(sourceId, game);
if (!game.isSimulation()) {
game.informPlayers(counteredObjectName + " is countered by " + sourceObject.getLogName());
}
@ -114,7 +114,7 @@ public class SpellStack extends ArrayDeque<StackObject> {
}
public StackObject getStackObject(UUID id) {
for (StackObject stackObject: this) {
for (StackObject stackObject : this) {
UUID objectId = stackObject.getId();
if (objectId.equals(id)) {
return stackObject;
@ -128,10 +128,10 @@ public class SpellStack extends ArrayDeque<StackObject> {
}
public Spell getSpell(UUID id) {
for (StackObject stackObject: this) {
for (StackObject stackObject : this) {
if (stackObject instanceof Spell) {
if (stackObject.getId().equals(id) || stackObject.getSourceId().equals(id)) {
return (Spell)stackObject;
return (Spell) stackObject;
}
}
}

View file

@ -1,31 +1,30 @@
/*
* 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.
*/
* 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.game.turn;
import java.io.Serializable;
@ -56,7 +55,7 @@ public class Turn implements Serializable {
private final List<Phase> phases = new ArrayList<>();
private boolean declareAttackersStepStarted = false;
private boolean endTurn; // indicates that an end turn effect has resolved.
public Turn() {
endTurn = false;
phases.add(new BeginningPhase());
@ -71,7 +70,7 @@ public class Turn implements Serializable {
this.currentPhase = turn.currentPhase.copy();
}
this.activePlayerId = turn.activePlayerId;
for (Phase phase: turn.phases) {
for (Phase phase : turn.phases) {
this.phases.add(phase.copy());
}
this.declareAttackersStepStarted = turn.declareAttackersStepStarted;
@ -91,7 +90,7 @@ public class Turn implements Serializable {
}
public Phase getPhase(TurnPhase turnPhase) {
for (Phase phase: phases) {
for (Phase phase : phases) {
if (phase.getType() == turnPhase) {
return phase;
}
@ -133,7 +132,7 @@ public class Turn implements Serializable {
this.activePlayerId = activePlayer.getId();
resetCounts();
game.getPlayer(activePlayer.getId()).beginTurn(game);
for (Phase phase: phases) {
for (Phase phase : phases) {
if (game.isPaused() || game.gameOver(null)) {
return;
}
@ -142,11 +141,11 @@ public class Turn implements Serializable {
game.fireEvent(new GameEvent(GameEvent.EventType.PHASE_CHANGED, activePlayer.getId(), null, activePlayer.getId()));
if (!game.getState().getTurnMods().skipPhase(activePlayer.getId(), currentPhase.getType())) {
if (phase.play(game, activePlayer.getId())) {
if(game.executingRollback()) {
if (game.executingRollback()) {
return;
}
//20091005 - 500.4/703.4n
game.emptyManaPools();
game.emptyManaPools();
game.saveState(false);
//20091005 - 500.8
@ -208,7 +207,7 @@ public class Turn implements Serializable {
}
private void resetCounts() {
for (Phase phase: phases) {
for (Phase phase : phases) {
phase.resetCount();
}
}
@ -223,7 +222,7 @@ public class Turn implements Serializable {
return false;
}
Phase phase;
switch(extraPhase) {
switch (extraPhase) {
case BEGINNING:
phase = new BeginningPhase();
break;
@ -251,52 +250,51 @@ public class Turn implements Serializable {
}
/*protected void playExtraTurns(Game game) {
while (game.getState().getTurnMods().extraTurn(activePlayerId)) {
this.play(game, activePlayerId);
}
}*/
/**
* Used for some spells with end turn effect (e.g. Time Stop).
*
* @param game
* @param activePlayerId
*/
while (game.getState().getTurnMods().extraTurn(activePlayerId)) {
this.play(game, activePlayerId);
}
}*/
/**
* Used for some spells with end turn effect (e.g. Time Stop).
*
* @param game
* @param activePlayerId
*/
public void endTurn(Game game, UUID activePlayerId) {
// Ending the turn this way (Time Stop) means the following things happen in order:
// Ending the turn this way (Time Stop) means the following things happen in order:
setEndTurnRequested(true);
// 1) All spells and abilities on the stack are exiled. This includes Time Stop, though it will continue to resolve.
// It also includes spells and abilities that can't be countered.
// 1) All spells and abilities on the stack are exiled. This includes Time Stop, though it will continue to resolve.
// It also includes spells and abilities that can't be countered.
while (!game.getStack().isEmpty()) {
StackObject stackObject = game.getStack().removeLast();
if (stackObject instanceof Spell) {
((Spell) stackObject).moveToExile(null, "", null, game);
}
}
// 2) All attacking and blocking creatures are removed from combat.
for (UUID attackerId: game.getCombat().getAttackers()) {
// 2) All attacking and blocking creatures are removed from combat.
for (UUID attackerId : game.getCombat().getAttackers()) {
Permanent permanent = game.getPermanent(attackerId);
if (permanent != null) {
permanent.removeFromCombat(game);
}
game.getCombat().removeAttacker(attackerId, game);
}
for (UUID blockerId: game.getCombat().getBlockers()) {
for (UUID blockerId : game.getCombat().getBlockers()) {
Permanent permanent = game.getPermanent(blockerId);
if (permanent != null) {
permanent.removeFromCombat(game);
}
}
// 3) State-based actions are checked. No player gets priority, and no triggered abilities are put onto the stack.
// 3) State-based actions are checked. No player gets priority, and no triggered abilities are put onto the stack.
// seems like trigger events have to be removed: http://tabakrules.tumblr.com/post/122350751009/days-undoing-has-been-officially-spoiled-on
game.getState().clearTriggeredAbilities();
game.checkStateAndTriggered(); // triggered effects don't go to stack because check of endTurnRequested
// 4) The current phase and/or step ends.
// 4) The current phase and/or step ends.
// The game skips straight to the cleanup step. The cleanup step happens in its entirety.
// this is caused by the endTurnRequest state
}
public boolean isDeclareAttackersStepStarted() {
@ -310,23 +308,23 @@ public class Turn implements Serializable {
public void setEndTurnRequested(boolean endTurn) {
this.endTurn = endTurn;
}
public boolean isEndTurnRequested() {
return endTurn;
}
public Turn copy() {
return new Turn(this);
}
public String getValue(int turnNum) {
StringBuilder sb = threadLocalBuilder.get();
sb.append("[").append(turnNum)
.append(":").append(currentPhase.getType())
.append(":").append(currentPhase.getStep().getType())
.append("]");
.append(":").append(currentPhase.getType())
.append(":").append(currentPhase.getStep().getType())
.append("]");
return sb.toString();
}
}

View file

@ -227,8 +227,6 @@ public abstract class PlayerImpl implements Player, Serializable {
protected UserData userData;
protected MatchPlayer matchPlayer;
protected String flagName;
/**
* During some steps we can't play anything
*/
@ -2043,6 +2041,9 @@ public abstract class PlayerImpl implements Player, Serializable {
@Override
public void declareBlocker(UUID defenderId, UUID blockerId, UUID attackerId, Game game) {
if (isHuman()) {
setStoredBookmark(game.bookmarkState());
}
Permanent blocker = game.getPermanent(blockerId);
CombatGroup group = game.getCombat().findGroup(attackerId);
if (blocker != null && group != null && group.canBlock(blocker, game)) {
@ -2459,6 +2460,22 @@ public abstract class PlayerImpl implements Player, Serializable {
}
}
}
// check if it's possible to play the top card of a library
for (UUID playerInRangeId : game.getState().getPlayersInRange(getId(), game)) {
Player player = game.getPlayer(playerInRangeId);
if (player != null) {
if (player.isTopCardRevealed() && player.getLibrary().size() > 0) {
Card card = player.getLibrary().getFromTop(game);
if (game.getContinuousEffects().asThough(card.getId(), AsThoughEffectType.PLAY_FROM_NOT_OWN_HAND_ZONE, getId(), game)) {
for (ActivatedAbility ability : card.getAbilities().getActivatedAbilities(Zone.HAND)) {
if (ability instanceof SpellAbility || ability instanceof PlayLandAbility) {
playable.add(ability);
}
}
}
}
}
}
// eliminate duplicate activated abilities
Map<String, Ability> playableActivated = new HashMap<>();
for (Permanent permanent : game.getBattlefield().getAllActivePermanents(playerId)) {
@ -2697,6 +2714,8 @@ public abstract class PlayerImpl implements Player, Serializable {
@Override
public void setUserData(UserData userData) {
this.userData = userData;
getManaPool().setAutoPayment(userData.isManaPoolAutomatic());
getManaPool().setAutoPaymentRestricted(userData.isManaPoolAutomaticRestricted());
}
@Override
@ -2895,6 +2914,12 @@ public abstract class PlayerImpl implements Player, Serializable {
result |= moveCardToHandWithInfo(card, source == null ? null : source.getSourceId(), game, fromZone);
}
return result;
case BATTLEFIELD:
result = false;
for (Card card : cards) {
result |= putOntoBattlefieldWithInfo(card, game, fromZone, source == null ? null : source.getSourceId());
}
return result;
default:
throw new UnsupportedOperationException("to Zone not supported yet");
}

View file

@ -17,10 +17,12 @@ public class UserData implements Serializable {
protected UserSkipPrioritySteps userSkipPrioritySteps;
protected String flagName;
protected boolean askMoveToGraveOrder;
protected boolean manaPoolAutomatic;
protected boolean manaPoolAutomaticRestricted;
public UserData(UserGroup userGroup, int avatarId, boolean showAbilityPickerForced,
boolean allowRequestShowHandCards, boolean confirmEmptyManaPool, UserSkipPrioritySteps userSkipPrioritySteps,
String flagName, boolean askMoveToGraveOrder) {
String flagName, boolean askMoveToGraveOrder, boolean manaPoolAutomatic, boolean manaPoolAutomaticRestricted) {
this.groupId = userGroup.getGroupId();
this.avatarId = avatarId;
this.showAbilityPickerForced = showAbilityPickerForced;
@ -29,6 +31,8 @@ public class UserData implements Serializable {
this.confirmEmptyManaPool = confirmEmptyManaPool;
this.flagName = flagName;
this.askMoveToGraveOrder = askMoveToGraveOrder;
this.manaPoolAutomatic = manaPoolAutomatic;
this.manaPoolAutomaticRestricted = manaPoolAutomaticRestricted;
}
public void update(UserData userData) {
@ -40,10 +44,12 @@ public class UserData implements Serializable {
this.confirmEmptyManaPool = userData.confirmEmptyManaPool;
this.flagName = userData.flagName;
this.askMoveToGraveOrder = userData.askMoveToGraveOrder;
this.manaPoolAutomatic = userData.manaPoolAutomatic;
this.manaPoolAutomaticRestricted = userData.manaPoolAutomaticRestricted;
}
public static UserData getDefaultUserDataView() {
return new UserData(UserGroup.DEFAULT, 0, false, false, true, null, "world.png", false);
return new UserData(UserGroup.DEFAULT, 0, false, false, true, null, "world.png", false, true, true);
}
public void setGroupId(int groupId) {
@ -98,6 +104,10 @@ public class UserData implements Serializable {
return flagName;
}
public void setFlagName(String flagName) {
this.flagName = flagName;
}
public boolean askMoveToGraveOrder() {
return askMoveToGraveOrder;
}
@ -106,4 +116,20 @@ public class UserData implements Serializable {
this.askMoveToGraveOrder = askMoveToGraveOrder;
}
public boolean isManaPoolAutomatic() {
return manaPoolAutomatic;
}
public void setManaPoolAutomatic(boolean manaPoolAutomatic) {
this.manaPoolAutomatic = manaPoolAutomatic;
}
public boolean isManaPoolAutomaticRestricted() {
return manaPoolAutomaticRestricted;
}
public void setManaPoolAutomaticRestricted(boolean manaPoolAutomaticRestricted) {
this.manaPoolAutomaticRestricted = manaPoolAutomaticRestricted;
}
}

View file

@ -0,0 +1,93 @@
/*
* 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.watchers.common;
import java.util.HashSet;
import java.util.Set;
import java.util.UUID;
import mage.cards.Card;
import mage.constants.CardType;
import mage.constants.WatcherScope;
import mage.constants.Zone;
import mage.game.Game;
import mage.game.events.GameEvent;
import mage.game.events.ZoneChangeEvent;
import mage.game.stack.Spell;
import mage.watchers.Watcher;
/**
*
* @author LevelX2
*/
public class CreatureWasCastWatcher extends Watcher {
private final Set<UUID> creaturesCasted = new HashSet<>();
public CreatureWasCastWatcher() {
super("CreatureWasCast", WatcherScope.GAME);
}
public CreatureWasCastWatcher(final CreatureWasCastWatcher watcher) {
super(watcher);
}
@Override
public void watch(GameEvent event, Game game) {
if (event.getType() == GameEvent.EventType.SPELL_CAST) {
Spell spell = (Spell) game.getObject(event.getTargetId());
if (spell != null) {
Card card = game.getCard(spell.getSourceId());
if (card != null && card.getCardType().contains(CardType.CREATURE)) {
creaturesCasted.add(card.getId());
}
}
}
if (event.getType() == GameEvent.EventType.ZONE_CHANGE
&& ((ZoneChangeEvent) event).getFromZone() == Zone.BATTLEFIELD) {
Card card = game.getCard(event.getTargetId());
if (card != null && card.getCardType().contains(CardType.CREATURE)) {
creaturesCasted.remove(card.getId());
}
}
}
public boolean wasCreatureCastThisTurn(UUID creatureSourceId) {
return creaturesCasted.contains(creatureSourceId);
}
@Override
public void reset() {
super.reset();
creaturesCasted.clear();
}
@Override
public CreatureWasCastWatcher copy() {
return new CreatureWasCastWatcher(this);
}
}