mirror of
https://github.com/magefree/mage.git
synced 2025-12-24 20:41:58 -08:00
Merge remote-tracking branch 'upstream/master'
# Please enter a commit message to explain why this merge is necessary, # especially if it merges an updated upstream into a topic branch. # # Lines starting with '#' will be ignored, and an empty message aborts # the commit.
This commit is contained in:
parent
f31bfa829e
commit
86107de54e
620 changed files with 5627 additions and 2239 deletions
|
|
@ -8,7 +8,7 @@ import mage.abilities.condition.Condition;
|
|||
import mage.abilities.condition.InvertCondition;
|
||||
import mage.abilities.condition.common.PermanentsOnTheBattlefieldCondition;
|
||||
import mage.abilities.decorator.ConditionalOneShotEffect;
|
||||
import mage.abilities.decorator.ConditionalTriggeredAbility;
|
||||
import mage.abilities.decorator.ConditionalInterveningIfTriggeredAbility;
|
||||
import mage.abilities.effects.OneShotEffect;
|
||||
import mage.constants.TargetController;
|
||||
import mage.filter.FilterPermanent;
|
||||
|
|
@ -19,7 +19,7 @@ import mage.filter.predicate.mageobject.ColorPredicate;
|
|||
*
|
||||
* @author TheElk801
|
||||
*/
|
||||
public class SanctuaryTriggeredAbility extends ConditionalTriggeredAbility {
|
||||
public class SanctuaryInterveningIfTriggeredAbility extends ConditionalInterveningIfTriggeredAbility {
|
||||
|
||||
private static Condition makeOrCondition(ObjectColor color1, ObjectColor color2) {
|
||||
FilterPermanent filter = new FilterPermanent();
|
||||
|
|
@ -48,7 +48,7 @@ public class SanctuaryTriggeredAbility extends ConditionalTriggeredAbility {
|
|||
return ability;
|
||||
}
|
||||
|
||||
public SanctuaryTriggeredAbility(OneShotEffect effect1, OneShotEffect effect2, ObjectColor color1, ObjectColor color2, String text) {
|
||||
public SanctuaryInterveningIfTriggeredAbility(OneShotEffect effect1, OneShotEffect effect2, ObjectColor color1, ObjectColor color2, String text) {
|
||||
super(makeTrigger(effect1, effect2, color1, color2), makeOrCondition(color1, color2), text);
|
||||
}
|
||||
}
|
||||
|
|
@ -230,7 +230,7 @@ public class AlternativeCostSourceAbility extends StaticAbility implements Alter
|
|||
if (condition == null || alternateCosts.size() == 1) {
|
||||
sb.append(" rather than pay this spell's mana cost");
|
||||
} else if (alternateCosts.isEmpty()) {
|
||||
sb.append("cast {this} without paying its mana cost");
|
||||
sb.append("cast this spell without paying its mana cost");
|
||||
}
|
||||
sb.append('.');
|
||||
if (numberCosts == 1 && remarkText != null) {
|
||||
|
|
|
|||
|
|
@ -207,7 +207,9 @@ public abstract class ManaCostImpl extends CostImpl implements ManaCost {
|
|||
return true;
|
||||
}
|
||||
Player player = game.getPlayer(controllerId);
|
||||
assignPayment(game, ability, player.getManaPool(), costToPay);
|
||||
if (!player.getManaPool().isForcedToPay()) {
|
||||
assignPayment(game, ability, player.getManaPool(), costToPay);
|
||||
}
|
||||
game.getState().getSpecialActions().removeManaActions();
|
||||
while (!isPaid()) {
|
||||
ManaCost unpaid = this.getUnpaid();
|
||||
|
|
|
|||
|
|
@ -117,7 +117,9 @@ public class ManaCostsImpl<T extends ManaCost> extends ArrayList<T> implements M
|
|||
}
|
||||
|
||||
Player player = game.getPlayer(controllerId);
|
||||
assignPayment(game, ability, player.getManaPool(), this);
|
||||
if (!player.getManaPool().isForcedToPay()) {
|
||||
assignPayment(game, ability, player.getManaPool(), this);
|
||||
}
|
||||
game.getState().getSpecialActions().removeManaActions();
|
||||
while (!isPaid()) {
|
||||
ManaCost unpaid = this.getUnpaid();
|
||||
|
|
@ -235,10 +237,15 @@ public class ManaCostsImpl<T extends ManaCost> extends ArrayList<T> implements M
|
|||
|
||||
@Override
|
||||
public void assignPayment(Game game, Ability ability, ManaPool pool, Cost costToPay) {
|
||||
if (!pool.isAutoPayment() && pool.getUnlockedManaType() == null) {
|
||||
boolean wasUnlockedManaType = (pool.getUnlockedManaType() != null);
|
||||
if (!pool.isAutoPayment() && !wasUnlockedManaType) {
|
||||
// if auto payment is inactive and no mana type was clicked manually - do nothing
|
||||
return;
|
||||
}
|
||||
ManaCosts referenceCosts = null;
|
||||
if (pool.isForcedToPay()) {
|
||||
referenceCosts = this.copy();
|
||||
}
|
||||
// attempt to pay colorless costs (not generic) mana costs first
|
||||
for (ManaCost cost : this) {
|
||||
if (!cost.isPaid() && cost instanceof ColorlessManaCost) {
|
||||
|
|
@ -318,6 +325,31 @@ public class ManaCostsImpl<T extends ManaCost> extends ArrayList<T> implements M
|
|||
}
|
||||
// stop using mana of the clicked mana type
|
||||
pool.lockManaType();
|
||||
if (!wasUnlockedManaType) {
|
||||
handleForcedToPayOnlyForCurrentPayment(game, pool, referenceCosts);
|
||||
}
|
||||
}
|
||||
|
||||
private void handleForcedToPayOnlyForCurrentPayment(Game game, ManaPool pool, ManaCosts referenceCosts) {
|
||||
// for Word of Command
|
||||
if (pool.isForcedToPay()) {
|
||||
if (referenceCosts != null && this.getText().equals(referenceCosts.getText())) {
|
||||
UUID playerId = pool.getPlayerId();
|
||||
Player player = game.getPlayer(playerId);
|
||||
if (player != null) {
|
||||
game.undo(playerId);
|
||||
this.clearPaid();
|
||||
this.setX(referenceCosts.getX());
|
||||
player.getManaPool().restoreMana(pool.getPoolBookmark());
|
||||
game.bookmarkState();
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public void forceManaRollback(Game game, ManaPool pool) {
|
||||
// for Word of Command
|
||||
handleForcedToPayOnlyForCurrentPayment(game, pool, this);
|
||||
}
|
||||
|
||||
@Override
|
||||
|
|
|
|||
|
|
@ -0,0 +1,104 @@
|
|||
package mage.abilities.decorator;
|
||||
|
||||
import mage.abilities.Modes;
|
||||
import mage.abilities.TriggeredAbility;
|
||||
import mage.abilities.TriggeredAbilityImpl;
|
||||
import mage.abilities.condition.Condition;
|
||||
import mage.abilities.effects.Effect;
|
||||
import mage.abilities.effects.Effects;
|
||||
import mage.constants.EffectType;
|
||||
import mage.game.Game;
|
||||
import mage.game.events.GameEvent;
|
||||
|
||||
/**
|
||||
* Adds condition to {@link mage.abilities.effects.ContinuousEffect}. Acts as
|
||||
* decorator.
|
||||
*
|
||||
* @author nantuko
|
||||
*/
|
||||
public class ConditionalInterveningIfTriggeredAbility extends TriggeredAbilityImpl {
|
||||
|
||||
protected TriggeredAbility ability;
|
||||
protected Condition condition;
|
||||
protected String abilityText;
|
||||
|
||||
/**
|
||||
* Triggered ability with a condition. Set the optionality for the trigger
|
||||
* ability itself.
|
||||
*
|
||||
* @param ability
|
||||
* @param condition
|
||||
* @param text explicit rule text for the ability, if null or empty, the
|
||||
* rule text generated by the triggered ability itself is used.
|
||||
*/
|
||||
public ConditionalInterveningIfTriggeredAbility(TriggeredAbility ability, Condition condition, String text) {
|
||||
super(ability.getZone(), null);
|
||||
this.ability = ability;
|
||||
this.modes = ability.getModes();
|
||||
this.condition = condition;
|
||||
this.abilityText = text;
|
||||
}
|
||||
|
||||
public ConditionalInterveningIfTriggeredAbility(final ConditionalInterveningIfTriggeredAbility triggered) {
|
||||
super(triggered);
|
||||
this.ability = triggered.ability.copy();
|
||||
this.condition = triggered.condition;
|
||||
this.abilityText = triggered.abilityText;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean checkInterveningIfClause(Game game) {
|
||||
return condition.apply(game, this);
|
||||
}
|
||||
|
||||
@Override
|
||||
public ConditionalInterveningIfTriggeredAbility copy() {
|
||||
return new ConditionalInterveningIfTriggeredAbility(this);
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean checkEventType(GameEvent event, Game game) {
|
||||
return ability.checkEventType(event, game);
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean checkTrigger(GameEvent event, Game game) {
|
||||
ability.setSourceId(this.getSourceId());
|
||||
ability.setControllerId(this.getControllerId());
|
||||
return ability.checkTrigger(event, game);
|
||||
}
|
||||
|
||||
@Override
|
||||
public String getRule() {
|
||||
if (abilityText == null || abilityText.isEmpty()) {
|
||||
return ability.getRule();
|
||||
}
|
||||
return abilityText;
|
||||
}
|
||||
|
||||
@Override
|
||||
public Effects getEffects() {
|
||||
return ability.getEffects();
|
||||
}
|
||||
|
||||
@Override
|
||||
public void addEffect(Effect effect) {
|
||||
ability.addEffect(effect);
|
||||
}
|
||||
|
||||
@Override
|
||||
public Modes getModes() {
|
||||
return ability.getModes();
|
||||
}
|
||||
|
||||
@Override
|
||||
public Effects getEffects(Game game, EffectType effectType) {
|
||||
return ability.getEffects(game, effectType);
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean isOptional() {
|
||||
return ability.isOptional();
|
||||
}
|
||||
|
||||
}
|
||||
|
|
@ -14,7 +14,7 @@ import mage.game.events.GameEvent;
|
|||
* Adds condition to {@link mage.abilities.effects.ContinuousEffect}. Acts as
|
||||
* decorator.
|
||||
*
|
||||
* @author nantuko
|
||||
* @author noahg
|
||||
*/
|
||||
public class ConditionalTriggeredAbility extends TriggeredAbilityImpl {
|
||||
|
||||
|
|
@ -46,11 +46,6 @@ public class ConditionalTriggeredAbility extends TriggeredAbilityImpl {
|
|||
this.abilityText = triggered.abilityText;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean checkInterveningIfClause(Game game) {
|
||||
return condition.apply(game, this);
|
||||
}
|
||||
|
||||
@Override
|
||||
public ConditionalTriggeredAbility copy() {
|
||||
return new ConditionalTriggeredAbility(this);
|
||||
|
|
@ -65,7 +60,7 @@ public class ConditionalTriggeredAbility extends TriggeredAbilityImpl {
|
|||
public boolean checkTrigger(GameEvent event, Game game) {
|
||||
ability.setSourceId(this.getSourceId());
|
||||
ability.setControllerId(this.getControllerId());
|
||||
return ability.checkTrigger(event, game);
|
||||
return ability.checkTrigger(event, game) && condition.apply(game, this);
|
||||
}
|
||||
|
||||
@Override
|
||||
|
|
|
|||
|
|
@ -543,7 +543,7 @@ public class ContinuousEffects implements Serializable {
|
|||
* @param game
|
||||
* @return
|
||||
*/
|
||||
private List<AsThoughEffect> getApplicableAsThoughEffects(AsThoughEffectType type, Game game) {
|
||||
public List<AsThoughEffect> getApplicableAsThoughEffects(AsThoughEffectType type, Game game) {
|
||||
List<AsThoughEffect> asThoughEffectsList = new ArrayList<>();
|
||||
if (asThoughEffectsMap.containsKey(type)) {
|
||||
for (AsThoughEffect effect : asThoughEffectsMap.get(type)) {
|
||||
|
|
|
|||
|
|
@ -33,7 +33,7 @@ public class CreateTokenCopyTargetEffect extends OneShotEffect {
|
|||
|
||||
private final UUID playerId;
|
||||
private final CardType additionalCardType;
|
||||
private boolean gainsHaste;
|
||||
private boolean hasHaste;
|
||||
private final int number;
|
||||
private List<Permanent> addedTokenPermanents;
|
||||
private SubType additionalSubType;
|
||||
|
|
@ -62,12 +62,12 @@ public class CreateTokenCopyTargetEffect extends OneShotEffect {
|
|||
this(playerId, null, false);
|
||||
}
|
||||
|
||||
public CreateTokenCopyTargetEffect(UUID playerId, CardType additionalCardType, boolean gainsHaste) {
|
||||
this(playerId, additionalCardType, gainsHaste, 1);
|
||||
public CreateTokenCopyTargetEffect(UUID playerId, CardType additionalCardType, boolean hasHaste) {
|
||||
this(playerId, additionalCardType, hasHaste, 1);
|
||||
}
|
||||
|
||||
public CreateTokenCopyTargetEffect(UUID playerId, CardType additionalCardType, boolean gainsHaste, int number) {
|
||||
this(playerId, additionalCardType, gainsHaste, number, false, false);
|
||||
public CreateTokenCopyTargetEffect(UUID playerId, CardType additionalCardType, boolean hasHaste, int number) {
|
||||
this(playerId, additionalCardType, hasHaste, number, false, false);
|
||||
}
|
||||
|
||||
/**
|
||||
|
|
@ -75,24 +75,24 @@ public class CreateTokenCopyTargetEffect extends OneShotEffect {
|
|||
* @param playerId null the token is controlled/owned by the controller of
|
||||
* the source ability
|
||||
* @param additionalCardType the token gains this card type in addition
|
||||
* @param gainsHaste the token gains haste
|
||||
* @param hasHaste the token gains haste
|
||||
* @param number number of tokens to put into play
|
||||
* @param tapped
|
||||
* @param attacking
|
||||
*/
|
||||
public CreateTokenCopyTargetEffect(UUID playerId, CardType additionalCardType, boolean gainsHaste, int number, boolean tapped, boolean attacking) {
|
||||
this(playerId, additionalCardType, gainsHaste, number, tapped, attacking, null);
|
||||
public CreateTokenCopyTargetEffect(UUID playerId, CardType additionalCardType, boolean hasHaste, int number, boolean tapped, boolean attacking) {
|
||||
this(playerId, additionalCardType, hasHaste, number, tapped, attacking, null);
|
||||
}
|
||||
|
||||
public CreateTokenCopyTargetEffect(UUID playerId, CardType additionalCardType, boolean gainsHaste, int number, boolean tapped, boolean attacking, UUID attackedPlayer) {
|
||||
this(playerId, additionalCardType, gainsHaste, number, tapped, attacking, attackedPlayer, Integer.MIN_VALUE, Integer.MIN_VALUE, false);
|
||||
public CreateTokenCopyTargetEffect(UUID playerId, CardType additionalCardType, boolean hasHaste, int number, boolean tapped, boolean attacking, UUID attackedPlayer) {
|
||||
this(playerId, additionalCardType, hasHaste, number, tapped, attacking, attackedPlayer, Integer.MIN_VALUE, Integer.MIN_VALUE, false);
|
||||
}
|
||||
|
||||
public CreateTokenCopyTargetEffect(UUID playerId, CardType additionalCardType, boolean gainsHaste, int number, boolean tapped, boolean attacking, UUID attackedPlayer, int power, int toughness, boolean gainsFlying) {
|
||||
public CreateTokenCopyTargetEffect(UUID playerId, CardType additionalCardType, boolean hasHaste, int number, boolean tapped, boolean attacking, UUID attackedPlayer, int power, int toughness, boolean gainsFlying) {
|
||||
super(Outcome.PutCreatureInPlay);
|
||||
this.playerId = playerId;
|
||||
this.additionalCardType = additionalCardType;
|
||||
this.gainsHaste = gainsHaste;
|
||||
this.hasHaste = hasHaste;
|
||||
this.addedTokenPermanents = new ArrayList<>();
|
||||
this.number = number;
|
||||
this.tapped = tapped;
|
||||
|
|
@ -107,7 +107,7 @@ public class CreateTokenCopyTargetEffect extends OneShotEffect {
|
|||
super(effect);
|
||||
this.playerId = effect.playerId;
|
||||
this.additionalCardType = effect.additionalCardType;
|
||||
this.gainsHaste = effect.gainsHaste;
|
||||
this.hasHaste = effect.hasHaste;
|
||||
this.addedTokenPermanents = new ArrayList<>(effect.addedTokenPermanents);
|
||||
this.number = effect.number;
|
||||
this.additionalSubType = effect.additionalSubType;
|
||||
|
|
@ -187,7 +187,7 @@ public class CreateTokenCopyTargetEffect extends OneShotEffect {
|
|||
if (additionalCardType != null && !token.getCardType().contains(additionalCardType)) {
|
||||
token.addCardType(additionalCardType);
|
||||
}
|
||||
if (gainsHaste) {
|
||||
if (hasHaste) {
|
||||
token.addAbility(HasteAbility.getInstance());
|
||||
}
|
||||
if (gainsFlying) {
|
||||
|
|
|
|||
|
|
@ -141,6 +141,7 @@ public class DoIfCostPaid extends OneShotEffect {
|
|||
String costText = cost.getText();
|
||||
if (costText != null
|
||||
&& !costText.toLowerCase(Locale.ENGLISH).startsWith("put")
|
||||
&& !costText.toLowerCase(Locale.ENGLISH).startsWith("return")
|
||||
&& !costText.toLowerCase(Locale.ENGLISH).startsWith("exile")
|
||||
&& !costText.toLowerCase(Locale.ENGLISH).startsWith("discard")
|
||||
&& !costText.toLowerCase(Locale.ENGLISH).startsWith("sacrifice")
|
||||
|
|
|
|||
|
|
@ -61,6 +61,7 @@ public class LookLibraryAndPickControllerEffect extends LookLibraryControllerEff
|
|||
private boolean putOnTopSelected;
|
||||
private boolean anyOrder;
|
||||
|
||||
//TODO: These constructors are a mess
|
||||
public LookLibraryAndPickControllerEffect(DynamicValue numberOfCards,
|
||||
boolean mayShuffleAfter, DynamicValue numberToPick,
|
||||
FilterCard pickFilter, boolean putOnTop) {
|
||||
|
|
|
|||
|
|
@ -0,0 +1,42 @@
|
|||
|
||||
package mage.abilities.effects.common;
|
||||
|
||||
import mage.MageObject;
|
||||
import mage.abilities.Ability;
|
||||
import mage.abilities.effects.OneShotEffect;
|
||||
import mage.constants.Outcome;
|
||||
import mage.constants.TargetController;
|
||||
import mage.game.Game;
|
||||
import mage.players.Player;
|
||||
|
||||
/**
|
||||
*
|
||||
* @author noahg
|
||||
*/
|
||||
public class RevealHandSourceControllerEffect extends OneShotEffect {
|
||||
|
||||
public RevealHandSourceControllerEffect() {
|
||||
super(Outcome.Discard);
|
||||
this.staticText = "reveal your hand";
|
||||
}
|
||||
|
||||
public RevealHandSourceControllerEffect(final RevealHandSourceControllerEffect effect) {
|
||||
super(effect);
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean apply(Game game, Ability source) {
|
||||
Player player = game.getPlayer(source.getControllerId());
|
||||
MageObject sourceObject = game.getObject(source.getSourceId());
|
||||
if (player != null && sourceObject != null) {
|
||||
player.revealCards(sourceObject.getIdName(), player.getHand(), game);
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
@Override
|
||||
public RevealHandSourceControllerEffect copy() {
|
||||
return new RevealHandSourceControllerEffect(this);
|
||||
}
|
||||
}
|
||||
|
|
@ -0,0 +1,74 @@
|
|||
/*
|
||||
*
|
||||
* 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.Mode;
|
||||
import mage.abilities.effects.OneShotEffect;
|
||||
import mage.constants.Outcome;
|
||||
import mage.constants.PhaseStep;
|
||||
import mage.game.Game;
|
||||
import mage.game.turn.TurnMod;
|
||||
import mage.players.Player;
|
||||
|
||||
/**
|
||||
*
|
||||
* @author noahg
|
||||
*/
|
||||
public class SkipNextDrawStepControllerEffect extends OneShotEffect {
|
||||
|
||||
public SkipNextDrawStepControllerEffect() {
|
||||
this("you skip your next draw step");
|
||||
}
|
||||
|
||||
public SkipNextDrawStepControllerEffect(String text) {
|
||||
super(Outcome.Detriment);
|
||||
this.staticText = text;
|
||||
}
|
||||
|
||||
public SkipNextDrawStepControllerEffect(SkipNextDrawStepControllerEffect effect) {
|
||||
super(effect);
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean apply(Game game, Ability source) {
|
||||
Player player = game.getPlayer(source.getControllerId());
|
||||
if (player != null) {
|
||||
game.getState().getTurnMods().add(new TurnMod(player.getId(), PhaseStep.DRAW));
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
@Override
|
||||
public SkipNextDrawStepControllerEffect copy() {
|
||||
return new SkipNextDrawStepControllerEffect(this);
|
||||
}
|
||||
}
|
||||
|
|
@ -0,0 +1,154 @@
|
|||
/*
|
||||
*
|
||||
* 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.MageObject;
|
||||
import mage.ObjectColor;
|
||||
import mage.abilities.Ability;
|
||||
import mage.abilities.Mode;
|
||||
import mage.abilities.effects.ContinuousEffectImpl;
|
||||
import mage.choices.ChoiceColor;
|
||||
import mage.constants.*;
|
||||
import mage.filter.FilterPermanent;
|
||||
import mage.game.Game;
|
||||
import mage.game.permanent.Permanent;
|
||||
import mage.game.stack.Spell;
|
||||
import mage.players.Player;
|
||||
|
||||
import java.util.UUID;
|
||||
|
||||
/**
|
||||
* @author LevelX
|
||||
*/
|
||||
public class BecomesColorAllEffect extends ContinuousEffectImpl {
|
||||
|
||||
private ObjectColor setColor;
|
||||
protected boolean loseOther;
|
||||
protected FilterPermanent filter;
|
||||
|
||||
/**
|
||||
* Set the color of a spell or permanent
|
||||
*
|
||||
* @param duration
|
||||
*/
|
||||
public BecomesColorAllEffect(Duration duration) {
|
||||
this(null, duration);
|
||||
}
|
||||
|
||||
public BecomesColorAllEffect(ObjectColor setColor, Duration duration) {
|
||||
this(setColor, duration, new FilterPermanent("All permanents"), true, null);
|
||||
}
|
||||
|
||||
public BecomesColorAllEffect(ObjectColor setColor, Duration duration, FilterPermanent filter, boolean loseOther, String text) {
|
||||
super(duration, Layer.ColorChangingEffects_5, SubLayer.NA, Outcome.Neutral);
|
||||
this.setColor = setColor;
|
||||
this.filter = filter;
|
||||
this.loseOther = loseOther;
|
||||
staticText = text;
|
||||
}
|
||||
|
||||
public BecomesColorAllEffect(final BecomesColorAllEffect effect) {
|
||||
super(effect);
|
||||
this.setColor = effect.setColor;
|
||||
this.filter = effect.filter;
|
||||
this.loseOther = effect.loseOther;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void init(Ability source, Game game) {
|
||||
Player controller = game.getPlayer(source.getControllerId());
|
||||
if (controller == null) {
|
||||
return;
|
||||
}
|
||||
if (setColor == null) {
|
||||
ChoiceColor choice = new ChoiceColor();
|
||||
if (!controller.choose(Outcome.PutManaInPool, choice, game)) {
|
||||
discard();
|
||||
return;
|
||||
}
|
||||
setColor = choice.getColor();
|
||||
if (!game.isSimulation()) {
|
||||
game.informPlayers(controller.getLogName() + " has chosen the color: " + setColor.toString());
|
||||
}
|
||||
}
|
||||
|
||||
super.init(source, game); //To change body of generated methods, choose Tools | Templates.
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean apply(Game game, Ability source) {
|
||||
Player controller = game.getPlayer(source.getControllerId());
|
||||
if (controller == null) {
|
||||
return false;
|
||||
}
|
||||
if (setColor != null) {
|
||||
for (Permanent permanent : game.getBattlefield().getActivePermanents(filter, source.getControllerId(), source.getSourceId(), game)) {
|
||||
if (permanent != null) {
|
||||
switch (layer) {
|
||||
case ColorChangingEffects_5:
|
||||
if (loseOther) {
|
||||
permanent.getColor(game).setColor(new ObjectColor());
|
||||
}
|
||||
permanent.getColor(game).addColor(setColor);
|
||||
break;
|
||||
}
|
||||
} else if (duration == Duration.Custom) {
|
||||
discard();
|
||||
}
|
||||
}
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
@Override
|
||||
public BecomesColorAllEffect copy() {
|
||||
return new BecomesColorAllEffect(this);
|
||||
}
|
||||
|
||||
@Override
|
||||
public String getText(Mode mode) {
|
||||
if (staticText != null && !staticText.isEmpty()) {
|
||||
return staticText;
|
||||
}
|
||||
StringBuilder sb = new StringBuilder();
|
||||
sb.append(filter.getMessage());
|
||||
sb.append(" become ");
|
||||
if (setColor == null) {
|
||||
sb.append("the color of your choice");
|
||||
} else {
|
||||
sb.append(setColor.getDescription());
|
||||
}
|
||||
if (!duration.toString().equals("")) {
|
||||
sb.append(' ').append(duration.toString());
|
||||
}
|
||||
return sb.toString();
|
||||
}
|
||||
}
|
||||
|
|
@ -0,0 +1,91 @@
|
|||
|
||||
package mage.abilities.effects.common.counter;
|
||||
|
||||
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.counters.Counter;
|
||||
import mage.game.Game;
|
||||
import mage.game.permanent.Permanent;
|
||||
import mage.util.CardUtil;
|
||||
|
||||
import java.util.Locale;
|
||||
|
||||
/**
|
||||
*
|
||||
* @author noahg
|
||||
*/
|
||||
public class RemoveCountersAttachedEffect extends OneShotEffect {
|
||||
|
||||
private Counter counter;
|
||||
private DynamicValue amount;
|
||||
private String textEnchanted;
|
||||
|
||||
public RemoveCountersAttachedEffect(Counter counter, String textEnchanted) {
|
||||
this(counter, new StaticValue(0), textEnchanted);
|
||||
}
|
||||
|
||||
/**
|
||||
*
|
||||
* @param counter
|
||||
* @param amount this amount will be added to the counter instances
|
||||
* @param textEnchanted text used for the enchanted permanent in rule text
|
||||
*/
|
||||
public RemoveCountersAttachedEffect(Counter counter, DynamicValue amount, String textEnchanted) {
|
||||
super(Outcome.UnboostCreature);
|
||||
this.counter = counter.copy();
|
||||
this.amount = amount;
|
||||
this.textEnchanted = textEnchanted;
|
||||
setText();
|
||||
}
|
||||
|
||||
public RemoveCountersAttachedEffect(final RemoveCountersAttachedEffect effect) {
|
||||
super(effect);
|
||||
if (effect.counter != null) {
|
||||
this.counter = effect.counter.copy();
|
||||
}
|
||||
this.amount = effect.amount;
|
||||
this.textEnchanted = effect.textEnchanted;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean apply(Game game, Ability source) {
|
||||
Permanent permanent = game.getPermanent(source.getSourceId());
|
||||
if (permanent != null && permanent.getAttachedTo() != null) {
|
||||
Permanent attachedTo = game.getPermanent(permanent.getAttachedTo());
|
||||
if (attachedTo != null && counter != null) {
|
||||
Counter newCounter = counter.copy();
|
||||
newCounter.add(amount.calculate(game, source, this));
|
||||
attachedTo.removeCounters(newCounter, game);
|
||||
}
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
private void setText() {
|
||||
StringBuilder sb = new StringBuilder();
|
||||
// put a +1/+1 counter on it
|
||||
sb.append("remove ");
|
||||
if (counter.getCount() > 1) {
|
||||
sb.append(CardUtil.numberToText(counter.getCount())).append(' ');
|
||||
sb.append(counter.getName().toLowerCase(Locale.ENGLISH)).append(" counters from ");
|
||||
} else {
|
||||
sb.append("a ");
|
||||
sb.append(counter.getName().toLowerCase(Locale.ENGLISH)).append(" counter from ");
|
||||
}
|
||||
sb.append(textEnchanted);
|
||||
if (!amount.getMessage().isEmpty()) {
|
||||
sb.append(" for each ").append(amount.getMessage());
|
||||
}
|
||||
staticText = sb.toString();
|
||||
}
|
||||
|
||||
@Override
|
||||
public RemoveCountersAttachedEffect copy() {
|
||||
return new RemoveCountersAttachedEffect(this);
|
||||
}
|
||||
|
||||
}
|
||||
|
|
@ -0,0 +1,94 @@
|
|||
package mage.abilities.effects.keyword;
|
||||
|
||||
import mage.abilities.Ability;
|
||||
import mage.abilities.Mode;
|
||||
import mage.abilities.costs.Cost;
|
||||
import mage.abilities.costs.mana.ManaCostsImpl;
|
||||
import mage.abilities.dynamicvalue.DynamicValue;
|
||||
import mage.abilities.effects.OneShotEffect;
|
||||
import mage.constants.AsThoughEffectType;
|
||||
import mage.constants.Outcome;
|
||||
import mage.game.Game;
|
||||
import mage.game.events.GameEvent;
|
||||
import mage.game.permanent.Permanent;
|
||||
import mage.players.Player;
|
||||
|
||||
import java.util.Locale;
|
||||
|
||||
public class EchoEffect extends OneShotEffect {
|
||||
|
||||
protected Cost cost;
|
||||
protected DynamicValue amount;
|
||||
|
||||
public EchoEffect(Cost costs) {
|
||||
super(Outcome.Sacrifice);
|
||||
this.cost = costs;
|
||||
this.amount = null;
|
||||
}
|
||||
|
||||
public EchoEffect(DynamicValue amount) {
|
||||
super(Outcome.Sacrifice);
|
||||
this.amount = amount;
|
||||
this.cost = null;
|
||||
}
|
||||
|
||||
public EchoEffect(final EchoEffect effect) {
|
||||
super(effect);
|
||||
this.cost = effect.cost;
|
||||
this.amount = effect.amount;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean apply(Game game, Ability source) {
|
||||
if (cost == null) {
|
||||
cost = new ManaCostsImpl(Integer.toString(amount.calculate(game, source, this)));
|
||||
}
|
||||
Player controller = game.getPlayer(source.getControllerId());
|
||||
if (controller != null
|
||||
&& source.getSourceObjectIfItStillExists(game) != null) {
|
||||
if (game.getContinuousEffects().asThough(source.getSourceId(), AsThoughEffectType.PAY_0_ECHO, source, source.getControllerId(), game) != null) {
|
||||
Cost altCost = new ManaCostsImpl("{0}");
|
||||
if (controller.chooseUse(Outcome.Benefit, "Pay {0} instead of the echo cost?", source, game)) {
|
||||
altCost.clearPaid();
|
||||
if (altCost.pay(source, game, source.getSourceId(), source.getControllerId(), false, null)) {
|
||||
game.fireEvent(GameEvent.getEvent(GameEvent.EventType.ECHO_PAID, source.getSourceId(), source.getSourceId(), source.getControllerId()));
|
||||
return true;
|
||||
}
|
||||
}
|
||||
}
|
||||
if (controller.chooseUse(Outcome.Benefit, "Pay " + cost.getText() + '?', source, game)) {
|
||||
cost.clearPaid();
|
||||
if (cost.pay(source, game, source.getSourceId(), source.getControllerId(), false, null)) {
|
||||
game.fireEvent(GameEvent.getEvent(GameEvent.EventType.ECHO_PAID, source.getSourceId(), source.getSourceId(), source.getControllerId()));
|
||||
return true;
|
||||
}
|
||||
}
|
||||
Permanent permanent = game.getPermanent(source.getSourceId());
|
||||
if (permanent != null) {
|
||||
permanent.sacrifice(source.getSourceId(), game);
|
||||
}
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
@Override
|
||||
public EchoEffect copy() {
|
||||
return new EchoEffect(this);
|
||||
}
|
||||
|
||||
@Override
|
||||
public String getText(Mode mode) {
|
||||
StringBuilder sb = new StringBuilder("sacrifice {this} unless you ");
|
||||
String costText = cost.getText();
|
||||
if (costText.toLowerCase(Locale.ENGLISH).startsWith("discard")) {
|
||||
sb.append(costText.substring(0, 1).toLowerCase(Locale.ENGLISH));
|
||||
sb.append(costText.substring(1));
|
||||
} else {
|
||||
sb.append("pay ").append(costText);
|
||||
}
|
||||
|
||||
return sb.toString();
|
||||
|
||||
}
|
||||
}
|
||||
|
|
@ -16,6 +16,7 @@ import mage.counters.CounterType;
|
|||
import mage.game.Game;
|
||||
import mage.game.events.GameEvent;
|
||||
import mage.game.events.GameEvent.EventType;
|
||||
import mage.game.events.ManaEvent;
|
||||
import mage.game.permanent.Permanent;
|
||||
import mage.players.Player;
|
||||
|
||||
|
|
@ -84,7 +85,7 @@ class CumulativeUpkeepEffect extends OneShotEffect {
|
|||
if (player.chooseUse(Outcome.Benefit, "Pay " + totalCost.getText() + '?', source, game)) {
|
||||
totalCost.clearPaid();
|
||||
if (totalCost.payOrRollback(source, game, source.getSourceId(), source.getControllerId())) {
|
||||
game.fireEvent(new GameEvent(EventType.PAID_CUMULATIVE_UPKEEP, permanent.getId(), permanent.getId(), player.getId(), ageCounter, false));
|
||||
game.fireEvent(new ManaEvent(EventType.PAID_CUMULATIVE_UPKEEP, permanent.getId(), permanent.getId(), player.getId(), totalCost.getUsedManaToPay()));
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -1,23 +1,18 @@
|
|||
|
||||
package mage.abilities.keyword;
|
||||
|
||||
import java.util.Locale;
|
||||
import java.util.UUID;
|
||||
import mage.abilities.Ability;
|
||||
import mage.abilities.Mode;
|
||||
import mage.abilities.TriggeredAbilityImpl;
|
||||
import mage.abilities.costs.Cost;
|
||||
import mage.abilities.costs.Costs;
|
||||
import mage.abilities.costs.CostsImpl;
|
||||
import mage.abilities.costs.mana.ManaCostsImpl;
|
||||
import mage.abilities.dynamicvalue.DynamicValue;
|
||||
import mage.abilities.effects.OneShotEffect;
|
||||
import mage.constants.Outcome;
|
||||
import mage.abilities.effects.keyword.EchoEffect;
|
||||
import mage.constants.Zone;
|
||||
import mage.game.Game;
|
||||
import mage.game.events.GameEvent;
|
||||
import mage.game.permanent.Permanent;
|
||||
import mage.players.Player;
|
||||
|
||||
import java.util.UUID;
|
||||
|
||||
/**
|
||||
*
|
||||
|
|
@ -82,7 +77,7 @@ public class EchoAbility extends TriggeredAbilityImpl {
|
|||
|
||||
@Override
|
||||
public boolean checkTrigger(GameEvent event, Game game) {
|
||||
// reset the echo paid state back, if creature enteres the battlefield
|
||||
// reset the echo paid state back, if creature enters the battlefield
|
||||
if (event.getType() == GameEvent.EventType.ENTERS_THE_BATTLEFIELD
|
||||
&& event.getTargetId().equals(this.getSourceId())) {
|
||||
|
||||
|
|
@ -128,70 +123,3 @@ public class EchoAbility extends TriggeredAbilityImpl {
|
|||
}
|
||||
}
|
||||
|
||||
class EchoEffect extends OneShotEffect {
|
||||
|
||||
protected Cost cost;
|
||||
protected DynamicValue amount;
|
||||
|
||||
public EchoEffect(Cost costs) {
|
||||
super(Outcome.Sacrifice);
|
||||
this.cost = costs;
|
||||
this.amount = null;
|
||||
}
|
||||
|
||||
public EchoEffect(DynamicValue amount) {
|
||||
super(Outcome.Sacrifice);
|
||||
this.amount = amount;
|
||||
this.cost = null;
|
||||
}
|
||||
|
||||
public EchoEffect(final EchoEffect effect) {
|
||||
super(effect);
|
||||
this.cost = effect.cost;
|
||||
this.amount = effect.amount;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean apply(Game game, Ability source) {
|
||||
if (amount != null) {
|
||||
cost = new ManaCostsImpl(Integer.toString(amount.calculate(game, source, this)));
|
||||
}
|
||||
Player controller = game.getPlayer(source.getControllerId());
|
||||
if (controller != null
|
||||
&& source.getSourceObjectIfItStillExists(game) != null) {
|
||||
if (controller.chooseUse(Outcome.Benefit, "Pay " + cost.getText() + '?', source, game)) {
|
||||
cost.clearPaid();
|
||||
if (cost.pay(source, game, source.getSourceId(), source.getControllerId(), false, null)) {
|
||||
game.fireEvent(GameEvent.getEvent(GameEvent.EventType.ECHO_PAID, source.getSourceId(), source.getSourceId(), source.getControllerId()));
|
||||
return true;
|
||||
}
|
||||
}
|
||||
Permanent permanent = game.getPermanent(source.getSourceId());
|
||||
if (permanent != null) {
|
||||
permanent.sacrifice(source.getSourceId(), game);
|
||||
}
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
@Override
|
||||
public EchoEffect copy() {
|
||||
return new EchoEffect(this);
|
||||
}
|
||||
|
||||
@Override
|
||||
public String getText(Mode mode) {
|
||||
StringBuilder sb = new StringBuilder("sacrifice {this} unless you ");
|
||||
String costText = cost.getText();
|
||||
if (costText.toLowerCase(Locale.ENGLISH).startsWith("discard")) {
|
||||
sb.append(costText.substring(0, 1).toLowerCase(Locale.ENGLISH));
|
||||
sb.append(costText.substring(1));
|
||||
} else {
|
||||
sb.append("pay ").append(costText);
|
||||
}
|
||||
|
||||
return sb.toString();
|
||||
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -16,7 +16,7 @@ import mage.abilities.costs.Cost;
|
|||
import mage.abilities.costs.Costs;
|
||||
import mage.abilities.costs.CostsImpl;
|
||||
import mage.abilities.costs.mana.ManaCostsImpl;
|
||||
import mage.abilities.decorator.ConditionalTriggeredAbility;
|
||||
import mage.abilities.decorator.ConditionalInterveningIfTriggeredAbility;
|
||||
import mage.abilities.effects.common.SacrificeSourceEffect;
|
||||
import mage.cards.Card;
|
||||
import mage.constants.Outcome;
|
||||
|
|
@ -42,7 +42,7 @@ public class EvokeAbility extends StaticAbility implements AlternativeSourceCost
|
|||
super(Zone.ALL, null);
|
||||
name = EVOKE_KEYWORD;
|
||||
this.addEvokeCost(manaString);
|
||||
Ability ability = new ConditionalTriggeredAbility(new EntersBattlefieldTriggeredAbility(new SacrificeSourceEffect()), EvokedCondition.instance, "Sacrifice {this} when it enters the battlefield and was evoked.");
|
||||
Ability ability = new ConditionalInterveningIfTriggeredAbility(new EntersBattlefieldTriggeredAbility(new SacrificeSourceEffect()), EvokedCondition.instance, "Sacrifice {this} when it enters the battlefield and was evoked.");
|
||||
ability.setRuleVisible(false);
|
||||
addSubAbility(ability);
|
||||
|
||||
|
|
|
|||
|
|
@ -11,7 +11,7 @@ import mage.abilities.TriggeredAbilityImpl;
|
|||
import mage.abilities.common.BeginningOfUpkeepTriggeredAbility;
|
||||
import mage.abilities.common.DiesTriggeredAbility;
|
||||
import mage.abilities.condition.common.SourceHasCounterCondition;
|
||||
import mage.abilities.decorator.ConditionalTriggeredAbility;
|
||||
import mage.abilities.decorator.ConditionalInterveningIfTriggeredAbility;
|
||||
import mage.abilities.dynamicvalue.common.StaticValue;
|
||||
import mage.abilities.effects.AsThoughEffectImpl;
|
||||
import mage.abilities.effects.common.counter.AddCountersSourceEffect;
|
||||
|
|
@ -36,7 +36,7 @@ public class RepairAbility extends DiesTriggeredAbility {
|
|||
|
||||
public RepairAbility(int count) {
|
||||
super(new AddCountersSourceEffect(CounterType.REPAIR.createInstance(), new StaticValue(count), false, true));
|
||||
addSubAbility(new RepairBeginningOfUpkeepTriggeredAbility());
|
||||
addSubAbility(new RepairBeginningOfUpkeepInterveningIfTriggeredAbility());
|
||||
addSubAbility(new RepairCastFromGraveyardTriggeredAbility());
|
||||
|
||||
ruleText = "Repair " + count + " <i>(When this creature dies, put " + count
|
||||
|
|
@ -127,9 +127,9 @@ class RepairCastFromGraveyardTriggeredAbility extends TriggeredAbilityImpl {
|
|||
}
|
||||
}
|
||||
|
||||
class RepairBeginningOfUpkeepTriggeredAbility extends ConditionalTriggeredAbility {
|
||||
class RepairBeginningOfUpkeepInterveningIfTriggeredAbility extends ConditionalInterveningIfTriggeredAbility {
|
||||
|
||||
public RepairBeginningOfUpkeepTriggeredAbility() {
|
||||
public RepairBeginningOfUpkeepInterveningIfTriggeredAbility() {
|
||||
super(new BeginningOfUpkeepTriggeredAbility(Zone.GRAVEYARD, new RemoveCounterSourceEffect(CounterType.REPAIR.createInstance()), TargetController.YOU, false),
|
||||
new SourceHasCounterCondition(CounterType.REPAIR),
|
||||
"At the beginning of your upkeep, remove a repair counter from {this}");
|
||||
|
|
@ -137,12 +137,12 @@ class RepairBeginningOfUpkeepTriggeredAbility extends ConditionalTriggeredAbilit
|
|||
|
||||
}
|
||||
|
||||
public RepairBeginningOfUpkeepTriggeredAbility(final RepairBeginningOfUpkeepTriggeredAbility effect) {
|
||||
public RepairBeginningOfUpkeepInterveningIfTriggeredAbility(final RepairBeginningOfUpkeepInterveningIfTriggeredAbility effect) {
|
||||
super(effect);
|
||||
}
|
||||
|
||||
@Override
|
||||
public RepairBeginningOfUpkeepTriggeredAbility copy() {
|
||||
return new RepairBeginningOfUpkeepTriggeredAbility(this);
|
||||
public RepairBeginningOfUpkeepInterveningIfTriggeredAbility copy() {
|
||||
return new RepairBeginningOfUpkeepInterveningIfTriggeredAbility(this);
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -14,7 +14,7 @@ import mage.abilities.condition.common.SuspendedCondition;
|
|||
import mage.abilities.costs.mana.ManaCost;
|
||||
import mage.abilities.costs.mana.ManaCostsImpl;
|
||||
import mage.abilities.costs.mana.VariableManaCost;
|
||||
import mage.abilities.decorator.ConditionalTriggeredAbility;
|
||||
import mage.abilities.decorator.ConditionalInterveningIfTriggeredAbility;
|
||||
import mage.abilities.effects.ContinuousEffect;
|
||||
import mage.abilities.effects.ContinuousEffectImpl;
|
||||
import mage.abilities.effects.OneShotEffect;
|
||||
|
|
@ -154,7 +154,7 @@ public class SuspendAbility extends SpecialAction {
|
|||
if (card.getManaCost().isEmpty()) {
|
||||
setRuleAtTheTop(true);
|
||||
}
|
||||
addSubAbility(new SuspendBeginningOfUpkeepTriggeredAbility());
|
||||
addSubAbility(new SuspendBeginningOfUpkeepInterveningIfTriggeredAbility());
|
||||
addSubAbility(new SuspendPlayCardAbility());
|
||||
}
|
||||
ruleText = sb.toString();
|
||||
|
|
@ -174,7 +174,7 @@ public class SuspendAbility extends SpecialAction {
|
|||
ability.setControllerId(card.getOwnerId());
|
||||
game.getState().addOtherAbility(card, ability);
|
||||
|
||||
SuspendBeginningOfUpkeepTriggeredAbility ability1 = new SuspendBeginningOfUpkeepTriggeredAbility();
|
||||
SuspendBeginningOfUpkeepInterveningIfTriggeredAbility ability1 = new SuspendBeginningOfUpkeepInterveningIfTriggeredAbility();
|
||||
ability1.setSourceId(card.getId());
|
||||
ability1.setControllerId(card.getOwnerId());
|
||||
game.getState().addOtherAbility(card, ability1);
|
||||
|
|
@ -343,7 +343,7 @@ class SuspendPlayCardEffect extends OneShotEffect {
|
|||
}
|
||||
if (!abilitiesToRemove.isEmpty()) {
|
||||
for (Ability ability : card.getAbilities()) {
|
||||
if (ability instanceof SuspendBeginningOfUpkeepTriggeredAbility || ability instanceof SuspendPlayCardAbility) {
|
||||
if (ability instanceof SuspendBeginningOfUpkeepInterveningIfTriggeredAbility || ability instanceof SuspendPlayCardAbility) {
|
||||
abilitiesToRemove.add(ability);
|
||||
}
|
||||
}
|
||||
|
|
@ -405,9 +405,9 @@ class GainHasteEffect extends ContinuousEffectImpl {
|
|||
|
||||
}
|
||||
|
||||
class SuspendBeginningOfUpkeepTriggeredAbility extends ConditionalTriggeredAbility {
|
||||
class SuspendBeginningOfUpkeepInterveningIfTriggeredAbility extends ConditionalInterveningIfTriggeredAbility {
|
||||
|
||||
public SuspendBeginningOfUpkeepTriggeredAbility() {
|
||||
public SuspendBeginningOfUpkeepInterveningIfTriggeredAbility() {
|
||||
super(new BeginningOfUpkeepTriggeredAbility(Zone.EXILED, new RemoveCounterSourceEffect(CounterType.TIME.createInstance()), TargetController.YOU, false),
|
||||
SuspendedCondition.instance,
|
||||
"At the beginning of your upkeep, if this card ({this}) is suspended, remove a time counter from it.");
|
||||
|
|
@ -415,12 +415,12 @@ class SuspendBeginningOfUpkeepTriggeredAbility extends ConditionalTriggeredAbili
|
|||
|
||||
}
|
||||
|
||||
public SuspendBeginningOfUpkeepTriggeredAbility(final SuspendBeginningOfUpkeepTriggeredAbility effect) {
|
||||
public SuspendBeginningOfUpkeepInterveningIfTriggeredAbility(final SuspendBeginningOfUpkeepInterveningIfTriggeredAbility effect) {
|
||||
super(effect);
|
||||
}
|
||||
|
||||
@Override
|
||||
public SuspendBeginningOfUpkeepTriggeredAbility copy() {
|
||||
return new SuspendBeginningOfUpkeepTriggeredAbility(this);
|
||||
public SuspendBeginningOfUpkeepInterveningIfTriggeredAbility copy() {
|
||||
return new SuspendBeginningOfUpkeepInterveningIfTriggeredAbility(this);
|
||||
}
|
||||
}
|
||||
|
|
|
|||
136
Mage/src/main/java/mage/abilities/meta/OrTriggeredAbility.java
Normal file
136
Mage/src/main/java/mage/abilities/meta/OrTriggeredAbility.java
Normal file
|
|
@ -0,0 +1,136 @@
|
|||
package mage.abilities.meta;
|
||||
|
||||
import mage.MageObject;
|
||||
import mage.abilities.TriggeredAbility;
|
||||
import mage.abilities.TriggeredAbilityImpl;
|
||||
import mage.abilities.effects.Effect;
|
||||
import mage.constants.Zone;
|
||||
import mage.game.Game;
|
||||
import mage.game.events.GameEvent;
|
||||
import mage.watchers.Watcher;
|
||||
|
||||
import java.util.ArrayList;
|
||||
import java.util.List;
|
||||
import java.util.UUID;
|
||||
|
||||
/**
|
||||
* A triggered ability that combines several others and triggers whenever one or more of them would. The abilities
|
||||
* passed in should have null as their effect, and should have their own targets set if necessary. All other information
|
||||
* will be passed in from changes to this Ability. Note: this does NOT work with abilities that have intervening if clauses.
|
||||
* @author noahg
|
||||
*/
|
||||
public class OrTriggeredAbility extends TriggeredAbilityImpl {
|
||||
|
||||
private final String ruleTrigger;
|
||||
private TriggeredAbility[] triggeredAbilities;
|
||||
private List<Integer> triggeringAbilities;
|
||||
|
||||
public OrTriggeredAbility(Zone zone, Effect effect, TriggeredAbility... abilities) {
|
||||
this(zone, effect, false, null, abilities);
|
||||
}
|
||||
|
||||
public OrTriggeredAbility(Zone zone, Effect effect, boolean optional, String ruleTrigger, TriggeredAbility... abilities) {
|
||||
super(zone, effect, optional);
|
||||
this.triggeredAbilities = abilities;
|
||||
this.ruleTrigger = ruleTrigger;
|
||||
this.triggeringAbilities = new ArrayList<>();
|
||||
for (TriggeredAbility ability : triggeredAbilities) {
|
||||
//Remove useless data
|
||||
ability.getEffects().clear();
|
||||
}
|
||||
}
|
||||
|
||||
public OrTriggeredAbility(OrTriggeredAbility ability) {
|
||||
super(ability);
|
||||
this.triggeredAbilities = new TriggeredAbility[ability.triggeredAbilities.length];
|
||||
for (int i = 0; i < this.triggeredAbilities.length; i++){
|
||||
this.triggeredAbilities[i] = ability.triggeredAbilities[i].copy();
|
||||
}
|
||||
this.triggeringAbilities = new ArrayList<>(ability.triggeringAbilities);
|
||||
this.ruleTrigger = ability.ruleTrigger;
|
||||
}
|
||||
|
||||
|
||||
@Override
|
||||
public boolean checkEventType(GameEvent event, Game game) {
|
||||
for (TriggeredAbility ability : triggeredAbilities) {
|
||||
if (ability.checkEventType(event, game)){
|
||||
System.out.println("Correct event type (" + event.getType() + ")");
|
||||
return true;
|
||||
}
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean checkTrigger(GameEvent event, Game game) {
|
||||
boolean toRet = false;
|
||||
for (int i = 0; i < triggeredAbilities.length; i++) {
|
||||
TriggeredAbility ability = triggeredAbilities[i];
|
||||
if (ability.checkEventType(event, game) && ability.checkTrigger(event, game)) {
|
||||
System.out.println("Triggered from " + ability.getRule());
|
||||
triggeringAbilities.add(i);
|
||||
toRet = true;
|
||||
}
|
||||
System.out.println("Checked " + ability.getRule());
|
||||
}
|
||||
return toRet;
|
||||
}
|
||||
|
||||
@Override
|
||||
public OrTriggeredAbility copy() {
|
||||
return new OrTriggeredAbility(this);
|
||||
}
|
||||
|
||||
@Override
|
||||
public String getRule() {
|
||||
if (ruleTrigger != null && !ruleTrigger.isEmpty()) {
|
||||
return ruleTrigger + super.getRule();
|
||||
}
|
||||
StringBuilder sb = new StringBuilder();
|
||||
if (triggeredAbilities[0].getRule().length() > 0) {
|
||||
sb.append(triggeredAbilities[0].getRule().substring(0, 1).toUpperCase())
|
||||
.append(triggeredAbilities[0].getRule().substring(1).toLowerCase());
|
||||
}
|
||||
|
||||
for (int i = 1; i < (triggeredAbilities.length - 1); i++) {
|
||||
sb.append(triggeredAbilities[i].getRule().toLowerCase());
|
||||
}
|
||||
|
||||
sb.append(" or ").append(triggeredAbilities[triggeredAbilities.length - 1].getRule().toLowerCase());
|
||||
return sb.toString() + super.getRule();
|
||||
}
|
||||
|
||||
|
||||
@Override
|
||||
public void setControllerId(UUID controllerId) {
|
||||
super.setControllerId(controllerId);
|
||||
for (TriggeredAbility ability : triggeredAbilities) {
|
||||
ability.setControllerId(controllerId);
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public void setSourceId(UUID sourceId) {
|
||||
super.setSourceId(sourceId);
|
||||
for (TriggeredAbility ability : triggeredAbilities) {
|
||||
ability.setSourceId(sourceId);
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public void addWatcher(Watcher watcher) {
|
||||
super.addWatcher(watcher);
|
||||
for (TriggeredAbility ability : triggeredAbilities) {
|
||||
ability.addWatcher(watcher);
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public void setSourceObject(MageObject sourceObject, Game game) {
|
||||
super.setSourceObject(sourceObject, game);
|
||||
for (TriggeredAbility ability : triggeredAbilities) {
|
||||
ability.setSourceObject(sourceObject, game);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -59,7 +59,7 @@ public abstract class CardImpl extends MageObjectImpl implements Card {
|
|||
|
||||
protected UUID ownerId;
|
||||
protected String cardNumber;
|
||||
public String expansionSetCode;
|
||||
protected String expansionSetCode;
|
||||
protected String tokenSetCode;
|
||||
protected String tokenDescriptor;
|
||||
protected Rarity rarity;
|
||||
|
|
|
|||
|
|
@ -18,6 +18,7 @@ public enum AsThoughEffectType {
|
|||
BLOCK_SWAMPWALK,
|
||||
BLOCK_MOUNTAINWALK,
|
||||
BLOCK_FORESTWALK,
|
||||
DAMAGE_NOT_BLOCKED,
|
||||
BE_BLOCKED,
|
||||
PLAY_FROM_NOT_OWN_HAND_ZONE,
|
||||
CAST_AS_INSTANT,
|
||||
|
|
@ -25,7 +26,7 @@ public enum AsThoughEffectType {
|
|||
DAMAGE,
|
||||
SHROUD,
|
||||
HEXPROOF,
|
||||
PAY,
|
||||
PAY_0_ECHO,
|
||||
LOOK_AT_FACE_DOWN,
|
||||
SPEND_OTHER_MANA,
|
||||
SPEND_ONLY_MANA,
|
||||
|
|
|
|||
|
|
@ -80,6 +80,7 @@ public enum CounterType {
|
|||
MUSTER("muster"),
|
||||
NET("net"),
|
||||
OMEN("omen"),
|
||||
ORE("ore"),
|
||||
P0P1(new BoostCounter(0, 1).name),
|
||||
P1P0(new BoostCounter(1, 0).name),
|
||||
P1P1(new BoostCounter(1, 1).name),
|
||||
|
|
|
|||
|
|
@ -28,13 +28,8 @@ public class AbilityPredicate implements Predicate<MageObject> {
|
|||
} else {
|
||||
abilities = input.getAbilities();
|
||||
}
|
||||
|
||||
for (Ability ability : abilities) {
|
||||
if (abilityClass.equals(ability.getClass())) {
|
||||
return true;
|
||||
}
|
||||
}
|
||||
return false;
|
||||
return abilities.stream().anyMatch(ability -> ability.getClass().equals(abilityClass));
|
||||
|
||||
}
|
||||
|
||||
@Override
|
||||
|
|
|
|||
|
|
@ -16,12 +16,8 @@ public class VariableManaCostPredicate implements Predicate<MageObject> {
|
|||
|
||||
@Override
|
||||
public boolean apply(MageObject input, Game game) {
|
||||
for (ManaCost manaCost : input.getManaCost()) {
|
||||
if (manaCost instanceof VariableManaCost) {
|
||||
return true;
|
||||
}
|
||||
}
|
||||
return false;
|
||||
return input.getManaCost().stream().anyMatch(manaCost -> manaCost instanceof VariableManaCost);
|
||||
|
||||
}
|
||||
|
||||
@Override
|
||||
|
|
|
|||
|
|
@ -19,8 +19,8 @@ public class OwnerIdPredicate implements Predicate<Card> {
|
|||
}
|
||||
|
||||
@Override
|
||||
public boolean apply(Card input, Game game) {
|
||||
return ownerId.equals(input.getOwnerId());
|
||||
public boolean apply(Card card, Game game) {
|
||||
return card.isOwnedBy(ownerId);
|
||||
}
|
||||
|
||||
@Override
|
||||
|
|
|
|||
|
|
@ -18,11 +18,14 @@ public class PlayerCanGainLifePredicate implements ObjectSourcePlayerPredicate<O
|
|||
@Override
|
||||
public boolean apply(ObjectSourcePlayer<Player> input, Game game) {
|
||||
Player player = input.getObject();
|
||||
return player.isCanGainLife();
|
||||
if(player != null) {
|
||||
return player.isCanGainLife();
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
@Override
|
||||
public String toString() {
|
||||
return "Player can gain live";
|
||||
return "Player can gain life";
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -17,12 +17,8 @@ public class CounterAnyPredicate implements Predicate<Permanent> {
|
|||
|
||||
@Override
|
||||
public boolean apply(Permanent input, Game game) {
|
||||
for (Counter counter: input.getCounters(game).values()) {
|
||||
if (counter.getCount()> 0) {
|
||||
return true;
|
||||
}
|
||||
}
|
||||
return false;
|
||||
return input.getCounters(game).values().stream().anyMatch(counter -> counter.getCount() > 0);
|
||||
|
||||
}
|
||||
|
||||
@Override
|
||||
|
|
|
|||
|
|
@ -1,7 +1,10 @@
|
|||
|
||||
package mage.filter.predicate.permanent;
|
||||
|
||||
import java.util.Objects;
|
||||
import java.util.UUID;
|
||||
|
||||
import mage.MageObject;
|
||||
import mage.filter.predicate.Predicate;
|
||||
import mage.game.Game;
|
||||
import mage.game.permanent.Permanent;
|
||||
|
|
@ -14,13 +17,11 @@ public class EnchantedPredicate implements Predicate<Permanent> {
|
|||
|
||||
@Override
|
||||
public boolean apply(Permanent input, Game game) {
|
||||
for (UUID attachmentId : input.getAttachments()) {
|
||||
Permanent attachment = game.getPermanent(attachmentId);
|
||||
if (attachment != null && attachment.isEnchantment()) {
|
||||
return true;
|
||||
}
|
||||
}
|
||||
return false;
|
||||
return input.getAttachments()
|
||||
.stream()
|
||||
.map(game::getPermanent)
|
||||
.filter(Objects::nonNull)
|
||||
.anyMatch(MageObject::isEnchantment);
|
||||
}
|
||||
|
||||
@Override
|
||||
|
|
|
|||
|
|
@ -5,27 +5,26 @@
|
|||
*/
|
||||
package mage.filter.predicate.permanent;
|
||||
|
||||
import java.util.UUID;
|
||||
import mage.constants.SubType;
|
||||
import mage.filter.predicate.Predicate;
|
||||
import mage.game.Game;
|
||||
import mage.game.permanent.Permanent;
|
||||
|
||||
import java.util.Objects;
|
||||
|
||||
/**
|
||||
*
|
||||
* @author LevelX2
|
||||
*/
|
||||
public class EquippedPredicate implements Predicate<Permanent> {
|
||||
|
||||
@Override
|
||||
public boolean apply(Permanent input, Game game) {
|
||||
for (UUID attachmentId : input.getAttachments()) {
|
||||
Permanent attachment = game.getPermanent(attachmentId);
|
||||
if (attachment != null && attachment.hasSubtype(SubType.EQUIPMENT, game)) {
|
||||
return true;
|
||||
}
|
||||
}
|
||||
return false;
|
||||
return input.getAttachments()
|
||||
.stream()
|
||||
.map(game::getPermanent)
|
||||
.filter(Objects::nonNull)
|
||||
.anyMatch(attachment -> attachment.hasSubtype(SubType.EQUIPMENT, game));
|
||||
|
||||
}
|
||||
|
||||
@Override
|
||||
|
|
|
|||
|
|
@ -398,6 +398,8 @@ public interface Game extends MageItem, Serializable {
|
|||
|
||||
void playPriority(UUID activePlayerId, boolean resuming);
|
||||
|
||||
void resetControlAfterSpellResolve(UUID topId);
|
||||
|
||||
boolean endTurn(Ability source);
|
||||
|
||||
int doAction(MageAction action);
|
||||
|
|
|
|||
|
|
@ -1396,6 +1396,7 @@ public abstract class GameImpl implements Game, Serializable {
|
|||
try {
|
||||
top = state.getStack().peek();
|
||||
top.resolve(this);
|
||||
resetControlAfterSpellResolve(top.getId());
|
||||
} finally {
|
||||
if (top != null) {
|
||||
state.getStack().remove(top, this); // seems partly redundant because move card from stack to grave is already done and the stack removed
|
||||
|
|
@ -1409,6 +1410,37 @@ public abstract class GameImpl implements Game, Serializable {
|
|||
}
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public void resetControlAfterSpellResolve(UUID topId) {
|
||||
// for Word of Command
|
||||
Spell spell = getSpellOrLKIStack(topId);
|
||||
if (spell != null) {
|
||||
if (spell.getCommandedBy() != null) {
|
||||
UUID commandedBy = spell.getCommandedBy();
|
||||
UUID spellControllerId = null;
|
||||
if (commandedBy.equals(spell.getControllerId())) {
|
||||
spellControllerId = spell.getSpellAbility().getFirstTarget(); // i.e. resolved spell is Word of Command
|
||||
} else {
|
||||
spellControllerId = spell.getControllerId(); // i.e. resolved spell is the target opponent's spell
|
||||
}
|
||||
if (commandedBy != null && spellControllerId != null) {
|
||||
Player turnController = getPlayer(commandedBy);
|
||||
if (turnController != null) {
|
||||
Player targetPlayer = getPlayer(spellControllerId);
|
||||
if (targetPlayer != null) {
|
||||
targetPlayer.setGameUnderYourControl(true, false);
|
||||
informPlayers(turnController.getLogName() + " lost control over " + targetPlayer.getLogName());
|
||||
if (targetPlayer.getTurnControlledBy().equals(turnController.getId())) {
|
||||
turnController.getPlayersUnderYourControl().remove(targetPlayer.getId());
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
spell.setCommandedBy(null);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* This checks if the stack gets filled iterated, without ever getting empty
|
||||
|
|
|
|||
|
|
@ -1,8 +1,6 @@
|
|||
|
||||
package mage.game.combat;
|
||||
|
||||
import java.io.Serializable;
|
||||
import java.util.*;
|
||||
import mage.MageObject;
|
||||
import mage.abilities.Ability;
|
||||
import mage.abilities.effects.RequirementEffect;
|
||||
|
|
@ -33,6 +31,9 @@ import mage.util.Copyable;
|
|||
import mage.util.trace.TraceUtil;
|
||||
import org.apache.log4j.Logger;
|
||||
|
||||
import java.io.Serializable;
|
||||
import java.util.*;
|
||||
|
||||
/**
|
||||
* @author BetaSteward_at_googlemail.com
|
||||
*/
|
||||
|
|
@ -47,7 +48,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
|
||||
// player and planeswalker ids
|
||||
protected Set<UUID> defenders = new HashSet<>();
|
||||
// how many creatures attack defending player
|
||||
protected Map<UUID, Set<UUID>> numberCreaturesDefenderAttackedBy = new HashMap<>();
|
||||
|
|
@ -310,31 +311,28 @@ public class Combat implements Serializable, Copyable<Combat> {
|
|||
for (UUID targetId : target.getTargets()) {
|
||||
Permanent permanent = game.getPermanent(targetId);
|
||||
if (permanent != null) {
|
||||
if (permanent != null) {
|
||||
|
||||
for (UUID bandedId : attacker.getBandedCards()) {
|
||||
permanent.addBandedCard(bandedId);
|
||||
Permanent banded = game.getPermanent(bandedId);
|
||||
if (banded != null) {
|
||||
banded.addBandedCard(targetId);
|
||||
}
|
||||
}
|
||||
permanent.addBandedCard(creatureId);
|
||||
attacker.addBandedCard(targetId);
|
||||
if (!permanent.getAbilities().containsKey(BandingAbility.getInstance().getId())) {
|
||||
filter.add(new AbilityPredicate(BandingAbility.class));
|
||||
for (UUID bandedId : attacker.getBandedCards()) {
|
||||
permanent.addBandedCard(bandedId);
|
||||
Permanent banded = game.getPermanent(bandedId);
|
||||
if (banded != null) {
|
||||
banded.addBandedCard(targetId);
|
||||
}
|
||||
}
|
||||
permanent.addBandedCard(creatureId);
|
||||
attacker.addBandedCard(targetId);
|
||||
if (!permanent.getAbilities().containsKey(BandingAbility.getInstance().getId())) {
|
||||
filter.add(new AbilityPredicate(BandingAbility.class));
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
}
|
||||
}
|
||||
if (isBanded) {
|
||||
StringBuilder sb = new StringBuilder(player.getLogName()).append(" formed a band with ").append((attacker.getBandedCards().size() + 1) + " creatures: ");
|
||||
sb.append(attacker.getLogName());
|
||||
int i = 0;
|
||||
for (UUID id : attacker.getBandedCards()) {
|
||||
i++;
|
||||
sb.append(", ");
|
||||
Permanent permanent = game.getPermanent(id);
|
||||
if (permanent != null) {
|
||||
|
|
@ -415,7 +413,6 @@ public class Combat implements Serializable, Copyable<Combat> {
|
|||
}
|
||||
|
||||
/**
|
||||
*
|
||||
* @param player
|
||||
* @param game
|
||||
* @return true if the attack with that set of creatures and attacked
|
||||
|
|
@ -486,7 +483,7 @@ public class Combat implements Serializable, Copyable<Combat> {
|
|||
* Handle the blocker selection process
|
||||
*
|
||||
* @param blockController player that controlls how to block, if null the
|
||||
* defender is the controller
|
||||
* defender is the controller
|
||||
* @param game
|
||||
*/
|
||||
public void selectBlockers(Player blockController, Game game) {
|
||||
|
|
@ -532,7 +529,6 @@ public class Combat implements Serializable, Copyable<Combat> {
|
|||
|
||||
/**
|
||||
* Add info about attacker blocked by blocker to the game log
|
||||
*
|
||||
*/
|
||||
private void logBlockerInfo(Player defender, Game game) {
|
||||
boolean shownDefendingPlayer = game.getPlayers().size() < 3; // only two players no ned to sow the attacked player
|
||||
|
|
@ -1191,9 +1187,7 @@ public class Combat implements Serializable, Copyable<Combat> {
|
|||
}
|
||||
break;
|
||||
case MULTIPLE:
|
||||
for (UUID opponentId : game.getOpponents(attackingPlayerId)) {
|
||||
attackablePlayers.add(opponentId);
|
||||
}
|
||||
attackablePlayers.addAll(game.getOpponents(attackingPlayerId));
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
|
@ -1281,10 +1275,10 @@ public class Combat implements Serializable, Copyable<Combat> {
|
|||
if (defenderAttackedBy.size() >= defendingPlayer.getMaxAttackedBy()) {
|
||||
Player attackingPlayer = game.getPlayer(game.getControllerId(attackerId));
|
||||
if (attackingPlayer != null && !game.isSimulation()) {
|
||||
game.informPlayer(attackingPlayer, new StringBuilder("No more than ")
|
||||
.append(CardUtil.numberToText(defendingPlayer.getMaxAttackedBy()))
|
||||
.append(" creatures can attack ")
|
||||
.append(defendingPlayer.getLogName()).toString());
|
||||
game.informPlayer(attackingPlayer, "No more than " +
|
||||
CardUtil.numberToText(defendingPlayer.getMaxAttackedBy()) +
|
||||
" creatures can attack " +
|
||||
defendingPlayer.getLogName());
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
|
@ -1314,7 +1308,7 @@ public class Combat implements Serializable, Copyable<Combat> {
|
|||
* @param playerId
|
||||
* @param game
|
||||
* @param solveBanding check whether also add creatures banded with
|
||||
* attackerId
|
||||
* attackerId
|
||||
*/
|
||||
public void addBlockingGroup(UUID blockerId, UUID attackerId, UUID playerId, Game game, boolean solveBanding) {
|
||||
Permanent blocker = game.getPermanent(blockerId);
|
||||
|
|
@ -1325,9 +1319,7 @@ public class Combat implements Serializable, Copyable<Combat> {
|
|||
// add all blocked attackers
|
||||
for (CombatGroup group : groups) {
|
||||
if (group.getBlockers().contains(blockerId)) {
|
||||
for (UUID attacker : group.attackers) {
|
||||
newGroup.attackers.add(attacker);
|
||||
}
|
||||
newGroup.attackers.addAll(group.attackers);
|
||||
}
|
||||
}
|
||||
blockingGroups.put(blockerId, newGroup);
|
||||
|
|
@ -1423,12 +1415,8 @@ public class Combat implements Serializable, Copyable<Combat> {
|
|||
}
|
||||
|
||||
public boolean hasFirstOrDoubleStrike(Game game) {
|
||||
for (CombatGroup group : groups) {
|
||||
if (group.hasFirstOrDoubleStrike(game)) {
|
||||
return true;
|
||||
}
|
||||
}
|
||||
return false;
|
||||
return groups.stream()
|
||||
.anyMatch(group -> group.hasFirstOrDoubleStrike(game));
|
||||
}
|
||||
|
||||
public CombatGroup findGroup(UUID attackerId) {
|
||||
|
|
@ -1449,7 +1437,7 @@ public class Combat implements Serializable, Copyable<Combat> {
|
|||
return null;
|
||||
}
|
||||
|
||||
// public int totalUnblockedDamage(Game game) {
|
||||
// public int totalUnblockedDamage(Game game) {
|
||||
// int total = 0;
|
||||
// for (CombatGroup group : groups) {
|
||||
// if (group.getBlockers().isEmpty()) {
|
||||
|
|
@ -1482,7 +1470,6 @@ public class Combat implements Serializable, Copyable<Combat> {
|
|||
}
|
||||
|
||||
/**
|
||||
*
|
||||
* @param attackerId
|
||||
* @return uuid of defending player or planeswalker
|
||||
*/
|
||||
|
|
|
|||
|
|
@ -3,6 +3,8 @@ package mage.game.combat;
|
|||
|
||||
import java.io.Serializable;
|
||||
import java.util.*;
|
||||
import java.util.stream.Stream;
|
||||
|
||||
import mage.abilities.common.ControllerAssignCombatDamageToBlockersAbility;
|
||||
import mage.abilities.common.ControllerDivideCombatDamageAbility;
|
||||
import mage.abilities.common.DamageAsThoughNotBlockedAbility;
|
||||
|
|
@ -12,6 +14,7 @@ import mage.abilities.keyword.DeathtouchAbility;
|
|||
import mage.abilities.keyword.DoubleStrikeAbility;
|
||||
import mage.abilities.keyword.FirstStrikeAbility;
|
||||
import mage.abilities.keyword.TrampleAbility;
|
||||
import mage.constants.AsThoughEffectType;
|
||||
import mage.constants.Outcome;
|
||||
import mage.filter.StaticFilters;
|
||||
import mage.game.Game;
|
||||
|
|
@ -60,19 +63,11 @@ public class CombatGroup implements Serializable, Copyable<CombatGroup> {
|
|||
}
|
||||
|
||||
public boolean hasFirstOrDoubleStrike(Game game) {
|
||||
for (UUID permId : attackers) {
|
||||
Permanent attacker = game.getPermanent(permId);
|
||||
if (attacker != null && hasFirstOrDoubleStrike(attacker)) {
|
||||
return true;
|
||||
}
|
||||
}
|
||||
for (UUID permId : blockers) {
|
||||
Permanent blocker = game.getPermanent(permId);
|
||||
if (blocker != null && hasFirstOrDoubleStrike(blocker)) {
|
||||
return true;
|
||||
}
|
||||
}
|
||||
return false;
|
||||
return Stream.concat(attackers.stream(), blockers.stream())
|
||||
.map(id -> game.getPermanent(id))
|
||||
.filter(Objects::nonNull)
|
||||
.anyMatch(this::hasFirstOrDoubleStrike);
|
||||
|
||||
}
|
||||
|
||||
public UUID getDefenderId() {
|
||||
|
|
@ -124,11 +119,14 @@ public class CombatGroup implements Serializable, Copyable<CombatGroup> {
|
|||
return;
|
||||
} else {
|
||||
Player player = game.getPlayer(defenderAssignsCombatDamage(game) ? defendingPlayerId : attacker.getControllerId());
|
||||
if (attacker.getAbilities().containsKey(DamageAsThoughNotBlockedAbility.getInstance().getId())) { // for handling creatures like Thorn Elemental
|
||||
if (player.chooseUse(Outcome.Damage, "Do you wish to assign damage for " + attacker.getLogName() + " as though it weren't blocked?", null, game)) {
|
||||
blocked = false;
|
||||
unblockedDamage(first, game);
|
||||
}
|
||||
if ((attacker.getAbilities().containsKey(DamageAsThoughNotBlockedAbility.getInstance().getId()) &&
|
||||
player.chooseUse(Outcome.Damage, "Do you wish to assign damage for "
|
||||
+ attacker.getLogName() + " as though it weren't blocked?", null, game)) ||
|
||||
game.getContinuousEffects().asThough(attacker.getId(), AsThoughEffectType.DAMAGE_NOT_BLOCKED
|
||||
, null, attacker.getControllerId(), game) != null) {
|
||||
// for handling creatures like Thorn Elemental
|
||||
blocked = false;
|
||||
unblockedDamage(first, game);
|
||||
}
|
||||
if (blockers.size() == 1) {
|
||||
singleBlockerDamage(player, first, game);
|
||||
|
|
|
|||
|
|
@ -6,8 +6,9 @@ import mage.constants.CardType;
|
|||
import mage.constants.SubType;
|
||||
|
||||
public final class AvatarToken2 extends TokenImpl {
|
||||
|
||||
public AvatarToken2() {
|
||||
super("Angel", "4/4 white Avatar creature token with flying");
|
||||
super("Avatar", "4/4 white Avatar creature token with flying");
|
||||
cardType.add(CardType.CREATURE);
|
||||
color.setWhite(true);
|
||||
subtype.add(SubType.AVATAR);
|
||||
|
|
|
|||
|
|
@ -0,0 +1,45 @@
|
|||
|
||||
package mage.game.permanent.token;
|
||||
|
||||
import mage.MageInt;
|
||||
import mage.constants.CardType;
|
||||
import mage.constants.SubType;
|
||||
|
||||
import java.util.ArrayList;
|
||||
import java.util.Arrays;
|
||||
import java.util.List;
|
||||
|
||||
/**
|
||||
*
|
||||
* @author BetaSteward_at_googlemail.com
|
||||
*/
|
||||
public final class BeastToken4 extends TokenImpl {
|
||||
|
||||
public BeastToken4() {
|
||||
this(null, 0);
|
||||
}
|
||||
|
||||
public BeastToken4(String setCode) {
|
||||
this(setCode, 0);
|
||||
}
|
||||
|
||||
public BeastToken4(String setCode, int tokenType) {
|
||||
super("Beast", "2/2 green Beast creature token");
|
||||
setOriginalExpansionSetCode(setCode != null ? setCode : "EXO");
|
||||
cardType.add(CardType.CREATURE);
|
||||
color.setGreen(true);
|
||||
subtype.add(SubType.BEAST);
|
||||
power = new MageInt(2);
|
||||
toughness = new MageInt(2);
|
||||
|
||||
}
|
||||
|
||||
public BeastToken4(final BeastToken4 token) {
|
||||
super(token);
|
||||
}
|
||||
|
||||
@Override
|
||||
public BeastToken4 copy() {
|
||||
return new BeastToken4(this);
|
||||
}
|
||||
}
|
||||
|
|
@ -67,6 +67,7 @@ public class Spell extends StackObjImpl implements Card {
|
|||
private boolean faceDown;
|
||||
private boolean countered;
|
||||
private boolean resolving = false;
|
||||
private UUID commandedBy = null; // for Word of Command
|
||||
|
||||
private boolean doneActivatingManaAbilities; // if this is true, the player is no longer allowed to pay the spell costs with activating of mana abilies
|
||||
|
||||
|
|
@ -121,6 +122,7 @@ public class Spell extends StackObjImpl implements Card {
|
|||
this.faceDown = spell.faceDown;
|
||||
this.countered = spell.countered;
|
||||
this.resolving = spell.resolving;
|
||||
this.commandedBy = spell.commandedBy;
|
||||
|
||||
this.doneActivatingManaAbilities = spell.doneActivatingManaAbilities;
|
||||
this.targetChanged = spell.targetChanged;
|
||||
|
|
@ -179,6 +181,12 @@ public class Spell extends StackObjImpl implements Card {
|
|||
return false;
|
||||
}
|
||||
this.resolving = true;
|
||||
if (commandedBy != null && !commandedBy.equals(getControllerId())) {
|
||||
Player turnController = game.getPlayer(commandedBy);
|
||||
if (turnController != null) {
|
||||
turnController.controlPlayersTurn(game, controller.getId());
|
||||
}
|
||||
}
|
||||
if (this.isInstant() || this.isSorcery()) {
|
||||
int index = 0;
|
||||
result = false;
|
||||
|
|
@ -1024,4 +1032,12 @@ public class Spell extends StackObjImpl implements Card {
|
|||
throw new UnsupportedOperationException("Not supported."); //To change body of generated methods, choose Tools | Templates.
|
||||
}
|
||||
|
||||
public void setCommandedBy(UUID playerId) {
|
||||
this.commandedBy = playerId;
|
||||
}
|
||||
|
||||
public UUID getCommandedBy() {
|
||||
return commandedBy;
|
||||
}
|
||||
|
||||
}
|
||||
|
|
|
|||
|
|
@ -37,10 +37,11 @@ public class SpellStack extends ArrayDeque<StackObject> {
|
|||
try {
|
||||
top = this.peek();
|
||||
top.resolve(game);
|
||||
game.resetControlAfterSpellResolve(top.getId());
|
||||
} finally {
|
||||
if (top != null) {
|
||||
if (contains(top)) {
|
||||
logger.warn("StackObject was still on the stack after resoving" + top.getName());
|
||||
logger.warn("StackObject was still on the stack after resolving" + top.getName());
|
||||
this.remove(top, game);
|
||||
}
|
||||
}
|
||||
|
|
@ -89,10 +90,10 @@ public class SpellStack extends ArrayDeque<StackObject> {
|
|||
game.informPlayers(counteredObjectName + " is countered by " + sourceObject.getLogName());
|
||||
}
|
||||
game.fireEvent(GameEvent.getEvent(GameEvent.EventType.COUNTERED, objectId, sourceId, stackObject.getControllerId()));
|
||||
return true;
|
||||
} else if (!game.isSimulation()) {
|
||||
game.informPlayers(counteredObjectName + " could not be countered by " + sourceObject.getLogName());
|
||||
}
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
|
|
|||
|
|
@ -35,6 +35,8 @@ public class ManaPool implements Serializable {
|
|||
private boolean autoPayment; // auto payment from mana pool: true - mode is active
|
||||
private boolean autoPaymentRestricted; // auto payment from mana pool: true - if auto Payment is on, it will only pay if one kind of mana is in the pool
|
||||
private ManaType unlockedManaType; // type of mana that was selected to pay manually
|
||||
private boolean forcedToPay; // for Word of Command
|
||||
private final List<ManaPoolItem> poolBookmark = new ArrayList<>(); // mana pool bookmark for rollback purposes
|
||||
|
||||
private final Set<ManaType> doNotEmptyManaTypes = new HashSet<>();
|
||||
|
||||
|
|
@ -43,6 +45,7 @@ public class ManaPool implements Serializable {
|
|||
autoPayment = true;
|
||||
autoPaymentRestricted = true;
|
||||
unlockedManaType = null;
|
||||
forcedToPay = false;
|
||||
}
|
||||
|
||||
public ManaPool(final ManaPool pool) {
|
||||
|
|
@ -53,6 +56,10 @@ public class ManaPool implements Serializable {
|
|||
this.autoPayment = pool.autoPayment;
|
||||
this.autoPaymentRestricted = pool.autoPaymentRestricted;
|
||||
this.unlockedManaType = pool.unlockedManaType;
|
||||
this.forcedToPay = pool.forcedToPay;
|
||||
for (ManaPoolItem item : pool.poolBookmark) {
|
||||
poolBookmark.add(item.copy());
|
||||
}
|
||||
this.doNotEmptyManaTypes.addAll(pool.doNotEmptyManaTypes);
|
||||
}
|
||||
|
||||
|
|
@ -87,12 +94,12 @@ public class ManaPool implements Serializable {
|
|||
* @return
|
||||
*/
|
||||
public boolean pay(ManaType manaType, Ability ability, Filter filter, Game game, Cost costToPay, Mana usedManaToPay) {
|
||||
if (!autoPayment && manaType != unlockedManaType) {
|
||||
if (!isAutoPayment() && manaType != unlockedManaType) {
|
||||
// if manual payment and the needed mana type was not unlocked, nothing will be paid
|
||||
return false;
|
||||
}
|
||||
ManaType possibleAsThoughtPoolManaType = null;
|
||||
if (autoPayment && autoPaymentRestricted && !wasManaAddedBeyondStock() && manaType != unlockedManaType) {
|
||||
if (isAutoPayment() && isAutoPaymentRestricted() && !wasManaAddedBeyondStock() && manaType != unlockedManaType) {
|
||||
// if automatic restricted payment and there is already mana in the pool
|
||||
// and the needed mana type was not unlocked, nothing will be paid
|
||||
if (unlockedManaType != null) {
|
||||
|
|
@ -111,6 +118,7 @@ public class ManaPool implements Serializable {
|
|||
lockManaType(); // pay only one mana if mana payment is set to manually
|
||||
return true;
|
||||
}
|
||||
|
||||
for (ManaPoolItem mana : manaItems) {
|
||||
if (filter != null) {
|
||||
if (!filter.match(mana.getSourceObject(), game)) {
|
||||
|
|
@ -120,7 +128,7 @@ public class ManaPool implements Serializable {
|
|||
}
|
||||
}
|
||||
}
|
||||
if (possibleAsThoughtPoolManaType == null && manaType != unlockedManaType && autoPayment && autoPaymentRestricted && mana.count() == mana.getStock()) {
|
||||
if (possibleAsThoughtPoolManaType == null && manaType != unlockedManaType && isAutoPayment() && isAutoPaymentRestricted() && mana.count() == mana.getStock()) {
|
||||
// no mana added beyond the stock so don't auto pay this
|
||||
continue;
|
||||
}
|
||||
|
|
@ -436,19 +444,19 @@ public class ManaPool implements Serializable {
|
|||
}
|
||||
|
||||
public boolean isAutoPayment() {
|
||||
return autoPayment;
|
||||
return autoPayment || forcedToPay;
|
||||
}
|
||||
|
||||
public void setAutoPayment(boolean autoPayment) {
|
||||
this.autoPayment = autoPayment;
|
||||
}
|
||||
|
||||
public void setAutoPaymentRestricted(boolean autoPaymentRestricted) {
|
||||
this.autoPaymentRestricted = autoPaymentRestricted;
|
||||
public boolean isAutoPaymentRestricted() {
|
||||
return autoPaymentRestricted || forcedToPay;
|
||||
}
|
||||
|
||||
public boolean isAutoPaymentRestricted() {
|
||||
return autoPaymentRestricted;
|
||||
public void setAutoPaymentRestricted(boolean autoPaymentRestricted) {
|
||||
this.autoPaymentRestricted = autoPaymentRestricted;
|
||||
}
|
||||
|
||||
public ManaType getUnlockedManaType() {
|
||||
|
|
@ -490,4 +498,39 @@ public class ManaPool implements Serializable {
|
|||
return itemsCopy;
|
||||
}
|
||||
|
||||
public void setForcedToPay(boolean forcedToPay) {
|
||||
this.forcedToPay = forcedToPay;
|
||||
}
|
||||
|
||||
public boolean isForcedToPay() {
|
||||
return forcedToPay;
|
||||
}
|
||||
|
||||
public UUID getPlayerId() {
|
||||
return playerId;
|
||||
}
|
||||
|
||||
public void storeMana() {
|
||||
poolBookmark.clear();
|
||||
poolBookmark.addAll(getManaItems());
|
||||
}
|
||||
|
||||
public List<ManaPoolItem> getPoolBookmark() {
|
||||
List<ManaPoolItem> itemsCopy = new ArrayList<>();
|
||||
for (ManaPoolItem manaItem : poolBookmark) {
|
||||
itemsCopy.add(manaItem.copy());
|
||||
}
|
||||
return itemsCopy;
|
||||
}
|
||||
|
||||
public void restoreMana(List<ManaPoolItem> manaList) {
|
||||
manaItems.clear();
|
||||
if (!manaList.isEmpty()) {
|
||||
List<ManaPoolItem> itemsCopy = new ArrayList<>();
|
||||
for (ManaPoolItem manaItem : manaList) {
|
||||
itemsCopy.add(manaItem.copy());
|
||||
}
|
||||
manaItems.addAll(itemsCopy);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -278,6 +278,8 @@ public interface Player extends MageItem, Copyable<Player> {
|
|||
*/
|
||||
void setTurnControlledBy(UUID playerId);
|
||||
|
||||
List<UUID> getTurnControllers();
|
||||
|
||||
UUID getTurnControlledBy();
|
||||
|
||||
/**
|
||||
|
|
@ -305,6 +307,8 @@ public interface Player extends MageItem, Copyable<Player> {
|
|||
*/
|
||||
void setGameUnderYourControl(boolean value);
|
||||
|
||||
void setGameUnderYourControl(boolean value, boolean fullRestore);
|
||||
|
||||
boolean isTestMode();
|
||||
|
||||
void setTestMode(boolean value);
|
||||
|
|
@ -856,6 +860,8 @@ public interface Player extends MageItem, Copyable<Player> {
|
|||
|
||||
Set<UUID> getUsersAllowedToSeeHandCards();
|
||||
|
||||
void setPayManaMode(boolean payManaMode);
|
||||
|
||||
boolean isInPayManaMode();
|
||||
|
||||
void setMatchPlayer(MatchPlayer matchPlayer);
|
||||
|
|
|
|||
|
|
@ -155,6 +155,7 @@ public abstract class PlayerImpl implements Player, Serializable {
|
|||
|
||||
protected boolean isGameUnderControl = true;
|
||||
protected UUID turnController;
|
||||
protected List<UUID> turnControllers = new ArrayList<>();
|
||||
protected Set<UUID> playersUnderYourControl = new HashSet<>();
|
||||
|
||||
protected Set<UUID> usersAllowedToSeeHandCards = new HashSet<>();
|
||||
|
|
@ -262,6 +263,8 @@ public abstract class PlayerImpl implements Player, Serializable {
|
|||
this.isGameUnderControl = player.isGameUnderControl;
|
||||
|
||||
this.turnController = player.turnController;
|
||||
this.turnControllers.clear();
|
||||
this.turnControllers.addAll(player.turnControllers);
|
||||
|
||||
this.passed = player.passed;
|
||||
this.passedTurn = player.passedTurn;
|
||||
|
|
@ -342,6 +345,8 @@ public abstract class PlayerImpl implements Player, Serializable {
|
|||
this.isGameUnderControl = player.isGameUnderControl();
|
||||
|
||||
this.turnController = player.getTurnControlledBy();
|
||||
this.turnControllers.clear();
|
||||
this.turnControllers.addAll(player.getTurnControllers());
|
||||
this.reachedNextTurnAfterLeaving = player.hasReachedNextTurnAfterLeaving();
|
||||
this.castSourceIdWithAlternateMana = player.getCastSourceIdWithAlternateMana();
|
||||
this.castSourceIdManaCosts = player.getCastSourceIdManaCosts();
|
||||
|
|
@ -397,6 +402,7 @@ public abstract class PlayerImpl implements Player, Serializable {
|
|||
this.turns = 0;
|
||||
this.isGameUnderControl = true;
|
||||
this.turnController = this.getId();
|
||||
this.turnControllers.clear();
|
||||
this.playersUnderYourControl.clear();
|
||||
|
||||
this.passed = false;
|
||||
|
|
@ -523,13 +529,13 @@ public abstract class PlayerImpl implements Player, Serializable {
|
|||
|
||||
@Override
|
||||
public void controlPlayersTurn(Game game, UUID playerId) {
|
||||
Player player = game.getPlayer(playerId);
|
||||
player.setTurnControlledBy(this.getId());
|
||||
game.informPlayers(getLogName() + " controls the turn of " + player.getLogName());
|
||||
if (!playerId.equals(this.getId())) {
|
||||
this.playersUnderYourControl.add(playerId);
|
||||
Player player = game.getPlayer(playerId);
|
||||
if (!player.hasLeft() && !player.hasLost()) {
|
||||
player.setGameUnderYourControl(false);
|
||||
player.setTurnControlledBy(this.getId());
|
||||
game.informPlayers(getLogName() + " controls the turn of " + player.getLogName());
|
||||
}
|
||||
DelayedTriggeredAbility ability = new AtTheEndOfTurnStepPostDelayedTriggeredAbility(new LoseControlOnOtherPlayersControllerEffect(this.getLogName(), player.getLogName()));
|
||||
ability.setSourceId(getId());
|
||||
|
|
@ -541,6 +547,12 @@ public abstract class PlayerImpl implements Player, Serializable {
|
|||
@Override
|
||||
public void setTurnControlledBy(UUID playerId) {
|
||||
this.turnController = playerId;
|
||||
this.turnControllers.add(playerId);
|
||||
}
|
||||
|
||||
@Override
|
||||
public List<UUID> getTurnControllers() {
|
||||
return this.turnControllers;
|
||||
}
|
||||
|
||||
@Override
|
||||
|
|
@ -566,9 +578,27 @@ public abstract class PlayerImpl implements Player, Serializable {
|
|||
|
||||
@Override
|
||||
public void setGameUnderYourControl(boolean value) {
|
||||
setGameUnderYourControl(value, true);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void setGameUnderYourControl(boolean value, boolean fullRestore) {
|
||||
this.isGameUnderControl = value;
|
||||
if (isGameUnderControl) {
|
||||
this.turnController = getId();
|
||||
if (fullRestore) {
|
||||
this.turnControllers.clear();
|
||||
this.turnController = getId();
|
||||
} else {
|
||||
if (turnControllers.size() > 0) {
|
||||
this.turnControllers.remove(turnControllers.size() - 1);
|
||||
}
|
||||
if (turnControllers.isEmpty()) {
|
||||
this.turnController = getId();
|
||||
} else {
|
||||
this.turnController = turnControllers.get(turnControllers.size() - 1);
|
||||
isGameUnderControl = false;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
|
@ -983,6 +1013,11 @@ public abstract class PlayerImpl implements Player, Serializable {
|
|||
return castSourceIdManaCosts;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void setPayManaMode(boolean payManaMode) {
|
||||
this.payManaMode = payManaMode;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean isInPayManaMode() {
|
||||
return payManaMode;
|
||||
|
|
@ -1853,7 +1888,7 @@ public abstract class PlayerImpl implements Player, Serializable {
|
|||
|
||||
@Override
|
||||
public int gainLife(int amount, Game game, UUID sourceId) {
|
||||
if (!canGainLife || amount == 0) {
|
||||
if (!canGainLife || amount <= 0) {
|
||||
return 0;
|
||||
}
|
||||
GameEvent event = new GameEvent(GameEvent.EventType.GAIN_LIFE, playerId, playerId, playerId, amount, false);
|
||||
|
|
|
|||
|
|
@ -2,6 +2,8 @@
|
|||
package mage.util;
|
||||
|
||||
import java.util.UUID;
|
||||
import java.util.stream.Stream;
|
||||
|
||||
import mage.MageObject;
|
||||
import mage.Mana;
|
||||
import mage.abilities.Ability;
|
||||
|
|
@ -354,13 +356,8 @@ public final class CardUtil {
|
|||
}
|
||||
|
||||
public static boolean checkNumeric(String s) {
|
||||
return s.chars().allMatch(Character::isDigit);
|
||||
|
||||
for (int i = 0; i < s.length(); i++) {
|
||||
if (!Character.isDigit(s.charAt(i))) {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
/**
|
||||
|
|
@ -442,9 +439,9 @@ public final class CardUtil {
|
|||
public static String getObjectZoneString(String text, MageObject mageObject, Game game) {
|
||||
int zoneChangeCounter = 0;
|
||||
if (mageObject instanceof Permanent) {
|
||||
zoneChangeCounter = ((Permanent) mageObject).getZoneChangeCounter(game);
|
||||
zoneChangeCounter = mageObject.getZoneChangeCounter(game);
|
||||
} else if (mageObject instanceof Card) {
|
||||
zoneChangeCounter = ((Card) mageObject).getZoneChangeCounter(game);
|
||||
zoneChangeCounter = mageObject.getZoneChangeCounter(game);
|
||||
}
|
||||
return getObjectZoneString(text, mageObject.getId(), game, zoneChangeCounter, false);
|
||||
}
|
||||
|
|
|
|||
|
|
@ -30,13 +30,11 @@ public class AttackedLastTurnWatcher extends Watcher {
|
|||
public AttackedLastTurnWatcher(final AttackedLastTurnWatcher watcher) {
|
||||
super(watcher);
|
||||
for (Entry<UUID, Set<MageObjectReference>> entry : watcher.attackedLastTurnCreatures.entrySet()) {
|
||||
Set<MageObjectReference> allAttackersCopy = new HashSet<>();
|
||||
allAttackersCopy.addAll(entry.getValue());
|
||||
Set<MageObjectReference> allAttackersCopy = new HashSet<>(entry.getValue());
|
||||
attackedLastTurnCreatures.put(entry.getKey(), allAttackersCopy);
|
||||
}
|
||||
for (Entry<UUID, Set<MageObjectReference>> entry : watcher.attackedThisTurnCreatures.entrySet()) {
|
||||
Set<MageObjectReference> allAttackersCopy = new HashSet<>();
|
||||
allAttackersCopy.addAll(entry.getValue());
|
||||
Set<MageObjectReference> allAttackersCopy = new HashSet<>(entry.getValue());
|
||||
attackedThisTurnCreatures.put(entry.getKey(), allAttackersCopy);
|
||||
}
|
||||
}
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue