This commit is contained in:
fireshoes 2016-10-17 23:45:47 -05:00
parent b733f911f7
commit 89dd981075
287 changed files with 3420 additions and 2315 deletions

View file

@ -24,11 +24,11 @@
* 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;
import java.util.UUID;
import mage.abilities.mana.ManaOptions;
import mage.game.Game;
/**
@ -38,20 +38,30 @@ import mage.game.Game;
public interface ActivatedAbility extends Ability {
boolean canActivate(UUID playerId, Game game);
/**
* Returns the minimal possible cost for what the ability can be activated
* or cast
*
* @param playerId
* @param game
* @return
*/
ManaOptions getMinimumCostToActivate(UUID playerId, Game game);
/**
* Creates a fresh copy of this activated ability.
*
*
* @return A new copy of this ability.
*/
@Override
ActivatedAbility copy();
ActivatedAbility copy();
/**
* Set a flag to know, that the ability is only created adn used to check
* Set a flag to know, that the ability is only created adn used to check
* what's playbable for the player.
*/
void setCheckPlayableMode();
boolean isCheckPlayableMode();
}

View file

@ -34,6 +34,7 @@ import mage.abilities.costs.mana.ManaCosts;
import mage.abilities.costs.mana.PhyrexianManaCost;
import mage.abilities.effects.Effect;
import mage.abilities.effects.Effects;
import mage.abilities.mana.ManaOptions;
import mage.cards.Card;
import mage.constants.AbilityType;
import mage.constants.AsThoughEffectType;
@ -199,6 +200,11 @@ public abstract class ActivatedAbilityImpl extends AbilityImpl implements Activa
return false;
}
@Override
public ManaOptions getMinimumCostToActivate(UUID playerId, Game game) {
return getManaCostsToPay().getOptions();
}
protected boolean controlsAbility(UUID playerId, Game game) {
if (this.controllerId != null && this.controllerId.equals(playerId)) {
return true;

View file

@ -40,8 +40,9 @@ import mage.game.Game;
*/
public class ActivationInfo {
protected int turnNum;
protected int activationCounter;
protected int turnNum = 0;
protected int activationCounter = 0;
protected String key;
public static ActivationInfo getInstance(Game game, UUID sourceId) {
return ActivationInfo.getInstance(game, sourceId, game.getState().getZoneChangeCounter(sourceId));
@ -49,17 +50,25 @@ public class ActivationInfo {
public static ActivationInfo getInstance(Game game, UUID sourceId, int zoneChangeCounter) {
String key = "ActivationInfo" + sourceId.toString() + zoneChangeCounter;
ActivationInfo activationInfo = (ActivationInfo) game.getState().getValue(key);
if (activationInfo == null) {
activationInfo = new ActivationInfo(game);
game.getState().setValue(key, activationInfo);
Integer activations = (Integer) game.getState().getValue(key);
ActivationInfo activationInfo;
if (activations != null) {
Integer turnNum = (Integer) game.getState().getValue(key + "T");
activationInfo = new ActivationInfo(game, turnNum, activations);
} else {
activationInfo = new ActivationInfo(game, game.getTurnNum(), 0);
}
activationInfo.setKey(key);
return activationInfo;
}
protected ActivationInfo(Game game) {
this.turnNum = game.getTurnNum();
this.activationCounter = 0;
public void setKey(String key) {
this.key = key;
}
protected ActivationInfo(Game game, int turnNum, int activationCounter) {
this.turnNum = turnNum;
this.activationCounter = activationCounter;
}
public void addActivation(Game game) {
@ -69,6 +78,8 @@ public class ActivationInfo {
} else {
activationCounter++;
}
game.getState().setValue(key, activationCounter);
game.getState().setValue(key + "T", turnNum);
}
public int getActivationCounter() {

View file

@ -1,16 +1,16 @@
/*
* Copyright 2010 BetaSteward_at_googlemail.com. All rights reserved.
*
*
* Redistribution and use in source and binary forms, with or without modification, are
* permitted provided that the following conditions are met:
*
*
* 1. Redistributions of source code must retain the above copyright notice, this list of
* conditions and the following disclaimer.
*
*
* 2. Redistributions in binary form must reproduce the above copyright notice, this list
* of conditions and the following disclaimer in the documentation and/or other materials
* provided with the distribution.
*
*
* THIS SOFTWARE IS PROVIDED BY BetaSteward_at_googlemail.com ``AS IS'' AND ANY EXPRESS OR IMPLIED
* WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND
* FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL BetaSteward_at_googlemail.com OR
@ -20,12 +20,11 @@
* ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
* NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF
* ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
*
*
* The views and conclusions contained in the software and documentation are those of the
* authors and should not be interpreted as representing official policies, either expressed
* or implied, of BetaSteward_at_googlemail.com.
*/
package mage.abilities;
import mage.abilities.effects.Effect;
@ -49,6 +48,7 @@ public abstract class DelayedTriggeredAbility extends TriggeredAbilityImpl {
public DelayedTriggeredAbility(Effect effect, Duration duration) {
this(effect, duration, true);
}
public DelayedTriggeredAbility(Effect effect, Duration duration, Boolean triggerOnlyOnce) {
this(effect, duration, triggerOnlyOnce, false);
}
@ -75,13 +75,25 @@ public abstract class DelayedTriggeredAbility extends TriggeredAbilityImpl {
public Boolean getTriggerOnlyOnce() {
return triggerOnlyOnce;
}
/**
* This method is called as the ability is added to the game (not as the
* ability triggers later)
*
* @param game
*/
public void initOnAdding(Game game) {
}
public void init(Game game) {
for (Effect effect: this.getEffects()) {
for (Effect effect : this.getEffects()) {
effect.getTargetPointer().init(game, this);
}
};
}
;
public boolean isInactive(Game game) {
return false;
}

View file

@ -9,7 +9,6 @@ import mage.abilities.effects.Effect;
import mage.constants.PhaseStep;
import mage.game.Game;
import mage.game.events.GameEvent;
import mage.game.turn.Step;
/**
*
@ -30,10 +29,10 @@ public class DealsDamageToOneOrMoreCreaturesTriggeredAbility extends DealsDamage
if (super.checkTrigger(event, game)) {
// check that combat damage does only once trigger also if multiple creatures were damaged because they block or were blocked by source
if (game.getTurn().getStepType().equals(PhaseStep.COMBAT_DAMAGE) || game.getTurn().getStepType().equals(PhaseStep.FIRST_COMBAT_DAMAGE)) {
Step step = (Step) game.getState().getValue("damageStep" + getOriginalId());
if (!game.getStep().equals(step)) {
Integer stepHash = (Integer) game.getState().getValue("damageStep" + getOriginalId());
if (stepHash == null || game.getStep().hashCode() != stepHash) {
// this ability did not trigger during this damage step
game.getState().setValue("damageStep" + getOriginalId(), game.getStep());
game.getState().setValue("damageStep" + getOriginalId(), game.getStep().hashCode());
return true;
}
} else {

View file

@ -59,7 +59,7 @@ public class ControllerGotLifeCount implements DynamicValue, MageSingleton {
}
public int calculate(Game game, UUID controllerId) {
PlayerGainedLifeWatcher watcher = (PlayerGainedLifeWatcher) game.getState().getWatchers().get("PlayerGainedLifeWatcher");
PlayerGainedLifeWatcher watcher = (PlayerGainedLifeWatcher) game.getState().getWatchers().get(PlayerGainedLifeWatcher.class.getName());
if (watcher != null) {
return watcher.getLiveGained(controllerId);
}

View file

@ -47,7 +47,7 @@ public abstract class ContinuousRuleModifyingEffectImpl extends ContinuousEffect
// 613.10
// Some continuous effects affect game rules rather than objects. For example, effects may modify
// a player’s maximum hand size, or say that a creature must attack this turn if able. These effects
// a player's maximum hand size, or say that a creature must attack this turn if able. These effects
// are applied after all other continuous effects have been applied. Continuous effects that affect
// the costs of spells or abilities are applied according to the order specified in rule 601.2e.
// All other such effects are applied in timestamp order. See also the rules for timestamp order

View file

@ -51,6 +51,7 @@ import mage.game.permanent.Permanent;
import mage.players.Player;
/**
* http://mtgsalvation.gamepedia.com/Land_changers
*
* @author LevelX2
*/
@ -58,6 +59,7 @@ public class BecomesBasicLandTargetEffect extends ContinuousEffectImpl {
protected boolean chooseLandType;
protected ArrayList<String> landTypes = new ArrayList();
protected ArrayList<String> landTypesToAdd = new ArrayList();
protected boolean loseOther; // loses all other abilities, card types, and creature types
public BecomesBasicLandTargetEffect(Duration duration) {
@ -99,6 +101,7 @@ public class BecomesBasicLandTargetEffect extends ContinuousEffectImpl {
public BecomesBasicLandTargetEffect(final BecomesBasicLandTargetEffect effect) {
super(effect);
this.landTypes.addAll(effect.landTypes);
this.landTypesToAdd.addAll(effect.landTypesToAdd);
this.chooseLandType = effect.chooseLandType;
this.loseOther = effect.loseOther;
}
@ -128,17 +131,8 @@ public class BecomesBasicLandTargetEffect extends ContinuousEffectImpl {
}
}
if (!loseOther) {
for (UUID targetPermanent : targetPointer.getTargets(game, source)) {
Permanent land = game.getPermanent(targetPermanent);
if (land != null) {
for (String type : land.getSubtype(game)) {
if (!landTypes.contains(type)) {
landTypes.add(type);
}
}
}
}
if (loseOther) {
landTypesToAdd.addAll(landTypes);
}
}
@ -153,15 +147,25 @@ public class BecomesBasicLandTargetEffect extends ContinuousEffectImpl {
if (!land.getCardType().contains(CardType.LAND)) {
land.getCardType().add(CardType.LAND);
}
// 305.7 Note that this doesn't remove any abilities that were granted to the land by other effects
// So the ability removing has to be done before Layer 6
land.removeAllAbilities(source.getSourceId(), game);
// 305.7
land.getSubtype(game).removeAll(CardRepository.instance.getLandTypes());
land.getSubtype(game).addAll(landTypes);
if (loseOther) {
// 305.7 Note that this doesn't remove any abilities that were granted to the land by other effects
// So the ability removing has to be done before Layer 6
land.removeAllAbilities(source.getSourceId(), game);
// 305.7
land.getSubtype(game).removeAll(CardRepository.instance.getLandTypes());
land.getSubtype(game).addAll(landTypes);
} else {
landTypesToAdd.clear();
for (String subtype : landTypes) {
if (!land.getSubtype(game).contains(subtype)) {
land.getSubtype(game).add(subtype);
landTypesToAdd.add(subtype);
}
}
}
break;
case AbilityAddingRemovingEffects_6:
for (String landType : landTypes) {
for (String landType : landTypesToAdd) {
switch (landType) {
case "Swamp":
land.addAbility(new BlackManaAbility(), source.getSourceId(), game);

View file

@ -0,0 +1,103 @@
/*
* 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.ruleModifying;
import java.util.List;
import java.util.UUID;
import mage.MageObject;
import mage.abilities.Ability;
import mage.abilities.effects.ContinuousRuleModifyingEffectImpl;
import mage.constants.Duration;
import mage.constants.Outcome;
import mage.filter.FilterPermanent;
import mage.game.Game;
import mage.game.events.GameEvent;
import mage.game.permanent.Permanent;
import mage.game.stack.StackObject;
import mage.players.Player;
import mage.target.Target;
/**
* 6/8/2016 If a spell or abilitys targets are changed, or if a copy of a spell
* or ability is put onto the stack and has new targets chosen, it doesnt have
* to target a Flagbearer.
*
* @author LevelX2
*/
public class TargetsHaveToTargetPermanentIfAbleEffect extends ContinuousRuleModifyingEffectImpl {
private final FilterPermanent filter;
public TargetsHaveToTargetPermanentIfAbleEffect(FilterPermanent filter) {
super(Duration.WhileOnBattlefield, Outcome.Detriment);
this.filter = filter;
staticText = "While choosing targets as part of casting a spell or activating an ability, your opponents must choose at least " + this.filter.getMessage() + " on the battlefield if able";
}
public TargetsHaveToTargetPermanentIfAbleEffect(final TargetsHaveToTargetPermanentIfAbleEffect effect) {
super(effect);
this.filter = effect.filter;
}
@Override
public TargetsHaveToTargetPermanentIfAbleEffect copy() {
return new TargetsHaveToTargetPermanentIfAbleEffect(this);
}
@Override
public boolean checksEventType(GameEvent event, Game game) {
return event.getType() == GameEvent.EventType.TARGETS_VALID;
}
@Override
public String getInfoMessage(Ability source, GameEvent event, Game game) {
MageObject mageObject = game.getObject(source.getSourceId());
if (mageObject != null) {
return "You must choose at least " + this.filter.getMessage() + " on the battlefield as target if able (" + mageObject.getIdName() + ").";
}
return null;
}
@Override
public boolean applies(GameEvent event, Ability source, Game game) {
Player controller = game.getPlayer(source.getControllerId());
Player targetingPlayer = game.getPlayer(event.getPlayerId());
if (controller != null
&& targetingPlayer.isHuman() // TODO: This target handling does only work for non AI players so AI logic
&& controller.hasOpponent(event.getPlayerId(), game)) {
StackObject stackObject = game.getStack().getStackObject(event.getSourceId());
if (stackObject.isCopy()) {
return false;
}
Ability ability = (Ability) getValue("targetAbility");
if (ability != null) {
// Get all the allowed permanents on the battlefield in range of the abilities controller
List<Permanent> allowedPermanents = game.getBattlefield().getActivePermanents(filter, event.getPlayerId(), event.getSourceId(), game);
if (!allowedPermanents.isEmpty()) {
boolean canTargetAllowedPermanent = false;
for (UUID modeId : ability.getModes().getSelectedModes()) {
ability.getModes().setActiveMode(modeId);
for (Target target : ability.getTargets()) {
// Check if already targeted
for (Permanent allowedPermanent : allowedPermanents) {
if (target.getTargets().contains(allowedPermanent.getId())) {
return false;
}
if (target.canTarget(allowedPermanent.getId(), source, game)) {
canTargetAllowedPermanent = true;
}
}
}
}
return canTargetAllowedPermanent;
}
}
}
return false;
}
}

View file

@ -93,7 +93,7 @@ public abstract class SearchTargetGraveyardHandLibraryForCardNameAndExileEffect
// cards in Graveyard
int cardsCount = (cardName.isEmpty() ? 0 : targetPlayer.getGraveyard().count(filter, game));
if (cardsCount > 0) {
filter.setMessage("card named " + cardName + " in the graveyard of " + targetPlayer.getLogName());
filter.setMessage("card named " + cardName + " in the graveyard of " + targetPlayer.getName());
TargetCard target = new TargetCard((graveyardExileOptional ? 0 : cardsCount), cardsCount, Zone.GRAVEYARD, filter);
if (controller.choose(Outcome.Exile, targetPlayer.getGraveyard(), target, game)) {
controller.moveCards(new CardsImpl(target.getTargets()), Zone.EXILED, source, game);
@ -102,7 +102,7 @@ public abstract class SearchTargetGraveyardHandLibraryForCardNameAndExileEffect
// cards in Hand
cardsCount = (cardName.isEmpty() ? 0 : targetPlayer.getHand().count(filter, game));
filter.setMessage("card named " + cardName + " in the hand of " + targetPlayer.getLogName());
filter.setMessage("card named " + cardName + " in the hand of " + targetPlayer.getName());
TargetCard target = new TargetCard(0, cardsCount, Zone.HAND, filter);
if (controller.choose(Outcome.Exile, targetPlayer.getHand(), target, game)) {
controller.moveCards(new CardsImpl(target.getTargets()), Zone.EXILED, source, game);

View file

@ -37,6 +37,8 @@ import mage.abilities.costs.mana.AlternateManaPaymentAbility;
import mage.abilities.costs.mana.ManaCost;
import mage.abilities.effects.OneShotEffect;
import mage.cards.Card;
import mage.cards.Cards;
import mage.cards.CardsImpl;
import mage.constants.AbilityType;
import mage.constants.ManaType;
import mage.constants.Outcome;
@ -56,17 +58,17 @@ import mage.util.CardUtil;
* applies only after the total cost of the spell with delve is determined.
* 702.65b Multiple instances of delve on the same spell are redundant.
*
* The rules for delve have changed slightly since it was last in an
* expansion. Previously, delve reduced the cost to cast a spell. Under the
* current rules, you exile cards from your graveyard at the same time you pay
* the spells cost. Exiling a card this way is simply another way to pay that
* cost. * Delve doesn't change a spells mana cost or converted mana cost. For
* example, Dead Drops converted mana cost is 10 even if you exiled three cards
* to cast it. * You cant exile cards to pay for the colored mana requirements
* of a spell with delve. * You cant exile more cards than the generic mana
* requirement of a spell with delve. For example, you cant exile more than
* nine cards from your graveyard to cast Dead Drop. * Because delve isn't an
* alternative cost, it can be used in conjunction with alternative costs.
* The rules for delve have changed slightly since it was last in an expansion.
* Previously, delve reduced the cost to cast a spell. Under the current rules,
* you exile cards from your graveyard at the same time you pay the spells
* cost. Exiling a card this way is simply another way to pay that cost. * Delve
* doesn't change a spells mana cost or converted mana cost. For example, Dead
* Drops converted mana cost is 10 even if you exiled three cards to cast it. *
* You cant exile cards to pay for the colored mana requirements of a spell
* with delve. * You cant exile more cards than the generic mana requirement of
* a spell with delve. For example, you cant exile more than nine cards from
* your graveyard to cast Dead Drop. * Because delve isn't an alternative cost,
* it can be used in conjunction with alternative costs.
*
* @author LevelX2
*
@ -155,18 +157,23 @@ class DelveEffect extends OneShotEffect {
Player controller = game.getPlayer(source.getControllerId());
if (controller != null) {
ExileFromGraveCost exileFromGraveCost = (ExileFromGraveCost) source.getCosts().get(0);
List<Card> exiledCards = exileFromGraveCost.getExiledCards();
if (exiledCards.size() > 0) {
Cards toDelve = new CardsImpl();
for (Card card : exiledCards) {
toDelve.add(card);
}
ManaPool manaPool = controller.getManaPool();
manaPool.addMana(new Mana(0, 0, 0, 0, 0, 0, 0, exiledCards.size()), game, source);
manaPool.addMana(new Mana(0, 0, 0, 0, 0, 0, 0, toDelve.size()), game, source);
manaPool.unlockManaType(ManaType.COLORLESS);
String keyString = CardUtil.getCardZoneString("delvedCards", source.getSourceId(), game);
@SuppressWarnings("unchecked")
List<Card> delvedCards = (List<Card>) game.getState().getValue(keyString);
Cards delvedCards = (Cards) game.getState().getValue(keyString);
if (delvedCards == null) {
game.getState().setValue(keyString, exiledCards);
game.getState().setValue(keyString, toDelve);
} else {
delvedCards.addAll(exiledCards);
delvedCards.addAll(toDelve);
}
}
return true;

View file

@ -28,10 +28,12 @@
package mage.abilities.keyword;
import java.util.UUID;
import mage.Mana;
import mage.abilities.SpellAbility;
import mage.abilities.costs.common.SacrificeTargetCost;
import mage.abilities.costs.mana.ManaCost;
import mage.abilities.costs.mana.ManaCosts;
import mage.abilities.mana.ManaOptions;
import mage.cards.Card;
import mage.constants.Outcome;
import mage.constants.SpellAbilityType;
@ -84,6 +86,26 @@ public class EmergeAbility extends SpellAbility {
return false;
}
@Override
public ManaOptions getMinimumCostToActivate(UUID playerId, Game game) {
int maxCMC = 0;
for (Permanent creature : game.getBattlefield().getActivePermanents(new FilterControlledCreaturePermanent(), playerId, this.getSourceId(), game)) {
int cmc = creature.getConvertedManaCost();
if (cmc > maxCMC) {
maxCMC = cmc;
}
}
ManaOptions manaOptions = super.getMinimumCostToActivate(playerId, game);
for (Mana mana : manaOptions) {
if (mana.getGeneric() > maxCMC) {
mana.setGeneric(mana.getGeneric() - maxCMC);
} else {
mana.setGeneric(0);
}
}
return manaOptions;
}
@Override
public boolean activate(Game game, boolean noMana) {
Player controller = game.getPlayer(this.getControllerId());

View file

@ -53,7 +53,11 @@ import mage.target.common.TargetCreaturePermanent;
public class ProvokeAbility extends AttacksTriggeredAbility {
public ProvokeAbility() {
super(new UntapTargetEffect(), true, "Provoke <i>(Whenever this attacks, you may have target creature defending player controls untap and block it if able.)</i>");
this("Provoke <i>(Whenever this attacks, you may have target creature defending player controls untap and block it if able.)</i>");
}
public ProvokeAbility(String text) {
super(new UntapTargetEffect(), true, text);
this.addEffect(new ProvokeRequirementEffect());
}

View file

@ -40,7 +40,7 @@ public class RepairAbility extends DiesTriggeredAbility {
addSubAbility(new RepairCastFromGraveyardTriggeredAbility());
ruleText = "Repair " + count + " <i>(When this creature dies, put " + count
+ " repair counters on it. At the beggining of your upkeep, remove a repair counter. "
+ " repair counters on it. At the beginning of your upkeep, remove a repair counter. "
+ "Whenever the last is removed, you may cast it from graveyard until end of turn.)</i>";
}

View file

@ -31,6 +31,7 @@ import java.io.Serializable;
import java.util.ArrayList;
import java.util.Date;
import java.util.EnumMap;
import java.util.GregorianCalendar;
import java.util.Iterator;
import java.util.List;
import mage.cards.repository.CardCriteria;
@ -126,17 +127,15 @@ public abstract class ExpansionSet implements Serializable {
protected int numBoosterDoubleFaced; // -1 = include normally 0 = exclude 1-n = include explicit
protected int ratioBoosterMythic;
protected String packageName;
protected int maxCardNumberInBooster; // used to ommit cards with collector numbers beyond the regular cards in a set for boosters
protected int maxCardNumberInBooster; // used to omit cards with collector numbers beyond the regular cards in a set for boosters
protected final EnumMap<Rarity, List<CardInfo>> savedCards;
public ExpansionSet(String name, String code, String packageName, Date releaseDate, SetType setType) {
public ExpansionSet(String name, String code, Date releaseDate, SetType setType) {
this.name = name;
this.code = code;
this.releaseDate = releaseDate;
this.setType = setType;
this.packageName = packageName;
this.maxCardNumberInBooster = Integer.MAX_VALUE;
savedCards = new EnumMap<>(Rarity.class);
}
@ -161,10 +160,6 @@ public abstract class ExpansionSet implements Serializable {
return setType;
}
public String getPackageName() {
return packageName;
}
public String getBlockName() {
return blockName;
}
@ -298,6 +293,11 @@ public abstract class ExpansionSet implements Serializable {
}
}
public static Date buildDate(int year, int month, int day) {
// The month starts with 0 = jan ... dec = 11
return new GregorianCalendar(year, month - 1, day).getTime();
}
/**
* Can be overwritten if sometimes special cards will be generated instead
* of common slots

View file

@ -43,7 +43,7 @@ import mage.cards.repository.CardRepository;
*/
public class DckDeckImporter extends DeckImporter {
private static final Pattern pattern = Pattern.compile("(SB:)?\\s*(\\d*)\\s*\\[([^]:]+):([^]:]+)\\].*");
private static final Pattern pattern = Pattern.compile("(SB:)?\\s*(\\d*)\\s*\\[([^]:]+):([^]:]+)\\]\\s*(.*)\\s*$");
private static final Pattern layoutPattern = Pattern.compile("LAYOUT (\\w+):\\((\\d+),(\\d+)\\)([^|]+)\\|(.*)$");
@ -70,6 +70,17 @@ public class DckDeckImporter extends DeckImporter {
DeckCardInfo deckCardInfo = null;
CardInfo cardInfo = CardRepository.instance.findCard(setCode, cardNum);
if (cardInfo == null) {
// Try alternate based on name
String cardName = m.group(5);
if (cardName != null && cardName.length() > 0) {
cardInfo = CardRepository.instance.findPreferedCoreExpansionCard(cardName, false);
sbMessage.append("Could not find card '" + cardName + "' in set " + setCode + " of number " + cardNum + ".\n");
if (cardInfo != null) {
sbMessage.append("Made substitution of " + cardInfo.getCardNumber() + ", " + cardInfo.getCard().getExpansionSetCode() + " instead.\n");
}
}
}
if (cardInfo != null) {
deckCardInfo = new DeckCardInfo(cardInfo.getName(), cardInfo.getCardNumber(), cardInfo.getSetCode());
}

View file

@ -29,12 +29,9 @@
package mage.cards.repository;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import mage.cards.*;
import mage.util.ClassScanner;
import org.apache.log4j.Logger;
/**
@ -54,13 +51,8 @@ public class CardScanner {
scanned = true;
List<CardInfo> cardsToAdd = new ArrayList<>();
Map<ClassLoader, List<String>> packageMap = new HashMap<>();
for (ExpansionSet set : Sets.getInstance().values()) {
ClassLoader cl = set.getClass().getClassLoader();
if(!packageMap.containsKey(cl)) packageMap.put(cl, new ArrayList<String>());
List<String> packages = packageMap.get(cl);
packages.add(set.getPackageName());
ExpansionRepository.instance.add(new ExpansionInfo(set));
}
ExpansionRepository.instance.setContentVersion(ExpansionRepository.instance.getContentVersionConstant());

View file

@ -53,6 +53,7 @@ public class RepositoryUtil {
}
public static long getDatabaseVersion(ConnectionSource connectionSource, String entityName) throws SQLException {
TableUtils.createTableIfNotExists(connectionSource, DatabaseVersion.class);
Dao<DatabaseVersion, Object> dbVersionDao = DaoManager.createDao(connectionSource, DatabaseVersion.class);
QueryBuilder<DatabaseVersion, Object> queryBuilder = dbVersionDao.queryBuilder();
@ -61,9 +62,8 @@ public class RepositoryUtil {
if (dbVersions.isEmpty()) {
return 0;
} else {
return dbVersions.get(0). getVersion();
return dbVersions.get(0).getVersion();
}
}
}

View file

@ -6,6 +6,7 @@
package mage.filter;
import mage.constants.CardType;
import mage.filter.common.FilterArtifactCard;
import mage.filter.common.FilterArtifactCreaturePermanent;
import mage.filter.common.FilterControlledArtifactPermanent;
import mage.filter.common.FilterControlledPermanent;
@ -18,11 +19,12 @@ import mage.filter.predicate.mageobject.CardTypePredicate;
*/
public class StaticFilters {
public static final FilterArtifactCreaturePermanent FILTER_ARTIFACT_CREATURE_PERMANENT = new FilterArtifactCreaturePermanent();
public static final FilterPermanent FILTER_PERMANENT_ARTIFACT_OR_CREATURE = new FilterPermanent("artifact or creature");
public static final FilterControlledPermanent FILTER_CONTROLLED_PERMANENT_ARTIFACT_OR_CREATURE = new FilterControlledPermanent("artifact or creature you control");
public static final FilterControlledPermanent FILTER_CONTROLLED_PERMANENT_ARTIFACT = new FilterControlledArtifactPermanent();
public static final FilterArtifactCard FILTER_CARD_ARTIFACT = new FilterArtifactCard();
public static final FilterCard FILTER_CARD_ARTIFACT_OR_CREATURE = new FilterCard("artifact or creature card");
public static final FilterArtifactCreaturePermanent FILTER_ARTIFACT_CREATURE_PERMANENT = new FilterArtifactCreaturePermanent();
static {
FILTER_PERMANENT_ARTIFACT_OR_CREATURE.add(Predicates.or(

View file

@ -159,7 +159,6 @@ public abstract class GameImpl implements Game, Serializable {
FILTER_LEGENDARY.add(new SupertypePredicate("Legendary"));
}
private transient Object customData;
protected boolean simulation = false;
@ -1520,10 +1519,11 @@ public abstract class GameImpl implements Game, Serializable {
// return addDelayedTriggeredAbility(delayedAbility);
DelayedTriggeredAbility newAbility = delayedAbility.copy();
newAbility.newId();
newAbility.initOnAdding(this);
// ability.init is called as the ability triggeres not now.
// If a FixedTarget pointer is already set from the effect setting up this delayed ability
// it has to be already initialized so it won't be overwitten as the ability triggers
state.addDelayedTriggeredAbility(newAbility);
getState().addDelayedTriggeredAbility(newAbility);
return newAbility.getId();
}
@ -1532,10 +1532,11 @@ public abstract class GameImpl implements Game, Serializable {
public UUID addDelayedTriggeredAbility(DelayedTriggeredAbility delayedAbility) {
DelayedTriggeredAbility newAbility = delayedAbility.copy();
newAbility.newId();
newAbility.initOnAdding(this);
// ability.init is called as the ability triggeres not now.
// If a FixedTarget pointer is already set from the effect setting up this delayed ability
// it has to be already initialized so it won't be overwitten as the ability triggers
state.addDelayedTriggeredAbility(newAbility);
getState().addDelayedTriggeredAbility(newAbility);
return newAbility.getId();
}

View file

@ -32,6 +32,7 @@ import java.util.ArrayList;
import java.util.Collection;
import java.util.Collections;
import java.util.HashMap;
import java.util.HashSet;
import java.util.Iterator;
import java.util.LinkedHashSet;
import java.util.LinkedList;
@ -182,7 +183,11 @@ public class GameState implements Serializable, Copyable<GameState> {
this.turnMods = state.turnMods.copy();
this.watchers = state.watchers.copy();
for (Map.Entry<String, Object> entry : state.values.entrySet()) {
this.values.put(entry.getKey(), entry.getValue());
if (entry.getValue() instanceof HashSet) {
this.values.put(entry.getKey(), (HashSet) ((HashSet) entry.getValue()).clone());
} else {
this.values.put(entry.getKey(), entry.getValue());
}
}
this.zones.putAll(state.zones);
this.simultaneousEvents.addAll(state.simultaneousEvents);
@ -893,8 +898,10 @@ public class GameState implements Serializable, Copyable<GameState> {
/**
* Best only use immutable objects, otherwise the states/values of the
* object may be changed by AI simulation, because the Value objects are not
* copied as the state class is copied.
* object may be changed by AI simulation or rollbacks, because the Value
* objects are not copied as the state class is copied. Mutable supported:
* HashSet with immutable entries (e.g. HashSet< UUID > or HashSet< String
* >)
*
* @param valueId
* @param value

View file

@ -171,6 +171,13 @@ public class GameEvent implements Serializable {
MANA_PAYED,
LOSES, LOST, WINS,
TARGET, TARGETED,
/* TARGETS_VALID
targetId id of the spell or id of stack ability the targets were set to
sourceId = targetId
playerId controller of the spell or stack ability
amount not used for this event
*/
TARGETS_VALID,
/* COUNTER
targetId id of the spell or id of stack ability
sourceId sourceId of the ability countering the spell or stack ability

View file

@ -4,13 +4,14 @@ import mage.MageInt;
import mage.constants.CardType;
public class OozeToken extends Token {
public OozeToken(MageInt power, MageInt toughness) {
public OozeToken(int power, int toughness) {
super("Ooze", power + "/" + toughness + " green ooze creature token");
cardType.add(CardType.CREATURE);
color.setGreen(true);
subtype.add("Ooze");
power = new MageInt(0);
toughness = new MageInt(0);
this.power = new MageInt(power);
this.toughness = new MageInt(toughness);
}
public OozeToken() {

View file

@ -2489,7 +2489,7 @@ public abstract class PlayerImpl implements Player, Serializable {
canBeCastRegularly = false;
}
if (canBeCastRegularly) {
ManaOptions abilityOptions = copy.getManaCostsToPay().getOptions();
ManaOptions abilityOptions = copy.getMinimumCostToActivate(playerId, game);
if (abilityOptions.isEmpty()) {
return true;
} else {
@ -2694,6 +2694,7 @@ public abstract class PlayerImpl implements Player, Serializable {
playable.add(ability);
}
}
ability.setControllerId(card.getOwnerId());
}
}
}

View file

@ -33,6 +33,7 @@ import java.util.UUID;
import mage.abilities.Ability;
import mage.constants.Outcome;
import mage.game.Game;
import mage.game.events.GameEvent;
/**
*
@ -94,6 +95,7 @@ public class Targets extends ArrayList<Target> {
if (!canChoose(source.getSourceId(), playerId, game)) {
return false;
}
int state = game.bookmarkState();
while (!isChosen()) {
Target target = this.getUnchosen().get(0);
UUID targetController = playerId;
@ -106,6 +108,12 @@ public class Targets extends ArrayList<Target> {
if (!target.chooseTarget(outcome, targetController, source, game)) {
return false;
}
// Check if there are some rules for targets are violated, if so reset the targets and start again
if (this.getUnchosen().isEmpty()
&& game.replaceEvent(new GameEvent(GameEvent.EventType.TARGETS_VALID, source.getSourceId(), source.getSourceId(), source.getControllerId()), source)) {
game.restoreState(state, "Targets");
clearChosen();
}
}
}
return true;

View file

@ -41,6 +41,18 @@ public class FixedTarget implements TargetPointer {
this.zoneChangeCounter = zoneChangeCounter;
}
/**
* Use this to set the target to exactly the zone the target is currently in
*
* @param targetId
* @param game
*/
public FixedTarget(UUID targetId, Game game) {
this.targetId = targetId;
this.initialized = true;
this.zoneChangeCounter = game.getState().getZoneChangeCounter(targetId);
}
public FixedTarget(final FixedTarget fixedTarget) {
this.targetId = fixedTarget.targetId;
this.zoneChangeCounter = fixedTarget.zoneChangeCounter;

View file

@ -25,7 +25,6 @@
* authors and should not be interpreted as representing official policies, either expressed
* or implied, of BetaSteward_at_googlemail.com.
*/
package mage.watchers.common;
import java.util.HashMap;
@ -37,8 +36,6 @@ import mage.game.Game;
import mage.game.events.GameEvent;
import mage.watchers.Watcher;
/**
* Counts amount of life gained during the current turn by players.
*
@ -47,11 +44,10 @@ import mage.watchers.Watcher;
*/
public class PlayerGainedLifeWatcher extends Watcher {
private Map<UUID, Integer> amountOfLifeGainedThisTurn = new HashMap<UUID, Integer>();
private final Map<UUID, Integer> amountOfLifeGainedThisTurn = new HashMap<>();
public PlayerGainedLifeWatcher() {
super("PlayerGainedLifeWatcher", WatcherScope.GAME);
super(PlayerGainedLifeWatcher.class.getName(), WatcherScope.GAME);
}
public PlayerGainedLifeWatcher(final PlayerGainedLifeWatcher watcher) {
@ -71,9 +67,9 @@ public class PlayerGainedLifeWatcher extends Watcher {
if (playerId != null) {
Integer amount = amountOfLifeGainedThisTurn.get(playerId);
if (amount == null) {
amount = Integer.valueOf(event.getAmount());
amount = event.getAmount();
} else {
amount = Integer.valueOf(amount + event.getAmount());
amount = amount + event.getAmount();
}
amountOfLifeGainedThisTurn.put(playerId, amount);
}
@ -83,7 +79,7 @@ public class PlayerGainedLifeWatcher extends Watcher {
public int getLiveGained(UUID playerId) {
Integer amount = amountOfLifeGainedThisTurn.get(playerId);
if (amount != null) {
return amount.intValue();
return amount;
}
return 0;
}