mirror of
https://github.com/magefree/mage.git
synced 2026-01-10 04:42:07 -08:00
Merge origin/master
This commit is contained in:
commit
d5e4ce9bf8
241 changed files with 9962 additions and 1334 deletions
|
|
@ -1,75 +0,0 @@
|
|||
/*
|
||||
* 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.constants.CardType;
|
||||
import mage.abilities.Ability;
|
||||
import mage.abilities.costs.CostImpl;
|
||||
import mage.filter.FilterPermanent;
|
||||
import mage.filter.predicate.mageobject.CardTypePredicate;
|
||||
import mage.game.Game;
|
||||
|
||||
/**
|
||||
*
|
||||
* @author BetaSteward_at_googlemail.com
|
||||
*/
|
||||
public class MetalcraftCost extends CostImpl {
|
||||
|
||||
private static final FilterPermanent filter = new FilterPermanent("artifact");
|
||||
|
||||
static {
|
||||
filter.add(new CardTypePredicate(CardType.ARTIFACT));
|
||||
}
|
||||
|
||||
public MetalcraftCost() {
|
||||
this.text = "Activate this ability only if you control three or more artifacts";
|
||||
}
|
||||
|
||||
public MetalcraftCost(final MetalcraftCost cost) {
|
||||
super(cost);
|
||||
}
|
||||
|
||||
@Override
|
||||
public MetalcraftCost copy() {
|
||||
return new MetalcraftCost(this);
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean canPay(Ability ability, UUID sourceId, UUID controllerId, Game game) {
|
||||
return game.getBattlefield().contains(filter, controllerId, 3, game);
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean pay(Ability ability, Game game, UUID sourceId, UUID controllerId, boolean noMana) {
|
||||
this.paid = true;
|
||||
return paid;
|
||||
}
|
||||
|
||||
}
|
||||
|
|
@ -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,21 +20,20 @@
|
|||
* 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;
|
||||
import mage.constants.Outcome;
|
||||
import mage.abilities.Ability;
|
||||
import mage.abilities.Mode;
|
||||
import mage.abilities.dynamicvalue.DynamicValue;
|
||||
import mage.abilities.dynamicvalue.common.StaticValue;
|
||||
import mage.abilities.effects.OneShotEffect;
|
||||
import mage.constants.Outcome;
|
||||
import mage.game.Game;
|
||||
import mage.game.permanent.Permanent;
|
||||
import mage.players.Player;
|
||||
|
|
@ -71,12 +70,11 @@ public class DamageMultiEffect extends OneShotEffect {
|
|||
public boolean apply(Game game, Ability source) {
|
||||
if (source.getTargets().size() > 0) {
|
||||
Target multiTarget = source.getTargets().get(0);
|
||||
for (UUID target: multiTarget.getTargets()) {
|
||||
for (UUID target : multiTarget.getTargets()) {
|
||||
Permanent permanent = game.getPermanent(target);
|
||||
if (permanent != null) {
|
||||
permanent.damage(multiTarget.getTargetAmount(target), source.getSourceId(), game, false, true);
|
||||
}
|
||||
else {
|
||||
} else {
|
||||
Player player = game.getPlayer(target);
|
||||
if (player != null) {
|
||||
player.damage(multiTarget.getTargetAmount(target), source.getSourceId(), game, false, true);
|
||||
|
|
@ -92,9 +90,6 @@ public class DamageMultiEffect extends OneShotEffect {
|
|||
if (staticText != null && !staticText.isEmpty()) {
|
||||
return staticText;
|
||||
}
|
||||
StringBuilder sb = new StringBuilder();
|
||||
sb.append("{source} deals ").append(amount.toString());
|
||||
sb.append(" damage divided as you choose among any number of target ").append(mode.getTargets().get(0).getTargetName());
|
||||
return sb.toString();
|
||||
return "{source} deals " + amount.toString() + " damage divided as you choose among any number of target " + mode.getTargets().get(0).getTargetName();
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -53,6 +53,7 @@ public class DoIfCostPaid extends OneShotEffect {
|
|||
effectText = effectText.substring(0, effectText.length() - 1);
|
||||
}
|
||||
message = getCostText() + " and " + effectText + "?";
|
||||
message = Character.toUpperCase(message.charAt(0)) + message.substring(1);
|
||||
} else {
|
||||
message = chooseUseText;
|
||||
}
|
||||
|
|
|
|||
|
|
@ -0,0 +1,99 @@
|
|||
/*
|
||||
* 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;
|
||||
|
||||
import java.util.UUID;
|
||||
import mage.abilities.Ability;
|
||||
import mage.abilities.dynamicvalue.DynamicValue;
|
||||
import mage.abilities.dynamicvalue.common.StaticValue;
|
||||
import mage.abilities.effects.OneShotEffect;
|
||||
import mage.constants.Outcome;
|
||||
import mage.constants.TargetController;
|
||||
import static mage.constants.TargetController.ANY;
|
||||
import static mage.constants.TargetController.OPPONENT;
|
||||
import mage.game.Game;
|
||||
import mage.players.Player;
|
||||
|
||||
/**
|
||||
*
|
||||
* @author LevelX2
|
||||
*/
|
||||
public class SetPlayerLifeAllEffect extends OneShotEffect {
|
||||
|
||||
private TargetController targetController;
|
||||
protected DynamicValue amount;
|
||||
|
||||
public SetPlayerLifeAllEffect(int amount) {
|
||||
this(amount, TargetController.ANY);
|
||||
}
|
||||
|
||||
public SetPlayerLifeAllEffect(DynamicValue amount) {
|
||||
this(amount, TargetController.ANY);
|
||||
}
|
||||
|
||||
public SetPlayerLifeAllEffect(int amount, TargetController targetController) {
|
||||
this(new StaticValue(amount), targetController);
|
||||
}
|
||||
|
||||
public SetPlayerLifeAllEffect(DynamicValue amount, TargetController targetController) {
|
||||
super(Outcome.DrawCard);
|
||||
this.amount = amount;
|
||||
this.targetController = targetController;
|
||||
staticText = setText();
|
||||
}
|
||||
|
||||
public SetPlayerLifeAllEffect(final SetPlayerLifeAllEffect effect) {
|
||||
super(effect);
|
||||
this.amount = effect.amount;
|
||||
this.targetController = effect.targetController;
|
||||
}
|
||||
|
||||
@Override
|
||||
public SetPlayerLifeAllEffect copy() {
|
||||
return new SetPlayerLifeAllEffect(this);
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean apply(Game game, Ability source) {
|
||||
Player sourcePlayer = game.getPlayer(source.getControllerId());
|
||||
switch (targetController) {
|
||||
case ANY:
|
||||
for (UUID playerId : sourcePlayer.getInRange()) {
|
||||
Player player = game.getPlayer(playerId);
|
||||
if (player != null) {
|
||||
player.setLife(amount.calculate(game, source, this), game);
|
||||
}
|
||||
}
|
||||
break;
|
||||
case OPPONENT:
|
||||
for (UUID playerId : game.getOpponents(sourcePlayer.getId())) {
|
||||
Player player = game.getPlayer(playerId);
|
||||
if (player != null) {
|
||||
player.setLife(amount.calculate(game, source, this), game);
|
||||
}
|
||||
}
|
||||
break;
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
private String setText() {
|
||||
StringBuilder sb = new StringBuilder("Each ");
|
||||
switch (targetController) {
|
||||
case ANY:
|
||||
sb.append("player");
|
||||
break;
|
||||
case OPPONENT:
|
||||
sb.append("opponent");
|
||||
break;
|
||||
default:
|
||||
throw new UnsupportedOperationException("Not supported value for targetController");
|
||||
}
|
||||
sb.append(" 's life total becomes ");
|
||||
sb.append(amount.toString());
|
||||
return sb.toString();
|
||||
}
|
||||
}
|
||||
|
|
@ -1,39 +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.effects.common.continuous;
|
||||
|
||||
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.abilities.Ability;
|
||||
import mage.abilities.effects.ContinuousEffectImpl;
|
||||
import mage.game.Game;
|
||||
import mage.players.Player;
|
||||
|
||||
|
|
@ -44,7 +43,7 @@ public class LifeTotalCantChangeControllerEffect extends ContinuousEffectImpl {
|
|||
|
||||
public LifeTotalCantChangeControllerEffect(Duration duration) {
|
||||
super(duration, Layer.PlayerEffects, SubLayer.NA, Outcome.Benefit);
|
||||
setText();
|
||||
staticText = "Your life total can't change. <i>(You can't gain or lose life. You can't pay any amount of life except 0.)</i>";
|
||||
}
|
||||
|
||||
public LifeTotalCantChangeControllerEffect(final LifeTotalCantChangeControllerEffect effect) {
|
||||
|
|
@ -66,8 +65,4 @@ public class LifeTotalCantChangeControllerEffect extends ContinuousEffectImpl {
|
|||
return true;
|
||||
}
|
||||
|
||||
private void setText() {
|
||||
staticText = "Your life total can't change. (You can't gain or lose life. You can't pay any amount of life except 0.)";
|
||||
}
|
||||
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -139,7 +139,7 @@ class HideawayExileEffect extends OneShotEffect {
|
|||
class HideawayLookAtFaceDownCardEffect extends AsThoughEffectImpl {
|
||||
|
||||
public HideawayLookAtFaceDownCardEffect() {
|
||||
super(AsThoughEffectType.REVEAL_FACE_DOWN, Duration.EndOfGame, Outcome.Benefit);
|
||||
super(AsThoughEffectType.LOOK_AT_FACE_DOWN, Duration.EndOfGame, Outcome.Benefit);
|
||||
staticText = "You may look at cards exiled with {this}";
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -237,7 +237,7 @@ public class MorphAbility extends StaticAbility implements AlternativeSourceCost
|
|||
if (player != null) {
|
||||
this.resetMorph();
|
||||
if (alternateCosts.canPay(ability, sourceId, controllerId, game)) {
|
||||
if (player.chooseUse(Outcome.Benefit, new StringBuilder("Cast this card as a 2/2 face-down creature for ").append(getCosts().getText()).append(" ?").toString(), ability, game)) {
|
||||
if (player.chooseUse(Outcome.Benefit, "Cast this card as a 2/2 face-down creature for " + getCosts().getText() + " ?", ability, game)) {
|
||||
activateMorph(game);
|
||||
// change mana costs
|
||||
ability.getManaCostsToPay().clear();
|
||||
|
|
|
|||
|
|
@ -1,50 +1,45 @@
|
|||
/*
|
||||
* 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.abilities.Ability;
|
||||
import mage.abilities.common.DiesTriggeredAbility;
|
||||
import mage.abilities.effects.OneShotEffect;
|
||||
import mage.abilities.effects.ReplacementEffectImpl;
|
||||
import mage.abilities.effects.common.ReturnSourceFromGraveyardToBattlefieldEffect;
|
||||
import mage.constants.Duration;
|
||||
import mage.constants.Outcome;
|
||||
import mage.counters.CounterType;
|
||||
import mage.counters.Counters;
|
||||
import mage.game.Game;
|
||||
import mage.game.events.EntersTheBattlefieldEvent;
|
||||
import mage.game.events.GameEvent;
|
||||
import mage.game.events.ZoneChangeEvent;
|
||||
import mage.game.permanent.Permanent;
|
||||
import mage.target.targetpointer.FixedTarget;
|
||||
|
||||
|
||||
|
||||
public class PersistAbility extends DiesTriggeredAbility {
|
||||
|
||||
public PersistAbility() {
|
||||
|
|
@ -73,7 +68,6 @@ public class PersistAbility extends DiesTriggeredAbility {
|
|||
if (permanent.getCounters().getCount(CounterType.M1M1) == 0) {
|
||||
FixedTarget fixedTarget = new FixedTarget(permanent.getId());
|
||||
fixedTarget.init(game, this);
|
||||
game.getState().setValue("persist" + getSourceId().toString(), fixedTarget);
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
|
@ -104,58 +98,9 @@ class PersistEffect extends OneShotEffect {
|
|||
|
||||
@Override
|
||||
public boolean apply(Game game, Ability source) {
|
||||
game.addEffect(new PersistReplacementEffect(), source);
|
||||
Counters countersToAdd = new Counters();
|
||||
countersToAdd.addCounter(CounterType.M1M1.createInstance());
|
||||
game.setEnterWithCounters(source.getSourceId(), countersToAdd);
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
||||
class PersistReplacementEffect extends ReplacementEffectImpl {
|
||||
|
||||
PersistReplacementEffect() {
|
||||
super(Duration.Custom, Outcome.UnboostCreature, false);
|
||||
selfScope = true;
|
||||
staticText = "return it to the battlefield under its owner's control with a -1/-1 counter on it";
|
||||
}
|
||||
|
||||
PersistReplacementEffect(final PersistReplacementEffect effect) {
|
||||
super(effect);
|
||||
}
|
||||
|
||||
@Override
|
||||
public PersistReplacementEffect copy() {
|
||||
return new PersistReplacementEffect(this);
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean replaceEvent(GameEvent event, Ability source, Game game) {
|
||||
Permanent permanent = game.getPermanent(event.getTargetId());
|
||||
if (permanent != null) {
|
||||
permanent.addCounters(CounterType.M1M1.createInstance(), game);
|
||||
}
|
||||
discard();
|
||||
return false;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean checksEventType(GameEvent event, Game game) {
|
||||
return event.getType() == GameEvent.EventType.ENTERS_THE_BATTLEFIELD;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean applies(GameEvent event, Ability source, Game game) {
|
||||
if (event.getTargetId().equals(source.getSourceId())) {
|
||||
Object fixedTarget = game.getState().getValue("persist" + source.getSourceId().toString());
|
||||
if (fixedTarget instanceof FixedTarget && ((FixedTarget) fixedTarget).getTarget().equals(source.getSourceId()) &&
|
||||
((FixedTarget) fixedTarget).getZoneChangeCounter() + 1 == game.getState().getZoneChangeCounter(source.getSourceId())) {
|
||||
|
||||
return true;
|
||||
}
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean apply(Game game, Ability source) {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -57,8 +57,8 @@ public class TransmuteAbility extends SimpleActivatedAbility {
|
|||
@Override
|
||||
public String getRule() {
|
||||
return new StringBuilder("Transmute ").append(this.getManaCosts().getText())
|
||||
.append(" (").append(this.getManaCosts().getText())
|
||||
.append(", Discard this card: Search your library for a card with the same converted mana cost as this card, reveal it, and put it into your hand. Then shuffle your library. Transmute only as a sorcery.)").toString();
|
||||
.append(" <i>(").append(this.getManaCosts().getText())
|
||||
.append(", Discard this card: Search your library for a card with the same converted mana cost as this card, reveal it, and put it into your hand. Then shuffle your library. Transmute only as a sorcery.)</i>").toString();
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -1,16 +1,13 @@
|
|||
package mage.abilities.keyword;
|
||||
|
||||
|
||||
import java.util.UUID;
|
||||
import mage.abilities.Ability;
|
||||
import mage.abilities.common.DiesTriggeredAbility;
|
||||
import mage.abilities.effects.OneShotEffect;
|
||||
import mage.abilities.effects.ReplacementEffectImpl;
|
||||
import mage.abilities.effects.common.ReturnSourceFromGraveyardToBattlefieldEffect;
|
||||
import mage.constants.Duration;
|
||||
import mage.constants.Outcome;
|
||||
import mage.constants.Zone;
|
||||
import mage.counters.CounterType;
|
||||
import mage.counters.Counters;
|
||||
import mage.game.Game;
|
||||
import mage.game.events.GameEvent;
|
||||
import mage.game.permanent.Permanent;
|
||||
|
|
@ -19,7 +16,7 @@ import mage.game.permanent.Permanent;
|
|||
* @author Loki
|
||||
*/
|
||||
public class UndyingAbility extends DiesTriggeredAbility {
|
||||
|
||||
|
||||
public UndyingAbility() {
|
||||
super(new UndyingEffect());
|
||||
this.addEffect(new ReturnSourceFromGraveyardToBattlefieldEffect(false, true));
|
||||
|
|
@ -39,7 +36,6 @@ public class UndyingAbility extends DiesTriggeredAbility {
|
|||
if (super.checkTrigger(event, game)) {
|
||||
Permanent permanent = (Permanent) game.getLastKnownInformation(event.getTargetId(), Zone.BATTLEFIELD);
|
||||
if (!permanent.getCounters().containsKey(CounterType.P1P1) || permanent.getCounters().getCount(CounterType.P1P1) == 0) {
|
||||
game.getState().setValue("undying" + getSourceId(),permanent.getId());
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
|
@ -70,58 +66,9 @@ class UndyingEffect extends OneShotEffect {
|
|||
|
||||
@Override
|
||||
public boolean apply(Game game, Ability source) {
|
||||
game.addEffect(new UndyingReplacementEffect(), source);
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
class UndyingReplacementEffect extends ReplacementEffectImpl {
|
||||
|
||||
UndyingReplacementEffect() {
|
||||
super(Duration.OneUse, Outcome.BoostCreature, false);
|
||||
selfScope = true;
|
||||
staticText = "return it to the battlefield under its owner's control with a +1/+1 counter on it";
|
||||
}
|
||||
|
||||
UndyingReplacementEffect(final UndyingReplacementEffect effect) {
|
||||
super(effect);
|
||||
}
|
||||
|
||||
@Override
|
||||
public UndyingReplacementEffect copy() {
|
||||
return new UndyingReplacementEffect(this);
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean replaceEvent(GameEvent event, Ability source, Game game) {
|
||||
Permanent permanent = game.getPermanent(event.getTargetId());
|
||||
if (permanent != null) {
|
||||
game.getState().setValue("undying" + source.getSourceId(), null);
|
||||
permanent.addCounters(CounterType.P1P1.createInstance(), game);
|
||||
}
|
||||
used = true;
|
||||
return false;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean checksEventType(GameEvent event, Game game) {
|
||||
return event.getType() == GameEvent.EventType.ENTERS_THE_BATTLEFIELD;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean applies(GameEvent event, Ability source, Game game) {
|
||||
if (event.getTargetId().equals(source.getSourceId())) {
|
||||
// Check if undying condition is true
|
||||
UUID targetId = (UUID) game.getState().getValue("undying" + source.getSourceId());
|
||||
if (targetId != null && targetId.equals(source.getSourceId())) {
|
||||
return true;
|
||||
}
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean apply(Game game, Ability source) {
|
||||
return false;
|
||||
Counters countersToAdd = new Counters();
|
||||
countersToAdd.addCounter(CounterType.P1P1.createInstance());
|
||||
game.setEnterWithCounters(source.getSourceId(), countersToAdd);
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -31,9 +31,14 @@ public class VanishingUpkeepAbility extends BeginningOfUpkeepTriggeredAbility {
|
|||
|
||||
@Override
|
||||
public String getRule() {
|
||||
return "Vanishing " + vanishingAmount
|
||||
if(vanishingAmount > 0) {
|
||||
return "Vanishing " + vanishingAmount
|
||||
+ " <i>(This permanent enters the battlefield with " + CardUtil.numberToText(vanishingAmount)
|
||||
+ " time counters on it. At the beginning of your upkeep, remove a time counter from it. When the last is removed, sacrifice it.)<i>";
|
||||
+ " time counters on it. At the beginning of your upkeep, remove a time counter from it. When the last is removed, sacrifice it.)</i>";
|
||||
}
|
||||
else {
|
||||
return "Vanishing <i>(At the beginning of your upkeep, remove a time counter from this permanent. When the last is removed, sacrifice it.)</i>";
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -1,7 +1,8 @@
|
|||
package mage.actions;
|
||||
|
||||
import java.util.ArrayList;
|
||||
|
||||
import java.util.List;
|
||||
import java.util.UUID;
|
||||
import mage.actions.impl.MageAction;
|
||||
import mage.actions.score.ArtificialScoringSystem;
|
||||
import mage.cards.Card;
|
||||
|
|
@ -9,9 +10,6 @@ import mage.constants.Zone;
|
|||
import mage.game.Game;
|
||||
import mage.game.events.GameEvent;
|
||||
import mage.players.Player;
|
||||
|
||||
import java.util.List;
|
||||
import java.util.UUID;
|
||||
import mage.util.CardUtil;
|
||||
|
||||
/**
|
||||
|
|
@ -39,7 +37,7 @@ public class MageDrawAction extends MageAction {
|
|||
* Draw and set action score.
|
||||
*
|
||||
* @param game Game context.
|
||||
* @return
|
||||
* @return
|
||||
*/
|
||||
@Override
|
||||
public int doAction(Game game) {
|
||||
|
|
@ -70,8 +68,8 @@ public class MageDrawAction extends MageAction {
|
|||
}
|
||||
|
||||
/**
|
||||
* Draw a card if possible (there is no replacement effect that prevent us from drawing).
|
||||
* Fire event about card drawn.
|
||||
* Draw a card if possible (there is no replacement effect that prevent us
|
||||
* from drawing). Fire event about card drawn.
|
||||
*
|
||||
* @param game
|
||||
* @return
|
||||
|
|
|
|||
|
|
@ -602,6 +602,8 @@ public abstract class CardImpl extends MageObjectImpl implements Card {
|
|||
PermanentCard permanent = new PermanentCard(this, event.getPlayerId(), game);
|
||||
// make sure the controller of all continuous effects of this card are switched to the current controller
|
||||
game.getContinuousEffects().setController(objectId, event.getPlayerId());
|
||||
// check if there are counters to add to the permanent (e.g. from non replacement effects like Persist)
|
||||
checkForCountersToAdd(permanent, game);
|
||||
game.addPermanent(permanent);
|
||||
setZone(Zone.BATTLEFIELD, game);
|
||||
game.setScopeRelevant(true);
|
||||
|
|
@ -621,6 +623,16 @@ public abstract class CardImpl extends MageObjectImpl implements Card {
|
|||
return false;
|
||||
}
|
||||
|
||||
private void checkForCountersToAdd(PermanentCard permanent, Game game) {
|
||||
Counters countersToAdd = game.getEnterWithCounters(permanent.getId());
|
||||
if (countersToAdd != null) {
|
||||
for (Counter counter : countersToAdd.values()) {
|
||||
permanent.addCounters(counter, game);
|
||||
}
|
||||
game.setEnterWithCounters(permanent.getId(), null);
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public void setFaceDown(boolean value, Game game) {
|
||||
game.getState().getCardState(objectId).setFaceDown(value);
|
||||
|
|
|
|||
|
|
@ -63,7 +63,7 @@ public enum CardRepository {
|
|||
// raise this if db structure was changed
|
||||
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 = 32;
|
||||
private static final long CARD_CONTENT_VERSION = 33;
|
||||
|
||||
private final Random random = new Random();
|
||||
private Dao<CardInfo, Object> cardDao;
|
||||
|
|
@ -250,7 +250,7 @@ public enum CardRepository {
|
|||
}
|
||||
return subtypes;
|
||||
}
|
||||
|
||||
|
||||
public Set<String> getLandTypes() {
|
||||
TreeSet<String> subtypes = new TreeSet<>();
|
||||
try {
|
||||
|
|
|
|||
|
|
@ -17,7 +17,7 @@ public enum AsThoughEffectType {
|
|||
DAMAGE,
|
||||
HEXPROOF,
|
||||
PAY,
|
||||
REVEAL_FACE_DOWN,
|
||||
LOOK_AT_FACE_DOWN,
|
||||
SPEND_ANY_MANA,
|
||||
TARGET
|
||||
}
|
||||
|
|
|
|||
|
|
@ -33,7 +33,7 @@ package mage.counters;
|
|||
* @author nantuko
|
||||
*/
|
||||
public enum CounterType {
|
||||
|
||||
|
||||
AGE("age"),
|
||||
AIM("aim"),
|
||||
ARROWHEAD("arrowhead"),
|
||||
|
|
@ -57,6 +57,7 @@ public enum CounterType {
|
|||
FUSE("fuse"),
|
||||
GOLD("gold"),
|
||||
HATCHLING("hatchling"),
|
||||
HEALING("healing"),
|
||||
HOOFPRINT("hoofprint"),
|
||||
ICE("ice"),
|
||||
JAVELIN("javelin"),
|
||||
|
|
@ -117,7 +118,7 @@ public enum CounterType {
|
|||
}
|
||||
|
||||
/**
|
||||
* Create instance of counter type with defined amount of counters of the
|
||||
* Create instance of counter type with defined amount of counters of the
|
||||
* given type.
|
||||
*
|
||||
* @param amount amount of counters of the given type.
|
||||
|
|
|
|||
|
|
@ -54,6 +54,7 @@ import mage.constants.MultiplayerAttackOption;
|
|||
import mage.constants.PlayerAction;
|
||||
import mage.constants.RangeOfInfluence;
|
||||
import mage.constants.Zone;
|
||||
import mage.counters.Counters;
|
||||
import mage.game.combat.Combat;
|
||||
import mage.game.command.Commander;
|
||||
import mage.game.command.Emblem;
|
||||
|
|
@ -436,4 +437,8 @@ public interface Game extends MageItem, Serializable {
|
|||
void rollbackTurns(int turnsToRollback);
|
||||
|
||||
boolean executingRollback();
|
||||
|
||||
void setEnterWithCounters(UUID sourceId, Counters counters);
|
||||
|
||||
Counters getEnterWithCounters(UUID sourceId);
|
||||
}
|
||||
|
|
|
|||
|
|
@ -78,6 +78,7 @@ import mage.constants.PlayerAction;
|
|||
import mage.constants.RangeOfInfluence;
|
||||
import mage.constants.Zone;
|
||||
import mage.counters.CounterType;
|
||||
import mage.counters.Counters;
|
||||
import mage.filter.Filter;
|
||||
import mage.filter.FilterPermanent;
|
||||
import mage.filter.common.FilterControlledCreaturePermanent;
|
||||
|
|
@ -207,6 +208,9 @@ public abstract class GameImpl implements Game, Serializable {
|
|||
private final int startLife;
|
||||
protected PlayerList playerList;
|
||||
|
||||
// used to set the counters a permanent adds the battlefield (if no replacement effect is used e.g. Persist)
|
||||
protected Map<UUID, Counters> enterWithCounters = new HashMap<>();
|
||||
|
||||
public GameImpl(MultiplayerAttackOption attackOption, RangeOfInfluence range, int freeMulligans, int startLife) {
|
||||
this.id = UUID.randomUUID();
|
||||
this.range = range;
|
||||
|
|
@ -246,6 +250,7 @@ public abstract class GameImpl implements Game, Serializable {
|
|||
this.priorityTime = game.priorityTime;
|
||||
this.saveGame = game.saveGame;
|
||||
this.startLife = game.startLife;
|
||||
this.enterWithCounters.putAll(game.enterWithCounters);
|
||||
}
|
||||
|
||||
@Override
|
||||
|
|
@ -2652,6 +2657,7 @@ public abstract class GameImpl implements Game, Serializable {
|
|||
for (Player playerObject : getPlayers().values()) {
|
||||
if (playerObject.isHuman() && playerObject.isInGame()) {
|
||||
playerObject.abort();
|
||||
playerObject.resetPlayerPassedActions();
|
||||
}
|
||||
}
|
||||
fireUpdatePlayersEvent();
|
||||
|
|
@ -2665,4 +2671,20 @@ public abstract class GameImpl implements Game, Serializable {
|
|||
return executingRollback;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void setEnterWithCounters(UUID sourceId, Counters counters) {
|
||||
if (counters == null) {
|
||||
if (enterWithCounters.containsKey(sourceId)) {
|
||||
enterWithCounters.remove(sourceId);
|
||||
}
|
||||
return;
|
||||
}
|
||||
enterWithCounters.put(sourceId, counters);
|
||||
}
|
||||
|
||||
@Override
|
||||
public Counters getEnterWithCounters(UUID sourceId) {
|
||||
return enterWithCounters.get(sourceId);
|
||||
}
|
||||
|
||||
}
|
||||
|
|
|
|||
|
|
@ -179,6 +179,8 @@ public interface Player extends MageItem, Copyable<Player> {
|
|||
|
||||
void resetPassed();
|
||||
|
||||
void resetPlayerPassedActions();
|
||||
|
||||
boolean getPassedTurn();
|
||||
|
||||
boolean getPassedUntilEndOfTurn();
|
||||
|
|
@ -564,8 +566,9 @@ public interface Player extends MageItem, Copyable<Player> {
|
|||
*
|
||||
* @param card
|
||||
* @param game
|
||||
* @return player looked at the card
|
||||
*/
|
||||
void revealFaceDownCard(Card card, Game game);
|
||||
boolean lookAtFaceDownCard(Card card, Game game);
|
||||
|
||||
/**
|
||||
* Set seconds left to play the game.
|
||||
|
|
@ -620,13 +623,12 @@ public interface Player extends MageItem, Copyable<Player> {
|
|||
*/
|
||||
boolean moveCards(Cards cards, Zone fromZone, Zone toZone, Ability source, Game game);
|
||||
|
||||
// boolean moveCards(Cards cards, Zone fromZone, Zone toZone, Ability source, Game game, boolean withName);
|
||||
boolean moveCards(Card card, Zone fromZone, Zone toZone, Ability source, Game game);
|
||||
|
||||
// boolean moveCards(Card card, Zone fromZone, Zone toZone, Ability source, Game game, boolean withName);
|
||||
boolean moveCards(Set<Card> cards, Zone fromZone, Zone toZone, Ability source, Game game);
|
||||
|
||||
// boolean moveCards(Set<Card> cards, Zone fromZone, Zone toZone, Ability source, Game game, boolean withName);
|
||||
boolean moveCardsToExile(Card card, Ability source, Game game, boolean withName, UUID exileId, String exileZoneName);
|
||||
|
||||
boolean moveCardsToExile(Set<Card> cards, Ability source, Game game, boolean withName, UUID exileId, String exileZoneName);
|
||||
|
||||
/**
|
||||
|
|
@ -683,9 +685,9 @@ public interface Player extends MageItem, Copyable<Player> {
|
|||
* @param source
|
||||
* @param game
|
||||
* @param fromZone if null, this info isn't postet
|
||||
* @return
|
||||
* @return Set<Cards> that were successful moved to graveyard
|
||||
*/
|
||||
boolean moveCardsToGraveyardWithInfo(Set<Card> cards, Ability source, Game game, Zone fromZone);
|
||||
Set<Card> moveCardsToGraveyardWithInfo(Set<Card> cards, Ability source, Game game, Zone fromZone);
|
||||
|
||||
/**
|
||||
* Uses card.moveToZone and posts a inform message about moving the card to
|
||||
|
|
|
|||
|
|
@ -34,6 +34,7 @@ import java.util.HashMap;
|
|||
import java.util.HashSet;
|
||||
import java.util.Iterator;
|
||||
import java.util.LinkedHashMap;
|
||||
import java.util.LinkedHashSet;
|
||||
import java.util.List;
|
||||
import java.util.Locale;
|
||||
import java.util.Map;
|
||||
|
|
@ -330,9 +331,21 @@ public abstract class PlayerImpl implements Player, Serializable {
|
|||
this.name = player.getName();
|
||||
this.human = player.isHuman();
|
||||
this.life = player.getLife();
|
||||
this.wins = player.hasWon();
|
||||
this.loses = player.hasLost();
|
||||
|
||||
// Don't restore more global states. If restored they are probably cause for unintended draws (https://github.com/magefree/mage/issues/1205).
|
||||
// this.wins = player.hasWon();
|
||||
// this.loses = player.hasLost();
|
||||
// this.left = player.hasLeft();
|
||||
// this.quit = player.hasQuit();
|
||||
// Makes no sense to restore
|
||||
// this.passed = player.isPassed();
|
||||
// this.priorityTimeLeft = player.getPriorityTimeLeft();
|
||||
// this.idleTimeout = player.hasIdleTimeout();
|
||||
// this.timerTimeout = player.hasTimerTimeout();
|
||||
// can't change so no need to restore
|
||||
// this.isTestMode = player.isTestMode();
|
||||
// This is meta data and should'nt be restored by rollback
|
||||
// this.userData = player.getUserData();
|
||||
this.library = player.getLibrary().copy();
|
||||
this.sideboard = player.getSideboard().copy();
|
||||
this.hand = player.getHand().copy();
|
||||
|
|
@ -349,10 +362,6 @@ public abstract class PlayerImpl implements Player, Serializable {
|
|||
this.manaPool = player.getManaPool().copy();
|
||||
this.turns = player.getTurns();
|
||||
|
||||
this.left = player.hasLeft();
|
||||
this.quit = player.hasQuit();
|
||||
this.timerTimeout = player.hasTimerTimeout();
|
||||
this.idleTimeout = player.hasIdleTimeout();
|
||||
this.range = player.getRange();
|
||||
this.canGainLife = player.isCanGainLife();
|
||||
this.canLoseLife = player.isCanLoseLife();
|
||||
|
|
@ -361,7 +370,6 @@ public abstract class PlayerImpl implements Player, Serializable {
|
|||
|
||||
this.inRange.clear();
|
||||
this.inRange.addAll(player.getInRange());
|
||||
this.userData = player.getUserData();
|
||||
this.canPayLifeCost = player.canPayLifeCost();
|
||||
this.canPaySacrificeCost = player.canPaySacrificeCost();
|
||||
this.loseByZeroOrLessLife = player.canLoseByZeroOrLessLife();
|
||||
|
|
@ -371,12 +379,9 @@ public abstract class PlayerImpl implements Player, Serializable {
|
|||
this.topCardRevealed = player.isTopCardRevealed();
|
||||
this.playersUnderYourControl.clear();
|
||||
this.playersUnderYourControl.addAll(player.getPlayersUnderYourControl());
|
||||
this.isTestMode = player.isTestMode();
|
||||
this.isGameUnderControl = player.isGameUnderControl();
|
||||
|
||||
this.turnController = player.getTurnControlledBy();
|
||||
this.passed = player.isPassed();
|
||||
this.priorityTimeLeft = player.getPriorityTimeLeft();
|
||||
this.reachedNextTurnAfterLeaving = player.hasReachedNextTurnAfterLeaving();
|
||||
this.castSourceIdWithAlternateMana = player.getCastSourceIdWithAlternateMana();
|
||||
this.castSourceIdManaCosts = player.getCastSourceIdManaCosts();
|
||||
|
|
@ -1187,8 +1192,9 @@ public abstract class PlayerImpl implements Player, Serializable {
|
|||
@Override
|
||||
public LinkedHashMap<UUID, ActivatedAbility> getUseableActivatedAbilities(MageObject object, Zone zone, Game game) {
|
||||
LinkedHashMap<UUID, ActivatedAbility> useable = new LinkedHashMap<>();
|
||||
if (!(object instanceof Permanent) || ((Permanent) object).canUseActivatedAbilities(game)) {
|
||||
for (Ability ability : object.getAbilities()) {
|
||||
boolean canUse = !(object instanceof Permanent) || ((Permanent) object).canUseActivatedAbilities(game);
|
||||
for (Ability ability : object.getAbilities()) {
|
||||
if (canUse || ability.getAbilityType().equals(AbilityType.SPECIAL_ACTION)) {
|
||||
if (ability.getZone().match(zone)) {
|
||||
if (ability instanceof ActivatedAbility) {
|
||||
if (((ActivatedAbility) ability).canActivate(playerId, game)) {
|
||||
|
|
@ -1205,16 +1211,20 @@ public abstract class PlayerImpl implements Player, Serializable {
|
|||
}
|
||||
}
|
||||
}
|
||||
if (zone != Zone.HAND) {
|
||||
if (Zone.GRAVEYARD.equals(zone) && canPlayCardsFromGraveyard()) {
|
||||
for (ActivatedAbility ability : object.getAbilities().getPlayableAbilities(Zone.HAND)) {
|
||||
}
|
||||
if (zone != Zone.HAND) {
|
||||
if (Zone.GRAVEYARD.equals(zone) && canPlayCardsFromGraveyard()) {
|
||||
for (ActivatedAbility ability : object.getAbilities().getPlayableAbilities(Zone.HAND)) {
|
||||
if (canUse || ability.getAbilityType().equals(AbilityType.SPECIAL_ACTION)) {
|
||||
if (ability.canActivate(playerId, game)) {
|
||||
useable.put(ability.getId(), ability);
|
||||
}
|
||||
}
|
||||
}
|
||||
if (zone != Zone.BATTLEFIELD && game.getContinuousEffects().asThough(object.getId(), AsThoughEffectType.PLAY_FROM_NOT_OWN_HAND_ZONE, this.getId(), game)) {
|
||||
for (Ability ability : object.getAbilities()) {
|
||||
}
|
||||
if (zone != Zone.BATTLEFIELD && game.getContinuousEffects().asThough(object.getId(), AsThoughEffectType.PLAY_FROM_NOT_OWN_HAND_ZONE, this.getId(), game)) {
|
||||
for (Ability ability : object.getAbilities()) {
|
||||
if (canUse || ability.getAbilityType().equals(AbilityType.SPECIAL_ACTION)) {
|
||||
ability.setControllerId(this.getId());
|
||||
if (ability instanceof ActivatedAbility && ability.getZone().match(Zone.HAND)
|
||||
&& ((ActivatedAbility) ability).canActivate(playerId, game)) {
|
||||
|
|
@ -1223,8 +1233,9 @@ public abstract class PlayerImpl implements Player, Serializable {
|
|||
}
|
||||
}
|
||||
}
|
||||
getOtherUseableActivatedAbilities(object, zone, game, useable);
|
||||
}
|
||||
getOtherUseableActivatedAbilities(object, zone, game, useable);
|
||||
|
||||
return useable;
|
||||
}
|
||||
|
||||
|
|
@ -1232,49 +1243,52 @@ public abstract class PlayerImpl implements Player, Serializable {
|
|||
private void getOtherUseableActivatedAbilities(MageObject object, Zone zone, Game game, Map<UUID, ActivatedAbility> useable) {
|
||||
Abilities<ActivatedAbility> otherAbilities = game.getState().getActivatedOtherAbilities(object.getId(), zone);
|
||||
if (otherAbilities != null) {
|
||||
boolean canUse = !(object instanceof Permanent) || ((Permanent) object).canUseActivatedAbilities(game);
|
||||
for (ActivatedAbility ability : otherAbilities) {
|
||||
Card card = game.getCard(ability.getSourceId());
|
||||
if (card.isSplitCard() && ability instanceof FlashbackAbility) {
|
||||
FlashbackAbility flashbackAbility;
|
||||
// Left Half
|
||||
if (card.getCardType().contains(CardType.INSTANT)) {
|
||||
flashbackAbility = new FlashbackAbility(((SplitCard) card).getLeftHalfCard().getManaCost(), TimingRule.INSTANT);
|
||||
} else {
|
||||
flashbackAbility = new FlashbackAbility(((SplitCard) card).getLeftHalfCard().getManaCost(), TimingRule.SORCERY);
|
||||
}
|
||||
flashbackAbility.setSourceId(card.getId());
|
||||
flashbackAbility.setControllerId(card.getOwnerId());
|
||||
flashbackAbility.setSpellAbilityType(SpellAbilityType.SPLIT_LEFT);
|
||||
flashbackAbility.setAbilityName(((SplitCard) card).getLeftHalfCard().getName());
|
||||
if (flashbackAbility.canActivate(playerId, game)) {
|
||||
useable.put(flashbackAbility.getId(), flashbackAbility);
|
||||
}
|
||||
// Right Half
|
||||
if (card.getCardType().contains(CardType.INSTANT)) {
|
||||
flashbackAbility = new FlashbackAbility(((SplitCard) card).getRightHalfCard().getManaCost(), TimingRule.INSTANT);
|
||||
} else {
|
||||
flashbackAbility = new FlashbackAbility(((SplitCard) card).getRightHalfCard().getManaCost(), TimingRule.SORCERY);
|
||||
}
|
||||
flashbackAbility.setSourceId(card.getId());
|
||||
flashbackAbility.setControllerId(card.getOwnerId());
|
||||
flashbackAbility.setSpellAbilityType(SpellAbilityType.SPLIT_RIGHT);
|
||||
flashbackAbility.setAbilityName(((SplitCard) card).getRightHalfCard().getName());
|
||||
if (flashbackAbility.canActivate(playerId, game)) {
|
||||
useable.put(flashbackAbility.getId(), flashbackAbility);
|
||||
}
|
||||
if (canUse || ability.getAbilityType().equals(AbilityType.SPECIAL_ACTION)) {
|
||||
Card card = game.getCard(ability.getSourceId());
|
||||
if (card.isSplitCard() && ability instanceof FlashbackAbility) {
|
||||
FlashbackAbility flashbackAbility;
|
||||
// Left Half
|
||||
if (card.getCardType().contains(CardType.INSTANT)) {
|
||||
flashbackAbility = new FlashbackAbility(((SplitCard) card).getLeftHalfCard().getManaCost(), TimingRule.INSTANT);
|
||||
} else {
|
||||
flashbackAbility = new FlashbackAbility(((SplitCard) card).getLeftHalfCard().getManaCost(), TimingRule.SORCERY);
|
||||
}
|
||||
flashbackAbility.setSourceId(card.getId());
|
||||
flashbackAbility.setControllerId(card.getOwnerId());
|
||||
flashbackAbility.setSpellAbilityType(SpellAbilityType.SPLIT_LEFT);
|
||||
flashbackAbility.setAbilityName(((SplitCard) card).getLeftHalfCard().getName());
|
||||
if (flashbackAbility.canActivate(playerId, game)) {
|
||||
useable.put(flashbackAbility.getId(), flashbackAbility);
|
||||
}
|
||||
// Right Half
|
||||
if (card.getCardType().contains(CardType.INSTANT)) {
|
||||
flashbackAbility = new FlashbackAbility(((SplitCard) card).getRightHalfCard().getManaCost(), TimingRule.INSTANT);
|
||||
} else {
|
||||
flashbackAbility = new FlashbackAbility(((SplitCard) card).getRightHalfCard().getManaCost(), TimingRule.SORCERY);
|
||||
}
|
||||
flashbackAbility.setSourceId(card.getId());
|
||||
flashbackAbility.setControllerId(card.getOwnerId());
|
||||
flashbackAbility.setSpellAbilityType(SpellAbilityType.SPLIT_RIGHT);
|
||||
flashbackAbility.setAbilityName(((SplitCard) card).getRightHalfCard().getName());
|
||||
if (flashbackAbility.canActivate(playerId, game)) {
|
||||
useable.put(flashbackAbility.getId(), flashbackAbility);
|
||||
}
|
||||
|
||||
} else {
|
||||
useable.put(ability.getId(), ability);
|
||||
} else {
|
||||
useable.put(ability.getId(), ability);
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
protected LinkedHashMap<UUID, ManaAbility> getUseableManaAbilities(MageObject object, Zone zone, Game game) {
|
||||
LinkedHashMap<UUID, ManaAbility> useable = new LinkedHashMap<>();
|
||||
if (!(object instanceof Permanent) || ((Permanent) object).canUseActivatedAbilities(game)) {
|
||||
for (ManaAbility ability : object.getAbilities().getManaAbilities(zone)) {
|
||||
boolean canUse = !(object instanceof Permanent) || ((Permanent) object).canUseActivatedAbilities(game);
|
||||
for (ManaAbility ability : object.getAbilities().getManaAbilities(zone)) {
|
||||
if (canUse || ability.getAbilityType().equals(AbilityType.SPECIAL_ACTION)) {
|
||||
if (ability.canActivate(playerId, game)) {
|
||||
useable.put(ability.getId(), ability);
|
||||
}
|
||||
|
|
@ -1823,6 +1837,15 @@ public abstract class PlayerImpl implements Player, Serializable {
|
|||
this.passed = this.loses || this.hasLeft();
|
||||
}
|
||||
|
||||
@Override
|
||||
public void resetPlayerPassedActions() {
|
||||
this.passedAllTurns = false;
|
||||
this.passedTurn = false;
|
||||
this.passedUntilEndOfTurn = false;
|
||||
this.passedUntilNextMain = false;
|
||||
this.passedUntilStackResolved = false;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void quit(Game game) {
|
||||
quit = true;
|
||||
|
|
@ -2817,11 +2840,15 @@ public abstract class PlayerImpl implements Player, Serializable {
|
|||
}
|
||||
|
||||
@Override
|
||||
public void revealFaceDownCard(Card card, Game game) {
|
||||
if (game.getContinuousEffects().asThough(card.getId(), AsThoughEffectType.REVEAL_FACE_DOWN, this.getId(), game)) {
|
||||
Cards cards = new CardsImpl(card);
|
||||
this.revealCards(getName(), cards, game);
|
||||
public boolean lookAtFaceDownCard(Card card, Game game) {
|
||||
if (game.getContinuousEffects().asThough(card.getId(), AsThoughEffectType.LOOK_AT_FACE_DOWN, this.getId(), game)) {
|
||||
if (chooseUse(Outcome.Benefit, "Look at that card?", null, game)) {
|
||||
Cards cards = new CardsImpl(card);
|
||||
this.lookAtCards(getName(), cards, game);
|
||||
return true;
|
||||
}
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
@Override
|
||||
|
|
@ -2908,45 +2935,59 @@ public abstract class PlayerImpl implements Player, Serializable {
|
|||
if (cards.isEmpty()) {
|
||||
return true;
|
||||
}
|
||||
game.fireEvent(new ZoneChangeGroupEvent(cards, source == null ? null : source.getSourceId(), this.getId(), fromZone, toZone));
|
||||
Set<Card> successfulMovedCards = new LinkedHashSet<>();
|
||||
switch (toZone) {
|
||||
case EXILED:
|
||||
boolean result = false;
|
||||
for (Card card : cards) {
|
||||
fromZone = game.getState().getZone(card.getId());
|
||||
boolean withName = (fromZone.equals(Zone.BATTLEFIELD) || fromZone.equals(Zone.STACK)) || !card.isFaceDown(game);
|
||||
result |= moveCardToExileWithInfo(card, null, "", source == null ? null : source.getSourceId(), game, fromZone, withName);
|
||||
if (moveCardToExileWithInfo(card, null, "", source == null ? null : source.getSourceId(), game, fromZone, withName)) {
|
||||
successfulMovedCards.add(card);
|
||||
}
|
||||
}
|
||||
return result;
|
||||
break;
|
||||
case GRAVEYARD:
|
||||
return moveCardsToGraveyardWithInfo(cards, source, game, fromZone);
|
||||
successfulMovedCards = moveCardsToGraveyardWithInfo(cards, source, game, fromZone);
|
||||
break;
|
||||
case HAND:
|
||||
result = false;
|
||||
for (Card card : cards) {
|
||||
fromZone = game.getState().getZone(card.getId());
|
||||
boolean hideCard = fromZone.equals(Zone.LIBRARY)
|
||||
|| (card.isFaceDown(game) && !fromZone.equals(Zone.STACK) && !fromZone.equals(Zone.BATTLEFIELD));
|
||||
result |= moveCardToHandWithInfo(card, source == null ? null : source.getSourceId(), game, !hideCard);
|
||||
if (moveCardToHandWithInfo(card, source == null ? null : source.getSourceId(), game, !hideCard)) {
|
||||
successfulMovedCards.add(card);
|
||||
}
|
||||
}
|
||||
return result;
|
||||
break;
|
||||
case BATTLEFIELD:
|
||||
result = false;
|
||||
for (Card card : cards) {
|
||||
fromZone = game.getState().getZone(card.getId());
|
||||
result |= putOntoBattlefieldWithInfo(card, game, fromZone, source == null ? null : source.getSourceId(), false, !card.isFaceDown(game));
|
||||
if (putOntoBattlefieldWithInfo(card, game, fromZone, source == null ? null : source.getSourceId(), false, !card.isFaceDown(game))) {
|
||||
successfulMovedCards.add(card);
|
||||
}
|
||||
}
|
||||
return result;
|
||||
break;
|
||||
case LIBRARY:
|
||||
result = false;
|
||||
for (Card card : cards) {
|
||||
fromZone = game.getState().getZone(card.getId());
|
||||
boolean withName = fromZone.equals(Zone.BATTLEFIELD) || !card.isFaceDown(game);
|
||||
result |= moveCardToLibraryWithInfo(card, source == null ? null : source.getSourceId(), game, fromZone, true, withName);
|
||||
if (moveCardToLibraryWithInfo(card, source == null ? null : source.getSourceId(), game, fromZone, true, withName)) {
|
||||
successfulMovedCards.add(card);
|
||||
}
|
||||
}
|
||||
return result;
|
||||
break;
|
||||
default:
|
||||
throw new UnsupportedOperationException("to Zone not supported yet");
|
||||
}
|
||||
game.fireEvent(new ZoneChangeGroupEvent(successfulMovedCards, source == null ? null : source.getSourceId(), this.getId(), fromZone, toZone));
|
||||
return successfulMovedCards.size() > 0;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean moveCardsToExile(Card card, Ability source, Game game, boolean withName, UUID exileId, String exileZoneName) {
|
||||
Set<Card> cards = new HashSet<>();
|
||||
cards.add(card);
|
||||
return moveCardsToExile(cards, source, game, withName, exileId, exileZoneName);
|
||||
}
|
||||
|
||||
@Override
|
||||
|
|
@ -2992,9 +3033,9 @@ public abstract class PlayerImpl implements Player, Serializable {
|
|||
}
|
||||
|
||||
@Override
|
||||
public boolean moveCardsToGraveyardWithInfo(Set<Card> allCards, Ability source, Game game, Zone fromZone) {
|
||||
boolean result = true;
|
||||
public Set<Card> moveCardsToGraveyardWithInfo(Set<Card> allCards, Ability source, Game game, Zone fromZone) {
|
||||
UUID sourceId = source == null ? null : source.getSourceId();
|
||||
Set<Card> movedCards = new LinkedHashSet<>();
|
||||
while (!allCards.isEmpty()) {
|
||||
// identify cards from one owner
|
||||
Cards cards = new CardsImpl();
|
||||
|
|
@ -3034,21 +3075,28 @@ public abstract class PlayerImpl implements Player, Serializable {
|
|||
cards.remove(targetObjectId);
|
||||
if (card != null) {
|
||||
fromZone = game.getState().getZone(card.getId());
|
||||
result &= choosingPlayer.moveCardToGraveyardWithInfo(card, sourceId, game, fromZone);
|
||||
if (choosingPlayer.moveCardToGraveyardWithInfo(card, sourceId, game, fromZone)) {
|
||||
movedCards.add(card);
|
||||
}
|
||||
}
|
||||
target.clearChosen();
|
||||
}
|
||||
if (cards.size() == 1) {
|
||||
result &= choosingPlayer.moveCardToGraveyardWithInfo(cards.getCards(game).iterator().next(), sourceId, game, fromZone);
|
||||
Card card = cards.getCards(game).iterator().next();
|
||||
if (card != null && choosingPlayer.moveCardToGraveyardWithInfo(card, sourceId, game, fromZone)) {
|
||||
movedCards.add(card);
|
||||
}
|
||||
}
|
||||
} else {
|
||||
for (Card card : cards.getCards(game)) {
|
||||
result &= choosingPlayer.moveCardToGraveyardWithInfo(card, sourceId, game, fromZone);
|
||||
if (choosingPlayer.moveCardToGraveyardWithInfo(card, sourceId, game, fromZone)) {
|
||||
movedCards.add(card);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
return result;
|
||||
return movedCards;
|
||||
}
|
||||
|
||||
@Override
|
||||
|
|
|
|||
|
|
@ -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.util;
|
||||
|
||||
import java.util.Arrays;
|
||||
|
|
@ -58,7 +57,6 @@ import mage.game.permanent.token.Token;
|
|||
import mage.game.stack.Spell;
|
||||
import mage.util.functions.CopyTokenFunction;
|
||||
|
||||
|
||||
/**
|
||||
* @author nantuko
|
||||
*/
|
||||
|
|
@ -71,16 +69,16 @@ public class CardUtil {
|
|||
private static final String regexWhite = ".*\\x7b.{0,2}W.{0,2}\\x7d.*";
|
||||
|
||||
private static final String SOURCE_EXILE_ZONE_TEXT = "SourceExileZone";
|
||||
|
||||
static String numberStrings[] = { "zero", "one", "two", "three", "four", "five", "six", "seven", "eight", "nine",
|
||||
"ten", "eleven", "twelve", "thirteen", "fourteen", "fifteen", "sixteen", "seventeen", "eighteen", "ninteen", "twenty"};
|
||||
|
||||
public static final String[] NON_CHANGELING_SUBTYPES_VALUES = new String[] { "Mountain", "Forest", "Plains", "Swamp", "Island",
|
||||
"Aura", "Curse", "Shrine",
|
||||
"Equipment", "Fortification", "Contraption",
|
||||
"Trap", "Arcane"};
|
||||
public static final Set<String> NON_CREATURE_SUBTYPES = new HashSet<>(Arrays.asList(NON_CHANGELING_SUBTYPES_VALUES));
|
||||
|
||||
static String numberStrings[] = {"zero", "one", "two", "three", "four", "five", "six", "seven", "eight", "nine",
|
||||
"ten", "eleven", "twelve", "thirteen", "fourteen", "fifteen", "sixteen", "seventeen", "eighteen", "ninteen", "twenty"};
|
||||
|
||||
public static final String[] NON_CHANGELING_SUBTYPES_VALUES = new String[]{"Mountain", "Forest", "Plains", "Swamp", "Island",
|
||||
"Aura", "Curse", "Shrine",
|
||||
"Equipment", "Fortification", "Contraption",
|
||||
"Trap", "Arcane"};
|
||||
public static final Set<String> NON_CREATURE_SUBTYPES = new HashSet<>(Arrays.asList(NON_CHANGELING_SUBTYPES_VALUES));
|
||||
|
||||
/**
|
||||
* Checks whether two cards share card types.
|
||||
*
|
||||
|
|
@ -102,6 +100,7 @@ public class CardUtil {
|
|||
|
||||
return false;
|
||||
}
|
||||
|
||||
/**
|
||||
* Checks whether two cards share card subtypes.
|
||||
*
|
||||
|
|
@ -116,10 +115,10 @@ public class CardUtil {
|
|||
}
|
||||
|
||||
if (card1.getCardType().contains(CardType.CREATURE) && card2.getCardType().contains(CardType.CREATURE)) {
|
||||
if (card1.getAbilities().contains(ChangelingAbility.getInstance()) ||
|
||||
card1.getSubtype().contains(ChangelingAbility.ALL_CREATURE_TYPE) ||
|
||||
card2.getAbilities().contains(ChangelingAbility.getInstance()) ||
|
||||
card2.getSubtype().contains(ChangelingAbility.ALL_CREATURE_TYPE)) {
|
||||
if (card1.getAbilities().contains(ChangelingAbility.getInstance())
|
||||
|| card1.getSubtype().contains(ChangelingAbility.ALL_CREATURE_TYPE)
|
||||
|| card2.getAbilities().contains(ChangelingAbility.getInstance())
|
||||
|| card2.getSubtype().contains(ChangelingAbility.ALL_CREATURE_TYPE)) {
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
|
@ -164,7 +163,7 @@ public class CardUtil {
|
|||
CardUtil.adjustAbilityCost((Ability) spellAbility, reduceCount);
|
||||
adjustAlternativeCosts(spellAbility, reduceCount);
|
||||
}
|
||||
|
||||
|
||||
public static ManaCosts<ManaCost> increaseCost(ManaCosts<ManaCost> manaCosts, int increaseCount) {
|
||||
return adjustCost(manaCosts, -increaseCount);
|
||||
}
|
||||
|
|
@ -207,8 +206,6 @@ public class CardUtil {
|
|||
}
|
||||
}
|
||||
|
||||
|
||||
|
||||
/**
|
||||
* Adjusts ability cost to be paid.
|
||||
*
|
||||
|
|
@ -220,10 +217,10 @@ public class CardUtil {
|
|||
ability.getManaCostsToPay().clear();
|
||||
ability.getManaCostsToPay().addAll(adjustedCost);
|
||||
}
|
||||
|
||||
|
||||
private static ManaCosts<ManaCost> adjustCost(ManaCosts<ManaCost> manaCosts, int reduceCount) {
|
||||
int restToReduce = reduceCount;
|
||||
ManaCosts<ManaCost> adjustedCost = new ManaCostsImpl<>();
|
||||
ManaCosts<ManaCost> adjustedCost = new ManaCostsImpl<>();
|
||||
boolean updated = false;
|
||||
for (ManaCost manaCost : manaCosts) {
|
||||
Mana mana = manaCost.getOptions().get(0);
|
||||
|
|
@ -251,18 +248,18 @@ public class CardUtil {
|
|||
|
||||
public static ManaCosts<ManaCost> removeVariableManaCost(ManaCosts<ManaCost> manaCosts) {
|
||||
ManaCosts<ManaCost> adjustedCost = new ManaCostsImpl<>();
|
||||
for (ManaCost manaCost: manaCosts) {
|
||||
for (ManaCost manaCost : manaCosts) {
|
||||
if (!(manaCost instanceof VariableManaCost)) {
|
||||
adjustedCost.add(manaCost);
|
||||
}
|
||||
}
|
||||
return adjustedCost;
|
||||
}
|
||||
|
||||
|
||||
public static void reduceCost(SpellAbility spellAbility, ManaCosts<ManaCost> manaCostsToReduce) {
|
||||
adjustCost(spellAbility, manaCostsToReduce, true);
|
||||
}
|
||||
|
||||
|
||||
public static void increaseCost(SpellAbility spellAbility, ManaCosts<ManaCost> manaCostsToIncrease) {
|
||||
ManaCosts<ManaCost> increasedCost = spellAbility.getManaCostsToPay().copy();
|
||||
|
||||
|
|
@ -279,19 +276,19 @@ public class CardUtil {
|
|||
*
|
||||
* @param spellAbility
|
||||
* @param manaCostsToReduce costs to reduce
|
||||
* @param convertToGeneric colored mana does reduce generic mana if no appropriate colored mana is in the costs included
|
||||
* @param convertToGeneric colored mana does reduce generic mana if no
|
||||
* appropriate colored mana is in the costs included
|
||||
*/
|
||||
public static void adjustCost(SpellAbility spellAbility, ManaCosts<ManaCost> manaCostsToReduce, boolean convertToGeneric) {
|
||||
ManaCosts<ManaCost> previousCost = spellAbility.getManaCostsToPay();
|
||||
ManaCosts<ManaCost> adjustedCost = new ManaCostsImpl<>();
|
||||
ManaCosts<ManaCost> adjustedCost = new ManaCostsImpl<>();
|
||||
// save X value (e.g. convoke ability)
|
||||
for (VariableCost vCost: previousCost.getVariableCosts()) {
|
||||
for (VariableCost vCost : previousCost.getVariableCosts()) {
|
||||
if (vCost instanceof VariableManaCost) {
|
||||
adjustedCost.add((VariableManaCost) vCost);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
Mana reduceMana = new Mana();
|
||||
for (ManaCost manaCost : manaCostsToReduce) {
|
||||
reduceMana.add(manaCost.getMana());
|
||||
|
|
@ -304,46 +301,46 @@ public class CardUtil {
|
|||
}
|
||||
if (mana.getBlack() > 0 && reduceMana.getBlack() > 0) {
|
||||
if (reduceMana.getBlack() > mana.getBlack()) {
|
||||
reduceMana.setBlack(reduceMana.getBlack()-mana.getBlack());
|
||||
reduceMana.setBlack(reduceMana.getBlack() - mana.getBlack());
|
||||
mana.setBlack(0);
|
||||
} else {
|
||||
mana.setBlack(mana.getBlack()-reduceMana.getBlack());
|
||||
mana.setBlack(mana.getBlack() - reduceMana.getBlack());
|
||||
reduceMana.setBlack(0);
|
||||
}
|
||||
}
|
||||
if (mana.getRed() > 0 && reduceMana.getRed() > 0) {
|
||||
if (reduceMana.getRed() > mana.getRed()) {
|
||||
reduceMana.setRed(reduceMana.getRed()-mana.getRed());
|
||||
reduceMana.setRed(reduceMana.getRed() - mana.getRed());
|
||||
mana.setRed(0);
|
||||
} else {
|
||||
mana.setRed(mana.getRed()-reduceMana.getRed());
|
||||
mana.setRed(mana.getRed() - reduceMana.getRed());
|
||||
reduceMana.setRed(0);
|
||||
}
|
||||
}
|
||||
if (mana.getBlue() > 0 && reduceMana.getBlue() > 0) {
|
||||
if (reduceMana.getBlue() > mana.getBlue()) {
|
||||
reduceMana.setBlue(reduceMana.getBlue()-mana.getBlue());
|
||||
reduceMana.setBlue(reduceMana.getBlue() - mana.getBlue());
|
||||
mana.setBlue(0);
|
||||
} else {
|
||||
mana.setBlue(mana.getBlue()-reduceMana.getBlue());
|
||||
mana.setBlue(mana.getBlue() - reduceMana.getBlue());
|
||||
reduceMana.setBlue(0);
|
||||
}
|
||||
}
|
||||
if (mana.getGreen() > 0 && reduceMana.getGreen() > 0) {
|
||||
if (reduceMana.getGreen() > mana.getGreen()) {
|
||||
reduceMana.setGreen(reduceMana.getGreen()-mana.getGreen());
|
||||
reduceMana.setGreen(reduceMana.getGreen() - mana.getGreen());
|
||||
mana.setGreen(0);
|
||||
} else {
|
||||
mana.setGreen(mana.getGreen()-reduceMana.getGreen());
|
||||
mana.setGreen(mana.getGreen() - reduceMana.getGreen());
|
||||
reduceMana.setGreen(0);
|
||||
}
|
||||
}
|
||||
if (mana.getWhite() > 0 && reduceMana.getWhite() > 0) {
|
||||
if (reduceMana.getWhite() > mana.getWhite()) {
|
||||
reduceMana.setWhite(reduceMana.getWhite()-mana.getWhite());
|
||||
reduceMana.setWhite(reduceMana.getWhite() - mana.getWhite());
|
||||
mana.setWhite(0);
|
||||
} else {
|
||||
mana.setWhite(mana.getWhite()-reduceMana.getWhite());
|
||||
mana.setWhite(mana.getWhite() - reduceMana.getWhite());
|
||||
reduceMana.setWhite(0);
|
||||
}
|
||||
}
|
||||
|
|
@ -384,10 +381,10 @@ public class CardUtil {
|
|||
spellAbility.getManaCostsToPay().clear();
|
||||
spellAbility.getManaCostsToPay().addAll(adjustedCost);
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Returns function that copies params\abilities from one card to {@link Token}.
|
||||
* Returns function that copies params\abilities from one card to
|
||||
* {@link Token}.
|
||||
*
|
||||
* @param target
|
||||
* @return
|
||||
|
|
@ -396,7 +393,7 @@ public class CardUtil {
|
|||
return new CopyTokenFunction(target);
|
||||
}
|
||||
|
||||
public static boolean isPermanentCard ( Card card ) {
|
||||
public static boolean isPermanentCard(Card card) {
|
||||
boolean permanent = false;
|
||||
|
||||
permanent |= card.getCardType().contains(CardType.ARTIFACT);
|
||||
|
|
@ -409,23 +406,24 @@ public class CardUtil {
|
|||
}
|
||||
|
||||
/**
|
||||
* Converts an integer number to string
|
||||
* Numbers > 20 will be returned as digits
|
||||
* Converts an integer number to string Numbers > 20 will be returned as
|
||||
* digits
|
||||
*
|
||||
* @param number
|
||||
* @return
|
||||
* @return
|
||||
*/
|
||||
public static String numberToText(int number) {
|
||||
return numberToText(number, "one");
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Converts an integer number to string like "one", "two", "three", ...
|
||||
* Numbers > 20 will be returned as digits
|
||||
*
|
||||
*
|
||||
* @param number number to convert to text
|
||||
* @param forOne if the number is 1, this string will be returnedinstead of "one".
|
||||
* @return
|
||||
* @param forOne if the number is 1, this string will be returnedinstead of
|
||||
* "one".
|
||||
* @return
|
||||
*/
|
||||
public static String numberToText(int number, String forOne) {
|
||||
if (number == 1 && forOne != null) {
|
||||
|
|
@ -458,8 +456,8 @@ public class CardUtil {
|
|||
}
|
||||
|
||||
public static boolean checkNumeric(String s) {
|
||||
for(int i = 0; i < s.length(); i++) {
|
||||
if(!Character.isDigit(s.charAt(i))) {
|
||||
for (int i = 0; i < s.length(); i++) {
|
||||
if (!Character.isDigit(s.charAt(i))) {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
|
@ -476,7 +474,7 @@ public class CardUtil {
|
|||
public static UUID getCardExileZoneId(Game game, Ability source) {
|
||||
return getCardExileZoneId(game, source.getSourceId());
|
||||
}
|
||||
|
||||
|
||||
public static UUID getCardExileZoneId(Game game, UUID sourceId) {
|
||||
return getCardExileZoneId(game, sourceId, false);
|
||||
}
|
||||
|
|
@ -484,7 +482,7 @@ public class CardUtil {
|
|||
public static UUID getCardExileZoneId(Game game, UUID sourceId, boolean previous) {
|
||||
return getExileZoneId(getCardZoneString(SOURCE_EXILE_ZONE_TEXT, sourceId, game, previous), game);
|
||||
}
|
||||
|
||||
|
||||
public static UUID getObjectExileZoneId(Game game, MageObject mageObject) {
|
||||
return getObjectExileZoneId(game, mageObject, false);
|
||||
}
|
||||
|
|
@ -499,26 +497,27 @@ public class CardUtil {
|
|||
if (zoneChangeCounter > 0 && previous) {
|
||||
zoneChangeCounter--;
|
||||
}
|
||||
return getExileZoneId(getObjectZoneString(SOURCE_EXILE_ZONE_TEXT,mageObject.getId(), game, zoneChangeCounter, false), game);
|
||||
return getExileZoneId(getObjectZoneString(SOURCE_EXILE_ZONE_TEXT, mageObject.getId(), game, zoneChangeCounter, false), game);
|
||||
}
|
||||
|
||||
|
||||
public static UUID getExileZoneId(Game game, UUID objectId, int zoneChangeCounter) {
|
||||
return getExileZoneId(getObjectZoneString(SOURCE_EXILE_ZONE_TEXT,objectId, game, zoneChangeCounter, false), game);
|
||||
return getExileZoneId(getObjectZoneString(SOURCE_EXILE_ZONE_TEXT, objectId, game, zoneChangeCounter, false), game);
|
||||
}
|
||||
|
||||
|
||||
public static UUID getExileZoneId(String key, Game game) {
|
||||
UUID exileId = (UUID) game.getState().getValue(key);
|
||||
if (exileId == null) {
|
||||
exileId = UUID.randomUUID();
|
||||
game.getState().setValue(key, exileId);
|
||||
}
|
||||
return exileId;
|
||||
return exileId;
|
||||
}
|
||||
|
||||
/**
|
||||
* Creates a string from text + cardId and the zoneChangeCounter of the card (from cardId).
|
||||
* This string can be used to save and get values that must be specific to a permanent instance.
|
||||
* So they won't match, if a permanent was e.g. exiled and came back immediately.
|
||||
* Creates a string from text + cardId and the zoneChangeCounter of the card
|
||||
* (from cardId). This string can be used to save and get values that must
|
||||
* be specific to a permanent instance. So they won't match, if a permanent
|
||||
* was e.g. exiled and came back immediately.
|
||||
*
|
||||
* @param text short value to describe the value
|
||||
* @param cardId id of the card
|
||||
|
|
@ -529,15 +528,15 @@ public class CardUtil {
|
|||
return getCardZoneString(text, cardId, game, false);
|
||||
}
|
||||
|
||||
public static String getCardZoneString(String text, UUID cardId, Game game, boolean previous) {
|
||||
int zoneChangeCounter= 0;
|
||||
public static String getCardZoneString(String text, UUID cardId, Game game, boolean previous) {
|
||||
int zoneChangeCounter = 0;
|
||||
Card card = game.getCard(cardId); // if called for a token, the id is enough
|
||||
if (card != null) {
|
||||
zoneChangeCounter = card.getZoneChangeCounter(game);
|
||||
}
|
||||
return getObjectZoneString(text,cardId, game, zoneChangeCounter, previous);
|
||||
return getObjectZoneString(text, cardId, game, zoneChangeCounter, previous);
|
||||
}
|
||||
|
||||
|
||||
public static String getObjectZoneString(String text, MageObject mageObject, Game game) {
|
||||
int zoneChangeCounter = 0;
|
||||
if (mageObject instanceof Permanent) {
|
||||
|
|
@ -547,22 +546,23 @@ public class CardUtil {
|
|||
}
|
||||
return getObjectZoneString(text, mageObject.getId(), game, zoneChangeCounter, false);
|
||||
}
|
||||
|
||||
|
||||
public static String getObjectZoneString(String text, UUID objectId, Game game, int zoneChangeCounter, boolean previous) {
|
||||
StringBuilder uniqueString = new StringBuilder();
|
||||
if (text != null) {
|
||||
uniqueString.append(text);
|
||||
}
|
||||
uniqueString.append(objectId);
|
||||
uniqueString.append(previous ? zoneChangeCounter - 1: zoneChangeCounter);
|
||||
return uniqueString.toString();
|
||||
uniqueString.append(previous ? zoneChangeCounter - 1 : zoneChangeCounter);
|
||||
return uniqueString.toString();
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Returns if the ability is used to check which cards
|
||||
* are playable on hand. (Issue #457)
|
||||
* Returns if the ability is used to check which cards are playable on hand.
|
||||
* (Issue #457)
|
||||
*
|
||||
* @param ability - ability to check
|
||||
* @return
|
||||
* @return
|
||||
*/
|
||||
public static boolean isCheckPlayableMode(Ability ability) {
|
||||
if (ability instanceof ActivatedAbility) {
|
||||
|
|
@ -572,8 +572,8 @@ public class CardUtil {
|
|||
}
|
||||
|
||||
/**
|
||||
* Adds tags to mark the additional info of a card
|
||||
* (e.g. blue font color)
|
||||
* Adds tags to mark the additional info of a card (e.g. blue font color)
|
||||
*
|
||||
* @param text text body
|
||||
* @return
|
||||
*/
|
||||
|
|
@ -584,7 +584,7 @@ public class CardUtil {
|
|||
public static boolean convertedManaCostsIsEqual(MageObject object1, MageObject object2) {
|
||||
Set<Integer> cmcObject1 = getCMC(object1);
|
||||
Set<Integer> cmcObject2 = getCMC(object2);
|
||||
for (Integer integer :cmcObject1) {
|
||||
for (Integer integer : cmcObject1) {
|
||||
if (cmcObject2.contains(integer)) {
|
||||
return true;
|
||||
}
|
||||
|
|
@ -595,7 +595,7 @@ public class CardUtil {
|
|||
public static Set<Integer> getCMC(MageObject object) {
|
||||
Set<Integer> cmcObject = new HashSet<>();
|
||||
if (object instanceof Spell) {
|
||||
cmcObject.add(((Spell)object).getConvertedManaCost());
|
||||
cmcObject.add(((Spell) object).getConvertedManaCost());
|
||||
} else if (object instanceof Card) {
|
||||
Card card = (Card) object;
|
||||
if (card instanceof SplitCard) {
|
||||
|
|
@ -605,16 +605,16 @@ public class CardUtil {
|
|||
} else {
|
||||
cmcObject.add(card.getManaCost().convertedManaCost());
|
||||
}
|
||||
}
|
||||
}
|
||||
return cmcObject;
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Gets the colors that are in the casting cost but also in the rules text
|
||||
* Gets the colors that are in the casting cost but also in the rules text
|
||||
* as far as not included in reminder text.
|
||||
*
|
||||
*
|
||||
* @param card
|
||||
* @return
|
||||
* @return
|
||||
*/
|
||||
public static FilterMana getColorIdentity(Card card) {
|
||||
FilterMana mana = new FilterMana();
|
||||
|
|
@ -644,8 +644,17 @@ public class CardUtil {
|
|||
}
|
||||
return mana;
|
||||
}
|
||||
|
||||
|
||||
public static boolean isNonCreatureSubtype(String subtype) {
|
||||
return NON_CREATURE_SUBTYPES.contains(subtype);
|
||||
}
|
||||
|
||||
public static boolean cardCanBePlayedNow(Card card, UUID playerId, Game game) {
|
||||
if (card.getCardType().contains(CardType.LAND)) {
|
||||
return game.canPlaySorcery(playerId) && game.getPlayer(playerId).canPlayLand();
|
||||
} else {
|
||||
return card.getSpellAbility() != null && card.getSpellAbility().spellCanBeActivatedRegularlyNow(playerId, game);
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue